18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * MPC83xx suspend support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Scott Wood <scottwood@freescale.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2006-2007 Freescale Semiconductor, Inc. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/pm.h> 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/ioport.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/wait.h> 158c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 168c2ecf20Sopenharmony_ci#include <linux/kthread.h> 178c2ecf20Sopenharmony_ci#include <linux/freezer.h> 188c2ecf20Sopenharmony_ci#include <linux/suspend.h> 198c2ecf20Sopenharmony_ci#include <linux/fsl_devices.h> 208c2ecf20Sopenharmony_ci#include <linux/of_address.h> 218c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 228c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 238c2ecf20Sopenharmony_ci#include <linux/export.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <asm/reg.h> 268c2ecf20Sopenharmony_ci#include <asm/io.h> 278c2ecf20Sopenharmony_ci#include <asm/time.h> 288c2ecf20Sopenharmony_ci#include <asm/mpc6xx.h> 298c2ecf20Sopenharmony_ci#include <asm/switch_to.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <sysdev/fsl_soc.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define PMCCR1_NEXT_STATE 0x0C /* Next state for power management */ 348c2ecf20Sopenharmony_ci#define PMCCR1_NEXT_STATE_SHIFT 2 358c2ecf20Sopenharmony_ci#define PMCCR1_CURR_STATE 0x03 /* Current state for power management*/ 368c2ecf20Sopenharmony_ci#define IMMR_SYSCR_OFFSET 0x100 378c2ecf20Sopenharmony_ci#define IMMR_RCW_OFFSET 0x900 388c2ecf20Sopenharmony_ci#define RCW_PCI_HOST 0x80000000 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_civoid mpc83xx_enter_deep_sleep(phys_addr_t immrbase); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct mpc83xx_pmc { 438c2ecf20Sopenharmony_ci u32 config; 448c2ecf20Sopenharmony_ci#define PMCCR_DLPEN 2 /* DDR SDRAM low power enable */ 458c2ecf20Sopenharmony_ci#define PMCCR_SLPEN 1 /* System low power enable */ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci u32 event; 488c2ecf20Sopenharmony_ci u32 mask; 498c2ecf20Sopenharmony_ci/* All but PMCI are deep-sleep only */ 508c2ecf20Sopenharmony_ci#define PMCER_GPIO 0x100 518c2ecf20Sopenharmony_ci#define PMCER_PCI 0x080 528c2ecf20Sopenharmony_ci#define PMCER_USB 0x040 538c2ecf20Sopenharmony_ci#define PMCER_ETSEC1 0x020 548c2ecf20Sopenharmony_ci#define PMCER_ETSEC2 0x010 558c2ecf20Sopenharmony_ci#define PMCER_TIMER 0x008 568c2ecf20Sopenharmony_ci#define PMCER_INT1 0x004 578c2ecf20Sopenharmony_ci#define PMCER_INT2 0x002 588c2ecf20Sopenharmony_ci#define PMCER_PMCI 0x001 598c2ecf20Sopenharmony_ci#define PMCER_ALL 0x1FF 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* deep-sleep only */ 628c2ecf20Sopenharmony_ci u32 config1; 638c2ecf20Sopenharmony_ci#define PMCCR1_USE_STATE 0x80000000 648c2ecf20Sopenharmony_ci#define PMCCR1_PME_EN 0x00000080 658c2ecf20Sopenharmony_ci#define PMCCR1_ASSERT_PME 0x00000040 668c2ecf20Sopenharmony_ci#define PMCCR1_POWER_OFF 0x00000020 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* deep-sleep only */ 698c2ecf20Sopenharmony_ci u32 config2; 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistruct mpc83xx_rcw { 738c2ecf20Sopenharmony_ci u32 rcwlr; 748c2ecf20Sopenharmony_ci u32 rcwhr; 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistruct mpc83xx_clock { 788c2ecf20Sopenharmony_ci u32 spmr; 798c2ecf20Sopenharmony_ci u32 occr; 808c2ecf20Sopenharmony_ci u32 sccr; 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistruct mpc83xx_syscr { 848c2ecf20Sopenharmony_ci __be32 sgprl; 858c2ecf20Sopenharmony_ci __be32 sgprh; 868c2ecf20Sopenharmony_ci __be32 spridr; 878c2ecf20Sopenharmony_ci __be32 :32; 888c2ecf20Sopenharmony_ci __be32 spcr; 898c2ecf20Sopenharmony_ci __be32 sicrl; 908c2ecf20Sopenharmony_ci __be32 sicrh; 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistruct mpc83xx_saved { 948c2ecf20Sopenharmony_ci u32 sicrl; 958c2ecf20Sopenharmony_ci u32 sicrh; 968c2ecf20Sopenharmony_ci u32 sccr; 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistruct pmc_type { 1008c2ecf20Sopenharmony_ci int has_deep_sleep; 1018c2ecf20Sopenharmony_ci}; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic struct platform_device *pmc_dev; 1048c2ecf20Sopenharmony_cistatic int has_deep_sleep, deep_sleeping; 1058c2ecf20Sopenharmony_cistatic int pmc_irq; 1068c2ecf20Sopenharmony_cistatic struct mpc83xx_pmc __iomem *pmc_regs; 1078c2ecf20Sopenharmony_cistatic struct mpc83xx_clock __iomem *clock_regs; 1088c2ecf20Sopenharmony_cistatic struct mpc83xx_syscr __iomem *syscr_regs; 1098c2ecf20Sopenharmony_cistatic struct mpc83xx_saved saved_regs; 1108c2ecf20Sopenharmony_cistatic int is_pci_agent, wake_from_pci; 1118c2ecf20Sopenharmony_cistatic phys_addr_t immrbase; 1128c2ecf20Sopenharmony_cistatic int pci_pm_state; 1138c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(agent_wq); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ciint fsl_deep_sleep(void) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci return deep_sleeping; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fsl_deep_sleep); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int mpc83xx_change_state(void) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci u32 curr_state; 1248c2ecf20Sopenharmony_ci u32 reg_cfg1 = in_be32(&pmc_regs->config1); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (is_pci_agent) { 1278c2ecf20Sopenharmony_ci pci_pm_state = (reg_cfg1 & PMCCR1_NEXT_STATE) >> 1288c2ecf20Sopenharmony_ci PMCCR1_NEXT_STATE_SHIFT; 1298c2ecf20Sopenharmony_ci curr_state = reg_cfg1 & PMCCR1_CURR_STATE; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (curr_state != pci_pm_state) { 1328c2ecf20Sopenharmony_ci reg_cfg1 &= ~PMCCR1_CURR_STATE; 1338c2ecf20Sopenharmony_ci reg_cfg1 |= pci_pm_state; 1348c2ecf20Sopenharmony_ci out_be32(&pmc_regs->config1, reg_cfg1); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci wake_up(&agent_wq); 1378c2ecf20Sopenharmony_ci return 1; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic irqreturn_t pmc_irq_handler(int irq, void *dev_id) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci u32 event = in_be32(&pmc_regs->event); 1478c2ecf20Sopenharmony_ci int ret = IRQ_NONE; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (mpc83xx_change_state()) 1508c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (event) { 1538c2ecf20Sopenharmony_ci out_be32(&pmc_regs->event, event); 1548c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return ret; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic void mpc83xx_suspend_restore_regs(void) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci out_be32(&syscr_regs->sicrl, saved_regs.sicrl); 1638c2ecf20Sopenharmony_ci out_be32(&syscr_regs->sicrh, saved_regs.sicrh); 1648c2ecf20Sopenharmony_ci out_be32(&clock_regs->sccr, saved_regs.sccr); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic void mpc83xx_suspend_save_regs(void) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci saved_regs.sicrl = in_be32(&syscr_regs->sicrl); 1708c2ecf20Sopenharmony_ci saved_regs.sicrh = in_be32(&syscr_regs->sicrh); 1718c2ecf20Sopenharmony_ci saved_regs.sccr = in_be32(&clock_regs->sccr); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int mpc83xx_suspend_enter(suspend_state_t state) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci int ret = -EAGAIN; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Don't go to sleep if there's a race where pci_pm_state changes 1798c2ecf20Sopenharmony_ci * between the agent thread checking it and the PM code disabling 1808c2ecf20Sopenharmony_ci * interrupts. 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci if (wake_from_pci) { 1838c2ecf20Sopenharmony_ci if (pci_pm_state != (deep_sleeping ? 3 : 2)) 1848c2ecf20Sopenharmony_ci goto out; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci out_be32(&pmc_regs->config1, 1878c2ecf20Sopenharmony_ci in_be32(&pmc_regs->config1) | PMCCR1_PME_EN); 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* Put the system into low-power mode and the RAM 1918c2ecf20Sopenharmony_ci * into self-refresh mode once the core goes to 1928c2ecf20Sopenharmony_ci * sleep. 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci out_be32(&pmc_regs->config, PMCCR_SLPEN | PMCCR_DLPEN); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* If it has deep sleep (i.e. it's an 831x or compatible), 1988c2ecf20Sopenharmony_ci * disable power to the core upon entering sleep mode. This will 1998c2ecf20Sopenharmony_ci * require going through the boot firmware upon a wakeup event. 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (deep_sleeping) { 2038c2ecf20Sopenharmony_ci mpc83xx_suspend_save_regs(); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci out_be32(&pmc_regs->mask, PMCER_ALL); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci out_be32(&pmc_regs->config1, 2088c2ecf20Sopenharmony_ci in_be32(&pmc_regs->config1) | PMCCR1_POWER_OFF); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci enable_kernel_fp(); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci mpc83xx_enter_deep_sleep(immrbase); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci out_be32(&pmc_regs->config1, 2158c2ecf20Sopenharmony_ci in_be32(&pmc_regs->config1) & ~PMCCR1_POWER_OFF); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci out_be32(&pmc_regs->mask, PMCER_PMCI); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci mpc83xx_suspend_restore_regs(); 2208c2ecf20Sopenharmony_ci } else { 2218c2ecf20Sopenharmony_ci out_be32(&pmc_regs->mask, PMCER_PMCI); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci mpc6xx_enter_standby(); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci ret = 0; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ciout: 2298c2ecf20Sopenharmony_ci out_be32(&pmc_regs->config1, 2308c2ecf20Sopenharmony_ci in_be32(&pmc_regs->config1) & ~PMCCR1_PME_EN); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return ret; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic void mpc83xx_suspend_end(void) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci deep_sleeping = 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic int mpc83xx_suspend_valid(suspend_state_t state) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci return state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int mpc83xx_suspend_begin(suspend_state_t state) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci switch (state) { 2488c2ecf20Sopenharmony_ci case PM_SUSPEND_STANDBY: 2498c2ecf20Sopenharmony_ci deep_sleeping = 0; 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci case PM_SUSPEND_MEM: 2538c2ecf20Sopenharmony_ci if (has_deep_sleep) 2548c2ecf20Sopenharmony_ci deep_sleeping = 1; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return 0; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci default: 2598c2ecf20Sopenharmony_ci return -EINVAL; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic int agent_thread_fn(void *data) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci while (1) { 2668c2ecf20Sopenharmony_ci wait_event_interruptible(agent_wq, pci_pm_state >= 2); 2678c2ecf20Sopenharmony_ci try_to_freeze(); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (signal_pending(current) || pci_pm_state < 2) 2708c2ecf20Sopenharmony_ci continue; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* With a preemptible kernel (or SMP), this could race with 2738c2ecf20Sopenharmony_ci * a userspace-driven suspend request. It's probably best 2748c2ecf20Sopenharmony_ci * to avoid mixing the two with such a configuration (or 2758c2ecf20Sopenharmony_ci * else fix it by adding a mutex to state_store that we can 2768c2ecf20Sopenharmony_ci * synchronize with). 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci wake_from_pci = 1; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci pm_suspend(pci_pm_state == 3 ? PM_SUSPEND_MEM : 2828c2ecf20Sopenharmony_ci PM_SUSPEND_STANDBY); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci wake_from_pci = 0; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return 0; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic void mpc83xx_set_agent(void) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci out_be32(&pmc_regs->config1, PMCCR1_USE_STATE); 2938c2ecf20Sopenharmony_ci out_be32(&pmc_regs->mask, PMCER_PMCI); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci kthread_run(agent_thread_fn, NULL, "PCI power mgt"); 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int mpc83xx_is_pci_agent(void) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct mpc83xx_rcw __iomem *rcw_regs; 3018c2ecf20Sopenharmony_ci int ret; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci rcw_regs = ioremap(get_immrbase() + IMMR_RCW_OFFSET, 3048c2ecf20Sopenharmony_ci sizeof(struct mpc83xx_rcw)); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (!rcw_regs) 3078c2ecf20Sopenharmony_ci return -ENOMEM; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci ret = !(in_be32(&rcw_regs->rcwhr) & RCW_PCI_HOST); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci iounmap(rcw_regs); 3128c2ecf20Sopenharmony_ci return ret; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic const struct platform_suspend_ops mpc83xx_suspend_ops = { 3168c2ecf20Sopenharmony_ci .valid = mpc83xx_suspend_valid, 3178c2ecf20Sopenharmony_ci .begin = mpc83xx_suspend_begin, 3188c2ecf20Sopenharmony_ci .enter = mpc83xx_suspend_enter, 3198c2ecf20Sopenharmony_ci .end = mpc83xx_suspend_end, 3208c2ecf20Sopenharmony_ci}; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic const struct of_device_id pmc_match[]; 3238c2ecf20Sopenharmony_cistatic int pmc_probe(struct platform_device *ofdev) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci const struct of_device_id *match; 3268c2ecf20Sopenharmony_ci struct device_node *np = ofdev->dev.of_node; 3278c2ecf20Sopenharmony_ci struct resource res; 3288c2ecf20Sopenharmony_ci const struct pmc_type *type; 3298c2ecf20Sopenharmony_ci int ret = 0; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci match = of_match_device(pmc_match, &ofdev->dev); 3328c2ecf20Sopenharmony_ci if (!match) 3338c2ecf20Sopenharmony_ci return -EINVAL; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci type = match->data; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (!of_device_is_available(np)) 3388c2ecf20Sopenharmony_ci return -ENODEV; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci has_deep_sleep = type->has_deep_sleep; 3418c2ecf20Sopenharmony_ci immrbase = get_immrbase(); 3428c2ecf20Sopenharmony_ci pmc_dev = ofdev; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci is_pci_agent = mpc83xx_is_pci_agent(); 3458c2ecf20Sopenharmony_ci if (is_pci_agent < 0) 3468c2ecf20Sopenharmony_ci return is_pci_agent; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci ret = of_address_to_resource(np, 0, &res); 3498c2ecf20Sopenharmony_ci if (ret) 3508c2ecf20Sopenharmony_ci return -ENODEV; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci pmc_irq = irq_of_parse_and_map(np, 0); 3538c2ecf20Sopenharmony_ci if (pmc_irq) { 3548c2ecf20Sopenharmony_ci ret = request_irq(pmc_irq, pmc_irq_handler, IRQF_SHARED, 3558c2ecf20Sopenharmony_ci "pmc", ofdev); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (ret) 3588c2ecf20Sopenharmony_ci return -EBUSY; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci pmc_regs = ioremap(res.start, sizeof(*pmc_regs)); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (!pmc_regs) { 3648c2ecf20Sopenharmony_ci ret = -ENOMEM; 3658c2ecf20Sopenharmony_ci goto out; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci ret = of_address_to_resource(np, 1, &res); 3698c2ecf20Sopenharmony_ci if (ret) { 3708c2ecf20Sopenharmony_ci ret = -ENODEV; 3718c2ecf20Sopenharmony_ci goto out_pmc; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci clock_regs = ioremap(res.start, sizeof(*clock_regs)); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (!clock_regs) { 3778c2ecf20Sopenharmony_ci ret = -ENOMEM; 3788c2ecf20Sopenharmony_ci goto out_pmc; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (has_deep_sleep) { 3828c2ecf20Sopenharmony_ci syscr_regs = ioremap(immrbase + IMMR_SYSCR_OFFSET, 3838c2ecf20Sopenharmony_ci sizeof(*syscr_regs)); 3848c2ecf20Sopenharmony_ci if (!syscr_regs) { 3858c2ecf20Sopenharmony_ci ret = -ENOMEM; 3868c2ecf20Sopenharmony_ci goto out_syscr; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (is_pci_agent) 3918c2ecf20Sopenharmony_ci mpc83xx_set_agent(); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci suspend_set_ops(&mpc83xx_suspend_ops); 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ciout_syscr: 3978c2ecf20Sopenharmony_ci iounmap(clock_regs); 3988c2ecf20Sopenharmony_ciout_pmc: 3998c2ecf20Sopenharmony_ci iounmap(pmc_regs); 4008c2ecf20Sopenharmony_ciout: 4018c2ecf20Sopenharmony_ci if (pmc_irq) 4028c2ecf20Sopenharmony_ci free_irq(pmc_irq, ofdev); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci return ret; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int pmc_remove(struct platform_device *ofdev) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci return -EPERM; 4108c2ecf20Sopenharmony_ci}; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic struct pmc_type pmc_types[] = { 4138c2ecf20Sopenharmony_ci { 4148c2ecf20Sopenharmony_ci .has_deep_sleep = 1, 4158c2ecf20Sopenharmony_ci }, 4168c2ecf20Sopenharmony_ci { 4178c2ecf20Sopenharmony_ci .has_deep_sleep = 0, 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci}; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic const struct of_device_id pmc_match[] = { 4228c2ecf20Sopenharmony_ci { 4238c2ecf20Sopenharmony_ci .compatible = "fsl,mpc8313-pmc", 4248c2ecf20Sopenharmony_ci .data = &pmc_types[0], 4258c2ecf20Sopenharmony_ci }, 4268c2ecf20Sopenharmony_ci { 4278c2ecf20Sopenharmony_ci .compatible = "fsl,mpc8349-pmc", 4288c2ecf20Sopenharmony_ci .data = &pmc_types[1], 4298c2ecf20Sopenharmony_ci }, 4308c2ecf20Sopenharmony_ci {} 4318c2ecf20Sopenharmony_ci}; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic struct platform_driver pmc_driver = { 4348c2ecf20Sopenharmony_ci .driver = { 4358c2ecf20Sopenharmony_ci .name = "mpc83xx-pmc", 4368c2ecf20Sopenharmony_ci .of_match_table = pmc_match, 4378c2ecf20Sopenharmony_ci }, 4388c2ecf20Sopenharmony_ci .probe = pmc_probe, 4398c2ecf20Sopenharmony_ci .remove = pmc_remove 4408c2ecf20Sopenharmony_ci}; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cibuiltin_platform_driver(pmc_driver); 443