diff options
-rw-r--r-- | arch/arm/mach-sunxi/pmic_bus.c | 3 | ||||
-rw-r--r-- | board/sunxi/board.c | 3 | ||||
-rw-r--r-- | drivers/power/Kconfig | 17 | ||||
-rw-r--r-- | drivers/power/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/axp313.c | 134 |
5 files changed, 155 insertions, 3 deletions
diff --git a/arch/arm/mach-sunxi/pmic_bus.c b/arch/arm/mach-sunxi/pmic_bus.c index c0908406370..8e7625fe057 100644 --- a/arch/arm/mach-sunxi/pmic_bus.c +++ b/arch/arm/mach-sunxi/pmic_bus.c @@ -22,6 +22,7 @@ #define AXP209_I2C_ADDR 0x34 #define AXP305_I2C_ADDR 0x36 +#define AXP313_I2C_ADDR 0x36 #define AXP221_CHIP_ADDR 0x68 @@ -34,6 +35,8 @@ static int pmic_i2c_address(void) return AXP152_I2C_ADDR; if (IS_ENABLED(CONFIG_AXP305_POWER)) return AXP305_I2C_ADDR; + if (IS_ENABLED(CONFIG_AXP313_POWER)) + return AXP313_I2C_ADDR; /* Other AXP2xx and AXP8xx variants */ return AXP209_I2C_ADDR; diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 80589f96402..8c12c8deade 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -569,7 +569,8 @@ void sunxi_board_init(void) #if defined CONFIG_AXP152_POWER || defined CONFIG_AXP209_POWER || \ defined CONFIG_AXP221_POWER || defined CONFIG_AXP305_POWER || \ - defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER + defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER || \ + defined CONFIG_AXP313_POWER power_failed = axp_init(); if (IS_ENABLED(CONFIG_AXP_DISABLE_BOOT_ON_POWERON) && !power_failed) { diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 83cb31c937a..2395720c99c 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -101,6 +101,15 @@ config AXP305_POWER Select this to enable support for the axp305 pmic found on most H616 boards. +config AXP313_POWER + bool "axp313 pmic support" + depends on MACH_SUN50I_H616 + select AXP_PMIC_BUS + select CMD_POWEROFF + ---help--- + Select this to enable support for the AXP313 PMIC found on some + H616 boards. + config AXP809_POWER bool "axp809 pmic support" depends on MACH_SUN9I @@ -143,9 +152,10 @@ config AXP_DCDC1_VOLT config AXP_DCDC2_VOLT int "axp pmic dcdc2 voltage" - depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER + depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP313_POWER default 900 if AXP818_POWER default 1400 if AXP152_POWER || AXP209_POWER + default 1000 if AXP313_POWER default 1200 if MACH_SUN6I default 1100 if MACH_SUN8I default 0 if MACH_SUN9I @@ -158,13 +168,15 @@ config AXP_DCDC2_VOLT On A80 boards dcdc2 powers the GPU and can be left off. On A83T boards dcdc2 is used for VDD-CPUA(cluster 0) and should be 0.9V. On R40 boards dcdc2 is VDD-CPU and should be 1.1V + On boards using the AXP313 it's often VDD-CPU. config AXP_DCDC3_VOLT int "axp pmic dcdc3 voltage" - depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER + depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP313_POWER default 900 if AXP809_POWER || AXP818_POWER default 1500 if AXP152_POWER default 1250 if AXP209_POWER + default 1100 if AXP313_POWER default 1100 if MACH_SUN8I_R40 default 1200 if MACH_SUN6I || MACH_SUN8I ---help--- @@ -177,6 +189,7 @@ config AXP_DCDC3_VOLT On A80 boards dcdc3 is used for VDD-CPUA(cluster 0) and should be 0.9V. On A83T boards dcdc3 is used for VDD-CPUB(cluster 1) and should be 0.9V. On R40 boards dcdc3 is VDD-SYS and VDD-GPU and should be 1.1V. + On boards using the AXP313 it's often VDD-DRAM and should be 1.1V for LPDDR4. config AXP_DCDC4_VOLT int "axp pmic dcdc4 voltage" diff --git a/drivers/power/Makefile b/drivers/power/Makefile index ba64b2c5938..c7ee4595fc8 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_AXP152_POWER) += axp152.o obj-$(CONFIG_AXP209_POWER) += axp209.o obj-$(CONFIG_AXP221_POWER) += axp221.o obj-$(CONFIG_AXP305_POWER) += axp305.o +obj-$(CONFIG_AXP313_POWER) += axp313.o obj-$(CONFIG_AXP809_POWER) += axp809.o obj-$(CONFIG_AXP818_POWER) += axp818.o obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o diff --git a/drivers/power/axp313.c b/drivers/power/axp313.c new file mode 100644 index 00000000000..bbc9e911115 --- /dev/null +++ b/drivers/power/axp313.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AXP313(a) driver + * + * (C) Copyright 2023 Arm Ltd. + * + * Based on axp305.c + * (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net> + * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com> + * (C) Copyright 2013 Oliver Schinagl <oliver@schinagl.nl> + */ + +#include <common.h> +#include <command.h> +#include <errno.h> +#include <asm/arch/pmic_bus.h> +#include <axp_pmic.h> + +enum axp313_reg { + AXP313_CHIP_VERSION = 0x03, + AXP313_OUTPUT_CTRL = 0x10, + AXP313_DCDC1_CTRL = 0x13, + AXP313_SHUTDOWN = 0x1a, +}; + +#define AXP313_CHIP_VERSION_MASK 0xcf +#define AXP313_CHIP_VERSION_AXP1530 0x48 +#define AXP313_CHIP_VERSION_AXP313A 0x4b +#define AXP313_CHIP_VERSION_AXP313B 0x4c + +#define AXP313_DCDC_SPLIT_OFFSET 71 +#define AXP313_DCDC_SPLIT_MVOLT 1200 + +#define AXP313_POWEROFF BIT(7) + +static u8 mvolt_to_cfg(int mvolt, int min, int max, int div) +{ + if (mvolt < min) + mvolt = min; + else if (mvolt > max) + mvolt = max; + + return (mvolt - min) / div; +} + +static int axp_set_dcdc(int dcdc_num, unsigned int mvolt) +{ + int ret; + u8 cfg, enable_mask = 1U << (dcdc_num - 1); + int volt_reg = AXP313_DCDC1_CTRL + dcdc_num - 1; + int max_mV; + + switch (dcdc_num) { + case 1: + case 2: + max_mV = 1540; + break; + case 3: + /* + * The manual defines a different split point, but tests + * show that it's the same 1200mV as for DCDC1/2. + */ + max_mV = 1840; + break; + default: + return -EINVAL; + } + + if (mvolt > AXP313_DCDC_SPLIT_MVOLT) + cfg = AXP313_DCDC_SPLIT_OFFSET + mvolt_to_cfg(mvolt, + AXP313_DCDC_SPLIT_MVOLT + 20, max_mV, 20); + else + cfg = mvolt_to_cfg(mvolt, 500, AXP313_DCDC_SPLIT_MVOLT, 10); + + if (mvolt == 0) + return pmic_bus_clrbits(AXP313_OUTPUT_CTRL, enable_mask); + + debug("DCDC%d: writing 0x%x to reg 0x%x\n", dcdc_num, cfg, volt_reg); + ret = pmic_bus_write(volt_reg, cfg); + if (ret) + return ret; + + return pmic_bus_setbits(AXP313_OUTPUT_CTRL, enable_mask); +} + +int axp_set_dcdc2(unsigned int mvolt) +{ + return axp_set_dcdc(2, mvolt); +} + +int axp_set_dcdc3(unsigned int mvolt) +{ + return axp_set_dcdc(3, mvolt); +} + +int axp_init(void) +{ + u8 axp_chip_id; + int ret; + + ret = pmic_bus_init(); + if (ret) + return ret; + + ret = pmic_bus_read(AXP313_CHIP_VERSION, &axp_chip_id); + if (ret) + return ret; + + axp_chip_id &= AXP313_CHIP_VERSION_MASK; + switch (axp_chip_id) { + case AXP313_CHIP_VERSION_AXP1530: + case AXP313_CHIP_VERSION_AXP313A: + case AXP313_CHIP_VERSION_AXP313B: + break; + default: + debug("unknown PMIC: 0x%x\n", axp_chip_id); + return -EINVAL; + } + + return ret; +} + +#if !CONFIG_IS_ENABLED(ARM_PSCI_FW) && !IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF) +int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + pmic_bus_write(AXP313_SHUTDOWN, AXP313_POWEROFF); + + /* infinite loop during shutdown */ + while (1) {} + + /* not reached */ + return 0; +} +#endif |