From 22cbf0776d396899f3a651b79af0bc50fc299cfb Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 6 May 2022 11:23:32 +0300 Subject: [PATCH 1/8] ARM: at91: pm: keep documentation inline with structure members Move documentation of bu to keep the same order as in the structure itself. Signed-off-by: Claudiu Beznea Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index 0fd609e26615..7ea4c7f13d28 100644 --- a/arch/arm/mach-at91/pm.c +++ b/arch/arm/mach-at91/pm.c @@ -65,9 +65,9 @@ struct at91_pm_sfrbu_regs { * @config_shdwc_ws: wakeup sources configuration function for SHDWC * @config_pmc_ws: wakeup srouces configuration function for PMC * @ws_ids: wakup sources of_device_id array + * @bu: backup unit mapped data (for backup mode) * @data: PM data to be used on last phase of suspend * @sfrbu_regs: SFRBU registers mapping - * @bu: backup unit mapped data (for backup mode) * @memcs: memory chip select */ struct at91_soc_pm { From b568c71de798d26e71164f08f179c86cd231d577 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 6 May 2022 11:23:33 +0300 Subject: [PATCH 2/8] ARM: at91: pm: introduce macros for pm mode replacement Introduce macros to replace standby/suspend mode if they depends on controllers that failed to map (or other errors). Macros keep track of the complementary mode to avoid having set the same AT91 PM mode for both suspend and standby. Signed-off-by: Claudiu Beznea Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/pm.c | 83 +++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index 7ea4c7f13d28..2a52ddac7692 100644 --- a/arch/arm/mach-at91/pm.c +++ b/arch/arm/mach-at91/pm.c @@ -888,10 +888,63 @@ static const struct of_device_id atmel_shdwc_ids[] = { { /* sentinel. */ } }; +/* + * Replaces _mode_to_replace with a supported mode that doesn't depend + * on controller pointed by _map_bitmask + * @_maps: u32 array containing AT91_PM_IOMAP() flags and indexed by AT91 + * PM mode + * @_map_bitmask: AT91_PM_IOMAP() bitmask; if _mode_to_replace depends on + * controller represented by _map_bitmask, _mode_to_replace needs to be + * updated + * @_mode_to_replace: standby_mode or suspend_mode that need to be + * updated + * @_mode_to_check: standby_mode or suspend_mode; this is needed here + * to avoid having standby_mode and suspend_mode set with the same AT91 + * PM mode + */ +#define AT91_PM_REPLACE_MODE(_maps, _map_bitmask, _mode_to_replace, \ + _mode_to_check) \ + do { \ + if (((_maps)[(_mode_to_replace)]) & (_map_bitmask)) { \ + int _mode_to_use, _mode_complementary; \ + /* Use ULP0 if it doesn't need _map_bitmask. */ \ + if (!((_maps)[AT91_PM_ULP0] & (_map_bitmask))) {\ + _mode_to_use = AT91_PM_ULP0; \ + _mode_complementary = AT91_PM_STANDBY; \ + } else { \ + _mode_to_use = AT91_PM_STANDBY; \ + _mode_complementary = AT91_PM_STANDBY; \ + } \ + \ + if ((_mode_to_check) != _mode_to_use) \ + (_mode_to_replace) = _mode_to_use; \ + else \ + (_mode_to_replace) = _mode_complementary;\ + } \ + } while (0) + +/* + * Replaces standby and suspend modes with default supported modes: + * ULP0 and STANDBY. + * @_maps: u32 array indexed by AT91 PM mode containing AT91_PM_IOMAP() + * flags + * @_map: controller specific name; standby and suspend mode need to be + * replaced in order to not depend on this controller + */ +#define AT91_PM_REPLACE_MODES(_maps, _map) \ + do { \ + AT91_PM_REPLACE_MODE((_maps), BIT(AT91_PM_IOMAP_##_map),\ + (soc_pm.data.standby_mode), \ + (soc_pm.data.suspend_mode)); \ + AT91_PM_REPLACE_MODE((_maps), BIT(AT91_PM_IOMAP_##_map),\ + (soc_pm.data.suspend_mode), \ + (soc_pm.data.standby_mode)); \ + } while (0) + static void __init at91_pm_modes_init(const u32 *maps, int len) { struct device_node *np; - int ret, mode; + int ret; ret = at91_pm_backup_init(); if (ret) { @@ -906,17 +959,7 @@ static void __init at91_pm_modes_init(const u32 *maps, int len) np = of_find_matching_node(NULL, atmel_shdwc_ids); if (!np) { pr_warn("%s: failed to find shdwc!\n", __func__); - - /* Use ULP0 if it doesn't needs SHDWC.*/ - if (!(maps[AT91_PM_ULP0] & AT91_PM_IOMAP(SHDWC))) - mode = AT91_PM_ULP0; - else - mode = AT91_PM_STANDBY; - - if (maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SHDWC)) - soc_pm.data.standby_mode = mode; - if (maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SHDWC)) - soc_pm.data.suspend_mode = mode; + AT91_PM_REPLACE_MODES(maps, SHDWC); } else { soc_pm.data.shdwc = of_iomap(np, 0); of_node_put(np); @@ -928,21 +971,7 @@ static void __init at91_pm_modes_init(const u32 *maps, int len) np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-sfrbu"); if (!np) { pr_warn("%s: failed to find sfrbu!\n", __func__); - - /* - * Use ULP0 if it doesn't need SHDWC or if SHDWC - * was already located. - */ - if (!(maps[AT91_PM_ULP0] & AT91_PM_IOMAP(SHDWC)) || - soc_pm.data.shdwc) - mode = AT91_PM_ULP0; - else - mode = AT91_PM_STANDBY; - - if (maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SFRBU)) - soc_pm.data.standby_mode = mode; - if (maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SFRBU)) - soc_pm.data.suspend_mode = mode; + AT91_PM_REPLACE_MODES(maps, SFRBU); } else { soc_pm.data.sfrbu = of_iomap(np, 0); of_node_put(np); From 9750d3b4d2a5d12f955d497a8ecc2a6b5edb24d5 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 6 May 2022 11:23:34 +0300 Subject: [PATCH 3/8] ARM: at91: pm: use kernel documentation style Use kernel documentation style. Along with it fix the naming of struct at91_pm_sfrbu_regs in documentation. Signed-off-by: Claudiu Beznea Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/pm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index 2a52ddac7692..84ada8e2a7fd 100644 --- a/arch/arm/mach-at91/pm.c +++ b/arch/arm/mach-at91/pm.c @@ -47,8 +47,8 @@ struct at91_pm_bu { unsigned long ddr_phy_calibration[BACKUP_DDR_PHY_CALIBRATION]; }; -/* - * struct at91_pm_sfrbu_offsets: registers mapping for SFRBU +/** + * struct at91_pm_sfrbu_regs - registers mapping for SFRBU * @pswbu: power switch BU control registers */ struct at91_pm_sfrbu_regs { @@ -81,7 +81,7 @@ struct at91_soc_pm { }; /** - * enum at91_pm_iomaps: IOs that needs to be mapped for different PM modes + * enum at91_pm_iomaps - IOs that needs to be mapped for different PM modes * @AT91_PM_IOMAP_SHDWC: SHDWC controller * @AT91_PM_IOMAP_SFRBU: SFRBU controller */ From b7fc72c63399925175368d378b1513d86f15d83b Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 6 May 2022 11:23:35 +0300 Subject: [PATCH 4/8] ARM: at91: pm: add quirks for pm SoCs supporting ULP0 or ULP1 modes and variants of Cadence Ethernet IP (controlled by macb driver) may behave buggy when Wake-on-Lan (WoL) is configured and WoL packet is received while in ULP0/ULP1. On some SoCs Ethernet interface is not working after resume. On other SoCs the CPU goes to abort on resume path when switching execution from internal SRAM to DRAM. For ULP1 + WoL the issue is related a particular restart sequence of the internal clocks when resuming. These clocks are automatically managed by PMC and may happen that GMAC peripheral clock is restarted few clock cycles before internal clocks causing blocking of Ethernet's DMA. As a consequence Ethernet TX transactions are stopped and RX transactions are partially stopped (packets are received by MAC, RX counters incremented but the data is not transferred to DRAM). The workaround for this is to disable Ethernet's peripheral clock when going to ULP1. Same behavior has been reproduced on ULP0 for some platforms (SAMA5D2, SAMA5D3) and the same workaround solves the issue. The problem has been solved on pm.c as quirk to avoid polluting the MACB driver with AT91 specific issues as this driver is generic to multiple vendors. At probe pointers to struct device_node are retrieved and on the at91_pm_enter() the quirk specifics are applied: for all Ethernet interfaces that were parsed the peripheral clocks are disabled. A special handling is done for modes in dns_modes mask as these are considered modes that blocks the system if WoL packet are received but for which applying quirk will lead to not waking up on WoL packets: in situation where Ethernet interface(s) has suspend mode in dns_modes mask and Ethernet interface(s) is the only available wakeup source the suspend is canceled. Signed-off-by: Claudiu Beznea Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/pm.c | 304 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 302 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index 84ada8e2a7fd..7b2617000845 100644 --- a/arch/arm/mach-at91/pm.c +++ b/arch/arm/mach-at91/pm.c @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -60,12 +61,63 @@ struct at91_pm_sfrbu_regs { } pswbu; }; +/** + * enum at91_pm_eth_clk - Ethernet clock indexes + * @AT91_PM_ETH_PCLK: pclk index + * @AT91_PM_ETH_HCLK: hclk index + * @AT91_PM_ETH_MAX_CLK: max index + */ +enum at91_pm_eth_clk { + AT91_PM_ETH_PCLK, + AT91_PM_ETH_HCLK, + AT91_PM_ETH_MAX_CLK, +}; + +/** + * enum at91_pm_eth - Ethernet controller indexes + * @AT91_PM_G_ETH: gigabit Ethernet controller index + * @AT91_PM_E_ETH: megabit Ethernet controller index + * @AT91_PM_MAX_ETH: max index + */ +enum at91_pm_eth { + AT91_PM_G_ETH, + AT91_PM_E_ETH, + AT91_PM_MAX_ETH, +}; + +/** + * struct at91_pm_quirk_eth - AT91 PM Ethernet quirks + * @dev: Ethernet device + * @np: Ethernet device node + * @clks: Ethernet clocks + * @modes: power management mode that this quirk applies to + * @dns_modes: do not suspend modes: stop suspending if Ethernet is configured + * as wakeup source but buggy and no other wakeup source is + * available + */ +struct at91_pm_quirk_eth { + struct device *dev; + struct device_node *np; + struct clk_bulk_data clks[AT91_PM_ETH_MAX_CLK]; + u32 modes; + u32 dns_modes; +}; + +/** + * struct at91_pm_quirks - AT91 PM quirks + * @eth: Ethernet quirks + */ +struct at91_pm_quirks { + struct at91_pm_quirk_eth eth[AT91_PM_MAX_ETH]; +}; + /** * struct at91_soc_pm - AT91 SoC power management data structure * @config_shdwc_ws: wakeup sources configuration function for SHDWC * @config_pmc_ws: wakeup srouces configuration function for PMC * @ws_ids: wakup sources of_device_id array * @bu: backup unit mapped data (for backup mode) + * @quirks: PM quirks * @data: PM data to be used on last phase of suspend * @sfrbu_regs: SFRBU registers mapping * @memcs: memory chip select @@ -75,6 +127,7 @@ struct at91_soc_pm { int (*config_pmc_ws)(void __iomem *pmc, u32 mode, u32 polarity); const struct of_device_id *ws_ids; struct at91_pm_bu *bu; + struct at91_pm_quirks quirks; struct at91_pm_data data; struct at91_pm_sfrbu_regs sfrbu_regs; void *memcs; @@ -84,10 +137,12 @@ struct at91_soc_pm { * enum at91_pm_iomaps - IOs that needs to be mapped for different PM modes * @AT91_PM_IOMAP_SHDWC: SHDWC controller * @AT91_PM_IOMAP_SFRBU: SFRBU controller + * @AT91_PM_IOMAP_ETHC: Ethernet controller */ enum at91_pm_iomaps { AT91_PM_IOMAP_SHDWC, AT91_PM_IOMAP_SFRBU, + AT91_PM_IOMAP_ETHC, }; #define AT91_PM_IOMAP(name) BIT(AT91_PM_IOMAP_##name) @@ -263,6 +318,141 @@ static int at91_sam9x60_config_pmc_ws(void __iomem *pmc, u32 mode, u32 polarity) return 0; } +static bool at91_pm_eth_quirk_is_valid(struct at91_pm_quirk_eth *eth) +{ + struct platform_device *pdev; + + /* Interface NA in DT. */ + if (!eth->np) + return false; + + /* No quirks for this interface and current suspend mode. */ + if (!(eth->modes & BIT(soc_pm.data.mode))) + return false; + + if (!eth->dev) { + /* Driver not probed. */ + pdev = of_find_device_by_node(eth->np); + if (!pdev) + return false; + eth->dev = &pdev->dev; + } + + /* No quirks if device isn't a wakeup source. */ + if (!device_may_wakeup(eth->dev)) { + put_device(eth->dev); + return false; + } + + /* put_device(eth->dev) is called at the end of suspend. */ + return true; +} + +static int at91_pm_config_quirks(bool suspend) +{ + struct at91_pm_quirk_eth *eth; + int i, j, ret, tmp; + + /* + * Ethernet IPs who's device_node pointers are stored into + * soc_pm.quirks.eth[].np cannot handle WoL packets while in ULP0, ULP1 + * or both due to a hardware bug. If they receive WoL packets while in + * ULP0 or ULP1 IPs could stop working or the whole system could stop + * working. We cannot handle this scenario in the ethernet driver itself + * as the driver is common to multiple vendors and also we only know + * here, in this file, if we suspend to ULP0 or ULP1 mode. Thus handle + * these scenarios here, as quirks. + */ + for (i = 0; i < AT91_PM_MAX_ETH; i++) { + eth = &soc_pm.quirks.eth[i]; + + if (!at91_pm_eth_quirk_is_valid(eth)) + continue; + + /* + * For modes in dns_modes mask the system blocks if quirk is not + * applied but if applied the interface doesn't act at WoL + * events. Thus take care to avoid suspending if this interface + * is the only configured wakeup source. + */ + if (suspend && eth->dns_modes & BIT(soc_pm.data.mode)) { + int ws_count = 0; +#ifdef CONFIG_PM_SLEEP + struct wakeup_source *ws; + + for_each_wakeup_source(ws) { + if (ws->dev == eth->dev) + continue; + + ws_count++; + break; + } +#endif + + /* + * Checking !ws is good for all platforms with issues + * even when both G_ETH and E_ETH are available as dns_modes + * is populated only on G_ETH interface. + */ + if (!ws_count) { + pr_err("AT91: PM: Ethernet cannot resume from WoL!"); + ret = -EPERM; + put_device(eth->dev); + eth->dev = NULL; + /* No need to revert clock settings for this eth. */ + i--; + goto clk_unconfigure; + } + } + + if (suspend) { + clk_bulk_disable_unprepare(AT91_PM_ETH_MAX_CLK, eth->clks); + } else { + ret = clk_bulk_prepare_enable(AT91_PM_ETH_MAX_CLK, + eth->clks); + if (ret) + goto clk_unconfigure; + /* + * Release the reference to eth->dev taken in + * at91_pm_eth_quirk_is_valid(). + */ + put_device(eth->dev); + eth->dev = NULL; + } + } + + return 0; + +clk_unconfigure: + /* + * In case of resume we reach this point if clk_prepare_enable() failed. + * we don't want to revert the previous clk_prepare_enable() for the + * other IP. + */ + for (j = i; j >= 0; j--) { + eth = &soc_pm.quirks.eth[j]; + if (suspend) { + if (!at91_pm_eth_quirk_is_valid(eth)) + continue; + + tmp = clk_bulk_prepare_enable(AT91_PM_ETH_MAX_CLK, eth->clks); + if (tmp) { + pr_err("AT91: PM: failed to enable %s clocks\n", + j == AT91_PM_G_ETH ? "geth" : "eth"); + } + } else { + /* + * Release the reference to eth->dev taken in + * at91_pm_eth_quirk_is_valid(). + */ + put_device(eth->dev); + eth->dev = NULL; + } + } + + return ret; +} + /* * Called after processes are frozen, but before we shutdown devices. */ @@ -427,6 +617,12 @@ static void at91_pm_suspend(suspend_state_t state) */ static int at91_pm_enter(suspend_state_t state) { + int ret; + + ret = at91_pm_config_quirks(true); + if (ret) + return ret; + #ifdef CONFIG_PINCTRL_AT91 /* * FIXME: this is needed to communicate between the pinctrl driver and @@ -464,6 +660,7 @@ error: #ifdef CONFIG_PINCTRL_AT91 at91_pinctrl_gpio_resume(); #endif + at91_pm_config_quirks(false); return 0; } @@ -888,6 +1085,20 @@ static const struct of_device_id atmel_shdwc_ids[] = { { /* sentinel. */ } }; +static const struct of_device_id gmac_ids[] __initconst = { + { .compatible = "atmel,sama5d3-gem" }, + { .compatible = "atmel,sama5d2-gem" }, + { .compatible = "atmel,sama5d29-gem" }, + { .compatible = "microchip,sama7g5-gem" }, + { }, +}; + +static const struct of_device_id emac_ids[] __initconst = { + { .compatible = "atmel,sama5d3-macb" }, + { .compatible = "microchip,sama7g5-emac" }, + { }, +}; + /* * Replaces _mode_to_replace with a supported mode that doesn't depend * on controller pointed by _map_bitmask @@ -941,8 +1152,30 @@ static const struct of_device_id atmel_shdwc_ids[] = { (soc_pm.data.standby_mode)); \ } while (0) +static int __init at91_pm_get_eth_clks(struct device_node *np, + struct clk_bulk_data *clks) +{ + clks[AT91_PM_ETH_PCLK].clk = of_clk_get_by_name(np, "pclk"); + if (IS_ERR(clks[AT91_PM_ETH_PCLK].clk)) + return PTR_ERR(clks[AT91_PM_ETH_PCLK].clk); + + clks[AT91_PM_ETH_HCLK].clk = of_clk_get_by_name(np, "hclk"); + if (IS_ERR(clks[AT91_PM_ETH_HCLK].clk)) + return PTR_ERR(clks[AT91_PM_ETH_HCLK].clk); + + return 0; +} + +static int __init at91_pm_eth_clks_empty(struct clk_bulk_data *clks) +{ + return IS_ERR(clks[AT91_PM_ETH_PCLK].clk) || + IS_ERR(clks[AT91_PM_ETH_HCLK].clk); +} + static void __init at91_pm_modes_init(const u32 *maps, int len) { + struct at91_pm_quirk_eth *gmac = &soc_pm.quirks.eth[AT91_PM_G_ETH]; + struct at91_pm_quirk_eth *emac = &soc_pm.quirks.eth[AT91_PM_E_ETH]; struct device_node *np; int ret; @@ -978,6 +1211,41 @@ static void __init at91_pm_modes_init(const u32 *maps, int len) } } + if ((at91_is_pm_mode_active(AT91_PM_ULP1) || + at91_is_pm_mode_active(AT91_PM_ULP0) || + at91_is_pm_mode_active(AT91_PM_ULP0_FAST)) && + (maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(ETHC) || + maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(ETHC))) { + np = of_find_matching_node(NULL, gmac_ids); + if (!np) { + np = of_find_matching_node(NULL, emac_ids); + if (np) + goto get_emac_clks; + AT91_PM_REPLACE_MODES(maps, ETHC); + goto unmap_unused_nodes; + } else { + gmac->np = np; + at91_pm_get_eth_clks(np, gmac->clks); + } + + np = of_find_matching_node(NULL, emac_ids); + if (!np) { + if (at91_pm_eth_clks_empty(gmac->clks)) + AT91_PM_REPLACE_MODES(maps, ETHC); + } else { +get_emac_clks: + emac->np = np; + ret = at91_pm_get_eth_clks(np, emac->clks); + if (ret && at91_pm_eth_clks_empty(gmac->clks)) { + of_node_put(gmac->np); + of_node_put(emac->np); + gmac->np = NULL; + emac->np = NULL; + } + } + } + +unmap_unused_nodes: /* Unmap all unnecessary. */ if (soc_pm.data.shdwc && !(maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SHDWC) || @@ -1213,17 +1481,30 @@ void __init sama5_pm_init(void) static const int modes[] __initconst = { AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP0_FAST, }; + static const u32 iomaps[] __initconst = { + [AT91_PM_ULP0] = AT91_PM_IOMAP(ETHC), + [AT91_PM_ULP0_FAST] = AT91_PM_IOMAP(ETHC), + }; int ret; if (!IS_ENABLED(CONFIG_SOC_SAMA5)) return; at91_pm_modes_validate(modes, ARRAY_SIZE(modes)); + at91_pm_modes_init(iomaps, ARRAY_SIZE(iomaps)); ret = at91_dt_ramc(false); if (ret) return; at91_pm_init(NULL); + + /* Quirks applies to ULP0, ULP0 fast and ULP1 modes. */ + soc_pm.quirks.eth[AT91_PM_G_ETH].modes = BIT(AT91_PM_ULP0) | + BIT(AT91_PM_ULP0_FAST) | + BIT(AT91_PM_ULP1); + /* Do not suspend in ULP0, ULP0 fast if GETH is the only wakeup source. */ + soc_pm.quirks.eth[AT91_PM_G_ETH].dns_modes = BIT(AT91_PM_ULP0) | + BIT(AT91_PM_ULP0_FAST); } void __init sama5d2_pm_init(void) @@ -1233,7 +1514,10 @@ void __init sama5d2_pm_init(void) AT91_PM_BACKUP, }; static const u32 iomaps[] __initconst = { - [AT91_PM_ULP1] = AT91_PM_IOMAP(SHDWC), + [AT91_PM_ULP0] = AT91_PM_IOMAP(ETHC), + [AT91_PM_ULP0_FAST] = AT91_PM_IOMAP(ETHC), + [AT91_PM_ULP1] = AT91_PM_IOMAP(SHDWC) | + AT91_PM_IOMAP(ETHC), [AT91_PM_BACKUP] = AT91_PM_IOMAP(SHDWC) | AT91_PM_IOMAP(SFRBU), }; @@ -1258,6 +1542,17 @@ void __init sama5d2_pm_init(void) soc_pm.sfrbu_regs.pswbu.ctrl = BIT(0); soc_pm.sfrbu_regs.pswbu.softsw = BIT(1); soc_pm.sfrbu_regs.pswbu.state = BIT(3); + + /* Quirk applies to ULP0, ULP0 fast and ULP1 modes. */ + soc_pm.quirks.eth[AT91_PM_G_ETH].modes = BIT(AT91_PM_ULP0) | + BIT(AT91_PM_ULP0_FAST) | + BIT(AT91_PM_ULP1); + /* + * Do not suspend in ULP0, ULP0 fast if GETH is the only wakeup + * source. + */ + soc_pm.quirks.eth[AT91_PM_G_ETH].dns_modes = BIT(AT91_PM_ULP0) | + BIT(AT91_PM_ULP0_FAST); } void __init sama7_pm_init(void) @@ -1268,7 +1563,8 @@ void __init sama7_pm_init(void) static const u32 iomaps[] __initconst = { [AT91_PM_ULP0] = AT91_PM_IOMAP(SFRBU), [AT91_PM_ULP1] = AT91_PM_IOMAP(SFRBU) | - AT91_PM_IOMAP(SHDWC), + AT91_PM_IOMAP(SHDWC) | + AT91_PM_IOMAP(ETHC), [AT91_PM_BACKUP] = AT91_PM_IOMAP(SFRBU) | AT91_PM_IOMAP(SHDWC), }; @@ -1293,6 +1589,10 @@ void __init sama7_pm_init(void) soc_pm.sfrbu_regs.pswbu.ctrl = BIT(0); soc_pm.sfrbu_regs.pswbu.softsw = BIT(1); soc_pm.sfrbu_regs.pswbu.state = BIT(2); + + /* Quirks applies to ULP1 for both Ethernet interfaces. */ + soc_pm.quirks.eth[AT91_PM_E_ETH].modes = BIT(AT91_PM_ULP1); + soc_pm.quirks.eth[AT91_PM_G_ETH].modes = BIT(AT91_PM_ULP1); } static int __init at91_pm_modes_select(char *str) From f611af4c3bfa26fc827b8074d288a3ce686916f2 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 9 May 2022 10:26:05 +0300 Subject: [PATCH 5/8] ARM: at91: Kconfig: implement PIT64B selection Implement PIT64B selection thus it will be available for the necessary targets (at the moment SAM9X60 and SAMA7G5) w/o the necessity to specify it via defconfig. With this the current CONFIG_TIMER_OF dependency of PIT64B driver could be removed. Signed-off-by: Claudiu Beznea Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/Kconfig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 279810381256..1531b4625c76 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -165,6 +165,15 @@ config ATMEL_CLOCKSOURCE_TCB to make a single 32-bit timer. It can also be used as a clock event device supporting oneshot mode. +config MICROCHIP_CLOCKSOURCE_PIT64B + bool "64-bit Periodic Interval Timer (PIT64B) support" + default SOC_SAM9X60 || SOC_SAMA7 + select MICROCHIP_PIT64B + help + Select this to get a high resolution clockevent (SAM9X60) or + clocksource and clockevent (SAMA7G5) based on Microchip 64-bit + Periodic Interval Timer. + config HAVE_AT91_UTMI bool From 0c9fd821175a5634e00aa391f5f202f8ed4c9223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= Date: Mon, 2 May 2022 17:22:18 +0200 Subject: [PATCH 6/8] ARM: at91: add code to handle secure calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since OP-TEE now has a more complete support for sama5d2, add necessary code to perform SMC calls. The detection of OP-TEE is based on a specific device-tree node path (/firmware/optee) such has done by some other SoC. A check is added to avoid doing SMC calls without having OP-TEE. Signed-off-by: Clément Léger Signed-off-by: Claudiu Beznea Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/Makefile | 2 +- arch/arm/mach-at91/sam_secure.c | 46 +++++++++++++++++++++++++++++++++ arch/arm/mach-at91/sam_secure.h | 14 ++++++++++ arch/arm/mach-at91/sama5.c | 2 ++ 4 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-at91/sam_secure.c create mode 100644 arch/arm/mach-at91/sam_secure.h diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile index 522b680b6446..0dcc37180588 100644 --- a/arch/arm/mach-at91/Makefile +++ b/arch/arm/mach-at91/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_SOC_AT91RM9200) += at91rm9200.o obj-$(CONFIG_SOC_AT91SAM9) += at91sam9.o obj-$(CONFIG_SOC_SAM9X60) += sam9x60.o -obj-$(CONFIG_SOC_SAMA5) += sama5.o +obj-$(CONFIG_SOC_SAMA5) += sama5.o sam_secure.o obj-$(CONFIG_SOC_SAMA7) += sama7.o obj-$(CONFIG_SOC_SAMV7) += samv7.o diff --git a/arch/arm/mach-at91/sam_secure.c b/arch/arm/mach-at91/sam_secure.c new file mode 100644 index 000000000000..2a01f7a7d13f --- /dev/null +++ b/arch/arm/mach-at91/sam_secure.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2022, Microchip + */ + +#include +#include + +#include "sam_secure.h" + +static bool optee_available; + +#define SAM_SIP_SMC_STD_CALL_VAL(func_num) \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_SIP, (func_num)) + +struct arm_smccc_res sam_smccc_call(u32 fn, u32 arg0, u32 arg1) +{ + struct arm_smccc_res res = {.a0 = -1}; + + if (WARN_ON(!optee_available)) + return res; + + arm_smccc_smc(SAM_SIP_SMC_STD_CALL_VAL(fn), arg0, arg1, 0, 0, 0, 0, 0, + &res); + + return res; +} + +void __init sam_secure_init(void) +{ + struct device_node *np; + + /* + * We only check that the OP-TEE node is present and available. The + * OP-TEE kernel driver is not needed for the type of interaction made + * with OP-TEE here so the driver's status is not checked. + */ + np = of_find_node_by_path("/firmware/optee"); + if (np && of_device_is_available(np)) + optee_available = true; + of_node_put(np); + + if (optee_available) + pr_info("Running under OP-TEE firmware\n"); +} diff --git a/arch/arm/mach-at91/sam_secure.h b/arch/arm/mach-at91/sam_secure.h new file mode 100644 index 000000000000..360036672f52 --- /dev/null +++ b/arch/arm/mach-at91/sam_secure.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2022, Microchip + */ + +#ifndef SAM_SECURE_H +#define SAM_SECURE_H + +#include + +void __init sam_secure_init(void); +struct arm_smccc_res sam_smccc_call(u32 fn, u32 arg0, u32 arg1); + +#endif /* SAM_SECURE_H */ diff --git a/arch/arm/mach-at91/sama5.c b/arch/arm/mach-at91/sama5.c index 89dab7cf01e8..de5dd28b392e 100644 --- a/arch/arm/mach-at91/sama5.c +++ b/arch/arm/mach-at91/sama5.c @@ -14,6 +14,7 @@ #include #include "generic.h" +#include "sam_secure.h" static void __init sama5_dt_device_init(void) { @@ -47,6 +48,7 @@ MACHINE_END static void __init sama5d2_init(void) { of_platform_default_populate(NULL, NULL, NULL); + sam_secure_init(); sama5d2_pm_init(); } From f2f5cf78a3336f0245a1a59e641ce98fa9503db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20L=C3=A9ger?= Date: Mon, 2 May 2022 17:22:19 +0200 Subject: [PATCH 7/8] ARM: at91: pm: add support for sama5d2 secure suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running with OP-TEE, the suspend control is handled securely. Suspend can be entered using PSCI support. Since the sama5d2 supports multiple suspend modes, add a new CONFIG_ATMEL_SECURE_PM which will send a SMC call to select the suspend mode at init time. "atmel.pm_modes" boot argument is still supported for compatibility purposes but the standby value is actually ignored since PSCI suspend is used and it only support one mode (suspend). Signed-off-by: Clément Léger Signed-off-by: Claudiu Beznea Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/Kconfig | 11 +++++++++- arch/arm/mach-at91/pm.c | 36 +++++++++++++++++++++++++++++++++ arch/arm/mach-at91/sam_secure.h | 4 ++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 1531b4625c76..0edb60f7ccc6 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -218,7 +218,16 @@ config SOC_SAMA5 select SRAM if PM config ATMEL_PM - bool + bool "Atmel PM support" + +config ATMEL_SECURE_PM + bool "Atmel Secure PM support" + depends on SOC_SAMA5D2 && ATMEL_PM + select ARM_PSCI + help + When running under a TEE, the suspend mode must be requested to be set + at TEE level. When enable, this option will use secure monitor calls + to set the suspend level. PSCI is then used to enter suspend. config SOC_SAMA7 bool diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index 7b2617000845..b1a43d7bc56c 100644 --- a/arch/arm/mach-at91/pm.c +++ b/arch/arm/mach-at91/pm.c @@ -28,6 +28,7 @@ #include "generic.h" #include "pm.h" +#include "sam_secure.h" #define BACKUP_DDR_PHY_CALIBRATION (9) @@ -1078,6 +1079,35 @@ securam_fail: return ret; } +static void at91_pm_secure_init(void) +{ + int suspend_mode; + struct arm_smccc_res res; + + suspend_mode = soc_pm.data.suspend_mode; + + res = sam_smccc_call(SAMA5_SMC_SIP_SET_SUSPEND_MODE, + suspend_mode, 0); + if (res.a0 == 0) { + pr_info("AT91: Secure PM: suspend mode set to %s\n", + pm_modes[suspend_mode].pattern); + return; + } + + pr_warn("AT91: Secure PM: %s mode not supported !\n", + pm_modes[suspend_mode].pattern); + + res = sam_smccc_call(SAMA5_SMC_SIP_GET_SUSPEND_MODE, 0, 0); + if (res.a0 == 0) { + pr_warn("AT91: Secure PM: failed to get default mode\n"); + return; + } + + pr_info("AT91: Secure PM: using default suspend mode %s\n", + pm_modes[suspend_mode].pattern); + + soc_pm.data.suspend_mode = res.a1; +} static const struct of_device_id atmel_shdwc_ids[] = { { .compatible = "atmel,sama5d2-shdwc" }, { .compatible = "microchip,sam9x60-shdwc" }, @@ -1526,6 +1556,12 @@ void __init sama5d2_pm_init(void) if (!IS_ENABLED(CONFIG_SOC_SAMA5D2)) return; + if (IS_ENABLED(CONFIG_ATMEL_SECURE_PM)) { + pr_warn("AT91: Secure PM: ignoring standby mode\n"); + at91_pm_secure_init(); + return; + } + at91_pm_modes_validate(modes, ARRAY_SIZE(modes)); at91_pm_modes_init(iomaps, ARRAY_SIZE(iomaps)); ret = at91_dt_ramc(false); diff --git a/arch/arm/mach-at91/sam_secure.h b/arch/arm/mach-at91/sam_secure.h index 360036672f52..1e7d8b20ba1e 100644 --- a/arch/arm/mach-at91/sam_secure.h +++ b/arch/arm/mach-at91/sam_secure.h @@ -8,6 +8,10 @@ #include +/* Secure Monitor mode APIs */ +#define SAMA5_SMC_SIP_SET_SUSPEND_MODE 0x400 +#define SAMA5_SMC_SIP_GET_SUSPEND_MODE 0x401 + void __init sam_secure_init(void); struct arm_smccc_res sam_smccc_call(u32 fn, u32 arg0, u32 arg1); From 6041558ebf1fc87d1e0bdeb51e985807dab06bed Mon Sep 17 00:00:00 2001 From: Kavyasree Kotagiri Date: Fri, 13 May 2022 14:55:30 +0530 Subject: [PATCH 8/8] ARM: at91: debug: add lan966 support Add support for low-level debugging on FLEXCOM USART of LAN966 SoC. Signed-off-by: Kavyasree Kotagiri Reviewed-by: Michael Walle Tested-by: Michael Walle Signed-off-by: Nicolas Ferre Link: https://lore.kernel.org/r/20220513092530.19213-1-kavyasree.kotagiri@microchip.com --- arch/arm/Kconfig.debug | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index 0c9497d549e3..77b2db882e75 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -210,6 +210,26 @@ choice Say Y here if you want kernel low-level debugging support on the FLEXCOM3 port of SAMA7G5. + config DEBUG_AT91_LAN966_FLEXCOM + bool "Kernel low-level debugging on LAN966 FLEXCOM USART" + select DEBUG_AT91_UART + depends on SOC_LAN966 + help + Say Y here if you want kernel low-level debugging support + on the FLEXCOM port of LAN966. + + DEBUG_UART_PHYS | DEBUG_UART_VIRT + + 0xe0040200 | 0xfd040200 | FLEXCOM0 + 0xe0044200 | 0xfd044200 | FLEXCOM1 + 0xe0060200 | 0xfd060200 | FLEXCOM2 + 0xe0064200 | 0xfd064200 | FLEXCOM3 + 0xe0070200 | 0xfd070200 | FLEXCOM4 + + By default, enabling FLEXCOM3 port. Based on requirement, use + DEBUG_UART_PHYS and DEBUG_UART_VIRT configurations from above + table. + config DEBUG_BCM2835 bool "Kernel low-level debugging on BCM2835 PL011 UART" depends on ARCH_BCM2835 && ARCH_MULTI_V6 @@ -1685,6 +1705,7 @@ config DEBUG_UART_PHYS default 0xd4017000 if DEBUG_MMP_UART2 default 0xd4018000 if DEBUG_MMP_UART3 default 0xe0000000 if DEBUG_SPEAR13XX + default 0xe0064200 if DEBUG_AT91_LAN966_FLEXCOM default 0xe1824200 if DEBUG_AT91_SAMA7G5_FLEXCOM3 default 0xe4007000 if DEBUG_HIP04_UART default 0xe6c40000 if DEBUG_RMOBILE_SCIFA0 @@ -1805,6 +1826,7 @@ config DEBUG_UART_VIRT default 0xfb10c000 if DEBUG_REALVIEW_PB1176_PORT default 0xfcfe8600 if DEBUG_BCM63XX_UART default 0xfd000000 if DEBUG_SPEAR3XX || DEBUG_SPEAR13XX + default 0xfd064200 if DEBUG_AT91_LAN966_FLEXCOM default 0xfd531000 if DEBUG_STIH41X_SBC_ASC1 default 0xfd883000 if DEBUG_ALPINE_UART0 default 0xfdd32000 if DEBUG_STIH41X_ASC2