[PATCH 1/4] power: pmic: Support pmic pf5300

From: Joy Zou joy.zou@nxp.com
Support NXP pmic pf5300
Signed-off-by: Joy Zou joy.zou@nxp.com Reviewed-by: Ye Li ye.li@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com --- drivers/power/pmic/Kconfig | 15 ++++ drivers/power/pmic/Makefile | 1 + drivers/power/pmic/pf5300.c | 168 ++++++++++++++++++++++++++++++++++++ include/power/pf5300.h | 100 +++++++++++++++++++++ 4 files changed, 284 insertions(+) create mode 100644 drivers/power/pmic/pf5300.c create mode 100644 include/power/pf5300.h
diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index 1a5ef279ed6..420c4099937 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -186,6 +186,21 @@ config SPL_DM_PMIC_PF0900 This config enables implementation of driver-model pmic uclass features for PMIC PF0900 in SPL. The driver implements read/write operations.
+config DM_PMIC_PF5300 + bool "Enable Driver Model for PMIC PF5300" + depends on DM_I2C + help + This config enables implementation of driver-model pmic uclass features + for PMIC PF5300. The driver implements read/write operations. + +config SPL_DM_PMIC_PF5300 + bool "Enable Driver Model for PMIC PF5300 in SPL" + depends on SPL_DM_PMIC + depends on SPL_DM_I2C + help + This config enables implementation of driver-model pmic uclass features + for PMIC PF5300 in SPL. The driver implements read/write operations. + config DM_PMIC_PFUZE100 bool "Enable Driver Model for PMIC PFUZE100" ---help--- diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 0041ab59406..74ace69daf5 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_$(SPL_)DM_PMIC_MP5416) += mp5416.o obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o obj-$(CONFIG_$(SPL_)DM_PMIC_PCA9450) += pca9450.o obj-$(CONFIG_$(SPL_)DM_PMIC_PF0900) += pf0900.o +obj-$(CONFIG_$(SPL_)DM_PMIC_PF5300) += pf5300.o obj-$(CONFIG_PMIC_S2MPS11) += s2mps11.o obj-$(CONFIG_DM_PMIC_SANDBOX) += sandbox.o i2c_pmic_emul.o obj-$(CONFIG_PMIC_AB8500) += ab8500.o diff --git a/drivers/power/pmic/pf5300.c b/drivers/power/pmic/pf5300.c new file mode 100644 index 00000000000..b66fa756109 --- /dev/null +++ b/drivers/power/pmic/pf5300.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2023 NXP + */ + +#include <asm/global_data.h> +#include <asm-generic/gpio.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <i2c.h> +#include <errno.h> +#include <fdtdec.h> +#include <linux/err.h> +#include <log.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/pf5300.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const struct pmic_child_info pmic_children_info[] = { + /* sw */ + { .prefix = "S", .driver = PF5300_REGULATOR_DRIVER}, + { }, +}; + +static int pf5300_reg_count(struct udevice *dev) +{ + return PF5300_MAX_REGISTER; +} + +static u8 crc8_j1850(u8 *data, u8 length) +{ + u8 t_crc; + u8 i, j; + + t_crc = 0xFF; + for (i = 0; i < length; i++) { + t_crc ^= data[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 pf5300_read(struct udevice *dev, uint reg, u8 *buff, + int len) +{ + u8 crcBuf[3]; + u8 data[2], crc; + int ret; + struct pf5300_priv *priv = dev_get_priv(dev); + + if (reg < PF5300_MAX_REGISTER) { + ret = dm_i2c_read(dev, reg, data, + priv->crc_en ? 2U : 1U); + if (ret) + return ret; + buff[0] = data[0]; + if (priv->crc_en) { + /* Get CRC */ + crcBuf[0] = priv->addr << 1U | 0x1U; + crcBuf[1] = reg; + crcBuf[2] = data[0]; + crc = crc8_j1850(crcBuf, 3U); + if (crc != data[1]) + return -EINVAL; + } + } else { + return -EINVAL; + } + return ret; +} + +static int pf5300_write(struct udevice *dev, uint reg, const u8 *buff, + int len) +{ + u8 crcBuf[3]; + u8 data[2]; + int ret; + struct pf5300_priv *priv = dev_get_priv(dev); + + if (reg < PF5300_MAX_REGISTER) { + data[0] = buff[0]; + if (priv->crc_en) { + /* Get CRC */ + crcBuf[0] = priv->addr << 1U; + crcBuf[1] = reg; + crcBuf[2] = data[0]; + data[1] = crc8_j1850(crcBuf, 3U); + } + /* Write data */ + ret = dm_i2c_write(dev, reg, data, + priv->crc_en ? 2U : 1U); + if (ret) + return ret; + } + return ret; +} + +static int pf5300_bind(struct udevice *dev) +{ + int children; + ofnode regulators_node; + + regulators_node = dev_read_subnode(dev, "regulators"); + if (!ofnode_valid(regulators_node)) { + debug("%s: %s regulators subnode not found!", __func__, + dev->name); + return -ENXIO; + } + + debug("%s: '%s' - found regulators subnode\n", __func__, dev->name); + + children = pmic_bind_children(dev, regulators_node, + pmic_children_info); + if (!children) + debug("%s: %s - no child found\n", __func__, dev->name); + + /* Always return success for this device */ + return 0; +} + +static int pf5300_probe(struct udevice *dev) +{ + struct pf5300_priv *priv = dev_get_priv(dev); + unsigned int reg; + int ret = 0; + + ret = ofnode_read_u32(dev_ofnode(dev), "reg", ®); + if (ret) + return ret; + priv->addr = reg; + + if (ofnode_read_bool(dev_ofnode(dev), "i2c-crc-enable")) + priv->crc_en = true; + + return ret; +} + +static struct dm_pmic_ops pf5300_ops = { + .reg_count = pf5300_reg_count, + .read = pf5300_read, + .write = pf5300_write, +}; + +static const struct udevice_id pf5300_ids[] = { + { .compatible = "nxp,pf5300", .data = PF5300_TYPE_PF5300, }, + { .compatible = "nxp,pf5301", .data = PF5300_TYPE_PF5301, }, + { .compatible = "nxp,pf5302", .data = PF5300_TYPE_PF5302, }, + { } +}; + +U_BOOT_DRIVER(pmic_pf5300) = { + .name = "pf5300 pmic", + .id = UCLASS_PMIC, + .of_match = pf5300_ids, + .bind = pf5300_bind, + .probe = pf5300_probe, + .ops = &pf5300_ops, + .priv_auto = sizeof(struct pf5300_priv), +}; diff --git a/include/power/pf5300.h b/include/power/pf5300.h new file mode 100644 index 00000000000..0d2fa548a70 --- /dev/null +++ b/include/power/pf5300.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2023 NXP + */ + +#ifndef PF5300_H_ +#define PF5300_H_ + +#define PF5300_REGULATOR_DRIVER "pf5300_regulator" + +struct pf5300_priv { + u8 addr; + bool crc_en; +}; + +enum pf5300_chip_type { + PF5300_TYPE_PF5300 = 0, + PF5300_TYPE_PF5301 = 1, + PF5300_TYPE_PF5302 = 2, + PF5300_TYPE_AMOUNT, +}; + +enum { + PF5300_SW1 = 0, + PF5300_REGULATOR_CNT, +}; + +enum { + PF5300_DVS_LEVEL_RUN = 0, + PF5300_DVS_LEVEL_STANDBY, + PF5300_DVS_LEVEL_MAX, +}; + +#define PF5300_SW1_VOLTAGE_NUM 0x100 + +enum { + PF5300_REG_DEV_ID = 0x00, + PF5300_REG_REV_ID = 0x01, + PF5300_REG_EMREV = 0x02, + PF5300_REG_PROG_ID = 0x03, + PF5300_REG_CONFIG1 = 0x04, + PF5300_REG_INT_STATUS1 = 0x05, + PF5300_REG_INT_SENSE1 = 0x06, + PF5300_REG_INT_STATUS2 = 0x07, + PF5300_REG_INT_SENSE2 = 0x08, + PF5300_REG_BIST_STAT1 = 0x09, + PF5300_REG_BIST_CTRL = 0x0A, + PF5300_REG_STATE = 0x0B, + PF5300_REG_STATE_CTRL = 0x0C, + PF5300_REG_SW1_VOLT = 0x0D, + PF5300_REG_SW1_STBY_VOLT = 0x0E, + PF5300_REG_SW1_CTRL1 = 0x0F, + PF5300_REG_SW1_CTRL2 = 0x10, + PF5300_REG_CLK_CTRL = 0x11, + PF5300_REG_SEQ_CTRL1 = 0x12, + PF5300_REG_SEQ_CTRL2 = 0x13, + PF5300_REG_RANDOM_CHK = 0x14, + PF5300_REG_RANDOM_GEN = 0x15, + PF5300_REG_WD_CTRL = 0x16, + PF5300_REG_WD_SEED = 0x17, + PF5300_REG_WD_ANSWER = 0x18, + PF5300_REG_FLT_CNT1 = 0x19, + PF5300_REG_FLT_CNT2 = 0x1A, + PF5300_MAX_REGISTER, +}; + +/* PF5300 SW1_CTRL1 */ +#define SW_MODE_OFF 0x00 +#define SW_MODE_PWM 0x0c + +#define SW1_MODE_MASK 0x0C +#define SW1_STBY_MODE_MASK 0x30 + +#define SW1_RAMP_MASK 0x03 + +/* PF5300 SW1_VOLT/SW1_STBY_VOLT MASK */ +#define SW1_VOLT_MASK 0xFF +#define SW1_STBY_VOLT_MASK 0xFF + +/* PF5300_REG_INT_STATUS1 bits */ +#define IRQ_SDWN 0x80 +#define IRQ_BG_ERR 0x40 +#define IRQ_CRC 0x20 +#define IRQ_SW1_DVS_DONE 0x10 +#define IRQ_SW1_ILIM 0x08 +#define IRQ_VMON_UV 0x04 +#define IRQ_VMON_OV 0x02 +#define IRQ_VIN_OVLO 0x01 + +/* PF5300_REG_INT_STATUS2 bits */ +#define IRQ_PGOOD_STUCK_AT0 0x80 +#define IRQ_PGOOD_STUCK_AT1 0x40 +#define IRQ_DVS_ERR 0x20 +#define IRQ_FSYNC 0x10 +#define IRQ_THERM_155 0x08 +#define IRQ_THERM_140 0x04 +#define IRQ_THERM_125 0x02 +#define IRQ_THERM_110 0x01 + +#endif

