1afe761f8SDave Gerlach // SPDX-License-Identifier: GPL-2.0
2afe761f8SDave Gerlach /*
3afe761f8SDave Gerlach * AM33XX Power Management Routines
4afe761f8SDave Gerlach *
5afe761f8SDave Gerlach * Copyright (C) 2012-2018 Texas Instruments Incorporated - http://www.ti.com/
6afe761f8SDave Gerlach * Vaibhav Bedia, Dave Gerlach
7afe761f8SDave Gerlach */
8afe761f8SDave Gerlach
95a99ae00SKeerthy #include <linux/clk.h>
10afe761f8SDave Gerlach #include <linux/cpu.h>
11afe761f8SDave Gerlach #include <linux/err.h>
12afe761f8SDave Gerlach #include <linux/genalloc.h>
13afe761f8SDave Gerlach #include <linux/kernel.h>
14afe761f8SDave Gerlach #include <linux/init.h>
15afe761f8SDave Gerlach #include <linux/io.h>
16afe761f8SDave Gerlach #include <linux/module.h>
175a99ae00SKeerthy #include <linux/nvmem-consumer.h>
18afe761f8SDave Gerlach #include <linux/of.h>
192152fbbdSTony Lindgren #include <linux/of_address.h>
20afe761f8SDave Gerlach #include <linux/platform_data/pm33xx.h>
21afe761f8SDave Gerlach #include <linux/platform_device.h>
2274033131STony Lindgren #include <linux/pm_runtime.h>
235a99ae00SKeerthy #include <linux/rtc.h>
245a99ae00SKeerthy #include <linux/rtc/rtc-omap.h>
25afe761f8SDave Gerlach #include <linux/sizes.h>
26afe761f8SDave Gerlach #include <linux/sram.h>
27afe761f8SDave Gerlach #include <linux/suspend.h>
28afe761f8SDave Gerlach #include <linux/ti-emif-sram.h>
29afe761f8SDave Gerlach #include <linux/wkup_m3_ipc.h>
30afe761f8SDave Gerlach
31afe761f8SDave Gerlach #include <asm/proc-fns.h>
32afe761f8SDave Gerlach #include <asm/suspend.h>
33afe761f8SDave Gerlach #include <asm/system_misc.h>
34afe761f8SDave Gerlach
35afe761f8SDave Gerlach #define AMX3_PM_SRAM_SYMBOL_OFFSET(sym) ((unsigned long)(sym) - \
36afe761f8SDave Gerlach (unsigned long)pm_sram->do_wfi)
37afe761f8SDave Gerlach
385a99ae00SKeerthy #define RTC_SCRATCH_RESUME_REG 0
395a99ae00SKeerthy #define RTC_SCRATCH_MAGIC_REG 1
405a99ae00SKeerthy #define RTC_REG_BOOT_MAGIC 0x8cd0 /* RTC */
415a99ae00SKeerthy #define GIC_INT_SET_PENDING_BASE 0x200
425a99ae00SKeerthy #define AM43XX_GIC_DIST_BASE 0x48241000
435a99ae00SKeerthy
442152fbbdSTony Lindgren static void __iomem *rtc_base_virt;
452152fbbdSTony Lindgren static struct clk *rtc_fck;
465a99ae00SKeerthy static u32 rtc_magic_val;
475a99ae00SKeerthy
48afe761f8SDave Gerlach static int (*am33xx_do_wfi_sram)(unsigned long unused);
49afe761f8SDave Gerlach static phys_addr_t am33xx_do_wfi_sram_phys;
50afe761f8SDave Gerlach
51afe761f8SDave Gerlach static struct gen_pool *sram_pool, *sram_pool_data;
52afe761f8SDave Gerlach static unsigned long ocmcram_location, ocmcram_location_data;
53afe761f8SDave Gerlach
545a99ae00SKeerthy static struct rtc_device *omap_rtc;
555a99ae00SKeerthy static void __iomem *gic_dist_base;
565a99ae00SKeerthy
57afe761f8SDave Gerlach static struct am33xx_pm_platform_data *pm_ops;
58afe761f8SDave Gerlach static struct am33xx_pm_sram_addr *pm_sram;
59afe761f8SDave Gerlach
60afe761f8SDave Gerlach static struct device *pm33xx_dev;
61afe761f8SDave Gerlach static struct wkup_m3_ipc *m3_ipc;
62afe761f8SDave Gerlach
635a99ae00SKeerthy #ifdef CONFIG_SUSPEND
645a99ae00SKeerthy static int rtc_only_idle;
655a99ae00SKeerthy static int retrigger_irq;
6674655749SDave Gerlach static unsigned long suspend_wfi_flags;
6774655749SDave Gerlach
685a99ae00SKeerthy static struct wkup_m3_wakeup_src wakeup_src = {.irq_nr = 0,
695a99ae00SKeerthy .src = "Unknown",
705a99ae00SKeerthy };
715a99ae00SKeerthy
725a99ae00SKeerthy static struct wkup_m3_wakeup_src rtc_alarm_wakeup = {
735a99ae00SKeerthy .irq_nr = 108, .src = "RTC Alarm",
745a99ae00SKeerthy };
755a99ae00SKeerthy
765a99ae00SKeerthy static struct wkup_m3_wakeup_src rtc_ext_wakeup = {
775a99ae00SKeerthy .irq_nr = 0, .src = "Ext wakeup",
785a99ae00SKeerthy };
795a99ae00SKeerthy #endif
805a99ae00SKeerthy
sram_suspend_address(unsigned long addr)81afe761f8SDave Gerlach static u32 sram_suspend_address(unsigned long addr)
82afe761f8SDave Gerlach {
83afe761f8SDave Gerlach return ((unsigned long)am33xx_do_wfi_sram +
84afe761f8SDave Gerlach AMX3_PM_SRAM_SYMBOL_OFFSET(addr));
85afe761f8SDave Gerlach }
86afe761f8SDave Gerlach
am33xx_push_sram_idle(void)871c6c0354SKeerthy static int am33xx_push_sram_idle(void)
881c6c0354SKeerthy {
891c6c0354SKeerthy struct am33xx_pm_ro_sram_data ro_sram_data;
901c6c0354SKeerthy int ret;
911c6c0354SKeerthy u32 table_addr, ro_data_addr;
921c6c0354SKeerthy void *copy_addr;
931c6c0354SKeerthy
941c6c0354SKeerthy ro_sram_data.amx3_pm_sram_data_virt = ocmcram_location_data;
951c6c0354SKeerthy ro_sram_data.amx3_pm_sram_data_phys =
961c6c0354SKeerthy gen_pool_virt_to_phys(sram_pool_data, ocmcram_location_data);
972152fbbdSTony Lindgren ro_sram_data.rtc_base_virt = rtc_base_virt;
981c6c0354SKeerthy
991c6c0354SKeerthy /* Save physical address to calculate resume offset during pm init */
1001c6c0354SKeerthy am33xx_do_wfi_sram_phys = gen_pool_virt_to_phys(sram_pool,
1011c6c0354SKeerthy ocmcram_location);
1021c6c0354SKeerthy
1031c6c0354SKeerthy am33xx_do_wfi_sram = sram_exec_copy(sram_pool, (void *)ocmcram_location,
1041c6c0354SKeerthy pm_sram->do_wfi,
1051c6c0354SKeerthy *pm_sram->do_wfi_sz);
1061c6c0354SKeerthy if (!am33xx_do_wfi_sram) {
1071c6c0354SKeerthy dev_err(pm33xx_dev,
1081c6c0354SKeerthy "PM: %s: am33xx_do_wfi copy to sram failed\n",
1091c6c0354SKeerthy __func__);
1101c6c0354SKeerthy return -ENODEV;
1111c6c0354SKeerthy }
1121c6c0354SKeerthy
1131c6c0354SKeerthy table_addr =
1141c6c0354SKeerthy sram_suspend_address((unsigned long)pm_sram->emif_sram_table);
1151c6c0354SKeerthy ret = ti_emif_copy_pm_function_table(sram_pool, (void *)table_addr);
1161c6c0354SKeerthy if (ret) {
1171c6c0354SKeerthy dev_dbg(pm33xx_dev,
1181c6c0354SKeerthy "PM: %s: EMIF function copy failed\n", __func__);
1191c6c0354SKeerthy return -EPROBE_DEFER;
1201c6c0354SKeerthy }
1211c6c0354SKeerthy
1221c6c0354SKeerthy ro_data_addr =
1231c6c0354SKeerthy sram_suspend_address((unsigned long)pm_sram->ro_sram_data);
1241c6c0354SKeerthy copy_addr = sram_exec_copy(sram_pool, (void *)ro_data_addr,
1251c6c0354SKeerthy &ro_sram_data,
1261c6c0354SKeerthy sizeof(ro_sram_data));
1271c6c0354SKeerthy if (!copy_addr) {
1281c6c0354SKeerthy dev_err(pm33xx_dev,
1291c6c0354SKeerthy "PM: %s: ro_sram_data copy to sram failed\n",
1301c6c0354SKeerthy __func__);
1311c6c0354SKeerthy return -ENODEV;
1321c6c0354SKeerthy }
1331c6c0354SKeerthy
1341c6c0354SKeerthy return 0;
1351c6c0354SKeerthy }
1361c6c0354SKeerthy
am33xx_do_sram_idle(u32 wfi_flags)1376afaff1cSDave Gerlach static int am33xx_do_sram_idle(u32 wfi_flags)
1386afaff1cSDave Gerlach {
1396afaff1cSDave Gerlach if (!m3_ipc || !pm_ops)
1406afaff1cSDave Gerlach return 0;
1416afaff1cSDave Gerlach
1426afaff1cSDave Gerlach if (wfi_flags & WFI_FLAG_WAKE_M3)
143edac869eSLee Jones m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_IDLE);
1446afaff1cSDave Gerlach
1456afaff1cSDave Gerlach return pm_ops->cpu_suspend(am33xx_do_wfi_sram, wfi_flags);
1466afaff1cSDave Gerlach }
1476afaff1cSDave Gerlach
am43xx_map_gic(void)1485a99ae00SKeerthy static int __init am43xx_map_gic(void)
1495a99ae00SKeerthy {
1505a99ae00SKeerthy gic_dist_base = ioremap(AM43XX_GIC_DIST_BASE, SZ_4K);
1515a99ae00SKeerthy
1525a99ae00SKeerthy if (!gic_dist_base)
1535a99ae00SKeerthy return -ENOMEM;
1545a99ae00SKeerthy
1555a99ae00SKeerthy return 0;
1565a99ae00SKeerthy }
1575a99ae00SKeerthy
158afe761f8SDave Gerlach #ifdef CONFIG_SUSPEND
rtc_wake_src(void)1594a65bbb9SYueHaibing static struct wkup_m3_wakeup_src rtc_wake_src(void)
1605a99ae00SKeerthy {
1615a99ae00SKeerthy u32 i;
1625a99ae00SKeerthy
1632152fbbdSTony Lindgren i = __raw_readl(rtc_base_virt + 0x44) & 0x40;
1645a99ae00SKeerthy
1655a99ae00SKeerthy if (i) {
1665a99ae00SKeerthy retrigger_irq = rtc_alarm_wakeup.irq_nr;
1675a99ae00SKeerthy return rtc_alarm_wakeup;
1685a99ae00SKeerthy }
1695a99ae00SKeerthy
1705a99ae00SKeerthy retrigger_irq = rtc_ext_wakeup.irq_nr;
1715a99ae00SKeerthy
1725a99ae00SKeerthy return rtc_ext_wakeup;
1735a99ae00SKeerthy }
1745a99ae00SKeerthy
am33xx_rtc_only_idle(unsigned long wfi_flags)1754a65bbb9SYueHaibing static int am33xx_rtc_only_idle(unsigned long wfi_flags)
1765a99ae00SKeerthy {
1775a99ae00SKeerthy omap_rtc_power_off_program(&omap_rtc->dev);
1785a99ae00SKeerthy am33xx_do_wfi_sram(wfi_flags);
1795a99ae00SKeerthy return 0;
1805a99ae00SKeerthy }
1815a99ae00SKeerthy
1822152fbbdSTony Lindgren /*
1832152fbbdSTony Lindgren * Note that the RTC module clock must be re-enabled only for rtc+ddr suspend.
1842152fbbdSTony Lindgren * And looks like the module can stay in SYSC_IDLE_SMART_WKUP mode configured
1852152fbbdSTony Lindgren * by the interconnect code just fine for both rtc+ddr suspend and retention
1862152fbbdSTony Lindgren * suspend.
1872152fbbdSTony Lindgren */
am33xx_pm_suspend(suspend_state_t suspend_state)188afe761f8SDave Gerlach static int am33xx_pm_suspend(suspend_state_t suspend_state)
189afe761f8SDave Gerlach {
190afe761f8SDave Gerlach int i, ret = 0;
191afe761f8SDave Gerlach
1925a99ae00SKeerthy if (suspend_state == PM_SUSPEND_MEM &&
1935a99ae00SKeerthy pm_ops->check_off_mode_enable()) {
1942152fbbdSTony Lindgren ret = clk_prepare_enable(rtc_fck);
1952152fbbdSTony Lindgren if (ret) {
1962152fbbdSTony Lindgren dev_err(pm33xx_dev, "Failed to enable clock: %i\n", ret);
1972152fbbdSTony Lindgren return ret;
1982152fbbdSTony Lindgren }
1992152fbbdSTony Lindgren
2005a99ae00SKeerthy pm_ops->save_context();
2015a99ae00SKeerthy suspend_wfi_flags |= WFI_FLAG_RTC_ONLY;
2025a99ae00SKeerthy clk_save_context();
2035a99ae00SKeerthy ret = pm_ops->soc_suspend(suspend_state, am33xx_rtc_only_idle,
2045a99ae00SKeerthy suspend_wfi_flags);
2055a99ae00SKeerthy
2065a99ae00SKeerthy suspend_wfi_flags &= ~WFI_FLAG_RTC_ONLY;
20745450f36SKeerthy dev_info(pm33xx_dev, "Entering RTC Only mode with DDR in self-refresh\n");
2085a99ae00SKeerthy
2095a99ae00SKeerthy if (!ret) {
2105a99ae00SKeerthy clk_restore_context();
2115a99ae00SKeerthy pm_ops->restore_context();
2125a99ae00SKeerthy m3_ipc->ops->set_rtc_only(m3_ipc);
2135a99ae00SKeerthy am33xx_push_sram_idle();
2145a99ae00SKeerthy }
2155a99ae00SKeerthy } else {
2165a99ae00SKeerthy ret = pm_ops->soc_suspend(suspend_state, am33xx_do_wfi_sram,
2175a99ae00SKeerthy suspend_wfi_flags);
2185a99ae00SKeerthy }
219afe761f8SDave Gerlach
220afe761f8SDave Gerlach if (ret) {
221afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: Kernel suspend failure\n");
222afe761f8SDave Gerlach } else {
223afe761f8SDave Gerlach i = m3_ipc->ops->request_pm_status(m3_ipc);
224afe761f8SDave Gerlach
225afe761f8SDave Gerlach switch (i) {
226afe761f8SDave Gerlach case 0:
227afe761f8SDave Gerlach dev_info(pm33xx_dev,
228afe761f8SDave Gerlach "PM: Successfully put all powerdomains to target state\n");
229afe761f8SDave Gerlach break;
230afe761f8SDave Gerlach case 1:
231afe761f8SDave Gerlach dev_err(pm33xx_dev,
232afe761f8SDave Gerlach "PM: Could not transition all powerdomains to target state\n");
233afe761f8SDave Gerlach ret = -1;
234afe761f8SDave Gerlach break;
235afe761f8SDave Gerlach default:
236afe761f8SDave Gerlach dev_err(pm33xx_dev,
237afe761f8SDave Gerlach "PM: CM3 returned unknown result = %d\n", i);
238afe761f8SDave Gerlach ret = -1;
239afe761f8SDave Gerlach }
2405a99ae00SKeerthy
2415a99ae00SKeerthy /* print the wakeup reason */
2425a99ae00SKeerthy if (rtc_only_idle) {
2435a99ae00SKeerthy wakeup_src = rtc_wake_src();
2445a99ae00SKeerthy pr_info("PM: Wakeup source %s\n", wakeup_src.src);
2455a99ae00SKeerthy } else {
2465a99ae00SKeerthy pr_info("PM: Wakeup source %s\n",
2475a99ae00SKeerthy m3_ipc->ops->request_wake_src(m3_ipc));
248afe761f8SDave Gerlach }
2495a99ae00SKeerthy }
2505a99ae00SKeerthy
2515a99ae00SKeerthy if (suspend_state == PM_SUSPEND_MEM && pm_ops->check_off_mode_enable())
2522152fbbdSTony Lindgren clk_disable_unprepare(rtc_fck);
253afe761f8SDave Gerlach
254afe761f8SDave Gerlach return ret;
255afe761f8SDave Gerlach }
256afe761f8SDave Gerlach
am33xx_pm_enter(suspend_state_t suspend_state)257afe761f8SDave Gerlach static int am33xx_pm_enter(suspend_state_t suspend_state)
258afe761f8SDave Gerlach {
259afe761f8SDave Gerlach int ret = 0;
260afe761f8SDave Gerlach
261afe761f8SDave Gerlach switch (suspend_state) {
262afe761f8SDave Gerlach case PM_SUSPEND_MEM:
263afe761f8SDave Gerlach case PM_SUSPEND_STANDBY:
264afe761f8SDave Gerlach ret = am33xx_pm_suspend(suspend_state);
265afe761f8SDave Gerlach break;
266afe761f8SDave Gerlach default:
267afe761f8SDave Gerlach ret = -EINVAL;
268afe761f8SDave Gerlach }
269afe761f8SDave Gerlach
270afe761f8SDave Gerlach return ret;
271afe761f8SDave Gerlach }
272afe761f8SDave Gerlach
am33xx_pm_begin(suspend_state_t state)273afe761f8SDave Gerlach static int am33xx_pm_begin(suspend_state_t state)
274afe761f8SDave Gerlach {
275afe761f8SDave Gerlach int ret = -EINVAL;
2765a99ae00SKeerthy struct nvmem_device *nvmem;
2775a99ae00SKeerthy
2785a99ae00SKeerthy if (state == PM_SUSPEND_MEM && pm_ops->check_off_mode_enable()) {
2795a99ae00SKeerthy nvmem = devm_nvmem_device_get(&omap_rtc->dev,
2805a99ae00SKeerthy "omap_rtc_scratch0");
281d8e0cecbSKeerthy if (!IS_ERR(nvmem))
2825a99ae00SKeerthy nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4, 4,
2835a99ae00SKeerthy (void *)&rtc_magic_val);
2845a99ae00SKeerthy rtc_only_idle = 1;
2855a99ae00SKeerthy } else {
2865a99ae00SKeerthy rtc_only_idle = 0;
2875a99ae00SKeerthy }
288afe761f8SDave Gerlach
2896afaff1cSDave Gerlach pm_ops->begin_suspend();
2906afaff1cSDave Gerlach
291afe761f8SDave Gerlach switch (state) {
292afe761f8SDave Gerlach case PM_SUSPEND_MEM:
293afe761f8SDave Gerlach ret = m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_DEEPSLEEP);
294afe761f8SDave Gerlach break;
295afe761f8SDave Gerlach case PM_SUSPEND_STANDBY:
296afe761f8SDave Gerlach ret = m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_STANDBY);
297afe761f8SDave Gerlach break;
298afe761f8SDave Gerlach }
299afe761f8SDave Gerlach
300afe761f8SDave Gerlach return ret;
301afe761f8SDave Gerlach }
302afe761f8SDave Gerlach
am33xx_pm_end(void)303afe761f8SDave Gerlach static void am33xx_pm_end(void)
304afe761f8SDave Gerlach {
3055a99ae00SKeerthy u32 val = 0;
3065a99ae00SKeerthy struct nvmem_device *nvmem;
3075a99ae00SKeerthy
3085a99ae00SKeerthy nvmem = devm_nvmem_device_get(&omap_rtc->dev, "omap_rtc_scratch0");
309d8e0cecbSKeerthy if (IS_ERR(nvmem))
310d8e0cecbSKeerthy return;
311d8e0cecbSKeerthy
312afe761f8SDave Gerlach m3_ipc->ops->finish_low_power(m3_ipc);
3135a99ae00SKeerthy if (rtc_only_idle) {
314d8e0cecbSKeerthy if (retrigger_irq) {
3155a99ae00SKeerthy /*
3165a99ae00SKeerthy * 32 bits of Interrupt Set-Pending correspond to 32
3175a99ae00SKeerthy * 32 interrupts. Compute the bit offset of the
3185a99ae00SKeerthy * Interrupt and set that particular bit
3195a99ae00SKeerthy * Compute the register offset by dividing interrupt
3205a99ae00SKeerthy * number by 32 and mutiplying by 4
3215a99ae00SKeerthy */
3225a99ae00SKeerthy writel_relaxed(1 << (retrigger_irq & 31),
3235a99ae00SKeerthy gic_dist_base + GIC_INT_SET_PENDING_BASE
3245a99ae00SKeerthy + retrigger_irq / 32 * 4);
325d8e0cecbSKeerthy }
326d8e0cecbSKeerthy
3275a99ae00SKeerthy nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4, 4,
3285a99ae00SKeerthy (void *)&val);
3295a99ae00SKeerthy }
3305a99ae00SKeerthy
3315a99ae00SKeerthy rtc_only_idle = 0;
3326afaff1cSDave Gerlach
3336afaff1cSDave Gerlach pm_ops->finish_suspend();
334afe761f8SDave Gerlach }
335afe761f8SDave Gerlach
am33xx_pm_valid(suspend_state_t state)336afe761f8SDave Gerlach static int am33xx_pm_valid(suspend_state_t state)
337afe761f8SDave Gerlach {
338afe761f8SDave Gerlach switch (state) {
339afe761f8SDave Gerlach case PM_SUSPEND_STANDBY:
340afe761f8SDave Gerlach case PM_SUSPEND_MEM:
341afe761f8SDave Gerlach return 1;
342afe761f8SDave Gerlach default:
343afe761f8SDave Gerlach return 0;
344afe761f8SDave Gerlach }
345afe761f8SDave Gerlach }
346afe761f8SDave Gerlach
347afe761f8SDave Gerlach static const struct platform_suspend_ops am33xx_pm_ops = {
348afe761f8SDave Gerlach .begin = am33xx_pm_begin,
349afe761f8SDave Gerlach .end = am33xx_pm_end,
350afe761f8SDave Gerlach .enter = am33xx_pm_enter,
351afe761f8SDave Gerlach .valid = am33xx_pm_valid,
352afe761f8SDave Gerlach };
353afe761f8SDave Gerlach #endif /* CONFIG_SUSPEND */
354afe761f8SDave Gerlach
am33xx_pm_set_ipc_ops(void)355afe761f8SDave Gerlach static void am33xx_pm_set_ipc_ops(void)
356afe761f8SDave Gerlach {
357afe761f8SDave Gerlach u32 resume_address;
358afe761f8SDave Gerlach int temp;
359afe761f8SDave Gerlach
360afe761f8SDave Gerlach temp = ti_emif_get_mem_type();
361afe761f8SDave Gerlach if (temp < 0) {
362afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: Cannot determine memory type, no PM available\n");
363afe761f8SDave Gerlach return;
364afe761f8SDave Gerlach }
365afe761f8SDave Gerlach m3_ipc->ops->set_mem_type(m3_ipc, temp);
366afe761f8SDave Gerlach
367afe761f8SDave Gerlach /* Physical resume address to be used by ROM code */
368afe761f8SDave Gerlach resume_address = am33xx_do_wfi_sram_phys +
369afe761f8SDave Gerlach *pm_sram->resume_offset + 0x4;
370afe761f8SDave Gerlach
371afe761f8SDave Gerlach m3_ipc->ops->set_resume_address(m3_ipc, (void *)resume_address);
372afe761f8SDave Gerlach }
373afe761f8SDave Gerlach
am33xx_pm_free_sram(void)374afe761f8SDave Gerlach static void am33xx_pm_free_sram(void)
375afe761f8SDave Gerlach {
376afe761f8SDave Gerlach gen_pool_free(sram_pool, ocmcram_location, *pm_sram->do_wfi_sz);
377afe761f8SDave Gerlach gen_pool_free(sram_pool_data, ocmcram_location_data,
378afe761f8SDave Gerlach sizeof(struct am33xx_pm_ro_sram_data));
379afe761f8SDave Gerlach }
380afe761f8SDave Gerlach
381afe761f8SDave Gerlach /*
382afe761f8SDave Gerlach * Push the minimal suspend-resume code to SRAM
383afe761f8SDave Gerlach */
am33xx_pm_alloc_sram(void)384afe761f8SDave Gerlach static int am33xx_pm_alloc_sram(void)
385afe761f8SDave Gerlach {
386afe761f8SDave Gerlach struct device_node *np;
387afe761f8SDave Gerlach int ret = 0;
388afe761f8SDave Gerlach
389afe761f8SDave Gerlach np = of_find_compatible_node(NULL, NULL, "ti,omap3-mpu");
390afe761f8SDave Gerlach if (!np) {
391afe761f8SDave Gerlach np = of_find_compatible_node(NULL, NULL, "ti,omap4-mpu");
392afe761f8SDave Gerlach if (!np) {
393afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: %s: Unable to find device node for mpu\n",
394afe761f8SDave Gerlach __func__);
395afe761f8SDave Gerlach return -ENODEV;
396afe761f8SDave Gerlach }
397afe761f8SDave Gerlach }
398afe761f8SDave Gerlach
399afe761f8SDave Gerlach sram_pool = of_gen_pool_get(np, "pm-sram", 0);
400afe761f8SDave Gerlach if (!sram_pool) {
401afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: %s: Unable to get sram pool for ocmcram\n",
402afe761f8SDave Gerlach __func__);
403afe761f8SDave Gerlach ret = -ENODEV;
404afe761f8SDave Gerlach goto mpu_put_node;
405afe761f8SDave Gerlach }
406afe761f8SDave Gerlach
407afe761f8SDave Gerlach sram_pool_data = of_gen_pool_get(np, "pm-sram", 1);
408afe761f8SDave Gerlach if (!sram_pool_data) {
409afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: %s: Unable to get sram data pool for ocmcram\n",
410afe761f8SDave Gerlach __func__);
411afe761f8SDave Gerlach ret = -ENODEV;
412afe761f8SDave Gerlach goto mpu_put_node;
413afe761f8SDave Gerlach }
414afe761f8SDave Gerlach
415afe761f8SDave Gerlach ocmcram_location = gen_pool_alloc(sram_pool, *pm_sram->do_wfi_sz);
416afe761f8SDave Gerlach if (!ocmcram_location) {
417afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: %s: Unable to allocate memory from ocmcram\n",
418afe761f8SDave Gerlach __func__);
419afe761f8SDave Gerlach ret = -ENOMEM;
420afe761f8SDave Gerlach goto mpu_put_node;
421afe761f8SDave Gerlach }
422afe761f8SDave Gerlach
423afe761f8SDave Gerlach ocmcram_location_data = gen_pool_alloc(sram_pool_data,
424afe761f8SDave Gerlach sizeof(struct emif_regs_amx3));
425afe761f8SDave Gerlach if (!ocmcram_location_data) {
426afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: Unable to allocate memory from ocmcram\n");
427afe761f8SDave Gerlach gen_pool_free(sram_pool, ocmcram_location, *pm_sram->do_wfi_sz);
428afe761f8SDave Gerlach ret = -ENOMEM;
429afe761f8SDave Gerlach }
430afe761f8SDave Gerlach
431afe761f8SDave Gerlach mpu_put_node:
432afe761f8SDave Gerlach of_node_put(np);
433afe761f8SDave Gerlach return ret;
434afe761f8SDave Gerlach }
435afe761f8SDave Gerlach
am33xx_pm_rtc_setup(void)4365a99ae00SKeerthy static int am33xx_pm_rtc_setup(void)
4375a99ae00SKeerthy {
4385a99ae00SKeerthy struct device_node *np;
4395a99ae00SKeerthy unsigned long val = 0;
4405a99ae00SKeerthy struct nvmem_device *nvmem;
4412152fbbdSTony Lindgren int error;
4425a99ae00SKeerthy
4435a99ae00SKeerthy np = of_find_node_by_name(NULL, "rtc");
4445a99ae00SKeerthy
4455a99ae00SKeerthy if (of_device_is_available(np)) {
4462152fbbdSTony Lindgren /* RTC interconnect target module clock */
4472152fbbdSTony Lindgren rtc_fck = of_clk_get_by_name(np->parent, "fck");
4482152fbbdSTony Lindgren if (IS_ERR(rtc_fck))
4492152fbbdSTony Lindgren return PTR_ERR(rtc_fck);
4502152fbbdSTony Lindgren
4512152fbbdSTony Lindgren rtc_base_virt = of_iomap(np, 0);
4522152fbbdSTony Lindgren if (!rtc_base_virt) {
4532152fbbdSTony Lindgren pr_warn("PM: could not iomap rtc");
4542152fbbdSTony Lindgren error = -ENODEV;
4552152fbbdSTony Lindgren goto err_clk_put;
4562152fbbdSTony Lindgren }
4572152fbbdSTony Lindgren
4585a99ae00SKeerthy omap_rtc = rtc_class_open("rtc0");
4595a99ae00SKeerthy if (!omap_rtc) {
4605a99ae00SKeerthy pr_warn("PM: rtc0 not available");
4612152fbbdSTony Lindgren error = -EPROBE_DEFER;
4622152fbbdSTony Lindgren goto err_iounmap;
4635a99ae00SKeerthy }
4645a99ae00SKeerthy
4655a99ae00SKeerthy nvmem = devm_nvmem_device_get(&omap_rtc->dev,
4665a99ae00SKeerthy "omap_rtc_scratch0");
467d8e0cecbSKeerthy if (!IS_ERR(nvmem)) {
4685a99ae00SKeerthy nvmem_device_read(nvmem, RTC_SCRATCH_MAGIC_REG * 4,
4695a99ae00SKeerthy 4, (void *)&rtc_magic_val);
4705a99ae00SKeerthy if ((rtc_magic_val & 0xffff) != RTC_REG_BOOT_MAGIC)
4715a99ae00SKeerthy pr_warn("PM: bootloader does not support rtc-only!\n");
4725a99ae00SKeerthy
4735a99ae00SKeerthy nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4,
4745a99ae00SKeerthy 4, (void *)&val);
4755a99ae00SKeerthy val = pm_sram->resume_address;
4765a99ae00SKeerthy nvmem_device_write(nvmem, RTC_SCRATCH_RESUME_REG * 4,
4775a99ae00SKeerthy 4, (void *)&val);
4785a99ae00SKeerthy }
4795a99ae00SKeerthy } else {
4805a99ae00SKeerthy pr_warn("PM: no-rtc available, rtc-only mode disabled.\n");
4815a99ae00SKeerthy }
4825a99ae00SKeerthy
4835a99ae00SKeerthy return 0;
4842152fbbdSTony Lindgren
4852152fbbdSTony Lindgren err_iounmap:
4862152fbbdSTony Lindgren iounmap(rtc_base_virt);
4872152fbbdSTony Lindgren err_clk_put:
4882152fbbdSTony Lindgren clk_put(rtc_fck);
4892152fbbdSTony Lindgren
4902152fbbdSTony Lindgren return error;
4915a99ae00SKeerthy }
4925a99ae00SKeerthy
am33xx_pm_probe(struct platform_device * pdev)493afe761f8SDave Gerlach static int am33xx_pm_probe(struct platform_device *pdev)
494afe761f8SDave Gerlach {
495afe761f8SDave Gerlach struct device *dev = &pdev->dev;
496afe761f8SDave Gerlach int ret;
497afe761f8SDave Gerlach
498afe761f8SDave Gerlach if (!of_machine_is_compatible("ti,am33xx") &&
499afe761f8SDave Gerlach !of_machine_is_compatible("ti,am43"))
500afe761f8SDave Gerlach return -ENODEV;
501afe761f8SDave Gerlach
502afe761f8SDave Gerlach pm_ops = dev->platform_data;
503afe761f8SDave Gerlach if (!pm_ops) {
504afe761f8SDave Gerlach dev_err(dev, "PM: Cannot get core PM ops!\n");
505afe761f8SDave Gerlach return -ENODEV;
506afe761f8SDave Gerlach }
507afe761f8SDave Gerlach
5085a99ae00SKeerthy ret = am43xx_map_gic();
5095a99ae00SKeerthy if (ret) {
5105a99ae00SKeerthy pr_err("PM: Could not ioremap GIC base\n");
5115a99ae00SKeerthy return ret;
5125a99ae00SKeerthy }
5135a99ae00SKeerthy
514afe761f8SDave Gerlach pm_sram = pm_ops->get_sram_addrs();
515afe761f8SDave Gerlach if (!pm_sram) {
516afe761f8SDave Gerlach dev_err(dev, "PM: Cannot get PM asm function addresses!!\n");
517afe761f8SDave Gerlach return -ENODEV;
518afe761f8SDave Gerlach }
519afe761f8SDave Gerlach
5205a99ae00SKeerthy m3_ipc = wkup_m3_ipc_get();
5215a99ae00SKeerthy if (!m3_ipc) {
5225a99ae00SKeerthy pr_err("PM: Cannot get wkup_m3_ipc handle\n");
5235a99ae00SKeerthy return -EPROBE_DEFER;
5245a99ae00SKeerthy }
5255a99ae00SKeerthy
526afe761f8SDave Gerlach pm33xx_dev = dev;
527afe761f8SDave Gerlach
528afe761f8SDave Gerlach ret = am33xx_pm_alloc_sram();
529afe761f8SDave Gerlach if (ret)
530*8f3c307bSMiaoqian Lin goto err_wkup_m3_ipc_put;
531afe761f8SDave Gerlach
5325a99ae00SKeerthy ret = am33xx_pm_rtc_setup();
533afe761f8SDave Gerlach if (ret)
534afe761f8SDave Gerlach goto err_free_sram;
535afe761f8SDave Gerlach
5365a99ae00SKeerthy ret = am33xx_push_sram_idle();
5375a99ae00SKeerthy if (ret)
53817ad4662SChristophe JAILLET goto err_unsetup_rtc;
539afe761f8SDave Gerlach
540afe761f8SDave Gerlach am33xx_pm_set_ipc_ops();
541afe761f8SDave Gerlach
542afe761f8SDave Gerlach #ifdef CONFIG_SUSPEND
543afe761f8SDave Gerlach suspend_set_ops(&am33xx_pm_ops);
544afe761f8SDave Gerlach
54574655749SDave Gerlach /*
54674655749SDave Gerlach * For a system suspend we must flush the caches, we want
54774655749SDave Gerlach * the DDR in self-refresh, we want to save the context
54874655749SDave Gerlach * of the EMIF, and we want the wkup_m3 to handle low-power
54974655749SDave Gerlach * transition.
55074655749SDave Gerlach */
55174655749SDave Gerlach suspend_wfi_flags |= WFI_FLAG_FLUSH_CACHE;
55274655749SDave Gerlach suspend_wfi_flags |= WFI_FLAG_SELF_REFRESH;
55374655749SDave Gerlach suspend_wfi_flags |= WFI_FLAG_SAVE_EMIF;
55474655749SDave Gerlach suspend_wfi_flags |= WFI_FLAG_WAKE_M3;
5555a99ae00SKeerthy #endif /* CONFIG_SUSPEND */
55674655749SDave Gerlach
55774033131STony Lindgren pm_runtime_enable(dev);
5582b704250SMinghao Chi ret = pm_runtime_resume_and_get(dev);
5592b704250SMinghao Chi if (ret < 0)
56074033131STony Lindgren goto err_pm_runtime_disable;
56174033131STony Lindgren
5626afaff1cSDave Gerlach ret = pm_ops->init(am33xx_do_sram_idle);
563afe761f8SDave Gerlach if (ret) {
564afe761f8SDave Gerlach dev_err(dev, "Unable to call core pm init!\n");
565afe761f8SDave Gerlach ret = -ENODEV;
56674033131STony Lindgren goto err_pm_runtime_put;
567afe761f8SDave Gerlach }
568afe761f8SDave Gerlach
569afe761f8SDave Gerlach return 0;
570afe761f8SDave Gerlach
57174033131STony Lindgren err_pm_runtime_put:
57274033131STony Lindgren pm_runtime_put_sync(dev);
57374033131STony Lindgren err_pm_runtime_disable:
57474033131STony Lindgren pm_runtime_disable(dev);
57517ad4662SChristophe JAILLET err_unsetup_rtc:
57617ad4662SChristophe JAILLET iounmap(rtc_base_virt);
57717ad4662SChristophe JAILLET clk_put(rtc_fck);
578afe761f8SDave Gerlach err_free_sram:
579afe761f8SDave Gerlach am33xx_pm_free_sram();
580afe761f8SDave Gerlach pm33xx_dev = NULL;
581*8f3c307bSMiaoqian Lin err_wkup_m3_ipc_put:
582*8f3c307bSMiaoqian Lin wkup_m3_ipc_put(m3_ipc);
583afe761f8SDave Gerlach return ret;
584afe761f8SDave Gerlach }
585afe761f8SDave Gerlach
am33xx_pm_remove(struct platform_device * pdev)586afe761f8SDave Gerlach static int am33xx_pm_remove(struct platform_device *pdev)
587afe761f8SDave Gerlach {
58874033131STony Lindgren pm_runtime_put_sync(&pdev->dev);
58974033131STony Lindgren pm_runtime_disable(&pdev->dev);
5906afaff1cSDave Gerlach if (pm_ops->deinit)
5916afaff1cSDave Gerlach pm_ops->deinit();
592afe761f8SDave Gerlach suspend_set_ops(NULL);
593afe761f8SDave Gerlach wkup_m3_ipc_put(m3_ipc);
594afe761f8SDave Gerlach am33xx_pm_free_sram();
5952152fbbdSTony Lindgren iounmap(rtc_base_virt);
5962152fbbdSTony Lindgren clk_put(rtc_fck);
597afe761f8SDave Gerlach return 0;
598afe761f8SDave Gerlach }
599afe761f8SDave Gerlach
600afe761f8SDave Gerlach static struct platform_driver am33xx_pm_driver = {
601afe761f8SDave Gerlach .driver = {
602afe761f8SDave Gerlach .name = "pm33xx",
603afe761f8SDave Gerlach },
604afe761f8SDave Gerlach .probe = am33xx_pm_probe,
605afe761f8SDave Gerlach .remove = am33xx_pm_remove,
606afe761f8SDave Gerlach };
607afe761f8SDave Gerlach module_platform_driver(am33xx_pm_driver);
608afe761f8SDave Gerlach
609afe761f8SDave Gerlach MODULE_ALIAS("platform:pm33xx");
610afe761f8SDave Gerlach MODULE_LICENSE("GPL v2");
611afe761f8SDave Gerlach MODULE_DESCRIPTION("am33xx power management driver");
612