Message ID | 1649166633-25872-5-git-send-email-quic_c_skakit@quicinc.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | Add Qualcomm Technologies, Inc. PM8008 regulator driver | expand |
Quoting Satya Priya (2022-04-05 06:50:31) > diff --git a/drivers/regulator/qcom-pm8008-regulator.c b/drivers/regulator/qcom-pm8008-regulator.c > new file mode 100644 > index 0000000..0f6d5cb > --- /dev/null > +++ b/drivers/regulator/qcom-pm8008-regulator.c > @@ -0,0 +1,205 @@ [...] > + > +static struct platform_driver pm8008_regulator_driver = { Why isn't this an i2c driver? > + .driver = { > + .name = "qcom,pm8008-regulators", > + }, > + .probe = pm8008_regulator_probe, > +};
On Tue, Apr 05, 2022 at 02:09:06PM -0500, Stephen Boyd wrote: > Quoting Satya Priya (2022-04-05 06:50:31) > > +static struct platform_driver pm8008_regulator_driver = { > Why isn't this an i2c driver? It's a MFD function driver isn't it?
On 4/5/2022 7:47 PM, Mark Brown wrote: > On Tue, Apr 05, 2022 at 07:20:31PM +0530, Satya Priya wrote: > >> +#include <linux/regulator/driver.h> >> +#include <linux/regulator/machine.h> > Why does the driver need machine.h? That's usually a bug, though I > didn't spot anywhere where it's used so it's probably just an extra > header. Yeah, I'll remove it. Thanks for spotting this. >> + .set_voltage_sel = pm8008_regulator_set_voltage, >> + .get_voltage = pm8008_regulator_get_voltage, > You shouldn't mix and match the selector and non-selector operations, > since the device just takes a voltage you may as well just use the > non-selector version for both. I was suggested to use set_voltage_sel on my previous posts. I think I'll use get_voltage_sel to avoid mixing selector and non-selector APIs. > Otherwise this all looks good, just those two minor points.
On 4/6/2022 12:39 AM, Stephen Boyd wrote: > Quoting Satya Priya (2022-04-05 06:50:31) >> + >> +static struct platform_driver pm8008_regulator_driver = { >> + .driver = { >> + .name = "qcom,pm8008-regulators", > Also, the name should be something like pm8008_regulators I've seen the other qcom regulator driver names, I think I shouldn't be using coma here. How about qcom-pm8008-regulator ? similar to the other regulator drivers
On 4/6/2022 12:39 AM, Stephen Boyd wrote: > Quoting Satya Priya (2022-04-05 06:50:31) >> diff --git a/drivers/regulator/qcom-pm8008-regulator.c b/drivers/regulator/qcom-pm8008-regulator.c >> new file mode 100644 >> index 0000000..0f6d5cb >> --- /dev/null >> +++ b/drivers/regulator/qcom-pm8008-regulator.c >> @@ -0,0 +1,205 @@ > [...] >> + >> +static struct platform_driver pm8008_regulator_driver = { > Why isn't this an i2c driver? The parent mfd driver(qcom-pm8008.c) uses i2c to communicate, this is the child to that and hence we don't use i2c driver here. >> + .driver = { >> + .name = "qcom,pm8008-regulators", >> + }, >> + .probe = pm8008_regulator_probe, >> +};
Quoting Mark Brown (2022-04-06 00:31:45) > On Tue, Apr 05, 2022 at 02:09:06PM -0500, Stephen Boyd wrote: > > Quoting Satya Priya (2022-04-05 06:50:31) > > > > +static struct platform_driver pm8008_regulator_driver = { > > > Why isn't this an i2c driver? > > It's a MFD function driver isn't it? The DT binding shows a single i2c node at i2c address 0x9. The compatible for it is "qcom,pm8008-regulators". It looks like an i2c device that is dedicated to providing regulators. I'd only expect to see an MFD if the device responding at i2c address 0x9 supported more than just regulators.
On Wed, Apr 06, 2022 at 08:23:11AM -0700, Stephen Boyd wrote: > Quoting Mark Brown (2022-04-06 00:31:45) > > It's a MFD function driver isn't it? > The DT binding shows a single i2c node at i2c address 0x9. The > compatible for it is "qcom,pm8008-regulators". It looks like an i2c > device that is dedicated to providing regulators. I'd only expect to see > an MFD if the device responding at i2c address 0x9 supported more than > just regulators. There's a MFD parent for it, and if it's for an I2C device for a pm8008 why would it have a -regulators in the name?
Quoting Mark Brown (2022-04-06 08:45:41) > On Wed, Apr 06, 2022 at 08:23:11AM -0700, Stephen Boyd wrote: > > Quoting Mark Brown (2022-04-06 00:31:45) > > > > It's a MFD function driver isn't it? > > > The DT binding shows a single i2c node at i2c address 0x9. The > > compatible for it is "qcom,pm8008-regulators". It looks like an i2c > > device that is dedicated to providing regulators. I'd only expect to see > > an MFD if the device responding at i2c address 0x9 supported more than > > just regulators. > > There's a MFD parent for it, and if it's for an I2C device for a pm8008 > why would it have a -regulators in the name? There are two i2c devices. One is pm8008 at i2c address 0x8 and one is pm8008-regulators at i2c address 0x9. Earlier revisions of this patch series were making it very confusing by redoing the pm8008 binding and adding the pm8008-regulator i2c address device to the same binding and driver. My guess is that this is one IC that responds to multiple i2c addresses. The "main" qcom,pm8008 address is 0x8 and that supports things like interrupts. Then there's an address for regulators at 0x9 which controls the handful of LDOs on the PMIC.
On Wed, Apr 06, 2022 at 08:51:48AM -0700, Stephen Boyd wrote: > Quoting Mark Brown (2022-04-06 08:45:41) > > There's a MFD parent for it, and if it's for an I2C device for a pm8008 > > why would it have a -regulators in the name? > There are two i2c devices. One is pm8008 at i2c address 0x8 and one is > pm8008-regulators at i2c address 0x9. Earlier revisions of this patch > series were making it very confusing by redoing the pm8008 binding and > adding the pm8008-regulator i2c address device to the same binding and > driver. > My guess is that this is one IC that responds to multiple i2c addresses. > The "main" qcom,pm8008 address is 0x8 and that supports things like > interrupts. Then there's an address for regulators at 0x9 which controls > the handful of LDOs on the PMIC. So it's like the TI TWL4030 and Palmas - in which case it should probably be handled similarly? Note that the original sumbission was *also* a MFD subfunction, but using a DT compatible to match the platform device - this is the first I've heard of this being a separate I2C function.
Quoting Mark Brown (2022-04-06 09:36:14) > On Wed, Apr 06, 2022 at 08:51:48AM -0700, Stephen Boyd wrote: > > Quoting Mark Brown (2022-04-06 08:45:41) > > > > There's a MFD parent for it, and if it's for an I2C device for a pm8008 > > > why would it have a -regulators in the name? > > > There are two i2c devices. One is pm8008 at i2c address 0x8 and one is > > pm8008-regulators at i2c address 0x9. Earlier revisions of this patch > > series were making it very confusing by redoing the pm8008 binding and > > adding the pm8008-regulator i2c address device to the same binding and > > driver. > > > My guess is that this is one IC that responds to multiple i2c addresses. > > The "main" qcom,pm8008 address is 0x8 and that supports things like > > interrupts. Then there's an address for regulators at 0x9 which controls > > the handful of LDOs on the PMIC. > > So it's like the TI TWL4030 and Palmas - in which case it should > probably be handled similarly? How did those work out? I wasn't involved and I don't know what you mean. Do they have multiple i2c addresses they respond to? > Note that the original sumbission was > *also* a MFD subfunction, but using a DT compatible to match the > platform device - this is the first I've heard of this being a separate > I2C function. I'm mainly looking at the dts file now. It clearly has two i2c devices at 0x8 and 0x9. Maybe the regulator driver followed the mfd design because the first driver for this device is an mfd.
On Wed, Apr 06, 2022 at 10:21:01AM -0700, Stephen Boyd wrote: > Quoting Mark Brown (2022-04-06 09:36:14) > > On Wed, Apr 06, 2022 at 08:51:48AM -0700, Stephen Boyd wrote: > > > My guess is that this is one IC that responds to multiple i2c addresses. > > > The "main" qcom,pm8008 address is 0x8 and that supports things like > > > interrupts. Then there's an address for regulators at 0x9 which controls > > > the handful of LDOs on the PMIC. > > So it's like the TI TWL4030 and Palmas - in which case it should > > probably be handled similarly? > How did those work out? I wasn't involved and I don't know what you > mean. Do they have multiple i2c addresses they respond to? Yes, exactly. The main device uses i2c_new_dummy_device() to instantiate the extras when it probes. See twl-core.c > > > Note that the original sumbission was > > *also* a MFD subfunction, but using a DT compatible to match the > > platform device - this is the first I've heard of this being a separate > > I2C function. > I'm mainly looking at the dts file now. It clearly has two i2c devices > at 0x8 and 0x9. Maybe the regulator driver followed the mfd design > because the first driver for this device is an mfd. I'm guessing from the naming that they're also externally described as the same device - presumably it's two dies shoved together in the same package for some reason without being otherwise joined up. Is the second device geniunely regulators only or does it have anything else bundled in there?
Quoting Mark Brown (2022-04-06 10:27:44) > On Wed, Apr 06, 2022 at 10:21:01AM -0700, Stephen Boyd wrote: > > Quoting Mark Brown (2022-04-06 09:36:14) > > > On Wed, Apr 06, 2022 at 08:51:48AM -0700, Stephen Boyd wrote: > > > > > My guess is that this is one IC that responds to multiple i2c addresses. > > > > The "main" qcom,pm8008 address is 0x8 and that supports things like > > > > interrupts. Then there's an address for regulators at 0x9 which controls > > > > the handful of LDOs on the PMIC. > > > > So it's like the TI TWL4030 and Palmas - in which case it should > > > probably be handled similarly? > > > How did those work out? I wasn't involved and I don't know what you > > mean. Do they have multiple i2c addresses they respond to? > > Yes, exactly. The main device uses i2c_new_dummy_device() to > instantiate the extras when it probes. See twl-core.c Cool. That approach sounds good to me. Then the regulators can be child nodes of the qcom,pm8008 node at i2c address 0x8? It still feels like making a struct driver for each regulator node is overkill and will waste memory. > > > > > > Note that the original sumbission was > > > *also* a MFD subfunction, but using a DT compatible to match the > > > platform device - this is the first I've heard of this being a separate > > > I2C function. > > > I'm mainly looking at the dts file now. It clearly has two i2c devices > > at 0x8 and 0x9. Maybe the regulator driver followed the mfd design > > because the first driver for this device is an mfd. > > I'm guessing from the naming that they're also externally described as > the same device - presumably it's two dies shoved together in the same > package for some reason without being otherwise joined up. Is the > second device geniunely regulators only or does it have anything else > bundled in there? I think it's regulators only. Pretty sure I asked qcom this a round or two ago on this patch series and they said that. Let's wait for Satya to respond.
On 4/6/2022 11:46 PM, Stephen Boyd wrote: > Quoting Mark Brown (2022-04-06 10:27:44) >> On Wed, Apr 06, 2022 at 10:21:01AM -0700, Stephen Boyd wrote: >>> Quoting Mark Brown (2022-04-06 09:36:14) >>>> On Wed, Apr 06, 2022 at 08:51:48AM -0700, Stephen Boyd wrote: >>>>> My guess is that this is one IC that responds to multiple i2c addresses. >>>>> The "main" qcom,pm8008 address is 0x8 and that supports things like >>>>> interrupts. Then there's an address for regulators at 0x9 which controls >>>>> the handful of LDOs on the PMIC. >>>> So it's like the TI TWL4030 and Palmas - in which case it should >>>> probably be handled similarly? >>> How did those work out? I wasn't involved and I don't know what you >>> mean. Do they have multiple i2c addresses they respond to? >> Yes, exactly. The main device uses i2c_new_dummy_device() to >> instantiate the extras when it probes. See twl-core.c > Cool. That approach sounds good to me. Then the regulators can be child > nodes of the qcom,pm8008 node at i2c address 0x8? It still feels like > making a struct driver for each regulator node is overkill and will > waste memory. > >>>> Note that the original sumbission was >>>> *also* a MFD subfunction, but using a DT compatible to match the >>>> platform device - this is the first I've heard of this being a separate >>>> I2C function. >>> I'm mainly looking at the dts file now. It clearly has two i2c devices >>> at 0x8 and 0x9. Maybe the regulator driver followed the mfd design >>> because the first driver for this device is an mfd. >> I'm guessing from the naming that they're also externally described as >> the same device - presumably it's two dies shoved together in the same >> package for some reason without being otherwise joined up. Is the >> second device geniunely regulators only or does it have anything else >> bundled in there? > I think it's regulators only. Pretty sure I asked qcom this a round or > two ago on this patch series and they said that. Let's wait for Satya to > respond. Yes, it contains regulators only.
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 5ef2306..06b0a19 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 1b64ad5..83eed71 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..0f6d5cb --- /dev/null +++ b/drivers/regulator/qcom-pm8008-regulator.c @@ -0,0 +1,205 @@ +// 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/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> + +#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; + u16 base; + 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; +}; + +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 base min_uv max_uv headroom_uv voltage_range */ + { "ldo1", "vdd_l1_l2", 0x4000, 528000, 1504000, 225000, nldo_ranges, }, + { "ldo2", "vdd_l1_l2", 0x4100, 528000, 1504000, 225000, nldo_ranges, }, + { "ldo3", "vdd_l3_l4", 0x4200, 1504000, 3400000, 300000, pldo_ranges, }, + { "ldo4", "vdd_l3_l4", 0x4300, 1504000, 3400000, 300000, pldo_ranges, }, + { "ldo5", "vdd_l5", 0x4400, 1504000, 3400000, 200000, pldo_ranges, }, + { "ldo6", "vdd_l6", 0x4500, 1504000, 3400000, 200000, pldo_ranges, }, + { "ldo7", "vdd_l7", 0x4600, 1504000, 3400000, 200000, pldo_ranges, }, +}; + +static int pm8008_regulator_get_voltage(struct regulator_dev *rdev) +{ + struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev); + __le16 mV; + int rc; + + rc = regmap_bulk_read(pm8008_reg->regmap, + LDO_VSET_LB_REG(pm8008_reg->base), (void *)&mV, 2); + if (rc < 0) { + dev_err(&rdev->dev, "failed to read regulator voltage rc=%d\n", rc); + return rc; + } + + return le16_to_cpu(mV) * 1000; +} + +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; + + 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 = 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) +{ + struct device *dev = &pdev->dev; + struct regulator_dev *rdev; + struct pm8008_regulator *pm8008_reg; + struct regmap *regmap; + struct regulator_config reg_config = {}; + int rc, i; + unsigned int reg; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) { + dev_err(dev, "parent regmap is missing\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(reg_data); i++) { + pm8008_reg = devm_kzalloc(dev, sizeof(*pm8008_reg), GFP_KERNEL); + if (!pm8008_reg) + return -ENOMEM; + + pm8008_reg->regmap = regmap; + pm8008_reg->dev = dev; + pm8008_reg->base = reg_data[i].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; + + reg_config.dev = dev->parent; + reg_config.driver_data = pm8008_reg; + + 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 struct platform_driver pm8008_regulator_driver = { + .driver = { + .name = "qcom,pm8008-regulators", + }, + .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 V7: - Removed unused Macros and headers. Changes in V8: - Changed the regulators_data struct name to pm8008_regulator_data Changes in V9: - Nothing has changed. drivers/regulator/Kconfig | 9 ++ drivers/regulator/Makefile | 1 + drivers/regulator/qcom-pm8008-regulator.c | 205 ++++++++++++++++++++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 drivers/regulator/qcom-pm8008-regulator.c