From patchwork Thu Nov 30 09:58:12 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Felix Brack X-Patchwork-Id: 842947 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 3ynXt96BJ0z9t16 for ; Thu, 30 Nov 2017 20:58:57 +1100 (AEDT) Received: by lists.denx.de (Postfix, from userid 105) id 9433FC21F2E; Thu, 30 Nov 2017 09:58:56 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=RCVD_IN_DNSWL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 485BBC21C4B; Thu, 30 Nov 2017 09:58:51 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 98586C21C4B; Thu, 30 Nov 2017 09:58:49 +0000 (UTC) Received: from mail.ltec.ch (mail.ltec.ch [95.143.48.181]) by lists.denx.de (Postfix) with ESMTPS id 403E3C21C34 for ; Thu, 30 Nov 2017 09:58:48 +0000 (UTC) Received: from nebula.ltec ([172.27.11.2] helo=vm64.ltec) by mail.ltec.ch with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_CBC_SHA256:128) (Exim 4.89) (envelope-from ) id 1eKLbw-0004LG-20; Thu, 30 Nov 2017 10:58:28 +0100 From: Felix Brack To: u-boot@lists.denx.de Date: Thu, 30 Nov 2017 10:58:12 +0100 Message-Id: <1512035892-26811-1-git-send-email-fb@ltec.ch> X-Mailer: git-send-email 2.7.4 Cc: jacob-chen@iotwrt.com, Zhiqiang.Hou@nxp.com Subject: [U-Boot] [PATCH v3] power: pmic/regulator: Add basic support for TPS65910 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Texas Instrument's TPS65910 PMIC contains 3 buck DC-DC converts, one boost DC-DC converter and 8 LDOs. This patch implements driver model support for the TPS65910 PMIC and its regulators making the get/set API for regulator value/enable available. This patch depends on the patch "am33xx: Add a function to query MPU voltage in uV" to build correctly. For boards relying on the DT include file tps65910.dtsi the v3 patch "power: extend prefix match to regulator-name property" and an appropriate regulator naming is also required. Signed-off-by: Felix Brack --- Changes in v3: - use dev_get_driver_data() instead of dev->driver_data - use dev_get_platdata() instead of dev->platdata - store unit address in platdata during ofdata_to_platdata() instead of queering it multiple times by dev_get_driver_data() - use pmic_reg_read() instead of pmic_read() wherever possible - eliminate the use small data types like u8 if not required Changes in v2: - move information about regulator supply power from the PMIC to the regulator driver which greatly simplifies and streamlines the code of both drivers (thanks Simon!) - use dev_get_uclass_platdata() to get the regulator constraints instead of reinventing the wheel and reading them from the DT - make pmic_tps65910_write() and pmic_tps65910_read() return the results of the i2c operation instead of obscuring it - generally review and eventually fix return values - modify regulator description in Kconfig - get_ctrl_reg_from_unit_addr() uses a lookup array instead of a switch statement - rename some local functions to prevent confusion with generic functions - reformatting code for better readability drivers/power/pmic/Kconfig | 8 + drivers/power/pmic/Makefile | 1 + drivers/power/pmic/pmic_tps65910_dm.c | 98 ++++++ drivers/power/regulator/Kconfig | 8 + drivers/power/regulator/Makefile | 1 + drivers/power/regulator/tps65910_regulator.c | 460 +++++++++++++++++++++++++++ include/power/tps65910_pmic.h | 130 ++++++++ 7 files changed, 706 insertions(+) create mode 100644 drivers/power/pmic/pmic_tps65910_dm.c create mode 100644 drivers/power/regulator/tps65910_regulator.c create mode 100644 include/power/tps65910_pmic.h diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index e3f9e4d..5d49c93 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -201,3 +201,11 @@ config POWER_MC34VR500 The MC34VR500 is used in conjunction with the FSL T1 and LS1 series SoC. It provides 4 buck DC-DC convertors and 5 LDOs, and it is accessed via an I2C interface. + +config DM_PMIC_TPS65910 + bool "Enable driver for Texas Instruments TPS65910 PMIC" + depends on DM_PMIC + ---help--- + The TPS65910 is a PMIC containing 3 buck DC-DC converters, one boost + DC-DC converter, 8 LDOs and a RTC. This driver binds the SMPS and LDO + pmic children. diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index f7bdfa5..7d6c583 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_PMIC_RK8XX) += rk8xx.o obj-$(CONFIG_PMIC_RN5T567) += rn5t567.o obj-$(CONFIG_PMIC_TPS65090) += tps65090.o obj-$(CONFIG_PMIC_S5M8767) += s5m8767.o +obj-$(CONFIG_DM_PMIC_TPS65910) += pmic_tps65910_dm.o obj-$(CONFIG_$(SPL_)PMIC_PALMAS) += palmas.o obj-$(CONFIG_$(SPL_)PMIC_LP873X) += lp873x.o obj-$(CONFIG_$(SPL_)PMIC_LP87565) += lp87565.o diff --git a/drivers/power/pmic/pmic_tps65910_dm.c b/drivers/power/pmic/pmic_tps65910_dm.c new file mode 100644 index 0000000..0127ce3 --- /dev/null +++ b/drivers/power/pmic/pmic_tps65910_dm.c @@ -0,0 +1,98 @@ +/* + * Copyright (C) EETS GmbH, 2017, Felix Brack + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static const struct pmic_child_info pmic_children_info[] = { + { .prefix = "ldo_", .driver = TPS65910_LDO_DRIVER }, + { .prefix = "buck_", .driver = TPS65910_BUCK_DRIVER }, + { .prefix = "boost_", .driver = TPS65910_BOOST_DRIVER }, + { }, +}; + +static int pmic_tps65910_reg_count(struct udevice *dev) +{ + return TPS65910_NUM_REGS; +} + +static int pmic_tps65910_write(struct udevice *dev, uint reg, const u8 *buffer, + int len) +{ + int ret; + + ret = dm_i2c_write(dev, reg, buffer, len); + if (ret) + error("%s write error on register %02x\n", dev->name, reg); + + return ret; +} + +static int pmic_tps65910_read(struct udevice *dev, uint reg, u8 *buffer, + int len) +{ + int ret; + + ret = dm_i2c_read(dev, reg, buffer, len); + if (ret) + error("%s read error on register %02x\n", dev->name, reg); + + return ret; +} + +static int pmic_tps65910_bind(struct udevice *dev) +{ + ofnode regulators_node; + int children; + + regulators_node = dev_read_subnode(dev, "regulators"); + if (!ofnode_valid(regulators_node)) { + debug("%s regulators subnode not found\n", dev->name); + return -EINVAL; + } + + children = pmic_bind_children(dev, regulators_node, pmic_children_info); + if (!children) + debug("%s has no children (regulators)\n", dev->name); + + return 0; +} + +static int pmic_tps65910_probe(struct udevice *dev) +{ + /* use I2C control interface instead of I2C smartreflex interface to + * access smartrefelex registers VDD1_OP_REG, VDD1_SR_REG, VDD2_OP_REG + * and VDD2_SR_REG + */ + return pmic_clrsetbits(dev, TPS65910_REG_DEVICE_CTRL, 0, + TPS65910_I2C_SEL_MASK); +} + +static struct dm_pmic_ops pmic_tps65910_ops = { + .reg_count = pmic_tps65910_reg_count, + .read = pmic_tps65910_read, + .write = pmic_tps65910_write, +}; + +static const struct udevice_id pmic_tps65910_match[] = { + { .compatible = "ti,tps65910" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(pmic_tps65910) = { + .name = "pmic_tps65910", + .id = UCLASS_PMIC, + .of_match = pmic_tps65910_match, + .bind = pmic_tps65910_bind, + .probe = pmic_tps65910_probe, + .ops = &pmic_tps65910_ops, +}; diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index c82a936..fb6a357 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -168,3 +168,11 @@ config DM_REGULATOR_LP87565 LP87565 series of PMICs have 4 single phase BUCKs that can also be configured in multi phase modes. The driver implements get/set api for value and enable. + +config DM_REGULATOR_TPS65910 + bool "Enable driver for TPS65910 PMIC regulators" + depends on DM_PMIC_TPS65910 + ---help--- + The TPS65910 PMIC provides 4 SMPSs and 8 LDOs. This driver supports all + regulator types of the TPS65910 (BUCK, BOOST and LDO). It implements + the get/set api for value and enable. diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index 18fb870..3eef297 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -20,3 +20,4 @@ obj-$(CONFIG_REGULATOR_TPS65090) += tps65090_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_PALMAS) += palmas_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_LP873X) += lp873x_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_LP87565) += lp87565_regulator.o +obj-$(CONFIG_DM_REGULATOR_TPS65910) += tps65910_regulator.o diff --git a/drivers/power/regulator/tps65910_regulator.c b/drivers/power/regulator/tps65910_regulator.c new file mode 100644 index 0000000..51bfd50 --- /dev/null +++ b/drivers/power/regulator/tps65910_regulator.c @@ -0,0 +1,460 @@ +/* + * Copyright (C) EETS GmbH, 2017, Felix Brack + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#define VOUT_CHOICE_COUNT 4 + +/* + * struct regulator_props - Properties of a LDO and VIO SMPS regulator + * + * All of these regulators allow setting one out of four output voltages. + * These output voltages are only achievable when supplying the regulator + * with a minimum input voltage. + * + * @vin_min[]: minimum supply input voltage in uV required to achieve the + * corresponding vout[] voltage + * @vout[]: regulator output voltage in uV + * @reg: I2C register used to set regulator voltage + */ +struct regulator_props { + int vin_min[VOUT_CHOICE_COUNT]; + int vout[VOUT_CHOICE_COUNT]; + int reg; +}; + +static const struct regulator_props ldo_props_vdig1 = { + .vin_min = { 1700000, 2100000, 2700000, 3200000 }, + .vout = { 1200000, 1500000, 1800000, 2700000 }, + .reg = TPS65910_REG_VDIG1 +}; + +static const struct regulator_props ldo_props_vdig2 = { + .vin_min = { 1700000, 1700000, 1700000, 2700000 }, + .vout = { 1000000, 1100000, 1200000, 1800000 }, + .reg = TPS65910_REG_VDIG2 +}; + +static const struct regulator_props ldo_props_vpll = { + .vin_min = { 2700000, 2700000, 2700000, 3000000 }, + .vout = { 1000000, 1100000, 1800000, 2500000 }, + .reg = TPS65910_REG_VPLL +}; + +static const struct regulator_props ldo_props_vdac = { + .vin_min = { 2700000, 3000000, 3200000, 3200000 }, + .vout = { 1800000, 2600000, 2800000, 2850000 }, + .reg = TPS65910_REG_VDAC +}; + +static const struct regulator_props ldo_props_vaux1 = { + .vin_min = { 2700000, 3200000, 3200000, 3200000 }, + .vout = { 1800000, 2500000, 2800000, 2850000 }, + .reg = TPS65910_REG_VAUX1 +}; + +static const struct regulator_props ldo_props_vaux2 = { + .vin_min = { 2700000, 3200000, 3200000, 3600000 }, + .vout = { 1800000, 2800000, 2900000, 3300000 }, + .reg = TPS65910_REG_VAUX2 +}; + +static const struct regulator_props ldo_props_vaux33 = { + .vin_min = { 2700000, 2700000, 3200000, 3600000 }, + .vout = { 1800000, 2000000, 2800000, 3300000 }, + .reg = TPS65910_REG_VAUX33 +}; + +static const struct regulator_props ldo_props_vmmc = { + .vin_min = { 2700000, 3200000, 3200000, 3600000 }, + .vout = { 1800000, 2800000, 3000000, 3300000 }, + .reg = TPS65910_REG_VMMC +}; + +static const struct regulator_props smps_props_vio = { + .vin_min = { 3200000, 3200000, 4000000, 4400000 }, + .vout = { 1500000, 1800000, 2500000, 3300000 }, + .reg = TPS65910_REG_VIO +}; + +/* lookup table of control registers indexed by regulator unit number */ +static const int ctrl_regs[] = { + TPS65910_REG_VRTC, + TPS65910_REG_VIO, + TPS65910_REG_VDD1, + TPS65910_REG_VDD2, + TPS65910_REG_VDD3, + TPS65910_REG_VDIG1, + TPS65910_REG_VDIG2, + TPS65910_REG_VPLL, + TPS65910_REG_VDAC, + TPS65910_REG_VAUX1, + TPS65910_REG_VAUX2, + TPS65910_REG_VAUX33, + TPS65910_REG_VMMC +}; + +/* supply names as used in DT */ +static const char * const supply_names[] = { + "vccio-supply", + "vcc1-supply", + "vcc2-supply", + "vcc3-supply", + "vcc4-supply", + "vcc5-supply", + "vcc6-supply", + "vcc7-supply" +}; + +/* lookup table of regulator supplies indexed by regulator unit number */ +static const int regulator_supplies[] = { + TPS65910_SUPPLY_VCC7, + TPS65910_SUPPLY_VCCIO, + TPS65910_SUPPLY_VCC1, + TPS65910_SUPPLY_VCC2, + TPS65910_SUPPLY_VCC7, + TPS65910_SUPPLY_VCC6, + TPS65910_SUPPLY_VCC6, + TPS65910_SUPPLY_VCC5, + TPS65910_SUPPLY_VCC5, + TPS65910_SUPPLY_VCC4, + TPS65910_SUPPLY_VCC4, + TPS65910_SUPPLY_VCC3, + TPS65910_SUPPLY_VCC3 +}; + +static int get_ctrl_reg_from_unit_addr(const uint unit_addr) +{ + if (unit_addr < ARRAY_SIZE(ctrl_regs)) + return ctrl_regs[unit_addr]; + return -ENXIO; +} + +static int tps65910_regulator_get_value(struct udevice *dev, + const struct regulator_props *rgp) +{ + int sel, val, vout; + struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); + int vin = pdata->supply; + + val = pmic_reg_read(dev->parent, rgp->reg); + if (val < 0) + return val; + sel = (val & TPS65910_SEL_MASK) >> 2; + vout = (vin >= *(rgp->vin_min + sel)) ? *(rgp->vout + sel) : 0; + vout = ((val & TPS65910_SUPPLY_STATE_MASK) == 1) ? vout : 0; + + return vout; +} + +static int tps65910_ldo_get_value(struct udevice *dev) +{ + struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); + int vin; + + if (!pdata) + return 0; + vin = pdata->supply; + + switch (pdata->unit) { + case TPS65910_UNIT_VRTC: + /* VRTC is fixed and can't be turned off */ + return (vin >= 2500000) ? 1830000 : 0; + case TPS65910_UNIT_VDIG1: + return tps65910_regulator_get_value(dev, &ldo_props_vdig1); + case TPS65910_UNIT_VDIG2: + return tps65910_regulator_get_value(dev, &ldo_props_vdig2); + case TPS65910_UNIT_VPLL: + return tps65910_regulator_get_value(dev, &ldo_props_vpll); + case TPS65910_UNIT_VDAC: + return tps65910_regulator_get_value(dev, &ldo_props_vdac); + case TPS65910_UNIT_VAUX1: + return tps65910_regulator_get_value(dev, &ldo_props_vaux1); + case TPS65910_UNIT_VAUX2: + return tps65910_regulator_get_value(dev, &ldo_props_vaux2); + case TPS65910_UNIT_VAUX33: + return tps65910_regulator_get_value(dev, &ldo_props_vaux33); + case TPS65910_UNIT_VMMC: + return tps65910_regulator_get_value(dev, &ldo_props_vmmc); + default: + return 0; + } +} + +static int tps65910_regulator_set_value(struct udevice *dev, + const struct regulator_props *ldo, + int uV) +{ + int val; + int sel = 0; + struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); + + do { + /* we only allow exact voltage matches */ + if (uV == *(ldo->vout + sel)) + break; + } while (++sel < VOUT_CHOICE_COUNT); + if (sel == VOUT_CHOICE_COUNT) + return -EINVAL; + if (pdata->supply < *(ldo->vin_min + sel)) + return -EINVAL; + + val = pmic_reg_read(dev->parent, ldo->reg); + if (val < 0) + return val; + val &= ~TPS65910_SEL_MASK; + val |= sel << 2; + return pmic_reg_write(dev->parent, ldo->reg, val); +} + +static int tps65910_ldo_set_value(struct udevice *dev, int uV) +{ + struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); + int vin = pdata->supply; + + switch (pdata->unit) { + case TPS65910_UNIT_VRTC: + /* VRTC is fixed to 1.83V and can't be turned off */ + if (vin < 2500000) + return -EINVAL; + return 0; + case TPS65910_UNIT_VDIG1: + return tps65910_regulator_set_value(dev, &ldo_props_vdig1, uV); + case TPS65910_UNIT_VDIG2: + return tps65910_regulator_set_value(dev, &ldo_props_vdig2, uV); + case TPS65910_UNIT_VPLL: + return tps65910_regulator_set_value(dev, &ldo_props_vpll, uV); + case TPS65910_UNIT_VDAC: + return tps65910_regulator_set_value(dev, &ldo_props_vdac, uV); + case TPS65910_UNIT_VAUX1: + return tps65910_regulator_set_value(dev, &ldo_props_vaux1, uV); + case TPS65910_UNIT_VAUX2: + return tps65910_regulator_set_value(dev, &ldo_props_vaux2, uV); + case TPS65910_UNIT_VAUX33: + return tps65910_regulator_set_value(dev, &ldo_props_vaux33, uV); + case TPS65910_UNIT_VMMC: + return tps65910_regulator_set_value(dev, &ldo_props_vmmc, uV); + default: + return 0; + } +} + +static int tps65910_get_enable(struct udevice *dev) +{ + int reg, val; + struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); + + reg = get_ctrl_reg_from_unit_addr(pdata->unit); + if (reg < 0) + return reg; + + val = pmic_reg_read(dev->parent, reg); + if (val < 0) + return val; + + /* bits 1:0 of regulator control register define state */ + return ((val & TPS65910_SUPPLY_STATE_MASK) == 1); +} + +static int tps65910_set_enable(struct udevice *dev, bool enable) +{ + int reg; + uint clr, set; + struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); + + reg = get_ctrl_reg_from_unit_addr(pdata->unit); + if (reg < 0) + return reg; + + if (enable) { + clr = TPS65910_SUPPLY_STATE_MASK & ~TPS65910_SUPPLY_STATE_ON; + set = TPS65910_SUPPLY_STATE_MASK & TPS65910_SUPPLY_STATE_ON; + } else { + clr = TPS65910_SUPPLY_STATE_MASK & ~TPS65910_SUPPLY_STATE_OFF; + set = TPS65910_SUPPLY_STATE_MASK & TPS65910_SUPPLY_STATE_OFF; + } + return pmic_clrsetbits(dev->parent, reg, clr, set); +} + +static int buck_get_vdd1_vdd2_value(struct udevice *dev, int reg_vdd) +{ + int gain; + int val = pmic_reg_read(dev, reg_vdd); + + if (val < 0) + return val; + gain = (val & TPS65910_GAIN_SEL_MASK) >> 6; + gain = (gain == 0) ? 1 : gain; + val = pmic_reg_read(dev, reg_vdd + 1); + if (val < 0) + return val; + if (val & TPS65910_VDD_SR_MASK) + /* use smart reflex value instead */ + val = pmic_reg_read(dev, reg_vdd + 2); + if (val < 0) + return val; + return (562500 + (val & TPS65910_VDD_SEL_MASK) * 12500) * gain; +} + +static int tps65910_buck_get_value(struct udevice *dev) +{ + struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); + + switch (pdata->unit) { + case TPS65910_UNIT_VIO: + return tps65910_regulator_get_value(dev, &smps_props_vio); + case TPS65910_UNIT_VDD1: + return buck_get_vdd1_vdd2_value(dev->parent, TPS65910_REG_VDD1); + case TPS65910_UNIT_VDD2: + return buck_get_vdd1_vdd2_value(dev->parent, TPS65910_REG_VDD2); + default: + return 0; + } +} + +static int buck_set_vdd1_vdd2_value(struct udevice *dev, int uV) +{ + int ret, reg_vdd, gain; + int val; + struct dm_regulator_uclass_platdata *uc_pdata; + struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); + + switch (pdata->unit) { + case TPS65910_UNIT_VDD1: + reg_vdd = TPS65910_REG_VDD1; + break; + case TPS65910_UNIT_VDD2: + reg_vdd = TPS65910_REG_VDD2; + break; + default: + return -EINVAL; + } + uc_pdata = dev_get_uclass_platdata(dev); + + /* check setpoint is within limits */ + if (uV < uc_pdata->min_uV) { + error("voltage %duV for %s too low\n", uV, dev->name); + return -EINVAL; + } + if (uV > uc_pdata->max_uV) { + error("voltage %duV for %s too high\n", uV, dev->name); + return -EINVAL; + } + + val = pmic_reg_read(dev->parent, reg_vdd); + if (val < 0) + return val; + gain = (val & TPS65910_GAIN_SEL_MASK) >> 6; + gain = (gain == 0) ? 1 : gain; + val = ((uV / gain) - 562500) / 12500; + if (val < TPS65910_VDD_SEL_MIN || val > TPS65910_VDD_SEL_MAX) + /* + * Neither do we change the gain, nor do we allow shutdown or + * any approximate value (for now) + */ + return -EPERM; + val &= TPS65910_VDD_SEL_MASK; + ret = pmic_reg_write(dev->parent, reg_vdd + 1, val); + if (ret) + return ret; + return 0; +} + +static int tps65910_buck_set_value(struct udevice *dev, int uV) +{ + struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); + + if (pdata->unit == TPS65910_UNIT_VIO) + return tps65910_regulator_set_value(dev, &smps_props_vio, uV); + + return buck_set_vdd1_vdd2_value(dev, uV); +} + +static int tps65910_boost_get_value(struct udevice *dev) +{ + int vout; + struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); + + vout = (pdata->supply >= 3000000) ? 5000000 : 0; + return vout; +} + +static int tps65910_regulator_ofdata_to_platdata(struct udevice *dev) +{ + struct udevice *supply; + int ret; + const char *supply_name; + struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); + + pdata->unit = dev_get_driver_data(dev); + if (pdata->unit < TPS65910_UNIT_VRTC || + pdata->unit > TPS65910_UNIT_VMMC) + return -EINVAL; + supply_name = supply_names[regulator_supplies[pdata->unit]]; + + debug("Looking up supply power %s\n", supply_name); + ret = device_get_supply_regulator(dev->parent, supply_name, &supply); + if (ret) { + debug(" missing supply power %s\n", supply_name); + return ret; + } + pdata->supply = regulator_get_value(supply); + if (pdata->supply < 0) { + debug(" invalid supply voltage for regulator %s\n", + supply->name); + return -EINVAL; + } + + return 0; +} + +static const struct dm_regulator_ops tps65910_boost_ops = { + .get_value = tps65910_boost_get_value, + .get_enable = tps65910_get_enable, + .set_enable = tps65910_set_enable, +}; + +U_BOOT_DRIVER(tps65910_boost) = { + .name = TPS65910_BOOST_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps65910_boost_ops, + .platdata_auto_alloc_size = sizeof(struct tps65910_regulator_pdata), + .ofdata_to_platdata = tps65910_regulator_ofdata_to_platdata, +}; + +static const struct dm_regulator_ops tps65910_buck_ops = { + .get_value = tps65910_buck_get_value, + .set_value = tps65910_buck_set_value, + .get_enable = tps65910_get_enable, + .set_enable = tps65910_set_enable, +}; + +U_BOOT_DRIVER(tps65910_buck) = { + .name = TPS65910_BUCK_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps65910_buck_ops, + .platdata_auto_alloc_size = sizeof(struct tps65910_regulator_pdata), + .ofdata_to_platdata = tps65910_regulator_ofdata_to_platdata, +}; + +static const struct dm_regulator_ops tps65910_ldo_ops = { + .get_value = tps65910_ldo_get_value, + .set_value = tps65910_ldo_set_value, + .get_enable = tps65910_get_enable, + .set_enable = tps65910_set_enable, +}; + +U_BOOT_DRIVER(tps65910_ldo) = { + .name = TPS65910_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps65910_ldo_ops, + .platdata_auto_alloc_size = sizeof(struct tps65910_regulator_pdata), + .ofdata_to_platdata = tps65910_regulator_ofdata_to_platdata, +}; diff --git a/include/power/tps65910_pmic.h b/include/power/tps65910_pmic.h new file mode 100644 index 0000000..e8d9ffa --- /dev/null +++ b/include/power/tps65910_pmic.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) EETS GmbH, 2017, Felix Brack + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __TPS65910_PMIC_H_ +#define __TPS65910_PMIC_H_ + +#define TPS65910_I2C_SEL_MASK (0x1 << 4) +#define TPS65910_VDD_SR_MASK (0x1 << 7) +#define TPS65910_GAIN_SEL_MASK (0x3 << 6) +#define TPS65910_VDD_SEL_MASK 0x7f +#define TPS65910_VDD_SEL_MIN 3 +#define TPS65910_VDD_SEL_MAX 75 +#define TPS65910_SEL_MASK (0x3 << 2) +#define TPS65910_SUPPLY_STATE_MASK 0x3 +#define TPS65910_SUPPLY_STATE_OFF 0x0 +#define TPS65910_SUPPLY_STATE_ON 0x1 + +/* i2c registers */ +enum { + TPS65910_REG_RTC_SEC = 0x00, + TPS65910_REG_RTC_MIN, + TPS65910_REG_RTC_HOUR, + TPS65910_REG_RTC_DAY, + TPS65910_REG_RTC_MONTH, + TPS65910_REG_RTC_YEAR, + TPS65910_REG_RTC_WEEK, + TPS65910_REG_RTC_ALARM_SEC = 0x08, + TPS65910_REG_RTC_ALARM_MIN, + TPS65910_REG_RTC_ALARM_HOUR, + TPS65910_REG_RTC_ALARM_DAY, + TPS65910_REG_RTC_ALARM_MONTH, + TPS65910_REG_RTC_ALARM_YEAR, + TPS65910_REG_RTC_CTRL = 0x10, + TPS65910_REG_RTC_STAT, + TPS65910_REG_RTC_INT, + TPS65910_REG_RTC_COMP_LSB, + TPS65910_REG_RTC_COMP_MSB, + TPS65910_REG_RTC_RESISTOR_PRG, + TPS65910_REG_RTC_RESET_STAT, + TPS65910_REG_BACKUP1, + TPS65910_REG_BACKUP2, + TPS65910_REG_BACKUP3, + TPS65910_REG_BACKUP4, + TPS65910_REG_BACKUP5, + TPS65910_REG_PUADEN, + TPS65910_REG_REF, + TPS65910_REG_VRTC, + TPS65910_REG_VIO = 0x20, + TPS65910_REG_VDD1, + TPS65910_REG_VDD1_VAL, + TPS65910_REG_VDD1_VAL_SR, + TPS65910_REG_VDD2, + TPS65910_REG_VDD2_VAL, + TPS65910_REG_VDD2_VAL_SR, + TPS65910_REG_VDD3, + TPS65910_REG_VDIG1 = 0x30, + TPS65910_REG_VDIG2, + TPS65910_REG_VAUX1, + TPS65910_REG_VAUX2, + TPS65910_REG_VAUX33, + TPS65910_REG_VMMC, + TPS65910_REG_VPLL, + TPS65910_REG_VDAC, + TPS65910_REG_THERM, + TPS65910_REG_BATTERY_BACKUP_CHARGE, + TPS65910_REG_DCDC_CTRL = 0x3e, + TPS65910_REG_DEVICE_CTRL, + TPS65910_REG_DEVICE_CTRL2, + TPS65910_REG_SLEEP_KEEP_LDO_ON, + TPS65910_REG_SLEEP_KEEP_RES_ON, + TPS65910_REG_SLEEP_SET_LDO_OFF, + TPS65910_REG_SLEEP_SET_RES_OFF, + TPS65910_REG_EN1_LDO_ASS, + TPS65910_REG_EM1_SMPS_ASS, + TPS65910_REG_EN2_LDO_ASS, + TPS65910_REG_EM2_SMPS_ASS, + TPS65910_REG_INT_STAT = 0x50, + TPS65910_REG_INT_MASK, + TPS65910_REG_INT_STAT2, + TPS65910_REG_INT_MASK2, + TPS65910_REG_GPIO = 0x60, + TPS65910_REG_JTAGREVNUM = 0x80, + TPS65910_NUM_REGS +}; + +/* chip supplies */ +enum { + TPS65910_SUPPLY_VCCIO = 0x00, + TPS65910_SUPPLY_VCC1, + TPS65910_SUPPLY_VCC2, + TPS65910_SUPPLY_VCC3, + TPS65910_SUPPLY_VCC4, + TPS65910_SUPPLY_VCC5, + TPS65910_SUPPLY_VCC6, + TPS65910_SUPPLY_VCC7, + TPS65910_NUM_SUPPLIES +}; + +/* regulator unit numbers */ +enum { + TPS65910_UNIT_VRTC = 0x00, + TPS65910_UNIT_VIO, + TPS65910_UNIT_VDD1, + TPS65910_UNIT_VDD2, + TPS65910_UNIT_VDD3, + TPS65910_UNIT_VDIG1, + TPS65910_UNIT_VDIG2, + TPS65910_UNIT_VPLL, + TPS65910_UNIT_VDAC, + TPS65910_UNIT_VAUX1, + TPS65910_UNIT_VAUX2, + TPS65910_UNIT_VAUX33, + TPS65910_UNIT_VMMC, +}; + +/* platform data */ +struct tps65910_regulator_pdata { + u32 supply; /* regulator supply voltage in uV */ + uint unit; /* unit-address according to DT */ +}; + +/* driver names */ +#define TPS65910_BUCK_DRIVER "tps65910_buck" +#define TPS65910_BOOST_DRIVER "tps65910_boost" +#define TPS65910_LDO_DRIVER "tps65910_ldo" + +#endif /* __TPS65910_PMIC_H_ */