[POWERPC] Fix sleep on powerbook 3400

Sleep on the powerbook 3400 has been broken since the change that made
powerbook_sleep_3400 call pmac_suspend_devices(), which disables
interrupts.  There are a couple of loops in powerbook_sleep_3400 that
depend on interrupts being enabled, and in fact it has to have
interrupts enabled at the point of going to sleep since it is an
interrupt from the PMU that wakes it up.

This fixes it by using pmu_wait_complete() instead of a spinloop, and
by explicitly enabling interrupts before putting the CPU into sleep
mode (which is OK since all interrupts except the PMU interrupt have
been disabled at the interrupt controller by this stage).

This changes the logic so that it keeps putting the CPU into sleep mode
until the completion of the interrupt transaction from the PMU that
signals the end of sleep.  Also, we now call pmu_unlock() before sleep
so that the via_pmu_interrupt() code can process the interrupt event
from the PMU properly.

Now that generic code saves and restores PCI state, it is no longer
necessary to do that here.  Thus pbook_pci_save/restore and related
functions are no longer necessary, so this removes them.

Lastly, this moves the ioremap of the memory controller to init code
rather than doing it on every sleep/wakeup cycle.

Signed-off-by: Paul Mackerras <paulus@samba.org>
This commit is contained in:
Paul Mackerras 2007-12-19 22:45:31 +11:00
parent 98f6740ea6
commit 887ef35ae4

View File

