[PATCH] ppc32: PowerMac PM rework
Benjamin Herrenschmidt
benh at kernel.crashing.org
Sat May 28 12:15:19 EST 2005
Hi !
This new version of my PM rework fixes bits & pieces, most
significantly, improves suspend to disk by properly saving/restoring
MacIO ASIC state, and fixes issues with suspend to disk vs. AGP/DRI.
Also, the previous patch had the backward compatible ioctl backward, it
would suspend to disk instead of to RAM :)
Applies on top of current git plus:
http://gate.crashing.org/~benh/ppc32-pmac-cpufreq-upd.diff
http://gate.crashing.org/~benh/ppc32-pmac-pmu-irq-pri.diff
http://gate.crashing.org/~benh/ppc32-pmu-wakeup.diff
Please test and report any issue.
Index: linux-work/arch/ppc/platforms/Makefile
===================================================================
--- linux-work.orig/arch/ppc/platforms/Makefile 2005-05-02 10:48:08.000000000 +1000
+++ linux-work/arch/ppc/platforms/Makefile 2005-05-28 10:30:21.000000000 +1000
@@ -10,8 +10,9 @@
obj-$(CONFIG_PCI) += apus_pci.o
endif
obj-$(CONFIG_PPC_PMAC) += pmac_pic.o pmac_setup.o pmac_time.o \
- pmac_feature.o pmac_pci.o pmac_sleep.o \
- pmac_low_i2c.o pmac_cache.o
+ pmac_feature.o pmac_pci.o pmac_sleep.o \
+ pmac_low_i2c.o pmac_cache.o pmac_pm.o
+
obj-$(CONFIG_PPC_CHRP) += chrp_setup.o chrp_time.o chrp_pci.o \
chrp_pegasos_eth.o
obj-$(CONFIG_PPC_PREP) += prep_pci.o prep_setup.o
Index: linux-work/arch/ppc/platforms/pmac_pm.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-work/arch/ppc/platforms/pmac_pm.c 2005-05-28 11:58:16.000000000 +1000
@@ -0,0 +1,802 @@
+/*
+ * PowerMac Power Management core
+ *
+ * Copyright (C) 2005 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * Bits from drivers/macintosh/via-pmu.c
+ *
+ * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi.
+ * Copyright (C) 2001-2002 Benjamin Herrenschmidt
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+#include <linux/suspend.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/cpu.h>
+#include <linux/syscalls.h>
+#include <linux/reboot.h>
+#include <linux/timer.h>
+
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/irq.h>
+#include <asm/pmac_feature.h>
+#include <asm/mmu_context.h>
+#include <asm/cputable.h>
+#include <asm/time.h>
+#include <asm/sections.h>
+#include <asm/system.h>
+#include <asm/open_pic.h>
+
+#ifdef CONFIG_PM_DEBUG
+#define DBG(fmt...) printk(fmt)
+#else
+#define DBG(fmt...)
+#endif
+
+/* debugging */
+int __fake_sleep;
+
+/* lid wakeup control (fixme) */
+int pmac_option_lid_wakeup __pmacdata;
+EXPORT_SYMBOL_GPL(pmac_option_lid_wakeup);
+
+/* main callback for suspend to RAM */
+static int (*pmac_pm_low_suspend)(void) __pmacdata;
+
+/* assembly stuff */
+extern void low_sleep_handler(void);
+/* pmac_pic stuff */
+extern void pmacpic_suspend(void);
+extern void pmacpic_resume(void);
+
+
+/*************************************************************************
+ *
+ * Here is the "old style" PowerMac PM notifiers. They are still used by a
+ * couple of drivers that haven't yet been fitted in the device model.
+ * They will ultimately be deprecated.
+ *
+ *************************************************************************/
+
+static LIST_HEAD(sleep_notifiers);
+
+int __pmac pmu_register_sleep_notifier(struct pmu_sleep_notifier *n)
+{
+ struct list_head *list;
+ struct pmu_sleep_notifier *notifier;
+
+ for (list = sleep_notifiers.next; list != &sleep_notifiers;
+ list = list->next) {
+ notifier = list_entry(list, struct pmu_sleep_notifier, list);
+ if (n->priority > notifier->priority)
+ break;
+ }
+ __list_add(&n->list, list->prev, list);
+ return 0;
+}
+EXPORT_SYMBOL(pmu_register_sleep_notifier);
+
+int __pmac pmu_unregister_sleep_notifier(struct pmu_sleep_notifier* n)
+{
+ if (n->list.next == 0)
+ return -ENOENT;
+ list_del(&n->list);
+ n->list.next = NULL;
+ return 0;
+}
+EXPORT_SYMBOL(pmu_unregister_sleep_notifier);
+
+/* Sleep is broadcast last-to-first */
+static int __pmac broadcast_sleep(int when, int fallback)
+{
+ int ret = PBOOK_SLEEP_OK;
+ struct list_head *list;
+ struct pmu_sleep_notifier *notifier;
+
+ for (list = sleep_notifiers.prev; list != &sleep_notifiers;
+ list = list->prev) {
+ notifier = list_entry(list, struct pmu_sleep_notifier, list);
+ ret = notifier->notifier_call(notifier, when);
+ if (ret != PBOOK_SLEEP_OK) {
+ DBG("sleep %d rejected by %p (%p)\n",
+ when, notifier, notifier->notifier_call);
+ for (; list != &sleep_notifiers; list = list->next) {
+ notifier = list_entry(list,
+ struct pmu_sleep_notifier, list);
+ notifier->notifier_call(notifier, fallback);
+ }
+ return ret;
+ }
+ }
+ return ret;
+}
+
+/* Wake is broadcast first-to-last */
+static int __pmac broadcast_wake(void)
+{
+ int ret = PBOOK_SLEEP_OK;
+ struct list_head *list;
+ struct pmu_sleep_notifier *notifier;
+
+ for (list = sleep_notifiers.next; list != &sleep_notifiers;
+ list = list->next) {
+ notifier = list_entry(list, struct pmu_sleep_notifier, list);
+ notifier->notifier_call(notifier, PBOOK_WAKE);
+ }
+ return ret;
+}
+
+/**************************************************************************
+ *
+ * Here, we have code to save/restore PCI config space. Most of that code
+ * should be removed once we are confident with the generic code, including
+ * with P2P bridges
+ *
+ **************************************************************************/
+
+static struct pci_save {
+ u16 command;
+ u16 cache_lat;
+ u16 intr;
+ u32 bars[6];
+ u32 rom_address;
+} *pbook_pci_saves __pmacdata;
+static int pbook_npci_saves __pmacdata;
+
+static void __pmac pbook_alloc_pci_save(void)
+{
+ int npci;
+ struct pci_dev *pd = NULL;
+
+ npci = 0;
+ while ((pd = pci_find_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 __pmac 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 __pmac pbook_pci_save(void)
+{
+ struct pci_save *ps = pbook_pci_saves;
+ struct pci_dev *pd = NULL;
+ int npci = pbook_npci_saves;
+ int i;
+
+ if (ps == NULL)
+ return;
+
+ while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
+ if (npci-- == 0)
+ return;
+ switch (pd->hdr_type) {
+ case PCI_HEADER_TYPE_NORMAL:
+ 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);
+ for (i=0; i<6; i++)
+ pci_read_config_dword(pd,
+ PCI_BASE_ADDRESS_0+i*4,
+ &ps->bars[i]);
+ break;
+ case PCI_HEADER_TYPE_BRIDGE:
+ pci_read_config_word(pd, PCI_COMMAND, &ps->command);
+ pci_read_config_word(pd, PCI_CACHE_LINE_SIZE,
+ &ps->cache_lat);
+ pci_read_config_dword(pd, PCI_ROM_ADDRESS1,
+ &ps->rom_address);
+ pci_read_config_dword(pd, PCI_BASE_ADDRESS_0,
+ &ps->bars[0]);
+ pci_read_config_dword(pd, PCI_BASE_ADDRESS_1,
+ &ps->bars[1]);
+ break;
+ case PCI_HEADER_TYPE_CARDBUS:
+ 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_BASE_ADDRESS_0,
+ &ps->bars[0]);
+ break;
+ }
+ ++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 __pmac 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 i;
+
+ while ((pd = pci_find_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 (i = 0; i < 6; ++i)
+ pci_write_config_dword(pd,
+ PCI_BASE_ADDRESS_0+i*4,
+ ps->bars[i]);
+ 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;
+ case PCI_HEADER_TYPE_BRIDGE:
+ pci_write_config_dword(pd, PCI_BASE_ADDRESS_0,
+ ps->bars[0]);
+ pci_write_config_dword(pd, PCI_BASE_ADDRESS_1,
+ ps->bars[1]);
+ 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_COMMAND, ps->command);
+ break;
+ case PCI_HEADER_TYPE_CARDBUS:
+ pci_write_config_dword(pd, PCI_BASE_ADDRESS_0,
+ ps->bars[0]);
+ 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;
+ }
+ }
+}
+
+/*************************************************************************
+ *
+ * Here are the per-machine family low level sleep routines and the
+ * par-machine family initialization routines.
+ *
+ *************************************************************************/
+
+
+#define PB3400_MEM_CTRL 0xf8000000
+#define PB3400_MEM_CTRL_SLEEP 0x70
+
+static void __iomem *mem_ctrl_3400 __pmacdata;
+
+static int __pmac pmac_pm_sleep_3400(void)
+{
+ int i, x;
+ unsigned int hid0;
+ unsigned long p;
+ struct adb_request sleep_req;
+ unsigned int __iomem *mem_ctrl_sleep;
+
+ mem_ctrl_sleep = mem_ctrl_3400 + PB3400_MEM_CTRL_SLEEP;
+
+ /* Set the memory controller to keep the memory refreshed
+ while we're asleep */
+ for (i = 0x403f; i >= 0x4000; --i) {
+ out_be32(mem_ctrl_sleep, i);
+ do {
+ x = (in_be32(mem_ctrl_sleep) >> 16) & 0x3ff;
+ } while (x == 0);
+ if (x >= 0x100)
+ break;
+ }
+
+ /* 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();
+
+ 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;
+
+ /* Put the CPU into sleep mode */
+ asm volatile("mfspr %0,1008" : "=r" (hid0) :);
+ hid0 = (hid0 & ~(HID0_NAP | HID0_DOZE)) | HID0_SLEEP;
+ asm volatile("mtspr 1008,%0" : : "r" (hid0));
+ _nmask_and_or_msr(0, MSR_POW | MSR_EE);
+ udelay(10);
+
+ /* 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();
+
+ mdelay(10);
+
+ return 0;
+}
+
+static void __pmac pmac_pm_init_3400(void)
+{
+ /* first map in the memory controller registers */
+ mem_ctrl_3400 = ioremap(PB3400_MEM_CTRL, 0x100);
+ if (mem_ctrl_3400 == NULL) {
+ printk(KERN_ERR "pmac_pm_init_3400: ioremap failed\n");
+ return;
+ }
+
+ pmac_pm_low_suspend = pmac_pm_sleep_3400;
+}
+
+#define GRACKLE_PM (1<<7)
+#define GRACKLE_DOZE (1<<5)
+#define GRACKLE_NAP (1<<4)
+#define GRACKLE_SLEEP (1<<3)
+
+static struct pci_dev *grackle __pmacdata;
+
+static int __pmac pmac_pm_sleep_grackle(void)
+{
+ unsigned long save_l2cr;
+ unsigned short pmcr1;
+ struct adb_request req;
+
+ /* Turn off various things. Darwin does some retry tests here... */
+ pmu_request(&req, NULL, 2, PMU_POWER_CTRL0,
+ PMU_POW0_OFF | PMU_POW0_HARD_DRIVE);
+ pmu_wait_complete(&req);
+ pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
+ PMU_POW_OFF | PMU_POW_BACKLIGHT |
+ PMU_POW_IRLED | PMU_POW_MEDIABAY);
+ pmu_wait_complete(&req);
+
+ /* For 750, save backside cache setting and disable it */
+ save_l2cr = _get_L2CR(); /* (returns -1 if not available) */
+
+ if (!__fake_sleep) {
+ /* Ask the PMU to put us to sleep */
+ pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
+ pmu_wait_complete(&req);
+ }
+
+ /* The VIA is supposed not to be restored correctly*/
+ pmu_save_via_state();
+
+ /* We shut down some HW */
+ pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1);
+
+ pci_read_config_word(grackle, 0x70, &pmcr1);
+ /* Apparently, MacOS uses NAP mode for Grackle ??? */
+ pmcr1 &= ~(GRACKLE_DOZE|GRACKLE_SLEEP);
+ pmcr1 |= GRACKLE_PM|GRACKLE_NAP;
+ pci_write_config_word(grackle, 0x70, pmcr1);
+
+ /* Call low-level ASM sleep handler */
+ if (__fake_sleep)
+ mdelay(5000);
+ else
+ low_sleep_handler();
+
+ /* We're awake again, stop grackle PM */
+ pci_read_config_word(grackle, 0x70, &pmcr1);
+ pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP);
+ pci_write_config_word(grackle, 0x70, pmcr1);
+
+ /* Make sure the PMU is idle */
+ pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
+ pmu_restore_via_state();
+
+ /* Restore L2 cache */
+ if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0)
+ _set_L2CR(save_l2cr);
+
+ /* Power things up */
+ pmu_unlock();
+ pmu_request(&req, NULL, 2, PMU_POWER_CTRL0,
+ PMU_POW0_ON | PMU_POW0_HARD_DRIVE);
+ pmu_wait_complete(&req);
+ pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
+ PMU_POW_ON | PMU_POW_BACKLIGHT | PMU_POW_CHARGER |
+ PMU_POW_IRLED | PMU_POW_MEDIABAY);
+ pmu_wait_complete(&req);
+
+ return 0;
+}
+
+static void __pmac pmac_pm_init_grackle(void)
+{
+ grackle = pci_find_slot(0, 0);
+ if (!grackle)
+ return;
+
+ pmac_pm_low_suspend = pmac_pm_sleep_grackle;
+}
+
+
+static int __pmac pmac_pm_sleep_core99(void)
+{
+ unsigned long save_l2cr;
+ unsigned long save_l3cr;
+ struct adb_request req;
+
+ /* Tell PMU what events will wake us up */
+ pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_CLR_WAKEUP_EVENTS,
+ 0xff, 0xff);
+ pmu_wait_complete(&req);
+ pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_SET_WAKEUP_EVENTS,
+ 0, PMU_PWR_WAKEUP_KEY |
+ (pmac_option_lid_wakeup ? PMU_PWR_WAKEUP_LID_OPEN : 0));
+ pmu_wait_complete(&req);
+
+ /* Save the state of the L2 and L3 caches */
+ save_l3cr = _get_L3CR(); /* (returns -1 if not available) */
+ save_l2cr = _get_L2CR(); /* (returns -1 if not available) */
+
+ if (!__fake_sleep) {
+ /* Ask the PMU to put us to sleep */
+ pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
+ pmu_wait_complete(&req);
+ }
+
+ /* The VIA is supposed not to be restored correctly*/
+ pmu_save_via_state();
+
+ /* Shut down various ASICs. There's a chance that we can no longer
+ * talk to the PMU after this, so I moved it to _after_ sending the
+ * sleep command to it. Still need to be checked.
+ */
+ pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 1);
+
+ /* Call low-level ASM sleep handler */
+ if (__fake_sleep)
+ mdelay(5000);
+ else
+ low_sleep_handler();
+
+ /* Restore Apple core ASICs state */
+ pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 0);
+
+ /* Restore VIA */
+ pmu_restore_via_state();
+
+ /* Restore video */
+ pmac_call_early_video_resume();
+
+ /* Restore L2 cache */
+ if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0)
+ _set_L2CR(save_l2cr);
+ /* Restore L3 cache */
+ if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0)
+ _set_L3CR(save_l3cr);
+
+ /* Tell PMU we are ready */
+ pmu_unlock();
+ pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
+ pmu_wait_complete(&req);
+
+ return 0;
+}
+
+static void __pmac pmac_pm_init_core99(void)
+{
+ pmac_pm_low_suspend = pmac_pm_sleep_core99;
+}
+
+
+/*************************************************************************
+ *
+ * Here are the callbacks interfacing with the common code.
+ *
+ *************************************************************************/
+
+static inline void wakeup_decrementer(void)
+{
+ /* No currently-supported powerbook has a 601,
+ * so use get_tbl, not native
+ */
+ last_jiffy_stamp(0) = tb_last_stamp = get_native_tbl();
+ set_dec(1);
+}
+
+static int __pmac pmac_pm_pre_freeze(suspend_state_t state)
+{
+ int ret;
+
+ DBG("%s(%d)\n", __FUNCTION__, state);
+
+ /* Check if suspend to RAM is possible */
+ if (state == PM_SUSPEND_MEM) {
+ if (pmac_pm_low_suspend == NULL)
+ return -EINVAL;
+ if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) < 0)
+ return -EINVAL;
+ if (num_online_cpus() > 1 || cpu_is_offline(0))
+ return -EAGAIN;
+ }
+
+ DBG("Notify (1) old style drivers...\n");
+
+ /* Notify old-style device drivers & userland */
+ ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, PBOOK_SLEEP_REJECT);
+ if (ret != PBOOK_SLEEP_OK) {
+ printk(KERN_ERR "Sleep rejected by drivers\n");
+ return -EBUSY;
+ }
+
+ /* Sync the disks. */
+
+ /* XXX It would be nice to have some way to ensure that
+ * nobody is dirtying any new buffers while we wait. That
+ * could be achieved using the refrigerator for processes
+ * that swsusp uses
+ */
+ DBG("Sync disks...\n");
+ sys_sync();
+
+ // return (state == PM_SUSPEND_DISK);
+ return 1;
+}
+
+static int __pmac pmac_pm_prepare(suspend_state_t state)
+{
+ int ret;
+
+ DBG("%s(%d)\n", __FUNCTION__, state);
+
+ /* Sleep can fail now. May not be very robust but useful for
+ * debugging
+ */
+ DBG("Notify (2) old style drivers...\n");
+ ret = broadcast_sleep(PBOOK_SLEEP_NOW, PBOOK_WAKE);
+ if (ret != PBOOK_SLEEP_OK) {
+ printk(KERN_ERR "Driver sleep failed\n");
+ return -EBUSY;
+ }
+
+ /* Allocate room for PCI save */
+ DBG("Alloc PCI stuffs...\n");
+ pbook_alloc_pci_save();
+
+ return 0;
+}
+
+static int __pmac pmac_pm_prepare_irqs(suspend_state_t state)
+{
+ DBG("%s(%d)\n", __FUNCTION__, state);
+
+ /* That state only applies to suspend to RAM */
+ if (state != PM_SUSPEND_MEM)
+ return 0;
+
+ /* Stop interrupts on openpic */
+ if (pmu_get_model() == PMU_KEYLARGO_BASED)
+ openpic_set_priority(0xf);
+
+ /* Stop preemption */
+ preempt_disable();
+
+ /* Disable clock spreading on some machines */
+ pmac_tweak_clock_spreading(0);
+
+ /* Make sure the decrementer won't interrupt us */
+ asm volatile("mtdec %0" : : "r" (0x7fffffff));
+ /* Make sure any pending DEC interrupt occurring while we did
+ * the above didn't re-enable the DEC */
+ mb();
+ asm volatile("mtdec %0" : : "r" (0x7fffffff));
+
+ /* We disable interrupts now and not in the generic code so
+ * that we entre pmac_pm_finish_irqs() with interrupts still
+ * off and can properly wakeup decrementer before enabling them.
+ */
+ local_irq_disable();
+
+ return 0;
+}
+
+static void __pmac pmac_pm_pre_suspend(suspend_state_t state)
+{
+ DBG("%s\n", __FUNCTION__);
+
+ /* Save the state of PCI config space for some slots */
+ pbook_pci_save();
+
+ /* Suspend openpic */
+ if (pmu_get_model() == PMU_KEYLARGO_BASED)
+ openpic_suspend();
+ else
+ pmacpic_suspend();
+
+ /* Giveup the lazy FPU & vec so we don't have to back them
+ * up from the low level code
+ */
+ enable_kernel_fp();
+
+#ifdef CONFIG_ALTIVEC
+ if (cur_cpu_spec[0]->cpu_features & CPU_FTR_ALTIVEC)
+ enable_kernel_altivec();
+#endif /* CONFIG_ALTIVEC */
+
+ /* save ASIC state on suspend to disk */
+ if (state == PM_SUSPEND_DISK)
+ pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,2);
+}
+
+static void __pmac pmac_pmdisk_save_state(void)
+{
+ pmac_pm_pre_suspend(PM_SUSPEND_DISK);
+}
+
+static void __pmac pmac_pm_post_suspend(suspend_state_t state)
+{
+ DBG("%s\n", __FUNCTION__);
+
+ /* Restore ASIC state on suspend to disk */
+ if (state == PM_SUSPEND_DISK)
+ pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
+
+ /* Restore the state of PCI config space for some slots */
+ pbook_pci_restore();
+
+ /* Resume openpic */
+ if (pmu_get_model() == PMU_KEYLARGO_BASED)
+ openpic_resume();
+ else
+ pmacpic_resume();
+
+ /* Restore userland MMU context */
+ set_context(current->active_mm->context, current->active_mm->pgd);
+}
+
+static void __pmac pmac_pmdisk_restore_state(void)
+{
+ pmac_pm_post_suspend(PM_SUSPEND_DISK);
+}
+
+static int __pmac pmac_pm_enter(suspend_state_t state)
+{
+ int rc = 0;
+
+ DBG("%s(%d)\n", __FUNCTION__, state);
+
+ if (state == PM_SUSPEND_MEM) {
+ pmac_pm_pre_suspend(state);
+ rc = pmac_pm_low_suspend();
+ pmac_pm_post_suspend(state);
+
+ } else if (state == PM_SUSPEND_DISK) {
+ device_shutdown();
+ machine_restart(NULL);
+ } else
+ rc = -EINVAL;
+ return rc;
+}
+
+static void __pmac pmac_pm_finish_irqs(suspend_state_t state)
+{
+ DBG("%s(%d)\n", __FUNCTION__, state);
+
+ /* That state only applies to suspend to RAM */
+ if (state != PM_SUSPEND_MEM)
+ return;
+
+ /* Force a fetch from the PIC on the first DEC interrupt. */
+ ppc_force_interrupt = 1;
+
+ /* Restart jiffies & scheduling */
+ wakeup_decrementer();
+
+ /* Start interrupts on openpic */
+ if (pmu_get_model() == PMU_KEYLARGO_BASED)
+ openpic_set_priority(0);
+
+ /* Re-enable local CPU interrupts */
+ local_irq_enable();
+ mdelay(10);
+ preempt_enable();
+
+ /* Re-enable clock spreading on some machines */
+ pmac_tweak_clock_spreading(1);
+}
+
+static void __pmac pmac_pm_finish(suspend_state_t state)
+{
+ DBG("%s(%d)\n", __FUNCTION__, state);
+
+ /* Free PCI save block */
+ pbook_free_pci_save();
+
+}
+
+static void __pmac pmac_pm_post_freeze(suspend_state_t state)
+{
+ DBG("%s(%d)\n", __FUNCTION__, state);
+
+ /* Broadcase old style wakeup */
+ broadcast_wake();
+}
+
+static struct pm_ops pmac_pm_ops __pmacdata = {
+ .pm_disk_mode = PM_DISK_PLATFORM,
+ .pre_freeze = pmac_pm_pre_freeze,
+ .prepare = pmac_pm_prepare,
+ .prepare_irqs = pmac_pm_prepare_irqs,
+ .enter = pmac_pm_enter,
+ .finish_irqs = pmac_pm_finish_irqs,
+ .finish = pmac_pm_finish,
+ .post_freeze = pmac_pm_post_freeze,
+};
+
+static int pmac_pm_init(void)
+{
+ if (_machine != _MACH_Pmac)
+ return 0;
+
+ switch (pmu_get_model()) {
+ case PMU_OHARE_BASED:
+ pmac_pm_init_3400();
+ break;
+ case PMU_HEATHROW_BASED:
+ case PMU_PADDINGTON_BASED:
+ pmac_pm_init_grackle();
+ break;
+ case PMU_KEYLARGO_BASED:
+ pmac_pm_init_core99();
+ break;
+ }
+
+#ifdef CONFIG_SOFTWARE_SUSPEND
+ ppc_md.save_processor_state = pmac_pmdisk_save_state;
+ ppc_md.restore_processor_state = pmac_pmdisk_restore_state;
+#endif
+
+ pm_set_ops(&pmac_pm_ops);
+
+ return 0;
+}
+
+late_initcall(pmac_pm_init);
Index: linux-work/arch/ppc/platforms/pmac_setup.c
===================================================================
--- linux-work.orig/arch/ppc/platforms/pmac_setup.c 2005-05-02 10:48:08.000000000 +1000
+++ linux-work/arch/ppc/platforms/pmac_setup.c 2005-05-28 10:30:21.000000000 +1000
@@ -423,68 +423,6 @@
#endif
}
-static int initializing = 1;
-/* TODO: Merge the suspend-to-ram with the common code !!!
- * currently, this is a stub implementation for suspend-to-disk
- * only
- */
-
-#ifdef CONFIG_SOFTWARE_SUSPEND
-
-static int pmac_pm_prepare(suspend_state_t state)
-{
- printk(KERN_DEBUG "%s(%d)\n", __FUNCTION__, state);
-
- return 0;
-}
-
-static int pmac_pm_enter(suspend_state_t state)
-{
- printk(KERN_DEBUG "%s(%d)\n", __FUNCTION__, state);
-
- /* Giveup the lazy FPU & vec so we don't have to back them
- * up from the low level code
- */
- enable_kernel_fp();
-
-#ifdef CONFIG_ALTIVEC
- if (cur_cpu_spec[0]->cpu_features & CPU_FTR_ALTIVEC)
- enable_kernel_altivec();
-#endif /* CONFIG_ALTIVEC */
-
- return 0;
-}
-
-static int pmac_pm_finish(suspend_state_t state)
-{
- printk(KERN_DEBUG "%s(%d)\n", __FUNCTION__, state);
-
- /* Restore userland MMU context */
- set_context(current->active_mm->context, current->active_mm->pgd);
-
- return 0;
-}
-
-static struct pm_ops pmac_pm_ops = {
- .pm_disk_mode = PM_DISK_SHUTDOWN,
- .prepare = pmac_pm_prepare,
- .enter = pmac_pm_enter,
- .finish = pmac_pm_finish,
-};
-
-#endif /* CONFIG_SOFTWARE_SUSPEND */
-
-static int pmac_late_init(void)
-{
- initializing = 0;
-#ifdef CONFIG_SOFTWARE_SUSPEND
- pm_set_ops(&pmac_pm_ops);
-#endif /* CONFIG_SOFTWARE_SUSPEND */
- return 0;
-}
-
-late_initcall(pmac_late_init);
-
/* can't be __init - can be called whenever a disk is first accessed */
void __pmac
note_bootable_part(dev_t dev, int part, int goodness)
@@ -492,7 +430,7 @@
static int found_boot = 0;
char *p;
- if (!initializing)
+ if (system_state != SYSTEM_BOOTING)
return;
if ((goodness <= current_root_goodness) &&
ROOT_DEV != DEFAULT_ROOT_DEVICE)
Index: linux-work/drivers/base/power/suspend.c
===================================================================
--- linux-work.orig/drivers/base/power/suspend.c 2005-05-26 13:27:08.000000000 +1000
+++ linux-work/drivers/base/power/suspend.c 2005-05-28 10:30:21.000000000 +1000
@@ -113,8 +113,19 @@
put_device(dev);
}
up(&dpm_list_sem);
- if (error)
+ if (error) {
+ /* we failed... before resuming, bring back devices from
+ * dpm_off_irq list back to main dpm_off list, we do want
+ * to call resume() on them, in case they partially suspended
+ * despite returning -EAGAIN
+ */
+ while (!list_empty(&dpm_off_irq)) {
+ struct list_head * entry = dpm_off_irq.next;
+ list_del(entry);
+ list_add(entry, &dpm_off);
+ }
dpm_resume();
+ }
up(&dpm_sem);
return error;
}
Index: linux-work/drivers/macintosh/via-pmu.c
===================================================================
--- linux-work.orig/drivers/macintosh/via-pmu.c 2005-05-28 10:30:17.000000000 +1000
+++ linux-work/drivers/macintosh/via-pmu.c 2005-05-28 10:30:21.000000000 +1000
@@ -14,10 +14,7 @@
* THIS DRIVER IS BECOMING A TOTAL MESS !
* - Cleanup atomically disabling reply to PMU events after
* a sleep or a freq. switch
- * - Move sleep code out of here to pmac_pm, merge into new
- * common PM infrastructure
* - Move backlight code out as well
- * - Save/Restore PCI space properly
*
*/
#include <stdarg.h>
@@ -45,8 +42,6 @@
#include <linux/device.h>
#include <linux/sysdev.h>
#include <linux/suspend.h>
-#include <linux/syscalls.h>
-#include <linux/cpu.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
@@ -54,11 +49,11 @@
#include <asm/system.h>
#include <asm/sections.h>
#include <asm/irq.h>
-#include <asm/pmac_feature.h>
#include <asm/uaccess.h>
-#include <asm/mmu_context.h>
#include <asm/cputable.h>
#include <asm/time.h>
+#include <asm/pmac_feature.h>
+
#ifdef CONFIG_PMAC_BACKLIGHT
#include <asm/backlight.h>
#endif
@@ -70,7 +65,6 @@
/* Some compile options */
#undef SUSPEND_USES_PMU
#define DEBUG_SLEEP
-#undef HACKED_PCI_SAVE
/* Misc minor number allocated for /dev/pmu */
#define PMU_MINOR 154
@@ -155,12 +149,12 @@
static u8 pmu_intr_mask;
static int pmu_version;
static int drop_interrupts;
-#ifdef CONFIG_PMAC_PBOOK
-static int option_lid_wakeup = 1;
-static int sleep_in_progress;
-#endif /* CONFIG_PMAC_PBOOK */
static unsigned long async_req_locks;
static unsigned int pmu_irq_stats[11];
+static int pmu_sys_suspended = 0;
+
+/* from pmac_pm ... */
+extern int pmac_option_lid_wakeup;
static struct proc_dir_entry *proc_pmu_root;
static struct proc_dir_entry *proc_pmu_info;
@@ -168,7 +162,6 @@
static struct proc_dir_entry *proc_pmu_options;
static int option_server_mode;
-#ifdef CONFIG_PMAC_PBOOK
int pmu_battery_count;
int pmu_cur_battery;
unsigned int pmu_power_flags;
@@ -176,14 +169,11 @@
static int query_batt_timer = BATTERY_POLLING_COUNT;
static struct adb_request batt_req;
static struct proc_dir_entry *proc_pmu_batt[PMU_MAX_BATTERIES];
-#endif /* CONFIG_PMAC_PBOOK */
#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT)
extern int disable_kernel_backlight;
#endif /* defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) */
-int __fake_sleep;
-int asleep;
struct notifier_block *sleep_notifier_list;
#ifdef CONFIG_ADB
@@ -210,11 +200,9 @@
static int pmu_set_backlight_level(int level, void* data);
static int pmu_set_backlight_enable(int on, int level, void* data);
#endif /* CONFIG_PMAC_BACKLIGHT */
-#ifdef CONFIG_PMAC_PBOOK
static void pmu_pass_intr(unsigned char *data, int len);
static int proc_get_batt(char *page, char **start, off_t off,
int count, int *eof, void *data);
-#endif /* CONFIG_PMAC_PBOOK */
static int proc_read_options(char *page, char **start, off_t off,
int count, int *eof, void *data);
static int proc_write_options(struct file *file, const char __user *buffer,
@@ -232,10 +220,6 @@
};
#endif /* CONFIG_ADB */
-extern void low_sleep_handler(void);
-extern void enable_kernel_altivec(void);
-extern void enable_kernel_fp(void);
-
#ifdef DEBUG_SLEEP
int pmu_polled_request(struct adb_request *req);
int pmu_wink(struct adb_request *req);
@@ -407,9 +391,7 @@
bright_req_1.complete = 1;
bright_req_2.complete = 1;
-#ifdef CONFIG_PMAC_PBOOK
batt_req.complete = 1;
-#endif
#ifdef CONFIG_PPC32
if (pmu_kind == PMU_KEYLARGO_BASED)
@@ -468,7 +450,6 @@
register_backlight_controller(&pmu_backlight_controller, NULL, "pmu");
#endif /* CONFIG_PMAC_BACKLIGHT */
-#ifdef CONFIG_PMAC_PBOOK
if (machine_is_compatible("AAPL,3400/2400") ||
machine_is_compatible("AAPL,3500")) {
int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO,
@@ -496,11 +477,9 @@
pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART;
}
}
-#endif /* CONFIG_PMAC_PBOOK */
/* Create /proc/pmu */
proc_pmu_root = proc_mkdir("pmu", NULL);
if (proc_pmu_root) {
-#ifdef CONFIG_PMAC_PBOOK
int i;
for (i=0; i<pmu_battery_count; i++) {
@@ -509,7 +488,6 @@
proc_pmu_batt[i] = create_proc_read_entry(title, 0, proc_pmu_root,
proc_get_batt, (void *)i);
}
-#endif /* CONFIG_PMAC_PBOOK */
proc_pmu_info = create_proc_read_entry("info", 0, proc_pmu_root,
proc_get_info, NULL);
@@ -595,17 +573,6 @@
return pmu_kind;
}
-#ifndef CONFIG_PPC64
-static inline void wakeup_decrementer(void)
-{
- set_dec(tb_ticks_per_jiffy);
- /* No currently-supported powerbook has a 601,
- * so use get_tbl, not native
- */
- last_jiffy_stamp(0) = tb_last_stamp = get_tbl();
-}
-#endif
-
static void pmu_set_server_mode(int server_mode)
{
struct adb_request req;
@@ -629,7 +596,6 @@
pmu_wait_complete(&req);
}
-#ifdef CONFIG_PMAC_PBOOK
/* This new version of the code for 2400/3400/3500 powerbooks
* is inspired from the implementation in gkrellm-pmu
@@ -803,6 +769,8 @@
static void __pmac
query_battery_state(void)
{
+ if (pmu_sys_suspended)
+ return;
if (test_and_set_bit(0, &async_req_locks))
return;
if (pmu_kind == PMU_OHARE_BASED)
@@ -813,8 +781,6 @@
2, PMU_SMART_BATTERY_STATE, pmu_cur_battery+1);
}
-#endif /* CONFIG_PMAC_PBOOK */
-
static int __pmac
proc_get_info(char *page, char **start, off_t off,
int count, int *eof, void *data)
@@ -823,11 +789,9 @@
p += sprintf(p, "PMU driver version : %d\n", PMU_DRIVER_VERSION);
p += sprintf(p, "PMU firmware version : %02x\n", pmu_version);
-#ifdef CONFIG_PMAC_PBOOK
p += sprintf(p, "AC Power : %d\n",
((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0));
p += sprintf(p, "Battery count : %d\n", pmu_battery_count);
-#endif /* CONFIG_PMAC_PBOOK */
return p - page;
}
@@ -859,7 +823,6 @@
return p - page;
}
-#ifdef CONFIG_PMAC_PBOOK
static int __pmac
proc_get_batt(char *page, char **start, off_t off,
int count, int *eof, void *data)
@@ -883,7 +846,6 @@
return p - page;
}
-#endif /* CONFIG_PMAC_PBOOK */
static int __pmac
proc_read_options(char *page, char **start, off_t off,
@@ -891,11 +853,9 @@
{
char *p = page;
-#ifdef CONFIG_PMAC_PBOOK
if (pmu_kind == PMU_KEYLARGO_BASED &&
pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0)
- p += sprintf(p, "lid_wakeup=%d\n", option_lid_wakeup);
-#endif /* CONFIG_PMAC_PBOOK */
+ p += sprintf(p, "lid_wakeup=%d\n", pmac_option_lid_wakeup);
if (pmu_kind == PMU_KEYLARGO_BASED)
p += sprintf(p, "server_mode=%d\n", option_server_mode);
@@ -932,12 +892,10 @@
*(val++) = 0;
while(*val == ' ')
val++;
-#ifdef CONFIG_PMAC_PBOOK
if (pmu_kind == PMU_KEYLARGO_BASED &&
pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0)
if (!strcmp(label, "lid_wakeup"))
- option_lid_wakeup = ((*val) == '1');
-#endif /* CONFIG_PMAC_PBOOK */
+ pmac_option_lid_wakeup = ((*val) == '1');
if (pmu_kind == PMU_KEYLARGO_BASED && !strcmp(label, "server_mode")) {
int new_value;
new_value = ((*val) == '1');
@@ -1344,7 +1302,6 @@
unsigned char ints, pirq;
int i = 0;
- asleep = 0;
if (drop_interrupts || len < 1) {
adb_int_pending = 0;
pmu_irq_stats[8]++;
@@ -1432,7 +1389,6 @@
}
/* Tick interrupt */
else if ((1 << pirq) & PMU_INT_TICK) {
-#ifdef CONFIG_PMAC_PBOOK
/* Environement or tick interrupt, query batteries */
if (pmu_battery_count) {
if ((--query_batt_timer) == 0) {
@@ -1447,7 +1403,6 @@
pmu_pass_intr(data, len);
} else {
pmu_pass_intr(data, len);
-#endif /* CONFIG_PMAC_PBOOK */
}
goto next;
}
@@ -1692,6 +1647,8 @@
if (vias == NULL)
return -ENODEV;
+ if (pmu_sys_suspended)
+ return -EIO;
if (on) {
pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT,
@@ -1719,6 +1676,8 @@
{
if (vias == NULL)
return -ENODEV;
+ if (pmu_sys_suspended)
+ return -EIO;
if (test_and_set_bit(1, &async_req_locks))
return -EAGAIN;
@@ -2062,201 +2021,9 @@
return -1;
}
-#ifdef CONFIG_PMAC_PBOOK
-
-static LIST_HEAD(sleep_notifiers);
-
-int
-pmu_register_sleep_notifier(struct pmu_sleep_notifier *n)
-{
- struct list_head *list;
- struct pmu_sleep_notifier *notifier;
-
- for (list = sleep_notifiers.next; list != &sleep_notifiers;
- list = list->next) {
- notifier = list_entry(list, struct pmu_sleep_notifier, list);
- if (n->priority > notifier->priority)
- break;
- }
- __list_add(&n->list, list->prev, list);
- return 0;
-}
-
-int
-pmu_unregister_sleep_notifier(struct pmu_sleep_notifier* n)
-{
- if (n->list.next == 0)
- return -ENOENT;
- list_del(&n->list);
- n->list.next = NULL;
- return 0;
-}
-
-/* Sleep is broadcast last-to-first */
-static int __pmac
-broadcast_sleep(int when, int fallback)
-{
- int ret = PBOOK_SLEEP_OK;
- struct list_head *list;
- struct pmu_sleep_notifier *notifier;
-
- for (list = sleep_notifiers.prev; list != &sleep_notifiers;
- list = list->prev) {
- notifier = list_entry(list, struct pmu_sleep_notifier, list);
- ret = notifier->notifier_call(notifier, when);
- if (ret != PBOOK_SLEEP_OK) {
- printk(KERN_DEBUG "sleep %d rejected by %p (%p)\n",
- when, notifier, notifier->notifier_call);
- for (; list != &sleep_notifiers; list = list->next) {
- notifier = list_entry(list, struct pmu_sleep_notifier, list);
- notifier->notifier_call(notifier, fallback);
- }
- return ret;
- }
- }
- return ret;
-}
-
-/* Wake is broadcast first-to-last */
-static int __pmac
-broadcast_wake(void)
-{
- int ret = PBOOK_SLEEP_OK;
- struct list_head *list;
- struct pmu_sleep_notifier *notifier;
-
- for (list = sleep_notifiers.next; list != &sleep_notifiers;
- list = list->next) {
- notifier = list_entry(list, struct pmu_sleep_notifier, list);
- notifier->notifier_call(notifier, PBOOK_WAKE);
- }
- return ret;
-}
-
-/*
- * This struct is used to store config register values for
- * PCI devices which may get powered off when we sleep.
- */
-static struct pci_save {
-#ifndef HACKED_PCI_SAVE
- u16 command;
- u16 cache_lat;
- u16 intr;
- u32 rom_address;
-#else
- u32 config[16];
-#endif
-} *pbook_pci_saves;
-static int pbook_npci_saves;
-
-static void __pmac
-pbook_alloc_pci_save(void)
-{
- int npci;
- struct pci_dev *pd = NULL;
-
- npci = 0;
- while ((pd = pci_find_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 __pmac
-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 __pmac
-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_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
- if (npci-- == 0)
- return;
-#ifndef HACKED_PCI_SAVE
- 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);
-#else
- int i;
- for (i=1;i<16;i++)
- pci_read_config_dword(pd, i<<4, &ps->config[i]);
-#endif
- ++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 __pmac
-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_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
-#ifdef HACKED_PCI_SAVE
- int i;
- if (npci-- == 0)
- return;
- ps++;
- for (i=2;i<16;i++)
- pci_write_config_dword(pd, i<<4, ps->config[i]);
- pci_write_config_dword(pd, 4, ps->config[1]);
-#else
- 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;
- }
-#endif
- }
-}
-
-#ifdef DEBUG_SLEEP
/* N.B. This doesn't work on the 3400 */
-void __pmac
-pmu_blink(int n)
+void __pmac pmu_blink(int n)
{
struct adb_request req;
@@ -2288,7 +2055,6 @@
}
mdelay(50);
}
-#endif
/*
* Put the powerbook to sleep.
@@ -2296,8 +2062,7 @@
static u32 save_via[8] __pmacdata;
-static void __pmac
-save_via_state(void)
+void __pmac pmu_save_via_state(void)
{
save_via[0] = in_8(&via[ANH]);
save_via[1] = in_8(&via[DIRA]);
@@ -2308,8 +2073,7 @@
save_via[6] = in_8(&via[T1CL]);
save_via[7] = in_8(&via[T1CH]);
}
-static void __pmac
-restore_via_state(void)
+void __pmac pmu_restore_via_state(void)
{
out_8(&via[ANH], save_via[0]);
out_8(&via[DIRA], save_via[1]);
@@ -2324,390 +2088,6 @@
out_8(&via[IER], IER_SET | SR_INT | CB1_INT);
}
-static int __pmac
-pmac_suspend_devices(void)
-{
- int ret;
-
- pm_prepare_console();
-
- /* Notify old-style device drivers & userland */
- ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, PBOOK_SLEEP_REJECT);
- if (ret != PBOOK_SLEEP_OK) {
- printk(KERN_ERR "Sleep rejected by drivers\n");
- return -EBUSY;
- }
-
- /* Sync the disks. */
- /* XXX It would be nice to have some way to ensure that
- * nobody is dirtying any new buffers while we wait. That
- * could be achieved using the refrigerator for processes
- * that swsusp uses
- */
- sys_sync();
-
- /* Sleep can fail now. May not be very robust but useful for debugging */
- ret = broadcast_sleep(PBOOK_SLEEP_NOW, PBOOK_WAKE);
- if (ret != PBOOK_SLEEP_OK) {
- printk(KERN_ERR "Driver sleep failed\n");
- return -EBUSY;
- }
-
- /* Send suspend call to devices, hold the device core's dpm_sem */
- ret = device_suspend(PMSG_SUSPEND);
- if (ret) {
- broadcast_wake();
- printk(KERN_ERR "Driver sleep failed\n");
- return -EBUSY;
- }
-
- /* Disable clock spreading on some machines */
- pmac_tweak_clock_spreading(0);
-
- /* Stop preemption */
- preempt_disable();
-
- /* Make sure the decrementer won't interrupt us */
- asm volatile("mtdec %0" : : "r" (0x7fffffff));
- /* Make sure any pending DEC interrupt occurring while we did
- * the above didn't re-enable the DEC */
- mb();
- asm volatile("mtdec %0" : : "r" (0x7fffffff));
-
- /* We can now disable MSR_EE. This code of course works properly only
- * on UP machines... For SMP, if we ever implement sleep, we'll have to
- * stop the "other" CPUs way before we do all that stuff.
- */
- local_irq_disable();
-
- /* Broadcast power down irq
- * This isn't that useful in most cases (only directly wired devices can
- * use this but still... This will take care of sysdev's as well, so
- * we exit from here with local irqs disabled and PIC off.
- */
- ret = device_power_down(PMSG_SUSPEND);
- if (ret) {
- wakeup_decrementer();
- local_irq_enable();
- preempt_enable();
- device_resume();
- broadcast_wake();
- printk(KERN_ERR "Driver powerdown failed\n");
- return -EBUSY;
- }
-
- /* Wait for completion of async backlight requests */
- while (!bright_req_1.complete || !bright_req_2.complete ||
- !batt_req.complete)
- pmu_poll();
-
- /* Giveup the lazy FPU & vec so we don't have to back them
- * up from the low level code
- */
- enable_kernel_fp();
-
-#ifdef CONFIG_ALTIVEC
- if (cpu_has_feature(CPU_FTR_ALTIVEC))
- enable_kernel_altivec();
-#endif /* CONFIG_ALTIVEC */
-
- return 0;
-}
-
-static int __pmac
-pmac_wakeup_devices(void)
-{
- mdelay(100);
-
- /* Power back up system devices (including the PIC) */
- device_power_up();
-
- /* Force a poll of ADB interrupts */
- adb_int_pending = 1;
- via_pmu_interrupt(0, NULL, NULL);
-
- /* Restart jiffies & scheduling */
- wakeup_decrementer();
-
- /* Re-enable local CPU interrupts */
- local_irq_enable();
- mdelay(10);
- preempt_enable();
-
- /* Re-enable clock spreading on some machines */
- pmac_tweak_clock_spreading(1);
-
- /* Resume devices */
- device_resume();
-
- /* Notify old style drivers */
- broadcast_wake();
-
- pm_restore_console();
-
- return 0;
-}
-
-#define GRACKLE_PM (1<<7)
-#define GRACKLE_DOZE (1<<5)
-#define GRACKLE_NAP (1<<4)
-#define GRACKLE_SLEEP (1<<3)
-
-int __pmac
-powerbook_sleep_grackle(void)
-{
- unsigned long save_l2cr;
- unsigned short pmcr1;
- struct adb_request req;
- int ret;
- struct pci_dev *grackle;
-
- grackle = pci_find_slot(0, 0);
- if (!grackle)
- return -ENODEV;
-
- ret = pmac_suspend_devices();
- if (ret) {
- printk(KERN_ERR "Sleep rejected by devices\n");
- return ret;
- }
-
- /* Turn off various things. Darwin does some retry tests here... */
- pmu_request(&req, NULL, 2, PMU_POWER_CTRL0, PMU_POW0_OFF|PMU_POW0_HARD_DRIVE);
- pmu_wait_complete(&req);
- pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
- PMU_POW_OFF|PMU_POW_BACKLIGHT|PMU_POW_IRLED|PMU_POW_MEDIABAY);
- pmu_wait_complete(&req);
-
- /* For 750, save backside cache setting and disable it */
- save_l2cr = _get_L2CR(); /* (returns -1 if not available) */
-
- if (!__fake_sleep) {
- /* Ask the PMU to put us to sleep */
- pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
- pmu_wait_complete(&req);
- }
-
- /* The VIA is supposed not to be restored correctly*/
- save_via_state();
- /* We shut down some HW */
- pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1);
-
- pci_read_config_word(grackle, 0x70, &pmcr1);
- /* Apparently, MacOS uses NAP mode for Grackle ??? */
- pmcr1 &= ~(GRACKLE_DOZE|GRACKLE_SLEEP);
- pmcr1 |= GRACKLE_PM|GRACKLE_NAP;
- pci_write_config_word(grackle, 0x70, pmcr1);
-
- /* Call low-level ASM sleep handler */
- if (__fake_sleep)
- mdelay(5000);
- else
- low_sleep_handler();
-
- /* We're awake again, stop grackle PM */
- pci_read_config_word(grackle, 0x70, &pmcr1);
- pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP);
- pci_write_config_word(grackle, 0x70, pmcr1);
-
- /* Make sure the PMU is idle */
- pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
- restore_via_state();
-
- /* Restore L2 cache */
- if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0)
- _set_L2CR(save_l2cr);
-
- /* Restore userland MMU context */
- set_context(current->active_mm->context, current->active_mm->pgd);
-
- /* Power things up */
- pmu_unlock();
- pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask);
- pmu_wait_complete(&req);
- pmu_request(&req, NULL, 2, PMU_POWER_CTRL0,
- PMU_POW0_ON|PMU_POW0_HARD_DRIVE);
- pmu_wait_complete(&req);
- pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
- PMU_POW_ON|PMU_POW_BACKLIGHT|PMU_POW_CHARGER|PMU_POW_IRLED|PMU_POW_MEDIABAY);
- pmu_wait_complete(&req);
-
- pmac_wakeup_devices();
-
- return 0;
-}
-
-static int __pmac
-powerbook_sleep_Core99(void)
-{
- unsigned long save_l2cr;
- unsigned long save_l3cr;
- struct adb_request req;
- int ret;
-
- if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) < 0) {
- printk(KERN_ERR "Sleep mode not supported on this machine\n");
- return -ENOSYS;
- }
-
- if (num_online_cpus() > 1 || cpu_is_offline(0))
- return -EAGAIN;
-
- ret = pmac_suspend_devices();
- if (ret) {
- printk(KERN_ERR "Sleep rejected by devices\n");
- return ret;
- }
-
- /* Stop environment and ADB interrupts */
- pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0);
- pmu_wait_complete(&req);
-
- /* Tell PMU what events will wake us up */
- pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_CLR_WAKEUP_EVENTS,
- 0xff, 0xff);
- pmu_wait_complete(&req);
- pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_SET_WAKEUP_EVENTS,
- 0, PMU_PWR_WAKEUP_KEY |
- (option_lid_wakeup ? PMU_PWR_WAKEUP_LID_OPEN : 0));
- pmu_wait_complete(&req);
-
- /* Save the state of the L2 and L3 caches */
- save_l3cr = _get_L3CR(); /* (returns -1 if not available) */
- save_l2cr = _get_L2CR(); /* (returns -1 if not available) */
-
- if (!__fake_sleep) {
- /* Ask the PMU to put us to sleep */
- pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
- pmu_wait_complete(&req);
- }
-
- /* The VIA is supposed not to be restored correctly*/
- save_via_state();
-
- /* Shut down various ASICs. There's a chance that we can no longer
- * talk to the PMU after this, so I moved it to _after_ sending the
- * sleep command to it. Still need to be checked.
- */
- pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 1);
-
- /* Call low-level ASM sleep handler */
- if (__fake_sleep)
- mdelay(5000);
- else
- low_sleep_handler();
-
- /* Restore Apple core ASICs state */
- pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 0);
-
- /* Restore VIA */
- restore_via_state();
-
- /* Restore video */
- pmac_call_early_video_resume();
-
- /* Restore L2 cache */
- if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0)
- _set_L2CR(save_l2cr);
- /* Restore L3 cache */
- if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0)
- _set_L3CR(save_l3cr);
-
- /* Restore userland MMU context */
- set_context(current->active_mm->context, current->active_mm->pgd);
-
- /* Tell PMU we are ready */
- pmu_unlock();
- pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
- pmu_wait_complete(&req);
- pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask);
- pmu_wait_complete(&req);
-
- pmac_wakeup_devices();
-
- return 0;
-}
-
-#define PB3400_MEM_CTRL 0xf8000000
-#define PB3400_MEM_CTRL_SLEEP 0x70
-
-static int __pmac
-powerbook_sleep_3400(void)
-{
- int ret, i, x;
- unsigned int hid0;
- unsigned long p;
- 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");
- return -ENOMEM;
- }
- mem_ctrl_sleep = mem_ctrl + PB3400_MEM_CTRL_SLEEP;
-
- /* Allocate room for PCI save */
- pbook_alloc_pci_save();
-
- 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) {
- out_be32(mem_ctrl_sleep, i);
- do {
- x = (in_be32(mem_ctrl_sleep) >> 16) & 0x3ff;
- } while (x == 0);
- if (x >= 0x100)
- break;
- }
-
- /* 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();
-
- 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 */
- asm volatile("mfspr %0,1008" : "=r" (hid0) :);
- hid0 = (hid0 & ~(HID0_NAP | HID0_DOZE)) | HID0_SLEEP;
- asm volatile("mtspr 1008,%0" : : "r" (hid0));
- _nmask_and_or_msr(0, MSR_POW | MSR_EE);
- udelay(10);
-
- /* 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_wakeup_devices();
- pbook_free_pci_save();
- iounmap(mem_ctrl);
-
- return 0;
-}
/*
* Support for /dev/pmu device
@@ -2896,24 +2276,10 @@
case PMU_IOC_SLEEP:
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- if (sleep_in_progress)
- return -EBUSY;
- sleep_in_progress = 1;
- switch (pmu_kind) {
- case PMU_OHARE_BASED:
- error = powerbook_sleep_3400();
- break;
- case PMU_HEATHROW_BASED:
- case PMU_PADDINGTON_BASED:
- error = powerbook_sleep_grackle();
- break;
- case PMU_KEYLARGO_BASED:
- error = powerbook_sleep_Core99();
- break;
- default:
- error = -ENOSYS;
- }
- sleep_in_progress = 0;
+ if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) < 0)
+ error = pm_suspend(PM_SUSPEND_DISK);
+ else
+ error = pm_suspend(PM_SUSPEND_MEM);
return error;
case PMU_IOC_CAN_SLEEP:
if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) < 0)
@@ -2926,8 +2292,6 @@
* the fbdev
*/
case PMU_IOC_GET_BACKLIGHT:
- if (sleep_in_progress)
- return -EBUSY;
error = get_backlight_level();
if (error < 0)
return error;
@@ -2935,8 +2299,6 @@
case PMU_IOC_SET_BACKLIGHT:
{
__u32 value;
- if (sleep_in_progress)
- return -EBUSY;
error = get_user(value, argp);
if (!error)
error = set_backlight_level(value);
@@ -2983,7 +2345,6 @@
if (misc_register(&pmu_device) < 0)
printk(KERN_ERR "via-pmu: cannot register misc device.\n");
}
-#endif /* CONFIG_PMAC_PBOOK */
#ifdef DEBUG_SLEEP
static inline void __pmac
@@ -3065,17 +2426,29 @@
#ifdef CONFIG_PM
-static int pmu_sys_suspended = 0;
-
static int pmu_sys_suspend(struct sys_device *sysdev, pm_message_t state)
{
- if (state != PM_SUSPEND_DISK || pmu_sys_suspended)
+ struct adb_request req;
+
+ if (pmu_sys_suspended)
return 0;
+ /* Stop environment interrupts */
+ pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0);
+ pmu_wait_complete(&req);
+
/* Suspend PMU event interrupts */
pmu_suspend();
+ /* Mark driver suspended */
pmu_sys_suspended = 1;
+
+ /* Wait for completion of async backlight requests */
+ while (!bright_req_1.complete || !bright_req_2.complete ||
+ !batt_req.complete)
+ pmu_poll();
+
+
return 0;
}
@@ -3086,6 +2459,10 @@
if (!pmu_sys_suspended)
return 0;
+ /* Restart environment interrupts */
+ pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask);
+ pmu_wait_complete(&req);
+
/* Tell PMU we are ready */
pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
pmu_wait_complete(&req);
@@ -3093,6 +2470,7 @@
/* Resume PMU event interrupts */
pmu_resume();
+ /* Mark driver ready for async requests */
pmu_sys_suspended = 0;
return 0;
@@ -3151,12 +2529,8 @@
EXPORT_SYMBOL(pmu_i2c_stdsub_write);
EXPORT_SYMBOL(pmu_i2c_simple_read);
EXPORT_SYMBOL(pmu_i2c_simple_write);
-#ifdef CONFIG_PMAC_PBOOK
-EXPORT_SYMBOL(pmu_register_sleep_notifier);
-EXPORT_SYMBOL(pmu_unregister_sleep_notifier);
EXPORT_SYMBOL(pmu_enable_irled);
EXPORT_SYMBOL(pmu_battery_count);
EXPORT_SYMBOL(pmu_batteries);
EXPORT_SYMBOL(pmu_power_flags);
-#endif /* CONFIG_PMAC_PBOOK */
Index: linux-work/include/asm-ppc/machdep.h
===================================================================
--- linux-work.orig/include/asm-ppc/machdep.h 2005-05-02 10:49:57.000000000 +1000
+++ linux-work/include/asm-ppc/machdep.h 2005-05-28 10:30:21.000000000 +1000
@@ -110,6 +110,12 @@
*/
long (*feature_call)(unsigned int feature, ...);
+#ifdef CONFIG_SOFTWARE_SUSPEND
+ int (*arch_prepare_suspend)(void);
+ void (*save_processor_state)(void);
+ void (*restore_processor_state)(void);
+#endif /* CONFIG_SOFTWARE_SUSPEND */
+
#ifdef CONFIG_SMP
/* functions for dealing with other cpus */
struct smp_ops_t *smp_ops;
Index: linux-work/include/asm-ppc/suspend.h
===================================================================
--- linux-work.orig/include/asm-ppc/suspend.h 2005-05-02 10:49:59.000000000 +1000
+++ linux-work/include/asm-ppc/suspend.h 2005-05-28 10:30:21.000000000 +1000
@@ -1,12 +1,44 @@
+#ifndef __PPC_SUSPEND_H
+#define __PPC_SUSPEND_H
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <asm/machdep.h>
+
+
+#ifdef CONFIG_SOFTWARE_SUSPEND
static inline int arch_prepare_suspend(void)
{
+ if (ppc_md.arch_prepare_suspend)
+ return ppc_md.arch_prepare_suspend();
return 0;
}
static inline void save_processor_state(void)
{
+ if (ppc_md.save_processor_state)
+ return ppc_md.save_processor_state();
}
static inline void restore_processor_state(void)
{
+ if (ppc_md.restore_processor_state)
+ return ppc_md.restore_processor_state();
}
+#else
+static inline int arch_prepare_suspend(void)
+{
+ return 0;
+}
+
+static inline void save_processor_state(void)
+{
+}
+
+static inline void restore_processor_state(void)
+{
+}
+#endif
+
+
+#endif /* __PPC_SUSPEND_H */
Index: linux-work/include/linux/pm.h
===================================================================
--- linux-work.orig/include/linux/pm.h 2005-05-02 10:50:24.000000000 +1000
+++ linux-work/include/linux/pm.h 2005-05-28 10:30:21.000000000 +1000
@@ -169,9 +169,38 @@
struct pm_ops {
suspend_disk_method_t pm_disk_mode;
+
+ /* Call before process freezing. If returns 0, then no freeze
+ * should be done, if 1, freeze, negative -> error
+ */
+ int (*pre_freeze)(suspend_state_t state);
+
+ /* called before devices are suspended */
int (*prepare)(suspend_state_t state);
+
+ /* called just before irqs are off and device second pass
+ * and sysdevs are suspended. This function can on some archs
+ * shut irqs off, in which case, they'll still be off when
+ * finish_irqs() is called.
+ */
+ int (*prepare_irqs)(suspend_state_t state);
+
+ /* called for entering the actual suspend state. Exits with
+ * machine worken up and interrupts off
+ */
int (*enter)(suspend_state_t state);
- int (*finish)(suspend_state_t state);
+
+ /* called after sysdevs and "irq off" devices have been
+ * worken up, irqs have just been restored to whatever state
+ * prepare_irqs() left them in.
+ */
+ void (*finish_irqs)(suspend_state_t state);
+
+ /* called after all devices are woken up, processes still frozen */
+ void (*finish)(suspend_state_t state);
+
+ /* called after unfreezing userland */
+ void (*post_freeze)(suspend_state_t state);
};
extern void pm_set_ops(struct pm_ops *);
Index: linux-work/include/linux/pmu.h
===================================================================
--- linux-work.orig/include/linux/pmu.h 2005-05-02 10:50:24.000000000 +1000
+++ linux-work/include/linux/pmu.h 2005-05-28 10:30:21.000000000 +1000
@@ -151,6 +151,9 @@
extern void pmu_suspend(void);
extern void pmu_resume(void);
+extern void pmu_save_via_state(void);
+extern void pmu_restore_via_state(void);
+
extern void pmu_enable_irled(int on);
extern void pmu_restart(void);
Index: linux-work/kernel/power/main.c
===================================================================
--- linux-work.orig/kernel/power/main.c 2005-05-26 13:27:08.000000000 +1000
+++ linux-work/kernel/power/main.c 2005-05-28 10:30:21.000000000 +1000
@@ -23,6 +23,7 @@
struct pm_ops * pm_ops = NULL;
suspend_disk_method_t pm_disk_mode = PM_DISK_SHUTDOWN;
+static int pm_process_frozen;
/**
* pm_set_ops - Set the global power method table.
@@ -49,32 +50,53 @@
static int suspend_prepare(suspend_state_t state)
{
int error = 0;
+ int freeze = 1;
if (!pm_ops || !pm_ops->enter)
return -EPERM;
pm_prepare_console();
- if (freeze_processes()) {
+ if (pm_ops->pre_freeze)
+ freeze = pm_ops->pre_freeze(state);
+ if (freeze < 0)
+ goto Console;
+
+ if (freeze && freeze_processes()) {
error = -EAGAIN;
goto Thaw;
}
+ pm_process_frozen = freeze;
if (pm_ops->prepare) {
+ pr_debug("preparing arch...\n");
if ((error = pm_ops->prepare(state)))
goto Thaw;
}
+ pr_debug("suspending devices...\n");
if ((error = device_suspend(PMSG_SUSPEND))) {
printk(KERN_ERR "Some devices failed to suspend\n");
goto Finish;
}
+
+ if (pm_ops->prepare_irqs) {
+ pr_debug("preparing arch irqs...\n");
+ if ((error = pm_ops->prepare_irqs(state)))
+ goto Finish;
+ }
+
return 0;
Finish:
if (pm_ops->finish)
pm_ops->finish(state);
Thaw:
- thaw_processes();
+ if (freeze)
+ thaw_processes();
+
+ if (pm_ops->post_freeze)
+ pm_ops->post_freeze(state);
+ Console:
pm_restore_console();
return error;
}
@@ -109,10 +131,18 @@
static void suspend_finish(suspend_state_t state)
{
+ if (!pm_ops)
+ return;
+
+ if (pm_ops->finish_irqs)
+ pm_ops->finish_irqs(state);
device_resume();
- if (pm_ops && pm_ops->finish)
+ if (pm_ops->finish)
pm_ops->finish(state);
- thaw_processes();
+ if (pm_process_frozen)
+ thaw_processes();
+ if (pm_ops->post_freeze)
+ pm_ops->post_freeze(state);
pm_restore_console();
}
Index: linux-work/arch/ppc/platforms/pmac_pic.c
===================================================================
--- linux-work.orig/arch/ppc/platforms/pmac_pic.c 2005-05-02 10:48:08.000000000 +1000
+++ linux-work/arch/ppc/platforms/pmac_pic.c 2005-05-28 10:30:21.000000000 +1000
@@ -619,7 +619,7 @@
return viaint;
}
-static int pmacpic_suspend(struct sys_device *sysdev, u32 state)
+void __pmac pmacpic_suspend(void)
{
int viaint = pmacpic_find_viaint();
@@ -636,11 +636,9 @@
/* make sure mask gets to controller before we return to caller */
mb();
(void)in_le32(&pmac_irq_hw[0]->enable);
-
- return 0;
}
-static int pmacpic_resume(struct sys_device *sysdev)
+void __pmac pmacpic_resume(void)
{
int i;
@@ -651,12 +649,11 @@
for (i = 0; i < max_real_irqs; ++i)
if (test_bit(i, sleep_save_mask))
pmac_unmask_irq(i);
-
- return 0;
}
#endif /* CONFIG_PM */
+#if 0
static struct sysdev_class pmacpic_sysclass = {
set_kset_name("pmac_pic"),
};
@@ -686,4 +683,4 @@
}
subsys_initcall(init_pmacpic_sysfs);
-
+#endif
Index: linux-work/arch/ppc/syslib/open_pic.c
===================================================================
--- linux-work.orig/arch/ppc/syslib/open_pic.c 2005-05-28 10:30:17.000000000 +1000
+++ linux-work/arch/ppc/syslib/open_pic.c 2005-05-28 10:30:21.000000000 +1000
@@ -946,11 +946,7 @@
save_irq_src_vp[irq - open_pic_irq_offset] |= OPENPIC_MASK;
}
-/* WARNING: Can be called directly by the cpufreq code with NULL parameter,
- * we need something better to deal with that... Maybe switch to S1 for
- * cpufreq changes
- */
-int openpic_suspend(struct sys_device *sysdev, u32 state)
+void openpic_suspend(void)
{
int i;
unsigned long flags;
@@ -959,11 +955,9 @@
if (openpic_suspend_count++ > 0) {
spin_unlock_irqrestore(&openpic_setup_lock, flags);
- return 0;
+ return;
}
- openpic_set_priority(0xf);
-
open_pic.enable = openpic_cached_enable_irq;
open_pic.disable = openpic_cached_disable_irq;
@@ -973,6 +967,8 @@
OPENPIC_CURRENT_TASK_PRIORITY_MASK, 0xf);
}
+ openpic_set_priority(0xf);
+
for (i=0; i<OPENPIC_NUM_IPI; i++)
save_ipi_vp[i] = openpic_read(&OpenPIC->Global.IPI_Vector_Priority(i));
for (i=0; i<NumSources; i++) {
@@ -983,15 +979,13 @@
}
spin_unlock_irqrestore(&openpic_setup_lock, flags);
-
- return 0;
}
/* WARNING: Can be called directly by the cpufreq code with NULL parameter,
* we need something better to deal with that... Maybe switch to S1 for
* cpufreq changes
*/
-int openpic_resume(struct sys_device *sysdev)
+void openpic_resume(void)
{
int i;
unsigned long flags;
@@ -1003,9 +997,11 @@
if ((--openpic_suspend_count) > 0) {
spin_unlock_irqrestore(&openpic_setup_lock, flags);
- return 0;
+ return;
}
+ openpic_set_priority(0xf);
+
/* OpenPIC sometimes seem to need some time to be fully back up... */
do {
openpic_set_spurious(OPENPIC_VEC_SPURIOUS);
@@ -1028,22 +1024,19 @@
} while (openpic_readfield(&ISR[i]->Vector_Priority, vppmask)
!= (save_irq_src_vp[i] & vppmask));
}
- for (i=0; i<NumProcessors; i++)
- openpic_write(&OpenPIC->Processor[i].Current_Task_Priority,
- save_cpu_task_pri[i]);
-
open_pic.enable = openpic_enable_irq;
open_pic.disable = openpic_disable_irq;
- openpic_set_priority(0);
+ for (i=0; i<NumProcessors; i++)
+ openpic_write(&OpenPIC->Processor[i].Current_Task_Priority,
+ save_cpu_task_pri[i]);
spin_unlock_irqrestore(&openpic_setup_lock, flags);
-
- return 0;
}
#endif /* CONFIG_PM */
+#if 0
static struct sysdev_class openpic_sysclass = {
set_kset_name("openpic"),
};
@@ -1087,3 +1080,4 @@
subsys_initcall(init_openpic_sysfs);
+#endif
Index: linux-work/include/asm-ppc/open_pic.h
===================================================================
--- linux-work.orig/include/asm-ppc/open_pic.h 2005-05-28 10:29:53.000000000 +1000
+++ linux-work/include/asm-ppc/open_pic.h 2005-05-28 10:30:21.000000000 +1000
@@ -64,6 +64,9 @@
extern void openpic_set_priority(u_int pri);
extern u_int openpic_get_priority(void);
+extern void openpic_suspend(void);
+extern void openpic_resume(void);
+
extern inline int openpic_to_irq(int irq)
{
/* IRQ 0 usually means 'disabled'.. don't mess with it
Index: linux-work/arch/ppc/kernel/time.c
===================================================================
--- linux-work.orig/arch/ppc/kernel/time.c 2005-05-02 10:48:08.000000000 +1000
+++ linux-work/arch/ppc/kernel/time.c 2005-05-28 10:30:21.000000000 +1000
@@ -86,6 +86,7 @@
unsigned tb_to_us;
unsigned tb_last_stamp;
unsigned long tb_to_ns_scale;
+int ppc_force_interrupt;
extern unsigned long wall_jiffies;
@@ -131,8 +132,11 @@
unsigned jiffy_stamp = last_jiffy_stamp(cpu);
extern void do_IRQ(struct pt_regs *);
- if (atomic_read(&ppc_n_lost_interrupts) != 0)
+ if ((atomic_read(&ppc_n_lost_interrupts) != 0) ||
+ ppc_force_interrupt) {
+ ppc_force_interrupt = 0;
do_IRQ(regs);
+ }
irq_enter();
Index: linux-work/arch/ppc/platforms/pmac_cpufreq.c
===================================================================
--- linux-work.orig/arch/ppc/platforms/pmac_cpufreq.c 2005-05-28 10:30:17.000000000 +1000
+++ linux-work/arch/ppc/platforms/pmac_cpufreq.c 2005-05-28 10:30:21.000000000 +1000
@@ -112,11 +112,11 @@
static inline void wakeup_decrementer(void)
{
- set_dec(tb_ticks_per_jiffy);
/* No currently-supported powerbook has a 601,
* so use get_tbl, not native
*/
last_jiffy_stamp(0) = tb_last_stamp = get_tbl();
+ set_dec(1);
}
#ifdef DEBUG_FREQ
@@ -340,6 +340,9 @@
/* Restore interrupts */
openpic_set_priority(pic_prio);
+ /* Force a fetch from the PIC on the first DEC interrupt. */
+ ppc_force_interrupt = 1;
+
/* Let interrupts flow again ... */
local_irq_restore(flags);
Index: linux-work/arch/ppc/platforms/pmac_feature.c
===================================================================
--- linux-work.orig/arch/ppc/platforms/pmac_feature.c 2005-05-03 13:51:57.000000000 +1000
+++ linux-work/arch/ppc/platforms/pmac_feature.c 2005-05-28 10:30:21.000000000 +1000
@@ -548,7 +548,7 @@
}
static void __pmac
-heathrow_sleep(struct macio_chip* macio, int secondary)
+heathrow_sleep(struct macio_chip* macio, int secondary, int turnoff)
{
if (secondary) {
dbdma_save(macio, save_alt_dbdma);
@@ -559,17 +559,22 @@
save_fcr[0] = MACIO_IN32(0x38);
save_fcr[1] = MACIO_IN32(0x3c);
save_mbcr = MACIO_IN32(0x34);
- /* Make sure sound is shut down */
- MACIO_BIS(HEATHROW_FCR, HRW_SOUND_POWER_N);
- MACIO_BIC(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE);
- /* This seems to be necessary as well or the fan
- * keeps coming up and battery drains fast */
- MACIO_BIC(HEATHROW_FCR, HRW_IOBUS_ENABLE);
- MACIO_BIC(HEATHROW_FCR, HRW_IDE0_RESET_N);
- /* Make sure eth is down even if module or sleep
- * won't work properly */
- MACIO_BIC(HEATHROW_FCR, HRW_BMAC_IO_ENABLE | HRW_BMAC_RESET);
+ if (turnoff) {
+ /* Make sure sound is shut down */
+ MACIO_BIS(HEATHROW_FCR, HRW_SOUND_POWER_N);
+ MACIO_BIC(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE);
+ /* This seems to be necessary as well or the fan
+ * keeps coming up and battery drains fast */
+ MACIO_BIC(HEATHROW_FCR, HRW_IOBUS_ENABLE);
+ MACIO_BIC(HEATHROW_FCR, HRW_IDE0_RESET_N);
+ /* Make sure eth is down even if module or sleep
+ * won't work properly */
+ MACIO_BIC(HEATHROW_FCR,
+ HRW_BMAC_IO_ENABLE | HRW_BMAC_RESET);
+ }
}
+ if (!turnoff)
+ return;
/* Make sure modem is shut down */
MACIO_OUT8(HRW_GPIO_MODEM_RESET,
MACIO_IN8(HRW_GPIO_MODEM_RESET) & ~1);
@@ -612,12 +617,16 @@
return -EPERM;
if (value == 1) {
if (macio_chips[1].type == macio_gatwick)
- heathrow_sleep(&macio_chips[0], 1);
- heathrow_sleep(&macio_chips[0], 0);
+ heathrow_sleep(&macio_chips[1], 1, 1);
+ heathrow_sleep(&macio_chips[0], 0, 1);
+ } else if (value == 2) {
+ if (macio_chips[1].type == macio_gatwick)
+ heathrow_sleep(&macio_chips[1], 1, 0);
+ heathrow_sleep(&macio_chips[0], 0, 0);
} else if (value == 0) {
heathrow_wakeup(&macio_chips[0], 0);
if (macio_chips[1].type == macio_gatwick)
- heathrow_wakeup(&macio_chips[0], 1);
+ heathrow_wakeup(&macio_chips[1], 1);
}
return 0;
}
@@ -1699,7 +1708,7 @@
static int __pmac
-core99_sleep(void)
+core99_sleep(int turnoff)
{
struct macio_chip* macio;
int i;
@@ -1709,26 +1718,28 @@
macio->type != macio_intrepid)
return -ENODEV;
- /* We power off the wireless slot in case it was not done
- * by the driver. We don't power it on automatically however
- */
- if (macio->flags & MACIO_FLAG_AIRPORT_ON)
- core99_airport_enable(macio->of_node, 0, 0);
+ if (turnoff) {
+ /* We power off the wireless slot in case it was not done
+ * by the driver. We don't power it on automatically however
+ */
+ if (macio->flags & MACIO_FLAG_AIRPORT_ON)
+ core99_airport_enable(macio->of_node, 0, 0);
- /* We power off the FW cable. Should be done by the driver... */
- if (macio->flags & MACIO_FLAG_FW_SUPPORTED) {
- core99_firewire_enable(NULL, 0, 0);
- core99_firewire_cable_power(NULL, 0, 0);
- }
+ /* We power off the FW cable.Should be done by the driver... */
+ if (macio->flags & MACIO_FLAG_FW_SUPPORTED) {
+ core99_firewire_enable(NULL, 0, 0);
+ core99_firewire_cable_power(NULL, 0, 0);
+ }
- /* We make sure int. modem is off (in case driver lost it) */
- if (macio->type == macio_keylargo)
- core99_modem_enable(macio->of_node, 0, 0);
- else
- pangea_modem_enable(macio->of_node, 0, 0);
+ /* We make sure int. modem is off (in case driver lost it) */
+ if (macio->type == macio_keylargo)
+ core99_modem_enable(macio->of_node, 0, 0);
+ else
+ pangea_modem_enable(macio->of_node, 0, 0);
- /* We make sure the sound is off as well */
- core99_sound_chip_enable(macio->of_node, 0, 0);
+ /* We make sure the sound is off as well */
+ core99_sound_chip_enable(macio->of_node, 0, 0);
+ }
/*
* Save various bits of KeyLargo
@@ -1756,26 +1767,32 @@
/* Save state & config of DBDMA channels */
dbdma_save(macio, save_dbdma);
- /*
- * Turn off as much as we can
- */
- if (macio->type == macio_pangea)
- pangea_shutdown(macio, 1);
- else if (macio->type == macio_intrepid)
- intrepid_shutdown(macio, 1);
- else if (macio->type == macio_keylargo)
- keylargo_shutdown(macio, 1);
+ if (turnoff) {
+ /*
+ * Turn off as much as we can
+ */
+ if (macio->type == macio_pangea)
+ pangea_shutdown(macio, 1);
+ else if (macio->type == macio_intrepid)
+ intrepid_shutdown(macio, 1);
+ else if (macio->type == macio_keylargo)
+ keylargo_shutdown(macio, 1);
+ }
/*
- * Put the host bridge to sleep
+ * Save host bridge state and put to sleep
*/
save_unin_clock_ctl = UN_IN(UNI_N_CLOCK_CNTL);
- /* Note: do not switch GMAC off, driver does it when necessary, WOL must keep it
- * enabled !
+
+ if (!turnoff)
+ return 0;
+
+ /* Note: do not switch GMAC off, driver does it when necessary,
+ * WOL must keep it enabled !
*/
UN_OUT(UNI_N_CLOCK_CNTL, save_unin_clock_ctl &
- ~(/*UNI_N_CLOCK_CNTL_GMAC|*/UNI_N_CLOCK_CNTL_FW/*|UNI_N_CLOCK_CNTL_PCI*/));
+ ~(UNI_N_CLOCK_CNTL_FW));
udelay(100);
UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_SLEEPING);
UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_SLEEP);
@@ -1876,7 +1893,9 @@
return -EPERM;
if (value == 1)
- return core99_sleep();
+ return core99_sleep(1);
+ else if (value == 2)
+ return core99_sleep(0);
else if (value == 0)
return core99_wake_up();
return 0;
Index: linux-work/include/asm-ppc/pmac_feature.h
===================================================================
--- linux-work.orig/include/asm-ppc/pmac_feature.h 2005-05-03 13:51:57.000000000 +1000
+++ linux-work/include/asm-ppc/pmac_feature.h 2005-05-28 10:30:21.000000000 +1000
@@ -245,6 +245,7 @@
* set the sleep state of the motherboard.
*
* Pass -1 as value to query for sleep capability
+ * Pass 2 to save state & not sleep
* Pass 1 to set IOs to sleep
* Pass 0 to set IOs to wake
*/
Index: linux-work/include/asm-ppc/irq.h
===================================================================
--- linux-work.orig/include/asm-ppc/irq.h 2005-05-02 10:49:57.000000000 +1000
+++ linux-work/include/asm-ppc/irq.h 2005-05-28 10:30:21.000000000 +1000
@@ -391,6 +391,7 @@
extern unsigned long ppc_cached_irq_mask[NR_MASK_WORDS];
extern unsigned long ppc_lost_interrupts[NR_MASK_WORDS];
extern atomic_t ppc_n_lost_interrupts;
+extern int ppc_force_interrupt;
struct irqaction;
struct pt_regs;
Index: linux-work/drivers/ide/ide-io.c
===================================================================
--- linux-work.orig/drivers/ide/ide-io.c 2005-05-02 10:48:09.000000000 +1000
+++ linux-work/drivers/ide/ide-io.c 2005-05-28 10:30:21.000000000 +1000
@@ -150,7 +150,7 @@
switch (rq->pm->pm_step) {
case ide_pm_flush_cache: /* Suspend step 1 (flush cache) complete */
- if (rq->pm->pm_state == 4)
+ if (rq->pm->pm_state == PM_SUSPEND_DISK)
rq->pm->pm_step = ide_pm_state_completed;
else
rq->pm->pm_step = idedisk_pm_standby;
Index: linux-work/drivers/video/aty/aty128fb.c
===================================================================
--- linux-work.orig/drivers/video/aty/aty128fb.c 2005-05-02 10:48:53.000000000 +1000
+++ linux-work/drivers/video/aty/aty128fb.c 2005-05-28 10:30:21.000000000 +1000
@@ -2337,21 +2337,15 @@
* can properly take care of D3 ? Also, with swsusp, we
* know we'll be rebooted, ...
*/
-#ifdef CONFIG_PPC_PMAC
- /* HACK ALERT ! Once I find a proper way to say to each driver
- * individually what will happen with it's PCI slot, I'll change
- * that. On laptops, the AGP slot is just unclocked, so D2 is
- * expected, while on desktops, the card is powered off
- */
- if (state >= 3)
- state = 2;
-#endif /* CONFIG_PPC_PMAC */
- if (state != 2 || state == pdev->dev.power.power_state)
+ if (pdev->dev.power.power_state)
return 0;
printk(KERN_DEBUG "aty128fb: suspending...\n");
+ if (state == PM_SUSPEND_DISK)
+ goto skip;
+
acquire_console_sem();
fb_set_suspend(info, 1);
@@ -2381,12 +2375,11 @@
* used dummy fb ops, 2.5 need proper support for this at the
* fbdev level
*/
- if (state == 2)
- aty128_set_suspend(par, 1);
+ aty128_set_suspend(par, 1);
release_console_sem();
-
- pdev->dev.power.power_state = state;
+ skip:
+ pdev->dev.power.power_state = 1;
return 0;
}
@@ -2400,8 +2393,7 @@
return 0;
/* Wakeup chip */
- if (pdev->dev.power.power_state == 2)
- aty128_set_suspend(par, 0);
+ aty128_set_suspend(par, 0);
par->asleep = 0;
/* Restore display & engine */
Index: linux-work/drivers/char/agp/uninorth-agp.c
===================================================================
--- linux-work.orig/drivers/char/agp/uninorth-agp.c 2005-05-02 10:48:09.000000000 +1000
+++ linux-work/drivers/char/agp/uninorth-agp.c 2005-05-28 11:37:38.000000000 +1000
@@ -347,6 +347,8 @@
struct agp_bridge_data *bridge;
u32 command;
+ uninorth_configure();
+
bridge = agp_find_bridge(pdev);
if (bridge == NULL)
return -ENODEV;
Index: linux-work/drivers/video/aty/radeon_base.c
===================================================================
--- linux-work.orig/drivers/video/aty/radeon_base.c 2005-05-28 11:25:03.000000000 +1000
+++ linux-work/drivers/video/aty/radeon_base.c 2005-05-28 11:31:14.000000000 +1000
@@ -2036,7 +2036,7 @@
*/
#ifdef CONFIG_PPC_OF
#undef SET_MC_FB_FROM_APERTURE
-static void fixup_memory_mappings(struct radeonfb_info *rinfo)
+void radeon_fixup_memory_mappings(struct radeonfb_info *rinfo)
{
u32 save_crtc_gen_cntl, save_crtc2_gen_cntl = 0;
u32 save_crtc_ext_cntl;
@@ -2360,7 +2360,7 @@
* to cause lockups when enabling the engine. We reconfigure
* the card internal memory mappings properly
*/
- fixup_memory_mappings(rinfo);
+ radeon_fixup_memory_mappings(rinfo);
#endif /* CONFIG_PPC_OF */
/* Get VRAM size and type */
Index: linux-work/drivers/video/aty/radeon_pm.c
===================================================================
--- linux-work.orig/drivers/video/aty/radeon_pm.c 2005-05-28 11:25:03.000000000 +1000
+++ linux-work/drivers/video/aty/radeon_pm.c 2005-05-28 11:26:41.000000000 +1000
@@ -2659,8 +2659,10 @@
radeon_set_suspend(rinfo, 0);
rinfo->asleep = 0;
- } else
+ } else {
+ radeon_fixup_memory_mappings(rinfo);
radeon_engine_idle();
+ }
/* Restore display & engine */
radeon_write_mode (rinfo, &rinfo->state, 1);
Index: linux-work/drivers/video/aty/radeonfb.h
===================================================================
--- linux-work.orig/drivers/video/aty/radeonfb.h 2005-05-02 10:48:53.000000000 +1000
+++ linux-work/drivers/video/aty/radeonfb.h 2005-05-28 11:25:54.000000000 +1000
@@ -618,6 +618,7 @@
extern void radeonfb_engine_reset(struct radeonfb_info *rinfo);
/* Other functions */
+extern void radeon_fixup_memory_mappings(struct radeonfb_info *rinfo);
extern int radeon_screen_blank(struct radeonfb_info *rinfo, int blank, int mode_switch);
extern void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode,
int reg_only);
More information about the Linuxppc-dev
mailing list