diff options
Diffstat (limited to 'drivers/regulator')
-rw-r--r-- | drivers/regulator/Kconfig | 14 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 2 | ||||
-rw-r--r-- | drivers/regulator/ad5398.c | 30 | ||||
-rw-r--r-- | drivers/regulator/axp20x-regulator.c | 14 | ||||
-rw-r--r-- | drivers/regulator/cros-ec-regulator.c | 4 | ||||
-rw-r--r-- | drivers/regulator/devres.c | 22 | ||||
-rw-r--r-- | drivers/regulator/of_regulator.c | 21 | ||||
-rw-r--r-- | drivers/regulator/pca9450-regulator.c | 91 | ||||
-rw-r--r-- | drivers/regulator/pcf50633-regulator.c | 124 | ||||
-rw-r--r-- | drivers/regulator/pf9453-regulator.c | 879 | ||||
-rw-r--r-- | drivers/regulator/rtq6752-regulator.c | 2 |
11 files changed, 1030 insertions, 173 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 39297f7d8177..807d07067951 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -981,6 +981,13 @@ config REGULATOR_PCA9450 Say y here to support the NXP PCA9450A/PCA9450B/PCA9450C PMIC regulator driver. +config REGULATOR_PF9453 + tristate "NXP PF9453 regulator driver" + depends on I2C + select REGMAP_I2C + help + Say y here to support the NXP PF9453 PMIC regulator driver. + config REGULATOR_PCAP tristate "Motorola PCAP2 regulator driver" depends on EZX_PCAP @@ -988,13 +995,6 @@ config REGULATOR_PCAP This driver provides support for the voltage regulators of the PCAP2 PMIC. -config REGULATOR_PCF50633 - tristate "NXP PCF50633 regulator driver" - depends on MFD_PCF50633 - help - Say Y here to support the voltage regulators and converters - on PCF50633 - config REGULATOR_PF8X00 tristate "NXP PF8100/PF8121A/PF8200 regulator driver" depends on I2C && OF diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 3d5a803dce8a..524e026c0273 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -123,6 +123,7 @@ obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o obj-$(CONFIG_REGULATOR_QCOM_USB_VBUS) += qcom_usb_vbus-regulator.o obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o obj-$(CONFIG_REGULATOR_PCA9450) += pca9450-regulator.o +obj-$(CONFIG_REGULATOR_PF9453) += pf9453-regulator.o obj-$(CONFIG_REGULATOR_PF8X00) += pf8x00-regulator.o obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o obj-$(CONFIG_REGULATOR_PV88060) += pv88060-regulator.o @@ -132,7 +133,6 @@ obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o obj-$(CONFIG_REGULATOR_PBIAS) += pbias-regulator.o obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o -obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o obj-$(CONFIG_REGULATOR_RAA215300) += raa215300.o obj-$(CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY) += rpi-panel-attiny-regulator.o obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o diff --git a/drivers/regulator/ad5398.c b/drivers/regulator/ad5398.c index 40f7dba42b5a..eb2a666a45cb 100644 --- a/drivers/regulator/ad5398.c +++ b/drivers/regulator/ad5398.c @@ -14,8 +14,9 @@ #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> -#define AD5398_CURRENT_EN_MASK 0x8000 +#define AD5398_SW_POWER_DOWN BIT(15) struct ad5398_chip_info { struct i2c_client *client; @@ -113,7 +114,7 @@ static int ad5398_set_current_limit(struct regulator_dev *rdev, int min_uA, int /* prepare register data */ selector = (selector << chip->current_offset) & chip->current_mask; - data = (unsigned short)selector | (data & AD5398_CURRENT_EN_MASK); + data = (unsigned short)selector | (data & AD5398_SW_POWER_DOWN); /* write the new current value back as well as enable bit */ ret = ad5398_write_reg(client, data); @@ -132,10 +133,10 @@ static int ad5398_is_enabled(struct regulator_dev *rdev) if (ret < 0) return ret; - if (data & AD5398_CURRENT_EN_MASK) - return 1; - else + if (data & AD5398_SW_POWER_DOWN) return 0; + else + return 1; } static int ad5398_enable(struct regulator_dev *rdev) @@ -149,10 +150,10 @@ static int ad5398_enable(struct regulator_dev *rdev) if (ret < 0) return ret; - if (data & AD5398_CURRENT_EN_MASK) + if (!(data & AD5398_SW_POWER_DOWN)) return 0; - data |= AD5398_CURRENT_EN_MASK; + data &= ~AD5398_SW_POWER_DOWN; ret = ad5398_write_reg(client, data); @@ -170,10 +171,10 @@ static int ad5398_disable(struct regulator_dev *rdev) if (ret < 0) return ret; - if (!(data & AD5398_CURRENT_EN_MASK)) + if (data & AD5398_SW_POWER_DOWN) return 0; - data &= ~AD5398_CURRENT_EN_MASK; + data |= AD5398_SW_POWER_DOWN; ret = ad5398_write_reg(client, data); @@ -221,15 +222,20 @@ static int ad5398_probe(struct i2c_client *client) const struct ad5398_current_data_format *df = (struct ad5398_current_data_format *)id->driver_data; - if (!init_data) - return -EINVAL; - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; config.dev = &client->dev; + if (client->dev.of_node) + init_data = of_get_regulator_init_data(&client->dev, + client->dev.of_node, + &ad5398_reg); + if (!init_data) + return -EINVAL; + config.init_data = init_data; + config.of_node = client->dev.of_node; config.driver_data = chip; chip->client = client; diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index dca99cfb7cbb..da891415efc0 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -371,8 +371,8 @@ .ops = &axp20x_ops, \ } -#define AXP_DESC_DELAY(_family, _id, _match, _supply, _min, _max, _step, _vreg, \ - _vmask, _ereg, _emask, _ramp_delay) \ +#define AXP_DESC(_family, _id, _match, _supply, _min, _max, _step, _vreg, \ + _vmask, _ereg, _emask) \ [_family##_##_id] = { \ .name = (_match), \ .supply_name = (_supply), \ @@ -388,15 +388,9 @@ .vsel_mask = (_vmask), \ .enable_reg = (_ereg), \ .enable_mask = (_emask), \ - .ramp_delay = (_ramp_delay), \ .ops = &axp20x_ops, \ } -#define AXP_DESC(_family, _id, _match, _supply, _min, _max, _step, _vreg, \ - _vmask, _ereg, _emask) \ - AXP_DESC_DELAY(_family, _id, _match, _supply, _min, _max, _step, _vreg, \ - _vmask, _ereg, _emask, 0) - #define AXP_DESC_SW(_family, _id, _match, _supply, _ereg, _emask) \ [_family##_##_id] = { \ .name = (_match), \ @@ -805,9 +799,9 @@ static const struct regulator_desc axp717_regulators[] = { axp717_dcdc3_ranges, AXP717_DCDC3_NUM_VOLTAGES, AXP717_DCDC3_CONTROL, AXP717_DCDC_V_OUT_MASK, AXP717_DCDC_OUTPUT_CONTROL, BIT(2), 640), - AXP_DESC_DELAY(AXP717, DCDC4, "dcdc4", "vin4", 1000, 3700, 100, + AXP_DESC(AXP717, DCDC4, "dcdc4", "vin4", 1000, 3700, 100, AXP717_DCDC4_CONTROL, AXP717_DCDC_V_OUT_MASK, - AXP717_DCDC_OUTPUT_CONTROL, BIT(3), 6400), + AXP717_DCDC_OUTPUT_CONTROL, BIT(3)), AXP_DESC(AXP717, ALDO1, "aldo1", "aldoin", 500, 3500, 100, AXP717_ALDO1_CONTROL, AXP717_LDO_V_OUT_MASK, AXP717_LDO0_OUTPUT_CONTROL, BIT(0)), diff --git a/drivers/regulator/cros-ec-regulator.c b/drivers/regulator/cros-ec-regulator.c index fb9643ed7a49..fb0767b33a36 100644 --- a/drivers/regulator/cros-ec-regulator.c +++ b/drivers/regulator/cros-ec-regulator.c @@ -138,8 +138,8 @@ static int cros_ec_regulator_init_info(struct device *dev, data->num_voltages = min_t(u16, ARRAY_SIZE(resp.voltages_mv), resp.num_voltages); data->voltages_mV = - devm_kmemdup(dev, resp.voltages_mv, - sizeof(u16) * data->num_voltages, GFP_KERNEL); + devm_kmemdup_array(dev, resp.voltages_mv, data->num_voltages, + sizeof(resp.voltages_mv[0]), GFP_KERNEL); if (!data->voltages_mV) return -ENOMEM; diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c index 36164aec30e8..2cf03042fddf 100644 --- a/drivers/regulator/devres.c +++ b/drivers/regulator/devres.c @@ -332,9 +332,8 @@ int devm_regulator_bulk_get_const(struct device *dev, int num_consumers, const struct regulator_bulk_data *in_consumers, struct regulator_bulk_data **out_consumers) { - *out_consumers = devm_kmemdup(dev, in_consumers, - num_consumers * sizeof(*in_consumers), - GFP_KERNEL); + *out_consumers = devm_kmemdup_array(dev, in_consumers, num_consumers, + sizeof(*in_consumers), GFP_KERNEL); if (*out_consumers == NULL) return -ENOMEM; @@ -772,6 +771,23 @@ static struct regulator *_devm_of_regulator_get(struct device *dev, struct devic } /** + * devm_of_regulator_get - Resource managed of_regulator_get() + * @dev: device used for dev_printk() messages and resource lifetime management + * @node: device node for regulator "consumer" + * @id: supply name or regulator ID. + * + * Managed of_regulator_get(). Regulators returned from this + * function are automatically regulator_put() on driver detach. See + * of_regulator_get() for more information. + */ +struct regulator *devm_of_regulator_get(struct device *dev, struct device_node *node, + const char *id) +{ + return _devm_of_regulator_get(dev, node, id, NORMAL_GET); +} +EXPORT_SYMBOL_GPL(devm_of_regulator_get); + +/** * devm_of_regulator_get_optional - Resource managed of_regulator_get_optional() * @dev: device used for dev_printk() messages and resource lifetime management * @node: device node for regulator "consumer" diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 011088c57891..32e88cada47a 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -698,6 +698,27 @@ struct regulator *_of_regulator_get(struct device *dev, struct device_node *node } /** + * of_regulator_get - get regulator via device tree lookup + * @dev: device used for dev_printk() messages + * @node: device node for regulator "consumer" + * @id: Supply name + * + * Return: pointer to struct regulator corresponding to the regulator producer, + * or PTR_ERR() encoded error number. + * + * This is intended for use by consumers that want to get a regulator + * supply directly from a device node. This will _not_ consider supply + * aliases. See regulator_dev_lookup(). + */ +struct regulator *of_regulator_get(struct device *dev, + struct device_node *node, + const char *id) +{ + return _of_regulator_get(dev, node, id, NORMAL_GET); +} +EXPORT_SYMBOL_GPL(of_regulator_get); + +/** * of_regulator_get_optional - get optional regulator via device tree lookup * @dev: device used for dev_printk() messages * @node: device node for regulator "consumer" diff --git a/drivers/regulator/pca9450-regulator.c b/drivers/regulator/pca9450-regulator.c index faa6b79c27d7..a56f3ab754fa 100644 --- a/drivers/regulator/pca9450-regulator.c +++ b/drivers/regulator/pca9450-regulator.c @@ -36,6 +36,7 @@ struct pca9450 { enum pca9450_chip_type type; unsigned int rcnt; int irq; + bool sd_vsel_fixed_low; }; static const struct regmap_range pca9450_status_range = { @@ -98,6 +99,61 @@ static const struct regulator_ops pca9450_ldo_regulator_ops = { .get_voltage_sel = regulator_get_voltage_sel_regmap, }; +static unsigned int pca9450_ldo5_get_reg_voltage_sel(struct regulator_dev *rdev) +{ + struct pca9450 *pca9450 = rdev_get_drvdata(rdev); + + if (pca9450->sd_vsel_fixed_low) + return PCA9450_REG_LDO5CTRL_L; + + if (pca9450->sd_vsel_gpio && !gpiod_get_value(pca9450->sd_vsel_gpio)) + return PCA9450_REG_LDO5CTRL_L; + + return rdev->desc->vsel_reg; +} + +static int pca9450_ldo5_get_voltage_sel_regmap(struct regulator_dev *rdev) +{ + unsigned int val; + int ret; + + ret = regmap_read(rdev->regmap, pca9450_ldo5_get_reg_voltage_sel(rdev), &val); + if (ret != 0) + return ret; + + val &= rdev->desc->vsel_mask; + val >>= ffs(rdev->desc->vsel_mask) - 1; + + return val; +} + +static int pca9450_ldo5_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned int sel) +{ + int ret; + + sel <<= ffs(rdev->desc->vsel_mask) - 1; + + ret = regmap_update_bits(rdev->regmap, pca9450_ldo5_get_reg_voltage_sel(rdev), + rdev->desc->vsel_mask, sel); + if (ret) + return ret; + + if (rdev->desc->apply_bit) + ret = regmap_update_bits(rdev->regmap, rdev->desc->apply_reg, + rdev->desc->apply_bit, + rdev->desc->apply_bit); + return ret; +} + +static const struct regulator_ops pca9450_ldo5_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage_sel = pca9450_ldo5_set_voltage_sel_regmap, + .get_voltage_sel = pca9450_ldo5_get_voltage_sel_regmap, +}; + /* * BUCK1/2/3 * 0.60 to 2.1875V (12.5mV step) @@ -453,14 +509,14 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .of_match = of_match_ptr("LDO5"), .regulators_node = of_match_ptr("regulators"), .id = PCA9450_LDO5, - .ops = &pca9450_ldo_regulator_ops, + .ops = &pca9450_ldo5_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = PCA9450_LDO5_VOLTAGE_NUM, .linear_ranges = pca9450_ldo5_volts, .n_linear_ranges = ARRAY_SIZE(pca9450_ldo5_volts), .vsel_reg = PCA9450_REG_LDO5CTRL_H, .vsel_mask = LDO5HOUT_MASK, - .enable_reg = PCA9450_REG_LDO5CTRL_H, + .enable_reg = PCA9450_REG_LDO5CTRL_L, .enable_mask = LDO5H_EN_MASK, .owner = THIS_MODULE, }, @@ -667,14 +723,14 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { .of_match = of_match_ptr("LDO5"), .regulators_node = of_match_ptr("regulators"), .id = PCA9450_LDO5, - .ops = &pca9450_ldo_regulator_ops, + .ops = &pca9450_ldo5_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = PCA9450_LDO5_VOLTAGE_NUM, .linear_ranges = pca9450_ldo5_volts, .n_linear_ranges = ARRAY_SIZE(pca9450_ldo5_volts), .vsel_reg = PCA9450_REG_LDO5CTRL_H, .vsel_mask = LDO5HOUT_MASK, - .enable_reg = PCA9450_REG_LDO5CTRL_H, + .enable_reg = PCA9450_REG_LDO5CTRL_L, .enable_mask = LDO5H_EN_MASK, .owner = THIS_MODULE, }, @@ -857,14 +913,14 @@ static const struct pca9450_regulator_desc pca9451a_regulators[] = { .of_match = of_match_ptr("LDO5"), .regulators_node = of_match_ptr("regulators"), .id = PCA9450_LDO5, - .ops = &pca9450_ldo_regulator_ops, + .ops = &pca9450_ldo5_regulator_ops, .type = REGULATOR_VOLTAGE, .n_voltages = PCA9450_LDO5_VOLTAGE_NUM, .linear_ranges = pca9450_ldo5_volts, .n_linear_ranges = ARRAY_SIZE(pca9450_ldo5_volts), .vsel_reg = PCA9450_REG_LDO5CTRL_H, .vsel_mask = LDO5HOUT_MASK, - .enable_reg = PCA9450_REG_LDO5CTRL_H, + .enable_reg = PCA9450_REG_LDO5CTRL_L, .enable_mask = LDO5H_EN_MASK, .owner = THIS_MODULE, }, @@ -915,6 +971,7 @@ static int pca9450_i2c_probe(struct i2c_client *i2c) of_device_get_match_data(&i2c->dev); const struct pca9450_regulator_desc *regulator_desc; struct regulator_config config = { }; + struct regulator_dev *ldo5; struct pca9450 *pca9450; unsigned int device_id, i; unsigned int reset_ctrl; @@ -980,11 +1037,15 @@ static int pca9450_i2c_probe(struct i2c_client *i2c) config.regmap = pca9450->regmap; config.dev = pca9450->dev; + config.driver_data = pca9450; rdev = devm_regulator_register(pca9450->dev, desc, &config); if (IS_ERR(rdev)) return dev_err_probe(pca9450->dev, PTR_ERR(rdev), "Failed to register regulator(%s)\n", desc->name); + + if (!strcmp(desc->name, "ldo5")) + ldo5 = rdev; } if (pca9450->irq) { @@ -1032,15 +1093,19 @@ static int pca9450_i2c_probe(struct i2c_client *i2c) } /* - * The driver uses the LDO5CTRL_H register to control the LDO5 regulator. - * This is only valid if the SD_VSEL input of the PMIC is high. Let's - * check if the pin is available as GPIO and set it to high. + * For LDO5 we need to be able to check the status of the SD_VSEL input in + * order to know which control register is used. Most boards connect SD_VSEL + * to the VSELECT signal, so we can use the GPIO that is internally routed + * to this signal (if SION bit is set in IOMUX). */ - pca9450->sd_vsel_gpio = gpiod_get_optional(pca9450->dev, "sd-vsel", GPIOD_OUT_HIGH); + pca9450->sd_vsel_gpio = gpiod_get_optional(&ldo5->dev, "sd-vsel", GPIOD_IN); + if (IS_ERR(pca9450->sd_vsel_gpio)) { + dev_err(&i2c->dev, "Failed to get SD_VSEL GPIO\n"); + return ret; + } - if (IS_ERR(pca9450->sd_vsel_gpio)) - return dev_err_probe(&i2c->dev, PTR_ERR(pca9450->sd_vsel_gpio), - "Failed to get SD_VSEL GPIO\n"); + pca9450->sd_vsel_fixed_low = + of_property_read_bool(ldo5->dev.of_node, "nxp,sd-vsel-fixed-low"); dev_info(&i2c->dev, "%s probed.\n", type == PCA9450_TYPE_PCA9450A ? "pca9450a" : diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c deleted file mode 100644 index 9f08a62c800e..000000000000 --- a/drivers/regulator/pcf50633-regulator.c +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* NXP PCF50633 PMIC Driver - * - * (C) 2006-2008 by Openmoko, Inc. - * Author: Balaji Rao <balajirrao@openmoko.org> - * All rights reserved. - * - * Broken down from monstrous PCF50633 driver mainly by - * Harald Welte and Andy Green and Werner Almesberger - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/device.h> -#include <linux/err.h> -#include <linux/platform_device.h> - -#include <linux/mfd/pcf50633/core.h> -#include <linux/mfd/pcf50633/pmic.h> - -#define PCF50633_REGULATOR(_name, _id, _min_uV, _uV_step, _min_sel, _n) \ - { \ - .name = _name, \ - .id = PCF50633_REGULATOR_##_id, \ - .ops = &pcf50633_regulator_ops, \ - .n_voltages = _n, \ - .min_uV = _min_uV, \ - .uV_step = _uV_step, \ - .linear_min_sel = _min_sel, \ - .type = REGULATOR_VOLTAGE, \ - .owner = THIS_MODULE, \ - .vsel_reg = PCF50633_REG_##_id##OUT, \ - .vsel_mask = 0xff, \ - .enable_reg = PCF50633_REG_##_id##OUT + 1, \ - .enable_mask = PCF50633_REGULATOR_ON, \ - } - -static const struct regulator_ops pcf50633_regulator_ops = { - .set_voltage_sel = regulator_set_voltage_sel_regmap, - .get_voltage_sel = regulator_get_voltage_sel_regmap, - .list_voltage = regulator_list_voltage_linear, - .map_voltage = regulator_map_voltage_linear, - .enable = regulator_enable_regmap, - .disable = regulator_disable_regmap, - .is_enabled = regulator_is_enabled_regmap, -}; - -static const struct regulator_desc regulators[] = { - [PCF50633_REGULATOR_AUTO] = - PCF50633_REGULATOR("auto", AUTO, 1800000, 25000, 0x2f, 128), - [PCF50633_REGULATOR_DOWN1] = - PCF50633_REGULATOR("down1", DOWN1, 625000, 25000, 0, 96), - [PCF50633_REGULATOR_DOWN2] = - PCF50633_REGULATOR("down2", DOWN2, 625000, 25000, 0, 96), - [PCF50633_REGULATOR_LDO1] = - PCF50633_REGULATOR("ldo1", LDO1, 900000, 100000, 0, 28), - [PCF50633_REGULATOR_LDO2] = - PCF50633_REGULATOR("ldo2", LDO2, 900000, 100000, 0, 28), - [PCF50633_REGULATOR_LDO3] = - PCF50633_REGULATOR("ldo3", LDO3, 900000, 100000, 0, 28), - [PCF50633_REGULATOR_LDO4] = - PCF50633_REGULATOR("ldo4", LDO4, 900000, 100000, 0, 28), - [PCF50633_REGULATOR_LDO5] = - PCF50633_REGULATOR("ldo5", LDO5, 900000, 100000, 0, 28), - [PCF50633_REGULATOR_LDO6] = - PCF50633_REGULATOR("ldo6", LDO6, 900000, 100000, 0, 28), - [PCF50633_REGULATOR_HCLDO] = - PCF50633_REGULATOR("hcldo", HCLDO, 900000, 100000, 0, 28), - [PCF50633_REGULATOR_MEMLDO] = - PCF50633_REGULATOR("memldo", MEMLDO, 900000, 100000, 0, 28), -}; - -static int pcf50633_regulator_probe(struct platform_device *pdev) -{ - struct regulator_dev *rdev; - struct pcf50633 *pcf; - struct regulator_config config = { }; - - /* Already set by core driver */ - pcf = dev_to_pcf50633(pdev->dev.parent); - - config.dev = &pdev->dev; - config.init_data = dev_get_platdata(&pdev->dev); - config.driver_data = pcf; - config.regmap = pcf->regmap; - - rdev = devm_regulator_register(&pdev->dev, ®ulators[pdev->id], - &config); - if (IS_ERR(rdev)) - return PTR_ERR(rdev); - - platform_set_drvdata(pdev, rdev); - - if (pcf->pdata->regulator_registered) - pcf->pdata->regulator_registered(pcf, pdev->id); - - return 0; -} - -static struct platform_driver pcf50633_regulator_driver = { - .driver = { - .name = "pcf50633-regulator", - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - }, - .probe = pcf50633_regulator_probe, -}; - -static int __init pcf50633_regulator_init(void) -{ - return platform_driver_register(&pcf50633_regulator_driver); -} -subsys_initcall(pcf50633_regulator_init); - -static void __exit pcf50633_regulator_exit(void) -{ - platform_driver_unregister(&pcf50633_regulator_driver); -} -module_exit(pcf50633_regulator_exit); - -MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>"); -MODULE_DESCRIPTION("PCF50633 regulator driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:pcf50633-regulator"); diff --git a/drivers/regulator/pf9453-regulator.c b/drivers/regulator/pf9453-regulator.c new file mode 100644 index 000000000000..ed6bf0f6c4fe --- /dev/null +++ b/drivers/regulator/pf9453-regulator.c @@ -0,0 +1,879 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2024 NXP. + * NXP PF9453 pmic driver + */ + +#include <linux/bits.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> + +struct pf9453_dvs_config { + unsigned int run_reg; /* dvs0 */ + unsigned int run_mask; + unsigned int standby_reg; /* dvs1 */ + unsigned int standby_mask; +}; + +struct pf9453_regulator_desc { + struct regulator_desc desc; + const struct pf9453_dvs_config dvs; +}; + +struct pf9453 { + struct device *dev; + struct regmap *regmap; + struct gpio_desc *sd_vsel_gpio; + int irq; +}; + +enum { + PF9453_BUCK1 = 0, + PF9453_BUCK2, + PF9453_BUCK3, + PF9453_BUCK4, + PF9453_LDO1, + PF9453_LDO2, + PF9453_LDOSNVS, + PF9453_REGULATOR_CNT +}; + +enum { + PF9453_DVS_LEVEL_RUN = 0, + PF9453_DVS_LEVEL_STANDBY, + PF9453_DVS_LEVEL_DPSTANDBY, + PF9453_DVS_LEVEL_MAX +}; + +#define PF9453_BUCK1_VOLTAGE_NUM 0x80 +#define PF9453_BUCK2_VOLTAGE_NUM 0x80 +#define PF9453_BUCK3_VOLTAGE_NUM 0x80 +#define PF9453_BUCK4_VOLTAGE_NUM 0x80 + +#define PF9453_LDO1_VOLTAGE_NUM 0x65 +#define PF9453_LDO2_VOLTAGE_NUM 0x3b +#define PF9453_LDOSNVS_VOLTAGE_NUM 0x59 + +enum { + PF9453_REG_DEV_ID = 0x00, + PF9453_REG_OTP_VER = 0x01, + PF9453_REG_INT1 = 0x02, + PF9453_REG_INT1_MASK = 0x03, + PF9453_REG_INT1_STATUS = 0x04, + PF9453_REG_VRFLT1_INT = 0x05, + PF9453_REG_VRFLT1_MASK = 0x06, + PF9453_REG_PWRON_STAT = 0x07, + PF9453_REG_RESET_CTRL = 0x08, + PF9453_REG_SW_RST = 0x09, + PF9453_REG_PWR_CTRL = 0x0a, + PF9453_REG_CONFIG1 = 0x0b, + PF9453_REG_CONFIG2 = 0x0c, + PF9453_REG_32K_CONFIG = 0x0d, + PF9453_REG_BUCK1CTRL = 0x10, + PF9453_REG_BUCK1OUT = 0x11, + PF9453_REG_BUCK2CTRL = 0x14, + PF9453_REG_BUCK2OUT = 0x15, + PF9453_REG_BUCK2OUT_STBY = 0x1d, + PF9453_REG_BUCK2OUT_MAX_LIMIT = 0x1f, + PF9453_REG_BUCK2OUT_MIN_LIMIT = 0x20, + PF9453_REG_BUCK3CTRL = 0x21, + PF9453_REG_BUCK3OUT = 0x22, + PF9453_REG_BUCK4CTRL = 0x2e, + PF9453_REG_BUCK4OUT = 0x2f, + PF9453_REG_LDO1OUT_L = 0x36, + PF9453_REG_LDO1CFG = 0x37, + PF9453_REG_LDO1OUT_H = 0x38, + PF9453_REG_LDOSNVS_CFG1 = 0x39, + PF9453_REG_LDOSNVS_CFG2 = 0x3a, + PF9453_REG_LDO2CFG = 0x3b, + PF9453_REG_LDO2OUT = 0x3c, + PF9453_REG_BUCK_POK = 0x3d, + PF9453_REG_LSW_CTRL1 = 0x40, + PF9453_REG_LSW_CTRL2 = 0x41, + PF9453_REG_LOCK = 0x4e, + PF9453_MAX_REG +}; + +#define PF9453_UNLOCK_KEY 0x5c +#define PF9453_LOCK_KEY 0x0 + +/* PF9453 BUCK ENMODE bits */ +#define BUCK_ENMODE_OFF 0x00 +#define BUCK_ENMODE_ONREQ 0x01 +#define BUCK_ENMODE_ONREQ_STBY 0x02 +#define BUCK_ENMODE_ONREQ_STBY_DPSTBY 0x03 + +/* PF9453 BUCK ENMODE bits */ +#define LDO_ENMODE_OFF 0x00 +#define LDO_ENMODE_ONREQ 0x01 +#define LDO_ENMODE_ONREQ_STBY 0x02 +#define LDO_ENMODE_ONREQ_STBY_DPSTBY 0x03 + +/* PF9453_REG_BUCK1_CTRL bits */ +#define BUCK1_LPMODE 0x30 +#define BUCK1_AD 0x08 +#define BUCK1_FPWM 0x04 +#define BUCK1_ENMODE_MASK GENMASK(1, 0) + +/* PF9453_REG_BUCK2_CTRL bits */ +#define BUCK2_RAMP_MASK GENMASK(7, 6) +#define BUCK2_RAMP_25MV 0x0 +#define BUCK2_RAMP_12P5MV 0x1 +#define BUCK2_RAMP_6P25MV 0x2 +#define BUCK2_RAMP_3P125MV 0x3 +#define BUCK2_LPMODE 0x30 +#define BUCK2_AD 0x08 +#define BUCK2_FPWM 0x04 +#define BUCK2_ENMODE_MASK GENMASK(1, 0) + +/* PF9453_REG_BUCK3_CTRL bits */ +#define BUCK3_LPMODE 0x30 +#define BUCK3_AD 0x08 +#define BUCK3_FPWM 0x04 +#define BUCK3_ENMODE_MASK GENMASK(1, 0) + +/* PF9453_REG_BUCK4_CTRL bits */ +#define BUCK4_LPMODE 0x30 +#define BUCK4_AD 0x08 +#define BUCK4_FPWM 0x04 +#define BUCK4_ENMODE_MASK GENMASK(1, 0) + +/* PF9453_REG_BUCK123_PRESET_EN bit */ +#define BUCK123_PRESET_EN 0x80 + +/* PF9453_BUCK1OUT bits */ +#define BUCK1OUT_MASK GENMASK(6, 0) + +/* PF9453_BUCK2OUT bits */ +#define BUCK2OUT_MASK GENMASK(6, 0) +#define BUCK2OUT_STBY_MASK GENMASK(6, 0) + +/* PF9453_REG_BUCK3OUT bits */ +#define BUCK3OUT_MASK GENMASK(6, 0) + +/* PF9453_REG_BUCK4OUT bits */ +#define BUCK4OUT_MASK GENMASK(6, 0) + +/* PF9453_REG_LDO1_VOLT bits */ +#define LDO1_EN_MASK GENMASK(1, 0) +#define LDO1OUT_MASK GENMASK(6, 0) + +/* PF9453_REG_LDO2_VOLT bits */ +#define LDO2_EN_MASK GENMASK(1, 0) +#define LDO2OUT_MASK GENMASK(6, 0) + +/* PF9453_REG_LDOSNVS_VOLT bits */ +#define LDOSNVS_EN_MASK GENMASK(0, 0) +#define LDOSNVSCFG1_MASK GENMASK(6, 0) + +/* PF9453_REG_IRQ bits */ +#define IRQ_RSVD 0x80 +#define IRQ_RSTB 0x40 +#define IRQ_ONKEY 0x20 +#define IRQ_RESETKEY 0x10 +#define IRQ_VR_FLT1 0x08 +#define IRQ_LOWVSYS 0x04 +#define IRQ_THERM_100 0x02 +#define IRQ_THERM_80 0x01 + +/* PF9453_REG_RESET_CTRL bits */ +#define WDOG_B_CFG_MASK GENMASK(7, 6) +#define WDOG_B_CFG_NONE 0x00 +#define WDOG_B_CFG_WARM 0x40 +#define WDOG_B_CFG_COLD 0x80 + +/* PF9453_REG_CONFIG2 bits */ +#define I2C_LT_MASK GENMASK(1, 0) +#define I2C_LT_FORCE_DISABLE 0x00 +#define I2C_LT_ON_STANDBY_RUN 0x01 +#define I2C_LT_ON_RUN 0x02 +#define I2C_LT_FORCE_ENABLE 0x03 + +static const struct regmap_range pf9453_status_range = { + .range_min = PF9453_REG_INT1, + .range_max = PF9453_REG_PWRON_STAT, +}; + +static const struct regmap_access_table pf9453_volatile_regs = { + .yes_ranges = &pf9453_status_range, + .n_yes_ranges = 1, +}; + +static const struct regmap_config pf9453_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_table = &pf9453_volatile_regs, + .max_register = PF9453_MAX_REG - 1, + .cache_type = REGCACHE_RBTREE, +}; + +/* + * BUCK2 + * BUCK2RAM[1:0] BUCK2 DVS ramp rate setting + * 00: 25mV/1usec + * 01: 25mV/2usec + * 10: 25mV/4usec + * 11: 25mV/8usec + */ +static const unsigned int pf9453_dvs_buck_ramp_table[] = { + 25000, 12500, 6250, 3125 +}; + +static bool is_reg_protect(uint reg) +{ + switch (reg) { + case PF9453_REG_BUCK1OUT: + case PF9453_REG_BUCK2OUT: + case PF9453_REG_BUCK3OUT: + case PF9453_REG_BUCK4OUT: + case PF9453_REG_LDO1OUT_L: + case PF9453_REG_LDO1OUT_H: + case PF9453_REG_LDO2OUT: + case PF9453_REG_LDOSNVS_CFG1: + case PF9453_REG_BUCK2OUT_MAX_LIMIT: + case PF9453_REG_BUCK2OUT_MIN_LIMIT: + return true; + default: + return false; + } +} + +static int pf9453_pmic_write(struct pf9453 *pf9453, unsigned int reg, u8 mask, unsigned int val) +{ + int ret = -EINVAL; + u8 data, key; + u32 rxBuf; + + /* If not updating entire register, perform a read-mod-write */ + data = val; + key = PF9453_UNLOCK_KEY; + + if (mask != 0xffU) { + /* Read data */ + ret = regmap_read(pf9453->regmap, reg, &rxBuf); + if (ret) { + dev_err(pf9453->dev, "Read reg=%0x error!\n", reg); + return ret; + } + data = (val & mask) | (rxBuf & (~mask)); + } + + if (reg < PF9453_MAX_REG) { + if (is_reg_protect(reg)) { + ret = regmap_raw_write(pf9453->regmap, PF9453_REG_LOCK, &key, 1U); + if (ret) { + dev_err(pf9453->dev, "Write reg=%0x error!\n", reg); + return ret; + } + + ret = regmap_raw_write(pf9453->regmap, reg, &data, 1U); + if (ret) { + dev_err(pf9453->dev, "Write reg=%0x error!\n", reg); + return ret; + } + + key = PF9453_LOCK_KEY; + ret = regmap_raw_write(pf9453->regmap, PF9453_REG_LOCK, &key, 1U); + if (ret) { + dev_err(pf9453->dev, "Write reg=%0x error!\n", reg); + return ret; + } + } else { + ret = regmap_raw_write(pf9453->regmap, reg, &data, 1U); + if (ret) { + dev_err(pf9453->dev, "Write reg=%0x error!\n", reg); + return ret; + } + } + } + + return ret; +} + +/** + * pf9453_regulator_enable_regmap for regmap users + * + * @rdev: regulator to operate on + * + * Regulators that use regmap for their register I/O can set the + * enable_reg and enable_mask fields in their descriptor and then use + * this as their enable() operation, saving some code. + */ +static int pf9453_regulator_enable_regmap(struct regulator_dev *rdev) +{ + struct pf9453 *pf9453 = dev_get_drvdata(rdev->dev.parent); + unsigned int val; + + if (rdev->desc->enable_is_inverted) { + val = rdev->desc->disable_val; + } else { + val = rdev->desc->enable_val; + if (!val) + val = rdev->desc->enable_mask; + } + + return pf9453_pmic_write(pf9453, rdev->desc->enable_reg, rdev->desc->enable_mask, val); +} + +/** + * pf9453_regulator_disable_regmap for regmap users + * + * @rdev: regulator to operate on + * + * Regulators that use regmap for their register I/O can set the + * enable_reg and enable_mask fields in their descriptor and then use + * this as their disable() operation, saving some code. + */ +static int pf9453_regulator_disable_regmap(struct regulator_dev *rdev) +{ + struct pf9453 *pf9453 = dev_get_drvdata(rdev->dev.parent); + unsigned int val; + + if (rdev->desc->enable_is_inverted) { + val = rdev->desc->enable_val; + if (!val) + val = rdev->desc->enable_mask; + } else { + val = rdev->desc->disable_val; + } + + return pf9453_pmic_write(pf9453, rdev->desc->enable_reg, rdev->desc->enable_mask, val); +} + +/** + * pf9453_regulator_set_voltage_sel_regmap for regmap users + * + * @rdev: regulator to operate on + * @sel: Selector to set + * + * Regulators that use regmap for their register I/O can set the + * vsel_reg and vsel_mask fields in their descriptor and then use this + * as their set_voltage_vsel operation, saving some code. + */ +static int pf9453_regulator_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned int sel) +{ + struct pf9453 *pf9453 = dev_get_drvdata(rdev->dev.parent); + int ret; + + sel <<= ffs(rdev->desc->vsel_mask) - 1; + ret = pf9453_pmic_write(pf9453, rdev->desc->vsel_reg, rdev->desc->vsel_mask, sel); + if (ret) + return ret; + + if (rdev->desc->apply_bit) + ret = pf9453_pmic_write(pf9453, rdev->desc->apply_reg, + rdev->desc->apply_bit, rdev->desc->apply_bit); + return ret; +} + +static int find_closest_bigger(unsigned int target, const unsigned int *table, + unsigned int num_sel, unsigned int *sel) +{ + unsigned int s, tmp, max, maxsel = 0; + bool found = false; + + max = table[0]; + + for (s = 0; s < num_sel; s++) { + if (table[s] > max) { + max = table[s]; + maxsel = s; + } + if (table[s] >= target) { + if (!found || table[s] - target < tmp - target) { + tmp = table[s]; + *sel = s; + found = true; + if (tmp == target) + break; + } + } + } + + if (!found) { + *sel = maxsel; + return -EINVAL; + } + + return 0; +} + +/** + * pf9453_regulator_set_ramp_delay_regmap + * + * @rdev: regulator to operate on + * + * Regulators that use regmap for their register I/O can set the ramp_reg + * and ramp_mask fields in their descriptor and then use this as their + * set_ramp_delay operation, saving some code. + */ +static int pf9453_regulator_set_ramp_delay_regmap(struct regulator_dev *rdev, int ramp_delay) +{ + struct pf9453 *pf9453 = dev_get_drvdata(rdev->dev.parent); + unsigned int sel; + int ret; + + if (WARN_ON(!rdev->desc->n_ramp_values || !rdev->desc->ramp_delay_table)) + return -EINVAL; + + ret = find_closest_bigger(ramp_delay, rdev->desc->ramp_delay_table, + rdev->desc->n_ramp_values, &sel); + + if (ret) { + dev_warn(rdev_get_dev(rdev), + "Can't set ramp-delay %u, setting %u\n", ramp_delay, + rdev->desc->ramp_delay_table[sel]); + } + + sel <<= ffs(rdev->desc->ramp_mask) - 1; + + return pf9453_pmic_write(pf9453, rdev->desc->ramp_reg, + rdev->desc->ramp_mask, sel); +} + +static const struct regulator_ops pf9453_dvs_buck_regulator_ops = { + .enable = pf9453_regulator_enable_regmap, + .disable = pf9453_regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage_sel = pf9453_regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_ramp_delay = pf9453_regulator_set_ramp_delay_regmap, +}; + +static const struct regulator_ops pf9453_buck_regulator_ops = { + .enable = pf9453_regulator_enable_regmap, + .disable = pf9453_regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage_sel = pf9453_regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, +}; + +static const struct regulator_ops pf9453_ldo_regulator_ops = { + .enable = pf9453_regulator_enable_regmap, + .disable = pf9453_regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage_sel = pf9453_regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +/* + * BUCK1/3/4 + * 0.60 to 3.775V (25mV step) + */ +static const struct linear_range pf9453_buck134_volts[] = { + REGULATOR_LINEAR_RANGE(600000, 0x00, 0x7F, 25000), +}; + +/* + * BUCK2 + * 0.60 to 2.1875V (12.5mV step) + */ +static const struct linear_range pf9453_buck2_volts[] = { + REGULATOR_LINEAR_RANGE(600000, 0x00, 0x7F, 12500), +}; + +/* + * LDO1 + * 0.8 to 3.3V (25mV step) + */ +static const struct linear_range pf9453_ldo1_volts[] = { + REGULATOR_LINEAR_RANGE(800000, 0x00, 0x64, 25000), +}; + +/* + * LDO2 + * 0.5 to 1.95V (25mV step) + */ +static const struct linear_range pf9453_ldo2_volts[] = { + REGULATOR_LINEAR_RANGE(500000, 0x00, 0x3A, 25000), +}; + +/* + * LDOSNVS + * 1.2 to 3.4V (25mV step) + */ +static const struct linear_range pf9453_ldosnvs_volts[] = { + REGULATOR_LINEAR_RANGE(1200000, 0x00, 0x58, 25000), +}; + +static int buck_set_dvs(const struct regulator_desc *desc, + struct device_node *np, struct pf9453 *pf9453, + char *prop, unsigned int reg, unsigned int mask) +{ + int ret, i; + u32 uv; + + ret = of_property_read_u32(np, prop, &uv); + if (ret == -EINVAL) + return 0; + else if (ret) + return ret; + + for (i = 0; i < desc->n_voltages; i++) { + ret = regulator_desc_list_voltage_linear_range(desc, i); + if (ret < 0) + continue; + if (ret == uv) { + i <<= ffs(desc->vsel_mask) - 1; + ret = pf9453_pmic_write(pf9453, reg, mask, i); + break; + } + } + + if (ret == 0) { + struct pf9453_regulator_desc *regulator = container_of(desc, + struct pf9453_regulator_desc, desc); + + /* Enable DVS control through PMIC_STBY_REQ for this BUCK */ + ret = pf9453_pmic_write(pf9453, regulator->desc.enable_reg, + BUCK2_LPMODE, BUCK2_LPMODE); + } + return ret; +} + +static int pf9453_set_dvs_levels(struct device_node *np, const struct regulator_desc *desc, + struct regulator_config *cfg) +{ + struct pf9453_regulator_desc *data = container_of(desc, struct pf9453_regulator_desc, desc); + struct pf9453 *pf9453 = dev_get_drvdata(cfg->dev); + const struct pf9453_dvs_config *dvs = &data->dvs; + unsigned int reg, mask; + int i, ret = 0; + char *prop; + + for (i = 0; i < PF9453_DVS_LEVEL_MAX; i++) { + switch (i) { + case PF9453_DVS_LEVEL_RUN: + prop = "nxp,dvs-run-voltage"; + reg = dvs->run_reg; + mask = dvs->run_mask; + break; + case PF9453_DVS_LEVEL_DPSTANDBY: + case PF9453_DVS_LEVEL_STANDBY: + prop = "nxp,dvs-standby-voltage"; + reg = dvs->standby_reg; + mask = dvs->standby_mask; + break; + default: + return -EINVAL; + } + + ret = buck_set_dvs(desc, np, pf9453, prop, reg, mask); + if (ret) + break; + } + + return ret; +} + +static const struct pf9453_regulator_desc pf9453_regulators[] = { + { + .desc = { + .name = "buck1", + .of_match = of_match_ptr("BUCK1"), + .regulators_node = of_match_ptr("regulators"), + .id = PF9453_BUCK1, + .ops = &pf9453_buck_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = PF9453_BUCK1_VOLTAGE_NUM, + .linear_ranges = pf9453_buck134_volts, + .n_linear_ranges = ARRAY_SIZE(pf9453_buck134_volts), + .vsel_reg = PF9453_REG_BUCK1OUT, + .vsel_mask = BUCK1OUT_MASK, + .enable_reg = PF9453_REG_BUCK1CTRL, + .enable_mask = BUCK1_ENMODE_MASK, + .enable_val = BUCK_ENMODE_ONREQ, + .owner = THIS_MODULE, + }, + }, + { + .desc = { + .name = "buck2", + .of_match = of_match_ptr("BUCK2"), + .regulators_node = of_match_ptr("regulators"), + .id = PF9453_BUCK2, + .ops = &pf9453_dvs_buck_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = PF9453_BUCK2_VOLTAGE_NUM, + .linear_ranges = pf9453_buck2_volts, + .n_linear_ranges = ARRAY_SIZE(pf9453_buck2_volts), + .vsel_reg = PF9453_REG_BUCK2OUT, + .vsel_mask = BUCK2OUT_MASK, + .enable_reg = PF9453_REG_BUCK2CTRL, + .enable_mask = BUCK2_ENMODE_MASK, + .enable_val = BUCK_ENMODE_ONREQ, + .ramp_reg = PF9453_REG_BUCK2CTRL, + .ramp_mask = BUCK2_RAMP_MASK, + .ramp_delay_table = pf9453_dvs_buck_ramp_table, + .n_ramp_values = ARRAY_SIZE(pf9453_dvs_buck_ramp_table), + .owner = THIS_MODULE, + .of_parse_cb = pf9453_set_dvs_levels, + }, + .dvs = { + .run_reg = PF9453_REG_BUCK2OUT, + .run_mask = BUCK2OUT_MASK, + .standby_reg = PF9453_REG_BUCK2OUT_STBY, + .standby_mask = BUCK2OUT_STBY_MASK, + }, + }, + { + .desc = { + .name = "buck3", + .of_match = of_match_ptr("BUCK3"), + .regulators_node = of_match_ptr("regulators"), + .id = PF9453_BUCK3, + .ops = &pf9453_buck_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = PF9453_BUCK3_VOLTAGE_NUM, + .linear_ranges = pf9453_buck134_volts, + .n_linear_ranges = ARRAY_SIZE(pf9453_buck134_volts), + .vsel_reg = PF9453_REG_BUCK3OUT, + .vsel_mask = BUCK3OUT_MASK, + .enable_reg = PF9453_REG_BUCK3CTRL, + .enable_mask = BUCK3_ENMODE_MASK, + .enable_val = BUCK_ENMODE_ONREQ, + .owner = THIS_MODULE, + }, + }, + { + .desc = { + .name = "buck4", + .of_match = of_match_ptr("BUCK4"), + .regulators_node = of_match_ptr("regulators"), + .id = PF9453_BUCK4, + .ops = &pf9453_buck_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = PF9453_BUCK4_VOLTAGE_NUM, + .linear_ranges = pf9453_buck134_volts, + .n_linear_ranges = ARRAY_SIZE(pf9453_buck134_volts), + .vsel_reg = PF9453_REG_BUCK4OUT, + .vsel_mask = BUCK4OUT_MASK, + .enable_reg = PF9453_REG_BUCK4CTRL, + .enable_mask = BUCK4_ENMODE_MASK, + .enable_val = BUCK_ENMODE_ONREQ, + .owner = THIS_MODULE, + }, + }, + { + .desc = { + .name = "ldo1", + .of_match = of_match_ptr("LDO1"), + .regulators_node = of_match_ptr("regulators"), + .id = PF9453_LDO1, + .ops = &pf9453_ldo_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = PF9453_LDO1_VOLTAGE_NUM, + .linear_ranges = pf9453_ldo1_volts, + .n_linear_ranges = ARRAY_SIZE(pf9453_ldo1_volts), + .vsel_reg = PF9453_REG_LDO1OUT_H, + .vsel_mask = LDO1OUT_MASK, + .enable_reg = PF9453_REG_LDO1CFG, + .enable_mask = LDO1_EN_MASK, + .enable_val = LDO_ENMODE_ONREQ, + .owner = THIS_MODULE, + }, + }, + { + .desc = { + .name = "ldo2", + .of_match = of_match_ptr("LDO2"), + .regulators_node = of_match_ptr("regulators"), + .id = PF9453_LDO2, + .ops = &pf9453_ldo_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = PF9453_LDO2_VOLTAGE_NUM, + .linear_ranges = pf9453_ldo2_volts, + .n_linear_ranges = ARRAY_SIZE(pf9453_ldo2_volts), + .vsel_reg = PF9453_REG_LDO2OUT, + .vsel_mask = LDO2OUT_MASK, + .enable_reg = PF9453_REG_LDO2CFG, + .enable_mask = LDO2_EN_MASK, + .enable_val = LDO_ENMODE_ONREQ, + .owner = THIS_MODULE, + }, + }, + { + .desc = { + .name = "ldosnvs", + .of_match = of_match_ptr("LDO-SNVS"), + .regulators_node = of_match_ptr("regulators"), + .id = PF9453_LDOSNVS, + .ops = &pf9453_ldo_regulator_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = PF9453_LDOSNVS_VOLTAGE_NUM, + .linear_ranges = pf9453_ldosnvs_volts, + .n_linear_ranges = ARRAY_SIZE(pf9453_ldosnvs_volts), + .vsel_reg = PF9453_REG_LDOSNVS_CFG1, + .vsel_mask = LDOSNVSCFG1_MASK, + .enable_reg = PF9453_REG_LDOSNVS_CFG2, + .enable_mask = LDOSNVS_EN_MASK, + .owner = THIS_MODULE, + }, + }, + { } +}; + +static irqreturn_t pf9453_irq_handler(int irq, void *data) +{ + struct pf9453 *pf9453 = data; + struct regmap *regmap = pf9453->regmap; + unsigned int status; + int ret; + + ret = regmap_read(regmap, PF9453_REG_INT1, &status); + if (ret < 0) { + dev_err(pf9453->dev, "Failed to read INT1(%d)\n", ret); + return IRQ_NONE; + } + + if (status & IRQ_RSTB) + dev_warn(pf9453->dev, "IRQ_RSTB interrupt.\n"); + + if (status & IRQ_ONKEY) + dev_warn(pf9453->dev, "IRQ_ONKEY interrupt.\n"); + + if (status & IRQ_VR_FLT1) + dev_warn(pf9453->dev, "VRFLT1 interrupt.\n"); + + if (status & IRQ_RESETKEY) + dev_warn(pf9453->dev, "IRQ_RESETKEY interrupt.\n"); + + if (status & IRQ_LOWVSYS) + dev_warn(pf9453->dev, "LOWVSYS interrupt.\n"); + + if (status & IRQ_THERM_100) + dev_warn(pf9453->dev, "IRQ_THERM_100 interrupt.\n"); + + if (status & IRQ_THERM_80) + dev_warn(pf9453->dev, "IRQ_THERM_80 interrupt.\n"); + + return IRQ_HANDLED; +} + +static int pf9453_i2c_probe(struct i2c_client *i2c) +{ + const struct pf9453_regulator_desc *regulator_desc = of_device_get_match_data(&i2c->dev); + struct regulator_config config = { }; + unsigned int reset_ctrl; + unsigned int device_id; + struct pf9453 *pf9453; + int ret; + + if (!i2c->irq) + return dev_err_probe(&i2c->dev, -EINVAL, "No IRQ configured?\n"); + + pf9453 = devm_kzalloc(&i2c->dev, sizeof(struct pf9453), GFP_KERNEL); + if (!pf9453) + return -ENOMEM; + + pf9453->regmap = devm_regmap_init_i2c(i2c, &pf9453_regmap_config); + if (IS_ERR(pf9453->regmap)) + return dev_err_probe(&i2c->dev, PTR_ERR(pf9453->regmap), + "regmap initialization failed\n"); + + pf9453->irq = i2c->irq; + pf9453->dev = &i2c->dev; + + dev_set_drvdata(&i2c->dev, pf9453); + + ret = regmap_read(pf9453->regmap, PF9453_REG_DEV_ID, &device_id); + if (ret) + return dev_err_probe(&i2c->dev, ret, "Read device id error\n"); + + /* Check your board and dts for match the right pmic */ + if ((device_id >> 4) != 0xb) + return dev_err_probe(&i2c->dev, -EINVAL, "Device id(%x) mismatched\n", + device_id >> 4); + + while (regulator_desc->desc.name) { + const struct regulator_desc *desc; + struct regulator_dev *rdev; + + desc = ®ulator_desc->desc; + + config.regmap = pf9453->regmap; + config.dev = pf9453->dev; + + rdev = devm_regulator_register(pf9453->dev, desc, &config); + if (IS_ERR(rdev)) + return dev_err_probe(pf9453->dev, PTR_ERR(rdev), + "Failed to register regulator(%s)\n", desc->name); + + regulator_desc++; + } + + ret = devm_request_threaded_irq(pf9453->dev, pf9453->irq, NULL, pf9453_irq_handler, + (IRQF_TRIGGER_FALLING | IRQF_ONESHOT), + "pf9453-irq", pf9453); + if (ret) + return dev_err_probe(pf9453->dev, ret, "Failed to request IRQ: %d\n", pf9453->irq); + + /* Unmask all interrupt except PWRON/WDOG/RSVD */ + ret = pf9453_pmic_write(pf9453, PF9453_REG_INT1_MASK, + IRQ_ONKEY | IRQ_RESETKEY | IRQ_RSTB | IRQ_VR_FLT1 + | IRQ_LOWVSYS | IRQ_THERM_100 | IRQ_THERM_80, IRQ_RSVD); + if (ret) + return dev_err_probe(&i2c->dev, ret, "Unmask irq error\n"); + + if (of_property_read_bool(i2c->dev.of_node, "nxp,wdog_b-warm-reset")) + reset_ctrl = WDOG_B_CFG_WARM; + else + reset_ctrl = WDOG_B_CFG_COLD; + + /* Set reset behavior on assertion of WDOG_B signal */ + ret = pf9453_pmic_write(pf9453, PF9453_REG_RESET_CTRL, WDOG_B_CFG_MASK, reset_ctrl); + if (ret) + return dev_err_probe(&i2c->dev, ret, "Failed to set WDOG_B reset behavior\n"); + + /* + * The driver uses the LDO1OUT_H register to control the LDO1 regulator. + * This is only valid if the SD_VSEL input of the PMIC is high. Let's + * check if the pin is available as GPIO and set it to high. + */ + pf9453->sd_vsel_gpio = gpiod_get_optional(pf9453->dev, "sd-vsel", GPIOD_OUT_HIGH); + + if (IS_ERR(pf9453->sd_vsel_gpio)) + return dev_err_probe(&i2c->dev, PTR_ERR(pf9453->sd_vsel_gpio), + "Failed to get SD_VSEL GPIO\n"); + + return 0; +} + +static const struct of_device_id pf9453_of_match[] = { + { + .compatible = "nxp,pf9453", + .data = pf9453_regulators, + }, + { } +}; +MODULE_DEVICE_TABLE(of, pf9453_of_match); + +static struct i2c_driver pf9453_i2c_driver = { + .driver = { + .name = "nxp-pf9453", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = pf9453_of_match, + }, + .probe = pf9453_i2c_probe, +}; + +module_i2c_driver(pf9453_i2c_driver); + +MODULE_AUTHOR("Joy Zou <joy.zou@nxp.com>"); +MODULE_DESCRIPTION("NXP PF9453 Power Management IC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/rtq6752-regulator.c b/drivers/regulator/rtq6752-regulator.c index d35d844eff3b..618904ede72c 100644 --- a/drivers/regulator/rtq6752-regulator.c +++ b/drivers/regulator/rtq6752-regulator.c @@ -105,7 +105,7 @@ static int rtq6752_get_error_flags(struct regulator_dev *rdev, unsigned int *flags) { unsigned int val, events = 0; - const unsigned int fault_mask[] = { + static const unsigned int fault_mask[] = { RTQ6752_PAVDDF_MASK, RTQ6752_NAVDDF_MASK }; int rid = rdev_get_id(rdev), ret; |