From a3010bcfede0b0f84da069e3dd67ffd1142ee286 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 22 Aug 2021 13:23:53 -0500 Subject: [PATCH 01/17] sunxi: Select SPL_SEPARATE_BSS sunxi-common.h defines CONFIG_SPL_BSS_START_ADDR to put SPL's BSS in DRAM. Due to this, we must select SPL_SEPARATE_BSS, or else SPL will attempt to load its DTB from the wrong address (after BSS in DRAM). This change fixes booting with SPL_OF_CONTROL=y. Signed-off-by: Samuel Holland Reviewed-by: Andre Przywara Signed-off-by: Andre Przywara --- arch/arm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 504abca0b71..ebd9468d11d 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1037,6 +1037,7 @@ config ARCH_SUNXI select OF_CONTROL select OF_SEPARATE select SPECIFY_CONSOLE_INDEX + select SPL_SEPARATE_BSS if SPL select SPL_STACK_R if SPL select SPL_SYS_MALLOC_SIMPLE if SPL select SPL_SYS_THUMB_BUILD if !ARM64 From 6617894b6903e1cfc829add1558143be75d6bcd0 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 12 Sep 2021 09:22:40 -0500 Subject: [PATCH 02/17] phy: sun4i-usb: Remove a couple of debug messages Both of these messages log the GPIO number of the ID detection GPIO, which is not terribly useful, especially in the VBUS detection function. Signed-off-by: Samuel Holland Reviewed-by: Andre Przywara Signed-off-by: Andre Przywara --- drivers/phy/allwinner/phy-sun4i-usb.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 82713b83815..5302b809ee6 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -393,8 +393,6 @@ int sun4i_usb_phy_vbus_detect(struct phy *phy) struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; int err, retries = 3; - debug("%s: id_det = %d\n", __func__, usb_phy->gpio_id_det); - if (usb_phy->gpio_vbus_det < 0) return usb_phy->gpio_vbus_det; @@ -417,8 +415,6 @@ int sun4i_usb_phy_id_detect(struct phy *phy) struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; - debug("%s: id_det = %d\n", __func__, usb_phy->gpio_id_det); - if (usb_phy->gpio_id_det < 0) return usb_phy->gpio_id_det; From 1da7b88cade196434b84e701c1dadcd3b37c97bc Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 12 Sep 2021 09:22:41 -0500 Subject: [PATCH 03/17] phy: sun4i-usb: Refactor VBUS detection to match Linux The Linux driver checks the VBUS detection GPIO first; then VBUS power supply; then finally assumes VBUS is present. When adding VBUS power supply support, we want to match that order, so we get the same behavior in case both a GPIO and a power supply are provided in the device tree. So refactor the function a bit to remove the early return, and use the same "assume VBUS is present" final fallback. Signed-off-by: Samuel Holland Acked-by: Andre Przywara Signed-off-by: Andre Przywara --- drivers/phy/allwinner/phy-sun4i-usb.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 5302b809ee6..827ecd70f27 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -391,20 +391,19 @@ int sun4i_usb_phy_vbus_detect(struct phy *phy) { struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; - int err, retries = 3; + int err = 1, retries = 3; - if (usb_phy->gpio_vbus_det < 0) - return usb_phy->gpio_vbus_det; - - err = gpio_get_value(usb_phy->gpio_vbus_det); - /* - * Vbus may have been provided by the board and just been turned of - * some milliseconds ago on reset, what we're measuring then is a - * residual charge on Vbus, sleep a bit and try again. - */ - while (err > 0 && retries--) { - mdelay(100); + if (usb_phy->gpio_vbus_det >= 0) { err = gpio_get_value(usb_phy->gpio_vbus_det); + /* + * Vbus may have been provided by the board and just turned off + * some milliseconds ago on reset. What we're measuring then is + * a residual charge on Vbus. Sleep a bit and try again. + */ + while (err > 0 && retries--) { + mdelay(100); + err = gpio_get_value(usb_phy->gpio_vbus_det); + } } return err; From 6fa41cdd19b93b27483f071f96da8b66bebd7a37 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 12 Sep 2021 09:22:42 -0500 Subject: [PATCH 04/17] phy: sun4i-usb: Support VBUS detection via power supply The device tree binding provides for getting VBUS state from a device referenced by phandle, as an optional alternative to using a GPIO. In U-Boot, where there is no power supply class, this VBUS detection will be implemented using a regulator device and its get_enable method. Let's hook this up to the PHY driver. Signed-off-by: Samuel Holland Acked-by: Andre Przywara Signed-off-by: Andre Przywara --- drivers/phy/allwinner/Kconfig | 1 + drivers/phy/allwinner/phy-sun4i-usb.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig index 6bfb79cbcad..d3ff82f73a0 100644 --- a/drivers/phy/allwinner/Kconfig +++ b/drivers/phy/allwinner/Kconfig @@ -4,6 +4,7 @@ config PHY_SUN4I_USB bool "Allwinner Sun4I USB PHY driver" depends on ARCH_SUNXI + select DM_REGULATOR select PHY help Enable this to support the transceiver that is part of Allwinner diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 827ecd70f27..ab2a5d17fcf 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -26,6 +26,7 @@ #include #include #include +#include #define REG_ISCR 0x00 #define REG_PHYCTL_A10 0x04 @@ -137,6 +138,7 @@ struct sun4i_usb_phy_data { void __iomem *base; const struct sun4i_usb_phy_cfg *cfg; struct sun4i_usb_phy_plat *usb_phy; + struct udevice *vbus_power_supply; }; static int initial_usb_scan_delay = CONFIG_INITIAL_USB_SCAN_DELAY; @@ -404,6 +406,8 @@ int sun4i_usb_phy_vbus_detect(struct phy *phy) mdelay(100); err = gpio_get_value(usb_phy->gpio_vbus_det); } + } else if (data->vbus_power_supply) { + err = regulator_get_enable(data->vbus_power_supply); } return err; @@ -447,6 +451,9 @@ static int sun4i_usb_phy_probe(struct udevice *dev) if (IS_ERR(data->base)) return PTR_ERR(data->base); + device_get_supply_regulator(dev, "usb0_vbus_power-supply", + &data->vbus_power_supply); + data->usb_phy = plat; for (i = 0; i < data->cfg->num_phys; i++) { struct sun4i_usb_phy_plat *phy = &plat[i]; From 00f82fcfba6ceff501bfd8e4fc2adb368f2905ac Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 12 Oct 2021 18:54:56 -0500 Subject: [PATCH 05/17] sunxi: A23/A33/H3: Actually move the secure monitor commit 1ebfc0c631e3 ("sunxi: A23/A33/H3: Move sun8i secure monitor to SRAM A2") attempted to move the secure monitor to SRAM A2. But not all sun8i SoCs have SRAM A2, so a check was put in for SUNXI_SRAM_A2_SIZE to avoid breaking the other SoCs. However, because the header providing SUNXI_SRAM_A2_SIZE was not included, this unintentionally skipped the new definitions on all SoCs. Fix this by including the right header. Fixes: 1ebfc0c631e3 ("sunxi: A23/A33/H3: Move sun8i secure monitor to SRAM A2") Signed-off-by: Samuel Holland Reviewed-by: Andre Przywara Signed-off-by: Andre Przywara --- include/configs/sun8i.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/configs/sun8i.h b/include/configs/sun8i.h index 27c9808a494..56363563662 100644 --- a/include/configs/sun8i.h +++ b/include/configs/sun8i.h @@ -12,6 +12,8 @@ * A23 specific configuration */ +#include + #ifdef SUNXI_SRAM_A2_SIZE /* * If the SoC has enough SRAM A2, use that for the secure monitor. From a13fa74a8e82d7c1d6c4c4945ae0dc4870a222c9 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 12 Oct 2021 19:35:41 -0500 Subject: [PATCH 06/17] tools: mksunxiboot: Use sunxi_image header directly When adding eGON support to mkimage, the struct boot_file_head definition was moved to its own header. This is the only thing mksunxiboot needed out of asm/arch/spl.h. Clean up the relative include by switching to new header. Signed-off-by: Samuel Holland Reviewed-by: Andre Przywara Signed-off-by: Andre Przywara --- tools/mksunxiboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mksunxiboot.c b/tools/mksunxiboot.c index a18c9d98bc7..becc36565b7 100644 --- a/tools/mksunxiboot.c +++ b/tools/mksunxiboot.c @@ -12,10 +12,10 @@ #include #include #include +#include #include #include #include "imagetool.h" -#include "../arch/arm/include/asm/arch-sunxi/spl.h" #define STAMP_VALUE 0x5F0A6C39 From 5b4afbd40c9bc1cac6705e27e9ec0683622ca86a Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 22 Aug 2021 18:18:03 -0500 Subject: [PATCH 07/17] include: axp_pmic: Add missing header guard definition This header attempted to avoid multiple inclusion using a header guard. But the preprocessor symbol was never defined, so the guard had no effect. Fix this by defining the symbol. Signed-off-by: Samuel Holland Reviewed-by: Andre Przywara Signed-off-by: Andre Przywara --- include/axp_pmic.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/axp_pmic.h b/include/axp_pmic.h index 46a017d2efa..2eab18937bc 100644 --- a/include/axp_pmic.h +++ b/include/axp_pmic.h @@ -5,6 +5,7 @@ * X-Powers AX Power Management IC support header */ #ifndef _AXP_PMIC_H_ +#define _AXP_PMIC_H_ #include From 344df3ca2c3c2e98c9ab4112e8ebd660ad2be6ea Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 22 Aug 2021 18:18:04 -0500 Subject: [PATCH 08/17] include: axp_pmic: Include headers for all variants A single DM-based driver should be able to support some feature for several PMIC variants where the interface is the same. For example, all PMIC variants use the same register bit to trigger poweroff. However, currently only definitions for a single PMIC are available at a time. This requires drivers to use #ifdefs and different indentifiers for each variant they support. Let's simplify this by making register definitions for all variants available from the header. Then no preprocessor conditions are needed; the driver can use the register definition from any variant that supports the relevant feature. An exception is the GPIO-related definitions, which do not use unique identifiers. So for now, keep them like before. They will be cleaned up along with the GPIO driver. Signed-off-by: Samuel Holland Reviewed-by: Andre Przywara Signed-off-by: Andre Przywara --- include/axp152.h | 2 ++ include/axp209.h | 2 ++ include/axp221.h | 2 ++ include/axp809.h | 2 ++ include/axp818.h | 2 ++ include/axp_pmic.h | 12 ------------ 6 files changed, 10 insertions(+), 12 deletions(-) diff --git a/include/axp152.h b/include/axp152.h index c81f172502c..10d845fec42 100644 --- a/include/axp152.h +++ b/include/axp152.h @@ -15,6 +15,7 @@ enum axp152_reg { #define AXP152_POWEROFF (1 << 7) /* For axp_gpio.c */ +#ifdef CONFIG_AXP152_POWER #define AXP_GPIO0_CTRL 0x90 #define AXP_GPIO1_CTRL 0x91 #define AXP_GPIO2_CTRL 0x92 @@ -24,3 +25,4 @@ enum axp152_reg { #define AXP_GPIO_CTRL_INPUT 0x02 /* Input */ #define AXP_GPIO_STATE 0x97 #define AXP_GPIO_STATE_OFFSET 0 +#endif diff --git a/include/axp209.h b/include/axp209.h index f4f1b2fe56d..30399a8d621 100644 --- a/include/axp209.h +++ b/include/axp209.h @@ -74,6 +74,7 @@ enum axp209_reg { #define AXP209_POWEROFF BIT(7) /* For axp_gpio.c */ +#ifdef CONFIG_AXP209_POWER #define AXP_POWER_STATUS 0x00 #define AXP_POWER_STATUS_VBUS_PRESENT BIT(5) #define AXP_GPIO0_CTRL 0x90 @@ -84,3 +85,4 @@ enum axp209_reg { #define AXP_GPIO_CTRL_INPUT 0x02 /* Input */ #define AXP_GPIO_STATE 0x94 #define AXP_GPIO_STATE_OFFSET 4 +#endif diff --git a/include/axp221.h b/include/axp221.h index caffb910f4b..a02e9b4f645 100644 --- a/include/axp221.h +++ b/include/axp221.h @@ -50,6 +50,7 @@ #define AXP221_SID 0x20 /* For axp_gpio.c */ +#ifdef CONFIG_AXP221_POWER #define AXP_POWER_STATUS 0x00 #define AXP_POWER_STATUS_VBUS_PRESENT (1 << 5) #define AXP_VBUS_IPSOUT 0x30 @@ -63,3 +64,4 @@ #define AXP_GPIO_CTRL_INPUT 0x02 /* Input */ #define AXP_GPIO_STATE 0x94 #define AXP_GPIO_STATE_OFFSET 0 +#endif diff --git a/include/axp809.h b/include/axp809.h index 86b29253306..430dbef622b 100644 --- a/include/axp809.h +++ b/include/axp809.h @@ -44,6 +44,7 @@ #define AXP809_SHUTDOWN_POWEROFF (1 << 7) /* For axp_gpio.c */ +#ifdef CONFIG_AXP809_POWER #define AXP_POWER_STATUS 0x00 #define AXP_POWER_STATUS_VBUS_PRESENT (1 << 5) #define AXP_VBUS_IPSOUT 0x30 @@ -57,3 +58,4 @@ #define AXP_GPIO_CTRL_INPUT 0x02 /* Input */ #define AXP_GPIO_STATE 0x94 #define AXP_GPIO_STATE_OFFSET 0 +#endif diff --git a/include/axp818.h b/include/axp818.h index b16fe0b1527..8bac6b67ca2 100644 --- a/include/axp818.h +++ b/include/axp818.h @@ -58,6 +58,7 @@ #define AXP818_SHUTDOWN_POWEROFF (1 << 7) /* For axp_gpio.c */ +#ifdef CONFIG_AXP818_POWER #define AXP_POWER_STATUS 0x00 #define AXP_POWER_STATUS_VBUS_PRESENT (1 << 5) #define AXP_VBUS_IPSOUT 0x30 @@ -71,3 +72,4 @@ #define AXP_GPIO_CTRL_INPUT 0x02 /* Input */ #define AXP_GPIO_STATE 0x94 #define AXP_GPIO_STATE_OFFSET 0 +#endif diff --git a/include/axp_pmic.h b/include/axp_pmic.h index 2eab18937bc..01ebba63479 100644 --- a/include/axp_pmic.h +++ b/include/axp_pmic.h @@ -9,24 +9,12 @@ #include -#ifdef CONFIG_AXP152_POWER #include -#endif -#ifdef CONFIG_AXP209_POWER #include -#endif -#ifdef CONFIG_AXP221_POWER #include -#endif -#ifdef CONFIG_AXP305_POWER #include -#endif -#ifdef CONFIG_AXP809_POWER #include -#endif -#ifdef CONFIG_AXP818_POWER #include -#endif #define AXP_PMIC_MODE_REG 0x3e #define AXP_PMIC_MODE_I2C 0x00 From 830e161eb4e9dbd3e9eb20ad6a3255eb8b4ed3c4 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 22 Aug 2021 18:18:05 -0500 Subject: [PATCH 09/17] power: axp: Avoid do_poweroff conflict with sysreset The sysreset uclass has an option to provide the do_poweroff() function. When that option is enabled, the AXP power drivers should not provide their own definition. For the AXP305, which is paired with 64-bit systems where TF-A provides PSCI, there is another possible conflict with the PSCI firmware driver. This driver can be enabled even if CONFIG_PSCI_RESET is disabled, so make sure to use the right symbol in the condition. Signed-off-by: Samuel Holland Reviewed-by: Andre Przywara Signed-off-by: Andre Przywara --- drivers/power/axp152.c | 2 ++ drivers/power/axp209.c | 2 ++ drivers/power/axp221.c | 2 ++ drivers/power/axp305.c | 2 +- drivers/power/axp809.c | 2 ++ drivers/power/axp818.c | 2 ++ 6 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/power/axp152.c b/drivers/power/axp152.c index d6e36125c12..a93987c1538 100644 --- a/drivers/power/axp152.c +++ b/drivers/power/axp152.c @@ -79,6 +79,7 @@ int axp_init(void) return 0; } +#if !IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF) int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { pmic_bus_write(AXP152_SHUTDOWN, AXP152_POWEROFF); @@ -89,3 +90,4 @@ int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) /* not reached */ return 0; } +#endif diff --git a/drivers/power/axp209.c b/drivers/power/axp209.c index ade531940b9..3447b9f0113 100644 --- a/drivers/power/axp209.c +++ b/drivers/power/axp209.c @@ -230,6 +230,7 @@ int axp_init(void) return 0; } +#if !IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF) int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { pmic_bus_write(AXP209_SHUTDOWN, AXP209_POWEROFF); @@ -240,3 +241,4 @@ int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) /* not reached */ return 0; } +#endif diff --git a/drivers/power/axp221.c b/drivers/power/axp221.c index 3446fe7365d..d251c314b98 100644 --- a/drivers/power/axp221.c +++ b/drivers/power/axp221.c @@ -264,6 +264,7 @@ int axp_get_sid(unsigned int *sid) return 0; } +#if !IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF) int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { pmic_bus_write(AXP221_SHUTDOWN, AXP221_SHUTDOWN_POWEROFF); @@ -274,3 +275,4 @@ int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) /* not reached */ return 0; } +#endif diff --git a/drivers/power/axp305.c b/drivers/power/axp305.c index 0191e4d427e..049ef07f746 100644 --- a/drivers/power/axp305.c +++ b/drivers/power/axp305.c @@ -69,7 +69,7 @@ int axp_init(void) return ret; } -#ifndef CONFIG_PSCI_RESET +#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(AXP305_SHUTDOWN, AXP305_POWEROFF); diff --git a/drivers/power/axp809.c b/drivers/power/axp809.c index 0396502cdb5..d327a584ded 100644 --- a/drivers/power/axp809.c +++ b/drivers/power/axp809.c @@ -219,6 +219,7 @@ int axp_init(void) return pmic_bus_init(); } +#if !IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF) int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { pmic_bus_write(AXP809_SHUTDOWN, AXP809_SHUTDOWN_POWEROFF); @@ -229,3 +230,4 @@ int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) /* not reached */ return 0; } +#endif diff --git a/drivers/power/axp818.c b/drivers/power/axp818.c index 2dc736467bb..08286ea3b55 100644 --- a/drivers/power/axp818.c +++ b/drivers/power/axp818.c @@ -255,6 +255,7 @@ int axp_init(void) return 0; } +#if !IS_ENABLED(CONFIG_SYSRESET_CMD_POWEROFF) int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { pmic_bus_write(AXP818_SHUTDOWN, AXP818_SHUTDOWN_POWEROFF); @@ -265,3 +266,4 @@ int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) /* not reached */ return 0; } +#endif From 95d9ffd7b678414cc79365c8d2be0d12c326a7ad Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 24 Oct 2021 21:00:10 -0500 Subject: [PATCH 10/17] power: pmic: axp: Implement poweroff via sysreset The AXP PMICs have the ability to power off the system. The existing code for this is duplicated for each PMIC variant, and uses the legacy non-DM "pmic_bus" interface. When SYSRESET is enabled, this can all be replaced with a sysreset device using the DM_PMIC interface. Since the trigger bit is the same on all PMIC variants, use the register definitions from the oldest supported PMIC. Signed-off-by: Samuel Holland Reviewed-by: Andre Przywara Signed-off-by: Andre Przywara --- drivers/power/pmic/Kconfig | 2 ++ drivers/power/pmic/axp.c | 49 +++++++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index 92e2ace279d..b9fda428df9 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -66,6 +66,8 @@ config PMIC_ACT8846 config PMIC_AXP bool "Enable Driver Model for X-Powers AXP PMICs" depends on DM_I2C + select SYSRESET_CMD_POWEROFF if SYSRESET && CMD_POWEROFF + imply CMD_POWEROFF if SYSRESET help This config enables driver-model PMIC uclass features for X-Powers AXP152, AXP2xx, and AXP8xx PMICs. diff --git a/drivers/power/pmic/axp.c b/drivers/power/pmic/axp.c index 74c94bdb47b..0f2b24a8b5f 100644 --- a/drivers/power/pmic/axp.c +++ b/drivers/power/pmic/axp.c @@ -1,8 +1,37 @@ // SPDX-License-Identifier: GPL-2.0+ +#include #include +#include #include #include +#include + +#if CONFIG_IS_ENABLED(SYSRESET) +static int axp_sysreset_request(struct udevice *dev, enum sysreset_t type) +{ + int ret; + + if (type != SYSRESET_POWER_OFF) + return -EPROTONOSUPPORT; + + ret = pmic_clrsetbits(dev->parent, AXP152_SHUTDOWN, 0, AXP152_POWEROFF); + if (ret < 0) + return ret; + + return -EINPROGRESS; +} + +static struct sysreset_ops axp_sysreset_ops = { + .request = axp_sysreset_request, +}; + +U_BOOT_DRIVER(axp_sysreset) = { + .name = "axp_sysreset", + .id = UCLASS_SYSRESET, + .ops = &axp_sysreset_ops, +}; +#endif static int axp_pmic_reg_count(struct udevice *dev) { @@ -16,6 +45,24 @@ static struct dm_pmic_ops axp_pmic_ops = { .write = dm_i2c_write, }; +static int axp_pmic_bind(struct udevice *dev) +{ + int ret; + + ret = dm_scan_fdt_dev(dev); + if (ret) + return ret; + + if (CONFIG_IS_ENABLED(SYSRESET)) { + ret = device_bind_driver_to_node(dev, "axp_sysreset", "axp_sysreset", + dev_ofnode(dev), NULL); + if (ret) + return ret; + } + + return 0; +} + static const struct udevice_id axp_pmic_ids[] = { { .compatible = "x-powers,axp152" }, { .compatible = "x-powers,axp202" }, @@ -33,6 +80,6 @@ U_BOOT_DRIVER(axp_pmic) = { .name = "axp_pmic", .id = UCLASS_PMIC, .of_match = axp_pmic_ids, - .bind = dm_scan_fdt_dev, + .bind = axp_pmic_bind, .ops = &axp_pmic_ops, }; From 0ec88323dae7a91f535c2c9db45cb3c925507ea2 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Tue, 6 Jul 2021 00:04:43 +0100 Subject: [PATCH 11/17] sunxi: SPL SPI: Allow larger SPL The more recent Allwinner SoCs BootROMs can actually load SPL images larger than 32KB. We use this on the H616 to fit the extra code needed for the PMIC into the image, and have provisions in board.c to respect that larger SPL size when booting from MMC. However the sunxi SPL SPI loader has a hardcoded load offset of 32KB, which will fail on the H616. To fix this, use the same algorithm we use for MMC: if the SPL size is smaller than 32KB, we use 32KB, otherwise we expect the U-Boot payload directly after the SPL code. This prepares for SPI booting with larger SPLs like on the H616. Signed-off-by: Andre Przywara Reviewed-by: Jagan Teki --- arch/arm/mach-sunxi/spl_spi_sunxi.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c b/arch/arm/mach-sunxi/spl_spi_sunxi.c index 15e86cbac8f..3499c4cc5f8 100644 --- a/arch/arm/mach-sunxi/spl_spi_sunxi.c +++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -326,10 +327,13 @@ static int spl_spi_load_image(struct spl_image_info *spl_image, int ret = 0; struct image_header *header; header = (struct image_header *)(CONFIG_SYS_TEXT_BASE); + int load_offset = readl(SPL_ADDR + 0x10); + + load_offset = max(load_offset, CONFIG_SYS_SPI_U_BOOT_OFFS); spi0_init(); - spi0_read_data((void *)header, CONFIG_SYS_SPI_U_BOOT_OFFS, 0x40); + spi0_read_data((void *)header, load_offset, 0x40); if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && image_get_magic(header) == FDT_MAGIC) { @@ -342,14 +346,14 @@ static int spl_spi_load_image(struct spl_image_info *spl_image, load.bl_len = 1; load.read = spi_load_read; ret = spl_load_simple_fit(spl_image, &load, - CONFIG_SYS_SPI_U_BOOT_OFFS, header); + load_offset, header); } else { ret = spl_parse_image_header(spl_image, header); if (ret) return ret; spi0_read_data((void *)spl_image->load_addr, - CONFIG_SYS_SPI_U_BOOT_OFFS, spl_image->size); + load_offset, spl_image->size); } spi0_deinit(); From e0c628d728d6f2b0ab01488706b1a9679512a982 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 11 Sep 2021 14:45:31 -0500 Subject: [PATCH 12/17] clk: sunxi: Extend DM_RESET selection to SPL The sunxi clock driver exposes a reset controller, so it selects the reset controller framework. Ensure that dependency is also satisfied when building the driver for the SPL. Signed-off-by: Samuel Holland Reviewed-by: Andre Przywara Signed-off-by: Andre Przywara --- drivers/clk/sunxi/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig index f89c7ffd42a..f19908113e1 100644 --- a/drivers/clk/sunxi/Kconfig +++ b/drivers/clk/sunxi/Kconfig @@ -2,6 +2,7 @@ config CLK_SUNXI bool "Clock support for Allwinner SoCs" depends on CLK && ARCH_SUNXI select DM_RESET + select SPL_DM_RESET if SPL_CLK default y help This enables support for common clock driver API on Allwinner From 93d34faeda4cb8bb059a6b6fbff4f9969c6f41a7 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 22 Aug 2021 13:53:27 -0500 Subject: [PATCH 13/17] watchdog: Add a driver for the sunxi watchdog This driver supports the sun4i/sun6i/sun20i watchdog timers. They have a maximum timeout of 16 seconds. Signed-off-by: Samuel Holland Signed-off-by: Andre Przywara --- drivers/watchdog/Kconfig | 8 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/sunxi_wdt.c | 188 +++++++++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 drivers/watchdog/sunxi_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 7d2d6ea5e90..d306054a8c2 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -27,6 +27,7 @@ config WATCHDOG_TIMEOUT_MSECS default 128000 if ARCH_MX31 || ARCH_MX5 || ARCH_MX6 default 128000 if ARCH_MX7 || ARCH_VF610 default 30000 if ARCH_SOCFPGA + default 16000 if ARCH_SUNXI default 60000 help Watchdog timeout in msec @@ -270,6 +271,13 @@ config WDT_STM32MP Enable the STM32 watchdog (IWDG) driver. Enable support to configure STM32's on-SoC watchdog. +config WDT_SUNXI + bool "Allwinner sunxi watchdog timer support" + depends on WDT && ARCH_SUNXI + default y + help + Enable support for the watchdog timer in Allwinner sunxi SoCs. + config XILINX_TB_WATCHDOG bool "Xilinx Axi watchdog timer support" depends on WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index f14415bb8e3..fa7ce583ce2 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -36,5 +36,6 @@ obj-$(CONFIG_WDT_SBSA) += sbsa_gwdt.o obj-$(CONFIG_WDT_K3_RTI) += rti_wdt.o obj-$(CONFIG_WDT_SP805) += sp805_wdt.o obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o +obj-$(CONFIG_WDT_SUNXI) += sunxi_wdt.o obj-$(CONFIG_WDT_TANGIER) += tangier_wdt.o obj-$(CONFIG_WDT_XILINX) += xilinx_wwdt.o diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c new file mode 100644 index 00000000000..b40a1d29caa --- /dev/null +++ b/drivers/watchdog/sunxi_wdt.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Derived from linux/drivers/watchdog/sunxi_wdt.c: + * Copyright (C) 2013 Carlo Caione + * Copyright (C) 2012 Henrik Nordstrom + */ + +#include +#include +#include +#include + +#define MSEC_PER_SEC 1000 + +#define WDT_MAX_TIMEOUT 16 +#define WDT_TIMEOUT_MASK 0xf + +#define WDT_CTRL_RELOAD ((1 << 0) | (0x0a57 << 1)) + +#define WDT_MODE_EN BIT(0) + +struct sunxi_wdt_reg { + u8 wdt_ctrl; + u8 wdt_cfg; + u8 wdt_mode; + u8 wdt_timeout_shift; + u8 wdt_reset_mask; + u8 wdt_reset_val; + u32 wdt_key_val; +}; + +struct sunxi_wdt_priv { + void __iomem *base; + const struct sunxi_wdt_reg *regs; +}; + +/* Map of timeout in seconds to register value */ +static const u8 wdt_timeout_map[1 + WDT_MAX_TIMEOUT] = { + [0] = 0x0, + [1] = 0x1, + [2] = 0x2, + [3] = 0x3, + [4] = 0x4, + [5] = 0x5, + [6] = 0x6, + [7] = 0x7, + [8] = 0x7, + [9] = 0x8, + [10] = 0x8, + [11] = 0x9, + [12] = 0x9, + [13] = 0xa, + [14] = 0xa, + [15] = 0xb, + [16] = 0xb, +}; + +static int sunxi_wdt_reset(struct udevice *dev) +{ + struct sunxi_wdt_priv *priv = dev_get_priv(dev); + const struct sunxi_wdt_reg *regs = priv->regs; + void __iomem *base = priv->base; + + writel(WDT_CTRL_RELOAD, base + regs->wdt_ctrl); + + return 0; +} + +static int sunxi_wdt_start(struct udevice *dev, u64 timeout, ulong flags) +{ + struct sunxi_wdt_priv *priv = dev_get_priv(dev); + const struct sunxi_wdt_reg *regs = priv->regs; + void __iomem *base = priv->base; + u32 val; + + timeout /= MSEC_PER_SEC; + if (timeout > WDT_MAX_TIMEOUT) + timeout = WDT_MAX_TIMEOUT; + + /* Set system reset function */ + val = readl(base + regs->wdt_cfg); + val &= ~regs->wdt_reset_mask; + val |= regs->wdt_reset_val; + val |= regs->wdt_key_val; + writel(val, base + regs->wdt_cfg); + + /* Set timeout and enable watchdog */ + val = readl(base + regs->wdt_mode); + val &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift); + val |= wdt_timeout_map[timeout] << regs->wdt_timeout_shift; + val |= WDT_MODE_EN; + val |= regs->wdt_key_val; + writel(val, base + regs->wdt_mode); + + return sunxi_wdt_reset(dev); +} + +static int sunxi_wdt_stop(struct udevice *dev) +{ + struct sunxi_wdt_priv *priv = dev_get_priv(dev); + const struct sunxi_wdt_reg *regs = priv->regs; + void __iomem *base = priv->base; + + writel(regs->wdt_key_val, base + regs->wdt_mode); + + return 0; +} + +static int sunxi_wdt_expire_now(struct udevice *dev, ulong flags) +{ + int ret; + + ret = sunxi_wdt_start(dev, 0, flags); + if (ret) + return ret; + + mdelay(500); + + return 0; +} + +static const struct wdt_ops sunxi_wdt_ops = { + .reset = sunxi_wdt_reset, + .start = sunxi_wdt_start, + .stop = sunxi_wdt_stop, + .expire_now = sunxi_wdt_expire_now, +}; + +static const struct sunxi_wdt_reg sun4i_wdt_reg = { + .wdt_ctrl = 0x00, + .wdt_cfg = 0x04, + .wdt_mode = 0x04, + .wdt_timeout_shift = 3, + .wdt_reset_mask = 0x2, + .wdt_reset_val = 0x2, +}; + +static const struct sunxi_wdt_reg sun6i_wdt_reg = { + .wdt_ctrl = 0x10, + .wdt_cfg = 0x14, + .wdt_mode = 0x18, + .wdt_timeout_shift = 4, + .wdt_reset_mask = 0x3, + .wdt_reset_val = 0x1, +}; + +static const struct sunxi_wdt_reg sun20i_wdt_reg = { + .wdt_ctrl = 0x10, + .wdt_cfg = 0x14, + .wdt_mode = 0x18, + .wdt_timeout_shift = 4, + .wdt_reset_mask = 0x03, + .wdt_reset_val = 0x01, + .wdt_key_val = 0x16aa0000, +}; + +static const struct udevice_id sunxi_wdt_ids[] = { + { .compatible = "allwinner,sun4i-a10-wdt", .data = (ulong)&sun4i_wdt_reg }, + { .compatible = "allwinner,sun6i-a31-wdt", .data = (ulong)&sun6i_wdt_reg }, + { .compatible = "allwinner,sun20i-d1-wdt", .data = (ulong)&sun20i_wdt_reg }, + { /* sentinel */ } +}; + +static int sunxi_wdt_probe(struct udevice *dev) +{ + struct sunxi_wdt_priv *priv = dev_get_priv(dev); + + priv->base = dev_remap_addr(dev); + if (!priv->base) + return -EINVAL; + + priv->regs = (void *)dev_get_driver_data(dev); + if (!priv->regs) + return -EINVAL; + + sunxi_wdt_stop(dev); + + return 0; +} + +U_BOOT_DRIVER(sunxi_wdt) = { + .name = "sunxi_wdt", + .id = UCLASS_WDT, + .of_match = sunxi_wdt_ids, + .probe = sunxi_wdt_probe, + .priv_auto = sizeof(struct sunxi_wdt_priv), + .ops = &sunxi_wdt_ops, +}; From b147bd3607f81664ba8f40f455c11ddad6ae8841 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 22 Aug 2021 13:53:28 -0500 Subject: [PATCH 14/17] sunxi: Enable watchdog timer support by default A watchdog helps recover from hangs or failure to boot an OS. It can also be used by the sysreset framework to intentionally reset the system. Now that a driver is available, let's enable this functionality on sunxi boards. Signed-off-by: Samuel Holland Reviewed-by: Andre Przywara Signed-off-by: Andre Przywara --- arch/arm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ebd9468d11d..02f8306f15a 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1066,6 +1066,7 @@ config ARCH_SUNXI imply SPL_POWER imply SPL_SERIAL imply USB_GADGET + imply WDT config ARCH_U8500 bool "ST-Ericsson U8500 Series" From 08bdb976b71ff61b894424e585e4421a706da24a Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 22 Aug 2021 13:53:29 -0500 Subject: [PATCH 15/17] sunxi: dts: H616: Enable the watchdog For some reason, the watchdog was disabled in the H616 device tree. Most likely this is a copy-paste from the H6 device tree: the H6 watchdog is disabled because it is broken in some chips. However, there is no evidence of issues with the H616 watchdog. Enable the watchdog node so it can be used by the driver. Signed-off-by: Samuel Holland Reviewed-by: Andre Przywara Signed-off-by: Andre Przywara --- arch/arm/dts/sun50i-h616.dtsi | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/dts/sun50i-h616.dtsi b/arch/arm/dts/sun50i-h616.dtsi index dd4d2f31111..2f71e853e96 100644 --- a/arch/arm/dts/sun50i-h616.dtsi +++ b/arch/arm/dts/sun50i-h616.dtsi @@ -122,7 +122,6 @@ reg = <0x030090a0 0x20>; interrupts = ; clocks = <&osc24M>; - status = "disabled"; }; pio: pinctrl@300b000 { From 03510bf62149d8cbdc39564dcd4a3195f6d8d79e Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 22 Jul 2021 14:30:04 +0800 Subject: [PATCH 16/17] sunxi: only include alias for eMMC when mmc2 used Some Allwinner SoCs (e.g. R329) doesn't have a MMC2 controller at all, and on boards that we do not utilize MMC2, the alias for it is just useless. Only include the alias when we specify CONFIG_MMC_SUNXI_EXTRA_SLOT to 2. Signed-off-by: Icenowy Zheng Reviewed-by: Andre Przywara Signed-off-by: Andre Przywara --- arch/arm/dts/sunxi-u-boot.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi index 4a6ed3a7dd5..b7244c1112a 100644 --- a/arch/arm/dts/sunxi-u-boot.dtsi +++ b/arch/arm/dts/sunxi-u-boot.dtsi @@ -13,7 +13,9 @@ / { aliases { mmc0 = &mmc0; +#if CONFIG_MMC_SUNXI_EXTRA_SLOT == 2 mmc1 = &mmc2; +#endif }; binman: binman { From c846fe43f0561311eb7261b34023a04646cdbd0d Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 22 Jul 2021 14:30:05 +0800 Subject: [PATCH 17/17] mmc: sunxi: conditionally include MMC2 initialization code Allwinner R329 has no MMC2. Only include the code of MMC2 if the base address of it is defined. Signed-off-by: Icenowy Zheng Reviewed-by: Andre Przywara Signed-off-by: Andre Przywara --- drivers/mmc/sunxi_mmc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c index c170c16d5ab..4bf8a9b92ce 100644 --- a/drivers/mmc/sunxi_mmc.c +++ b/drivers/mmc/sunxi_mmc.c @@ -72,10 +72,12 @@ static int mmc_resource_init(int sdc_no) priv->reg = (struct sunxi_mmc *)SUNXI_MMC1_BASE; priv->mclkreg = &ccm->sd1_clk_cfg; break; +#ifdef SUNXI_MMC2_BASE case 2: priv->reg = (struct sunxi_mmc *)SUNXI_MMC2_BASE; priv->mclkreg = &ccm->sd2_clk_cfg; break; +#endif #ifdef SUNXI_MMC3_BASE case 3: priv->reg = (struct sunxi_mmc *)SUNXI_MMC3_BASE;