suspend.c revision f25c525c1412675d2b23d5d88660fb5c9f3a5341
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/init.h> 14d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/pm.h> 15d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/types.h> 16d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/ioport.h> 17d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/interrupt.h> 18d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/wait.h> 19d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/kthread.h> 20d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/freezer.h> 21d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/suspend.h> 22d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/fsl_devices.h> 23d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <linux/of_platform.h> 24d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 25d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <asm/reg.h> 26d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <asm/io.h> 27d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <asm/time.h> 28d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <asm/mpc6xx.h> 29d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 30d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#include <sysdev/fsl_soc.h> 31d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 32d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCCR1_NEXT_STATE 0x0C /* Next state for power management */ 33d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCCR1_NEXT_STATE_SHIFT 2 34d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCCR1_CURR_STATE 0x03 /* Current state for power management*/ 35d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define IMMR_RCW_OFFSET 0x900 36d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define RCW_PCI_HOST 0x80000000 37d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 38d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodvoid mpc83xx_enter_deep_sleep(phys_addr_t immrbase); 39d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 40d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstruct mpc83xx_pmc { 41d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 config; 42d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCCR_DLPEN 2 /* DDR SDRAM low power enable */ 43d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCCR_SLPEN 1 /* System low power enable */ 44d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 45d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 event; 46d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 mask; 47d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood/* All but PMCI are deep-sleep only */ 48d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_GPIO 0x100 49d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_PCI 0x080 50d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_USB 0x040 51d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_ETSEC1 0x020 52d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_ETSEC2 0x010 53d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_TIMER 0x008 54d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_INT1 0x004 55d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_INT2 0x002 56d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_PMCI 0x001 57d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCER_ALL 0x1FF 58d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 59d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood /* deep-sleep only */ 60d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 config1; 61d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCCR1_USE_STATE 0x80000000 62d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCCR1_PME_EN 0x00000080 63d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCCR1_ASSERT_PME 0x00000040 64d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood#define PMCCR1_POWER_OFF 0x00000020 65d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 66d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood /* deep-sleep only */ 67d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 config2; 68d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood}; 69d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 70d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstruct mpc83xx_rcw { 71d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 rcwlr; 72d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 rcwhr; 73d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood}; 74d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 75d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstruct mpc83xx_clock { 76d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 spmr; 77d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 occr; 78d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 sccr; 79d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood}; 80d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 81d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstruct pmc_type { 82d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood int has_deep_sleep; 83d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood}; 84d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 85d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic struct of_device *pmc_dev; 86d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int has_deep_sleep, deep_sleeping; 87d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int pmc_irq; 88d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic struct mpc83xx_pmc __iomem *pmc_regs; 89d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic struct mpc83xx_clock __iomem *clock_regs; 90d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int is_pci_agent, wake_from_pci; 91d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic phys_addr_t immrbase; 92d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int pci_pm_state; 93d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic DECLARE_WAIT_QUEUE_HEAD(agent_wq); 94d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 95d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodint fsl_deep_sleep(void) 96d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 97d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return deep_sleeping; 98d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 992e9d546eda5888962a441da1e96bbf92cb5b1cbbAnton VorontsovEXPORT_SYMBOL(fsl_deep_sleep); 100d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 101d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int mpc83xx_change_state(void) 102d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 103d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 curr_state; 104d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 reg_cfg1 = in_be32(&pmc_regs->config1); 105d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 106d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (is_pci_agent) { 107d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood pci_pm_state = (reg_cfg1 & PMCCR1_NEXT_STATE) >> 108d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood PMCCR1_NEXT_STATE_SHIFT; 109d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood curr_state = reg_cfg1 & PMCCR1_CURR_STATE; 110d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 111d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (curr_state != pci_pm_state) { 112d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood reg_cfg1 &= ~PMCCR1_CURR_STATE; 113d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood reg_cfg1 |= pci_pm_state; 114d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->config1, reg_cfg1); 115d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 116d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood wake_up(&agent_wq); 117d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return 1; 118d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 119d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 120d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 121d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return 0; 122d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 123d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 124d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic irqreturn_t pmc_irq_handler(int irq, void *dev_id) 125d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 126d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood u32 event = in_be32(&pmc_regs->event); 127d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood int ret = IRQ_NONE; 128d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 129d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (mpc83xx_change_state()) 130d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = IRQ_HANDLED; 131d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 132d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (event) { 133d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->event, event); 134d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = IRQ_HANDLED; 135d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 136d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 137d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return ret; 138d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 139d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 140d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int mpc83xx_suspend_enter(suspend_state_t state) 141d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 142d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood int ret = -EAGAIN; 143d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 144d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood /* Don't go to sleep if there's a race where pci_pm_state changes 145d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * between the agent thread checking it and the PM code disabling 146d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * interrupts. 147d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood */ 148d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (wake_from_pci) { 149d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (pci_pm_state != (deep_sleeping ? 3 : 2)) 150d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood goto out; 151d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 152d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->config1, 153d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood in_be32(&pmc_regs->config1) | PMCCR1_PME_EN); 154d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 155d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 156d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood /* Put the system into low-power mode and the RAM 157d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * into self-refresh mode once the core goes to 158d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * sleep. 159d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood */ 160d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 161d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->config, PMCCR_SLPEN | PMCCR_DLPEN); 162d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 163d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood /* If it has deep sleep (i.e. it's an 831x or compatible), 164d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * disable power to the core upon entering sleep mode. This will 165d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * require going through the boot firmware upon a wakeup event. 166d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood */ 167d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 168d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (deep_sleeping) { 169d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->mask, PMCER_ALL); 170d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 171d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->config1, 172d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood in_be32(&pmc_regs->config1) | PMCCR1_POWER_OFF); 173d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 174d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood enable_kernel_fp(); 175d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 176d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood mpc83xx_enter_deep_sleep(immrbase); 177d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 178d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->config1, 179d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood in_be32(&pmc_regs->config1) & ~PMCCR1_POWER_OFF); 180d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 181d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->mask, PMCER_PMCI); 182d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } else { 183d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->mask, PMCER_PMCI); 184d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 185d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood mpc6xx_enter_standby(); 186d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 187d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 188d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = 0; 189d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 190d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodout: 191d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->config1, 192d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood in_be32(&pmc_regs->config1) & ~PMCCR1_PME_EN); 193d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 194d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return ret; 195d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 196d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 197f25c525c1412675d2b23d5d88660fb5c9f3a5341Anton Vorontsovstatic void mpc83xx_suspend_end(void) 198d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 199d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood deep_sleeping = 0; 200d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 201d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 202d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int mpc83xx_suspend_valid(suspend_state_t state) 203d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 204d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM; 205d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 206d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 207d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int mpc83xx_suspend_begin(suspend_state_t state) 208d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 209d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood switch (state) { 210d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood case PM_SUSPEND_STANDBY: 211d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood deep_sleeping = 0; 212d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return 0; 213d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 214d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood case PM_SUSPEND_MEM: 215d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (has_deep_sleep) 216d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood deep_sleeping = 1; 217d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 218d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return 0; 219d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 220d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood default: 221d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return -EINVAL; 222d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 223d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 224d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 225d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int agent_thread_fn(void *data) 226d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 227d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood while (1) { 228d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood wait_event_interruptible(agent_wq, pci_pm_state >= 2); 229d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood try_to_freeze(); 230d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 231d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (signal_pending(current) || pci_pm_state < 2) 232d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood continue; 233d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 234d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood /* With a preemptible kernel (or SMP), this could race with 235d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * a userspace-driven suspend request. It's probably best 236d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * to avoid mixing the two with such a configuration (or 237d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * else fix it by adding a mutex to state_store that we can 238d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood * synchronize with). 239d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood */ 240d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 241d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood wake_from_pci = 1; 242d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 243d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood pm_suspend(pci_pm_state == 3 ? PM_SUSPEND_MEM : 244d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood PM_SUSPEND_STANDBY); 245d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 246d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood wake_from_pci = 0; 247d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 248d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 249d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return 0; 250d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 251d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 252d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic void mpc83xx_set_agent(void) 253d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 254d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->config1, PMCCR1_USE_STATE); 255d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood out_be32(&pmc_regs->mask, PMCER_PMCI); 256d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 257d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood kthread_run(agent_thread_fn, NULL, "PCI power mgt"); 258d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 259d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 260d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int mpc83xx_is_pci_agent(void) 261d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 262d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood struct mpc83xx_rcw __iomem *rcw_regs; 263d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood int ret; 264d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 265d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood rcw_regs = ioremap(get_immrbase() + IMMR_RCW_OFFSET, 266d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood sizeof(struct mpc83xx_rcw)); 267d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 268d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (!rcw_regs) 269d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return -ENOMEM; 270d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 271d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = !(in_be32(&rcw_regs->rcwhr) & RCW_PCI_HOST); 272d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 273d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood iounmap(rcw_regs); 274d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return ret; 275d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 276d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 277d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic struct platform_suspend_ops mpc83xx_suspend_ops = { 278d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .valid = mpc83xx_suspend_valid, 279d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .begin = mpc83xx_suspend_begin, 280d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .enter = mpc83xx_suspend_enter, 281f25c525c1412675d2b23d5d88660fb5c9f3a5341Anton Vorontsov .end = mpc83xx_suspend_end, 282d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood}; 283d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 284d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int pmc_probe(struct of_device *ofdev, 285d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood const struct of_device_id *match) 286d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 287d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood struct device_node *np = ofdev->node; 288d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood struct resource res; 289d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood struct pmc_type *type = match->data; 290d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood int ret = 0; 291d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 292d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (!of_device_is_available(np)) 293d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return -ENODEV; 294d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 295d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood has_deep_sleep = type->has_deep_sleep; 296d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood immrbase = get_immrbase(); 297d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood pmc_dev = ofdev; 298d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 299d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood is_pci_agent = mpc83xx_is_pci_agent(); 300d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (is_pci_agent < 0) 301d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return is_pci_agent; 302d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 303d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = of_address_to_resource(np, 0, &res); 304d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (ret) 305d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return -ENODEV; 306d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 307d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood pmc_irq = irq_of_parse_and_map(np, 0); 308d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (pmc_irq != NO_IRQ) { 309d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = request_irq(pmc_irq, pmc_irq_handler, IRQF_SHARED, 310d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood "pmc", ofdev); 311d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 312d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (ret) 313d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return -EBUSY; 314d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 315d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 316d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood pmc_regs = ioremap(res.start, sizeof(struct mpc83xx_pmc)); 317d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 318d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (!pmc_regs) { 319d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = -ENOMEM; 320d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood goto out; 321d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 322d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 323d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = of_address_to_resource(np, 1, &res); 324d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (ret) { 325d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = -ENODEV; 326d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood goto out_pmc; 327d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 328d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 329d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood clock_regs = ioremap(res.start, sizeof(struct mpc83xx_pmc)); 330d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 331d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (!clock_regs) { 332d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood ret = -ENOMEM; 333d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood goto out_pmc; 334d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 335d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 336d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (is_pci_agent) 337d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood mpc83xx_set_agent(); 338d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 339d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood suspend_set_ops(&mpc83xx_suspend_ops); 340d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return 0; 341d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 342d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodout_pmc: 343d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood iounmap(pmc_regs); 344d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodout: 345d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood if (pmc_irq != NO_IRQ) 346d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood free_irq(pmc_irq, ofdev); 347d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 348d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return ret; 349d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 350d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 351d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int pmc_remove(struct of_device *ofdev) 352d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 353d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return -EPERM; 354d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood}; 355d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 356d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic struct pmc_type pmc_types[] = { 357d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood { 358d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .has_deep_sleep = 1, 359d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood }, 360d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood { 361d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .has_deep_sleep = 0, 362d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood } 363d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood}; 364d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 365d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic struct of_device_id pmc_match[] = { 366d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood { 367d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .compatible = "fsl,mpc8313-pmc", 368d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .data = &pmc_types[0], 369d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood }, 370d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood { 371d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .compatible = "fsl,mpc8349-pmc", 372d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .data = &pmc_types[1], 373d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood }, 374d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood {} 375d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood}; 376d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 377d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic struct of_platform_driver pmc_driver = { 378d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .name = "mpc83xx-pmc", 379d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .match_table = pmc_match, 380d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .probe = pmc_probe, 381d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood .remove = pmc_remove 382d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood}; 383d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 384d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodstatic int pmc_init(void) 385d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood{ 386d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood return of_register_platform_driver(&pmc_driver); 387d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood} 388d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Wood 389d49747bdfb2ddebea24d1580da55b79d093d48a9Scott Woodmodule_init(pmc_init); 390