| // SPDX-License-Identifier: GPL-2.0 | |
| // Copyright 2025 NXP. | |
| // NXP PF0900 pmic driver | |
| #include <linux/bitfield.h> | |
| #include <linux/crc8.h> | |
| #include <linux/err.h> | |
| #include <linux/gpio/consumer.h> | |
| #include <linux/i2c.h> | |
| #include <linux/interrupt.h> | |
| #include <linux/kernel.h> | |
| #include <linux/module.h> | |
| #include <linux/of.h> | |
| #include <linux/of_device.h> | |
| #include <linux/platform_device.h> | |
| #include <linux/regmap.h> | |
| #include <linux/regulator/driver.h> | |
| #include <linux/regulator/machine.h> | |
| #include <linux/regulator/of_regulator.h> | |
| enum pf0900_regulators { | |
| PF0900_SW1 = 0, | |
| PF0900_SW2, | |
| PF0900_SW3, | |
| PF0900_SW4, | |
| PF0900_SW5, | |
| PF0900_LDO1, | |
| PF0900_LDO2, | |
| PF0900_LDO3, | |
| PF0900_VAON, | |
| PF0900_REGULATOR_CNT, | |
| }; | |
| enum { | |
| PF0900_DVS_LEVEL_RUN = 0, | |
| PF0900_DVS_LEVEL_STANDBY, | |
| PF0900_DVS_LEVEL_MAX, | |
| }; | |
| #define PF0900_VAON_VOLTAGE_NUM 0x03 | |
| #define PF0900_SW_VOLTAGE_NUM 0x100 | |
| #define PF0900_LDO_VOLTAGE_NUM 0x20 | |
| #define REGU_SW_CNT 0x5 | |
| #define REGU_LDO_VAON_CNT 0x4 | |
| enum { | |
| PF0900_REG_DEV_ID = 0x00, | |
| PF0900_REG_DEV_FAM = 0x01, | |
| PF0900_REG_REV_ID = 0x02, | |
| PF0900_REG_PROG_ID1 = 0x03, | |
| PF0900_REG_PROG_ID2 = 0x04, | |
| PF0900_REG_SYSTEM_INT = 0x05, | |
| PF0900_REG_STATUS1_INT = 0x06, | |
| PF0900_REG_STATUS1_MSK = 0x07, | |
| PF0900_REG_STATUS1_SNS = 0x08, | |
| PF0900_REG_STATUS2_INT = 0x09, | |
| PF0900_REG_STATUS2_MSK = 0x0A, | |
| PF0900_REG_STATUS2_SNS = 0x0B, | |
| PF0900_REG_STATUS3_INT = 0x0C, | |
| PF0900_REG_STATUS3_MSK = 0x0D, | |
| PF0900_REG_SW_MODE_INT = 0x0E, | |
| PF0900_REG_SW_MODE_MSK = 0x0F, | |
| PF0900_REG_SW_ILIM_INT = 0x10, | |
| PF0900_REG_SW_ILIM_MSK = 0x11, | |
| PF0900_REG_SW_ILIM_SNS = 0x12, | |
| PF0900_REG_LDO_ILIM_INT = 0x13, | |
| PF0900_REG_LDO_ILIM_MSK = 0x14, | |
| PF0900_REG_LDO_ILIM_SNS = 0x15, | |
| PF0900_REG_SW_UV_INT = 0x16, | |
| PF0900_REG_SW_UV_MSK = 0x17, | |
| PF0900_REG_SW_UV_SNS = 0x18, | |
| PF0900_REG_SW_OV_INT = 0x19, | |
| PF0900_REG_SW_OV_MSK = 0x1A, | |
| PF0900_REG_SW_OV_SNS = 0x1B, | |
| PF0900_REG_LDO_UV_INT = 0x1C, | |
| PF0900_REG_LDO_UV_MSK = 0x1D, | |
| PF0900_REG_LDO_UV_SNS = 0x1E, | |
| PF0900_REG_LDO_OV_INT = 0x1F, | |
| PF0900_REG_LDO_OV_MSK = 0x20, | |
| PF0900_REG_LDO_OV_SNS = 0x21, | |
| PF0900_REG_PWRON_INT = 0x22, | |
| PF0900_REG_IO_INT = 0x24, | |
| PF0900_REG_IO_MSK = 0x25, | |
| PF0900_REG_IO_SNS = 0x26, | |
| PF0900_REG_IOSHORT_SNS = 0x27, | |
| PF0900_REG_ABIST_OV1 = 0x28, | |
| PF0900_REG_ABIST_OV2 = 0x29, | |
| PF0900_REG_ABIST_UV1 = 0x2A, | |
| PF0900_REG_ABIST_UV2 = 0x2B, | |
| PF0900_REG_ABIST_IO = 0x2C, | |
| PF0900_REG_TEST_FLAGS = 0x2D, | |
| PF0900_REG_HFAULT_FLAGS = 0x2E, | |
| PF0900_REG_FAULT_FLAGS = 0x2F, | |
| PF0900_REG_FS0B_CFG = 0x30, | |
| PF0900_REG_FCCU_CFG = 0x31, | |
| PF0900_REG_RSTB_CFG1 = 0x32, | |
| PF0900_REG_SYSTEM_CMD = 0x33, | |
| PF0900_REG_FS0B_CMD = 0x34, | |
| PF0900_REG_SECURE_WR1 = 0x35, | |
| PF0900_REG_SECURE_WR2 = 0x36, | |
| PF0900_REG_VMON_CFG1 = 0x37, | |
| PF0900_REG_SYS_CFG1 = 0x38, | |
| PF0900_REG_GPO_CFG = 0x39, | |
| PF0900_REG_GPO_CTRL = 0x3A, | |
| PF0900_REG_PWRUP_CFG = 0x3B, | |
| PF0900_REG_RSTB_PWRUP = 0x3C, | |
| PF0900_REG_GPIO1_PWRUP = 0x3D, | |
| PF0900_REG_GPIO2_PWRUP = 0x3E, | |
| PF0900_REG_GPIO3_PWRUP = 0x3F, | |
| PF0900_REG_GPIO4_PWRUP = 0x40, | |
| PF0900_REG_VMON1_PWRUP = 0x41, | |
| PF0900_REG_VMON2_PWRUP = 0x42, | |
| PF0900_REG_SW1_PWRUP = 0x43, | |
| PF0900_REG_SW2_PWRUP = 0x44, | |
| PF0900_REG_SW3_PWRUP = 0x45, | |
| PF0900_REG_SW4_PWRUP = 0x46, | |
| PF0900_REG_SW5_PWRUP = 0x47, | |
| PF0900_REG_LDO1_PWRUP = 0x48, | |
| PF0900_REG_LDO2_PWRUP = 0x49, | |
| PF0900_REG_LDO3_PWRUP = 0x4A, | |
| PF0900_REG_VAON_PWRUP = 0x4B, | |
| PF0900_REG_FREQ_CTRL = 0x4C, | |
| PF0900_REG_PWRON_CFG = 0x4D, | |
| PF0900_REG_WD_CTRL1 = 0x4E, | |
| PF0900_REG_WD_CTRL2 = 0x4F, | |
| PF0900_REG_WD_CFG1 = 0x50, | |
| PF0900_REG_WD_CFG2 = 0x51, | |
| PF0900_REG_WD_CNT1 = 0x52, | |
| PF0900_REG_WD_CNT2 = 0x53, | |
| PF0900_REG_FAULT_CFG = 0x54, | |
| PF0900_REG_FAULT_CNT = 0x55, | |
| PF0900_REG_DFS_CNT = 0x56, | |
| PF0900_REG_AMUX_CFG = 0x57, | |
| PF0900_REG_VMON1_RUN_CFG = 0x58, | |
| PF0900_REG_VMON1_STBY_CFG = 0x59, | |
| PF0900_REG_VMON1_CTRL = 0x5A, | |
| PF0900_REG_VMON2_RUN_CFG = 0x5B, | |
| PF0900_REG_VMON2_STBY_CFG = 0x5C, | |
| PF0900_REG_VMON2_CTRL = 0x5D, | |
| PF0900_REG_SW1_VRUN = 0x5E, | |
| PF0900_REG_SW1_VSTBY = 0x5F, | |
| PF0900_REG_SW1_MODE = 0x60, | |
| PF0900_REG_SW1_CFG1 = 0x61, | |
| PF0900_REG_SW1_CFG2 = 0x62, | |
| PF0900_REG_SW2_VRUN = 0x63, | |
| PF0900_REG_SW2_VSTBY = 0x64, | |
| PF0900_REG_SW2_MODE = 0x65, | |
| PF0900_REG_SW2_CFG1 = 0x66, | |
| PF0900_REG_SW2_CFG2 = 0x67, | |
| PF0900_REG_SW3_VRUN = 0x68, | |
| PF0900_REG_SW3_VSTBY = 0x69, | |
| PF0900_REG_SW3_MODE = 0x6A, | |
| PF0900_REG_SW3_CFG1 = 0x6B, | |
| PF0900_REG_SW3_CFG2 = 0x6C, | |
| PF0900_REG_SW4_VRUN = 0x6D, | |
| PF0900_REG_SW4_VSTBY = 0x6E, | |
| PF0900_REG_SW4_MODE = 0x6F, | |
| PF0900_REG_SW4_CFG1 = 0x70, | |
| PF0900_REG_SW4_CFG2 = 0x71, | |
| PF0900_REG_SW5_VRUN = 0x72, | |
| PF0900_REG_SW5_VSTBY = 0x73, | |
| PF0900_REG_SW5_MODE = 0x74, | |
| PF0900_REG_SW5_CFG1 = 0x75, | |
| PF0900_REG_SW5_CFG2 = 0x76, | |
| PF0900_REG_LDO1_RUN = 0x77, | |
| PF0900_REG_LDO1_STBY = 0x78, | |
| PF0900_REG_LDO1_CFG2 = 0x79, | |
| PF0900_REG_LDO2_RUN = 0x7A, | |
| PF0900_REG_LDO2_STBY = 0x7B, | |
| PF0900_REG_LDO2_CFG2 = 0x7C, | |
| PF0900_REG_LDO3_RUN = 0x7D, | |
| PF0900_REG_LDO3_STBY = 0x7E, | |
| PF0900_REG_LDO3_CFG2 = 0x7F, | |
| PF0900_REG_VAON_CFG1 = 0x80, | |
| PF0900_REG_VAON_CFG2 = 0x81, | |
| PF0900_REG_SYS_DIAG = 0x82, | |
| PF0900_MAX_REGISTER, | |
| }; | |
| /* PF0900 SW MODE */ | |
| #define SW_RUN_MODE_OFF 0x00 | |
| #define SW_RUN_MODE_PWM 0x01 | |
| #define SW_RUN_MODE_PFM 0x02 | |
| #define SW_STBY_MODE_OFF 0x00 | |
| #define SW_STBY_MODE_PWM 0x04 | |
| #define SW_STBY_MODE_PFM 0x08 | |
| /* PF0900 SW MODE MASK */ | |
| #define SW_RUN_MODE_MASK GENMASK(1, 0) | |
| #define SW_STBY_MODE_MASK GENMASK(3, 2) | |
| /* PF0900 SW VRUN/VSTBY MASK */ | |
| #define PF0900_SW_VOL_MASK GENMASK(7, 0) | |
| /* PF0900_REG_VAON_CFG1 bits */ | |
| #define PF0900_VAON_1P8V 0x01 | |
| #define PF0900_VAON_MASK GENMASK(1, 0) | |
| /* PF0900_REG_SWX_CFG1 MASK */ | |
| #define PF0900_SW_DVS_MASK GENMASK(4, 3) | |
| /* PF0900_REG_LDO_RUN MASK */ | |
| #define VLDO_RUN_MASK GENMASK(4, 0) | |
| #define LDO_RUN_EN_MASK BIT(5) | |
| /* PF0900_REG_STATUS1_INT bits */ | |
| #define PF0900_IRQ_PWRUP BIT(3) | |
| /* PF0900_REG_ILIM_INT bits */ | |
| #define PF0900_IRQ_SW1_IL BIT(0) | |
| #define PF0900_IRQ_SW2_IL BIT(1) | |
| #define PF0900_IRQ_SW3_IL BIT(2) | |
| #define PF0900_IRQ_SW4_IL BIT(3) | |
| #define PF0900_IRQ_SW5_IL BIT(4) | |
| #define PF0900_IRQ_LDO1_IL BIT(0) | |
| #define PF0900_IRQ_LDO2_IL BIT(1) | |
| #define PF0900_IRQ_LDO3_IL BIT(2) | |
| /* PF0900_REG_UV_INT bits */ | |
| #define PF0900_IRQ_SW1_UV BIT(0) | |
| #define PF0900_IRQ_SW2_UV BIT(1) | |
| #define PF0900_IRQ_SW3_UV BIT(2) | |
| #define PF0900_IRQ_SW4_UV BIT(3) | |
| #define PF0900_IRQ_SW5_UV BIT(4) | |
| #define PF0900_IRQ_LDO1_UV BIT(0) | |
| #define PF0900_IRQ_LDO2_UV BIT(1) | |
| #define PF0900_IRQ_LDO3_UV BIT(2) | |
| #define PF0900_IRQ_VAON_UV BIT(3) | |
| /* PF0900_REG_OV_INT bits */ | |
| #define PF0900_IRQ_SW1_OV BIT(0) | |
| #define PF0900_IRQ_SW2_OV BIT(1) | |
| #define PF0900_IRQ_SW3_OV BIT(2) | |
| #define PF0900_IRQ_SW4_OV BIT(3) | |
| #define PF0900_IRQ_SW5_OV BIT(4) | |
| #define PF0900_IRQ_LDO1_OV BIT(0) | |
| #define PF0900_IRQ_LDO2_OV BIT(1) | |
| #define PF0900_IRQ_LDO3_OV BIT(2) | |
| #define PF0900_IRQ_VAON_OV BIT(3) | |
| struct pf0900_regulator_desc { | |
| struct regulator_desc desc; | |
| unsigned int suspend_enable_mask; | |
| unsigned int suspend_voltage_reg; | |
| unsigned int suspend_voltage_cache; | |
| }; | |
| struct pf0900_drvdata { | |
| const struct pf0900_regulator_desc *desc; | |
| unsigned int rcnt; | |
| }; | |
| struct pf0900 { | |
| struct device *dev; | |
| struct regmap *regmap; | |
| const struct pf0900_drvdata *drvdata; | |
| struct regulator_dev *rdevs[PF0900_REGULATOR_CNT]; | |
| int irq; | |
| unsigned short addr; | |
| bool crc_en; | |
| }; | |
| enum pf0900_regulator_type { | |
| PF0900_SW = 0, | |
| PF0900_LDO, | |
| }; | |
| #define PF0900_REGU_IRQ(_reg, _type, _event) \ | |
| { \ | |
| .reg = _reg, \ | |
| .type = _type, \ | |
| .event = _event, \ | |
| } | |
| struct pf0900_regulator_irq { | |
| unsigned int reg; | |
| unsigned int type; | |
| unsigned int event; | |
| }; | |
| static const struct regmap_range pf0900_range = { | |
| .range_min = PF0900_REG_DEV_ID, | |
| .range_max = PF0900_REG_SYS_DIAG, | |
| }; | |
| static const struct regmap_access_table pf0900_volatile_regs = { | |
| .yes_ranges = &pf0900_range, | |
| .n_yes_ranges = 1, | |
| }; | |
| static const struct regmap_config pf0900_regmap_config = { | |
| .reg_bits = 8, | |
| .val_bits = 8, | |
| .volatile_table = &pf0900_volatile_regs, | |
| .max_register = PF0900_MAX_REGISTER - 1, | |
| .cache_type = REGCACHE_MAPLE, | |
| }; | |
| static uint8_t crc8_j1850(unsigned short addr, unsigned int reg, | |
| unsigned int val) | |
| { | |
| uint8_t crcBuf[3]; | |
| uint8_t t_crc; | |
| uint8_t i, j; | |
| crcBuf[0] = addr; | |
| crcBuf[1] = reg; | |
| crcBuf[2] = val; | |
| t_crc = 0xFF; | |
| /* | |
| * The CRC calculation is based on the standard CRC-8-SAE as | |
| * defined in the SAE-J1850 specification with the following | |
| * characteristics. | |
| * Polynomial = 0x1D | |
| * Initial Value = 0xFF | |
| * The CRC byte is calculated by shifting 24-bit data through | |
| * the CRC polynomial.The 24-bits package is built as follows: | |
| * DEVICE_ADDR[b8] + REGISTER_ADDR [b8] +DATA[b8] | |
| * The DEVICE_ADDR is calculated as the 7-bit slave address | |
| * shifted left one space plus the corresponding read/write bit. | |
| * (7Bit Address [b7] << 1 ) + R/W = DEVICE_ADDR[b8] | |
| */ | |
| for (i = 0; i < sizeof(crcBuf); i++) { | |
| t_crc ^= crcBuf[i]; | |
| for (j = 0; j < 8; j++) { | |
| if ((t_crc & 0x80) != 0) { | |
| t_crc <<= 1; | |
| t_crc ^= 0x1D; | |
| } else { | |
| t_crc <<= 1; | |
| } | |
| } | |
| } | |
| return t_crc; | |
| } | |
| static int pf0900_regmap_read(void *context, unsigned int reg, | |
| unsigned int *val) | |
| { | |
| struct device *dev = context; | |
| struct i2c_client *i2c = to_i2c_client(dev); | |
| struct pf0900 *pf0900 = dev_get_drvdata(dev); | |
| int ret; | |
| u8 crc; | |
| if (!pf0900 || !pf0900->dev) | |
| return -EINVAL; | |
| if (reg >= PF0900_MAX_REGISTER) { | |
| dev_err(pf0900->dev, "Invalid register address: 0x%x\n", reg); | |
| return -EINVAL; | |
| } | |
| if (pf0900->crc_en) { | |
| ret = i2c_smbus_read_word_data(i2c, reg); | |
| if (ret < 0) { | |
| dev_err(pf0900->dev, "Read error at reg=0x%x: %d\n", reg, ret); | |
| return ret; | |
| } | |
| *val = (u16)ret; | |
| crc = crc8_j1850(pf0900->addr << 1 | 0x1, reg, FIELD_GET(GENMASK(7, 0), *val)); | |
| if (crc != FIELD_GET(GENMASK(15, 8), *val)) { | |
| dev_err(pf0900->dev, "Crc check error!\n"); | |
| return -EINVAL; | |
| } | |
| *val = FIELD_GET(GENMASK(7, 0), *val); | |
| } else { | |
| ret = i2c_smbus_read_byte_data(i2c, reg); | |
| if (ret < 0) { | |
| dev_err(pf0900->dev, "Read error at reg=0x%x: %d\n", reg, ret); | |
| return ret; | |
| } | |
| *val = ret; | |
| } | |
| return 0; | |
| } | |
| static int pf0900_regmap_write(void *context, unsigned int reg, | |
| unsigned int val) | |
| { | |
| struct device *dev = context; | |
| struct i2c_client *i2c = to_i2c_client(dev); | |
| struct pf0900 *pf0900 = dev_get_drvdata(dev); | |
| uint8_t data[2]; | |
| int ret; | |
| if (!pf0900 || !pf0900->dev) | |
| return -EINVAL; | |
| if (reg >= PF0900_MAX_REGISTER) { | |
| dev_err(pf0900->dev, "Invalid register address: 0x%x\n", reg); | |
| return -EINVAL; | |
| } | |
| data[0] = val; | |
| if (pf0900->crc_en) { | |
| /* Get CRC */ | |
| data[1] = crc8_j1850(pf0900->addr << 1, reg, data[0]); | |
| val = FIELD_PREP(GENMASK(15, 8), data[1]) | data[0]; | |
| ret = i2c_smbus_write_word_data(i2c, reg, val); | |
| } else { | |
| ret = i2c_smbus_write_byte_data(i2c, reg, data[0]); | |
| } | |
| if (ret) { | |
| dev_err(pf0900->dev, "Write reg=0x%x error!\n", reg); | |
| return ret; | |
| } | |
| return 0; | |
| } | |
| static int pf0900_suspend_enable(struct regulator_dev *rdev) | |
| { | |
| struct pf0900_regulator_desc *rdata = rdev_get_drvdata(rdev); | |
| struct regmap *rmap = rdev_get_regmap(rdev); | |
| return regmap_update_bits(rmap, rdata->desc.enable_reg, | |
| rdata->suspend_enable_mask, SW_STBY_MODE_PFM); | |
| } | |
| static int pf0900_suspend_disable(struct regulator_dev *rdev) | |
| { | |
| struct pf0900_regulator_desc *rdata = rdev_get_drvdata(rdev); | |
| struct regmap *rmap = rdev_get_regmap(rdev); | |
| return regmap_update_bits(rmap, rdata->desc.enable_reg, | |
| rdata->suspend_enable_mask, SW_STBY_MODE_OFF); | |
| } | |
| static int pf0900_set_suspend_voltage(struct regulator_dev *rdev, int uV) | |
| { | |
| struct pf0900_regulator_desc *rdata = rdev_get_drvdata(rdev); | |
| struct regmap *rmap = rdev_get_regmap(rdev); | |
| int ret; | |
| if (rdata->suspend_voltage_cache == uV) | |
| return 0; | |
| ret = regulator_map_voltage_iterate(rdev, uV, uV); | |
| if (ret < 0) { | |
| dev_err(rdev_get_dev(rdev), "failed to map %i uV\n", uV); | |
| return ret; | |
| } | |
| dev_dbg(rdev_get_dev(rdev), "uV: %i, reg: 0x%x, msk: 0x%x, val: 0x%x\n", | |
| uV, rdata->suspend_voltage_reg, rdata->desc.vsel_mask, ret); | |
| ret = regmap_update_bits(rmap, rdata->suspend_voltage_reg, | |
| rdata->desc.vsel_mask, ret); | |
| if (ret < 0) { | |
| dev_err(rdev_get_dev(rdev), "failed to set %i uV\n", uV); | |
| return ret; | |
| } | |
| rdata->suspend_voltage_cache = uV; | |
| return 0; | |
| } | |
| static const struct regmap_bus pf0900_regmap_bus = { | |
| .reg_read = pf0900_regmap_read, | |
| .reg_write = pf0900_regmap_write, | |
| }; | |
| static const struct regulator_ops pf0900_avon_regulator_ops = { | |
| .list_voltage = regulator_list_voltage_table, | |
| .set_voltage_sel = regulator_set_voltage_sel_regmap, | |
| .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
| }; | |
| static const struct regulator_ops pf0900_dvs_sw_regulator_ops = { | |
| .enable = regulator_enable_regmap, | |
| .disable = regulator_disable_regmap, | |
| .is_enabled = regulator_is_enabled_regmap, | |
| .list_voltage = regulator_list_voltage_linear_range, | |
| .set_voltage_sel = regulator_set_voltage_sel_regmap, | |
| .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
| .set_voltage_time_sel = regulator_set_voltage_time_sel, | |
| .set_ramp_delay = regulator_set_ramp_delay_regmap, | |
| .set_suspend_enable = pf0900_suspend_enable, | |
| .set_suspend_disable = pf0900_suspend_disable, | |
| .set_suspend_voltage = pf0900_set_suspend_voltage, | |
| }; | |
| static const struct regulator_ops pf0900_ldo_regulator_ops = { | |
| .enable = regulator_enable_regmap, | |
| .disable = regulator_disable_regmap, | |
| .is_enabled = regulator_is_enabled_regmap, | |
| .list_voltage = regulator_list_voltage_linear_range, | |
| .set_voltage_sel = regulator_set_voltage_sel_regmap, | |
| .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
| }; | |
| /* | |
| * SW1/2/3/4/5 | |
| * SW1_DVS[1:0] SW1 DVS ramp rate setting | |
| * 00: 15.6mV/8usec | |
| * 01: 15.6mV/4usec | |
| * 10: 15.6mV/2usec | |
| * 11: 15.6mV/1usec | |
| */ | |
| static const unsigned int pf0900_dvs_sw_ramp_table[] = { | |
| 1950, 3900, 7800, 15600 | |
| }; | |
| /* VAON 1.8V, 3.0V, or 3.3V */ | |
| static const int pf0900_vaon_voltages[] = { | |
| 0, 1800000, 3000000, 3300000, | |
| }; | |
| /* | |
| * SW1 0.5V to 3.3V | |
| * 0.5V to 1.35V (6.25mV step) | |
| * 1.8V to 2.5V (125mV step) | |
| * 2.8V to 3.3V (250mV step) | |
| */ | |
| static const struct linear_range pf0900_dvs_sw1_volts[] = { | |
| REGULATOR_LINEAR_RANGE(0, 0x00, 0x08, 0), | |
| REGULATOR_LINEAR_RANGE(500000, 0x09, 0x91, 6250), | |
| REGULATOR_LINEAR_RANGE(0, 0x92, 0x9E, 0), | |
| REGULATOR_LINEAR_RANGE(1500000, 0x9F, 0x9F, 0), | |
| REGULATOR_LINEAR_RANGE(1800000, 0xA0, 0xD8, 12500), | |
| REGULATOR_LINEAR_RANGE(0, 0xD9, 0xDF, 0), | |
| REGULATOR_LINEAR_RANGE(2800000, 0xE0, 0xF4, 25000), | |
| REGULATOR_LINEAR_RANGE(0, 0xF5, 0xFF, 0), | |
| }; | |
| /* | |
| * SW2/3/4/5 0.3V to 3.3V | |
| * 0.45V to 1.35V (6.25mV step) | |
| * 1.8V to 2.5V (125mV step) | |
| * 2.8V to 3.3V (250mV step) | |
| */ | |
| static const struct linear_range pf0900_dvs_sw2345_volts[] = { | |
| REGULATOR_LINEAR_RANGE(300000, 0x00, 0x00, 0), | |
| REGULATOR_LINEAR_RANGE(450000, 0x01, 0x91, 6250), | |
| REGULATOR_LINEAR_RANGE(0, 0x92, 0x9E, 0), | |
| REGULATOR_LINEAR_RANGE(1500000, 0x9F, 0x9F, 0), | |
| REGULATOR_LINEAR_RANGE(1800000, 0xA0, 0xD8, 12500), | |
| REGULATOR_LINEAR_RANGE(0, 0xD9, 0xDF, 0), | |
| REGULATOR_LINEAR_RANGE(2800000, 0xE0, 0xF4, 25000), | |
| REGULATOR_LINEAR_RANGE(0, 0xF5, 0xFF, 0), | |
| }; | |
| /* | |
| * LDO1 | |
| * 0.75V to 3.3V | |
| */ | |
| static const struct linear_range pf0900_ldo1_volts[] = { | |
| REGULATOR_LINEAR_RANGE(750000, 0x00, 0x0F, 50000), | |
| REGULATOR_LINEAR_RANGE(1800000, 0x10, 0x1F, 100000), | |
| }; | |
| /* | |
| * LDO2/3 | |
| * 0.65V to 3.3V (50mV step) | |
| */ | |
| static const struct linear_range pf0900_ldo23_volts[] = { | |
| REGULATOR_LINEAR_RANGE(650000, 0x00, 0x0D, 50000), | |
| REGULATOR_LINEAR_RANGE(1400000, 0x0E, 0x0F, 100000), | |
| REGULATOR_LINEAR_RANGE(1800000, 0x10, 0x1F, 100000), | |
| }; | |
| static const struct pf0900_regulator_desc pf0900_regulators[] = { | |
| { | |
| .desc = { | |
| .name = "sw1", | |
| .of_match = of_match_ptr("sw1"), | |
| .regulators_node = of_match_ptr("regulators"), | |
| .id = PF0900_SW1, | |
| .ops = &pf0900_dvs_sw_regulator_ops, | |
| .type = REGULATOR_VOLTAGE, | |
| .n_voltages = PF0900_SW_VOLTAGE_NUM, | |
| .linear_ranges = pf0900_dvs_sw1_volts, | |
| .n_linear_ranges = ARRAY_SIZE(pf0900_dvs_sw1_volts), | |
| .vsel_reg = PF0900_REG_SW1_VRUN, | |
| .vsel_mask = PF0900_SW_VOL_MASK, | |
| .enable_reg = PF0900_REG_SW1_MODE, | |
| .enable_mask = SW_RUN_MODE_MASK, | |
| .enable_val = SW_RUN_MODE_PWM, | |
| .ramp_reg = PF0900_REG_SW1_CFG1, | |
| .ramp_mask = PF0900_SW_DVS_MASK, | |
| .ramp_delay_table = pf0900_dvs_sw_ramp_table, | |
| .n_ramp_values = ARRAY_SIZE(pf0900_dvs_sw_ramp_table), | |
| .owner = THIS_MODULE, | |
| }, | |
| .suspend_enable_mask = SW_STBY_MODE_MASK, | |
| .suspend_voltage_reg = PF0900_REG_SW1_VSTBY, | |
| }, | |
| { | |
| .desc = { | |
| .name = "sw2", | |
| .of_match = of_match_ptr("sw2"), | |
| .regulators_node = of_match_ptr("regulators"), | |
| .id = PF0900_SW2, | |
| .ops = &pf0900_dvs_sw_regulator_ops, | |
| .type = REGULATOR_VOLTAGE, | |
| .n_voltages = PF0900_SW_VOLTAGE_NUM, | |
| .linear_ranges = pf0900_dvs_sw2345_volts, | |
| .n_linear_ranges = ARRAY_SIZE(pf0900_dvs_sw2345_volts), | |
| .vsel_reg = PF0900_REG_SW2_VRUN, | |
| .vsel_mask = PF0900_SW_VOL_MASK, | |
| .enable_reg = PF0900_REG_SW2_MODE, | |
| .enable_mask = SW_RUN_MODE_MASK, | |
| .enable_val = SW_RUN_MODE_PWM, | |
| .ramp_reg = PF0900_REG_SW2_CFG1, | |
| .ramp_mask = PF0900_SW_DVS_MASK, | |
| .ramp_delay_table = pf0900_dvs_sw_ramp_table, | |
| .n_ramp_values = ARRAY_SIZE(pf0900_dvs_sw_ramp_table), | |
| .owner = THIS_MODULE, | |
| }, | |
| .suspend_enable_mask = SW_STBY_MODE_MASK, | |
| .suspend_voltage_reg = PF0900_REG_SW2_VSTBY, | |
| }, | |
| { | |
| .desc = { | |
| .name = "sw3", | |
| .of_match = of_match_ptr("sw3"), | |
| .regulators_node = of_match_ptr("regulators"), | |
| .id = PF0900_SW3, | |
| .ops = &pf0900_dvs_sw_regulator_ops, | |
| .type = REGULATOR_VOLTAGE, | |
| .n_voltages = PF0900_SW_VOLTAGE_NUM, | |
| .linear_ranges = pf0900_dvs_sw2345_volts, | |
| .n_linear_ranges = ARRAY_SIZE(pf0900_dvs_sw2345_volts), | |
| .vsel_reg = PF0900_REG_SW3_VRUN, | |
| .vsel_mask = PF0900_SW_VOL_MASK, | |
| .enable_reg = PF0900_REG_SW3_MODE, | |
| .enable_mask = SW_RUN_MODE_MASK, | |
| .enable_val = SW_RUN_MODE_PWM, | |
| .ramp_reg = PF0900_REG_SW3_CFG1, | |
| .ramp_mask = PF0900_SW_DVS_MASK, | |
| .ramp_delay_table = pf0900_dvs_sw_ramp_table, | |
| .n_ramp_values = ARRAY_SIZE(pf0900_dvs_sw_ramp_table), | |
| .owner = THIS_MODULE, | |
| }, | |
| .suspend_enable_mask = SW_STBY_MODE_MASK, | |
| .suspend_voltage_reg = PF0900_REG_SW3_VSTBY, | |
| }, | |
| { | |
| .desc = { | |
| .name = "sw4", | |
| .of_match = of_match_ptr("sw4"), | |
| .regulators_node = of_match_ptr("regulators"), | |
| .id = PF0900_SW4, | |
| .ops = &pf0900_dvs_sw_regulator_ops, | |
| .type = REGULATOR_VOLTAGE, | |
| .n_voltages = PF0900_SW_VOLTAGE_NUM, | |
| .linear_ranges = pf0900_dvs_sw2345_volts, | |
| .n_linear_ranges = ARRAY_SIZE(pf0900_dvs_sw2345_volts), | |
| .vsel_reg = PF0900_REG_SW4_VRUN, | |
| .vsel_mask = PF0900_SW_VOL_MASK, | |
| .enable_reg = PF0900_REG_SW4_MODE, | |
| .enable_mask = SW_RUN_MODE_MASK, | |
| .enable_val = SW_RUN_MODE_PWM, | |
| .ramp_reg = PF0900_REG_SW4_CFG1, | |
| .ramp_mask = PF0900_SW_DVS_MASK, | |
| .ramp_delay_table = pf0900_dvs_sw_ramp_table, | |
| .n_ramp_values = ARRAY_SIZE(pf0900_dvs_sw_ramp_table), | |
| .owner = THIS_MODULE, | |
| }, | |
| .suspend_enable_mask = SW_STBY_MODE_MASK, | |
| .suspend_voltage_reg = PF0900_REG_SW4_VSTBY, | |
| }, | |
| { | |
| .desc = { | |
| .name = "sw5", | |
| .of_match = of_match_ptr("sw5"), | |
| .regulators_node = of_match_ptr("regulators"), | |
| .id = PF0900_SW5, | |
| .ops = &pf0900_dvs_sw_regulator_ops, | |
| .type = REGULATOR_VOLTAGE, | |
| .n_voltages = PF0900_SW_VOLTAGE_NUM, | |
| .linear_ranges = pf0900_dvs_sw2345_volts, | |
| .n_linear_ranges = ARRAY_SIZE(pf0900_dvs_sw2345_volts), | |
| .vsel_reg = PF0900_REG_SW5_VRUN, | |
| .vsel_mask = PF0900_SW_VOL_MASK, | |
| .enable_reg = PF0900_REG_SW5_MODE, | |
| .enable_mask = SW_RUN_MODE_MASK, | |
| .enable_val = SW_RUN_MODE_PWM, | |
| .ramp_reg = PF0900_REG_SW5_CFG1, | |
| .ramp_mask = PF0900_SW_DVS_MASK, | |
| .ramp_delay_table = pf0900_dvs_sw_ramp_table, | |
| .n_ramp_values = ARRAY_SIZE(pf0900_dvs_sw_ramp_table), | |
| .owner = THIS_MODULE, | |
| }, | |
| .suspend_enable_mask = SW_STBY_MODE_MASK, | |
| .suspend_voltage_reg = PF0900_REG_SW5_VSTBY, | |
| }, | |
| { | |
| .desc = { | |
| .name = "ldo1", | |
| .of_match = of_match_ptr("ldo1"), | |
| .regulators_node = of_match_ptr("regulators"), | |
| .id = PF0900_LDO1, | |
| .ops = &pf0900_ldo_regulator_ops, | |
| .type = REGULATOR_VOLTAGE, | |
| .n_voltages = PF0900_LDO_VOLTAGE_NUM, | |
| .linear_ranges = pf0900_ldo1_volts, | |
| .n_linear_ranges = ARRAY_SIZE(pf0900_ldo1_volts), | |
| .vsel_reg = PF0900_REG_LDO1_RUN, | |
| .vsel_mask = VLDO_RUN_MASK, | |
| .enable_reg = PF0900_REG_LDO1_RUN, | |
| .enable_mask = LDO_RUN_EN_MASK, | |
| .owner = THIS_MODULE, | |
| }, | |
| }, | |
| { | |
| .desc = { | |
| .name = "ldo2", | |
| .of_match = of_match_ptr("ldo2"), | |
| .regulators_node = of_match_ptr("regulators"), | |
| .id = PF0900_LDO2, | |
| .ops = &pf0900_ldo_regulator_ops, | |
| .type = REGULATOR_VOLTAGE, | |
| .n_voltages = PF0900_LDO_VOLTAGE_NUM, | |
| .linear_ranges = pf0900_ldo23_volts, | |
| .n_linear_ranges = ARRAY_SIZE(pf0900_ldo23_volts), | |
| .vsel_reg = PF0900_REG_LDO2_RUN, | |
| .vsel_mask = VLDO_RUN_MASK, | |
| .enable_reg = PF0900_REG_LDO2_RUN, | |
| .enable_mask = LDO_RUN_EN_MASK, | |
| .owner = THIS_MODULE, | |
| }, | |
| }, | |
| { | |
| .desc = { | |
| .name = "ldo3", | |
| .of_match = of_match_ptr("ldo3"), | |
| .regulators_node = of_match_ptr("regulators"), | |
| .id = PF0900_LDO3, | |
| .ops = &pf0900_ldo_regulator_ops, | |
| .type = REGULATOR_VOLTAGE, | |
| .n_voltages = PF0900_LDO_VOLTAGE_NUM, | |
| .linear_ranges = pf0900_ldo23_volts, | |
| .n_linear_ranges = ARRAY_SIZE(pf0900_ldo23_volts), | |
| .vsel_reg = PF0900_REG_LDO3_RUN, | |
| .vsel_mask = VLDO_RUN_MASK, | |
| .enable_reg = PF0900_REG_LDO3_RUN, | |
| .enable_mask = LDO_RUN_EN_MASK, | |
| .owner = THIS_MODULE, | |
| }, | |
| }, | |
| { | |
| .desc = { | |
| .name = "vaon", | |
| .of_match = of_match_ptr("vaon"), | |
| .regulators_node = of_match_ptr("regulators"), | |
| .id = PF0900_VAON, | |
| .ops = &pf0900_avon_regulator_ops, | |
| .type = REGULATOR_VOLTAGE, | |
| .n_voltages = PF0900_VAON_VOLTAGE_NUM, | |
| .volt_table = pf0900_vaon_voltages, | |
| .enable_reg = PF0900_REG_VAON_CFG1, | |
| .enable_mask = PF0900_VAON_MASK, | |
| .enable_val = PF0900_VAON_1P8V, | |
| .vsel_reg = PF0900_REG_VAON_CFG1, | |
| .vsel_mask = PF0900_VAON_MASK, | |
| .owner = THIS_MODULE, | |
| }, | |
| }, | |
| }; | |
| static const struct pf0900_regulator_irq regu_irqs[] = { | |
| PF0900_REGU_IRQ(PF0900_REG_SW_ILIM_INT, PF0900_SW, REGULATOR_ERROR_OVER_CURRENT_WARN), | |
| PF0900_REGU_IRQ(PF0900_REG_LDO_ILIM_INT, PF0900_LDO, REGULATOR_ERROR_OVER_CURRENT_WARN), | |
| PF0900_REGU_IRQ(PF0900_REG_SW_UV_INT, PF0900_SW, REGULATOR_ERROR_UNDER_VOLTAGE_WARN), | |
| PF0900_REGU_IRQ(PF0900_REG_LDO_UV_INT, PF0900_LDO, REGULATOR_ERROR_UNDER_VOLTAGE_WARN), | |
| PF0900_REGU_IRQ(PF0900_REG_SW_OV_INT, PF0900_SW, REGULATOR_ERROR_OVER_VOLTAGE_WARN), | |
| PF0900_REGU_IRQ(PF0900_REG_LDO_OV_INT, PF0900_LDO, REGULATOR_ERROR_OVER_VOLTAGE_WARN), | |
| }; | |
| static irqreturn_t pf0900_irq_handler(int irq, void *data) | |
| { | |
| unsigned int val, regu, i, index; | |
| struct pf0900 *pf0900 = data; | |
| int ret; | |
| for (i = 0; i < ARRAY_SIZE(regu_irqs); i++) { | |
| ret = regmap_read(pf0900->regmap, regu_irqs[i].reg, &val); | |
| if (ret < 0) { | |
| dev_err(pf0900->dev, "Failed to read %d\n", ret); | |
| return IRQ_NONE; | |
| } | |
| if (val) { | |
| ret = regmap_write_bits(pf0900->regmap, regu_irqs[i].reg, val, val); | |
| if (ret < 0) { | |
| dev_err(pf0900->dev, "Failed to update %d\n", ret); | |
| return IRQ_NONE; | |
| } | |
| if (regu_irqs[i].type == PF0900_SW) { | |
| for (index = 0; index < REGU_SW_CNT; index++) { | |
| if (val & BIT(index)) { | |
| regu = (enum pf0900_regulators)index; | |
| regulator_notifier_call_chain(pf0900->rdevs[regu], | |
| regu_irqs[i].event, | |
| NULL); | |
| } | |
| } | |
| } else if (regu_irqs[i].type == PF0900_LDO) { | |
| for (index = 0; index < REGU_LDO_VAON_CNT; index++) { | |
| if (val & BIT(index)) { | |
| regu = (enum pf0900_regulators)index + PF0900_LDO1; | |
| regulator_notifier_call_chain(pf0900->rdevs[regu], | |
| regu_irqs[i].event, | |
| NULL); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return IRQ_HANDLED; | |
| } | |
| static int pf0900_i2c_probe(struct i2c_client *i2c) | |
| { | |
| const struct pf0900_regulator_desc *regulator_desc; | |
| const struct pf0900_drvdata *drvdata = NULL; | |
| struct device_node *np = i2c->dev.of_node; | |
| unsigned int device_id, device_fam, i; | |
| struct regulator_config config = { }; | |
| struct pf0900 *pf0900; | |
| int ret; | |
| if (!i2c->irq) | |
| return dev_err_probe(&i2c->dev, -EINVAL, "No IRQ configured?\n"); | |
| pf0900 = devm_kzalloc(&i2c->dev, sizeof(struct pf0900), GFP_KERNEL); | |
| if (!pf0900) | |
| return -ENOMEM; | |
| drvdata = device_get_match_data(&i2c->dev); | |
| if (!drvdata) | |
| return dev_err_probe(&i2c->dev, -EINVAL, "unable to find driver data\n"); | |
| regulator_desc = drvdata->desc; | |
| pf0900->drvdata = drvdata; | |
| pf0900->crc_en = of_property_read_bool(np, "nxp,i2c-crc-enable"); | |
| pf0900->irq = i2c->irq; | |
| pf0900->dev = &i2c->dev; | |
| pf0900->addr = i2c->addr; | |
| dev_set_drvdata(&i2c->dev, pf0900); | |
| pf0900->regmap = devm_regmap_init(&i2c->dev, &pf0900_regmap_bus, &i2c->dev, | |
| &pf0900_regmap_config); | |
| if (IS_ERR(pf0900->regmap)) | |
| return dev_err_probe(&i2c->dev, PTR_ERR(pf0900->regmap), | |
| "regmap initialization failed\n"); | |
| ret = regmap_read(pf0900->regmap, PF0900_REG_DEV_ID, &device_id); | |
| if (ret) | |
| return dev_err_probe(&i2c->dev, ret, "Read device id error\n"); | |
| ret = regmap_read(pf0900->regmap, PF0900_REG_DEV_FAM, &device_fam); | |
| if (ret) | |
| return dev_err_probe(&i2c->dev, ret, "Read device fam error\n"); | |
| /* Check your board and dts for match the right pmic */ | |
| if (device_fam == 0x09 && (device_id & 0x1F) != 0x0) | |
| return dev_err_probe(&i2c->dev, -EINVAL, "Device id(%x) mismatched\n", | |
| device_id >> 4); | |
| for (i = 0; i < drvdata->rcnt; i++) { | |
| const struct regulator_desc *desc; | |
| const struct pf0900_regulator_desc *r; | |
| r = ®ulator_desc[i]; | |
| desc = &r->desc; | |
| config.regmap = pf0900->regmap; | |
| config.driver_data = (void *)r; | |
| config.dev = pf0900->dev; | |
| pf0900->rdevs[i] = devm_regulator_register(pf0900->dev, desc, &config); | |
| if (IS_ERR(pf0900->rdevs[i])) | |
| return dev_err_probe(pf0900->dev, PTR_ERR(pf0900->rdevs[i]), | |
| "Failed to register regulator(%s)\n", desc->name); | |
| } | |
| ret = devm_request_threaded_irq(pf0900->dev, pf0900->irq, NULL, | |
| pf0900_irq_handler, | |
| (IRQF_TRIGGER_FALLING | IRQF_ONESHOT), | |
| "pf0900-irq", pf0900); | |
| if (ret != 0) | |
| return dev_err_probe(pf0900->dev, ret, "Failed to request IRQ: %d\n", | |
| pf0900->irq); | |
| /* | |
| * The PWRUP_M is unmasked by default. When the device enter in RUN state, | |
| * it will assert the PWRUP_I interrupt and assert the INTB pin to inform | |
| * the MCU that it has finished the power up sequence properly. | |
| */ | |
| ret = regmap_write_bits(pf0900->regmap, PF0900_REG_STATUS1_INT, PF0900_IRQ_PWRUP, | |
| PF0900_IRQ_PWRUP); | |
| if (ret) | |
| return dev_err_probe(&i2c->dev, ret, "Clean PWRUP_I error\n"); | |
| /* mask interrupt PWRUP */ | |
| ret = regmap_update_bits(pf0900->regmap, PF0900_REG_STATUS1_MSK, PF0900_IRQ_PWRUP, | |
| PF0900_IRQ_PWRUP); | |
| if (ret) | |
| return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n"); | |
| ret = regmap_update_bits(pf0900->regmap, PF0900_REG_SW_ILIM_MSK, PF0900_IRQ_SW1_IL | | |
| PF0900_IRQ_SW2_IL | PF0900_IRQ_SW3_IL | PF0900_IRQ_SW4_IL | | |
| PF0900_IRQ_SW5_IL, 0); | |
| if (ret) | |
| return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n"); | |
| ret = regmap_update_bits(pf0900->regmap, PF0900_REG_SW_UV_MSK, PF0900_IRQ_SW1_UV | | |
| PF0900_IRQ_SW2_UV | PF0900_IRQ_SW3_UV | PF0900_IRQ_SW4_UV | | |
| PF0900_IRQ_SW5_UV, 0); | |
| if (ret) | |
| return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n"); | |
| ret = regmap_update_bits(pf0900->regmap, PF0900_REG_SW_OV_MSK, PF0900_IRQ_SW1_OV | | |
| PF0900_IRQ_SW2_OV | PF0900_IRQ_SW3_OV | PF0900_IRQ_SW4_OV | | |
| PF0900_IRQ_SW5_OV, 0); | |
| if (ret) | |
| return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n"); | |
| ret = regmap_update_bits(pf0900->regmap, PF0900_REG_LDO_ILIM_MSK, PF0900_IRQ_LDO1_IL | | |
| PF0900_IRQ_LDO2_IL | PF0900_IRQ_LDO3_IL, 0); | |
| if (ret) | |
| return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n"); | |
| ret = regmap_update_bits(pf0900->regmap, PF0900_REG_LDO_UV_MSK, PF0900_IRQ_LDO1_UV | | |
| PF0900_IRQ_LDO2_UV | PF0900_IRQ_LDO3_UV | PF0900_IRQ_VAON_UV, 0); | |
| if (ret) | |
| return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n"); | |
| ret = regmap_update_bits(pf0900->regmap, PF0900_REG_LDO_OV_MSK, PF0900_IRQ_LDO1_OV | | |
| PF0900_IRQ_LDO2_OV | PF0900_IRQ_LDO3_OV | PF0900_IRQ_VAON_OV, 0); | |
| if (ret) | |
| return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n"); | |
| return 0; | |
| } | |
| static struct pf0900_drvdata pf0900_drvdata = { | |
| .desc = pf0900_regulators, | |
| .rcnt = ARRAY_SIZE(pf0900_regulators), | |
| }; | |
| static const struct of_device_id pf0900_of_match[] = { | |
| { .compatible = "nxp,pf0900", .data = &pf0900_drvdata}, | |
| { } | |
| }; | |
| MODULE_DEVICE_TABLE(of, pf0900_of_match); | |
| static struct i2c_driver pf0900_i2c_driver = { | |
| .driver = { | |
| .name = "nxp-pf0900", | |
| .of_match_table = pf0900_of_match, | |
| }, | |
| .probe = pf0900_i2c_probe, | |
| }; | |
| module_i2c_driver(pf0900_i2c_driver); | |
| MODULE_AUTHOR("Joy Zou <joy.zou@nxp.com>"); | |
| MODULE_DESCRIPTION("NXP PF0900 Power Management IC driver"); | |
| MODULE_LICENSE("GPL"); |