diff options
-rw-r--r-- | drivers/regulator/Kconfig | 7 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 1 | ||||
-rw-r--r-- | drivers/regulator/qcom-pm8008-regulator.c | 198 |
3 files changed, 206 insertions, 0 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index d333be2bea3b..17982e1cbf4d 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1027,6 +1027,13 @@ 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 PM8008 PMIC regulators" + depends on MFD_QCOM_PM8008 + help + Select this option to enable support for the voltage regulators in + Qualcomm PM8008 PMICs. + config REGULATOR_QCOM_REFGEN tristate "Qualcomm REFGEN regulator driver" depends on ARCH_QCOM || COMPILE_TEST diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index ba15fa5f30ad..ca4d09c60867 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -112,6 +112,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_REFGEN) += qcom-refgen-regulator.o obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o diff --git a/drivers/regulator/qcom-pm8008-regulator.c b/drivers/regulator/qcom-pm8008-regulator.c new file mode 100644 index 000000000000..da017c1969d0 --- /dev/null +++ b/drivers/regulator/qcom-pm8008-regulator.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2024 Linaro Limited + */ + +#include <linux/array_size.h> +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/math.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> + +#include <asm/byteorder.h> + +#define DEFAULT_VOLTAGE_STEPPER_RATE 38400 + +#define LDO_STEPPER_CTL_REG 0x3b +#define STEP_RATE_MASK GENMASK(1, 0) + +#define LDO_VSET_LB_REG 0x40 + +#define LDO_ENABLE_REG 0x46 +#define ENABLE_BIT BIT(7) + +struct pm8008_regulator { + struct regmap *regmap; + struct regulator_desc desc; + unsigned int base; +}; + +struct pm8008_regulator_data { + const char *name; + const char *supply_name; + unsigned int base; + int min_dropout_uV; + const struct linear_range *voltage_range; +}; + +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 pm8008_reg_data[] = { + { "ldo1", "vdd-l1-l2", 0x4000, 225000, nldo_ranges, }, + { "ldo2", "vdd-l1-l2", 0x4100, 225000, nldo_ranges, }, + { "ldo3", "vdd-l3-l4", 0x4200, 300000, pldo_ranges, }, + { "ldo4", "vdd-l3-l4", 0x4300, 300000, pldo_ranges, }, + { "ldo5", "vdd-l5", 0x4400, 200000, pldo_ranges, }, + { "ldo6", "vdd-l6", 0x4500, 200000, pldo_ranges, }, + { "ldo7", "vdd-l7", 0x4600, 200000, pldo_ranges, }, +}; + +static int pm8008_regulator_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel) +{ + struct pm8008_regulator *preg = rdev_get_drvdata(rdev); + unsigned int mV; + __le16 val; + int ret; + + ret = regulator_list_voltage_linear_range(rdev, sel); + if (ret < 0) + return ret; + + mV = DIV_ROUND_UP(ret, 1000); + + val = cpu_to_le16(mV); + + ret = regmap_bulk_write(preg->regmap, preg->base + LDO_VSET_LB_REG, + &val, sizeof(val)); + if (ret < 0) + return ret; + + return 0; +} + +static int pm8008_regulator_get_voltage_sel(struct regulator_dev *rdev) +{ + struct pm8008_regulator *preg = rdev_get_drvdata(rdev); + unsigned int uV; + __le16 val; + int ret; + + ret = regmap_bulk_read(preg->regmap, preg->base + LDO_VSET_LB_REG, + &val, sizeof(val)); + if (ret < 0) + return ret; + + uV = le16_to_cpu(val) * 1000; + + return (uV - preg->desc.min_uV) / preg->desc.uV_step; +} + +static const struct regulator_ops pm8008_regulator_ops = { + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = pm8008_regulator_set_voltage_sel, + .get_voltage_sel = pm8008_regulator_get_voltage_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static int pm8008_regulator_probe(struct platform_device *pdev) +{ + const struct pm8008_regulator_data *data; + struct regulator_config config = {}; + struct device *dev = &pdev->dev; + struct pm8008_regulator *preg; + struct regulator_desc *desc; + struct regulator_dev *rdev; + struct regmap *regmap; + unsigned int val; + int ret, i; + + regmap = dev_get_regmap(dev->parent, "secondary"); + if (!regmap) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(pm8008_reg_data); i++) { + data = &pm8008_reg_data[i]; + + preg = devm_kzalloc(dev, sizeof(*preg), GFP_KERNEL); + if (!preg) + return -ENOMEM; + + preg->regmap = regmap; + preg->base = data->base; + + desc = &preg->desc; + + desc->name = data->name; + desc->supply_name = data->supply_name; + desc->of_match = data->name; + desc->regulators_node = of_match_ptr("regulators"); + desc->ops = &pm8008_regulator_ops; + desc->type = REGULATOR_VOLTAGE; + desc->owner = THIS_MODULE; + + desc->linear_ranges = data->voltage_range; + desc->n_linear_ranges = 1; + desc->uV_step = desc->linear_ranges[0].step; + desc->min_uV = desc->linear_ranges[0].min; + desc->n_voltages = linear_range_values_in_range(&desc->linear_ranges[0]); + + ret = regmap_read(regmap, preg->base + LDO_STEPPER_CTL_REG, &val); + if (ret < 0) { + dev_err(dev, "failed to read step rate: %d\n", ret); + return ret; + } + val &= STEP_RATE_MASK; + desc->ramp_delay = DEFAULT_VOLTAGE_STEPPER_RATE >> val; + + desc->min_dropout_uV = data->min_dropout_uV; + + desc->enable_reg = preg->base + LDO_ENABLE_REG; + desc->enable_mask = ENABLE_BIT; + + config.dev = dev->parent; + config.driver_data = preg; + config.regmap = regmap; + + rdev = devm_regulator_register(dev, desc, &config); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(dev, "failed to register regulator %s: %d\n", + desc->name, ret); + return ret; + } + } + + return 0; +} + +static const struct platform_device_id pm8008_regulator_id_table[] = { + { "pm8008-regulator" }, + { } +}; +MODULE_DEVICE_TABLE(platform, pm8008_regulator_id_table); + +static struct platform_driver pm8008_regulator_driver = { + .driver = { + .name = "qcom-pm8008-regulator", + }, + .probe = pm8008_regulator_probe, + .id_table = pm8008_regulator_id_table, +}; +module_platform_driver(pm8008_regulator_driver); + +MODULE_DESCRIPTION("Qualcomm PM8008 PMIC regulator driver"); +MODULE_LICENSE("GPL"); |