1d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood/* 2d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * MPC83xx suspend support 3d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * 4d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * Author: Scott Wood <scottwood@freescale.com> 5d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * 6d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * Copyright (c) 2006-2007 Freescale Semiconductor, Inc. 7d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * 8d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * This program is free software; you can redistribute it and/or modify it 9d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * under the terms of the GNU General Public License version 2 as published 10d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * by the Free Software Foundation. 11d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood */ 12d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 13d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/pm.h> 14d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/types.h> 15d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/ioport.h> 16d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/interrupt.h> 17d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/wait.h> 18d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/kthread.h> 19d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/freezer.h> 20d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/suspend.h> 21d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/fsl_devices.h> 2226a2056eb21fff26caf99d19ad5448e9403db55dRob Herring#include <linux/of_address.h> 2326a2056eb21fff26caf99d19ad5448e9403db55dRob Herring#include <linux/of_irq.h> 24d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/of_platform.h> 2566b15db69c2553036cc25f6e2e74fe7e3aa2761ePaul Gortmaker#include <linux/export.h> 26d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 27d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <asm/reg.h> 28d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <asm/io.h> 29d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <asm/time.h> 30d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <asm/mpc6xx.h> 31ae3a197e3d0bfe3f4bf1693723e82dc018c096f3David Howells#include <asm/switch_to.h> 32d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 33d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <sysdev/fsl_soc.h> 34d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 35d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCCR1_NEXT_STATE 0x0C /* Next state for power management */ 36d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCCR1_NEXT_STATE_SHIFT 2 37d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCCR1_CURR_STATE 0x03 /* Current state for power management*/ 3887faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov#define IMMR_SYSCR_OFFSET 0x100 39d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define IMMR_RCW_OFFSET 0x900 40d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define RCW_PCI_HOST 0x80000000 41d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 42d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodvoid mpc83xx_enter_deep_sleep(phys_addr_t immrbase); 43d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 44d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstruct mpc83xx_pmc { 45d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 config; 46d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCCR_DLPEN 2 /* DDR SDRAM low power enable */ 47d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCCR_SLPEN 1 /* System low power enable */ 48d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 49d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 event; 50d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 mask; 51d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood/* All but PMCI are deep-sleep only */ 52d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_GPIO 0x100 53d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_PCI 0x080 54d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_USB 0x040 55d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_ETSEC1 0x020 56d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_ETSEC2 0x010 57d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_TIMER 0x008 58d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_INT1 0x004 59d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_INT2 0x002 60d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_PMCI 0x001 61d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_ALL 0x1FF 62d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 63d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood /* deep-sleep only */ 64d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 config1; 65d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCCR1_USE_STATE 0x80000000 66d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCCR1_PME_EN 0x00000080 67d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCCR1_ASSERT_PME 0x00000040 68d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCCR1_POWER_OFF 0x00000020 69d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 70d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood /* deep-sleep only */ 71d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 config2; 72d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood}; 73d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 74d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstruct mpc83xx_rcw { 75d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 rcwlr; 76d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 rcwhr; 77d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood}; 78d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 79d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstruct mpc83xx_clock { 80d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 spmr; 81d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 occr; 82d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 sccr; 83d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood}; 84d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 8587faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsovstruct mpc83xx_syscr { 8687faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov __be32 sgprl; 8787faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov __be32 sgprh; 8887faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov __be32 spridr; 8987faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov __be32 :32; 9087faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov __be32 spcr; 9187faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov __be32 sicrl; 9287faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov __be32 sicrh; 9387faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov}; 9487faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov 9587faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsovstruct mpc83xx_saved { 9687faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov u32 sicrl; 9787faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov u32 sicrh; 9887faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov u32 sccr; 9987faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov}; 10087faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov 101d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstruct pmc_type { 102d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood int has_deep_sleep; 103d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood}; 104d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 105a454dc50590c6d758abba016a303a221f2f1b4b8Grant Likelystatic struct platform_device *pmc_dev; 106d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int has_deep_sleep, deep_sleeping; 107d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int pmc_irq; 108d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic struct mpc83xx_pmc __iomem *pmc_regs; 109d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic struct mpc83xx_clock __iomem *clock_regs; 11087faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsovstatic struct mpc83xx_syscr __iomem *syscr_regs; 11187faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsovstatic struct mpc83xx_saved saved_regs; 112d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int is_pci_agent, wake_from_pci; 113d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic phys_addr_t immrbase; 114d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int pci_pm_state; 115d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic DECLARE_WAIT_QUEUE_HEAD(agent_wq); 116d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 117d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodint fsl_deep_sleep(void) 118d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 119d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return deep_sleeping; 120d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 1212e9d546eda5888962a441da1e96bbf92cb5b1cbbAnton VorontsovEXPORT_SYMBOL(fsl_deep_sleep); 122d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 123d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int mpc83xx_change_state(void) 124d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 125d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 curr_state; 126d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 reg_cfg1 = in_be32(&pmc_regs->config1); 127d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 128d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (is_pci_agent) { 129d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood pci_pm_state = (reg_cfg1 & PMCCR1_NEXT_STATE) >> 130d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood PMCCR1_NEXT_STATE_SHIFT; 131d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood curr_state = reg_cfg1 & PMCCR1_CURR_STATE; 132d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 133d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (curr_state != pci_pm_state) { 134d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood reg_cfg1 &= ~PMCCR1_CURR_STATE; 135d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood reg_cfg1 |= pci_pm_state; 136d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->config1, reg_cfg1); 137d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 138d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood wake_up(&agent_wq); 139d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return 1; 140d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 141d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 142d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 143d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return 0; 144d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 145d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 146d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic irqreturn_t pmc_irq_handler(int irq, void *dev_id) 147d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 148d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 event = in_be32(&pmc_regs->event); 149d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood int ret = IRQ_NONE; 150d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 151d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (mpc83xx_change_state()) 152d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = IRQ_HANDLED; 153d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 154d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (event) { 155d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->event, event); 156d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = IRQ_HANDLED; 157d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 158d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 159d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return ret; 160d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 161d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 16287faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsovstatic void mpc83xx_suspend_restore_regs(void) 16387faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov{ 16487faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov out_be32(&syscr_regs->sicrl, saved_regs.sicrl); 16587faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov out_be32(&syscr_regs->sicrh, saved_regs.sicrh); 16687faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov out_be32(&clock_regs->sccr, saved_regs.sccr); 16787faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov} 16887faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov 16987faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsovstatic void mpc83xx_suspend_save_regs(void) 17087faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov{ 17187faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov saved_regs.sicrl = in_be32(&syscr_regs->sicrl); 17287faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov saved_regs.sicrh = in_be32(&syscr_regs->sicrh); 17387faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov saved_regs.sccr = in_be32(&clock_regs->sccr); 17487faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov} 17587faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov 176d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int mpc83xx_suspend_enter(suspend_state_t state) 177d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 178d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood int ret = -EAGAIN; 179d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 180d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood /* Don't go to sleep if there's a race where pci_pm_state changes 181d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * between the agent thread checking it and the PM code disabling 182d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * interrupts. 183d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood */ 184d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (wake_from_pci) { 185d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (pci_pm_state != (deep_sleeping ? 3 : 2)) 186d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood goto out; 187d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 188d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->config1, 189d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood in_be32(&pmc_regs->config1) | PMCCR1_PME_EN); 190d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 191d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 192d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood /* Put the system into low-power mode and the RAM 193d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * into self-refresh mode once the core goes to 194d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * sleep. 195d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood */ 196d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 197d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->config, PMCCR_SLPEN | PMCCR_DLPEN); 198d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 199d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood /* If it has deep sleep (i.e. it's an 831x or compatible), 200d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * disable power to the core upon entering sleep mode. This will 201d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * require going through the boot firmware upon a wakeup event. 202d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood */ 203d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 204d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (deep_sleeping) { 20587faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov mpc83xx_suspend_save_regs(); 20687faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov 207d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->mask, PMCER_ALL); 208d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 209d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->config1, 210d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood in_be32(&pmc_regs->config1) | PMCCR1_POWER_OFF); 211d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 212d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood enable_kernel_fp(); 213d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 214d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood mpc83xx_enter_deep_sleep(immrbase); 215d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 216d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->config1, 217d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood in_be32(&pmc_regs->config1) & ~PMCCR1_POWER_OFF); 218d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 219d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->mask, PMCER_PMCI); 22087faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov 22187faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov mpc83xx_suspend_restore_regs(); 222d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } else { 223d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->mask, PMCER_PMCI); 224d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 225d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood mpc6xx_enter_standby(); 226d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 227d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 228d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = 0; 229d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 230d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodout: 231d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->config1, 232d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood in_be32(&pmc_regs->config1) & ~PMCCR1_PME_EN); 233d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 234d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return ret; 235d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 236d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 237f25c525c1412675d2b23d5d88660fb5c9f3a5341Anton Vorontsovstatic void mpc83xx_suspend_end(void) 238d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 239d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood deep_sleeping = 0; 240d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 241d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 242d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int mpc83xx_suspend_valid(suspend_state_t state) 243d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 244d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM; 245d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 246d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 247d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int mpc83xx_suspend_begin(suspend_state_t state) 248d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 249d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood switch (state) { 250d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood case PM_SUSPEND_STANDBY: 251d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood deep_sleeping = 0; 252d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return 0; 253d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 254d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood case PM_SUSPEND_MEM: 255d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (has_deep_sleep) 256d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood deep_sleeping = 1; 257d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 258d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return 0; 259d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 260d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood default: 261d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return -EINVAL; 262d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 263d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 264d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 265d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int agent_thread_fn(void *data) 266d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 267d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood while (1) { 268d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood wait_event_interruptible(agent_wq, pci_pm_state >= 2); 269d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood try_to_freeze(); 270d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 271d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (signal_pending(current) || pci_pm_state < 2) 272d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood continue; 273d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 274d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood /* With a preemptible kernel (or SMP), this could race with 275d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * a userspace-driven suspend request. It's probably best 276d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * to avoid mixing the two with such a configuration (or 277d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * else fix it by adding a mutex to state_store that we can 278d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * synchronize with). 279d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood */ 280d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 281d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood wake_from_pci = 1; 282d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 283d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood pm_suspend(pci_pm_state == 3 ? PM_SUSPEND_MEM : 284d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood PM_SUSPEND_STANDBY); 285d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 286d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood wake_from_pci = 0; 287d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 288d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 289d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return 0; 290d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 291d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 292d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic void mpc83xx_set_agent(void) 293d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 294d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->config1, PMCCR1_USE_STATE); 295d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->mask, PMCER_PMCI); 296d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 297d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood kthread_run(agent_thread_fn, NULL, "PCI power mgt"); 298d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 299d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 300d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int mpc83xx_is_pci_agent(void) 301d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 302d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood struct mpc83xx_rcw __iomem *rcw_regs; 303d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood int ret; 304d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 305d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood rcw_regs = ioremap(get_immrbase() + IMMR_RCW_OFFSET, 306d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood sizeof(struct mpc83xx_rcw)); 307d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 308d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (!rcw_regs) 309d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return -ENOMEM; 310d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 311d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = !(in_be32(&rcw_regs->rcwhr) & RCW_PCI_HOST); 312d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 313d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood iounmap(rcw_regs); 314d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return ret; 315d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 316d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 3172f55ac072f5344519348c0c94b3d2f4cca46847bLionel Debrouxstatic const struct platform_suspend_ops mpc83xx_suspend_ops = { 318d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .valid = mpc83xx_suspend_valid, 319d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .begin = mpc83xx_suspend_begin, 320d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .enter = mpc83xx_suspend_enter, 321f25c525c1412675d2b23d5d88660fb5c9f3a5341Anton Vorontsov .end = mpc83xx_suspend_end, 322d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood}; 323d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 324ce6d73c94d7cfbdc3dc31860fe14af2f37d1b4feUwe Kleine-Königstatic const struct of_device_id pmc_match[]; 325000061245a6797d542854106463b6b20fbdcb12eGrant Likelystatic int pmc_probe(struct platform_device *ofdev) 326d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 327b1608d69cb804e414d0887140ba08a9398e4e638Grant Likely const struct of_device_id *match; 32861c7a080a5a061c976988fd4b844dfb468dda255Grant Likely struct device_node *np = ofdev->dev.of_node; 329d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood struct resource res; 330bfef61d03eba45984207208222b5a3506258e537Uwe Kleine-König const struct pmc_type *type; 331d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood int ret = 0; 332d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 333b1608d69cb804e414d0887140ba08a9398e4e638Grant Likely match = of_match_device(pmc_match, &ofdev->dev); 334b1608d69cb804e414d0887140ba08a9398e4e638Grant Likely if (!match) 335000061245a6797d542854106463b6b20fbdcb12eGrant Likely return -EINVAL; 336000061245a6797d542854106463b6b20fbdcb12eGrant Likely 337b1608d69cb804e414d0887140ba08a9398e4e638Grant Likely type = match->data; 338000061245a6797d542854106463b6b20fbdcb12eGrant Likely 339d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (!of_device_is_available(np)) 340d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return -ENODEV; 341d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 342d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood has_deep_sleep = type->has_deep_sleep; 343d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood immrbase = get_immrbase(); 344d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood pmc_dev = ofdev; 345d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 346d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood is_pci_agent = mpc83xx_is_pci_agent(); 347d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (is_pci_agent < 0) 348d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return is_pci_agent; 349d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 350d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = of_address_to_resource(np, 0, &res); 351d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (ret) 352d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return -ENODEV; 353d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 354d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood pmc_irq = irq_of_parse_and_map(np, 0); 355d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (pmc_irq != NO_IRQ) { 356d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = request_irq(pmc_irq, pmc_irq_handler, IRQF_SHARED, 357d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood "pmc", ofdev); 358d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 359d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (ret) 360d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return -EBUSY; 361d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 362d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 363d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood pmc_regs = ioremap(res.start, sizeof(struct mpc83xx_pmc)); 364d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 365d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (!pmc_regs) { 366d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = -ENOMEM; 367d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood goto out; 368d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 369d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 370d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = of_address_to_resource(np, 1, &res); 371d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (ret) { 372d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = -ENODEV; 373d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood goto out_pmc; 374d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 375d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 376d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood clock_regs = ioremap(res.start, sizeof(struct mpc83xx_pmc)); 377d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 378d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (!clock_regs) { 379d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = -ENOMEM; 380d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood goto out_pmc; 381d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 382d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 38387faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov if (has_deep_sleep) { 38487faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov syscr_regs = ioremap(immrbase + IMMR_SYSCR_OFFSET, 38587faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov sizeof(*syscr_regs)); 38687faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov if (!syscr_regs) { 38787faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov ret = -ENOMEM; 38887faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov goto out_syscr; 38987faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov } 39087faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov } 39187faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov 392d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (is_pci_agent) 393d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood mpc83xx_set_agent(); 394d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 395d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood suspend_set_ops(&mpc83xx_suspend_ops); 396d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return 0; 397d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 39887faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsovout_syscr: 39987faaabb09d0802d54ff863a16ca821d7c669cb6Anton Vorontsov iounmap(clock_regs); 400d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodout_pmc: 401d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood iounmap(pmc_regs); 402d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodout: 403d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (pmc_irq != NO_IRQ) 404d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood free_irq(pmc_irq, ofdev); 405d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 406d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return ret; 407d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 408d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 409a454dc50590c6d758abba016a303a221f2f1b4b8Grant Likelystatic int pmc_remove(struct platform_device *ofdev) 410d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 411d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return -EPERM; 412d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood}; 413d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 414d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic struct pmc_type pmc_types[] = { 415d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood { 416d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .has_deep_sleep = 1, 417d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood }, 418d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood { 419d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .has_deep_sleep = 0, 420d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 421d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood}; 422d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 423ce6d73c94d7cfbdc3dc31860fe14af2f37d1b4feUwe Kleine-Königstatic const struct of_device_id pmc_match[] = { 424d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood { 425d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .compatible = "fsl,mpc8313-pmc", 426d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .data = &pmc_types[0], 427d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood }, 428d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood { 429d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .compatible = "fsl,mpc8349-pmc", 430d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .data = &pmc_types[1], 431d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood }, 432d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood {} 433d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood}; 434d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 435000061245a6797d542854106463b6b20fbdcb12eGrant Likelystatic struct platform_driver pmc_driver = { 4364018294b53d1dae026880e45f174c1cc63b5d435Grant Likely .driver = { 4374018294b53d1dae026880e45f174c1cc63b5d435Grant Likely .name = "mpc83xx-pmc", 4384018294b53d1dae026880e45f174c1cc63b5d435Grant Likely .owner = THIS_MODULE, 4394018294b53d1dae026880e45f174c1cc63b5d435Grant Likely .of_match_table = pmc_match, 4404018294b53d1dae026880e45f174c1cc63b5d435Grant Likely }, 441d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .probe = pmc_probe, 442d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .remove = pmc_remove 443d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood}; 444d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 445d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int pmc_init(void) 446d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 447000061245a6797d542854106463b6b20fbdcb12eGrant Likely return platform_driver_register(&pmc_driver); 448d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 449d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 450d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodmodule_init(pmc_init); 451