diff options
author | Tom Rini <trini@konsulko.com> | 2015-08-06 19:56:03 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2015-08-06 19:56:03 -0400 |
commit | ae27120c31d58b8bb694d9155bcffdcfae8552a6 (patch) | |
tree | 8fcd4823406dc3adfb82174314198e9396c24feb /drivers/power | |
parent | f05fa6781ae1122f348e66b5b26acbfe552f6602 (diff) | |
parent | fac971b2b5efbdb6ed2d12ebdbf7e029c5ed30e8 (diff) |
Merge git://git.denx.de/u-boot-dm
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/pmic/Kconfig | 18 | ||||
-rw-r--r-- | drivers/power/pmic/Makefile | 5 | ||||
-rw-r--r-- | drivers/power/pmic/max77686.c | 6 | ||||
-rw-r--r-- | drivers/power/pmic/pmic-uclass.c | 2 | ||||
-rw-r--r-- | drivers/power/pmic/pmic_tps65090.c | 310 | ||||
-rw-r--r-- | drivers/power/pmic/pmic_tps65090_ec.c | 218 | ||||
-rw-r--r-- | drivers/power/pmic/s5m8767.c | 95 | ||||
-rw-r--r-- | drivers/power/pmic/tps65090.c | 94 | ||||
-rw-r--r-- | drivers/power/regulator/Kconfig | 19 | ||||
-rw-r--r-- | drivers/power/regulator/Makefile | 2 | ||||
-rw-r--r-- | drivers/power/regulator/max77686.c | 28 | ||||
-rw-r--r-- | drivers/power/regulator/regulator-uclass.c | 4 | ||||
-rw-r--r-- | drivers/power/regulator/s5m8767.c | 269 | ||||
-rw-r--r-- | drivers/power/regulator/tps65090_regulator.c | 138 |
14 files changed, 662 insertions, 546 deletions
diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index 164f42143f..7b98189ad8 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -41,3 +41,21 @@ config DM_PMIC_SANDBOX - set by i2c emul driver's probe() (defaults in header) Driver binding info: doc/device-tree-bindings/pmic/sandbox.txt + +config PMIC_S5M8767 + bool "Enable Driver Model for the Samsung S5M8767 PMIC" + depends on DM_PMIC + ---help--- + The S5M8767 PMIC provides a large array of LDOs and BUCKs for use + as a SoC power controller. It also provides 32KHz clock outputs. This + driver provides basic register access and sets up the attached + regulators if regulator support is enabled. + +config PMIC_TPS65090 + bool "Enable driver for Texas Instruments TPS65090 PMIC" + depends on DM_PMIC + ---help--- + The TPS65090 is a PMIC containing several LDOs, DC to DC convertors, + FETs and a battery charger. This driver provides register access + only, and you can enable the regulator/charger drivers separately if + required. diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 4ad6df36c2..6ef149aee7 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -8,6 +8,9 @@ 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_PMIC_TPS65090) += tps65090.o +obj-$(CONFIG_PMIC_S5M8767) += s5m8767.o + obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o obj-$(CONFIG_POWER_MAX77696) += pmic_max77696.o obj-$(CONFIG_POWER_MAX8998) += pmic_max8998.o @@ -15,8 +18,6 @@ obj-$(CONFIG_POWER_MAX8997) += pmic_max8997.o obj-$(CONFIG_POWER_MUIC_MAX8997) += muic_max8997.o obj-$(CONFIG_POWER_MAX77686) += pmic_max77686.o obj-$(CONFIG_POWER_PFUZE100) += pmic_pfuze100.o -obj-$(CONFIG_POWER_TPS65090_I2C) += pmic_tps65090.o -obj-$(CONFIG_POWER_TPS65090_EC) += pmic_tps65090_ec.o obj-$(CONFIG_POWER_TPS65217) += pmic_tps65217.o obj-$(CONFIG_POWER_TPS65218) += pmic_tps62362.o obj-$(CONFIG_POWER_TPS65218) += pmic_tps65218.o diff --git a/drivers/power/pmic/max77686.c b/drivers/power/pmic/max77686.c index 3523b4a2de..dc5a54a6a3 100644 --- a/drivers/power/pmic/max77686.c +++ b/drivers/power/pmic/max77686.c @@ -17,8 +17,8 @@ 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 }, + { .prefix = "LDO", .driver = MAX77686_LDO_DRIVER }, + { .prefix = "BUCK", .driver = MAX77686_BUCK_DRIVER }, { }, }; @@ -84,7 +84,7 @@ static const struct udevice_id max77686_ids[] = { }; U_BOOT_DRIVER(pmic_max77686) = { - .name = "max77686 pmic", + .name = "max77686_pmic", .id = UCLASS_PMIC, .of_match = max77686_ids, .bind = max77686_bind, diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c index d99cb9aada..49709f3084 100644 --- a/drivers/power/pmic/pmic-uclass.c +++ b/drivers/power/pmic/pmic-uclass.c @@ -142,7 +142,7 @@ int pmic_reg_write(struct udevice *dev, uint reg, uint value) u8 byte = value; debug("%s: reg=%x, value=%x\n", __func__, reg, value); - return pmic_read(dev, reg, &byte, 1); + return pmic_write(dev, reg, &byte, 1); } int pmic_clrsetbits(struct udevice *dev, uint reg, uint clr, uint set) diff --git a/drivers/power/pmic/pmic_tps65090.c b/drivers/power/pmic/pmic_tps65090.c deleted file mode 100644 index 337903acec..0000000000 --- a/drivers/power/pmic/pmic_tps65090.c +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright (c) 2012 The Chromium OS Authors. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <errno.h> -#include <fdtdec.h> -#include <i2c.h> -#include <power/pmic.h> -#include <power/tps65090_pmic.h> - -DECLARE_GLOBAL_DATA_PTR; - -#define TPS65090_NAME "TPS65090_PMIC" - -/* TPS65090 register addresses */ -enum { - REG_IRQ1 = 0, - REG_CG_CTRL0 = 4, - REG_CG_STATUS1 = 0xa, - REG_FET1_CTRL = 0x0f, - REG_FET2_CTRL, - REG_FET3_CTRL, - REG_FET4_CTRL, - REG_FET5_CTRL, - REG_FET6_CTRL, - REG_FET7_CTRL, - TPS65090_NUM_REGS, -}; - -enum { - IRQ1_VBATG = 1 << 3, - CG_CTRL0_ENC_MASK = 0x01, - - MAX_FET_NUM = 7, - MAX_CTRL_READ_TRIES = 5, - - /* TPS65090 FET_CTRL register values */ - FET_CTRL_TOFET = 1 << 7, /* Timeout, startup, overload */ - FET_CTRL_PGFET = 1 << 4, /* Power good for FET status */ - FET_CTRL_WAIT = 3 << 2, /* Overcurrent timeout max */ - FET_CTRL_ADENFET = 1 << 1, /* Enable output auto discharge */ - FET_CTRL_ENFET = 1 << 0, /* Enable FET */ -}; - -/** - * Checks for a valid FET number - * - * @param fet_id FET number to check - * @return 0 if ok, -EINVAL if FET value is out of range - */ -static int tps65090_check_fet(unsigned int fet_id) -{ - if (fet_id == 0 || fet_id > MAX_FET_NUM) { - debug("parameter fet_id is out of range, %u not in 1 ~ %u\n", - fet_id, MAX_FET_NUM); - return -EINVAL; - } - - return 0; -} - -/** - * Set the power state for a FET - * - * @param pmic pmic structure for the tps65090 - * @param fet_id Fet number to set (1..MAX_FET_NUM) - * @param set 1 to power on FET, 0 to power off - * @return -EIO if we got a comms error, -EAGAIN if the FET failed to - * change state. If all is ok, returns 0. - */ -static int tps65090_fet_set(struct pmic *pmic, int fet_id, bool set) -{ - int retry; - u32 reg, value; - - value = FET_CTRL_ADENFET | FET_CTRL_WAIT; - if (set) - value |= FET_CTRL_ENFET; - - if (pmic_reg_write(pmic, REG_FET1_CTRL + fet_id - 1, value)) - return -EIO; - - /* Try reading until we get a result */ - for (retry = 0; retry < MAX_CTRL_READ_TRIES; retry++) { - if (pmic_reg_read(pmic, REG_FET1_CTRL + fet_id - 1, ®)) - return -EIO; - - /* Check that the fet went into the expected state */ - if (!!(reg & FET_CTRL_PGFET) == set) - return 0; - - /* If we got a timeout, there is no point in waiting longer */ - if (reg & FET_CTRL_TOFET) - break; - - mdelay(1); - } - - debug("FET %d: Power good should have set to %d but reg=%#02x\n", - fet_id, set, reg); - return -EAGAIN; -} - -int tps65090_fet_enable(unsigned int fet_id) -{ - struct pmic *pmic; - ulong start; - int loops; - int ret; - - ret = tps65090_check_fet(fet_id); - if (ret) - return ret; - - pmic = pmic_get(TPS65090_NAME); - if (!pmic) - return -EACCES; - - start = get_timer(0); - for (loops = 0;; loops++) { - ret = tps65090_fet_set(pmic, fet_id, true); - if (!ret) - break; - - if (get_timer(start) > 100) - break; - - /* Turn it off and try again until we time out */ - tps65090_fet_set(pmic, fet_id, false); - } - - if (ret) - debug("%s: FET%d failed to power on: time=%lums, loops=%d\n", - __func__, fet_id, get_timer(start), loops); - else if (loops) - debug("%s: FET%d powered on after %lums, loops=%d\n", - __func__, fet_id, get_timer(start), loops); - - /* - * Unfortunately, there are some conditions where the power - * good bit will be 0, but the fet still comes up. One such - * case occurs with the lcd backlight. We'll just return 0 here - * and assume that the fet will eventually come up. - */ - if (ret == -EAGAIN) - ret = 0; - - return ret; -} - -int tps65090_fet_disable(unsigned int fet_id) -{ - struct pmic *pmic; - int ret; - - ret = tps65090_check_fet(fet_id); - if (ret) - return ret; - - pmic = pmic_get(TPS65090_NAME); - if (!pmic) - return -EACCES; - ret = tps65090_fet_set(pmic, fet_id, false); - - return ret; -} - -int tps65090_fet_is_enabled(unsigned int fet_id) -{ - struct pmic *pmic; - u32 reg; - int ret; - - ret = tps65090_check_fet(fet_id); - if (ret) - return ret; - - pmic = pmic_get(TPS65090_NAME); - if (!pmic) - return -ENODEV; - ret = pmic_reg_read(pmic, REG_FET1_CTRL + fet_id - 1, ®); - if (ret) { - debug("fail to read FET%u_CTRL register over I2C", fet_id); - return -EIO; - } - - return reg & FET_CTRL_ENFET; -} - -int tps65090_get_charging(void) -{ - struct pmic *pmic; - u32 val; - int ret; - - pmic = pmic_get(TPS65090_NAME); - if (!pmic) - return -EACCES; - - ret = pmic_reg_read(pmic, REG_CG_CTRL0, &val); - if (ret) - return ret; - - return !!(val & CG_CTRL0_ENC_MASK); -} - -static int tps65090_charger_state(struct pmic *pmic, int state, - int current) -{ - u32 val; - int ret; - - ret = pmic_reg_read(pmic, REG_CG_CTRL0, &val); - if (!ret) { - if (state == PMIC_CHARGER_ENABLE) - val |= CG_CTRL0_ENC_MASK; - else - val &= ~CG_CTRL0_ENC_MASK; - ret = pmic_reg_write(pmic, REG_CG_CTRL0, val); - } - if (ret) { - debug("%s: Failed to read/write register\n", __func__); - return ret; - } - - return 0; -} - -int tps65090_get_status(void) -{ - struct pmic *pmic; - u32 val; - int ret; - - pmic = pmic_get(TPS65090_NAME); - if (!pmic) - return -EACCES; - - ret = pmic_reg_read(pmic, REG_CG_STATUS1, &val); - if (ret) - return ret; - - return val; -} - -static int tps65090_charger_bat_present(struct pmic *pmic) -{ - u32 val; - int ret; - - ret = pmic_reg_read(pmic, REG_IRQ1, &val); - if (ret) - return ret; - - return !!(val & IRQ1_VBATG); -} - -static struct power_chrg power_chrg_pmic_ops = { - .chrg_bat_present = tps65090_charger_bat_present, - .chrg_state = tps65090_charger_state, -}; - -int tps65090_init(void) -{ - struct pmic *p; - int bus; - int addr; - const void *blob = gd->fdt_blob; - int node, parent; - - node = fdtdec_next_compatible(blob, 0, COMPAT_TI_TPS65090); - if (node < 0) { - debug("PMIC: No node for PMIC Chip in device tree\n"); - debug("node = %d\n", node); - return -ENODEV; - } - - parent = fdt_parent_offset(blob, node); - if (parent < 0) { - debug("%s: Cannot find node parent\n", __func__); - return -EINVAL; - } - - bus = i2c_get_bus_num_fdt(parent); - if (bus < 0) { - debug("%s: Cannot find I2C bus\n", __func__); - return -ENOENT; - } - addr = fdtdec_get_int(blob, node, "reg", TPS65090_I2C_ADDR); - p = pmic_alloc(); - if (!p) { - printf("%s: POWER allocation error!\n", __func__); - return -ENOMEM; - } - - p->name = TPS65090_NAME; - p->bus = bus; - p->interface = PMIC_I2C; - p->number_of_regs = TPS65090_NUM_REGS; - p->hw.i2c.addr = addr; - p->hw.i2c.tx_num = 1; - p->chrg = &power_chrg_pmic_ops; - - puts("TPS65090 PMIC init\n"); - - return 0; -} diff --git a/drivers/power/pmic/pmic_tps65090_ec.c b/drivers/power/pmic/pmic_tps65090_ec.c deleted file mode 100644 index ac0d44fec8..0000000000 --- a/drivers/power/pmic/pmic_tps65090_ec.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (c) 2013 The Chromium OS Authors. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <cros_ec.h> -#include <errno.h> -#include <power/tps65090_pmic.h> - -DECLARE_GLOBAL_DATA_PTR; - -#define TPS65090_ADDR 0x48 - -static struct tps65090 { - struct cros_ec_dev *dev; /* The CROS_EC device */ -} config; - -/* TPS65090 register addresses */ -enum { - REG_IRQ1 = 0, - REG_CG_CTRL0 = 4, - REG_CG_STATUS1 = 0xa, - REG_FET1_CTRL = 0x0f, - REG_FET2_CTRL, - REG_FET3_CTRL, - REG_FET4_CTRL, - REG_FET5_CTRL, - REG_FET6_CTRL, - REG_FET7_CTRL, - TPS65090_NUM_REGS, -}; - -enum { - IRQ1_VBATG = 1 << 3, - CG_CTRL0_ENC_MASK = 0x01, - - MAX_FET_NUM = 7, - MAX_CTRL_READ_TRIES = 5, - - /* TPS65090 FET_CTRL register values */ - FET_CTRL_TOFET = 1 << 7, /* Timeout, startup, overload */ - FET_CTRL_PGFET = 1 << 4, /* Power good for FET status */ - FET_CTRL_WAIT = 3 << 2, /* Overcurrent timeout max */ - FET_CTRL_ADENFET = 1 << 1, /* Enable output auto discharge */ - FET_CTRL_ENFET = 1 << 0, /* Enable FET */ -}; - -/** - * tps65090_read - read a byte from tps6090 - * - * @param reg The register address to read from. - * @param val We'll return value value read here. - * @return 0 if ok; error if EC returns failure. - */ -static int tps65090_read(u32 reg, u8 *val) -{ - return cros_ec_i2c_xfer(config.dev, TPS65090_ADDR, reg, 1, - val, 1, true); -} - -/** - * tps65090_write - write a byte to tps6090 - * - * @param reg The register address to write to. - * @param val The value to write. - * @return 0 if ok; error if EC returns failure. - */ -static int tps65090_write(u32 reg, u8 val) -{ - return cros_ec_i2c_xfer(config.dev, TPS65090_ADDR, reg, 1, - &val, 1, false); -} - -/** - * Checks for a valid FET number - * - * @param fet_id FET number to check - * @return 0 if ok, -EINVAL if FET value is out of range - */ -static int tps65090_check_fet(unsigned int fet_id) -{ - if (fet_id == 0 || fet_id > MAX_FET_NUM) { - debug("parameter fet_id is out of range, %u not in 1 ~ %u\n", - fet_id, MAX_FET_NUM); - return -EINVAL; - } - - return 0; -} - -/** - * Set the power state for a FET - * - * @param fet_id Fet number to set (1..MAX_FET_NUM) - * @param set 1 to power on FET, 0 to power off - * @return -EIO if we got a comms error, -EAGAIN if the FET failed to - * change state. If all is ok, returns 0. - */ -static int tps65090_fet_set(int fet_id, bool set) -{ - int retry; - u8 reg, value; - - value = FET_CTRL_ADENFET | FET_CTRL_WAIT; - if (set) - value |= FET_CTRL_ENFET; - - if (tps65090_write(REG_FET1_CTRL + fet_id - 1, value)) - return -EIO; - - /* Try reading until we get a result */ - for (retry = 0; retry < MAX_CTRL_READ_TRIES; retry++) { - if (tps65090_read(REG_FET1_CTRL + fet_id - 1, ®)) - return -EIO; - - /* Check that the fet went into the expected state */ - if (!!(reg & FET_CTRL_PGFET) == set) - return 0; - - /* If we got a timeout, there is no point in waiting longer */ - if (reg & FET_CTRL_TOFET) - break; - - mdelay(1); - } - - debug("FET %d: Power good should have set to %d but reg=%#02x\n", - fet_id, set, reg); - return -EAGAIN; -} - -int tps65090_fet_enable(unsigned int fet_id) -{ - ulong start; - int loops; - int ret; - - ret = tps65090_check_fet(fet_id); - if (ret) - return ret; - - start = get_timer(0); - for (loops = 0;; loops++) { - ret = tps65090_fet_set(fet_id, true); - if (!ret) - break; - - if (get_timer(start) > 100) - break; - - /* Turn it off and try again until we time out */ - tps65090_fet_set(fet_id, false); - } - - if (ret) { - debug("%s: FET%d failed to power on: time=%lums, loops=%d\n", - __func__, fet_id, get_timer(start), loops); - } else if (loops) { - debug("%s: FET%d powered on after %lums, loops=%d\n", - __func__, fet_id, get_timer(start), loops); - } - /* - * Unfortunately, there are some conditions where the power - * good bit will be 0, but the fet still comes up. One such - * case occurs with the lcd backlight. We'll just return 0 here - * and assume that the fet will eventually come up. - */ - if (ret == -EAGAIN) - ret = 0; - - return ret; -} - -int tps65090_fet_disable(unsigned int fet_id) -{ - int ret; - - ret = tps65090_check_fet(fet_id); - if (ret) - return ret; - - ret = tps65090_fet_set(fet_id, false); - - return ret; -} - -int tps65090_fet_is_enabled(unsigned int fet_id) -{ - u8 reg = 0; - int ret; - - ret = tps65090_check_fet(fet_id); - if (ret) - return ret; - ret = tps65090_read(REG_FET1_CTRL + fet_id - 1, ®); - if (ret) { - debug("fail to read FET%u_CTRL register over I2C", fet_id); - return -EIO; - } - - return reg & FET_CTRL_ENFET; -} - -int tps65090_init(void) -{ - puts("TPS65090 PMIC EC init\n"); - - config.dev = board_get_cros_ec_dev(); - if (!config.dev) { - debug("%s: no cros_ec device: cannot init tps65090\n", - __func__); - return -ENODEV; - } - - return 0; -} diff --git a/drivers/power/pmic/s5m8767.c b/drivers/power/pmic/s5m8767.c new file mode 100644 index 0000000000..075fe7e2f1 --- /dev/null +++ b/drivers/power/pmic/s5m8767.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2015 Google, Inc + * + * 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/s5m8767.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const struct pmic_child_info pmic_children_info[] = { + { .prefix = "LDO", .driver = S5M8767_LDO_DRIVER }, + { .prefix = "BUCK", .driver = S5M8767_BUCK_DRIVER }, + { }, +}; + +static int s5m8767_reg_count(struct udevice *dev) +{ + return S5M8767_NUM_OF_REGS; +} + +static int s5m8767_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 s5m8767_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; +} + +int s5m8767_enable_32khz_cp(struct udevice *dev) +{ + return pmic_clrsetbits(dev, S5M8767_EN32KHZ_CP, 0, 1 << 1); +} + +static int s5m8767_bind(struct udevice *dev) +{ + int node; + const void *blob = gd->fdt_blob; + int children; + + node = fdt_subnode_offset(blob, dev->of_offset, "regulators"); + if (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, 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 s5m8767_ops = { + .reg_count = s5m8767_reg_count, + .read = s5m8767_read, + .write = s5m8767_write, +}; + +static const struct udevice_id s5m8767_ids[] = { + { .compatible = "samsung,s5m8767-pmic" }, + { } +}; + +U_BOOT_DRIVER(pmic_s5m8767) = { + .name = "s5m8767_pmic", + .id = UCLASS_PMIC, + .of_match = s5m8767_ids, + .bind = s5m8767_bind, + .ops = &s5m8767_ops, +}; diff --git a/drivers/power/pmic/tps65090.c b/drivers/power/pmic/tps65090.c new file mode 100644 index 0000000000..4797f327fa --- /dev/null +++ b/drivers/power/pmic/tps65090.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <i2c.h> +#include <power/pmic.h> +#include <power/tps65090.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const struct pmic_child_info pmic_children_info[] = { + { .prefix = "fet", .driver = TPS65090_FET_DRIVER }, + { }, +}; + +static int tps65090_reg_count(struct udevice *dev) +{ + return TPS65090_NUM_REGS; +} + +static int tps65090_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 tps65090_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{ + int ret; + + ret = dm_i2c_read(dev, reg, buff, len); + if (ret) { + error("read error %d from device: %p register: %#x!", ret, dev, + reg); + return -EIO; + } + + return 0; +} + +static int tps65090_bind(struct udevice *dev) +{ + int regulators_node; + const void *blob = gd->fdt_blob; + int children; + + regulators_node = fdt_subnode_offset(blob, dev->of_offset, + "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 tps65090_ops = { + .reg_count = tps65090_reg_count, + .read = tps65090_read, + .write = tps65090_write, +}; + +static const struct udevice_id tps65090_ids[] = { + { .compatible = "ti,tps65090" }, + { } +}; + +U_BOOT_DRIVER(pmic_tps65090) = { + .name = "tps65090 pmic", + .id = UCLASS_PMIC, + .of_match = tps65090_ids, + .bind = tps65090_bind, + .ops = &tps65090_ops, +}; diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index 6289b83910..e85c69231e 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -32,6 +32,15 @@ config DM_REGULATOR_FIXED features for fixed value regulators. The driver implements get/set api for enable and get only for voltage value. +config REGULATOR_S5M8767 + bool "Enable support for S5M8767 regulator" + depends on DM_REGULATOR && PMIC_S5M8767 + ---help--- + This enables the regulator features of the S5M8767, allowing voltages + to be set, etc. The driver is not fully complete but supports most + common requirements, including all LDOs and BUCKs. This allows many + supplies to be set automatically using the device tree values. + config DM_REGULATOR_SANDBOX bool "Enable Driver Model for Sandbox PMIC regulator" depends on DM_REGULATOR && DM_PMIC_SANDBOX @@ -61,3 +70,13 @@ config DM_REGULATOR_SANDBOX A detailed information can be found in header: '<power/sandbox_pmic.h>' Binding info: 'doc/device-tree-bindings/pmic/max77686.txt' + +config REGULATOR_TPS65090 + bool "Enable driver for TPS65090 PMIC regulators" + depends on PMIC_TPS65090 + ---help--- + The TPS65090 provides several FETs (Field-effect Transistors, + effectively switches) which are supported by this driver as + regulators, one for each FET. The standard regulator interface is + supported, but it is only possible to turn the regulators on or off. + There is no voltage/current control. diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index 96aa624961..08d7b0d26d 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -8,4 +8,6 @@ obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_DM_REGULATOR_FIXED) += fixed.o +obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o obj-$(CONFIG_DM_REGULATOR_SANDBOX) += sandbox.o +obj-$(CONFIG_REGULATOR_TPS65090) += tps65090_regulator.o diff --git a/drivers/power/regulator/max77686.c b/drivers/power/regulator/max77686.c index 37ebe94521..946b87c60a 100644 --- a/drivers/power/regulator/max77686.c +++ b/drivers/power/regulator/max77686.c @@ -61,10 +61,14 @@ static struct dm_regulator_mode max77686_buck_mode_onoff[] = { MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"), }; -static const char max77686_buck_addr[] = { +static const char max77686_buck_ctrl[] = { 0xff, 0x10, 0x12, 0x1c, 0x26, 0x30, 0x32, 0x34, 0x36, 0x38 }; +static const char max77686_buck_out[] = { + 0xff, 0x11, 0x14, 0x1e, 0x28, 0x31, 0x33, 0x35, 0x37, 0x39 +}; + static int max77686_buck_volt2hex(int buck, int uV) { unsigned int hex = 0; @@ -77,13 +81,15 @@ static int max77686_buck_volt2hex(int buck, int uV) /* 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; + break; default: - /* hex = (uV - 750000) / 50000; */ + /* + * hex = (uV - 750000) / 50000. We assume that dynamic voltage + * scaling via GPIOs is not enabled and don't support that. + * If this is enabled then the driver will need to take that + * into account anrd check different registers depending on + * the current setting See the datasheet for details. + */ hex = (uV - MAX77686_BUCK_UV_HMIN) / MAX77686_BUCK_UV_HSTEP; hex_max = MAX77686_BUCK_VOLT_MAX_HEX; break; @@ -368,18 +374,18 @@ static int max77686_buck_val(struct udevice *dev, int op, int *uV) *uV = 0; /* &buck_out = ctrl + 1 */ - adr = max77686_buck_addr[buck] + 1; + adr = max77686_buck_out[buck]; /* 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; + break; default: mask = MAX77686_BUCK_VOLT_MASK; + break; } ret = pmic_read(dev->parent, adr, &val, 1); @@ -549,7 +555,7 @@ static int max77686_buck_mode(struct udevice *dev, int op, int *opmode) return -EINVAL; } - adr = max77686_buck_addr[buck]; + adr = max77686_buck_ctrl[buck]; /* mask */ switch (buck) { diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c index 12e141b4ad..f3fe7a55e1 100644 --- a/drivers/power/regulator/regulator-uclass.c +++ b/drivers/power/regulator/regulator-uclass.c @@ -319,8 +319,10 @@ int regulators_enable_boot_on(bool verbose) dev && !ret; uclass_next_device(&dev)) { ret = regulator_autoset(dev); - if (ret == -EMEDIUMTYPE) + if (ret == -EMEDIUMTYPE) { + ret = 0; continue; + } if (verbose) regulator_show(dev, ret); } diff --git a/drivers/power/regulator/s5m8767.c b/drivers/power/regulator/s5m8767.c new file mode 100644 index 0000000000..93a3c942d1 --- /dev/null +++ b/drivers/power/regulator/s5m8767.c @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2015 Google, Inc + * + * 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/s5m8767.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const struct sec_voltage_desc buck_v1 = { + .max = 2225000, + .min = 650000, + .step = 6250, +}; + +static const struct sec_voltage_desc buck_v2 = { + .max = 1600000, + .min = 600000, + .step = 6250, +}; + +static const struct sec_voltage_desc buck_v3 = { + .max = 3000000, + .min = 750000, + .step = 12500, +}; + +static const struct sec_voltage_desc ldo_v1 = { + .max = 3950000, + .min = 800000, + .step = 50000, +}; + +static const struct sec_voltage_desc ldo_v2 = { + .max = 2375000, + .min = 800000, + .step = 25000, +}; + +static const struct s5m8767_para buck_param[] = { + /* + * | voltage ----| | enable -| voltage + * regnum addr bpos mask addr on desc + */ + {S5M8767_BUCK1, 0x33, 0x0, 0xff, 0x32, 0x3, &buck_v1}, + {S5M8767_BUCK2, 0x35, 0x0, 0xff, 0x34, 0x1, &buck_v2}, + {S5M8767_BUCK3, 0x3e, 0x0, 0xff, 0x3d, 0x1, &buck_v2}, + {S5M8767_BUCK4, 0x47, 0x0, 0xff, 0x46, 0x1, &buck_v2}, + {S5M8767_BUCK5, 0x50, 0x0, 0xff, 0x4f, 0x3, &buck_v1}, + {S5M8767_BUCK6, 0x55, 0x0, 0xff, 0x54, 0x3, &buck_v1}, + {S5M8767_BUCK7, 0x57, 0x0, 0xff, 0x56, 0x3, &buck_v3}, + {S5M8767_BUCK8, 0x59, 0x0, 0xff, 0x58, 0x3, &buck_v3}, + {S5M8767_BUCK9, 0x5b, 0x0, 0xff, 0x5a, 0x3, &buck_v3}, +}; + +static const struct s5m8767_para ldo_param[] = { + {S5M8767_LDO1, 0x5c, 0x0, 0x3f, 0x5c, 0x3, &ldo_v2}, + {S5M8767_LDO2, 0x5d, 0x0, 0x3f, 0x5d, 0x1, &ldo_v2}, + {S5M8767_LDO3, 0x61, 0x0, 0x3f, 0x61, 0x3, &ldo_v1}, + {S5M8767_LDO4, 0x62, 0x0, 0x3f, 0x62, 0x3, &ldo_v1}, + {S5M8767_LDO5, 0x63, 0x0, 0x3f, 0x63, 0x3, &ldo_v1}, + {S5M8767_LDO6, 0x64, 0x0, 0x3f, 0x64, 0x1, &ldo_v2}, + {S5M8767_LDO7, 0x65, 0x0, 0x3f, 0x65, 0x1, &ldo_v2}, + {S5M8767_LDO8, 0x66, 0x0, 0x3f, 0x66, 0x1, &ldo_v2}, + {S5M8767_LDO9, 0x67, 0x0, 0x3f, 0x67, 0x3, &ldo_v1}, + {S5M8767_LDO10, 0x68, 0x0, 0x3f, 0x68, 0x1, &ldo_v1}, + {S5M8767_LDO11, 0x69, 0x0, 0x3f, 0x69, 0x1, &ldo_v1}, + {S5M8767_LDO12, 0x6a, 0x0, 0x3f, 0x6a, 0x1, &ldo_v1}, + {S5M8767_LDO13, 0x6b, 0x0, 0x3f, 0x6b, 0x3, &ldo_v1}, + {S5M8767_LDO14, 0x6c, 0x0, 0x3f, 0x6c, 0x1, &ldo_v1}, + {S5M8767_LDO15, 0x6d, 0x0, 0x3f, 0x6d, 0x1, &ldo_v2}, + {S5M8767_LDO16, 0x6e, 0x0, 0x3f, 0x6e, 0x1, &ldo_v1}, + {S5M8767_LDO17, 0x6f, 0x0, 0x3f, 0x6f, 0x3, &ldo_v1}, + {S5M8767_LDO18, 0x70, 0x0, 0x3f, 0x70, 0x3, &ldo_v1}, + {S5M8767_LDO19, 0x71, 0x0, 0x3f, 0x71, 0x3, &ldo_v1}, + {S5M8767_LDO20, 0x72, 0x0, 0x3f, 0x72, 0x3, &ldo_v1}, + {S5M8767_LDO21, 0x73, 0x0, 0x3f, 0x73, 0x3, &ldo_v1}, + {S5M8767_LDO22, 0x74, 0x0, 0x3f, 0x74, 0x3, &ldo_v1}, + {S5M8767_LDO23, 0x75, 0x0, 0x3f, 0x75, 0x3, &ldo_v1}, + {S5M8767_LDO24, 0x76, 0x0, 0x3f, 0x76, 0x3, &ldo_v1}, + {S5M8767_LDO25, 0x77, 0x0, 0x3f, 0x77, 0x3, &ldo_v1}, + {S5M8767_LDO26, 0x78, 0x0, 0x3f, 0x78, 0x3, &ldo_v1}, + {S5M8767_LDO27, 0x79, 0x0, 0x3f, 0x79, 0x3, &ldo_v1}, + {S5M8767_LDO28, 0x7a, 0x0, 0x3f, 0x7a, 0x3, &ldo_v1}, +}; + +enum { + ENABLE_SHIFT = 6, + ENABLE_MASK = 3, +}; + +static int reg_get_value(struct udevice *dev, const struct s5m8767_para *param) +{ + const struct sec_voltage_desc *desc; + int ret, uv, val; + + ret = pmic_reg_read(dev->parent, param->vol_addr); + if (ret < 0) + return ret; + + desc = param->vol; + val = (ret >> param->vol_bitpos) & param->vol_bitmask; + uv = desc->min + val * desc->step; + + return uv; +} + +static int reg_set_value(struct udevice *dev, const struct s5m8767_para *param, + int uv) +{ + const struct sec_voltage_desc *desc; + int ret, val; + + desc = param->vol; + if (uv < desc->min || uv > desc->max) + return -EINVAL; + val = (uv - desc->min) / desc->step; + val = (val & param->vol_bitmask) << param->vol_bitpos; + ret = pmic_clrsetbits(dev->parent, param->vol_addr, + param->vol_bitmask << param->vol_bitpos, + val); + + return ret; +} + +static int s5m8767_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 = 0; + + return 0; +} +static int ldo_get_value(struct udevice *dev) +{ + int ldo = dev->driver_data; + + return reg_get_value(dev, &ldo_param[ldo]); +} + +static int ldo_set_value(struct udevice *dev, int uv) +{ + int ldo = dev->driver_data; + + return reg_set_value(dev, &ldo_param[ldo], uv); +} + +static int reg_get_enable(struct udevice *dev, const struct s5m8767_para *param) +{ + bool enable; + int ret; + + ret = pmic_reg_read(dev->parent, param->reg_enaddr); + if (ret < 0) + return ret; + + enable = (ret >> ENABLE_SHIFT) & ENABLE_MASK; + + return enable; +} + +static int reg_set_enable(struct udevice *dev, const struct s5m8767_para *param, + bool enable) +{ + int ret; + + ret = pmic_reg_read(dev->parent, param->reg_enaddr); + if (ret < 0) + return ret; + + ret = pmic_clrsetbits(dev->parent, param->reg_enaddr, + ENABLE_MASK << ENABLE_SHIFT, + enable ? param->reg_enbiton << ENABLE_SHIFT : 0); + + return ret; +} + +static bool ldo_get_enable(struct udevice *dev) +{ + int ldo = dev->driver_data; + + return reg_get_enable(dev, &ldo_param[ldo]); +} + +static int ldo_set_enable(struct udevice *dev, bool enable) +{ + int ldo = dev->driver_data; + + return reg_set_enable(dev, &ldo_param[ldo], enable); +} + +static int s5m8767_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 = 0; + + return 0; +} + +static int buck_get_value(struct udevice *dev) +{ + int buck = dev->driver_data; + + return reg_get_value(dev, &buck_param[buck]); +} + +static int buck_set_value(struct udevice *dev, int uv) +{ + int buck = dev->driver_data; + + return reg_set_value(dev, &buck_param[buck], uv); +} + +static bool buck_get_enable(struct udevice *dev) +{ + int buck = dev->driver_data; + + return reg_get_enable(dev, &buck_param[buck]); +} + +static int buck_set_enable(struct udevice *dev, bool enable) +{ + int buck = dev->driver_data; + + return reg_set_enable(dev, &buck_param[buck], enable); +} + +static const struct dm_regulator_ops s5m8767_ldo_ops = { + .get_value = ldo_get_value, + .set_value = ldo_set_value, + .get_enable = ldo_get_enable, + .set_enable = ldo_set_enable, +}; + +U_BOOT_DRIVER(s5m8767_ldo) = { + .name = S5M8767_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &s5m8767_ldo_ops, + .probe = s5m8767_ldo_probe, +}; + +static const struct dm_regulator_ops s5m8767_buck_ops = { + .get_value = buck_get_value, + .set_value = buck_set_value, + .get_enable = buck_get_enable, + .set_enable = buck_set_enable, +}; + +U_BOOT_DRIVER(s5m8767_buck) = { + .name = S5M8767_BUCK_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &s5m8767_buck_ops, + .probe = s5m8767_buck_probe, +}; diff --git a/drivers/power/regulator/tps65090_regulator.c b/drivers/power/regulator/tps65090_regulator.c new file mode 100644 index 0000000000..affc504071 --- /dev/null +++ b/drivers/power/regulator/tps65090_regulator.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/tps65090.h> + +static int tps65090_fet_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + + uc_pdata = dev_get_uclass_platdata(dev); + + uc_pdata->type = REGULATOR_TYPE_OTHER; + uc_pdata->mode_count = 0; + + return 0; +} + +static bool tps65090_fet_get_enable(struct udevice *dev) +{ + struct udevice *pmic = dev_get_parent(dev); + int ret, fet_id; + + fet_id = dev->driver_data; + debug("%s: fet_id=%d\n", __func__, fet_id); + + ret = pmic_reg_read(pmic, REG_FET_BASE + fet_id); + if (ret < 0) + return ret; + + return ret & FET_CTRL_ENFET; +} + +/** + * Set the power state for a FET + * + * @param pmic pmic structure for the tps65090 + * @param fet_id FET number to set (1..MAX_FET_NUM) + * @param set 1 to power on FET, 0 to power off + * @return -EIO if we got a comms error, -EAGAIN if the FET failed to + * change state. If all is ok, returns 0. + */ +static int tps65090_fet_set(struct udevice *pmic, int fet_id, bool set) +{ + int retry; + u32 value; + int ret; + + value = FET_CTRL_ADENFET | FET_CTRL_WAIT; + if (set) + value |= FET_CTRL_ENFET; + + if (pmic_reg_write(pmic, REG_FET_BASE + fet_id, value)) + return -EIO; + + /* Try reading until we get a result */ + for (retry = 0; retry < MAX_CTRL_READ_TRIES; retry++) { + ret = pmic_reg_read(pmic, REG_FET_BASE + fet_id); + if (ret < 0) + return ret; + + /* Check that the FET went into the expected state */ + debug("%s: flags=%x\n", __func__, ret); + if (!!(ret & FET_CTRL_PGFET) == set) + return 0; + + /* If we got a timeout, there is no point in waiting longer */ + if (ret & FET_CTRL_TOFET) + break; + + mdelay(1); + } + + debug("FET %d: Power good should have set to %d but reg=%#02x\n", + fet_id, set, ret); + return -EAGAIN; +} + +static int tps65090_fet_set_enable(struct udevice *dev, bool enable) +{ + struct udevice *pmic = dev_get_parent(dev); + int ret, fet_id; + ulong start; + int loops; + + fet_id = dev->driver_data; + debug("%s: fet_id=%d, enable=%d\n", __func__, fet_id, enable); + + start = get_timer(0); + for (loops = 0;; loops++) { + ret = tps65090_fet_set(pmic, fet_id, enable); + if (!ret) + break; + + if (get_timer(start) > 100) + break; + + /* Turn it off and try again until we time out */ + tps65090_fet_set(pmic, fet_id, false); + } + + if (ret) + debug("%s: FET%d failed to power on: time=%lums, loops=%d\n", + __func__, fet_id, get_timer(start), loops); + else if (loops) + debug("%s: FET%d powered on after %lums, loops=%d\n", + __func__, fet_id, get_timer(start), loops); + + /* + * Unfortunately there are some conditions where the power-good bit + * will be 0, but the FET still comes up. One such case occurs with + * the LCD backlight on snow. We'll just return 0 here and assume + * that the FET will eventually come up. + */ + if (ret == -EAGAIN) + ret = 0; + + return ret; +} + +static const struct dm_regulator_ops tps65090_fet_ops = { + .get_enable = tps65090_fet_get_enable, + .set_enable = tps65090_fet_set_enable, +}; + +U_BOOT_DRIVER(tps65090_fet) = { + .name = TPS65090_FET_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps65090_fet_ops, + .probe = tps65090_fet_probe, +}; |