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:
commit
dc5d8bfa3a
7 changed files with 506 additions and 35 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
46
arch/arm/mach-at91/sam_secure.c
Normal file
46
arch/arm/mach-at91/sam_secure.c
Normal 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");
|
||||
}
|
||||
18
arch/arm/mach-at91/sam_secure.h
Normal file
18
arch/arm/mach-at91/sam_secure.h
Normal 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 */
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue