diff options
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/Kconfig | 8 | ||||
-rw-r--r-- | drivers/power/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/as3722.c | 16 | ||||
-rw-r--r-- | drivers/power/pmic/Kconfig | 43 | ||||
-rw-r--r-- | drivers/power/pmic/Makefile | 3 | ||||
-rw-r--r-- | drivers/power/pmic/i2c_pmic_emul.c | 142 | ||||
-rw-r--r-- | drivers/power/pmic/max77686.c | 92 | ||||
-rw-r--r-- | drivers/power/pmic/pmic-uclass.c | 145 | ||||
-rw-r--r-- | drivers/power/pmic/pmic_max77686.c | 2 | ||||
-rw-r--r-- | drivers/power/pmic/sandbox.c | 79 | ||||
-rw-r--r-- | drivers/power/regulator/Kconfig | 63 | ||||
-rw-r--r-- | drivers/power/regulator/Makefile | 11 | ||||
-rw-r--r-- | drivers/power/regulator/fixed.c | 126 | ||||
-rw-r--r-- | drivers/power/regulator/max77686.c | 825 | ||||
-rw-r--r-- | drivers/power/regulator/regulator-uclass.c | 332 | ||||
-rw-r--r-- | drivers/power/regulator/sandbox.c | 355 |
16 files changed, 2238 insertions, 5 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index f8f0239484f..23cdd714ae6 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -1,3 +1,9 @@ +menu "Power" + +source "drivers/power/pmic/Kconfig" + +source "drivers/power/regulator/Kconfig" + config AXP221_POWER boolean "axp221 / axp223 pmic support" depends on MACH_SUN6I || MACH_SUN8I @@ -73,3 +79,5 @@ config AXP221_ELDO3_VOLT disable eldo3. On some A31(s) tablets it might be used to supply 1.2V for the SSD2828 chip (converter of parallel LCD interface into MIPI DSI). + +endmenu diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 214565241ed..a2d3c047db8 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_TPS6586X_POWER) += tps6586x.o obj-$(CONFIG_TWL4030_POWER) += twl4030.o obj-$(CONFIG_TWL6030_POWER) += twl6030.o obj-$(CONFIG_PALMAS_POWER) += palmas.o - obj-$(CONFIG_POWER) += power_core.o obj-$(CONFIG_DIALOG_POWER) += power_dialog.o obj-$(CONFIG_POWER_FSL) += power_fsl.o diff --git a/drivers/power/as3722.c b/drivers/power/as3722.c index a60bb5f83fb..c09e1de06f4 100644 --- a/drivers/power/as3722.c +++ b/drivers/power/as3722.c @@ -27,7 +27,7 @@ #define AS3722_DEVICE_ID 0x0c #define AS3722_ASIC_ID2 0x91 -static int as3722_read(struct udevice *pmic, u8 reg, u8 *value) +int as3722_read(struct udevice *pmic, u8 reg, u8 *value) { int err; @@ -38,7 +38,7 @@ static int as3722_read(struct udevice *pmic, u8 reg, u8 *value) return 0; } -static int as3722_write(struct udevice *pmic, u8 reg, u8 value) +int as3722_write(struct udevice *pmic, u8 reg, u8 value) { int err; @@ -234,6 +234,15 @@ int as3722_gpio_direction_output(struct udevice *pmic, unsigned int gpio, return 0; } +/* Temporary function until we get the pmic framework */ +int as3722_get(struct udevice **devp) +{ + int bus = 0; + int address = 0x40; + + return i2c_get_chip_for_busnum(bus, address, 1, devp); +} + int as3722_init(struct udevice **devp) { struct udevice *pmic; @@ -258,7 +267,8 @@ int as3722_init(struct udevice **devp) debug("AS3722 revision %#x found on I2C bus %u, address %#x\n", revision, bus, address); - *devp = pmic; + if (devp) + *devp = pmic; return 0; } diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig new file mode 100644 index 00000000000..164f42143fa --- /dev/null +++ b/drivers/power/pmic/Kconfig @@ -0,0 +1,43 @@ +config DM_PMIC + bool "Enable Driver Model for PMIC drivers (UCLASS_PMIC)" + depends on DM + ---help--- + This config enables the driver-model PMIC support. + UCLASS_PMIC - designed to provide an I/O interface for PMIC devices. + For the multi-function PMIC devices, this can be used as parent I/O + device for each IC's interface. Then, each children uses its parent + for read/write. For detailed description, please refer to the files: + - 'drivers/power/pmic/pmic-uclass.c' + - 'include/power/pmic.h' + +config DM_PMIC_MAX77686 + bool "Enable Driver Model for PMIC MAX77686" + depends on DM_PMIC + ---help--- + This config enables implementation of driver-model pmic uclass features + for PMIC MAX77686. The driver implements read/write operations. + +config DM_PMIC_SANDBOX + bool "Enable Driver Model for emulated Sandbox PMIC " + depends on DM_PMIC + ---help--- + Enable the driver for Sandbox PMIC emulation. The emulated PMIC device + depends on two drivers: + - sandbox PMIC I/O driver - implements dm pmic operations + - sandbox PMIC i2c emul driver - emulates the PMIC's I2C transmission + + A detailed information can be found in header: '<power/sandbox_pmic.h>' + + The Sandbox PMIC info: + * I/O interface: + - I2C chip address: 0x40 + - first register address: 0x0 + - register count: 0x10 + * Adjustable outputs: + - 2x LDO + - 2x BUCK + - Each, with a different operating conditions (header). + * Reset values: + - set by i2c emul driver's probe() (defaults in header) + + Driver binding info: doc/device-tree-bindings/pmic/sandbox.txt diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 985cfdb9018..ae86f041f34 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -5,6 +5,9 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_DM_PMIC) += pmic-uclass.o +obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o +obj-$(CONFIG_DM_PMIC_SANDBOX) += sandbox.o i2c_pmic_emul.o obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o obj-$(CONFIG_POWER_MAX8998) += pmic_max8998.o obj-$(CONFIG_POWER_MAX8997) += pmic_max8997.o diff --git a/drivers/power/pmic/i2c_pmic_emul.c b/drivers/power/pmic/i2c_pmic_emul.c new file mode 100644 index 00000000000..aeab5c949ad --- /dev/null +++ b/drivers/power/pmic/i2c_pmic_emul.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2015 Samsung Electronics + * Przemyslaw Marczak <p.marczak@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <power/pmic.h> +#include <power/sandbox_pmic.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * struct sandbox_i2c_pmic_plat_data - platform data for the PMIC + * + * @rw_reg: PMICs register of the chip I/O transaction + * @reg: PMICs registers array + */ +struct sandbox_i2c_pmic_plat_data { + u8 rw_reg; + u8 reg[SANDBOX_PMIC_REG_COUNT]; +}; + +static int sandbox_i2c_pmic_read_data(struct udevice *emul, uchar chip, + uchar *buffer, int len) +{ + struct sandbox_i2c_pmic_plat_data *plat = dev_get_platdata(emul); + + if (plat->rw_reg + len > SANDBOX_PMIC_REG_COUNT) { + error("Request exceeds PMIC register range! Max register: %#x", + SANDBOX_PMIC_REG_COUNT); + return -EFAULT; + } + + debug("Read PMIC: %#x at register: %#x count: %d\n", + (unsigned)chip & 0xff, plat->rw_reg, len); + + memcpy(buffer, &plat->reg[plat->rw_reg], len); + + return 0; +} + +static int sandbox_i2c_pmic_write_data(struct udevice *emul, uchar chip, + uchar *buffer, int len, + bool next_is_read) +{ + struct sandbox_i2c_pmic_plat_data *plat = dev_get_platdata(emul); + + /* Probe only */ + if (!len) + return 0; + + /* Set PMIC register for I/O */ + plat->rw_reg = *buffer; + + debug("Write PMIC: %#x at register: %#x count: %d\n", + (unsigned)chip & 0xff, plat->rw_reg, len); + + /* For read operation, set (write) only chip reg */ + if (next_is_read) + return 0; + + buffer++; + len--; + + if (plat->rw_reg + len > SANDBOX_PMIC_REG_COUNT) { + error("Request exceeds PMIC register range! Max register: %#x", + SANDBOX_PMIC_REG_COUNT); + } + + memcpy(&plat->reg[plat->rw_reg], buffer, len); + + return 0; +} + +static int sandbox_i2c_pmic_xfer(struct udevice *emul, struct i2c_msg *msg, + int nmsgs) +{ + int ret = 0; + + for (; nmsgs > 0; nmsgs--, msg++) { + bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD); + if (msg->flags & I2C_M_RD) { + ret = sandbox_i2c_pmic_read_data(emul, msg->addr, + msg->buf, msg->len); + } else { + ret = sandbox_i2c_pmic_write_data(emul, msg->addr, + msg->buf, msg->len, + next_is_read); + } + + if (ret) + break; + } + + return ret; +} + +static int sandbox_i2c_pmic_ofdata_to_platdata(struct udevice *emul) +{ + struct sandbox_i2c_pmic_plat_data *plat = dev_get_platdata(emul); + const u8 *reg_defaults; + + debug("%s:%d Setting PMIC default registers\n", __func__, __LINE__); + + reg_defaults = fdtdec_locate_byte_array(gd->fdt_blob, emul->of_offset, + "reg-defaults", + SANDBOX_PMIC_REG_COUNT); + + if (!reg_defaults) { + error("Property \"reg-defaults\" not found for device: %s!", + emul->name); + return -EINVAL; + } + + memcpy(&plat->reg, reg_defaults, SANDBOX_PMIC_REG_COUNT); + + return 0; +} + +struct dm_i2c_ops sandbox_i2c_pmic_emul_ops = { + .xfer = sandbox_i2c_pmic_xfer, +}; + +static const struct udevice_id sandbox_i2c_pmic_ids[] = { + { .compatible = "sandbox,i2c-pmic" }, + { } +}; + +U_BOOT_DRIVER(sandbox_i2c_pmic_emul) = { + .name = "sandbox_i2c_pmic_emul", + .id = UCLASS_I2C_EMUL, + .of_match = sandbox_i2c_pmic_ids, + .ofdata_to_platdata = sandbox_i2c_pmic_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct sandbox_i2c_pmic_plat_data), + .ops = &sandbox_i2c_pmic_emul_ops, +}; diff --git a/drivers/power/pmic/max77686.c b/drivers/power/pmic/max77686.c new file mode 100644 index 00000000000..3523b4a2de2 --- /dev/null +++ b/drivers/power/pmic/max77686.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2014-2015 Samsung Electronics + * Przemyslaw Marczak <p.marczak@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/max77686_pmic.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const struct pmic_child_info pmic_children_info[] = { + { .prefix = "ldo", .driver = MAX77686_LDO_DRIVER }, + { .prefix = "buck", .driver = MAX77686_BUCK_DRIVER }, + { }, +}; + +static int max77686_reg_count(struct udevice *dev) +{ + return MAX77686_NUM_OF_REGS; +} + +static int max77686_write(struct udevice *dev, uint reg, const uint8_t *buff, + int len) +{ + if (dm_i2c_write(dev, reg, buff, len)) { + error("write error to device: %p register: %#x!", dev, reg); + return -EIO; + } + + return 0; +} + +static int max77686_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{ + if (dm_i2c_read(dev, reg, buff, len)) { + error("read error from device: %p register: %#x!", dev, reg); + return -EIO; + } + + return 0; +} + +static int max77686_bind(struct udevice *dev) +{ + int regulators_node; + const void *blob = gd->fdt_blob; + int children; + + regulators_node = fdt_subnode_offset(blob, dev->of_offset, + "voltage-regulators"); + if (regulators_node <= 0) { + debug("%s: %s regulators subnode not found!", __func__, + dev->name); + return -ENXIO; + } + + debug("%s: '%s' - found regulators subnode\n", __func__, dev->name); + + children = pmic_bind_children(dev, regulators_node, pmic_children_info); + if (!children) + debug("%s: %s - no child found\n", __func__, dev->name); + + /* Always return success for this device */ + return 0; +} + +static struct dm_pmic_ops max77686_ops = { + .reg_count = max77686_reg_count, + .read = max77686_read, + .write = max77686_write, +}; + +static const struct udevice_id max77686_ids[] = { + { .compatible = "maxim,max77686" }, + { } +}; + +U_BOOT_DRIVER(pmic_max77686) = { + .name = "max77686 pmic", + .id = UCLASS_PMIC, + .of_match = max77686_ids, + .bind = max77686_bind, + .ops = &max77686_ops, +}; diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c new file mode 100644 index 00000000000..812ac13baa3 --- /dev/null +++ b/drivers/power/pmic/pmic-uclass.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2014-2015 Samsung Electronics + * Przemyslaw Marczak <p.marczak@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <dm/lists.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <power/pmic.h> +#include <linux/ctype.h> + +DECLARE_GLOBAL_DATA_PTR; + +static ulong str_get_num(const char *ptr, const char *maxptr) +{ + if (!ptr || !maxptr) + return 0; + + while (!isdigit(*ptr) && ptr++ < maxptr); + + return simple_strtoul(ptr, NULL, 0); +} + +int pmic_bind_children(struct udevice *pmic, int offset, + const struct pmic_child_info *child_info) +{ + const struct pmic_child_info *info; + const void *blob = gd->fdt_blob; + struct driver *drv; + struct udevice *child; + const char *node_name; + int node_name_len; + int bind_count = 0; + int node; + int prefix_len; + int ret; + + debug("%s for '%s' at node offset: %d\n", __func__, pmic->name, + pmic->of_offset); + + for (node = fdt_first_subnode(blob, offset); + node > 0; + node = fdt_next_subnode(blob, node)) { + node_name = fdt_get_name(blob, node, &node_name_len); + + debug("* Found child node: '%s' at offset:%d\n", node_name, + node); + + child = NULL; + for (info = child_info; info->prefix && info->driver; info++) { + prefix_len = strlen(info->prefix); + if (strncasecmp(info->prefix, node_name, prefix_len)) + continue; + + debug(" - compatible prefix: '%s'\n", info->prefix); + + drv = lists_driver_lookup_name(info->driver); + if (!drv) { + debug(" - driver: '%s' not found!\n", + info->driver); + continue; + } + + debug(" - found child driver: '%s'\n", drv->name); + + ret = device_bind(pmic, drv, node_name, NULL, + node, &child); + if (ret) { + debug(" - child binding error: %d\n", ret); + continue; + } + + debug(" - bound child device: '%s'\n", child->name); + + child->driver_data = str_get_num(node_name + + prefix_len, + node_name + + node_name_len); + + debug(" - set 'child->driver_data': %lu\n", + child->driver_data); + break; + } + + if (child) + bind_count++; + else + debug(" - compatible prefix not found\n"); + } + + debug("Bound: %d childs for PMIC: '%s'\n", bind_count, pmic->name); + return bind_count; +} + +int pmic_get(const char *name, struct udevice **devp) +{ + return uclass_get_device_by_name(UCLASS_PMIC, name, devp); +} + +int pmic_reg_count(struct udevice *dev) +{ + const struct dm_pmic_ops *ops = dev_get_driver_ops(dev); + + if (!ops || !ops->reg_count) + return -ENOSYS; + + return ops->reg_count(dev); +} + +int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len) +{ + const struct dm_pmic_ops *ops = dev_get_driver_ops(dev); + + if (!buffer) + return -EFAULT; + + if (!ops || !ops->read) + return -ENOSYS; + + return ops->read(dev, reg, buffer, len); +} + +int pmic_write(struct udevice *dev, uint reg, const uint8_t *buffer, int len) +{ + const struct dm_pmic_ops *ops = dev_get_driver_ops(dev); + + if (!buffer) + return -EFAULT; + + if (!ops || !ops->write) + return -ENOSYS; + + return ops->write(dev, reg, buffer, len); +} + +UCLASS_DRIVER(pmic) = { + .id = UCLASS_PMIC, + .name = "pmic", +}; diff --git a/drivers/power/pmic/pmic_max77686.c b/drivers/power/pmic/pmic_max77686.c index 95b1a57ca2b..1ad810acc26 100644 --- a/drivers/power/pmic/pmic_max77686.c +++ b/drivers/power/pmic/pmic_max77686.c @@ -295,7 +295,7 @@ int pmic_init(unsigned char bus) p->name = name; p->interface = PMIC_I2C; - p->number_of_regs = PMIC_NUM_OF_REGS; + p->number_of_regs = MAX77686_NUM_OF_REGS; p->hw.i2c.tx_num = 1; puts("Board PMIC init\n"); diff --git a/drivers/power/pmic/sandbox.c b/drivers/power/pmic/sandbox.c new file mode 100644 index 00000000000..3e56acd5e27 --- /dev/null +++ b/drivers/power/pmic/sandbox.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2015 Samsung Electronics + * Przemyslaw Marczak <p.marczak@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/sandbox_pmic.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const struct pmic_child_info pmic_children_info[] = { + { .prefix = SANDBOX_OF_LDO_PREFIX, .driver = SANDBOX_LDO_DRIVER }, + { .prefix = SANDBOX_OF_BUCK_PREFIX, .driver = SANDBOX_BUCK_DRIVER }, + { }, +}; + +static int sandbox_pmic_reg_count(struct udevice *dev) +{ + return SANDBOX_PMIC_REG_COUNT; +} + +static int sandbox_pmic_write(struct udevice *dev, uint reg, + const uint8_t *buff, int len) +{ + if (dm_i2c_write(dev, reg, buff, len)) { + error("write error to device: %p register: %#x!", dev, reg); + return -EIO; + } + + return 0; +} + +static int sandbox_pmic_read(struct udevice *dev, uint reg, + uint8_t *buff, int len) +{ + if (dm_i2c_read(dev, reg, buff, len)) { + error("read error from device: %p register: %#x!", dev, reg); + return -EIO; + } + + return 0; +} + +static int sandbox_pmic_bind(struct udevice *dev) +{ + if (!pmic_bind_children(dev, dev->of_offset, pmic_children_info)) + error("%s:%d PMIC: %s - no child found!", __func__, __LINE__, + dev->name); + + /* Always return success for this device - allows for PMIC I/O */ + return 0; +} + +static struct dm_pmic_ops sandbox_pmic_ops = { + .reg_count = sandbox_pmic_reg_count, + .read = sandbox_pmic_read, + .write = sandbox_pmic_write, +}; + +static const struct udevice_id sandbox_pmic_ids[] = { + { .compatible = "sandbox,pmic" }, + { } +}; + +U_BOOT_DRIVER(sandbox_pmic) = { + .name = "sandbox_pmic", + .id = UCLASS_PMIC, + .of_match = sandbox_pmic_ids, + .bind = sandbox_pmic_bind, + .ops = &sandbox_pmic_ops, +}; diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig new file mode 100644 index 00000000000..6289b83910c --- /dev/null +++ b/drivers/power/regulator/Kconfig @@ -0,0 +1,63 @@ +config DM_REGULATOR + bool "Enable Driver Model for REGULATOR drivers (UCLASS_REGULATOR)" + depends on DM + ---help--- + This config enables the driver model regulator support. + UCLASS_REGULATOR - designed to provide a common API for basic regulator's + functions, like get/set Voltage or Current value, enable state, etc... + Note: + When enabling this, please read the description, found in the files: + - 'include/power/pmic.h' + - 'include/power/regulator.h' + - 'drivers/power/pmic/pmic-uclass.c' + - 'drivers/power/pmic/regulator-uclass.c' + It's important to call the device_bind() with the proper node offset, + when binding the regulator devices. The pmic_bind_childs() can be used + for this purpose if PMIC I/O driver is implemented or dm_scan_fdt_node() + otherwise. Detailed information can be found in the header file. + +config DM_REGULATOR_MAX77686 + bool "Enable Driver Model for REGULATOR MAX77686" + depends on DM_REGULATOR && DM_PMIC_MAX77686 + ---help--- + This config enables implementation of driver-model regulator uclass + features for REGULATOR MAX77686. The driver implements get/set api for: + value, enable and mode. + +config DM_REGULATOR_FIXED + bool "Enable Driver Model for REGULATOR Fixed value" + depends on DM_REGULATOR + ---help--- + This config enables implementation of driver-model regulator uclass + features for fixed value regulators. The driver implements get/set api + for enable and get only for voltage value. + +config DM_REGULATOR_SANDBOX + bool "Enable Driver Model for Sandbox PMIC regulator" + depends on DM_REGULATOR && DM_PMIC_SANDBOX + ---help--- + Enable the regulator driver for emulated Sandbox PMIC. + The emulated PMIC device depends on two drivers: + - sandbox PMIC I/O driver - implements dm pmic operations + - sandbox PMIC regulator driver - implements dm regulator operations + - sandbox PMIC i2c emul driver - emulates the PMIC's I2C transmission + + The regulator driver provides uclass operations for sandbox PMIC's + regulators. The driver implements get/set api for: voltage, current, + operation mode and enable state. + The driver supports LDO and BUCK regulators. + + The Sandbox PMIC info: + * I/O interface: + - I2C chip address: 0x40 + - first register address: 0x0 + - register count: 0x10 + * Adjustable outputs: + - 2x LDO + - 2x BUCK + - Each, with a different operating conditions (header). + * Reset values: + - set by i2c emul driver's probe() (defaults in header) + + A detailed information can be found in header: '<power/sandbox_pmic.h>' + Binding info: 'doc/device-tree-bindings/pmic/max77686.txt' diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile new file mode 100644 index 00000000000..96aa6249611 --- /dev/null +++ b/drivers/power/regulator/Makefile @@ -0,0 +1,11 @@ +# +# Copyright (C) 2015 Samsung Electronics +# Przemyslaw Marczak <p.marczak@samsung.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o +obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o +obj-$(CONFIG_DM_REGULATOR_FIXED) += fixed.o +obj-$(CONFIG_DM_REGULATOR_SANDBOX) += sandbox.o diff --git a/drivers/power/regulator/fixed.c b/drivers/power/regulator/fixed.c new file mode 100644 index 00000000000..d053817fc22 --- /dev/null +++ b/drivers/power/regulator/fixed.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2015 Samsung Electronics + * + * Przemyslaw Marczak <p.marczak@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <asm/gpio.h> +#include <power/pmic.h> +#include <power/regulator.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct fixed_regulator_platdata { + struct gpio_desc gpio; /* GPIO for regulator enable control */ +}; + +static int fixed_regulator_ofdata_to_platdata(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + struct fixed_regulator_platdata *dev_pdata; + struct gpio_desc *gpio; + int ret; + + dev_pdata = dev_get_platdata(dev); + uc_pdata = dev_get_uclass_platdata(dev); + if (!uc_pdata) + return -ENXIO; + + /* Set type to fixed */ + uc_pdata->type = REGULATOR_TYPE_FIXED; + + /* Get fixed regulator gpio desc */ + gpio = &dev_pdata->gpio; + ret = gpio_request_by_name(dev, "gpio", 0, gpio, GPIOD_IS_OUT); + if (ret) + debug("Fixed regulator gpio - not found! Error: %d", ret); + + return 0; +} + +static int fixed_regulator_get_value(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + + uc_pdata = dev_get_uclass_platdata(dev); + if (!uc_pdata) + return -ENXIO; + + if (uc_pdata->min_uV != uc_pdata->max_uV) { + debug("Invalid constraints for: %s\n", uc_pdata->name); + return -EINVAL; + } + + return uc_pdata->min_uV; +} + +static int fixed_regulator_get_current(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + + uc_pdata = dev_get_uclass_platdata(dev); + if (!uc_pdata) + return -ENXIO; + + if (uc_pdata->min_uA != uc_pdata->max_uA) { + debug("Invalid constraints for: %s\n", uc_pdata->name); + return -EINVAL; + } + + return uc_pdata->min_uA; +} + +static bool fixed_regulator_get_enable(struct udevice *dev) +{ + struct fixed_regulator_platdata *dev_pdata = dev_get_platdata(dev); + + if (!dev_pdata->gpio.dev) + return false; + + return dm_gpio_get_value(&dev_pdata->gpio); +} + +static int fixed_regulator_set_enable(struct udevice *dev, bool enable) +{ + struct fixed_regulator_platdata *dev_pdata = dev_get_platdata(dev); + int ret; + + if (!dev_pdata->gpio.dev) + return -ENOSYS; + + ret = dm_gpio_set_value(&dev_pdata->gpio, enable); + if (ret) { + error("Can't set regulator : %s gpio to: %d\n", dev->name, + enable); + return ret; + } + return 0; +} + +static const struct dm_regulator_ops fixed_regulator_ops = { + .get_value = fixed_regulator_get_value, + .get_current = fixed_regulator_get_current, + .get_enable = fixed_regulator_get_enable, + .set_enable = fixed_regulator_set_enable, +}; + +static const struct udevice_id fixed_regulator_ids[] = { + { .compatible = "regulator-fixed" }, + { }, +}; + +U_BOOT_DRIVER(fixed_regulator) = { + .name = "fixed regulator", + .id = UCLASS_REGULATOR, + .ops = &fixed_regulator_ops, + .of_match = fixed_regulator_ids, + .ofdata_to_platdata = fixed_regulator_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct fixed_regulator_platdata), +}; diff --git a/drivers/power/regulator/max77686.c b/drivers/power/regulator/max77686.c new file mode 100644 index 00000000000..37ebe94521c --- /dev/null +++ b/drivers/power/regulator/max77686.c @@ -0,0 +1,825 @@ +/* + * Copyright (C) 2012-2015 Samsung Electronics + * + * Rajeshwari Shinde <rajeshwari.s@samsung.com> + * Przemyslaw Marczak <p.marczak@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/max77686_pmic.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define MODE(_id, _val, _name) { \ + .id = _id, \ + .register_value = _val, \ + .name = _name, \ +} + +/* LDO: 1,3,4,5,9,17,18,19,20,21,22,23,24,26,26,27 */ +static struct dm_regulator_mode max77686_ldo_mode_standby1[] = { + MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"), + MODE(OPMODE_LPM, MAX77686_LDO_MODE_LPM, "LPM"), + MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"), + MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"), +}; + +/* LDO: 2,6,7,8,10,11,12,14,15,16 */ +static struct dm_regulator_mode max77686_ldo_mode_standby2[] = { + MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"), + MODE(OPMODE_STANDBY, MAX77686_LDO_MODE_STANDBY, "ON/OFF"), + MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"), + MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"), +}; + +/* Buck: 1 */ +static struct dm_regulator_mode max77686_buck_mode_standby[] = { + MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"), + MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"), + MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"), +}; + +/* Buck: 2,3,4 */ +static struct dm_regulator_mode max77686_buck_mode_lpm[] = { + MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"), + MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"), + MODE(OPMODE_LPM, MAX77686_BUCK_MODE_LPM, "LPM"), + MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"), +}; + +/* Buck: 5,6,7,8,9 */ +static struct dm_regulator_mode max77686_buck_mode_onoff[] = { + MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"), + MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"), +}; + +static const char max77686_buck_addr[] = { + 0xff, 0x10, 0x12, 0x1c, 0x26, 0x30, 0x32, 0x34, 0x36, 0x38 +}; + +static int max77686_buck_volt2hex(int buck, int uV) +{ + unsigned int hex = 0; + unsigned int hex_max = 0; + + switch (buck) { + case 2: + case 3: + case 4: + /* hex = (uV - 600000) / 12500; */ + hex = (uV - MAX77686_BUCK_UV_LMIN) / MAX77686_BUCK_UV_LSTEP; + hex_max = MAX77686_BUCK234_VOLT_MAX_HEX; + /** + * Those use voltage scaller - temporary not implemented + * so return just 0 + */ + return -ENOSYS; + default: + /* hex = (uV - 750000) / 50000; */ + hex = (uV - MAX77686_BUCK_UV_HMIN) / MAX77686_BUCK_UV_HSTEP; + hex_max = MAX77686_BUCK_VOLT_MAX_HEX; + break; + } + + if (hex >= 0 && hex <= hex_max) + return hex; + + error("Value: %d uV is wrong for BUCK%d", uV, buck); + return -EINVAL; +} + +static int max77686_buck_hex2volt(int buck, int hex) +{ + unsigned uV = 0; + unsigned int hex_max = 0; + + if (hex < 0) + goto bad_hex; + + switch (buck) { + case 2: + case 3: + case 4: + hex_max = MAX77686_BUCK234_VOLT_MAX_HEX; + if (hex > hex_max) + goto bad_hex; + + /* uV = hex * 12500 + 600000; */ + uV = hex * MAX77686_BUCK_UV_LSTEP + MAX77686_BUCK_UV_LMIN; + break; + default: + hex_max = MAX77686_BUCK_VOLT_MAX_HEX; + if (hex > hex_max) + goto bad_hex; + + /* uV = hex * 50000 + 750000; */ + uV = hex * MAX77686_BUCK_UV_HSTEP + MAX77686_BUCK_UV_HMIN; + break; + } + + return uV; + +bad_hex: + error("Value: %#x is wrong for BUCK%d", hex, buck); + return -EINVAL; +} + +static int max77686_ldo_volt2hex(int ldo, int uV) +{ + unsigned int hex = 0; + + switch (ldo) { + case 1: + case 2: + case 6: + case 7: + case 8: + case 15: + hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_LSTEP; + /* hex = (uV - 800000) / 25000; */ + break; + default: + hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_HSTEP; + /* hex = (uV - 800000) / 50000; */ + } + + if (hex >= 0 && hex <= MAX77686_LDO_VOLT_MAX_HEX) + return hex; + + error("Value: %d uV is wrong for LDO%d", uV, ldo); + return -EINVAL; +} + +static int max77686_ldo_hex2volt(int ldo, int hex) +{ + unsigned int uV = 0; + + if (hex > MAX77686_LDO_VOLT_MAX_HEX) + goto bad_hex; + + switch (ldo) { + case 1: + case 2: + case 6: + case 7: + case 8: + case 15: + /* uV = hex * 25000 + 800000; */ + uV = hex * MAX77686_LDO_UV_LSTEP + MAX77686_LDO_UV_MIN; + break; + default: + /* uV = hex * 50000 + 800000; */ + uV = hex * MAX77686_LDO_UV_HSTEP + MAX77686_LDO_UV_MIN; + } + + return uV; + +bad_hex: + error("Value: %#x is wrong for ldo%d", hex, ldo); + return -EINVAL; +} + +static int max77686_ldo_hex2mode(int ldo, int hex) +{ + if (hex > MAX77686_LDO_MODE_MASK) + return -EINVAL; + + switch (hex) { + case MAX77686_LDO_MODE_OFF: + return OPMODE_OFF; + case MAX77686_LDO_MODE_LPM: /* == MAX77686_LDO_MODE_STANDBY: */ + /* The same mode values but different meaning for each ldo */ + switch (ldo) { + case 2: + case 6: + case 7: + case 8: + case 10: + case 11: + case 12: + case 14: + case 15: + case 16: + return OPMODE_STANDBY; + default: + return OPMODE_LPM; + } + case MAX77686_LDO_MODE_STANDBY_LPM: + return OPMODE_STANDBY_LPM; + case MAX77686_LDO_MODE_ON: + return OPMODE_ON; + default: + return -EINVAL; + } +} + +static int max77686_buck_hex2mode(int buck, int hex) +{ + if (hex > MAX77686_BUCK_MODE_MASK) + return -EINVAL; + + switch (hex) { + case MAX77686_BUCK_MODE_OFF: + return OPMODE_OFF; + case MAX77686_BUCK_MODE_ON: + return OPMODE_ON; + case MAX77686_BUCK_MODE_STANDBY: + switch (buck) { + case 1: + case 2: + case 3: + case 4: + return OPMODE_STANDBY; + default: + return -EINVAL; + } + case MAX77686_BUCK_MODE_LPM: + switch (buck) { + case 2: + case 3: + case 4: + return OPMODE_LPM; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int max77686_buck_modes(int buck, struct dm_regulator_mode **modesp) +{ + int ret = -EINVAL; + + if (buck < 1 || buck > MAX77686_BUCK_NUM) + return ret; + + switch (buck) { + case 1: + *modesp = max77686_buck_mode_standby; + ret = ARRAY_SIZE(max77686_buck_mode_standby); + break; + case 2: + case 3: + case 4: + *modesp = max77686_buck_mode_lpm; + ret = ARRAY_SIZE(max77686_buck_mode_lpm); + break; + default: + *modesp = max77686_buck_mode_onoff; + ret = ARRAY_SIZE(max77686_buck_mode_onoff); + } + + return ret; +} + +static int max77686_ldo_modes(int ldo, struct dm_regulator_mode **modesp, + struct udevice *dev) +{ + int ret = -EINVAL; + + if (ldo < 1 || ldo > MAX77686_LDO_NUM) + return ret; + + switch (ldo) { + case 2: + case 6: + case 7: + case 8: + case 10: + case 11: + case 12: + case 14: + case 15: + case 16: + *modesp = max77686_ldo_mode_standby2; + ret = ARRAY_SIZE(max77686_ldo_mode_standby2); + break; + default: + *modesp = max77686_ldo_mode_standby1; + ret = ARRAY_SIZE(max77686_ldo_mode_standby1); + } + + return ret; +} + +static int max77686_ldo_val(struct udevice *dev, int op, int *uV) +{ + unsigned int ret, hex, adr; + unsigned char val; + int ldo; + + if (op == PMIC_OP_GET) + *uV = 0; + + ldo = dev->driver_data; + if (ldo < 1 || ldo > MAX77686_LDO_NUM) { + error("Wrong ldo number: %d", ldo); + return -EINVAL; + } + + adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1; + + ret = pmic_read(dev->parent, adr, &val, 1); + if (ret) + return ret; + + if (op == PMIC_OP_GET) { + val &= MAX77686_LDO_VOLT_MASK; + ret = max77686_ldo_hex2volt(ldo, val); + if (ret < 0) + return ret; + *uV = ret; + return 0; + } + + hex = max77686_ldo_volt2hex(ldo, *uV); + if (hex < 0) + return hex; + + val &= ~MAX77686_LDO_VOLT_MASK; + val |= hex; + ret = pmic_write(dev->parent, adr, &val, 1); + + return ret; +} + +static int max77686_buck_val(struct udevice *dev, int op, int *uV) +{ + unsigned int hex, ret, mask, adr; + unsigned char val; + int buck; + + buck = dev->driver_data; + if (buck < 1 || buck > MAX77686_BUCK_NUM) { + error("Wrong buck number: %d", buck); + return -EINVAL; + } + + if (op == PMIC_OP_GET) + *uV = 0; + + /* &buck_out = ctrl + 1 */ + adr = max77686_buck_addr[buck] + 1; + + /* mask */ + switch (buck) { + case 2: + case 3: + case 4: + /* Those use voltage scallers - will support in the future */ + mask = MAX77686_BUCK234_VOLT_MASK; + return -ENOSYS; + default: + mask = MAX77686_BUCK_VOLT_MASK; + } + + ret = pmic_read(dev->parent, adr, &val, 1); + if (ret) + return ret; + + if (op == PMIC_OP_GET) { + val &= mask; + ret = max77686_buck_hex2volt(buck, val); + if (ret < 0) + return ret; + *uV = ret; + return 0; + } + + hex = max77686_buck_volt2hex(buck, *uV); + if (hex < 0) + return hex; + + val &= ~mask; + val |= hex; + ret = pmic_write(dev->parent, adr, &val, 1); + + return ret; +} + +static int max77686_ldo_mode(struct udevice *dev, int op, int *opmode) +{ + unsigned int ret, adr, mode; + unsigned char val; + int ldo; + + if (op == PMIC_OP_GET) + *opmode = -EINVAL; + + ldo = dev->driver_data; + if (ldo < 1 || ldo > MAX77686_LDO_NUM) { + error("Wrong ldo number: %d", ldo); + return -EINVAL; + } + + adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1; + + ret = pmic_read(dev->parent, adr, &val, 1); + if (ret) + return ret; + + if (op == PMIC_OP_GET) { + val &= MAX77686_LDO_MODE_MASK; + ret = max77686_ldo_hex2mode(ldo, val); + if (ret < 0) + return ret; + *opmode = ret; + return 0; + } + + /* mode */ + switch (*opmode) { + case OPMODE_OFF: + mode = MAX77686_LDO_MODE_OFF; + break; + case OPMODE_LPM: + switch (ldo) { + case 2: + case 6: + case 7: + case 8: + case 10: + case 11: + case 12: + case 14: + case 15: + case 16: + return -EINVAL; + default: + mode = MAX77686_LDO_MODE_LPM; + } + break; + case OPMODE_STANDBY: + switch (ldo) { + case 2: + case 6: + case 7: + case 8: + case 10: + case 11: + case 12: + case 14: + case 15: + case 16: + mode = MAX77686_LDO_MODE_STANDBY; + break; + default: + return -EINVAL; + } + break; + case OPMODE_STANDBY_LPM: + mode = MAX77686_LDO_MODE_STANDBY_LPM; + break; + case OPMODE_ON: + mode = MAX77686_LDO_MODE_ON; + break; + default: + mode = 0xff; + } + + if (mode == 0xff) { + error("Wrong mode: %d for ldo%d", *opmode, ldo); + return -EINVAL; + } + + val &= ~MAX77686_LDO_MODE_MASK; + val |= mode; + ret = pmic_write(dev->parent, adr, &val, 1); + + return ret; +} + +static int max77686_ldo_enable(struct udevice *dev, int op, bool *enable) +{ + int ret, on_off; + + if (op == PMIC_OP_GET) { + ret = max77686_ldo_mode(dev, op, &on_off); + if (ret) + return ret; + + switch (on_off) { + case OPMODE_OFF: + *enable = 0; + break; + case OPMODE_ON: + *enable = 1; + break; + default: + return -EINVAL; + } + } else if (op == PMIC_OP_SET) { + switch (*enable) { + case 0: + on_off = OPMODE_OFF; + break; + case 1: + on_off = OPMODE_ON; + break; + default: + return -EINVAL; + } + + ret = max77686_ldo_mode(dev, op, &on_off); + if (ret) + return ret; + } + + return 0; +} + +static int max77686_buck_mode(struct udevice *dev, int op, int *opmode) +{ + unsigned int ret, mask, adr, mode, mode_shift; + unsigned char val; + int buck; + + buck = dev->driver_data; + if (buck < 1 || buck > MAX77686_BUCK_NUM) { + error("Wrong buck number: %d", buck); + return -EINVAL; + } + + adr = max77686_buck_addr[buck]; + + /* mask */ + switch (buck) { + case 2: + case 3: + case 4: + mode_shift = MAX77686_BUCK_MODE_SHIFT_2; + break; + default: + mode_shift = MAX77686_BUCK_MODE_SHIFT_1; + } + + mask = MAX77686_BUCK_MODE_MASK << mode_shift; + + ret = pmic_read(dev->parent, adr, &val, 1); + if (ret) + return ret; + + if (op == PMIC_OP_GET) { + val &= mask; + val >>= mode_shift; + ret = max77686_buck_hex2mode(buck, val); + if (ret < 0) + return ret; + *opmode = ret; + return 0; + } + + /* mode */ + switch (*opmode) { + case OPMODE_OFF: + mode = MAX77686_BUCK_MODE_OFF; + break; + case OPMODE_STANDBY: + switch (buck) { + case 1: + case 2: + case 3: + case 4: + mode = MAX77686_BUCK_MODE_STANDBY << mode_shift; + break; + default: + mode = 0xff; + } + break; + case OPMODE_LPM: + switch (buck) { + case 2: + case 3: + case 4: + mode = MAX77686_BUCK_MODE_LPM << mode_shift; + break; + default: + mode = 0xff; + } + break; + case OPMODE_ON: + mode = MAX77686_BUCK_MODE_ON << mode_shift; + break; + default: + mode = 0xff; + } + + if (mode == 0xff) { + error("Wrong mode: %d for buck: %d\n", *opmode, buck); + return -EINVAL; + } + + val &= ~mask; + val |= mode; + ret = pmic_write(dev->parent, adr, &val, 1); + + return ret; +} + +static int max77686_buck_enable(struct udevice *dev, int op, bool *enable) +{ + int ret, on_off; + + if (op == PMIC_OP_GET) { + ret = max77686_buck_mode(dev, op, &on_off); + if (ret) + return ret; + + switch (on_off) { + case OPMODE_OFF: + *enable = false; + break; + case OPMODE_ON: + *enable = true; + break; + default: + return -EINVAL; + } + } else if (op == PMIC_OP_SET) { + switch (*enable) { + case 0: + on_off = OPMODE_OFF; + break; + case 1: + on_off = OPMODE_ON; + break; + default: + return -EINVAL; + } + + ret = max77686_buck_mode(dev, op, &on_off); + if (ret) + return ret; + } + + return 0; +} + +static int max77686_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + + uc_pdata = dev_get_uclass_platdata(dev); + + uc_pdata->type = REGULATOR_TYPE_LDO; + uc_pdata->mode_count = max77686_ldo_modes(dev->driver_data, + &uc_pdata->mode, dev); + + return 0; +} + +static int ldo_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = max77686_ldo_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int ldo_set_value(struct udevice *dev, int uV) +{ + return max77686_ldo_val(dev, PMIC_OP_SET, &uV); +} + +static bool ldo_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = max77686_ldo_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int ldo_set_enable(struct udevice *dev, bool enable) +{ + return max77686_ldo_enable(dev, PMIC_OP_SET, &enable); +} + +static int ldo_get_mode(struct udevice *dev) +{ + int mode; + int ret; + + ret = max77686_ldo_mode(dev, PMIC_OP_GET, &mode); + if (ret) + return ret; + + return mode; +} + +static int ldo_set_mode(struct udevice *dev, int mode) +{ + return max77686_ldo_mode(dev, PMIC_OP_SET, &mode); +} + +static int max77686_buck_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + + uc_pdata = dev_get_uclass_platdata(dev); + + uc_pdata->type = REGULATOR_TYPE_BUCK; + uc_pdata->mode_count = max77686_buck_modes(dev->driver_data, + &uc_pdata->mode); + + return 0; +} + +static int buck_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = max77686_buck_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int buck_set_value(struct udevice *dev, int uV) +{ + return max77686_buck_val(dev, PMIC_OP_SET, &uV); +} + +static bool buck_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = max77686_buck_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int buck_set_enable(struct udevice *dev, bool enable) +{ + return max77686_buck_enable(dev, PMIC_OP_SET, &enable); +} + +static int buck_get_mode(struct udevice *dev) +{ + int mode; + int ret; + + ret = max77686_buck_mode(dev, PMIC_OP_GET, &mode); + if (ret) + return ret; + + return mode; +} + +static int buck_set_mode(struct udevice *dev, int mode) +{ + return max77686_buck_mode(dev, PMIC_OP_SET, &mode); +} + +static const struct dm_regulator_ops max77686_ldo_ops = { + .get_value = ldo_get_value, + .set_value = ldo_set_value, + .get_enable = ldo_get_enable, + .set_enable = ldo_set_enable, + .get_mode = ldo_get_mode, + .set_mode = ldo_set_mode, +}; + +U_BOOT_DRIVER(max77686_ldo) = { + .name = MAX77686_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &max77686_ldo_ops, + .probe = max77686_ldo_probe, +}; + +static const struct dm_regulator_ops max77686_buck_ops = { + .get_value = buck_get_value, + .set_value = buck_set_value, + .get_enable = buck_get_enable, + .set_enable = buck_set_enable, + .get_mode = buck_get_mode, + .set_mode = buck_set_mode, +}; + +U_BOOT_DRIVER(max77686_buck) = { + .name = MAX77686_BUCK_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &max77686_buck_ops, + .probe = max77686_buck_probe, +}; diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c new file mode 100644 index 00000000000..31ffd44454c --- /dev/null +++ b/drivers/power/regulator/regulator-uclass.c @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2014-2015 Samsung Electronics + * Przemyslaw Marczak <p.marczak@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <dm/uclass-internal.h> +#include <power/pmic.h> +#include <power/regulator.h> + +DECLARE_GLOBAL_DATA_PTR; + +int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + + *modep = NULL; + + uc_pdata = dev_get_uclass_platdata(dev); + if (!uc_pdata) + return -ENXIO; + + *modep = uc_pdata->mode; + return uc_pdata->mode_count; +} + +int regulator_get_value(struct udevice *dev) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + + if (!ops || !ops->get_value) + return -ENOSYS; + + return ops->get_value(dev); +} + +int regulator_set_value(struct udevice *dev, int uV) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + + if (!ops || !ops->set_value) + return -ENOSYS; + + return ops->set_value(dev, uV); +} + +int regulator_get_current(struct udevice *dev) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + + if (!ops || !ops->get_current) + return -ENOSYS; + + return ops->get_current(dev); +} + +int regulator_set_current(struct udevice *dev, int uA) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + + if (!ops || !ops->set_current) + return -ENOSYS; + + return ops->set_current(dev, uA); +} + +bool regulator_get_enable(struct udevice *dev) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + + if (!ops || !ops->get_enable) + return -ENOSYS; + + return ops->get_enable(dev); +} + +int regulator_set_enable(struct udevice *dev, bool enable) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + + if (!ops || !ops->set_enable) + return -ENOSYS; + + return ops->set_enable(dev, enable); +} + +int regulator_get_mode(struct udevice *dev) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + + if (!ops || !ops->get_mode) + return -ENOSYS; + + return ops->get_mode(dev); +} + +int regulator_set_mode(struct udevice *dev, int mode) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + + if (!ops || !ops->set_mode) + return -ENOSYS; + + return ops->set_mode(dev, mode); +} + +int regulator_get_by_platname(const char *plat_name, struct udevice **devp) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + struct udevice *dev; + int ret; + + *devp = NULL; + + for (ret = uclass_find_first_device(UCLASS_REGULATOR, &dev); dev; + ret = uclass_find_next_device(&dev)) { + if (ret) + continue; + + uc_pdata = dev_get_uclass_platdata(dev); + if (!uc_pdata || strcmp(plat_name, uc_pdata->name)) + continue; + + return uclass_get_device_tail(dev, 0, devp); + } + + debug("%s: can't find: %s\n", __func__, plat_name); + + return -ENODEV; +} + +int regulator_get_by_devname(const char *devname, struct udevice **devp) +{ + return uclass_get_device_by_name(UCLASS_REGULATOR, devname, devp); +} + +static int failed(int ret, bool verbose, const char *fmt, ...) +{ + va_list args; + char buf[64]; + + if (verbose == false) + return ret; + + va_start(args, fmt); + vscnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + printf(buf); + + if (!ret) + return 0; + + printf(" (ret: %d)", ret); + + return ret; +} + +int regulator_autoset(const char *platname, + struct udevice **devp, + bool verbose) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + struct udevice *dev; + int ret; + + if (devp) + *devp = NULL; + + ret = regulator_get_by_platname(platname, &dev); + if (ret) { + error("Can get the regulator: %s!", platname); + return ret; + } + + uc_pdata = dev_get_uclass_platdata(dev); + if (!uc_pdata) { + error("Can get the regulator %s uclass platdata!", platname); + return -ENXIO; + } + + if (!uc_pdata->always_on && !uc_pdata->boot_on) + goto retdev; + + if (verbose) + printf("%s@%s: ", dev->name, uc_pdata->name); + + /* Those values are optional (-ENODATA if unset) */ + if ((uc_pdata->min_uV != -ENODATA) && + (uc_pdata->max_uV != -ENODATA) && + (uc_pdata->min_uV == uc_pdata->max_uV)) { + ret = regulator_set_value(dev, uc_pdata->min_uV); + if (failed(ret, verbose, "set %d uV", uc_pdata->min_uV)) + goto exit; + } + + /* Those values are optional (-ENODATA if unset) */ + if ((uc_pdata->min_uA != -ENODATA) && + (uc_pdata->max_uA != -ENODATA) && + (uc_pdata->min_uA == uc_pdata->max_uA)) { + ret = regulator_set_current(dev, uc_pdata->min_uA); + if (failed(ret, verbose, "; set %d uA", uc_pdata->min_uA)) + goto exit; + } + + ret = regulator_set_enable(dev, true); + if (failed(ret, verbose, "; enabling", uc_pdata->min_uA)) + goto exit; + +retdev: + if (devp) + *devp = dev; +exit: + if (verbose) + printf("\n"); + + return ret; +} + +int regulator_list_autoset(const char *list_platname[], + struct udevice *list_devp[], + bool verbose) +{ + struct udevice *dev; + int error = 0, i = 0, ret; + + while (list_platname[i]) { + ret = regulator_autoset(list_platname[i], &dev, verbose); + if (ret & !error) + error = ret; + + if (list_devp) + list_devp[i] = dev; + + i++; + } + + return error; +} + +static bool regulator_name_is_unique(struct udevice *check_dev, + const char *check_name) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + struct udevice *dev; + int check_len = strlen(check_name); + int ret; + int len; + + for (ret = uclass_find_first_device(UCLASS_REGULATOR, &dev); dev; + ret = uclass_find_next_device(&dev)) { + if (ret || dev == check_dev) + continue; + + uc_pdata = dev_get_uclass_platdata(dev); + len = strlen(uc_pdata->name); + if (len != check_len) + continue; + + if (!strcmp(uc_pdata->name, check_name)) + return false; + } + + return true; +} + +static int regulator_post_bind(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + int offset = dev->of_offset; + const void *blob = gd->fdt_blob; + const char *property = "regulator-name"; + + uc_pdata = dev_get_uclass_platdata(dev); + if (!uc_pdata) + return -ENXIO; + + /* Regulator's mandatory constraint */ + uc_pdata->name = fdt_getprop(blob, offset, property, NULL); + if (!uc_pdata->name) { + debug("%s: dev: %s has no property 'regulator-name'\n", + __func__, dev->name); + return -EINVAL; + } + + if (regulator_name_is_unique(dev, uc_pdata->name)) + return 0; + + error("\"%s\" of dev: \"%s\", has nonunique value: \"%s\"", + property, dev->name, uc_pdata->name); + + return -EINVAL; +} + +static int regulator_pre_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + int offset = dev->of_offset; + + uc_pdata = dev_get_uclass_platdata(dev); + if (!uc_pdata) + return -ENXIO; + + /* Regulator's optional constraints */ + uc_pdata->min_uV = fdtdec_get_int(gd->fdt_blob, offset, + "regulator-min-microvolt", -ENODATA); + uc_pdata->max_uV = fdtdec_get_int(gd->fdt_blob, offset, + "regulator-max-microvolt", -ENODATA); + uc_pdata->min_uA = fdtdec_get_int(gd->fdt_blob, offset, + "regulator-min-microamp", -ENODATA); + uc_pdata->max_uA = fdtdec_get_int(gd->fdt_blob, offset, + "regulator-max-microamp", -ENODATA); + uc_pdata->always_on = fdtdec_get_bool(gd->fdt_blob, offset, + "regulator-always-on"); + uc_pdata->boot_on = fdtdec_get_bool(gd->fdt_blob, offset, + "regulator-boot-on"); + + return 0; +} + +UCLASS_DRIVER(regulator) = { + .id = UCLASS_REGULATOR, + .name = "regulator", + .post_bind = regulator_post_bind, + .pre_probe = regulator_pre_probe, + .per_device_platdata_auto_alloc_size = + sizeof(struct dm_regulator_uclass_platdata), +}; diff --git a/drivers/power/regulator/sandbox.c b/drivers/power/regulator/sandbox.c new file mode 100644 index 00000000000..2cca579a571 --- /dev/null +++ b/drivers/power/regulator/sandbox.c @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2015 Samsung Electronics + * Przemyslaw Marczak <p.marczak@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/sandbox_pmic.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define MODE(_id, _val, _name) [_id] = { \ + .id = _id, \ + .register_value = _val, \ + .name = _name, \ +} + +#define RANGE(_min, _max, _step) { \ + .min = _min, \ + .max = _max, \ + .step = _step, \ +} + +/* + * struct output_range - helper structure type to define the range of output + * operating values (current/voltage), limited by the PMIC IC design. + * + * @min - minimum value + * @max - maximum value + * @step - step value +*/ +struct output_range { + int min; + int max; + int step; +}; + +/* BUCK: 1,2 - voltage range */ +static struct output_range buck_voltage_range[] = { + RANGE(OUT_BUCK1_UV_MIN, OUT_BUCK1_UV_MAX, OUT_BUCK1_UV_STEP), + RANGE(OUT_BUCK2_UV_MIN, OUT_BUCK2_UV_MAX, OUT_BUCK2_UV_STEP), +}; + +/* BUCK: 1 - current range */ +static struct output_range buck_current_range[] = { + RANGE(OUT_BUCK1_UA_MIN, OUT_BUCK1_UA_MAX, OUT_BUCK1_UA_STEP), +}; + +/* BUCK operating modes */ +static struct dm_regulator_mode sandbox_buck_modes[] = { + MODE(BUCK_OM_OFF, OM2REG(BUCK_OM_OFF), "OFF"), + MODE(BUCK_OM_ON, OM2REG(BUCK_OM_ON), "ON"), + MODE(BUCK_OM_PWM, OM2REG(BUCK_OM_PWM), "PWM"), +}; + +/* LDO: 1,2 - voltage range */ +static struct output_range ldo_voltage_range[] = { + RANGE(OUT_LDO1_UV_MIN, OUT_LDO1_UV_MAX, OUT_LDO1_UV_STEP), + RANGE(OUT_LDO2_UV_MIN, OUT_LDO2_UV_MAX, OUT_LDO2_UV_STEP), +}; + +/* LDO: 1 - current range */ +static struct output_range ldo_current_range[] = { + RANGE(OUT_LDO1_UA_MIN, OUT_LDO1_UA_MAX, OUT_LDO1_UA_STEP), +}; + +/* LDO operating modes */ +static struct dm_regulator_mode sandbox_ldo_modes[] = { + MODE(LDO_OM_OFF, OM2REG(LDO_OM_OFF), "OFF"), + MODE(LDO_OM_ON, OM2REG(LDO_OM_ON), "ON"), + MODE(LDO_OM_SLEEP, OM2REG(LDO_OM_SLEEP), "SLEEP"), + MODE(LDO_OM_STANDBY, OM2REG(LDO_OM_STANDBY), "STANDBY"), +}; + +int out_get_value(struct udevice *dev, int output_count, int reg_type, + struct output_range *range) +{ + uint8_t reg_val; + uint reg; + int ret; + + if (dev->driver_data > output_count) { + error("Unknown regulator number: %lu for PMIC %s!", + dev->driver_data, dev->name); + return -EINVAL; + } + + reg = (dev->driver_data - 1) * OUT_REG_COUNT + reg_type; + ret = pmic_read(dev->parent, reg, ®_val, 1); + if (ret) { + error("PMIC read failed: %d\n", ret); + return ret; + } + + ret = REG2VAL(range[dev->driver_data - 1].min, + range[dev->driver_data - 1].step, + reg_val); + + return ret; +} + +static int out_set_value(struct udevice *dev, int output_count, int reg_type, + struct output_range *range, int value) +{ + uint8_t reg_val; + uint reg; + int ret; + int max_value; + + if (dev->driver_data > output_count) { + error("Unknown regulator number: %lu for PMIC %s!", + dev->driver_data, dev->name); + return -EINVAL; + } + + max_value = range[dev->driver_data - 1].max; + if (value > max_value) { + error("Wrong value for %s: %lu. Max is: %d.", + dev->name, dev->driver_data, max_value); + return -EINVAL; + } + + reg_val = VAL2REG(range[dev->driver_data - 1].min, + range[dev->driver_data - 1].step, + value); + + reg = (dev->driver_data - 1) * OUT_REG_COUNT + reg_type; + ret = pmic_write(dev->parent, reg, ®_val, 1); + if (ret) { + error("PMIC write failed: %d\n", ret); + return ret; + } + + return 0; +} + +static int out_get_mode(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + uint8_t reg_val; + uint reg; + int ret; + int i; + + uc_pdata = dev_get_uclass_platdata(dev); + + reg = (dev->driver_data - 1) * OUT_REG_COUNT + OUT_REG_OM; + ret = pmic_read(dev->parent, reg, ®_val, 1); + if (ret) { + error("PMIC read failed: %d\n", ret); + return ret; + } + + for (i = 0; i < uc_pdata->mode_count; i++) { + if (reg_val == uc_pdata->mode[i].register_value) + return uc_pdata->mode[i].id; + } + + error("Unknown operation mode for %s!", dev->name); + return -EINVAL; +} + +static int out_set_mode(struct udevice *dev, int mode) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + int reg_val = -1; + uint reg; + int ret; + int i; + + uc_pdata = dev_get_uclass_platdata(dev); + + if (mode >= uc_pdata->mode_count) + return -EINVAL; + + for (i = 0; i < uc_pdata->mode_count; i++) { + if (mode == uc_pdata->mode[i].id) { + reg_val = uc_pdata->mode[i].register_value; + break; + } + } + + if (reg_val == -1) { + error("Unknown operation mode for %s!", dev->name); + return -EINVAL; + } + + reg = (dev->driver_data - 1) * OUT_REG_COUNT + OUT_REG_OM; + ret = pmic_write(dev->parent, reg, (uint8_t *)®_val, 1); + if (ret) { + error("PMIC write failed: %d\n", ret); + return ret; + } + + return 0; +} + +static int buck_get_voltage(struct udevice *dev) +{ + return out_get_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UV, + buck_voltage_range); +} + +static int buck_set_voltage(struct udevice *dev, int uV) +{ + return out_set_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UV, + buck_voltage_range, uV); +} + +static int buck_get_current(struct udevice *dev) +{ + /* BUCK2 - unsupported */ + if (dev->driver_data == 2) + return -ENOSYS; + + return out_get_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UA, + buck_current_range); +} + +static int buck_set_current(struct udevice *dev, int uA) +{ + /* BUCK2 - unsupported */ + if (dev->driver_data == 2) + return -ENOSYS; + + return out_set_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UA, + buck_current_range, uA); +} + +static bool buck_get_enable(struct udevice *dev) +{ + if (out_get_mode(dev) == BUCK_OM_OFF) + return false; + + return true; +} + +static int buck_set_enable(struct udevice *dev, bool enable) +{ + return out_set_mode(dev, enable ? BUCK_OM_ON : BUCK_OM_OFF); +} + +static int sandbox_buck_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + + uc_pdata = dev_get_uclass_platdata(dev); + + uc_pdata->type = REGULATOR_TYPE_BUCK; + uc_pdata->mode = sandbox_buck_modes; + uc_pdata->mode_count = ARRAY_SIZE(sandbox_buck_modes); + + return 0; +} + +static const struct dm_regulator_ops sandbox_buck_ops = { + .get_value = buck_get_voltage, + .set_value = buck_set_voltage, + .get_current = buck_get_current, + .set_current = buck_set_current, + .get_enable = buck_get_enable, + .set_enable = buck_set_enable, + .get_mode = out_get_mode, + .set_mode = out_set_mode, +}; + +U_BOOT_DRIVER(sandbox_buck) = { + .name = SANDBOX_BUCK_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &sandbox_buck_ops, + .probe = sandbox_buck_probe, +}; + +static int ldo_get_voltage(struct udevice *dev) +{ + return out_get_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UV, + ldo_voltage_range); +} + +static int ldo_set_voltage(struct udevice *dev, int uV) +{ + return out_set_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UV, + ldo_voltage_range, uV); +} + +static int ldo_get_current(struct udevice *dev) +{ + /* LDO2 - unsupported */ + if (dev->driver_data == 2) + return -ENOSYS; + + return out_get_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UA, + ldo_current_range); +} + +static int ldo_set_current(struct udevice *dev, int uA) +{ + /* LDO2 - unsupported */ + if (dev->driver_data == 2) + return -ENOSYS; + + return out_set_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UA, + ldo_current_range, uA); +} + +static bool ldo_get_enable(struct udevice *dev) +{ + if (out_get_mode(dev) == LDO_OM_OFF) + return false; + + return true; +} + +static int ldo_set_enable(struct udevice *dev, bool enable) +{ + return out_set_mode(dev, enable ? LDO_OM_ON : LDO_OM_OFF); +} + +static int sandbox_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + + uc_pdata = dev_get_uclass_platdata(dev); + + uc_pdata->type = REGULATOR_TYPE_LDO; + uc_pdata->mode = sandbox_ldo_modes; + uc_pdata->mode_count = ARRAY_SIZE(sandbox_ldo_modes); + + return 0; +} + +static const struct dm_regulator_ops sandbox_ldo_ops = { + .get_value = ldo_get_voltage, + .set_value = ldo_set_voltage, + .get_current = ldo_get_current, + .set_current = ldo_set_current, + .get_enable = ldo_get_enable, + .set_enable = ldo_set_enable, + .get_mode = out_get_mode, + .set_mode = out_set_mode, +}; + +U_BOOT_DRIVER(sandbox_ldo) = { + .name = SANDBOX_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &sandbox_ldo_ops, + .probe = sandbox_ldo_probe, +}; |