diff options
Diffstat (limited to 'drivers/regulator')
-rw-r--r-- | drivers/regulator/Kconfig | 39 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 5 | ||||
-rw-r--r-- | drivers/regulator/anatop-regulator.c | 29 | ||||
-rw-r--r-- | drivers/regulator/bcm2835.c | 35 | ||||
-rw-r--r-- | drivers/regulator/core.c | 634 | ||||
-rw-r--r-- | drivers/regulator/fixed.c | 72 | ||||
-rw-r--r-- | drivers/regulator/helpers.c | 28 | ||||
-rw-r--r-- | drivers/regulator/of_regulator.c | 150 | ||||
-rw-r--r-- | drivers/regulator/pfuze.c | 42 | ||||
-rw-r--r-- | drivers/regulator/rk808-regulator.c | 963 | ||||
-rw-r--r-- | drivers/regulator/scmi-regulator.c | 394 | ||||
-rw-r--r-- | drivers/regulator/stm32-pwr.c | 220 | ||||
-rw-r--r-- | drivers/regulator/stm32-vrefbuf.c | 219 | ||||
-rw-r--r-- | drivers/regulator/stpmic1_regulator.c | 34 |
14 files changed, 2453 insertions, 411 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 28bd69a2a5..17e217f0bb 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only menuconfig REGULATOR bool "voltage regulator support" @@ -19,7 +20,23 @@ config REGULATOR_BCM283X config REGULATOR_PFUZE bool "Freescale PFUZE100/200/3000 regulator driver" depends on I2C - depends on ARCH_IMX6 + depends on ARCH_IMX || COMPILE_TEST + +config REGULATOR_STM32_PWR + bool "STMicroelectronics STM32 PWR" + depends on ARCH_STM32MP + help + This driver supports internal regulators (1V1, 1V8, 3V3) in the + STMicroelectronics STM32 chips. + +config REGULATOR_STM32_VREFBUF + tristate "STMicroelectronics STM32 VREFBUF" + depends on ARCH_STM32 || COMPILE_TEST + help + This driver supports STMicroelectronics STM32 VREFBUF (voltage + reference buffer) which can be used as voltage reference for + internal ADCs, DACs and also for external components through + dedicated Vref+ pin. config REGULATOR_STPMIC1 tristate "STMicroelectronics STPMIC1 PMIC Regulators" @@ -38,4 +55,24 @@ config REGULATOR_ANATOP regulators. It is recommended that this option be enabled on i.MX6 platform. + +config REGULATOR_ARM_SCMI + tristate "SCMI based regulator driver" + depends on ARM_SCMI_PROTOCOL && OFDEVICE + help + This adds the regulator driver support for ARM platforms using SCMI + protocol for device voltage management. + This driver uses SCMI Message Protocol driver to interact with the + firmware providing the device Voltage functionality. + +config REGULATOR_RK808 + tristate "Rockchip RK805/RK808/RK809/RK817/RK818 Power regulators" + depends on MFD_RK808 + help + Select this option to enable the power regulator of ROCKCHIP + PMIC RK805,RK809&RK817,RK808 and RK818. + This driver supports the control of different power rails of device + through regulator interface. The device supports multiple DCDC/LDO + outputs which can be controlled by i2c communication. + endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index e27e155cf6..95e42719d1 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_REGULATOR) += core.o helpers.o obj-$(CONFIG_OFDEVICE) += of_regulator.o obj-$(CONFIG_REGULATOR_FIXED) += fixed.o @@ -5,3 +6,7 @@ obj-$(CONFIG_REGULATOR_BCM283X) += bcm2835.o obj-$(CONFIG_REGULATOR_PFUZE) += pfuze.o obj-$(CONFIG_REGULATOR_STPMIC1) += stpmic1_regulator.o obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o +obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o +obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o +obj-$(CONFIG_REGULATOR_ARM_SCMI) += scmi-regulator.o +obj-$(CONFIG_REGULATOR_RK808) += rk808-regulator.o diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c index 7ec9446a0a..4b4c174304 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -1,27 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. */ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - #include <common.h> #include <init.h> #include <mfd/syscon.h> -#include <regmap.h> +#include <linux/regmap.h> #include <regulator.h> struct anatop_regulator { @@ -48,9 +33,9 @@ static struct regulator_ops anatop_rops = { .list_voltage = regulator_list_voltage_linear, }; -static int anatop_regulator_probe(struct device_d *dev) +static int anatop_regulator_probe(struct device *dev) { - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; struct device_node *anatop_np; struct regulator_desc *rdesc; struct regulator_dev *rdev; @@ -67,6 +52,7 @@ static int anatop_regulator_probe(struct device_d *dev) rdev->desc = rdesc; rdev->regmap = syscon_node_to_regmap(anatop_np); + rdev->dev = dev; if (IS_ERR(rdev->regmap)) return PTR_ERR(rdev->regmap); @@ -145,15 +131,16 @@ static int anatop_regulator_probe(struct device_d *dev) } } - return of_regulator_register(rdev, dev->device_node); + return of_regulator_register(rdev, dev->of_node); } static const struct of_device_id of_anatop_regulator_match_tbl[] = { { .compatible = "fsl,anatop-regulator", }, { /* end */ } }; +MODULE_DEVICE_TABLE(of, of_anatop_regulator_match_tbl); -static struct driver_d anatop_regulator_driver = { +static struct driver anatop_regulator_driver = { .name = "anatop_regulator", .probe = anatop_regulator_probe, .of_compatible = DRV_OF_COMPAT(of_anatop_regulator_match_tbl), diff --git a/drivers/regulator/bcm2835.c b/drivers/regulator/bcm2835.c index ea7cf7fe1e..fa9fc47207 100644 --- a/drivers/regulator/bcm2835.c +++ b/drivers/regulator/bcm2835.c @@ -1,20 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * bcm2835 regulator support * * Copyright (c) 2015 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> - * - * GPLv2 Only */ #include <common.h> #include <malloc.h> #include <init.h> #include <regulator.h> -#include <mach/mbox.h> +#include <mach/bcm283x/mbox.h> #define REG_DEV(_id, _name) \ { \ - .id = _id, \ + .id = _id, \ .devname = _name,\ } @@ -22,7 +21,6 @@ static struct regulator_bcm2835 { int id; char *devname; - struct device_d *dev; struct regulator_dev rdev; struct regulator_desc rdesc; } regs[] = { @@ -43,8 +41,9 @@ struct msg_set_power_state { u32 end_tag; }; -static int regulator_bcm2835_set(struct regulator_bcm2835 *rb, int state) +static int regulator_bcm2835_set(struct regulator_dev *rdev, int state) { + struct regulator_bcm2835 *rb = container_of(rdev, struct regulator_bcm2835, rdev); BCM2835_MBOX_STACK_ALIGN(struct msg_set_power_state, msg_pwr); int ret; @@ -59,8 +58,8 @@ static int regulator_bcm2835_set(struct regulator_bcm2835 *rb, int state) ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg_pwr->hdr); if (ret) { - dev_err(rb->dev ,"bcm2835: Could not set module %u power state\n", - rb->id); + dev_err(rdev->dev, "bcm2835: Could not set module %u power state\n", + rb->id); return ret; } @@ -69,16 +68,12 @@ static int regulator_bcm2835_set(struct regulator_bcm2835 *rb, int state) static int regulator_bcm2835_enable(struct regulator_dev *rdev) { - struct regulator_bcm2835 *rb = container_of(rdev, struct regulator_bcm2835, rdev); - - return regulator_bcm2835_set(rb, BCM2835_MBOX_SET_POWER_STATE_REQ_ON); + return regulator_bcm2835_set(rdev, BCM2835_MBOX_SET_POWER_STATE_REQ_ON); } static int regulator_bcm2835_disable(struct regulator_dev *rdev) { - struct regulator_bcm2835 *rb = container_of(rdev, struct regulator_bcm2835, rdev); - - return regulator_bcm2835_set(rb, BCM2835_MBOX_SET_POWER_STATE_REQ_OFF); + return regulator_bcm2835_set(rdev, BCM2835_MBOX_SET_POWER_STATE_REQ_OFF); } struct msg_get_power_state { @@ -101,8 +96,8 @@ static int regulator_bcm2835_is_enabled(struct regulator_dev *rdev) ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg_pwr->hdr); if (ret) { - dev_err(rb->dev ,"bcm2835: Could not get module %u power state\n", - rb->id); + dev_err(rdev->dev, "bcm2835: Could not get module %u power state\n", + rb->id); return ret; } @@ -115,7 +110,7 @@ const static struct regulator_ops bcm2835_ops = { .is_enabled = regulator_bcm2835_is_enabled, }; -static int regulator_bcm2835_probe(struct device_d *dev) +static int regulator_bcm2835_probe(struct device *dev) { struct regulator_bcm2835 *rb; int ret, i; @@ -125,9 +120,9 @@ static int regulator_bcm2835_probe(struct device_d *dev) rb->rdesc.ops = &bcm2835_ops; rb->rdev.desc = &rb->rdesc; - rb->dev = dev; + rb->rdev.dev = dev; - ret = dev_regulator_register(&rb->rdev, rb->devname, NULL); + ret = dev_regulator_register(&rb->rdev, rb->devname); if (ret) return ret; } @@ -135,7 +130,7 @@ static int regulator_bcm2835_probe(struct device_d *dev) return 0; } -static struct driver_d regulator_bcm2835_driver = { +static struct driver regulator_bcm2835_driver = { .name = "regulator-bcm2835", .probe = regulator_bcm2835_probe, }; diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 4ca035ae94..bbba3b0b57 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1,104 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * barebox regulator support * * Copyright (c) 2014 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> #include <regulator.h> #include <of.h> #include <malloc.h> #include <linux/err.h> +#include <deep-probe.h> static LIST_HEAD(regulator_list); -struct regulator_internal { - struct list_head list; - struct device_node *node; - struct regulator_dev *rdev; - int enable_count; - int enable_time_us; - int min_uv; - int max_uv; - char *name; - const char *supply; - struct list_head consumer_list; -}; - struct regulator { - struct regulator_internal *ri; + struct regulator_dev *rdev; + struct regulator_dev *rdev_consumer; struct list_head list; - struct device_d *dev; + struct device *dev; }; +const char *rdev_get_name(struct regulator_dev *rdev) +{ + if (rdev->name) + return rdev->name; + + return ""; +} + static int regulator_map_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) { if (rdev->desc->ops->list_voltage == regulator_list_voltage_linear) return regulator_map_voltage_linear(rdev, min_uV, max_uV); - return -ENOSYS; + if (rdev->desc->ops->list_voltage == regulator_list_voltage_linear_range) + return regulator_map_voltage_linear_range(rdev, min_uV, max_uV); + + return regulator_map_voltage_iterate(rdev, min_uV, max_uV); } -static int regulator_enable_internal(struct regulator_internal *ri) +static int regulator_enable_rdev(struct regulator_dev *rdev) { int ret; - if (ri->enable_count) { - ri->enable_count++; + if (rdev->enable_count) { + rdev->enable_count++; return 0; } - if (!ri->rdev->desc->ops->enable) + if (!rdev->desc->ops->enable) return -ENOSYS; - ret = ri->rdev->desc->ops->enable(ri->rdev); + /* turn on parent regulator */ + ret = regulator_enable(rdev->supply); if (ret) return ret; - if (ri->enable_time_us) - udelay(ri->enable_time_us); + ret = rdev->desc->ops->enable(rdev); + if (ret) { + regulator_disable(rdev->supply); + return ret; + } + + if (rdev->enable_time_us) + udelay(rdev->enable_time_us); - ri->enable_count++; + rdev->enable_count++; return 0; } -static int regulator_disable_internal(struct regulator_internal *ri) +static int regulator_disable_rdev(struct regulator_dev *rdev) { int ret; - if (!ri->enable_count) + if (!rdev->enable_count) return -EINVAL; - if (!ri->rdev->desc->ops->disable) + if (rdev->enable_count > 1) { + rdev->enable_count--; + return 0; + } + + /* Crisis averted, be loud about the unbalanced regulator use */ + if (WARN_ON(rdev->always_on)) + return -EPERM; + + if (!rdev->desc->ops->disable) return -ENOSYS; - ret = ri->rdev->desc->ops->disable(ri->rdev); + ret = rdev->desc->ops->disable(rdev); if (ret) return ret; - ri->enable_count--; + rdev->enable_count--; - return 0; + return regulator_disable(rdev->supply); } -static int regulator_set_voltage_internal(struct regulator_internal *ri, +static int regulator_set_voltage_rdev(struct regulator_dev *rdev, int min_uV, int max_uV) { - struct regulator_dev *rdev = ri->rdev; const struct regulator_ops *ops = rdev->desc->ops; unsigned int selector; int best_val = 0; @@ -122,89 +125,233 @@ static int regulator_set_voltage_internal(struct regulator_internal *ri, return -ENOSYS; } -static struct regulator_internal * __regulator_register(struct regulator_dev *rd, const char *name) +static int regulator_get_voltage_rdev(struct regulator_dev *rdev) { - struct regulator_internal *ri; - int ret; + int sel, ret; + + if (rdev->desc->ops->get_voltage_sel) { + sel = rdev->desc->ops->get_voltage_sel(rdev); + if (sel < 0) + return sel; + ret = rdev->desc->ops->list_voltage(rdev, sel); + } else if (rdev->desc->ops->get_voltage) { + ret = rdev->desc->ops->get_voltage(rdev); + } else if (rdev->desc->ops->list_voltage) { + ret = rdev->desc->ops->list_voltage(rdev, 0); + } else if (rdev->desc->fixed_uV && (rdev->desc->n_voltages == 1)) { + ret = rdev->desc->fixed_uV; + } else if (rdev->min_uv && rdev->min_uv == rdev->max_uv) { + ret = rdev->min_uv; + } else if (rdev->supply) { + ret = regulator_get_voltage(rdev->supply); + } else { + return -EINVAL; + } + + return ret; +} + +static int regulator_resolve_supply(struct regulator_dev *rdev) +{ + struct regulator *supply; + const char *supply_name; + + if (!rdev || rdev->supply) + return 0; + + supply_name = rdev->desc->supply_name; + if (!supply_name) + return 0; + + rdev_dbg(rdev, "resolving %s\n", supply_name); + + supply = regulator_get(rdev->dev, supply_name); + if (IS_ERR(supply)) { + if (deep_probe_is_supported()) + return PTR_ERR(supply); + + /* For historic reasons, some regulator consumers don't handle + * -EPROBE_DEFER (e.g. vmmc-supply). If we now start propagating + * parent EPROBE_DEFER, previously requested vmmc-supply with + * always-on parent that worked before will end up not being + * requested breaking MMC use. So for non-deep probe systems, + * just make best effort to resolve, but don't fail the get if + * we couldn't. If you want to get rid of this warning, consider + * migrating your platform to have deep probe support. + */ + rdev_warn(rdev, "Failed to get '%s' regulator (ignored).\n", + supply_name); + return 0; + } + + if (supply) + supply->rdev_consumer = rdev; + + rdev->supply = supply; + + return 0; +} + +static int regulator_init_voltage(struct regulator_dev *rdev) +{ + int target_min, target_max, current_uV, ret; + + if (!rdev->min_uv || !rdev->max_uv) + return 0; + + current_uV = regulator_get_voltage_rdev(rdev); + if (current_uV < 0) { + /* This regulator can't be read and must be initialized */ + rdev_info(rdev, "Setting %d-%duV\n", rdev->min_uv, rdev->max_uv); + regulator_set_voltage_rdev(rdev, rdev->min_uv, rdev->max_uv); + current_uV = regulator_get_voltage_rdev(rdev); + } + + if (current_uV < 0) { + if (current_uV != -EPROBE_DEFER) + rdev_err(rdev, + "failed to get the current voltage: %pe\n", + ERR_PTR(current_uV)); + return current_uV; + } + + /* + * If we're below the minimum voltage move up to the + * minimum voltage, if we're above the maximum voltage + * then move down to the maximum. + */ + target_min = current_uV; + target_max = current_uV; + + if (current_uV < rdev->min_uv) { + target_min = rdev->min_uv; + target_max = rdev->min_uv; + } + + if (current_uV > rdev->max_uv) { + target_min = rdev->max_uv; + target_max = rdev->max_uv; + } + + if (target_min != current_uV || target_max != current_uV) { + rdev_info(rdev, "Bringing %duV into %d-%duV\n", + current_uV, target_min, target_max); + ret = regulator_set_voltage_rdev(rdev, target_min, target_max); + if (ret < 0) { + rdev_err(rdev, + "failed to apply %d-%duV constraint: %pe\n", + target_min, target_max, ERR_PTR(ret)); + return ret; + } + } + + return 0; +} - ri = xzalloc(sizeof(*ri)); - ri->rdev = rd; +static int __regulator_register(struct regulator_dev *rdev, const char *name) +{ + int ret; - INIT_LIST_HEAD(&ri->consumer_list); + INIT_LIST_HEAD(&rdev->consumer_list); - list_add_tail(&ri->list, ®ulator_list); + list_add_tail(&rdev->list, ®ulator_list); if (name) - ri->name = xstrdup(name); + rdev->name = xstrdup(name); - if (rd->boot_on) { - ret = regulator_enable_internal(ri); + ret = regulator_init_voltage(rdev); + if (ret) + goto err; + + if (rdev->boot_on || rdev->always_on) { + ret = regulator_resolve_supply(rdev); + if (ret < 0) + goto err; + + ret = regulator_enable_rdev(rdev); if (ret && ret != -ENOSYS) goto err; } - return ri; + return 0; err: - list_del(&ri->list); - free(ri->name); - free(ri); + list_del(&rdev->list); + free((char *)rdev->name); - return ERR_PTR(ret); + return ret; } #ifdef CONFIG_OFDEVICE /* * of_regulator_register - register a regulator corresponding to a device_node - * @rd: the regulator device providing the ops + * rdev: the regulator device providing the ops * @node: the device_node this regulator corresponds to * * Return: 0 for success or a negative error code */ -int of_regulator_register(struct regulator_dev *rd, struct device_node *node) +int of_regulator_register(struct regulator_dev *rdev, struct device_node *node) { - struct regulator_internal *ri; const char *name; + int ret; + + if (!rdev || !node) + return -EINVAL; - rd->boot_on = of_property_read_bool(node, "regulator-boot-on"); + rdev->boot_on = of_property_read_bool(node, "regulator-boot-on"); + rdev->always_on = of_property_read_bool(node, "regulator-always-on"); name = of_get_property(node, "regulator-name", NULL); + if (!name) + name = node->name; - ri = __regulator_register(rd, name); - ri->node = node; + rdev->node = node; + node->dev = rdev->dev; + + if (rdev->desc->off_on_delay) + rdev->enable_time_us = rdev->desc->off_on_delay; + + if (rdev->desc->fixed_uV && rdev->desc->n_voltages == 1) + rdev->min_uv = rdev->max_uv = rdev->desc->fixed_uV; of_property_read_u32(node, "regulator-enable-ramp-delay", - &ri->enable_time_us); + &rdev->enable_time_us); of_property_read_u32(node, "regulator-min-microvolt", - &ri->min_uv); + &rdev->min_uv); of_property_read_u32(node, "regulator-max-microvolt", - &ri->max_uv); + &rdev->max_uv); + + ret = __regulator_register(rdev, name); + if (ret) + return ret; return 0; } -static struct regulator_internal *of_regulator_get(struct device_d *dev, const char *supply) +static struct regulator_dev *of_regulator_get(struct device *dev, + const char *supply) { char *propname; - struct regulator_internal *ri; - struct device_node *node; - - propname = basprintf("%s-supply", supply); + struct regulator_dev *rdev; + struct device_node *node, *node_parent; + int ret; /* * If the device does have a device node return the dummy regulator. */ - if (!dev->device_node) + if (!dev->of_node) return NULL; + propname = basprintf("%s-supply", supply); + /* * If the device node does not contain a supply property, this device doesn't * need a regulator. Return the dummy regulator in this case. */ - if (!of_get_property(dev->device_node, propname, NULL)) { + if (!of_get_property(dev->of_node, propname, NULL)) { dev_dbg(dev, "No %s-supply node found, using dummy regulator\n", supply); - ri = NULL; + rdev = NULL; goto out; } @@ -212,17 +359,38 @@ static struct regulator_internal *of_regulator_get(struct device_d *dev, const c * The device node specifies a supply, so it's mandatory. Return an error when * something goes wrong below. */ - node = of_parse_phandle(dev->device_node, propname, 0); + node = of_parse_phandle(dev->of_node, propname, 0); if (!node) { dev_dbg(dev, "No %s node found\n", propname); - ri = ERR_PTR(-EINVAL); + rdev = ERR_PTR(-EINVAL); + goto out; + } + + ret = of_device_ensure_probed(node); + if (ret) { + /* + * If "barebox,allow-dummy-supply" property is set for regulator + * provider allow use of dummy regulator (NULL is returned). + * Check regulator node and its parent if this setting is set + * PMIC wide. + */ + node_parent = of_get_parent(node); + if (of_get_property(node, "barebox,allow-dummy-supply", NULL) || + of_get_property(node_parent, "barebox,allow-dummy-supply", NULL)) { + dev_dbg(dev, "Allow use of dummy regulator for " \ + "%s-supply\n", supply); + rdev = NULL; + goto out; + } + + rdev = ERR_PTR(ret); goto out; } - list_for_each_entry(ri, ®ulator_list, list) { - if (ri->node == node) { - dev_dbg(dev, "Using %s regulator from %s\n", - propname, node->full_name); + list_for_each_entry(rdev, ®ulator_list, list) { + if (rdev->node == node) { + dev_dbg(dev, "Using %s regulator from %pOF\n", + propname, node); goto out; } } @@ -232,52 +400,54 @@ static struct regulator_internal *of_regulator_get(struct device_d *dev, const c * added in future initcalls, so, instead of reporting a * complete failure report probe deferral */ - ri = ERR_PTR(-EPROBE_DEFER); + rdev = ERR_PTR(-EPROBE_DEFER); out: free(propname); - return ri; + return rdev; } #else -static struct regulator_internal *of_regulator_get(struct device_d *dev, const char *supply) +static struct regulator_dev *of_regulator_get(struct device *dev, + const char *supply) { return NULL; } #endif -int dev_regulator_register(struct regulator_dev *rd, const char * name, const char* supply) +int dev_regulator_register(struct regulator_dev *rdev, const char *name) { - struct regulator_internal *ri; - - ri = __regulator_register(rd, name); + int ret; - ri->supply = supply; + ret = __regulator_register(rdev, name); + if (ret) + return ret; return 0; } -static struct regulator_internal *dev_regulator_get(struct device_d *dev, const char *supply) +static struct regulator_dev *dev_regulator_get(struct device *dev, + const char *supply) { - struct regulator_internal *ri; - struct regulator_internal *ret = NULL; + struct regulator_dev *rdev; + struct regulator_dev *ret = NULL; int match, best = 0; const char *dev_id = dev ? dev_name(dev) : NULL; - list_for_each_entry(ri, ®ulator_list, list) { + list_for_each_entry(rdev, ®ulator_list, list) { match = 0; - if (ri->name) { - if (!dev_id || strcmp(ri->name, dev_id)) + if (rdev->name) { + if (!dev_id || strcmp(rdev->name, dev_id)) continue; match += 2; } - if (ri->supply) { - if (!supply || strcmp(ri->supply, supply)) + if (rdev->desc->supply_name) { + if (!supply || strcmp(rdev->desc->supply_name, supply)) continue; match += 1; } if (match > best) { - ret = ri; + ret = rdev; if (match != 3) best = match; else @@ -298,31 +468,73 @@ static struct regulator_internal *dev_regulator_get(struct device_d *dev, const * * Return: a regulator object or an error pointer */ -struct regulator *regulator_get(struct device_d *dev, const char *supply) +struct regulator *regulator_get(struct device *dev, const char *supply) { - struct regulator_internal *ri = NULL; + struct regulator_dev *rdev = NULL; struct regulator *r; + int ret; - if (dev->device_node) { - ri = of_regulator_get(dev, supply); - if (IS_ERR(ri)) - return ERR_CAST(ri); + if (dev->of_node && supply) { + rdev = of_regulator_get(dev, supply); + if (IS_ERR(rdev)) + return ERR_CAST(rdev); } - if (!ri) { - ri = dev_regulator_get(dev, supply); - if (IS_ERR(ri)) - return ERR_CAST(ri); + if (!rdev) { + rdev = dev_regulator_get(dev, supply); + if (IS_ERR(rdev)) + return ERR_CAST(rdev); } - if (!ri) + if (!rdev) return NULL; + ret = regulator_resolve_supply(rdev); + if (ret < 0) + return ERR_PTR(ret); + r = xzalloc(sizeof(*r)); - r->ri = ri; + r->rdev = rdev; r->dev = dev; - list_add_tail(&r->list, &ri->consumer_list); + list_add_tail(&r->list, &rdev->consumer_list); + + return r; +} + +void regulator_put(struct regulator *r) +{ + if (IS_ERR_OR_NULL(r)) + return; + list_del(&r->list); + free(r); +} + +static struct regulator_dev *regulator_by_name(const char *name) +{ + struct regulator_dev *rdev; + + list_for_each_entry(rdev, ®ulator_list, list) { + if (rdev->name && !strcmp(rdev->name, name)) + return rdev; + } + + return NULL; +} + +struct regulator *regulator_get_name(const char *name) +{ + struct regulator_dev *rdev; + struct regulator *r; + + rdev = regulator_by_name(name); + if (!rdev) + return ERR_PTR(-ENODEV); + + r = xzalloc(sizeof(*r)); + r->rdev = rdev; + + list_add_tail(&r->list, &rdev->consumer_list); return r; } @@ -341,7 +553,7 @@ int regulator_enable(struct regulator *r) if (!r) return 0; - return regulator_enable_internal(r->ri); + return regulator_enable_rdev(r->rdev); } /* @@ -358,7 +570,7 @@ int regulator_disable(struct regulator *r) if (!r) return 0; - return regulator_disable_internal(r->ri); + return regulator_disable_rdev(r->rdev); } int regulator_set_voltage(struct regulator *r, int min_uV, int max_uV) @@ -366,31 +578,199 @@ int regulator_set_voltage(struct regulator *r, int min_uV, int max_uV) if (!r) return 0; - return regulator_set_voltage_internal(r->ri, min_uV, max_uV); + return regulator_set_voltage_rdev(r->rdev, min_uV, max_uV); +} + +/** + * regulator_bulk_get - get multiple regulator consumers + * + * @dev: Device to supply + * @num_consumers: Number of consumers to register + * @consumers: Configuration of consumers; clients are stored here. + * + * @return 0 on success, an errno on failure. + * + * This helper function allows drivers to get several regulator + * consumers in one operation. If any of the regulators cannot be + * acquired then any regulators that were allocated will be freed + * before returning to the caller. + */ +int regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers) +{ + int i; + int ret; + + for (i = 0; i < num_consumers; i++) + consumers[i].consumer = NULL; + + for (i = 0; i < num_consumers; i++) { + consumers[i].consumer = regulator_get(dev, + consumers[i].supply); + if (IS_ERR(consumers[i].consumer)) { + ret = PTR_ERR(consumers[i].consumer); + consumers[i].consumer = NULL; + goto err; + } + } + + return 0; + +err: + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get supply '%s': %d\n", + consumers[i].supply, ret); + else + dev_dbg(dev, "Failed to get supply '%s', deferring\n", + consumers[i].supply); + + return ret; } +EXPORT_SYMBOL_GPL(regulator_bulk_get); -static void regulator_print_one(struct regulator_internal *ri) +/** + * regulator_bulk_enable - enable multiple regulator consumers + * + * @num_consumers: Number of consumers + * @consumers: Consumer data; clients are stored here. + * @return 0 on success, an errno on failure + * + * This convenience API allows consumers to enable multiple regulator + * clients in a single API call. If any consumers cannot be enabled + * then any others that were enabled will be disabled again prior to + * return. + */ +int regulator_bulk_enable(int num_consumers, + struct regulator_bulk_data *consumers) { + int ret; + int i; + + for (i = 0; i < num_consumers; i++) { + ret = regulator_enable(consumers[i].consumer); + if (ret) + goto err; + } + + return 0; + +err: + while (--i >= 0) + regulator_disable(consumers[i].consumer); + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_bulk_enable); + +/** + * regulator_bulk_disable - disable multiple regulator consumers + * + * @num_consumers: Number of consumers + * @consumers: Consumer data; clients are stored here. + * @return 0 on success, an errno on failure + * + * This convenience API allows consumers to disable multiple regulator + * clients in a single API call. If any consumers cannot be disabled + * then any others that were disabled will be enabled again prior to + * return. + */ +int regulator_bulk_disable(int num_consumers, + struct regulator_bulk_data *consumers) +{ + int i; + int ret, r; + + for (i = num_consumers - 1; i >= 0; --i) { + ret = regulator_disable(consumers[i].consumer); + if (ret != 0) + goto err; + } + + return 0; + +err: + pr_err("Failed to disable %s: %d\n", consumers[i].supply, ret); + for (++i; i < num_consumers; ++i) { + r = regulator_enable(consumers[i].consumer); + if (r != 0) + pr_err("Failed to re-enable %s: %d\n", + consumers[i].supply, r); + } + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_bulk_disable); + +/** + * regulator_bulk_free - free multiple regulator consumers + * + * @num_consumers: Number of consumers + * @consumers: Consumer data; clients are stored here. + * + * This convenience API allows consumers to free multiple regulator + * clients in a single API call. + */ +void regulator_bulk_free(int num_consumers, + struct regulator_bulk_data *consumers) +{ + int i; + + for (i = 0; i < num_consumers; i++) + consumers[i].consumer = NULL; +} +EXPORT_SYMBOL_GPL(regulator_bulk_free); + +int regulator_get_voltage(struct regulator *regulator) +{ + if (!regulator) + return -EINVAL; + + return regulator_get_voltage_rdev(regulator->rdev); +} +EXPORT_SYMBOL_GPL(regulator_get_voltage); + +static int regulator_name_indent(unsigned flags) +{ + return 30 + (flags & REGULATOR_PRINT_DEVS ? 50 : 0); +} + +static void regulator_print_one(struct regulator_dev *rdev, int level, unsigned flags) +{ + const char *name = rdev->name; struct regulator *r; + char buf[256]; + + if (!rdev) + return; - printf("%-20s %6d %10d %10d\n", ri->name, ri->enable_count, ri->min_uv, ri->max_uv); + if (flags & REGULATOR_PRINT_DEVS) { + snprintf(buf, sizeof(buf), "%s %s", dev_name(rdev->dev), rdev->name); + name = buf; + } - if (!list_empty(&ri->consumer_list)) { - printf(" consumers:\n"); + printf("%*s%-*s %6d %10d %10d\n", level * 3, "", + regulator_name_indent(flags) - level * 3, + name, rdev->enable_count, rdev->min_uv, rdev->max_uv); - list_for_each_entry(r, &ri->consumer_list, list) - printf(" %s\n", dev_name(r->dev)); + list_for_each_entry(r, &rdev->consumer_list, list) { + if (r->rdev_consumer) + regulator_print_one(r->rdev_consumer, level + 1, flags); + else + printf("%*s%s\n", (level + 1) * 3, "", r->dev ? dev_name(r->dev) : "none"); } } /* * regulators_print - print informations about all regulators */ -void regulators_print(void) +void regulators_print(unsigned flags) { - struct regulator_internal *ri; + struct regulator_dev *rdev; - printf("%-20s %6s %10s %10s\n", "name", "enable", "min_uv", "max_uv"); - list_for_each_entry(ri, ®ulator_list, list) - regulator_print_one(ri); + printf("%-*s %6s %10s %10s\n", regulator_name_indent(flags), + "name", "enable", "min_uv", "max_uv"); + list_for_each_entry(rdev, ®ulator_list, list) { + if (!rdev->supply) + regulator_print_one(rdev, 0, flags); + } } diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index cb5d785817..0edb5ceb10 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -1,33 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * fixed regulator support * * Copyright (c) 2014 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> #include <malloc.h> #include <init.h> #include <regulator.h> #include <of.h> -#include <of_gpio.h> -#include <gpio.h> +#include <linux/gpio/consumer.h> struct regulator_fixed { - int gpio; - int active_low; - int always_on; + struct gpio_desc *gpio; struct regulator_dev rdev; struct regulator_desc rdesc; }; @@ -36,23 +21,14 @@ static int regulator_fixed_enable(struct regulator_dev *rdev) { struct regulator_fixed *fix = container_of(rdev, struct regulator_fixed, rdev); - if (!gpio_is_valid(fix->gpio)) - return 0; - - return gpio_direction_output(fix->gpio, !fix->active_low); + return gpiod_direction_output(fix->gpio, true); } static int regulator_fixed_disable(struct regulator_dev *rdev) { struct regulator_fixed *fix = container_of(rdev, struct regulator_fixed, rdev); - if (fix->always_on) - return 0; - - if (!gpio_is_valid(fix->gpio)) - return 0; - - return gpio_direction_output(fix->gpio, fix->active_low); + return gpiod_direction_output(fix->gpio, false); } const static struct regulator_ops fixed_ops = { @@ -60,40 +36,37 @@ const static struct regulator_ops fixed_ops = { .disable = regulator_fixed_disable, }; -static int regulator_fixed_probe(struct device_d *dev) +static int regulator_fixed_probe(struct device *dev) { + struct device_node *np = dev->of_node; struct regulator_fixed *fix; - enum of_gpio_flags gpioflags; + u32 delay; int ret; - if (!dev->device_node) + if (!dev->of_node) return -EINVAL; fix = xzalloc(sizeof(*fix)); - fix->gpio = -EINVAL; - - if (of_get_property(dev->device_node, "gpio", NULL)) { - fix->gpio = of_get_named_gpio_flags(dev->device_node, "gpio", 0, &gpioflags); - if (fix->gpio < 0) { - ret = fix->gpio; - goto err; - } - if (gpioflags & OF_GPIO_ACTIVE_LOW) - fix->active_low = 1; + fix->gpio = gpiod_get_optional(dev, NULL, GPIOD_ASIS); + if (IS_ERR(fix->gpio)) { + ret = PTR_ERR(fix->gpio); + goto err; } fix->rdesc.ops = &fixed_ops; fix->rdev.desc = &fix->rdesc; + fix->rdev.dev = dev; - if (of_find_property(dev->device_node, "regulator-always-on", NULL)) { - fix->always_on = 1; - regulator_fixed_enable(&fix->rdev); - } + if (!of_property_read_u32(np, "off-on-delay-us", &delay)) + fix->rdesc.off_on_delay = delay; + + if (of_find_property(np, "vin-supply", NULL)) + fix->rdesc.supply_name = "vin"; - ret = of_regulator_register(&fix->rdev, dev->device_node); + ret = of_regulator_register(&fix->rdev, np); if (ret) - return ret; + goto err; return 0; err: @@ -106,8 +79,9 @@ static struct of_device_id regulator_fixed_of_ids[] = { { .compatible = "regulator-fixed", }, { } }; +MODULE_DEVICE_TABLE(of, regulator_fixed_of_ids); -static struct driver_d regulator_fixed_driver = { +static struct driver regulator_fixed_driver = { .name = "regulator-fixed", .probe = regulator_fixed_probe, .of_compatible = DRV_OF_COMPAT(regulator_fixed_of_ids), diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index c4877cecf7..80102e2c10 100644 --- a/drivers/regulator/helpers.c +++ b/drivers/regulator/helpers.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> -#include <regmap.h> +#include <linux/regmap.h> #include <regulator.h> /** @@ -369,4 +370,29 @@ int regulator_map_voltage_iterate(struct regulator_dev *rdev, } EXPORT_SYMBOL_GPL(regulator_map_voltage_iterate); +/** + * regulator_list_voltage_table - List voltages with table based mapping + * + * @rdev: Regulator device + * @selector: Selector to convert into a voltage + * + * Regulators with table based mapping between voltages and + * selectors can set volt_table in the regulator descriptor + * and then use this function as their list_voltage() operation. + */ +int regulator_list_voltage_table(struct regulator_dev *rdev, + unsigned int selector) +{ + if (!rdev->desc->volt_table) { + BUG_ON(!rdev->desc->volt_table); + return -EINVAL; + } + if (selector >= rdev->desc->n_voltages) + return -EINVAL; + if (selector < rdev->desc->linear_min_sel) + return 0; + + return rdev->desc->volt_table[selector]; +} +EXPORT_SYMBOL_GPL(regulator_list_voltage_table); diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 3e8caa8710..10f75a4f1c 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -9,145 +9,6 @@ #include <common.h> #include <of.h> #include <linux/regulator/of_regulator.h> -#include <linux/regulator/machine.h> - -static int of_get_regulation_constraints(struct device_d *dev, - struct device_node *np, - struct regulator_init_data **init_data, - const struct regulator_desc *desc) -{ - struct regulation_constraints *constraints = &(*init_data)->constraints; - int ret; - u32 pval; - - constraints->name = of_get_property(np, "regulator-name", NULL); - - if (!of_property_read_u32(np, "regulator-min-microvolt", &pval)) - constraints->min_uV = pval; - - if (!of_property_read_u32(np, "regulator-max-microvolt", &pval)) - constraints->max_uV = pval; - - /* Voltage change possible? */ - if (constraints->min_uV != constraints->max_uV) - constraints->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE; - - /* Do we have a voltage range, if so try to apply it? */ - if (constraints->min_uV && constraints->max_uV) - constraints->apply_uV = true; - - if (!of_property_read_u32(np, "regulator-microvolt-offset", &pval)) - constraints->uV_offset = pval; - if (!of_property_read_u32(np, "regulator-min-microamp", &pval)) - constraints->min_uA = pval; - if (!of_property_read_u32(np, "regulator-max-microamp", &pval)) - constraints->max_uA = pval; - - if (!of_property_read_u32(np, "regulator-input-current-limit-microamp", - &pval)) - constraints->ilim_uA = pval; - - /* Current change possible? */ - if (constraints->min_uA != constraints->max_uA) - constraints->valid_ops_mask |= REGULATOR_CHANGE_CURRENT; - - constraints->boot_on = of_property_read_bool(np, "regulator-boot-on"); - constraints->always_on = of_property_read_bool(np, "regulator-always-on"); - if (!constraints->always_on) /* status change should be possible. */ - constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS; - - constraints->pull_down = of_property_read_bool(np, "regulator-pull-down"); - - if (of_property_read_bool(np, "regulator-allow-bypass")) - constraints->valid_ops_mask |= REGULATOR_CHANGE_BYPASS; - - if (of_property_read_bool(np, "regulator-allow-set-load")) - constraints->valid_ops_mask |= REGULATOR_CHANGE_DRMS; - - ret = of_property_read_u32(np, "regulator-ramp-delay", &pval); - if (!ret) { - if (pval) - constraints->ramp_delay = pval; - else - constraints->ramp_disable = true; - } - - ret = of_property_read_u32(np, "regulator-settling-time-us", &pval); - if (!ret) - constraints->settling_time = pval; - - ret = of_property_read_u32(np, "regulator-settling-time-up-us", &pval); - if (!ret) - constraints->settling_time_up = pval; - if (constraints->settling_time_up && constraints->settling_time) { - pr_warn("%pOFn: ambiguous configuration for settling time, ignoring 'regulator-settling-time-up-us'\n", - np); - constraints->settling_time_up = 0; - } - - ret = of_property_read_u32(np, "regulator-settling-time-down-us", - &pval); - if (!ret) - constraints->settling_time_down = pval; - if (constraints->settling_time_down && constraints->settling_time) { - pr_warn("%pOFn: ambiguous configuration for settling time, ignoring 'regulator-settling-time-down-us'\n", - np); - constraints->settling_time_down = 0; - } - - ret = of_property_read_u32(np, "regulator-enable-ramp-delay", &pval); - if (!ret) - constraints->enable_time = pval; - - constraints->soft_start = of_property_read_bool(np, - "regulator-soft-start"); - ret = of_property_read_u32(np, "regulator-active-discharge", &pval); - if (!ret) { - constraints->active_discharge = - (pval) ? REGULATOR_ACTIVE_DISCHARGE_ENABLE : - REGULATOR_ACTIVE_DISCHARGE_DISABLE; - } - - if (!of_property_read_u32(np, "regulator-system-load", &pval)) - constraints->system_load = pval; - - if (!of_property_read_u32(np, "regulator-max-step-microvolt", - &pval)) - constraints->max_uV_step = pval; - - constraints->over_current_protection = of_property_read_bool(np, - "regulator-over-current-protection"); - - return 0; -} - -/** - * of_get_regulator_init_data - extract regulator_init_data structure info - * @dev: device requesting for regulator_init_data - * @node: regulator device node - * @desc: regulator description - * - * Populates regulator_init_data structure by extracting data from device - * tree node, returns a pointer to the populated structure or NULL if memory - * alloc fails. - */ -struct regulator_init_data *of_get_regulator_init_data(struct device_d *dev, - struct device_node *node, - const struct regulator_desc *desc) -{ - struct regulator_init_data *init_data; - - if (!node) - return NULL; - - init_data = xzalloc(sizeof(*init_data)); - - if (of_get_regulation_constraints(dev, node, &init_data, desc)) - return NULL; - - return init_data; -} -EXPORT_SYMBOL_GPL(of_get_regulator_init_data); struct devm_of_regulator_matches { struct of_regulator_match *matches; @@ -172,7 +33,7 @@ struct devm_of_regulator_matches { * * Returns the number of matches found or a negative error code on failure. */ -int of_regulator_match(struct device_d *dev, struct device_node *node, +int of_regulator_match(struct device *dev, struct device_node *node, struct of_regulator_match *matches, unsigned int num_matches) { @@ -192,7 +53,6 @@ int of_regulator_match(struct device_d *dev, struct device_node *node, for (i = 0; i < num_matches; i++) { struct of_regulator_match *match = &matches[i]; - match->init_data = NULL; match->of_node = NULL; } @@ -210,14 +70,6 @@ int of_regulator_match(struct device_d *dev, struct device_node *node, if (strcmp(match->name, name)) continue; - match->init_data = of_get_regulator_init_data(dev, child, - match->desc); - if (!match->init_data) { - dev_err(dev, - "failed to parse DT for regulator %pOFn\n", - child); - return -EINVAL; - } match->of_node = child; count++; break; diff --git a/drivers/regulator/pfuze.c b/drivers/regulator/pfuze.c index 1950ffb04c..3e8890b10c 100644 --- a/drivers/regulator/pfuze.c +++ b/drivers/regulator/pfuze.c @@ -1,17 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2017 Sascha Hauer, Pengutronix - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * */ #include <common.h> @@ -21,18 +10,15 @@ #include <errno.h> #include <malloc.h> #include <of.h> -#include <regmap.h> +#include <linux/regmap.h> #include <mfd/pfuze.h> #include <i2c/i2c.h> #include <poweroff.h> -#include <mach/imx6.h> #define DRIVERNAME "pfuze" -#define MC13XXX_NUMREGS 0x3f - #define PFUZE100_SW1ABMODE 0x23 #define PFUZE100_SW1CMODE 0x31 #define PFUZE100_SW2MODE 0x38 @@ -54,7 +40,7 @@ #define PFUZE100_VGENxSTBY BIT(5) struct pfuze { - struct device_d *dev; + struct device *dev; struct regmap *map; struct i2c_client *client; int revision; @@ -144,8 +130,6 @@ static void pfuze_power_off_prepare(struct poweroff_handler *handler) regmap_write_bits(pfuze_dev->map, PFUZE100_VGEN6VOL, PFUZE100_VGENxLPWR | PFUZE100_VGENxSTBY, PFUZE100_VGENxSTBY); - - imx6_pm_stby_poweroff(); } static struct regmap_bus regmap_pfuze_i2c_bus = { @@ -159,7 +143,7 @@ static const struct regmap_config pfuze_regmap_i2c_config = { .max_register = 127, }; -static int __init pfuze_probe(struct device_d *dev) +static int __init pfuze_probe(struct device *dev) { int ret; @@ -180,7 +164,7 @@ static int __init pfuze_probe(struct device_d *dev) if (pfuze_init_callback) pfuze_init_callback(pfuze_dev->map); - if (of_property_read_bool(dev->device_node, + if (of_property_read_bool(dev->of_node, "fsl,pmic-stby-poweroff")) return poweroff_handler_register_fn(pfuze_power_off_prepare); @@ -202,23 +186,13 @@ static __maybe_unused struct of_device_id pfuze_dt_ids[] = { { .compatible = "fsl,pfuze3001" }, { } }; +MODULE_DEVICE_TABLE(of, pfuze_dt_ids); -static struct driver_d pfuze_i2c_driver = { +static struct driver pfuze_i2c_driver = { .name = "pfuze-i2c", .probe = pfuze_probe, .id_table = pfuze_ids, .of_compatible = DRV_OF_COMPAT(pfuze_dt_ids), }; -static int __init pfuze_init(void) -{ - int ret; - - ret = i2c_driver_register(&pfuze_i2c_driver); - if (ret) - return ret; - - return 0; - -} -late_initcall(pfuze_init); +device_i2c_driver(pfuze_i2c_driver); diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c new file mode 100644 index 0000000000..adb0262314 --- /dev/null +++ b/drivers/regulator/rk808-regulator.c @@ -0,0 +1,963 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Regulator driver for Rockchip RK808 + * + * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd + * + * Author: Chris Zhong <zyw@rock-chips.com> + * Author: Zhang Qing <zhangqing@rock-chips.com> + */ + +#include <common.h> +#include <driver.h> +#include <gpio.h> +#include <init.h> +#include <i2c/i2c.h> +#include <of_device.h> +#include <linux/regmap.h> +#include <linux/regulator/of_regulator.h> +#include <regulator.h> +#include <linux/mfd/rk808.h> + +/* Field Definitions */ +#define RK808_BUCK_VSEL_MASK 0x3f +#define RK808_BUCK4_VSEL_MASK 0xf +#define RK808_LDO_VSEL_MASK 0x1f + +#define RK809_BUCK5_VSEL_MASK 0x7 + +#define RK817_LDO_VSEL_MASK 0x7f +#define RK817_BOOST_VSEL_MASK 0x7 +#define RK817_BUCK_VSEL_MASK 0x7f + +#define RK818_BUCK_VSEL_MASK 0x3f +#define RK818_BUCK4_VSEL_MASK 0x1f +#define RK818_LDO_VSEL_MASK 0x1f +#define RK818_LDO3_ON_VSEL_MASK 0xf +#define RK818_BOOST_ON_VSEL_MASK 0xe0 + +#define ENABLE_MASK(id) (BIT(id) | BIT(4 + (id))) +#define DISABLE_VAL(id) (BIT(4 + (id))) + +#define RK817_BOOST_DESC(_supply_name, _min, _max, _step, _vreg,\ + _vmask, _ereg, _emask, _enval, _disval, _etime) \ + {{ \ + .supply_name = (_supply_name), \ + .n_voltages = (((_max) - (_min)) / (_step) + 1), \ + .min_uV = (_min) * 1000, \ + .uV_step = (_step) * 1000, \ + .vsel_reg = (_vreg), \ + .vsel_mask = (_vmask), \ + .enable_reg = (_ereg), \ + .enable_mask = (_emask), \ + .enable_val = (_enval), \ + .disable_val = (_disval), \ + .off_on_delay = (_etime), \ + .ops = &rk817_boost_ops, \ + }} + +#define RK8XX_DESC_COM(_supply_name, _min, _max, _step, _vreg, \ + _vmask, _ereg, _emask, _enval, _disval, _etime, _ops) \ + {{ \ + .supply_name = (_supply_name), \ + .n_voltages = (((_max) - (_min)) / (_step) + 1), \ + .min_uV = (_min) * 1000, \ + .uV_step = (_step) * 1000, \ + .vsel_reg = (_vreg), \ + .vsel_mask = (_vmask), \ + .enable_reg = (_ereg), \ + .enable_mask = (_emask), \ + .enable_val = (_enval), \ + .disable_val = (_disval), \ + .off_on_delay = (_etime), \ + .ops = _ops, \ + }} + +#define RK805_DESC(_supply_name, _min, _max, _step, _vreg, \ + _vmask, _ereg, _emask, _etime) \ + RK8XX_DESC_COM(_supply_name, _min, _max, _step, _vreg, \ + _vmask, _ereg, _emask, 0, 0, _etime, &rk808_reg_ops) + +#define RK8XX_DESC(_supply_name, _min, _max, _step, _vreg, \ + _vmask, _ereg, _emask, _etime) \ + RK8XX_DESC_COM(_supply_name, _min, _max, _step, _vreg, \ + _vmask, _ereg, _emask, 0, 0, _etime, &rk808_reg_ops) + +#define RK817_DESC(_supply_name, _min, _max, _step, _vreg, \ + _vmask, _ereg, _emask, _disval, _etime) \ + RK8XX_DESC_COM(_supply_name, _min, _max, _step, _vreg, \ + _vmask, _ereg, _emask, _emask, _disval, _etime, &rk817_reg_ops) + +#define RKXX_DESC_SWITCH_COM(_supply_name,_ereg, _emask, \ + _enval, _disval, _ops) \ + {{ \ + .supply_name = (_supply_name), \ + .enable_reg = (_ereg), \ + .enable_mask = (_emask), \ + .enable_val = (_enval), \ + .disable_val = (_disval), \ + .ops = _ops \ + }} + +#define RK817_DESC_SWITCH(_supply_name, _ereg, _emask, \ + _disval) \ + RKXX_DESC_SWITCH_COM(_supply_name, _ereg, _emask, \ + _emask, _disval, &rk817_switch_ops) + +#define RK8XX_DESC_SWITCH(_supply_name, _ereg, _emask) \ + RKXX_DESC_SWITCH_COM(_supply_name, _ereg, _emask, \ + 0, 0, &rk808_switch_ops) + +static const struct regulator_linear_range rk808_ldo3_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(800000, 0, 13, 100000), + REGULATOR_LINEAR_RANGE(2500000, 15, 15, 0), +}; + +#define RK809_BUCK5_SEL_CNT (8) + +static const struct regulator_linear_range rk809_buck5_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(1500000, 0, 0, 0), + REGULATOR_LINEAR_RANGE(1800000, 1, 3, 200000), + REGULATOR_LINEAR_RANGE(2800000, 4, 5, 200000), + REGULATOR_LINEAR_RANGE(3300000, 6, 7, 300000), +}; + +#define RK817_BUCK1_MIN0 500000 +#define RK817_BUCK1_MAX0 1500000 + +#define RK817_BUCK1_MIN1 1600000 +#define RK817_BUCK1_MAX1 2400000 + +#define RK817_BUCK3_MAX1 3400000 + +#define RK817_BUCK1_STP0 12500 +#define RK817_BUCK1_STP1 100000 + +#define RK817_BUCK1_SEL0 ((RK817_BUCK1_MAX0 - RK817_BUCK1_MIN0) /\ + RK817_BUCK1_STP0) +#define RK817_BUCK1_SEL1 ((RK817_BUCK1_MAX1 - RK817_BUCK1_MIN1) /\ + RK817_BUCK1_STP1) + +#define RK817_BUCK3_SEL1 ((RK817_BUCK3_MAX1 - RK817_BUCK1_MIN1) /\ + RK817_BUCK1_STP1) + +#define RK817_BUCK1_SEL_CNT (RK817_BUCK1_SEL0 + RK817_BUCK1_SEL1 + 1) +#define RK817_BUCK3_SEL_CNT (RK817_BUCK1_SEL0 + RK817_BUCK3_SEL1 + 1) + +static const struct regulator_linear_range rk817_buck1_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(RK817_BUCK1_MIN0, 0, + RK817_BUCK1_SEL0, RK817_BUCK1_STP0), + REGULATOR_LINEAR_RANGE(RK817_BUCK1_MIN1, RK817_BUCK1_SEL0 + 1, + RK817_BUCK1_SEL_CNT, RK817_BUCK1_STP1), +}; + +static const struct regulator_linear_range rk817_buck3_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(RK817_BUCK1_MIN0, 0, + RK817_BUCK1_SEL0, RK817_BUCK1_STP0), + REGULATOR_LINEAR_RANGE(RK817_BUCK1_MIN1, RK817_BUCK1_SEL0 + 1, + RK817_BUCK3_SEL_CNT, RK817_BUCK1_STP1), +}; + +struct rk_regulator_cfg { + struct regulator_desc desc; + struct regulator_dev rdev; +}; + +static int rk8xx_is_enabled_wmsk_regmap(struct regulator_dev *rdev) +{ + unsigned int val; + int ret; + + ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val); + if (ret != 0) + return ret; + + /* add write mask bit */ + val |= (rdev->desc->enable_mask & 0xf0); + val &= rdev->desc->enable_mask; + + if (rdev->desc->enable_is_inverted) { + if (rdev->desc->enable_val) + return val != rdev->desc->enable_val; + return (val == 0); + } + if (rdev->desc->enable_val) + return val == rdev->desc->enable_val; + return val != 0; +} + +static struct regulator_ops rk808_buck1_2_ops = { + .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 struct regulator_ops rk808_reg_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static struct regulator_ops rk808_reg_ops_ranges = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static struct regulator_ops rk808_switch_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static const struct regulator_linear_range rk805_buck_1_2_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(712500, 0, 59, 12500), + REGULATOR_LINEAR_RANGE(1800000, 60, 62, 200000), + REGULATOR_LINEAR_RANGE(2300000, 63, 63, 0), +}; + +static const struct regulator_ops rk809_buck5_ops_range = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = rk8xx_is_enabled_wmsk_regmap, +}; + +static const struct regulator_ops rk817_reg_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = rk8xx_is_enabled_wmsk_regmap, +}; + +static const struct regulator_ops rk817_boost_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = rk8xx_is_enabled_wmsk_regmap, +}; + +static const struct regulator_ops rk817_buck_ops_range = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = rk8xx_is_enabled_wmsk_regmap, +}; + +static const struct regulator_ops rk817_switch_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = rk8xx_is_enabled_wmsk_regmap, +}; + +static struct rk_regulator_cfg rk805_reg[] = { + {{ + /* .name = "DCDC_REG1", */ + .supply_name = "vcc1", + .ops = &rk808_reg_ops_ranges, + .n_voltages = 64, + .linear_ranges = rk805_buck_1_2_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk805_buck_1_2_voltage_ranges), + .vsel_reg = RK805_BUCK1_ON_VSEL_REG, + .vsel_mask = RK818_BUCK_VSEL_MASK, + .enable_reg = RK805_DCDC_EN_REG, + .enable_mask = BIT(0), + }}, {{ + /* .name = "DCDC_REG2", */ + .supply_name = "vcc2", + .ops = &rk808_reg_ops_ranges, + .n_voltages = 64, + .linear_ranges = rk805_buck_1_2_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk805_buck_1_2_voltage_ranges), + .vsel_reg = RK805_BUCK2_ON_VSEL_REG, + .vsel_mask = RK818_BUCK_VSEL_MASK, + .enable_reg = RK805_DCDC_EN_REG, + .enable_mask = BIT(1), + }}, {{ + /* .name = "DCDC_REG3", */ + .supply_name = "vcc3", + .ops = &rk808_switch_ops, + .n_voltages = 1, + .enable_reg = RK805_DCDC_EN_REG, + .enable_mask = BIT(2), + }}, + + RK805_DESC(/* "DCDC_REG4", */ "vcc4", 800, 3400, 100, + RK805_BUCK4_ON_VSEL_REG, RK818_BUCK4_VSEL_MASK, + RK805_DCDC_EN_REG, BIT(3), 0), + + RK805_DESC(/* "LDO_REG1", */ "vcc5", 800, 3400, 100, + RK805_LDO1_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK805_LDO_EN_REG, + BIT(0), 400), + RK805_DESC(/* "LDO_REG2", */ "vcc5", 800, 3400, 100, + RK805_LDO2_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK805_LDO_EN_REG, + BIT(1), 400), + RK805_DESC(/* "LDO_REG3", */ "vcc6", 800, 3400, 100, + RK805_LDO3_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK805_LDO_EN_REG, + BIT(2), 400), +}; +static_assert(ARRAY_SIZE(rk805_reg) == RK805_NUM_REGULATORS); + +static struct rk_regulator_cfg rk808_reg[] = { + {{ + /* .name = "DCDC_REG1", */ + .supply_name = "vcc1", + .ops = &rk808_buck1_2_ops, + .min_uV = 712500, + .uV_step = 12500, + .n_voltages = 64, + .vsel_reg = RK808_BUCK1_ON_VSEL_REG, + .vsel_mask = RK808_BUCK_VSEL_MASK, + .enable_reg = RK808_DCDC_EN_REG, + .enable_mask = BIT(0), + }}, {{ + /* .name = "DCDC_REG2", */ + .supply_name = "vcc2", + .ops = &rk808_buck1_2_ops, + .min_uV = 712500, + .uV_step = 12500, + .n_voltages = 64, + .vsel_reg = RK808_BUCK2_ON_VSEL_REG, + .vsel_mask = RK808_BUCK_VSEL_MASK, + .enable_reg = RK808_DCDC_EN_REG, + .enable_mask = BIT(1), + }}, {{ + /* .name = "DCDC_REG3", */ + .supply_name = "vcc3", + .ops = &rk808_switch_ops, + .n_voltages = 1, + .enable_reg = RK808_DCDC_EN_REG, + .enable_mask = BIT(2), + }}, {{ + /* .name = "DCDC_REG4", */ + .supply_name = "vcc4", + .ops = &rk808_reg_ops, + .min_uV = 1800000, + .uV_step = 100000, + .n_voltages = 16, + .vsel_reg = RK808_BUCK4_ON_VSEL_REG, + .vsel_mask = RK808_BUCK4_VSEL_MASK, + .enable_reg = RK808_DCDC_EN_REG, + .enable_mask = BIT(3), + }}, {{ + /* .name = "LDO_REG1", */ + .supply_name = "vcc6", + .ops = &rk808_reg_ops, + .min_uV = 1800000, + .uV_step = 100000, + .n_voltages = 17, + .vsel_reg = RK808_LDO1_ON_VSEL_REG, + .vsel_mask = RK808_LDO_VSEL_MASK, + .enable_reg = RK808_LDO_EN_REG, + .enable_mask = BIT(0), + .off_on_delay = 400, + }}, {{ + /* .name = "LDO_REG2", */ + .supply_name = "vcc6", + .ops = &rk808_reg_ops, + .min_uV = 1800000, + .uV_step = 100000, + .n_voltages = 17, + .vsel_reg = RK808_LDO2_ON_VSEL_REG, + .vsel_mask = RK808_LDO_VSEL_MASK, + .enable_reg = RK808_LDO_EN_REG, + .enable_mask = BIT(1), + .off_on_delay = 400, + }}, {{ + /* .name = "LDO_REG3", */ + .supply_name = "vcc7", + .ops = &rk808_reg_ops_ranges, + .n_voltages = 16, + .linear_ranges = rk808_ldo3_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk808_ldo3_voltage_ranges), + .vsel_reg = RK808_LDO3_ON_VSEL_REG, + .vsel_mask = RK808_BUCK4_VSEL_MASK, + .enable_reg = RK808_LDO_EN_REG, + .enable_mask = BIT(2), + .off_on_delay = 400, + }}, {{ + /* .name = "LDO_REG4", */ + .supply_name = "vcc9", + .ops = &rk808_reg_ops, + .min_uV = 1800000, + .uV_step = 100000, + .n_voltages = 17, + .vsel_reg = RK808_LDO4_ON_VSEL_REG, + .vsel_mask = RK808_LDO_VSEL_MASK, + .enable_reg = RK808_LDO_EN_REG, + .enable_mask = BIT(3), + .off_on_delay = 400, + }}, {{ + /* .name = "LDO_REG5", */ + .supply_name = "vcc9", + .ops = &rk808_reg_ops, + .min_uV = 1800000, + .uV_step = 100000, + .n_voltages = 17, + .vsel_reg = RK808_LDO5_ON_VSEL_REG, + .vsel_mask = RK808_LDO_VSEL_MASK, + .enable_reg = RK808_LDO_EN_REG, + .enable_mask = BIT(4), + .off_on_delay = 400, + }}, {{ + /* .name = "LDO_REG6", */ + .supply_name = "vcc10", + .ops = &rk808_reg_ops, + .min_uV = 800000, + .uV_step = 100000, + .n_voltages = 18, + .vsel_reg = RK808_LDO6_ON_VSEL_REG, + .vsel_mask = RK808_LDO_VSEL_MASK, + .enable_reg = RK808_LDO_EN_REG, + .enable_mask = BIT(5), + .off_on_delay = 400, + }}, {{ + /* .name = "LDO_REG7", */ + .supply_name = "vcc7", + .ops = &rk808_reg_ops, + .min_uV = 800000, + .uV_step = 100000, + .n_voltages = 18, + .vsel_reg = RK808_LDO7_ON_VSEL_REG, + .vsel_mask = RK808_LDO_VSEL_MASK, + .enable_reg = RK808_LDO_EN_REG, + .enable_mask = BIT(6), + .off_on_delay = 400, + }}, {{ + /* .name = "LDO_REG8", */ + .supply_name = "vcc11", + .ops = &rk808_reg_ops, + .min_uV = 1800000, + .uV_step = 100000, + .n_voltages = 17, + .vsel_reg = RK808_LDO8_ON_VSEL_REG, + .vsel_mask = RK808_LDO_VSEL_MASK, + .enable_reg = RK808_LDO_EN_REG, + .enable_mask = BIT(7), + .off_on_delay = 400, + }}, {{ + /* .name = "SWITCH_REG1", */ + .supply_name = "vcc8", + .ops = &rk808_switch_ops, + .enable_reg = RK808_DCDC_EN_REG, + .enable_mask = BIT(5), + }}, {{ + /* .name = "SWITCH_REG2", */ + .supply_name = "vcc12", + .ops = &rk808_switch_ops, + .enable_reg = RK808_DCDC_EN_REG, + .enable_mask = BIT(6), + }}, +}; +static_assert(ARRAY_SIZE(rk808_reg) == RK808_NUM_REGULATORS); + +static struct rk_regulator_cfg rk809_reg[] = { + {{ + /* .name = "DCDC_REG1", */ + .supply_name = "vcc1", + .ops = &rk817_buck_ops_range, + .n_voltages = RK817_BUCK1_SEL_CNT + 1, + .linear_ranges = rk817_buck1_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk817_buck1_voltage_ranges), + .vsel_reg = RK817_BUCK1_ON_VSEL_REG, + .vsel_mask = RK817_BUCK_VSEL_MASK, + .enable_reg = RK817_POWER_EN_REG(0), + .enable_mask = ENABLE_MASK(RK817_ID_DCDC1), + .enable_val = ENABLE_MASK(RK817_ID_DCDC1), + .disable_val = DISABLE_VAL(RK817_ID_DCDC1), + }}, {{ + /* .name = "DCDC_REG2", */ + .supply_name = "vcc2", + .ops = &rk817_buck_ops_range, + .n_voltages = RK817_BUCK1_SEL_CNT + 1, + .linear_ranges = rk817_buck1_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk817_buck1_voltage_ranges), + .vsel_reg = RK817_BUCK2_ON_VSEL_REG, + .vsel_mask = RK817_BUCK_VSEL_MASK, + .enable_reg = RK817_POWER_EN_REG(0), + .enable_mask = ENABLE_MASK(RK817_ID_DCDC2), + .enable_val = ENABLE_MASK(RK817_ID_DCDC2), + .disable_val = DISABLE_VAL(RK817_ID_DCDC2), + }}, {{ + /* .name = "DCDC_REG3", */ + .supply_name = "vcc3", + .ops = &rk817_buck_ops_range, + .n_voltages = RK817_BUCK1_SEL_CNT + 1, + .linear_ranges = rk817_buck1_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk817_buck1_voltage_ranges), + .vsel_reg = RK817_BUCK3_ON_VSEL_REG, + .vsel_mask = RK817_BUCK_VSEL_MASK, + .enable_reg = RK817_POWER_EN_REG(0), + .enable_mask = ENABLE_MASK(RK817_ID_DCDC3), + .enable_val = ENABLE_MASK(RK817_ID_DCDC3), + .disable_val = DISABLE_VAL(RK817_ID_DCDC3), + }}, {{ + /* .name = "DCDC_REG4", */ + .supply_name = "vcc4", + .ops = &rk817_buck_ops_range, + .n_voltages = RK817_BUCK3_SEL_CNT + 1, + .linear_ranges = rk817_buck3_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk817_buck3_voltage_ranges), + .vsel_reg = RK817_BUCK4_ON_VSEL_REG, + .vsel_mask = RK817_BUCK_VSEL_MASK, + .enable_reg = RK817_POWER_EN_REG(0), + .enable_mask = ENABLE_MASK(RK817_ID_DCDC4), + .enable_val = ENABLE_MASK(RK817_ID_DCDC4), + .disable_val = DISABLE_VAL(RK817_ID_DCDC4), + }}, + RK817_DESC(/* "LDO_REG1", */ "vcc5", 600, 3400, 25, + RK817_LDO_ON_VSEL_REG(0), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(1), ENABLE_MASK(0), + DISABLE_VAL(0), 400), + RK817_DESC(/* "LDO_REG2", */ "vcc5", 600, 3400, 25, + RK817_LDO_ON_VSEL_REG(1), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(1), ENABLE_MASK(1), + DISABLE_VAL(1), 400), + RK817_DESC(/* "LDO_REG3", */ "vcc5", 600, 3400, 25, + RK817_LDO_ON_VSEL_REG(2), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(1), ENABLE_MASK(2), + DISABLE_VAL(2), 400), + RK817_DESC(/* "LDO_REG4", */ "vcc6", 600, 3400, 25, + RK817_LDO_ON_VSEL_REG(3), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(1), ENABLE_MASK(3), + DISABLE_VAL(3), 400), + RK817_DESC(/* "LDO_REG5", */ "vcc6", 600, 3400, 25, + RK817_LDO_ON_VSEL_REG(4), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(2), ENABLE_MASK(0), + DISABLE_VAL(0), 400), + RK817_DESC(/* "LDO_REG6", */ "vcc6", 600, 3400, 25, + RK817_LDO_ON_VSEL_REG(5), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(2), ENABLE_MASK(1), + DISABLE_VAL(1), 400), + RK817_DESC(/* "LDO_REG7", */ "vcc7", 600, 3400, 25, + RK817_LDO_ON_VSEL_REG(6), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(2), ENABLE_MASK(2), + DISABLE_VAL(2), 400), + RK817_DESC(/* "LDO_REG8", */ "vcc7", 600, 3400, 25, + RK817_LDO_ON_VSEL_REG(7), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(2), ENABLE_MASK(3), + DISABLE_VAL(3), 400), + RK817_DESC(/* "LDO_REG9", */ "vcc7", 600, 3400, 25, + RK817_LDO_ON_VSEL_REG(8), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(3), ENABLE_MASK(0), + DISABLE_VAL(0), 400), + {{ + /* .name = "DCDC_REG5", */ + .supply_name = "vcc9", + .ops = &rk809_buck5_ops_range, + .n_voltages = RK809_BUCK5_SEL_CNT, + .linear_ranges = rk809_buck5_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk809_buck5_voltage_ranges), + .vsel_reg = RK809_BUCK5_CONFIG(0), + .vsel_mask = RK809_BUCK5_VSEL_MASK, + .enable_reg = RK817_POWER_EN_REG(3), + .enable_mask = ENABLE_MASK(1), + .enable_val = ENABLE_MASK(1), + .disable_val = DISABLE_VAL(1), + }}, + RK817_DESC_SWITCH(/* "SWITCH_REG1", */ "vcc9", + RK817_POWER_EN_REG(3), ENABLE_MASK(2), DISABLE_VAL(2)), + RK817_DESC_SWITCH(/* "SWITCH_REG2", */ "vcc8", + RK817_POWER_EN_REG(3), ENABLE_MASK(3), DISABLE_VAL(3)), +}; +static_assert(ARRAY_SIZE(rk809_reg) == RK809_NUM_REGULATORS); + +static struct rk_regulator_cfg rk817_reg[] = { + {{ + /* .name = "DCDC_REG1", */ + .supply_name = "vcc1", + .ops = &rk817_buck_ops_range, + .n_voltages = RK817_BUCK1_SEL_CNT + 1, + .linear_ranges = rk817_buck1_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk817_buck1_voltage_ranges), + .vsel_reg = RK817_BUCK1_ON_VSEL_REG, + .vsel_mask = RK817_BUCK_VSEL_MASK, + .enable_reg = RK817_POWER_EN_REG(0), + .enable_mask = ENABLE_MASK(RK817_ID_DCDC1), + .enable_val = ENABLE_MASK(RK817_ID_DCDC1), + .disable_val = DISABLE_VAL(RK817_ID_DCDC1), + }}, {{ + /* .name = "DCDC_REG2", */ + .supply_name = "vcc2", + .ops = &rk817_buck_ops_range, + .n_voltages = RK817_BUCK1_SEL_CNT + 1, + .linear_ranges = rk817_buck1_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk817_buck1_voltage_ranges), + .vsel_reg = RK817_BUCK2_ON_VSEL_REG, + .vsel_mask = RK817_BUCK_VSEL_MASK, + .enable_reg = RK817_POWER_EN_REG(0), + .enable_mask = ENABLE_MASK(RK817_ID_DCDC2), + .enable_val = ENABLE_MASK(RK817_ID_DCDC2), + .disable_val = DISABLE_VAL(RK817_ID_DCDC2), + }}, {{ + /* .name = "DCDC_REG3", */ + .supply_name = "vcc3", + .ops = &rk817_buck_ops_range, + .n_voltages = RK817_BUCK1_SEL_CNT + 1, + .linear_ranges = rk817_buck1_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk817_buck1_voltage_ranges), + .vsel_reg = RK817_BUCK3_ON_VSEL_REG, + .vsel_mask = RK817_BUCK_VSEL_MASK, + .enable_reg = RK817_POWER_EN_REG(0), + .enable_mask = ENABLE_MASK(RK817_ID_DCDC3), + .enable_val = ENABLE_MASK(RK817_ID_DCDC3), + .disable_val = DISABLE_VAL(RK817_ID_DCDC3), + }}, {{ + /* .name = "DCDC_REG4", */ + .supply_name = "vcc4", + .ops = &rk817_buck_ops_range, + .n_voltages = RK817_BUCK3_SEL_CNT + 1, + .linear_ranges = rk817_buck3_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk817_buck3_voltage_ranges), + .vsel_reg = RK817_BUCK4_ON_VSEL_REG, + .vsel_mask = RK817_BUCK_VSEL_MASK, + .enable_reg = RK817_POWER_EN_REG(0), + .enable_mask = ENABLE_MASK(RK817_ID_DCDC4), + .enable_val = ENABLE_MASK(RK817_ID_DCDC4), + .disable_val = DISABLE_VAL(RK817_ID_DCDC4), + }}, + RK817_DESC(/* "LDO_REG1", */ "vcc5", 600, 3400, 25, + RK817_LDO_ON_VSEL_REG(0), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(1), ENABLE_MASK(0), + DISABLE_VAL(0), 400), + RK817_DESC(/* "LDO_REG2", */ "vcc5", 600, 3400, 25, + RK817_LDO_ON_VSEL_REG(1), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(1), ENABLE_MASK(1), + DISABLE_VAL(1), 400), + RK817_DESC(/* "LDO_REG3", */ "vcc5", 600, 3400, 25, + RK817_LDO_ON_VSEL_REG(2), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(1), ENABLE_MASK(2), + DISABLE_VAL(2), 400), + RK817_DESC(/* "LDO_REG4", */ "vcc6", 600, 3400, 25, + RK817_LDO_ON_VSEL_REG(3), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(1), ENABLE_MASK(3), + DISABLE_VAL(3), 400), + RK817_DESC(/* "LDO_REG5", */ "vcc6", 600, 3400, 25, + RK817_LDO_ON_VSEL_REG(4), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(2), ENABLE_MASK(0), + DISABLE_VAL(0), 400), + RK817_DESC(/* "LDO_REG6", */ "vcc6", 600, 3400, 25, + RK817_LDO_ON_VSEL_REG(5), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(2), ENABLE_MASK(1), + DISABLE_VAL(1), 400), + RK817_DESC(/* "LDO_REG7", */ "vcc7", 600, 3400, 25, + RK817_LDO_ON_VSEL_REG(6), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(2), ENABLE_MASK(2), + DISABLE_VAL(2), 400), + RK817_DESC(/* "LDO_REG8", */ "vcc7", 600, 3400, 25, + RK817_LDO_ON_VSEL_REG(7), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(2), ENABLE_MASK(3), + DISABLE_VAL(3), 400), + RK817_DESC(/* "LDO_REG9", */ "vcc7", 600, 3400, 25, + RK817_LDO_ON_VSEL_REG(8), RK817_LDO_VSEL_MASK, + RK817_POWER_EN_REG(3), ENABLE_MASK(0), + DISABLE_VAL(0), 400), + RK817_BOOST_DESC(/* "BOOST", */ "vcc8", 4700, 5400, 100, + RK817_BOOST_OTG_CFG, RK817_BOOST_VSEL_MASK, + RK817_POWER_EN_REG(3), ENABLE_MASK(1), ENABLE_MASK(1), + DISABLE_VAL(1), 400), + RK817_DESC_SWITCH(/* "OTG_SWITCH", */ "vcc9", + RK817_POWER_EN_REG(3), ENABLE_MASK(2), DISABLE_VAL(2)), +}; +static_assert(ARRAY_SIZE(rk817_reg) == RK817_NUM_REGULATORS); + +static struct rk_regulator_cfg rk818_reg[] = { + {{ + /* .name = "DCDC_REG1", */ + .supply_name = "vcc1", + .ops = &rk808_reg_ops, + .min_uV = 712500, + .uV_step = 12500, + .n_voltages = 64, + .vsel_reg = RK818_BUCK1_ON_VSEL_REG, + .vsel_mask = RK818_BUCK_VSEL_MASK, + .enable_reg = RK818_DCDC_EN_REG, + .enable_mask = BIT(0), + }}, {{ + /* .name = "DCDC_REG2", */ + .supply_name = "vcc2", + .ops = &rk808_reg_ops, + .min_uV = 712500, + .uV_step = 12500, + .n_voltages = 64, + .vsel_reg = RK818_BUCK2_ON_VSEL_REG, + .vsel_mask = RK818_BUCK_VSEL_MASK, + .enable_reg = RK818_DCDC_EN_REG, + .enable_mask = BIT(1), + }}, {{ + /* .name = "DCDC_REG3", */ + .supply_name = "vcc3", + .ops = &rk808_switch_ops, + .n_voltages = 1, + .enable_reg = RK818_DCDC_EN_REG, + .enable_mask = BIT(2), + }}, + RK8XX_DESC(/* "DCDC_REG4", */ "vcc4", 1800, 3600, 100, + RK818_BUCK4_ON_VSEL_REG, RK818_BUCK4_VSEL_MASK, + RK818_DCDC_EN_REG, BIT(3), 0), + RK8XX_DESC(/* "DCDC_BOOST", */ "boost", 4700, 5400, 100, + RK818_BOOST_LDO9_ON_VSEL_REG, RK818_BOOST_ON_VSEL_MASK, + RK818_DCDC_EN_REG, BIT(4), 0), + RK8XX_DESC(/* "LDO_REG1", */ "vcc6", 1800, 3400, 100, + RK818_LDO1_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG, + BIT(0), 400), + RK8XX_DESC(/* "LDO_REG2", */ "vcc6", 1800, 3400, 100, + RK818_LDO2_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG, + BIT(1), 400), + {{ + /* .name = "LDO_REG3", */ + .supply_name = "vcc7", + .ops = &rk808_reg_ops_ranges, + .n_voltages = 16, + .linear_ranges = rk808_ldo3_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk808_ldo3_voltage_ranges), + .vsel_reg = RK818_LDO3_ON_VSEL_REG, + .vsel_mask = RK818_LDO3_ON_VSEL_MASK, + .enable_reg = RK818_LDO_EN_REG, + .enable_mask = BIT(2), + .off_on_delay = 400, + }}, + RK8XX_DESC(/* "LDO_REG4", */ "vcc8", 1800, 3400, 100, + RK818_LDO4_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG, + BIT(3), 400), + RK8XX_DESC(/* "LDO_REG5", */ "vcc7", 1800, 3400, 100, + RK818_LDO5_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG, + BIT(4), 400), + RK8XX_DESC(/* "LDO_REG6", */ "vcc8", 800, 2500, 100, + RK818_LDO6_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG, + BIT(5), 400), + RK8XX_DESC(/* "LDO_REG7", */ "vcc7", 800, 2500, 100, + RK818_LDO7_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG, + BIT(6), 400), + RK8XX_DESC(/* "LDO_REG8", */ "vcc8", 1800, 3400, 100, + RK818_LDO8_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG, + BIT(7), 400), + RK8XX_DESC(/* "LDO_REG9", */ "vcc9", 1800, 3400, 100, + RK818_BOOST_LDO9_ON_VSEL_REG, RK818_LDO_VSEL_MASK, + RK818_DCDC_EN_REG, BIT(5), 400), + RK8XX_DESC_SWITCH(/* "SWITCH_REG", */ "vcc9", + RK818_DCDC_EN_REG, BIT(6)), + RK8XX_DESC_SWITCH(/* "HDMI_SWITCH", */ "h_5v", + RK818_H5V_EN_REG, BIT(0)), + RK8XX_DESC_SWITCH(/* "OTG_SWITCH", */ "usb", + RK818_DCDC_EN_REG, BIT(7)), +}; +static_assert(ARRAY_SIZE(rk818_reg) == RK818_NUM_REGULATORS); + +static int rk808_regulator_register(struct rk808 *rk808, int id, + struct of_regulator_match *match, + struct rk_regulator_cfg *cfg) +{ + struct device *dev = &rk808->i2c->dev; + int ret; + + if (!match->of_node) { + dev_dbg(dev, "Skip missing DTB regulator %s", match->name); + return 0; + } + + cfg->rdev.desc = &cfg->desc; + cfg->rdev.dev = dev; + cfg->rdev.regmap = rk808->regmap; + + ret = of_regulator_register(&cfg->rdev, match->of_node); + if (ret) + return dev_err_probe(dev, ret, "failed to register %s regulator\n", + match->name); + + dev_dbg(dev, "registered %s\n", match->name); + + return 0; +} + +#define MATCH(variant, _name, _id) [RK##variant##_ID_##_id] = \ + { .name = #_name, .desc = &rk##variant##_reg[RK##variant##_ID_##_id].desc } + +static struct of_regulator_match rk805_reg_matches[] = { + MATCH(805, DCDC_REG1, DCDC1), + MATCH(805, DCDC_REG2, DCDC2), + MATCH(805, DCDC_REG3, DCDC3), + MATCH(805, DCDC_REG4, DCDC4), + MATCH(805, LDO_REG1, LDO1), + MATCH(805, LDO_REG2, LDO2), + MATCH(805, LDO_REG3, LDO3), +}; +static_assert(ARRAY_SIZE(rk805_reg_matches) == RK805_NUM_REGULATORS); + +static struct of_regulator_match rk808_reg_matches[] = { + MATCH(808, DCDC_REG1, DCDC1), + MATCH(808, DCDC_REG2, DCDC2), + MATCH(808, DCDC_REG3, DCDC3), + MATCH(808, DCDC_REG4, DCDC4), + MATCH(808, LDO_REG1, LDO1), + MATCH(808, LDO_REG2, LDO2), + MATCH(808, LDO_REG3, LDO3), + MATCH(808, LDO_REG4, LDO4), + MATCH(808, LDO_REG5, LDO5), + MATCH(808, LDO_REG6, LDO6), + MATCH(808, LDO_REG7, LDO7), + MATCH(808, LDO_REG8, LDO8), + MATCH(808, SWITCH_REG1, SWITCH1), + MATCH(808, SWITCH_REG2, SWITCH2), +}; +static_assert(ARRAY_SIZE(rk808_reg_matches) == RK808_NUM_REGULATORS); + +static struct of_regulator_match rk809_reg_matches[] = { + MATCH(809, DCDC_REG1, DCDC1), + MATCH(809, DCDC_REG2, DCDC2), + MATCH(809, DCDC_REG3, DCDC3), + MATCH(809, DCDC_REG4, DCDC4), + MATCH(809, LDO_REG1, LDO1), + MATCH(809, LDO_REG2, LDO2), + MATCH(809, LDO_REG3, LDO3), + MATCH(809, LDO_REG4, LDO4), + MATCH(809, LDO_REG5, LDO5), + MATCH(809, LDO_REG6, LDO6), + MATCH(809, LDO_REG7, LDO7), + MATCH(809, LDO_REG8, LDO8), + MATCH(809, LDO_REG9, LDO9), + MATCH(809, DCDC_REG5, DCDC5), + MATCH(809, SWITCH_REG1, SW1), + MATCH(809, SWITCH_REG2, SW2), +}; +static_assert(ARRAY_SIZE(rk809_reg_matches) == RK809_NUM_REGULATORS); + +static struct of_regulator_match rk817_reg_matches[] = { + MATCH(817, DCDC_REG1, DCDC1), + MATCH(817, DCDC_REG2, DCDC2), + MATCH(817, DCDC_REG3, DCDC3), + MATCH(817, DCDC_REG4, DCDC4), + MATCH(817, LDO_REG1, LDO1), + MATCH(817, LDO_REG2, LDO2), + MATCH(817, LDO_REG3, LDO3), + MATCH(817, LDO_REG4, LDO4), + MATCH(817, LDO_REG5, LDO5), + MATCH(817, LDO_REG6, LDO6), + MATCH(817, LDO_REG7, LDO7), + MATCH(817, LDO_REG8, LDO8), + MATCH(817, LDO_REG9, LDO9), + MATCH(817, BOOST, BOOST), + MATCH(817, OTG_SWITCH, BOOST_OTG_SW), +}; +static_assert(ARRAY_SIZE(rk817_reg_matches) == RK817_NUM_REGULATORS); + +static struct of_regulator_match rk818_reg_matches[] = { + MATCH(818, DCDC_REG1, DCDC1), + MATCH(818, DCDC_REG2, DCDC2), + MATCH(818, DCDC_REG3, DCDC3), + MATCH(818, DCDC_REG4, DCDC4), + MATCH(818, DCDC_BOOST, BOOST), + MATCH(818, LDO_REG1, LDO1), + MATCH(818, LDO_REG2, LDO2), + MATCH(818, LDO_REG3, LDO3), + MATCH(818, LDO_REG4, LDO4), + MATCH(818, LDO_REG5, LDO5), + MATCH(818, LDO_REG6, LDO6), + MATCH(818, LDO_REG7, LDO7), + MATCH(818, LDO_REG8, LDO8), + MATCH(818, LDO_REG9, LDO9), + MATCH(818, SWITCH_REG, SWITCH), + MATCH(818, HDMI_SWITCH, HDMI_SWITCH), + MATCH(818, OTG_SWITCH, OTG_SWITCH), +}; +static_assert(ARRAY_SIZE(rk818_reg_matches) == RK818_NUM_REGULATORS); + +static int rk808_regulator_dt_parse(struct device *dev, + struct of_regulator_match *matches, + int nregulators) +{ + struct device_node *np = dev->of_node; + + np = of_get_child_by_name(np, "regulators"); + if (!np) + return -ENOENT; + + return of_regulator_match(dev, np, matches, nregulators); +} + +static int rk808_regulator_probe(struct device *dev) +{ + struct rk808 *rk808 = dev->parent->priv; + struct rk_regulator_cfg *regulators; + struct of_regulator_match *matches; + int ret, i, nregulators; + + switch (rk808->variant) { + case RK805_ID: + regulators = rk805_reg; + matches = rk805_reg_matches; + nregulators = RK805_NUM_REGULATORS; + break; + case RK808_ID: + regulators = rk808_reg; + matches = rk808_reg_matches; + nregulators = RK809_NUM_REGULATORS; + break; + case RK809_ID: + regulators = rk809_reg; + matches = rk809_reg_matches; + nregulators = RK809_NUM_REGULATORS; + break; + case RK817_ID: + regulators = rk817_reg; + matches = rk817_reg_matches; + nregulators = RK817_NUM_REGULATORS; + break; + case RK818_ID: + regulators = rk818_reg; + matches = rk818_reg_matches; + nregulators = RK818_NUM_REGULATORS; + break; + default: + dev_err(dev, "unsupported RK8XX ID %lu\n", rk808->variant); + return -EINVAL; + } + + ret = rk808_regulator_dt_parse(&rk808->i2c->dev, matches, nregulators); + if (ret < 0) + return ret; + + /* Instantiate the regulators */ + for (i = 0; i < nregulators; i++) { + ret = rk808_regulator_register(rk808, i, &matches[i], + ®ulators[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +static struct driver rk808_regulator_driver = { + .name = "rk808-regulator", + .probe = rk808_regulator_probe, +}; +device_platform_driver(rk808_regulator_driver); + +MODULE_DESCRIPTION("regulator driver for the rk808 series PMICs"); +MODULE_AUTHOR("Chris Zhong<zyw@rock-chips.com>"); +MODULE_AUTHOR("Zhang Qing<zhangqing@rock-chips.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rk808-regulator"); diff --git a/drivers/regulator/scmi-regulator.c b/drivers/regulator/scmi-regulator.c new file mode 100644 index 0000000000..6f22fa6420 --- /dev/null +++ b/drivers/regulator/scmi-regulator.c @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// System Control and Management Interface (SCMI) based regulator driver +// +// Copyright (C) 2020-2021 ARM Ltd. +// +// Implements a regulator driver on top of the SCMI Voltage Protocol. +// +// The ARM SCMI Protocol aims in general to hide as much as possible all the +// underlying operational details while providing an abstracted interface for +// its users to operate upon: as a consequence the resulting operational +// capabilities and configurability of this regulator device are much more +// limited than the ones usually available on a standard physical regulator. +// +// The supported SCMI regulator ops are restricted to the bare minimum: +// +// - 'status_ops': enable/disable/is_enabled +// - 'voltage_ops': get_voltage_sel/set_voltage_sel +// list_voltage/map_voltage +// +// Each SCMI regulator instance is associated, through the means of a proper DT +// entry description, to a specific SCMI Voltage Domain. + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <common.h> +#include <of.h> +#include <regulator.h> +#include <linux/regulator/of_regulator.h> +#include <linux/scmi_protocol.h> +#include <linux/slab.h> +#include <linux/types.h> + +static const struct scmi_voltage_proto_ops *voltage_ops; + +struct scmi_reg_desc { + struct regulator_desc desc; + const char *name; + const char *supply_name; +}; + +struct scmi_regulator { + u32 id; + struct scmi_device *sdev; + struct scmi_protocol_handle *ph; + struct regulator_dev rdev; + struct device_node *of_node; + struct scmi_reg_desc sdesc; +}; + +struct scmi_regulator_info { + int num_doms; + struct scmi_regulator **sregv; +}; + +static inline struct scmi_regulator *to_scmi_regulator(struct regulator_dev *rdev) +{ + return container_of(rdev, struct scmi_regulator, rdev); +} + +static int scmi_reg_enable(struct regulator_dev *rdev) +{ + struct scmi_regulator *sreg = to_scmi_regulator(rdev); + + return voltage_ops->config_set(sreg->ph, sreg->id, + SCMI_VOLTAGE_ARCH_STATE_ON); +} + +static int scmi_reg_disable(struct regulator_dev *rdev) +{ + struct scmi_regulator *sreg = to_scmi_regulator(rdev); + + return voltage_ops->config_set(sreg->ph, sreg->id, + SCMI_VOLTAGE_ARCH_STATE_OFF); +} + +static int scmi_reg_is_enabled(struct regulator_dev *rdev) +{ + int ret; + u32 config; + struct scmi_regulator *sreg = to_scmi_regulator(rdev); + struct scmi_reg_desc *sdesc = &sreg->sdesc; + + ret = voltage_ops->config_get(sreg->ph, sreg->id, &config); + if (ret) { + dev_err(&sreg->sdev->dev, + "Error %d reading regulator %s status.\n", + ret, sdesc->name); + return ret; + } + + return config & SCMI_VOLTAGE_ARCH_STATE_ON; +} + +static int scmi_reg_get_voltage_sel(struct regulator_dev *rdev) +{ + int ret; + s32 volt_uV; + struct scmi_regulator *sreg = to_scmi_regulator(rdev); + + ret = voltage_ops->level_get(sreg->ph, sreg->id, &volt_uV); + if (ret) + return ret; + + return sreg->sdesc.desc.ops->map_voltage(rdev, volt_uV, volt_uV); +} + +static int scmi_reg_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + s32 volt_uV; + struct scmi_regulator *sreg = to_scmi_regulator(rdev); + + volt_uV = sreg->sdesc.desc.ops->list_voltage(rdev, selector); + if (volt_uV <= 0) + return -EINVAL; + + return voltage_ops->level_set(sreg->ph, sreg->id, 0x0, volt_uV); +} + +static const struct regulator_ops scmi_reg_fixed_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, +}; + +static const struct regulator_ops scmi_reg_linear_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, + .get_voltage_sel = scmi_reg_get_voltage_sel, + .set_voltage_sel = scmi_reg_set_voltage_sel, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, +}; + +static const struct regulator_ops scmi_reg_discrete_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, + .get_voltage_sel = scmi_reg_get_voltage_sel, + .set_voltage_sel = scmi_reg_set_voltage_sel, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_iterate, +}; + +static int +scmi_config_linear_regulator_mappings(struct scmi_regulator *sreg, + const struct scmi_voltage_info *vinfo) +{ + struct regulator_desc *desc = &sreg->sdesc.desc; + s32 delta_uV; + + /* + * Note that SCMI voltage domains describable by linear ranges + * (segments) {low, high, step} are guaranteed to come in one single + * triplet by the SCMI Voltage Domain protocol support itself. + */ + + delta_uV = (vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH] - + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]); + + /* Rule out buggy negative-intervals answers from fw */ + if (delta_uV < 0) { + dev_err(&sreg->sdev->dev, + "Invalid volt-range %d-%duV for domain %d\n", + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW], + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH], + sreg->id); + return -EINVAL; + } + + if (!delta_uV) { + /* Just one fixed voltage exposed by SCMI */ + desc->fixed_uV = + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]; + desc->n_voltages = 1; + desc->ops = &scmi_reg_fixed_ops; + } else { + /* One simple linear mapping. */ + desc->min_uV = + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]; + desc->uV_step = + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_STEP]; + desc->linear_min_sel = 0; + desc->n_voltages = (delta_uV / desc->uV_step) + 1; + desc->ops = &scmi_reg_linear_ops; + } + + return 0; +} + +static int +scmi_config_discrete_regulator_mappings(struct scmi_regulator *sreg, + const struct scmi_voltage_info *vinfo) +{ + struct regulator_desc *desc = &sreg->sdesc.desc; + + /* Discrete non linear levels are mapped to volt_table */ + desc->n_voltages = vinfo->num_levels; + + if (desc->n_voltages > 1) { + desc->volt_table = (const unsigned int *)vinfo->levels_uv; + desc->ops = &scmi_reg_discrete_ops; + } else { + desc->fixed_uV = vinfo->levels_uv[0]; + desc->ops = &scmi_reg_fixed_ops; + } + + return 0; +} + +static int scmi_regulator_common_init(struct scmi_regulator *sreg) +{ + int ret; + struct device *dev = &sreg->sdev->dev; + const struct scmi_voltage_info *vinfo; + struct scmi_reg_desc *sdesc = &sreg->sdesc; + + vinfo = voltage_ops->info_get(sreg->ph, sreg->id); + if (!vinfo) { + dev_warn(dev, "Failure to get voltage domain %d\n", + sreg->id); + return -ENODEV; + } + + /* + * Regulator framework does not fully support negative voltages + * so we discard any voltage domain reported as supporting negative + * voltages: as a consequence each levels_uv entry is guaranteed to + * be non-negative from here on. + */ + if (vinfo->negative_volts_allowed) { + dev_warn(dev, "Negative voltages NOT supported...skip %pOF\n", + sreg->of_node); + return -EOPNOTSUPP; + } + + sdesc->name = basprintf("%s", vinfo->name); + if (!sdesc->name) + return -ENOMEM; + + if (vinfo->segmented) + ret = scmi_config_linear_regulator_mappings(sreg, vinfo); + else + ret = scmi_config_discrete_regulator_mappings(sreg, vinfo); + + return ret; +} + +static int process_scmi_regulator_of_node(struct scmi_device *sdev, + struct scmi_protocol_handle *ph, + struct device_node *np, + struct scmi_regulator_info *rinfo) +{ + u32 dom, ret; + + ret = of_property_read_u32(np, "reg", &dom); + if (ret) + return ret; + + if (dom >= rinfo->num_doms) + return -ENODEV; + + if (rinfo->sregv[dom]) { + dev_err(&sdev->dev, + "SCMI Voltage Domain %d already in use. Skipping: %pOF\n", + dom, np); + return -EINVAL; + } + + rinfo->sregv[dom] = kzalloc(sizeof(struct scmi_regulator), GFP_KERNEL); + if (!rinfo->sregv[dom]) + return -ENOMEM; + + rinfo->sregv[dom]->id = dom; + rinfo->sregv[dom]->sdev = sdev; + rinfo->sregv[dom]->ph = ph; + + /* get hold of good nodes */ + rinfo->sregv[dom]->of_node = np; + + dev_dbg(&sdev->dev, + "Found SCMI Regulator entry -- OF node [%d] -> %pOF\n", + dom, np); + + return 0; +} + +static int scmi_regulator_probe(struct scmi_device *sdev) +{ + int d, ret, num_doms; + struct device_node *np, *child; + const struct scmi_handle *handle = sdev->handle; + struct scmi_regulator_info *rinfo; + struct scmi_protocol_handle *ph; + struct scmi_reg_desc *sdesc; + + if (!handle) + return -ENODEV; + + voltage_ops = handle->dev_protocol_get(sdev, SCMI_PROTOCOL_VOLTAGE, &ph); + if (IS_ERR(voltage_ops)) + return PTR_ERR(voltage_ops); + + num_doms = voltage_ops->num_domains_get(ph); + if (num_doms <= 0) { + if (!num_doms) { + dev_err(&sdev->dev, + "number of voltage domains invalid\n"); + num_doms = -EINVAL; + } else { + dev_err(&sdev->dev, + "failed to get voltage domains - err:%d\n", + num_doms); + } + + return num_doms; + } + + rinfo = kzalloc(sizeof(*rinfo), GFP_KERNEL); + if (!rinfo) + return -ENOMEM; + + /* Allocate pointers array for all possible domains */ + rinfo->sregv = kcalloc(num_doms, sizeof(void *), GFP_KERNEL); + if (!rinfo->sregv) + return -ENOMEM; + + rinfo->num_doms = num_doms; + + /* + * Start collecting into rinfo->sregv possibly good SCMI Regulators as + * described by a well-formed DT entry and associated with an existing + * plausible SCMI Voltage Domain number, all belonging to this SCMI + * platform instance node (handle->dev->of_node). + */ + np = of_find_node_by_name(handle->dev->of_node, "regulators"); + for_each_child_of_node(np, child) { + ret = process_scmi_regulator_of_node(sdev, ph, child, rinfo); + /* abort on any mem issue */ + if (ret == -ENOMEM) + return ret; + } + + /* + * Register a regulator for each valid regulator-DT-entry that we + * can successfully reach via SCMI and has a valid associated voltage + * domain. + */ + for (d = 0; d < num_doms; d++) { + struct scmi_regulator *sreg = rinfo->sregv[d]; + + /* Skip empty slots */ + if (!sreg) + continue; + + sdesc = &sreg->sdesc; + + ret = scmi_regulator_common_init(sreg); + /* Skip invalid voltage domains */ + if (ret) + continue; + + sreg->rdev.desc = &sdesc->desc; + sreg->rdev.dev = &sdev->dev; + + ret = of_regulator_register(&sreg->rdev, sreg->of_node); + if (ret) + continue; + + dev_dbg(&sdev->dev, + "Regulator %s registered for domain [%d]\n", + sreg->sdesc.name, sreg->id); + } + + return 0; +} + +static const struct scmi_device_id scmi_regulator_id_table[] = { + { SCMI_PROTOCOL_VOLTAGE, "regulator" }, + { }, +}; + +static struct scmi_driver scmi_drv = { + .name = "scmi-regulator", + .probe = scmi_regulator_probe, + .id_table = scmi_regulator_id_table, +}; +core_scmi_driver(scmi_drv); + +MODULE_AUTHOR("Cristian Marussi <cristian.marussi@arm.com>"); +MODULE_DESCRIPTION("ARM SCMI regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/stm32-pwr.c b/drivers/regulator/stm32-pwr.c new file mode 100644 index 0000000000..ca03529b7f --- /dev/null +++ b/drivers/regulator/stm32-pwr.c @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) STMicroelectronics 2019 +// Authors: Gabriel Fernandez <gabriel.fernandez@st.com> +// Pascal Paillet <p.paillet@st.com>. + +#include <common.h> +#include <init.h> +#include <driver.h> +#include <linux/iopoll.h> +#include <of.h> +#include <regulator.h> + +/* + * Registers description + */ +#define REG_PWR_CR3 0x0C + +#define USB_3_3_EN BIT(24) +#define USB_3_3_RDY BIT(26) +#define REG_1_8_EN BIT(28) +#define REG_1_8_RDY BIT(29) +#define REG_1_1_EN BIT(30) +#define REG_1_1_RDY BIT(31) + +/* list of supported regulators */ +enum { + PWR_REG11, + PWR_REG18, + PWR_USB33, + STM32PWR_REG_NUM_REGS +}; + +struct stm32_pwr_desc { + struct regulator_desc desc; + const char *name; + const char *supply_name; +}; + +static u32 ready_mask_table[STM32PWR_REG_NUM_REGS] = { + [PWR_REG11] = REG_1_1_RDY, + [PWR_REG18] = REG_1_8_RDY, + [PWR_USB33] = USB_3_3_RDY, +}; + +struct stm32_pwr_reg { + void __iomem *base; + u32 ready_mask; + struct regulator_dev rdev; + struct regulator *supply; +}; + +static inline struct stm32_pwr_reg *to_pwr_reg(struct regulator_dev *rdev) +{ + return container_of(rdev, struct stm32_pwr_reg, rdev); +} + +static inline struct stm32_pwr_desc *to_desc(struct regulator_dev *rdev) +{ + return container_of(rdev->desc, struct stm32_pwr_desc, desc); +} + +static int stm32_pwr_reg_is_ready(struct regulator_dev *rdev) +{ + struct stm32_pwr_reg *priv = to_pwr_reg(rdev); + u32 val; + + val = readl(priv->base + REG_PWR_CR3); + + return (val & priv->ready_mask); +} + +static int stm32_pwr_reg_is_enabled(struct regulator_dev *rdev) +{ + struct stm32_pwr_reg *priv = to_pwr_reg(rdev); + u32 val; + + val = readl(priv->base + REG_PWR_CR3); + + return (val & rdev->desc->enable_mask); +} + +static int stm32_pwr_reg_enable(struct regulator_dev *rdev) +{ + struct stm32_pwr_reg *priv = to_pwr_reg(rdev); + struct stm32_pwr_desc *desc = to_desc(rdev); + int ret; + u32 val; + + val = readl(priv->base + REG_PWR_CR3); + val |= rdev->desc->enable_mask; + writel(val, priv->base + REG_PWR_CR3); + + /* use an arbitrary timeout of 20ms */ + ret = readx_poll_timeout(stm32_pwr_reg_is_ready, rdev, val, val, + 20 * USEC_PER_MSEC); + if (ret) + dev_err(rdev->dev, "%s: regulator enable timed out!\n", + desc->name); + + return ret; +} + +static int stm32_pwr_reg_disable(struct regulator_dev *rdev) +{ + struct stm32_pwr_reg *priv = to_pwr_reg(rdev); + struct stm32_pwr_desc *desc = to_desc(rdev); + int ret; + u32 val; + + val = readl(priv->base + REG_PWR_CR3); + val &= ~rdev->desc->enable_mask; + writel(val, priv->base + REG_PWR_CR3); + + /* use an arbitrary timeout of 20ms */ + ret = readx_poll_timeout(stm32_pwr_reg_is_ready, rdev, val, !val, + 20 * USEC_PER_MSEC); + if (ret) + dev_err(rdev->dev, "%s: regulator disable timed out!\n", + desc->name); + + return ret; +} + +static const struct regulator_ops stm32_pwr_reg_ops = { + .enable = stm32_pwr_reg_enable, + .disable = stm32_pwr_reg_disable, + .is_enabled = stm32_pwr_reg_is_enabled, +}; + +#define PWR_REG(_id, _name, _volt, _en, _supply) \ + [_id] = { { \ + .n_voltages = 1, \ + .ops = &stm32_pwr_reg_ops, \ + .min_uV = _volt, \ + .enable_mask = _en, \ + }, .name = _name, .supply_name = _supply, } + +static const struct stm32_pwr_desc stm32_pwr_desc[] = { + PWR_REG(PWR_REG11, "reg11", 1100000, REG_1_1_EN, "vdd"), + PWR_REG(PWR_REG18, "reg18", 1800000, REG_1_8_EN, "vdd"), + PWR_REG(PWR_USB33, "usb33", 3300000, USB_3_3_EN, "vdd_3v3_usbfs"), +}; + +static int stm32_pwr_regulator_probe(struct device *dev) +{ + struct stm32_pwr_reg *priv; + struct device_node *child; + struct resource *iores; + int i, ret; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + + for_each_child_of_node(dev->of_node, child) { + const struct stm32_pwr_desc *desc = NULL; + + for (i = 0; i < STM32PWR_REG_NUM_REGS; i++) { + const char *name; + + name = of_get_property(child, "regulator-name", NULL); + if (name && strcmp(stm32_pwr_desc[i].name, name)) { + desc = &stm32_pwr_desc[i]; + break; + } + } + + if (!desc) { + dev_warn(dev, "Skipping unknown child node %s\n", + child->name); + continue; + } + + priv = xzalloc(sizeof(*priv)); + priv->base = IOMEM(iores->start); + priv->ready_mask = ready_mask_table[i]; + + priv->rdev.desc = &desc->desc; + priv->rdev.dev = dev; + + priv->supply = regulator_get(dev, desc->supply_name); + if (IS_ERR(priv->supply)) { + ret = PTR_ERR(priv->supply); + goto release_region; + } + + ret = of_regulator_register(&priv->rdev, child); + if (ret) { + dev_err(dev, "%s: Failed to register regulator: %d\n", + desc->name, ret); + goto release_region; + } + + regulator_enable(priv->supply); + } + + return 0; + +release_region: + release_region(iores); + + return ret; +} + +static const struct of_device_id stm32_pwr_of_match[] = { + { .compatible = "st,stm32mp1,pwr-reg", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, stm32_pwr_of_match); + +static struct driver stm32_pwr_driver = { + .probe = stm32_pwr_regulator_probe, + .name = "stm32-pwr-regulator", + .of_compatible = stm32_pwr_of_match, +}; +device_platform_driver(stm32_pwr_driver); + +MODULE_DESCRIPTION("STM32MP1 PWR voltage regulator driver"); +MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/stm32-vrefbuf.c b/drivers/regulator/stm32-vrefbuf.c new file mode 100644 index 0000000000..18cf53e735 --- /dev/null +++ b/drivers/regulator/stm32-vrefbuf.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) STMicroelectronics 2017 + * + * Author: Fabrice Gasnier <fabrice.gasnier@st.com> + */ + +#include <common.h> +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/iopoll.h> +#include <of.h> +#include <io.h> +#include <regulator.h> + +/* STM32 VREFBUF registers */ +#define STM32_VREFBUF_CSR 0x00 + +/* STM32 VREFBUF CSR bitfields */ +#define STM32_VRS GENMASK(6, 4) +#define STM32_VRR BIT(3) +#define STM32_HIZ BIT(1) +#define STM32_ENVR BIT(0) + +#define STM32_VREFBUF_AUTO_SUSPEND_DELAY_MS 10 + +struct stm32_vrefbuf { + void __iomem *base; + struct clk *clk; + struct device *dev; + struct regulator_dev rdev; +}; + +struct stm32_vrefbuf_desc { + struct regulator_desc desc; + const char *supply_name; +}; + +static inline struct stm32_vrefbuf *to_stm32_vrefbuf(struct regulator_dev *rdev) +{ + return container_of(rdev, struct stm32_vrefbuf, rdev); +} + +static const unsigned int stm32_vrefbuf_voltages[] = { + /* Matches resp. VRS = 000b, 001b, 010b, 011b */ + 2500000, 2048000, 1800000, 1500000, +}; + +static int stm32_vrefbuf_enable(struct regulator_dev *rdev) +{ + struct stm32_vrefbuf *priv = to_stm32_vrefbuf(rdev); + u32 val; + int ret; + + val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); + val = (val & ~STM32_HIZ) | STM32_ENVR; + writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); + + /* + * Vrefbuf startup time depends on external capacitor: wait here for + * VRR to be set. That means output has reached expected value. + * ~650us sleep should be enough for caps up to 1.5uF. Use 10ms as + * arbitrary timeout. + */ + ret = readl_poll_timeout(priv->base + STM32_VREFBUF_CSR, val, + val & STM32_VRR, 10000); + if (ret) { + dev_err(priv->dev, "stm32 vrefbuf timed out!\n"); + val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); + val = (val & ~STM32_ENVR) | STM32_HIZ; + writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); + } + + return ret; +} + +static int stm32_vrefbuf_disable(struct regulator_dev *rdev) +{ + struct stm32_vrefbuf *priv = to_stm32_vrefbuf(rdev); + u32 val; + + val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); + val &= ~STM32_ENVR; + writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); + + return 0; +} + +static int stm32_vrefbuf_is_enabled(struct regulator_dev *rdev) +{ + struct stm32_vrefbuf *priv = to_stm32_vrefbuf(rdev); + int ret; + + ret = readl_relaxed(priv->base + STM32_VREFBUF_CSR) & STM32_ENVR; + + return ret; +} + +static int stm32_vrefbuf_set_voltage_sel(struct regulator_dev *rdev, + unsigned sel) +{ + struct stm32_vrefbuf *priv = to_stm32_vrefbuf(rdev); + u32 val; + + val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); + val = (val & ~STM32_VRS) | FIELD_PREP(STM32_VRS, sel); + writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); + + return 0; +} + +static int stm32_vrefbuf_get_voltage_sel(struct regulator_dev *rdev) +{ + struct stm32_vrefbuf *priv = to_stm32_vrefbuf(rdev); + u32 val; + int ret; + + val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); + ret = FIELD_GET(STM32_VRS, val); + + return ret; +} + +static const struct regulator_ops stm32_vrefbuf_volt_ops = { + .enable = stm32_vrefbuf_enable, + .disable = stm32_vrefbuf_disable, + .is_enabled = stm32_vrefbuf_is_enabled, + .get_voltage_sel = stm32_vrefbuf_get_voltage_sel, + .set_voltage_sel = stm32_vrefbuf_set_voltage_sel, + .list_voltage = regulator_list_voltage_table, +}; + +static const struct stm32_vrefbuf_desc stm32_vrefbuf_regu = { + .desc = { + .volt_table = stm32_vrefbuf_voltages, + .n_voltages = ARRAY_SIZE(stm32_vrefbuf_voltages), + .ops = &stm32_vrefbuf_volt_ops, + .off_on_delay = 1000, + }, + .supply_name = "vdda", +}; + +static int stm32_vrefbuf_probe(struct device *dev) +{ + struct stm32_vrefbuf *priv; + struct regulator_dev *rdev; + struct regulator *supply; + int ret; + + supply = regulator_get(dev, stm32_vrefbuf_regu.supply_name); + if (IS_ERR(supply)) + return PTR_ERR(supply); + + priv = xzalloc(sizeof(*priv)); + priv->dev = dev; + + priv->base = dev_request_mem_region(dev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->clk = clk_get(dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + ret = clk_enable(priv->clk); + if (ret) { + dev_err(dev, "clk enable failed with error %d\n", ret); + return ret; + } + + rdev = &priv->rdev; + + rdev->dev = dev; + rdev->desc = &stm32_vrefbuf_regu.desc; + + ret = of_regulator_register(rdev, dev->of_node); + if (ret) { + ret = PTR_ERR(rdev); + dev_err(dev, "register failed with error %d\n", ret); + goto err_clk_dis; + } + + regulator_enable(supply); + + dev->priv = priv; + + return 0; + +err_clk_dis: + clk_disable(priv->clk); + + return ret; +} + +static void stm32_vrefbuf_remove(struct device *dev) +{ + struct stm32_vrefbuf *priv = dev->priv; + + clk_disable(priv->clk); +}; + +static const struct of_device_id __maybe_unused stm32_vrefbuf_of_match[] = { + { .compatible = "st,stm32-vrefbuf", }, + {}, +}; +MODULE_DEVICE_TABLE(of, stm32_vrefbuf_of_match); + +static struct driver stm32_vrefbuf_driver = { + .probe = stm32_vrefbuf_probe, + .name = "stm32-vrefbuf", + .remove = stm32_vrefbuf_remove, + .of_compatible = stm32_vrefbuf_of_match, +}; +device_platform_driver(stm32_vrefbuf_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics STM32 VREFBUF driver"); +MODULE_ALIAS("platform:stm32-vrefbuf"); diff --git a/drivers/regulator/stpmic1_regulator.c b/drivers/regulator/stpmic1_regulator.c index aaaba092c1..3ed351b580 100644 --- a/drivers/regulator/stpmic1_regulator.c +++ b/drivers/regulator/stpmic1_regulator.c @@ -1,11 +1,11 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only // Copyright (C) STMicroelectronics 2018 // Author: Pascal Paillet <p.paillet@st.com> for STMicroelectronics. #include <common.h> #include <init.h> #include <of_device.h> -#include <regmap.h> +#include <linux/regmap.h> #include <linux/regulator/of_regulator.h> #include <regulator.h> #include <linux/mfd/stpmic1.h> @@ -21,7 +21,6 @@ * @icc_mask: icc register mask */ struct stpmic1_regulator_cfg { - struct device_d *dev; struct regulator_dev rdev; struct regulator_desc desc; u8 mask_reset_reg; @@ -127,6 +126,7 @@ static const struct regulator_ops stpmic1_ldo_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, }; @@ -136,6 +136,7 @@ static const struct regulator_ops stpmic1_ldo3_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, }; @@ -151,6 +152,7 @@ static const struct regulator_ops stpmic1_buck_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, }; @@ -183,6 +185,7 @@ static const struct regulator_ops stpmic1_switch_regul_ops = { .enable_mask = LDO_ENABLE_MASK, \ .enable_val = 1, \ .disable_val = 0, \ + .supply_name = #base, \ } #define REG_LDO3(ids, base) { \ @@ -196,6 +199,7 @@ static const struct regulator_ops stpmic1_switch_regul_ops = { .enable_mask = LDO_ENABLE_MASK, \ .enable_val = 1, \ .disable_val = 0, \ + .supply_name = #base, \ } #define REG_LDO4(ids, base) { \ @@ -206,6 +210,7 @@ static const struct regulator_ops stpmic1_switch_regul_ops = { .enable_mask = LDO_ENABLE_MASK, \ .enable_val = 1, \ .disable_val = 0, \ + .supply_name = #base, \ } #define REG_BUCK(ids, base) { \ @@ -219,6 +224,7 @@ static const struct regulator_ops stpmic1_switch_regul_ops = { .enable_mask = BUCK_ENABLE_MASK, \ .enable_val = 1, \ .disable_val = 0, \ + .supply_name = #base, \ } #define REG_VREF_DDR(ids, base) { \ @@ -229,6 +235,7 @@ static const struct regulator_ops stpmic1_switch_regul_ops = { .enable_mask = BUCK_ENABLE_MASK, \ .enable_val = 1, \ .disable_val = 0, \ + .supply_name = #base, \ } #define REG_BOOST(ids, base) { \ @@ -239,6 +246,7 @@ static const struct regulator_ops stpmic1_switch_regul_ops = { .enable_mask = BOOST_ENABLED, \ .enable_val = BOOST_ENABLED, \ .disable_val = 0, \ + .supply_name = #base, \ } #define REG_VBUS_OTG(ids, base) { \ @@ -249,6 +257,7 @@ static const struct regulator_ops stpmic1_switch_regul_ops = { .enable_mask = USBSW_OTG_SWITCH_ENABLED, \ .enable_val = USBSW_OTG_SWITCH_ENABLED, \ .disable_val = 0, \ + .supply_name = #base, \ } #define REG_SW_OUT(ids, base) { \ @@ -259,6 +268,7 @@ static const struct regulator_ops stpmic1_switch_regul_ops = { .enable_mask = SWIN_SWOUT_ENABLED, \ .enable_val = SWIN_SWOUT_ENABLED, \ .disable_val = 0, \ + .supply_name = #base, \ } static struct stpmic1_regulator_cfg stpmic1_regulator_cfgs[] = { @@ -377,14 +387,19 @@ static struct of_regulator_match stpmic1_matches[] = { MATCH(pwr_sw2, SW_OUT), }; -static int stpmic1_regulator_register(struct device_d *dev, int id, +static int stpmic1_regulator_register(struct device *dev, int id, struct of_regulator_match *match, struct stpmic1_regulator_cfg *cfg) { int ret; - cfg->dev = dev; + if (!match->of_node) { + dev_dbg(dev, "Skip missing DTB regulator %s", match->name); + return 0; + } + cfg->rdev.desc = &cfg->desc; + cfg->rdev.dev = dev; cfg->rdev.regmap = dev_get_regmap(dev->parent, NULL); if (IS_ERR(cfg->rdev.regmap)) return PTR_ERR(cfg->rdev.regmap); @@ -400,11 +415,11 @@ static int stpmic1_regulator_register(struct device_d *dev, int id, return 0; } -static int stpmic1_regulator_probe(struct device_d *dev) +static int stpmic1_regulator_probe(struct device *dev) { int i, ret; - ret = of_regulator_match(dev, dev->device_node, stpmic1_matches, + ret = of_regulator_match(dev, dev->of_node, stpmic1_matches, ARRAY_SIZE(stpmic1_matches)); if (ret < 0) { dev_err(dev, "Error in PMIC regulator device tree node"); @@ -427,10 +442,11 @@ static __maybe_unused const struct of_device_id stpmic1_regulator_of_match[] = { { .compatible = "st,stpmic1-regulators" }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, stpmic1_regulator_of_match); -static struct driver_d stpmic1_regulator_driver = { +static struct driver stpmic1_regulator_driver = { .name = "stpmic1-regulator", .probe = stpmic1_regulator_probe, .of_compatible = DRV_OF_COMPAT(stpmic1_regulator_of_match), }; -device_platform_driver(stpmic1_regulator_driver); +coredevice_platform_driver(stpmic1_regulator_driver); |