xref: /kernel/linux/linux-6.6/drivers/soc/ti/pm33xx.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * AM33XX Power Management Routines
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012-2018 Texas Instruments Incorporated - http://www.ti.com/
662306a36Sopenharmony_ci *	Vaibhav Bedia, Dave Gerlach
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/clk.h>
1062306a36Sopenharmony_ci#include <linux/cpu.h>
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/genalloc.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/io.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/nvmem-consumer.h>
1862306a36Sopenharmony_ci#include <linux/of.h>
1962306a36Sopenharmony_ci#include <linux/of_address.h>
2062306a36Sopenharmony_ci#include <linux/platform_data/pm33xx.h>
2162306a36Sopenharmony_ci#include <linux/platform_device.h>
2262306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2362306a36Sopenharmony_ci#include <linux/rtc.h>
2462306a36Sopenharmony_ci#include <linux/rtc/rtc-omap.h>
2562306a36Sopenharmony_ci#include <linux/sizes.h>
2662306a36Sopenharmony_ci#include <linux/sram.h>
2762306a36Sopenharmony_ci#include <linux/suspend.h>
2862306a36Sopenharmony_ci#include <linux/ti-emif-sram.h>
2962306a36Sopenharmony_ci#include <linux/wkup_m3_ipc.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <asm/proc-fns.h>
3262306a36Sopenharmony_ci#include <asm/suspend.h>
3362306a36Sopenharmony_ci#include <asm/system_misc.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define AMX3_PM_SRAM_SYMBOL_OFFSET(sym) ((unsigned long)(sym) - \
3662306a36Sopenharmony_ci					 (unsigned long)pm_sram->do_wfi)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define RTC_SCRATCH_RESUME_REG	0
3962306a36Sopenharmony_ci#define RTC_SCRATCH_MAGIC_REG	1
4062306a36Sopenharmony_ci#define RTC_REG_BOOT_MAGIC	0x8cd0 /* RTC */
4162306a36Sopenharmony_ci#define GIC_INT_SET_PENDING_BASE 0x200
4262306a36Sopenharmony_ci#define AM43XX_GIC_DIST_BASE	0x48241000
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic void __iomem *rtc_base_virt;
4562306a36Sopenharmony_cistatic struct clk *rtc_fck;
4662306a36Sopenharmony_cistatic u32 rtc_magic_val;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int (*am33xx_do_wfi_sram)(unsigned long unused);
4962306a36Sopenharmony_cistatic phys_addr_t am33xx_do_wfi_sram_phys;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic struct gen_pool *sram_pool, *sram_pool_data;
5262306a36Sopenharmony_cistatic unsigned long ocmcram_location, ocmcram_location_data;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic struct rtc_device *omap_rtc;
5562306a36Sopenharmony_cistatic void __iomem *gic_dist_base;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic struct am33xx_pm_platform_data *pm_ops;
5862306a36Sopenharmony_cistatic struct am33xx_pm_sram_addr *pm_sram;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic struct device *pm33xx_dev;
6162306a36Sopenharmony_cistatic struct wkup_m3_ipc *m3_ipc;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#ifdef CONFIG_SUSPEND
6462306a36Sopenharmony_cistatic int rtc_only_idle;
6562306a36Sopenharmony_cistatic int retrigger_irq;
6662306a36Sopenharmony_cistatic unsigned long suspend_wfi_flags;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic struct wkup_m3_wakeup_src wakeup_src = {.irq_nr = 0,
6962306a36Sopenharmony_ci	.src = "Unknown",
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic struct wkup_m3_wakeup_src rtc_alarm_wakeup = {
7362306a36Sopenharmony_ci	.irq_nr = 108, .src = "RTC Alarm",
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic struct wkup_m3_wakeup_src rtc_ext_wakeup = {
7762306a36Sopenharmony_ci	.irq_nr = 0, .src = "Ext wakeup",
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci#endif
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic u32 sram_suspend_address(unsigned long addr)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	return ((unsigned long)am33xx_do_wfi_sram +
8462306a36Sopenharmony_ci		AMX3_PM_SRAM_SYMBOL_OFFSET(addr));
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int am33xx_push_sram_idle(void)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct am33xx_pm_ro_sram_data ro_sram_data;
9062306a36Sopenharmony_ci	int ret;
9162306a36Sopenharmony_ci	u32 table_addr, ro_data_addr;
9262306a36Sopenharmony_ci	void *copy_addr;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	ro_sram_data.amx3_pm_sram_data_virt = ocmcram_location_data;
9562306a36Sopenharmony_ci	ro_sram_data.amx3_pm_sram_data_phys =
9662306a36Sopenharmony_ci		gen_pool_virt_to_phys(sram_pool_data, ocmcram_location_data);
9762306a36Sopenharmony_ci	ro_sram_data.rtc_base_virt = rtc_base_virt;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	/* Save physical address to calculate resume offset during pm init */
10062306a36Sopenharmony_ci	am33xx_do_wfi_sram_phys = gen_pool_virt_to_phys(sram_pool,
10162306a36Sopenharmony_ci							ocmcram_location);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	am33xx_do_wfi_sram = sram_exec_copy(sram_pool, (void *)ocmcram_location,
10462306a36Sopenharmony_ci					    pm_sram->do_wfi,
10562306a36Sopenharmony_ci					    *pm_sram->do_wfi_sz);
10662306a36Sopenharmony_ci	if (!am33xx_do_wfi_sram) {
10762306a36Sopenharmony_ci		dev_err(pm33xx_dev,
10862306a36Sopenharmony_ci			"PM: %s: am33xx_do_wfi copy to sram failed\n",
10962306a36Sopenharmony_ci			__func__);
11062306a36Sopenharmony_ci		return -ENODEV;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	table_addr =
11462306a36Sopenharmony_ci		sram_suspend_address((unsigned long)pm_sram->emif_sram_table);
11562306a36Sopenharmony_ci	ret = ti_emif_copy_pm_function_table(sram_pool, (void *)table_addr);
11662306a36Sopenharmony_ci	if (ret) {
11762306a36Sopenharmony_ci		dev_dbg(pm33xx_dev,
11862306a36Sopenharmony_ci			"PM: %s: EMIF function copy failed\n", __func__);
11962306a36Sopenharmony_ci		return -EPROBE_DEFER;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	ro_data_addr =
12362306a36Sopenharmony_ci		sram_suspend_address((unsigned long)pm_sram->ro_sram_data);
12462306a36Sopenharmony_ci	copy_addr = sram_exec_copy(sram_pool, (void *)ro_data_addr,
12562306a36Sopenharmony_ci				   &ro_sram_data,
12662306a36Sopenharmony_ci				   sizeof(ro_sram_data));
12762306a36Sopenharmony_ci	if (!copy_addr) {
12862306a36Sopenharmony_ci		dev_err(pm33xx_dev,
12962306a36Sopenharmony_ci			"PM: %s: ro_sram_data copy to sram failed\n",
13062306a36Sopenharmony_ci			__func__);
13162306a36Sopenharmony_ci		return -ENODEV;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	return 0;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic int am33xx_do_sram_idle(u32 wfi_flags)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	if (!m3_ipc || !pm_ops)
14062306a36Sopenharmony_ci		return 0;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (wfi_flags & WFI_FLAG_WAKE_M3)
14362306a36Sopenharmony_ci		m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_IDLE);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return pm_ops->cpu_suspend(am33xx_do_wfi_sram, wfi_flags);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic int __init am43xx_map_gic(void)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	gic_dist_base = ioremap(AM43XX_GIC_DIST_BASE, SZ_4K);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (!gic_dist_base)
15362306a36Sopenharmony_ci		return -ENOMEM;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return 0;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci#ifdef CONFIG_SUSPEND
15962306a36Sopenharmony_cistatic struct wkup_m3_wakeup_src rtc_wake_src(void)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	u32 i;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	i = __raw_readl(rtc_base_virt + 0x44) & 0x40;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (i) {
16662306a36Sopenharmony_ci		retrigger_irq = rtc_alarm_wakeup.irq_nr;
16762306a36Sopenharmony_ci		return rtc_alarm_wakeup;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	retrigger_irq = rtc_ext_wakeup.irq_nr;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	return rtc_ext_wakeup;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic int am33xx_rtc_only_idle(unsigned long wfi_flags)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	omap_rtc_power_off_program(&omap_rtc->dev);
17862306a36Sopenharmony_ci	am33xx_do_wfi_sram(wfi_flags);
17962306a36Sopenharmony_ci	return 0;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/*
18362306a36Sopenharmony_ci * Note that the RTC module clock must be re-enabled only for rtc+ddr suspend.
18462306a36Sopenharmony_ci * And looks like the module can stay in SYSC_IDLE_SMART_WKUP mode configured
18562306a36Sopenharmony_ci * by the interconnect code just fine for both rtc+ddr suspend and retention
18662306a36Sopenharmony_ci * suspend.
18762306a36Sopenharmony_ci */
18862306a36Sopenharmony_cistatic int am33xx_pm_suspend(suspend_state_t suspend_state)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	int i, ret = 0;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (suspend_state == PM_SUSPEND_MEM &&
19362306a36Sopenharmony_ci	    pm_ops->check_off_mode_enable()) {
19462306a36Sopenharmony_ci		ret = clk_prepare_enable(rtc_fck);
19562306a36Sopenharmony_ci		if (ret) {
19662306a36Sopenharmony_ci			dev_err(pm33xx_dev, "Failed to enable clock: %i\n", ret);
19762306a36Sopenharmony_ci			return ret;
19862306a36Sopenharmony_ci		}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci		pm_ops->save_context();
20162306a36Sopenharmony_ci		suspend_wfi_flags |= WFI_FLAG_RTC_ONLY;
20262306a36Sopenharmony_ci		clk_save_context();
20362306a36Sopenharmony_ci		ret = pm_ops->soc_suspend(suspend_state, am33xx_rtc_only_idle,
20462306a36Sopenharmony_ci					  suspend_wfi_flags);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci		suspend_wfi_flags &= ~WFI_FLAG_RTC_ONLY;
20762306a36Sopenharmony_ci		dev_info(pm33xx_dev, "Entering RTC Only mode with DDR in self-refresh\n");
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci		if (!ret) {
21062306a36Sopenharmony_ci			clk_restore_context();
21162306a36Sopenharmony_ci			pm_ops->restore_context();
21262306a36Sopenharmony_ci			m3_ipc->ops->set_rtc_only(m3_ipc);
21362306a36Sopenharmony_ci			am33xx_push_sram_idle();
21462306a36Sopenharmony_ci		}
21562306a36Sopenharmony_ci	} else {
21662306a36Sopenharmony_ci		ret = pm_ops->soc_suspend(suspend_state, am33xx_do_wfi_sram,
21762306a36Sopenharmony_ci					  suspend_wfi_flags);
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (ret) {
22162306a36Sopenharmony_ci		dev_err(pm33xx_dev, "PM: Kernel suspend failure\n");
22262306a36Sopenharmony_ci	} else {
22362306a36Sopenharmony_ci		i = m3_ipc->ops->request_pm_status(m3_ipc);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		switch (i) {
22662306a36Sopenharmony_ci		case 0:
22762306a36Sopenharmony_ci			dev_info(pm33xx_dev,
22862306a36Sopenharmony_ci				 "PM: Successfully put all powerdomains to target state\n");
22962306a36Sopenharmony_ci			break;
23062306a36Sopenharmony_ci		case 1:
23162306a36Sopenharmony_ci			dev_err(pm33xx_dev,
23262306a36Sopenharmony_ci				"PM: Could not transition all powerdomains to target state\n");
23362306a36Sopenharmony_ci			ret = -1;
23462306a36Sopenharmony_ci			break;
23562306a36Sopenharmony_ci		default:
23662306a36Sopenharmony_ci			dev_err(pm33xx_dev,
23762306a36Sopenharmony_ci				"PM: CM3 returned unknown result = %d\n", i);
23862306a36Sopenharmony_ci			ret = -1;
23962306a36Sopenharmony_ci		}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci		/* print the wakeup reason */
24262306a36Sopenharmony_ci		if (rtc_only_idle) {
24362306a36Sopenharmony_ci			wakeup_src = rtc_wake_src();
24462306a36Sopenharmony_ci			pr_info("PM: Wakeup source %s\n", wakeup_src.src);
24562306a36Sopenharmony_ci		} else {
24662306a36Sopenharmony_ci			pr_info("PM: Wakeup source %s\n",
24762306a36Sopenharmony_ci				m3_ipc->ops->request_wake_src(m3_ipc));
24862306a36Sopenharmony_ci		}
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (suspend_state == PM_SUSPEND_MEM && pm_ops->check_off_mode_enable())
25262306a36Sopenharmony_ci		clk_disable_unprepare(rtc_fck);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return ret;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic int am33xx_pm_enter(suspend_state_t suspend_state)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	int ret = 0;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	switch (suspend_state) {
26262306a36Sopenharmony_ci	case PM_SUSPEND_MEM:
26362306a36Sopenharmony_ci	case PM_SUSPEND_STANDBY:
26462306a36Sopenharmony_ci		ret = am33xx_pm_suspend(suspend_state);
26562306a36Sopenharmony_ci		break;
26662306a36Sopenharmony_ci	default:
26762306a36Sopenharmony_ci		ret = -EINVAL;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return ret;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic int am33xx_pm_begin(suspend_state_t state)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	int ret = -EINVAL;
27662306a36Sopenharmony_ci	struct nvmem_device *nvmem;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (state == PM_SUSPEND_MEM && pm_ops->check_off_mode_enable()) {
27962306a36Sopenharmony_ci		nvmem = devm_nvmem_device_get(&omap_rtc->dev,
28062306a36Sopenharmony_ci					      "omap_rtc_scratch0");
28162306a36Sopenharmony_ci		if (!IS_ERR(nvmem))
28262306a36Sopenharmony_ci			nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4, 4,
28362306a36Sopenharmony_ci					   (void *)&rtc_magic_val);
28462306a36Sopenharmony_ci		rtc_only_idle = 1;
28562306a36Sopenharmony_ci	} else {
28662306a36Sopenharmony_ci		rtc_only_idle = 0;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	pm_ops->begin_suspend();
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	switch (state) {
29262306a36Sopenharmony_ci	case PM_SUSPEND_MEM:
29362306a36Sopenharmony_ci		ret = m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_DEEPSLEEP);
29462306a36Sopenharmony_ci		break;
29562306a36Sopenharmony_ci	case PM_SUSPEND_STANDBY:
29662306a36Sopenharmony_ci		ret = m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_STANDBY);
29762306a36Sopenharmony_ci		break;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return ret;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic void am33xx_pm_end(void)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	u32 val = 0;
30662306a36Sopenharmony_ci	struct nvmem_device *nvmem;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	nvmem = devm_nvmem_device_get(&omap_rtc->dev, "omap_rtc_scratch0");
30962306a36Sopenharmony_ci	if (IS_ERR(nvmem))
31062306a36Sopenharmony_ci		return;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	m3_ipc->ops->finish_low_power(m3_ipc);
31362306a36Sopenharmony_ci	if (rtc_only_idle) {
31462306a36Sopenharmony_ci		if (retrigger_irq) {
31562306a36Sopenharmony_ci			/*
31662306a36Sopenharmony_ci			 * 32 bits of Interrupt Set-Pending correspond to 32
31762306a36Sopenharmony_ci			 * 32 interrupts. Compute the bit offset of the
31862306a36Sopenharmony_ci			 * Interrupt and set that particular bit
31962306a36Sopenharmony_ci			 * Compute the register offset by dividing interrupt
32062306a36Sopenharmony_ci			 * number by 32 and mutiplying by 4
32162306a36Sopenharmony_ci			 */
32262306a36Sopenharmony_ci			writel_relaxed(1 << (retrigger_irq & 31),
32362306a36Sopenharmony_ci				       gic_dist_base + GIC_INT_SET_PENDING_BASE
32462306a36Sopenharmony_ci				       + retrigger_irq / 32 * 4);
32562306a36Sopenharmony_ci		}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci		nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4, 4,
32862306a36Sopenharmony_ci				   (void *)&val);
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	rtc_only_idle = 0;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	pm_ops->finish_suspend();
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic int am33xx_pm_valid(suspend_state_t state)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	switch (state) {
33962306a36Sopenharmony_ci	case PM_SUSPEND_STANDBY:
34062306a36Sopenharmony_ci	case PM_SUSPEND_MEM:
34162306a36Sopenharmony_ci		return 1;
34262306a36Sopenharmony_ci	default:
34362306a36Sopenharmony_ci		return 0;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic const struct platform_suspend_ops am33xx_pm_ops = {
34862306a36Sopenharmony_ci	.begin		= am33xx_pm_begin,
34962306a36Sopenharmony_ci	.end		= am33xx_pm_end,
35062306a36Sopenharmony_ci	.enter		= am33xx_pm_enter,
35162306a36Sopenharmony_ci	.valid		= am33xx_pm_valid,
35262306a36Sopenharmony_ci};
35362306a36Sopenharmony_ci#endif /* CONFIG_SUSPEND */
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic void am33xx_pm_set_ipc_ops(void)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	u32 resume_address;
35862306a36Sopenharmony_ci	int temp;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	temp = ti_emif_get_mem_type();
36162306a36Sopenharmony_ci	if (temp < 0) {
36262306a36Sopenharmony_ci		dev_err(pm33xx_dev, "PM: Cannot determine memory type, no PM available\n");
36362306a36Sopenharmony_ci		return;
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci	m3_ipc->ops->set_mem_type(m3_ipc, temp);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	/* Physical resume address to be used by ROM code */
36862306a36Sopenharmony_ci	resume_address = am33xx_do_wfi_sram_phys +
36962306a36Sopenharmony_ci			 *pm_sram->resume_offset + 0x4;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	m3_ipc->ops->set_resume_address(m3_ipc, (void *)resume_address);
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic void am33xx_pm_free_sram(void)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	gen_pool_free(sram_pool, ocmcram_location, *pm_sram->do_wfi_sz);
37762306a36Sopenharmony_ci	gen_pool_free(sram_pool_data, ocmcram_location_data,
37862306a36Sopenharmony_ci		      sizeof(struct am33xx_pm_ro_sram_data));
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci/*
38262306a36Sopenharmony_ci * Push the minimal suspend-resume code to SRAM
38362306a36Sopenharmony_ci */
38462306a36Sopenharmony_cistatic int am33xx_pm_alloc_sram(void)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct device_node *np;
38762306a36Sopenharmony_ci	int ret = 0;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "ti,omap3-mpu");
39062306a36Sopenharmony_ci	if (!np) {
39162306a36Sopenharmony_ci		np = of_find_compatible_node(NULL, NULL, "ti,omap4-mpu");
39262306a36Sopenharmony_ci		if (!np) {
39362306a36Sopenharmony_ci			dev_err(pm33xx_dev, "PM: %s: Unable to find device node for mpu\n",
39462306a36Sopenharmony_ci				__func__);
39562306a36Sopenharmony_ci			return -ENODEV;
39662306a36Sopenharmony_ci		}
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	sram_pool = of_gen_pool_get(np, "pm-sram", 0);
40062306a36Sopenharmony_ci	if (!sram_pool) {
40162306a36Sopenharmony_ci		dev_err(pm33xx_dev, "PM: %s: Unable to get sram pool for ocmcram\n",
40262306a36Sopenharmony_ci			__func__);
40362306a36Sopenharmony_ci		ret = -ENODEV;
40462306a36Sopenharmony_ci		goto mpu_put_node;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	sram_pool_data = of_gen_pool_get(np, "pm-sram", 1);
40862306a36Sopenharmony_ci	if (!sram_pool_data) {
40962306a36Sopenharmony_ci		dev_err(pm33xx_dev, "PM: %s: Unable to get sram data pool for ocmcram\n",
41062306a36Sopenharmony_ci			__func__);
41162306a36Sopenharmony_ci		ret = -ENODEV;
41262306a36Sopenharmony_ci		goto mpu_put_node;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	ocmcram_location = gen_pool_alloc(sram_pool, *pm_sram->do_wfi_sz);
41662306a36Sopenharmony_ci	if (!ocmcram_location) {
41762306a36Sopenharmony_ci		dev_err(pm33xx_dev, "PM: %s: Unable to allocate memory from ocmcram\n",
41862306a36Sopenharmony_ci			__func__);
41962306a36Sopenharmony_ci		ret = -ENOMEM;
42062306a36Sopenharmony_ci		goto mpu_put_node;
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	ocmcram_location_data = gen_pool_alloc(sram_pool_data,
42462306a36Sopenharmony_ci					       sizeof(struct emif_regs_amx3));
42562306a36Sopenharmony_ci	if (!ocmcram_location_data) {
42662306a36Sopenharmony_ci		dev_err(pm33xx_dev, "PM: Unable to allocate memory from ocmcram\n");
42762306a36Sopenharmony_ci		gen_pool_free(sram_pool, ocmcram_location, *pm_sram->do_wfi_sz);
42862306a36Sopenharmony_ci		ret = -ENOMEM;
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cimpu_put_node:
43262306a36Sopenharmony_ci	of_node_put(np);
43362306a36Sopenharmony_ci	return ret;
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_cistatic int am33xx_pm_rtc_setup(void)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	struct device_node *np;
43962306a36Sopenharmony_ci	unsigned long val = 0;
44062306a36Sopenharmony_ci	struct nvmem_device *nvmem;
44162306a36Sopenharmony_ci	int error;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	np = of_find_node_by_name(NULL, "rtc");
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (of_device_is_available(np)) {
44662306a36Sopenharmony_ci		/* RTC interconnect target module clock */
44762306a36Sopenharmony_ci		rtc_fck = of_clk_get_by_name(np->parent, "fck");
44862306a36Sopenharmony_ci		if (IS_ERR(rtc_fck))
44962306a36Sopenharmony_ci			return PTR_ERR(rtc_fck);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci		rtc_base_virt = of_iomap(np, 0);
45262306a36Sopenharmony_ci		if (!rtc_base_virt) {
45362306a36Sopenharmony_ci			pr_warn("PM: could not iomap rtc");
45462306a36Sopenharmony_ci			error = -ENODEV;
45562306a36Sopenharmony_ci			goto err_clk_put;
45662306a36Sopenharmony_ci		}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		omap_rtc = rtc_class_open("rtc0");
45962306a36Sopenharmony_ci		if (!omap_rtc) {
46062306a36Sopenharmony_ci			pr_warn("PM: rtc0 not available");
46162306a36Sopenharmony_ci			error = -EPROBE_DEFER;
46262306a36Sopenharmony_ci			goto err_iounmap;
46362306a36Sopenharmony_ci		}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		nvmem = devm_nvmem_device_get(&omap_rtc->dev,
46662306a36Sopenharmony_ci					      "omap_rtc_scratch0");
46762306a36Sopenharmony_ci		if (!IS_ERR(nvmem)) {
46862306a36Sopenharmony_ci			nvmem_device_read(nvmem, RTC_SCRATCH_MAGIC_REG * 4,
46962306a36Sopenharmony_ci					  4, (void *)&rtc_magic_val);
47062306a36Sopenharmony_ci			if ((rtc_magic_val & 0xffff) != RTC_REG_BOOT_MAGIC)
47162306a36Sopenharmony_ci				pr_warn("PM: bootloader does not support rtc-only!\n");
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci			nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4,
47462306a36Sopenharmony_ci					   4, (void *)&val);
47562306a36Sopenharmony_ci			val = pm_sram->resume_address;
47662306a36Sopenharmony_ci			nvmem_device_write(nvmem, RTC_SCRATCH_RESUME_REG * 4,
47762306a36Sopenharmony_ci					   4, (void *)&val);
47862306a36Sopenharmony_ci		}
47962306a36Sopenharmony_ci	} else {
48062306a36Sopenharmony_ci		pr_warn("PM: no-rtc available, rtc-only mode disabled.\n");
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	return 0;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cierr_iounmap:
48662306a36Sopenharmony_ci	iounmap(rtc_base_virt);
48762306a36Sopenharmony_cierr_clk_put:
48862306a36Sopenharmony_ci	clk_put(rtc_fck);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	return error;
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic int am33xx_pm_probe(struct platform_device *pdev)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
49662306a36Sopenharmony_ci	int ret;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	if (!of_machine_is_compatible("ti,am33xx") &&
49962306a36Sopenharmony_ci	    !of_machine_is_compatible("ti,am43"))
50062306a36Sopenharmony_ci		return -ENODEV;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	pm_ops = dev->platform_data;
50362306a36Sopenharmony_ci	if (!pm_ops) {
50462306a36Sopenharmony_ci		dev_err(dev, "PM: Cannot get core PM ops!\n");
50562306a36Sopenharmony_ci		return -ENODEV;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	ret = am43xx_map_gic();
50962306a36Sopenharmony_ci	if (ret) {
51062306a36Sopenharmony_ci		pr_err("PM: Could not ioremap GIC base\n");
51162306a36Sopenharmony_ci		return ret;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	pm_sram = pm_ops->get_sram_addrs();
51562306a36Sopenharmony_ci	if (!pm_sram) {
51662306a36Sopenharmony_ci		dev_err(dev, "PM: Cannot get PM asm function addresses!!\n");
51762306a36Sopenharmony_ci		return -ENODEV;
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	m3_ipc = wkup_m3_ipc_get();
52162306a36Sopenharmony_ci	if (!m3_ipc) {
52262306a36Sopenharmony_ci		pr_err("PM: Cannot get wkup_m3_ipc handle\n");
52362306a36Sopenharmony_ci		return -EPROBE_DEFER;
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	pm33xx_dev = dev;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	ret = am33xx_pm_alloc_sram();
52962306a36Sopenharmony_ci	if (ret)
53062306a36Sopenharmony_ci		goto err_wkup_m3_ipc_put;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	ret = am33xx_pm_rtc_setup();
53362306a36Sopenharmony_ci	if (ret)
53462306a36Sopenharmony_ci		goto err_free_sram;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	ret = am33xx_push_sram_idle();
53762306a36Sopenharmony_ci	if (ret)
53862306a36Sopenharmony_ci		goto err_unsetup_rtc;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	am33xx_pm_set_ipc_ops();
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci#ifdef CONFIG_SUSPEND
54362306a36Sopenharmony_ci	suspend_set_ops(&am33xx_pm_ops);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	/*
54662306a36Sopenharmony_ci	 * For a system suspend we must flush the caches, we want
54762306a36Sopenharmony_ci	 * the DDR in self-refresh, we want to save the context
54862306a36Sopenharmony_ci	 * of the EMIF, and we want the wkup_m3 to handle low-power
54962306a36Sopenharmony_ci	 * transition.
55062306a36Sopenharmony_ci	 */
55162306a36Sopenharmony_ci	suspend_wfi_flags |= WFI_FLAG_FLUSH_CACHE;
55262306a36Sopenharmony_ci	suspend_wfi_flags |= WFI_FLAG_SELF_REFRESH;
55362306a36Sopenharmony_ci	suspend_wfi_flags |= WFI_FLAG_SAVE_EMIF;
55462306a36Sopenharmony_ci	suspend_wfi_flags |= WFI_FLAG_WAKE_M3;
55562306a36Sopenharmony_ci#endif /* CONFIG_SUSPEND */
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	pm_runtime_enable(dev);
55862306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(dev);
55962306a36Sopenharmony_ci	if (ret < 0)
56062306a36Sopenharmony_ci		goto err_pm_runtime_disable;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	ret = pm_ops->init(am33xx_do_sram_idle);
56362306a36Sopenharmony_ci	if (ret) {
56462306a36Sopenharmony_ci		dev_err(dev, "Unable to call core pm init!\n");
56562306a36Sopenharmony_ci		ret = -ENODEV;
56662306a36Sopenharmony_ci		goto err_pm_runtime_put;
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	return 0;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_cierr_pm_runtime_put:
57262306a36Sopenharmony_ci	pm_runtime_put_sync(dev);
57362306a36Sopenharmony_cierr_pm_runtime_disable:
57462306a36Sopenharmony_ci	pm_runtime_disable(dev);
57562306a36Sopenharmony_cierr_unsetup_rtc:
57662306a36Sopenharmony_ci	iounmap(rtc_base_virt);
57762306a36Sopenharmony_ci	clk_put(rtc_fck);
57862306a36Sopenharmony_cierr_free_sram:
57962306a36Sopenharmony_ci	am33xx_pm_free_sram();
58062306a36Sopenharmony_ci	pm33xx_dev = NULL;
58162306a36Sopenharmony_cierr_wkup_m3_ipc_put:
58262306a36Sopenharmony_ci	wkup_m3_ipc_put(m3_ipc);
58362306a36Sopenharmony_ci	return ret;
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cistatic int am33xx_pm_remove(struct platform_device *pdev)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	pm_runtime_put_sync(&pdev->dev);
58962306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
59062306a36Sopenharmony_ci	if (pm_ops->deinit)
59162306a36Sopenharmony_ci		pm_ops->deinit();
59262306a36Sopenharmony_ci	suspend_set_ops(NULL);
59362306a36Sopenharmony_ci	wkup_m3_ipc_put(m3_ipc);
59462306a36Sopenharmony_ci	am33xx_pm_free_sram();
59562306a36Sopenharmony_ci	iounmap(rtc_base_virt);
59662306a36Sopenharmony_ci	clk_put(rtc_fck);
59762306a36Sopenharmony_ci	return 0;
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic struct platform_driver am33xx_pm_driver = {
60162306a36Sopenharmony_ci	.driver = {
60262306a36Sopenharmony_ci		.name   = "pm33xx",
60362306a36Sopenharmony_ci	},
60462306a36Sopenharmony_ci	.probe = am33xx_pm_probe,
60562306a36Sopenharmony_ci	.remove = am33xx_pm_remove,
60662306a36Sopenharmony_ci};
60762306a36Sopenharmony_cimodule_platform_driver(am33xx_pm_driver);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ciMODULE_ALIAS("platform:pm33xx");
61062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
61162306a36Sopenharmony_ciMODULE_DESCRIPTION("am33xx power management driver");
612