From: Joy Zou joy.zou@nxp.com
Add regulator support for NXP pmic pf5300.
Signed-off-by: Joy Zou joy.zou@nxp.com Reviewed-by: Ye Li ye.li@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com --- drivers/power/regulator/Kconfig | 15 ++ drivers/power/regulator/Makefile | 1 + drivers/power/regulator/pf5300.c | 247 +++++++++++++++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 drivers/power/regulator/pf5300.c
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index b2f487b050b..69dfb07df50 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -111,6 +111,21 @@ config SPL_DM_REGULATOR_PF0900 This config enables implementation of driver-model regulator uclass features for regulators on ROHM PF0900 in SPL.
+config DM_REGULATOR_PF5300 + bool "Enable Driver Model for NXP PF5300 regulators" + depends on DM_REGULATOR && DM_PMIC_PF5300 + help + This config enables implementation of driver-model regulator uclass + features for regulators on NXP PF5300 PMICs. PF5300 contains 1 sw. + The driver implements get/set api for value and enable. + +config SPL_DM_REGULATOR_PF5300 + bool "Enable Driver Model for NXP PF5300 regulators in SPL" + depends on DM_REGULATOR_PF0900 && SPL + help + This config enables implementation of driver-model regulator uclass + features for regulators on ROHM PF5300 in SPL. + config DM_REGULATOR_DA9063 bool "Enable Driver Model for REGULATOR DA9063" depends on DM_REGULATOR && DM_PMIC_DA9063 diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index a49700dfd25..7a580d45b72 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_BD71837) += bd71837.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_PCA9450) += pca9450.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_PF0900) += pf0900.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_PF5300) += pf5300.o obj-$(CONFIG_$(SPL_)REGULATOR_PWM) += pwm_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_FAN53555) += fan53555.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_COMMON) += regulator_common.o diff --git a/drivers/power/regulator/pf5300.c b/drivers/power/regulator/pf5300.c new file mode 100644 index 00000000000..6df24934e09 --- /dev/null +++ b/drivers/power/regulator/pf5300.c @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NXP PF5300 regulator driver + * Copyright 2023 NXP + */ + +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <linux/bitops.h> +#include <power/pf5300.h> +#include <power/pmic.h> +#include <power/regulator.h> + +/** + * struct pf5300_vrange - describe linear range of voltages + * + * @min_volt: smallest voltage in range + * @step: how much voltage changes at each selector step + * @min_sel: smallest selector in the range + * @max_sel: maximum selector in the range + */ +struct pf5300_vrange { + unsigned int min_volt; + u8 min_sel; + u8 max_sel; + unsigned int step; +}; + +/** + * struct pf5300_plat - describe regulator control registers + * + * @name: name of the regulator. Used for matching the dt-entry + * @enable_reg: register address used to enable/disable regulator + * @enablemask: register mask used to enable/disable regulator + * @volt_reg: register address used to configure regulator voltage + * @volt_mask: register mask used to configure regulator voltage + * @ranges: pointer to ranges of regulator voltages and matching register + * values + * @numranges: number of voltage ranges pointed by ranges + */ +struct pf5300_plat { + const char *name; + u8 enable_reg; + u8 enablemask; + u8 volt_reg; + u8 volt_mask; + struct pf5300_vrange *ranges; + unsigned int numranges; +}; + +#define PCA_RANGE(_min, _sel_low, _sel_hi, _vstep) \ +{ \ + .min_volt = (_min), .min_sel = (_sel_low), \ + .max_sel = (_sel_hi), .step = (_vstep),\ +} + +#define PCA_DATA(_name, enreg, enmask, vreg, vmask, _range) \ +{ \ + .name = (_name), .enable_reg = (enreg), .enablemask = (enmask), \ + .volt_reg = (vreg), .volt_mask = (vmask), .ranges = (_range), \ + .numranges = ARRAY_SIZE(_range) \ +} + +static struct pf5300_vrange pf5300_sw1_vranges[] = { + PCA_RANGE(500000, 0x00, 0x8C, 5000), + PCA_RANGE(0, 0x8D, 0xFF, 0), +}; + +static struct pf5300_plat pf5301_reg_data[] = { + PCA_DATA("SW1_2a", PF5300_REG_SW1_CTRL1, SW_MODE_PWM, + PF5300_REG_SW1_VOLT, SW1_VOLT_MASK, + pf5300_sw1_vranges), +}; + +static struct pf5300_plat pf5302_reg_data[] = { + PCA_DATA("SW1_29", PF5300_REG_SW1_CTRL1, SW_MODE_PWM, + PF5300_REG_SW1_VOLT, SW1_VOLT_MASK, + pf5300_sw1_vranges), +}; + +static int vrange_find_value(struct pf5300_vrange *r, unsigned int sel, + unsigned int *val) +{ + if (!val || sel < r->min_sel || sel > r->max_sel) + return -EINVAL; + + *val = r->min_volt + r->step * (sel - r->min_sel); + return 0; +} + +static int vrange_find_selector(struct pf5300_vrange *r, int val, + unsigned int *sel) +{ + int ret = -EINVAL; + int num_vals = r->max_sel - r->min_sel + 1; + + if (val >= r->min_volt && + val <= r->min_volt + r->step * (num_vals - 1)) { + if (r->step) { + *sel = r->min_sel + ((val - r->min_volt) / r->step); + ret = 0; + } else { + *sel = r->min_sel; + ret = 0; + } + } + return ret; +} + +static int pf5300_get_enable(struct udevice *dev) +{ + struct pf5300_plat *plat = dev_get_plat(dev); + int val; + + val = pmic_reg_read(dev->parent, plat->enable_reg); + if (val < 0) + return val; + + return (val & plat->enablemask); +} + +static int pf5300_set_enable(struct udevice *dev, bool enable) +{ + int val = 0; + int tmp = 0; + struct pf5300_plat *plat = dev_get_plat(dev); + + if (enable) + tmp = plat->enablemask; + val = pmic_reg_read(dev->parent, plat->enable_reg); + if (tmp < 0) + return tmp; + val = (tmp & plat->enablemask) | (val & (~plat->enablemask)); + + return pmic_reg_write(dev->parent, plat->enable_reg, val); +} + +static int pf5300_get_value(struct udevice *dev) +{ + struct pf5300_plat *plat = dev_get_plat(dev); + unsigned int reg, tmp; + int i, ret; + + ret = pmic_reg_read(dev->parent, plat->volt_reg); + if (ret < 0) + return ret; + + reg = ret; + reg &= plat->volt_mask; + + for (i = 0; i < plat->numranges; i++) { + struct pf5300_vrange *r = &plat->ranges[i]; + + if (!vrange_find_value(r, reg, &tmp)) + return tmp; + } + + pr_err("Unknown voltage value read from pmic\n"); + + return -EINVAL; +} + +static int pf5300_set_value(struct udevice *dev, int uvolt) +{ + struct pf5300_plat *plat = dev_get_plat(dev); + unsigned int sel; + int i, val, found = 0; + + for (i = 0; i < plat->numranges; i++) { + struct pf5300_vrange *r = &plat->ranges[i]; + + found = !vrange_find_selector(r, uvolt, &sel); + if (found) { + unsigned int tmp; + + /* + * We require exactly the requested value to be + * supported - this can be changed later if needed + */ + found = !vrange_find_value(r, sel, &tmp); + if (found && tmp == uvolt) + break; + found = 0; + } + } + + if (!found) + return -EINVAL; + + val = pmic_reg_read(dev->parent, plat->volt_reg); + if (val < 0) + return val; + val = (sel & plat->volt_mask) | (val & (~plat->volt_mask)); + return pmic_reg_write(dev->parent, plat->volt_reg, val); +} + +static int pf5300_regulator_probe(struct udevice *dev) +{ + struct pf5300_plat *plat = dev_get_plat(dev); + int i, type; + + type = dev_get_driver_data(dev_get_parent(dev)); + + if (type != PF5300_TYPE_PF5300 && type != PF5300_TYPE_PF5301 && + type != PF5300_TYPE_PF5302) { + debug("Unknown PMIC type\n"); + return -EINVAL; + } + + if (type == PF5300_TYPE_PF5301) { + for (i = 0; i < ARRAY_SIZE(pf5301_reg_data); i++) { + if (strcmp(dev->name, pf5301_reg_data[i].name)) + continue; + *plat = pf5301_reg_data[i]; + + return 0; + } + } else if (type == PF5300_TYPE_PF5302) { + for (i = 0; i < ARRAY_SIZE(pf5302_reg_data); i++) { + if (strcmp(dev->name, pf5302_reg_data[i].name)) + continue; + *plat = pf5301_reg_data[i]; + + return 0; + } + } + + pr_err("Unknown regulator '%s'\n", dev->name); + + return -ENOENT; +} + +static const struct dm_regulator_ops pf5300_regulator_ops = { + .get_value = pf5300_get_value, + .set_value = pf5300_set_value, + .get_enable = pf5300_get_enable, + .set_enable = pf5300_set_enable, +}; + +U_BOOT_DRIVER(pf5300_regulator) = { + .name = PF5300_REGULATOR_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &pf5300_regulator_ops, + .probe = pf5300_regulator_probe, + .plat_auto = sizeof(struct pf5300_plat), +};

