diff options
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/pmic/Kconfig | 7 | ||||
-rw-r--r-- | drivers/power/pmic/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/pmic/tps65941.c | 83 | ||||
-rw-r--r-- | drivers/power/regulator/Kconfig | 20 | ||||
-rw-r--r-- | drivers/power/regulator/Makefile | 2 | ||||
-rw-r--r-- | drivers/power/regulator/tps62360_regulator.c | 123 | ||||
-rw-r--r-- | drivers/power/regulator/tps65941_regulator.c | 407 |
7 files changed, 643 insertions, 0 deletions
diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index 4718dc700c..b4bf018674 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -275,3 +275,10 @@ config SPL_PMIC_LP87565 help The LP87565 is a PMIC containing a bunch of SMPS. This driver binds the pmic children in SPL. + +config PMIC_TPS65941 + bool "Enable driver for Texas Instruments TPS65941 PMIC" + depends on DM_PMIC + help + The TPS65941 is a PMIC containing a bunch of SMPS & LDOs. + This driver binds the pmic children. diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 888dbb2857..ec64327805 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -39,3 +39,4 @@ obj-$(CONFIG_POWER_TPS65218) += pmic_tps65218.o obj-$(CONFIG_POWER_TPS65910) += pmic_tps65910.o obj-$(CONFIG_POWER_HI6553) += pmic_hi6553.o obj-$(CONFIG_POWER_MC34VR500) += pmic_mc34vr500.o +obj-$(CONFIG_PMIC_TPS65941) += tps65941.o diff --git a/drivers/power/pmic/tps65941.c b/drivers/power/pmic/tps65941.c new file mode 100644 index 0000000000..e8f3c950bd --- /dev/null +++ b/drivers/power/pmic/tps65941.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2019 Texas Instruments Incorporated, <www.ti.com> + * Keerthy <j-keerthy@ti.com> + */ + +#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/tps65941.h> +#include <dm/device.h> + +static const struct pmic_child_info pmic_children_info[] = { + { .prefix = "ldo", .driver = TPS65941_LDO_DRIVER }, + { .prefix = "buck", .driver = TPS65941_BUCK_DRIVER }, + { }, +}; + +static int tps65941_write(struct udevice *dev, uint reg, const uint8_t *buff, + int len) +{ + if (dm_i2c_write(dev, reg, buff, len)) { + pr_err("write error to device: %p register: %#x!\n", dev, reg); + return -EIO; + } + + return 0; +} + +static int tps65941_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{ + if (dm_i2c_read(dev, reg, buff, len)) { + pr_err("read error from device: %p register: %#x!\n", dev, reg); + return -EIO; + } + + return 0; +} + +static int tps65941_bind(struct udevice *dev) +{ + ofnode regulators_node; + int children; + + regulators_node = dev_read_subnode(dev, "regulators"); + if (!ofnode_valid(regulators_node)) { + debug("%s: %s regulators subnode not found!\n", __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) + printf("%s: %s - no child found\n", __func__, dev->name); + + /* Always return success for this device */ + return 0; +} + +static struct dm_pmic_ops tps65941_ops = { + .read = tps65941_read, + .write = tps65941_write, +}; + +static const struct udevice_id tps65941_ids[] = { + { .compatible = "ti,tps659411", .data = TPS659411 }, + { .compatible = "ti,tps659413", .data = TPS659413 }, + { } +}; + +U_BOOT_DRIVER(pmic_tps65941) = { + .name = "tps65941_pmic", + .id = UCLASS_PMIC, + .of_match = tps65941_ids, + .bind = tps65941_bind, + .ops = &tps65941_ops, +}; diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index 9aa00fad42..25fc787a29 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -273,6 +273,16 @@ config DM_REGULATOR_TPS65910 regulator types of the TPS65910 (BUCK, BOOST and LDO). It implements the get/set api for value and enable. +config DM_REGULATOR_TPS62360 + bool "Enable driver for TPS6236x Power Regulator" + depends on DM_REGULATOR + help + The TPS6236X DC/DC step down converter provides a single output + power line peaking at 3A current. This driver supports all four + variants of the chip (TPS62360, TPS62361, TPS62362, TPS62363). It + implements the get/set api for value only, as the power line is + always on. + config DM_REGULATOR_STPMIC1 bool "Enable driver for STPMIC1 regulators" depends on DM_REGULATOR && PMIC_STPMIC1 @@ -313,3 +323,13 @@ config SPL_DM_REGULATOR_LP873X This enables implementation of driver-model regulator uclass features for REGULATOR LP873X and the family of LP873X PMICs. The driver implements get/set api for: value and enable in SPL. + +config DM_REGULATOR_TPS65941 + bool "Enable driver for TPS65941 PMIC regulators" + depends on PMIC_TPS65941 + help + This enables implementation of driver-model regulator uclass + features for REGULATOR TPS65941 and the family of TPS65941 PMICs. + TPS65941 series of PMICs have 5 single phase BUCKs that can also + be configured in multi phase modes & 4 LDOs. The driver implements + get/set api for value and enable. diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index 6a3d4bbee4..b611c901ba 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -26,4 +26,6 @@ obj-$(CONFIG_$(SPL_)DM_REGULATOR_LP873X) += lp873x_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_LP87565) += lp87565_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o obj-$(CONFIG_DM_REGULATOR_TPS65910) += tps65910_regulator.o +obj-$(CONFIG_DM_REGULATOR_TPS62360) += tps62360_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_STPMIC1) += stpmic1.o +obj-$(CONFIG_DM_REGULATOR_TPS65941) += tps65941_regulator.o diff --git a/drivers/power/regulator/tps62360_regulator.c b/drivers/power/regulator/tps62360_regulator.c new file mode 100644 index 0000000000..3b123f503c --- /dev/null +++ b/drivers/power/regulator/tps62360_regulator.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * Tero Kristo <t-kristo@ti.com> + */ + +#include <common.h> +#include <dm.h> +#include <i2c.h> +#include <power/regulator.h> + +#define TPS62360_REG_SET0 0 + +#define TPS62360_I2C_CHIP 0x60 + +#define TPS62360_VSEL_STEPSIZE 10000 /* In uV */ + +struct tps62360_regulator_config { + u32 vmin; + u32 vmax; +}; + +struct tps62360_regulator_pdata { + u8 vsel_offset; + struct udevice *i2c; + struct tps62360_regulator_config *config; +}; + +/* + * TPS62362/TPS62363 are just re-using these values for now, their preset + * voltage values are just different compared to TPS62360/TPS62361. + */ +static struct tps62360_regulator_config tps62360_data = { + .vmin = 770000, + .vmax = 1400000, +}; + +static struct tps62360_regulator_config tps62361_data = { + .vmin = 500000, + .vmax = 1770000, +}; + +static int tps62360_regulator_set_value(struct udevice *dev, int uV) +{ + struct tps62360_regulator_pdata *pdata = dev_get_platdata(dev); + u8 regval; + + if (uV < pdata->config->vmin || uV > pdata->config->vmax) + return -EINVAL; + + uV -= pdata->config->vmin; + + uV = DIV_ROUND_UP(uV, TPS62360_VSEL_STEPSIZE); + + if (uV > U8_MAX) + return -EINVAL; + + regval = (u8)uV; + + return dm_i2c_write(pdata->i2c, TPS62360_REG_SET0 + pdata->vsel_offset, + ®val, 1); +} + +static int tps62360_regulator_get_value(struct udevice *dev) +{ + u8 regval; + int ret; + struct tps62360_regulator_pdata *pdata = dev_get_platdata(dev); + + ret = dm_i2c_read(pdata->i2c, TPS62360_REG_SET0 + pdata->vsel_offset, + ®val, 1); + if (ret) { + dev_err(dev, "i2c read failed: %d\n", ret); + return ret; + } + + return (u32)regval * TPS62360_VSEL_STEPSIZE + pdata->config->vmin; +} + +static int tps62360_regulator_ofdata_to_platdata(struct udevice *dev) +{ + struct tps62360_regulator_pdata *pdata = dev_get_platdata(dev); + u8 vsel0; + u8 vsel1; + int ret; + + pdata->config = (void *)dev_get_driver_data(dev); + + vsel0 = dev_read_bool(dev, "ti,vsel0-state-high"); + vsel1 = dev_read_bool(dev, "ti,vsel1-state-high"); + + pdata->vsel_offset = vsel0 + vsel1 * 2; + + ret = i2c_get_chip(dev->parent, TPS62360_I2C_CHIP, 1, &pdata->i2c); + if (ret) { + dev_err(dev, "i2c dev get failed.\n"); + return ret; + } + + return 0; +} + +static const struct dm_regulator_ops tps62360_regulator_ops = { + .get_value = tps62360_regulator_get_value, + .set_value = tps62360_regulator_set_value, +}; + +static const struct udevice_id tps62360_regulator_ids[] = { + { .compatible = "ti,tps62360", .data = (ulong)&tps62360_data }, + { .compatible = "ti,tps62361", .data = (ulong)&tps62361_data }, + { .compatible = "ti,tps62362", .data = (ulong)&tps62360_data }, + { .compatible = "ti,tps62363", .data = (ulong)&tps62361_data }, + { }, +}; + +U_BOOT_DRIVER(tps62360_regulator) = { + .name = "tps62360_regulator", + .id = UCLASS_REGULATOR, + .ops = &tps62360_regulator_ops, + .of_match = tps62360_regulator_ids, + .platdata_auto_alloc_size = sizeof(struct tps62360_regulator_pdata), + .ofdata_to_platdata = tps62360_regulator_ofdata_to_platdata, +}; diff --git a/drivers/power/regulator/tps65941_regulator.c b/drivers/power/regulator/tps65941_regulator.c new file mode 100644 index 0000000000..a00ef58129 --- /dev/null +++ b/drivers/power/regulator/tps65941_regulator.c @@ -0,0 +1,407 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2019 + * Texas Instruments Incorporated, <www.ti.com> + * + * Keerthy <j-keerthy@ti.com> + */ + +#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/tps65941.h> + +static const char tps65941_buck_ctrl[TPS65941_BUCK_NUM] = {0x4, 0x6, 0x8, 0xA, + 0xC}; +static const char tps65941_buck_vout[TPS65941_BUCK_NUM] = {0xE, 0x10, 0x12, + 0x14, 0x16}; +static const char tps65941_ldo_ctrl[TPS65941_BUCK_NUM] = {0x1D, 0x1E, 0x1F, + 0x20}; +static const char tps65941_ldo_vout[TPS65941_BUCK_NUM] = {0x23, 0x24, 0x25, + 0x26}; + +static int tps65941_buck_enable(struct udevice *dev, int op, bool *enable) +{ + int ret; + unsigned int adr; + struct dm_regulator_uclass_platdata *uc_pdata; + + uc_pdata = dev_get_uclass_platdata(dev); + adr = uc_pdata->ctrl_reg; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + ret &= TPS65941_BUCK_MODE_MASK; + + if (ret) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + if (*enable) + ret |= TPS65941_BUCK_MODE_MASK; + else + ret &= ~TPS65941_BUCK_MODE_MASK; + ret = pmic_reg_write(dev->parent, adr, ret); + if (ret) + return ret; + } + + return 0; +} + +static int tps65941_buck_volt2val(int uV) +{ + if (uV > TPS65941_BUCK_VOLT_MAX) + return -EINVAL; + else if (uV > 1650000) + return (uV - 1660000) / 20000 + 0xAB; + else if (uV > 1110000) + return (uV - 1110000) / 10000 + 0x73; + else if (uV > 600000) + return (uV - 600000) / 5000 + 0x0F; + else if (uV >= 300000) + return (uV - 300000) / 20000 + 0x00; + else + return -EINVAL; +} + +static int tps65941_buck_val2volt(int val) +{ + if (val > TPS65941_BUCK_VOLT_MAX_HEX) + return -EINVAL; + else if (val > 0xAB) + return 1660000 + (val - 0xAB) * 20000; + else if (val > 0x73) + return 1100000 + (val - 0x73) * 10000; + else if (val > 0xF) + return 600000 + (val - 0xF) * 5000; + else if (val >= 0x0) + return 300000 + val * 5000; + else + return -EINVAL; +} + +int tps65941_lookup_slew(int id) +{ + switch (id) { + case 0: + return 33000; + case 1: + return 20000; + case 2: + return 10000; + case 3: + return 5000; + case 4: + return 2500; + case 5: + return 1300; + case 6: + return 630; + case 7: + return 310; + default: + return -1; + } +} + +static int tps65941_buck_val(struct udevice *dev, int op, int *uV) +{ + unsigned int hex, adr; + int ret, delta, uwait, slew; + struct dm_regulator_uclass_platdata *uc_pdata; + + uc_pdata = dev_get_uclass_platdata(dev); + + if (op == PMIC_OP_GET) + *uV = 0; + + adr = uc_pdata->volt_reg; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + ret &= TPS65941_BUCK_VOLT_MASK; + ret = tps65941_buck_val2volt(ret); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + *uV = ret; + return 0; + } + + /* + * Compute the delta voltage, find the slew rate and wait + * for the appropriate amount of time after voltage switch + */ + if (*uV > ret) + delta = *uV - ret; + else + delta = ret - *uV; + + slew = pmic_reg_read(dev->parent, uc_pdata->ctrl_reg + 1); + if (slew < 0) + return ret; + + slew &= TP65941_BUCK_CONF_SLEW_MASK; + slew = tps65941_lookup_slew(slew); + if (slew <= 0) + return ret; + + uwait = delta / slew; + + hex = tps65941_buck_volt2val(*uV); + if (hex < 0) + return hex; + + ret &= 0x0; + ret = hex; + + ret = pmic_reg_write(dev->parent, adr, ret); + + udelay(uwait); + + return ret; +} + +static int tps65941_ldo_enable(struct udevice *dev, int op, bool *enable) +{ + int ret; + unsigned int adr; + struct dm_regulator_uclass_platdata *uc_pdata; + + uc_pdata = dev_get_uclass_platdata(dev); + adr = uc_pdata->ctrl_reg; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + ret &= TPS65941_LDO_MODE_MASK; + + if (ret) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + if (*enable) + ret |= TPS65941_LDO_MODE_MASK; + else + ret &= ~TPS65941_LDO_MODE_MASK; + ret = pmic_reg_write(dev->parent, adr, ret); + if (ret) + return ret; + } + + return 0; +} + +static int tps65941_ldo_val2volt(int val) +{ + if (val > TPS65941_LDO_VOLT_MAX_HEX || val < TPS65941_LDO_VOLT_MIN_HEX) + return -EINVAL; + else if (val >= TPS65941_LDO_VOLT_MIN_HEX) + return 600000 + (val - TPS65941_LDO_VOLT_MIN_HEX) * 50000; + else + return -EINVAL; +} + +static int tps65941_ldo_val(struct udevice *dev, int op, int *uV) +{ + unsigned int hex, adr; + int ret; + struct dm_regulator_uclass_platdata *uc_pdata; + + uc_pdata = dev_get_uclass_platdata(dev); + + if (op == PMIC_OP_GET) + *uV = 0; + + adr = uc_pdata->volt_reg; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + ret &= TPS65941_LDO_VOLT_MASK; + ret = tps65941_ldo_val2volt(ret); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + *uV = ret; + return 0; + } + + hex = tps65941_buck_volt2val(*uV); + if (hex < 0) + return hex; + + ret &= 0x0; + ret = hex; + + ret = pmic_reg_write(dev->parent, adr, ret); + + return ret; +} + +static int tps65941_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + int idx; + + uc_pdata = dev_get_uclass_platdata(dev); + uc_pdata->type = REGULATOR_TYPE_LDO; + + idx = dev->driver_data; + if (idx == 1 || idx == 2 || idx == 3 || idx == 4) { + debug("Single phase regulator\n"); + } else { + printf("Wrong ID for regulator\n"); + return -EINVAL; + } + + uc_pdata->ctrl_reg = tps65941_ldo_ctrl[idx - 1]; + uc_pdata->volt_reg = tps65941_ldo_vout[idx - 1]; + + return 0; +} + +static int tps65941_buck_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + int idx; + + uc_pdata = dev_get_uclass_platdata(dev); + uc_pdata->type = REGULATOR_TYPE_BUCK; + + idx = dev->driver_data; + if (idx == 1 || idx == 2 || idx == 3 || idx == 4 || idx == 5) { + debug("Single phase regulator\n"); + } else if (idx == 12) { + idx = 1; + } else if (idx == 34) { + idx = 3; + } else if (idx == 1234) { + idx = 1; + } else { + printf("Wrong ID for regulator\n"); + return -EINVAL; + } + + uc_pdata->ctrl_reg = tps65941_buck_ctrl[idx - 1]; + uc_pdata->volt_reg = tps65941_buck_vout[idx - 1]; + + return 0; +} + +static int ldo_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = tps65941_ldo_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int ldo_set_value(struct udevice *dev, int uV) +{ + return tps65941_ldo_val(dev, PMIC_OP_SET, &uV); +} + +static int ldo_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = tps65941_ldo_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int ldo_set_enable(struct udevice *dev, bool enable) +{ + return tps65941_ldo_enable(dev, PMIC_OP_SET, &enable); +} + +static int buck_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = tps65941_buck_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int buck_set_value(struct udevice *dev, int uV) +{ + return tps65941_buck_val(dev, PMIC_OP_SET, &uV); +} + +static int buck_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = tps65941_buck_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int buck_set_enable(struct udevice *dev, bool enable) +{ + return tps65941_buck_enable(dev, PMIC_OP_SET, &enable); +} + +static const struct dm_regulator_ops tps65941_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(tps65941_ldo) = { + .name = TPS65941_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps65941_ldo_ops, + .probe = tps65941_ldo_probe, +}; + +static const struct dm_regulator_ops tps65941_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(tps65941_buck) = { + .name = TPS65941_BUCK_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps65941_buck_ops, + .probe = tps65941_buck_probe, +}; |