Message ID | 1652275113-10277-8-git-send-email-quic_c_skakit@quicinc.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | Add Qualcomm Technologies, Inc. PM8008 regulator driver | expand |
Hi Satya, url: https://github.com/intel-lab-lkp/linux/commits/Satya-Priya/Add-Qualcomm-Technologies-Inc-PM8008-regulator-driver/20220511-212136 base: https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git for-mfd-next config: nios2-randconfig-m031-20220512 (https://download.01.org/0day-ci/archive/20220513/202205130908.O31s3mhc-lkp@intel.com/config) compiler: nios2-linux-gcc (GCC) 11.3.0 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> Reported-by: Dan Carpenter <dan.carpenter@oracle.com> New smatch warnings: drivers/regulator/qcom-pm8008-regulator.c:174 pm8008_regulator_probe() error: buffer overflow 'reg_data' 7 <= 7 (assuming for loop doesn't break) Old smatch warnings: drivers/regulator/qcom-pm8008-regulator.c:175 pm8008_regulator_probe() error: buffer overflow 'reg_data' 7 <= 7 (assuming for loop doesn't break) drivers/regulator/qcom-pm8008-regulator.c:176 pm8008_regulator_probe() error: buffer overflow 'reg_data' 7 <= 7 (assuming for loop doesn't break) drivers/regulator/qcom-pm8008-regulator.c:178 pm8008_regulator_probe() error: buffer overflow 'reg_data' 7 <= 7 (assuming for loop doesn't break) drivers/regulator/qcom-pm8008-regulator.c:180 pm8008_regulator_probe() error: buffer overflow 'reg_data' 7 <= 7 (assuming for loop doesn't break) drivers/regulator/qcom-pm8008-regulator.c:180 pm8008_regulator_probe() error: buffer overflow 'reg_data' 7 <= 7 (assuming for loop doesn't break) drivers/regulator/qcom-pm8008-regulator.c:182 pm8008_regulator_probe() error: buffer overflow 'reg_data' 7 <= 7 (assuming for loop doesn't break) drivers/regulator/qcom-pm8008-regulator.c:186 pm8008_regulator_probe() error: buffer overflow 'reg_data' 7 <= 7 (assuming for loop doesn't break) drivers/regulator/qcom-pm8008-regulator.c:196 pm8008_regulator_probe() error: buffer overflow 'reg_data' 7 <= 7 (assuming for loop doesn't break) vim +174 drivers/regulator/qcom-pm8008-regulator.c 3337142527c77d Satya Priya 2022-05-11 122 static int pm8008_regulator_probe(struct platform_device *pdev) 3337142527c77d Satya Priya 2022-05-11 123 { 3337142527c77d Satya Priya 2022-05-11 124 int rc, i; 3337142527c77d Satya Priya 2022-05-11 125 u32 base; 3337142527c77d Satya Priya 2022-05-11 126 unsigned int reg; 3337142527c77d Satya Priya 2022-05-11 127 const char *name; 3337142527c77d Satya Priya 2022-05-11 128 struct device *dev = &pdev->dev; 3337142527c77d Satya Priya 2022-05-11 129 struct regulator_config reg_config = {}; 3337142527c77d Satya Priya 2022-05-11 130 struct regulator_dev *rdev; 3337142527c77d Satya Priya 2022-05-11 131 const struct pm8008_data *chip = dev_get_drvdata(pdev->dev.parent); 3337142527c77d Satya Priya 2022-05-11 132 struct pm8008_regulator *pm8008_reg; 3337142527c77d Satya Priya 2022-05-11 133 3337142527c77d Satya Priya 2022-05-11 134 pm8008_reg = devm_kzalloc(dev, sizeof(*pm8008_reg), GFP_KERNEL); 3337142527c77d Satya Priya 2022-05-11 135 if (!pm8008_reg) 3337142527c77d Satya Priya 2022-05-11 136 return -ENOMEM; 3337142527c77d Satya Priya 2022-05-11 137 3337142527c77d Satya Priya 2022-05-11 138 pm8008_reg->regmap = pm8008_get_regmap(chip); 3337142527c77d Satya Priya 2022-05-11 139 if (!pm8008_reg->regmap) { 3337142527c77d Satya Priya 2022-05-11 140 dev_err(dev, "parent regmap is missing\n"); 3337142527c77d Satya Priya 2022-05-11 141 return -EINVAL; 3337142527c77d Satya Priya 2022-05-11 142 } 3337142527c77d Satya Priya 2022-05-11 143 3337142527c77d Satya Priya 2022-05-11 144 pm8008_reg->dev = dev; 3337142527c77d Satya Priya 2022-05-11 145 3337142527c77d Satya Priya 2022-05-11 146 rc = of_property_read_string(dev->of_node, "regulator-name", &name); 3337142527c77d Satya Priya 2022-05-11 147 if (rc) 3337142527c77d Satya Priya 2022-05-11 148 return rc; 3337142527c77d Satya Priya 2022-05-11 149 3337142527c77d Satya Priya 2022-05-11 150 /* get the required regulator data */ 3337142527c77d Satya Priya 2022-05-11 151 for (i = 0; i < ARRAY_SIZE(reg_data); i++) 3337142527c77d Satya Priya 2022-05-11 152 if (strstr(name, reg_data[i].name)) 3337142527c77d Satya Priya 2022-05-11 153 break; This code assumes that we will find a match but it would be more robust to add a check. if (i == ARRAY_SIZE(reg_data)) return -ENODEV; 3337142527c77d Satya Priya 2022-05-11 154 3337142527c77d Satya Priya 2022-05-11 155 rc = of_property_read_u32_index(dev->of_node, "reg", 1, &base); 3337142527c77d Satya Priya 2022-05-11 156 if (rc < 0) { 3337142527c77d Satya Priya 2022-05-11 157 dev_err(dev, "%s: failed to get regulator base rc=%d\n", name, rc); 3337142527c77d Satya Priya 2022-05-11 158 return rc; 3337142527c77d Satya Priya 2022-05-11 159 } 3337142527c77d Satya Priya 2022-05-11 160 pm8008_reg->base = base; 3337142527c77d Satya Priya 2022-05-11 161 3337142527c77d Satya Priya 2022-05-11 162 /* get slew rate */ 3337142527c77d Satya Priya 2022-05-11 163 rc = regmap_bulk_read(pm8008_reg->regmap, 3337142527c77d Satya Priya 2022-05-11 164 LDO_STEPPER_CTL_REG(pm8008_reg->base), ®, 1); 3337142527c77d Satya Priya 2022-05-11 165 if (rc < 0) { 3337142527c77d Satya Priya 2022-05-11 166 dev_err(dev, "failed to read step rate configuration rc=%d\n", rc); 3337142527c77d Satya Priya 2022-05-11 167 return rc; 3337142527c77d Satya Priya 2022-05-11 168 } 3337142527c77d Satya Priya 2022-05-11 169 reg &= STEP_RATE_MASK; 3337142527c77d Satya Priya 2022-05-11 170 pm8008_reg->step_rate = DEFAULT_VOLTAGE_STEPPER_RATE >> reg; 3337142527c77d Satya Priya 2022-05-11 171 3337142527c77d Satya Priya 2022-05-11 172 pm8008_reg->rdesc.type = REGULATOR_VOLTAGE; 3337142527c77d Satya Priya 2022-05-11 173 pm8008_reg->rdesc.ops = &pm8008_regulator_ops; 3337142527c77d Satya Priya 2022-05-11 @174 pm8008_reg->rdesc.name = reg_data[i].name; 3337142527c77d Satya Priya 2022-05-11 175 pm8008_reg->rdesc.supply_name = reg_data[i].supply_name; 3337142527c77d Satya Priya 2022-05-11 176 pm8008_reg->rdesc.of_match = reg_data[i].name; 3337142527c77d Satya Priya 2022-05-11 177 pm8008_reg->rdesc.uV_step = VSET_STEP_UV; 3337142527c77d Satya Priya 2022-05-11 178 pm8008_reg->rdesc.min_uV = reg_data[i].min_uv; 3337142527c77d Satya Priya 2022-05-11 179 pm8008_reg->rdesc.n_voltages 3337142527c77d Satya Priya 2022-05-11 180 = ((reg_data[i].max_uv - reg_data[i].min_uv) 3337142527c77d Satya Priya 2022-05-11 181 / pm8008_reg->rdesc.uV_step) + 1; 3337142527c77d Satya Priya 2022-05-11 182 pm8008_reg->rdesc.linear_ranges = reg_data[i].voltage_range; 3337142527c77d Satya Priya 2022-05-11 183 pm8008_reg->rdesc.n_linear_ranges = 1; 3337142527c77d Satya Priya 2022-05-11 184 pm8008_reg->rdesc.enable_reg = LDO_ENABLE_REG(pm8008_reg->base); 3337142527c77d Satya Priya 2022-05-11 185 pm8008_reg->rdesc.enable_mask = ENABLE_BIT; 3337142527c77d Satya Priya 2022-05-11 186 pm8008_reg->rdesc.min_dropout_uV = reg_data[i].min_dropout_uv; 3337142527c77d Satya Priya 2022-05-11 187 pm8008_reg->voltage_selector = -ENOTRECOVERABLE; 3337142527c77d Satya Priya 2022-05-11 188 3337142527c77d Satya Priya 2022-05-11 189 reg_config.dev = dev->parent; 3337142527c77d Satya Priya 2022-05-11 190 reg_config.driver_data = pm8008_reg; 3337142527c77d Satya Priya 2022-05-11 191 reg_config.regmap = pm8008_reg->regmap; 3337142527c77d Satya Priya 2022-05-11 192 3337142527c77d Satya Priya 2022-05-11 193 rdev = devm_regulator_register(dev, &pm8008_reg->rdesc, ®_config); 3337142527c77d Satya Priya 2022-05-11 194 if (IS_ERR(rdev)) { 3337142527c77d Satya Priya 2022-05-11 195 rc = PTR_ERR(rdev); 3337142527c77d Satya Priya 2022-05-11 196 dev_err(dev, "%s: failed to register regulator rc=%d\n", 3337142527c77d Satya Priya 2022-05-11 197 reg_data[i].name, rc); 3337142527c77d Satya Priya 2022-05-11 198 return rc; 3337142527c77d Satya Priya 2022-05-11 199 } 3337142527c77d Satya Priya 2022-05-11 200 3337142527c77d Satya Priya 2022-05-11 201 return 0; 3337142527c77d Satya Priya 2022-05-11 202 }
Quoting Satya Priya (2022-05-11 06:18:31) > diff --git a/drivers/regulator/qcom-pm8008-regulator.c b/drivers/regulator/qcom-pm8008-regulator.c > new file mode 100644 > index 0000000..0361f02 > --- /dev/null > +++ b/drivers/regulator/qcom-pm8008-regulator.c > @@ -0,0 +1,221 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* Copyright (c) 2022, The Linux Foundation. All rights reserved. */ > + > +#include <linux/device.h> > +#include <linux/kernel.h> > +#include <linux/mfd/qcom_pm8008.h> > +#include <linux/module.h> > +#include <linux/of.h> Is this include used? > +#include <linux/of_device.h> > +#include <linux/platform_device.h> > +#include <linux/regmap.h> > +#include <linux/regulator/driver.h> > +#include <linux/regulator/of_regulator.h> Is this include used? > + > +#define VSET_STEP_MV 8 > +#define VSET_STEP_UV (VSET_STEP_MV * 1000) > + > +#define LDO_ENABLE_REG(base) ((base) + 0x46) > +#define ENABLE_BIT BIT(7) > + > +#define LDO_VSET_LB_REG(base) ((base) + 0x40) > + > +#define LDO_STEPPER_CTL_REG(base) ((base) + 0x3b) > +#define DEFAULT_VOLTAGE_STEPPER_RATE 38400 > +#define STEP_RATE_MASK GENMASK(1, 0) > + > +struct pm8008_regulator_data { > + const char *name; > + const char *supply_name; > + int min_uv; > + int max_uv; > + int min_dropout_uv; > + const struct linear_range *voltage_range; > +}; > + > +struct pm8008_regulator { > + struct device *dev; > + struct regmap *regmap; > + struct regulator_desc rdesc; > + u16 base; > + int step_rate; > + int voltage_selector; > +}; > + > +static const struct linear_range nldo_ranges[] = { > + REGULATOR_LINEAR_RANGE(528000, 0, 122, 8000), > +}; > + > +static const struct linear_range pldo_ranges[] = { > + REGULATOR_LINEAR_RANGE(1504000, 0, 237, 8000), > +}; > + > +static const struct pm8008_regulator_data reg_data[] = { > + /* name parent min_uv max_uv headroom_uv voltage_range */ > + { "ldo1", "vdd_l1_l2", 528000, 1504000, 225000, nldo_ranges, }, > + { "ldo2", "vdd_l1_l2", 528000, 1504000, 225000, nldo_ranges, }, > + { "ldo3", "vdd_l3_l4", 1504000, 3400000, 300000, pldo_ranges, }, > + { "ldo4", "vdd_l3_l4", 1504000, 3400000, 300000, pldo_ranges, }, > + { "ldo5", "vdd_l5", 1504000, 3400000, 200000, pldo_ranges, }, > + { "ldo6", "vdd_l6", 1504000, 3400000, 200000, pldo_ranges, }, > + { "ldo7", "vdd_l7", 1504000, 3400000, 200000, pldo_ranges, }, > +}; > + > +static int pm8008_regulator_get_voltage(struct regulator_dev *rdev) > +{ > + struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev); > + > + return pm8008_reg->voltage_selector; > +} > + > +static inline int pm8008_write_voltage(struct pm8008_regulator *pm8008_reg, > + int mV) > +{ > + __le16 vset_raw; > + > + vset_raw = cpu_to_le16(mV); > + > + return regmap_bulk_write(pm8008_reg->regmap, > + LDO_VSET_LB_REG(pm8008_reg->base), > + (const void *)&vset_raw, sizeof(vset_raw)); Does sparse complain about casting away __le16? > +} > + > +static int pm8008_regulator_set_voltage_time(struct regulator_dev *rdev, > + int old_uV, int new_uv) > +{ > + struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev); > + > + return DIV_ROUND_UP(abs(new_uv - old_uV), pm8008_reg->step_rate); > +} > +
On 5/17/2022 12:33 AM, Stephen Boyd wrote: > Quoting Satya Priya (2022-05-11 06:18:31) >> diff --git a/drivers/regulator/qcom-pm8008-regulator.c b/drivers/regulator/qcom-pm8008-regulator.c >> new file mode 100644 >> index 0000000..0361f02 >> --- /dev/null >> +++ b/drivers/regulator/qcom-pm8008-regulator.c >> @@ -0,0 +1,221 @@ >> +// SPDX-License-Identifier: GPL-2.0-only >> +/* Copyright (c) 2022, The Linux Foundation. All rights reserved. */ >> + >> +#include <linux/device.h> >> +#include <linux/kernel.h> >> +#include <linux/mfd/qcom_pm8008.h> >> +#include <linux/module.h> >> +#include <linux/of.h> > Is this include used? This is used for of_property_read_* APIs. >> +#include <linux/of_device.h> >> +#include <linux/platform_device.h> >> +#include <linux/regmap.h> >> +#include <linux/regulator/driver.h> >> +#include <linux/regulator/of_regulator.h> > Is this include used? This is not required, I will remove this. >> + >> +#define VSET_STEP_MV 8 >> +#define VSET_STEP_UV (VSET_STEP_MV * 1000) >> + >> +#define LDO_ENABLE_REG(base) ((base) + 0x46) >> +#define ENABLE_BIT BIT(7) >> + >> +#define LDO_VSET_LB_REG(base) ((base) + 0x40) >> + >> +#define LDO_STEPPER_CTL_REG(base) ((base) + 0x3b) >> +#define DEFAULT_VOLTAGE_STEPPER_RATE 38400 >> +#define STEP_RATE_MASK GENMASK(1, 0) >> + >> +struct pm8008_regulator_data { >> + const char *name; >> + const char *supply_name; >> + int min_uv; >> + int max_uv; >> + int min_dropout_uv; >> + const struct linear_range *voltage_range; >> +}; >> + >> +struct pm8008_regulator { >> + struct device *dev; >> + struct regmap *regmap; >> + struct regulator_desc rdesc; >> + u16 base; >> + int step_rate; >> + int voltage_selector; >> +}; >> + >> +static const struct linear_range nldo_ranges[] = { >> + REGULATOR_LINEAR_RANGE(528000, 0, 122, 8000), >> +}; >> + >> +static const struct linear_range pldo_ranges[] = { >> + REGULATOR_LINEAR_RANGE(1504000, 0, 237, 8000), >> +}; >> + >> +static const struct pm8008_regulator_data reg_data[] = { >> + /* name parent min_uv max_uv headroom_uv voltage_range */ >> + { "ldo1", "vdd_l1_l2", 528000, 1504000, 225000, nldo_ranges, }, >> + { "ldo2", "vdd_l1_l2", 528000, 1504000, 225000, nldo_ranges, }, >> + { "ldo3", "vdd_l3_l4", 1504000, 3400000, 300000, pldo_ranges, }, >> + { "ldo4", "vdd_l3_l4", 1504000, 3400000, 300000, pldo_ranges, }, >> + { "ldo5", "vdd_l5", 1504000, 3400000, 200000, pldo_ranges, }, >> + { "ldo6", "vdd_l6", 1504000, 3400000, 200000, pldo_ranges, }, >> + { "ldo7", "vdd_l7", 1504000, 3400000, 200000, pldo_ranges, }, >> +}; >> + >> +static int pm8008_regulator_get_voltage(struct regulator_dev *rdev) >> +{ >> + struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev); >> + >> + return pm8008_reg->voltage_selector; >> +} >> + >> +static inline int pm8008_write_voltage(struct pm8008_regulator *pm8008_reg, >> + int mV) >> +{ >> + __le16 vset_raw; >> + >> + vset_raw = cpu_to_le16(mV); >> + >> + return regmap_bulk_write(pm8008_reg->regmap, >> + LDO_VSET_LB_REG(pm8008_reg->base), >> + (const void *)&vset_raw, sizeof(vset_raw)); > Does sparse complain about casting away __le16? No. >> +} >> + >> +static int pm8008_regulator_set_voltage_time(struct regulator_dev *rdev, >> + int old_uV, int new_uv) >> +{ >> + struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev); >> + >> + return DIV_ROUND_UP(abs(new_uv - old_uV), pm8008_reg->step_rate); >> +} >> +
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index cbe0f96..2c6d9c2 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -925,6 +925,15 @@ config REGULATOR_PWM This driver supports PWM controlled voltage regulators. PWM duty cycle can increase or decrease the voltage. +config REGULATOR_QCOM_PM8008 + tristate "Qualcomm Technologies, Inc. PM8008 PMIC regulators" + depends on MFD_QCOM_PM8008 + help + Select this option to get support for the voltage regulators + of Qualcomm Technologies, Inc. PM8008 PMIC chip. PM8008 has 7 LDO + regulators. This driver provides support for basic operations like + set/get voltage and enable/disable. + config REGULATOR_QCOM_RPM tristate "Qualcomm RPM regulator driver" depends on MFD_QCOM_RPM diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 8d3ee8b..169e686 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -101,6 +101,7 @@ obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o obj-$(CONFIG_REGULATOR_MTK_DVFSRC) += mtk-dvfsrc-regulator.o obj-$(CONFIG_REGULATOR_QCOM_LABIBB) += qcom-labibb-regulator.o +obj-$(CONFIG_REGULATOR_QCOM_PM8008) += qcom-pm8008-regulator.o obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o diff --git a/drivers/regulator/qcom-pm8008-regulator.c b/drivers/regulator/qcom-pm8008-regulator.c new file mode 100644 index 0000000..0361f02 --- /dev/null +++ b/drivers/regulator/qcom-pm8008-regulator.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2022, The Linux Foundation. All rights reserved. */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/mfd/qcom_pm8008.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/of_regulator.h> + +#define VSET_STEP_MV 8 +#define VSET_STEP_UV (VSET_STEP_MV * 1000) + +#define LDO_ENABLE_REG(base) ((base) + 0x46) +#define ENABLE_BIT BIT(7) + +#define LDO_VSET_LB_REG(base) ((base) + 0x40) + +#define LDO_STEPPER_CTL_REG(base) ((base) + 0x3b) +#define DEFAULT_VOLTAGE_STEPPER_RATE 38400 +#define STEP_RATE_MASK GENMASK(1, 0) + +struct pm8008_regulator_data { + const char *name; + const char *supply_name; + int min_uv; + int max_uv; + int min_dropout_uv; + const struct linear_range *voltage_range; +}; + +struct pm8008_regulator { + struct device *dev; + struct regmap *regmap; + struct regulator_desc rdesc; + u16 base; + int step_rate; + int voltage_selector; +}; + +static const struct linear_range nldo_ranges[] = { + REGULATOR_LINEAR_RANGE(528000, 0, 122, 8000), +}; + +static const struct linear_range pldo_ranges[] = { + REGULATOR_LINEAR_RANGE(1504000, 0, 237, 8000), +}; + +static const struct pm8008_regulator_data reg_data[] = { + /* name parent min_uv max_uv headroom_uv voltage_range */ + { "ldo1", "vdd_l1_l2", 528000, 1504000, 225000, nldo_ranges, }, + { "ldo2", "vdd_l1_l2", 528000, 1504000, 225000, nldo_ranges, }, + { "ldo3", "vdd_l3_l4", 1504000, 3400000, 300000, pldo_ranges, }, + { "ldo4", "vdd_l3_l4", 1504000, 3400000, 300000, pldo_ranges, }, + { "ldo5", "vdd_l5", 1504000, 3400000, 200000, pldo_ranges, }, + { "ldo6", "vdd_l6", 1504000, 3400000, 200000, pldo_ranges, }, + { "ldo7", "vdd_l7", 1504000, 3400000, 200000, pldo_ranges, }, +}; + +static int pm8008_regulator_get_voltage(struct regulator_dev *rdev) +{ + struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev); + + return pm8008_reg->voltage_selector; +} + +static inline int pm8008_write_voltage(struct pm8008_regulator *pm8008_reg, + int mV) +{ + __le16 vset_raw; + + vset_raw = cpu_to_le16(mV); + + return regmap_bulk_write(pm8008_reg->regmap, + LDO_VSET_LB_REG(pm8008_reg->base), + (const void *)&vset_raw, sizeof(vset_raw)); +} + +static int pm8008_regulator_set_voltage_time(struct regulator_dev *rdev, + int old_uV, int new_uv) +{ + struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev); + + return DIV_ROUND_UP(abs(new_uv - old_uV), pm8008_reg->step_rate); +} + +static int pm8008_regulator_set_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev); + int rc, mV; + + /* voltage control register is set with voltage in millivolts */ + mV = DIV_ROUND_UP(regulator_list_voltage_linear_range(rdev, selector), + 1000); + if (mV < 0) + return mV; + + rc = pm8008_write_voltage(pm8008_reg, mV); + if (rc < 0) + return rc; + + pm8008_reg->voltage_selector = selector; + dev_dbg(&rdev->dev, "voltage set to %d\n", mV * 1000); + return 0; +} + +static const struct regulator_ops pm8008_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_voltage_sel = pm8008_regulator_set_voltage, + .get_voltage_sel = pm8008_regulator_get_voltage, + .list_voltage = regulator_list_voltage_linear, + .set_voltage_time = pm8008_regulator_set_voltage_time, +}; + +static int pm8008_regulator_probe(struct platform_device *pdev) +{ + int rc, i; + u32 base; + unsigned int reg; + const char *name; + struct device *dev = &pdev->dev; + struct regulator_config reg_config = {}; + struct regulator_dev *rdev; + const struct pm8008_data *chip = dev_get_drvdata(pdev->dev.parent); + struct pm8008_regulator *pm8008_reg; + + pm8008_reg = devm_kzalloc(dev, sizeof(*pm8008_reg), GFP_KERNEL); + if (!pm8008_reg) + return -ENOMEM; + + pm8008_reg->regmap = pm8008_get_regmap(chip); + if (!pm8008_reg->regmap) { + dev_err(dev, "parent regmap is missing\n"); + return -EINVAL; + } + + pm8008_reg->dev = dev; + + rc = of_property_read_string(dev->of_node, "regulator-name", &name); + if (rc) + return rc; + + /* get the required regulator data */ + for (i = 0; i < ARRAY_SIZE(reg_data); i++) + if (strstr(name, reg_data[i].name)) + break; + + rc = of_property_read_u32_index(dev->of_node, "reg", 1, &base); + if (rc < 0) { + dev_err(dev, "%s: failed to get regulator base rc=%d\n", name, rc); + return rc; + } + pm8008_reg->base = base; + + /* get slew rate */ + rc = regmap_bulk_read(pm8008_reg->regmap, + LDO_STEPPER_CTL_REG(pm8008_reg->base), ®, 1); + if (rc < 0) { + dev_err(dev, "failed to read step rate configuration rc=%d\n", rc); + return rc; + } + reg &= STEP_RATE_MASK; + pm8008_reg->step_rate = DEFAULT_VOLTAGE_STEPPER_RATE >> reg; + + pm8008_reg->rdesc.type = REGULATOR_VOLTAGE; + pm8008_reg->rdesc.ops = &pm8008_regulator_ops; + pm8008_reg->rdesc.name = reg_data[i].name; + pm8008_reg->rdesc.supply_name = reg_data[i].supply_name; + pm8008_reg->rdesc.of_match = reg_data[i].name; + pm8008_reg->rdesc.uV_step = VSET_STEP_UV; + pm8008_reg->rdesc.min_uV = reg_data[i].min_uv; + pm8008_reg->rdesc.n_voltages + = ((reg_data[i].max_uv - reg_data[i].min_uv) + / pm8008_reg->rdesc.uV_step) + 1; + pm8008_reg->rdesc.linear_ranges = reg_data[i].voltage_range; + pm8008_reg->rdesc.n_linear_ranges = 1; + pm8008_reg->rdesc.enable_reg = LDO_ENABLE_REG(pm8008_reg->base); + pm8008_reg->rdesc.enable_mask = ENABLE_BIT; + pm8008_reg->rdesc.min_dropout_uV = reg_data[i].min_dropout_uv; + pm8008_reg->voltage_selector = -ENOTRECOVERABLE; + + reg_config.dev = dev->parent; + reg_config.driver_data = pm8008_reg; + reg_config.regmap = pm8008_reg->regmap; + + rdev = devm_regulator_register(dev, &pm8008_reg->rdesc, ®_config); + if (IS_ERR(rdev)) { + rc = PTR_ERR(rdev); + dev_err(dev, "%s: failed to register regulator rc=%d\n", + reg_data[i].name, rc); + return rc; + } + + return 0; +} + +static const struct of_device_id pm8008_regulator_match_table[] = { + { .compatible = "qcom,pm8008-regulator", }, + { } +}; +MODULE_DEVICE_TABLE(of, pm8008_regulator_match_table); + +static struct platform_driver pm8008_regulator_driver = { + .driver = { + .name = "qcom-pm8008-regulator", + .of_match_table = pm8008_regulator_match_table, + }, + .probe = pm8008_regulator_probe, +}; + +module_platform_driver(pm8008_regulator_driver); + +MODULE_DESCRIPTION("Qualcomm PM8008 PMIC Regulator Driver"); +MODULE_LICENSE("GPL");
Qualcomm Technologies, Inc. PM8008 is an I2C controlled PMIC containing 7 LDO regulators. Add a PM8008 regulator driver to support PMIC regulator management via the regulator framework. Signed-off-by: Satya Priya <quic_c_skakit@quicinc.com> --- Changes in V12: - Get base from reg property in DT node. Changes in V11: - Added of_device_id table and compatible to register the ldos. Changes in V10: - Changed the driver name. - Removed unused header. - Use get_voltage_sel. Changes in V9: - Nothing has changed. Changes in V8: - Changed the regulators_data struct name to pm8008_regulator_data drivers/regulator/Kconfig | 9 ++ drivers/regulator/Makefile | 1 + drivers/regulator/qcom-pm8008-regulator.c | 221 ++++++++++++++++++++++++++++++ 3 files changed, 231 insertions(+) create mode 100644 drivers/regulator/qcom-pm8008-regulator.c