From: Joy Zou joy.zou@nxp.com
Support NXP PMIC pf9453.
Reviewed-by: Ye Li ye.li@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com Signed-off-by: Joy Zou joy.zou@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com --- drivers/power/pmic/Kconfig | 15 ++++ drivers/power/pmic/Makefile | 1 + drivers/power/pmic/pf9453.c | 173 ++++++++++++++++++++++++++++++++++++ include/power/pf9453.h | 71 +++++++++++++++ 4 files changed, 260 insertions(+) create mode 100644 drivers/power/pmic/pf9453.c create mode 100644 include/power/pf9453.h
diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index 420c4099937..42e405901de 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -171,6 +171,21 @@ config SPL_DM_PMIC_PCA9450 This config enables implementation of driver-model pmic uclass features for PMIC PCA9450 in SPL. The driver implements read/write operations.
+config DM_PMIC_PF9453 + bool "Enable Driver Model for PMIC PF9453" + depends on DM_I2C + help + This config enables implementation of driver-model pmic uclass features + for PMIC PF9453. The driver implements read/write operations. + +config SPL_DM_PMIC_PF9453 + bool "Enable Driver Model for PMIC PF9453 in SPL" + depends on SPL_DM_PMIC + depends on SPL_DM_I2C + help + This config enables implementation of driver-model pmic uclass features + for PMIC PF9453 in SPL. The driver implements read/write operations. + config DM_PMIC_PF0900 bool "Enable Driver Model for PMIC PF0900" depends on DM_I2C diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 74ace69daf5..7cee4ee0582 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_$(SPL_)DM_PMIC_BD71837) += bd71837.o obj-$(CONFIG_$(SPL_)DM_PMIC_MP5416) += mp5416.o obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o obj-$(CONFIG_$(SPL_)DM_PMIC_PCA9450) += pca9450.o +obj-$(CONFIG_$(SPL_)DM_PMIC_PF9453) += pf9453.o obj-$(CONFIG_$(SPL_)DM_PMIC_PF0900) += pf0900.o obj-$(CONFIG_$(SPL_)DM_PMIC_PF5300) += pf5300.o obj-$(CONFIG_PMIC_S2MPS11) += s2mps11.o diff --git a/drivers/power/pmic/pf9453.c b/drivers/power/pmic/pf9453.c new file mode 100644 index 00000000000..893a7771d0e --- /dev/null +++ b/drivers/power/pmic/pf9453.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include <asm/global_data.h> +#include <asm-generic/gpio.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <i2c.h> +#include <linux/err.h> +#include <log.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/pf9453.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const struct pmic_child_info pmic_children_info[] = { + /* buck */ + { .prefix = "b", .driver = PF9453_REGULATOR_DRIVER}, + { .prefix = "B", .driver = PF9453_REGULATOR_DRIVER}, + /* ldo */ + { .prefix = "l", .driver = PF9453_REGULATOR_DRIVER}, + { .prefix = "L", .driver = PF9453_REGULATOR_DRIVER}, + { }, +}; + +struct pf9453_priv { + struct gpio_desc *sd_vsel_gpio; +}; + +static int pf9453_reg_count(struct udevice *dev) +{ + return PF9453_REG_NUM; +} + +static bool is_reg_protect(uint reg) +{ + switch (reg) { + case PF9453_BUCK1OUT: + case PF9453_BUCK2OUT: + case PF9453_BUCK3OUT: + case PF9453_BUCK4OUT: + case PF9453_LDO1OUT_L: + case PF9453_LDO1OUT_H: + case PF9453_LDO2OUT: + case PF9453_LDOSNVS_CFG1: + case PF9453_BUCK2OUT_MAX_LIMIT: + case PF9453_BUCK2OUT_MIN_LIMIT: + return true; + default: + return false; + } +} + +static int pf9453_write(struct udevice *dev, uint reg, const uint8_t *buff, + int len) +{ + bool is_pro; + u32 val = PF9453_UNLOCK_KEY; + + is_pro = is_reg_protect(reg); + if (is_pro) { + if (dm_i2c_write(dev, PF9453_REG_LOCK, (uint8_t *)&val, 1)) { + pr_err("write error to device: %p register: %#x!\n", dev, reg); + return -EIO; + } + + if (dm_i2c_write(dev, reg, buff, len)) { + pr_err("write error to device: %p register: %#x!\n", dev, reg); + return -EIO; + } + + val = PF9453_LOCK_KEY; + if (dm_i2c_write(dev, PF9453_REG_LOCK, (uint8_t *)&val, 1)) { + pr_err("write error to device: %p register: %#x!\n", dev, reg); + return -EIO; + } + } else { + + if (dm_i2c_write(dev, reg, buff, len)) { + pr_err("write error to device: %p register: %#x!\n", dev, reg); + return -EIO; + } + } + + return 0; +} + +static int pf9453_read(struct udevice *dev, uint reg, uint8_t *buff, + int len) +{ + if (dm_i2c_read(dev, reg, buff, len)) { + pr_err("read error from device: %p register: %#x!\n", dev, reg); + return -EIO; + } + + return 0; +} + +static int pf9453_bind(struct udevice *dev) +{ + int children; + ofnode regulators_node; + + regulators_node = dev_read_subnode(dev, "regulators"); + if (!ofnode_valid(regulators_node)) { + debug("%s: %s regulators subnode not found!", __func__, + dev->name); + return -ENXIO; + } + + debug("%s: '%s' - found regulators subnode\n", __func__, dev->name); + + children = pmic_bind_children(dev, regulators_node, + pmic_children_info); + if (!children) + debug("%s: %s - no child found\n", __func__, dev->name); + + /* Always return success for this device */ + return 0; +} + +static int pf9453_probe(struct udevice *dev) +{ + struct pf9453_priv *priv = dev_get_priv(dev); + unsigned int reset_ctrl; + int ret = 0; + + if (CONFIG_IS_ENABLED(DM_GPIO) && CONFIG_IS_ENABLED(DM_REGULATOR_PF9453)) { + priv->sd_vsel_gpio = devm_gpiod_get_optional(dev, "sd-vsel", + GPIOD_IS_OUT | + GPIOD_IS_OUT_ACTIVE); + if (IS_ERR(priv->sd_vsel_gpio)) { + ret = PTR_ERR(priv->sd_vsel_gpio); + dev_err(dev, "Failed to request SD_VSEL GPIO: %d\n", ret); + if (ret) + return ret; + } + } + + if (ofnode_read_bool(dev_ofnode(dev), "nxp,wdog_b-warm-reset")) + reset_ctrl = PF9453_PMIC_RESET_WDOG_B_CFG_WARM; + else + reset_ctrl = PF9453_PMIC_RESET_WDOG_B_CFG_COLD; + + return pmic_clrsetbits(dev, PF9453_RESET_CTRL, + PF9453_PMIC_RESET_WDOG_B_CFG_MASK, reset_ctrl); +} + +static struct dm_pmic_ops pf9453_ops = { + .reg_count = pf9453_reg_count, + .read = pf9453_read, + .write = pf9453_write, +}; + +static const struct udevice_id pf9453_ids[] = { + { .compatible = "nxp,pf9453", .data = NXP_CHIP_TYPE_PF9453, }, + { } +}; + +U_BOOT_DRIVER(pmic_pf9453) = { + .name = "pf9453 pmic", + .id = UCLASS_PMIC, + .of_match = pf9453_ids, + .bind = pf9453_bind, + .probe = pf9453_probe, + .ops = &pf9453_ops, + .priv_auto = sizeof(struct pf9453_priv), +}; diff --git a/include/power/pf9453.h b/include/power/pf9453.h new file mode 100644 index 00000000000..fd9617edc2c --- /dev/null +++ b/include/power/pf9453.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2024 NXP + */ + +#ifndef PF9453_H_ +#define PF9453_H_ + +#define PF9453_REGULATOR_DRIVER "pf9453_regulator" + +enum { + PF9453_REG_DEV_ID = 0x00, + PF9453_OTP_VER = 0x01, + PF9453_INT1 = 0x02, + PF9453_INT1_MSK = 0x03, + PF9453_INT1_STATUS = 0x04, + PF9453_VRFLT1_INT = 0x05, + PF9453_VRFLT1_MASK = 0x06, + PF9453_PWRON_STAT = 0x07, + PF9453_RESET_CTRL = 0x08, + PF9453_SW_RST = 0x09, + PF9453_PWR_CTRL = 0x0a, + PF9453_CONFIG1 = 0x0b, + PF9453_CONFIG2 = 0x0c, + PF9453_32K_CONFIG = 0x0d, + PF9453_BUCK1CTRL = 0x10, + PF9453_BUCK1OUT = 0x11, + PF9453_BUCK2CTRL = 0x14, + PF9453_BUCK2OUT = 0x15, + PF9453_BUCK2OUT_STBY = 0x1D, + PF9453_BUCK2OUT_MAX_LIMIT = 0x1F, + PF9453_BUCK2OUT_MIN_LIMIT = 0x20, + PF9453_BUCK3CTRL = 0x21, + PF9453_BUCK3OUT = 0x22, + PF9453_BUCK4CTRL = 0x2e, + PF9453_BUCK4OUT = 0x2f, + PF9453_LDO1OUT_L = 0x36, + PF9453_LDO1CFG = 0x37, + PF9453_LDO1OUT_H = 0x38, + PF9453_LDOSNVS_CFG1 = 0x39, + PF9453_LDOSNVS_CFG2 = 0x3a, + PF9453_LDO2CFG = 0x3b, + PF9453_LDO2OUT = 0x3c, + PF9453_BUCK_POK = 0x3d, + PF9453_LSW_CTRL1 = 0x40, + PF9453_LSW_CTRL2 = 0x41, + PF9453_REG_LOCK = 0x4e, + PF9453_REG_NUM, +}; + +int power_pf9453_init(unsigned char bus, unsigned char addr); + +enum { + NXP_CHIP_TYPE_PF9453 = 0, + NXP_CHIP_TYPE_AMOUNT +}; + +#define PF9453_UNLOCK_KEY 0x5c +#define PF9453_LOCK_KEY 0x0 + +#define PF9453_EN_MODE_MASK 0x3 +#define PF9453_BUCK_RUN_MASK 0x7f +#define PF9453_LDO1_MASK 0x7f +#define PF9453_LDO2_MASK 0x3f +#define PF9453_LDOSNVS_MASK 0x7f + +#define PF9453_PMIC_RESET_WDOG_B_CFG_MASK 0xc0 +#define PF9453_PMIC_RESET_WDOG_B_CFG_WARM 0x40 +#define PF9453_PMIC_RESET_WDOG_B_CFG_COLD 0x80 + +#endif

