diff options
Diffstat (limited to 'arch/sh/kernel/cpu/shmobile')
-rw-r--r-- | arch/sh/kernel/cpu/shmobile/Makefile | 2 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/shmobile/cpuidle.c | 113 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/shmobile/pm.c | 42 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/shmobile/pm_runtime.c | 303 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/shmobile/sleep.S | 155 |
5 files changed, 538 insertions, 77 deletions
diff --git a/arch/sh/kernel/cpu/shmobile/Makefile b/arch/sh/kernel/cpu/shmobile/Makefile index 08bfa7c7db29..a39f88ea1a85 100644 --- a/arch/sh/kernel/cpu/shmobile/Makefile +++ b/arch/sh/kernel/cpu/shmobile/Makefile @@ -4,3 +4,5 @@ # Power Management & Sleep mode obj-$(CONFIG_PM) += pm.o sleep.o +obj-$(CONFIG_CPU_IDLE) += cpuidle.o +obj-$(CONFIG_PM_RUNTIME) += pm_runtime.o diff --git a/arch/sh/kernel/cpu/shmobile/cpuidle.c b/arch/sh/kernel/cpu/shmobile/cpuidle.c new file mode 100644 index 000000000000..1c504bd972c3 --- /dev/null +++ b/arch/sh/kernel/cpu/shmobile/cpuidle.c @@ -0,0 +1,113 @@ +/* + * arch/sh/kernel/cpu/shmobile/cpuidle.c + * + * Cpuidle support code for SuperH Mobile + * + * Copyright (C) 2009 Magnus Damm + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/suspend.h> +#include <linux/cpuidle.h> +#include <asm/suspend.h> +#include <asm/uaccess.h> +#include <asm/hwblk.h> + +static unsigned long cpuidle_mode[] = { + SUSP_SH_SLEEP, /* regular sleep mode */ + SUSP_SH_SLEEP | SUSP_SH_SF, /* sleep mode + self refresh */ + SUSP_SH_STANDBY | SUSP_SH_SF, /* software standby mode + self refresh */ +}; + +static int cpuidle_sleep_enter(struct cpuidle_device *dev, + struct cpuidle_state *state) +{ + unsigned long allowed_mode = arch_hwblk_sleep_mode(); + ktime_t before, after; + int requested_state = state - &dev->states[0]; + int allowed_state; + int k; + + /* convert allowed mode to allowed state */ + for (k = ARRAY_SIZE(cpuidle_mode) - 1; k > 0; k--) + if (cpuidle_mode[k] == allowed_mode) + break; + + allowed_state = k; + + /* take the following into account for sleep mode selection: + * - allowed_state: best mode allowed by hardware (clock deps) + * - requested_state: best mode allowed by software (latencies) + */ + k = min_t(int, allowed_state, requested_state); + + dev->last_state = &dev->states[k]; + before = ktime_get(); + sh_mobile_call_standby(cpuidle_mode[k]); + after = ktime_get(); + return ktime_to_ns(ktime_sub(after, before)) >> 10; +} + +static struct cpuidle_device cpuidle_dev; +static struct cpuidle_driver cpuidle_driver = { + .name = "sh_idle", + .owner = THIS_MODULE, +}; + +void sh_mobile_setup_cpuidle(void) +{ + struct cpuidle_device *dev = &cpuidle_dev; + struct cpuidle_state *state; + int i; + + cpuidle_register_driver(&cpuidle_driver); + + for (i = 0; i < CPUIDLE_STATE_MAX; i++) { + dev->states[i].name[0] = '\0'; + dev->states[i].desc[0] = '\0'; + } + + i = CPUIDLE_DRIVER_STATE_START; + + state = &dev->states[i++]; + snprintf(state->name, CPUIDLE_NAME_LEN, "C0"); + strncpy(state->desc, "SuperH Sleep Mode", CPUIDLE_DESC_LEN); + state->exit_latency = 1; + state->target_residency = 1 * 2; + state->power_usage = 3; + state->flags = 0; + state->flags |= CPUIDLE_FLAG_SHALLOW; + state->flags |= CPUIDLE_FLAG_TIME_VALID; + state->enter = cpuidle_sleep_enter; + + dev->safe_state = state; + + state = &dev->states[i++]; + snprintf(state->name, CPUIDLE_NAME_LEN, "C1"); + strncpy(state->desc, "SuperH Sleep Mode [SF]", CPUIDLE_DESC_LEN); + state->exit_latency = 100; + state->target_residency = 1 * 2; + state->power_usage = 1; + state->flags = 0; + state->flags |= CPUIDLE_FLAG_TIME_VALID; + state->enter = cpuidle_sleep_enter; + + state = &dev->states[i++]; + snprintf(state->name, CPUIDLE_NAME_LEN, "C2"); + strncpy(state->desc, "SuperH Mobile Standby Mode [SF]", CPUIDLE_DESC_LEN); + state->exit_latency = 2300; + state->target_residency = 1 * 2; + state->power_usage = 1; + state->flags = 0; + state->flags |= CPUIDLE_FLAG_TIME_VALID; + state->enter = cpuidle_sleep_enter; + + dev->state_count = i; + + cpuidle_register_device(dev); +} diff --git a/arch/sh/kernel/cpu/shmobile/pm.c b/arch/sh/kernel/cpu/shmobile/pm.c index 8c067adf6830..ee3c2aaf66fb 100644 --- a/arch/sh/kernel/cpu/shmobile/pm.c +++ b/arch/sh/kernel/cpu/shmobile/pm.c @@ -1,5 +1,5 @@ /* - * arch/sh/kernel/cpu/sh4a/pm-sh_mobile.c + * arch/sh/kernel/cpu/shmobile/pm.c * * Power management support code for SuperH Mobile * @@ -32,40 +32,20 @@ * * R-standby mode is unsupported, but will be added in the future * U-standby mode is low priority since it needs bootloader hacks - * - * All modes should be tied in with cpuidle. But before that can - * happen we need to keep track of enabled hardware blocks so we - * can avoid entering sleep modes that stop clocks to hardware - * blocks that are in use even though the cpu core is idle. */ +#define ILRAM_BASE 0xe5200000 + extern const unsigned char sh_mobile_standby[]; extern const unsigned int sh_mobile_standby_size; -static void sh_mobile_call_standby(unsigned long mode) +void sh_mobile_call_standby(unsigned long mode) { - extern void *vbr_base; - void *onchip_mem = (void *)0xe5200000; /* ILRAM */ - void (*standby_onchip_mem)(unsigned long) = onchip_mem; - - /* Note: Wake up from sleep may generate exceptions! - * Setup VBR to point to on-chip ram if self-refresh is - * going to be used. - */ - if (mode & SUSP_SH_SF) - asm volatile("ldc %0, vbr" : : "r" (onchip_mem) : "memory"); - - /* Copy the assembly snippet to the otherwise ununsed ILRAM */ - memcpy(onchip_mem, sh_mobile_standby, sh_mobile_standby_size); - wmb(); - ctrl_barrier(); + void *onchip_mem = (void *)ILRAM_BASE; + void (*standby_onchip_mem)(unsigned long, unsigned long) = onchip_mem; /* Let assembly snippet in on-chip memory handle the rest */ - standby_onchip_mem(mode); - - /* Put VBR back in System RAM again */ - if (mode & SUSP_SH_SF) - asm volatile("ldc %0, vbr" : : "r" (&vbr_base) : "memory"); + standby_onchip_mem(mode, ILRAM_BASE); } static int sh_pm_enter(suspend_state_t state) @@ -85,7 +65,15 @@ static struct platform_suspend_ops sh_pm_ops = { static int __init sh_pm_init(void) { + void *onchip_mem = (void *)ILRAM_BASE; + + /* Copy the assembly snippet to the otherwise ununsed ILRAM */ + memcpy(onchip_mem, sh_mobile_standby, sh_mobile_standby_size); + wmb(); + ctrl_barrier(); + suspend_set_ops(&sh_pm_ops); + sh_mobile_setup_cpuidle(); return 0; } diff --git a/arch/sh/kernel/cpu/shmobile/pm_runtime.c b/arch/sh/kernel/cpu/shmobile/pm_runtime.c new file mode 100644 index 000000000000..7c615b17e209 --- /dev/null +++ b/arch/sh/kernel/cpu/shmobile/pm_runtime.c @@ -0,0 +1,303 @@ +/* + * arch/sh/kernel/cpu/shmobile/pm_runtime.c + * + * Runtime PM support code for SuperH Mobile + * + * Copyright (C) 2009 Magnus Damm + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/pm_runtime.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <asm/hwblk.h> + +static DEFINE_SPINLOCK(hwblk_lock); +static LIST_HEAD(hwblk_idle_list); +static struct work_struct hwblk_work; + +extern struct hwblk_info *hwblk_info; + +static void platform_pm_runtime_not_idle(struct platform_device *pdev) +{ + unsigned long flags; + + /* remove device from idle list */ + spin_lock_irqsave(&hwblk_lock, flags); + if (test_bit(PDEV_ARCHDATA_FLAG_IDLE, &pdev->archdata.flags)) { + list_del(&pdev->archdata.entry); + __clear_bit(PDEV_ARCHDATA_FLAG_IDLE, &pdev->archdata.flags); + } + spin_unlock_irqrestore(&hwblk_lock, flags); +} + +static int __platform_pm_runtime_resume(struct platform_device *pdev) +{ + struct device *d = &pdev->dev; + struct pdev_archdata *ad = &pdev->archdata; + int hwblk = ad->hwblk_id; + int ret = -ENOSYS; + + dev_dbg(d, "__platform_pm_runtime_resume() [%d]\n", hwblk); + + if (d->driver && d->driver->pm && d->driver->pm->runtime_resume) { + hwblk_enable(hwblk_info, hwblk); + ret = 0; + + if (test_bit(PDEV_ARCHDATA_FLAG_SUSP, &ad->flags)) { + ret = d->driver->pm->runtime_resume(d); + if (!ret) + clear_bit(PDEV_ARCHDATA_FLAG_SUSP, &ad->flags); + else + hwblk_disable(hwblk_info, hwblk); + } + } + + dev_dbg(d, "__platform_pm_runtime_resume() [%d] - returns %d\n", + hwblk, ret); + + return ret; +} + +static int __platform_pm_runtime_suspend(struct platform_device *pdev) +{ + struct device *d = &pdev->dev; + struct pdev_archdata *ad = &pdev->archdata; + int hwblk = ad->hwblk_id; + int ret = -ENOSYS; + + dev_dbg(d, "__platform_pm_runtime_suspend() [%d]\n", hwblk); + + if (d->driver && d->driver->pm && d->driver->pm->runtime_suspend) { + BUG_ON(!test_bit(PDEV_ARCHDATA_FLAG_IDLE, &ad->flags)); + + hwblk_enable(hwblk_info, hwblk); + ret = d->driver->pm->runtime_suspend(d); + hwblk_disable(hwblk_info, hwblk); + + if (!ret) { + set_bit(PDEV_ARCHDATA_FLAG_SUSP, &ad->flags); + platform_pm_runtime_not_idle(pdev); + hwblk_cnt_dec(hwblk_info, hwblk, HWBLK_CNT_IDLE); + } + } + + dev_dbg(d, "__platform_pm_runtime_suspend() [%d] - returns %d\n", + hwblk, ret); + + return ret; +} + +static void platform_pm_runtime_work(struct work_struct *work) +{ + struct platform_device *pdev; + unsigned long flags; + int ret; + + /* go through the idle list and suspend one device at a time */ + do { + spin_lock_irqsave(&hwblk_lock, flags); + if (list_empty(&hwblk_idle_list)) + pdev = NULL; + else + pdev = list_first_entry(&hwblk_idle_list, + struct platform_device, + archdata.entry); + spin_unlock_irqrestore(&hwblk_lock, flags); + + if (pdev) { + mutex_lock(&pdev->archdata.mutex); + ret = __platform_pm_runtime_suspend(pdev); + + /* at this point the platform device may be: + * suspended: ret = 0, FLAG_SUSP set, clock stopped + * failed: ret < 0, FLAG_IDLE set, clock stopped + */ + mutex_unlock(&pdev->archdata.mutex); + } else { + ret = -ENODEV; + } + } while (!ret); +} + +/* this function gets called from cpuidle context when all devices in the + * main power domain are unused but some are counted as idle, ie the hwblk + * counter values are (HWBLK_CNT_USAGE == 0) && (HWBLK_CNT_IDLE != 0) + */ +void platform_pm_runtime_suspend_idle(void) +{ + queue_work(pm_wq, &hwblk_work); +} + +int platform_pm_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pdev_archdata *ad = &pdev->archdata; + unsigned long flags; + int hwblk = ad->hwblk_id; + int ret = 0; + + dev_dbg(dev, "platform_pm_runtime_suspend() [%d]\n", hwblk); + + /* ignore off-chip platform devices */ + if (!hwblk) + goto out; + + /* interrupt context not allowed */ + might_sleep(); + + /* catch misconfigured drivers not starting with resume */ + if (test_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags)) { + ret = -EINVAL; + goto out; + } + + /* serialize */ + mutex_lock(&ad->mutex); + + /* disable clock */ + hwblk_disable(hwblk_info, hwblk); + + /* put device on idle list */ + spin_lock_irqsave(&hwblk_lock, flags); + list_add_tail(&pdev->archdata.entry, &hwblk_idle_list); + __set_bit(PDEV_ARCHDATA_FLAG_IDLE, &pdev->archdata.flags); + spin_unlock_irqrestore(&hwblk_lock, flags); + + /* increase idle count */ + hwblk_cnt_inc(hwblk_info, hwblk, HWBLK_CNT_IDLE); + + /* at this point the platform device is: + * idle: ret = 0, FLAG_IDLE set, clock stopped + */ + mutex_unlock(&ad->mutex); + +out: + dev_dbg(dev, "platform_pm_runtime_suspend() [%d] returns %d\n", + hwblk, ret); + + return ret; +} + +int platform_pm_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pdev_archdata *ad = &pdev->archdata; + int hwblk = ad->hwblk_id; + int ret = 0; + + dev_dbg(dev, "platform_pm_runtime_resume() [%d]\n", hwblk); + + /* ignore off-chip platform devices */ + if (!hwblk) + goto out; + + /* interrupt context not allowed */ + might_sleep(); + + /* serialize */ + mutex_lock(&ad->mutex); + + /* make sure device is removed from idle list */ + platform_pm_runtime_not_idle(pdev); + + /* decrease idle count */ + if (!test_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags) && + !test_bit(PDEV_ARCHDATA_FLAG_SUSP, &pdev->archdata.flags)) + hwblk_cnt_dec(hwblk_info, hwblk, HWBLK_CNT_IDLE); + + /* resume the device if needed */ + ret = __platform_pm_runtime_resume(pdev); + + /* the driver has been initialized now, so clear the init flag */ + clear_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags); + + /* at this point the platform device may be: + * resumed: ret = 0, flags = 0, clock started + * failed: ret < 0, FLAG_SUSP set, clock stopped + */ + mutex_unlock(&ad->mutex); +out: + dev_dbg(dev, "platform_pm_runtime_resume() [%d] returns %d\n", + hwblk, ret); + + return ret; +} + +int platform_pm_runtime_idle(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + int hwblk = pdev->archdata.hwblk_id; + int ret = 0; + + dev_dbg(dev, "platform_pm_runtime_idle() [%d]\n", hwblk); + + /* ignore off-chip platform devices */ + if (!hwblk) + goto out; + + /* interrupt context not allowed, use pm_runtime_put()! */ + might_sleep(); + + /* suspend synchronously to disable clocks immediately */ + ret = pm_runtime_suspend(dev); +out: + dev_dbg(dev, "platform_pm_runtime_idle() [%d] done!\n", hwblk); + return ret; +} + +static int platform_bus_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + struct platform_device *pdev = to_platform_device(dev); + int hwblk = pdev->archdata.hwblk_id; + + /* ignore off-chip platform devices */ + if (!hwblk) + return 0; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + INIT_LIST_HEAD(&pdev->archdata.entry); + mutex_init(&pdev->archdata.mutex); + /* platform devices without drivers should be disabled */ + hwblk_enable(hwblk_info, hwblk); + hwblk_disable(hwblk_info, hwblk); + /* make sure driver re-inits itself once */ + __set_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags); + break; + /* TODO: add BUS_NOTIFY_BIND_DRIVER and increase idle count */ + case BUS_NOTIFY_BOUND_DRIVER: + /* keep track of number of devices in use per hwblk */ + hwblk_cnt_inc(hwblk_info, hwblk, HWBLK_CNT_DEVICES); + break; + case BUS_NOTIFY_UNBOUND_DRIVER: + /* keep track of number of devices in use per hwblk */ + hwblk_cnt_dec(hwblk_info, hwblk, HWBLK_CNT_DEVICES); + /* make sure driver re-inits itself once */ + __set_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags); + break; + case BUS_NOTIFY_DEL_DEVICE: + break; + } + return 0; +} + +static struct notifier_block platform_bus_notifier = { + .notifier_call = platform_bus_notify +}; + +static int __init sh_pm_runtime_init(void) +{ + INIT_WORK(&hwblk_work, platform_pm_runtime_work); + + bus_register_notifier(&platform_bus_type, &platform_bus_notifier); + return 0; +} +core_initcall(sh_pm_runtime_init); diff --git a/arch/sh/kernel/cpu/shmobile/sleep.S b/arch/sh/kernel/cpu/shmobile/sleep.S index baf2d7d46b05..a439e6c7824f 100644 --- a/arch/sh/kernel/cpu/shmobile/sleep.S +++ b/arch/sh/kernel/cpu/shmobile/sleep.S @@ -16,19 +16,52 @@ #include <asm/asm-offsets.h> #include <asm/suspend.h> +/* + * Kernel mode register usage, see entry.S: + * k0 scratch + * k1 scratch + * k4 scratch + */ +#define k0 r0 +#define k1 r1 +#define k4 r4 + /* manage self-refresh and enter standby mode. * this code will be copied to on-chip memory and executed from there. */ .balign 4096,0,4096 ENTRY(sh_mobile_standby) + + /* save original vbr */ + stc vbr, r1 + mova saved_vbr, r0 + mov.l r1, @r0 + + /* point vbr to our on-chip memory page */ + ldc r5, vbr + + /* save return address */ + mova saved_spc, r0 + sts pr, r5 + mov.l r5, @r0 + + /* save sr */ + mova saved_sr, r0 + stc sr, r5 + mov.l r5, @r0 + + /* save mode flags */ + mova saved_mode, r0 + mov.l r4, @r0 + + /* put mode flags in r0 */ mov r4, r0 tst #SUSP_SH_SF, r0 bt skip_set_sf #ifdef CONFIG_CPU_SUBTYPE_SH7724 /* DBSC: put memory in self-refresh mode */ - mov.l dben_reg, r4 mov.l dben_data0, r1 mov.l r1, @r4 @@ -60,14 +93,6 @@ ENTRY(sh_mobile_standby) #endif skip_set_sf: - tst #SUSP_SH_SLEEP, r0 - bt test_standby - - /* set mode to "sleep mode" */ - bra do_sleep - mov #0x00, r1 - -test_standby: tst #SUSP_SH_STANDBY, r0 bt test_rstandby @@ -85,77 +110,107 @@ test_rstandby: test_ustandby: tst #SUSP_SH_USTANDBY, r0 - bt done_sleep + bt force_sleep /* set mode to "u-standby mode" */ - mov #0x10, r1 + bra do_sleep + mov #0x10, r1 - /* fall-through */ +force_sleep: + + /* set mode to "sleep mode" */ + mov #0x00, r1 do_sleep: /* setup and enter selected standby mode */ mov.l 5f, r4 mov.l r1, @r4 +again: sleep + bra again + nop + +restore_jump_vbr: + /* setup spc with return address to c code */ + mov.l saved_spc, k0 + ldc k0, spc + + /* restore vbr */ + mov.l saved_vbr, k0 + ldc k0, vbr + + /* setup ssr with saved sr */ + mov.l saved_sr, k0 + ldc k0, ssr + + /* get mode flags */ + mov.l saved_mode, k0 done_sleep: /* reset standby mode to sleep mode */ - mov.l 5f, r4 - mov #0x00, r1 - mov.l r1, @r4 + mov.l 5f, k4 + mov #0x00, k1 + mov.l k1, @k4 - tst #SUSP_SH_SF, r0 + tst #SUSP_SH_SF, k0 bt skip_restore_sf #ifdef CONFIG_CPU_SUBTYPE_SH7724 /* DBSC: put memory in auto-refresh mode */ + mov.l dbrfpdn0_reg, k4 + mov.l dbrfpdn0_data0, k1 + mov.l k1, @k4 - mov.l dbrfpdn0_reg, r4 - mov.l dbrfpdn0_data0, r1 - mov.l r1, @r4 - - /* sleep 140 ns */ - nop + nop /* sleep 140 ns */ nop nop nop - mov.l dbcmdcnt_reg, r4 - mov.l dbcmdcnt_data0, r1 - mov.l r1, @r4 + mov.l dbcmdcnt_reg, k4 + mov.l dbcmdcnt_data0, k1 + mov.l k1, @k4 - mov.l dbcmdcnt_reg, r4 - mov.l dbcmdcnt_data1, r1 - mov.l r1, @r4 + mov.l dbcmdcnt_reg, k4 + mov.l dbcmdcnt_data1, k1 + mov.l k1, @k4 - mov.l dben_reg, r4 - mov.l dben_data1, r1 - mov.l r1, @r4 + mov.l dben_reg, k4 + mov.l dben_data1, k1 + mov.l k1, @k4 - mov.l dbrfpdn0_reg, r4 - mov.l dbrfpdn0_data2, r1 - mov.l r1, @r4 + mov.l dbrfpdn0_reg, k4 + mov.l dbrfpdn0_data2, k1 + mov.l k1, @k4 #else /* SBSC: set auto-refresh mode */ - mov.l 1f, r4 - mov.l @r4, r2 - mov.l 4f, r3 - and r3, r2 - mov.l r2, @r4 - mov.l 6f, r4 - mov.l 7f, r1 - mov.l 8f, r2 - mov.l @r4, r3 - mov #-1, r4 - add r4, r3 - or r2, r3 - mov.l r3, @r1 + mov.l 1f, k4 + mov.l @k4, k0 + mov.l 4f, k1 + and k1, k0 + mov.l k0, @k4 + mov.l 6f, k4 + mov.l 8f, k0 + mov.l @k4, k1 + mov #-1, k4 + add k4, k1 + or k1, k0 + mov.l 7f, k1 + mov.l k0, @k1 #endif skip_restore_sf: - rts + /* jump to vbr vector */ + mov.l saved_vbr, k0 + mov.l offset_vbr, k4 + add k4, k0 + jmp @k0 nop .balign 4 +saved_mode: .long 0 +saved_spc: .long 0 +saved_sr: .long 0 +saved_vbr: .long 0 +offset_vbr: .long 0x600 #ifdef CONFIG_CPU_SUBTYPE_SH7724 dben_reg: .long 0xfd000010 /* DBEN */ dben_data0: .long 0 @@ -178,12 +233,12 @@ dbcmdcnt_data1: .long 4 7: .long 0xfe400018 /* RTCNT */ 8: .long 0xa55a0000 + /* interrupt vector @ 0x600 */ .balign 0x400,0,0x400 .long 0xdeadbeef .balign 0x200,0,0x200 - /* sh7722 will end up here in sleep mode */ - rte + bra restore_jump_vbr nop sh_mobile_standby_end: |