162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/init.h> 362306a36Sopenharmony_ci#include <linux/suspend.h> 462306a36Sopenharmony_ci#include <linux/io.h> 562306a36Sopenharmony_ci#include <linux/of_address.h> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <asm/time.h> 862306a36Sopenharmony_ci#include <asm/cacheflush.h> 962306a36Sopenharmony_ci#include <asm/mpc52xx.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* these are defined in mpc52xx_sleep.S, and only used here */ 1262306a36Sopenharmony_ciextern void mpc52xx_deep_sleep(void __iomem *sram, void __iomem *sdram_regs, 1362306a36Sopenharmony_ci struct mpc52xx_cdm __iomem *, struct mpc52xx_intr __iomem*); 1462306a36Sopenharmony_ciextern void mpc52xx_ds_sram(void); 1562306a36Sopenharmony_ciextern const long mpc52xx_ds_sram_size; 1662306a36Sopenharmony_ciextern void mpc52xx_ds_cached(void); 1762306a36Sopenharmony_ciextern const long mpc52xx_ds_cached_size; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic void __iomem *mbar; 2062306a36Sopenharmony_cistatic void __iomem *sdram; 2162306a36Sopenharmony_cistatic struct mpc52xx_cdm __iomem *cdm; 2262306a36Sopenharmony_cistatic struct mpc52xx_intr __iomem *intr; 2362306a36Sopenharmony_cistatic struct mpc52xx_gpio_wkup __iomem *gpiow; 2462306a36Sopenharmony_cistatic void __iomem *sram; 2562306a36Sopenharmony_cistatic int sram_size; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct mpc52xx_suspend mpc52xx_suspend; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic int mpc52xx_pm_valid(suspend_state_t state) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci switch (state) { 3262306a36Sopenharmony_ci case PM_SUSPEND_STANDBY: 3362306a36Sopenharmony_ci return 1; 3462306a36Sopenharmony_ci default: 3562306a36Sopenharmony_ci return 0; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ciint mpc52xx_set_wakeup_gpio(u8 pin, u8 level) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci u16 tmp; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci /* enable gpio */ 4462306a36Sopenharmony_ci out_8(&gpiow->wkup_gpioe, in_8(&gpiow->wkup_gpioe) | (1 << pin)); 4562306a36Sopenharmony_ci /* set as input */ 4662306a36Sopenharmony_ci out_8(&gpiow->wkup_ddr, in_8(&gpiow->wkup_ddr) & ~(1 << pin)); 4762306a36Sopenharmony_ci /* enable deep sleep interrupt */ 4862306a36Sopenharmony_ci out_8(&gpiow->wkup_inten, in_8(&gpiow->wkup_inten) | (1 << pin)); 4962306a36Sopenharmony_ci /* low/high level creates wakeup interrupt */ 5062306a36Sopenharmony_ci tmp = in_be16(&gpiow->wkup_itype); 5162306a36Sopenharmony_ci tmp &= ~(0x3 << (pin * 2)); 5262306a36Sopenharmony_ci tmp |= (!level + 1) << (pin * 2); 5362306a36Sopenharmony_ci out_be16(&gpiow->wkup_itype, tmp); 5462306a36Sopenharmony_ci /* master enable */ 5562306a36Sopenharmony_ci out_8(&gpiow->wkup_maste, 1); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return 0; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ciint mpc52xx_pm_prepare(void) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct device_node *np; 6362306a36Sopenharmony_ci static const struct of_device_id immr_ids[] = { 6462306a36Sopenharmony_ci { .compatible = "fsl,mpc5200-immr", }, 6562306a36Sopenharmony_ci { .compatible = "fsl,mpc5200b-immr", }, 6662306a36Sopenharmony_ci { .type = "soc", .compatible = "mpc5200", }, /* lite5200 */ 6762306a36Sopenharmony_ci { .type = "builtin", .compatible = "mpc5200", }, /* efika */ 6862306a36Sopenharmony_ci {} 6962306a36Sopenharmony_ci }; 7062306a36Sopenharmony_ci struct resource res; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* map the whole register space */ 7362306a36Sopenharmony_ci np = of_find_matching_node(NULL, immr_ids); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (of_address_to_resource(np, 0, &res)) { 7662306a36Sopenharmony_ci pr_err("mpc52xx_pm_prepare(): could not get IMMR address\n"); 7762306a36Sopenharmony_ci of_node_put(np); 7862306a36Sopenharmony_ci return -ENOSYS; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci mbar = ioremap(res.start, 0xc000); /* we should map whole region including SRAM */ 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci of_node_put(np); 8462306a36Sopenharmony_ci if (!mbar) { 8562306a36Sopenharmony_ci pr_err("mpc52xx_pm_prepare(): could not map registers\n"); 8662306a36Sopenharmony_ci return -ENOSYS; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci /* these offsets are from mpc5200 users manual */ 8962306a36Sopenharmony_ci sdram = mbar + 0x100; 9062306a36Sopenharmony_ci cdm = mbar + 0x200; 9162306a36Sopenharmony_ci intr = mbar + 0x500; 9262306a36Sopenharmony_ci gpiow = mbar + 0xc00; 9362306a36Sopenharmony_ci sram = mbar + 0x8000; /* Those will be handled by the */ 9462306a36Sopenharmony_ci sram_size = 0x4000; /* bestcomm driver soon */ 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* call board suspend code, if applicable */ 9762306a36Sopenharmony_ci if (mpc52xx_suspend.board_suspend_prepare) 9862306a36Sopenharmony_ci mpc52xx_suspend.board_suspend_prepare(mbar); 9962306a36Sopenharmony_ci else { 10062306a36Sopenharmony_ci printk(KERN_ALERT "%s: %i don't know how to wake up the board\n", 10162306a36Sopenharmony_ci __func__, __LINE__); 10262306a36Sopenharmony_ci goto out_unmap; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci out_unmap: 10862306a36Sopenharmony_ci iounmap(mbar); 10962306a36Sopenharmony_ci return -ENOSYS; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cichar saved_sram[0x4000]; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ciint mpc52xx_pm_enter(suspend_state_t state) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci u32 clk_enables; 11862306a36Sopenharmony_ci u32 msr, hid0; 11962306a36Sopenharmony_ci u32 intr_main_mask; 12062306a36Sopenharmony_ci void __iomem * irq_0x500 = (void __iomem *)CONFIG_KERNEL_START + 0x500; 12162306a36Sopenharmony_ci unsigned long irq_0x500_stop = (unsigned long)irq_0x500 + mpc52xx_ds_cached_size; 12262306a36Sopenharmony_ci char saved_0x500[0x600-0x500]; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (WARN_ON(mpc52xx_ds_cached_size > sizeof(saved_0x500))) 12562306a36Sopenharmony_ci return -ENOMEM; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* disable all interrupts in PIC */ 12862306a36Sopenharmony_ci intr_main_mask = in_be32(&intr->main_mask); 12962306a36Sopenharmony_ci out_be32(&intr->main_mask, intr_main_mask | 0x1ffff); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* don't let DEC expire any time soon */ 13262306a36Sopenharmony_ci mtspr(SPRN_DEC, 0x7fffffff); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* save SRAM */ 13562306a36Sopenharmony_ci memcpy(saved_sram, sram, sram_size); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* copy low level suspend code to sram */ 13862306a36Sopenharmony_ci memcpy(sram, mpc52xx_ds_sram, mpc52xx_ds_sram_size); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci out_8(&cdm->ccs_sleep_enable, 1); 14162306a36Sopenharmony_ci out_8(&cdm->osc_sleep_enable, 1); 14262306a36Sopenharmony_ci out_8(&cdm->ccs_qreq_test, 1); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* disable all but SDRAM and bestcomm (SRAM) clocks */ 14562306a36Sopenharmony_ci clk_enables = in_be32(&cdm->clk_enables); 14662306a36Sopenharmony_ci out_be32(&cdm->clk_enables, clk_enables & 0x00088000); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* disable power management */ 14962306a36Sopenharmony_ci msr = mfmsr(); 15062306a36Sopenharmony_ci mtmsr(msr & ~MSR_POW); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* enable sleep mode, disable others */ 15362306a36Sopenharmony_ci hid0 = mfspr(SPRN_HID0); 15462306a36Sopenharmony_ci mtspr(SPRN_HID0, (hid0 & ~(HID0_DOZE | HID0_NAP | HID0_DPM)) | HID0_SLEEP); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* save original, copy our irq handler, flush from dcache and invalidate icache */ 15762306a36Sopenharmony_ci memcpy(saved_0x500, irq_0x500, mpc52xx_ds_cached_size); 15862306a36Sopenharmony_ci memcpy(irq_0x500, mpc52xx_ds_cached, mpc52xx_ds_cached_size); 15962306a36Sopenharmony_ci flush_icache_range((unsigned long)irq_0x500, irq_0x500_stop); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* call low-level sleep code */ 16262306a36Sopenharmony_ci mpc52xx_deep_sleep(sram, sdram, cdm, intr); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* restore original irq handler */ 16562306a36Sopenharmony_ci memcpy(irq_0x500, saved_0x500, mpc52xx_ds_cached_size); 16662306a36Sopenharmony_ci flush_icache_range((unsigned long)irq_0x500, irq_0x500_stop); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* restore old power mode */ 16962306a36Sopenharmony_ci mtmsr(msr & ~MSR_POW); 17062306a36Sopenharmony_ci mtspr(SPRN_HID0, hid0); 17162306a36Sopenharmony_ci mtmsr(msr); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci out_be32(&cdm->clk_enables, clk_enables); 17462306a36Sopenharmony_ci out_8(&cdm->ccs_sleep_enable, 0); 17562306a36Sopenharmony_ci out_8(&cdm->osc_sleep_enable, 0); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* restore SRAM */ 17862306a36Sopenharmony_ci memcpy(sram, saved_sram, sram_size); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* reenable interrupts in PIC */ 18162306a36Sopenharmony_ci out_be32(&intr->main_mask, intr_main_mask); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci return 0; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_civoid mpc52xx_pm_finish(void) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci /* call board resume code */ 18962306a36Sopenharmony_ci if (mpc52xx_suspend.board_resume_finish) 19062306a36Sopenharmony_ci mpc52xx_suspend.board_resume_finish(mbar); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci iounmap(mbar); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic const struct platform_suspend_ops mpc52xx_pm_ops = { 19662306a36Sopenharmony_ci .valid = mpc52xx_pm_valid, 19762306a36Sopenharmony_ci .prepare = mpc52xx_pm_prepare, 19862306a36Sopenharmony_ci .enter = mpc52xx_pm_enter, 19962306a36Sopenharmony_ci .finish = mpc52xx_pm_finish, 20062306a36Sopenharmony_ci}; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ciint __init mpc52xx_pm_init(void) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci suspend_set_ops(&mpc52xx_pm_ops); 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci} 207