From: Joy Zou joy.zou@nxp.com
Add regulator support for NXP PMIC pf9453 for imx91 qsb.
Reviewed-by: Ye Li ye.li@nxp.com Reviewed-by: Peng Fan peng.fan@nxp.com Signed-off-by: Joy Zou joy.zou@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com --- drivers/power/regulator/Kconfig | 15 ++ drivers/power/regulator/Makefile | 1 + drivers/power/regulator/pf9453.c | 256 +++++++++++++++++++++++++++++++ 3 files changed, 272 insertions(+) create mode 100644 drivers/power/regulator/pf9453.c
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index 69dfb07df50..4aa963e745c 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -96,6 +96,21 @@ config SPL_DM_REGULATOR_PCA9450 This config enables implementation of driver-model regulator uclass features for regulators on ROHM PCA9450 in SPL.
+config DM_REGULATOR_PF9453 + bool "Enable Driver Model for NXP PF9453 regulators" + depends on DM_REGULATOR && DM_PMIC_PF9453 + help + This config enables implementation of driver-model regulator uclass + features for regulators on NXP PF9453 PMICs. PF9453 contains 6 bucks + and 5 LDOS. The driver implements get/set api for value and enable. + +config SPL_DM_REGULATOR_PF9453 + bool "Enable Driver Model for NXP PF9453 regulators in SPL" + depends on DM_REGULATOR_PF9453 && SPL + help + This config enables implementation of driver-model regulator uclass + features for regulators on ROHM PF9453 in SPL. + config DM_REGULATOR_PF0900 bool "Enable Driver Model for NXP PF0900 regulators" depends on DM_REGULATOR && DM_PMIC_PF0900 diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index 7a580d45b72..9e9a4da6217 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_DM_REGULATOR_NPCM8XX) += npcm8xx_regulator.o obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_BD71837) += bd71837.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_PCA9450) += pca9450.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_PF9453) += pf9453.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_PF0900) += pf0900.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_PF5300) += pf5300.o obj-$(CONFIG_$(SPL_)REGULATOR_PWM) += pwm_regulator.o diff --git a/drivers/power/regulator/pf9453.c b/drivers/power/regulator/pf9453.c new file mode 100644 index 00000000000..d11c69c33e6 --- /dev/null +++ b/drivers/power/regulator/pf9453.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NXP PF9453 regulator driver + * Copyright 2024 NXP + */ + +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <linux/bitops.h> +#include <power/pf9453.h> +#include <power/pmic.h> +#include <power/regulator.h> + +/** + * struct pf9453_vrange - describe linear range of voltages + * + * @min_volt: smallest voltage in range + * @step: how much voltage changes at each selector step + * @min_sel: smallest selector in the range + * @max_sel: maximum selector in the range + */ +struct pf9453_vrange { + unsigned int min_volt; + unsigned int step; + u8 min_sel; + u8 max_sel; +}; + +/** + * struct pf9453_plat - describe regulator control registers + * + * @name: name of the regulator. Used for matching the dt-entry + * @enable_reg: register address used to enable/disable regulator + * @enablemask: register mask used to enable/disable regulator + * @volt_reg: register address used to configure regulator voltage + * @volt_mask: register mask used to configure regulator voltage + * @ranges: pointer to ranges of regulator voltages and matching register + * values + * @numranges: number of voltage ranges pointed by ranges + */ +struct pf9453_plat { + const char *name; + u8 enable_reg; + u8 enablemask; + u8 volt_reg; + u8 volt_mask; + struct pf9453_vrange *ranges; + unsigned int numranges; +}; + +#define PCA_RANGE(_min, _vstep, _sel_low, _sel_hi) \ +{ \ + .min_volt = (_min), .step = (_vstep), \ + .min_sel = (_sel_low), .max_sel = (_sel_hi), \ +} + +#define PCA_DATA(_name, enreg, enmask, vreg, vmask, _range) \ +{ \ + .name = (_name), .enable_reg = (enreg), .enablemask = (enmask), \ + .volt_reg = (vreg), .volt_mask = (vmask), .ranges = (_range), \ + .numranges = ARRAY_SIZE(_range) \ +} + +static struct pf9453_vrange pf9453_buck134_vranges[] = { + PCA_RANGE(600000, 25000, 0, 0x7f), +}; + +static struct pf9453_vrange pf9453_buck2_vranges[] = { + PCA_RANGE(600000, 12500, 0, 0x7f), +}; + +static struct pf9453_vrange pf9453_ldo1_vranges[] = { + PCA_RANGE(800000, 25000, 0x0, 0x64), +}; + +static struct pf9453_vrange pf9453_ldo2_vranges[] = { + PCA_RANGE(500000, 25000, 0x0, 0x3a), +}; + +static struct pf9453_vrange pf9453_ldosnvs_vranges[] = { + PCA_RANGE(800000, 25000, 0x0, 0x58), +}; + +static struct pf9453_plat pf9453_reg_data[] = { + PCA_DATA("BUCK1", PF9453_BUCK1CTRL, PF9453_EN_MODE_MASK, + PF9453_BUCK1OUT, PF9453_BUCK_RUN_MASK, + pf9453_buck134_vranges), + PCA_DATA("BUCK2", PF9453_BUCK2CTRL, PF9453_EN_MODE_MASK, + PF9453_BUCK2OUT, PF9453_BUCK_RUN_MASK, + pf9453_buck2_vranges), + PCA_DATA("BUCK3", PF9453_BUCK3CTRL, PF9453_EN_MODE_MASK, + PF9453_BUCK3OUT, PF9453_BUCK_RUN_MASK, + pf9453_buck134_vranges), + PCA_DATA("BUCK4", PF9453_BUCK4CTRL, PF9453_EN_MODE_MASK, + PF9453_BUCK4OUT, PF9453_BUCK_RUN_MASK, + pf9453_buck134_vranges), + /* LDOs */ + PCA_DATA("LDO1", PF9453_LDO1CFG, PF9453_EN_MODE_MASK, + PF9453_LDO1OUT_H, PF9453_LDO1_MASK, + pf9453_ldo1_vranges), + PCA_DATA("LDO2", PF9453_LDO2CFG, PF9453_EN_MODE_MASK, + PF9453_LDO2OUT, PF9453_LDO2_MASK, + pf9453_ldo2_vranges), + PCA_DATA("LDOS_NVS", PF9453_LDOSNVS_CFG2, PF9453_EN_MODE_MASK, + PF9453_LDOSNVS_CFG1, PF9453_LDOSNVS_MASK, + pf9453_ldosnvs_vranges), +}; + +static int vrange_find_value(struct pf9453_vrange *r, unsigned int sel, + unsigned int *val) +{ + if (!val || sel < r->min_sel || sel > r->max_sel) + return -EINVAL; + + *val = r->min_volt + r->step * (sel - r->min_sel); + return 0; +} + +static int vrange_find_selector(struct pf9453_vrange *r, int val, + unsigned int *sel) +{ + int ret = -EINVAL; + int num_vals = r->max_sel - r->min_sel + 1; + + if (val >= r->min_volt && + val <= r->min_volt + r->step * (num_vals - 1)) { + if (r->step) { + *sel = r->min_sel + ((val - r->min_volt) / r->step); + ret = 0; + } else { + *sel = r->min_sel; + ret = 0; + } + } + return ret; +} + +static int pf9453_get_enable(struct udevice *dev) +{ + struct pf9453_plat *plat = dev_get_plat(dev); + int val; + + val = pmic_reg_read(dev->parent, plat->enable_reg); + if (val < 0) + return val; + + return (val & plat->enablemask); +} + +static int pf9453_set_enable(struct udevice *dev, bool enable) +{ + int val = 0; + struct pf9453_plat *plat = dev_get_plat(dev); + + if (enable) + val = plat->enablemask; + + return pmic_clrsetbits(dev->parent, plat->enable_reg, plat->enablemask, + val); +} + +static int pf9453_get_value(struct udevice *dev) +{ + struct pf9453_plat *plat = dev_get_plat(dev); + unsigned int reg, tmp; + int i, ret; + + ret = pmic_reg_read(dev->parent, plat->volt_reg); + if (ret < 0) + return ret; + + reg = ret; + reg &= plat->volt_mask; + + for (i = 0; i < plat->numranges; i++) { + struct pf9453_vrange *r = &plat->ranges[i]; + + if (!vrange_find_value(r, reg, &tmp)) + return tmp; + } + + pr_err("Unknown voltage value read from pmic\n"); + + return -EINVAL; +} + +static int pf9453_set_value(struct udevice *dev, int uvolt) +{ + struct pf9453_plat *plat = dev_get_plat(dev); + unsigned int sel; + int i, found = 0; + + for (i = 0; i < plat->numranges; i++) { + struct pf9453_vrange *r = &plat->ranges[i]; + + found = !vrange_find_selector(r, uvolt, &sel); + if (found) { + unsigned int tmp; + + /* + * We require exactly the requested value to be + * supported - this can be changed later if needed + */ + found = !vrange_find_value(r, sel, &tmp); + if (found && tmp == uvolt) + break; + found = 0; + } + } + + if (!found) + return -EINVAL; + + return pmic_clrsetbits(dev->parent, plat->volt_reg, + plat->volt_mask, sel); +} + +static int pf9453_regulator_probe(struct udevice *dev) +{ + struct pf9453_plat *plat = dev_get_plat(dev); + int i, type; + + type = dev_get_driver_data(dev_get_parent(dev)); + + if (type != NXP_CHIP_TYPE_PF9453) { + debug("Unknown PMIC type\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(pf9453_reg_data); i++) { + if (strcmp(dev->name, pf9453_reg_data[i].name)) + continue; + *plat = pf9453_reg_data[i]; + return 0; + } + + pr_err("Unknown regulator '%s'\n", dev->name); + + return -ENOENT; +} + +static const struct dm_regulator_ops pf9453_regulator_ops = { + .get_value = pf9453_get_value, + .set_value = pf9453_set_value, + .get_enable = pf9453_get_enable, + .set_enable = pf9453_set_enable, +}; + +U_BOOT_DRIVER(pf9453_regulator) = { + .name = PF9453_REGULATOR_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &pf9453_regulator_ops, + .probe = pf9453_regulator_probe, + .plat_auto = sizeof(struct pf9453_plat), +};

On Mon, Sep 30, 2024 at 11:55:27AM +0800, Peng Fan (OSS) wrote:
From: Joy Zou joy.zou@nxp.com
Support NXP pmic pf5300
Signed-off-by: Joy Zou joy.zou@nxp.com Reviewed-by: Ye Li ye.li@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com
drivers/power/pmic/Kconfig | 15 ++++ drivers/power/pmic/Makefile | 1 + drivers/power/pmic/pf5300.c | 168 ++++++++++++++++++++++++++++++++++++ include/power/pf5300.h | 100 +++++++++++++++++++++ 4 files changed, 284 insertions(+) create mode 100644 drivers/power/pmic/pf5300.c create mode 100644 include/power/pf5300.h
This series needs to be rebased based on the feedback I just gave for pf0900. In addition, please make sure all patches are checkpatch.pl clean, thanks.
participants (2)
-
Peng Fan (OSS)
-
Tom Rini