From 19790632648be6fff7a4898350bd52565bde7c96 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 13 Nov 2017 18:55:01 -0700 Subject: binman: Support accessing binman tables at run time Binman construct images consisting of multiple binary files. These files sometimes need to know (at run timme) where their peers are located. For example, SPL may want to know where U-Boot is located in the image, so that it can jump to U-Boot correctly on boot. In general the positions where the binaries end up after binman has finished packing them cannot be known at compile time. One reason for this is that binman does not know the size of the binaries until everything is compiled, linked and converted to binaries with objcopy. To make this work, we add a feature to binman which checks each binary for symbol names starting with '_binman'. These are then decoded to figure out which entry and property they refer to. Then binman writes the value of this symbol into the appropriate binary. With this, the symbol will have the correct value at run time. Macros are used to make this easier to use. As an example, this declares a symbol that will access the 'u-boot-spl' entry to find the 'pos' value (i.e. the position of SPL in the image): binman_sym_declare(unsigned long, u_boot_spl, pos); This converts to a symbol called '_binman_u_boot_spl_prop_pos' in any binary that includes it. Binman then updates the value in that binary, ensuring that it can be accessed at runtime with: ulong u_boot_pos = binman_sym(ulong, u_boot_spl, pos); This assigns the variable u_boot_pos to the position of SPL in the image. Signed-off-by: Simon Glass --- include/binman_sym.h | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 include/binman_sym.h (limited to 'include') diff --git a/include/binman_sym.h b/include/binman_sym.h new file mode 100644 index 00000000000..3999b26d8d4 --- /dev/null +++ b/include/binman_sym.h @@ -0,0 +1,80 @@ +/* + * Symbol access for symbols set up by binman as part of the build. + * + * This allows C code to access the position of a particular part of the image + * assembled by binman. + * + * Copyright (c) 2017 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __BINMAN_SYM_H +#define __BINMAN_SYM_H + +#define BINMAN_SYM_MISSING (-1UL) + +#ifdef CONFIG_BINMAN + +/** + * binman_symname() - Internal fnuction to get a binman symbol name + * + * @entry_name: Name of the entry to look for (e.g. 'u_boot_spl') + * @_prop_name: Property value to get from that entry (e.g. 'pos') + * @returns name of the symbol for that entry and property + */ +#define binman_symname(_entry_name, _prop_name) \ + _binman_ ## _entry_name ## _prop_ ## _prop_name + +/** + * binman_sym_declare() - Declare a symbol that will be used at run-time + * + * @_type: Type f the symbol (e.g. unsigned long) + * @entry_name: Name of the entry to look for (e.g. 'u_boot_spl') + * @_prop_name: Property value to get from that entry (e.g. 'pos') + */ +#define binman_sym_declare(_type, _entry_name, _prop_name) \ + _type binman_symname(_entry_name, _prop_name) \ + __attribute__((aligned(4), unused, section(".binman_sym"))) + +/** + * binman_sym_declare_optional() - Declare an optional symbol + * + * If this symbol cannot be provided by binman, an error will not be generated. + * Instead the image will be assigned the value BINMAN_SYM_MISSING. + * + * @_type: Type f the symbol (e.g. unsigned long) + * @entry_name: Name of the entry to look for (e.g. 'u_boot_spl') + * @_prop_name: Property value to get from that entry (e.g. 'pos') + */ +#define binman_sym_declare_optional(_type, _entry_name, _prop_name) \ + _type binman_symname(_entry_name, _prop_name) \ + __attribute__((aligned(4), weak, unused, \ + section(".binman_sym"))) + +/** + * binman_sym() - Access a previously declared symbol + * + * This is used to get the value of a symbol. E.g.: + * + * ulong address = binman_sym(ulong, u_boot_spl, pos); + * + * @_type: Type f the symbol (e.g. unsigned long) + * @entry_name: Name of the entry to look for (e.g. 'u_boot_spl') + * @_prop_name: Property value to get from that entry (e.g. 'pos') + * @returns value of that property (filled in by binman) + */ +#define binman_sym(_type, _entry_name, _prop_name) \ + (*(_type *)&binman_symname(_entry_name, _prop_name)) + +#else /* !BINMAN */ + +#define binman_sym_declare(_type, _entry_name, _prop_name) + +#define binman_sym_declare_optional(_type, _entry_name, _prop_name) + +#define binman_sym(_type, _entry_name, _prop_name) BINMAN_SYM_MISSING + +#endif /* BINMAN */ + +#endif -- cgit v1.3.1 From 8bee2d251afb61c203aa94877cf5077731822ed5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 13 Nov 2017 18:55:03 -0700 Subject: binman: Add binman symbol support to SPL Allow SPL to access binman symbols and use this to get the address of U-Boot. This falls back to CONFIG_SYS_TEXT_BASE if the binman symbol is not available. Signed-off-by: Simon Glass --- common/spl/spl.c | 16 ++++++++++++++-- include/binman_sym.h | 13 +++++++++++++ include/spl.h | 11 +++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/common/spl/spl.c b/common/spl/spl.c index 3bb20c7822f..76c1963611c 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -32,6 +33,9 @@ DECLARE_GLOBAL_DATA_PTR; u32 *boot_params_ptr = NULL; +/* See spl.h for information about this */ +binman_sym_declare(ulong, u_boot_any, pos); + /* Define board data structure */ static bd_t bdata __attribute__ ((section(".data"))); @@ -120,9 +124,17 @@ __weak void spl_board_prepare_for_boot(void) void spl_set_header_raw_uboot(struct spl_image_info *spl_image) { + ulong u_boot_pos = binman_sym(ulong, u_boot_any, pos); + spl_image->size = CONFIG_SYS_MONITOR_LEN; - spl_image->entry_point = CONFIG_SYS_UBOOT_START; - spl_image->load_addr = CONFIG_SYS_TEXT_BASE; + if (u_boot_pos != BINMAN_SYM_MISSING) { + /* biman does not support separate entry addresses at present */ + spl_image->entry_point = u_boot_pos; + spl_image->load_addr = u_boot_pos; + } else { + spl_image->entry_point = CONFIG_SYS_UBOOT_START; + spl_image->load_addr = CONFIG_SYS_TEXT_BASE; + } spl_image->os = IH_OS_U_BOOT; spl_image->name = "U-Boot"; } diff --git a/include/binman_sym.h b/include/binman_sym.h index 3999b26d8d4..87d03d52945 100644 --- a/include/binman_sym.h +++ b/include/binman_sym.h @@ -37,6 +37,17 @@ _type binman_symname(_entry_name, _prop_name) \ __attribute__((aligned(4), unused, section(".binman_sym"))) +/** + * binman_sym_extern() - Declare a extern symbol that will be used at run-time + * + * @_type: Type f the symbol (e.g. unsigned long) + * @entry_name: Name of the entry to look for (e.g. 'u_boot_spl') + * @_prop_name: Property value to get from that entry (e.g. 'pos') + */ +#define binman_sym_extern(_type, _entry_name, _prop_name) \ + extern _type binman_symname(_entry_name, _prop_name) \ + __attribute__((aligned(4), unused, section(".binman_sym"))) + /** * binman_sym_declare_optional() - Declare an optional symbol * @@ -73,6 +84,8 @@ #define binman_sym_declare_optional(_type, _entry_name, _prop_name) +#define binman_sym_extern(_type, _entry_name, _prop_name) + #define binman_sym(_type, _entry_name, _prop_name) BINMAN_SYM_MISSING #endif /* BINMAN */ diff --git a/include/spl.h b/include/spl.h index 308ce7b563b..c14448b8fc9 100644 --- a/include/spl.h +++ b/include/spl.h @@ -7,6 +7,8 @@ #ifndef _SPL_H_ #define _SPL_H_ +#include + /* Platform-specific defines */ #include #include @@ -51,6 +53,15 @@ struct spl_load_info { void *buf); }; +/* + * We need to know the position of U-Boot in memory so we can jump to it. We + * allow any U-Boot binary to be used (u-boot.bin, u-boot-nodtb.bin, + * u-boot.img), hence the '_any'. These is no checking here that the correct + * image is found. For * example if u-boot.img is used we don't check that + * spl_parse_image_header() can parse a valid header. + */ +binman_sym_extern(ulong, u_boot_any, pos); + /** * spl_load_simple_fit() - Loads a fit image from a device. * @spl_image: Image description to set up -- cgit v1.3.1 From bf802f5d544f85c03b4097ab23d078be43c61855 Mon Sep 17 00:00:00 2001 From: Felix Brack Date: Mon, 27 Nov 2017 09:14:16 +0100 Subject: power: extend prefix match to regulator-name property This patch extends pmic_bind_children prefix matching. In addition to the node name the property regulator-name is used while trying to match prefixes. This allows assigning different drivers to regulator nodes named regulator@1 and regulator@10 for example. I have discarded the idea of using other properties then regulator-name as I do not see any benefit in using property compatible or even regulator-compatible. Of course I am open to change this if there are good reasons to do so. Signed-off-by: Felix Brack Reviewed-by: Simon Glass --- arch/sandbox/dts/sandbox_pmic.dtsi | 6 ++++++ doc/device-tree-bindings/regulator/regulator.txt | 16 ++++++++++++++-- drivers/power/pmic/pmic-uclass.c | 11 +++++++++-- include/power/sandbox_pmic.h | 5 ++++- test/dm/regulator.c | 2 ++ 5 files changed, 35 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/arch/sandbox/dts/sandbox_pmic.dtsi b/arch/sandbox/dts/sandbox_pmic.dtsi index ce261b930e1..acb47993964 100644 --- a/arch/sandbox/dts/sandbox_pmic.dtsi +++ b/arch/sandbox/dts/sandbox_pmic.dtsi @@ -75,4 +75,10 @@ regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; }; + + no_match_by_nodename { + regulator-name = "buck_SUPPLY_1.5V"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + }; }; diff --git a/doc/device-tree-bindings/regulator/regulator.txt b/doc/device-tree-bindings/regulator/regulator.txt index 918711eb4d8..65b69c42789 100644 --- a/doc/device-tree-bindings/regulator/regulator.txt +++ b/doc/device-tree-bindings/regulator/regulator.txt @@ -2,7 +2,8 @@ Voltage/Current regulator Binding: The regulator devices don't use the "compatible" property. The binding is done -by the prefix of regulator node's name. Usually the pmic I/O driver will provide +by the prefix of regulator node's name, or, if this fails, by the prefix of the +regulator's "regulator-name" property. Usually the pmic I/O driver will provide the array of 'struct pmic_child_info' with the prefixes and compatible drivers. The bind is done by calling function: pmic_bind_childs(). Example drivers: @@ -15,8 +16,19 @@ For the node name e.g.: "prefix[:alpha:]num { ... }": Example the prefix "ldo" will pass for: "ldo1", "ldo@1", "ldoreg@1, ... +Binding by means of the node's name is preferred. However if the node names +would produce ambiguous prefixes (like "regulator@1" and "regualtor@11") and you +can't or do not want to change them then binding against the "regulator-name" +property is possible. The syntax for the prefix of the "regulator-name" property +is the same as the one for the regulator's node name. +Use case: a regulator named "regulator@1" to be bound to a driver named +"LDO_DRV" and a regulator named "regualator@11" to be bound to an other driver +named "BOOST_DRV". Using prefix "regualtor@1" for driver matching would load +the same driver for both regulators, hence the prefix is ambiguous. + Optional properties: -- regulator-name: a string, required by the regulator uclass +- regulator-name: a string, required by the regulator uclass, used for driver + binding if binding by node's name prefix fails - regulator-min-microvolt: a minimum allowed Voltage value - regulator-max-microvolt: a maximum allowed Voltage value - regulator-min-microamp: a minimum allowed Current value diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c index 64964e4e963..9347b406889 100644 --- a/drivers/power/pmic/pmic-uclass.c +++ b/drivers/power/pmic/pmic-uclass.c @@ -26,6 +26,7 @@ int pmic_bind_children(struct udevice *pmic, ofnode parent, struct driver *drv; struct udevice *child; const char *node_name; + const char *reg_name; int bind_count = 0; ofnode node; int prefix_len; @@ -44,8 +45,14 @@ int pmic_bind_children(struct udevice *pmic, ofnode parent, debug(" - compatible prefix: '%s'\n", info->prefix); prefix_len = strlen(info->prefix); - if (strncmp(info->prefix, node_name, prefix_len)) - continue; + if (strncmp(info->prefix, node_name, prefix_len)) { + reg_name = ofnode_read_string(node, + "regulator-name"); + if (!reg_name) + continue; + if (strncmp(info->prefix, reg_name, prefix_len)) + continue; + } drv = lists_driver_lookup_name(info->driver); if (!drv) { diff --git a/include/power/sandbox_pmic.h b/include/power/sandbox_pmic.h index 7fdbfb9fc6f..c5e6fda2ea0 100644 --- a/include/power/sandbox_pmic.h +++ b/include/power/sandbox_pmic.h @@ -13,7 +13,7 @@ #define SANDBOX_BUCK_DRIVER "sandbox_buck" #define SANDBOX_OF_BUCK_PREFIX "buck" -#define SANDBOX_BUCK_COUNT 2 +#define SANDBOX_BUCK_COUNT 3 #define SANDBOX_LDO_COUNT 2 /* * Sandbox PMIC registers: @@ -109,6 +109,9 @@ enum { #define SANDBOX_BUCK1_PLATNAME "SUPPLY_1.2V" #define SANDBOX_BUCK2_DEVNAME "buck2" #define SANDBOX_BUCK2_PLATNAME "SUPPLY_3.3V" +/* BUCK3: for testing fallback regulator prefix matching during bind */ +#define SANDBOX_BUCK3_DEVNAME "no_match_by_nodename" +#define SANDBOX_BUCK3_PLATNAME "buck_SUPPLY_1.5V" /* LDO names */ #define SANDBOX_LDO1_DEVNAME "ldo1" #define SANDBOX_LDO1_PLATNAME "VDD_EMMC_1.8V" diff --git a/test/dm/regulator.c b/test/dm/regulator.c index 3d0056f2dc7..395381d4bd2 100644 --- a/test/dm/regulator.c +++ b/test/dm/regulator.c @@ -27,6 +27,7 @@ DECLARE_GLOBAL_DATA_PTR; enum { BUCK1, BUCK2, + BUCK3, LDO1, LDO2, OUTPUT_COUNT, @@ -42,6 +43,7 @@ static const char *regulator_names[OUTPUT_COUNT][OUTPUT_NAME_COUNT] = { /* devname, platname */ { SANDBOX_BUCK1_DEVNAME, SANDBOX_BUCK1_PLATNAME }, { SANDBOX_BUCK2_DEVNAME, SANDBOX_BUCK2_PLATNAME }, + { SANDBOX_BUCK3_DEVNAME, SANDBOX_BUCK3_PLATNAME }, { SANDBOX_LDO1_DEVNAME, SANDBOX_LDO1_PLATNAME}, { SANDBOX_LDO2_DEVNAME, SANDBOX_LDO2_PLATNAME}, }; -- cgit v1.3.1 From 854dfbf99b89c114ba100905e1500b8ace60e0f9 Mon Sep 17 00:00:00 2001 From: Felix Brack Date: Thu, 30 Nov 2017 13:52:37 +0100 Subject: power: pmic/regulator: Add basic support for TPS65910 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 Reviewed-by: Simon Glass --- 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 | 459 +++++++++++++++++++++++++++ include/power/tps65910_pmic.h | 130 ++++++++ 7 files changed, 705 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 (limited to 'include') diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index e3f9e4dfc0d..5d49c93f32a 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 f7bdfa56096..7d6c583d347 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 00000000000..0127ce3aa05 --- /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 8892fa14e02..26fb9368ea9 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -188,3 +188,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 6c149a92634..7a2e76dc829 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -21,3 +21,4 @@ obj-$(CONFIG_$(SPL_)DM_REGULATOR_PALMAS) += palmas_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_PBIAS) += pbias_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 00000000000..5e2ec8f3639 --- /dev/null +++ b/drivers/power/regulator/tps65910_regulator.c @@ -0,0 +1,459 @@ +/* + * 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_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 00000000000..e8d9ffaa9fb --- /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_ */ -- cgit v1.3.1