diff options
Diffstat (limited to 'roms/u-boot/drivers/power/regulator')
31 files changed, 10517 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/power/regulator/Kconfig b/roms/u-boot/drivers/power/regulator/Kconfig new file mode 100644 index 000000000..17942e299 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/Kconfig @@ -0,0 +1,373 @@ +config DM_REGULATOR + bool "Enable Driver Model for REGULATOR drivers (UCLASS_REGULATOR)" + depends on DM + ---help--- + This config enables the driver model regulator support. + UCLASS_REGULATOR - designed to provide a common API for basic regulator's + functions, like get/set Voltage or Current value, enable state, etc... + Note: + When enabling this, please read the description, found in the files: + - 'include/power/pmic.h' + - 'include/power/regulator.h' + - 'drivers/power/pmic/pmic-uclass.c' + - 'drivers/power/pmic/regulator-uclass.c' + It's important to call the device_bind() with the proper node offset, + when binding the regulator devices. The pmic_bind_childs() can be used + for this purpose if PMIC I/O driver is implemented or dm_scan_fdt_dev() + otherwise. Detailed information can be found in the header file. + +config SPL_DM_REGULATOR + bool "Enable regulators for SPL" + depends on DM_REGULATOR && SPL_POWER_SUPPORT + ---help--- + Regulators are seldom needed in SPL. Even if they are accessed, some + code space can be saved by accessing the PMIC registers directly. + Enable this option if you need regulators in SPL and can cope with + the extra code size. + +config REGULATOR_ACT8846 + bool "Enable driver for ACT8846 regulator" + depends on DM_REGULATOR && PMIC_ACT8846 + ---help--- + Enable support for the regulator functions of the ACT8846 PMIC. The + driver implements get/set api for the various BUCKS and LDOS supported + by the PMIC device. This driver is controlled by a device tree node + which includes voltage limits. + +config REGULATOR_AS3722 + bool "Enable driver for AS7322 regulator" + depends on DM_REGULATOR && PMIC_AS3722 + help + Enable support for the regulator functions of the AS3722. The + driver implements enable/disable for step-down bucks and LDOs, + but does not yet support change voltages. Currently this must be + done using direct register writes to the PMIC. + +config DM_REGULATOR_BD71837 + bool "Enable Driver Model for ROHM BD71837/BD71847 regulators" + depends on DM_REGULATOR && DM_PMIC_BD71837 + help + This config enables implementation of driver-model regulator uclass + features for regulators on ROHM BD71837 and BD71847 PMICs. + BD71837 contains 8 bucks and 7 LDOS. BD71847 is reduced version + containing 6 bucks and 6 LDOs. The driver implements get/set api for + value and enable. + +config SPL_DM_REGULATOR_BD71837 + bool "Enable Driver Model for ROHM BD71837/BD71847 regulators in SPL" + depends on DM_REGULATOR_BD71837 + help + This config enables implementation of driver-model regulator uclass + features for regulators on ROHM BD71837 and BD71847 in SPL. + +config DM_REGULATOR_DA9063 + bool "Enable Driver Model for REGULATOR DA9063" + depends on DM_REGULATOR && DM_PMIC_DA9063 + help + This config enables implementation of driver-model regulator uclass + features for REGULATOR DA9063. + The driver implements get/set api for value, enable and mode for all + regulators. It also implements the get/set api for current for the + buck regulators. + +config SPL_DM_REGULATOR_DA9063 + bool "Enable Driver Model for REGULATOR DA9063 in SPL" + depends on DM_REGULATOR && DM_PMIC_DA9063 && SPL + help + This config enables implementation of driver-model regulator uclass + features for REGULATOR DA9063. + The driver implements get/set api for value, enable and mode for all + regulators. It also implements the get/set api for current for the + buck regulators. + +config DM_REGULATOR_PFUZE100 + bool "Enable Driver Model for REGULATOR PFUZE100" + depends on DM_REGULATOR && DM_PMIC_PFUZE100 + ---help--- + This config enables implementation of driver-model regulator uclass + features for REGULATOR PFUZE100. The driver implements get/set api for: + value, enable and mode. + +config REGULATOR_PWM + bool "Enable driver for PWM regulators" + depends on DM_REGULATOR + ---help--- + Enable support for the PWM regulator functions which voltage are + controlled by PWM duty ratio. Some of Rockchip board using this kind + of regulator. The driver implements get/set api for the various BUCKS. + This driver is controlled by a device tree node + which includes voltage limits. + +config SPL_REGULATOR_PWM + bool "Enable Driver for PWM regulators in SPL" + depends on REGULATOR_PWM + help + This config enables implementation of driver-model regulator uclass + features for PWM regulators in SPL. + +config DM_REGULATOR_MAX77686 + bool "Enable Driver Model for REGULATOR MAX77686" + depends on DM_REGULATOR && DM_PMIC_MAX77686 + ---help--- + This config enables implementation of driver-model regulator uclass + features for REGULATOR MAX77686. The driver implements get/set api for: + value, enable and mode. + +config DM_REGULATOR_FAN53555 + bool "Enable Driver Model for REGULATOR FAN53555" + depends on DM_PMIC_FAN53555 + help + This config enables implementation of driver-model regulator + uclass features for the FAN53555 regulator. The FAN53555 is + a (family of) single-output regulators that supports + transitioning between two different output voltages based on + an voltage selection pin. + + The driver implements a get/set api for the voltage of the + 'normal mode' voltage only. Switching to 'suspend mode' + (i.e. the alternate voltage), disabling output via software, + or switching the mode is not supported by this driver (at + this time). + +config DM_REGULATOR_COMMON + bool + depends on DM_REGULATOR + +config SPL_DM_REGULATOR_COMMON + bool + depends on DM_REGULATOR + +config DM_REGULATOR_FIXED + bool "Enable Driver Model for REGULATOR Fixed value" + depends on DM_REGULATOR + select DM_REGULATOR_COMMON + ---help--- + This config enables implementation of driver-model regulator uclass + features for fixed value regulators. The driver implements get/set api + for enable and get only for voltage value. + +config SPL_DM_REGULATOR_FIXED + bool "Enable Driver Model for REGULATOR Fixed value in SPL" + depends on DM_REGULATOR_FIXED + select SPL_DM_REGULATOR_COMMON + ---help--- + This config enables implementation of driver-model regulator uclass + features for fixed value regulators in SPL. + +config DM_REGULATOR_GPIO + bool "Enable Driver Model for GPIO REGULATOR" + depends on DM_REGULATOR && DM_GPIO + select DM_REGULATOR_COMMON + ---help--- + This config enables implementation of driver-model regulator uclass + features for gpio regulators. The driver implements get/set for + voltage value. + +config SPL_DM_REGULATOR_GPIO + bool "Enable Driver Model for GPIO REGULATOR in SPL" + depends on DM_REGULATOR_GPIO && SPL_GPIO_SUPPORT + select SPL_DM_REGULATOR_COMMON + ---help--- + This config enables implementation of driver-model regulator uclass + features for gpio regulators in SPL. + +config REGULATOR_RK8XX + bool "Enable driver for RK8XX regulators" + depends on DM_REGULATOR && PMIC_RK8XX + ---help--- + Enable support for the regulator functions of the RK8XX PMIC. The + driver implements get/set api for the various BUCKS and LDOs supported + by the PMIC device. This driver is controlled by a device tree node + which includes voltage limits. + +config DM_REGULATOR_S2MPS11 + bool "Enable driver for S2MPS11 regulator" + depends on DM_REGULATOR && PMIC_S2MPS11 + ---help--- + This enables implementation of driver-model regulator uclass + features for REGULATOR S2MPS11. + The driver implements get/set api for: value and enable. + +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 + ---help--- + Enable the regulator driver for emulated Sandbox PMIC. + The emulated PMIC device depends on two drivers: + - sandbox PMIC I/O driver - implements dm pmic operations + - sandbox PMIC regulator driver - implements dm regulator operations + - sandbox PMIC i2c emul driver - emulates the PMIC's I2C transmission + + The regulator driver provides uclass operations for sandbox PMIC's + regulators. The driver implements get/set api for: voltage, current, + operation mode and enable state. + The driver supports LDO and BUCK regulators. + + The Sandbox PMIC info: + * I/O interface: + - I2C chip address: 0x40 + - first register address: 0x0 + - register count: 0x10 + * Adjustable outputs: + - 2x LDO + - 2x BUCK + - Each, with a different operating conditions (header). + * Reset values: + - set by i2c emul driver's probe() (defaults in header) + + A detailed information can be found in header: '<power/sandbox_pmic.h>' + Binding info: 'doc/device-tree-bindings/pmic/max77686.txt' + +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. + +config DM_REGULATOR_PALMAS + bool "Enable driver for PALMAS PMIC regulators" + depends on PMIC_PALMAS + ---help--- + This enables implementation of driver-model regulator uclass + features for REGULATOR PALMAS and the family of PALMAS PMICs. + The driver implements get/set api for: value and enable. + +config DM_REGULATOR_PBIAS + bool "Enable driver for PBIAS regulator" + depends on DM_REGULATOR + select REGMAP + select SYSCON + ---help--- + This enables implementation of driver-model regulator uclass + features for pseudo-regulator PBIAS found in the OMAP SOCs. + This pseudo-regulator is used to provide a BIAS voltage to MMC1 + signal pads and must be configured properly during a voltage switch. + Voltage switching is required by some operating modes of SDcards and + eMMC. + +config DM_REGULATOR_LP873X + bool "Enable driver for LP873X PMIC regulators" + depends on PMIC_LP873X + ---help--- + 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. + +config DM_REGULATOR_LP87565 + bool "Enable driver for LP87565 PMIC regulators" + depends on PMIC_LP87565 + ---help--- + This enables implementation of driver-model regulator uclass + features for REGULATOR LP87565 and the family of LP87565 PMICs. + 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_STM32_VREFBUF + bool "Enable driver for STMicroelectronics STM32 VREFBUF" + depends on DM_REGULATOR && (STM32H7 || ARCH_STM32MP) + help + This driver supports STMicroelectronics STM32 VREFBUF (voltage + reference buffer) which can be used as voltage reference for + internal ADCs, DACs and also for external components through + dedicated Vref+ pin. + +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. + +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 + ---help--- + Enable support for the regulator functions of the STPMIC1 PMIC. The + driver implements get/set api for the various BUCKS and LDOs supported + by the PMIC device. This driver is controlled by a device tree node + which includes voltage limits. + +config DM_REGULATOR_ANATOP + bool "Enable driver for ANATOP regulators" + depends on DM_REGULATOR + select REGMAP + select SYSCON + help + Enable support for the Freescale i.MX on-chip ANATOP LDO + regulators. It is recommended that this option be enabled on + i.MX6 platform. + +config SPL_DM_REGULATOR_STPMIC1 + bool "Enable driver for STPMIC1 regulators in SPL" + depends on SPL_DM_REGULATOR && PMIC_STPMIC1 + help + Enable support for the regulator functions of the STPMIC1 PMIC in SPL. + +config SPL_DM_REGULATOR_PALMAS + bool "Enable driver for PALMAS PMIC regulators" + depends on SPL_PMIC_PALMAS + help + This enables implementation of driver-model regulator uclass + features for REGULATOR PALMAS and the family of PALMAS PMICs. + The driver implements get/set api for: value and enable in SPL. + +config SPL_DM_REGULATOR_LP87565 + bool "Enable driver for LP87565 PMIC regulators" + depends on SPL_PMIC_LP87565 + help + This enables implementation of driver-model regulator uclass + features for REGULATOR LP87565 and the family of LP87565 PMICs. + 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 in SPL. + +config SPL_DM_REGULATOR_LP873X + bool "Enable driver for LP873X PMIC regulators" + depends on SPL_PMIC_LP873X + help + 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. + +config DM_REGULATOR_SCMI + bool "Enable driver for SCMI voltage domain regulators" + depends on DM_REGULATOR + select SCMI_AGENT + help + Enable this option if you want to support regulators exposed through + the SCMI voltage domain protocol by a SCMI server. diff --git a/roms/u-boot/drivers/power/regulator/Makefile b/roms/u-boot/drivers/power/regulator/Makefile new file mode 100644 index 000000000..677134c82 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/Makefile @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2015 Samsung Electronics +# Przemyslaw Marczak <p.marczak@samsung.com> +# + +obj-$(CONFIG_$(SPL_)DM_REGULATOR) += regulator-uclass.o +obj-$(CONFIG_REGULATOR_ACT8846) += act8846.o +obj-$(CONFIG_REGULATOR_AS3722) += as3722_regulator.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_DA9063) += da9063.o +obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o +obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_BD71837) += bd71837.o +obj-$(CONFIG_$(SPL_)REGULATOR_PWM) += pwm_regulator.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_FAN53555) += fan53555.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_COMMON) += regulator_common.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_FIXED) += fixed.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_GPIO) += gpio-regulator.o +obj-$(CONFIG_REGULATOR_RK8XX) += rk8xx.o +obj-$(CONFIG_DM_REGULATOR_S2MPS11) += s2mps11_regulator.o +obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o +obj-$(CONFIG_DM_REGULATOR_SANDBOX) += sandbox.o +obj-$(CONFIG_REGULATOR_TPS65090) += tps65090_regulator.o +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_$(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 +obj-$(CONFIG_DM_REGULATOR_SCMI) += scmi_regulator.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_ANATOP) += anatop_regulator.o diff --git a/roms/u-boot/drivers/power/regulator/act8846.c b/roms/u-boot/drivers/power/regulator/act8846.c new file mode 100644 index 000000000..bdce97365 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/act8846.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * Based on Rockchip's drivers/power/pmic/pmic_act8846.c: + * Copyright (C) 2012 rockchips + * zyw <zyw@rock-chips.com> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <power/act8846_pmic.h> +#include <power/pmic.h> +#include <power/regulator.h> + +static const u16 voltage_map[] = { + 600, 625, 650, 675, 700, 725, 750, 775, + 800, 825, 850, 875, 900, 925, 950, 975, + 1000, 1025, 1050, 1075, 1100, 1125, 1150, 1175, + 1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550, + 1600, 1650, 1700, 1750, 1800, 1850, 1900, 1950, + 2000, 2050, 2100, 2150, 2200, 2250, 2300, 2350, + 2400, 2500, 2600, 2700, 2800, 2900, 3000, 3100, + 3200, 3300, 3400, 3500, 3600, 3700, 3800, 3900, +}; + +enum { + REG_SYS0, + REG_SYS1, + REG1_VOL = 0x10, + REG1_CTL = 0X11, + REG2_VOL0 = 0x20, + REG2_VOL1, + REG2_CTL, + REG3_VOL0 = 0x30, + REG3_VOL1, + REG3_CTL, + REG4_VOL0 = 0x40, + REG4_VOL1, + REG4_CTL, + REG5_VOL = 0x50, + REG5_CTL, + REG6_VOL = 0X58, + REG6_CTL, + REG7_VOL = 0x60, + REG7_CTL, + REG8_VOL = 0x68, + REG8_CTL, + REG9_VOL = 0x70, + REG9_CTL, + REG10_VOL = 0x80, + REG10_CTL, + REG11_VOL = 0x90, + REG11_CTL, + REG12_VOL = 0xa0, + REG12_CTL, + REG13 = 0xb1, +}; + +static const u8 addr_vol[] = { + 0, REG1_VOL, REG2_VOL0, REG3_VOL0, REG4_VOL0, + REG5_VOL, REG6_VOL, REG7_VOL, REG8_VOL, REG9_VOL, + REG10_VOL, REG11_VOL, REG12_VOL, +}; + +static const u8 addr_ctl[] = { + 0, REG1_CTL, REG2_CTL, REG3_CTL, REG4_CTL, + REG5_CTL, REG6_CTL, REG7_CTL, REG8_CTL, REG9_CTL, + REG10_CTL, REG11_CTL, REG12_CTL, +}; + +static int check_volt_table(const u16 *volt_table, int uvolt) +{ + int i; + + for (i = VOL_MIN_IDX; i < VOL_MAX_IDX; i++) { + if (uvolt <= (volt_table[i] * 1000)) + return i; + } + return -EINVAL; +} + +static int reg_get_value(struct udevice *dev) +{ + int reg = dev->driver_data; + int ret; + + ret = pmic_reg_read(dev->parent, addr_vol[reg]); + if (ret < 0) + return ret; + + return voltage_map[ret & LDO_VOL_MASK] * 1000; +} + +static int reg_set_value(struct udevice *dev, int uvolt) +{ + int reg = dev->driver_data; + int val; + + val = check_volt_table(voltage_map, uvolt); + if (val < 0) + return val; + + return pmic_clrsetbits(dev->parent, addr_vol[reg], LDO_VOL_MASK, val); +} + +static int reg_set_enable(struct udevice *dev, bool enable) +{ + int reg = dev->driver_data; + + return pmic_clrsetbits(dev->parent, addr_ctl[reg], LDO_EN_MASK, + enable ? LDO_EN_MASK : 0); +} + +static int reg_get_enable(struct udevice *dev) +{ + int reg = dev->driver_data; + int ret; + + ret = pmic_reg_read(dev->parent, addr_ctl[reg]); + if (ret < 0) + return ret; + + return ret & LDO_EN_MASK ? true : false; +} + +static int act8846_reg_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + int reg = dev->driver_data; + + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = reg <= 4 ? REGULATOR_TYPE_BUCK : REGULATOR_TYPE_LDO; + uc_pdata->mode_count = 0; + + return 0; +} + +static const struct dm_regulator_ops act8846_reg_ops = { + .get_value = reg_get_value, + .set_value = reg_set_value, + .get_enable = reg_get_enable, + .set_enable = reg_set_enable, +}; + +U_BOOT_DRIVER(act8846_buck) = { + .name = "act8846_reg", + .id = UCLASS_REGULATOR, + .ops = &act8846_reg_ops, + .probe = act8846_reg_probe, +}; diff --git a/roms/u-boot/drivers/power/regulator/anatop_regulator.c b/roms/u-boot/drivers/power/regulator/anatop_regulator.c new file mode 100644 index 000000000..096a1565d --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/anatop_regulator.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2021 Linaro + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <regmap.h> +#include <syscon.h> +#include <dm/device-internal.h> +#include <dm/device_compat.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/ioport.h> +#include <power/pmic.h> +#include <power/regulator.h> + +#define LDO_RAMP_UP_UNIT_IN_CYCLES 64 /* 64 cycles per step */ +#define LDO_RAMP_UP_FREQ_IN_MHZ 24 /* cycle based on 24M OSC */ + +#define LDO_POWER_GATE 0x00 +#define LDO_FET_FULL_ON 0x1f + +#define BIT_WIDTH_MAX 32 + +#define ANATOP_REGULATOR_STEP 25000 +#define MIN_DROPOUT_UV 125000 + +struct anatop_regulator { + const char *name; + struct regmap *regmap; + struct udevice *supply; + u32 control_reg; + u32 vol_bit_shift; + u32 vol_bit_width; + u32 min_bit_val; + u32 min_voltage; + u32 max_voltage; + u32 delay_reg; + u32 delay_bit_shift; + u32 delay_bit_width; +}; + +static u32 anatop_get_bits(struct udevice *dev, u32 addr, int bit_shift, + int bit_width) +{ + const struct anatop_regulator *anatop_reg = dev_get_plat(dev); + int err; + u32 val, mask; + + if (bit_width == BIT_WIDTH_MAX) + mask = ~0; + else + mask = (1 << bit_width) - 1; + + err = regmap_read(anatop_reg->regmap, addr, &val); + if (err) { + dev_dbg(dev, "cannot read reg (%d)\n", err); + return err; + } + + val = (val >> bit_shift) & mask; + + return val; +} + +static int anatop_set_bits(struct udevice *dev, u32 addr, int bit_shift, + int bit_width, u32 data) +{ + const struct anatop_regulator *anatop_reg = dev_get_plat(dev); + int err; + u32 val, mask; + + if (bit_width == 32) + mask = ~0; + else + mask = (1 << bit_width) - 1; + + err = regmap_read(anatop_reg->regmap, addr, &val); + if (err) { + dev_dbg(dev, "cannot read reg (%d)\n", err); + return err; + } + val = val & ~(mask << bit_shift); + err = regmap_write(anatop_reg->regmap, + addr, (data << bit_shift) | val); + if (err) { + dev_dbg(dev, "cannot write reg (%d)\n", err); + return err; + } + + return 0; +} + +static int anatop_get_voltage(struct udevice *dev) +{ + const struct anatop_regulator *anatop_reg = dev_get_plat(dev); + u32 sel; + u32 val; + + if (!anatop_reg->control_reg) + return -ENOSYS; + + val = anatop_get_bits(dev, + anatop_reg->control_reg, + anatop_reg->vol_bit_shift, + anatop_reg->vol_bit_width); + + sel = val - anatop_reg->min_bit_val; + + return sel * ANATOP_REGULATOR_STEP + anatop_reg->min_voltage; +} + +static int anatop_set_voltage(struct udevice *dev, int uV) +{ + const struct anatop_regulator *anatop_reg = dev_get_plat(dev); + u32 val; + u32 sel; + int ret; + + dev_dbg(dev, "uv %d, min %d, max %d\n", uV, anatop_reg->min_voltage, + anatop_reg->max_voltage); + + if (uV < anatop_reg->min_voltage) + return -EINVAL; + + if (!anatop_reg->control_reg) + return -ENOSYS; + + sel = DIV_ROUND_UP(uV - anatop_reg->min_voltage, + ANATOP_REGULATOR_STEP); + if (sel * ANATOP_REGULATOR_STEP + anatop_reg->min_voltage > + anatop_reg->max_voltage) + return -EINVAL; + val = anatop_reg->min_bit_val + sel; + dev_dbg(dev, "calculated val %d\n", val); + + if (anatop_reg->supply) { + ret = regulator_set_value(anatop_reg->supply, + uV + MIN_DROPOUT_UV); + if (ret) + return ret; + } + + ret = anatop_set_bits(dev, + anatop_reg->control_reg, + anatop_reg->vol_bit_shift, + anatop_reg->vol_bit_width, + val); + + return ret; +} + +static const struct dm_regulator_ops anatop_regulator_ops = { + .set_value = anatop_set_voltage, + .get_value = anatop_get_voltage, +}; + +static int anatop_regulator_probe(struct udevice *dev) +{ + struct anatop_regulator *anatop_reg; + struct dm_regulator_uclass_plat *uc_pdata; + struct udevice *syscon; + int ret = 0; + u32 val; + + anatop_reg = dev_get_plat(dev); + uc_pdata = dev_get_uclass_plat(dev); + + anatop_reg->name = ofnode_read_string(dev_ofnode(dev), + "regulator-name"); + if (!anatop_reg->name) + return log_msg_ret("regulator-name", -EINVAL); + + ret = device_get_supply_regulator(dev, "vin-supply", + &anatop_reg->supply); + if (ret != -ENODEV) { + if (ret) + return log_msg_ret("get vin-supply", ret); + + ret = regulator_set_enable(anatop_reg->supply, true); + if (ret) + return ret; + } + + ret = dev_read_u32(dev, + "anatop-reg-offset", + &anatop_reg->control_reg); + if (ret) + return log_msg_ret("anatop-reg-offset", ret); + + ret = dev_read_u32(dev, + "anatop-vol-bit-width", + &anatop_reg->vol_bit_width); + if (ret) + return log_msg_ret("anatop-vol-bit-width", ret); + + ret = dev_read_u32(dev, + "anatop-vol-bit-shift", + &anatop_reg->vol_bit_shift); + if (ret) + return log_msg_ret("anatop-vol-bit-shift", ret); + + ret = dev_read_u32(dev, + "anatop-min-bit-val", + &anatop_reg->min_bit_val); + if (ret) + return log_msg_ret("anatop-min-bit-val", ret); + + ret = dev_read_u32(dev, + "anatop-min-voltage", + &anatop_reg->min_voltage); + if (ret) + return log_msg_ret("anatop-min-voltage", ret); + + ret = dev_read_u32(dev, + "anatop-max-voltage", + &anatop_reg->max_voltage); + if (ret) + return log_msg_ret("anatop-max-voltage", ret); + + /* read LDO ramp up setting, only for core reg */ + dev_read_u32(dev, "anatop-delay-reg-offset", + &anatop_reg->delay_reg); + dev_read_u32(dev, "anatop-delay-bit-width", + &anatop_reg->delay_bit_width); + dev_read_u32(dev, "anatop-delay-bit-shift", + &anatop_reg->delay_bit_shift); + + syscon = dev_get_parent(dev); + if (!syscon) { + dev_dbg(dev, "unable to find syscon device\n"); + return -ENOENT; + } + + anatop_reg->regmap = syscon_get_regmap(syscon); + if (IS_ERR(anatop_reg->regmap)) { + dev_dbg(dev, "unable to find regmap (%ld)\n", + PTR_ERR(anatop_reg->regmap)); + return -ENOENT; + } + + /* check whether need to care about LDO ramp up speed */ + if (anatop_reg->delay_bit_width) { + /* + * the delay for LDO ramp up time is + * based on the register setting, we need + * to calculate how many steps LDO need to + * ramp up, and how much delay needed. (us) + */ + val = anatop_get_bits(dev, + anatop_reg->delay_reg, + anatop_reg->delay_bit_shift, + anatop_reg->delay_bit_width); + uc_pdata->ramp_delay = (LDO_RAMP_UP_UNIT_IN_CYCLES << val) + / LDO_RAMP_UP_FREQ_IN_MHZ + 1; + } + + return 0; +} + +static const struct udevice_id of_anatop_regulator_match_tbl[] = { + { .compatible = "fsl,anatop-regulator", }, + { /* end */ } +}; + +U_BOOT_DRIVER(anatop_regulator) = { + .name = "anatop_regulator", + .id = UCLASS_REGULATOR, + .ops = &anatop_regulator_ops, + .of_match = of_anatop_regulator_match_tbl, + .plat_auto = sizeof(struct anatop_regulator), + .probe = anatop_regulator_probe, +}; diff --git a/roms/u-boot/drivers/power/regulator/as3722_regulator.c b/roms/u-boot/drivers/power/regulator/as3722_regulator.c new file mode 100644 index 000000000..ec0776b44 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/as3722_regulator.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * Placeholder regulator driver for as3722. + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <power/as3722.h> +#include <power/pmic.h> +#include <power/regulator.h> + +#define AS3722_LDO_CONTROL0_MAX_INDEX 7 + +static int stepdown_get_value(struct udevice *dev) +{ + return -ENOSYS; +} + +static int stepdown_set_value(struct udevice *dev, int uvolt) +{ + return -ENOSYS; +} + +static int stepdown_set_enable(struct udevice *dev, bool enable) +{ + struct udevice *pmic = dev_get_parent(dev); + int sd = dev->driver_data; + int ret; + + ret = pmic_clrsetbits(pmic, AS3722_SD_CONTROL, 0, 1 << sd); + if (ret < 0) { + debug("%s: failed to write SD control register: %d", __func__, + ret); + return ret; + } + + return 0; +} + +static int stepdown_get_enable(struct udevice *dev) +{ + struct udevice *pmic = dev_get_parent(dev); + int sd = dev->driver_data; + int ret; + + ret = pmic_reg_read(pmic, AS3722_SD_CONTROL); + if (ret < 0) { + debug("%s: failed to read SD control register: %d", __func__, + ret); + return ret; + } + + return ret & (1 << sd) ? true : false; +} + +static int ldo_get_value(struct udevice *dev) +{ + return -ENOSYS; +} + +static int ldo_set_value(struct udevice *dev, int uvolt) +{ + return -ENOSYS; +} + +static int ldo_set_enable(struct udevice *dev, bool enable) +{ + struct udevice *pmic = dev_get_parent(dev); + u8 ctrl_reg = AS3722_LDO_CONTROL0; + int ldo = dev->driver_data; + int ret; + + if (ldo > AS3722_LDO_CONTROL0_MAX_INDEX) { + ctrl_reg = AS3722_LDO_CONTROL1; + ldo -= 8; + } + + ret = pmic_clrsetbits(pmic, ctrl_reg, !enable << ldo, enable << ldo); + if (ret < 0) { + debug("%s: failed to write LDO control register: %d", __func__, + ret); + return ret; + } + + return 0; +} + +static int ldo_get_enable(struct udevice *dev) +{ + struct udevice *pmic = dev_get_parent(dev); + u8 ctrl_reg = AS3722_LDO_CONTROL0; + int ldo = dev->driver_data; + int ret; + + if (ldo > AS3722_LDO_CONTROL0_MAX_INDEX) { + ctrl_reg = AS3722_LDO_CONTROL1; + ldo -= 8; + } + + ret = pmic_reg_read(pmic, ctrl_reg); + if (ret < 0) { + debug("%s: failed to read SD control register: %d", __func__, + ret); + return ret; + } + + return ret & (1 << ldo) ? true : false; +} + +static int as3722_stepdown_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_BUCK; + + return 0; +} + +static int as3722_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_LDO; + + return 0; +} + +static const struct dm_regulator_ops as3722_stepdown_ops = { + .get_value = stepdown_get_value, + .set_value = stepdown_set_value, + .get_enable = stepdown_get_enable, + .set_enable = stepdown_set_enable, +}; + +static const struct dm_regulator_ops as3722_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(as3722_stepdown) = { + .name = "as3722_stepdown", + .id = UCLASS_REGULATOR, + .ops = &as3722_stepdown_ops, + .probe = as3722_stepdown_probe, +}; + +U_BOOT_DRIVER(as3722_ldo) = { + .name = "as3722_ldo", + .id = UCLASS_REGULATOR, + .ops = &as3722_ldo_ops, + .probe = as3722_ldo_probe, +}; diff --git a/roms/u-boot/drivers/power/regulator/bd71837.c b/roms/u-boot/drivers/power/regulator/bd71837.c new file mode 100644 index 000000000..74011d629 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/bd71837.c @@ -0,0 +1,470 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 ROHM Semiconductors + * + * ROHM BD71837 regulator driver + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <linux/bitops.h> +#include <power/bd71837.h> +#include <power/pmic.h> +#include <power/regulator.h> + +#define HW_STATE_CONTROL 0 +#define DEBUG + +/** + * struct bd71837_vrange - describe linear range of voltages + * + * @min_volt: smallest voltage in range + * @step: how much voltage changes at each selector step + * @min_sel: smallest selector in the range + * @max_sel: maximum selector in the range + * @rangeval: register value used to select this range if selectible + * ranges are supported + */ +struct bd71837_vrange { + unsigned int min_volt; + unsigned int step; + u8 min_sel; + u8 max_sel; + u8 rangeval; +}; + +/** + * struct bd71837_plat - describe regulator control registers + * + * @name: name of the regulator. Used for matching the dt-entry + * @enable_reg: register address used to enable/disable regulator + * @enablemask: register mask used to enable/disable regulator + * @volt_reg: register address used to configure regulator voltage + * @volt_mask: register mask used to configure regulator voltage + * @ranges: pointer to ranges of regulator voltages and matching register + * values + * @numranges: number of voltage ranges pointed by ranges + * @rangemask: mask for selecting used ranges if multiple ranges are supported + * @sel_mask: bit to toggle in order to transfer the register control to SW + * @dvs: whether the voltage can be changed when regulator is enabled + */ +struct bd71837_plat { + const char *name; + u8 enable_reg; + u8 enablemask; + u8 volt_reg; + u8 volt_mask; + struct bd71837_vrange *ranges; + unsigned int numranges; + u8 rangemask; + u8 sel_mask; + bool dvs; +}; + +#define BD_RANGE(_min, _vstep, _sel_low, _sel_hi, _range_sel) \ +{ \ + .min_volt = (_min), .step = (_vstep), .min_sel = (_sel_low), \ + .max_sel = (_sel_hi), .rangeval = (_range_sel) \ +} + +#define BD_DATA(_name, enreg, enmask, vreg, vmask, _range, rmask, _dvs, sel) \ +{ \ + .name = (_name), .enable_reg = (enreg), .enablemask = (enmask), \ + .volt_reg = (vreg), .volt_mask = (vmask), .ranges = (_range), \ + .numranges = ARRAY_SIZE(_range), .rangemask = (rmask), .dvs = (_dvs), \ + .sel_mask = (sel) \ +} + +static struct bd71837_vrange dvs_buck_vranges[] = { + BD_RANGE(700000, 10000, 0, 0x3c, 0), + BD_RANGE(1300000, 0, 0x3d, 0x3f, 0), +}; + +static struct bd71837_vrange bd71847_buck3_vranges[] = { + BD_RANGE(700000, 100000, 0x00, 0x03, 0), + BD_RANGE(1050000, 50000, 0x04, 0x05, 0), + BD_RANGE(1200000, 150000, 0x06, 0x07, 0), + BD_RANGE(550000, 50000, 0x0, 0x7, 0x40), + BD_RANGE(675000, 100000, 0x0, 0x3, 0x80), + BD_RANGE(1025000, 50000, 0x4, 0x5, 0x80), + BD_RANGE(1175000, 150000, 0x6, 0x7, 0x80), +}; + +static struct bd71837_vrange bd71847_buck4_vranges[] = { + BD_RANGE(3000000, 100000, 0x00, 0x03, 0), + BD_RANGE(2600000, 100000, 0x00, 0x03, 40), +}; + +static struct bd71837_vrange bd71837_buck5_vranges[] = { + BD_RANGE(700000, 100000, 0, 0x3, 0), + BD_RANGE(1050000, 50000, 0x04, 0x05, 0), + BD_RANGE(1200000, 150000, 0x06, 0x07, 0), + BD_RANGE(675000, 100000, 0x0, 0x3, 0x80), + BD_RANGE(1025000, 50000, 0x04, 0x05, 0x80), + BD_RANGE(1175000, 150000, 0x06, 0x07, 0x80), +}; + +static struct bd71837_vrange bd71837_buck6_vranges[] = { + BD_RANGE(3000000, 100000, 0x00, 0x03, 0), +}; + +static struct bd71837_vrange nodvs_buck3_vranges[] = { + BD_RANGE(1605000, 90000, 0, 1, 0), + BD_RANGE(1755000, 45000, 2, 4, 0), + BD_RANGE(1905000, 45000, 5, 7, 0), +}; + +static struct bd71837_vrange nodvs_buck4_vranges[] = { + BD_RANGE(800000, 10000, 0x00, 0x3C, 0), +}; + +static struct bd71837_vrange ldo1_vranges[] = { + BD_RANGE(3000000, 100000, 0x00, 0x03, 0), + BD_RANGE(1600000, 100000, 0x00, 0x03, 0x20), +}; + +static struct bd71837_vrange ldo2_vranges[] = { + BD_RANGE(900000, 0, 0, 0, 0), + BD_RANGE(800000, 0, 1, 1, 0), +}; + +static struct bd71837_vrange ldo3_vranges[] = { + BD_RANGE(1800000, 100000, 0x00, 0x0f, 0), +}; + +static struct bd71837_vrange ldo4_vranges[] = { + BD_RANGE(900000, 100000, 0x00, 0x09, 0), +}; + +static struct bd71837_vrange bd71837_ldo5_vranges[] = { + BD_RANGE(1800000, 100000, 0x00, 0x0f, 0), +}; + +static struct bd71837_vrange bd71847_ldo5_vranges[] = { + BD_RANGE(1800000, 100000, 0x00, 0x0f, 0), + BD_RANGE(800000, 100000, 0x00, 0x0f, 0x20), +}; + +static struct bd71837_vrange ldo6_vranges[] = { + BD_RANGE(900000, 100000, 0x00, 0x09, 0), +}; + +static struct bd71837_vrange ldo7_vranges[] = { + BD_RANGE(1800000, 100000, 0x00, 0x0f, 0), +}; + +/* + * We use enable mask 'HW_STATE_CONTROL' to indicate that this regulator + * must not be enabled or disabled by SW. The typical use-case for BD71837 + * is powering NXP i.MX8. In this use-case we (for now) only allow control + * for BUCK3 and BUCK4 which are not boot critical. + */ +static struct bd71837_plat bd71837_reg_data[] = { +/* Bucks 1-4 which support dynamic voltage scaling */ + BD_DATA("BUCK1", BD718XX_BUCK1_CTRL, HW_STATE_CONTROL, + BD718XX_BUCK1_VOLT_RUN, DVS_BUCK_RUN_MASK, dvs_buck_vranges, 0, + true, BD718XX_BUCK_SEL), + BD_DATA("BUCK2", BD718XX_BUCK2_CTRL, HW_STATE_CONTROL, + BD718XX_BUCK2_VOLT_RUN, DVS_BUCK_RUN_MASK, dvs_buck_vranges, 0, + true, BD718XX_BUCK_SEL), + BD_DATA("BUCK3", BD71837_BUCK3_CTRL, BD718XX_BUCK_EN, + BD71837_BUCK3_VOLT_RUN, DVS_BUCK_RUN_MASK, dvs_buck_vranges, 0, + true, BD718XX_BUCK_SEL), + BD_DATA("BUCK4", BD71837_BUCK4_CTRL, BD718XX_BUCK_EN, + BD71837_BUCK4_VOLT_RUN, DVS_BUCK_RUN_MASK, dvs_buck_vranges, 0, + true, BD718XX_BUCK_SEL), +/* Bucks 5-8 which do not support dynamic voltage scaling */ + BD_DATA("BUCK5", BD718XX_1ST_NODVS_BUCK_CTRL, HW_STATE_CONTROL, + BD718XX_1ST_NODVS_BUCK_VOLT, BD718XX_1ST_NODVS_BUCK_MASK, + bd71837_buck5_vranges, 0x80, false, BD718XX_BUCK_SEL), + BD_DATA("BUCK6", BD718XX_2ND_NODVS_BUCK_CTRL, HW_STATE_CONTROL, + BD718XX_2ND_NODVS_BUCK_VOLT, BD71837_BUCK6_MASK, + bd71837_buck6_vranges, 0, false, BD718XX_BUCK_SEL), + BD_DATA("BUCK7", BD718XX_3RD_NODVS_BUCK_CTRL, HW_STATE_CONTROL, + BD718XX_3RD_NODVS_BUCK_VOLT, BD718XX_3RD_NODVS_BUCK_MASK, + nodvs_buck3_vranges, 0, false, BD718XX_BUCK_SEL), + BD_DATA("BUCK8", BD718XX_4TH_NODVS_BUCK_CTRL, HW_STATE_CONTROL, + BD718XX_4TH_NODVS_BUCK_VOLT, BD718XX_4TH_NODVS_BUCK_MASK, + nodvs_buck4_vranges, 0, false, BD718XX_BUCK_SEL), +/* LDOs */ + BD_DATA("LDO1", BD718XX_LDO1_VOLT, HW_STATE_CONTROL, BD718XX_LDO1_VOLT, + BD718XX_LDO1_MASK, ldo1_vranges, 0x20, false, BD718XX_LDO_SEL), + BD_DATA("LDO2", BD718XX_LDO2_VOLT, HW_STATE_CONTROL, BD718XX_LDO2_VOLT, + BD718XX_LDO2_MASK, ldo2_vranges, 0, false, BD718XX_LDO_SEL), + BD_DATA("LDO3", BD718XX_LDO3_VOLT, HW_STATE_CONTROL, BD718XX_LDO3_VOLT, + BD718XX_LDO3_MASK, ldo3_vranges, 0, false, BD718XX_LDO_SEL), + BD_DATA("LDO4", BD718XX_LDO4_VOLT, HW_STATE_CONTROL, BD718XX_LDO4_VOLT, + BD718XX_LDO4_MASK, ldo4_vranges, 0, false, BD718XX_LDO_SEL), + BD_DATA("LDO5", BD718XX_LDO5_VOLT, HW_STATE_CONTROL, BD718XX_LDO5_VOLT, + BD71837_LDO5_MASK, bd71837_ldo5_vranges, 0, false, + BD718XX_LDO_SEL), + BD_DATA("LDO6", BD718XX_LDO6_VOLT, HW_STATE_CONTROL, BD718XX_LDO6_VOLT, + BD718XX_LDO6_MASK, ldo6_vranges, 0, false, BD718XX_LDO_SEL), + BD_DATA("LDO7", BD71837_LDO7_VOLT, HW_STATE_CONTROL, BD71837_LDO7_VOLT, + BD71837_LDO7_MASK, ldo7_vranges, 0, false, BD718XX_LDO_SEL), +}; + +static struct bd71837_plat bd71847_reg_data[] = { +/* Bucks 1 and 2 which support dynamic voltage scaling */ + BD_DATA("BUCK1", BD718XX_BUCK1_CTRL, HW_STATE_CONTROL, + BD718XX_BUCK1_VOLT_RUN, DVS_BUCK_RUN_MASK, dvs_buck_vranges, 0, + true, BD718XX_BUCK_SEL), + BD_DATA("BUCK2", BD718XX_BUCK2_CTRL, HW_STATE_CONTROL, + BD718XX_BUCK2_VOLT_RUN, DVS_BUCK_RUN_MASK, dvs_buck_vranges, 0, + true, BD718XX_BUCK_SEL), +/* Bucks 3-6 which do not support dynamic voltage scaling */ + BD_DATA("BUCK3", BD718XX_1ST_NODVS_BUCK_CTRL, HW_STATE_CONTROL, + BD718XX_1ST_NODVS_BUCK_VOLT, BD718XX_1ST_NODVS_BUCK_MASK, + bd71847_buck3_vranges, 0xc0, false, BD718XX_BUCK_SEL), + BD_DATA("BUCK4", BD718XX_2ND_NODVS_BUCK_CTRL, HW_STATE_CONTROL, + BD718XX_2ND_NODVS_BUCK_VOLT, BD71837_BUCK6_MASK, + bd71847_buck4_vranges, 0x40, false, BD718XX_BUCK_SEL), + BD_DATA("BUCK5", BD718XX_3RD_NODVS_BUCK_CTRL, HW_STATE_CONTROL, + BD718XX_3RD_NODVS_BUCK_VOLT, BD718XX_3RD_NODVS_BUCK_MASK, + nodvs_buck3_vranges, 0, false, BD718XX_BUCK_SEL), + BD_DATA("BUCK6", BD718XX_4TH_NODVS_BUCK_CTRL, HW_STATE_CONTROL, + BD718XX_4TH_NODVS_BUCK_VOLT, BD718XX_4TH_NODVS_BUCK_MASK, + nodvs_buck4_vranges, 0, false, BD718XX_BUCK_SEL), +/* LDOs */ + BD_DATA("LDO1", BD718XX_LDO1_VOLT, HW_STATE_CONTROL, BD718XX_LDO1_VOLT, + BD718XX_LDO1_MASK, ldo1_vranges, 0x20, false, BD718XX_LDO_SEL), + BD_DATA("LDO2", BD718XX_LDO2_VOLT, HW_STATE_CONTROL, BD718XX_LDO2_VOLT, + BD718XX_LDO2_MASK, ldo2_vranges, 0, false, BD718XX_LDO_SEL), + BD_DATA("LDO3", BD718XX_LDO3_VOLT, HW_STATE_CONTROL, BD718XX_LDO3_VOLT, + BD718XX_LDO3_MASK, ldo3_vranges, 0, false, BD718XX_LDO_SEL), + BD_DATA("LDO4", BD718XX_LDO4_VOLT, HW_STATE_CONTROL, BD718XX_LDO4_VOLT, + BD718XX_LDO4_MASK, ldo4_vranges, 0, false, BD718XX_LDO_SEL), + BD_DATA("LDO5", BD718XX_LDO5_VOLT, HW_STATE_CONTROL, BD718XX_LDO5_VOLT, + BD71847_LDO5_MASK, bd71847_ldo5_vranges, 0x20, false, + BD718XX_LDO_SEL), + BD_DATA("LDO6", BD718XX_LDO6_VOLT, HW_STATE_CONTROL, BD718XX_LDO6_VOLT, + BD718XX_LDO6_MASK, ldo6_vranges, 0, false, BD718XX_LDO_SEL), +}; + +static int vrange_find_value(struct bd71837_vrange *r, unsigned int sel, + unsigned int *val) +{ + if (!val || sel < r->min_sel || sel > r->max_sel) + return -EINVAL; + + *val = r->min_volt + r->step * (sel - r->min_sel); + return 0; +} + +static int vrange_find_selector(struct bd71837_vrange *r, int val, + unsigned int *sel) +{ + int ret = -EINVAL; + int num_vals = r->max_sel - r->min_sel + 1; + + if (val >= r->min_volt && + val <= r->min_volt + r->step * (num_vals - 1)) { + if (r->step) { + *sel = r->min_sel + ((val - r->min_volt) / r->step); + ret = 0; + } else { + *sel = r->min_sel; + ret = 0; + } + } + return ret; +} + +static int bd71837_get_enable(struct udevice *dev) +{ + int val; + struct bd71837_plat *plat = dev_get_plat(dev); + + /* + * boot critical regulators on bd71837 must not be controlled by sw + * due to the 'feature' which leaves power rails down if bd71837 is + * reseted to snvs state. hence we can't get the state here. + * + * if we are alive it means we probably are on run state and + * if the regulator can't be controlled we can assume it is + * enabled. + */ + if (plat->enablemask == HW_STATE_CONTROL) + return 1; + + val = pmic_reg_read(dev->parent, plat->enable_reg); + if (val < 0) + return val; + + return (val & plat->enablemask); +} + +static int bd71837_set_enable(struct udevice *dev, bool enable) +{ + int val = 0; + struct bd71837_plat *plat = dev_get_plat(dev); + + /* + * boot critical regulators on bd71837 must not be controlled by sw + * due to the 'feature' which leaves power rails down if bd71837 is + * reseted to snvs state. Hence we can't set the state here. + */ + if (plat->enablemask == HW_STATE_CONTROL) + return -EINVAL; + + if (enable) + val = plat->enablemask; + + return pmic_clrsetbits(dev->parent, plat->enable_reg, plat->enablemask, + val); +} + +static int bd71837_set_value(struct udevice *dev, int uvolt) +{ + unsigned int sel; + unsigned int range; + int i; + int found = 0; + struct bd71837_plat *plat = dev_get_plat(dev); + + /* + * An under/overshooting may occur if voltage is changed for other + * regulators but buck 1,2,3 or 4 when regulator is enabled. Prevent + * change to protect the HW + */ + if (!plat->dvs) + if (bd71837_get_enable(dev)) { + pr_err("Only DVS bucks can be changed when enabled\n"); + return -EINVAL; + } + + for (i = 0; i < plat->numranges; i++) { + struct bd71837_vrange *r = &plat->ranges[i]; + + found = !vrange_find_selector(r, uvolt, &sel); + if (found) { + unsigned int tmp; + + /* + * We require exactly the requested value to be + * supported - this can be changed later if needed + */ + range = r->rangeval; + found = !vrange_find_value(r, sel, &tmp); + if (found && tmp == uvolt) + break; + found = 0; + } + } + + if (!found) + return -EINVAL; + + sel <<= ffs(plat->volt_mask) - 1; + + if (plat->rangemask) + sel |= range; + + return pmic_clrsetbits(dev->parent, plat->volt_reg, plat->volt_mask | + plat->rangemask, sel); +} + +static int bd71837_get_value(struct udevice *dev) +{ + unsigned int reg, range; + unsigned int tmp; + struct bd71837_plat *plat = dev_get_plat(dev); + int i; + + reg = pmic_reg_read(dev->parent, plat->volt_reg); + if (((int)reg) < 0) + return reg; + + range = reg & plat->rangemask; + + reg &= plat->volt_mask; + reg >>= ffs(plat->volt_mask) - 1; + + for (i = 0; i < plat->numranges; i++) { + struct bd71837_vrange *r = &plat->ranges[i]; + + if (plat->rangemask && ((plat->rangemask & range) != + r->rangeval)) + continue; + + if (!vrange_find_value(r, reg, &tmp)) + return tmp; + } + + pr_err("Unknown voltage value read from pmic\n"); + + return -EINVAL; +} + +static int bd71837_regulator_probe(struct udevice *dev) +{ + struct bd71837_plat *plat = dev_get_plat(dev); + int i, ret; + struct dm_regulator_uclass_plat *uc_pdata; + int type; + struct bd71837_plat *init_data; + int data_amnt; + + type = dev_get_driver_data(dev_get_parent(dev)); + + switch (type) { + case ROHM_CHIP_TYPE_BD71837: + init_data = bd71837_reg_data; + data_amnt = ARRAY_SIZE(bd71837_reg_data); + break; + case ROHM_CHIP_TYPE_BD71847: + init_data = bd71847_reg_data; + data_amnt = ARRAY_SIZE(bd71847_reg_data); + break; + default: + debug("Unknown PMIC type\n"); + init_data = NULL; + data_amnt = 0; + break; + } + + for (i = 0; i < data_amnt; i++) { + if (!strcmp(dev->name, init_data[i].name)) { + *plat = init_data[i]; + if (plat->enablemask != HW_STATE_CONTROL) { + /* + * Take the regulator under SW control. Ensure + * the initial state matches dt flags and then + * write the SEL bit + */ + uc_pdata = dev_get_uclass_plat(dev); + ret = bd71837_set_enable(dev, + !!(uc_pdata->boot_on || + uc_pdata->always_on)); + if (ret) + return ret; + + return pmic_clrsetbits(dev->parent, + plat->enable_reg, + plat->sel_mask, + plat->sel_mask); + } + return 0; + } + } + + pr_err("Unknown regulator '%s'\n", dev->name); + + return -ENOENT; +} + +static const struct dm_regulator_ops bd71837_regulator_ops = { + .get_value = bd71837_get_value, + .set_value = bd71837_set_value, + .get_enable = bd71837_get_enable, + .set_enable = bd71837_set_enable, +}; + +U_BOOT_DRIVER(bd71837_regulator) = { + .name = BD718XX_REGULATOR_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &bd71837_regulator_ops, + .probe = bd71837_regulator_probe, + .plat_auto = sizeof(struct bd71837_plat), +}; diff --git a/roms/u-boot/drivers/power/regulator/da9063.c b/roms/u-boot/drivers/power/regulator/da9063.c new file mode 100644 index 000000000..8df1abcf7 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/da9063.c @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Flowbird + * Martin Fuzzey <martin.fuzzey@flowbird.group> + */ + +#include <common.h> +#include <dm.h> +#include <linux/bitops.h> +#include <power/da9063_pmic.h> +#include <power/pmic.h> +#include <power/regulator.h> + +#define DA9063_BUCK_EN 0x01 +#define DA9063_LDO_EN 0x01 +#define DA9063_VBUCK_MASK 0x7F +#define DA9063_BUCK_SL 0x80 +#define DA9063_LDO_SL 0x80 + +#define DA9063_VLDO1_MASK 0x3F +#define DA9063_VLDO2_MASK 0x3F +#define DA9063_VLDO3_MASK 0x7F +#define DA9063_VLDO4_MASK 0x7F +#define DA9063_VLDO5_MASK 0x3F +#define DA9063_VLDO6_MASK 0x3F +#define DA9063_VLDO7_MASK 0x3F +#define DA9063_VLDO8_MASK 0x3F +#define DA9063_VLDO9_MASK 0x3F +#define DA9063_VLDO10_MASK 0x3F +#define DA9063_VLDO11_MASK 0x3F + +#define DA9063_BUCK_MODE_MASK 0xC0 +#define DA9063_BUCK_MODE_MANUAL 0x00 +#define DA9063_BUCK_MODE_SLEEP 0x40 +#define DA9063_BUCK_MODE_SYNC 0x80 +#define DA9063_BUCK_MODE_AUTO 0xC0 + +#define DA9063_BIO_ILIM_MASK 0x0F +#define DA9063_BMEM_ILIM_MASK 0xF0 +#define DA9063_BPRO_ILIM_MASK 0x0F +#define DA9063_BPERI_ILIM_MASK 0xF0 +#define DA9063_BCORE1_ILIM_MASK 0x0F +#define DA9063_BCORE2_ILIM_MASK 0xF0 + +struct da9063_reg_info { + uint min_uV; + uint step_uV; + uint max_uV; + uint min_uA; + uint step_uA; + uint max_uA; + uint en_reg; + uint vsel_reg; + uint mode_reg; + uint ilim_reg; + u8 en_mask; + u8 vsel_mask; + u8 ilim_mask; + const char *dt_node_name; + const int *current_limits; +}; + +struct da9063_priv { + const struct da9063_reg_info *reg_info; +}; + +static struct dm_regulator_mode da9063_ldo_modes[] = { + { .id = DA9063_LDOMODE_SLEEP, + .register_value = DA9063_LDO_SL, .name = "SLEEP" }, + { .id = DA9063_LDOMODE_NORMAL, + .register_value = 0, .name = "NORMAL" }, +}; + +#define DA9063_LDO(regl_name, min_mV, step_mV, max_mV) \ + .min_uV = (min_mV) * 1000, \ + .step_uV = (step_mV) * 1000, \ + .max_uV = (max_mV) * 1000, \ + .en_reg = DA9063_REG_##regl_name##_CONT, \ + .en_mask = DA9063_LDO_EN, \ + .vsel_reg = DA9063_REG_V##regl_name##_A, \ + .vsel_mask = DA9063_V##regl_name##_MASK, \ + .mode_reg = DA9063_REG_V##regl_name##_A \ + +/* This array is directly indexed so must stay in numerical order */ +static const struct da9063_reg_info da9063_ldo_info[] = { + { DA9063_LDO(LDO1, 600, 20, 1860) }, + { DA9063_LDO(LDO2, 600, 20, 1860) }, + { DA9063_LDO(LDO3, 900, 20, 3440) }, + { DA9063_LDO(LDO4, 900, 20, 3440) }, + { DA9063_LDO(LDO5, 900, 50, 3600) }, + { DA9063_LDO(LDO6, 900, 50, 3600) }, + { DA9063_LDO(LDO7, 900, 50, 3600) }, + { DA9063_LDO(LDO8, 900, 50, 3600) }, + { DA9063_LDO(LDO9, 950, 50, 3600) }, + { DA9063_LDO(LDO10, 900, 50, 3600) }, + { DA9063_LDO(LDO11, 900, 50, 3600) }, +}; + +static struct dm_regulator_mode da9063_buck_modes[] = { + { .id = DA9063_BUCKMODE_SLEEP, + .register_value = DA9063_BUCK_MODE_SLEEP, .name = "SLEEP" }, + { .id = DA9063_BUCKMODE_SYNC, + .register_value = DA9063_BUCK_MODE_SYNC, .name = "SYNC" }, + { .id = DA9063_BUCKMODE_AUTO, + .register_value = DA9063_BUCK_MODE_AUTO, .name = "AUTO" }, +}; + +#define DA9063_BUCK(regl_name, dt_name, \ + min_mV, step_mV, max_mV, \ + min_mA, step_mA, max_mA, _ilim_reg) \ + .dt_node_name = dt_name, \ + .min_uV = (min_mV) * 1000, \ + .step_uV = (step_mV) * 1000, \ + .max_uV = (max_mV) * 1000, \ + .min_uA = (min_mA) * 1000, \ + .step_uA = (step_mA) * 1000, \ + .max_uA = (max_mA) * 1000, \ + .en_reg = DA9063_REG_##regl_name##_CONT, \ + .en_mask = DA9063_BUCK_EN, \ + .vsel_reg = DA9063_REG_V##regl_name##_A, \ + .vsel_mask = DA9063_VBUCK_MASK, \ + .mode_reg = DA9063_REG_##regl_name##_CFG, \ + .ilim_reg = DA9063_REG_BUCK_ILIM_##_ilim_reg, \ + .ilim_mask = DA9063_##regl_name##_ILIM_MASK + +static const struct da9063_reg_info da9063_buck_info[] = { + /* mV mA */ + { DA9063_BUCK(BCORE1, "bcore1", 300, 10, 1570, 500, 100, 2000, C) }, + { DA9063_BUCK(BCORE2, "bcore2", 300, 10, 1570, 500, 100, 2000, C) }, + { DA9063_BUCK(BPRO, "bpro", 530, 10, 1800, 500, 100, 2000, B) }, + { DA9063_BUCK(BMEM, "bmem", 800, 20, 3340, 1500, 100, 3000, A) }, + { DA9063_BUCK(BIO, "bio", 800, 20, 3340, 1500, 100, 3000, A) }, + { DA9063_BUCK(BPERI, "bperi", 800, 20, 3340, 1500, 100, 3000, B) }, +}; + +static int da9063_get_enable(struct udevice *dev) +{ + const struct da9063_priv *priv = dev_get_priv(dev); + const struct da9063_reg_info *info = priv->reg_info; + int ret; + + ret = pmic_reg_read(dev->parent, info->en_reg); + if (ret < 0) + return ret; + + return ret & info->en_mask ? true : false; +} + +static int da9063_set_enable(struct udevice *dev, bool enable) +{ + const struct da9063_priv *priv = dev_get_priv(dev); + const struct da9063_reg_info *info = priv->reg_info; + + return pmic_clrsetbits(dev->parent, info->en_reg, + info->en_mask, enable ? info->en_mask : 0); +} + +static int da9063_get_voltage(struct udevice *dev) +{ + const struct da9063_priv *priv = dev_get_priv(dev); + const struct da9063_reg_info *info = priv->reg_info; + int ret; + + ret = pmic_reg_read(dev->parent, info->vsel_reg); + if (ret < 0) + return ret; + + return info->min_uV + (ret & info->vsel_mask) * info->step_uV; +} + +static int da9063_set_voltage(struct udevice *dev, int uV) +{ + const struct da9063_priv *priv = dev_get_priv(dev); + const struct da9063_reg_info *info = priv->reg_info; + uint sel; + + if (uV < info->min_uV || uV > info->max_uV) + return -EINVAL; + + sel = (uV - info->min_uV) / info->step_uV; + + return pmic_clrsetbits(dev->parent, info->vsel_reg, + info->vsel_mask, sel); +} + +static const struct dm_regulator_mode + *da9063_find_mode_by_id(int id, + const struct dm_regulator_mode *modes, + uint mode_count) +{ + for (; mode_count; mode_count--) { + if (modes->id == id) + return modes; + modes++; + } + return NULL; +} + +static int ldo_get_mode(struct udevice *dev) +{ + const struct da9063_priv *priv = dev_get_priv(dev); + const struct da9063_reg_info *info = priv->reg_info; + int val; + + val = pmic_reg_read(dev->parent, info->mode_reg); + if (val < 0) + return val; + + if (val & DA9063_LDO_SL) + return DA9063_LDOMODE_SLEEP; + else + return DA9063_LDOMODE_NORMAL; +} + +static int ldo_set_mode(struct udevice *dev, int mode_id) +{ + const struct da9063_priv *priv = dev_get_priv(dev); + const struct da9063_reg_info *info = priv->reg_info; + const struct dm_regulator_mode *mode; + + mode = da9063_find_mode_by_id(mode_id, + da9063_ldo_modes, + ARRAY_SIZE(da9063_ldo_modes)); + if (!mode) + return -EINVAL; + + return pmic_clrsetbits(dev->parent, info->mode_reg, + DA9063_LDO_SL, mode->register_value); +} + +static int buck_get_mode(struct udevice *dev) +{ + const struct da9063_priv *priv = dev_get_priv(dev); + const struct da9063_reg_info *info = priv->reg_info; + int i; + int val; + + val = pmic_reg_read(dev->parent, info->mode_reg); + if (val < 0) + return val; + + val &= DA9063_BUCK_MODE_MASK; + if (val == DA9063_BUCK_MODE_MANUAL) { + val = pmic_reg_read(dev->parent, info->vsel_reg); + if (val < 0) + return val; + + if (val & DA9063_BUCK_SL) + return DA9063_BUCKMODE_SLEEP; + else + return DA9063_BUCKMODE_SYNC; + } + + for (i = 0; i < ARRAY_SIZE(da9063_buck_modes); i++) { + if (da9063_buck_modes[i].register_value == val) + return da9063_buck_modes[i].id; + } + + return -EINVAL; +} + +static int buck_set_mode(struct udevice *dev, int mode_id) +{ + const struct da9063_priv *priv = dev_get_priv(dev); + const struct da9063_reg_info *info = priv->reg_info; + const struct dm_regulator_mode *mode; + + mode = da9063_find_mode_by_id(mode_id, + da9063_buck_modes, + ARRAY_SIZE(da9063_buck_modes)); + if (!mode) + return -EINVAL; + + return pmic_clrsetbits(dev->parent, info->mode_reg, + DA9063_BUCK_MODE_MASK, mode->register_value); +} + +static int buck_get_current_limit(struct udevice *dev) +{ + const struct da9063_priv *priv = dev_get_priv(dev); + const struct da9063_reg_info *info = priv->reg_info; + int val; + + val = pmic_reg_read(dev->parent, info->ilim_reg); + if (val < 0) + return val; + + val &= info->ilim_mask; + val >>= (ffs(info->ilim_mask) - 1); + + return info->min_uA + val * info->step_uA; +} + +static int buck_set_current_limit(struct udevice *dev, int uA) +{ + const struct da9063_priv *priv = dev_get_priv(dev); + const struct da9063_reg_info *info = priv->reg_info; + int val; + + if (uA < info->min_uA || uA > info->max_uA) + return -EINVAL; + + val = (uA - info->min_uA) / info->step_uA; + val <<= (ffs(info->ilim_mask) - 1); + + return pmic_clrsetbits(dev->parent, info->ilim_reg, + info->ilim_mask, val); +} + +static int da9063_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + struct da9063_priv *priv = dev_get_priv(dev); + + /* LDOs are named numerically in DT so can directly index */ + if (dev->driver_data < 1 || + dev->driver_data > ARRAY_SIZE(da9063_ldo_info)) + return -EINVAL; + priv->reg_info = &da9063_ldo_info[dev->driver_data - 1]; + + uc_pdata = dev_get_uclass_plat(dev); + uc_pdata->type = REGULATOR_TYPE_LDO; + uc_pdata->mode = da9063_ldo_modes; + uc_pdata->mode_count = ARRAY_SIZE(da9063_ldo_modes); + + return 0; +} + +static int da9063_buck_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + struct da9063_priv *priv = dev_get_priv(dev); + int i; + + /* Bucks have names rather than numbers so need to match with DT */ + for (i = 0; i < ARRAY_SIZE(da9063_buck_info); i++) { + const struct da9063_reg_info *info = &da9063_buck_info[i]; + + if (!strcmp(info->dt_node_name, dev->name)) { + priv->reg_info = info; + break; + } + } + if (!priv->reg_info) + return -ENODEV; + + uc_pdata = dev_get_uclass_plat(dev); + uc_pdata->type = REGULATOR_TYPE_BUCK; + uc_pdata->mode = da9063_buck_modes; + uc_pdata->mode_count = ARRAY_SIZE(da9063_buck_modes); + + return 0; +} + +static const struct dm_regulator_ops da9063_ldo_ops = { + .get_value = da9063_get_voltage, + .set_value = da9063_set_voltage, + .get_enable = da9063_get_enable, + .set_enable = da9063_set_enable, + .get_mode = ldo_get_mode, + .set_mode = ldo_set_mode, +}; + +U_BOOT_DRIVER(da9063_ldo) = { + .name = DA9063_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &da9063_ldo_ops, + .probe = da9063_ldo_probe, + .priv_auto = sizeof(struct da9063_priv), +}; + +static const struct dm_regulator_ops da9063_buck_ops = { + .get_value = da9063_get_voltage, + .set_value = da9063_set_voltage, + .get_enable = da9063_get_enable, + .set_enable = da9063_set_enable, + .get_mode = buck_get_mode, + .set_mode = buck_set_mode, + .get_current = buck_get_current_limit, + .set_current = buck_set_current_limit, +}; + +U_BOOT_DRIVER(da9063_buck) = { + .name = DA9063_BUCK_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &da9063_buck_ops, + .probe = da9063_buck_probe, + .priv_auto = sizeof(struct da9063_priv), +}; diff --git a/roms/u-boot/drivers/power/regulator/fan53555.c b/roms/u-boot/drivers/power/regulator/fan53555.c new file mode 100644 index 000000000..9d8a235b7 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/fan53555.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) 2018 Theobroma Systems Design und Consulting GmbH + */ + +#include <common.h> +#include <bitfield.h> +#include <errno.h> +#include <dm.h> +#include <fdtdec.h> +#include <i2c.h> +#include <log.h> +#include <asm/gpio.h> +#include <linux/bitops.h> +#include <power/fan53555.h> +#include <power/pmic.h> +#include <power/regulator.h> + +/** + * struct ic_types - definition of fan53555-family devices + * + * @die_id: Identifies the DIE_ID (lower nibble of the ID1 register) + * @die_rev: Identifies the DIE_REV (lower nibble of the ID2 register) + * @vsel_min: starting voltage (step 0) in uV + * @vsel_step: increment of the voltage in uV + * + * The voltage ramp (i.e. minimum voltage and step) is selected from the + * combination of 2 nibbles: DIE_ID and DIE_REV. + * + * See http://www.onsemi.com/pub/Collateral/FAN53555-D.pdf for details. + */ +static const struct { + unsigned int vendor; + u8 die_id; + u8 die_rev; + bool check_rev; + u32 vsel_min; + u32 vsel_step; +} ic_types[] = { + /* Option 00 */ + { FAN53555_VENDOR_FAIRCHILD, 0x0, 0x3, true, 600000, 10000 }, + /* Option 13 */ + { FAN53555_VENDOR_FAIRCHILD, 0x0, 0xf, true, 800000, 10000 }, + /* Option 23 */ + { FAN53555_VENDOR_FAIRCHILD, 0x0, 0xc, true, 600000, 12500 }, + /* Option 01 */ + { FAN53555_VENDOR_FAIRCHILD, 0x1, 0x3, true, 600000, 10000 }, + /* Option 03 */ + { FAN53555_VENDOR_FAIRCHILD, 0x3, 0x3, true, 600000, 10000 }, + /* Option 04 */ + { FAN53555_VENDOR_FAIRCHILD, 0x4, 0xf, true, 603000, 12826 }, + /* Option 05 */ + { FAN53555_VENDOR_FAIRCHILD, 0x5, 0x3, true, 600000, 10000 }, + /* Option 08 */ + { FAN53555_VENDOR_FAIRCHILD, 0x8, 0x1, true, 600000, 10000 }, + /* Option 08 */ + { FAN53555_VENDOR_FAIRCHILD, 0x8, 0xf, true, 600000, 10000 }, + /* Option 09 */ + { FAN53555_VENDOR_FAIRCHILD, 0xc, 0xf, true, 603000, 12826 }, + /* SYL82X */ + { FAN53555_VENDOR_SILERGY, 0x8, 0x0, false, 712500, 12500 }, + /* SYL83X */ + { FAN53555_VENDOR_SILERGY, 0x9, 0x0, false, 712500, 12500 }, +}; + +/* I2C-accessible byte-sized registers */ +enum { + /* Voltage setting */ + FAN53555_VSEL0 = 0x00, + FAN53555_VSEL1, + /* Control register */ + FAN53555_CONTROL, + /* IC Type */ + FAN53555_ID1, + /* IC mask version */ + FAN53555_ID2, + /* Monitor register */ + FAN53555_MONITOR, +}; + +struct fan53555_plat { + /* Voltage setting register */ + unsigned int vol_reg; + unsigned int sleep_reg; + +}; + +struct fan53555_priv { + /* IC Vendor */ + unsigned int vendor; + /* IC Type and Rev */ + unsigned int die_id; + unsigned int die_rev; + /* Voltage range and step(linear) */ + unsigned int vsel_min; + unsigned int vsel_step; + /* Voltage slew rate limiting */ + unsigned int slew_rate; + /* Sleep voltage cache */ + unsigned int sleep_vol_cache; +}; + +static int fan53555_regulator_of_to_plat(struct udevice *dev) +{ + struct fan53555_plat *dev_pdata = dev_get_plat(dev); + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 sleep_vsel; + + /* This is a buck regulator */ + uc_pdata->type = REGULATOR_TYPE_BUCK; + + sleep_vsel = dev_read_u32_default(dev, "fcs,suspend-voltage-selector", + FAN53555_VSEL1); + + /* + * Depending on the device-tree settings, the 'normal mode' + * voltage is either controlled by VSEL0 or VSEL1. + */ + switch (sleep_vsel) { + case FAN53555_VSEL0: + dev_pdata->sleep_reg = FAN53555_VSEL0; + dev_pdata->vol_reg = FAN53555_VSEL1; + break; + case FAN53555_VSEL1: + dev_pdata->sleep_reg = FAN53555_VSEL1; + dev_pdata->vol_reg = FAN53555_VSEL0; + break; + default: + pr_err("%s: invalid vsel id %d\n", dev->name, sleep_vsel); + return -EINVAL; + } + + return 0; +} + +static int fan53555_regulator_get_value(struct udevice *dev) +{ + struct fan53555_plat *pdata = dev_get_plat(dev); + struct fan53555_priv *priv = dev_get_priv(dev); + int reg; + int voltage; + + /* We only support a single voltage selector (i.e. 'normal' mode). */ + reg = pmic_reg_read(dev->parent, pdata->vol_reg); + if (reg < 0) + return reg; + voltage = priv->vsel_min + (reg & 0x3f) * priv->vsel_step; + + debug("%s: %d uV\n", __func__, voltage); + return voltage; +} + +static int fan53555_regulator_set_value(struct udevice *dev, int uV) +{ + struct fan53555_plat *pdata = dev_get_plat(dev); + struct fan53555_priv *priv = dev_get_priv(dev); + u8 vol; + + vol = (uV - priv->vsel_min) / priv->vsel_step; + debug("%s: uV=%d; writing volume %d: %02x\n", + __func__, uV, pdata->vol_reg, vol); + + return pmic_clrsetbits(dev->parent, pdata->vol_reg, GENMASK(6, 0), vol); +} + +static int fan53555_voltages_setup(struct udevice *dev) +{ + struct fan53555_priv *priv = dev_get_priv(dev); + int i; + + /* Init voltage range and step */ + for (i = 0; i < ARRAY_SIZE(ic_types); ++i) { + if (ic_types[i].vendor != priv->vendor) + continue; + + if (ic_types[i].die_id != priv->die_id) + continue; + + if (ic_types[i].check_rev && + ic_types[i].die_rev != priv->die_rev) + continue; + + priv->vsel_min = ic_types[i].vsel_min; + priv->vsel_step = ic_types[i].vsel_step; + + return 0; + } + + pr_err("%s: %s: die id %d rev %d not supported!\n", + dev->name, __func__, priv->die_id, priv->die_rev); + return -EINVAL; +} + +enum { + DIE_ID_SHIFT = 0, + DIE_ID_WIDTH = 4, + DIE_REV_SHIFT = 0, + DIE_REV_WIDTH = 4, +}; + +static int fan53555_probe(struct udevice *dev) +{ + struct fan53555_priv *priv = dev_get_priv(dev); + int ID1, ID2; + + debug("%s\n", __func__); + + /* read chip ID1 and ID2 (two registers, starting at ID1) */ + ID1 = pmic_reg_read(dev->parent, FAN53555_ID1); + if (ID1 < 0) + return ID1; + + ID2 = pmic_reg_read(dev->parent, FAN53555_ID2); + if (ID2 < 0) + return ID2; + + /* extract vendor, die_id and die_rev */ + priv->vendor = dev->driver_data; + priv->die_id = ID1 & GENMASK(3, 0); + priv->die_rev = ID2 & GENMASK(3, 0); + + if (fan53555_voltages_setup(dev) < 0) + return -ENODATA; + + debug("%s: FAN53555 option %d rev %d detected\n", + __func__, priv->die_id, priv->die_rev); + + return 0; +} + +static const struct dm_regulator_ops fan53555_regulator_ops = { + .get_value = fan53555_regulator_get_value, + .set_value = fan53555_regulator_set_value, +}; + +U_BOOT_DRIVER(fan53555_regulator) = { + .name = "fan53555_regulator", + .id = UCLASS_REGULATOR, + .ops = &fan53555_regulator_ops, + .of_to_plat = fan53555_regulator_of_to_plat, + .plat_auto = sizeof(struct fan53555_plat), + .priv_auto = sizeof(struct fan53555_priv), + .probe = fan53555_probe, +}; diff --git a/roms/u-boot/drivers/power/regulator/fixed.c b/roms/u-boot/drivers/power/regulator/fixed.c new file mode 100644 index 000000000..d3e0fb672 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/fixed.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Samsung Electronics + * + * Przemyslaw Marczak <p.marczak@samsung.com> + */ + +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <log.h> +#include <power/pmic.h> +#include <power/regulator.h> + +#include "regulator_common.h" + +static int fixed_regulator_of_to_plat(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + struct regulator_common_plat *dev_pdata; + + dev_pdata = dev_get_plat(dev); + uc_pdata = dev_get_uclass_plat(dev); + if (!uc_pdata) + return -ENXIO; + + uc_pdata->type = REGULATOR_TYPE_FIXED; + + return regulator_common_of_to_plat(dev, dev_pdata, "gpio"); +} + +static int fixed_regulator_get_value(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + if (!uc_pdata) + return -ENXIO; + + if (uc_pdata->min_uV != uc_pdata->max_uV) { + debug("Invalid constraints for: %s\n", uc_pdata->name); + return -EINVAL; + } + + return uc_pdata->min_uV; +} + +static int fixed_regulator_get_current(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + if (!uc_pdata) + return -ENXIO; + + if (uc_pdata->min_uA != uc_pdata->max_uA) { + debug("Invalid constraints for: %s\n", uc_pdata->name); + return -EINVAL; + } + + return uc_pdata->min_uA; +} + +static int fixed_regulator_get_enable(struct udevice *dev) +{ + return regulator_common_get_enable(dev, dev_get_plat(dev)); +} + +static int fixed_regulator_set_enable(struct udevice *dev, bool enable) +{ + return regulator_common_set_enable(dev, dev_get_plat(dev), enable); +} + +static const struct dm_regulator_ops fixed_regulator_ops = { + .get_value = fixed_regulator_get_value, + .get_current = fixed_regulator_get_current, + .get_enable = fixed_regulator_get_enable, + .set_enable = fixed_regulator_set_enable, +}; + +static const struct udevice_id fixed_regulator_ids[] = { + { .compatible = "regulator-fixed" }, + { }, +}; + +U_BOOT_DRIVER(regulator_fixed) = { + .name = "regulator_fixed", + .id = UCLASS_REGULATOR, + .ops = &fixed_regulator_ops, + .of_match = fixed_regulator_ids, + .of_to_plat = fixed_regulator_of_to_plat, + .plat_auto = sizeof(struct regulator_common_plat), +}; diff --git a/roms/u-boot/drivers/power/regulator/gpio-regulator.c b/roms/u-boot/drivers/power/regulator/gpio-regulator.c new file mode 100644 index 000000000..e5e08a33d --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/gpio-regulator.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 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 <log.h> +#include <asm/gpio.h> +#include <power/pmic.h> +#include <power/regulator.h> + +#include "regulator_common.h" + +#define GPIO_REGULATOR_MAX_STATES 2 + +struct gpio_regulator_plat { + struct regulator_common_plat common; + struct gpio_desc gpio; /* GPIO for regulator voltage control */ + int states[GPIO_REGULATOR_MAX_STATES]; + int voltages[GPIO_REGULATOR_MAX_STATES]; +}; + +static int gpio_regulator_of_to_plat(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + struct gpio_regulator_plat *dev_pdata; + struct gpio_desc *gpio; + int ret, count, i, j; + u32 states_array[GPIO_REGULATOR_MAX_STATES * 2]; + + dev_pdata = dev_get_plat(dev); + uc_pdata = dev_get_uclass_plat(dev); + if (!uc_pdata) + return -ENXIO; + + /* Set type to gpio */ + uc_pdata->type = REGULATOR_TYPE_GPIO; + + /* + * Get gpio regulator gpio desc + * Assuming one GPIO per regulator. + * Can be extended later to multiple GPIOs + * per gpio-regulator. As of now no instance with multiple + * gpios is presnt + */ + gpio = &dev_pdata->gpio; + ret = gpio_request_by_name(dev, "gpios", 0, gpio, GPIOD_IS_OUT); + if (ret) + debug("regulator gpio - not found! Error: %d", ret); + + ret = dev_read_size(dev, "states"); + if (ret < 0) + return ret; + + count = ret / sizeof(states_array[0]); + if (count > ARRAY_SIZE(states_array)) { + debug("regulator gpio - to many states (%d > %d)", + count / 2, GPIO_REGULATOR_MAX_STATES); + count = ARRAY_SIZE(states_array); + } + + ret = dev_read_u32_array(dev, "states", states_array, count); + if (ret < 0) + return ret; + + for (i = 0, j = 0; i < count; i += 2) { + dev_pdata->voltages[j] = states_array[i]; + dev_pdata->states[j] = states_array[i + 1]; + j++; + } + + return regulator_common_of_to_plat(dev, &dev_pdata->common, "enable-gpios"); +} + +static int gpio_regulator_get_value(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + struct gpio_regulator_plat *dev_pdata = dev_get_plat(dev); + int enable; + + if (!dev_pdata->gpio.dev) + return -ENOSYS; + + uc_pdata = dev_get_uclass_plat(dev); + if (uc_pdata->min_uV > uc_pdata->max_uV) { + debug("Invalid constraints for: %s\n", uc_pdata->name); + return -EINVAL; + } + + enable = dm_gpio_get_value(&dev_pdata->gpio); + if (enable == dev_pdata->states[0]) + return dev_pdata->voltages[0]; + else + return dev_pdata->voltages[1]; +} + +static int gpio_regulator_set_value(struct udevice *dev, int uV) +{ + struct gpio_regulator_plat *dev_pdata = dev_get_plat(dev); + int ret; + bool enable; + + if (!dev_pdata->gpio.dev) + return -ENOSYS; + + if (uV == dev_pdata->voltages[0]) + enable = dev_pdata->states[0]; + else if (uV == dev_pdata->voltages[1]) + enable = dev_pdata->states[1]; + else + return -EINVAL; + + ret = dm_gpio_set_value(&dev_pdata->gpio, enable); + if (ret) { + pr_err("Can't set regulator : %s gpio to: %d\n", dev->name, + enable); + return ret; + } + + return 0; +} + +static int gpio_regulator_get_enable(struct udevice *dev) +{ + struct gpio_regulator_plat *dev_pdata = dev_get_plat(dev); + return regulator_common_get_enable(dev, &dev_pdata->common); +} + +static int gpio_regulator_set_enable(struct udevice *dev, bool enable) +{ + struct gpio_regulator_plat *dev_pdata = dev_get_plat(dev); + return regulator_common_set_enable(dev, &dev_pdata->common, enable); +} + +static const struct dm_regulator_ops gpio_regulator_ops = { + .get_value = gpio_regulator_get_value, + .set_value = gpio_regulator_set_value, + .get_enable = gpio_regulator_get_enable, + .set_enable = gpio_regulator_set_enable, +}; + +static const struct udevice_id gpio_regulator_ids[] = { + { .compatible = "regulator-gpio" }, + { }, +}; + +U_BOOT_DRIVER(gpio_regulator) = { + .name = "gpio regulator", + .id = UCLASS_REGULATOR, + .ops = &gpio_regulator_ops, + .of_match = gpio_regulator_ids, + .of_to_plat = gpio_regulator_of_to_plat, + .plat_auto = sizeof(struct gpio_regulator_plat), +}; diff --git a/roms/u-boot/drivers/power/regulator/lp873x_regulator.c b/roms/u-boot/drivers/power/regulator/lp873x_regulator.c new file mode 100644 index 000000000..ec1037d7a --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/lp873x_regulator.c @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 + * 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/lp873x.h> + +static const char lp873x_buck_ctrl[LP873X_BUCK_NUM] = {0x2, 0x4}; +static const char lp873x_buck_volt[LP873X_BUCK_NUM] = {0x6, 0x7}; +static const char lp873x_ldo_ctrl[LP873X_LDO_NUM] = {0x8, 0x9}; +static const char lp873x_ldo_volt[LP873X_LDO_NUM] = {0xA, 0xB}; + +static int lp873x_buck_enable(struct udevice *dev, int op, bool *enable) +{ + int ret; + unsigned int adr; + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + adr = uc_pdata->ctrl_reg; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + ret &= LP873X_BUCK_MODE_MASK; + + if (ret) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + if (*enable) + ret |= LP873X_BUCK_MODE_MASK; + else + ret &= ~(LP873X_BUCK_MODE_MASK); + ret = pmic_reg_write(dev->parent, adr, ret); + if (ret) + return ret; + } + + return 0; +} + +static int lp873x_buck_volt2hex(int uV) +{ + if (uV > LP873X_BUCK_VOLT_MAX) + return -EINVAL; + else if (uV > 1400000) + return (uV - 1420000) / 20000 + 0x9E; + else if (uV > 730000) + return (uV - 735000) / 5000 + 0x18; + else if (uV >= 700000) + return (uV - 700000) / 10000 + 0x1; + else + return -EINVAL; +} + +static int lp873x_buck_hex2volt(int hex) +{ + if (hex > LP873X_BUCK_VOLT_MAX_HEX) + return -EINVAL; + else if (hex > 0x9D) + return 1400000 + (hex - 0x9D) * 20000; + else if (hex > 0x17) + return 730000 + (hex - 0x17) * 5000; + else if (hex >= 0x14) + return 700000 + (hex - 0x14) * 10000; + else + return -EINVAL; +} + +static int lp873x_buck_val(struct udevice *dev, int op, int *uV) +{ + unsigned int hex, adr; + int ret; + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(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; + + if (op == PMIC_OP_GET) { + ret &= LP873X_BUCK_VOLT_MASK; + ret = lp873x_buck_hex2volt(ret); + if (ret < 0) + return ret; + *uV = ret; + + return 0; + } + + hex = lp873x_buck_volt2hex(*uV); + if (hex < 0) + return hex; + + ret &= 0x0; + ret |= hex; + + ret = pmic_reg_write(dev->parent, adr, ret); + + return ret; +} + +static int lp873x_ldo_enable(struct udevice *dev, int op, bool *enable) +{ + int ret; + unsigned int adr; + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + adr = uc_pdata->ctrl_reg; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + ret &= LP873X_LDO_MODE_MASK; + + if (ret) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + if (*enable) + ret |= LP873X_LDO_MODE_MASK; + else + ret &= ~(LP873X_LDO_MODE_MASK); + + ret = pmic_reg_write(dev->parent, adr, ret); + if (ret) + return ret; + } + + return 0; +} + +static int lp873x_ldo_volt2hex(int uV) +{ + if (uV > LP873X_LDO_VOLT_MAX) + return -EINVAL; + + return (uV - 800000) / 100000; +} + +static int lp873x_ldo_hex2volt(int hex) +{ + if (hex > LP873X_LDO_VOLT_MAX_HEX) + return -EINVAL; + + if (!hex) + return 0; + + return (hex * 100000) + 800000; +} + +static int lp873x_ldo_val(struct udevice *dev, int op, int *uV) +{ + unsigned int hex, adr; + int ret; + + struct dm_regulator_uclass_plat *uc_pdata; + + if (op == PMIC_OP_GET) + *uV = 0; + + uc_pdata = dev_get_uclass_plat(dev); + + adr = uc_pdata->volt_reg; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + ret &= LP873X_LDO_VOLT_MASK; + ret = lp873x_ldo_hex2volt(ret); + if (ret < 0) + return ret; + *uV = ret; + return 0; + } + + hex = lp873x_ldo_volt2hex(*uV); + if (hex < 0) + return hex; + + ret &= ~LP873X_LDO_VOLT_MASK; + ret |= hex; + if (*uV > 1650000) + ret |= 0x80; + ret = pmic_reg_write(dev->parent, adr, ret); + + return ret; +} + +static int lp873x_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + uc_pdata->type = REGULATOR_TYPE_LDO; + + int idx = dev->driver_data; + if (idx >= LP873X_LDO_NUM) { + printf("Wrong ID for regulator\n"); + return -1; + } + + uc_pdata->ctrl_reg = lp873x_ldo_ctrl[idx]; + uc_pdata->volt_reg = lp873x_ldo_volt[idx]; + + return 0; +} + +static int ldo_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = lp873x_ldo_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int ldo_set_value(struct udevice *dev, int uV) +{ + return lp873x_ldo_val(dev, PMIC_OP_SET, &uV); +} + +static int ldo_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = lp873x_ldo_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int ldo_set_enable(struct udevice *dev, bool enable) +{ + return lp873x_ldo_enable(dev, PMIC_OP_SET, &enable); +} + +static int lp873x_buck_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + int idx; + + uc_pdata = dev_get_uclass_plat(dev); + uc_pdata->type = REGULATOR_TYPE_BUCK; + + idx = dev->driver_data; + if (idx >= LP873X_BUCK_NUM) { + printf("Wrong ID for regulator\n"); + return -1; + } + + uc_pdata->ctrl_reg = lp873x_buck_ctrl[idx]; + uc_pdata->volt_reg = lp873x_buck_volt[idx]; + + return 0; +} + +static int buck_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = lp873x_buck_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int buck_set_value(struct udevice *dev, int uV) +{ + return lp873x_buck_val(dev, PMIC_OP_SET, &uV); +} + +static int buck_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + + ret = lp873x_buck_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int buck_set_enable(struct udevice *dev, bool enable) +{ + return lp873x_buck_enable(dev, PMIC_OP_SET, &enable); +} + +static const struct dm_regulator_ops lp873x_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(lp873x_ldo) = { + .name = LP873X_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &lp873x_ldo_ops, + .probe = lp873x_ldo_probe, +}; + +static const struct dm_regulator_ops lp873x_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(lp873x_buck) = { + .name = LP873X_BUCK_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &lp873x_buck_ops, + .probe = lp873x_buck_probe, +}; diff --git a/roms/u-boot/drivers/power/regulator/lp87565_regulator.c b/roms/u-boot/drivers/power/regulator/lp87565_regulator.c new file mode 100644 index 000000000..7214dc1b8 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/lp87565_regulator.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2017 + * 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 <log.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/lp87565.h> + +static const char lp87565_buck_ctrl1[LP87565_BUCK_NUM] = {0x2, 0x4, 0x6, 0x8, 0x2, 0x6}; +static const char lp87565_buck_vout[LP87565_BUCK_NUM] = {0xA, 0xC, 0xE, 0x10, 0xA, 0xE }; + +static int lp87565_buck_enable(struct udevice *dev, int op, bool *enable) +{ + int ret; + unsigned int adr; + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + adr = uc_pdata->ctrl_reg; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + ret &= LP87565_BUCK_MODE_MASK; + + if (ret) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + if (*enable) + ret |= LP87565_BUCK_MODE_MASK; + else + ret &= ~LP87565_BUCK_MODE_MASK; + ret = pmic_reg_write(dev->parent, adr, ret); + if (ret) + return ret; + } + + return 0; +} + +static int lp87565_buck_volt2val(int uV) +{ + if (uV > LP87565_BUCK_VOLT_MAX) + return -EINVAL; + else if (uV > 1400000) + return (uV - 1420000) / 20000 + 0x9E; + else if (uV > 730000) + return (uV - 735000) / 5000 + 0x18; + else if (uV >= 500000) + return (uV - 500000) / 10000; + else + return -EINVAL; +} + +static int lp87565_buck_val2volt(int val) +{ + if (val > LP87565_BUCK_VOLT_MAX_HEX) + return -EINVAL; + else if (val > 0x9D) + return 1400000 + (val - 0x9D) * 20000; + else if (val > 0x17) + return 730000 + (val - 0x17) * 5000; + else if (val >= 0x0) + return 500000 + val * 10000; + else + return -EINVAL; +} + +static int lp87565_buck_val(struct udevice *dev, int op, int *uV) +{ + unsigned int hex, adr; + int ret; + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(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; + + if (op == PMIC_OP_GET) { + ret &= LP87565_BUCK_VOLT_MASK; + ret = lp87565_buck_val2volt(ret); + if (ret < 0) + return ret; + *uV = ret; + + return 0; + } + + hex = lp87565_buck_volt2val(*uV); + if (hex < 0) + return hex; + + ret &= 0x0; + ret = hex; + + ret = pmic_reg_write(dev->parent, adr, ret); + + return ret; +} + +static int lp87565_buck_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + int idx; + + uc_pdata = dev_get_uclass_plat(dev); + uc_pdata->type = REGULATOR_TYPE_BUCK; + + idx = dev->driver_data; + if (idx == 0 || idx == 1 || idx == 2 || idx == 3) { + debug("Single phase regulator\n"); + } else if (idx == 23) { + idx = 5; + } else if (idx == 10) { + idx = 4; + } else { + printf("Wrong ID for regulator\n"); + return -EINVAL; + } + + uc_pdata->ctrl_reg = lp87565_buck_ctrl1[idx]; + uc_pdata->volt_reg = lp87565_buck_vout[idx]; + + return 0; +} + +static int buck_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = lp87565_buck_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int buck_set_value(struct udevice *dev, int uV) +{ + return lp87565_buck_val(dev, PMIC_OP_SET, &uV); +} + +static int buck_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + + ret = lp87565_buck_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int buck_set_enable(struct udevice *dev, bool enable) +{ + return lp87565_buck_enable(dev, PMIC_OP_SET, &enable); +} + +static const struct dm_regulator_ops lp87565_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(lp87565_buck) = { + .name = LP87565_BUCK_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &lp87565_buck_ops, + .probe = lp87565_buck_probe, +}; diff --git a/roms/u-boot/drivers/power/regulator/max77686.c b/roms/u-boot/drivers/power/regulator/max77686.c new file mode 100644 index 000000000..f05d791b7 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/max77686.c @@ -0,0 +1,816 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2012-2015 Samsung Electronics + * + * Rajeshwari Shinde <rajeshwari.s@samsung.com> + * Przemyslaw Marczak <p.marczak@samsung.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/max77686_pmic.h> + +#define MODE(_id, _val, _name) { \ + .id = _id, \ + .register_value = _val, \ + .name = _name, \ +} + +/* LDO: 1,3,4,5,9,17,18,19,20,21,22,23,24,26,26,27 */ +static struct dm_regulator_mode max77686_ldo_mode_standby1[] = { + MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"), + MODE(OPMODE_LPM, MAX77686_LDO_MODE_LPM, "LPM"), + MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"), + MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"), +}; + +/* LDO: 2,6,7,8,10,11,12,14,15,16 */ +static struct dm_regulator_mode max77686_ldo_mode_standby2[] = { + MODE(OPMODE_OFF, MAX77686_LDO_MODE_OFF, "OFF"), + MODE(OPMODE_STANDBY, MAX77686_LDO_MODE_STANDBY, "ON/OFF"), + MODE(OPMODE_STANDBY_LPM, MAX77686_LDO_MODE_STANDBY_LPM, "ON/LPM"), + MODE(OPMODE_ON, MAX77686_LDO_MODE_ON, "ON"), +}; + +/* Buck: 1 */ +static struct dm_regulator_mode max77686_buck_mode_standby[] = { + MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"), + MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"), + MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"), +}; + +/* Buck: 2,3,4 */ +static struct dm_regulator_mode max77686_buck_mode_lpm[] = { + MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"), + MODE(OPMODE_STANDBY, MAX77686_BUCK_MODE_STANDBY, "ON/OFF"), + MODE(OPMODE_LPM, MAX77686_BUCK_MODE_LPM, "LPM"), + MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"), +}; + +/* Buck: 5,6,7,8,9 */ +static struct dm_regulator_mode max77686_buck_mode_onoff[] = { + MODE(OPMODE_OFF, MAX77686_BUCK_MODE_OFF, "OFF"), + MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"), +}; + +static const char max77686_buck_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) +{ + int hex = 0; + int hex_max = 0; + + switch (buck) { + case 2: + case 3: + case 4: + /* hex = (uV - 600000) / 12500; */ + hex = (uV - MAX77686_BUCK_UV_LMIN) / MAX77686_BUCK_UV_LSTEP; + hex_max = MAX77686_BUCK234_VOLT_MAX_HEX; + break; + default: + /* + * 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 and 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; + } + + if (hex >= 0 && hex <= hex_max) + return hex; + + pr_err("Value: %d uV is wrong for BUCK%d", uV, buck); + return -EINVAL; +} + +static int max77686_buck_hex2volt(int buck, int hex) +{ + unsigned uV = 0; + int hex_max = 0; + + if (hex < 0) + goto bad_hex; + + switch (buck) { + case 2: + case 3: + case 4: + hex_max = MAX77686_BUCK234_VOLT_MAX_HEX; + if (hex > hex_max) + goto bad_hex; + + /* uV = hex * 12500 + 600000; */ + uV = hex * MAX77686_BUCK_UV_LSTEP + MAX77686_BUCK_UV_LMIN; + break; + default: + hex_max = MAX77686_BUCK_VOLT_MAX_HEX; + if (hex > hex_max) + goto bad_hex; + + /* uV = hex * 50000 + 750000; */ + uV = hex * MAX77686_BUCK_UV_HSTEP + MAX77686_BUCK_UV_HMIN; + break; + } + + return uV; + +bad_hex: + pr_err("Value: %#x is wrong for BUCK%d", hex, buck); + return -EINVAL; +} + +static int max77686_ldo_volt2hex(int ldo, int uV) +{ + int hex = 0; + + switch (ldo) { + case 1: + case 2: + case 6: + case 7: + case 8: + case 15: + hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_LSTEP; + /* hex = (uV - 800000) / 25000; */ + break; + default: + hex = (uV - MAX77686_LDO_UV_MIN) / MAX77686_LDO_UV_HSTEP; + /* hex = (uV - 800000) / 50000; */ + } + + if (hex >= 0 && hex <= MAX77686_LDO_VOLT_MAX_HEX) + return hex; + + pr_err("Value: %d uV is wrong for LDO%d", uV, ldo); + return -EINVAL; +} + +static int max77686_ldo_hex2volt(int ldo, int hex) +{ + unsigned int uV = 0; + + if (hex > MAX77686_LDO_VOLT_MAX_HEX) + goto bad_hex; + + switch (ldo) { + case 1: + case 2: + case 6: + case 7: + case 8: + case 15: + /* uV = hex * 25000 + 800000; */ + uV = hex * MAX77686_LDO_UV_LSTEP + MAX77686_LDO_UV_MIN; + break; + default: + /* uV = hex * 50000 + 800000; */ + uV = hex * MAX77686_LDO_UV_HSTEP + MAX77686_LDO_UV_MIN; + } + + return uV; + +bad_hex: + pr_err("Value: %#x is wrong for ldo%d", hex, ldo); + return -EINVAL; +} + +static int max77686_ldo_hex2mode(int ldo, int hex) +{ + if (hex > MAX77686_LDO_MODE_MASK) + return -EINVAL; + + switch (hex) { + case MAX77686_LDO_MODE_OFF: + return OPMODE_OFF; + case MAX77686_LDO_MODE_LPM: /* == MAX77686_LDO_MODE_STANDBY: */ + /* The same mode values but different meaning for each ldo */ + switch (ldo) { + case 2: + case 6: + case 7: + case 8: + case 10: + case 11: + case 12: + case 14: + case 15: + case 16: + return OPMODE_STANDBY; + default: + return OPMODE_LPM; + } + case MAX77686_LDO_MODE_STANDBY_LPM: + return OPMODE_STANDBY_LPM; + case MAX77686_LDO_MODE_ON: + return OPMODE_ON; + default: + return -EINVAL; + } +} + +static int max77686_buck_hex2mode(int buck, int hex) +{ + if (hex > MAX77686_BUCK_MODE_MASK) + return -EINVAL; + + switch (hex) { + case MAX77686_BUCK_MODE_OFF: + return OPMODE_OFF; + case MAX77686_BUCK_MODE_ON: + return OPMODE_ON; + case MAX77686_BUCK_MODE_STANDBY: + switch (buck) { + case 1: + case 2: + case 3: + case 4: + return OPMODE_STANDBY; + default: + return -EINVAL; + } + case MAX77686_BUCK_MODE_LPM: + switch (buck) { + case 2: + case 3: + case 4: + return OPMODE_LPM; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int max77686_buck_modes(int buck, struct dm_regulator_mode **modesp) +{ + int ret = -EINVAL; + + if (buck < 1 || buck > MAX77686_BUCK_NUM) + return ret; + + switch (buck) { + case 1: + *modesp = max77686_buck_mode_standby; + ret = ARRAY_SIZE(max77686_buck_mode_standby); + break; + case 2: + case 3: + case 4: + *modesp = max77686_buck_mode_lpm; + ret = ARRAY_SIZE(max77686_buck_mode_lpm); + break; + default: + *modesp = max77686_buck_mode_onoff; + ret = ARRAY_SIZE(max77686_buck_mode_onoff); + } + + return ret; +} + +static int max77686_ldo_modes(int ldo, struct dm_regulator_mode **modesp, + struct udevice *dev) +{ + int ret = -EINVAL; + + if (ldo < 1 || ldo > MAX77686_LDO_NUM) + return ret; + + switch (ldo) { + case 2: + case 6: + case 7: + case 8: + case 10: + case 11: + case 12: + case 14: + case 15: + case 16: + *modesp = max77686_ldo_mode_standby2; + ret = ARRAY_SIZE(max77686_ldo_mode_standby2); + break; + default: + *modesp = max77686_ldo_mode_standby1; + ret = ARRAY_SIZE(max77686_ldo_mode_standby1); + } + + return ret; +} + +static int max77686_ldo_val(struct udevice *dev, int op, int *uV) +{ + unsigned int adr; + unsigned char val; + int hex, ldo, ret; + + if (op == PMIC_OP_GET) + *uV = 0; + + ldo = dev->driver_data; + if (ldo < 1 || ldo > MAX77686_LDO_NUM) { + pr_err("Wrong ldo number: %d", ldo); + return -EINVAL; + } + + adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1; + + ret = pmic_read(dev->parent, adr, &val, 1); + if (ret) + return ret; + + if (op == PMIC_OP_GET) { + val &= MAX77686_LDO_VOLT_MASK; + ret = max77686_ldo_hex2volt(ldo, val); + if (ret < 0) + return ret; + *uV = ret; + return 0; + } + + hex = max77686_ldo_volt2hex(ldo, *uV); + if (hex < 0) + return hex; + + val &= ~MAX77686_LDO_VOLT_MASK; + val |= hex; + ret = pmic_write(dev->parent, adr, &val, 1); + + return ret; +} + +static int max77686_buck_val(struct udevice *dev, int op, int *uV) +{ + unsigned int mask, adr; + unsigned char val; + int hex, buck, ret; + + buck = dev->driver_data; + if (buck < 1 || buck > MAX77686_BUCK_NUM) { + pr_err("Wrong buck number: %d", buck); + return -EINVAL; + } + + if (op == PMIC_OP_GET) + *uV = 0; + + /* &buck_out = ctrl + 1 */ + adr = max77686_buck_out[buck]; + + /* mask */ + switch (buck) { + case 2: + case 3: + case 4: + mask = MAX77686_BUCK234_VOLT_MASK; + break; + default: + mask = MAX77686_BUCK_VOLT_MASK; + break; + } + + ret = pmic_read(dev->parent, adr, &val, 1); + if (ret) + return ret; + + if (op == PMIC_OP_GET) { + val &= mask; + ret = max77686_buck_hex2volt(buck, val); + if (ret < 0) + return ret; + *uV = ret; + return 0; + } + + hex = max77686_buck_volt2hex(buck, *uV); + if (hex < 0) + return hex; + + val &= ~mask; + val |= hex; + ret = pmic_write(dev->parent, adr, &val, 1); + + return ret; +} + +static int max77686_ldo_mode(struct udevice *dev, int op, int *opmode) +{ + unsigned int adr, mode; + unsigned char val; + int ldo, ret; + + if (op == PMIC_OP_GET) + *opmode = -EINVAL; + + ldo = dev->driver_data; + if (ldo < 1 || ldo > MAX77686_LDO_NUM) { + pr_err("Wrong ldo number: %d", ldo); + return -EINVAL; + } + + adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1; + + ret = pmic_read(dev->parent, adr, &val, 1); + if (ret) + return ret; + + if (op == PMIC_OP_GET) { + val &= MAX77686_LDO_MODE_MASK; + ret = max77686_ldo_hex2mode(ldo, val); + if (ret < 0) + return ret; + *opmode = ret; + return 0; + } + + /* mode */ + switch (*opmode) { + case OPMODE_OFF: + mode = MAX77686_LDO_MODE_OFF; + break; + case OPMODE_LPM: + switch (ldo) { + case 2: + case 6: + case 7: + case 8: + case 10: + case 11: + case 12: + case 14: + case 15: + case 16: + return -EINVAL; + default: + mode = MAX77686_LDO_MODE_LPM; + } + break; + case OPMODE_STANDBY: + switch (ldo) { + case 2: + case 6: + case 7: + case 8: + case 10: + case 11: + case 12: + case 14: + case 15: + case 16: + mode = MAX77686_LDO_MODE_STANDBY; + break; + default: + return -EINVAL; + } + break; + case OPMODE_STANDBY_LPM: + mode = MAX77686_LDO_MODE_STANDBY_LPM; + break; + case OPMODE_ON: + mode = MAX77686_LDO_MODE_ON; + break; + default: + mode = 0xff; + } + + if (mode == 0xff) { + pr_err("Wrong mode: %d for ldo%d", *opmode, ldo); + return -EINVAL; + } + + val &= ~MAX77686_LDO_MODE_MASK; + val |= mode; + ret = pmic_write(dev->parent, adr, &val, 1); + + return ret; +} + +static int max77686_ldo_enable(struct udevice *dev, int op, bool *enable) +{ + int ret, on_off; + + if (op == PMIC_OP_GET) { + ret = max77686_ldo_mode(dev, op, &on_off); + if (ret) + return ret; + + switch (on_off) { + case OPMODE_OFF: + *enable = false; + break; + case OPMODE_ON: + *enable = true; + break; + default: + return -EINVAL; + } + } else if (op == PMIC_OP_SET) { + if (*enable) + on_off = OPMODE_ON; + else + on_off = OPMODE_OFF; + + ret = max77686_ldo_mode(dev, op, &on_off); + if (ret) + return ret; + } + + return 0; +} + +static int max77686_buck_mode(struct udevice *dev, int op, int *opmode) +{ + unsigned int mask, adr, mode, mode_shift; + unsigned char val; + int buck, ret; + + buck = dev->driver_data; + if (buck < 1 || buck > MAX77686_BUCK_NUM) { + pr_err("Wrong buck number: %d", buck); + return -EINVAL; + } + + adr = max77686_buck_ctrl[buck]; + + /* mask */ + switch (buck) { + case 2: + case 3: + case 4: + mode_shift = MAX77686_BUCK_MODE_SHIFT_2; + break; + default: + mode_shift = MAX77686_BUCK_MODE_SHIFT_1; + } + + mask = MAX77686_BUCK_MODE_MASK << mode_shift; + + ret = pmic_read(dev->parent, adr, &val, 1); + if (ret) + return ret; + + if (op == PMIC_OP_GET) { + val &= mask; + val >>= mode_shift; + ret = max77686_buck_hex2mode(buck, val); + if (ret < 0) + return ret; + *opmode = ret; + return 0; + } + + /* mode */ + switch (*opmode) { + case OPMODE_OFF: + mode = MAX77686_BUCK_MODE_OFF; + break; + case OPMODE_STANDBY: + switch (buck) { + case 1: + case 2: + case 3: + case 4: + mode = MAX77686_BUCK_MODE_STANDBY << mode_shift; + break; + default: + mode = 0xff; + } + break; + case OPMODE_LPM: + switch (buck) { + case 2: + case 3: + case 4: + mode = MAX77686_BUCK_MODE_LPM << mode_shift; + break; + default: + mode = 0xff; + } + break; + case OPMODE_ON: + mode = MAX77686_BUCK_MODE_ON << mode_shift; + break; + default: + mode = 0xff; + } + + if (mode == 0xff) { + pr_err("Wrong mode: %d for buck: %d\n", *opmode, buck); + return -EINVAL; + } + + val &= ~mask; + val |= mode; + ret = pmic_write(dev->parent, adr, &val, 1); + + return ret; +} + +static int max77686_buck_enable(struct udevice *dev, int op, bool *enable) +{ + int ret, on_off; + + if (op == PMIC_OP_GET) { + ret = max77686_buck_mode(dev, op, &on_off); + if (ret) + return ret; + + switch (on_off) { + case OPMODE_OFF: + *enable = false; + break; + case OPMODE_ON: + *enable = true; + break; + default: + return -EINVAL; + } + } else if (op == PMIC_OP_SET) { + if (*enable) + on_off = OPMODE_ON; + else + on_off = OPMODE_OFF; + + ret = max77686_buck_mode(dev, op, &on_off); + if (ret) + return ret; + } + + return 0; +} + +static int max77686_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_LDO; + uc_pdata->mode_count = max77686_ldo_modes(dev->driver_data, + &uc_pdata->mode, dev); + + return 0; +} + +static int ldo_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = max77686_ldo_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int ldo_set_value(struct udevice *dev, int uV) +{ + return max77686_ldo_val(dev, PMIC_OP_SET, &uV); +} + +static int ldo_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = max77686_ldo_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int ldo_set_enable(struct udevice *dev, bool enable) +{ + return max77686_ldo_enable(dev, PMIC_OP_SET, &enable); +} + +static int ldo_get_mode(struct udevice *dev) +{ + int mode; + int ret; + + ret = max77686_ldo_mode(dev, PMIC_OP_GET, &mode); + if (ret) + return ret; + + return mode; +} + +static int ldo_set_mode(struct udevice *dev, int mode) +{ + return max77686_ldo_mode(dev, PMIC_OP_SET, &mode); +} + +static int max77686_buck_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_BUCK; + uc_pdata->mode_count = max77686_buck_modes(dev->driver_data, + &uc_pdata->mode); + + return 0; +} + +static int buck_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = max77686_buck_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int buck_set_value(struct udevice *dev, int uV) +{ + return max77686_buck_val(dev, PMIC_OP_SET, &uV); +} + +static int buck_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = max77686_buck_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int buck_set_enable(struct udevice *dev, bool enable) +{ + return max77686_buck_enable(dev, PMIC_OP_SET, &enable); +} + +static int buck_get_mode(struct udevice *dev) +{ + int mode; + int ret; + + ret = max77686_buck_mode(dev, PMIC_OP_GET, &mode); + if (ret) + return ret; + + return mode; +} + +static int buck_set_mode(struct udevice *dev, int mode) +{ + return max77686_buck_mode(dev, PMIC_OP_SET, &mode); +} + +static const struct dm_regulator_ops max77686_ldo_ops = { + .get_value = ldo_get_value, + .set_value = ldo_set_value, + .get_enable = ldo_get_enable, + .set_enable = ldo_set_enable, + .get_mode = ldo_get_mode, + .set_mode = ldo_set_mode, +}; + +U_BOOT_DRIVER(max77686_ldo) = { + .name = MAX77686_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &max77686_ldo_ops, + .probe = max77686_ldo_probe, +}; + +static const struct dm_regulator_ops max77686_buck_ops = { + .get_value = buck_get_value, + .set_value = buck_set_value, + .get_enable = buck_get_enable, + .set_enable = buck_set_enable, + .get_mode = buck_get_mode, + .set_mode = buck_set_mode, +}; + +U_BOOT_DRIVER(max77686_buck) = { + .name = MAX77686_BUCK_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &max77686_buck_ops, + .probe = max77686_buck_probe, +}; diff --git a/roms/u-boot/drivers/power/regulator/palmas_regulator.c b/roms/u-boot/drivers/power/regulator/palmas_regulator.c new file mode 100644 index 000000000..aaa5f3cfc --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/palmas_regulator.c @@ -0,0 +1,490 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 + * 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/palmas.h> + +#define REGULATOR_ON 0x1 +#define REGULATOR_OFF 0x0 + +#define SMPS_MODE_MASK 0x3 +#define SMPS_MODE_SHIFT 0x0 +#define LDO_MODE_MASK 0x1 +#define LDO_MODE_SHIFT 0x0 + +static const char palmas_smps_ctrl[][PALMAS_SMPS_NUM] = { + {0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38, 0x3c}, + {0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38}, + {0x20, 0x24, 0x2c, 0x30, 0x38}, +}; + +static const char palmas_smps_volt[][PALMAS_SMPS_NUM] = { + {0x23, 0x27, 0x2b, 0x2f, 0x33, 0x37, 0x3b, 0x3c}, + {0x23, 0x27, 0x2b, 0x2f, 0x33, 0x37, 0x3b}, + {0x23, 0x27, 0x2f, 0x33, 0x3B} +}; + +static const char palmas_ldo_ctrl[][PALMAS_LDO_NUM] = { + {0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x62, 0x64}, + {0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x62, 0x64}, + {0x50, 0x52, 0x54, 0x5e, 0x62} +}; + +static const char palmas_ldo_volt[][PALMAS_LDO_NUM] = { + {0x51, 0x53, 0x55, 0x57, 0x59, 0x5b, 0x5d, 0x5f, 0x61, 0x63, 0x65}, + {0x51, 0x53, 0x55, 0x57, 0x59, 0x5b, 0x5d, 0x5f, 0x61, 0x63, 0x65}, + {0x51, 0x53, 0x55, 0x5f, 0x63} +}; + +static int palmas_smps_enable(struct udevice *dev, int op, bool *enable) +{ + int ret; + unsigned int adr; + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + adr = uc_pdata->ctrl_reg; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + ret &= PALMAS_SMPS_STATUS_MASK; + + if (ret) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + if (*enable) + ret |= PALMAS_SMPS_MODE_MASK; + else + ret &= ~(PALMAS_SMPS_MODE_MASK); + + ret = pmic_reg_write(dev->parent, adr, ret); + if (ret) + return ret; + } + + return 0; +} + +static int palmas_smps_volt2hex(int uV) +{ + if (uV > PALMAS_LDO_VOLT_MAX) + return -EINVAL; + + if (uV > 1650000) + return (uV - 1000000) / 20000 + 0x6; + + if (uV == 500000) + return 0x6; + else + return 0x6 + ((uV - 500000) / 10000); +} + +static int palmas_smps_hex2volt(int hex, bool range) +{ + unsigned int uV = 0; + + if (hex > PALMAS_SMPS_VOLT_MAX_HEX) + return -EINVAL; + + if (hex < 0x7) + uV = 500000; + else + uV = 500000 + (hex - 0x6) * 10000; + + if (range) + uV *= 2; + + return uV; +} + +static int palmas_smps_val(struct udevice *dev, int op, int *uV) +{ + unsigned int hex, adr; + int ret; + bool range; + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(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; + + if (op == PMIC_OP_GET) { + if (ret & PALMAS_SMPS_RANGE_MASK) + range = true; + else + range = false; + + ret &= PALMAS_SMPS_VOLT_MASK; + ret = palmas_smps_hex2volt(ret, range); + if (ret < 0) + return ret; + *uV = ret; + + return 0; + } + + hex = palmas_smps_volt2hex(*uV); + if (hex < 0) + return hex; + + ret &= ~PALMAS_SMPS_VOLT_MASK; + ret |= hex; + if (*uV > 1650000) + ret |= PALMAS_SMPS_RANGE_MASK; + + return pmic_reg_write(dev->parent, adr, ret); +} + +static int palmas_ldo_bypass_enable(struct udevice *dev, bool enabled) +{ + int type = dev_get_driver_data(dev_get_parent(dev)); + struct dm_regulator_uclass_plat *p; + unsigned int adr; + int reg; + + if (type == TPS65917) { + /* bypass available only on LDO1 and LDO2 */ + if (dev->driver_data > 2) + return -ENOTSUPP; + } else if (type == TPS659038) { + /* bypass available only on LDO9 */ + if (dev->driver_data != 9) + return -ENOTSUPP; + } + + p = dev_get_uclass_plat(dev); + adr = p->ctrl_reg; + + reg = pmic_reg_read(dev->parent, adr); + if (reg < 0) + return reg; + + if (enabled) + reg |= PALMAS_LDO_BYPASS_EN; + else + reg &= ~PALMAS_LDO_BYPASS_EN; + + return pmic_reg_write(dev->parent, adr, reg); +} + +static int palmas_ldo_enable(struct udevice *dev, int op, bool *enable) +{ + int ret; + unsigned int adr; + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + adr = uc_pdata->ctrl_reg; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + ret &= PALMAS_LDO_STATUS_MASK; + + if (ret) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + if (*enable) + ret |= PALMAS_LDO_MODE_MASK; + else + ret &= ~(PALMAS_LDO_MODE_MASK); + + ret = pmic_reg_write(dev->parent, adr, ret); + if (ret) + return ret; + + ret = palmas_ldo_bypass_enable(dev, false); + if (ret && (ret != -ENOTSUPP)) + return ret; + } + + return 0; +} + +static int palmas_ldo_volt2hex(int uV) +{ + if (uV > PALMAS_LDO_VOLT_MAX) + return -EINVAL; + + return (uV - 850000) / 50000; +} + +static int palmas_ldo_hex2volt(int hex) +{ + if (hex > PALMAS_LDO_VOLT_MAX_HEX) + return -EINVAL; + + if (!hex) + return 0; + + return (hex * 50000) + 850000; +} + +static int palmas_ldo_val(struct udevice *dev, int op, int *uV) +{ + unsigned int hex, adr; + int ret; + + struct dm_regulator_uclass_plat *uc_pdata; + + if (op == PMIC_OP_GET) + *uV = 0; + + uc_pdata = dev_get_uclass_plat(dev); + + adr = uc_pdata->volt_reg; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + ret &= PALMAS_LDO_VOLT_MASK; + ret = palmas_ldo_hex2volt(ret); + if (ret < 0) + return ret; + *uV = ret; + return 0; + } + + hex = palmas_ldo_volt2hex(*uV); + if (hex < 0) + return hex; + + ret &= ~PALMAS_LDO_VOLT_MASK; + ret |= hex; + if (*uV > 1650000) + ret |= 0x80; + + return pmic_reg_write(dev->parent, adr, ret); +} + +static int palmas_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + struct udevice *parent; + + uc_pdata = dev_get_uclass_plat(dev); + + parent = dev_get_parent(dev); + int type = dev_get_driver_data(parent); + + uc_pdata->type = REGULATOR_TYPE_LDO; + + if (dev->driver_data) { + u8 idx = dev->driver_data - 1; + uc_pdata->ctrl_reg = palmas_ldo_ctrl[type][idx]; + uc_pdata->volt_reg = palmas_ldo_volt[type][idx]; + } else { + /* check for ldoln and ldousb cases */ + if (!strcmp("ldoln", dev->name)) { + uc_pdata->ctrl_reg = palmas_ldo_ctrl[type][9]; + uc_pdata->volt_reg = palmas_ldo_volt[type][9]; + } else if (!strcmp("ldousb", dev->name)) { + uc_pdata->ctrl_reg = palmas_ldo_ctrl[type][10]; + uc_pdata->volt_reg = palmas_ldo_volt[type][10]; + } + } + + return 0; +} + +static int ldo_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = palmas_ldo_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int ldo_set_value(struct udevice *dev, int uV) +{ + return palmas_ldo_val(dev, PMIC_OP_SET, &uV); +} + +static int ldo_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = palmas_ldo_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int ldo_set_enable(struct udevice *dev, bool enable) +{ + return palmas_ldo_enable(dev, PMIC_OP_SET, &enable); +} + +static int palmas_smps_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + struct udevice *parent; + int idx; + + uc_pdata = dev_get_uclass_plat(dev); + + parent = dev_get_parent(dev); + int type = dev_get_driver_data(parent); + + uc_pdata->type = REGULATOR_TYPE_BUCK; + + switch (type) { + case PALMAS: + case TPS659038: + switch (dev->driver_data) { + case 123: + case 12: + uc_pdata->ctrl_reg = palmas_smps_ctrl[type][0]; + uc_pdata->volt_reg = palmas_smps_volt[type][0]; + break; + case 3: + uc_pdata->ctrl_reg = palmas_smps_ctrl[type][1]; + uc_pdata->volt_reg = palmas_smps_volt[type][1]; + break; + case 45: + uc_pdata->ctrl_reg = palmas_smps_ctrl[type][2]; + uc_pdata->volt_reg = palmas_smps_volt[type][2]; + break; + case 6: + case 7: + case 8: + case 9: + case 10: + idx = dev->driver_data - 3; + uc_pdata->ctrl_reg = palmas_smps_ctrl[type][idx]; + uc_pdata->volt_reg = palmas_smps_volt[type][idx]; + break; + + default: + printf("Wrong ID for regulator\n"); + } + break; + + case TPS65917: + switch (dev->driver_data) { + case 1: + case 2: + case 3: + case 4: + case 5: + idx = dev->driver_data - 1; + uc_pdata->ctrl_reg = palmas_smps_ctrl[type][idx]; + uc_pdata->volt_reg = palmas_smps_volt[type][idx]; + break; + case 12: + idx = 0; + uc_pdata->ctrl_reg = palmas_smps_ctrl[type][idx]; + uc_pdata->volt_reg = palmas_smps_volt[type][idx]; + break; + default: + printf("Wrong ID for regulator\n"); + } + break; + + default: + printf("Invalid PMIC ID\n"); + } + + return 0; +} + +static int smps_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = palmas_smps_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int smps_set_value(struct udevice *dev, int uV) +{ + return palmas_smps_val(dev, PMIC_OP_SET, &uV); +} + +static int smps_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = palmas_smps_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int smps_set_enable(struct udevice *dev, bool enable) +{ + return palmas_smps_enable(dev, PMIC_OP_SET, &enable); +} + +static const struct dm_regulator_ops palmas_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(palmas_ldo) = { + .name = PALMAS_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &palmas_ldo_ops, + .probe = palmas_ldo_probe, +}; + +static const struct dm_regulator_ops palmas_smps_ops = { + .get_value = smps_get_value, + .set_value = smps_set_value, + .get_enable = smps_get_enable, + .set_enable = smps_set_enable, +}; + +U_BOOT_DRIVER(palmas_smps) = { + .name = PALMAS_SMPS_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &palmas_smps_ops, + .probe = palmas_smps_probe, +}; diff --git a/roms/u-boot/drivers/power/regulator/pbias_regulator.c b/roms/u-boot/drivers/power/regulator/pbias_regulator.c new file mode 100644 index 000000000..5bf186e4d --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/pbias_regulator.c @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 Texas Instruments Incorporated, <www.ti.com> + * Jean-Jacques Hiblot <jjhiblot@ti.com> + */ + +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <log.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <regmap.h> +#include <syscon.h> +#include <linux/bitops.h> +#include <linux/ioport.h> +#include <dm/device-internal.h> +#include <dm/read.h> +#ifdef CONFIG_MMC_OMAP36XX_PINS +#include <asm/arch/sys_proto.h> +#include <asm/io.h> +#include <asm/arch/mux.h> +#endif + +struct pbias_reg_info { + u32 enable; + u32 enable_mask; + u32 disable_val; + u32 vmode; + unsigned int enable_time; + char *name; +}; + +struct pbias_priv { + struct regmap *regmap; + int offset; +}; + +static const struct pmic_child_info pmic_children_info[] = { + { .prefix = "pbias", .driver = "pbias_regulator"}, + { }, +}; + +static int pbias_write(struct udevice *dev, uint reg, const uint8_t *buff, + int len) +{ + struct pbias_priv *priv = dev_get_priv(dev); + u32 val = *(u32 *)buff; + + if (len != 4) + return -EINVAL; + + return regmap_write(priv->regmap, priv->offset, val); +} + +static int pbias_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{ + struct pbias_priv *priv = dev_get_priv(dev); + + if (len != 4) + return -EINVAL; + + return regmap_read(priv->regmap, priv->offset, (u32 *)buff); +} + +static int pbias_of_to_plat(struct udevice *dev) +{ + struct pbias_priv *priv = dev_get_priv(dev); + struct udevice *syscon; + struct regmap *regmap; + struct resource res; + int err; + + err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, + "syscon", &syscon); + if (err) { + pr_err("%s: unable to find syscon device (%d)\n", __func__, + err); + return err; + } + + regmap = syscon_get_regmap(syscon); + if (IS_ERR(regmap)) { + pr_err("%s: unable to find regmap (%ld)\n", __func__, + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + priv->regmap = regmap; + + err = dev_read_resource(dev, 0, &res); + if (err) { + pr_err("%s: unable to find offset (%d)\n", __func__, err); + return err; + } + priv->offset = res.start; + + return 0; +} + +static int pbias_bind(struct udevice *dev) +{ + int children; + + children = pmic_bind_children(dev, dev_ofnode(dev), + pmic_children_info); + if (!children) + debug("%s: %s - no child found\n", __func__, dev->name); + + return 0; +} + +static struct dm_pmic_ops pbias_ops = { + .read = pbias_read, + .write = pbias_write, +}; + +static const struct udevice_id pbias_ids[] = { + { .compatible = "ti,pbias-dra7" }, + { .compatible = "ti,pbias-omap2" }, + { .compatible = "ti,pbias-omap3" }, + { .compatible = "ti,pbias-omap4" }, + { .compatible = "ti,pbias-omap5" }, + { } +}; + +U_BOOT_DRIVER(pbias_pmic) = { + .name = "pbias_pmic", + .id = UCLASS_PMIC, + .of_match = pbias_ids, + .bind = pbias_bind, + .ops = &pbias_ops, + .of_to_plat = pbias_of_to_plat, + .priv_auto = sizeof(struct pbias_priv), +}; + +static const struct pbias_reg_info pbias_mmc_omap2430 = { + .enable = BIT(1), + .enable_mask = BIT(1), + .vmode = BIT(0), + .disable_val = 0, + .enable_time = 100, + .name = "pbias_mmc_omap2430" +}; + +static const struct pbias_reg_info pbias_sim_omap3 = { + .enable = BIT(9), + .enable_mask = BIT(9), + .vmode = BIT(8), + .enable_time = 100, + .name = "pbias_sim_omap3" +}; + +static const struct pbias_reg_info pbias_mmc_omap4 = { + .enable = BIT(26) | BIT(22), + .enable_mask = BIT(26) | BIT(25) | BIT(22), + .disable_val = BIT(25), + .vmode = BIT(21), + .enable_time = 100, + .name = "pbias_mmc_omap4" +}; + +static const struct pbias_reg_info pbias_mmc_omap5 = { + .enable = BIT(27) | BIT(26), + .enable_mask = BIT(27) | BIT(25) | BIT(26), + .disable_val = BIT(25), + .vmode = BIT(21), + .enable_time = 100, + .name = "pbias_mmc_omap5" +}; + +static const struct pbias_reg_info *pbias_reg_infos[] = { + &pbias_mmc_omap5, + &pbias_mmc_omap4, + &pbias_sim_omap3, + &pbias_mmc_omap2430, + NULL +}; + +static int pbias_regulator_probe(struct udevice *dev) +{ + const struct pbias_reg_info **p = pbias_reg_infos; + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + + while (*p) { + int rc; + + rc = dev_read_stringlist_search(dev, "regulator-name", + (*p)->name); + if (rc >= 0) { + debug("found regulator %s\n", (*p)->name); + break; + } else if (rc != -ENODATA) { + return rc; + } + p++; + } + if (!*p) { + int i = 0; + const char *s; + + debug("regulator "); + while (dev_read_string_index(dev, "regulator-name", i++, &s) >= 0) + debug("%s'%s' ", (i > 1) ? ", " : "", s); + debug("%s not supported\n", (i > 2) ? "are" : "is"); + return -EINVAL; + } + + uc_pdata->type = REGULATOR_TYPE_OTHER; + dev_set_priv(dev, (void *)*p); + + return 0; +} + +static int pbias_regulator_get_value(struct udevice *dev) +{ + const struct pbias_reg_info *p = dev_get_priv(dev); + int rc; + u32 reg; + + rc = pmic_read(dev->parent, 0, (uint8_t *)®, sizeof(reg)); + if (rc) + return rc; + + debug("%s voltage id %s\n", p->name, + (reg & p->vmode) ? "3.0v" : "1.8v"); + return (reg & p->vmode) ? 3000000 : 1800000; +} + +static int pbias_regulator_set_value(struct udevice *dev, int uV) +{ + const struct pbias_reg_info *p = dev_get_priv(dev); + int rc, ret; + u32 reg; +#ifdef CONFIG_MMC_OMAP36XX_PINS + u32 wkup_ctrl = readl(OMAP34XX_CTRL_WKUP_CTRL); +#endif + + rc = pmic_read(dev->parent, 0, (uint8_t *)®, sizeof(reg)); + if (rc) + return rc; + + if (uV == 3300000) + reg |= p->vmode; + else if (uV == 1800000) + reg &= ~p->vmode; + else + return -EINVAL; + + debug("Setting %s voltage to %s\n", p->name, + (reg & p->vmode) ? "3.0v" : "1.8v"); + +#ifdef CONFIG_MMC_OMAP36XX_PINS + if (get_cpu_family() == CPU_OMAP36XX) { + /* Disable extended drain IO before changing PBIAS */ + wkup_ctrl &= ~OMAP34XX_CTRL_WKUP_CTRL_GPIO_IO_PWRDNZ; + writel(wkup_ctrl, OMAP34XX_CTRL_WKUP_CTRL); + } +#endif + ret = pmic_write(dev->parent, 0, (uint8_t *)®, sizeof(reg)); +#ifdef CONFIG_MMC_OMAP36XX_PINS + if (get_cpu_family() == CPU_OMAP36XX) { + /* Enable extended drain IO after changing PBIAS */ + writel(wkup_ctrl | + OMAP34XX_CTRL_WKUP_CTRL_GPIO_IO_PWRDNZ, + OMAP34XX_CTRL_WKUP_CTRL); + } +#endif + return ret; +} + +static int pbias_regulator_get_enable(struct udevice *dev) +{ + const struct pbias_reg_info *p = dev_get_priv(dev); + int rc; + u32 reg; + + rc = pmic_read(dev->parent, 0, (uint8_t *)®, sizeof(reg)); + if (rc) + return rc; + + debug("%s id %s\n", p->name, + (reg & p->enable_mask) == (p->disable_val) ? "on" : "off"); + + return (reg & p->enable_mask) == (p->disable_val); +} + +static int pbias_regulator_set_enable(struct udevice *dev, bool enable) +{ + const struct pbias_reg_info *p = dev_get_priv(dev); + int rc; + u32 reg; +#ifdef CONFIG_MMC_OMAP36XX_PINS + u32 wkup_ctrl = readl(OMAP34XX_CTRL_WKUP_CTRL); +#endif + + debug("Turning %s %s\n", enable ? "on" : "off", p->name); + +#ifdef CONFIG_MMC_OMAP36XX_PINS + if (get_cpu_family() == CPU_OMAP36XX) { + /* Disable extended drain IO before changing PBIAS */ + wkup_ctrl &= ~OMAP34XX_CTRL_WKUP_CTRL_GPIO_IO_PWRDNZ; + writel(wkup_ctrl, OMAP34XX_CTRL_WKUP_CTRL); + } +#endif + + rc = pmic_read(dev->parent, 0, (uint8_t *)®, sizeof(reg)); + if (rc) + return rc; + + reg &= ~p->enable_mask; + if (enable) + reg |= p->enable; + else + reg |= p->disable_val; + + rc = pmic_write(dev->parent, 0, (uint8_t *)®, sizeof(reg)); + +#ifdef CONFIG_MMC_OMAP36XX_PINS + if (get_cpu_family() == CPU_OMAP36XX) { + /* Enable extended drain IO after changing PBIAS */ + writel(wkup_ctrl | + OMAP34XX_CTRL_WKUP_CTRL_GPIO_IO_PWRDNZ, + OMAP34XX_CTRL_WKUP_CTRL); + } +#endif + + if (rc) + return rc; + + if (enable) + udelay(p->enable_time); + + return 0; +} + +static const struct dm_regulator_ops pbias_regulator_ops = { + .get_value = pbias_regulator_get_value, + .set_value = pbias_regulator_set_value, + .get_enable = pbias_regulator_get_enable, + .set_enable = pbias_regulator_set_enable, +}; + +U_BOOT_DRIVER(pbias_regulator) = { + .name = "pbias_regulator", + .id = UCLASS_REGULATOR, + .ops = &pbias_regulator_ops, + .probe = pbias_regulator_probe, +}; diff --git a/roms/u-boot/drivers/power/regulator/pfuze100.c b/roms/u-boot/drivers/power/regulator/pfuze100.c new file mode 100644 index 000000000..698a6fa59 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/pfuze100.c @@ -0,0 +1,576 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2017 NXP + * + * Peng Fan <peng.fan@nxp.com> + */ + +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <log.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/pfuze100_pmic.h> + +/** + * struct pfuze100_regulator_desc - regulator descriptor + * + * @name: Identify name for the regulator. + * @type: Indicates the regulator type. + * @uV_step: Voltage increase for each selector. + * @vsel_reg: Register for adjust regulator voltage for normal. + * @vsel_mask: Mask bit for setting regulator voltage for normal. + * @stby_reg: Register for adjust regulator voltage for standby. + * @stby_mask: Mask bit for setting regulator voltage for standby. + * @volt_table: Voltage mapping table (if table based mapping). + * @voltage: Current voltage for REGULATOR_TYPE_FIXED type regulator. + */ +struct pfuze100_regulator_desc { + char *name; + enum regulator_type type; + unsigned int uV_step; + unsigned int vsel_reg; + unsigned int vsel_mask; + unsigned int stby_reg; + unsigned int stby_mask; + unsigned int *volt_table; + unsigned int voltage; +}; + +/** + * struct pfuze100_regulator_plat - platform data for pfuze100 + * + * @desc: Points the description entry of one regulator of pfuze100 + */ +struct pfuze100_regulator_plat { + struct pfuze100_regulator_desc *desc; +}; + +#define PFUZE100_FIXED_REG(_name, base, vol) \ + { \ + .name = #_name, \ + .type = REGULATOR_TYPE_FIXED, \ + .voltage = (vol), \ + } + +#define PFUZE100_SW_REG(_name, base, step) \ + { \ + .name = #_name, \ + .type = REGULATOR_TYPE_BUCK, \ + .uV_step = (step), \ + .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \ + .vsel_mask = 0x3F, \ + .stby_reg = (base) + PFUZE100_STBY_OFFSET, \ + .stby_mask = 0x3F, \ + } + +#define PFUZE100_SWB_REG(_name, base, mask, step, voltages) \ + { \ + .name = #_name, \ + .type = REGULATOR_TYPE_BUCK, \ + .uV_step = (step), \ + .vsel_reg = (base), \ + .vsel_mask = (mask), \ + .volt_table = (voltages), \ + } + +#define PFUZE100_SNVS_REG(_name, base, mask, voltages) \ + { \ + .name = #_name, \ + .type = REGULATOR_TYPE_OTHER, \ + .vsel_reg = (base), \ + .vsel_mask = (mask), \ + .volt_table = (voltages), \ + } + +#define PFUZE100_VGEN_REG(_name, base, step) \ + { \ + .name = #_name, \ + .type = REGULATOR_TYPE_LDO, \ + .uV_step = (step), \ + .vsel_reg = (base), \ + .vsel_mask = 0xF, \ + .stby_reg = (base), \ + .stby_mask = 0x20, \ + } + +#define PFUZE3000_VCC_REG(_name, base, step) \ + { \ + .name = #_name, \ + .type = REGULATOR_TYPE_LDO, \ + .uV_step = (step), \ + .vsel_reg = (base), \ + .vsel_mask = 0x3, \ + .stby_reg = (base), \ + .stby_mask = 0x20, \ +} + +#define PFUZE3000_SW1_REG(_name, base, step) \ + { \ + .name = #_name, \ + .type = REGULATOR_TYPE_BUCK, \ + .uV_step = (step), \ + .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \ + .vsel_mask = 0x1F, \ + .stby_reg = (base) + PFUZE100_STBY_OFFSET, \ + .stby_mask = 0x1F, \ + } + +#define PFUZE3000_SW2_REG(_name, base, step) \ + { \ + .name = #_name, \ + .type = REGULATOR_TYPE_BUCK, \ + .uV_step = (step), \ + .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \ + .vsel_mask = 0x7, \ + .stby_reg = (base) + PFUZE100_STBY_OFFSET, \ + .stby_mask = 0x7, \ + } + +#define PFUZE3000_SW3_REG(_name, base, step) \ + { \ + .name = #_name, \ + .type = REGULATOR_TYPE_BUCK, \ + .uV_step = (step), \ + .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \ + .vsel_mask = 0xF, \ + .stby_reg = (base) + PFUZE100_STBY_OFFSET, \ + .stby_mask = 0xF, \ + } + +static unsigned int pfuze100_swbst[] = { + 5000000, 5050000, 5100000, 5150000 +}; + +static unsigned int pfuze100_vsnvs[] = { + 1000000, 1100000, 1200000, 1300000, 1500000, 1800000, 3000000, -1 +}; + +static unsigned int pfuze3000_vsnvs[] = { + -1, -1, -1, -1, -1, -1, 3000000, -1 +}; + +static unsigned int pfuze3000_sw2lo[] = { + 1500000, 1550000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000 +}; + +/* PFUZE100 */ +static struct pfuze100_regulator_desc pfuze100_regulators[] = { + PFUZE100_SW_REG(sw1ab, PFUZE100_SW1ABVOL, 25000), + PFUZE100_SW_REG(sw1c, PFUZE100_SW1CVOL, 25000), + PFUZE100_SW_REG(sw2, PFUZE100_SW2VOL, 25000), + PFUZE100_SW_REG(sw3a, PFUZE100_SW3AVOL, 25000), + PFUZE100_SW_REG(sw3b, PFUZE100_SW3BVOL, 25000), + PFUZE100_SW_REG(sw4, PFUZE100_SW4VOL, 25000), + PFUZE100_SWB_REG(swbst, PFUZE100_SWBSTCON1, 0x3, 50000, pfuze100_swbst), + PFUZE100_SNVS_REG(vsnvs, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs), + PFUZE100_FIXED_REG(vrefddr, PFUZE100_VREFDDRCON, 750000), + PFUZE100_VGEN_REG(vgen1, PFUZE100_VGEN1VOL, 50000), + PFUZE100_VGEN_REG(vgen2, PFUZE100_VGEN2VOL, 50000), + PFUZE100_VGEN_REG(vgen3, PFUZE100_VGEN3VOL, 100000), + PFUZE100_VGEN_REG(vgen4, PFUZE100_VGEN4VOL, 100000), + PFUZE100_VGEN_REG(vgen5, PFUZE100_VGEN5VOL, 100000), + PFUZE100_VGEN_REG(vgen6, PFUZE100_VGEN6VOL, 100000), +}; + +/* PFUZE200 */ +static struct pfuze100_regulator_desc pfuze200_regulators[] = { + PFUZE100_SW_REG(sw1ab, PFUZE100_SW1ABVOL, 25000), + PFUZE100_SW_REG(sw2, PFUZE100_SW2VOL, 25000), + PFUZE100_SW_REG(sw3a, PFUZE100_SW3AVOL, 25000), + PFUZE100_SW_REG(sw3b, PFUZE100_SW3BVOL, 25000), + PFUZE100_SWB_REG(swbst, PFUZE100_SWBSTCON1, 0x3, 50000, pfuze100_swbst), + PFUZE100_SNVS_REG(vsnvs, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs), + PFUZE100_FIXED_REG(vrefddr, PFUZE100_VREFDDRCON, 750000), + PFUZE100_VGEN_REG(vgen1, PFUZE100_VGEN1VOL, 50000), + PFUZE100_VGEN_REG(vgen2, PFUZE100_VGEN2VOL, 50000), + PFUZE100_VGEN_REG(vgen3, PFUZE100_VGEN3VOL, 100000), + PFUZE100_VGEN_REG(vgen4, PFUZE100_VGEN4VOL, 100000), + PFUZE100_VGEN_REG(vgen5, PFUZE100_VGEN5VOL, 100000), + PFUZE100_VGEN_REG(vgen6, PFUZE100_VGEN6VOL, 100000), +}; + +/* PFUZE3000 */ +static struct pfuze100_regulator_desc pfuze3000_regulators[] = { + PFUZE3000_SW1_REG(sw1a, PFUZE100_SW1ABVOL, 25000), + PFUZE3000_SW1_REG(sw1b, PFUZE100_SW1CVOL, 25000), + PFUZE100_SWB_REG(sw2, PFUZE100_SW2VOL, 0x7, 50000, pfuze3000_sw2lo), + PFUZE3000_SW3_REG(sw3, PFUZE100_SW3AVOL, 50000), + PFUZE100_SWB_REG(swbst, PFUZE100_SWBSTCON1, 0x3, 50000, pfuze100_swbst), + PFUZE100_SNVS_REG(vsnvs, PFUZE100_VSNVSVOL, 0x7, pfuze3000_vsnvs), + PFUZE100_FIXED_REG(vrefddr, PFUZE100_VREFDDRCON, 750000), + PFUZE100_VGEN_REG(vldo1, PFUZE100_VGEN1VOL, 100000), + PFUZE100_VGEN_REG(vldo2, PFUZE100_VGEN2VOL, 50000), + PFUZE3000_VCC_REG(vccsd, PFUZE100_VGEN3VOL, 150000), + PFUZE3000_VCC_REG(v33, PFUZE100_VGEN4VOL, 150000), + PFUZE100_VGEN_REG(vldo3, PFUZE100_VGEN5VOL, 100000), + PFUZE100_VGEN_REG(vldo4, PFUZE100_VGEN6VOL, 100000), +}; + +#define MODE(_id, _val, _name) { \ + .id = _id, \ + .register_value = _val, \ + .name = _name, \ +} + +/* SWx Buck regulator mode */ +static struct dm_regulator_mode pfuze_sw_modes[] = { + MODE(OFF_OFF, OFF_OFF, "OFF_OFF"), + MODE(PWM_OFF, PWM_OFF, "PWM_OFF"), + MODE(PFM_OFF, PFM_OFF, "PFM_OFF"), + MODE(APS_OFF, APS_OFF, "APS_OFF"), + MODE(PWM_PWM, PWM_PWM, "PWM_PWM"), + MODE(PWM_APS, PWM_APS, "PWM_APS"), + MODE(APS_APS, APS_APS, "APS_APS"), + MODE(APS_PFM, APS_PFM, "APS_PFM"), + MODE(PWM_PFM, PWM_PFM, "PWM_PFM"), +}; + +/* Boost Buck regulator mode for normal operation */ +static struct dm_regulator_mode pfuze_swbst_modes[] = { + MODE(SWBST_MODE_OFF, SWBST_MODE_OFF , "SWBST_MODE_OFF"), + MODE(SWBST_MODE_PFM, SWBST_MODE_PFM, "SWBST_MODE_PFM"), + MODE(SWBST_MODE_AUTO, SWBST_MODE_AUTO, "SWBST_MODE_AUTO"), + MODE(SWBST_MODE_APS, SWBST_MODE_APS, "SWBST_MODE_APS"), +}; + +/* VGENx LDO regulator mode for normal operation */ +static struct dm_regulator_mode pfuze_ldo_modes[] = { + MODE(LDO_MODE_OFF, LDO_MODE_OFF, "LDO_MODE_OFF"), + MODE(LDO_MODE_ON, LDO_MODE_ON, "LDO_MODE_ON"), +}; + +static struct pfuze100_regulator_desc *se_desc(struct pfuze100_regulator_desc *desc, + int size, + const char *name) +{ + int i; + + for (i = 0; i < size; desc++) { + if (!strcmp(desc->name, name)) + return desc; + continue; + } + + return NULL; +} + +static int pfuze100_regulator_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + struct pfuze100_regulator_plat *plat = dev_get_plat(dev); + struct pfuze100_regulator_desc *desc; + + switch (dev_get_driver_data(dev_get_parent(dev))) { + case PFUZE100: + desc = se_desc(pfuze100_regulators, + ARRAY_SIZE(pfuze100_regulators), + dev->name); + break; + case PFUZE200: + desc = se_desc(pfuze200_regulators, + ARRAY_SIZE(pfuze200_regulators), + dev->name); + break; + case PFUZE3000: + desc = se_desc(pfuze3000_regulators, + ARRAY_SIZE(pfuze3000_regulators), + dev->name); + break; + default: + debug("Unsupported PFUZE\n"); + return -EINVAL; + } + if (!desc) { + debug("Do not support regulator %s\n", dev->name); + return -EINVAL; + } + + plat->desc = desc; + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = desc->type; + if (uc_pdata->type == REGULATOR_TYPE_BUCK) { + if (!strcmp(dev->name, "swbst")) { + uc_pdata->mode = pfuze_swbst_modes; + uc_pdata->mode_count = ARRAY_SIZE(pfuze_swbst_modes); + } else { + uc_pdata->mode = pfuze_sw_modes; + uc_pdata->mode_count = ARRAY_SIZE(pfuze_sw_modes); + } + } else if (uc_pdata->type == REGULATOR_TYPE_LDO) { + uc_pdata->mode = pfuze_ldo_modes; + uc_pdata->mode_count = ARRAY_SIZE(pfuze_ldo_modes); + } else { + uc_pdata->mode = NULL; + uc_pdata->mode_count = 0; + } + + return 0; +} + +static int pfuze100_regulator_mode(struct udevice *dev, int op, int *opmode) +{ + int val; + struct pfuze100_regulator_plat *plat = dev_get_plat(dev); + struct pfuze100_regulator_desc *desc = plat->desc; + + if (op == PMIC_OP_GET) { + if (desc->type == REGULATOR_TYPE_BUCK) { + if (!strcmp(dev->name, "swbst")) { + val = pmic_reg_read(dev->parent, + desc->vsel_reg); + if (val < 0) + return val; + + val &= SWBST_MODE_MASK; + val >>= SWBST_MODE_SHIFT; + *opmode = val; + + return 0; + } + val = pmic_reg_read(dev->parent, + desc->vsel_reg + + PFUZE100_MODE_OFFSET); + if (val < 0) + return val; + + val &= SW_MODE_MASK; + val >>= SW_MODE_SHIFT; + *opmode = val; + + return 0; + + } else if (desc->type == REGULATOR_TYPE_LDO) { + val = pmic_reg_read(dev->parent, desc->vsel_reg); + if (val < 0) + return val; + + val &= LDO_MODE_MASK; + val >>= LDO_MODE_SHIFT; + *opmode = val; + + return 0; + } else { + return -EINVAL; + } + } + + if (desc->type == REGULATOR_TYPE_BUCK) { + if (!strcmp(dev->name, "swbst")) + return pmic_clrsetbits(dev->parent, desc->vsel_reg, + SWBST_MODE_MASK, + *opmode << SWBST_MODE_SHIFT); + + val = pmic_clrsetbits(dev->parent, + desc->vsel_reg + PFUZE100_MODE_OFFSET, + SW_MODE_MASK, + *opmode << SW_MODE_SHIFT); + + } else if (desc->type == REGULATOR_TYPE_LDO) { + val = pmic_clrsetbits(dev->parent, desc->vsel_reg, + LDO_MODE_MASK, + *opmode << LDO_MODE_SHIFT); + return val; + } else { + return -EINVAL; + } + + return 0; +} + +static int pfuze100_regulator_enable(struct udevice *dev, int op, bool *enable) +{ + int val; + int ret, on_off; + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + + if (op == PMIC_OP_GET) { + if (!strcmp(dev->name, "vrefddr")) { + val = pmic_reg_read(dev->parent, PFUZE100_VREFDDRCON); + if (val < 0) + return val; + + if (val & VREFDDRCON_EN) + *enable = true; + else + *enable = false; + return 0; + } + ret = pfuze100_regulator_mode(dev, op, &on_off); + if (ret) + return ret; + switch (on_off) { + /* OFF_OFF, SWBST_MODE_OFF, LDO_MODE_OFF have same value */ + case OFF_OFF: + *enable = false; + break; + default: + *enable = true; + break; + } + } else if (op == PMIC_OP_SET) { + if (!strcmp(dev->name, "vrefddr")) { + val = pmic_reg_read(dev->parent, PFUZE100_VREFDDRCON); + if (val < 0) + return val; + + if (val & VREFDDRCON_EN) + return 0; + val |= VREFDDRCON_EN; + + return pmic_reg_write(dev->parent, PFUZE100_VREFDDRCON, + val); + } + + if (uc_pdata->type == REGULATOR_TYPE_LDO) { + on_off = *enable ? LDO_MODE_ON : LDO_MODE_OFF; + } else if (uc_pdata->type == REGULATOR_TYPE_BUCK) { + if (!strcmp(dev->name, "swbst")) + on_off = *enable ? SWBST_MODE_AUTO : + SWBST_MODE_OFF; + else + on_off = *enable ? APS_PFM : OFF_OFF; + } else { + return -EINVAL; + } + + return pfuze100_regulator_mode(dev, op, &on_off); + } + + return 0; +} + +static int pfuze100_regulator_val(struct udevice *dev, int op, int *uV) +{ + int i; + int val; + struct pfuze100_regulator_plat *plat = dev_get_plat(dev); + struct pfuze100_regulator_desc *desc = plat->desc; + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + + if (op == PMIC_OP_GET) { + *uV = 0; + if (uc_pdata->type == REGULATOR_TYPE_FIXED) { + *uV = desc->voltage; + } else if (desc->volt_table) { + val = pmic_reg_read(dev->parent, desc->vsel_reg); + if (val < 0) + return val; + val &= desc->vsel_mask; + *uV = desc->volt_table[val]; + } else { + if (uc_pdata->min_uV < 0) { + debug("Need to provide min_uV in dts.\n"); + return -EINVAL; + } + val = pmic_reg_read(dev->parent, desc->vsel_reg); + if (val < 0) + return val; + val &= desc->vsel_mask; + *uV = uc_pdata->min_uV + (int)val * desc->uV_step; + } + + return 0; + } + + if (uc_pdata->type == REGULATOR_TYPE_FIXED) { + debug("Set voltage for REGULATOR_TYPE_FIXED regulator\n"); + return -EINVAL; + } else if (desc->volt_table) { + for (i = 0; i <= desc->vsel_mask; i++) { + if (*uV == desc->volt_table[i]) + break; + } + if (i == desc->vsel_mask + 1) { + debug("Unsupported voltage %u\n", *uV); + return -EINVAL; + } + + return pmic_clrsetbits(dev->parent, desc->vsel_reg, + desc->vsel_mask, i); + } else { + if (uc_pdata->min_uV < 0) { + debug("Need to provide min_uV in dts.\n"); + return -EINVAL; + } + return pmic_clrsetbits(dev->parent, desc->vsel_reg, + desc->vsel_mask, + (*uV - uc_pdata->min_uV) / desc->uV_step); + } + + return 0; +} + +static int pfuze100_regulator_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = pfuze100_regulator_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int pfuze100_regulator_set_value(struct udevice *dev, int uV) +{ + return pfuze100_regulator_val(dev, PMIC_OP_SET, &uV); +} + +static int pfuze100_regulator_get_enable(struct udevice *dev) +{ + int ret; + bool enable = false; + + ret = pfuze100_regulator_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int pfuze100_regulator_set_enable(struct udevice *dev, bool enable) +{ + return pfuze100_regulator_enable(dev, PMIC_OP_SET, &enable); +} + +static int pfuze100_regulator_get_mode(struct udevice *dev) +{ + int mode; + int ret; + + ret = pfuze100_regulator_mode(dev, PMIC_OP_GET, &mode); + if (ret) + return ret; + + return mode; +} + +static int pfuze100_regulator_set_mode(struct udevice *dev, int mode) +{ + return pfuze100_regulator_mode(dev, PMIC_OP_SET, &mode); +} + +static const struct dm_regulator_ops pfuze100_regulator_ops = { + .get_value = pfuze100_regulator_get_value, + .set_value = pfuze100_regulator_set_value, + .get_enable = pfuze100_regulator_get_enable, + .set_enable = pfuze100_regulator_set_enable, + .get_mode = pfuze100_regulator_get_mode, + .set_mode = pfuze100_regulator_set_mode, +}; + +U_BOOT_DRIVER(pfuze100_regulator) = { + .name = "pfuze100_regulator", + .id = UCLASS_REGULATOR, + .ops = &pfuze100_regulator_ops, + .probe = pfuze100_regulator_probe, + .plat_auto = sizeof(struct pfuze100_regulator_plat), +}; diff --git a/roms/u-boot/drivers/power/regulator/pwm_regulator.c b/roms/u-boot/drivers/power/regulator/pwm_regulator.c new file mode 100644 index 000000000..ca59f3ae3 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/pwm_regulator.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Rockchip Electronics Co., Ltd + * + * Based on kernel drivers/regulator/pwm-regulator.c + * Copyright (C) 2014 - STMicroelectronics Inc. + * Author: Lee Jones <lee.jones@linaro.org> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <pwm.h> +#include <asm/global_data.h> +#include <dm/device_compat.h> +#include <power/regulator.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct pwm_regulator_info { + /* pwm id corresponding to the PWM driver */ + int pwm_id; + /* the period of one PWM cycle */ + int period_ns; + /* + * the polarity of one PWM + * 0: normal polarity + * 1: inverted polarity + */ + bool polarity; + struct udevice *pwm; + /* initialize voltage of regulator */ + int init_voltage; + /* the maximum voltage of regulator */ + int max_voltage; + /* the minimum voltage of regulator */ + int min_voltage; + /* the current voltage of regulator */ + int volt_uV; +}; + +static int pwm_regulator_enable(struct udevice *dev, bool enable) +{ + struct pwm_regulator_info *priv = dev_get_priv(dev); + + return pwm_set_enable(priv->pwm, priv->pwm_id, enable); +} + +static int pwm_voltage_to_duty_cycle_percentage(struct udevice *dev, int req_uV) +{ + struct pwm_regulator_info *priv = dev_get_priv(dev); + int min_uV = priv->min_voltage; + int max_uV = priv->max_voltage; + int diff = max_uV - min_uV; + + return ((req_uV * 100) - (min_uV * 100)) / diff; +} + +static int pwm_regulator_get_voltage(struct udevice *dev) +{ + struct pwm_regulator_info *priv = dev_get_priv(dev); + + return priv->volt_uV; +} + +static int pwm_regulator_set_voltage(struct udevice *dev, int uvolt) +{ + struct pwm_regulator_info *priv = dev_get_priv(dev); + int duty_cycle; + int ret = 0; + + duty_cycle = pwm_voltage_to_duty_cycle_percentage(dev, uvolt); + + ret = pwm_set_invert(priv->pwm, priv->pwm_id, priv->polarity); + if (ret) { + dev_err(dev, "Failed to init PWM\n"); + return ret; + } + + ret = pwm_set_config(priv->pwm, priv->pwm_id, + priv->period_ns, (priv->period_ns / 100) * duty_cycle); + if (ret) { + dev_err(dev, "Failed to configure PWM\n"); + return ret; + } + + priv->volt_uV = uvolt; + + return ret; +} + +static int pwm_regulator_of_to_plat(struct udevice *dev) +{ + struct pwm_regulator_info *priv = dev_get_priv(dev); + struct ofnode_phandle_args args; + int ret; + + ret = dev_read_phandle_with_args(dev, "pwms", "#pwm-cells", 0, 0, &args); + if (ret) { + debug("%s: Cannot get PWM phandle: ret=%d\n", __func__, ret); + return ret; + } + + priv->period_ns = args.args[1]; + priv->polarity = args.args[2]; + + priv->init_voltage = dev_read_u32_default(dev, "regulator-init-microvolt", -1); + if (priv->init_voltage < 0) { + printf("Cannot find regulator pwm init_voltage\n"); + return -EINVAL; + } + + ret = uclass_get_device_by_ofnode(UCLASS_PWM, args.node, &priv->pwm); + if (ret) { + debug("%s: Cannot get PWM: ret=%d\n", __func__, ret); + return ret; + } + + return 0; +} + +static int pwm_regulator_probe(struct udevice *dev) +{ + struct pwm_regulator_info *priv = dev_get_priv(dev); + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_BUCK; + uc_pdata->mode_count = 0; + priv->max_voltage = uc_pdata->max_uV; + priv->min_voltage = uc_pdata->min_uV; + + if (priv->init_voltage) + pwm_regulator_set_voltage(dev, priv->init_voltage); + + return 0; +} + +static const struct dm_regulator_ops pwm_regulator_ops = { + .get_value = pwm_regulator_get_voltage, + .set_value = pwm_regulator_set_voltage, + .set_enable = pwm_regulator_enable, +}; + +static const struct udevice_id pwm_regulator_ids[] = { + { .compatible = "pwm-regulator" }, + { } +}; + +U_BOOT_DRIVER(pwm_regulator) = { + .name = "pwm_regulator", + .id = UCLASS_REGULATOR, + .ops = &pwm_regulator_ops, + .probe = pwm_regulator_probe, + .of_match = pwm_regulator_ids, + .of_to_plat = pwm_regulator_of_to_plat, + .priv_auto = sizeof(struct pwm_regulator_info), +}; diff --git a/roms/u-boot/drivers/power/regulator/regulator-uclass.c b/roms/u-boot/drivers/power/regulator/regulator-uclass.c new file mode 100644 index 000000000..fac960682 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/regulator-uclass.c @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014-2015 Samsung Electronics + * Przemyslaw Marczak <p.marczak@samsung.com> + */ + +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <log.h> +#include <dm/uclass-internal.h> +#include <linux/delay.h> +#include <power/pmic.h> +#include <power/regulator.h> + +int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + *modep = NULL; + + uc_pdata = dev_get_uclass_plat(dev); + if (!uc_pdata) + return -ENXIO; + + *modep = uc_pdata->mode; + return uc_pdata->mode_count; +} + +int regulator_get_value(struct udevice *dev) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + + if (!ops || !ops->get_value) + return -ENOSYS; + + return ops->get_value(dev); +} + +static void regulator_set_value_ramp_delay(struct udevice *dev, int old_uV, + int new_uV, unsigned int ramp_delay) +{ + int delay = DIV_ROUND_UP(abs(new_uV - old_uV), ramp_delay); + + debug("regulator %s: delay %u us (%d uV -> %d uV)\n", dev->name, delay, + old_uV, new_uV); + + udelay(delay); +} + +int regulator_set_value(struct udevice *dev, int uV) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + struct dm_regulator_uclass_plat *uc_pdata; + int ret, old_uV = uV, is_enabled = 0; + + uc_pdata = dev_get_uclass_plat(dev); + if (uc_pdata->min_uV != -ENODATA && uV < uc_pdata->min_uV) + return -EINVAL; + if (uc_pdata->max_uV != -ENODATA && uV > uc_pdata->max_uV) + return -EINVAL; + + if (!ops || !ops->set_value) + return -ENOSYS; + + if (uc_pdata->ramp_delay) { + is_enabled = regulator_get_enable(dev); + old_uV = regulator_get_value(dev); + } + + ret = ops->set_value(dev, uV); + + if (!ret) { + if (uc_pdata->ramp_delay && old_uV > 0 && is_enabled) + regulator_set_value_ramp_delay(dev, old_uV, uV, + uc_pdata->ramp_delay); + } + + return ret; +} + +int regulator_set_suspend_value(struct udevice *dev, int uV) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + if (uc_pdata->min_uV != -ENODATA && uV < uc_pdata->min_uV) + return -EINVAL; + if (uc_pdata->max_uV != -ENODATA && uV > uc_pdata->max_uV) + return -EINVAL; + + if (!ops->set_suspend_value) + return -ENOSYS; + + return ops->set_suspend_value(dev, uV); +} + +int regulator_get_suspend_value(struct udevice *dev) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + + if (!ops->get_suspend_value) + return -ENOSYS; + + return ops->get_suspend_value(dev); +} + +/* + * To be called with at most caution as there is no check + * before setting the actual voltage value. + */ +int regulator_set_value_force(struct udevice *dev, int uV) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + + if (!ops || !ops->set_value) + return -ENOSYS; + + return ops->set_value(dev, uV); +} + +int regulator_get_current(struct udevice *dev) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + + if (!ops || !ops->get_current) + return -ENOSYS; + + return ops->get_current(dev); +} + +int regulator_set_current(struct udevice *dev, int uA) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + if (uc_pdata->min_uA != -ENODATA && uA < uc_pdata->min_uA) + return -EINVAL; + if (uc_pdata->max_uA != -ENODATA && uA > uc_pdata->max_uA) + return -EINVAL; + + if (!ops || !ops->set_current) + return -ENOSYS; + + return ops->set_current(dev, uA); +} + +int regulator_get_enable(struct udevice *dev) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + + if (!ops || !ops->get_enable) + return -ENOSYS; + + return ops->get_enable(dev); +} + +int regulator_set_enable(struct udevice *dev, bool enable) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + struct dm_regulator_uclass_plat *uc_pdata; + int ret, old_enable = 0; + + if (!ops || !ops->set_enable) + return -ENOSYS; + + uc_pdata = dev_get_uclass_plat(dev); + if (!enable && uc_pdata->always_on) + return -EACCES; + + if (uc_pdata->ramp_delay) + old_enable = regulator_get_enable(dev); + + ret = ops->set_enable(dev, enable); + if (!ret) { + if (uc_pdata->ramp_delay && !old_enable && enable) { + int uV = regulator_get_value(dev); + + if (uV > 0) { + regulator_set_value_ramp_delay(dev, 0, uV, + uc_pdata->ramp_delay); + } + } + } + + return ret; +} + +int regulator_set_enable_if_allowed(struct udevice *dev, bool enable) +{ + int ret; + + ret = regulator_set_enable(dev, enable); + if (ret == -ENOSYS || ret == -EACCES) + return 0; + + return ret; +} + +int regulator_set_suspend_enable(struct udevice *dev, bool enable) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + + if (!ops->set_suspend_enable) + return -ENOSYS; + + return ops->set_suspend_enable(dev, enable); +} + +int regulator_get_suspend_enable(struct udevice *dev) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + + if (!ops->get_suspend_enable) + return -ENOSYS; + + return ops->get_suspend_enable(dev); +} + +int regulator_get_mode(struct udevice *dev) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + + if (!ops || !ops->get_mode) + return -ENOSYS; + + return ops->get_mode(dev); +} + +int regulator_set_mode(struct udevice *dev, int mode) +{ + const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); + + if (!ops || !ops->set_mode) + return -ENOSYS; + + return ops->set_mode(dev, mode); +} + +int regulator_get_by_platname(const char *plat_name, struct udevice **devp) +{ + struct dm_regulator_uclass_plat *uc_pdata; + struct udevice *dev; + int ret; + + *devp = NULL; + + for (ret = uclass_find_first_device(UCLASS_REGULATOR, &dev); dev; + ret = uclass_find_next_device(&dev)) { + if (ret) { + debug("regulator %s, ret=%d\n", dev->name, ret); + continue; + } + + uc_pdata = dev_get_uclass_plat(dev); + if (!uc_pdata || strcmp(plat_name, uc_pdata->name)) + continue; + + return uclass_get_device_tail(dev, 0, devp); + } + + debug("%s: can't find: %s, ret=%d\n", __func__, plat_name, ret); + + return -ENODEV; +} + +int regulator_get_by_devname(const char *devname, struct udevice **devp) +{ + return uclass_get_device_by_name(UCLASS_REGULATOR, devname, devp); +} + +int device_get_supply_regulator(struct udevice *dev, const char *supply_name, + struct udevice **devp) +{ + return uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + supply_name, devp); +} + +int regulator_autoset(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + int ret = 0; + + uc_pdata = dev_get_uclass_plat(dev); + + ret = regulator_set_suspend_enable(dev, uc_pdata->suspend_on); + if (!ret && uc_pdata->suspend_on) { + ret = regulator_set_suspend_value(dev, uc_pdata->suspend_uV); + if (!ret) + return ret; + } + + if (!uc_pdata->always_on && !uc_pdata->boot_on) + return -EMEDIUMTYPE; + + if (uc_pdata->type == REGULATOR_TYPE_FIXED) + return regulator_set_enable(dev, true); + + if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UV) + ret = regulator_set_value(dev, uc_pdata->min_uV); + if (uc_pdata->init_uV > 0) + ret = regulator_set_value(dev, uc_pdata->init_uV); + if (!ret && (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UA)) + ret = regulator_set_current(dev, uc_pdata->min_uA); + + if (!ret) + ret = regulator_set_enable(dev, true); + + return ret; +} + +int regulator_unset(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + if (uc_pdata && uc_pdata->force_off) + return regulator_set_enable(dev, false); + + return -EMEDIUMTYPE; +} + +static void regulator_show(struct udevice *dev, int ret) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + + printf("%s@%s: ", dev->name, uc_pdata->name); + if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UV) + printf("set %d uV", uc_pdata->min_uV); + if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UA) + printf("; set %d uA", uc_pdata->min_uA); + printf("; enabling"); + if (ret) + printf(" (ret: %d)", ret); + printf("\n"); +} + +int regulator_autoset_by_name(const char *platname, struct udevice **devp) +{ + struct udevice *dev; + int ret; + + ret = regulator_get_by_platname(platname, &dev); + if (devp) + *devp = dev; + if (ret) { + debug("Can get the regulator: %s (err=%d)\n", platname, ret); + return ret; + } + + return regulator_autoset(dev); +} + +int regulator_list_autoset(const char *list_platname[], + struct udevice *list_devp[], + bool verbose) +{ + struct udevice *dev; + int error = 0, i = 0, ret; + + while (list_platname[i]) { + ret = regulator_autoset_by_name(list_platname[i], &dev); + if (ret != -EMEDIUMTYPE && verbose) + regulator_show(dev, ret); + if (ret & !error) + error = ret; + + if (list_devp) + list_devp[i] = dev; + + i++; + } + + return error; +} + +static bool regulator_name_is_unique(struct udevice *check_dev, + const char *check_name) +{ + struct dm_regulator_uclass_plat *uc_pdata; + struct udevice *dev; + int check_len = strlen(check_name); + int ret; + int len; + + for (ret = uclass_find_first_device(UCLASS_REGULATOR, &dev); dev; + ret = uclass_find_next_device(&dev)) { + if (ret || dev == check_dev) + continue; + + uc_pdata = dev_get_uclass_plat(dev); + len = strlen(uc_pdata->name); + if (len != check_len) + continue; + + if (!strcmp(uc_pdata->name, check_name)) + return false; + } + + return true; +} + +static int regulator_post_bind(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + const char *property = "regulator-name"; + + uc_pdata = dev_get_uclass_plat(dev); + + /* Regulator's mandatory constraint */ + uc_pdata->name = dev_read_string(dev, property); + if (!uc_pdata->name) { + debug("%s: dev '%s' has no property '%s'\n", + __func__, dev->name, property); + uc_pdata->name = dev_read_name(dev); + if (!uc_pdata->name) + return -EINVAL; + } + + if (regulator_name_is_unique(dev, uc_pdata->name)) + return 0; + + debug("'%s' of dev: '%s', has nonunique value: '%s\n", + property, dev->name, uc_pdata->name); + + return -EINVAL; +} + +static int regulator_pre_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + ofnode node; + + uc_pdata = dev_get_uclass_plat(dev); + if (!uc_pdata) + return -ENXIO; + + /* Regulator's optional constraints */ + uc_pdata->min_uV = dev_read_u32_default(dev, "regulator-min-microvolt", + -ENODATA); + uc_pdata->max_uV = dev_read_u32_default(dev, "regulator-max-microvolt", + -ENODATA); + uc_pdata->init_uV = dev_read_u32_default(dev, "regulator-init-microvolt", + -ENODATA); + uc_pdata->min_uA = dev_read_u32_default(dev, "regulator-min-microamp", + -ENODATA); + uc_pdata->max_uA = dev_read_u32_default(dev, "regulator-max-microamp", + -ENODATA); + uc_pdata->always_on = dev_read_bool(dev, "regulator-always-on"); + uc_pdata->boot_on = dev_read_bool(dev, "regulator-boot-on"); + uc_pdata->ramp_delay = dev_read_u32_default(dev, "regulator-ramp-delay", + 0); + uc_pdata->force_off = dev_read_bool(dev, "regulator-force-boot-off"); + + node = dev_read_subnode(dev, "regulator-state-mem"); + if (ofnode_valid(node)) { + uc_pdata->suspend_on = !ofnode_read_bool(node, "regulator-off-in-suspend"); + if (ofnode_read_u32(node, "regulator-suspend-microvolt", &uc_pdata->suspend_uV)) + uc_pdata->suspend_uV = uc_pdata->max_uV; + } else { + uc_pdata->suspend_on = true; + uc_pdata->suspend_uV = uc_pdata->max_uV; + } + + /* Those values are optional (-ENODATA if unset) */ + if ((uc_pdata->min_uV != -ENODATA) && + (uc_pdata->max_uV != -ENODATA) && + (uc_pdata->min_uV == uc_pdata->max_uV)) + uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_UV; + + /* Those values are optional (-ENODATA if unset) */ + if ((uc_pdata->min_uA != -ENODATA) && + (uc_pdata->max_uA != -ENODATA) && + (uc_pdata->min_uA == uc_pdata->max_uA)) + uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_UA; + + return 0; +} + +int regulators_enable_boot_on(bool verbose) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_REGULATOR, &uc); + if (ret) + return ret; + for (uclass_first_device(UCLASS_REGULATOR, &dev); + dev; + uclass_next_device(&dev)) { + ret = regulator_autoset(dev); + if (ret == -EMEDIUMTYPE) { + ret = 0; + continue; + } + if (verbose) + regulator_show(dev, ret); + if (ret == -ENOSYS) + ret = 0; + } + + return ret; +} + +int regulators_enable_boot_off(bool verbose) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_REGULATOR, &uc); + if (ret) + return ret; + for (uclass_first_device(UCLASS_REGULATOR, &dev); + dev; + uclass_next_device(&dev)) { + ret = regulator_unset(dev); + if (ret == -EMEDIUMTYPE) { + ret = 0; + continue; + } + if (verbose) + regulator_show(dev, ret); + if (ret == -ENOSYS) + ret = 0; + } + + return ret; +} + +UCLASS_DRIVER(regulator) = { + .id = UCLASS_REGULATOR, + .name = "regulator", + .post_bind = regulator_post_bind, + .pre_probe = regulator_pre_probe, + .per_device_plat_auto = sizeof(struct dm_regulator_uclass_plat), +}; diff --git a/roms/u-boot/drivers/power/regulator/regulator_common.c b/roms/u-boot/drivers/power/regulator/regulator_common.c new file mode 100644 index 000000000..93d8196b3 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/regulator_common.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Disruptive Technologies Research AS + * Sven Schwermer <sven.svenschwermer@disruptive-technologies.com> + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <linux/delay.h> +#include <power/regulator.h> + +#include "regulator_common.h" + +int regulator_common_of_to_plat(struct udevice *dev, + struct regulator_common_plat *dev_pdata, + const char *enable_gpio_name) +{ + struct gpio_desc *gpio; + int flags = GPIOD_IS_OUT; + int ret; + + if (!dev_read_bool(dev, "enable-active-high")) + flags |= GPIOD_ACTIVE_LOW; + if (dev_read_bool(dev, "regulator-boot-on")) + flags |= GPIOD_IS_OUT_ACTIVE; + + /* Get optional enable GPIO desc */ + gpio = &dev_pdata->gpio; + ret = gpio_request_by_name(dev, enable_gpio_name, 0, gpio, flags); + if (ret) { + debug("Regulator '%s' optional enable GPIO - not found! Error: %d\n", + dev->name, ret); + if (ret != -ENOENT) + return ret; + } + + /* Get optional ramp up delay */ + dev_pdata->startup_delay_us = dev_read_u32_default(dev, + "startup-delay-us", 0); + dev_pdata->off_on_delay_us = + dev_read_u32_default(dev, "off-on-delay-us", 0); + if (!dev_pdata->off_on_delay_us) { + dev_pdata->off_on_delay_us = + dev_read_u32_default(dev, "u-boot,off-on-delay-us", 0); + } + + return 0; +} + +int regulator_common_get_enable(const struct udevice *dev, + struct regulator_common_plat *dev_pdata) +{ + /* Enable GPIO is optional */ + if (!dev_pdata->gpio.dev) + return true; + + return dm_gpio_get_value(&dev_pdata->gpio); +} + +int regulator_common_set_enable(const struct udevice *dev, + struct regulator_common_plat *dev_pdata, bool enable) +{ + int ret; + + debug("%s: dev='%s', enable=%d, delay=%d, has_gpio=%d\n", __func__, + dev->name, enable, dev_pdata->startup_delay_us, + dm_gpio_is_valid(&dev_pdata->gpio)); + /* Enable GPIO is optional */ + if (!dm_gpio_is_valid(&dev_pdata->gpio)) { + if (!enable) + return -ENOSYS; + return 0; + } + + ret = dm_gpio_set_value(&dev_pdata->gpio, enable); + if (ret) { + pr_err("Can't set regulator : %s gpio to: %d\n", dev->name, + enable); + return ret; + } + + if (enable && dev_pdata->startup_delay_us) + udelay(dev_pdata->startup_delay_us); + debug("%s: done\n", __func__); + + if (!enable && dev_pdata->off_on_delay_us) + udelay(dev_pdata->off_on_delay_us); + + return 0; +} diff --git a/roms/u-boot/drivers/power/regulator/regulator_common.h b/roms/u-boot/drivers/power/regulator/regulator_common.h new file mode 100644 index 000000000..c10492f01 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/regulator_common.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Disruptive Technologies Research AS + * Sven Schwermer <sven.svenschwermer@disruptive-technologies.com> + */ + +#ifndef _REGULATOR_COMMON_H +#define _REGULATOR_COMMON_H + +#include <asm/gpio.h> + +struct regulator_common_plat { + struct gpio_desc gpio; /* GPIO for regulator enable control */ + unsigned int startup_delay_us; + unsigned int off_on_delay_us; +}; + +int regulator_common_of_to_plat(struct udevice *dev, + struct regulator_common_plat *dev_pdata, const + char *enable_gpio_name); +int regulator_common_get_enable(const struct udevice *dev, + struct regulator_common_plat *dev_pdata); +int regulator_common_set_enable(const struct udevice *dev, + struct regulator_common_plat *dev_pdata, bool enable); + +#endif /* _REGULATOR_COMMON_H */ diff --git a/roms/u-boot/drivers/power/regulator/rk8xx.c b/roms/u-boot/drivers/power/regulator/rk8xx.c new file mode 100644 index 000000000..0ee07ad29 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/rk8xx.c @@ -0,0 +1,1158 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * Based on Rockchip's drivers/power/pmic/pmic_rk808.c: + * Copyright (C) 2012 rockchips + * zyw <zyw@rock-chips.com> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <power/rk8xx_pmic.h> +#include <power/pmic.h> +#include <power/regulator.h> + +#ifndef CONFIG_SPL_BUILD +#define ENABLE_DRIVER +#endif + +/* Not used or exisit register and configure */ +#define NA 0xff + +/* Field Definitions */ +#define RK808_BUCK_VSEL_MASK 0x3f +#define RK808_BUCK4_VSEL_MASK 0xf +#define RK808_LDO_VSEL_MASK 0x1f + +/* RK809 BUCK5 */ +#define RK809_BUCK5_CONFIG(n) (0xde + (n) * 1) +#define RK809_BUCK5_VSEL_MASK 0x07 + +/* RK817 BUCK */ +#define RK817_BUCK_ON_VSEL(n) (0xbb + 3 * ((n) - 1)) +#define RK817_BUCK_SLP_VSEL(n) (0xbc + 3 * ((n) - 1)) +#define RK817_BUCK_VSEL_MASK 0x7f +#define RK817_BUCK_CONFIG(i) (0xba + (i) * 3) + +/* RK817 LDO */ +#define RK817_LDO_ON_VSEL(n) (0xcc + 2 * ((n) - 1)) +#define RK817_LDO_SLP_VSEL(n) (0xcd + 2 * ((n) - 1)) +#define RK817_LDO_VSEL_MASK 0x7f + +/* RK817 ENABLE */ +#define RK817_POWER_EN(n) (0xb1 + (n)) +#define RK817_POWER_SLP_EN(n) (0xb5 + (n)) + +#define RK818_BUCK_VSEL_MASK 0x3f +#define RK818_BUCK4_VSEL_MASK 0x1f +#define RK818_LDO_VSEL_MASK 0x1f +#define RK818_LDO3_ON_VSEL_MASK 0xf +#define RK818_BOOST_ON_VSEL_MASK 0xe0 +#define RK818_USB_ILIM_SEL_MASK 0x0f +#define RK818_USB_CHG_SD_VSEL_MASK 0x70 + +/* + * Ramp delay + */ +#define RK805_RAMP_RATE_OFFSET 3 +#define RK805_RAMP_RATE_MASK (3 << RK805_RAMP_RATE_OFFSET) +#define RK805_RAMP_RATE_3MV_PER_US (0 << RK805_RAMP_RATE_OFFSET) +#define RK805_RAMP_RATE_6MV_PER_US (1 << RK805_RAMP_RATE_OFFSET) +#define RK805_RAMP_RATE_12_5MV_PER_US (2 << RK805_RAMP_RATE_OFFSET) +#define RK805_RAMP_RATE_25MV_PER_US (3 << RK805_RAMP_RATE_OFFSET) + +#define RK808_RAMP_RATE_OFFSET 3 +#define RK808_RAMP_RATE_MASK (3 << RK808_RAMP_RATE_OFFSET) +#define RK808_RAMP_RATE_2MV_PER_US (0 << RK808_RAMP_RATE_OFFSET) +#define RK808_RAMP_RATE_4MV_PER_US (1 << RK808_RAMP_RATE_OFFSET) +#define RK808_RAMP_RATE_6MV_PER_US (2 << RK808_RAMP_RATE_OFFSET) +#define RK808_RAMP_RATE_10MV_PER_US (3 << RK808_RAMP_RATE_OFFSET) + +#define RK817_RAMP_RATE_OFFSET 6 +#define RK817_RAMP_RATE_MASK (0x3 << RK817_RAMP_RATE_OFFSET) +#define RK817_RAMP_RATE_3MV_PER_US (0x0 << RK817_RAMP_RATE_OFFSET) +#define RK817_RAMP_RATE_6_3MV_PER_US (0x1 << RK817_RAMP_RATE_OFFSET) +#define RK817_RAMP_RATE_12_5MV_PER_US (0x2 << RK817_RAMP_RATE_OFFSET) +#define RK817_RAMP_RATE_25MV_PER_US (0x3 << RK817_RAMP_RATE_OFFSET) + +struct rk8xx_reg_info { + uint min_uv; + uint step_uv; + u8 vsel_reg; + u8 vsel_sleep_reg; + u8 config_reg; + u8 vsel_mask; + u8 min_sel; +}; + +static const struct rk8xx_reg_info rk808_buck[] = { + { 712500, 12500, REG_BUCK1_ON_VSEL, REG_BUCK1_SLP_VSEL, REG_BUCK1_CONFIG, RK808_BUCK_VSEL_MASK, }, + { 712500, 12500, REG_BUCK2_ON_VSEL, REG_BUCK2_SLP_VSEL, REG_BUCK2_CONFIG, RK808_BUCK_VSEL_MASK, }, + { 712500, 12500, NA, NA, REG_BUCK3_CONFIG, RK808_BUCK_VSEL_MASK, }, + { 1800000, 100000, REG_BUCK4_ON_VSEL, REG_BUCK4_SLP_VSEL, REG_BUCK4_CONFIG, RK808_BUCK4_VSEL_MASK, }, +}; + +static const struct rk8xx_reg_info rk816_buck[] = { + /* buck 1 */ + { 712500, 12500, REG_BUCK1_ON_VSEL, REG_BUCK1_SLP_VSEL, REG_BUCK1_CONFIG, RK818_BUCK_VSEL_MASK, 0x00, }, + { 1800000, 200000, REG_BUCK1_ON_VSEL, REG_BUCK1_SLP_VSEL, REG_BUCK1_CONFIG, RK818_BUCK_VSEL_MASK, 0x3c, }, + { 2300000, 0, REG_BUCK1_ON_VSEL, REG_BUCK1_SLP_VSEL, REG_BUCK1_CONFIG, RK818_BUCK_VSEL_MASK, 0x3f, }, + /* buck 2 */ + { 712500, 12500, REG_BUCK2_ON_VSEL, REG_BUCK2_SLP_VSEL, REG_BUCK2_CONFIG, RK818_BUCK_VSEL_MASK, 0x00, }, + { 1800000, 200000, REG_BUCK2_ON_VSEL, REG_BUCK2_SLP_VSEL, REG_BUCK2_CONFIG, RK818_BUCK_VSEL_MASK, 0x3c, }, + { 2300000, 0, REG_BUCK2_ON_VSEL, REG_BUCK2_SLP_VSEL, REG_BUCK2_CONFIG, RK818_BUCK_VSEL_MASK, 0x3f, }, + /* buck 3 */ + { 712500, 12500, NA, NA, REG_BUCK3_CONFIG, RK818_BUCK_VSEL_MASK, }, + /* buck 4 */ + { 800000, 100000, REG_BUCK4_ON_VSEL, REG_BUCK4_SLP_VSEL, REG_BUCK4_CONFIG, RK818_BUCK4_VSEL_MASK, }, +}; + +static const struct rk8xx_reg_info rk809_buck5[] = { + /* buck 5 */ + { 1500000, 0, RK809_BUCK5_CONFIG(0), RK809_BUCK5_CONFIG(1), NA, RK809_BUCK5_VSEL_MASK, 0x00, }, + { 1800000, 200000, RK809_BUCK5_CONFIG(0), RK809_BUCK5_CONFIG(1), NA, RK809_BUCK5_VSEL_MASK, 0x01, }, + { 2800000, 200000, RK809_BUCK5_CONFIG(0), RK809_BUCK5_CONFIG(1), NA, RK809_BUCK5_VSEL_MASK, 0x04, }, + { 3300000, 300000, RK809_BUCK5_CONFIG(0), RK809_BUCK5_CONFIG(1), NA, RK809_BUCK5_VSEL_MASK, 0x06, }, +}; + +static const struct rk8xx_reg_info rk817_buck[] = { + /* buck 1 */ + { 500000, 12500, RK817_BUCK_ON_VSEL(1), RK817_BUCK_SLP_VSEL(1), RK817_BUCK_CONFIG(1), RK817_BUCK_VSEL_MASK, 0x00, }, + { 1500000, 100000, RK817_BUCK_ON_VSEL(1), RK817_BUCK_SLP_VSEL(1), RK817_BUCK_CONFIG(1), RK817_BUCK_VSEL_MASK, 0x50, }, + { 2400000, 0, RK817_BUCK_ON_VSEL(1), RK817_BUCK_SLP_VSEL(1), RK817_BUCK_CONFIG(1), RK817_BUCK_VSEL_MASK, 0x59, }, + /* buck 2 */ + { 500000, 12500, RK817_BUCK_ON_VSEL(2), RK817_BUCK_SLP_VSEL(2), RK817_BUCK_CONFIG(2), RK817_BUCK_VSEL_MASK, 0x00, }, + { 1500000, 100000, RK817_BUCK_ON_VSEL(2), RK817_BUCK_SLP_VSEL(2), RK817_BUCK_CONFIG(2), RK817_BUCK_VSEL_MASK, 0x50, }, + { 2400000, 0, RK817_BUCK_ON_VSEL(2), RK817_BUCK_SLP_VSEL(2), RK817_BUCK_CONFIG(2), RK817_BUCK_VSEL_MASK, 0x59, }, + /* buck 3 */ + { 500000, 12500, RK817_BUCK_ON_VSEL(3), RK817_BUCK_SLP_VSEL(3), RK817_BUCK_CONFIG(3), RK817_BUCK_VSEL_MASK, 0x00, }, + { 1500000, 100000, RK817_BUCK_ON_VSEL(3), RK817_BUCK_SLP_VSEL(3), RK817_BUCK_CONFIG(3), RK817_BUCK_VSEL_MASK, 0x50, }, + { 2400000, 0, RK817_BUCK_ON_VSEL(3), RK817_BUCK_SLP_VSEL(3), RK817_BUCK_CONFIG(3), RK817_BUCK_VSEL_MASK, 0x59, }, + /* buck 4 */ + { 500000, 12500, RK817_BUCK_ON_VSEL(4), RK817_BUCK_SLP_VSEL(4), RK817_BUCK_CONFIG(4), RK817_BUCK_VSEL_MASK, 0x00, }, + { 1500000, 100000, RK817_BUCK_ON_VSEL(4), RK817_BUCK_SLP_VSEL(4), RK817_BUCK_CONFIG(4), RK817_BUCK_VSEL_MASK, 0x50, }, + { 3400000, 0, RK817_BUCK_ON_VSEL(4), RK817_BUCK_SLP_VSEL(4), RK817_BUCK_CONFIG(4), RK817_BUCK_VSEL_MASK, 0x63, }, +}; + +static const struct rk8xx_reg_info rk818_buck[] = { + { 712500, 12500, REG_BUCK1_ON_VSEL, REG_BUCK1_SLP_VSEL, REG_BUCK1_CONFIG, RK818_BUCK_VSEL_MASK, }, + { 712500, 12500, REG_BUCK2_ON_VSEL, REG_BUCK2_SLP_VSEL, REG_BUCK2_CONFIG, RK818_BUCK_VSEL_MASK, }, + { 712500, 12500, NA, NA, REG_BUCK3_CONFIG, RK818_BUCK_VSEL_MASK, }, + { 1800000, 100000, REG_BUCK4_ON_VSEL, REG_BUCK4_SLP_VSEL, REG_BUCK4_CONFIG, RK818_BUCK4_VSEL_MASK, }, +}; + +#ifdef ENABLE_DRIVER +static const struct rk8xx_reg_info rk808_ldo[] = { + { 1800000, 100000, REG_LDO1_ON_VSEL, REG_LDO1_SLP_VSEL, NA, RK808_LDO_VSEL_MASK, }, + { 1800000, 100000, REG_LDO2_ON_VSEL, REG_LDO2_SLP_VSEL, NA, RK808_LDO_VSEL_MASK, }, + { 800000, 100000, REG_LDO3_ON_VSEL, REG_LDO3_SLP_VSEL, NA, RK808_BUCK4_VSEL_MASK, }, + { 1800000, 100000, REG_LDO4_ON_VSEL, REG_LDO4_SLP_VSEL, NA, RK808_LDO_VSEL_MASK, }, + { 1800000, 100000, REG_LDO5_ON_VSEL, REG_LDO5_SLP_VSEL, NA, RK808_LDO_VSEL_MASK, }, + { 800000, 100000, REG_LDO6_ON_VSEL, REG_LDO6_SLP_VSEL, NA, RK808_LDO_VSEL_MASK, }, + { 800000, 100000, REG_LDO7_ON_VSEL, REG_LDO7_SLP_VSEL, NA, RK808_LDO_VSEL_MASK, }, + { 1800000, 100000, REG_LDO8_ON_VSEL, REG_LDO8_SLP_VSEL, NA, RK808_LDO_VSEL_MASK, }, +}; + +static const struct rk8xx_reg_info rk816_ldo[] = { + { 800000, 100000, REG_LDO1_ON_VSEL, REG_LDO1_SLP_VSEL, NA, RK818_LDO_VSEL_MASK, }, + { 800000, 100000, REG_LDO2_ON_VSEL, REG_LDO2_SLP_VSEL, NA, RK818_LDO_VSEL_MASK, }, + { 800000, 100000, REG_LDO3_ON_VSEL, REG_LDO3_SLP_VSEL, NA, RK818_LDO_VSEL_MASK, }, + { 800000, 100000, REG_LDO4_ON_VSEL, REG_LDO4_SLP_VSEL, NA, RK818_LDO_VSEL_MASK, }, + { 800000, 100000, REG_LDO5_ON_VSEL, REG_LDO5_SLP_VSEL, NA, RK818_LDO_VSEL_MASK, }, + { 800000, 100000, REG_LDO6_ON_VSEL, REG_LDO6_SLP_VSEL, NA, RK818_LDO_VSEL_MASK, }, +}; + +static const struct rk8xx_reg_info rk817_ldo[] = { + /* ldo1 */ + { 600000, 25000, RK817_LDO_ON_VSEL(1), RK817_LDO_SLP_VSEL(1), NA, RK817_LDO_VSEL_MASK, 0x00, }, + { 3400000, 0, RK817_LDO_ON_VSEL(1), RK817_LDO_SLP_VSEL(1), NA, RK817_LDO_VSEL_MASK, 0x70, }, + /* ldo2 */ + { 600000, 25000, RK817_LDO_ON_VSEL(2), RK817_LDO_SLP_VSEL(2), NA, RK817_LDO_VSEL_MASK, 0x00, }, + { 3400000, 0, RK817_LDO_ON_VSEL(2), RK817_LDO_SLP_VSEL(2), NA, RK817_LDO_VSEL_MASK, 0x70, }, + /* ldo3 */ + { 600000, 25000, RK817_LDO_ON_VSEL(3), RK817_LDO_SLP_VSEL(3), NA, RK817_LDO_VSEL_MASK, 0x00, }, + { 3400000, 0, RK817_LDO_ON_VSEL(3), RK817_LDO_SLP_VSEL(3), NA, RK817_LDO_VSEL_MASK, 0x70, }, + /* ldo4 */ + { 600000, 25000, RK817_LDO_ON_VSEL(4), RK817_LDO_SLP_VSEL(4), NA, RK817_LDO_VSEL_MASK, 0x00, }, + { 3400000, 0, RK817_LDO_ON_VSEL(4), RK817_LDO_SLP_VSEL(4), NA, RK817_LDO_VSEL_MASK, 0x70, }, + /* ldo5 */ + { 600000, 25000, RK817_LDO_ON_VSEL(5), RK817_LDO_SLP_VSEL(5), NA, RK817_LDO_VSEL_MASK, 0x00, }, + { 3400000, 0, RK817_LDO_ON_VSEL(5), RK817_LDO_SLP_VSEL(5), NA, RK817_LDO_VSEL_MASK, 0x70, }, + /* ldo6 */ + { 600000, 25000, RK817_LDO_ON_VSEL(6), RK817_LDO_SLP_VSEL(6), NA, RK817_LDO_VSEL_MASK, 0x00, }, + { 3400000, 0, RK817_LDO_ON_VSEL(6), RK817_LDO_SLP_VSEL(6), NA, RK817_LDO_VSEL_MASK, 0x70, }, + /* ldo7 */ + { 600000, 25000, RK817_LDO_ON_VSEL(7), RK817_LDO_SLP_VSEL(7), NA, RK817_LDO_VSEL_MASK, 0x00, }, + { 3400000, 0, RK817_LDO_ON_VSEL(7), RK817_LDO_SLP_VSEL(7), NA, RK817_LDO_VSEL_MASK, 0x70, }, + /* ldo8 */ + { 600000, 25000, RK817_LDO_ON_VSEL(8), RK817_LDO_SLP_VSEL(8), NA, RK817_LDO_VSEL_MASK, 0x00, }, + { 3400000, 0, RK817_LDO_ON_VSEL(8), RK817_LDO_SLP_VSEL(8), NA, RK817_LDO_VSEL_MASK, 0x70, }, + /* ldo9 */ + { 600000, 25000, RK817_LDO_ON_VSEL(9), RK817_LDO_SLP_VSEL(9), NA, RK817_LDO_VSEL_MASK, 0x00, }, + { 3400000, 0, RK817_LDO_ON_VSEL(9), RK817_LDO_SLP_VSEL(9), NA, RK817_LDO_VSEL_MASK, 0x70, }, +}; + +static const struct rk8xx_reg_info rk818_ldo[] = { + { 1800000, 100000, REG_LDO1_ON_VSEL, REG_LDO1_SLP_VSEL, NA, RK818_LDO_VSEL_MASK, }, + { 1800000, 100000, REG_LDO2_ON_VSEL, REG_LDO2_SLP_VSEL, NA, RK818_LDO_VSEL_MASK, }, + { 800000, 100000, REG_LDO3_ON_VSEL, REG_LDO3_SLP_VSEL, NA, RK818_LDO3_ON_VSEL_MASK, }, + { 1800000, 100000, REG_LDO4_ON_VSEL, REG_LDO4_SLP_VSEL, NA, RK818_LDO_VSEL_MASK, }, + { 1800000, 100000, REG_LDO5_ON_VSEL, REG_LDO5_SLP_VSEL, NA, RK818_LDO_VSEL_MASK, }, + { 800000, 100000, REG_LDO6_ON_VSEL, REG_LDO6_SLP_VSEL, NA, RK818_LDO_VSEL_MASK, }, + { 800000, 100000, REG_LDO7_ON_VSEL, REG_LDO7_SLP_VSEL, NA, RK818_LDO_VSEL_MASK, }, + { 1800000, 100000, REG_LDO8_ON_VSEL, REG_LDO8_SLP_VSEL, NA, RK818_LDO_VSEL_MASK, }, +}; +#endif + +static const u16 rk818_chrg_cur_input_array[] = { + 450, 800, 850, 1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000 +}; + +static const uint rk818_chrg_shutdown_vsel_array[] = { + 2780000, 2850000, 2920000, 2990000, 3060000, 3130000, 3190000, 3260000 +}; + +static const struct rk8xx_reg_info *get_buck_reg(struct udevice *pmic, + int num, int uvolt) +{ + struct rk8xx_priv *priv = dev_get_priv(pmic); + + switch (priv->variant) { + case RK805_ID: + case RK816_ID: + switch (num) { + case 0: + case 1: + if (uvolt <= 1450000) + return &rk816_buck[num * 3 + 0]; + else if (uvolt <= 2200000) + return &rk816_buck[num * 3 + 1]; + else + return &rk816_buck[num * 3 + 2]; + default: + return &rk816_buck[num + 4]; + } + + case RK809_ID: + case RK817_ID: + switch (num) { + case 0 ... 2: + if (uvolt < 1500000) + return &rk817_buck[num * 3 + 0]; + else if (uvolt < 2400000) + return &rk817_buck[num * 3 + 1]; + else + return &rk817_buck[num * 3 + 2]; + case 3: + if (uvolt < 1500000) + return &rk817_buck[num * 3 + 0]; + else if (uvolt < 3400000) + return &rk817_buck[num * 3 + 1]; + else + return &rk817_buck[num * 3 + 2]; + /* BUCK5 for RK809 */ + default: + if (uvolt < 1800000) + return &rk809_buck5[0]; + else if (uvolt < 2800000) + return &rk809_buck5[1]; + else if (uvolt < 3300000) + return &rk809_buck5[2]; + else + return &rk809_buck5[3]; + } + case RK818_ID: + return &rk818_buck[num]; + default: + return &rk808_buck[num]; + } +} + +static int _buck_set_value(struct udevice *pmic, int buck, int uvolt) +{ + const struct rk8xx_reg_info *info = get_buck_reg(pmic, buck, uvolt); + struct rk8xx_priv *priv = dev_get_priv(pmic); + int mask = info->vsel_mask; + int val; + + if (info->vsel_reg == NA) + return -ENOSYS; + + if (info->step_uv == 0) /* Fixed voltage */ + val = info->min_sel; + else + val = ((uvolt - info->min_uv) / info->step_uv) + info->min_sel; + + debug("%s: volt=%d, buck=%d, reg=0x%x, mask=0x%x, val=0x%x\n", + __func__, uvolt, buck + 1, info->vsel_reg, mask, val); + + if (priv->variant == RK816_ID) { + pmic_clrsetbits(pmic, info->vsel_reg, mask, val); + return pmic_clrsetbits(pmic, RK816_REG_DCDC_EN2, + 1 << 7, 1 << 7); + } else { + return pmic_clrsetbits(pmic, info->vsel_reg, mask, val); + } +} + +static int _buck_set_enable(struct udevice *pmic, int buck, bool enable) +{ + uint mask, value, en_reg; + int ret = 0; + struct rk8xx_priv *priv = dev_get_priv(pmic); + + switch (priv->variant) { + case RK805_ID: + case RK816_ID: + if (buck >= 4) { + buck -= 4; + en_reg = RK816_REG_DCDC_EN2; + } else { + en_reg = RK816_REG_DCDC_EN1; + } + if (enable) + value = ((1 << buck) | (1 << (buck + 4))); + else + value = ((0 << buck) | (1 << (buck + 4))); + ret = pmic_reg_write(pmic, en_reg, value); + break; + + case RK808_ID: + case RK818_ID: + mask = 1 << buck; + if (enable) { + ret = pmic_clrsetbits(pmic, REG_DCDC_ILMAX, + 0, 3 << (buck * 2)); + if (ret) + return ret; + } + ret = pmic_clrsetbits(pmic, REG_DCDC_EN, mask, + enable ? mask : 0); + break; + case RK809_ID: + case RK817_ID: + if (buck < 4) { + if (enable) + value = ((1 << buck) | (1 << (buck + 4))); + else + value = ((0 << buck) | (1 << (buck + 4))); + ret = pmic_reg_write(pmic, RK817_POWER_EN(0), value); + /* BUCK5 for RK809 */ + } else { + if (enable) + value = ((1 << 1) | (1 << 5)); + else + value = ((0 << 1) | (1 << 5)); + ret = pmic_reg_write(pmic, RK817_POWER_EN(3), value); + } + break; + default: + ret = -EINVAL; + } + + return ret; +} + +#ifdef ENABLE_DRIVER +static int _buck_set_suspend_value(struct udevice *pmic, int buck, int uvolt) +{ + const struct rk8xx_reg_info *info = get_buck_reg(pmic, buck, uvolt); + int mask = info->vsel_mask; + int val; + + if (info->vsel_sleep_reg == NA) + return -ENOSYS; + + if (info->step_uv == 0) + val = info->min_sel; + else + val = ((uvolt - info->min_uv) / info->step_uv) + info->min_sel; + + debug("%s: volt=%d, buck=%d, reg=0x%x, mask=0x%x, val=0x%x\n", + __func__, uvolt, buck + 1, info->vsel_sleep_reg, mask, val); + + return pmic_clrsetbits(pmic, info->vsel_sleep_reg, mask, val); +} + +static int _buck_get_enable(struct udevice *pmic, int buck) +{ + struct rk8xx_priv *priv = dev_get_priv(pmic); + uint mask = 0; + int ret = 0; + + switch (priv->variant) { + case RK805_ID: + case RK816_ID: + if (buck >= 4) { + mask = 1 << (buck - 4); + ret = pmic_reg_read(pmic, RK816_REG_DCDC_EN2); + } else { + mask = 1 << buck; + ret = pmic_reg_read(pmic, RK816_REG_DCDC_EN1); + } + break; + case RK808_ID: + case RK818_ID: + mask = 1 << buck; + ret = pmic_reg_read(pmic, REG_DCDC_EN); + if (ret < 0) + return ret; + break; + case RK809_ID: + case RK817_ID: + if (buck < 4) { + mask = 1 << buck; + ret = pmic_reg_read(pmic, RK817_POWER_EN(0)); + /* BUCK5 for RK809 */ + } else { + mask = 1 << 1; + ret = pmic_reg_read(pmic, RK817_POWER_EN(3)); + } + break; + } + + if (ret < 0) + return ret; + + return ret & mask ? true : false; +} + +static int _buck_set_suspend_enable(struct udevice *pmic, int buck, bool enable) +{ + uint mask = 0; + int ret; + struct rk8xx_priv *priv = dev_get_priv(pmic); + + switch (priv->variant) { + case RK805_ID: + case RK816_ID: + mask = 1 << buck; + ret = pmic_clrsetbits(pmic, RK816_REG_DCDC_SLP_EN, mask, + enable ? mask : 0); + break; + case RK808_ID: + case RK818_ID: + mask = 1 << buck; + ret = pmic_clrsetbits(pmic, REG_SLEEP_SET_OFF1, mask, + enable ? 0 : mask); + break; + case RK809_ID: + case RK817_ID: + if (buck < 4) + mask = 1 << buck; + else + mask = 1 << 5; /* BUCK5 for RK809 */ + ret = pmic_clrsetbits(pmic, RK817_POWER_SLP_EN(0), mask, + enable ? mask : 0); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int _buck_get_suspend_enable(struct udevice *pmic, int buck) +{ + struct rk8xx_priv *priv = dev_get_priv(pmic); + int ret, val; + uint mask = 0; + + switch (priv->variant) { + case RK805_ID: + case RK816_ID: + mask = 1 << buck; + val = pmic_reg_read(pmic, RK816_REG_DCDC_SLP_EN); + if (val < 0) + return val; + ret = val & mask ? 1 : 0; + break; + case RK808_ID: + case RK818_ID: + mask = 1 << buck; + val = pmic_reg_read(pmic, REG_SLEEP_SET_OFF1); + if (val < 0) + return val; + ret = val & mask ? 0 : 1; + break; + case RK809_ID: + case RK817_ID: + if (buck < 4) + mask = 1 << buck; + else + mask = 1 << 5; /* BUCK5 for RK809 */ + + val = pmic_reg_read(pmic, RK817_POWER_SLP_EN(0)); + if (val < 0) + return val; + ret = val & mask ? 1 : 0; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct rk8xx_reg_info *get_ldo_reg(struct udevice *pmic, + int num, int uvolt) +{ + struct rk8xx_priv *priv = dev_get_priv(pmic); + + switch (priv->variant) { + case RK805_ID: + case RK816_ID: + return &rk816_ldo[num]; + case RK809_ID: + case RK817_ID: + if (uvolt < 3400000) + return &rk817_ldo[num * 2 + 0]; + else + return &rk817_ldo[num * 2 + 1]; + case RK818_ID: + return &rk818_ldo[num]; + default: + return &rk808_ldo[num]; + } +} + +static int _ldo_get_enable(struct udevice *pmic, int ldo) +{ + struct rk8xx_priv *priv = dev_get_priv(pmic); + uint mask = 0; + int ret = 0; + + switch (priv->variant) { + case RK805_ID: + case RK816_ID: + if (ldo >= 4) { + mask = 1 << (ldo - 4); + ret = pmic_reg_read(pmic, RK816_REG_LDO_EN2); + } else { + mask = 1 << ldo; + ret = pmic_reg_read(pmic, RK816_REG_LDO_EN1); + } + break; + case RK808_ID: + case RK818_ID: + mask = 1 << ldo; + ret = pmic_reg_read(pmic, REG_LDO_EN); + if (ret < 0) + return ret; + break; + case RK809_ID: + case RK817_ID: + if (ldo < 4) { + mask = 1 << ldo; + ret = pmic_reg_read(pmic, RK817_POWER_EN(1)); + } else if (ldo < 8) { + mask = 1 << (ldo - 4); + ret = pmic_reg_read(pmic, RK817_POWER_EN(2)); + } else if (ldo == 8) { + mask = 1 << 0; + ret = pmic_reg_read(pmic, RK817_POWER_EN(3)); + } else { + return false; + } + break; + } + + if (ret < 0) + return ret; + + return ret & mask ? true : false; +} + +static int _ldo_set_enable(struct udevice *pmic, int ldo, bool enable) +{ + struct rk8xx_priv *priv = dev_get_priv(pmic); + uint mask, value, en_reg; + int ret = 0; + + switch (priv->variant) { + case RK805_ID: + case RK816_ID: + if (ldo >= 4) { + ldo -= 4; + en_reg = RK816_REG_LDO_EN2; + } else { + en_reg = RK816_REG_LDO_EN1; + } + if (enable) + value = ((1 << ldo) | (1 << (ldo + 4))); + else + value = ((0 << ldo) | (1 << (ldo + 4))); + + ret = pmic_reg_write(pmic, en_reg, value); + break; + case RK808_ID: + case RK818_ID: + mask = 1 << ldo; + ret = pmic_clrsetbits(pmic, REG_LDO_EN, mask, + enable ? mask : 0); + break; + case RK809_ID: + case RK817_ID: + if (ldo < 4) { + en_reg = RK817_POWER_EN(1); + } else if (ldo < 8) { + ldo -= 4; + en_reg = RK817_POWER_EN(2); + } else if (ldo == 8) { + ldo = 0; /* BIT 0 */ + en_reg = RK817_POWER_EN(3); + } else { + return -EINVAL; + } + if (enable) + value = ((1 << ldo) | (1 << (ldo + 4))); + else + value = ((0 << ldo) | (1 << (ldo + 4))); + ret = pmic_reg_write(pmic, en_reg, value); + break; + } + + return ret; +} + +static int _ldo_set_suspend_enable(struct udevice *pmic, int ldo, bool enable) +{ + struct rk8xx_priv *priv = dev_get_priv(pmic); + uint mask; + int ret = 0; + + switch (priv->variant) { + case RK805_ID: + case RK816_ID: + mask = 1 << ldo; + ret = pmic_clrsetbits(pmic, RK816_REG_LDO_SLP_EN, mask, + enable ? mask : 0); + break; + case RK808_ID: + case RK818_ID: + mask = 1 << ldo; + ret = pmic_clrsetbits(pmic, REG_SLEEP_SET_OFF2, mask, + enable ? 0 : mask); + break; + case RK809_ID: + case RK817_ID: + if (ldo == 8) { + mask = 1 << 4; /* LDO9 */ + ret = pmic_clrsetbits(pmic, RK817_POWER_SLP_EN(0), mask, + enable ? mask : 0); + } else { + mask = 1 << ldo; + ret = pmic_clrsetbits(pmic, RK817_POWER_SLP_EN(1), mask, + enable ? mask : 0); + } + break; + } + + return ret; +} + +static int _ldo_get_suspend_enable(struct udevice *pmic, int ldo) +{ + struct rk8xx_priv *priv = dev_get_priv(pmic); + int val, ret = 0; + uint mask; + + switch (priv->variant) { + case RK805_ID: + case RK816_ID: + mask = 1 << ldo; + val = pmic_reg_read(pmic, RK816_REG_LDO_SLP_EN); + if (val < 0) + return val; + ret = val & mask ? 1 : 0; + break; + case RK808_ID: + case RK818_ID: + mask = 1 << ldo; + val = pmic_reg_read(pmic, REG_SLEEP_SET_OFF2); + if (val < 0) + return val; + ret = val & mask ? 0 : 1; + break; + case RK809_ID: + case RK817_ID: + if (ldo == 8) { + mask = 1 << 4; /* LDO9 */ + val = pmic_reg_read(pmic, RK817_POWER_SLP_EN(0)); + if (val < 0) + return val; + ret = val & mask ? 1 : 0; + } else { + mask = 1 << ldo; + val = pmic_reg_read(pmic, RK817_POWER_SLP_EN(1)); + if (val < 0) + return val; + ret = val & mask ? 1 : 0; + } + break; + } + + return ret; +} + +static int buck_get_value(struct udevice *dev) +{ + int buck = dev->driver_data - 1; + /* We assume level-1 voltage is enough for usage in U-Boot */ + const struct rk8xx_reg_info *info = get_buck_reg(dev->parent, buck, 0); + int mask = info->vsel_mask; + int ret, val; + + if (info->vsel_reg == NA) + return -ENOSYS; + + ret = pmic_reg_read(dev->parent, info->vsel_reg); + if (ret < 0) + return ret; + val = ret & mask; + + return info->min_uv + val * info->step_uv; +} + +static int buck_set_value(struct udevice *dev, int uvolt) +{ + int buck = dev->driver_data - 1; + + return _buck_set_value(dev->parent, buck, uvolt); +} + +static int buck_get_suspend_value(struct udevice *dev) +{ + int buck = dev->driver_data - 1; + /* We assume level-1 voltage is enough for usage in U-Boot */ + const struct rk8xx_reg_info *info = get_buck_reg(dev->parent, buck, 0); + int mask = info->vsel_mask; + int ret, val; + + if (info->vsel_sleep_reg == NA) + return -ENOSYS; + + ret = pmic_reg_read(dev->parent, info->vsel_sleep_reg); + if (ret < 0) + return ret; + + val = ret & mask; + + return info->min_uv + val * info->step_uv; +} + +static int buck_set_suspend_value(struct udevice *dev, int uvolt) +{ + int buck = dev->driver_data - 1; + + return _buck_set_suspend_value(dev->parent, buck, uvolt); +} + +static int buck_set_enable(struct udevice *dev, bool enable) +{ + int buck = dev->driver_data - 1; + + return _buck_set_enable(dev->parent, buck, enable); +} + +static int buck_set_suspend_enable(struct udevice *dev, bool enable) +{ + int buck = dev->driver_data - 1; + + return _buck_set_suspend_enable(dev->parent, buck, enable); +} + +static int buck_get_suspend_enable(struct udevice *dev) +{ + int buck = dev->driver_data - 1; + + return _buck_get_suspend_enable(dev->parent, buck); +} + +static int buck_get_enable(struct udevice *dev) +{ + int buck = dev->driver_data - 1; + + return _buck_get_enable(dev->parent, buck); +} + +static int ldo_get_value(struct udevice *dev) +{ + int ldo = dev->driver_data - 1; + const struct rk8xx_reg_info *info = get_ldo_reg(dev->parent, ldo, 0); + int mask = info->vsel_mask; + int ret, val; + + if (info->vsel_reg == NA) + return -ENOSYS; + ret = pmic_reg_read(dev->parent, info->vsel_reg); + if (ret < 0) + return ret; + val = ret & mask; + + return info->min_uv + val * info->step_uv; +} + +static int ldo_set_value(struct udevice *dev, int uvolt) +{ + int ldo = dev->driver_data - 1; + const struct rk8xx_reg_info *info = get_ldo_reg(dev->parent, ldo, uvolt); + int mask = info->vsel_mask; + int val; + + if (info->vsel_reg == NA) + return -ENOSYS; + + if (info->step_uv == 0) + val = info->min_sel; + else + val = ((uvolt - info->min_uv) / info->step_uv) + info->min_sel; + + debug("%s: volt=%d, ldo=%d, reg=0x%x, mask=0x%x, val=0x%x\n", + __func__, uvolt, ldo + 1, info->vsel_reg, mask, val); + + return pmic_clrsetbits(dev->parent, info->vsel_reg, mask, val); +} + +static int ldo_set_suspend_value(struct udevice *dev, int uvolt) +{ + int ldo = dev->driver_data - 1; + const struct rk8xx_reg_info *info = get_ldo_reg(dev->parent, ldo, uvolt); + int mask = info->vsel_mask; + int val; + + if (info->vsel_sleep_reg == NA) + return -ENOSYS; + + if (info->step_uv == 0) + val = info->min_sel; + else + val = ((uvolt - info->min_uv) / info->step_uv) + info->min_sel; + + debug("%s: volt=%d, ldo=%d, reg=0x%x, mask=0x%x, val=0x%x\n", + __func__, uvolt, ldo + 1, info->vsel_sleep_reg, mask, val); + + return pmic_clrsetbits(dev->parent, info->vsel_sleep_reg, mask, val); +} + +static int ldo_get_suspend_value(struct udevice *dev) +{ + int ldo = dev->driver_data - 1; + const struct rk8xx_reg_info *info = get_ldo_reg(dev->parent, ldo, 0); + int mask = info->vsel_mask; + int val, ret; + + if (info->vsel_sleep_reg == NA) + return -ENOSYS; + + ret = pmic_reg_read(dev->parent, info->vsel_sleep_reg); + if (ret < 0) + return ret; + + val = ret & mask; + + return info->min_uv + val * info->step_uv; +} + +static int ldo_set_enable(struct udevice *dev, bool enable) +{ + int ldo = dev->driver_data - 1; + + return _ldo_set_enable(dev->parent, ldo, enable); +} + +static int ldo_set_suspend_enable(struct udevice *dev, bool enable) +{ + int ldo = dev->driver_data - 1; + + return _ldo_set_suspend_enable(dev->parent, ldo, enable); +} + +static int ldo_get_suspend_enable(struct udevice *dev) +{ + int ldo = dev->driver_data - 1; + + return _ldo_get_suspend_enable(dev->parent, ldo); +} + +static int ldo_get_enable(struct udevice *dev) +{ + int ldo = dev->driver_data - 1; + + return _ldo_get_enable(dev->parent, ldo); +} + +static int switch_set_enable(struct udevice *dev, bool enable) +{ + struct rk8xx_priv *priv = dev_get_priv(dev->parent); + int ret = 0, sw = dev->driver_data - 1; + uint mask = 0; + + switch (priv->variant) { + case RK808_ID: + mask = 1 << (sw + 5); + ret = pmic_clrsetbits(dev->parent, REG_DCDC_EN, mask, + enable ? mask : 0); + break; + case RK809_ID: + mask = (1 << (sw + 2)) | (1 << (sw + 6)); + ret = pmic_clrsetbits(dev->parent, RK817_POWER_EN(3), mask, + enable ? mask : 0); + break; + case RK818_ID: + mask = 1 << 6; + ret = pmic_clrsetbits(dev->parent, REG_DCDC_EN, mask, + enable ? mask : 0); + break; + } + + debug("%s: switch%d, enable=%d, mask=0x%x\n", + __func__, sw + 1, enable, mask); + + return ret; +} + +static int switch_get_enable(struct udevice *dev) +{ + struct rk8xx_priv *priv = dev_get_priv(dev->parent); + int ret = 0, sw = dev->driver_data - 1; + uint mask = 0; + + switch (priv->variant) { + case RK808_ID: + mask = 1 << (sw + 5); + ret = pmic_reg_read(dev->parent, REG_DCDC_EN); + break; + case RK809_ID: + mask = 1 << (sw + 2); + ret = pmic_reg_read(dev->parent, RK817_POWER_EN(3)); + break; + case RK818_ID: + mask = 1 << 6; + ret = pmic_reg_read(dev->parent, REG_DCDC_EN); + break; + } + + if (ret < 0) + return ret; + + return ret & mask ? true : false; +} + +static int switch_set_suspend_value(struct udevice *dev, int uvolt) +{ + return 0; +} + +static int switch_get_suspend_value(struct udevice *dev) +{ + return 0; +} + +static int switch_set_suspend_enable(struct udevice *dev, bool enable) +{ + struct rk8xx_priv *priv = dev_get_priv(dev->parent); + int ret = 0, sw = dev->driver_data - 1; + uint mask = 0; + + switch (priv->variant) { + case RK808_ID: + mask = 1 << (sw + 5); + ret = pmic_clrsetbits(dev->parent, REG_SLEEP_SET_OFF1, mask, + enable ? 0 : mask); + break; + case RK809_ID: + mask = 1 << (sw + 6); + ret = pmic_clrsetbits(dev->parent, RK817_POWER_SLP_EN(0), mask, + enable ? mask : 0); + break; + case RK818_ID: + mask = 1 << 6; + ret = pmic_clrsetbits(dev->parent, REG_SLEEP_SET_OFF1, mask, + enable ? 0 : mask); + break; + } + + debug("%s: switch%d, enable=%d, mask=0x%x\n", + __func__, sw + 1, enable, mask); + + return ret; +} + +static int switch_get_suspend_enable(struct udevice *dev) +{ + struct rk8xx_priv *priv = dev_get_priv(dev->parent); + int val, ret = 0, sw = dev->driver_data - 1; + uint mask = 0; + + switch (priv->variant) { + case RK808_ID: + mask = 1 << (sw + 5); + val = pmic_reg_read(dev->parent, REG_SLEEP_SET_OFF1); + if (val < 0) + return val; + ret = val & mask ? 0 : 1; + break; + case RK809_ID: + mask = 1 << (sw + 6); + val = pmic_reg_read(dev->parent, RK817_POWER_SLP_EN(0)); + if (val < 0) + return val; + ret = val & mask ? 1 : 0; + break; + case RK818_ID: + mask = 1 << 6; + val = pmic_reg_read(dev->parent, REG_SLEEP_SET_OFF1); + if (val < 0) + return val; + ret = val & mask ? 0 : 1; + break; + } + + return ret; +} + +/* + * RK8xx switch does not need to set the voltage, + * but if dts set regulator-min-microvolt/regulator-max-microvolt, + * will cause regulator set value fail and not to enable this switch. + * So add an empty function to return success. + */ +static int switch_get_value(struct udevice *dev) +{ + return 0; +} + +static int switch_set_value(struct udevice *dev, int uvolt) +{ + return 0; +} + +static int rk8xx_buck_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_BUCK; + uc_pdata->mode_count = 0; + + return 0; +} + +static int rk8xx_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_LDO; + uc_pdata->mode_count = 0; + + return 0; +} + +static int rk8xx_switch_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_FIXED; + uc_pdata->mode_count = 0; + + return 0; +} + +static const struct dm_regulator_ops rk8xx_buck_ops = { + .get_value = buck_get_value, + .set_value = buck_set_value, + .set_suspend_value = buck_set_suspend_value, + .get_suspend_value = buck_get_suspend_value, + .get_enable = buck_get_enable, + .set_enable = buck_set_enable, + .set_suspend_enable = buck_set_suspend_enable, + .get_suspend_enable = buck_get_suspend_enable, +}; + +static const struct dm_regulator_ops rk8xx_ldo_ops = { + .get_value = ldo_get_value, + .set_value = ldo_set_value, + .set_suspend_value = ldo_set_suspend_value, + .get_suspend_value = ldo_get_suspend_value, + .get_enable = ldo_get_enable, + .set_enable = ldo_set_enable, + .set_suspend_enable = ldo_set_suspend_enable, + .get_suspend_enable = ldo_get_suspend_enable, +}; + +static const struct dm_regulator_ops rk8xx_switch_ops = { + .get_value = switch_get_value, + .set_value = switch_set_value, + .get_enable = switch_get_enable, + .set_enable = switch_set_enable, + .set_suspend_enable = switch_set_suspend_enable, + .get_suspend_enable = switch_get_suspend_enable, + .set_suspend_value = switch_set_suspend_value, + .get_suspend_value = switch_get_suspend_value, +}; + +U_BOOT_DRIVER(rk8xx_buck) = { + .name = "rk8xx_buck", + .id = UCLASS_REGULATOR, + .ops = &rk8xx_buck_ops, + .probe = rk8xx_buck_probe, +}; + +U_BOOT_DRIVER(rk8xx_ldo) = { + .name = "rk8xx_ldo", + .id = UCLASS_REGULATOR, + .ops = &rk8xx_ldo_ops, + .probe = rk8xx_ldo_probe, +}; + +U_BOOT_DRIVER(rk8xx_switch) = { + .name = "rk8xx_switch", + .id = UCLASS_REGULATOR, + .ops = &rk8xx_switch_ops, + .probe = rk8xx_switch_probe, +}; +#endif + +int rk8xx_spl_configure_buck(struct udevice *pmic, int buck, int uvolt) +{ + int ret; + + ret = _buck_set_value(pmic, buck, uvolt); + if (ret) + return ret; + + return _buck_set_enable(pmic, buck, true); +} + +int rk818_spl_configure_usb_input_current(struct udevice *pmic, int current_ma) +{ + uint i; + + for (i = 0; i < ARRAY_SIZE(rk818_chrg_cur_input_array); i++) + if (current_ma <= rk818_chrg_cur_input_array[i]) + break; + + return pmic_clrsetbits(pmic, REG_USB_CTRL, RK818_USB_ILIM_SEL_MASK, i); +} + +int rk818_spl_configure_usb_chrg_shutdown(struct udevice *pmic, int uvolt) +{ + uint i; + + for (i = 0; i < ARRAY_SIZE(rk818_chrg_shutdown_vsel_array); i++) + if (uvolt <= rk818_chrg_shutdown_vsel_array[i]) + break; + + return pmic_clrsetbits(pmic, REG_USB_CTRL, RK818_USB_CHG_SD_VSEL_MASK, + i); +} diff --git a/roms/u-boot/drivers/power/regulator/s2mps11_regulator.c b/roms/u-boot/drivers/power/regulator/s2mps11_regulator.c new file mode 100644 index 000000000..1c6d8358d --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/s2mps11_regulator.c @@ -0,0 +1,608 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Samsung Electronics + * Jaehoon Chung <jh80.chung@samsung.com> + */ + +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <linux/delay.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/s2mps11.h> + +#define MODE(_id, _val, _name) { \ + .id = _id, \ + .register_value = _val, \ + .name = _name, \ +} + +/* BUCK : 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 */ +static struct dm_regulator_mode s2mps11_buck_modes[] = { + MODE(OP_OFF, S2MPS11_BUCK_MODE_OFF, "OFF"), + MODE(OP_STANDBY, S2MPS11_BUCK_MODE_STANDBY, "ON/OFF"), + MODE(OP_ON, S2MPS11_BUCK_MODE_STANDBY, "ON"), +}; + +static struct dm_regulator_mode s2mps11_ldo_modes[] = { + MODE(OP_OFF, S2MPS11_LDO_MODE_OFF, "OFF"), + MODE(OP_STANDBY, S2MPS11_LDO_MODE_STANDBY, "ON/OFF"), + MODE(OP_STANDBY_LPM, S2MPS11_LDO_MODE_STANDBY_LPM, "ON/LPM"), + MODE(OP_ON, S2MPS11_LDO_MODE_ON, "ON"), +}; + +static const char s2mps11_buck_ctrl[] = { + 0xff, 0x25, 0x27, 0x29, 0x2b, 0x2d, 0x33, 0x35, 0x37, 0x39, 0x3b +}; + +static const char s2mps11_buck_out[] = { + 0xff, 0x26, 0x28, 0x2a, 0x2c, 0x2f, 0x34, 0x36, 0x38, 0x3a, 0x3c +}; + +static int s2mps11_buck_hex2volt(int buck, int hex) +{ + unsigned int uV = 0; + + if (hex < 0) + goto bad; + + switch (buck) { + case 7: + case 8: + case 10: + if (hex > S2MPS11_BUCK7_8_10_VOLT_MAX_HEX) + goto bad; + + uV = hex * S2MPS11_BUCK_HSTEP + S2MPS11_BUCK_UV_HMIN; + break; + case 9: + if (hex > S2MPS11_BUCK9_VOLT_MAX_HEX) + goto bad; + uV = hex * S2MPS11_BUCK9_STEP * 2 + S2MPS11_BUCK9_UV_MIN; + break; + default: + if (buck == 5 && hex > S2MPS11_BUCK5_VOLT_MAX_HEX) + goto bad; + else if (buck != 5 && hex > S2MPS11_BUCK_VOLT_MAX_HEX) + goto bad; + + uV = hex * S2MPS11_BUCK_LSTEP + S2MPS11_BUCK_UV_MIN; + break; + } + + return uV; +bad: + pr_err("Value: %#x is wrong for BUCK%d", hex, buck); + return -EINVAL; +} + +static int s2mps11_buck_volt2hex(int buck, int uV) +{ + int hex; + + switch (buck) { + case 7: + case 8: + case 10: + hex = (uV - S2MPS11_BUCK_UV_HMIN) / S2MPS11_BUCK_HSTEP; + if (hex > S2MPS11_BUCK7_8_10_VOLT_MAX_HEX) + goto bad; + + break; + case 9: + hex = (uV - S2MPS11_BUCK9_UV_MIN) / S2MPS11_BUCK9_STEP; + if (hex > S2MPS11_BUCK9_VOLT_MAX_HEX) + goto bad; + break; + default: + hex = (uV - S2MPS11_BUCK_UV_MIN) / S2MPS11_BUCK_LSTEP; + if (buck == 5 && hex > S2MPS11_BUCK5_VOLT_MAX_HEX) + goto bad; + else if (buck != 5 && hex > S2MPS11_BUCK_VOLT_MAX_HEX) + goto bad; + break; + }; + + if (hex >= 0) + return hex; + +bad: + pr_err("Value: %d uV is wrong for BUCK%d", uV, buck); + return -EINVAL; +} + +static int s2mps11_buck_val(struct udevice *dev, int op, int *uV) +{ + int hex, buck, ret; + u32 mask, addr; + u8 val; + + buck = dev->driver_data; + if (buck < 1 || buck > S2MPS11_BUCK_NUM) { + pr_err("Wrong buck number: %d\n", buck); + return -EINVAL; + } + + if (op == PMIC_OP_GET) + *uV = 0; + + addr = s2mps11_buck_out[buck]; + + switch (buck) { + case 9: + mask = S2MPS11_BUCK9_VOLT_MASK; + break; + default: + mask = S2MPS11_BUCK_VOLT_MASK; + break; + } + + ret = pmic_read(dev->parent, addr, &val, 1); + if (ret) + return ret; + + if (op == PMIC_OP_GET) { + val &= mask; + ret = s2mps11_buck_hex2volt(buck, val); + if (ret < 0) + return ret; + *uV = ret; + return 0; + } + + hex = s2mps11_buck_volt2hex(buck, *uV); + if (hex < 0) + return hex; + + val &= ~mask; + val |= hex; + ret = pmic_write(dev->parent, addr, &val, 1); + + return ret; +} + +static int s2mps11_buck_mode(struct udevice *dev, int op, int *opmode) +{ + unsigned int addr, mode; + unsigned char val; + int buck, ret; + + buck = dev->driver_data; + if (buck < 1 || buck > S2MPS11_BUCK_NUM) { + pr_err("Wrong buck number: %d\n", buck); + return -EINVAL; + } + + addr = s2mps11_buck_ctrl[buck]; + + ret = pmic_read(dev->parent, addr, &val, 1); + if (ret) + return ret; + + if (op == PMIC_OP_GET) { + val &= (S2MPS11_BUCK_MODE_MASK << S2MPS11_BUCK_MODE_SHIFT); + switch (val) { + case S2MPS11_BUCK_MODE_OFF: + *opmode = OP_OFF; + break; + case S2MPS11_BUCK_MODE_STANDBY: + *opmode = OP_STANDBY; + break; + case S2MPS11_BUCK_MODE_ON: + *opmode = OP_ON; + break; + default: + return -EINVAL; + } + return 0; + } + + switch (*opmode) { + case OP_OFF: + mode = S2MPS11_BUCK_MODE_OFF; + break; + case OP_STANDBY: + mode = S2MPS11_BUCK_MODE_STANDBY; + break; + case OP_ON: + mode = S2MPS11_BUCK_MODE_ON; + break; + default: + pr_err("Wrong mode: %d for buck: %d\n", *opmode, buck); + return -EINVAL; + } + + val &= ~(S2MPS11_BUCK_MODE_MASK << S2MPS11_BUCK_MODE_SHIFT); + val |= mode; + ret = pmic_write(dev->parent, addr, &val, 1); + + return ret; +} + +static int s2mps11_buck_enable(struct udevice *dev, int op, bool *enable) +{ + int ret, on_off; + + if (op == PMIC_OP_GET) { + ret = s2mps11_buck_mode(dev, op, &on_off); + if (ret) + return ret; + switch (on_off) { + case OP_OFF: + *enable = false; + break; + case OP_ON: + *enable = true; + break; + default: + return -EINVAL; + } + } else if (op == PMIC_OP_SET) { + if (*enable) + on_off = OP_ON; + else + on_off = OP_OFF; + + ret = s2mps11_buck_mode(dev, op, &on_off); + if (ret) + return ret; + } + + return 0; +} + +static int buck_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = s2mps11_buck_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + return uV; +} + +static int buck_set_value(struct udevice *dev, int uV) +{ + return s2mps11_buck_val(dev, PMIC_OP_SET, &uV); +} + +static int buck_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = s2mps11_buck_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + return enable; +} + +static int buck_set_enable(struct udevice *dev, bool enable) +{ + return s2mps11_buck_enable(dev, PMIC_OP_SET, &enable); +} + +static int buck_get_mode(struct udevice *dev) +{ + int mode; + int ret; + + ret = s2mps11_buck_mode(dev, PMIC_OP_GET, &mode); + if (ret) + return ret; + + return mode; +} + +static int buck_set_mode(struct udevice *dev, int mode) +{ + return s2mps11_buck_mode(dev, PMIC_OP_SET, &mode); +} + +static int s2mps11_buck_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_BUCK; + uc_pdata->mode = s2mps11_buck_modes; + uc_pdata->mode_count = ARRAY_SIZE(s2mps11_buck_modes); + + return 0; +} + +static const struct dm_regulator_ops s2mps11_buck_ops = { + .get_value = buck_get_value, + .set_value = buck_set_value, + .get_enable = buck_get_enable, + .set_enable = buck_set_enable, + .get_mode = buck_get_mode, + .set_mode = buck_set_mode, +}; + +U_BOOT_DRIVER(s2mps11_buck) = { + .name = S2MPS11_BUCK_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &s2mps11_buck_ops, + .probe = s2mps11_buck_probe, +}; + +static int s2mps11_ldo_hex2volt(int ldo, int hex) +{ + unsigned int uV = 0; + + if (hex > S2MPS11_LDO_VOLT_MAX_HEX) { + pr_err("Value: %#x is wrong for LDO%d", hex, ldo); + return -EINVAL; + } + + switch (ldo) { + case 1: + case 6: + case 11: + case 22: + case 23: + case 27: + case 35: + uV = hex * S2MPS11_LDO_STEP + S2MPS11_LDO_UV_MIN; + break; + default: + uV = hex * S2MPS11_LDO_STEP * 2 + S2MPS11_LDO_UV_MIN; + break; + } + + return uV; +} + +static int s2mps11_ldo_volt2hex(int ldo, int uV) +{ + int hex = 0; + + switch (ldo) { + case 1: + case 6: + case 11: + case 22: + case 23: + case 27: + case 35: + hex = (uV - S2MPS11_LDO_UV_MIN) / S2MPS11_LDO_STEP; + break; + default: + hex = (uV - S2MPS11_LDO_UV_MIN) / (S2MPS11_LDO_STEP * 2); + break; + } + + if (hex >= 0 && hex <= S2MPS11_LDO_VOLT_MAX_HEX) + return hex; + + pr_err("Value: %d uV is wrong for LDO%d", uV, ldo); + return -EINVAL; + + return 0; +} + +static int s2mps11_ldo_val(struct udevice *dev, int op, int *uV) +{ + unsigned int addr; + unsigned char val; + int hex, ldo, ret; + + ldo = dev->driver_data; + if (ldo < 1 || ldo > S2MPS11_LDO_NUM) { + pr_err("Wrong ldo number: %d\n", ldo); + return -EINVAL; + } + + addr = S2MPS11_REG_L1CTRL + ldo - 1; + + ret = pmic_read(dev->parent, addr, &val, 1); + if (ret) + return ret; + + if (op == PMIC_OP_GET) { + *uV = 0; + val &= S2MPS11_LDO_VOLT_MASK; + ret = s2mps11_ldo_hex2volt(ldo, val); + if (ret < 0) + return ret; + + *uV = ret; + return 0; + } + + hex = s2mps11_ldo_volt2hex(ldo, *uV); + if (hex < 0) + return hex; + + val &= ~S2MPS11_LDO_VOLT_MASK; + val |= hex; + ret = pmic_write(dev->parent, addr, &val, 1); + + return ret; +} + +static int s2mps11_ldo_mode(struct udevice *dev, int op, int *opmode) +{ + unsigned int addr, mode; + unsigned char val; + int ldo, ret; + + ldo = dev->driver_data; + if (ldo < 1 || ldo > S2MPS11_LDO_NUM) { + pr_err("Wrong ldo number: %d\n", ldo); + return -EINVAL; + } + addr = S2MPS11_REG_L1CTRL + ldo - 1; + + ret = pmic_read(dev->parent, addr, &val, 1); + if (ret) + return ret; + + if (op == PMIC_OP_GET) { + val &= (S2MPS11_LDO_MODE_MASK << S2MPS11_LDO_MODE_SHIFT); + switch (val) { + case S2MPS11_LDO_MODE_OFF: + *opmode = OP_OFF; + break; + case S2MPS11_LDO_MODE_STANDBY: + *opmode = OP_STANDBY; + break; + case S2MPS11_LDO_MODE_STANDBY_LPM: + *opmode = OP_STANDBY_LPM; + break; + case S2MPS11_LDO_MODE_ON: + *opmode = OP_ON; + break; + default: + return -EINVAL; + } + return 0; + } + + switch (*opmode) { + case OP_OFF: + mode = S2MPS11_LDO_MODE_OFF; + break; + case OP_STANDBY: + mode = S2MPS11_LDO_MODE_STANDBY; + break; + case OP_STANDBY_LPM: + mode = S2MPS11_LDO_MODE_STANDBY_LPM; + break; + case OP_ON: + mode = S2MPS11_LDO_MODE_ON; + break; + default: + pr_err("Wrong mode: %d for ldo: %d\n", *opmode, ldo); + return -EINVAL; + } + + val &= ~(S2MPS11_LDO_MODE_MASK << S2MPS11_LDO_MODE_SHIFT); + val |= mode; + ret = pmic_write(dev->parent, addr, &val, 1); + + return ret; +} + +static int s2mps11_ldo_enable(struct udevice *dev, int op, bool *enable) +{ + int ret, on_off; + + if (op == PMIC_OP_GET) { + ret = s2mps11_ldo_mode(dev, op, &on_off); + if (ret) + return ret; + switch (on_off) { + case OP_OFF: + *enable = false; + break; + case OP_ON: + *enable = true; + break; + default: + return -EINVAL; + } + } else if (op == PMIC_OP_SET) { + if (*enable) + on_off = OP_ON; + else + on_off = OP_OFF; + + ret = s2mps11_ldo_mode(dev, op, &on_off); + if (ret) + return ret; + } + + return 0; +} + +static int ldo_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = s2mps11_ldo_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int ldo_set_value(struct udevice *dev, int uV) +{ + return s2mps11_ldo_val(dev, PMIC_OP_SET, &uV); +} + +static int ldo_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = s2mps11_ldo_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + return enable; +} + +static int ldo_set_enable(struct udevice *dev, bool enable) +{ + int ret; + + ret = s2mps11_ldo_enable(dev, PMIC_OP_SET, &enable); + if (ret) + return ret; + + /* Wait the "enable delay" for voltage to start to rise */ + udelay(15); + + return 0; +} + +static int ldo_get_mode(struct udevice *dev) +{ + int mode, ret; + + ret = s2mps11_ldo_mode(dev, PMIC_OP_GET, &mode); + if (ret) + return ret; + return mode; +} + +static int ldo_set_mode(struct udevice *dev, int mode) +{ + return s2mps11_ldo_mode(dev, PMIC_OP_SET, &mode); +} + +static int s2mps11_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + uc_pdata->type = REGULATOR_TYPE_LDO; + uc_pdata->mode = s2mps11_ldo_modes; + uc_pdata->mode_count = ARRAY_SIZE(s2mps11_ldo_modes); + + return 0; +} + +static const struct dm_regulator_ops s2mps11_ldo_ops = { + .get_value = ldo_get_value, + .set_value = ldo_set_value, + .get_enable = ldo_get_enable, + .set_enable = ldo_set_enable, + .get_mode = ldo_get_mode, + .set_mode = ldo_set_mode, +}; + +U_BOOT_DRIVER(s2mps11_ldo) = { + .name = S2MPS11_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &s2mps11_ldo_ops, + .probe = s2mps11_ldo_probe, +}; diff --git a/roms/u-boot/drivers/power/regulator/s5m8767.c b/roms/u-boot/drivers/power/regulator/s5m8767.c new file mode 100644 index 000000000..ad0b98621 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/s5m8767.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Google, Inc + */ + +#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> + +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_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(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 int 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_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(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 int 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/roms/u-boot/drivers/power/regulator/sandbox.c b/roms/u-boot/drivers/power/regulator/sandbox.c new file mode 100644 index 000000000..c52fe3d10 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/sandbox.c @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Samsung Electronics + * Przemyslaw Marczak <p.marczak@samsung.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/sandbox_pmic.h> + +#define MODE(_id, _val, _name) [_id] = { \ + .id = _id, \ + .register_value = _val, \ + .name = _name, \ +} + +#define RANGE(_min, _max, _step) { \ + .min = _min, \ + .max = _max, \ + .step = _step, \ +} + +/* + * struct output_range - helper structure type to define the range of output + * operating values (current/voltage), limited by the PMIC IC design. + * + * @min - minimum value + * @max - maximum value + * @step - step value +*/ +struct output_range { + int min; + int max; + int step; +}; + +/* BUCK: 1,2 - voltage range */ +static struct output_range buck_voltage_range[] = { + RANGE(OUT_BUCK1_UV_MIN, OUT_BUCK1_UV_MAX, OUT_BUCK1_UV_STEP), + RANGE(OUT_BUCK2_UV_MIN, OUT_BUCK2_UV_MAX, OUT_BUCK2_UV_STEP), +}; + +/* BUCK: 1 - current range */ +static struct output_range buck_current_range[] = { + RANGE(OUT_BUCK1_UA_MIN, OUT_BUCK1_UA_MAX, OUT_BUCK1_UA_STEP), +}; + +/* BUCK operating modes */ +static struct dm_regulator_mode sandbox_buck_modes[] = { + MODE(BUCK_OM_OFF, OM2REG(BUCK_OM_OFF), "OFF"), + MODE(BUCK_OM_ON, OM2REG(BUCK_OM_ON), "ON"), + MODE(BUCK_OM_PWM, OM2REG(BUCK_OM_PWM), "PWM"), +}; + +/* LDO: 1,2 - voltage range */ +static struct output_range ldo_voltage_range[] = { + RANGE(OUT_LDO1_UV_MIN, OUT_LDO1_UV_MAX, OUT_LDO1_UV_STEP), + RANGE(OUT_LDO2_UV_MIN, OUT_LDO2_UV_MAX, OUT_LDO2_UV_STEP), +}; + +/* LDO: 1 - current range */ +static struct output_range ldo_current_range[] = { + RANGE(OUT_LDO1_UA_MIN, OUT_LDO1_UA_MAX, OUT_LDO1_UA_STEP), +}; + +/* LDO operating modes */ +static struct dm_regulator_mode sandbox_ldo_modes[] = { + MODE(LDO_OM_OFF, OM2REG(LDO_OM_OFF), "OFF"), + MODE(LDO_OM_ON, OM2REG(LDO_OM_ON), "ON"), + MODE(LDO_OM_SLEEP, OM2REG(LDO_OM_SLEEP), "SLEEP"), + MODE(LDO_OM_STANDBY, OM2REG(LDO_OM_STANDBY), "STANDBY"), +}; + +int out_get_value(struct udevice *dev, int output_count, int reg_type, + struct output_range *range) +{ + uint8_t reg_val; + uint reg; + int ret; + + if (dev->driver_data > output_count) { + pr_err("Unknown regulator number: %lu for PMIC %s!", + dev->driver_data, dev->name); + return -EINVAL; + } + + reg = (dev->driver_data - 1) * OUT_REG_COUNT + reg_type; + ret = pmic_read(dev->parent, reg, ®_val, 1); + if (ret) { + pr_err("PMIC read failed: %d\n", ret); + return ret; + } + + ret = REG2VAL(range[dev->driver_data - 1].min, + range[dev->driver_data - 1].step, + reg_val); + + return ret; +} + +static int out_set_value(struct udevice *dev, int output_count, int reg_type, + struct output_range *range, int value) +{ + uint8_t reg_val; + uint reg; + int ret; + int max_value; + + if (dev->driver_data > output_count) { + pr_err("Unknown regulator number: %lu for PMIC %s!", + dev->driver_data, dev->name); + return -EINVAL; + } + + max_value = range[dev->driver_data - 1].max; + if (value > max_value) { + pr_err("Wrong value for %s: %lu. Max is: %d.", + dev->name, dev->driver_data, max_value); + return -EINVAL; + } + + reg_val = VAL2REG(range[dev->driver_data - 1].min, + range[dev->driver_data - 1].step, + value); + + reg = (dev->driver_data - 1) * OUT_REG_COUNT + reg_type; + ret = pmic_write(dev->parent, reg, ®_val, 1); + if (ret) { + pr_err("PMIC write failed: %d\n", ret); + return ret; + } + + return 0; +} + +static int out_get_mode(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + uint8_t reg_val; + uint reg; + int ret; + int i; + + uc_pdata = dev_get_uclass_plat(dev); + + reg = (dev->driver_data - 1) * OUT_REG_COUNT + OUT_REG_OM; + ret = pmic_read(dev->parent, reg, ®_val, 1); + if (ret) { + pr_err("PMIC read failed: %d\n", ret); + return ret; + } + + for (i = 0; i < uc_pdata->mode_count; i++) { + if (reg_val == uc_pdata->mode[i].register_value) + return uc_pdata->mode[i].id; + } + + pr_err("Unknown operation mode for %s!", dev->name); + return -EINVAL; +} + +static int out_set_mode(struct udevice *dev, int mode) +{ + struct dm_regulator_uclass_plat *uc_pdata; + int reg_val = -1; + uint reg; + int ret; + int i; + + uc_pdata = dev_get_uclass_plat(dev); + + if (mode >= uc_pdata->mode_count) + return -EINVAL; + + for (i = 0; i < uc_pdata->mode_count; i++) { + if (mode == uc_pdata->mode[i].id) { + reg_val = uc_pdata->mode[i].register_value; + break; + } + } + + if (reg_val == -1) { + pr_err("Unknown operation mode for %s!", dev->name); + return -EINVAL; + } + + reg = (dev->driver_data - 1) * OUT_REG_COUNT + OUT_REG_OM; + ret = pmic_write(dev->parent, reg, (uint8_t *)®_val, 1); + if (ret) { + pr_err("PMIC write failed: %d\n", ret); + return ret; + } + + return 0; +} + +static int buck_get_voltage(struct udevice *dev) +{ + return out_get_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UV, + buck_voltage_range); +} + +static int buck_set_voltage(struct udevice *dev, int uV) +{ + return out_set_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UV, + buck_voltage_range, uV); +} + +static int buck_get_current(struct udevice *dev) +{ + /* BUCK2 - unsupported */ + if (dev->driver_data == 2) + return -ENOSYS; + + return out_get_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UA, + buck_current_range); +} + +static int buck_set_current(struct udevice *dev, int uA) +{ + /* BUCK2 - unsupported */ + if (dev->driver_data == 2) + return -ENOSYS; + + return out_set_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UA, + buck_current_range, uA); +} + +static int buck_get_enable(struct udevice *dev) +{ + if (out_get_mode(dev) == BUCK_OM_OFF) + return false; + + return true; +} + +static int buck_set_enable(struct udevice *dev, bool enable) +{ + return out_set_mode(dev, enable ? BUCK_OM_ON : BUCK_OM_OFF); +} + +static int sandbox_buck_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_BUCK; + uc_pdata->mode = sandbox_buck_modes; + uc_pdata->mode_count = ARRAY_SIZE(sandbox_buck_modes); + + return 0; +} + +static const struct dm_regulator_ops sandbox_buck_ops = { + .get_value = buck_get_voltage, + .set_value = buck_set_voltage, + .get_current = buck_get_current, + .set_current = buck_set_current, + .get_enable = buck_get_enable, + .set_enable = buck_set_enable, + .get_mode = out_get_mode, + .set_mode = out_set_mode, +}; + +U_BOOT_DRIVER(sandbox_buck) = { + .name = SANDBOX_BUCK_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &sandbox_buck_ops, + .probe = sandbox_buck_probe, +}; + +static int ldo_get_voltage(struct udevice *dev) +{ + return out_get_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UV, + ldo_voltage_range); +} + +static int ldo_set_voltage(struct udevice *dev, int uV) +{ + return out_set_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UV, + ldo_voltage_range, uV); +} + +static int ldo_get_current(struct udevice *dev) +{ + /* LDO2 - unsupported */ + if (dev->driver_data == 2) + return -ENOSYS; + + return out_get_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UA, + ldo_current_range); +} + +static int ldo_set_current(struct udevice *dev, int uA) +{ + /* LDO2 - unsupported */ + if (dev->driver_data == 2) + return -ENOSYS; + + return out_set_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UA, + ldo_current_range, uA); +} + +static int ldo_get_enable(struct udevice *dev) +{ + if (out_get_mode(dev) == LDO_OM_OFF) + return false; + + return true; +} + +static int ldo_set_enable(struct udevice *dev, bool enable) +{ + return out_set_mode(dev, enable ? LDO_OM_ON : LDO_OM_OFF); +} + +static int sandbox_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_LDO; + uc_pdata->mode = sandbox_ldo_modes; + uc_pdata->mode_count = ARRAY_SIZE(sandbox_ldo_modes); + + return 0; +} + +static const struct dm_regulator_ops sandbox_ldo_ops = { + .get_value = ldo_get_voltage, + .set_value = ldo_set_voltage, + .get_current = ldo_get_current, + .set_current = ldo_set_current, + .get_enable = ldo_get_enable, + .set_enable = ldo_set_enable, + .get_mode = out_get_mode, + .set_mode = out_set_mode, +}; + +U_BOOT_DRIVER(sandbox_ldo) = { + .name = SANDBOX_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &sandbox_ldo_ops, + .probe = sandbox_ldo_probe, +}; diff --git a/roms/u-boot/drivers/power/regulator/scmi_regulator.c b/roms/u-boot/drivers/power/regulator/scmi_regulator.c new file mode 100644 index 000000000..b3142bf4e --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/scmi_regulator.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020-2021 Linaro Limited + */ +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <scmi_agent.h> +#include <scmi_protocols.h> +#include <asm/types.h> +#include <dm/device.h> +#include <dm/device_compat.h> +#include <dm/device-internal.h> +#include <linux/kernel.h> +#include <power/regulator.h> + +/** + * struct scmi_regulator_platdata - Platform data for a scmi voltage domain regulator + * @domain_id: ID representing the regulator for the related SCMI agent + */ +struct scmi_regulator_platdata { + u32 domain_id; +}; + +static int scmi_voltd_set_enable(struct udevice *dev, bool enable) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_config_set_in in = { + .domain_id = pdata->domain_id, + .config = enable ? SCMI_VOLTD_CONFIG_ON : SCMI_VOLTD_CONFIG_OFF, + }; + struct scmi_voltd_config_set_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + SCMI_VOLTAGE_DOMAIN_CONFIG_SET, + in, out); + int ret; + + ret = devm_scmi_process_msg(dev->parent->parent, &msg); + if (ret) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret) + return ret; + + return ret; +} + +static int scmi_voltd_get_enable(struct udevice *dev) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_config_get_in in = { + .domain_id = pdata->domain_id, + }; + struct scmi_voltd_config_get_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + SCMI_VOLTAGE_DOMAIN_CONFIG_GET, + in, out); + int ret; + + ret = devm_scmi_process_msg(dev->parent->parent, &msg); + if (ret < 0) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret < 0) + return ret; + + return out.config == SCMI_VOLTD_CONFIG_ON; +} + +static int scmi_voltd_set_voltage_level(struct udevice *dev, int uV) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_level_set_in in = { + .domain_id = pdata->domain_id, + .voltage_level = uV, + }; + struct scmi_voltd_level_set_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + SCMI_VOLTAGE_DOMAIN_LEVEL_SET, + in, out); + int ret; + + ret = devm_scmi_process_msg(dev->parent->parent, &msg); + if (ret < 0) + return ret; + + return scmi_to_linux_errno(out.status); +} + +static int scmi_voltd_get_voltage_level(struct udevice *dev) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_level_get_in in = { + .domain_id = pdata->domain_id, + }; + struct scmi_voltd_level_get_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + SCMI_VOLTAGE_DOMAIN_LEVEL_GET, + in, out); + int ret; + + ret = devm_scmi_process_msg(dev->parent->parent, &msg); + if (ret < 0) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret < 0) + return ret; + + return out.voltage_level; +} + +static int scmi_regulator_of_to_plat(struct udevice *dev) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + fdt_addr_t reg; + + reg = dev_read_addr(dev); + if (reg == FDT_ADDR_T_NONE) + return -EINVAL; + + pdata->domain_id = (u32)reg; + + return 0; +} + +static int scmi_regulator_probe(struct udevice *dev) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_attr_in in = { 0 }; + struct scmi_voltd_attr_out out = { 0 }; + struct scmi_msg scmi_msg = { + .protocol_id = SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + .message_id = SCMI_VOLTAGE_DOMAIN_ATTRIBUTES, + .in_msg = (u8 *)&in, + .in_msg_sz = sizeof(in), + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + /* Check voltage domain is known from SCMI server */ + in.domain_id = pdata->domain_id; + + ret = devm_scmi_process_msg(dev->parent->parent, &scmi_msg); + if (ret) { + dev_err(dev, "Failed to query voltage domain %u: %d\n", + pdata->domain_id, ret); + return -ENXIO; + } + + return 0; +} + +static const struct dm_regulator_ops scmi_voltd_ops = { + .get_value = scmi_voltd_get_voltage_level, + .set_value = scmi_voltd_set_voltage_level, + .get_enable = scmi_voltd_get_enable, + .set_enable = scmi_voltd_set_enable, +}; + +U_BOOT_DRIVER(scmi_regulator) = { + .name = "scmi_regulator", + .id = UCLASS_REGULATOR, + .ops = &scmi_voltd_ops, + .probe = scmi_regulator_probe, + .of_to_plat = scmi_regulator_of_to_plat, + .plat_auto = sizeof(struct scmi_regulator_platdata), +}; + +static int scmi_regulator_bind(struct udevice *dev) +{ + struct driver *drv; + ofnode node; + int ret; + + drv = DM_DRIVER_GET(scmi_regulator); + + ofnode_for_each_subnode(node, dev_ofnode(dev)) { + ret = device_bind(dev, drv, ofnode_get_name(node), + NULL, node, NULL); + if (ret) + return ret; + } + + return 0; +} + +U_BOOT_DRIVER(scmi_voltage_domain) = { + .name = "scmi_voltage_domain", + .id = UCLASS_NOP, + .bind = scmi_regulator_bind, +}; diff --git a/roms/u-boot/drivers/power/regulator/stm32-vrefbuf.c b/roms/u-boot/drivers/power/regulator/stm32-vrefbuf.c new file mode 100644 index 000000000..c37998a4b --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/stm32-vrefbuf.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * Author: Fabrice Gasnier <fabrice.gasnier@st.com> + * + * Originally based on the Linux kernel v4.16 drivers/regulator/stm32-vrefbuf.c + */ + +#define LOG_CATEGORY UCLASS_REGULATOR + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <asm/io.h> +#include <dm/device_compat.h> +#include <linux/bitops.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <power/regulator.h> + +/* STM32 VREFBUF registers */ +#define STM32_VREFBUF_CSR 0x00 + +/* STM32 VREFBUF CSR bitfields */ +#define STM32_VRS GENMASK(6, 4) +#define STM32_VRS_SHIFT 4 +#define STM32_VRR BIT(3) +#define STM32_HIZ BIT(1) +#define STM32_ENVR BIT(0) + +struct stm32_vrefbuf { + void __iomem *base; + struct clk clk; + struct udevice *vdda_supply; +}; + +static const int stm32_vrefbuf_voltages[] = { + /* Matches resp. VRS = 000b, 001b, 010b, 011b */ + 2500000, 2048000, 1800000, 1500000, +}; + +static int stm32_vrefbuf_set_enable(struct udevice *dev, bool enable) +{ + struct stm32_vrefbuf *priv = dev_get_priv(dev); + u32 val; + int ret; + + if (enable && !(readl(priv->base + STM32_VREFBUF_CSR) & STM32_ENVR)) { + /* + * There maybe an overshoot: + * - when disabling, then re-enabling vrefbuf too quickly + * - or upon platform reset as external capacitor maybe slow + * discharging (VREFBUF is HiZ at reset by default). + * So force active discharge (HiZ=0) for 1ms before enabling. + */ + clrbits_le32(priv->base + STM32_VREFBUF_CSR, STM32_HIZ); + udelay(1000); + } + + clrsetbits_le32(priv->base + STM32_VREFBUF_CSR, STM32_ENVR, + enable ? STM32_ENVR : 0); + if (!enable) + return 0; + + /* + * Vrefbuf startup time depends on external capacitor: wait here for + * VRR to be set. That means output has reached expected value. + * ~650us sleep should be enough for caps up to 1.5uF. Use 10ms as + * arbitrary timeout. + */ + ret = readl_poll_timeout(priv->base + STM32_VREFBUF_CSR, val, + val & STM32_VRR, 10000); + if (ret < 0) { + dev_err(dev, "stm32 vrefbuf timed out: %d\n", ret); + clrsetbits_le32(priv->base + STM32_VREFBUF_CSR, STM32_ENVR, + STM32_HIZ); + return ret; + } + + return 0; +} + +static int stm32_vrefbuf_get_enable(struct udevice *dev) +{ + struct stm32_vrefbuf *priv = dev_get_priv(dev); + + return readl(priv->base + STM32_VREFBUF_CSR) & STM32_ENVR; +} + +static int stm32_vrefbuf_set_value(struct udevice *dev, int uV) +{ + struct stm32_vrefbuf *priv = dev_get_priv(dev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(stm32_vrefbuf_voltages); i++) { + if (uV == stm32_vrefbuf_voltages[i]) { + clrsetbits_le32(priv->base + STM32_VREFBUF_CSR, + STM32_VRS, i << STM32_VRS_SHIFT); + return 0; + } + } + + return -EINVAL; +} + +static int stm32_vrefbuf_get_value(struct udevice *dev) +{ + struct stm32_vrefbuf *priv = dev_get_priv(dev); + u32 val; + + val = readl(priv->base + STM32_VREFBUF_CSR) & STM32_VRS; + val >>= STM32_VRS_SHIFT; + + return stm32_vrefbuf_voltages[val]; +} + +static const struct dm_regulator_ops stm32_vrefbuf_ops = { + .get_value = stm32_vrefbuf_get_value, + .set_value = stm32_vrefbuf_set_value, + .get_enable = stm32_vrefbuf_get_enable, + .set_enable = stm32_vrefbuf_set_enable, +}; + +static int stm32_vrefbuf_probe(struct udevice *dev) +{ + struct stm32_vrefbuf *priv = dev_get_priv(dev); + int ret; + + priv->base = dev_read_addr_ptr(dev); + + ret = clk_get_by_index(dev, 0, &priv->clk); + if (ret) { + dev_err(dev, "Can't get clock: %d\n", ret); + return ret; + } + + ret = clk_enable(&priv->clk); + if (ret) { + dev_err(dev, "Can't enable clock: %d\n", ret); + return ret; + } + + ret = device_get_supply_regulator(dev, "vdda-supply", + &priv->vdda_supply); + if (ret) { + dev_dbg(dev, "No vdda-supply: %d\n", ret); + return 0; + } + + ret = regulator_set_enable(priv->vdda_supply, true); + if (ret) { + dev_err(dev, "Can't enable vdda-supply: %d\n", ret); + clk_disable(&priv->clk); + } + + return ret; +} + +static const struct udevice_id stm32_vrefbuf_ids[] = { + { .compatible = "st,stm32-vrefbuf" }, + { } +}; + +U_BOOT_DRIVER(stm32_vrefbuf) = { + .name = "stm32-vrefbuf", + .id = UCLASS_REGULATOR, + .of_match = stm32_vrefbuf_ids, + .probe = stm32_vrefbuf_probe, + .ops = &stm32_vrefbuf_ops, + .priv_auto = sizeof(struct stm32_vrefbuf), +}; diff --git a/roms/u-boot/drivers/power/regulator/stpmic1.c b/roms/u-boot/drivers/power/regulator/stpmic1.c new file mode 100644 index 000000000..4839d8343 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/stpmic1.c @@ -0,0 +1,674 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * Author: Christophe Kerello <christophe.kerello@st.com> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <linux/delay.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/stpmic1.h> + +struct stpmic1_range { + int min_uv; + int min_sel; + int max_sel; + int step; +}; + +struct stpmic1_output { + const struct stpmic1_range *ranges; + int nbranges; +}; + +#define STPMIC1_MODE(_id, _val, _name) { \ + .id = _id, \ + .register_value = _val, \ + .name = _name, \ +} + +#define STPMIC1_RANGE(_min_uv, _min_sel, _max_sel, _step) { \ + .min_uv = _min_uv, \ + .min_sel = _min_sel, \ + .max_sel = _max_sel, \ + .step = _step, \ +} + +#define STPMIC1_OUTPUT(_ranges, _nbranges) { \ + .ranges = _ranges, \ + .nbranges = _nbranges, \ +} + +static int stpmic1_output_find_uv(int sel, + const struct stpmic1_output *output) +{ + const struct stpmic1_range *range; + int i; + + for (i = 0, range = output->ranges; + i < output->nbranges; i++, range++) { + if (sel >= range->min_sel && sel <= range->max_sel) + return range->min_uv + + (sel - range->min_sel) * range->step; + } + + return -EINVAL; +} + +static int stpmic1_output_find_sel(int uv, + const struct stpmic1_output *output) +{ + const struct stpmic1_range *range; + int i; + + for (i = 0, range = output->ranges; + i < output->nbranges; i++, range++) { + if (uv == range->min_uv && !range->step) + return range->min_sel; + + if (uv >= range->min_uv && + uv <= range->min_uv + + (range->max_sel - range->min_sel) * range->step) + return range->min_sel + + (uv - range->min_uv) / range->step; + } + + return -EINVAL; +} + +/* + * BUCK regulators + */ + +static const struct stpmic1_range buck1_ranges[] = { + STPMIC1_RANGE(725000, 0, 4, 0), + STPMIC1_RANGE(725000, 5, 36, 25000), + STPMIC1_RANGE(1500000, 37, 63, 0), +}; + +static const struct stpmic1_range buck2_ranges[] = { + STPMIC1_RANGE(1000000, 0, 17, 0), + STPMIC1_RANGE(1050000, 18, 19, 0), + STPMIC1_RANGE(1100000, 20, 21, 0), + STPMIC1_RANGE(1150000, 22, 23, 0), + STPMIC1_RANGE(1200000, 24, 25, 0), + STPMIC1_RANGE(1250000, 26, 27, 0), + STPMIC1_RANGE(1300000, 28, 29, 0), + STPMIC1_RANGE(1350000, 30, 31, 0), + STPMIC1_RANGE(1400000, 32, 33, 0), + STPMIC1_RANGE(1450000, 34, 35, 0), + STPMIC1_RANGE(1500000, 36, 63, 0), +}; + +static const struct stpmic1_range buck3_ranges[] = { + STPMIC1_RANGE(1000000, 0, 19, 0), + STPMIC1_RANGE(1100000, 20, 23, 0), + STPMIC1_RANGE(1200000, 24, 27, 0), + STPMIC1_RANGE(1300000, 28, 31, 0), + STPMIC1_RANGE(1400000, 32, 35, 0), + STPMIC1_RANGE(1500000, 36, 55, 100000), + STPMIC1_RANGE(3400000, 56, 63, 0), +}; + +static const struct stpmic1_range buck4_ranges[] = { + STPMIC1_RANGE(600000, 0, 27, 25000), + STPMIC1_RANGE(1300000, 28, 29, 0), + STPMIC1_RANGE(1350000, 30, 31, 0), + STPMIC1_RANGE(1400000, 32, 33, 0), + STPMIC1_RANGE(1450000, 34, 35, 0), + STPMIC1_RANGE(1500000, 36, 60, 100000), + STPMIC1_RANGE(3900000, 61, 63, 0), +}; + +/* BUCK: 1,2,3,4 - voltage ranges */ +static const struct stpmic1_output buck_voltage_range[] = { + STPMIC1_OUTPUT(buck1_ranges, ARRAY_SIZE(buck1_ranges)), + STPMIC1_OUTPUT(buck2_ranges, ARRAY_SIZE(buck2_ranges)), + STPMIC1_OUTPUT(buck3_ranges, ARRAY_SIZE(buck3_ranges)), + STPMIC1_OUTPUT(buck4_ranges, ARRAY_SIZE(buck4_ranges)), +}; + +/* BUCK modes */ +static const struct dm_regulator_mode buck_modes[] = { + STPMIC1_MODE(STPMIC1_PREG_MODE_HP, STPMIC1_PREG_MODE_HP, "HP"), + STPMIC1_MODE(STPMIC1_PREG_MODE_LP, STPMIC1_PREG_MODE_LP, "LP"), +}; + +static int stpmic1_buck_get_uv(struct udevice *dev, int buck) +{ + int sel; + + sel = pmic_reg_read(dev, STPMIC1_BUCKX_MAIN_CR(buck)); + if (sel < 0) + return sel; + + sel &= STPMIC1_BUCK_VOUT_MASK; + sel >>= STPMIC1_BUCK_VOUT_SHIFT; + + return stpmic1_output_find_uv(sel, &buck_voltage_range[buck]); +} + +static int stpmic1_buck_get_value(struct udevice *dev) +{ + return stpmic1_buck_get_uv(dev->parent, dev->driver_data - 1); +} + +static int stpmic1_buck_set_value(struct udevice *dev, int uv) +{ + int sel, buck = dev->driver_data - 1; + + sel = stpmic1_output_find_sel(uv, &buck_voltage_range[buck]); + if (sel < 0) + return sel; + + return pmic_clrsetbits(dev->parent, + STPMIC1_BUCKX_MAIN_CR(buck), + STPMIC1_BUCK_VOUT_MASK, + sel << STPMIC1_BUCK_VOUT_SHIFT); +} + +static int stpmic1_buck_get_enable(struct udevice *dev) +{ + int ret; + + ret = pmic_reg_read(dev->parent, + STPMIC1_BUCKX_MAIN_CR(dev->driver_data - 1)); + if (ret < 0) + return false; + + return ret & STPMIC1_BUCK_ENA ? true : false; +} + +static int stpmic1_buck_set_enable(struct udevice *dev, bool enable) +{ + struct dm_regulator_uclass_plat *uc_pdata; + int delay = enable ? STPMIC1_DEFAULT_START_UP_DELAY_MS : + STPMIC1_DEFAULT_STOP_DELAY_MS; + int ret, uv; + + /* if regulator is already in the wanted state, nothing to do */ + if (stpmic1_buck_get_enable(dev) == enable) + return 0; + + if (enable) { + uc_pdata = dev_get_uclass_plat(dev); + uv = stpmic1_buck_get_value(dev); + if (uv < uc_pdata->min_uV || uv > uc_pdata->max_uV) + stpmic1_buck_set_value(dev, uc_pdata->min_uV); + } + + ret = pmic_clrsetbits(dev->parent, + STPMIC1_BUCKX_MAIN_CR(dev->driver_data - 1), + STPMIC1_BUCK_ENA, enable ? STPMIC1_BUCK_ENA : 0); + mdelay(delay); + + return ret; +} + +static int stpmic1_buck_get_mode(struct udevice *dev) +{ + int ret; + + ret = pmic_reg_read(dev->parent, + STPMIC1_BUCKX_MAIN_CR(dev->driver_data - 1)); + if (ret < 0) + return ret; + + return ret & STPMIC1_BUCK_PREG_MODE ? STPMIC1_PREG_MODE_LP : + STPMIC1_PREG_MODE_HP; +} + +static int stpmic1_buck_set_mode(struct udevice *dev, int mode) +{ + return pmic_clrsetbits(dev->parent, + STPMIC1_BUCKX_MAIN_CR(dev->driver_data - 1), + STPMIC1_BUCK_PREG_MODE, + mode ? STPMIC1_BUCK_PREG_MODE : 0); +} + +static int stpmic1_buck_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + if (!dev->driver_data || dev->driver_data > STPMIC1_MAX_BUCK) + return -EINVAL; + + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_BUCK; + uc_pdata->mode = (struct dm_regulator_mode *)buck_modes; + uc_pdata->mode_count = ARRAY_SIZE(buck_modes); + + return 0; +} + +static const struct dm_regulator_ops stpmic1_buck_ops = { + .get_value = stpmic1_buck_get_value, + .set_value = stpmic1_buck_set_value, + .get_enable = stpmic1_buck_get_enable, + .set_enable = stpmic1_buck_set_enable, + .get_mode = stpmic1_buck_get_mode, + .set_mode = stpmic1_buck_set_mode, +}; + +U_BOOT_DRIVER(stpmic1_buck) = { + .name = "stpmic1_buck", + .id = UCLASS_REGULATOR, + .ops = &stpmic1_buck_ops, + .probe = stpmic1_buck_probe, +}; + +/* + * LDO regulators + */ + +static const struct stpmic1_range ldo12_ranges[] = { + STPMIC1_RANGE(1700000, 0, 7, 0), + STPMIC1_RANGE(1700000, 8, 24, 100000), + STPMIC1_RANGE(3300000, 25, 31, 0), +}; + +static const struct stpmic1_range ldo3_ranges[] = { + STPMIC1_RANGE(1700000, 0, 7, 0), + STPMIC1_RANGE(1700000, 8, 24, 100000), + STPMIC1_RANGE(3300000, 25, 30, 0), + /* Sel 31 is special case when LDO3 is in mode sync_source (BUCK2/2) */ +}; + +static const struct stpmic1_range ldo5_ranges[] = { + STPMIC1_RANGE(1700000, 0, 7, 0), + STPMIC1_RANGE(1700000, 8, 30, 100000), + STPMIC1_RANGE(3900000, 31, 31, 0), +}; + +static const struct stpmic1_range ldo6_ranges[] = { + STPMIC1_RANGE(900000, 0, 24, 100000), + STPMIC1_RANGE(3300000, 25, 31, 0), +}; + +/* LDO: 1,2,3,4,5,6 - voltage ranges */ +static const struct stpmic1_output ldo_voltage_range[] = { + STPMIC1_OUTPUT(ldo12_ranges, ARRAY_SIZE(ldo12_ranges)), + STPMIC1_OUTPUT(ldo12_ranges, ARRAY_SIZE(ldo12_ranges)), + STPMIC1_OUTPUT(ldo3_ranges, ARRAY_SIZE(ldo3_ranges)), + STPMIC1_OUTPUT(NULL, 0), + STPMIC1_OUTPUT(ldo5_ranges, ARRAY_SIZE(ldo5_ranges)), + STPMIC1_OUTPUT(ldo6_ranges, ARRAY_SIZE(ldo6_ranges)), +}; + +/* LDO modes */ +static const struct dm_regulator_mode ldo_modes[] = { + STPMIC1_MODE(STPMIC1_LDO_MODE_NORMAL, + STPMIC1_LDO_MODE_NORMAL, "NORMAL"), + STPMIC1_MODE(STPMIC1_LDO_MODE_BYPASS, + STPMIC1_LDO_MODE_BYPASS, "BYPASS"), + STPMIC1_MODE(STPMIC1_LDO_MODE_SINK_SOURCE, + STPMIC1_LDO_MODE_SINK_SOURCE, "SINK SOURCE"), +}; + +static int stpmic1_ldo_get_value(struct udevice *dev) +{ + int sel, ldo = dev->driver_data - 1; + + sel = pmic_reg_read(dev->parent, STPMIC1_LDOX_MAIN_CR(ldo)); + if (sel < 0) + return sel; + + /* ldo4 => 3,3V */ + if (ldo == STPMIC1_LDO4) + return STPMIC1_LDO4_UV; + + sel &= STPMIC1_LDO12356_VOUT_MASK; + sel >>= STPMIC1_LDO12356_VOUT_SHIFT; + + /* ldo3, sel = 31 => BUCK2/2 */ + if (ldo == STPMIC1_LDO3 && sel == STPMIC1_LDO3_DDR_SEL) + return stpmic1_buck_get_uv(dev->parent, STPMIC1_BUCK2) / 2; + + return stpmic1_output_find_uv(sel, &ldo_voltage_range[ldo]); +} + +static int stpmic1_ldo_set_value(struct udevice *dev, int uv) +{ + int sel, ldo = dev->driver_data - 1; + + /* ldo4 => not possible */ + if (ldo == STPMIC1_LDO4) + return -EINVAL; + + sel = stpmic1_output_find_sel(uv, &ldo_voltage_range[ldo]); + if (sel < 0) + return sel; + + return pmic_clrsetbits(dev->parent, + STPMIC1_LDOX_MAIN_CR(ldo), + STPMIC1_LDO12356_VOUT_MASK, + sel << STPMIC1_LDO12356_VOUT_SHIFT); +} + +static int stpmic1_ldo_get_enable(struct udevice *dev) +{ + int ret; + + ret = pmic_reg_read(dev->parent, + STPMIC1_LDOX_MAIN_CR(dev->driver_data - 1)); + if (ret < 0) + return false; + + return ret & STPMIC1_LDO_ENA ? true : false; +} + +static int stpmic1_ldo_set_enable(struct udevice *dev, bool enable) +{ + struct dm_regulator_uclass_plat *uc_pdata; + int delay = enable ? STPMIC1_DEFAULT_START_UP_DELAY_MS : + STPMIC1_DEFAULT_STOP_DELAY_MS; + int ret, uv; + + /* if regulator is already in the wanted state, nothing to do */ + if (stpmic1_ldo_get_enable(dev) == enable) + return 0; + + if (enable) { + uc_pdata = dev_get_uclass_plat(dev); + uv = stpmic1_ldo_get_value(dev); + if (uv < uc_pdata->min_uV || uv > uc_pdata->max_uV) + stpmic1_ldo_set_value(dev, uc_pdata->min_uV); + } + + ret = pmic_clrsetbits(dev->parent, + STPMIC1_LDOX_MAIN_CR(dev->driver_data - 1), + STPMIC1_LDO_ENA, enable ? STPMIC1_LDO_ENA : 0); + mdelay(delay); + + return ret; +} + +static int stpmic1_ldo_get_mode(struct udevice *dev) +{ + int ret, ldo = dev->driver_data - 1; + + if (ldo != STPMIC1_LDO3) + return -EINVAL; + + ret = pmic_reg_read(dev->parent, STPMIC1_LDOX_MAIN_CR(ldo)); + if (ret < 0) + return ret; + + if (ret & STPMIC1_LDO3_MODE) + return STPMIC1_LDO_MODE_BYPASS; + + ret &= STPMIC1_LDO12356_VOUT_MASK; + ret >>= STPMIC1_LDO12356_VOUT_SHIFT; + + return ret == STPMIC1_LDO3_DDR_SEL ? STPMIC1_LDO_MODE_SINK_SOURCE : + STPMIC1_LDO_MODE_NORMAL; +} + +static int stpmic1_ldo_set_mode(struct udevice *dev, int mode) +{ + int ret, ldo = dev->driver_data - 1; + + if (ldo != STPMIC1_LDO3) + return -EINVAL; + + ret = pmic_reg_read(dev->parent, STPMIC1_LDOX_MAIN_CR(ldo)); + if (ret < 0) + return ret; + + switch (mode) { + case STPMIC1_LDO_MODE_SINK_SOURCE: + ret &= ~STPMIC1_LDO12356_VOUT_MASK; + ret |= STPMIC1_LDO3_DDR_SEL << STPMIC1_LDO12356_VOUT_SHIFT; + /* fallthrough */ + case STPMIC1_LDO_MODE_NORMAL: + ret &= ~STPMIC1_LDO3_MODE; + break; + case STPMIC1_LDO_MODE_BYPASS: + ret |= STPMIC1_LDO3_MODE; + break; + } + + return pmic_reg_write(dev->parent, STPMIC1_LDOX_MAIN_CR(ldo), ret); +} + +static int stpmic1_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + if (!dev->driver_data || dev->driver_data > STPMIC1_MAX_LDO) + return -EINVAL; + + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_LDO; + if (dev->driver_data - 1 == STPMIC1_LDO3) { + uc_pdata->mode = (struct dm_regulator_mode *)ldo_modes; + uc_pdata->mode_count = ARRAY_SIZE(ldo_modes); + } else { + uc_pdata->mode_count = 0; + } + + return 0; +} + +static const struct dm_regulator_ops stpmic1_ldo_ops = { + .get_value = stpmic1_ldo_get_value, + .set_value = stpmic1_ldo_set_value, + .get_enable = stpmic1_ldo_get_enable, + .set_enable = stpmic1_ldo_set_enable, + .get_mode = stpmic1_ldo_get_mode, + .set_mode = stpmic1_ldo_set_mode, +}; + +U_BOOT_DRIVER(stpmic1_ldo) = { + .name = "stpmic1_ldo", + .id = UCLASS_REGULATOR, + .ops = &stpmic1_ldo_ops, + .probe = stpmic1_ldo_probe, +}; + +/* + * VREF DDR regulator + */ + +static int stpmic1_vref_ddr_get_value(struct udevice *dev) +{ + /* BUCK2/2 */ + return stpmic1_buck_get_uv(dev->parent, STPMIC1_BUCK2) / 2; +} + +static int stpmic1_vref_ddr_get_enable(struct udevice *dev) +{ + int ret; + + ret = pmic_reg_read(dev->parent, STPMIC1_REFDDR_MAIN_CR); + if (ret < 0) + return false; + + return ret & STPMIC1_VREF_ENA ? true : false; +} + +static int stpmic1_vref_ddr_set_enable(struct udevice *dev, bool enable) +{ + int delay = enable ? STPMIC1_DEFAULT_START_UP_DELAY_MS : + STPMIC1_DEFAULT_STOP_DELAY_MS; + int ret; + + /* if regulator is already in the wanted state, nothing to do */ + if (stpmic1_vref_ddr_get_enable(dev) == enable) + return 0; + + ret = pmic_clrsetbits(dev->parent, STPMIC1_REFDDR_MAIN_CR, + STPMIC1_VREF_ENA, enable ? STPMIC1_VREF_ENA : 0); + mdelay(delay); + + return ret; +} + +static int stpmic1_vref_ddr_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_FIXED; + uc_pdata->mode_count = 0; + + return 0; +} + +static const struct dm_regulator_ops stpmic1_vref_ddr_ops = { + .get_value = stpmic1_vref_ddr_get_value, + .get_enable = stpmic1_vref_ddr_get_enable, + .set_enable = stpmic1_vref_ddr_set_enable, +}; + +U_BOOT_DRIVER(stpmic1_vref_ddr) = { + .name = "stpmic1_vref_ddr", + .id = UCLASS_REGULATOR, + .ops = &stpmic1_vref_ddr_ops, + .probe = stpmic1_vref_ddr_probe, +}; + +/* + * BOOST regulator + */ + +static int stpmic1_boost_get_enable(struct udevice *dev) +{ + int ret; + + ret = pmic_reg_read(dev->parent, STPMIC1_BST_SW_CR); + if (ret < 0) + return false; + + return ret & STPMIC1_BST_ON ? true : false; +} + +static int stpmic1_boost_set_enable(struct udevice *dev, bool enable) +{ + int ret; + + ret = pmic_reg_read(dev->parent, STPMIC1_BST_SW_CR); + if (ret < 0) + return ret; + + if (!enable && ret & STPMIC1_PWR_SW_ON) + return -EINVAL; + + /* if regulator is already in the wanted state, nothing to do */ + if (!!(ret & STPMIC1_BST_ON) == enable) + return 0; + + ret = pmic_clrsetbits(dev->parent, STPMIC1_BST_SW_CR, + STPMIC1_BST_ON, + enable ? STPMIC1_BST_ON : 0); + if (enable) + mdelay(STPMIC1_USB_BOOST_START_UP_DELAY_MS); + + return ret; +} + +static int stpmic1_boost_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_FIXED; + uc_pdata->mode_count = 0; + + return 0; +} + +static const struct dm_regulator_ops stpmic1_boost_ops = { + .get_enable = stpmic1_boost_get_enable, + .set_enable = stpmic1_boost_set_enable, +}; + +U_BOOT_DRIVER(stpmic1_boost) = { + .name = "stpmic1_boost", + .id = UCLASS_REGULATOR, + .ops = &stpmic1_boost_ops, + .probe = stpmic1_boost_probe, +}; + +/* + * USB power switch + */ + +static int stpmic1_pwr_sw_get_enable(struct udevice *dev) +{ + uint mask = 1 << dev->driver_data; + int ret; + + ret = pmic_reg_read(dev->parent, STPMIC1_BST_SW_CR); + if (ret < 0) + return false; + + return ret & mask ? true : false; +} + +static int stpmic1_pwr_sw_set_enable(struct udevice *dev, bool enable) +{ + uint mask = 1 << dev->driver_data; + int delay = enable ? STPMIC1_DEFAULT_START_UP_DELAY_MS : + STPMIC1_DEFAULT_STOP_DELAY_MS; + int ret; + + ret = pmic_reg_read(dev->parent, STPMIC1_BST_SW_CR); + if (ret < 0) + return ret; + + /* if regulator is already in the wanted state, nothing to do */ + if (!!(ret & mask) == enable) + return 0; + + /* Boost management */ + if (enable && !(ret & STPMIC1_BST_ON)) { + pmic_clrsetbits(dev->parent, STPMIC1_BST_SW_CR, + STPMIC1_BST_ON, STPMIC1_BST_ON); + mdelay(STPMIC1_USB_BOOST_START_UP_DELAY_MS); + } else if (!enable && ret & STPMIC1_BST_ON && + (ret & STPMIC1_PWR_SW_ON) != STPMIC1_PWR_SW_ON) { + pmic_clrsetbits(dev->parent, STPMIC1_BST_SW_CR, + STPMIC1_BST_ON, 0); + } + + ret = pmic_clrsetbits(dev->parent, STPMIC1_BST_SW_CR, + mask, enable ? mask : 0); + mdelay(delay); + + return ret; +} + +static int stpmic1_pwr_sw_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata; + + if (!dev->driver_data || dev->driver_data > STPMIC1_MAX_PWR_SW) + return -EINVAL; + + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_FIXED; + uc_pdata->mode_count = 0; + + return 0; +} + +static const struct dm_regulator_ops stpmic1_pwr_sw_ops = { + .get_enable = stpmic1_pwr_sw_get_enable, + .set_enable = stpmic1_pwr_sw_set_enable, +}; + +U_BOOT_DRIVER(stpmic1_pwr_sw) = { + .name = "stpmic1_pwr_sw", + .id = UCLASS_REGULATOR, + .ops = &stpmic1_pwr_sw_ops, + .probe = stpmic1_pwr_sw_probe, +}; diff --git a/roms/u-boot/drivers/power/regulator/tps62360_regulator.c b/roms/u-boot/drivers/power/regulator/tps62360_regulator.c new file mode 100644 index 000000000..b9f450453 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/tps62360_regulator.c @@ -0,0 +1,124 @@ +// 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 <dm/device_compat.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_plat(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_plat(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_probe(struct udevice *dev) +{ + struct tps62360_regulator_pdata *pdata = dev_get_plat(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, + .plat_auto = sizeof(struct tps62360_regulator_pdata), + .probe = tps62360_regulator_probe, +}; diff --git a/roms/u-boot/drivers/power/regulator/tps65090_regulator.c b/roms/u-boot/drivers/power/regulator/tps65090_regulator.c new file mode 100644 index 000000000..174ee58d0 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/tps65090_regulator.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2015 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <linux/delay.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_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_OTHER; + uc_pdata->mode_count = 0; + + return 0; +} + +static int 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, +}; diff --git a/roms/u-boot/drivers/power/regulator/tps65910_regulator.c b/roms/u-boot/drivers/power/regulator/tps65910_regulator.c new file mode 100644 index 000000000..0ed4952a1 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/tps65910_regulator.c @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) EETS GmbH, 2017, Felix Brack <f.brack@eets.ch> + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/tps65910_pmic.h> + +#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_plat(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_plat(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_plat(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_plat(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_plat(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_plat(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_plat(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_plat *uc_pdata; + struct tps65910_regulator_pdata *pdata = dev_get_plat(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_plat(dev); + + /* check setpoint is within limits */ + if (uV < uc_pdata->min_uV) { + pr_err("voltage %duV for %s too low\n", uV, dev->name); + return -EINVAL; + } + if (uV > uc_pdata->max_uV) { + pr_err("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_plat(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_plat(dev); + + vout = (pdata->supply >= 3000000) ? 5000000 : 0; + return vout; +} + +static int tps65910_regulator_of_to_plat(struct udevice *dev) +{ + struct udevice *supply; + int ret; + const char *supply_name; + struct tps65910_regulator_pdata *pdata = dev_get_plat(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, + .plat_auto = sizeof(struct tps65910_regulator_pdata), + .of_to_plat = tps65910_regulator_of_to_plat, +}; + +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, + .plat_auto = sizeof(struct tps65910_regulator_pdata), + .of_to_plat = tps65910_regulator_of_to_plat, +}; + +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, + .plat_auto = sizeof(struct tps65910_regulator_pdata), + .of_to_plat = tps65910_regulator_of_to_plat, +}; diff --git a/roms/u-boot/drivers/power/regulator/tps65941_regulator.c b/roms/u-boot/drivers/power/regulator/tps65941_regulator.c new file mode 100644 index 000000000..d73f83248 --- /dev/null +++ b/roms/u-boot/drivers/power/regulator/tps65941_regulator.c @@ -0,0 +1,409 @@ +// 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 <log.h> +#include <linux/delay.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_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(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_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(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_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(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_plat *uc_pdata; + + uc_pdata = dev_get_uclass_plat(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_plat *uc_pdata; + int idx; + + uc_pdata = dev_get_uclass_plat(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_plat *uc_pdata; + int idx; + + uc_pdata = dev_get_uclass_plat(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, +}; |