xref: /openbmc/linux/arch/arm/mach-at91/pm.c (revision 060f35a317ef09101b128f399dce7ed13d019461)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29d041268SAndrew Victor /*
39d041268SAndrew Victor  * arch/arm/mach-at91/pm.c
49d041268SAndrew Victor  * AT91 Power Management
59d041268SAndrew Victor  *
69d041268SAndrew Victor  * Copyright (C) 2005 David Brownell
79d041268SAndrew Victor  */
89d041268SAndrew Victor 
9d2e46790SAlexandre Belloni #include <linux/genalloc.h>
109824c447SAlexandre Belloni #include <linux/io.h>
119824c447SAlexandre Belloni #include <linux/of_address.h>
12f5598d34SAlexandre Belloni #include <linux/of.h>
13d2d4716dSClaudiu Beznea #include <linux/of_fdt.h>
14d2e46790SAlexandre Belloni #include <linux/of_platform.h>
15d4b564a5SRob Herring #include <linux/platform_device.h>
167693e18eSAlexandre Belloni #include <linux/parser.h>
179824c447SAlexandre Belloni #include <linux/suspend.h>
189824c447SAlexandre Belloni 
19b7fc72c6SClaudiu Beznea #include <linux/clk.h>
202edb90aeSBoris BREZILLON #include <linux/clk/at91_pmc.h>
2195701b1cSPhilippe Mazenauer #include <linux/platform_data/atmel.h>
229d041268SAndrew Victor 
23385acc0dSWenyou Yang #include <asm/cacheflush.h>
249824c447SAlexandre Belloni #include <asm/fncpy.h>
25fbc7edcaSAlexandre Belloni #include <asm/system_misc.h>
2624a0f5c5SAlexandre Belloni #include <asm/suspend.h>
279d041268SAndrew Victor 
289d041268SAndrew Victor #include "generic.h"
291ea60cf7SAlbin Tonnerre #include "pm.h"
30f2f5cf78SClément Léger #include "sam_secure.h"
319d041268SAndrew Victor 
32d2d4716dSClaudiu Beznea #define BACKUP_DDR_PHY_CALIBRATION	(9)
33d2d4716dSClaudiu Beznea 
34f19dd1dfSClaudiu Beznea /**
35f19dd1dfSClaudiu Beznea  * struct at91_pm_bu - AT91 power management backup unit data structure
36f19dd1dfSClaudiu Beznea  * @suspended: true if suspended to backup mode
37f19dd1dfSClaudiu Beznea  * @reserved: reserved
38f19dd1dfSClaudiu Beznea  * @canary: canary data for memory checking after exit from backup mode
39f19dd1dfSClaudiu Beznea  * @resume: resume API
40d2d4716dSClaudiu Beznea  * @ddr_phy_calibration: DDR PHY calibration data: ZQ0CR0, first 8 words
41d2d4716dSClaudiu Beznea  * of the memory
42f19dd1dfSClaudiu Beznea  */
43f19dd1dfSClaudiu Beznea struct at91_pm_bu {
44f19dd1dfSClaudiu Beznea 	int suspended;
45f19dd1dfSClaudiu Beznea 	unsigned long reserved;
46f19dd1dfSClaudiu Beznea 	phys_addr_t canary;
47f19dd1dfSClaudiu Beznea 	phys_addr_t resume;
48d2d4716dSClaudiu Beznea 	unsigned long ddr_phy_calibration[BACKUP_DDR_PHY_CALIBRATION];
49f19dd1dfSClaudiu Beznea };
50f19dd1dfSClaudiu Beznea 
519750d3b4SClaudiu Beznea /**
529750d3b4SClaudiu Beznea  * struct at91_pm_sfrbu_regs - registers mapping for SFRBU
53ac809e78SClaudiu Beznea  * @pswbu: power switch BU control registers
54ac809e78SClaudiu Beznea  */
55ac809e78SClaudiu Beznea struct at91_pm_sfrbu_regs {
56ac809e78SClaudiu Beznea 	struct {
57ac809e78SClaudiu Beznea 		u32 key;
58ac809e78SClaudiu Beznea 		u32 ctrl;
59ac809e78SClaudiu Beznea 		u32 state;
60ac809e78SClaudiu Beznea 		u32 softsw;
61ac809e78SClaudiu Beznea 	} pswbu;
62ac809e78SClaudiu Beznea };
63ac809e78SClaudiu Beznea 
640a7a2443SClaudiu Beznea /**
65b7fc72c6SClaudiu Beznea  * enum at91_pm_eth_clk - Ethernet clock indexes
66b7fc72c6SClaudiu Beznea  * @AT91_PM_ETH_PCLK: pclk index
67b7fc72c6SClaudiu Beznea  * @AT91_PM_ETH_HCLK: hclk index
68b7fc72c6SClaudiu Beznea  * @AT91_PM_ETH_MAX_CLK: max index
69b7fc72c6SClaudiu Beznea  */
70b7fc72c6SClaudiu Beznea enum at91_pm_eth_clk {
71b7fc72c6SClaudiu Beznea 	AT91_PM_ETH_PCLK,
72b7fc72c6SClaudiu Beznea 	AT91_PM_ETH_HCLK,
73b7fc72c6SClaudiu Beznea 	AT91_PM_ETH_MAX_CLK,
74b7fc72c6SClaudiu Beznea };
75b7fc72c6SClaudiu Beznea 
76b7fc72c6SClaudiu Beznea /**
77b7fc72c6SClaudiu Beznea  * enum at91_pm_eth - Ethernet controller indexes
78b7fc72c6SClaudiu Beznea  * @AT91_PM_G_ETH: gigabit Ethernet controller index
79b7fc72c6SClaudiu Beznea  * @AT91_PM_E_ETH: megabit Ethernet controller index
80b7fc72c6SClaudiu Beznea  * @AT91_PM_MAX_ETH: max index
81b7fc72c6SClaudiu Beznea  */
82b7fc72c6SClaudiu Beznea enum at91_pm_eth {
83b7fc72c6SClaudiu Beznea 	AT91_PM_G_ETH,
84b7fc72c6SClaudiu Beznea 	AT91_PM_E_ETH,
85b7fc72c6SClaudiu Beznea 	AT91_PM_MAX_ETH,
86b7fc72c6SClaudiu Beznea };
87b7fc72c6SClaudiu Beznea 
88b7fc72c6SClaudiu Beznea /**
89b7fc72c6SClaudiu Beznea  * struct at91_pm_quirk_eth - AT91 PM Ethernet quirks
90b7fc72c6SClaudiu Beznea  * @dev: Ethernet device
91b7fc72c6SClaudiu Beznea  * @np: Ethernet device node
92b7fc72c6SClaudiu Beznea  * @clks: Ethernet clocks
93b7fc72c6SClaudiu Beznea  * @modes: power management mode that this quirk applies to
94b7fc72c6SClaudiu Beznea  * @dns_modes: do not suspend modes: stop suspending if Ethernet is configured
95b7fc72c6SClaudiu Beznea  *	       as wakeup source but buggy and no other wakeup source is
96b7fc72c6SClaudiu Beznea  *	       available
97b7fc72c6SClaudiu Beznea  */
98b7fc72c6SClaudiu Beznea struct at91_pm_quirk_eth {
99b7fc72c6SClaudiu Beznea 	struct device *dev;
100b7fc72c6SClaudiu Beznea 	struct device_node *np;
101b7fc72c6SClaudiu Beznea 	struct clk_bulk_data clks[AT91_PM_ETH_MAX_CLK];
102b7fc72c6SClaudiu Beznea 	u32 modes;
103b7fc72c6SClaudiu Beznea 	u32 dns_modes;
104b7fc72c6SClaudiu Beznea };
105b7fc72c6SClaudiu Beznea 
106b7fc72c6SClaudiu Beznea /**
107b7fc72c6SClaudiu Beznea  * struct at91_pm_quirks - AT91 PM quirks
108b7fc72c6SClaudiu Beznea  * @eth: Ethernet quirks
109b7fc72c6SClaudiu Beznea  */
110b7fc72c6SClaudiu Beznea struct at91_pm_quirks {
111b7fc72c6SClaudiu Beznea 	struct at91_pm_quirk_eth eth[AT91_PM_MAX_ETH];
112b7fc72c6SClaudiu Beznea };
113b7fc72c6SClaudiu Beznea 
114b7fc72c6SClaudiu Beznea /**
1150a7a2443SClaudiu Beznea  * struct at91_soc_pm - AT91 SoC power management data structure
1160a7a2443SClaudiu Beznea  * @config_shdwc_ws: wakeup sources configuration function for SHDWC
1170a7a2443SClaudiu Beznea  * @config_pmc_ws: wakeup srouces configuration function for PMC
1180a7a2443SClaudiu Beznea  * @ws_ids: wakup sources of_device_id array
11922cbf077SClaudiu Beznea  * @bu: backup unit mapped data (for backup mode)
120b7fc72c6SClaudiu Beznea  * @quirks: PM quirks
1210a7a2443SClaudiu Beznea  * @data: PM data to be used on last phase of suspend
122ac809e78SClaudiu Beznea  * @sfrbu_regs: SFRBU registers mapping
123d2d4716dSClaudiu Beznea  * @memcs: memory chip select
1240a7a2443SClaudiu Beznea  */
125c3f5b8fdSClaudiu Beznea struct at91_soc_pm {
126a958156dSClaudiu Beznea 	int (*config_shdwc_ws)(void __iomem *shdwc, u32 *mode, u32 *polarity);
127a958156dSClaudiu Beznea 	int (*config_pmc_ws)(void __iomem *pmc, u32 mode, u32 polarity);
128a958156dSClaudiu Beznea 	const struct of_device_id *ws_ids;
129f19dd1dfSClaudiu Beznea 	struct at91_pm_bu *bu;
130b7fc72c6SClaudiu Beznea 	struct at91_pm_quirks quirks;
131c3f5b8fdSClaudiu Beznea 	struct at91_pm_data data;
132ac809e78SClaudiu Beznea 	struct at91_pm_sfrbu_regs sfrbu_regs;
133d2d4716dSClaudiu Beznea 	void *memcs;
134c3f5b8fdSClaudiu Beznea };
135c3f5b8fdSClaudiu Beznea 
136404956f4SClaudiu Beznea /**
1379750d3b4SClaudiu Beznea  * enum at91_pm_iomaps - IOs that needs to be mapped for different PM modes
138404956f4SClaudiu Beznea  * @AT91_PM_IOMAP_SHDWC:	SHDWC controller
139404956f4SClaudiu Beznea  * @AT91_PM_IOMAP_SFRBU:	SFRBU controller
140b7fc72c6SClaudiu Beznea  * @AT91_PM_IOMAP_ETHC:		Ethernet controller
141404956f4SClaudiu Beznea  */
142404956f4SClaudiu Beznea enum at91_pm_iomaps {
143404956f4SClaudiu Beznea 	AT91_PM_IOMAP_SHDWC,
144404956f4SClaudiu Beznea 	AT91_PM_IOMAP_SFRBU,
145b7fc72c6SClaudiu Beznea 	AT91_PM_IOMAP_ETHC,
146404956f4SClaudiu Beznea };
147404956f4SClaudiu Beznea 
148404956f4SClaudiu Beznea #define AT91_PM_IOMAP(name)	BIT(AT91_PM_IOMAP_##name)
149404956f4SClaudiu Beznea 
150c3f5b8fdSClaudiu Beznea static struct at91_soc_pm soc_pm = {
151c3f5b8fdSClaudiu Beznea 	.data = {
152c3f5b8fdSClaudiu Beznea 		.standby_mode = AT91_PM_STANDBY,
153c3f5b8fdSClaudiu Beznea 		.suspend_mode = AT91_PM_ULP0,
154c3f5b8fdSClaudiu Beznea 	},
155c3f5b8fdSClaudiu Beznea };
156c3f5b8fdSClaudiu Beznea 
1577693e18eSAlexandre Belloni static const match_table_t pm_modes __initconst = {
158514e2a29SClaudiu Beznea 	{ AT91_PM_STANDBY,	"standby" },
159514e2a29SClaudiu Beznea 	{ AT91_PM_ULP0,		"ulp0" },
160e70bfc2fSClaudiu Beznea 	{ AT91_PM_ULP0_FAST,    "ulp0-fast" },
1615b56c182SWenyou Yang 	{ AT91_PM_ULP1,		"ulp1" },
1627693e18eSAlexandre Belloni 	{ AT91_PM_BACKUP,	"backup" },
1637693e18eSAlexandre Belloni 	{ -1, NULL },
1647693e18eSAlexandre Belloni };
1657693e18eSAlexandre Belloni 
1664d767bc3SAlexandre Belloni #define at91_ramc_read(id, field) \
167c3f5b8fdSClaudiu Beznea 	__raw_readl(soc_pm.data.ramc[id] + field)
1684d767bc3SAlexandre Belloni 
1694d767bc3SAlexandre Belloni #define at91_ramc_write(id, field, value) \
170c3f5b8fdSClaudiu Beznea 	__raw_writel(value, soc_pm.data.ramc[id] + field)
1715ad945eaSDaniel Lezcano 
at91_pm_valid_state(suspend_state_t state)1729d041268SAndrew Victor static int at91_pm_valid_state(suspend_state_t state)
1739d041268SAndrew Victor {
1749d041268SAndrew Victor 	switch (state) {
1759d041268SAndrew Victor 		case PM_SUSPEND_ON:
1769d041268SAndrew Victor 		case PM_SUSPEND_STANDBY:
1779d041268SAndrew Victor 		case PM_SUSPEND_MEM:
1789d041268SAndrew Victor 			return 1;
1799d041268SAndrew Victor 
1809d041268SAndrew Victor 		default:
1819d041268SAndrew Victor 			return 0;
1829d041268SAndrew Victor 	}
1839d041268SAndrew Victor }
1849d041268SAndrew Victor 
18524a0f5c5SAlexandre Belloni static int canary = 0xA5A5A5A5;
1869d041268SAndrew Victor 
187d7484f5cSClaudiu Beznea struct wakeup_source_info {
188d7484f5cSClaudiu Beznea 	unsigned int pmc_fsmr_bit;
189d7484f5cSClaudiu Beznea 	unsigned int shdwc_mr_bit;
190d7484f5cSClaudiu Beznea 	bool set_polarity;
191d7484f5cSClaudiu Beznea };
192d7484f5cSClaudiu Beznea 
193d7484f5cSClaudiu Beznea static const struct wakeup_source_info ws_info[] = {
194d7484f5cSClaudiu Beznea 	{ .pmc_fsmr_bit = AT91_PMC_FSTT(10),	.set_polarity = true },
195d7484f5cSClaudiu Beznea 	{ .pmc_fsmr_bit = AT91_PMC_RTCAL,	.shdwc_mr_bit = BIT(17) },
196d7484f5cSClaudiu Beznea 	{ .pmc_fsmr_bit = AT91_PMC_USBAL },
197d7484f5cSClaudiu Beznea 	{ .pmc_fsmr_bit = AT91_PMC_SDMMC_CD },
198eaedc0d3SClaudiu Beznea 	{ .pmc_fsmr_bit = AT91_PMC_RTTAL },
199eaedc0d3SClaudiu Beznea 	{ .pmc_fsmr_bit = AT91_PMC_RXLP_MCE },
200d7484f5cSClaudiu Beznea };
201d7484f5cSClaudiu Beznea 
202d7484f5cSClaudiu Beznea static const struct of_device_id sama5d2_ws_ids[] = {
203d7484f5cSClaudiu Beznea 	{ .compatible = "atmel,sama5d2-gem",		.data = &ws_info[0] },
204ddc980daSClaudiu Beznea 	{ .compatible = "atmel,sama5d2-rtc",		.data = &ws_info[1] },
205d7484f5cSClaudiu Beznea 	{ .compatible = "atmel,sama5d3-udc",		.data = &ws_info[2] },
206d7484f5cSClaudiu Beznea 	{ .compatible = "atmel,at91rm9200-ohci",	.data = &ws_info[2] },
207d7484f5cSClaudiu Beznea 	{ .compatible = "usb-ohci",			.data = &ws_info[2] },
208d7484f5cSClaudiu Beznea 	{ .compatible = "atmel,at91sam9g45-ehci",	.data = &ws_info[2] },
209d7484f5cSClaudiu Beznea 	{ .compatible = "usb-ehci",			.data = &ws_info[2] },
210d7484f5cSClaudiu Beznea 	{ .compatible = "atmel,sama5d2-sdhci",		.data = &ws_info[3] },
211d7484f5cSClaudiu Beznea 	{ /* sentinel */ }
212d7484f5cSClaudiu Beznea };
213d7484f5cSClaudiu Beznea 
214eaedc0d3SClaudiu Beznea static const struct of_device_id sam9x60_ws_ids[] = {
21564152266SClaudiu Beznea 	{ .compatible = "microchip,sam9x60-rtc",	.data = &ws_info[1] },
216eaedc0d3SClaudiu Beznea 	{ .compatible = "atmel,at91rm9200-ohci",	.data = &ws_info[2] },
217eaedc0d3SClaudiu Beznea 	{ .compatible = "usb-ohci",			.data = &ws_info[2] },
218eaedc0d3SClaudiu Beznea 	{ .compatible = "atmel,at91sam9g45-ehci",	.data = &ws_info[2] },
219eaedc0d3SClaudiu Beznea 	{ .compatible = "usb-ehci",			.data = &ws_info[2] },
22064152266SClaudiu Beznea 	{ .compatible = "microchip,sam9x60-rtt",	.data = &ws_info[4] },
221eaedc0d3SClaudiu Beznea 	{ .compatible = "cdns,sam9x60-macb",		.data = &ws_info[5] },
222eaedc0d3SClaudiu Beznea 	{ /* sentinel */ }
223eaedc0d3SClaudiu Beznea };
224eaedc0d3SClaudiu Beznea 
2256501330fSClaudiu Beznea static const struct of_device_id sama7g5_ws_ids[] = {
2261c40169bSClaudiu Beznea 	{ .compatible = "microchip,sama7g5-rtc",	.data = &ws_info[1] },
2276501330fSClaudiu Beznea 	{ .compatible = "microchip,sama7g5-ohci",	.data = &ws_info[2] },
2286501330fSClaudiu Beznea 	{ .compatible = "usb-ohci",			.data = &ws_info[2] },
2296501330fSClaudiu Beznea 	{ .compatible = "atmel,at91sam9g45-ehci",	.data = &ws_info[2] },
2306501330fSClaudiu Beznea 	{ .compatible = "usb-ehci",			.data = &ws_info[2] },
2316501330fSClaudiu Beznea 	{ .compatible = "microchip,sama7g5-sdhci",	.data = &ws_info[3] },
2321c40169bSClaudiu Beznea 	{ .compatible = "microchip,sama7g5-rtt",	.data = &ws_info[4] },
2336501330fSClaudiu Beznea 	{ /* sentinel */ }
2346501330fSClaudiu Beznea };
2356501330fSClaudiu Beznea 
at91_pm_config_ws(unsigned int pm_mode,bool set)236d7484f5cSClaudiu Beznea static int at91_pm_config_ws(unsigned int pm_mode, bool set)
237d7484f5cSClaudiu Beznea {
238d7484f5cSClaudiu Beznea 	const struct wakeup_source_info *wsi;
239d7484f5cSClaudiu Beznea 	const struct of_device_id *match;
240d7484f5cSClaudiu Beznea 	struct platform_device *pdev;
241d7484f5cSClaudiu Beznea 	struct device_node *np;
242d7484f5cSClaudiu Beznea 	unsigned int mode = 0, polarity = 0, val = 0;
243d7484f5cSClaudiu Beznea 
244d7484f5cSClaudiu Beznea 	if (pm_mode != AT91_PM_ULP1)
245d7484f5cSClaudiu Beznea 		return 0;
246d7484f5cSClaudiu Beznea 
247a958156dSClaudiu Beznea 	if (!soc_pm.data.pmc || !soc_pm.data.shdwc || !soc_pm.ws_ids)
248d7484f5cSClaudiu Beznea 		return -EPERM;
249d7484f5cSClaudiu Beznea 
250d7484f5cSClaudiu Beznea 	if (!set) {
251c3f5b8fdSClaudiu Beznea 		writel(mode, soc_pm.data.pmc + AT91_PMC_FSMR);
252d7484f5cSClaudiu Beznea 		return 0;
253d7484f5cSClaudiu Beznea 	}
254d7484f5cSClaudiu Beznea 
255a958156dSClaudiu Beznea 	if (soc_pm.config_shdwc_ws)
256a958156dSClaudiu Beznea 		soc_pm.config_shdwc_ws(soc_pm.data.shdwc, &mode, &polarity);
257d7484f5cSClaudiu Beznea 
258d7484f5cSClaudiu Beznea 	/* SHDWC.MR */
259c3f5b8fdSClaudiu Beznea 	val = readl(soc_pm.data.shdwc + 0x04);
260d7484f5cSClaudiu Beznea 
261d7484f5cSClaudiu Beznea 	/* Loop through defined wakeup sources. */
262a958156dSClaudiu Beznea 	for_each_matching_node_and_match(np, soc_pm.ws_ids, &match) {
263d7484f5cSClaudiu Beznea 		pdev = of_find_device_by_node(np);
264d7484f5cSClaudiu Beznea 		if (!pdev)
265d7484f5cSClaudiu Beznea 			continue;
266d7484f5cSClaudiu Beznea 
267d7484f5cSClaudiu Beznea 		if (device_may_wakeup(&pdev->dev)) {
268d7484f5cSClaudiu Beznea 			wsi = match->data;
269d7484f5cSClaudiu Beznea 
270d7484f5cSClaudiu Beznea 			/* Check if enabled on SHDWC. */
271d7484f5cSClaudiu Beznea 			if (wsi->shdwc_mr_bit && !(val & wsi->shdwc_mr_bit))
27295590a62Szhong jiang 				goto put_device;
273d7484f5cSClaudiu Beznea 
274d7484f5cSClaudiu Beznea 			mode |= wsi->pmc_fsmr_bit;
275d7484f5cSClaudiu Beznea 			if (wsi->set_polarity)
276d7484f5cSClaudiu Beznea 				polarity |= wsi->pmc_fsmr_bit;
277d7484f5cSClaudiu Beznea 		}
278d7484f5cSClaudiu Beznea 
27995590a62Szhong jiang put_device:
28095590a62Szhong jiang 		put_device(&pdev->dev);
281d7484f5cSClaudiu Beznea 	}
282d7484f5cSClaudiu Beznea 
283d7484f5cSClaudiu Beznea 	if (mode) {
284a958156dSClaudiu Beznea 		if (soc_pm.config_pmc_ws)
285a958156dSClaudiu Beznea 			soc_pm.config_pmc_ws(soc_pm.data.pmc, mode, polarity);
286d7484f5cSClaudiu Beznea 	} else {
287d7484f5cSClaudiu Beznea 		pr_err("AT91: PM: no ULP1 wakeup sources found!");
288d7484f5cSClaudiu Beznea 	}
289d7484f5cSClaudiu Beznea 
290d7484f5cSClaudiu Beznea 	return mode ? 0 : -EPERM;
291d7484f5cSClaudiu Beznea }
292d7484f5cSClaudiu Beznea 
at91_sama5d2_config_shdwc_ws(void __iomem * shdwc,u32 * mode,u32 * polarity)293a958156dSClaudiu Beznea static int at91_sama5d2_config_shdwc_ws(void __iomem *shdwc, u32 *mode,
294a958156dSClaudiu Beznea 					u32 *polarity)
295a958156dSClaudiu Beznea {
296a958156dSClaudiu Beznea 	u32 val;
297a958156dSClaudiu Beznea 
298a958156dSClaudiu Beznea 	/* SHDWC.WUIR */
299a958156dSClaudiu Beznea 	val = readl(shdwc + 0x0c);
300a958156dSClaudiu Beznea 	*mode |= (val & 0x3ff);
301a958156dSClaudiu Beznea 	*polarity |= ((val >> 16) & 0x3ff);
302a958156dSClaudiu Beznea 
303a958156dSClaudiu Beznea 	return 0;
304a958156dSClaudiu Beznea }
305a958156dSClaudiu Beznea 
at91_sama5d2_config_pmc_ws(void __iomem * pmc,u32 mode,u32 polarity)306a958156dSClaudiu Beznea static int at91_sama5d2_config_pmc_ws(void __iomem *pmc, u32 mode, u32 polarity)
307a958156dSClaudiu Beznea {
308a958156dSClaudiu Beznea 	writel(mode, pmc + AT91_PMC_FSMR);
309a958156dSClaudiu Beznea 	writel(polarity, pmc + AT91_PMC_FSPR);
310a958156dSClaudiu Beznea 
311a958156dSClaudiu Beznea 	return 0;
312a958156dSClaudiu Beznea }
313a958156dSClaudiu Beznea 
at91_sam9x60_config_pmc_ws(void __iomem * pmc,u32 mode,u32 polarity)314eaedc0d3SClaudiu Beznea static int at91_sam9x60_config_pmc_ws(void __iomem *pmc, u32 mode, u32 polarity)
315eaedc0d3SClaudiu Beznea {
316eaedc0d3SClaudiu Beznea 	writel(mode, pmc + AT91_PMC_FSMR);
317eaedc0d3SClaudiu Beznea 
318eaedc0d3SClaudiu Beznea 	return 0;
319eaedc0d3SClaudiu Beznea }
320eaedc0d3SClaudiu Beznea 
at91_pm_eth_quirk_is_valid(struct at91_pm_quirk_eth * eth)321b7fc72c6SClaudiu Beznea static bool at91_pm_eth_quirk_is_valid(struct at91_pm_quirk_eth *eth)
322b7fc72c6SClaudiu Beznea {
323b7fc72c6SClaudiu Beznea 	struct platform_device *pdev;
324b7fc72c6SClaudiu Beznea 
325b7fc72c6SClaudiu Beznea 	/* Interface NA in DT. */
326b7fc72c6SClaudiu Beznea 	if (!eth->np)
327b7fc72c6SClaudiu Beznea 		return false;
328b7fc72c6SClaudiu Beznea 
329b7fc72c6SClaudiu Beznea 	/* No quirks for this interface and current suspend mode. */
330b7fc72c6SClaudiu Beznea 	if (!(eth->modes & BIT(soc_pm.data.mode)))
331b7fc72c6SClaudiu Beznea 		return false;
332b7fc72c6SClaudiu Beznea 
333b7fc72c6SClaudiu Beznea 	if (!eth->dev) {
334b7fc72c6SClaudiu Beznea 		/* Driver not probed. */
335b7fc72c6SClaudiu Beznea 		pdev = of_find_device_by_node(eth->np);
336b7fc72c6SClaudiu Beznea 		if (!pdev)
337b7fc72c6SClaudiu Beznea 			return false;
338ccd4923dSClaudiu Beznea 		/* put_device(eth->dev) is called at the end of suspend. */
339b7fc72c6SClaudiu Beznea 		eth->dev = &pdev->dev;
340b7fc72c6SClaudiu Beznea 	}
341b7fc72c6SClaudiu Beznea 
342b7fc72c6SClaudiu Beznea 	/* No quirks if device isn't a wakeup source. */
343ccd4923dSClaudiu Beznea 	if (!device_may_wakeup(eth->dev))
344b7fc72c6SClaudiu Beznea 		return false;
345b7fc72c6SClaudiu Beznea 
346b7fc72c6SClaudiu Beznea 	return true;
347b7fc72c6SClaudiu Beznea }
348b7fc72c6SClaudiu Beznea 
at91_pm_config_quirks(bool suspend)349b7fc72c6SClaudiu Beznea static int at91_pm_config_quirks(bool suspend)
350b7fc72c6SClaudiu Beznea {
351b7fc72c6SClaudiu Beznea 	struct at91_pm_quirk_eth *eth;
352b7fc72c6SClaudiu Beznea 	int i, j, ret, tmp;
353b7fc72c6SClaudiu Beznea 
354b7fc72c6SClaudiu Beznea 	/*
355b7fc72c6SClaudiu Beznea 	 * Ethernet IPs who's device_node pointers are stored into
356b7fc72c6SClaudiu Beznea 	 * soc_pm.quirks.eth[].np cannot handle WoL packets while in ULP0, ULP1
357b7fc72c6SClaudiu Beznea 	 * or both due to a hardware bug. If they receive WoL packets while in
358b7fc72c6SClaudiu Beznea 	 * ULP0 or ULP1 IPs could stop working or the whole system could stop
359b7fc72c6SClaudiu Beznea 	 * working. We cannot handle this scenario in the ethernet driver itself
360b7fc72c6SClaudiu Beznea 	 * as the driver is common to multiple vendors and also we only know
361b7fc72c6SClaudiu Beznea 	 * here, in this file, if we suspend to ULP0 or ULP1 mode. Thus handle
362b7fc72c6SClaudiu Beznea 	 * these scenarios here, as quirks.
363b7fc72c6SClaudiu Beznea 	 */
364b7fc72c6SClaudiu Beznea 	for (i = 0; i < AT91_PM_MAX_ETH; i++) {
365b7fc72c6SClaudiu Beznea 		eth = &soc_pm.quirks.eth[i];
366b7fc72c6SClaudiu Beznea 
367b7fc72c6SClaudiu Beznea 		if (!at91_pm_eth_quirk_is_valid(eth))
368b7fc72c6SClaudiu Beznea 			continue;
369b7fc72c6SClaudiu Beznea 
370b7fc72c6SClaudiu Beznea 		/*
371b7fc72c6SClaudiu Beznea 		 * For modes in dns_modes mask the system blocks if quirk is not
372b7fc72c6SClaudiu Beznea 		 * applied but if applied the interface doesn't act at WoL
373b7fc72c6SClaudiu Beznea 		 * events. Thus take care to avoid suspending if this interface
374b7fc72c6SClaudiu Beznea 		 * is the only configured wakeup source.
375b7fc72c6SClaudiu Beznea 		 */
376b7fc72c6SClaudiu Beznea 		if (suspend && eth->dns_modes & BIT(soc_pm.data.mode)) {
377b7fc72c6SClaudiu Beznea 			int ws_count = 0;
378b7fc72c6SClaudiu Beznea #ifdef CONFIG_PM_SLEEP
379b7fc72c6SClaudiu Beznea 			struct wakeup_source *ws;
380b7fc72c6SClaudiu Beznea 
381b7fc72c6SClaudiu Beznea 			for_each_wakeup_source(ws) {
382b7fc72c6SClaudiu Beznea 				if (ws->dev == eth->dev)
383b7fc72c6SClaudiu Beznea 					continue;
384b7fc72c6SClaudiu Beznea 
385b7fc72c6SClaudiu Beznea 				ws_count++;
386b7fc72c6SClaudiu Beznea 				break;
387b7fc72c6SClaudiu Beznea 			}
388b7fc72c6SClaudiu Beznea #endif
389b7fc72c6SClaudiu Beznea 
390b7fc72c6SClaudiu Beznea 			/*
391b7fc72c6SClaudiu Beznea 			 * Checking !ws is good for all platforms with issues
392b7fc72c6SClaudiu Beznea 			 * even when both G_ETH and E_ETH are available as dns_modes
393b7fc72c6SClaudiu Beznea 			 * is populated only on G_ETH interface.
394b7fc72c6SClaudiu Beznea 			 */
395b7fc72c6SClaudiu Beznea 			if (!ws_count) {
396b7fc72c6SClaudiu Beznea 				pr_err("AT91: PM: Ethernet cannot resume from WoL!");
397b7fc72c6SClaudiu Beznea 				ret = -EPERM;
398b7fc72c6SClaudiu Beznea 				put_device(eth->dev);
399b7fc72c6SClaudiu Beznea 				eth->dev = NULL;
400b7fc72c6SClaudiu Beznea 				/* No need to revert clock settings for this eth. */
401b7fc72c6SClaudiu Beznea 				i--;
402b7fc72c6SClaudiu Beznea 				goto clk_unconfigure;
403b7fc72c6SClaudiu Beznea 			}
404b7fc72c6SClaudiu Beznea 		}
405b7fc72c6SClaudiu Beznea 
406b7fc72c6SClaudiu Beznea 		if (suspend) {
407b7fc72c6SClaudiu Beznea 			clk_bulk_disable_unprepare(AT91_PM_ETH_MAX_CLK, eth->clks);
408b7fc72c6SClaudiu Beznea 		} else {
409b7fc72c6SClaudiu Beznea 			ret = clk_bulk_prepare_enable(AT91_PM_ETH_MAX_CLK,
410b7fc72c6SClaudiu Beznea 						      eth->clks);
411b7fc72c6SClaudiu Beznea 			if (ret)
412b7fc72c6SClaudiu Beznea 				goto clk_unconfigure;
413b7fc72c6SClaudiu Beznea 			/*
414b7fc72c6SClaudiu Beznea 			 * Release the reference to eth->dev taken in
415b7fc72c6SClaudiu Beznea 			 * at91_pm_eth_quirk_is_valid().
416b7fc72c6SClaudiu Beznea 			 */
417b7fc72c6SClaudiu Beznea 			put_device(eth->dev);
418b7fc72c6SClaudiu Beznea 			eth->dev = NULL;
419b7fc72c6SClaudiu Beznea 		}
420b7fc72c6SClaudiu Beznea 	}
421b7fc72c6SClaudiu Beznea 
422b7fc72c6SClaudiu Beznea 	return 0;
423b7fc72c6SClaudiu Beznea 
424b7fc72c6SClaudiu Beznea clk_unconfigure:
425b7fc72c6SClaudiu Beznea 	/*
426b7fc72c6SClaudiu Beznea 	 * In case of resume we reach this point if clk_prepare_enable() failed.
427b7fc72c6SClaudiu Beznea 	 * we don't want to revert the previous clk_prepare_enable() for the
428b7fc72c6SClaudiu Beznea 	 * other IP.
429b7fc72c6SClaudiu Beznea 	 */
430b7fc72c6SClaudiu Beznea 	for (j = i; j >= 0; j--) {
431b7fc72c6SClaudiu Beznea 		eth = &soc_pm.quirks.eth[j];
432b7fc72c6SClaudiu Beznea 		if (suspend) {
433b7fc72c6SClaudiu Beznea 			if (!at91_pm_eth_quirk_is_valid(eth))
434b7fc72c6SClaudiu Beznea 				continue;
435b7fc72c6SClaudiu Beznea 
436b7fc72c6SClaudiu Beznea 			tmp = clk_bulk_prepare_enable(AT91_PM_ETH_MAX_CLK, eth->clks);
437b7fc72c6SClaudiu Beznea 			if (tmp) {
438b7fc72c6SClaudiu Beznea 				pr_err("AT91: PM: failed to enable %s clocks\n",
439b7fc72c6SClaudiu Beznea 				       j == AT91_PM_G_ETH ? "geth" : "eth");
440b7fc72c6SClaudiu Beznea 			}
441ccd4923dSClaudiu Beznea 		}
442ccd4923dSClaudiu Beznea 
443b7fc72c6SClaudiu Beznea 		/*
444b7fc72c6SClaudiu Beznea 		 * Release the reference to eth->dev taken in
445b7fc72c6SClaudiu Beznea 		 * at91_pm_eth_quirk_is_valid().
446b7fc72c6SClaudiu Beznea 		 */
447b7fc72c6SClaudiu Beznea 		put_device(eth->dev);
448b7fc72c6SClaudiu Beznea 		eth->dev = NULL;
449b7fc72c6SClaudiu Beznea 	}
450b7fc72c6SClaudiu Beznea 
451b7fc72c6SClaudiu Beznea 	return ret;
452b7fc72c6SClaudiu Beznea }
453b7fc72c6SClaudiu Beznea 
4549d041268SAndrew Victor /*
4559d041268SAndrew Victor  * Called after processes are frozen, but before we shutdown devices.
4569d041268SAndrew Victor  */
at91_pm_begin(suspend_state_t state)457c697eeceSRafael J. Wysocki static int at91_pm_begin(suspend_state_t state)
4589d041268SAndrew Victor {
459fe4c09e5SClaudiu Beznea 	int ret;
460fe4c09e5SClaudiu Beznea 
4617693e18eSAlexandre Belloni 	switch (state) {
4627693e18eSAlexandre Belloni 	case PM_SUSPEND_MEM:
463c3f5b8fdSClaudiu Beznea 		soc_pm.data.mode = soc_pm.data.suspend_mode;
4647693e18eSAlexandre Belloni 		break;
4657693e18eSAlexandre Belloni 
4667693e18eSAlexandre Belloni 	case PM_SUSPEND_STANDBY:
467c3f5b8fdSClaudiu Beznea 		soc_pm.data.mode = soc_pm.data.standby_mode;
4687693e18eSAlexandre Belloni 		break;
4697693e18eSAlexandre Belloni 
4707693e18eSAlexandre Belloni 	default:
471c3f5b8fdSClaudiu Beznea 		soc_pm.data.mode = -1;
4727693e18eSAlexandre Belloni 	}
4737693e18eSAlexandre Belloni 
474fe4c09e5SClaudiu Beznea 	ret = at91_pm_config_ws(soc_pm.data.mode, true);
475fe4c09e5SClaudiu Beznea 	if (ret)
476fe4c09e5SClaudiu Beznea 		return ret;
477fe4c09e5SClaudiu Beznea 
478fe4c09e5SClaudiu Beznea 	if (soc_pm.data.mode == AT91_PM_BACKUP)
479fe4c09e5SClaudiu Beznea 		soc_pm.bu->suspended = 1;
480fe4c09e5SClaudiu Beznea 	else if (soc_pm.bu)
481fe4c09e5SClaudiu Beznea 		soc_pm.bu->suspended = 0;
482fe4c09e5SClaudiu Beznea 
483fe4c09e5SClaudiu Beznea 	return 0;
4849d041268SAndrew Victor }
4859d041268SAndrew Victor 
4869d041268SAndrew Victor /*
4879d041268SAndrew Victor  * Verify that all the clocks are correct before entering
4889d041268SAndrew Victor  * slow-clock mode.
4899d041268SAndrew Victor  */
at91_pm_verify_clocks(void)4909d041268SAndrew Victor static int at91_pm_verify_clocks(void)
4919d041268SAndrew Victor {
4929d041268SAndrew Victor 	unsigned long scsr;
4939d041268SAndrew Victor 	int i;
4949d041268SAndrew Victor 
495c3f5b8fdSClaudiu Beznea 	scsr = readl(soc_pm.data.pmc + AT91_PMC_SCSR);
4969d041268SAndrew Victor 
4979d041268SAndrew Victor 	/* USB must not be using PLLB */
498c3f5b8fdSClaudiu Beznea 	if ((scsr & soc_pm.data.uhp_udp_mask) != 0) {
4997f96b1caSRyan Mallon 		pr_err("AT91: PM - Suspend-to-RAM with USB still active\n");
5009d041268SAndrew Victor 		return 0;
5019d041268SAndrew Victor 	}
5029d041268SAndrew Victor 
5039d041268SAndrew Victor 	/* PCK0..PCK3 must be disabled, or configured to use clk32k */
5049d041268SAndrew Victor 	for (i = 0; i < 4; i++) {
5059d041268SAndrew Victor 		u32 css;
5069d041268SAndrew Victor 
5079d041268SAndrew Victor 		if ((scsr & (AT91_PMC_PCK0 << i)) == 0)
5089d041268SAndrew Victor 			continue;
509c3f5b8fdSClaudiu Beznea 		css = readl(soc_pm.data.pmc + AT91_PMC_PCKR(i)) & AT91_PMC_CSS;
5109d041268SAndrew Victor 		if (css != AT91_PMC_CSS_SLOW) {
5117f96b1caSRyan Mallon 			pr_err("AT91: PM - Suspend-to-RAM with PCK%d src %d\n", i, css);
5129d041268SAndrew Victor 			return 0;
5139d041268SAndrew Victor 		}
5149d041268SAndrew Victor 	}
5159d041268SAndrew Victor 
5169d041268SAndrew Victor 	return 1;
5179d041268SAndrew Victor }
5189d041268SAndrew Victor 
5199d041268SAndrew Victor /*
5209d041268SAndrew Victor  * Call this from platform driver suspend() to see how deeply to suspend.
5219d041268SAndrew Victor  * For example, some controllers (like OHCI) need one of the PLL clocks
5229d041268SAndrew Victor  * in order to act as a wakeup source, and those are not available when
5239d041268SAndrew Victor  * going into slow clock mode.
5249d041268SAndrew Victor  *
5259d041268SAndrew Victor  * REVISIT: generalize as clk_will_be_available(clk)?  Other platforms have
5269d041268SAndrew Victor  * the very same problem (but not using at91 main_clk), and it'd be better
5279d041268SAndrew Victor  * to add one generic API rather than lots of platform-specific ones.
5289d041268SAndrew Victor  */
at91_suspend_entering_slow_clock(void)5299d041268SAndrew Victor int at91_suspend_entering_slow_clock(void)
5309d041268SAndrew Victor {
531c3f5b8fdSClaudiu Beznea 	return (soc_pm.data.mode >= AT91_PM_ULP0);
5329d041268SAndrew Victor }
5339d041268SAndrew Victor EXPORT_SYMBOL(at91_suspend_entering_slow_clock);
5349d041268SAndrew Victor 
53565cc1a59SAlexandre Belloni static void (*at91_suspend_sram_fn)(struct at91_pm_data *);
53665cc1a59SAlexandre Belloni extern void at91_pm_suspend_in_sram(struct at91_pm_data *pm_data);
5375726a8b9SWenyou Yang extern u32 at91_pm_suspend_in_sram_sz;
538f5d0f457SAndrew Victor 
at91_suspend_finish(unsigned long val)53924a0f5c5SAlexandre Belloni static int at91_suspend_finish(unsigned long val)
54023be4be5SWenyou Yang {
5417a94b83aSClaudiu Beznea 	unsigned char modified_gray_code[] = {
5427a94b83aSClaudiu Beznea 		0x00, 0x01, 0x02, 0x03, 0x06, 0x07, 0x04, 0x05, 0x0c, 0x0d,
5437a94b83aSClaudiu Beznea 		0x0e, 0x0f, 0x0a, 0x0b, 0x08, 0x09, 0x18, 0x19, 0x1a, 0x1b,
5447a94b83aSClaudiu Beznea 		0x1e, 0x1f, 0x1c, 0x1d, 0x14, 0x15, 0x16, 0x17, 0x12, 0x13,
5457a94b83aSClaudiu Beznea 		0x10, 0x11,
5467a94b83aSClaudiu Beznea 	};
5477a94b83aSClaudiu Beznea 	unsigned int tmp, index;
548d2d4716dSClaudiu Beznea 	int i;
549d2d4716dSClaudiu Beznea 
550d2d4716dSClaudiu Beznea 	if (soc_pm.data.mode == AT91_PM_BACKUP && soc_pm.data.ramc_phy) {
551d2d4716dSClaudiu Beznea 		/*
5527a94b83aSClaudiu Beznea 		 * Bootloader will perform DDR recalibration and will try to
5537a94b83aSClaudiu Beznea 		 * restore the ZQ0SR0 with the value saved here. But the
5547a94b83aSClaudiu Beznea 		 * calibration is buggy and restoring some values from ZQ0SR0
5557a94b83aSClaudiu Beznea 		 * is forbidden and risky thus we need to provide processed
5567a94b83aSClaudiu Beznea 		 * values for these (modified gray code values).
5577a94b83aSClaudiu Beznea 		 */
5587a94b83aSClaudiu Beznea 		tmp = readl(soc_pm.data.ramc_phy + DDR3PHY_ZQ0SR0);
5597a94b83aSClaudiu Beznea 
5607a94b83aSClaudiu Beznea 		/* Store pull-down output impedance select. */
5617a94b83aSClaudiu Beznea 		index = (tmp >> DDR3PHY_ZQ0SR0_PDO_OFF) & 0x1f;
5627a94b83aSClaudiu Beznea 		soc_pm.bu->ddr_phy_calibration[0] = modified_gray_code[index];
5637a94b83aSClaudiu Beznea 
5647a94b83aSClaudiu Beznea 		/* Store pull-up output impedance select. */
5657a94b83aSClaudiu Beznea 		index = (tmp >> DDR3PHY_ZQ0SR0_PUO_OFF) & 0x1f;
5667a94b83aSClaudiu Beznea 		soc_pm.bu->ddr_phy_calibration[0] |= modified_gray_code[index];
5677a94b83aSClaudiu Beznea 
5687a94b83aSClaudiu Beznea 		/* Store pull-down on-die termination impedance select. */
5697a94b83aSClaudiu Beznea 		index = (tmp >> DDR3PHY_ZQ0SR0_PDODT_OFF) & 0x1f;
5707a94b83aSClaudiu Beznea 		soc_pm.bu->ddr_phy_calibration[0] |= modified_gray_code[index];
5717a94b83aSClaudiu Beznea 
5727a94b83aSClaudiu Beznea 		/* Store pull-up on-die termination impedance select. */
5737a94b83aSClaudiu Beznea 		index = (tmp >> DDR3PHY_ZQ0SRO_PUODT_OFF) & 0x1f;
5747a94b83aSClaudiu Beznea 		soc_pm.bu->ddr_phy_calibration[0] |= modified_gray_code[index];
5757a94b83aSClaudiu Beznea 
5767a94b83aSClaudiu Beznea 		/*
577d2d4716dSClaudiu Beznea 		 * The 1st 8 words of memory might get corrupted in the process
578d2d4716dSClaudiu Beznea 		 * of DDR PHY recalibration; it is saved here in securam and it
579d2d4716dSClaudiu Beznea 		 * will be restored later, after recalibration, by bootloader
580d2d4716dSClaudiu Beznea 		 */
581d2d4716dSClaudiu Beznea 		for (i = 1; i < BACKUP_DDR_PHY_CALIBRATION; i++)
582d2d4716dSClaudiu Beznea 			soc_pm.bu->ddr_phy_calibration[i] =
583d2d4716dSClaudiu Beznea 				*((unsigned int *)soc_pm.memcs + (i - 1));
584d2d4716dSClaudiu Beznea 	}
585d2d4716dSClaudiu Beznea 
586385acc0dSWenyou Yang 	flush_cache_all();
587385acc0dSWenyou Yang 	outer_disable();
588385acc0dSWenyou Yang 
589c3f5b8fdSClaudiu Beznea 	at91_suspend_sram_fn(&soc_pm.data);
590385acc0dSWenyou Yang 
59124a0f5c5SAlexandre Belloni 	return 0;
59224a0f5c5SAlexandre Belloni }
59324a0f5c5SAlexandre Belloni 
594*4aa4a004SNicolas Ferre /**
595*4aa4a004SNicolas Ferre  * at91_pm_switch_ba_to_auto() - Configure Backup Unit Power Switch
596*4aa4a004SNicolas Ferre  * to automatic/hardware mode.
597*4aa4a004SNicolas Ferre  *
598*4aa4a004SNicolas Ferre  * The Backup Unit Power Switch can be managed either by software or hardware.
599*4aa4a004SNicolas Ferre  * Enabling hardware mode allows the automatic transition of power between
600*4aa4a004SNicolas Ferre  * VDDANA (or VDDIN33) and VDDBU (or VBAT, respectively), based on the
601*4aa4a004SNicolas Ferre  * availability of these power sources.
602*4aa4a004SNicolas Ferre  *
603*4aa4a004SNicolas Ferre  * If the Backup Unit Power Switch is already in automatic mode, no action is
604*4aa4a004SNicolas Ferre  * required. If it is in software-controlled mode, it is switched to automatic
605*4aa4a004SNicolas Ferre  * mode to enhance safety and eliminate the need for toggling between power
606*4aa4a004SNicolas Ferre  * sources.
607*4aa4a004SNicolas Ferre  */
at91_pm_switch_ba_to_auto(void)608*4aa4a004SNicolas Ferre static void at91_pm_switch_ba_to_auto(void)
609ac809e78SClaudiu Beznea {
610ac809e78SClaudiu Beznea 	unsigned int offset = offsetof(struct at91_pm_sfrbu_regs, pswbu);
611ac809e78SClaudiu Beznea 	unsigned int val;
612ac809e78SClaudiu Beznea 
613ac809e78SClaudiu Beznea 	/* Just for safety. */
614ac809e78SClaudiu Beznea 	if (!soc_pm.data.sfrbu)
615ac809e78SClaudiu Beznea 		return;
616ac809e78SClaudiu Beznea 
617ac809e78SClaudiu Beznea 	val = readl(soc_pm.data.sfrbu + offset);
618ac809e78SClaudiu Beznea 
619*4aa4a004SNicolas Ferre 	/* Already on auto/hardware. */
620*4aa4a004SNicolas Ferre 	if (!(val & soc_pm.sfrbu_regs.pswbu.ctrl))
621ac809e78SClaudiu Beznea 		return;
622ac809e78SClaudiu Beznea 
623*4aa4a004SNicolas Ferre 	val &= ~soc_pm.sfrbu_regs.pswbu.ctrl;
624*4aa4a004SNicolas Ferre 	val |= soc_pm.sfrbu_regs.pswbu.key;
625ac809e78SClaudiu Beznea 	writel(val, soc_pm.data.sfrbu + offset);
626ac809e78SClaudiu Beznea }
627ac809e78SClaudiu Beznea 
at91_pm_suspend(suspend_state_t state)62824a0f5c5SAlexandre Belloni static void at91_pm_suspend(suspend_state_t state)
62924a0f5c5SAlexandre Belloni {
630c3f5b8fdSClaudiu Beznea 	if (soc_pm.data.mode == AT91_PM_BACKUP) {
631*4aa4a004SNicolas Ferre 		at91_pm_switch_ba_to_auto();
632ac809e78SClaudiu Beznea 
63324a0f5c5SAlexandre Belloni 		cpu_suspend(0, at91_suspend_finish);
63424a0f5c5SAlexandre Belloni 
63524a0f5c5SAlexandre Belloni 		/* The SRAM is lost between suspend cycles */
63624a0f5c5SAlexandre Belloni 		at91_suspend_sram_fn = fncpy(at91_suspend_sram_fn,
63724a0f5c5SAlexandre Belloni 					     &at91_pm_suspend_in_sram,
63824a0f5c5SAlexandre Belloni 					     at91_pm_suspend_in_sram_sz);
63924a0f5c5SAlexandre Belloni 	} else {
64024a0f5c5SAlexandre Belloni 		at91_suspend_finish(0);
64124a0f5c5SAlexandre Belloni 	}
64224a0f5c5SAlexandre Belloni 
643385acc0dSWenyou Yang 	outer_resume();
64423be4be5SWenyou Yang }
64523be4be5SWenyou Yang 
6467693e18eSAlexandre Belloni /*
6477693e18eSAlexandre Belloni  * STANDBY mode has *all* drivers suspended; ignores irqs not marked as 'wakeup'
6487693e18eSAlexandre Belloni  * event sources; and reduces DRAM power.  But otherwise it's identical to
6497693e18eSAlexandre Belloni  * PM_SUSPEND_ON: cpu idle, and nothing fancy done with main or cpu clocks.
6507693e18eSAlexandre Belloni  *
651514e2a29SClaudiu Beznea  * AT91_PM_ULP0 is like STANDBY plus slow clock mode, so drivers must
6527693e18eSAlexandre Belloni  * suspend more deeply, the master clock switches to the clk32k and turns off
6537693e18eSAlexandre Belloni  * the main oscillator
6547693e18eSAlexandre Belloni  *
6557693e18eSAlexandre Belloni  * AT91_PM_BACKUP turns off the whole SoC after placing the DDR in self refresh
6567693e18eSAlexandre Belloni  */
at91_pm_enter(suspend_state_t state)6579d041268SAndrew Victor static int at91_pm_enter(suspend_state_t state)
6589d041268SAndrew Victor {
659b7fc72c6SClaudiu Beznea 	int ret;
660b7fc72c6SClaudiu Beznea 
661b7fc72c6SClaudiu Beznea 	ret = at91_pm_config_quirks(true);
662b7fc72c6SClaudiu Beznea 	if (ret)
663b7fc72c6SClaudiu Beznea 		return ret;
664b7fc72c6SClaudiu Beznea 
6659d041268SAndrew Victor 	switch (state) {
6669d041268SAndrew Victor 	case PM_SUSPEND_MEM:
6677693e18eSAlexandre Belloni 	case PM_SUSPEND_STANDBY:
6689d041268SAndrew Victor 		/*
6699d041268SAndrew Victor 		 * Ensure that clocks are in a valid state.
6709d041268SAndrew Victor 		 */
671c3f5b8fdSClaudiu Beznea 		if (soc_pm.data.mode >= AT91_PM_ULP0 &&
6727693e18eSAlexandre Belloni 		    !at91_pm_verify_clocks())
6739d041268SAndrew Victor 			goto error;
6749d041268SAndrew Victor 
67523be4be5SWenyou Yang 		at91_pm_suspend(state);
67623be4be5SWenyou Yang 
6779d041268SAndrew Victor 		break;
6789d041268SAndrew Victor 
6799d041268SAndrew Victor 	case PM_SUSPEND_ON:
6808aeeda82SNicolas Ferre 		cpu_do_idle();
6819d041268SAndrew Victor 		break;
6829d041268SAndrew Victor 
6839d041268SAndrew Victor 	default:
6849d041268SAndrew Victor 		pr_debug("AT91: PM - bogus suspend state %d\n", state);
6859d041268SAndrew Victor 		goto error;
6869d041268SAndrew Victor 	}
6879d041268SAndrew Victor 
6889d041268SAndrew Victor error:
689b7fc72c6SClaudiu Beznea 	at91_pm_config_quirks(false);
6909d041268SAndrew Victor 	return 0;
6919d041268SAndrew Victor }
6929d041268SAndrew Victor 
693c697eeceSRafael J. Wysocki /*
694c697eeceSRafael J. Wysocki  * Called right prior to thawing processes.
695c697eeceSRafael J. Wysocki  */
at91_pm_end(void)696c697eeceSRafael J. Wysocki static void at91_pm_end(void)
697c697eeceSRafael J. Wysocki {
698c3f5b8fdSClaudiu Beznea 	at91_pm_config_ws(soc_pm.data.mode, false);
699c697eeceSRafael J. Wysocki }
700c697eeceSRafael J. Wysocki 
7019d041268SAndrew Victor 
7022f55ac07SLionel Debroux static const struct platform_suspend_ops at91_pm_ops = {
7039d041268SAndrew Victor 	.valid	= at91_pm_valid_state,
704c697eeceSRafael J. Wysocki 	.begin	= at91_pm_begin,
7059d041268SAndrew Victor 	.enter	= at91_pm_enter,
706c697eeceSRafael J. Wysocki 	.end	= at91_pm_end,
7079d041268SAndrew Victor };
7089d041268SAndrew Victor 
7095ad945eaSDaniel Lezcano static struct platform_device at91_cpuidle_device = {
7105ad945eaSDaniel Lezcano 	.name = "cpuidle-at91",
7115ad945eaSDaniel Lezcano };
7125ad945eaSDaniel Lezcano 
713a18d0699SAlexandre Belloni /*
714a18d0699SAlexandre Belloni  * The AT91RM9200 goes into self-refresh mode with this command, and will
715a18d0699SAlexandre Belloni  * terminate self-refresh automatically on the next SDRAM access.
716a18d0699SAlexandre Belloni  *
717a18d0699SAlexandre Belloni  * Self-refresh mode is exited as soon as a memory access is made, but we don't
718a18d0699SAlexandre Belloni  * know for sure when that happens. However, we need to restore the low-power
719a18d0699SAlexandre Belloni  * mode if it was enabled before going idle. Restoring low-power mode while
720a18d0699SAlexandre Belloni  * still in self-refresh is "not recommended", but seems to work.
721a18d0699SAlexandre Belloni  */
at91rm9200_standby(void)722a18d0699SAlexandre Belloni static void at91rm9200_standby(void)
723a18d0699SAlexandre Belloni {
724a18d0699SAlexandre Belloni 	asm volatile(
725a18d0699SAlexandre Belloni 		"b    1f\n\t"
726a18d0699SAlexandre Belloni 		".align    5\n\t"
727a18d0699SAlexandre Belloni 		"1:  mcr    p15, 0, %0, c7, c10, 4\n\t"
7285a2d4f05SAlexandre Belloni 		"    str    %2, [%1, %3]\n\t"
729a18d0699SAlexandre Belloni 		"    mcr    p15, 0, %0, c7, c0, 4\n\t"
730a18d0699SAlexandre Belloni 		:
731c3f5b8fdSClaudiu Beznea 		: "r" (0), "r" (soc_pm.data.ramc[0]),
7325a2d4f05SAlexandre Belloni 		  "r" (1), "r" (AT91_MC_SDRAMC_SRR));
733a18d0699SAlexandre Belloni }
734a18d0699SAlexandre Belloni 
735a18d0699SAlexandre Belloni /* We manage both DDRAM/SDRAM controllers, we need more than one value to
736a18d0699SAlexandre Belloni  * remember.
737a18d0699SAlexandre Belloni  */
at91_ddr_standby(void)738a18d0699SAlexandre Belloni static void at91_ddr_standby(void)
739a18d0699SAlexandre Belloni {
740a18d0699SAlexandre Belloni 	/* Those two values allow us to delay self-refresh activation
741a18d0699SAlexandre Belloni 	 * to the maximum. */
742a18d0699SAlexandre Belloni 	u32 lpr0, lpr1 = 0;
74356387634SAlexandre Belloni 	u32 mdr, saved_mdr0, saved_mdr1 = 0;
744a18d0699SAlexandre Belloni 	u32 saved_lpr0, saved_lpr1 = 0;
745a18d0699SAlexandre Belloni 
74656387634SAlexandre Belloni 	/* LPDDR1 --> force DDR2 mode during self-refresh */
74756387634SAlexandre Belloni 	saved_mdr0 = at91_ramc_read(0, AT91_DDRSDRC_MDR);
74856387634SAlexandre Belloni 	if ((saved_mdr0 & AT91_DDRSDRC_MD) == AT91_DDRSDRC_MD_LOW_POWER_DDR) {
74956387634SAlexandre Belloni 		mdr = saved_mdr0 & ~AT91_DDRSDRC_MD;
75056387634SAlexandre Belloni 		mdr |= AT91_DDRSDRC_MD_DDR2;
75156387634SAlexandre Belloni 		at91_ramc_write(0, AT91_DDRSDRC_MDR, mdr);
75256387634SAlexandre Belloni 	}
75356387634SAlexandre Belloni 
754c3f5b8fdSClaudiu Beznea 	if (soc_pm.data.ramc[1]) {
755a18d0699SAlexandre Belloni 		saved_lpr1 = at91_ramc_read(1, AT91_DDRSDRC_LPR);
756a18d0699SAlexandre Belloni 		lpr1 = saved_lpr1 & ~AT91_DDRSDRC_LPCB;
757a18d0699SAlexandre Belloni 		lpr1 |= AT91_DDRSDRC_LPCB_SELF_REFRESH;
75856387634SAlexandre Belloni 		saved_mdr1 = at91_ramc_read(1, AT91_DDRSDRC_MDR);
75956387634SAlexandre Belloni 		if ((saved_mdr1 & AT91_DDRSDRC_MD) == AT91_DDRSDRC_MD_LOW_POWER_DDR) {
76056387634SAlexandre Belloni 			mdr = saved_mdr1 & ~AT91_DDRSDRC_MD;
76156387634SAlexandre Belloni 			mdr |= AT91_DDRSDRC_MD_DDR2;
76256387634SAlexandre Belloni 			at91_ramc_write(1, AT91_DDRSDRC_MDR, mdr);
76356387634SAlexandre Belloni 		}
764a18d0699SAlexandre Belloni 	}
765a18d0699SAlexandre Belloni 
766a18d0699SAlexandre Belloni 	saved_lpr0 = at91_ramc_read(0, AT91_DDRSDRC_LPR);
767a18d0699SAlexandre Belloni 	lpr0 = saved_lpr0 & ~AT91_DDRSDRC_LPCB;
768a18d0699SAlexandre Belloni 	lpr0 |= AT91_DDRSDRC_LPCB_SELF_REFRESH;
769a18d0699SAlexandre Belloni 
770a18d0699SAlexandre Belloni 	/* self-refresh mode now */
771a18d0699SAlexandre Belloni 	at91_ramc_write(0, AT91_DDRSDRC_LPR, lpr0);
772c3f5b8fdSClaudiu Beznea 	if (soc_pm.data.ramc[1])
773a18d0699SAlexandre Belloni 		at91_ramc_write(1, AT91_DDRSDRC_LPR, lpr1);
774a18d0699SAlexandre Belloni 
775a18d0699SAlexandre Belloni 	cpu_do_idle();
776a18d0699SAlexandre Belloni 
77756387634SAlexandre Belloni 	at91_ramc_write(0, AT91_DDRSDRC_MDR, saved_mdr0);
778a18d0699SAlexandre Belloni 	at91_ramc_write(0, AT91_DDRSDRC_LPR, saved_lpr0);
779c3f5b8fdSClaudiu Beznea 	if (soc_pm.data.ramc[1]) {
78056387634SAlexandre Belloni 		at91_ramc_write(0, AT91_DDRSDRC_MDR, saved_mdr1);
781a18d0699SAlexandre Belloni 		at91_ramc_write(1, AT91_DDRSDRC_LPR, saved_lpr1);
782a18d0699SAlexandre Belloni 	}
78356387634SAlexandre Belloni }
784a18d0699SAlexandre Belloni 
sama5d3_ddr_standby(void)78560b89f19SNicolas Ferre static void sama5d3_ddr_standby(void)
78660b89f19SNicolas Ferre {
78760b89f19SNicolas Ferre 	u32 lpr0;
78860b89f19SNicolas Ferre 	u32 saved_lpr0;
78960b89f19SNicolas Ferre 
79060b89f19SNicolas Ferre 	saved_lpr0 = at91_ramc_read(0, AT91_DDRSDRC_LPR);
79160b89f19SNicolas Ferre 	lpr0 = saved_lpr0 & ~AT91_DDRSDRC_LPCB;
79260b89f19SNicolas Ferre 	lpr0 |= AT91_DDRSDRC_LPCB_POWER_DOWN;
79360b89f19SNicolas Ferre 
79460b89f19SNicolas Ferre 	at91_ramc_write(0, AT91_DDRSDRC_LPR, lpr0);
79560b89f19SNicolas Ferre 
79660b89f19SNicolas Ferre 	cpu_do_idle();
79760b89f19SNicolas Ferre 
79860b89f19SNicolas Ferre 	at91_ramc_write(0, AT91_DDRSDRC_LPR, saved_lpr0);
79960b89f19SNicolas Ferre }
80060b89f19SNicolas Ferre 
801a18d0699SAlexandre Belloni /* We manage both DDRAM/SDRAM controllers, we need more than one value to
802a18d0699SAlexandre Belloni  * remember.
803a18d0699SAlexandre Belloni  */
at91sam9_sdram_standby(void)804a18d0699SAlexandre Belloni static void at91sam9_sdram_standby(void)
805a18d0699SAlexandre Belloni {
806a18d0699SAlexandre Belloni 	u32 lpr0, lpr1 = 0;
807a18d0699SAlexandre Belloni 	u32 saved_lpr0, saved_lpr1 = 0;
808a18d0699SAlexandre Belloni 
809c3f5b8fdSClaudiu Beznea 	if (soc_pm.data.ramc[1]) {
810a18d0699SAlexandre Belloni 		saved_lpr1 = at91_ramc_read(1, AT91_SDRAMC_LPR);
811a18d0699SAlexandre Belloni 		lpr1 = saved_lpr1 & ~AT91_SDRAMC_LPCB;
812a18d0699SAlexandre Belloni 		lpr1 |= AT91_SDRAMC_LPCB_SELF_REFRESH;
813a18d0699SAlexandre Belloni 	}
814a18d0699SAlexandre Belloni 
815a18d0699SAlexandre Belloni 	saved_lpr0 = at91_ramc_read(0, AT91_SDRAMC_LPR);
816a18d0699SAlexandre Belloni 	lpr0 = saved_lpr0 & ~AT91_SDRAMC_LPCB;
817a18d0699SAlexandre Belloni 	lpr0 |= AT91_SDRAMC_LPCB_SELF_REFRESH;
818a18d0699SAlexandre Belloni 
819a18d0699SAlexandre Belloni 	/* self-refresh mode now */
820a18d0699SAlexandre Belloni 	at91_ramc_write(0, AT91_SDRAMC_LPR, lpr0);
821c3f5b8fdSClaudiu Beznea 	if (soc_pm.data.ramc[1])
822a18d0699SAlexandre Belloni 		at91_ramc_write(1, AT91_SDRAMC_LPR, lpr1);
823a18d0699SAlexandre Belloni 
824a18d0699SAlexandre Belloni 	cpu_do_idle();
825a18d0699SAlexandre Belloni 
826a18d0699SAlexandre Belloni 	at91_ramc_write(0, AT91_SDRAMC_LPR, saved_lpr0);
827c3f5b8fdSClaudiu Beznea 	if (soc_pm.data.ramc[1])
828a18d0699SAlexandre Belloni 		at91_ramc_write(1, AT91_SDRAMC_LPR, saved_lpr1);
829a18d0699SAlexandre Belloni }
830a18d0699SAlexandre Belloni 
sama7g5_standby(void)8319584e726SClaudiu Beznea static void sama7g5_standby(void)
8329584e726SClaudiu Beznea {
8339584e726SClaudiu Beznea 	int pwrtmg, ratio;
8349584e726SClaudiu Beznea 
8359584e726SClaudiu Beznea 	pwrtmg = readl(soc_pm.data.ramc[0] + UDDRC_PWRCTL);
8369584e726SClaudiu Beznea 	ratio = readl(soc_pm.data.pmc + AT91_PMC_RATIO);
8379584e726SClaudiu Beznea 
8389584e726SClaudiu Beznea 	/*
8399584e726SClaudiu Beznea 	 * Place RAM into self-refresh after a maximum idle clocks. The maximum
8409584e726SClaudiu Beznea 	 * idle clocks is configured by bootloader in
8419584e726SClaudiu Beznea 	 * UDDRC_PWRMGT.SELFREF_TO_X32.
8429584e726SClaudiu Beznea 	 */
8439584e726SClaudiu Beznea 	writel(pwrtmg | UDDRC_PWRCTL_SELFREF_EN,
8449584e726SClaudiu Beznea 	       soc_pm.data.ramc[0] + UDDRC_PWRCTL);
8459584e726SClaudiu Beznea 	/* Divide CPU clock by 16. */
8469584e726SClaudiu Beznea 	writel(ratio & ~AT91_PMC_RATIO_RATIO, soc_pm.data.pmc + AT91_PMC_RATIO);
8479584e726SClaudiu Beznea 
8489584e726SClaudiu Beznea 	cpu_do_idle();
8499584e726SClaudiu Beznea 
8509584e726SClaudiu Beznea 	/* Restore previous configuration. */
8519584e726SClaudiu Beznea 	writel(ratio, soc_pm.data.pmc + AT91_PMC_RATIO);
8529584e726SClaudiu Beznea 	writel(pwrtmg, soc_pm.data.ramc[0] + UDDRC_PWRCTL);
8539584e726SClaudiu Beznea }
8549584e726SClaudiu Beznea 
855aab02d61SAlexandre Belloni struct ramc_info {
856aab02d61SAlexandre Belloni 	void (*idle)(void);
857aab02d61SAlexandre Belloni 	unsigned int memctrl;
858aab02d61SAlexandre Belloni };
859aab02d61SAlexandre Belloni 
860aab02d61SAlexandre Belloni static const struct ramc_info ramc_infos[] __initconst = {
861aab02d61SAlexandre Belloni 	{ .idle = at91rm9200_standby, .memctrl = AT91_MEMCTRL_MC},
862aab02d61SAlexandre Belloni 	{ .idle = at91sam9_sdram_standby, .memctrl = AT91_MEMCTRL_SDRAMC},
863aab02d61SAlexandre Belloni 	{ .idle = at91_ddr_standby, .memctrl = AT91_MEMCTRL_DDRSDR},
864aab02d61SAlexandre Belloni 	{ .idle = sama5d3_ddr_standby, .memctrl = AT91_MEMCTRL_DDRSDR},
8659584e726SClaudiu Beznea 	{ .idle = sama7g5_standby, },
866aab02d61SAlexandre Belloni };
867aab02d61SAlexandre Belloni 
8680527873bSArnd Bergmann static const struct of_device_id ramc_ids[] __initconst = {
869aab02d61SAlexandre Belloni 	{ .compatible = "atmel,at91rm9200-sdramc", .data = &ramc_infos[0] },
870aab02d61SAlexandre Belloni 	{ .compatible = "atmel,at91sam9260-sdramc", .data = &ramc_infos[1] },
871aab02d61SAlexandre Belloni 	{ .compatible = "atmel,at91sam9g45-ddramc", .data = &ramc_infos[2] },
872aab02d61SAlexandre Belloni 	{ .compatible = "atmel,sama5d3-ddramc", .data = &ramc_infos[3] },
8739584e726SClaudiu Beznea 	{ .compatible = "microchip,sama7g5-uddrc", .data = &ramc_infos[4], },
874827de1f1SAlexandre Belloni 	{ /*sentinel*/ }
875827de1f1SAlexandre Belloni };
876827de1f1SAlexandre Belloni 
877892e1f4aSClaudiu Beznea static const struct of_device_id ramc_phy_ids[] __initconst = {
878892e1f4aSClaudiu Beznea 	{ .compatible = "microchip,sama7g5-ddr3phy", },
879892e1f4aSClaudiu Beznea 	{ /* Sentinel. */ },
880892e1f4aSClaudiu Beznea };
881892e1f4aSClaudiu Beznea 
at91_dt_ramc(bool phy_mandatory)8821605de1bSClaudiu Beznea static __init int at91_dt_ramc(bool phy_mandatory)
883827de1f1SAlexandre Belloni {
884827de1f1SAlexandre Belloni 	struct device_node *np;
885827de1f1SAlexandre Belloni 	const struct of_device_id *of_id;
886827de1f1SAlexandre Belloni 	int idx = 0;
887e56d75a9SAlexandre Belloni 	void *standby = NULL;
888aab02d61SAlexandre Belloni 	const struct ramc_info *ramc;
8891605de1bSClaudiu Beznea 	int ret;
890827de1f1SAlexandre Belloni 
891827de1f1SAlexandre Belloni 	for_each_matching_node_and_match(np, ramc_ids, &of_id) {
892c3f5b8fdSClaudiu Beznea 		soc_pm.data.ramc[idx] = of_iomap(np, 0);
8931605de1bSClaudiu Beznea 		if (!soc_pm.data.ramc[idx]) {
8941605de1bSClaudiu Beznea 			pr_err("unable to map ramc[%d] cpu registers\n", idx);
8951605de1bSClaudiu Beznea 			ret = -ENOMEM;
896bb29e409SWan Jiabing 			of_node_put(np);
8971605de1bSClaudiu Beznea 			goto unmap_ramc;
8981605de1bSClaudiu Beznea 		}
899827de1f1SAlexandre Belloni 
900aab02d61SAlexandre Belloni 		ramc = of_id->data;
9012c26cb4dSClaudiu Beznea 		if (ramc) {
902827de1f1SAlexandre Belloni 			if (!standby)
903aab02d61SAlexandre Belloni 				standby = ramc->idle;
904c3f5b8fdSClaudiu Beznea 			soc_pm.data.memctrl = ramc->memctrl;
9052c26cb4dSClaudiu Beznea 		}
906827de1f1SAlexandre Belloni 
907827de1f1SAlexandre Belloni 		idx++;
908827de1f1SAlexandre Belloni 	}
909827de1f1SAlexandre Belloni 
9101605de1bSClaudiu Beznea 	if (!idx) {
9111605de1bSClaudiu Beznea 		pr_err("unable to find compatible ram controller node in dtb\n");
9121605de1bSClaudiu Beznea 		ret = -ENODEV;
9131605de1bSClaudiu Beznea 		goto unmap_ramc;
9141605de1bSClaudiu Beznea 	}
915827de1f1SAlexandre Belloni 
916892e1f4aSClaudiu Beznea 	/* Lookup for DDR PHY node, if any. */
917892e1f4aSClaudiu Beznea 	for_each_matching_node_and_match(np, ramc_phy_ids, &of_id) {
918892e1f4aSClaudiu Beznea 		soc_pm.data.ramc_phy = of_iomap(np, 0);
9191605de1bSClaudiu Beznea 		if (!soc_pm.data.ramc_phy) {
9201605de1bSClaudiu Beznea 			pr_err("unable to map ramc phy cpu registers\n");
9211605de1bSClaudiu Beznea 			ret = -ENOMEM;
922bb29e409SWan Jiabing 			of_node_put(np);
9231605de1bSClaudiu Beznea 			goto unmap_ramc;
9241605de1bSClaudiu Beznea 		}
925892e1f4aSClaudiu Beznea 	}
926892e1f4aSClaudiu Beznea 
9271605de1bSClaudiu Beznea 	if (phy_mandatory && !soc_pm.data.ramc_phy) {
9281605de1bSClaudiu Beznea 		pr_err("DDR PHY is mandatory!\n");
9291605de1bSClaudiu Beznea 		ret = -ENODEV;
9301605de1bSClaudiu Beznea 		goto unmap_ramc;
9311605de1bSClaudiu Beznea 	}
932892e1f4aSClaudiu Beznea 
933827de1f1SAlexandre Belloni 	if (!standby) {
934827de1f1SAlexandre Belloni 		pr_warn("ramc no standby function available\n");
9351605de1bSClaudiu Beznea 		return 0;
936827de1f1SAlexandre Belloni 	}
937827de1f1SAlexandre Belloni 
938e56d75a9SAlexandre Belloni 	at91_cpuidle_device.dev.platform_data = standby;
9391605de1bSClaudiu Beznea 
9401605de1bSClaudiu Beznea 	return 0;
9411605de1bSClaudiu Beznea 
9421605de1bSClaudiu Beznea unmap_ramc:
9431605de1bSClaudiu Beznea 	while (idx)
9441605de1bSClaudiu Beznea 		iounmap(soc_pm.data.ramc[--idx]);
9451605de1bSClaudiu Beznea 
9461605de1bSClaudiu Beznea 	return ret;
947827de1f1SAlexandre Belloni }
948827de1f1SAlexandre Belloni 
at91rm9200_idle(void)949ab6778eeSBen Dooks static void at91rm9200_idle(void)
950fbc7edcaSAlexandre Belloni {
951fbc7edcaSAlexandre Belloni 	/*
952fbc7edcaSAlexandre Belloni 	 * Disable the processor clock.  The processor will be automatically
953fbc7edcaSAlexandre Belloni 	 * re-enabled by an interrupt or by a reset.
954fbc7edcaSAlexandre Belloni 	 */
955c3f5b8fdSClaudiu Beznea 	writel(AT91_PMC_PCK, soc_pm.data.pmc + AT91_PMC_SCDR);
956fbc7edcaSAlexandre Belloni }
957fbc7edcaSAlexandre Belloni 
at91sam9_idle(void)958ab6778eeSBen Dooks static void at91sam9_idle(void)
959fbc7edcaSAlexandre Belloni {
960c3f5b8fdSClaudiu Beznea 	writel(AT91_PMC_PCK, soc_pm.data.pmc + AT91_PMC_SCDR);
961fbc7edcaSAlexandre Belloni 	cpu_do_idle();
962fbc7edcaSAlexandre Belloni }
963fbc7edcaSAlexandre Belloni 
at91_pm_sram_init(void)964d2e46790SAlexandre Belloni static void __init at91_pm_sram_init(void)
965d2e46790SAlexandre Belloni {
966d2e46790SAlexandre Belloni 	struct gen_pool *sram_pool;
967d2e46790SAlexandre Belloni 	phys_addr_t sram_pbase;
968d2e46790SAlexandre Belloni 	unsigned long sram_base;
969d2e46790SAlexandre Belloni 	struct device_node *node;
9704a031f7dSAlexandre Belloni 	struct platform_device *pdev = NULL;
971d2e46790SAlexandre Belloni 
9724a031f7dSAlexandre Belloni 	for_each_compatible_node(node, NULL, "mmio-sram") {
9734a031f7dSAlexandre Belloni 		pdev = of_find_device_by_node(node);
9744a031f7dSAlexandre Belloni 		if (pdev) {
9754a031f7dSAlexandre Belloni 			of_node_put(node);
9764a031f7dSAlexandre Belloni 			break;
9774a031f7dSAlexandre Belloni 		}
978d2e46790SAlexandre Belloni 	}
979d2e46790SAlexandre Belloni 
980d2e46790SAlexandre Belloni 	if (!pdev) {
981d2e46790SAlexandre Belloni 		pr_warn("%s: failed to find sram device!\n", __func__);
9824a031f7dSAlexandre Belloni 		return;
983d2e46790SAlexandre Belloni 	}
984d2e46790SAlexandre Belloni 
98573858173SVladimir Zapolskiy 	sram_pool = gen_pool_get(&pdev->dev, NULL);
986d2e46790SAlexandre Belloni 	if (!sram_pool) {
987d2e46790SAlexandre Belloni 		pr_warn("%s: sram pool unavailable!\n", __func__);
988f87a4f02Syu kuai 		goto out_put_device;
989d2e46790SAlexandre Belloni 	}
990d2e46790SAlexandre Belloni 
9915726a8b9SWenyou Yang 	sram_base = gen_pool_alloc(sram_pool, at91_pm_suspend_in_sram_sz);
992d2e46790SAlexandre Belloni 	if (!sram_base) {
9935726a8b9SWenyou Yang 		pr_warn("%s: unable to alloc sram!\n", __func__);
994f87a4f02Syu kuai 		goto out_put_device;
995d2e46790SAlexandre Belloni 	}
996d2e46790SAlexandre Belloni 
997d2e46790SAlexandre Belloni 	sram_pbase = gen_pool_virt_to_phys(sram_pool, sram_base);
9985726a8b9SWenyou Yang 	at91_suspend_sram_fn = __arm_ioremap_exec(sram_pbase,
9995726a8b9SWenyou Yang 					at91_pm_suspend_in_sram_sz, false);
10005726a8b9SWenyou Yang 	if (!at91_suspend_sram_fn) {
1001d94e688cSWenyou Yang 		pr_warn("SRAM: Could not map\n");
1002f87a4f02Syu kuai 		goto out_put_device;
1003d94e688cSWenyou Yang 	}
1004d94e688cSWenyou Yang 
10055726a8b9SWenyou Yang 	/* Copy the pm suspend handler to SRAM */
10065726a8b9SWenyou Yang 	at91_suspend_sram_fn = fncpy(at91_suspend_sram_fn,
10075726a8b9SWenyou Yang 			&at91_pm_suspend_in_sram, at91_pm_suspend_in_sram_sz);
1008f87a4f02Syu kuai 	return;
1009f87a4f02Syu kuai 
1010f87a4f02Syu kuai out_put_device:
1011f87a4f02Syu kuai 	put_device(&pdev->dev);
1012f87a4f02Syu kuai 	return;
1013d2e46790SAlexandre Belloni }
1014d2e46790SAlexandre Belloni 
at91_is_pm_mode_active(int pm_mode)1015d7484f5cSClaudiu Beznea static bool __init at91_is_pm_mode_active(int pm_mode)
1016d7484f5cSClaudiu Beznea {
1017c3f5b8fdSClaudiu Beznea 	return (soc_pm.data.standby_mode == pm_mode ||
1018c3f5b8fdSClaudiu Beznea 		soc_pm.data.suspend_mode == pm_mode);
1019d7484f5cSClaudiu Beznea }
1020d7484f5cSClaudiu Beznea 
at91_pm_backup_scan_memcs(unsigned long node,const char * uname,int depth,void * data)1021d2d4716dSClaudiu Beznea static int __init at91_pm_backup_scan_memcs(unsigned long node,
1022d2d4716dSClaudiu Beznea 					    const char *uname, int depth,
1023d2d4716dSClaudiu Beznea 					    void *data)
1024d2d4716dSClaudiu Beznea {
1025d2d4716dSClaudiu Beznea 	const char *type;
1026d2d4716dSClaudiu Beznea 	const __be32 *reg;
1027d2d4716dSClaudiu Beznea 	int *located = data;
1028d2d4716dSClaudiu Beznea 	int size;
1029d2d4716dSClaudiu Beznea 
1030d2d4716dSClaudiu Beznea 	/* Memory node already located. */
1031d2d4716dSClaudiu Beznea 	if (*located)
1032d2d4716dSClaudiu Beznea 		return 0;
1033d2d4716dSClaudiu Beznea 
1034d2d4716dSClaudiu Beznea 	type = of_get_flat_dt_prop(node, "device_type", NULL);
1035d2d4716dSClaudiu Beznea 
1036d2d4716dSClaudiu Beznea 	/* We are scanning "memory" nodes only. */
1037d2d4716dSClaudiu Beznea 	if (!type || strcmp(type, "memory"))
1038d2d4716dSClaudiu Beznea 		return 0;
1039d2d4716dSClaudiu Beznea 
1040d2d4716dSClaudiu Beznea 	reg = of_get_flat_dt_prop(node, "reg", &size);
1041d2d4716dSClaudiu Beznea 	if (reg) {
1042d2d4716dSClaudiu Beznea 		soc_pm.memcs = __va((phys_addr_t)be32_to_cpu(*reg));
1043d2d4716dSClaudiu Beznea 		*located = 1;
1044d2d4716dSClaudiu Beznea 	}
1045d2d4716dSClaudiu Beznea 
1046d2d4716dSClaudiu Beznea 	return 0;
1047d2d4716dSClaudiu Beznea }
1048d2d4716dSClaudiu Beznea 
at91_pm_backup_init(void)1049d7484f5cSClaudiu Beznea static int __init at91_pm_backup_init(void)
105024a0f5c5SAlexandre Belloni {
105124a0f5c5SAlexandre Belloni 	struct gen_pool *sram_pool;
105224a0f5c5SAlexandre Belloni 	struct device_node *np;
1053629ba8eeSClaudiu Beznea 	struct platform_device *pdev;
1054d2d4716dSClaudiu Beznea 	int ret = -ENODEV, located = 0;
105524a0f5c5SAlexandre Belloni 
1056f205adb6SClaudiu Beznea 	if (!IS_ENABLED(CONFIG_SOC_SAMA5D2) &&
1057f205adb6SClaudiu Beznea 	    !IS_ENABLED(CONFIG_SOC_SAMA7G5))
10582fa86e52SClaudiu Beznea 		return -EPERM;
10592fa86e52SClaudiu Beznea 
1060d7484f5cSClaudiu Beznea 	if (!at91_is_pm_mode_active(AT91_PM_BACKUP))
1061d7484f5cSClaudiu Beznea 		return 0;
10627693e18eSAlexandre Belloni 
106324a0f5c5SAlexandre Belloni 	np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-securam");
106424a0f5c5SAlexandre Belloni 	if (!np)
1065404956f4SClaudiu Beznea 		return ret;
106624a0f5c5SAlexandre Belloni 
106724a0f5c5SAlexandre Belloni 	pdev = of_find_device_by_node(np);
106824a0f5c5SAlexandre Belloni 	of_node_put(np);
106924a0f5c5SAlexandre Belloni 	if (!pdev) {
107024a0f5c5SAlexandre Belloni 		pr_warn("%s: failed to find securam device!\n", __func__);
1071404956f4SClaudiu Beznea 		return ret;
107224a0f5c5SAlexandre Belloni 	}
107324a0f5c5SAlexandre Belloni 
107424a0f5c5SAlexandre Belloni 	sram_pool = gen_pool_get(&pdev->dev, NULL);
107524a0f5c5SAlexandre Belloni 	if (!sram_pool) {
107624a0f5c5SAlexandre Belloni 		pr_warn("%s: securam pool unavailable!\n", __func__);
107724a0f5c5SAlexandre Belloni 		goto securam_fail;
107824a0f5c5SAlexandre Belloni 	}
107924a0f5c5SAlexandre Belloni 
1080f19dd1dfSClaudiu Beznea 	soc_pm.bu = (void *)gen_pool_alloc(sram_pool, sizeof(struct at91_pm_bu));
1081f19dd1dfSClaudiu Beznea 	if (!soc_pm.bu) {
108224a0f5c5SAlexandre Belloni 		pr_warn("%s: unable to alloc securam!\n", __func__);
1083d7484f5cSClaudiu Beznea 		ret = -ENOMEM;
108424a0f5c5SAlexandre Belloni 		goto securam_fail;
108524a0f5c5SAlexandre Belloni 	}
108624a0f5c5SAlexandre Belloni 
1087f19dd1dfSClaudiu Beznea 	soc_pm.bu->suspended = 0;
1088f19dd1dfSClaudiu Beznea 	soc_pm.bu->canary = __pa_symbol(&canary);
1089f19dd1dfSClaudiu Beznea 	soc_pm.bu->resume = __pa_symbol(cpu_resume);
1090d2d4716dSClaudiu Beznea 	if (soc_pm.data.ramc_phy) {
1091d2d4716dSClaudiu Beznea 		of_scan_flat_dt(at91_pm_backup_scan_memcs, &located);
1092d2d4716dSClaudiu Beznea 		if (!located)
1093d2d4716dSClaudiu Beznea 			goto securam_fail;
1094d2d4716dSClaudiu Beznea 	}
109524a0f5c5SAlexandre Belloni 
1096d7484f5cSClaudiu Beznea 	return 0;
109724a0f5c5SAlexandre Belloni 
109824a0f5c5SAlexandre Belloni securam_fail:
1099ba5e60c9SPeng Hao 	put_device(&pdev->dev);
1100d7484f5cSClaudiu Beznea 	return ret;
1101d7484f5cSClaudiu Beznea }
110228732238SAlexandre Belloni 
at91_pm_secure_init(void)110391d60e25SFabio Estevam static void __init at91_pm_secure_init(void)
1104f2f5cf78SClément Léger {
1105f2f5cf78SClément Léger 	int suspend_mode;
1106f2f5cf78SClément Léger 	struct arm_smccc_res res;
1107f2f5cf78SClément Léger 
1108f2f5cf78SClément Léger 	suspend_mode = soc_pm.data.suspend_mode;
1109f2f5cf78SClément Léger 
1110f2f5cf78SClément Léger 	res = sam_smccc_call(SAMA5_SMC_SIP_SET_SUSPEND_MODE,
1111f2f5cf78SClément Léger 			     suspend_mode, 0);
1112f2f5cf78SClément Léger 	if (res.a0 == 0) {
1113f2f5cf78SClément Léger 		pr_info("AT91: Secure PM: suspend mode set to %s\n",
1114f2f5cf78SClément Léger 			pm_modes[suspend_mode].pattern);
1115f2f5cf78SClément Léger 		return;
1116f2f5cf78SClément Léger 	}
1117f2f5cf78SClément Léger 
1118f2f5cf78SClément Léger 	pr_warn("AT91: Secure PM: %s mode not supported !\n",
1119f2f5cf78SClément Léger 		pm_modes[suspend_mode].pattern);
1120f2f5cf78SClément Léger 
1121f2f5cf78SClément Léger 	res = sam_smccc_call(SAMA5_SMC_SIP_GET_SUSPEND_MODE, 0, 0);
1122f2f5cf78SClément Léger 	if (res.a0 == 0) {
1123f2f5cf78SClément Léger 		pr_warn("AT91: Secure PM: failed to get default mode\n");
1124f2f5cf78SClément Léger 		return;
1125f2f5cf78SClément Léger 	}
1126f2f5cf78SClément Léger 
1127f2f5cf78SClément Léger 	pr_info("AT91: Secure PM: using default suspend mode %s\n",
1128f2f5cf78SClément Léger 		pm_modes[suspend_mode].pattern);
1129f2f5cf78SClément Léger 
1130f2f5cf78SClément Léger 	soc_pm.data.suspend_mode = res.a1;
1131f2f5cf78SClément Léger }
1132ec6e618cSClaudiu Beznea static const struct of_device_id atmel_shdwc_ids[] = {
1133ec6e618cSClaudiu Beznea 	{ .compatible = "atmel,sama5d2-shdwc" },
1134ec6e618cSClaudiu Beznea 	{ .compatible = "microchip,sam9x60-shdwc" },
1135ad9bc2e3SClaudiu Beznea 	{ .compatible = "microchip,sama7g5-shdwc" },
1136ec6e618cSClaudiu Beznea 	{ /* sentinel. */ }
1137ec6e618cSClaudiu Beznea };
1138ec6e618cSClaudiu Beznea 
1139b7fc72c6SClaudiu Beznea static const struct of_device_id gmac_ids[] __initconst = {
1140b7fc72c6SClaudiu Beznea 	{ .compatible = "atmel,sama5d3-gem" },
1141b7fc72c6SClaudiu Beznea 	{ .compatible = "atmel,sama5d2-gem" },
1142b7fc72c6SClaudiu Beznea 	{ .compatible = "atmel,sama5d29-gem" },
1143b7fc72c6SClaudiu Beznea 	{ .compatible = "microchip,sama7g5-gem" },
1144b7fc72c6SClaudiu Beznea 	{ },
1145b7fc72c6SClaudiu Beznea };
1146b7fc72c6SClaudiu Beznea 
1147b7fc72c6SClaudiu Beznea static const struct of_device_id emac_ids[] __initconst = {
1148b7fc72c6SClaudiu Beznea 	{ .compatible = "atmel,sama5d3-macb" },
1149b7fc72c6SClaudiu Beznea 	{ .compatible = "microchip,sama7g5-emac" },
1150b7fc72c6SClaudiu Beznea 	{ },
1151b7fc72c6SClaudiu Beznea };
1152b7fc72c6SClaudiu Beznea 
1153b568c71dSClaudiu Beznea /*
1154b568c71dSClaudiu Beznea  * Replaces _mode_to_replace with a supported mode that doesn't depend
1155b568c71dSClaudiu Beznea  * on controller pointed by _map_bitmask
1156b568c71dSClaudiu Beznea  * @_maps: u32 array containing AT91_PM_IOMAP() flags and indexed by AT91
1157b568c71dSClaudiu Beznea  * PM mode
1158b568c71dSClaudiu Beznea  * @_map_bitmask: AT91_PM_IOMAP() bitmask; if _mode_to_replace depends on
1159b568c71dSClaudiu Beznea  * controller represented by _map_bitmask, _mode_to_replace needs to be
1160b568c71dSClaudiu Beznea  * updated
1161b568c71dSClaudiu Beznea  * @_mode_to_replace: standby_mode or suspend_mode that need to be
1162b568c71dSClaudiu Beznea  * updated
1163b568c71dSClaudiu Beznea  * @_mode_to_check: standby_mode or suspend_mode; this is needed here
1164b568c71dSClaudiu Beznea  * to avoid having standby_mode and suspend_mode set with the same AT91
1165b568c71dSClaudiu Beznea  * PM mode
1166b568c71dSClaudiu Beznea  */
1167b568c71dSClaudiu Beznea #define AT91_PM_REPLACE_MODE(_maps, _map_bitmask, _mode_to_replace,	\
1168b568c71dSClaudiu Beznea 			     _mode_to_check)				\
1169b568c71dSClaudiu Beznea 	do {								\
1170b568c71dSClaudiu Beznea 		if (((_maps)[(_mode_to_replace)]) & (_map_bitmask)) {	\
1171b568c71dSClaudiu Beznea 			int _mode_to_use, _mode_complementary;		\
1172b568c71dSClaudiu Beznea 			/* Use ULP0 if it doesn't need _map_bitmask. */	\
1173b568c71dSClaudiu Beznea 			if (!((_maps)[AT91_PM_ULP0] & (_map_bitmask))) {\
1174b568c71dSClaudiu Beznea 				_mode_to_use = AT91_PM_ULP0;		\
1175b568c71dSClaudiu Beznea 				_mode_complementary = AT91_PM_STANDBY;	\
1176b568c71dSClaudiu Beznea 			} else {					\
1177b568c71dSClaudiu Beznea 				_mode_to_use = AT91_PM_STANDBY;		\
1178b568c71dSClaudiu Beznea 				_mode_complementary = AT91_PM_STANDBY;	\
1179b568c71dSClaudiu Beznea 			}						\
1180b568c71dSClaudiu Beznea 									\
1181b568c71dSClaudiu Beznea 			if ((_mode_to_check) != _mode_to_use)		\
1182b568c71dSClaudiu Beznea 				(_mode_to_replace) = _mode_to_use;	\
1183b568c71dSClaudiu Beznea 			else						\
1184b568c71dSClaudiu Beznea 				(_mode_to_replace) = _mode_complementary;\
1185b568c71dSClaudiu Beznea 		}							\
1186b568c71dSClaudiu Beznea 	} while (0)
1187b568c71dSClaudiu Beznea 
1188b568c71dSClaudiu Beznea /*
1189b568c71dSClaudiu Beznea  * Replaces standby and suspend modes with default supported modes:
1190b568c71dSClaudiu Beznea  * ULP0 and STANDBY.
1191b568c71dSClaudiu Beznea  * @_maps: u32 array indexed by AT91 PM mode containing AT91_PM_IOMAP()
1192b568c71dSClaudiu Beznea  * flags
1193b568c71dSClaudiu Beznea  * @_map: controller specific name; standby and suspend mode need to be
1194b568c71dSClaudiu Beznea  * replaced in order to not depend on this controller
1195b568c71dSClaudiu Beznea  */
1196b568c71dSClaudiu Beznea #define AT91_PM_REPLACE_MODES(_maps, _map)				\
1197b568c71dSClaudiu Beznea 	do {								\
1198b568c71dSClaudiu Beznea 		AT91_PM_REPLACE_MODE((_maps), BIT(AT91_PM_IOMAP_##_map),\
1199b568c71dSClaudiu Beznea 				     (soc_pm.data.standby_mode),	\
1200b568c71dSClaudiu Beznea 				     (soc_pm.data.suspend_mode));	\
1201b568c71dSClaudiu Beznea 		AT91_PM_REPLACE_MODE((_maps), BIT(AT91_PM_IOMAP_##_map),\
1202b568c71dSClaudiu Beznea 				     (soc_pm.data.suspend_mode),	\
1203b568c71dSClaudiu Beznea 				     (soc_pm.data.standby_mode));	\
1204b568c71dSClaudiu Beznea 	} while (0)
1205b568c71dSClaudiu Beznea 
at91_pm_get_eth_clks(struct device_node * np,struct clk_bulk_data * clks)1206b7fc72c6SClaudiu Beznea static int __init at91_pm_get_eth_clks(struct device_node *np,
1207b7fc72c6SClaudiu Beznea 				       struct clk_bulk_data *clks)
1208b7fc72c6SClaudiu Beznea {
1209b7fc72c6SClaudiu Beznea 	clks[AT91_PM_ETH_PCLK].clk = of_clk_get_by_name(np, "pclk");
1210b7fc72c6SClaudiu Beznea 	if (IS_ERR(clks[AT91_PM_ETH_PCLK].clk))
1211b7fc72c6SClaudiu Beznea 		return PTR_ERR(clks[AT91_PM_ETH_PCLK].clk);
1212b7fc72c6SClaudiu Beznea 
1213b7fc72c6SClaudiu Beznea 	clks[AT91_PM_ETH_HCLK].clk = of_clk_get_by_name(np, "hclk");
1214b7fc72c6SClaudiu Beznea 	if (IS_ERR(clks[AT91_PM_ETH_HCLK].clk))
1215b7fc72c6SClaudiu Beznea 		return PTR_ERR(clks[AT91_PM_ETH_HCLK].clk);
1216b7fc72c6SClaudiu Beznea 
1217b7fc72c6SClaudiu Beznea 	return 0;
1218b7fc72c6SClaudiu Beznea }
1219b7fc72c6SClaudiu Beznea 
at91_pm_eth_clks_empty(struct clk_bulk_data * clks)1220b7fc72c6SClaudiu Beznea static int __init at91_pm_eth_clks_empty(struct clk_bulk_data *clks)
1221b7fc72c6SClaudiu Beznea {
1222b7fc72c6SClaudiu Beznea 	return IS_ERR(clks[AT91_PM_ETH_PCLK].clk) ||
1223b7fc72c6SClaudiu Beznea 	       IS_ERR(clks[AT91_PM_ETH_HCLK].clk);
1224b7fc72c6SClaudiu Beznea }
1225b7fc72c6SClaudiu Beznea 
at91_pm_modes_init(const u32 * maps,int len)1226404956f4SClaudiu Beznea static void __init at91_pm_modes_init(const u32 *maps, int len)
1227d7484f5cSClaudiu Beznea {
1228b7fc72c6SClaudiu Beznea 	struct at91_pm_quirk_eth *gmac = &soc_pm.quirks.eth[AT91_PM_G_ETH];
1229b7fc72c6SClaudiu Beznea 	struct at91_pm_quirk_eth *emac = &soc_pm.quirks.eth[AT91_PM_E_ETH];
1230d7484f5cSClaudiu Beznea 	struct device_node *np;
1231b568c71dSClaudiu Beznea 	int ret;
1232d7484f5cSClaudiu Beznea 
1233d7484f5cSClaudiu Beznea 	ret = at91_pm_backup_init();
1234d7484f5cSClaudiu Beznea 	if (ret) {
1235404956f4SClaudiu Beznea 		if (soc_pm.data.standby_mode == AT91_PM_BACKUP)
1236404956f4SClaudiu Beznea 			soc_pm.data.standby_mode = AT91_PM_ULP0;
1237404956f4SClaudiu Beznea 		if (soc_pm.data.suspend_mode == AT91_PM_BACKUP)
1238404956f4SClaudiu Beznea 			soc_pm.data.suspend_mode = AT91_PM_ULP0;
1239404956f4SClaudiu Beznea 	}
1240404956f4SClaudiu Beznea 
1241404956f4SClaudiu Beznea 	if (maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SHDWC) ||
1242404956f4SClaudiu Beznea 	    maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SHDWC)) {
1243404956f4SClaudiu Beznea 		np = of_find_matching_node(NULL, atmel_shdwc_ids);
1244404956f4SClaudiu Beznea 		if (!np) {
1245404956f4SClaudiu Beznea 			pr_warn("%s: failed to find shdwc!\n", __func__);
1246b568c71dSClaudiu Beznea 			AT91_PM_REPLACE_MODES(maps, SHDWC);
1247404956f4SClaudiu Beznea 		} else {
1248404956f4SClaudiu Beznea 			soc_pm.data.shdwc = of_iomap(np, 0);
1249404956f4SClaudiu Beznea 			of_node_put(np);
1250404956f4SClaudiu Beznea 		}
1251404956f4SClaudiu Beznea 	}
1252404956f4SClaudiu Beznea 
1253404956f4SClaudiu Beznea 	if (maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SFRBU) ||
1254404956f4SClaudiu Beznea 	    maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SFRBU)) {
1255404956f4SClaudiu Beznea 		np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-sfrbu");
1256404956f4SClaudiu Beznea 		if (!np) {
1257404956f4SClaudiu Beznea 			pr_warn("%s: failed to find sfrbu!\n", __func__);
1258b568c71dSClaudiu Beznea 			AT91_PM_REPLACE_MODES(maps, SFRBU);
1259404956f4SClaudiu Beznea 		} else {
1260404956f4SClaudiu Beznea 			soc_pm.data.sfrbu = of_iomap(np, 0);
1261404956f4SClaudiu Beznea 			of_node_put(np);
1262404956f4SClaudiu Beznea 		}
1263404956f4SClaudiu Beznea 	}
1264404956f4SClaudiu Beznea 
1265b7fc72c6SClaudiu Beznea 	if ((at91_is_pm_mode_active(AT91_PM_ULP1) ||
1266b7fc72c6SClaudiu Beznea 	     at91_is_pm_mode_active(AT91_PM_ULP0) ||
1267b7fc72c6SClaudiu Beznea 	     at91_is_pm_mode_active(AT91_PM_ULP0_FAST)) &&
1268b7fc72c6SClaudiu Beznea 	    (maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(ETHC) ||
1269b7fc72c6SClaudiu Beznea 	     maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(ETHC))) {
1270b7fc72c6SClaudiu Beznea 		np = of_find_matching_node(NULL, gmac_ids);
1271b7fc72c6SClaudiu Beznea 		if (!np) {
1272b7fc72c6SClaudiu Beznea 			np = of_find_matching_node(NULL, emac_ids);
1273b7fc72c6SClaudiu Beznea 			if (np)
1274b7fc72c6SClaudiu Beznea 				goto get_emac_clks;
1275b7fc72c6SClaudiu Beznea 			AT91_PM_REPLACE_MODES(maps, ETHC);
1276b7fc72c6SClaudiu Beznea 			goto unmap_unused_nodes;
1277b7fc72c6SClaudiu Beznea 		} else {
1278b7fc72c6SClaudiu Beznea 			gmac->np = np;
1279b7fc72c6SClaudiu Beznea 			at91_pm_get_eth_clks(np, gmac->clks);
1280b7fc72c6SClaudiu Beznea 		}
1281b7fc72c6SClaudiu Beznea 
1282b7fc72c6SClaudiu Beznea 		np = of_find_matching_node(NULL, emac_ids);
1283b7fc72c6SClaudiu Beznea 		if (!np) {
1284b7fc72c6SClaudiu Beznea 			if (at91_pm_eth_clks_empty(gmac->clks))
1285b7fc72c6SClaudiu Beznea 				AT91_PM_REPLACE_MODES(maps, ETHC);
1286b7fc72c6SClaudiu Beznea 		} else {
1287b7fc72c6SClaudiu Beznea get_emac_clks:
1288b7fc72c6SClaudiu Beznea 			emac->np = np;
1289b7fc72c6SClaudiu Beznea 			ret = at91_pm_get_eth_clks(np, emac->clks);
1290b7fc72c6SClaudiu Beznea 			if (ret && at91_pm_eth_clks_empty(gmac->clks)) {
1291b7fc72c6SClaudiu Beznea 				of_node_put(gmac->np);
1292b7fc72c6SClaudiu Beznea 				of_node_put(emac->np);
1293b7fc72c6SClaudiu Beznea 				gmac->np = NULL;
1294b7fc72c6SClaudiu Beznea 				emac->np = NULL;
1295b7fc72c6SClaudiu Beznea 			}
1296b7fc72c6SClaudiu Beznea 		}
1297b7fc72c6SClaudiu Beznea 	}
1298b7fc72c6SClaudiu Beznea 
1299b7fc72c6SClaudiu Beznea unmap_unused_nodes:
1300404956f4SClaudiu Beznea 	/* Unmap all unnecessary. */
1301404956f4SClaudiu Beznea 	if (soc_pm.data.shdwc &&
1302404956f4SClaudiu Beznea 	    !(maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SHDWC) ||
1303404956f4SClaudiu Beznea 	      maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SHDWC))) {
1304404956f4SClaudiu Beznea 		iounmap(soc_pm.data.shdwc);
1305404956f4SClaudiu Beznea 		soc_pm.data.shdwc = NULL;
1306404956f4SClaudiu Beznea 	}
1307404956f4SClaudiu Beznea 
1308404956f4SClaudiu Beznea 	if (soc_pm.data.sfrbu &&
1309404956f4SClaudiu Beznea 	    !(maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SFRBU) ||
1310404956f4SClaudiu Beznea 	      maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SFRBU))) {
1311404956f4SClaudiu Beznea 		iounmap(soc_pm.data.sfrbu);
1312404956f4SClaudiu Beznea 		soc_pm.data.sfrbu = NULL;
1313d7484f5cSClaudiu Beznea 	}
1314d7484f5cSClaudiu Beznea 
1315d7484f5cSClaudiu Beznea 	return;
1316d7484f5cSClaudiu Beznea }
1317d7484f5cSClaudiu Beznea 
131813f16017SAlexandre Belloni struct pmc_info {
131913f16017SAlexandre Belloni 	unsigned long uhp_udp_mask;
13206ec1587bSClaudiu Beznea 	unsigned long mckr;
13210be298a9SClaudiu Beznea 	unsigned long version;
132213f16017SAlexandre Belloni };
132313f16017SAlexandre Belloni 
132413f16017SAlexandre Belloni static const struct pmc_info pmc_infos[] __initconst = {
13256ec1587bSClaudiu Beznea 	{
13266ec1587bSClaudiu Beznea 		.uhp_udp_mask = AT91RM9200_PMC_UHP | AT91RM9200_PMC_UDP,
13276ec1587bSClaudiu Beznea 		.mckr = 0x30,
13280be298a9SClaudiu Beznea 		.version = AT91_PMC_V1,
13296ec1587bSClaudiu Beznea 	},
13306ec1587bSClaudiu Beznea 
13316ec1587bSClaudiu Beznea 	{
13326ec1587bSClaudiu Beznea 		.uhp_udp_mask = AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP,
13336ec1587bSClaudiu Beznea 		.mckr = 0x30,
13340be298a9SClaudiu Beznea 		.version = AT91_PMC_V1,
13356ec1587bSClaudiu Beznea 	},
13366ec1587bSClaudiu Beznea 	{
13376ec1587bSClaudiu Beznea 		.uhp_udp_mask = AT91SAM926x_PMC_UHP,
13386ec1587bSClaudiu Beznea 		.mckr = 0x30,
13390be298a9SClaudiu Beznea 		.version = AT91_PMC_V1,
13406ec1587bSClaudiu Beznea 	},
13416ec1587bSClaudiu Beznea 	{	.uhp_udp_mask = 0,
13426ec1587bSClaudiu Beznea 		.mckr = 0x30,
13430be298a9SClaudiu Beznea 		.version = AT91_PMC_V1,
13446ec1587bSClaudiu Beznea 	},
13456ec1587bSClaudiu Beznea 	{
13466ec1587bSClaudiu Beznea 		.uhp_udp_mask = AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP,
13476ec1587bSClaudiu Beznea 		.mckr = 0x28,
13480be298a9SClaudiu Beznea 		.version = AT91_PMC_V2,
13496ec1587bSClaudiu Beznea 	},
1350ccdbdf33SClaudiu Beznea 	{
1351ccdbdf33SClaudiu Beznea 		.mckr = 0x28,
1352ccdbdf33SClaudiu Beznea 		.version = AT91_PMC_V2,
1353ccdbdf33SClaudiu Beznea 	},
1354ccdbdf33SClaudiu Beznea 
135513f16017SAlexandre Belloni };
135613f16017SAlexandre Belloni 
13575737b73eSAlexandre Belloni static const struct of_device_id atmel_pmc_ids[] __initconst = {
135813f16017SAlexandre Belloni 	{ .compatible = "atmel,at91rm9200-pmc", .data = &pmc_infos[0] },
135913f16017SAlexandre Belloni 	{ .compatible = "atmel,at91sam9260-pmc", .data = &pmc_infos[1] },
136091f87180SAlexandre Belloni 	{ .compatible = "atmel,at91sam9261-pmc", .data = &pmc_infos[1] },
136191f87180SAlexandre Belloni 	{ .compatible = "atmel,at91sam9263-pmc", .data = &pmc_infos[1] },
136213f16017SAlexandre Belloni 	{ .compatible = "atmel,at91sam9g45-pmc", .data = &pmc_infos[2] },
136313f16017SAlexandre Belloni 	{ .compatible = "atmel,at91sam9n12-pmc", .data = &pmc_infos[1] },
136491f87180SAlexandre Belloni 	{ .compatible = "atmel,at91sam9rl-pmc", .data = &pmc_infos[3] },
136513f16017SAlexandre Belloni 	{ .compatible = "atmel,at91sam9x5-pmc", .data = &pmc_infos[1] },
136613f16017SAlexandre Belloni 	{ .compatible = "atmel,sama5d3-pmc", .data = &pmc_infos[1] },
136791f87180SAlexandre Belloni 	{ .compatible = "atmel,sama5d4-pmc", .data = &pmc_infos[1] },
136813f16017SAlexandre Belloni 	{ .compatible = "atmel,sama5d2-pmc", .data = &pmc_infos[1] },
13696ec1587bSClaudiu Beznea 	{ .compatible = "microchip,sam9x60-pmc", .data = &pmc_infos[4] },
1370ccdbdf33SClaudiu Beznea 	{ .compatible = "microchip,sama7g5-pmc", .data = &pmc_infos[5] },
13715737b73eSAlexandre Belloni 	{ /* sentinel */ },
13725737b73eSAlexandre Belloni };
13735737b73eSAlexandre Belloni 
at91_pm_modes_validate(const int * modes,int len)137439add360SClaudiu Beznea static void __init at91_pm_modes_validate(const int *modes, int len)
137539add360SClaudiu Beznea {
137639add360SClaudiu Beznea 	u8 i, standby = 0, suspend = 0;
137739add360SClaudiu Beznea 	int mode;
137839add360SClaudiu Beznea 
137939add360SClaudiu Beznea 	for (i = 0; i < len; i++) {
138039add360SClaudiu Beznea 		if (standby && suspend)
138139add360SClaudiu Beznea 			break;
138239add360SClaudiu Beznea 
138339add360SClaudiu Beznea 		if (modes[i] == soc_pm.data.standby_mode && !standby) {
138439add360SClaudiu Beznea 			standby = 1;
138539add360SClaudiu Beznea 			continue;
138639add360SClaudiu Beznea 		}
138739add360SClaudiu Beznea 
138839add360SClaudiu Beznea 		if (modes[i] == soc_pm.data.suspend_mode && !suspend) {
138939add360SClaudiu Beznea 			suspend = 1;
139039add360SClaudiu Beznea 			continue;
139139add360SClaudiu Beznea 		}
139239add360SClaudiu Beznea 	}
139339add360SClaudiu Beznea 
139439add360SClaudiu Beznea 	if (!standby) {
139539add360SClaudiu Beznea 		if (soc_pm.data.suspend_mode == AT91_PM_STANDBY)
139639add360SClaudiu Beznea 			mode = AT91_PM_ULP0;
139739add360SClaudiu Beznea 		else
139839add360SClaudiu Beznea 			mode = AT91_PM_STANDBY;
139939add360SClaudiu Beznea 
140039add360SClaudiu Beznea 		pr_warn("AT91: PM: %s mode not supported! Using %s.\n",
140139add360SClaudiu Beznea 			pm_modes[soc_pm.data.standby_mode].pattern,
140239add360SClaudiu Beznea 			pm_modes[mode].pattern);
140339add360SClaudiu Beznea 		soc_pm.data.standby_mode = mode;
140439add360SClaudiu Beznea 	}
140539add360SClaudiu Beznea 
140639add360SClaudiu Beznea 	if (!suspend) {
140739add360SClaudiu Beznea 		if (soc_pm.data.standby_mode == AT91_PM_ULP0)
140839add360SClaudiu Beznea 			mode = AT91_PM_STANDBY;
140939add360SClaudiu Beznea 		else
141039add360SClaudiu Beznea 			mode = AT91_PM_ULP0;
141139add360SClaudiu Beznea 
141239add360SClaudiu Beznea 		pr_warn("AT91: PM: %s mode not supported! Using %s.\n",
141339add360SClaudiu Beznea 			pm_modes[soc_pm.data.suspend_mode].pattern,
141439add360SClaudiu Beznea 			pm_modes[mode].pattern);
141539add360SClaudiu Beznea 		soc_pm.data.suspend_mode = mode;
141639add360SClaudiu Beznea 	}
141739add360SClaudiu Beznea }
141839add360SClaudiu Beznea 
at91_pm_init(void (* pm_idle)(void))1419fbc7edcaSAlexandre Belloni static void __init at91_pm_init(void (*pm_idle)(void))
14209d041268SAndrew Victor {
14215737b73eSAlexandre Belloni 	struct device_node *pmc_np;
142213f16017SAlexandre Belloni 	const struct of_device_id *of_id;
142313f16017SAlexandre Belloni 	const struct pmc_info *pmc;
1424f5d0f457SAndrew Victor 
14255ad945eaSDaniel Lezcano 	if (at91_cpuidle_device.dev.platform_data)
14265ad945eaSDaniel Lezcano 		platform_device_register(&at91_cpuidle_device);
14275ad945eaSDaniel Lezcano 
142813f16017SAlexandre Belloni 	pmc_np = of_find_matching_node_and_match(NULL, atmel_pmc_ids, &of_id);
1429c3f5b8fdSClaudiu Beznea 	soc_pm.data.pmc = of_iomap(pmc_np, 0);
1430e222f943SClaudiu Beznea 	of_node_put(pmc_np);
1431c3f5b8fdSClaudiu Beznea 	if (!soc_pm.data.pmc) {
14325737b73eSAlexandre Belloni 		pr_err("AT91: PM not supported, PMC not found\n");
14335737b73eSAlexandre Belloni 		return;
14345737b73eSAlexandre Belloni 	}
14355737b73eSAlexandre Belloni 
143613f16017SAlexandre Belloni 	pmc = of_id->data;
1437c3f5b8fdSClaudiu Beznea 	soc_pm.data.uhp_udp_mask = pmc->uhp_udp_mask;
14386ec1587bSClaudiu Beznea 	soc_pm.data.pmc_mckr_offset = pmc->mckr;
14390be298a9SClaudiu Beznea 	soc_pm.data.pmc_version = pmc->version;
144013f16017SAlexandre Belloni 
1441fbc7edcaSAlexandre Belloni 	if (pm_idle)
1442fbc7edcaSAlexandre Belloni 		arm_pm_idle = pm_idle;
1443fbc7edcaSAlexandre Belloni 
14445737b73eSAlexandre Belloni 	at91_pm_sram_init();
14455737b73eSAlexandre Belloni 
14467693e18eSAlexandre Belloni 	if (at91_suspend_sram_fn) {
144726398a70SRafael J. Wysocki 		suspend_set_ops(&at91_pm_ops);
14487693e18eSAlexandre Belloni 		pr_info("AT91: PM: standby: %s, suspend: %s\n",
1449c3f5b8fdSClaudiu Beznea 			pm_modes[soc_pm.data.standby_mode].pattern,
1450c3f5b8fdSClaudiu Beznea 			pm_modes[soc_pm.data.suspend_mode].pattern);
14517693e18eSAlexandre Belloni 	} else {
1452d94e688cSWenyou Yang 		pr_info("AT91: PM not supported, due to no SRAM allocated\n");
14539d041268SAndrew Victor 	}
14547693e18eSAlexandre Belloni }
14554db0ba22SAlexandre Belloni 
at91rm9200_pm_init(void)1456ad3fc3e3SNicolas Ferre void __init at91rm9200_pm_init(void)
14574db0ba22SAlexandre Belloni {
14581605de1bSClaudiu Beznea 	int ret;
14591605de1bSClaudiu Beznea 
1460dbeb0c8eSArnd Bergmann 	if (!IS_ENABLED(CONFIG_SOC_AT91RM9200))
1461dbeb0c8eSArnd Bergmann 		return;
1462dbeb0c8eSArnd Bergmann 
146339add360SClaudiu Beznea 	/*
146439add360SClaudiu Beznea 	 * Force STANDBY and ULP0 mode to avoid calling
146539add360SClaudiu Beznea 	 * at91_pm_modes_validate() which may increase booting time.
146639add360SClaudiu Beznea 	 * Platform supports anyway only STANDBY and ULP0 modes.
146739add360SClaudiu Beznea 	 */
146839add360SClaudiu Beznea 	soc_pm.data.standby_mode = AT91_PM_STANDBY;
146939add360SClaudiu Beznea 	soc_pm.data.suspend_mode = AT91_PM_ULP0;
147039add360SClaudiu Beznea 
14711605de1bSClaudiu Beznea 	ret = at91_dt_ramc(false);
14721605de1bSClaudiu Beznea 	if (ret)
14731605de1bSClaudiu Beznea 		return;
1474827de1f1SAlexandre Belloni 
14754db0ba22SAlexandre Belloni 	/*
14764db0ba22SAlexandre Belloni 	 * AT91RM9200 SDRAM low-power mode cannot be used with self-refresh.
14774db0ba22SAlexandre Belloni 	 */
1478d7d45f25SAlexandre Belloni 	at91_ramc_write(0, AT91_MC_SDRAMC_LPR, 0);
14794db0ba22SAlexandre Belloni 
1480fbc7edcaSAlexandre Belloni 	at91_pm_init(at91rm9200_idle);
14814db0ba22SAlexandre Belloni }
14824db0ba22SAlexandre Belloni 
sam9x60_pm_init(void)148301c7031cSClaudiu Beznea void __init sam9x60_pm_init(void)
148401c7031cSClaudiu Beznea {
148539add360SClaudiu Beznea 	static const int modes[] __initconst = {
148639add360SClaudiu Beznea 		AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP0_FAST, AT91_PM_ULP1,
148739add360SClaudiu Beznea 	};
1488404956f4SClaudiu Beznea 	static const int iomaps[] __initconst = {
1489404956f4SClaudiu Beznea 		[AT91_PM_ULP1]		= AT91_PM_IOMAP(SHDWC),
1490404956f4SClaudiu Beznea 	};
14911605de1bSClaudiu Beznea 	int ret;
149239add360SClaudiu Beznea 
1493eb0df9b7SClaudiu Beznea 	if (!IS_ENABLED(CONFIG_SOC_SAM9X60))
149401c7031cSClaudiu Beznea 		return;
149501c7031cSClaudiu Beznea 
149639add360SClaudiu Beznea 	at91_pm_modes_validate(modes, ARRAY_SIZE(modes));
1497404956f4SClaudiu Beznea 	at91_pm_modes_init(iomaps, ARRAY_SIZE(iomaps));
14981605de1bSClaudiu Beznea 	ret = at91_dt_ramc(false);
14991605de1bSClaudiu Beznea 	if (ret)
15001605de1bSClaudiu Beznea 		return;
15011605de1bSClaudiu Beznea 
1502faf6dc64SAlexandre Belloni 	at91_pm_init(NULL);
1503eaedc0d3SClaudiu Beznea 
1504eaedc0d3SClaudiu Beznea 	soc_pm.ws_ids = sam9x60_ws_ids;
1505eaedc0d3SClaudiu Beznea 	soc_pm.config_pmc_ws = at91_sam9x60_config_pmc_ws;
150601c7031cSClaudiu Beznea }
150701c7031cSClaudiu Beznea 
at91sam9_pm_init(void)150813469192SAlexandre Belloni void __init at91sam9_pm_init(void)
1509bf02280eSNicolas Ferre {
15101605de1bSClaudiu Beznea 	int ret;
15111605de1bSClaudiu Beznea 
1512dbeb0c8eSArnd Bergmann 	if (!IS_ENABLED(CONFIG_SOC_AT91SAM9))
1513dbeb0c8eSArnd Bergmann 		return;
1514dbeb0c8eSArnd Bergmann 
151539add360SClaudiu Beznea 	/*
151639add360SClaudiu Beznea 	 * Force STANDBY and ULP0 mode to avoid calling
151739add360SClaudiu Beznea 	 * at91_pm_modes_validate() which may increase booting time.
151839add360SClaudiu Beznea 	 * Platform supports anyway only STANDBY and ULP0 modes.
151939add360SClaudiu Beznea 	 */
152039add360SClaudiu Beznea 	soc_pm.data.standby_mode = AT91_PM_STANDBY;
152139add360SClaudiu Beznea 	soc_pm.data.suspend_mode = AT91_PM_ULP0;
152239add360SClaudiu Beznea 
15231605de1bSClaudiu Beznea 	ret = at91_dt_ramc(false);
15241605de1bSClaudiu Beznea 	if (ret)
15251605de1bSClaudiu Beznea 		return;
15261605de1bSClaudiu Beznea 
1527fbc7edcaSAlexandre Belloni 	at91_pm_init(at91sam9_idle);
1528fbc7edcaSAlexandre Belloni }
1529fbc7edcaSAlexandre Belloni 
sama5_pm_init(void)1530fbc7edcaSAlexandre Belloni void __init sama5_pm_init(void)
1531fbc7edcaSAlexandre Belloni {
153239add360SClaudiu Beznea 	static const int modes[] __initconst = {
153339add360SClaudiu Beznea 		AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP0_FAST,
153439add360SClaudiu Beznea 	};
1535b7fc72c6SClaudiu Beznea 	static const u32 iomaps[] __initconst = {
1536b7fc72c6SClaudiu Beznea 		[AT91_PM_ULP0]		= AT91_PM_IOMAP(ETHC),
1537b7fc72c6SClaudiu Beznea 		[AT91_PM_ULP0_FAST]	= AT91_PM_IOMAP(ETHC),
1538b7fc72c6SClaudiu Beznea 	};
15391605de1bSClaudiu Beznea 	int ret;
154039add360SClaudiu Beznea 
1541dbeb0c8eSArnd Bergmann 	if (!IS_ENABLED(CONFIG_SOC_SAMA5))
1542dbeb0c8eSArnd Bergmann 		return;
1543dbeb0c8eSArnd Bergmann 
154439add360SClaudiu Beznea 	at91_pm_modes_validate(modes, ARRAY_SIZE(modes));
1545b7fc72c6SClaudiu Beznea 	at91_pm_modes_init(iomaps, ARRAY_SIZE(iomaps));
15461605de1bSClaudiu Beznea 	ret = at91_dt_ramc(false);
15471605de1bSClaudiu Beznea 	if (ret)
15481605de1bSClaudiu Beznea 		return;
15491605de1bSClaudiu Beznea 
1550fbc7edcaSAlexandre Belloni 	at91_pm_init(NULL);
1551b7fc72c6SClaudiu Beznea 
1552b7fc72c6SClaudiu Beznea 	/* Quirks applies to ULP0, ULP0 fast and ULP1 modes. */
1553b7fc72c6SClaudiu Beznea 	soc_pm.quirks.eth[AT91_PM_G_ETH].modes = BIT(AT91_PM_ULP0) |
1554b7fc72c6SClaudiu Beznea 						 BIT(AT91_PM_ULP0_FAST) |
1555b7fc72c6SClaudiu Beznea 						 BIT(AT91_PM_ULP1);
1556b7fc72c6SClaudiu Beznea 	/* Do not suspend in ULP0, ULP0 fast if GETH is the only wakeup source. */
1557b7fc72c6SClaudiu Beznea 	soc_pm.quirks.eth[AT91_PM_G_ETH].dns_modes = BIT(AT91_PM_ULP0) |
1558b7fc72c6SClaudiu Beznea 						     BIT(AT91_PM_ULP0_FAST);
1559bf02280eSNicolas Ferre }
156024a0f5c5SAlexandre Belloni 
sama5d2_pm_init(void)156124a0f5c5SAlexandre Belloni void __init sama5d2_pm_init(void)
156224a0f5c5SAlexandre Belloni {
156339add360SClaudiu Beznea 	static const int modes[] __initconst = {
156439add360SClaudiu Beznea 		AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP0_FAST, AT91_PM_ULP1,
156539add360SClaudiu Beznea 		AT91_PM_BACKUP,
156639add360SClaudiu Beznea 	};
1567404956f4SClaudiu Beznea 	static const u32 iomaps[] __initconst = {
1568b7fc72c6SClaudiu Beznea 		[AT91_PM_ULP0]		= AT91_PM_IOMAP(ETHC),
1569b7fc72c6SClaudiu Beznea 		[AT91_PM_ULP0_FAST]	= AT91_PM_IOMAP(ETHC),
1570b7fc72c6SClaudiu Beznea 		[AT91_PM_ULP1]		= AT91_PM_IOMAP(SHDWC) |
1571b7fc72c6SClaudiu Beznea 					  AT91_PM_IOMAP(ETHC),
1572404956f4SClaudiu Beznea 		[AT91_PM_BACKUP]	= AT91_PM_IOMAP(SHDWC) |
1573404956f4SClaudiu Beznea 					  AT91_PM_IOMAP(SFRBU),
1574404956f4SClaudiu Beznea 	};
15751605de1bSClaudiu Beznea 	int ret;
157639add360SClaudiu Beznea 
1577dbeb0c8eSArnd Bergmann 	if (!IS_ENABLED(CONFIG_SOC_SAMA5D2))
1578dbeb0c8eSArnd Bergmann 		return;
1579dbeb0c8eSArnd Bergmann 
1580f2f5cf78SClément Léger 	if (IS_ENABLED(CONFIG_ATMEL_SECURE_PM)) {
1581f2f5cf78SClément Léger 		pr_warn("AT91: Secure PM: ignoring standby mode\n");
1582f2f5cf78SClément Léger 		at91_pm_secure_init();
1583f2f5cf78SClément Léger 		return;
1584f2f5cf78SClément Léger 	}
1585f2f5cf78SClément Léger 
158639add360SClaudiu Beznea 	at91_pm_modes_validate(modes, ARRAY_SIZE(modes));
1587404956f4SClaudiu Beznea 	at91_pm_modes_init(iomaps, ARRAY_SIZE(iomaps));
15881605de1bSClaudiu Beznea 	ret = at91_dt_ramc(false);
15891605de1bSClaudiu Beznea 	if (ret)
15901605de1bSClaudiu Beznea 		return;
15911605de1bSClaudiu Beznea 
159239add360SClaudiu Beznea 	at91_pm_init(NULL);
1593a958156dSClaudiu Beznea 
1594a958156dSClaudiu Beznea 	soc_pm.ws_ids = sama5d2_ws_ids;
1595a958156dSClaudiu Beznea 	soc_pm.config_shdwc_ws = at91_sama5d2_config_shdwc_ws;
1596a958156dSClaudiu Beznea 	soc_pm.config_pmc_ws = at91_sama5d2_config_pmc_ws;
1597ac809e78SClaudiu Beznea 
1598ac809e78SClaudiu Beznea 	soc_pm.sfrbu_regs.pswbu.key = (0x4BD20C << 8);
1599ac809e78SClaudiu Beznea 	soc_pm.sfrbu_regs.pswbu.ctrl = BIT(0);
1600ac809e78SClaudiu Beznea 	soc_pm.sfrbu_regs.pswbu.softsw = BIT(1);
1601ac809e78SClaudiu Beznea 	soc_pm.sfrbu_regs.pswbu.state = BIT(3);
1602b7fc72c6SClaudiu Beznea 
1603b7fc72c6SClaudiu Beznea 	/* Quirk applies to ULP0, ULP0 fast and ULP1 modes. */
1604b7fc72c6SClaudiu Beznea 	soc_pm.quirks.eth[AT91_PM_G_ETH].modes = BIT(AT91_PM_ULP0) |
1605b7fc72c6SClaudiu Beznea 						 BIT(AT91_PM_ULP0_FAST) |
1606b7fc72c6SClaudiu Beznea 						 BIT(AT91_PM_ULP1);
1607b7fc72c6SClaudiu Beznea 	/*
1608b7fc72c6SClaudiu Beznea 	 * Do not suspend in ULP0, ULP0 fast if GETH is the only wakeup
1609b7fc72c6SClaudiu Beznea 	 * source.
1610b7fc72c6SClaudiu Beznea 	 */
1611b7fc72c6SClaudiu Beznea 	soc_pm.quirks.eth[AT91_PM_G_ETH].dns_modes = BIT(AT91_PM_ULP0) |
1612b7fc72c6SClaudiu Beznea 						     BIT(AT91_PM_ULP0_FAST);
161324a0f5c5SAlexandre Belloni }
16147693e18eSAlexandre Belloni 
sama7_pm_init(void)16156501330fSClaudiu Beznea void __init sama7_pm_init(void)
16166501330fSClaudiu Beznea {
16176501330fSClaudiu Beznea 	static const int modes[] __initconst = {
16186501330fSClaudiu Beznea 		AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP1, AT91_PM_BACKUP,
16196501330fSClaudiu Beznea 	};
16206501330fSClaudiu Beznea 	static const u32 iomaps[] __initconst = {
16216501330fSClaudiu Beznea 		[AT91_PM_ULP0]		= AT91_PM_IOMAP(SFRBU),
16226501330fSClaudiu Beznea 		[AT91_PM_ULP1]		= AT91_PM_IOMAP(SFRBU) |
1623b7fc72c6SClaudiu Beznea 					  AT91_PM_IOMAP(SHDWC) |
1624b7fc72c6SClaudiu Beznea 					  AT91_PM_IOMAP(ETHC),
16256501330fSClaudiu Beznea 		[AT91_PM_BACKUP]	= AT91_PM_IOMAP(SFRBU) |
16266501330fSClaudiu Beznea 					  AT91_PM_IOMAP(SHDWC),
16276501330fSClaudiu Beznea 	};
16281605de1bSClaudiu Beznea 	int ret;
16296501330fSClaudiu Beznea 
16306501330fSClaudiu Beznea 	if (!IS_ENABLED(CONFIG_SOC_SAMA7))
16316501330fSClaudiu Beznea 		return;
16326501330fSClaudiu Beznea 
16336501330fSClaudiu Beznea 	at91_pm_modes_validate(modes, ARRAY_SIZE(modes));
16346501330fSClaudiu Beznea 
16351605de1bSClaudiu Beznea 	ret = at91_dt_ramc(true);
16361605de1bSClaudiu Beznea 	if (ret)
16371605de1bSClaudiu Beznea 		return;
16381605de1bSClaudiu Beznea 
16396501330fSClaudiu Beznea 	at91_pm_modes_init(iomaps, ARRAY_SIZE(iomaps));
16406501330fSClaudiu Beznea 	at91_pm_init(NULL);
16416501330fSClaudiu Beznea 
16426501330fSClaudiu Beznea 	soc_pm.ws_ids = sama7g5_ws_ids;
16436501330fSClaudiu Beznea 	soc_pm.config_pmc_ws = at91_sam9x60_config_pmc_ws;
1644ac809e78SClaudiu Beznea 
1645ac809e78SClaudiu Beznea 	soc_pm.sfrbu_regs.pswbu.key = (0x4BD20C << 8);
1646ac809e78SClaudiu Beznea 	soc_pm.sfrbu_regs.pswbu.ctrl = BIT(0);
1647ac809e78SClaudiu Beznea 	soc_pm.sfrbu_regs.pswbu.softsw = BIT(1);
1648ac809e78SClaudiu Beznea 	soc_pm.sfrbu_regs.pswbu.state = BIT(2);
1649b7fc72c6SClaudiu Beznea 
1650b7fc72c6SClaudiu Beznea 	/* Quirks applies to ULP1 for both Ethernet interfaces. */
1651b7fc72c6SClaudiu Beznea 	soc_pm.quirks.eth[AT91_PM_E_ETH].modes = BIT(AT91_PM_ULP1);
1652b7fc72c6SClaudiu Beznea 	soc_pm.quirks.eth[AT91_PM_G_ETH].modes = BIT(AT91_PM_ULP1);
16536501330fSClaudiu Beznea }
16546501330fSClaudiu Beznea 
at91_pm_modes_select(char * str)16557693e18eSAlexandre Belloni static int __init at91_pm_modes_select(char *str)
16567693e18eSAlexandre Belloni {
16577693e18eSAlexandre Belloni 	char *s;
16587693e18eSAlexandre Belloni 	substring_t args[MAX_OPT_ARGS];
16597693e18eSAlexandre Belloni 	int standby, suspend;
16607693e18eSAlexandre Belloni 
16617693e18eSAlexandre Belloni 	if (!str)
16627693e18eSAlexandre Belloni 		return 0;
16637693e18eSAlexandre Belloni 
16647693e18eSAlexandre Belloni 	s = strsep(&str, ",");
16657693e18eSAlexandre Belloni 	standby = match_token(s, pm_modes, args);
16667693e18eSAlexandre Belloni 	if (standby < 0)
16677693e18eSAlexandre Belloni 		return 0;
16687693e18eSAlexandre Belloni 
16697693e18eSAlexandre Belloni 	suspend = match_token(str, pm_modes, args);
16707693e18eSAlexandre Belloni 	if (suspend < 0)
16717693e18eSAlexandre Belloni 		return 0;
16727693e18eSAlexandre Belloni 
1673c3f5b8fdSClaudiu Beznea 	soc_pm.data.standby_mode = standby;
1674c3f5b8fdSClaudiu Beznea 	soc_pm.data.suspend_mode = suspend;
16757693e18eSAlexandre Belloni 
16767693e18eSAlexandre Belloni 	return 0;
16777693e18eSAlexandre Belloni }
16787693e18eSAlexandre Belloni early_param("atmel.pm_modes", at91_pm_modes_select);
1679