Unverified Commit dc5d8bfa authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

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: default avatarArnd Bergmann <arnd@arndb.de>
parents f03e9509 6041558e
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -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
+19 −1
Original line number Diff line number Diff line
@@ -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
+1 −1
Original line number Diff line number Diff line
@@ -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

+398 −33
Original line number Diff line number Diff line
@@ -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 @@ static int at91_pm_enter(suspend_state_t state)
#ifdef CONFIG_PINCTRL_AT91
	at91_pinctrl_gpio_resume();
#endif
	at91_pm_config_quirks(false);
	return 0;
}

@@ -881,6 +1079,35 @@ static int __init at91_pm_backup_init(void)
	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 −0
Original line number Diff line number Diff line
// 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");
}
Loading