PM changes for am335x and am437x

This series adds support for am437x RTC-only mode in suspend. In the
 RTC-only mode suspend, everything is shut down except the RTC. This
 makes the power consumption very low for suspend mode.
 
 To support RTC-only mode, we need to export omap_rtc_power_off_program()
 from the rtc driver and improve PM code to save and restore the wkup
 domain context. As RTC-only mode depends on the device being wired
 properly for things like memory, we need to also check for the machine
 type before we allow it. We also need to run DDR3 hardware leveling on
 resume.
 
 Note that there is a trivial merge conflict between the RTC branch
 and these changes where the RTC branch makes tm2bcd() a void function
 and the error handling parts can be just dropped.
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEEkgNvrZJU/QSQYIcQG9Q+yVyrpXMFAlywwLIRHHRvbnlAYXRv
 bWlkZS5jb20ACgkQG9Q+yVyrpXOBhg/+NRYTBaO4pmFKlTMKs+bgdpftHf6wPSID
 kUy62YiKhMMgbe6J+Ts/Hk2kCibuOfFmQ0axGFdHzMhpwTGThsWFiwaVr7OKF6ZE
 DwOlJ1ZwM2OXcdf8wVupRL/54Tg1Wjc6y/T63Q5K+IqKz7gjdf6n7CXdYn22qTmp
 4WK81dW1fx+9QNBbGBmMXEbNRlEImxeZRnWF1zJvydOCxO0jRbgnlGgpZJr36TeL
 Jj+HN+4fD6WmaQ3Slbgcr0jbAowjjTuyUfFwzEBe9G2KlxRyrxDd4ryY78tNxcIe
 L+yNMm7RGHD1Mb0+tdmpXESuj+O4hsuXglnK5vl19yc95nbwbTeRu0sv+WBEAal4
 mY7xTMzT08rLT7fDXuJ+5vZNbR1TLGIekmNnj5BvV8LyZ9ypXToOlOCtFonQxULu
 HFsiDkOOdKQhkuzw+V8EBksMXdD5crTVxyHY95hguvWTBUMTJjBo0Bq/9EmhKx0m
 HOglctIivV1/GFg/82QKatjXLLeuXtFqUJa7lc6GIkVteAvEhrbg2DQ8QhnZ8Zg+
 ghx0kKsA+KXvOItI6JrMjk/GQuwXTcIytkUhfTVuVlN+gV2PbGFCbohJOatwtXkp
 0+VQ0H6AN04FJPqgLDR1njvks6Ll503gbnqodDNL/xgaN+Pjw5zTabkM/hdIyCNV
 KS6XhdlYvtQ=
 =yNbG
 -----END PGP SIGNATURE-----

Merge tag 'omap-for-v5.2/am4-pm-v2-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap into arm/drivers

PM changes for am335x and am437x

This series adds support for am437x RTC-only mode in suspend. In the
RTC-only mode suspend, everything is shut down except the RTC. This
makes the power consumption very low for suspend mode.

To support RTC-only mode, we need to export omap_rtc_power_off_program()
from the rtc driver and improve PM code to save and restore the wkup
domain context. As RTC-only mode depends on the device being wired
properly for things like memory, we need to also check for the machine
type before we allow it. We also need to run DDR3 hardware leveling on
resume.

Note that there is a trivial merge conflict between the RTC branch
and these changes where the RTC branch makes tm2bcd() a void function
and the error handling parts can be just dropped.

* tag 'omap-for-v5.2/am4-pm-v2-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap:
  ARM: OMAP2+: sleep43xx: Run EMIF HW leveling on resume path
  memory: ti-emif-sram: Add ti_emif_run_hw_leveling for DDR3 hardware leveling
  soc: ti: pm33xx: AM437X: Add rtc_only with ddr in self-refresh support
  soc: ti: pm33xx: Move the am33xx_push_sram_idle to the top
  ARM: OMAP2+: pm33xx: Add support for rtc+ddr in self refresh mode
  rtc: OMAP: Add support for rtc-only mode

Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
Olof Johansson 2019-04-28 23:04:13 -07:00
commit 0065198e67
11 changed files with 408 additions and 61 deletions

View File