@ -202,6 +202,12 @@ static int proc_read_options(char *page, char **start, off_t off,
static int proc_write_options(struct file *file, const char __user *buffer,
unsigned long count, void *data);
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
static void powerbook_sleep_init_3400(void);
#else
#define powerbook_sleep_init_3400() do { } while (0)
#endif
#ifdef CONFIG_ADB
struct adb_driver via_pmu_driver = {
"PMU",
@ -449,6 +455,10 @@ static int __init via_pmu_start(void)
pmu_poll();
} while (pmu_state != idle);
/* Do allocations and ioremaps that will be needed for sleep */
if (pmu_kind == PMU_OHARE_BASED)
powerbook_sleep_init_3400();
return 0;
}
@ -1719,108 +1729,6 @@ pmu_present(void)
}
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
/*
* This struct is used to store config register values for
* PCI devices which may get powered off when we sleep.
*/
static struct pci_save {
u16 command;
u16 cache_lat;
u16 intr;
u32 rom_address;
} *pbook_pci_saves;
static int pbook_npci_saves;
static void
pbook_alloc_pci_save(void)
{
int npci;
struct pci_dev *pd = NULL;
npci = 0;
while ((pd = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
++npci;
}
if (npci == 0)
return;
pbook_pci_saves = (struct pci_save *)
kmalloc(npci * sizeof(struct pci_save), GFP_KERNEL);
pbook_npci_saves = npci;
}
static void
pbook_free_pci_save(void)
{
if (pbook_pci_saves == NULL)
return;
kfree(pbook_pci_saves);
pbook_pci_saves = NULL;
pbook_npci_saves = 0;
}
static void
pbook_pci_save(void)
{
struct pci_save *ps = pbook_pci_saves;
struct pci_dev *pd = NULL;
int npci = pbook_npci_saves;
if (ps == NULL)
return;
while ((pd = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
if (npci-- == 0) {
pci_dev_put(pd);
return;
}
pci_read_config_word(pd, PCI_COMMAND, &ps->command);
pci_read_config_word(pd, PCI_CACHE_LINE_SIZE, &ps->cache_lat);
pci_read_config_word(pd, PCI_INTERRUPT_LINE, &ps->intr);
pci_read_config_dword(pd, PCI_ROM_ADDRESS, &ps->rom_address);
++ps;
}
}
/* For this to work, we must take care of a few things: If gmac was enabled
* during boot, it will be in the pci dev list. If it's disabled at this point
* (and it will probably be), then you can't access it's config space.
*/
static void
pbook_pci_restore(void)
{
u16 cmd;
struct pci_save *ps = pbook_pci_saves - 1;
struct pci_dev *pd = NULL;
int npci = pbook_npci_saves;
int j;
while ((pd = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
if (npci-- == 0)
return;
ps++;
if (ps->command == 0)
continue;
pci_read_config_word(pd, PCI_COMMAND, &cmd);
if ((ps->command & ~cmd) == 0)
continue;
switch (pd->hdr_type) {
case PCI_HEADER_TYPE_NORMAL:
for (j = 0; j < 6; ++j)
pci_write_config_dword(pd,
PCI_BASE_ADDRESS_0 + j*4,
pd->resource[j].start);
pci_write_config_dword(pd, PCI_ROM_ADDRESS,
ps->rom_address);
pci_write_config_word(pd, PCI_CACHE_LINE_SIZE,
ps->cache_lat);
pci_write_config_word(pd, PCI_INTERRUPT_LINE,
ps->intr);
pci_write_config_word(pd, PCI_COMMAND, ps->command);
break;
}
}
}
#ifdef DEBUG_SLEEP
/* N.B. This doesn't work on the 3400 */
void
@ -2200,37 +2108,34 @@ powerbook_sleep_Core99(void)
#define PB3400_MEM_CTRL 0xf8000000
#define PB3400_MEM_CTRL_SLEEP 0x70
static int
powerbook_sleep_3400(void)
static void __iomem *pb3400_mem_ctrl;
static void powerbook_sleep_init_3400(void)
{
/* map in the memory controller registers */
pb3400_mem_ctrl = ioremap(PB3400_MEM_CTRL, 0x100);
if (pb3400_mem_ctrl == NULL)
printk(KERN_WARNING "ioremap failed: sleep won't be possible");
}
static int powerbook_sleep_3400(void)
{
int ret, i, x;
unsigned int hid0;
unsigned long p;
unsigned long msr;
struct adb_request sleep_req;
void __iomem *mem_ctrl;
unsigned int __iomem *mem_ctrl_sleep;
/* first map in the memory controller registers */
mem_ctrl = ioremap(PB3400_MEM_CTRL, 0x100);
if (mem_ctrl == NULL) {
printk("powerbook_sleep_3400: ioremap failed\n");
if (pb3400_mem_ctrl == NULL)
return -ENOMEM;
}
mem_ctrl_sleep = mem_ctrl + PB3400_MEM_CTRL_SLEEP;
/* Allocate room for PCI save */
pbook_alloc_pci_save();
mem_ctrl_sleep = pb3400_mem_ctrl + PB3400_MEM_CTRL_SLEEP;
ret = pmac_suspend_devices();
if (ret) {
pbook_free_pci_save();
printk(KERN_ERR "Sleep rejected by devices\n");
return ret;
}
/* Save the state of PCI config space for some slots */
pbook_pci_save();
/* Set the memory controller to keep the memory refreshed
while we're asleep */
for (i = 0x403f; i >= 0x4000; --i) {
@ -2244,36 +2149,31 @@ powerbook_sleep_3400(void)
/* Ask the PMU to put us to sleep */
pmu_request(&sleep_req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
while (!sleep_req.complete)
mb();
pmu_wait_complete(&sleep_req);
pmu_unlock();
pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1);
pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 1);
/* displacement-flush the L2 cache - necessary? */
for (p = KERNELBASE; p < KERNELBASE + 0x100000; p += 0x1000)
i = *(volatile int *)p;
asleep = 1;
/* Put the CPU into sleep mode */
hid0 = mfspr(SPRN_HID0);
hid0 = (hid0 & ~(HID0_NAP | HID0_DOZE)) | HID0_SLEEP;
mtspr(SPRN_HID0, hid0);
mtmsr(mfmsr() | MSR_POW | MSR_EE);
udelay(10);
local_irq_enable();
msr = mfmsr() | MSR_POW;
while (asleep) {
mb();
mtmsr(msr);
isync();
}
local_irq_disable();
/* OK, we're awake again, start restoring things */
out_be32(mem_ctrl_sleep, 0x3f);
pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
pbook_pci_restore();
pmu_unlock();
/* wait for the PMU interrupt sequence to complete */
while (asleep)
mb();
pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 0);
pmac_wakeup_devices();
pbook_free_pci_save();
iounmap(mem_ctrl);
return 0;
}