Loading drivers/regulator/qcom_pm8008-regulator.c +130 −31 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */ /* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "PM8008: %s: " fmt, __func__ Loading Loading @@ -60,6 +60,8 @@ #define LDO_STEPPER_CTL_REG(base) (base + 0x3b) #define STEP_RATE_MASK GENMASK(1, 0) /* Step rate in uV/us */ #define PM8010_STEP_RATE 4800 #define LDO_PD_CTL_REG(base) (base + 0xA0) #define STRONG_PD_EN_BIT BIT(7) Loading @@ -67,6 +69,11 @@ #define MAX_REG_NAME 20 #define PM8008_MAX_LDO 7 enum pmic_subtype { PM8008_SUBTYPE, PM8010_SUBTYPE, }; struct pm8008_chip { struct device *dev; struct regmap *regmap; Loading @@ -75,11 +82,20 @@ struct pm8008_chip { int ocp_irq; }; struct reg_init_data { u8 offset; u8 data; }; struct regulator_data { char *name; char *supply_name; int min_uv; int max_uv; int hpm_min_load_ua; int min_dropout_uv; const struct reg_init_data *reg_init; unsigned int reg_init_size; }; struct pm8008_regulator { Loading @@ -96,17 +112,52 @@ struct pm8008_regulator { int min_dropout_uv; int step_rate; bool enable_ocp_broadcast; enum pmic_subtype pmic_subtype; }; static const struct regulator_data pm8008_reg_data[PM8008_MAX_LDO] = { /* name parent min_uv max_uv hpm_load headroom_uv */ {"l1", "vdd_l1_l2", 528000, 1504000, 30000, 225000}, {"l2", "vdd_l1_l2", 528000, 1504000, 30000, 225000}, {"l3", "vdd_l3_l4", 1504000, 3400000, 10000, 200000}, {"l4", "vdd_l3_l4", 1504000, 3400000, 10000, 200000}, {"l5", "vdd_l5", 1504000, 3400000, 10000, 300000}, {"l6", "vdd_l6", 1504000, 3400000, 10000, 300000}, {"l7", "vdd_l7", 1504000, 3400000, 10000, 300000}, }; static struct regulator_data reg_data[] = { /* name, parent, min load, headroom */ {"pm8008_l1", "vdd_l1_l2", 10000, 225000}, {"pm8008_l2", "vdd_l1_l2", 10000, 225000}, {"pm8008_l3", "vdd_l3_l4", 10000, 200000}, {"pm8008_l4", "vdd_l3_l4", 10000, 200000}, {"pm8008_l5", "vdd_l5", 10000, 300000}, {"pm8008_l6", "vdd_l6", 10000, 300000}, {"pm8008_l7", "vdd_l7", 10000, 300000}, static const struct reg_init_data pm8010_p300_reg_init_data[] = { {0x55, 0x8A}, {0x77, 0x03}, }; static const struct reg_init_data pm8010_p600_reg_init_data[] = { {0x76, 0x07}, {0x77, 0x03}, }; /* * PM8010 LDOs 3, 4, and 6 can physically output a minimum of 1808 mV. However, * 1504 mV is specified here to match PM8008 and to avoid the parent supply of * these regulators being stuck at an unnecessarily high voltage as a result of * the framework maintaining a minimum vote of 1808 mV + headroom at all times * (even when the LDOs are OFF). This would waste power. The LDO hardware * automatically rounds up programmed voltages to supported set points. */ static const struct regulator_data pm8010_reg_data[PM8008_MAX_LDO] = { /* name parent min_uv max_uv hpm_load headroom_uv */ {"l1", "vdd_l1_l2", 528000, 1544000, 30000, 100000}, {"l2", "vdd_l1_l2", 528000, 1544000, 30000, 100000}, {"l3", "vdd_l3_l4", 1504000, 3312000, 10000, 300000, pm8010_p300_reg_init_data, ARRAY_SIZE(pm8010_p300_reg_init_data)}, {"l4", "vdd_l3_l4", 1504000, 3312000, 10000, 300000, pm8010_p300_reg_init_data, ARRAY_SIZE(pm8010_p300_reg_init_data)}, {"l5", "vdd_l5", 1504000, 3544000, 10000, 300000, pm8010_p600_reg_init_data, ARRAY_SIZE(pm8010_p600_reg_init_data)}, {"l6", "vdd_l6", 1504000, 3312000, 10000, 300000, pm8010_p300_reg_init_data, ARRAY_SIZE(pm8010_p300_reg_init_data)}, {"l7", "vdd_l7", 1504000, 3544000, 10000, 300000, pm8010_p600_reg_init_data, ARRAY_SIZE(pm8010_p600_reg_init_data)}, }; /* common functions */ Loading @@ -121,7 +172,8 @@ static int pm8008_read(struct regmap *regmap, u16 reg, u8 *val, int count) return rc; } static int pm8008_write(struct regmap *regmap, u16 reg, u8 *val, int count) static int pm8008_write(struct regmap *regmap, u16 reg, const u8 *val, int count) { int rc; Loading Loading @@ -559,6 +611,25 @@ static int pm8008_ldo_cb(struct notifier_block *nb, ulong event, void *data) return NOTIFY_OK; } static int pm8008_regulator_register_init(struct pm8008_regulator *pm8008_reg, const struct regulator_data *reg_data) { int i, rc; if (!reg_data->reg_init) return 0; for (i = 0; i < reg_data->reg_init_size; i++) { rc = pm8008_write(pm8008_reg->regmap, pm8008_reg->base + reg_data->reg_init[i].offset, ®_data->reg_init[i].data, 1); if (rc < 0) return rc; } return 0; } static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg, const char *name) { Loading @@ -567,13 +638,17 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg, struct device *dev = pm8008_reg->dev; struct device_node *reg_node = pm8008_reg->of_node; char buff[MAX_REG_NAME]; const struct regulator_data *reg_data; int rc, i, init_voltage; u32 base = 0; u8 reg; reg_data = pm8008_reg->pmic_subtype == PM8008_SUBTYPE ? pm8008_reg_data : pm8010_reg_data; /* get regulator data */ for (i = 0; i < PM8008_MAX_LDO; i++) if (!strcmp(reg_data[i].name, name)) if (strstr(name, reg_data[i].name)) break; if (i == PM8008_MAX_LDO) { Loading @@ -588,6 +663,10 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg, } pm8008_reg->base = base; rc = pm8008_regulator_register_init(pm8008_reg, ®_data[i]); if (rc) return rc; pm8008_reg->min_dropout_uv = reg_data[i].min_dropout_uv; of_property_read_u32(reg_node, "qcom,min-dropout-voltage", &pm8008_reg->min_dropout_uv); Loading Loading @@ -622,6 +701,7 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg, } /* get slew rate */ if (pm8008_reg->pmic_subtype == PM8008_SUBTYPE) { rc = pm8008_read(pm8008_reg->regmap, LDO_STEPPER_CTL_REG(pm8008_reg->base), ®, 1); if (rc < 0) { Loading @@ -630,6 +710,9 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg, return rc; } pm8008_reg->step_rate = 38400 >> (reg & STEP_RATE_MASK); } else { pm8008_reg->step_rate = PM8010_STEP_RATE; } scnprintf(buff, MAX_REG_NAME, "%s-supply", reg_data[i].supply_name); if (of_find_property(dev->of_node, buff, NULL)) { Loading Loading @@ -713,6 +796,18 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg, return 0; } static const struct of_device_id pm8008_regulator_match_table[] = { { .compatible = "qcom,pm8008-regulator", .data = (void *)(uintptr_t)PM8008_SUBTYPE, }, { .compatible = "qcom,pm8010-regulator", .data = (void *)(uintptr_t)PM8010_SUBTYPE, }, { }, }; /* PMIC probe and helper function */ static int pm8008_parse_regulator(struct regmap *regmap, struct device *dev) { Loading @@ -720,8 +815,18 @@ static int pm8008_parse_regulator(struct regmap *regmap, struct device *dev) const char *name; struct device_node *child; struct pm8008_regulator *pm8008_reg; const struct of_device_id *match; enum pmic_subtype pmic_subtype; bool ocp; match = of_match_node(pm8008_regulator_match_table, dev->of_node); if (match) { pmic_subtype = (uintptr_t)match->data; } else { dev_err(dev, "could not find compatible string match\n"); return -ENODEV; } ocp = of_property_read_bool(dev->of_node, "qcom,enable-ocp-broadcast"); /* parse each subnode and register regulator for regulator child */ Loading @@ -734,6 +839,7 @@ static int pm8008_parse_regulator(struct regmap *regmap, struct device *dev) pm8008_reg->of_node = child; pm8008_reg->dev = dev; pm8008_reg->enable_ocp_broadcast = ocp; pm8008_reg->pmic_subtype = pmic_subtype; rc = of_property_read_string(child, "regulator-name", &name); if (rc) Loading Loading @@ -923,13 +1029,6 @@ static int pm8008_chip_remove(struct platform_device *pdev) return 0; } static const struct of_device_id pm8008_regulator_match_table[] = { { .compatible = "qcom,pm8008-regulator", }, { }, }; static struct platform_driver pm8008_regulator_driver = { .driver = { .name = "qcom,pm8008-regulator", Loading Loading
drivers/regulator/qcom_pm8008-regulator.c +130 −31 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */ /* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "PM8008: %s: " fmt, __func__ Loading Loading @@ -60,6 +60,8 @@ #define LDO_STEPPER_CTL_REG(base) (base + 0x3b) #define STEP_RATE_MASK GENMASK(1, 0) /* Step rate in uV/us */ #define PM8010_STEP_RATE 4800 #define LDO_PD_CTL_REG(base) (base + 0xA0) #define STRONG_PD_EN_BIT BIT(7) Loading @@ -67,6 +69,11 @@ #define MAX_REG_NAME 20 #define PM8008_MAX_LDO 7 enum pmic_subtype { PM8008_SUBTYPE, PM8010_SUBTYPE, }; struct pm8008_chip { struct device *dev; struct regmap *regmap; Loading @@ -75,11 +82,20 @@ struct pm8008_chip { int ocp_irq; }; struct reg_init_data { u8 offset; u8 data; }; struct regulator_data { char *name; char *supply_name; int min_uv; int max_uv; int hpm_min_load_ua; int min_dropout_uv; const struct reg_init_data *reg_init; unsigned int reg_init_size; }; struct pm8008_regulator { Loading @@ -96,17 +112,52 @@ struct pm8008_regulator { int min_dropout_uv; int step_rate; bool enable_ocp_broadcast; enum pmic_subtype pmic_subtype; }; static const struct regulator_data pm8008_reg_data[PM8008_MAX_LDO] = { /* name parent min_uv max_uv hpm_load headroom_uv */ {"l1", "vdd_l1_l2", 528000, 1504000, 30000, 225000}, {"l2", "vdd_l1_l2", 528000, 1504000, 30000, 225000}, {"l3", "vdd_l3_l4", 1504000, 3400000, 10000, 200000}, {"l4", "vdd_l3_l4", 1504000, 3400000, 10000, 200000}, {"l5", "vdd_l5", 1504000, 3400000, 10000, 300000}, {"l6", "vdd_l6", 1504000, 3400000, 10000, 300000}, {"l7", "vdd_l7", 1504000, 3400000, 10000, 300000}, }; static struct regulator_data reg_data[] = { /* name, parent, min load, headroom */ {"pm8008_l1", "vdd_l1_l2", 10000, 225000}, {"pm8008_l2", "vdd_l1_l2", 10000, 225000}, {"pm8008_l3", "vdd_l3_l4", 10000, 200000}, {"pm8008_l4", "vdd_l3_l4", 10000, 200000}, {"pm8008_l5", "vdd_l5", 10000, 300000}, {"pm8008_l6", "vdd_l6", 10000, 300000}, {"pm8008_l7", "vdd_l7", 10000, 300000}, static const struct reg_init_data pm8010_p300_reg_init_data[] = { {0x55, 0x8A}, {0x77, 0x03}, }; static const struct reg_init_data pm8010_p600_reg_init_data[] = { {0x76, 0x07}, {0x77, 0x03}, }; /* * PM8010 LDOs 3, 4, and 6 can physically output a minimum of 1808 mV. However, * 1504 mV is specified here to match PM8008 and to avoid the parent supply of * these regulators being stuck at an unnecessarily high voltage as a result of * the framework maintaining a minimum vote of 1808 mV + headroom at all times * (even when the LDOs are OFF). This would waste power. The LDO hardware * automatically rounds up programmed voltages to supported set points. */ static const struct regulator_data pm8010_reg_data[PM8008_MAX_LDO] = { /* name parent min_uv max_uv hpm_load headroom_uv */ {"l1", "vdd_l1_l2", 528000, 1544000, 30000, 100000}, {"l2", "vdd_l1_l2", 528000, 1544000, 30000, 100000}, {"l3", "vdd_l3_l4", 1504000, 3312000, 10000, 300000, pm8010_p300_reg_init_data, ARRAY_SIZE(pm8010_p300_reg_init_data)}, {"l4", "vdd_l3_l4", 1504000, 3312000, 10000, 300000, pm8010_p300_reg_init_data, ARRAY_SIZE(pm8010_p300_reg_init_data)}, {"l5", "vdd_l5", 1504000, 3544000, 10000, 300000, pm8010_p600_reg_init_data, ARRAY_SIZE(pm8010_p600_reg_init_data)}, {"l6", "vdd_l6", 1504000, 3312000, 10000, 300000, pm8010_p300_reg_init_data, ARRAY_SIZE(pm8010_p300_reg_init_data)}, {"l7", "vdd_l7", 1504000, 3544000, 10000, 300000, pm8010_p600_reg_init_data, ARRAY_SIZE(pm8010_p600_reg_init_data)}, }; /* common functions */ Loading @@ -121,7 +172,8 @@ static int pm8008_read(struct regmap *regmap, u16 reg, u8 *val, int count) return rc; } static int pm8008_write(struct regmap *regmap, u16 reg, u8 *val, int count) static int pm8008_write(struct regmap *regmap, u16 reg, const u8 *val, int count) { int rc; Loading Loading @@ -559,6 +611,25 @@ static int pm8008_ldo_cb(struct notifier_block *nb, ulong event, void *data) return NOTIFY_OK; } static int pm8008_regulator_register_init(struct pm8008_regulator *pm8008_reg, const struct regulator_data *reg_data) { int i, rc; if (!reg_data->reg_init) return 0; for (i = 0; i < reg_data->reg_init_size; i++) { rc = pm8008_write(pm8008_reg->regmap, pm8008_reg->base + reg_data->reg_init[i].offset, ®_data->reg_init[i].data, 1); if (rc < 0) return rc; } return 0; } static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg, const char *name) { Loading @@ -567,13 +638,17 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg, struct device *dev = pm8008_reg->dev; struct device_node *reg_node = pm8008_reg->of_node; char buff[MAX_REG_NAME]; const struct regulator_data *reg_data; int rc, i, init_voltage; u32 base = 0; u8 reg; reg_data = pm8008_reg->pmic_subtype == PM8008_SUBTYPE ? pm8008_reg_data : pm8010_reg_data; /* get regulator data */ for (i = 0; i < PM8008_MAX_LDO; i++) if (!strcmp(reg_data[i].name, name)) if (strstr(name, reg_data[i].name)) break; if (i == PM8008_MAX_LDO) { Loading @@ -588,6 +663,10 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg, } pm8008_reg->base = base; rc = pm8008_regulator_register_init(pm8008_reg, ®_data[i]); if (rc) return rc; pm8008_reg->min_dropout_uv = reg_data[i].min_dropout_uv; of_property_read_u32(reg_node, "qcom,min-dropout-voltage", &pm8008_reg->min_dropout_uv); Loading Loading @@ -622,6 +701,7 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg, } /* get slew rate */ if (pm8008_reg->pmic_subtype == PM8008_SUBTYPE) { rc = pm8008_read(pm8008_reg->regmap, LDO_STEPPER_CTL_REG(pm8008_reg->base), ®, 1); if (rc < 0) { Loading @@ -630,6 +710,9 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg, return rc; } pm8008_reg->step_rate = 38400 >> (reg & STEP_RATE_MASK); } else { pm8008_reg->step_rate = PM8010_STEP_RATE; } scnprintf(buff, MAX_REG_NAME, "%s-supply", reg_data[i].supply_name); if (of_find_property(dev->of_node, buff, NULL)) { Loading Loading @@ -713,6 +796,18 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg, return 0; } static const struct of_device_id pm8008_regulator_match_table[] = { { .compatible = "qcom,pm8008-regulator", .data = (void *)(uintptr_t)PM8008_SUBTYPE, }, { .compatible = "qcom,pm8010-regulator", .data = (void *)(uintptr_t)PM8010_SUBTYPE, }, { }, }; /* PMIC probe and helper function */ static int pm8008_parse_regulator(struct regmap *regmap, struct device *dev) { Loading @@ -720,8 +815,18 @@ static int pm8008_parse_regulator(struct regmap *regmap, struct device *dev) const char *name; struct device_node *child; struct pm8008_regulator *pm8008_reg; const struct of_device_id *match; enum pmic_subtype pmic_subtype; bool ocp; match = of_match_node(pm8008_regulator_match_table, dev->of_node); if (match) { pmic_subtype = (uintptr_t)match->data; } else { dev_err(dev, "could not find compatible string match\n"); return -ENODEV; } ocp = of_property_read_bool(dev->of_node, "qcom,enable-ocp-broadcast"); /* parse each subnode and register regulator for regulator child */ Loading @@ -734,6 +839,7 @@ static int pm8008_parse_regulator(struct regmap *regmap, struct device *dev) pm8008_reg->of_node = child; pm8008_reg->dev = dev; pm8008_reg->enable_ocp_broadcast = ocp; pm8008_reg->pmic_subtype = pmic_subtype; rc = of_property_read_string(child, "regulator-name", &name); if (rc) Loading Loading @@ -923,13 +1029,6 @@ static int pm8008_chip_remove(struct platform_device *pdev) return 0; } static const struct of_device_id pm8008_regulator_match_table[] = { { .compatible = "qcom,pm8008-regulator", }, { }, }; static struct platform_driver pm8008_regulator_driver = { .driver = { .name = "qcom,pm8008-regulator", Loading