@ -10,6 +10,12 @@
#include <asm/suspend.h>
#include <linux/errno.h>
#include <linux/platform_data/pm33xx.h>
#include <linux/clk.h>
#include <linux/platform_data/gpio-omap.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/wkup_m3_ipc.h>
#include <linux/of.h>
#include <linux/rtc.h>
#include "cm33xx.h"
#include "common.h"
@ -38,6 +44,29 @@ static int am43xx_map_scu(void)
return 0;
}
static int am33xx_check_off_mode_enable(void)
{
if (enable_off_mode)
pr_warn("WARNING: This platform does not support off-mode, entering DeepSleep suspend.\n");
/* off mode not supported on am335x so return 0 always */
return 0;
}
static int am43xx_check_off_mode_enable(void)
{
/*
* Check for am437x-gp-evm which has the right Hardware design to
* support this mode reliably.
*/
if (of_machine_is_compatible("ti,am437x-gp-evm") && enable_off_mode)
return enable_off_mode;
else if (enable_off_mode)
pr_warn("WARNING: This platform does not support off-mode, entering DeepSleep suspend.\n");
return 0;
}
static int amx3_common_init(void)
{
gfx_pwrdm = pwrdm_lookup("gfx_pwrdm");
@ -139,7 +168,9 @@ static int am43xx_suspend(unsigned int state, int (*fn)(unsigned long),
scu_power_mode(scu_base, SCU_PM_POWEROFF);
ret = cpu_suspend(args, fn);
scu_power_mode(scu_base, SCU_PM_NORMAL);
amx3_post_suspend_common();
if (!am43xx_check_off_mode_enable())
amx3_post_suspend_common();
return ret;
}
@ -161,10 +192,48 @@ void __iomem *am43xx_get_rtc_base_addr(void)
return omap_hwmod_get_mpu_rt_va(rtc_oh);
}
static void am43xx_save_context(void)
{
}
static void am33xx_save_context(void)
{
omap_intc_save_context();
}
static void am33xx_restore_context(void)
{
omap_intc_restore_context();
}
static void am43xx_restore_context(void)
{
/*
* HACK: restore dpll_per_clkdcoldo register contents, to avoid
* breaking suspend-resume
*/
writel_relaxed(0x0, AM33XX_L4_WK_IO_ADDRESS(0x44df2e14));
}
static void am43xx_prepare_rtc_suspend(void)
{
omap_hwmod_enable(rtc_oh);
}
static void am43xx_prepare_rtc_resume(void)
{
omap_hwmod_idle(rtc_oh);
}
static struct am33xx_pm_platform_data am33xx_ops = {
.init = am33xx_suspend_init,
.soc_suspend = am33xx_suspend,
.get_sram_addrs = amx3_get_sram_addrs,
.save_context = am33xx_save_context,
.restore_context = am33xx_restore_context,
.prepare_rtc_suspend = am43xx_prepare_rtc_suspend,
.prepare_rtc_resume = am43xx_prepare_rtc_resume,
.check_off_mode_enable = am33xx_check_off_mode_enable,
.get_rtc_base_addr = am43xx_get_rtc_base_addr,
};
@ -172,6 +241,11 @@ static struct am33xx_pm_platform_data am43xx_ops = {
.init = am43xx_suspend_init,
.soc_suspend = am43xx_suspend,
.get_sram_addrs = amx3_get_sram_addrs,
.save_context = am43xx_save_context,
.restore_context = am43xx_restore_context,
.prepare_rtc_suspend = am43xx_prepare_rtc_suspend,
.prepare_rtc_resume = am43xx_prepare_rtc_resume,
.check_off_mode_enable = am43xx_check_off_mode_enable,
.get_rtc_base_addr = am43xx_get_rtc_base_addr,
};

View File

