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