AT91 & POLARFIRE SoC #1 for 5.19:

- Power Management: add possibility to implement
   specific pm quirks for some SoCs
 - Kconfig update for AT91 PIT64 and LAN966 low-level debugging
 - sama5d2: add secure calls to OP-TEE and secure suspend
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQQ5TRCVIBiyi/S+BG4fOrpwrNPNDAUCYn5J7QAKCRAfOrpwrNPN
 DEqbAQDDUnFDji86O9rIXX2I9+kKwUoblSAuiyNISZz8oh/JcwD/aW7svsKvpdjI
 zHQj2TIQcJM9qZowmj/ExMEvsPGf4Q4=
 =PXjW
 -----END PGP SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAmJ+TjIACgkQmmx57+YA
 GNkfyw/9EzuMrocMBd29S5uT82maFCrEsjuFbxaxFxgTwdqDzWK2kVORnDwSj6Xb
 Sm1K6zO1sAh5xNnGIdjiLenz3Gdlx3cYnggnPVqw6jN/8QEM9FVIy6tgZllrbHHS
 lgTWlPfyBxU1n4b2XOELTBTsxOZZz/AtETEnidowJ0bwOSK/QIqdVZr3uVKC1pEB
 OzBtdzTLy75Jtrq4ASk5O32+XVlHJZrWZ17JWvCXZPCDcgCGiAcXiHqtC0+Gl+U3
 vzpxMCITQJaPco3qKsO4X8NvM7BCMX2KD2/ADER8ngxO7nUZqRTCiUXQEid1f9qW
 Y7/sPAo+/s457zvHXjr3HQ7ezF3FVi7vB+PV3vsi+R1bftRifFPKVcM6IxI+2sP8
 9bzDTEeg5NWStrAFZehCW7v8VFIKDVm+n8Hb6M8xkZbB9dRcxr9wBALJooIAEOWa
 j8D4pw4RqMixHJdxDfNd4omz+5UCQUSI2O1yhFdddwqqlWdIgEEJ7hPfcRJOaFzs
 dLYFwQQRxndwzNbX5v862314rABfbvpQv8iACedRUJGfuB82GuP2bfZrBca7ZwkT
 JmQBKFn4PT5LK+T0EdUHmA62hEmZ1Cahg0UyDCNR9mIcHRZ/fjwkzlcwycEXjX9N
 y7pQhpsbvPu3uOejHzfeutBNzVG/9yPMcdqVd/UB2IsQDF7EOtk=
 =/56p
 -----END PGP SIGNATURE-----

Merge tag 'at91-soc-5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/at91/linux into arm/soc

AT91 & POLARFIRE SoC #1 for 5.19:

- Power Management: add possibility to implement
  specific pm quirks for some SoCs
- Kconfig update for AT91 PIT64 and LAN966 low-level debugging
- sama5d2: add secure calls to OP-TEE and secure suspend

* tag 'at91-soc-5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/at91/linux:
  ARM: at91: debug: add lan966 support
  ARM: at91: pm: add support for sama5d2 secure suspend
  ARM: at91: add code to handle secure calls
  ARM: at91: Kconfig: implement PIT64B selection
  ARM: at91: pm: add quirks for pm
  ARM: at91: pm: use kernel documentation style
  ARM: at91: pm: introduce macros for pm mode replacement
  ARM: at91: pm: keep documentation inline with structure members

Link: https://lore.kernel.org/r/20220513121701.77683-1-nicolas.ferre@microchip.com
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Arnd Bergmann 2022-05-13 14:25:21 +02:00
commit dc5d8bfa3a
7 changed files with 506 additions and 35 deletions

View file

@ -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

View file

@ -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
@ -209,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

View file

@ -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

View file

@ -15,6 +15,7 @@
#include <linux/parser.h>
#include <linux/suspend.h>
#include <linux/clk.h>
#include <linux/clk/at91_pmc.h>
#include <linux/platform_data/atmel.h>
@ -27,6 +28,7 @@
#include "generic.h"
#include "pm.h"
#include "sam_secure.h"
#define BACKUP_DDR_PHY_CALIBRATION (9)
@ -47,8 +49,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 {
@ -60,14 +62,65 @@ 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
* @bu: backup unit mapped data (for backup mode)
* @memcs: memory chip select
*/
struct at91_soc_pm {
@ -75,19 +128,22 @@ 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;
};
/**
* 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
* @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 +319,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 +618,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 +661,7 @@ error:
#ifdef CONFIG_PINCTRL_AT91
at91_pinctrl_gpio_resume();
#endif
at91_pm_config_quirks(false);
return 0;
}
@ -881,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" },
@ -888,10 +1115,99 @@ 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
* @_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 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, mode;
int ret;
ret = at91_pm_backup_init();
if (ret) {
@ -906,17 +1222,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,27 +1234,48 @@ 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);
}
}
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) ||
@ -1184,17 +1511,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)
@ -1204,7 +1544,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),
};
@ -1213,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);
@ -1229,6 +1578,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)
@ -1239,7 +1599,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),
};
@ -1264,6 +1625,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)

View file

@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2022, Microchip
*/
#include <linux/arm-smccc.h>
#include <linux/of.h>
#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");
}

View file

@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2022, Microchip
*/
#ifndef SAM_SECURE_H
#define SAM_SECURE_H
#include <linux/arm-smccc.h>
/* 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);
#endif /* SAM_SECURE_H */

View file

@ -14,6 +14,7 @@
#include <asm/system_misc.h>
#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();
}