| // 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, |
| }, |
| }, |
| }; |
| |
| 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"); |