diff options
Diffstat (limited to 'drivers/regulator')
-rw-r--r-- | drivers/regulator/Kconfig | 18 | ||||
-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/core.c | 92 | ||||
-rw-r--r-- | drivers/regulator/cros-ec-regulator.c | 4 | ||||
-rw-r--r-- | drivers/regulator/devres.c | 22 | ||||
-rw-r--r-- | drivers/regulator/dummy.c | 37 | ||||
-rw-r--r-- | drivers/regulator/irq_helpers.c | 16 | ||||
-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/rk808-regulator.c | 4 | ||||
-rw-r--r-- | drivers/regulator/rtq2208-regulator.c | 216 | ||||
-rw-r--r-- | drivers/regulator/rtq6752-regulator.c | 2 | ||||
-rw-r--r-- | drivers/regulator/s2mps11.c | 92 |
17 files changed, 1321 insertions, 343 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 39297f7d8177..05e32d764028 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 @@ -1330,10 +1330,10 @@ config REGULATOR_S2MPA01 via I2C bus. S2MPA01 has 10 Bucks and 26 LDO outputs. config REGULATOR_S2MPS11 - tristate "Samsung S2MPS11/13/14/15/S2MPU02 voltage regulator" + tristate "Samsung S2MPS11/13/14/15/S2MPU02/05 voltage regulator" depends on MFD_SEC_CORE || COMPILE_TEST help - This driver supports a Samsung S2MPS11/13/14/15/S2MPU02 voltage + This driver supports a Samsung S2MPS11/13/14/15/S2MPU02/05 voltage output regulator via I2C bus. The chip is comprised of high efficient Buck converters including Dual-Phase Buck converter, Buck-Boost converter, various LDOs. 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/core.c b/drivers/regulator/core.c index 4ddf0efead68..90629a756693 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1830,12 +1830,49 @@ static const struct file_operations constraint_flags_fops = { #define REG_STR_SIZE 64 +static void link_and_create_debugfs(struct regulator *regulator, struct regulator_dev *rdev, + struct device *dev) +{ + int err = 0; + + if (dev) { + regulator->dev = dev; + + /* Add a link to the device sysfs entry */ + err = sysfs_create_link_nowarn(&rdev->dev.kobj, &dev->kobj, + regulator->supply_name); + if (err) { + rdev_dbg(rdev, "could not add device link %s: %pe\n", + dev->kobj.name, ERR_PTR(err)); + /* non-fatal */ + } + } + + if (err != -EEXIST) { + regulator->debugfs = debugfs_create_dir(regulator->supply_name, rdev->debugfs); + if (IS_ERR(regulator->debugfs)) { + rdev_dbg(rdev, "Failed to create debugfs directory\n"); + regulator->debugfs = NULL; + } + } + + if (regulator->debugfs) { + debugfs_create_u32("uA_load", 0444, regulator->debugfs, + ®ulator->uA_load); + debugfs_create_u32("min_uV", 0444, regulator->debugfs, + ®ulator->voltage[PM_SUSPEND_ON].min_uV); + debugfs_create_u32("max_uV", 0444, regulator->debugfs, + ®ulator->voltage[PM_SUSPEND_ON].max_uV); + debugfs_create_file("constraint_flags", 0444, regulator->debugfs, + regulator, &constraint_flags_fops); + } +} + static struct regulator *create_regulator(struct regulator_dev *rdev, struct device *dev, const char *supply_name) { struct regulator *regulator; - int err = 0; lockdep_assert_held_once(&rdev->mutex.base); @@ -1868,38 +1905,6 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, list_add(®ulator->list, &rdev->consumer_list); - if (dev) { - regulator->dev = dev; - - /* Add a link to the device sysfs entry */ - err = sysfs_create_link_nowarn(&rdev->dev.kobj, &dev->kobj, - supply_name); - if (err) { - rdev_dbg(rdev, "could not add device link %s: %pe\n", - dev->kobj.name, ERR_PTR(err)); - /* non-fatal */ - } - } - - if (err != -EEXIST) { - regulator->debugfs = debugfs_create_dir(supply_name, rdev->debugfs); - if (IS_ERR(regulator->debugfs)) { - rdev_dbg(rdev, "Failed to create debugfs directory\n"); - regulator->debugfs = NULL; - } - } - - if (regulator->debugfs) { - debugfs_create_u32("uA_load", 0444, regulator->debugfs, - ®ulator->uA_load); - debugfs_create_u32("min_uV", 0444, regulator->debugfs, - ®ulator->voltage[PM_SUSPEND_ON].min_uV); - debugfs_create_u32("max_uV", 0444, regulator->debugfs, - ®ulator->voltage[PM_SUSPEND_ON].max_uV); - debugfs_create_file("constraint_flags", 0444, regulator->debugfs, - regulator, &constraint_flags_fops); - } - /* * Check now if the regulator is an always on regulator - if * it is then we don't need to do nearly so much work for @@ -2069,6 +2074,10 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) if (have_full_constraints()) { r = dummy_regulator_rdev; + if (!r) { + ret = -EPROBE_DEFER; + goto out; + } get_device(&r->dev); } else { dev_err(dev, "Failed to resolve %s-supply for %s\n", @@ -2086,6 +2095,10 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) goto out; } r = dummy_regulator_rdev; + if (!r) { + ret = -EPROBE_DEFER; + goto out; + } get_device(&r->dev); } @@ -2133,6 +2146,9 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) regulator_unlock_two(rdev, r, &ww_ctx); + /* rdev->supply was created in set_supply() */ + link_and_create_debugfs(rdev->supply, r, &rdev->dev); + /* * In set_machine_constraints() we may have turned this regulator on * but we couldn't propagate to the supply if it hadn't been resolved @@ -2211,8 +2227,10 @@ struct regulator *_regulator_get_common(struct regulator_dev *rdev, struct devic * enabled, even if it isn't hooked up, and just * provide a dummy. */ - dev_warn(dev, "supply %s not found, using dummy regulator\n", id); rdev = dummy_regulator_rdev; + if (!rdev) + return ERR_PTR(-EPROBE_DEFER); + dev_warn(dev, "supply %s not found, using dummy regulator\n", id); get_device(&rdev->dev); break; @@ -2271,6 +2289,8 @@ struct regulator *_regulator_get_common(struct regulator_dev *rdev, struct devic return regulator; } + link_and_create_debugfs(regulator, rdev, dev); + rdev->open_count++; if (get_type == EXCLUSIVE_GET) { rdev->exclusive = 1; @@ -5262,8 +5282,8 @@ static void regulator_handle_critical(struct regulator_dev *rdev, if (!reason) return; - hw_protection_shutdown(reason, - rdev->constraints->uv_less_critical_window_ms); + hw_protection_trigger(reason, + rdev->constraints->uv_less_critical_window_ms); } /** 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/dummy.c b/drivers/regulator/dummy.c index 5b9b9e4e762d..e5197ec7234d 100644 --- a/drivers/regulator/dummy.c +++ b/drivers/regulator/dummy.c @@ -13,7 +13,7 @@ #include <linux/err.h> #include <linux/export.h> -#include <linux/platform_device.h> +#include <linux/device/faux.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> @@ -37,15 +37,15 @@ static const struct regulator_desc dummy_desc = { .ops = &dummy_ops, }; -static int dummy_regulator_probe(struct platform_device *pdev) +static int dummy_regulator_probe(struct faux_device *fdev) { struct regulator_config config = { }; int ret; - config.dev = &pdev->dev; + config.dev = &fdev->dev; config.init_data = &dummy_initdata; - dummy_regulator_rdev = devm_regulator_register(&pdev->dev, &dummy_desc, + dummy_regulator_rdev = devm_regulator_register(&fdev->dev, &dummy_desc, &config); if (IS_ERR(dummy_regulator_rdev)) { ret = PTR_ERR(dummy_regulator_rdev); @@ -56,36 +56,17 @@ static int dummy_regulator_probe(struct platform_device *pdev) return 0; } -static struct platform_driver dummy_regulator_driver = { - .probe = dummy_regulator_probe, - .driver = { - .name = "reg-dummy", - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - }, +struct faux_device_ops dummy_regulator_driver = { + .probe = dummy_regulator_probe, }; -static struct platform_device *dummy_pdev; +static struct faux_device *dummy_fdev; void __init regulator_dummy_init(void) { - int ret; - - dummy_pdev = platform_device_alloc("reg-dummy", -1); - if (!dummy_pdev) { + dummy_fdev = faux_device_create("reg-dummy", NULL, &dummy_regulator_driver); + if (!dummy_fdev) { pr_err("Failed to allocate dummy regulator device\n"); return; } - - ret = platform_device_add(dummy_pdev); - if (ret != 0) { - pr_err("Failed to register dummy regulator device: %d\n", ret); - platform_device_put(dummy_pdev); - return; - } - - ret = platform_driver_register(&dummy_regulator_driver); - if (ret != 0) { - pr_err("Failed to register dummy regulator driver: %d\n", ret); - platform_device_unregister(dummy_pdev); - } } diff --git a/drivers/regulator/irq_helpers.c b/drivers/regulator/irq_helpers.c index 0aa188b2bbb2..5742faee8071 100644 --- a/drivers/regulator/irq_helpers.c +++ b/drivers/regulator/irq_helpers.c @@ -64,16 +64,16 @@ static void regulator_notifier_isr_work(struct work_struct *work) reread: if (d->fatal_cnt && h->retry_cnt > d->fatal_cnt) { if (!d->die) - return hw_protection_shutdown("Regulator HW failure? - no IC recovery", - REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS); + return hw_protection_trigger("Regulator HW failure? - no IC recovery", + REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS); ret = d->die(rid); /* * If the 'last resort' IC recovery failed we will have * nothing else left to do... */ if (ret) - return hw_protection_shutdown("Regulator HW failure. IC recovery failed", - REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS); + return hw_protection_trigger("Regulator HW failure. IC recovery failed", + REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS); /* * If h->die() was implemented we assume recovery has been @@ -263,14 +263,14 @@ fail_out: if (d->fatal_cnt && h->retry_cnt > d->fatal_cnt) { /* If we have no recovery, just try shut down straight away */ if (!d->die) { - hw_protection_shutdown("Regulator failure. Retry count exceeded", - REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS); + hw_protection_trigger("Regulator failure. Retry count exceeded", + REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS); } else { ret = d->die(rid); /* If die() failed shut down as a last attempt to save the HW */ if (ret) - hw_protection_shutdown("Regulator failure. Recovery failed", - REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS); + hw_protection_trigger("Regulator failure. Recovery failed", + REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS); } } 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/rk808-regulator.c b/drivers/regulator/rk808-regulator.c index 7d82bd1b36df..1e8142479656 100644 --- a/drivers/regulator/rk808-regulator.c +++ b/drivers/regulator/rk808-regulator.c @@ -270,8 +270,8 @@ static const unsigned int rk817_buck1_4_ramp_table[] = { static int rk806_set_mode_dcdc(struct regulator_dev *rdev, unsigned int mode) { - int rid = rdev_get_id(rdev); - int ctr_bit, reg; + unsigned int rid = rdev_get_id(rdev); + unsigned int ctr_bit, reg; reg = RK806_POWER_FPWM_EN0 + rid / 8; ctr_bit = rid % 8; diff --git a/drivers/regulator/rtq2208-regulator.c b/drivers/regulator/rtq2208-regulator.c index 5925fa7a9a06..9cde7181b0f0 100644 --- a/drivers/regulator/rtq2208-regulator.c +++ b/drivers/regulator/rtq2208-regulator.c @@ -27,6 +27,11 @@ #define RTQ2208_REG_LDO1_CFG 0xB1 #define RTQ2208_REG_LDO2_CFG 0xC1 #define RTQ2208_REG_LDO_DVS_CTRL 0xD0 +#define RTQ2208_REG_HIDDEN_BUCKPH 0x55 +#define RTQ2208_REG_HIDDEN_LDOCFG0 0x8F +#define RTQ2208_REG_HIDDEN_LDOCFG1 0x96 +#define RTQ2208_REG_HIDDEN0 0xFE +#define RTQ2208_REG_HIDDEN1 0xFF /* Mask */ #define RTQ2208_BUCK_NR_MTP_SEL_MASK GENMASK(7, 0) @@ -45,6 +50,11 @@ #define RTQ2208_LDO1_VOSEL_SD_MASK BIT(5) #define RTQ2208_LDO2_DISCHG_EN_MASK BIT(6) #define RTQ2208_LDO2_VOSEL_SD_MASK BIT(7) +#define RTQ2208_MASK_BUCKPH_GROUP1 GENMASK(6, 4) +#define RTQ2208_MASK_BUCKPH_GROUP2 GENMASK(2, 0) +#define RTQ2208_MASK_LDO2_OPT0 BIT(7) +#define RTQ2208_MASK_LDO2_OPT1 BIT(6) +#define RTQ2208_MASK_LDO1_FIXED BIT(6) /* Size */ #define RTQ2208_VOUT_MAXNUM 256 @@ -245,11 +255,6 @@ static const unsigned int rtq2208_ldo_volt_table[] = { 3300000, }; -static struct of_regulator_match rtq2208_ldo_match[] = { - {.name = "ldo2", }, - {.name = "ldo1", }, -}; - static unsigned int rtq2208_of_map_mode(unsigned int mode) { switch (mode) { @@ -344,59 +349,6 @@ static irqreturn_t rtq2208_irq_handler(int irqno, void *devid) return IRQ_HANDLED; } -static int rtq2208_of_get_ldo_dvs_ability(struct device *dev) -{ - struct device_node *np; - struct of_regulator_match *match; - struct regulator_desc *desc; - struct regulator_init_data *init_data; - u32 fixed_uV; - int ret, i; - - if (!dev->of_node) - return -ENODEV; - - np = of_get_child_by_name(dev->of_node, "regulators"); - if (!np) - np = dev->of_node; - - ret = of_regulator_match(dev, np, rtq2208_ldo_match, ARRAY_SIZE(rtq2208_ldo_match)); - - of_node_put(np); - - if (ret < 0) - return ret; - - for (i = 0; i < ARRAY_SIZE(rtq2208_ldo_match); i++) { - match = rtq2208_ldo_match + i; - init_data = match->init_data; - desc = (struct regulator_desc *)match->desc; - - if (!init_data || !desc) - continue; - - /* specify working fixed voltage if the propery exists */ - ret = of_property_read_u32(match->of_node, "richtek,fixed-microvolt", &fixed_uV); - - if (!ret) { - if (fixed_uV != init_data->constraints.min_uV || - fixed_uV != init_data->constraints.max_uV) - return -EINVAL; - desc->n_voltages = 1; - desc->fixed_uV = fixed_uV; - desc->fixed_uV = init_data->constraints.min_uV; - desc->ops = &rtq2208_regulator_ldo_fix_ops; - } else { - desc->n_voltages = ARRAY_SIZE(rtq2208_ldo_volt_table); - desc->volt_table = rtq2208_ldo_volt_table; - desc->ops = &rtq2208_regulator_ldo_adj_ops; - } - } - - return 0; -} - - #define BUCK_INFO(_name, _id) \ { \ .name = _name, \ @@ -424,9 +376,11 @@ static const struct linear_range rtq2208_vout_range[] = { REGULATOR_LINEAR_RANGE(1310000, 181, 255, 10000), }; -static void rtq2208_init_regulator_desc(struct rtq2208_regulator_desc *rdesc, int mtp_sel, int idx) +static void rtq2208_init_regulator_desc(struct rtq2208_regulator_desc *rdesc, int mtp_sel, int idx, + unsigned int ldo1_fixed, unsigned int ldo2_fixed) { struct regulator_desc *desc; + unsigned int fixed_uV; static const struct { char *name; int base; @@ -462,7 +416,8 @@ static void rtq2208_init_regulator_desc(struct rtq2208_regulator_desc *rdesc, in rdesc->mode_mask = RTQ2208_BUCK_NRMODE_MASK; - if (idx >= RTQ2208_BUCK_B && idx <= RTQ2208_BUCK_E) { + switch (idx) { + case RTQ2208_BUCK_B ... RTQ2208_BUCK_E: /* init buck desc */ desc->ops = &rtq2208_regulator_buck_ops; desc->vsel_reg = curr_info->base + VSEL_SHIFT(mtp_sel); @@ -480,7 +435,19 @@ static void rtq2208_init_regulator_desc(struct rtq2208_regulator_desc *rdesc, in rdesc->suspend_config_reg = BUCK_RG_SHIFT(curr_info->base, 4); rdesc->suspend_enable_mask = RTQ2208_BUCK_EN_STR_MASK; rdesc->suspend_mode_mask = RTQ2208_BUCK_STRMODE_MASK; - } else { + break; + default: + fixed_uV = idx == RTQ2208_LDO2 ? ldo2_fixed : ldo1_fixed; + if (fixed_uV) { + desc->n_voltages = 1; + desc->fixed_uV = fixed_uV; + desc->ops = &rtq2208_regulator_ldo_fix_ops; + } else { + desc->n_voltages = ARRAY_SIZE(rtq2208_ldo_volt_table); + desc->volt_table = rtq2208_ldo_volt_table; + desc->ops = &rtq2208_regulator_ldo_adj_ops; + } + /* init ldo desc */ desc->active_discharge_reg = RTQ2208_REG_LDO_DVS_CTRL; desc->active_discharge_on = curr_info->dis_on; @@ -490,13 +457,15 @@ static void rtq2208_init_regulator_desc(struct rtq2208_regulator_desc *rdesc, in rdesc->suspend_config_reg = curr_info->base; rdesc->suspend_enable_mask = RTQ2208_LDO_EN_STR_MASK; + break; } } static int rtq2208_parse_regulator_dt_data(int n_regulator, const unsigned int *regulator_idx_table, - struct rtq2208_regulator_desc *rdesc[RTQ2208_LDO_MAX], struct device *dev) + struct rtq2208_regulator_desc *rdesc[RTQ2208_LDO_MAX], struct device *dev, + unsigned int ldo1_fixed, unsigned int ldo2_fixed) { - int mtp_sel, i, idx, ret; + int mtp_sel, i, idx; /* get mtp_sel0 or mtp_sel1 */ mtp_sel = device_property_read_bool(dev, "richtek,mtp-sel-high"); @@ -508,43 +477,101 @@ static int rtq2208_parse_regulator_dt_data(int n_regulator, const unsigned int * if (!rdesc[i]) return -ENOMEM; - rtq2208_init_regulator_desc(rdesc[i], mtp_sel, idx); - - /* init ldo dvs ability */ - if (idx >= RTQ2208_LDO2) - rtq2208_ldo_match[idx - RTQ2208_LDO2].desc = &rdesc[i]->desc; + rtq2208_init_regulator_desc(rdesc[i], mtp_sel, idx, ldo1_fixed, ldo2_fixed); } - /* init ldo fixed_uV */ - ret = rtq2208_of_get_ldo_dvs_ability(dev); - if (ret) - return dev_err_probe(dev, ret, "Failed to get ldo fixed_uV\n"); - return 0; } -/** different slave address corresponds different used bucks - * slave address 0x10: BUCK[BCA FGE] - * slave address 0x20: BUCK[BC FGHE] - * slave address 0x40: BUCK[C G] - */ -static int rtq2208_regulator_check(int slave_addr, int *num, - int *regulator_idx_table, unsigned int *buck_masks) +static int rtq2208_regulator_check(struct device *dev, int *num, int *regulator_idx_table, + unsigned int *buck_masks, unsigned int *ldo1_fixed_uV, + unsigned int *ldo2_fixed_uV) { - static bool rtq2208_used_table[3][RTQ2208_LDO_MAX] = { - /* BUCK[BCA FGE], LDO[12] */ - {1, 1, 0, 1, 1, 1, 0, 1, 1, 1}, - /* BUCK[BC FGHE], LDO[12]*/ - {1, 1, 0, 0, 1, 1, 1, 1, 1, 1}, - /* BUCK[C G], LDO[12] */ - {0, 1, 0, 0, 0, 1, 0, 0, 1, 1}, - }; - int i, idx = ffs(slave_addr >> 4) - 1; + struct regmap *regmap = dev_get_regmap(dev, NULL); + bool rtq2208_used_table[RTQ2208_LDO_MAX] = {0}; + u8 entry_key[] = { 0x69, 0x01 }; + unsigned int buck_phase, ldo_cfg0, ldo_cfg1; + int i, ret; u8 mask; + ret = regmap_raw_write(regmap, RTQ2208_REG_HIDDEN0, entry_key, ARRAY_SIZE(entry_key)); + if (ret) + return dev_err_probe(dev, ret, "Failed to enter hidden page\n"); + + ret = regmap_read(regmap, RTQ2208_REG_HIDDEN_BUCKPH, &buck_phase); + if (ret) + return dev_err_probe(dev, ret, "Failed to read buck phase configuration\n"); + + ret = regmap_read(regmap, RTQ2208_REG_HIDDEN_LDOCFG0, &ldo_cfg0); + if (ret) + return dev_err_probe(dev, ret, "Failed to read ldo cfg0\n"); + + ret = regmap_read(regmap, RTQ2208_REG_HIDDEN_LDOCFG1, &ldo_cfg1); + if (ret) + return dev_err_probe(dev, ret, "Failed to read ldo cfg1\n"); + + ret = regmap_write(regmap, RTQ2208_REG_HIDDEN1, 0x00); + if (ret) + return dev_err_probe(dev, ret, "Failed to exit hidden page\n"); + + dev_info(dev, "BUCK Phase 0x%x\n", buck_phase); + /* + * Use buck phase configuration to assign used table mask + * GROUP1 GROUP2 + * 0 -> 2P + 2P BC FG + * 1 -> 2P + 1P + 1P BCA FGE + * 2 -> 1P + 1P + 1P + 1P BCDA FGHE + * 3 -> 3P + 1P BC FG + * others -> 4P C G + */ + switch (FIELD_GET(RTQ2208_MASK_BUCKPH_GROUP1, buck_phase)) { + case 2: + rtq2208_used_table[RTQ2208_BUCK_D] = true; + fallthrough; + case 1: + rtq2208_used_table[RTQ2208_BUCK_A] = true; + fallthrough; + case 0: + case 3: + rtq2208_used_table[RTQ2208_BUCK_B] = true; + fallthrough; + default: + rtq2208_used_table[RTQ2208_BUCK_C] = true; + break; + } + + switch (FIELD_GET(RTQ2208_MASK_BUCKPH_GROUP2, buck_phase)) { + case 2: + rtq2208_used_table[RTQ2208_BUCK_F] = true; + fallthrough; + case 1: + rtq2208_used_table[RTQ2208_BUCK_E] = true; + fallthrough; + case 0: + case 3: + rtq2208_used_table[RTQ2208_BUCK_H] = true; + fallthrough; + default: + rtq2208_used_table[RTQ2208_BUCK_G] = true; + break; + } + + *ldo1_fixed_uV = FIELD_GET(RTQ2208_MASK_LDO1_FIXED, ldo_cfg1) ? 1200000 : 0; + + if (!FIELD_GET(RTQ2208_MASK_LDO2_OPT0, ldo_cfg0) && + !FIELD_GET(RTQ2208_MASK_LDO2_OPT1, ldo_cfg1)) + *ldo2_fixed_uV = 0; + else if (FIELD_GET(RTQ2208_MASK_LDO2_OPT1, ldo_cfg1)) + *ldo2_fixed_uV = 900000; + else + *ldo2_fixed_uV = 1200000; + + /* By default, LDO1 & LDO2 are always used */ + rtq2208_used_table[RTQ2208_LDO1] = rtq2208_used_table[RTQ2208_LDO2] = true; + for (i = 0; i < RTQ2208_LDO_MAX; i++) { - if (!rtq2208_used_table[idx][i]) + if (!rtq2208_used_table[i]) continue; regulator_idx_table[(*num)++] = i; @@ -559,7 +586,7 @@ static int rtq2208_regulator_check(int slave_addr, int *num, static const struct regmap_config rtq2208_regmap_config = { .reg_bits = 8, .val_bits = 8, - .max_register = 0xEF, + .max_register = 0xFF, }; static int rtq2208_probe(struct i2c_client *i2c) @@ -573,6 +600,7 @@ static int rtq2208_probe(struct i2c_client *i2c) int i, ret = 0, idx, n_regulator = 0; unsigned int regulator_idx_table[RTQ2208_LDO_MAX], buck_masks[RTQ2208_BUCK_NUM_IRQ_REGS] = {0x33, 0x33, 0x33, 0x33, 0x33}; + unsigned int ldo1_fixed_uV, ldo2_fixed_uV; rdev_map = devm_kzalloc(dev, sizeof(struct rtq2208_rdev_map), GFP_KERNEL); if (!rdev_map) @@ -583,7 +611,8 @@ static int rtq2208_probe(struct i2c_client *i2c) return dev_err_probe(dev, PTR_ERR(regmap), "Failed to allocate regmap\n"); /* get needed regulator */ - ret = rtq2208_regulator_check(i2c->addr, &n_regulator, regulator_idx_table, buck_masks); + ret = rtq2208_regulator_check(dev, &n_regulator, regulator_idx_table, buck_masks, + &ldo1_fixed_uV, &ldo2_fixed_uV); if (ret) return dev_err_probe(dev, ret, "Failed to check used regulators\n"); @@ -593,7 +622,8 @@ static int rtq2208_probe(struct i2c_client *i2c) cfg.dev = dev; /* init regulator desc */ - ret = rtq2208_parse_regulator_dt_data(n_regulator, regulator_idx_table, rdesc, dev); + ret = rtq2208_parse_regulator_dt_data(n_regulator, regulator_idx_table, rdesc, dev, + ldo1_fixed_uV, ldo2_fixed_uV); if (ret) return ret; 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; diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 7dcf92af8f15..04ae9c6150bd 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -21,6 +21,7 @@ #include <linux/mfd/samsung/s2mps14.h> #include <linux/mfd/samsung/s2mps15.h> #include <linux/mfd/samsung/s2mpu02.h> +#include <linux/mfd/samsung/s2mpu05.h> /* The highest number of possible regulators for supported devices. */ #define S2MPS_REGULATOR_MAX S2MPS13_REGULATOR_MAX @@ -254,6 +255,9 @@ static int s2mps11_regulator_enable(struct regulator_dev *rdev) else val = rdev->desc->enable_mask; break; + case S2MPU05: + val = rdev->desc->enable_mask; + break; default: return -EINVAL; } @@ -1118,6 +1122,86 @@ static const struct regulator_desc s2mpu02_regulators[] = { regulator_desc_s2mpu02_buck7(7), }; +#define regulator_desc_s2mpu05_ldo_reg(num, min, step, reg) { \ + .name = "ldo"#num, \ + .id = S2MPU05_LDO##num, \ + .ops = &s2mpu02_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = min, \ + .uV_step = step, \ + .n_voltages = S2MPU05_LDO_N_VOLTAGES, \ + .vsel_reg = reg, \ + .vsel_mask = S2MPU05_LDO_VSEL_MASK, \ + .enable_reg = reg, \ + .enable_mask = S2MPU05_ENABLE_MASK, \ + .enable_time = S2MPU05_ENABLE_TIME_LDO \ +} + +#define regulator_desc_s2mpu05_ldo(num, reg, min, step) \ + regulator_desc_s2mpu05_ldo_reg(num, min, step, S2MPU05_REG_L##num##reg) + +#define regulator_desc_s2mpu05_ldo1(num, reg) \ + regulator_desc_s2mpu05_ldo(num, reg, S2MPU05_LDO_MIN1, S2MPU05_LDO_STEP1) + +#define regulator_desc_s2mpu05_ldo2(num, reg) \ + regulator_desc_s2mpu05_ldo(num, reg, S2MPU05_LDO_MIN1, S2MPU05_LDO_STEP2) + +#define regulator_desc_s2mpu05_ldo3(num, reg) \ + regulator_desc_s2mpu05_ldo(num, reg, S2MPU05_LDO_MIN2, S2MPU05_LDO_STEP2) + +#define regulator_desc_s2mpu05_ldo4(num, reg) \ + regulator_desc_s2mpu05_ldo(num, reg, S2MPU05_LDO_MIN3, S2MPU05_LDO_STEP2) + +#define regulator_desc_s2mpu05_buck(num, which) { \ + .name = "buck"#num, \ + .id = S2MPU05_BUCK##num, \ + .ops = &s2mpu02_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPU05_BUCK_MIN##which, \ + .uV_step = S2MPU05_BUCK_STEP##which, \ + .n_voltages = S2MPU05_BUCK_N_VOLTAGES, \ + .vsel_reg = S2MPU05_REG_B##num##CTRL2, \ + .vsel_mask = S2MPU05_BUCK_VSEL_MASK, \ + .enable_reg = S2MPU05_REG_B##num##CTRL1, \ + .enable_mask = S2MPU05_ENABLE_MASK, \ + .enable_time = S2MPU05_ENABLE_TIME_BUCK##num \ +} + +#define regulator_desc_s2mpu05_buck123(num) regulator_desc_s2mpu05_buck(num, 1) +#define regulator_desc_s2mpu05_buck45(num) regulator_desc_s2mpu05_buck(num, 2) + +static const struct regulator_desc s2mpu05_regulators[] = { + regulator_desc_s2mpu05_ldo4(1, CTRL), + regulator_desc_s2mpu05_ldo3(2, CTRL), + regulator_desc_s2mpu05_ldo2(3, CTRL), + regulator_desc_s2mpu05_ldo1(4, CTRL), + regulator_desc_s2mpu05_ldo1(5, CTRL), + regulator_desc_s2mpu05_ldo1(6, CTRL), + regulator_desc_s2mpu05_ldo2(7, CTRL), + regulator_desc_s2mpu05_ldo3(8, CTRL), + regulator_desc_s2mpu05_ldo4(9, CTRL1), + regulator_desc_s2mpu05_ldo4(10, CTRL), + /* LDOs 11-24 are used for CP. They aren't documented. */ + regulator_desc_s2mpu05_ldo2(25, CTRL), + regulator_desc_s2mpu05_ldo3(26, CTRL), + regulator_desc_s2mpu05_ldo2(27, CTRL), + regulator_desc_s2mpu05_ldo3(28, CTRL), + regulator_desc_s2mpu05_ldo3(29, CTRL), + regulator_desc_s2mpu05_ldo2(30, CTRL), + regulator_desc_s2mpu05_ldo3(31, CTRL), + regulator_desc_s2mpu05_ldo3(32, CTRL), + regulator_desc_s2mpu05_ldo3(33, CTRL), + regulator_desc_s2mpu05_ldo3(34, CTRL), + regulator_desc_s2mpu05_ldo3(35, CTRL), + regulator_desc_s2mpu05_buck123(1), + regulator_desc_s2mpu05_buck123(2), + regulator_desc_s2mpu05_buck123(3), + regulator_desc_s2mpu05_buck45(4), + regulator_desc_s2mpu05_buck45(5), +}; + static int s2mps11_pmic_probe(struct platform_device *pdev) { struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); @@ -1159,6 +1243,11 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) regulators = s2mpu02_regulators; BUILD_BUG_ON(S2MPS_REGULATOR_MAX < ARRAY_SIZE(s2mpu02_regulators)); break; + case S2MPU05: + rdev_num = ARRAY_SIZE(s2mpu05_regulators); + regulators = s2mpu05_regulators; + BUILD_BUG_ON(S2MPS_REGULATOR_MAX < ARRAY_SIZE(s2mpu05_regulators)); + break; default: dev_err(&pdev->dev, "Invalid device type: %u\n", s2mps11->dev_type); @@ -1228,6 +1317,7 @@ static const struct platform_device_id s2mps11_pmic_id[] = { { "s2mps14-regulator", S2MPS14X}, { "s2mps15-regulator", S2MPS15X}, { "s2mpu02-regulator", S2MPU02}, + { "s2mpu05-regulator", S2MPU05}, { }, }; MODULE_DEVICE_TABLE(platform, s2mps11_pmic_id); @@ -1245,5 +1335,5 @@ module_platform_driver(s2mps11_pmic_driver); /* Module information */ MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>"); -MODULE_DESCRIPTION("Samsung S2MPS11/S2MPS14/S2MPS15/S2MPU02 Regulator Driver"); +MODULE_DESCRIPTION("Samsung S2MPS11/14/15/S2MPU02/05 Regulator Driver"); MODULE_LICENSE("GPL"); |