Commit 25e9b4db authored by Russell King's avatar Russell King

[ARM] Add better PM support to SA1111 and SA11x0.

This follows our existing PM interfaces.
parent 7725cb81
...@@ -743,18 +743,11 @@ struct sa1111_save_data { ...@@ -743,18 +743,11 @@ struct sa1111_save_data {
static int sa1111_suspend(struct device *dev, u32 state, u32 level) static int sa1111_suspend(struct device *dev, u32 state, u32 level)
{ {
struct sa1111 *sachip = dev_get_drvdata(dev); struct sa1111 *sachip = dev_get_drvdata(dev);
struct sa1111_save_data *save;
unsigned long flags; unsigned long flags;
char *base; char *base;
/* if (!dev->saved_state && level == SUSPEND_NOTIFY)
* Save state.
*/
if (level == SUSPEND_SAVE_STATE ||
level == SUSPEND_DISABLE ||
level == SUSPEND_POWER_DOWN) {
struct sa1111_save_data *save;
if (!dev->saved_state)
dev->saved_state = kmalloc(sizeof(struct sa1111_save_data), GFP_KERNEL); dev->saved_state = kmalloc(sizeof(struct sa1111_save_data), GFP_KERNEL);
if (!dev->saved_state) if (!dev->saved_state)
return -ENOMEM; return -ENOMEM;
...@@ -762,6 +755,11 @@ static int sa1111_suspend(struct device *dev, u32 state, u32 level) ...@@ -762,6 +755,11 @@ static int sa1111_suspend(struct device *dev, u32 state, u32 level)
save = (struct sa1111_save_data *)dev->saved_state; save = (struct sa1111_save_data *)dev->saved_state;
spin_lock_irqsave(&sachip->lock, flags); spin_lock_irqsave(&sachip->lock, flags);
/*
* Save state.
*/
if (level == SUSPEND_SAVE_STATE) {
base = sachip->base; base = sachip->base;
save->skcr = sa1111_readl(base + SA1111_SKCR); save->skcr = sa1111_readl(base + SA1111_SKCR);
save->skpcr = sa1111_readl(base + SA1111_SKPCR); save->skpcr = sa1111_readl(base + SA1111_SKPCR);
...@@ -779,25 +777,20 @@ static int sa1111_suspend(struct device *dev, u32 state, u32 level) ...@@ -779,25 +777,20 @@ static int sa1111_suspend(struct device *dev, u32 state, u32 level)
save->wakepol1 = sa1111_readl(base + SA1111_WAKEPOL1); save->wakepol1 = sa1111_readl(base + SA1111_WAKEPOL1);
save->wakeen0 = sa1111_readl(base + SA1111_WAKEEN0); save->wakeen0 = sa1111_readl(base + SA1111_WAKEEN0);
save->wakeen1 = sa1111_readl(base + SA1111_WAKEEN1); save->wakeen1 = sa1111_readl(base + SA1111_WAKEEN1);
spin_unlock_irqrestore(&sachip->lock, flags);
} }
/* /*
* Disable. * Disable.
*/ */
if (level == SUSPEND_DISABLE && state == 4) { if (level == SUSPEND_POWER_DOWN && state == 4) {
unsigned int val; unsigned int val = sa1111_readl(sachip->base + SA1111_SKCR);
spin_lock_irqsave(&sachip->lock, flags);
base = sachip->base;
sa1111_writel(0, base + SA1111_SKPWM0); sa1111_writel(val | SKCR_SLEEP, sachip->base + SA1111_SKCR);
sa1111_writel(0, base + SA1111_SKPWM1); sa1111_writel(0, sachip->base + SA1111_SKPWM0);
val = sa1111_readl(base + SA1111_SKCR); sa1111_writel(0, sachip->base + SA1111_SKPWM1);
sa1111_writel(val | SKCR_SLEEP, base + SA1111_SKCR); }
spin_unlock_irqrestore(&sachip->lock, flags); spin_unlock_irqrestore(&sachip->lock, flags);
}
return 0; return 0;
} }
...@@ -819,17 +812,15 @@ static int sa1111_resume(struct device *dev, u32 level) ...@@ -819,17 +812,15 @@ static int sa1111_resume(struct device *dev, u32 level)
unsigned long flags, id; unsigned long flags, id;
char *base; char *base;
if (level != RESUME_RESTORE_STATE && level != RESUME_ENABLE)
return 0;
save = (struct sa1111_save_data *)dev->saved_state; save = (struct sa1111_save_data *)dev->saved_state;
if (!save) if (!save)
return 0; return 0;
dev->saved_state = NULL; spin_lock_irqsave(&sachip->lock, flags);
/* /*
* Ensure that the SA1111 is still here. * Ensure that the SA1111 is still here.
* FIXME: shouldn't do this here.
*/ */
id = sa1111_readl(sachip->base + SA1111_SKID); id = sa1111_readl(sachip->base + SA1111_SKID);
if ((id & SKID_ID_MASK) != SKID_SA1111_ID) { if ((id & SKID_ID_MASK) != SKID_SA1111_ID) {
...@@ -839,9 +830,17 @@ static int sa1111_resume(struct device *dev, u32 level) ...@@ -839,9 +830,17 @@ static int sa1111_resume(struct device *dev, u32 level)
return 0; return 0;
} }
spin_lock_irqsave(&sachip->lock, flags); /*
* First of all, wake up the chip.
*/
if (level == RESUME_POWER_ON) {
sa1111_wake(sachip); sa1111_wake(sachip);
sa1111_writel(0, sachip->base + SA1111_INTC + SA1111_INTEN0);
sa1111_writel(0, sachip->base + SA1111_INTC + SA1111_INTEN1);
}
if (level == RESUME_RESTORE_STATE) {
base = sachip->base; base = sachip->base;
sa1111_writel(save->skcr, base + SA1111_SKCR); sa1111_writel(save->skcr, base + SA1111_SKCR);
sa1111_writel(save->skpcr, base + SA1111_SKPCR); sa1111_writel(save->skpcr, base + SA1111_SKPCR);
...@@ -859,9 +858,14 @@ static int sa1111_resume(struct device *dev, u32 level) ...@@ -859,9 +858,14 @@ static int sa1111_resume(struct device *dev, u32 level)
sa1111_writel(save->wakepol1, base + SA1111_WAKEPOL1); sa1111_writel(save->wakepol1, base + SA1111_WAKEPOL1);
sa1111_writel(save->wakeen0, base + SA1111_WAKEEN0); sa1111_writel(save->wakeen0, base + SA1111_WAKEEN0);
sa1111_writel(save->wakeen1, base + SA1111_WAKEEN1); sa1111_writel(save->wakeen1, base + SA1111_WAKEEN1);
}
spin_unlock_irqrestore(&sachip->lock, flags); spin_unlock_irqrestore(&sachip->lock, flags);
if (level == RESUME_ENABLE) {
dev->saved_state = NULL;
kfree(save); kfree(save);
}
return 0; return 0;
} }
......
...@@ -68,7 +68,7 @@ static int sa1100_gpio_type(unsigned int irq, unsigned int type) ...@@ -68,7 +68,7 @@ static int sa1100_gpio_type(unsigned int irq, unsigned int type)
} }
/* /*
* GPIO IRQs must be acknoledged. This is for IRQs from 0 to 10. * GPIO IRQs must be acknowledged. This is for IRQs from 0 to 10.
*/ */
static void sa1100_low_gpio_ack(unsigned int irq) static void sa1100_low_gpio_ack(unsigned int irq)
{ {
...@@ -211,6 +211,99 @@ static struct resource irq_resource = { ...@@ -211,6 +211,99 @@ static struct resource irq_resource = {
.end = 0x9005ffff, .end = 0x9005ffff,
}; };
struct sa1100irq_state {
unsigned int saved;
unsigned int icmr;
unsigned int iclr;
unsigned int iccr;
};
static int sa1100irq_suspend(struct device *dev, u32 state, u32 level)
{
struct sa1100irq_state *st;
if (!dev->saved_state && level == SUSPEND_NOTIFY)
dev->saved_state = kmalloc(sizeof(struct sa1100irq_state),
GFP_KERNEL);
if (!dev->saved_state)
return -ENOMEM;
if (level == SUSPEND_POWER_DOWN) {
st = (struct sa1100irq_state *)dev->saved_state;
st->saved = 1;
st->icmr = ICMR;
st->iclr = ICLR;
st->iccr = ICCR;
/*
* Disable all GPIO-based interrupts.
*/
ICMR &= ~(IC_GPIO11_27|IC_GPIO10|IC_GPIO9|IC_GPIO8|IC_GPIO7|
IC_GPIO6|IC_GPIO5|IC_GPIO4|IC_GPIO3|IC_GPIO2|
IC_GPIO1|IC_GPIO0);
/*
* Set the appropriate edges for wakeup.
*/
GRER = PWER & GPIO_IRQ_rising_edge;
GFER = PWER & GPIO_IRQ_falling_edge;
/*
* Clear any pending GPIO interrupts.
*/
GEDR = GEDR;
}
return 0;
}
static int sa1100irq_resume(struct device *dev, u32 level)
{
struct sa1100irq_state *st;
if (level == RESUME_POWER_ON) {
st = (struct sa1100irq_state *)dev->saved_state;
dev->saved_state = NULL;
if (st->saved) {
ICCR = st->iccr;
ICLR = st->iclr;
GRER = GPIO_IRQ_rising_edge & GPIO_IRQ_mask;
GFER = GPIO_IRQ_falling_edge & GPIO_IRQ_mask;
ICMR = st->icmr;
}
kfree(st);
}
return 0;
}
static struct device_driver sa1100irq_driver = {
.name = "sa11x0-irq",
.bus = &system_bus_type,
.suspend = sa1100irq_suspend,
.resume = sa1100irq_resume,
};
static struct sys_device sa1100irq_device = {
.name = "irq",
.id = 0,
.dev = {
.name = "Intel SA11x0 [Interrupt Controller]",
.driver = &sa1100irq_driver,
},
};
static int __init sa1100irq_init_devicefs(void)
{
driver_register(&sa1100irq_driver);
return sys_device_register(&sa1100irq_device);
}
device_initcall(sa1100irq_init_devicefs);
void __init sa1100_init_irq(void) void __init sa1100_init_irq(void)
{ {
unsigned int irq; unsigned int irq;
......
...@@ -45,10 +45,9 @@ enum { SLEEP_SAVE_SP = 0, ...@@ -45,10 +45,9 @@ enum { SLEEP_SAVE_SP = 0,
SLEEP_SAVE_OSCR, SLEEP_SAVE_OIER, SLEEP_SAVE_OSCR, SLEEP_SAVE_OIER,
SLEEP_SAVE_OSMR0, SLEEP_SAVE_OSMR1, SLEEP_SAVE_OSMR2, SLEEP_SAVE_OSMR3, SLEEP_SAVE_OSMR0, SLEEP_SAVE_OSMR1, SLEEP_SAVE_OSMR2, SLEEP_SAVE_OSMR3,
SLEEP_SAVE_GPDR, SLEEP_SAVE_GRER, SLEEP_SAVE_GFER, SLEEP_SAVE_GAFR, SLEEP_SAVE_GPDR, SLEEP_SAVE_GAFR,
SLEEP_SAVE_PPDR, SLEEP_SAVE_PPSR, SLEEP_SAVE_PPAR, SLEEP_SAVE_PSDR, SLEEP_SAVE_PPDR, SLEEP_SAVE_PPSR, SLEEP_SAVE_PPAR, SLEEP_SAVE_PSDR,
SLEEP_SAVE_ICMR,
SLEEP_SAVE_Ser1SDCR0, SLEEP_SAVE_Ser1SDCR0,
SLEEP_SAVE_SIZE SLEEP_SAVE_SIZE
...@@ -71,8 +70,6 @@ int pm_do_suspend(void) ...@@ -71,8 +70,6 @@ int pm_do_suspend(void)
SAVE(OIER); SAVE(OIER);
SAVE(GPDR); SAVE(GPDR);
SAVE(GRER);
SAVE(GFER);
SAVE(GAFR); SAVE(GAFR);
SAVE(PPDR); SAVE(PPDR);
...@@ -82,13 +79,6 @@ int pm_do_suspend(void) ...@@ -82,13 +79,6 @@ int pm_do_suspend(void)
SAVE(Ser1SDCR0); SAVE(Ser1SDCR0);
SAVE(ICMR);
/* ... maybe a global variable initialized by arch code to set this? */
GRER = PWER;
GFER = 0;
GEDR = GEDR;
/* Clear previous reset status */ /* Clear previous reset status */
RCSR = RCSR_HWR | RCSR_SWR | RCSR_WDR | RCSR_SMR; RCSR = RCSR_HWR | RCSR_SWR | RCSR_WDR | RCSR_SMR;
...@@ -98,22 +88,23 @@ int pm_do_suspend(void) ...@@ -98,22 +88,23 @@ int pm_do_suspend(void)
/* go zzz */ /* go zzz */
sa1100_cpu_suspend(); sa1100_cpu_suspend();
/* ensure not to come back here if it wasn't intended */ /*
* Ensure not to come back here if it wasn't intended
*/
PSPR = 0; PSPR = 0;
#ifdef DEBUG /*
printk(KERN_DEBUG "*** made it back from resume\n"); * Ensure interrupt sources are disabled; we will re-init
#endif * the interrupt subsystem via the device manager.
*/
ICLR = 0;
ICCR = 1;
ICMR = 0;
/* restore registers */ /* restore registers */
RESTORE(GPDR); RESTORE(GPDR);
RESTORE(GRER);
RESTORE(GFER);
RESTORE(GAFR); RESTORE(GAFR);
/* clear any edge detect bit */
GEDR = GEDR;
RESTORE(PPDR); RESTORE(PPDR);
RESTORE(PPSR); RESTORE(PPSR);
RESTORE(PPAR); RESTORE(PPAR);
...@@ -121,6 +112,9 @@ int pm_do_suspend(void) ...@@ -121,6 +112,9 @@ int pm_do_suspend(void)
RESTORE(Ser1SDCR0); RESTORE(Ser1SDCR0);
/*
* Clear the peripheral sleep-hold bit.
*/
PSSR = PSSR_PH; PSSR = PSSR_PH;
RESTORE(OSMR0); RESTORE(OSMR0);
...@@ -130,10 +124,6 @@ int pm_do_suspend(void) ...@@ -130,10 +124,6 @@ int pm_do_suspend(void)
RESTORE(OSCR); RESTORE(OSCR);
RESTORE(OIER); RESTORE(OIER);
ICLR = 0;
ICCR = 1;
RESTORE(ICMR);
/* restore current time */ /* restore current time */
xtime.tv_sec = RCNR; xtime.tv_sec = RCNR;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment