forked from Minki/linux
[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:
parent
98f6740ea6
commit
887ef35ae4
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user