@ -368,6 +368,9 @@ wait_emif_enable1:
mov r1, #AM43XX_EMIF_POWEROFF_DISABLE
str r1, [r2, #0x0]
ldr r1, [r9, #EMIF_PM_RUN_HW_LEVELING]
blx r1
#ifdef CONFIG_CACHE_L2X0
ldr r2, l2_cache_base
ldr r0, [r2, #L2X0_CTRL]

View File

@ -537,6 +537,9 @@
#define MCONNID_SHIFT 0
#define MCONNID_MASK (0xff << 0)
/* READ_WRITE_LEVELING_CONTROL */
#define RDWRLVLFULL_START 0x80000000
/* DDR_PHY_CTRL_1 - EMIF4D */
#define DLL_SLAVE_DLY_CTRL_SHIFT_4D 4
#define DLL_SLAVE_DLY_CTRL_MASK_4D (0xFF << 4)
@ -598,6 +601,7 @@ extern struct emif_regs_amx3 ti_emif_regs_amx3;
void ti_emif_save_context(void);
void ti_emif_restore_context(void);
void ti_emif_run_hw_leveling(void);
void ti_emif_enter_sr(void);
void ti_emif_exit_sr(void);
void ti_emif_abort_sr(void);

View File

@ -138,6 +138,9 @@ static int ti_emif_alloc_sram(struct device *dev,
emif_data->pm_functions.exit_sr =
sram_resume_address(emif_data,
(unsigned long)ti_emif_exit_sr);
emif_data->pm_functions.run_hw_leveling =
sram_resume_address(emif_data,
(unsigned long)ti_emif_run_hw_leveling);
emif_data->pm_data.regs_virt =
(struct emif_regs_amx3 *)emif_data->ti_emif_sram_data_virt;

View File

@ -27,6 +27,7 @@
#define EMIF_POWER_MGMT_SELF_REFRESH_MODE_MASK 0x0700
#define EMIF_SDCFG_TYPE_DDR2 0x2 << SDRAM_TYPE_SHIFT
#define EMIF_SDCFG_TYPE_DDR3 0x3 << SDRAM_TYPE_SHIFT
#define EMIF_STATUS_READY 0x4
#define AM43XX_EMIF_PHY_CTRL_REG_COUNT 0x120
@ -244,6 +245,46 @@ emif_skip_restore_extra_regs:
mov pc, lr
ENDPROC(ti_emif_restore_context)
/*
* void ti_emif_run_hw_leveling(void)
*
* Used during resume to run hardware leveling again and restore the
* configuration of the EMIF PHY, only for DDR3.
*/
ENTRY(ti_emif_run_hw_leveling)
adr r4, ti_emif_pm_sram_data
ldr r0, [r4, #EMIF_PM_BASE_ADDR_PHYS_OFFSET]
ldr r3, [r0, #EMIF_READ_WRITE_LEVELING_CONTROL]
orr r3, r3, #RDWRLVLFULL_START
ldr r2, [r0, #EMIF_SDRAM_CONFIG]
and r2, r2, #SDRAM_TYPE_MASK
cmp r2, #EMIF_SDCFG_TYPE_DDR3
bne skip_hwlvl
str r3, [r0, #EMIF_READ_WRITE_LEVELING_CONTROL]
/*
* If EMIF registers are touched during initial stage of HW
* leveling sequence there will be an L3 NOC timeout error issued
* as the EMIF will not respond, which is not fatal, but it is
* avoidable. This small wait loop is enough time for this condition
* to clear, even at worst case of CPU running at max speed of 1Ghz.
*/
mov r2, #0x2000
1:
subs r2, r2, #0x1
bne 1b
/* Bit clears when operation is complete */
2: ldr r1, [r0, #EMIF_READ_WRITE_LEVELING_CONTROL]
tst r1, #RDWRLVLFULL_START
bne 2b
skip_hwlvl:
mov pc, lr
ENDPROC(ti_emif_run_hw_leveling)
/*
* void ti_emif_enter_sr(void)
*

View File

@ -415,15 +415,12 @@ static int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
static struct omap_rtc *omap_rtc_power_off_rtc;
/*
* omap_rtc_poweroff: RTC-controlled power off
*
* The RTC can be used to control an external PMIC via the pmic_power_en pin,
* which can be configured to transition to OFF on ALARM2 events.
*
* Called with local interrupts disabled.
/**
* omap_rtc_power_off_program: Set the pmic power off sequence. The RTC
* generates pmic_pwr_enable control, which can be used to control an external
* PMIC.
*/
static void omap_rtc_power_off(void)
int omap_rtc_power_off_program(struct device *dev)
{
struct omap_rtc *rtc = omap_rtc_power_off_rtc;
struct rtc_time tm;
@ -437,6 +434,9 @@ static void omap_rtc_power_off(void)
rtc_writel(rtc, OMAP_RTC_PMIC_REG, val | OMAP_RTC_PMIC_POWER_EN_EN);
again:
/* Clear any existing ALARM2 event */
rtc_writel(rtc, OMAP_RTC_STATUS_REG, OMAP_RTC_STATUS_ALARM2);
/* set alarm one second from now */
omap_rtc_read_time_raw(rtc, &tm);
seconds = tm.tm_sec;
@ -447,7 +447,7 @@ again:
if (tm2bcd(&tm) < 0) {
dev_err(&rtc->rtc->dev, "power off failed\n");
rtc->type->lock(rtc);
return;
return -EINVAL;
}
rtc_wait_not_busy(rtc);
@ -477,6 +477,39 @@ again:
rtc->type->lock(rtc);
return 0;
}
EXPORT_SYMBOL(omap_rtc_power_off_program);
/*
* omap_rtc_poweroff: RTC-controlled power off
*
* The RTC can be used to control an external PMIC via the pmic_power_en pin,
* which can be configured to transition to OFF on ALARM2 events.
*
* Notes:
* The one-second alarm offset is the shortest offset possible as the alarm
* registers must be set before the next timer update and the offset
* calculation is too heavy for everything to be done within a single access
* period (~15 us).
*
* Called with local interrupts disabled.
*/
static void omap_rtc_power_off(void)
{
struct rtc_device *rtc = omap_rtc_power_off_rtc->rtc;
u32 val;
omap_rtc_power_off_program(rtc->dev.parent);
/* Set PMIC power enable and EXT_WAKEUP in case PB power on is used */
omap_rtc_power_off_rtc->type->unlock(omap_rtc_power_off_rtc);
val = rtc_readl(omap_rtc_power_off_rtc, OMAP_RTC_PMIC_REG);
val |= OMAP_RTC_PMIC_POWER_EN_EN | OMAP_RTC_PMIC_EXT_WKUP_POL(0) |
OMAP_RTC_PMIC_EXT_WKUP_EN(0);
rtc_writel(omap_rtc_power_off_rtc, OMAP_RTC_PMIC_REG, val);
omap_rtc_power_off_rtc->type->lock(omap_rtc_power_off_rtc);
/*
* Wait for alarm to trigger (within one second) and external PMIC to
* power off the system. Add a 500 ms margin for external latencies

View File

@ -45,11 +45,12 @@ config KEYSTONE_NAVIGATOR_DMA
config AMX3_PM
tristate "AMx3 Power Management"
depends on SOC_AM33XX || SOC_AM43XX
depends on WKUP_M3_IPC && TI_EMIF_SRAM && SRAM
depends on WKUP_M3_IPC && TI_EMIF_SRAM && SRAM && RTC_DRV_OMAP
help
Enable power management on AM335x and AM437x. Required for suspend to mem
and standby states on both AM335x and AM437x platforms and for deeper cpuidle
c-states on AM335x.
c-states on AM335x. Also required for rtc and ddr in self-refresh low
power mode on AM437x platforms.
config WKUP_M3_IPC
tristate "TI AMx3 Wkup-M3 IPC Driver"

View File

@ -6,6 +6,7 @@
* Vaibhav Bedia, Dave Gerlach
*/
#include <linux/clk.h>
#include <linux/cpu.h>
#include <linux/err.h>
#include <linux/genalloc.h>
@ -13,9 +14,12 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/platform_data/pm33xx.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <linux/rtc/rtc-omap.h>
#include <linux/sizes.h>
#include <linux/sram.h>
#include <linux/suspend.h>
@ -29,33 +33,162 @@
#define AMX3_PM_SRAM_SYMBOL_OFFSET(sym) ((unsigned long)(sym) - \
(unsigned long)pm_sram->do_wfi)
#define RTC_SCRATCH_RESUME_REG 0
#define RTC_SCRATCH_MAGIC_REG 1
#define RTC_REG_BOOT_MAGIC 0x8cd0 /* RTC */
#define GIC_INT_SET_PENDING_BASE 0x200
#define AM43XX_GIC_DIST_BASE 0x48241000
static u32 rtc_magic_val;
static int (*am33xx_do_wfi_sram)(unsigned long unused);
static phys_addr_t am33xx_do_wfi_sram_phys;
static struct gen_pool *sram_pool, *sram_pool_data;
static unsigned long ocmcram_location, ocmcram_location_data;
static struct rtc_device *omap_rtc;
static void __iomem *gic_dist_base;
static struct am33xx_pm_platform_data *pm_ops;
static struct am33xx_pm_sram_addr *pm_sram;
static struct device *pm33xx_dev;
static struct wkup_m3_ipc *m3_ipc;
#ifdef CONFIG_SUSPEND
static int rtc_only_idle;
static int retrigger_irq;
static unsigned long suspend_wfi_flags;
static struct wkup_m3_wakeup_src wakeup_src = {.irq_nr = 0,
.src = "Unknown",
};
static struct wkup_m3_wakeup_src rtc_alarm_wakeup = {
.irq_nr = 108, .src = "RTC Alarm",
};
static struct wkup_m3_wakeup_src rtc_ext_wakeup = {
.irq_nr = 0, .src = "Ext wakeup",
};
#endif
static u32 sram_suspend_address(unsigned long addr)
{
return ((unsigned long)am33xx_do_wfi_sram +
AMX3_PM_SRAM_SYMBOL_OFFSET(addr));
}
static int am33xx_push_sram_idle(void)
{
struct am33xx_pm_ro_sram_data ro_sram_data;
int ret;
u32 table_addr, ro_data_addr;
void *copy_addr;
ro_sram_data.amx3_pm_sram_data_virt = ocmcram_location_data;
ro_sram_data.amx3_pm_sram_data_phys =
gen_pool_virt_to_phys(sram_pool_data, ocmcram_location_data);
ro_sram_data.rtc_base_virt = pm_ops->get_rtc_base_addr();
/* Save physical address to calculate resume offset during pm init */
am33xx_do_wfi_sram_phys = gen_pool_virt_to_phys(sram_pool,
ocmcram_location);
am33xx_do_wfi_sram = sram_exec_copy(sram_pool, (void *)ocmcram_location,
pm_sram->do_wfi,
*pm_sram->do_wfi_sz);
if (!am33xx_do_wfi_sram) {
dev_err(pm33xx_dev,
"PM: %s: am33xx_do_wfi copy to sram failed\n",
__func__);
return -ENODEV;
}
table_addr =
sram_suspend_address((unsigned long)pm_sram->emif_sram_table);
ret = ti_emif_copy_pm_function_table(sram_pool, (void *)table_addr);
if (ret) {
dev_dbg(pm33xx_dev,
"PM: %s: EMIF function copy failed\n", __func__);
return -EPROBE_DEFER;
}
ro_data_addr =
sram_suspend_address((unsigned long)pm_sram->ro_sram_data);
copy_addr = sram_exec_copy(sram_pool, (void *)ro_data_addr,
&ro_sram_data,
sizeof(ro_sram_data));
if (!copy_addr) {
dev_err(pm33xx_dev,
"PM: %s: ro_sram_data copy to sram failed\n",
__func__);
return -ENODEV;
}
return 0;
}
static int __init am43xx_map_gic(void)
{
gic_dist_base = ioremap(AM43XX_GIC_DIST_BASE, SZ_4K);
if (!gic_dist_base)
return -ENOMEM;
return 0;
}
#ifdef CONFIG_SUSPEND
struct wkup_m3_wakeup_src rtc_wake_src(void)
{
u32 i;
i = __raw_readl(pm_ops->get_rtc_base_addr() + 0x44) & 0x40;
if (i) {
retrigger_irq = rtc_alarm_wakeup.irq_nr;
return rtc_alarm_wakeup;
}
retrigger_irq = rtc_ext_wakeup.irq_nr;
return rtc_ext_wakeup;
}
int am33xx_rtc_only_idle(unsigned long wfi_flags)
{
omap_rtc_power_off_program(&omap_rtc->dev);
am33xx_do_wfi_sram(wfi_flags);
return 0;
}
static int am33xx_pm_suspend(suspend_state_t suspend_state)
{
int i, ret = 0;
ret = pm_ops->soc_suspend((unsigned long)suspend_state,
am33xx_do_wfi_sram, suspend_wfi_flags);
if (suspend_state == PM_SUSPEND_MEM &&
pm_ops->check_off_mode_enable()) {
pm_ops->prepare_rtc_suspend();
pm_ops->save_context();
suspend_wfi_flags |= WFI_FLAG_RTC_ONLY;
clk_save_context();
ret = pm_ops->soc_suspend(suspend_state, am33xx_rtc_only_idle,
suspend_wfi_flags);
suspend_wfi_flags &= ~WFI_FLAG_RTC_ONLY;
if (!ret) {
clk_restore_context();
pm_ops->restore_context();
m3_ipc->ops->set_rtc_only(m3_ipc);
am33xx_push_sram_idle();
}
} else {
ret = pm_ops->soc_suspend(suspend_state, am33xx_do_wfi_sram,
suspend_wfi_flags);
}
if (ret) {
dev_err(pm33xx_dev, "PM: Kernel suspend failure\n");
@ -77,8 +210,20 @@ static int am33xx_pm_suspend(suspend_state_t suspend_state)
"PM: CM3 returned unknown result = %d\n", i);
ret = -1;
}
/* print the wakeup reason */
if (rtc_only_idle) {
wakeup_src = rtc_wake_src();
pr_info("PM: Wakeup source %s\n", wakeup_src.src);
} else {
pr_info("PM: Wakeup source %s\n",
m3_ipc->ops->request_wake_src(m3_ipc));
}
}
if (suspend_state == PM_SUSPEND_MEM && pm_ops->check_off_mode_enable())
pm_ops->prepare_rtc_resume();
return ret;
}
@ -101,6 +246,18 @@ static int am33xx_pm_enter(suspend_state_t suspend_state)
static int am33xx_pm_begin(suspend_state_t state)
{
int ret = -EINVAL;
struct nvmem_device *nvmem;
if (state == PM_SUSPEND_MEM && pm_ops->check_off_mode_enable()) {
nvmem = devm_nvmem_device_get(&omap_rtc->dev,
"omap_rtc_scratch0");
if (nvmem)
nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4, 4,
(void *)&rtc_magic_val);
rtc_only_idle = 1;
} else {
rtc_only_idle = 0;
}
switch (state) {
case PM_SUSPEND_MEM:
@ -116,7 +273,28 @@ static int am33xx_pm_begin(suspend_state_t state)
static void am33xx_pm_end(void)
{
u32 val = 0;
struct nvmem_device *nvmem;
nvmem = devm_nvmem_device_get(&omap_rtc->dev, "omap_rtc_scratch0");
m3_ipc->ops->finish_low_power(m3_ipc);
if (rtc_only_idle) {
if (retrigger_irq)
/*
* 32 bits of Interrupt Set-Pending correspond to 32
* 32 interrupts. Compute the bit offset of the
* Interrupt and set that particular bit
* Compute the register offset by dividing interrupt
* number by 32 and mutiplying by 4
*/
writel_relaxed(1 << (retrigger_irq & 31),
gic_dist_base + GIC_INT_SET_PENDING_BASE
+ retrigger_irq / 32 * 4);
nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4, 4,
(void *)&val);
}
rtc_only_idle = 0;
}
static int am33xx_pm_valid(suspend_state_t state)
@ -219,51 +397,37 @@ mpu_put_node:
return ret;
}
static int am33xx_push_sram_idle(void)
static int am33xx_pm_rtc_setup(void)
{
struct am33xx_pm_ro_sram_data ro_sram_data;
int ret;
u32 table_addr, ro_data_addr;
void *copy_addr;
struct device_node *np;
unsigned long val = 0;
struct nvmem_device *nvmem;
ro_sram_data.amx3_pm_sram_data_virt = ocmcram_location_data;
ro_sram_data.amx3_pm_sram_data_phys =
gen_pool_virt_to_phys(sram_pool_data, ocmcram_location_data);
ro_sram_data.rtc_base_virt = pm_ops->get_rtc_base_addr();
np = of_find_node_by_name(NULL, "rtc");
/* Save physical address to calculate resume offset during pm init */
am33xx_do_wfi_sram_phys = gen_pool_virt_to_phys(sram_pool,
ocmcram_location);
if (of_device_is_available(np)) {
omap_rtc = rtc_class_open("rtc0");
if (!omap_rtc) {
pr_warn("PM: rtc0 not available");
return -EPROBE_DEFER;
}
am33xx_do_wfi_sram = sram_exec_copy(sram_pool, (void *)ocmcram_location,
pm_sram->do_wfi,
*pm_sram->do_wfi_sz);
if (!am33xx_do_wfi_sram) {
dev_err(pm33xx_dev,
"PM: %s: am33xx_do_wfi copy to sram failed\n",
__func__);
return -ENODEV;
}
nvmem = devm_nvmem_device_get(&omap_rtc->dev,
"omap_rtc_scratch0");
if (nvmem) {
nvmem_device_read(nvmem, RTC_SCRATCH_MAGIC_REG * 4,
4, (void *)&rtc_magic_val);
if ((rtc_magic_val & 0xffff) != RTC_REG_BOOT_MAGIC)
pr_warn("PM: bootloader does not support rtc-only!\n");
table_addr =
sram_suspend_address((unsigned long)pm_sram->emif_sram_table);
ret = ti_emif_copy_pm_function_table(sram_pool, (void *)table_addr);
if (ret) {
dev_dbg(pm33xx_dev,
"PM: %s: EMIF function copy failed\n", __func__);
return -EPROBE_DEFER;
}
ro_data_addr =
sram_suspend_address((unsigned long)pm_sram->ro_sram_data);
copy_addr = sram_exec_copy(sram_pool, (void *)ro_data_addr,
&ro_sram_data,
sizeof(ro_sram_data));
if (!copy_addr) {
dev_err(pm33xx_dev,
"PM: %s: ro_sram_data copy to sram failed\n",
__func__);
return -ENODEV;
nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4,
4, (void *)&val);
val = pm_sram->resume_address;
nvmem_device_write(nvmem, RTC_SCRATCH_RESUME_REG * 4,
4, (void *)&val);
}
} else {
pr_warn("PM: no-rtc available, rtc-only mode disabled.\n");
}
return 0;
@ -284,34 +448,42 @@ static int am33xx_pm_probe(struct platform_device *pdev)
return -ENODEV;
}
ret = am43xx_map_gic();
if (ret) {
pr_err("PM: Could not ioremap GIC base\n");
return ret;
}
pm_sram = pm_ops->get_sram_addrs();
if (!pm_sram) {
dev_err(dev, "PM: Cannot get PM asm function addresses!!\n");
return -ENODEV;
}
m3_ipc = wkup_m3_ipc_get();
if (!m3_ipc) {
pr_err("PM: Cannot get wkup_m3_ipc handle\n");
return -EPROBE_DEFER;
}
pm33xx_dev = dev;
ret = am33xx_pm_alloc_sram();
if (ret)
return ret;
ret = am33xx_push_sram_idle();
ret = am33xx_pm_rtc_setup();
if (ret)
goto err_free_sram;
m3_ipc = wkup_m3_ipc_get();
if (!m3_ipc) {
dev_dbg(dev, "PM: Cannot get wkup_m3_ipc handle\n");
ret = -EPROBE_DEFER;
ret = am33xx_push_sram_idle();
if (ret)
goto err_free_sram;
}
am33xx_pm_set_ipc_ops();
#ifdef CONFIG_SUSPEND
suspend_set_ops(&am33xx_pm_ops);
#endif /* CONFIG_SUSPEND */
/*
* For a system suspend we must flush the caches, we want
@ -323,6 +495,7 @@ static int am33xx_pm_probe(struct platform_device *pdev)
suspend_wfi_flags |= WFI_FLAG_SELF_REFRESH;
suspend_wfi_flags |= WFI_FLAG_SAVE_EMIF;
suspend_wfi_flags |= WFI_FLAG_WAKE_M3;
#endif /* CONFIG_SUSPEND */
ret = pm_ops->init();
if (ret) {

View File

@ -51,6 +51,11 @@ struct am33xx_pm_platform_data {
unsigned long args);
struct am33xx_pm_sram_addr *(*get_sram_addrs)(void);
void __iomem *(*get_rtc_base_addr)(void);
void (*save_context)(void);
void (*restore_context)(void);
void (*prepare_rtc_suspend)(void);
void (*prepare_rtc_resume)(void);
int (*check_off_mode_enable)(void);
};
struct am33xx_pm_sram_data {

View File

@ -0,0 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_RTCOMAP_H_
#define _LINUX_RTCOMAP_H_
int omap_rtc_power_off_program(struct device *dev);
#endif /* _LINUX_RTCOMAP_H_ */

View File

@ -55,6 +55,7 @@ struct ti_emif_pm_data {
struct ti_emif_pm_functions {
u32 save_context;
u32 restore_context;
u32 run_hw_leveling;
u32 enter_sr;
u32 exit_sr;
u32 abort_sr;
@ -126,6 +127,8 @@ static inline void ti_emif_asm_offsets(void)
offsetof(struct ti_emif_pm_functions, save_context));
DEFINE(EMIF_PM_RESTORE_CONTEXT_OFFSET,
offsetof(struct ti_emif_pm_functions, restore_context));
DEFINE(EMIF_PM_RUN_HW_LEVELING,
offsetof(struct ti_emif_pm_functions, run_hw_leveling));
DEFINE(EMIF_PM_ENTER_SR_OFFSET,
offsetof(struct ti_emif_pm_functions, enter_sr));
DEFINE(EMIF_PM_EXIT_SR_OFFSET,