Index: linux-work/arch/ppc/platforms/Makefile =================================================================== --- linux-work.orig/arch/ppc/platforms/Makefile 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/arch/ppc/platforms/Makefile 2005-05-30 10:52:32.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-30 10:52:32.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 = 1; +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-30 10:52:26.000000000 +1000 +++ linux-work/arch/ppc/platforms/pmac_setup.c 2005-05-30 10:52:32.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-30 10:52:26.000000000 +1000 +++ linux-work/drivers/base/power/suspend.c 2005-05-30 10:52:32.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-30 10:52:26.000000000 +1000 +++ linux-work/drivers/macintosh/via-pmu.c 2005-05-30 10:52:32.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 @@ -45,8 +42,6 @@ #include #include #include -#include -#include #include #include #include @@ -54,11 +49,11 @@ #include #include #include -#include #include -#include #include #include +#include + #ifdef CONFIG_PMAC_BACKLIGHT #include #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= 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); @@ -2976,14 +2338,16 @@ PMU_MINOR, "pmu", &pmu_device_fops }; -void pmu_device_init(void) +static int pmu_device_init(void) { if (!via) - return; + return 0; if (misc_register(&pmu_device) < 0) printk(KERN_ERR "via-pmu: cannot register misc device.\n"); + + return 0; } -#endif /* CONFIG_PMAC_PBOOK */ +device_initcall(pmu_device_init); #ifdef DEBUG_SLEEP static inline void __pmac @@ -3065,17 +2429,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 +2462,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 +2473,7 @@ /* Resume PMU event interrupts */ pmu_resume(); + /* Mark driver ready for async requests */ pmu_sys_suspended = 0; return 0; @@ -3151,12 +2532,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-30 10:52:26.000000000 +1000 +++ linux-work/include/asm-ppc/machdep.h 2005-05-30 10:52:32.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-30 10:52:26.000000000 +1000 +++ linux-work/include/asm-ppc/suspend.h 2005-05-30 10:52:32.000000000 +1000 @@ -1,12 +1,44 @@ +#ifndef __PPC_SUSPEND_H +#define __PPC_SUSPEND_H + +#include +#include +#include + + +#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-30 10:52:26.000000000 +1000 +++ linux-work/include/linux/pm.h 2005-05-30 11:57:08.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 *); @@ -205,7 +234,7 @@ * or something similar soon. */ -#define PMSG_FREEZE ((__force pm_message_t) 3) +#define PMSG_FREEZE ((__force pm_message_t) 2) #define PMSG_SUSPEND ((__force pm_message_t) 3) #define PMSG_ON ((__force pm_message_t) 0) Index: linux-work/include/linux/pmu.h =================================================================== --- linux-work.orig/include/linux/pmu.h 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/include/linux/pmu.h 2005-05-30 10:52:32.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); @@ -165,8 +168,6 @@ extern int pmu_i2c_simple_read(int bus, int addr, u8* data, int len); extern int pmu_i2c_simple_write(int bus, int addr, u8* data, int len); - -#ifdef CONFIG_PMAC_PBOOK /* * Stuff for putting the powerbook to sleep and waking it again. * @@ -235,6 +236,4 @@ extern struct pmu_battery_info pmu_batteries[PMU_MAX_BATTERIES]; extern unsigned int pmu_power_flags; -#endif /* CONFIG_PMAC_PBOOK */ - #endif /* __KERNEL__ */ Index: linux-work/kernel/power/main.c =================================================================== --- linux-work.orig/kernel/power/main.c 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/kernel/power/main.c 2005-05-30 10:52:32.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-30 10:52:26.000000000 +1000 +++ linux-work/arch/ppc/platforms/pmac_pic.c 2005-05-30 10:52:32.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-30 10:52:26.000000000 +1000 +++ linux-work/arch/ppc/syslib/open_pic.c 2005-05-30 10:52:32.000000000 +1000 @@ -945,11 +945,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; @@ -958,11 +954,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; @@ -972,6 +966,8 @@ OPENPIC_CURRENT_TASK_PRIORITY_MASK, 0xf); } + openpic_set_priority(0xf); + for (i=0; iGlobal.IPI_Vector_Priority(i)); for (i=0; i 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); @@ -1027,22 +1023,19 @@ } while (openpic_readfield(&ISR[i]->Vector_Priority, vppmask) != (save_irq_src_vp[i] & vppmask)); } - for (i=0; iProcessor[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; iProcessor[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"), }; @@ -1086,3 +1079,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-30 10:52:26.000000000 +1000 +++ linux-work/include/asm-ppc/open_pic.h 2005-05-30 10:52:32.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-30 10:52:26.000000000 +1000 +++ linux-work/arch/ppc/kernel/time.c 2005-05-30 10:52:32.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-30 10:52:26.000000000 +1000 +++ linux-work/arch/ppc/platforms/pmac_cpufreq.c 2005-05-30 10:52:32.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-30 10:52:26.000000000 +1000 +++ linux-work/arch/ppc/platforms/pmac_feature.c 2005-05-30 10:52:32.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-30 10:52:26.000000000 +1000 +++ linux-work/include/asm-ppc/pmac_feature.h 2005-05-30 10:52:32.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-30 10:52:26.000000000 +1000 +++ linux-work/include/asm-ppc/irq.h 2005-05-30 10:52:32.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-30 10:52:26.000000000 +1000 +++ linux-work/drivers/ide/ide-io.c 2005-05-30 12:54:44.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 == PMSG_FREEZE) 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-30 10:52:26.000000000 +1000 +++ linux-work/drivers/video/aty/aty128fb.c 2005-05-30 12:09:09.000000000 +1000 @@ -350,10 +350,8 @@ static int default_cmode __initdata = CMODE_8; #endif -#ifdef CONFIG_PMAC_PBOOK static int default_crt_on __initdata = 0; static int default_lcd_on __initdata = 1; -#endif #ifdef CONFIG_MTRR static int mtrr = 1; @@ -1249,7 +1247,6 @@ return 0; } -#ifdef CONFIG_PMAC_PBOOK static void aty128_set_crt_enable(struct aty128fb_par *par, int on) { if (on) { @@ -1284,7 +1281,6 @@ aty_st_le32(LVDS_GEN_CNTL, reg); } } -#endif /* CONFIG_PMAC_PBOOK */ static void aty128_set_pll(struct aty128_pll *pll, const struct aty128fb_par *par) { @@ -1491,12 +1487,11 @@ info->fix.visual = par->crtc.bpp == 8 ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR; -#ifdef CONFIG_PMAC_PBOOK if (par->chip_gen == rage_M3) { aty128_set_crt_enable(par, par->crt_on); aty128_set_lcd_enable(par, par->lcd_on); } -#endif + if (par->accel_flags & FB_ACCELF_TEXT) aty128_init_engine(par); @@ -1652,7 +1647,6 @@ return 0; while ((this_opt = strsep(&options, ",")) != NULL) { -#ifdef CONFIG_PMAC_PBOOK if (!strncmp(this_opt, "lcd:", 4)) { default_lcd_on = simple_strtoul(this_opt+4, NULL, 0); continue; @@ -1660,7 +1654,6 @@ default_crt_on = simple_strtoul(this_opt+4, NULL, 0); continue; } -#endif #ifdef CONFIG_MTRR if(!strncmp(this_opt, "nomtrr", 6)) { mtrr = 0; @@ -1752,10 +1745,8 @@ info->fbops = &aty128fb_ops; info->flags = FBINFO_FLAG_DEFAULT; -#ifdef CONFIG_PMAC_PBOOK par->lcd_on = default_lcd_on; par->crt_on = default_crt_on; -#endif var = default_var; #ifdef CONFIG_PPC_PMAC @@ -2035,12 +2026,10 @@ aty_st_8(CRTC_EXT_CNTL+1, state); -#ifdef CONFIG_PMAC_PBOOK if (par->chip_gen == rage_M3) { aty128_set_crt_enable(par, par->crt_on && !blank); aty128_set_lcd_enable(par, par->lcd_on && !blank); } -#endif #ifdef CONFIG_PMAC_BACKLIGHT if ((_machine == _MACH_Pmac) && !blank) set_backlight_enable(1); @@ -2124,7 +2113,6 @@ static int aty128fb_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg, struct fb_info *info) { -#ifdef CONFIG_PMAC_PBOOK struct aty128fb_par *par = info->par; u32 value; int rc; @@ -2149,7 +2137,6 @@ value = (par->crt_on << 1) | par->lcd_on; return put_user(value, (__u32 __user *)arg); } -#endif return -EINVAL; } @@ -2337,21 +2324,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 != PMSG_ON) return 0; printk(KERN_DEBUG "aty128fb: suspending...\n"); + if (state != PMSG_SUSPEND) + goto skip; + acquire_console_sem(); fb_set_suspend(info, 1); @@ -2381,12 +2362,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; } @@ -2396,12 +2376,11 @@ struct fb_info *info = pci_get_drvdata(pdev); struct aty128fb_par *par = info->par; - if (pdev->dev.power.power_state == 0) + if (pdev->dev.power.power_state == PMSG_ON) 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-30 10:52:26.000000000 +1000 +++ linux-work/drivers/char/agp/uninorth-agp.c 2005-05-30 10:52:32.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-30 10:52:26.000000000 +1000 +++ linux-work/drivers/video/aty/radeon_base.c 2005-05-30 10:52:32.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 */ @@ -2505,7 +2505,9 @@ err_release_fb: framebuffer_release(info); err_disable: +#if 0 pci_disable_device(pdev); +#endif err_out: return ret; } Index: linux-work/drivers/video/aty/radeon_pm.c =================================================================== --- linux-work.orig/drivers/video/aty/radeon_pm.c 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/drivers/video/aty/radeon_pm.c 2005-05-30 11:57:26.000000000 +1000 @@ -2537,7 +2537,7 @@ * really cause any problem at this point, provided that the wakeup * code knows that any state in memory may not match the HW */ - if (state != PM_SUSPEND_MEM) + if (state != PMSG_SUSPEND) goto done; acquire_console_sem(); @@ -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-30 10:52:26.000000000 +1000 +++ linux-work/drivers/video/aty/radeonfb.h 2005-05-30 10:52:32.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); Index: linux-work/drivers/macintosh/Kconfig =================================================================== --- linux-work.orig/drivers/macintosh/Kconfig 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/drivers/macintosh/Kconfig 2005-05-30 10:52:32.000000000 +1000 @@ -86,33 +86,18 @@ on the "SMU" system control chip which replaces the old PMU. If you don't know, say Y. -config PMAC_PBOOK - bool "Power management support for PowerBooks" - depends on ADB_PMU - ---help--- - This provides support for putting a PowerBook to sleep; it also - enables media bay support. Power management works on the - PB2400/3400/3500, Wallstreet, Lombard, and Bronze PowerBook G3 and - the Titanium Powerbook G4, as well as the iBooks. You should get - the power management daemon, pmud, to make it work and you must have - the /dev/pmu device (see the pmud README). - - Get pmud from . - - If you have a PowerBook, you should say Y here. - - You may also want to compile the dma sound driver as a module and - have it autoloaded. The act of removing the module shuts down the - sound hardware for more power savings. - -config PM - bool - depends on PPC_PMAC && ADB_PMU && PMAC_PBOOK - default y - config PMAC_APM_EMU tristate "APM emulation" - depends on PMAC_PBOOK + depends on PPC_PMAC && PPC32 && PM + +config PMAC_MEDIABAY + bool "Support PowerBook hotswap media bay" + depends on PPC_PMAC && PPC32 + help + This option adds support for older PowerBook's hotswap media bay + that can contains batteries, floppy drives, or IDE devices. PCI + devices are not fully supported in the bay as I never had one to + try with # made a separate option since backlight may end up beeing used # on non-powerbook machines (but only on PMU based ones AFAIK) Index: linux-work/drivers/macintosh/adb.c =================================================================== --- linux-work.orig/drivers/macintosh/adb.c 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/drivers/macintosh/adb.c 2005-05-30 10:52:32.000000000 +1000 @@ -90,7 +90,7 @@ static int autopoll_devs; int __adb_probe_sync; -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM static int adb_notify_sleep(struct pmu_sleep_notifier *self, int when); static struct pmu_sleep_notifier adb_sleep_notifier = { adb_notify_sleep, @@ -320,9 +320,9 @@ printk(KERN_WARNING "Warning: no ADB interface detected\n"); adb_controller = NULL; } else { -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM pmu_register_sleep_notifier(&adb_sleep_notifier); -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PM */ #ifdef CONFIG_PPC if (machine_is_compatible("AAPL,PowerBook1998") || machine_is_compatible("PowerBook1,1")) @@ -337,7 +337,7 @@ __initcall(adb_init); -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM /* * notify clients before sleep and reset bus afterwards */ @@ -378,7 +378,7 @@ } return PBOOK_SLEEP_OK; } -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PM */ static int do_adb_reset_bus(void) Index: linux-work/arch/ppc/platforms/pmac_sleep.S =================================================================== --- linux-work.orig/arch/ppc/platforms/pmac_sleep.S 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/arch/ppc/platforms/pmac_sleep.S 2005-05-30 10:52:32.000000000 +1000 @@ -46,7 +46,7 @@ .section .text .align 5 -#if defined(CONFIG_PMAC_PBOOK) || defined(CONFIG_CPU_FREQ_PMAC) +#if defined(CONFIG_PM) || defined(CONFIG_CPU_FREQ_PMAC) /* This gets called by via-pmu.c late during the sleep process. * The PMU was already send the sleep command and will shut us down @@ -382,7 +382,7 @@ isync rfi -#endif /* defined(CONFIG_PMAC_PBOOK) || defined(CONFIG_CPU_FREQ) */ +#endif /* defined(CONFIG_PM) || defined(CONFIG_CPU_FREQ) */ .section .data .balign L1_CACHE_LINE_SIZE Index: linux-work/arch/ppc/platforms/pmac_time.c =================================================================== --- linux-work.orig/arch/ppc/platforms/pmac_time.c 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/arch/ppc/platforms/pmac_time.c 2005-05-30 10:52:32.000000000 +1000 @@ -206,7 +206,7 @@ return 1; } -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM /* * Reset the time after a sleep. */ @@ -238,7 +238,7 @@ static struct pmu_sleep_notifier time_sleep_notifier __pmacdata = { time_sleep_notify, SLEEP_LEVEL_MISC, }; -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PM */ /* * Query the OF and get the decr frequency. @@ -251,9 +251,9 @@ struct device_node *cpu; unsigned int freq, *fp; -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM pmu_register_sleep_notifier(&time_sleep_notifier); -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PM */ /* We assume MacRISC2 machines have correct device-tree * calibration. That's better since the VIA itself seems Index: linux-work/drivers/block/swim3.c =================================================================== --- linux-work.orig/drivers/block/swim3.c 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/drivers/block/swim3.c 2005-05-30 10:52:32.000000000 +1000 @@ -253,7 +253,7 @@ static int swim3_add_device(struct device_node *swims); int swim3_init(void); -#ifndef CONFIG_PMAC_PBOOK +#ifndef CONFIG_PMAC_MEDIABAY #define check_media_bay(which, what) 1 #endif @@ -297,9 +297,11 @@ int i; for(i=0;imedia_bay && check_media_bay(fs->media_bay, MB_FD)) return -ENXIO; +#endif switch (cmd) { case FDEJECT: @@ -881,8 +885,10 @@ int n, err = 0; if (fs->ref_count == 0) { +#ifdef CONFIG_PMAC_MEDIABAY if (fs->media_bay && check_media_bay(fs->media_bay, MB_FD)) return -ENXIO; +#endif out_8(&sw->setup, S_IBM_DRIVE | S_FCLK_DIV2); out_8(&sw->control_bic, 0xff); out_8(&sw->mode, 0x95); @@ -967,8 +973,10 @@ struct swim3 __iomem *sw; int ret, n; +#ifdef CONFIG_PMAC_MEDIABAY if (fs->media_bay && check_media_bay(fs->media_bay, MB_FD)) return -ENXIO; +#endif sw = fs->swim3; grab_drive(fs, revalidating, 0); Index: linux-work/drivers/char/misc.c =================================================================== --- linux-work.orig/drivers/char/misc.c 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/drivers/char/misc.c 2005-05-30 10:52:32.000000000 +1000 @@ -311,9 +311,6 @@ #ifdef CONFIG_BVME6000 rtc_DP8570A_init(); #endif -#ifdef CONFIG_PMAC_PBOOK - pmu_device_init(); -#endif #ifdef CONFIG_TOSHIBA tosh_init(); #endif Index: linux-work/drivers/ide/ppc/pmac.c =================================================================== --- linux-work.orig/drivers/ide/ppc/pmac.c 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/drivers/ide/ppc/pmac.c 2005-05-30 10:52:32.000000000 +1000 @@ -1324,9 +1324,9 @@ /* XXX FIXME: Media bay stuff need re-organizing */ if (np->parent && np->parent->name && strcasecmp(np->parent->name, "media-bay") == 0) { -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PMAC_MEDIABAY media_bay_set_ide_infos(np->parent, pmif->regbase, pmif->irq, hwif->index); -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PMAC_MEDIABAY */ pmif->mediabay = 1; if (!bidp) pmif->aapl_bus_id = 1; @@ -1382,10 +1382,10 @@ hwif->index, model_name[pmif->kind], pmif->aapl_bus_id, pmif->mediabay ? " (mediabay)" : "", hwif->irq); -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PMAC_MEDIABAY if (pmif->mediabay && check_media_bay_by_base(pmif->regbase, MB_CD) == 0) hwif->noprobe = 0; -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PMAC_MEDIABAY */ hwif->sg_max_nents = MAX_DCMDS; Index: linux-work/drivers/ieee1394/ohci1394.c =================================================================== --- linux-work.orig/drivers/ieee1394/ohci1394.c 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/drivers/ieee1394/ohci1394.c 2005-05-30 10:52:32.000000000 +1000 @@ -3538,8 +3538,8 @@ static int ohci1394_pci_resume (struct pci_dev *pdev) { -#ifdef CONFIG_PMAC_PBOOK - { +#ifdef CONFIG_PPC_PMAC + if (_machine == _MACH_Pmac) { struct device_node *of_node; /* Re-enable 1394 */ @@ -3547,7 +3547,7 @@ if (of_node) pmac_call_feature (PMAC_FTR_1394_ENABLE, of_node, 0, 1); } -#endif +#endif /* CONFIG_PPC_PMAC */ pci_enable_device(pdev); @@ -3557,8 +3557,8 @@ static int ohci1394_pci_suspend (struct pci_dev *pdev, pm_message_t state) { -#ifdef CONFIG_PMAC_PBOOK - { +#ifdef CONFIG_PPC_PMAC + if (_machine == _MACH_Pmac) { struct device_node *of_node; /* Disable 1394 */ Index: linux-work/drivers/macintosh/Makefile =================================================================== --- linux-work.orig/drivers/macintosh/Makefile 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/drivers/macintosh/Makefile 2005-05-30 10:52:32.000000000 +1000 @@ -6,7 +6,7 @@ obj-$(CONFIG_PPC_PMAC) += macio_asic.o -obj-$(CONFIG_PMAC_PBOOK) += mediabay.o +obj-$(CONFIG_PMAC_MEDIABAY) += mediabay.o obj-$(CONFIG_MAC_EMUMOUSEBTN) += mac_hid.o obj-$(CONFIG_INPUT_ADBHID) += adbhid.o obj-$(CONFIG_ANSLCD) += ans-lcd.o Index: linux-work/drivers/usb/host/ohci-pci.c =================================================================== --- linux-work.orig/drivers/usb/host/ohci-pci.c 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/drivers/usb/host/ohci-pci.c 2005-05-30 10:52:32.000000000 +1000 @@ -14,14 +14,11 @@ * This file is licenced under the GPL. */ -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PPC_PMAC #include #include #include #include -#ifndef CONFIG_PM -# define CONFIG_PM -#endif #endif #ifndef CONFIG_PCI @@ -132,7 +129,7 @@ /* let things settle down a bit */ msleep (100); -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PPC_PMAC if (_machine == _MACH_Pmac) { struct device_node *of_node; @@ -141,7 +138,7 @@ if (of_node) pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0); } -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PPC_PMAC */ return 0; } @@ -151,7 +148,7 @@ struct ohci_hcd *ohci = hcd_to_ohci (hcd); int retval = 0; -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PPC_PMAC if (_machine == _MACH_Pmac) { struct device_node *of_node; @@ -160,7 +157,7 @@ if (of_node) pmac_call_feature (PMAC_FTR_USB_ENABLE, of_node, 0, 1); } -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PPC_PMAC */ /* resume root hub */ if (time_before (jiffies, ohci->next_statechange)) Index: linux-work/drivers/video/chipsfb.c =================================================================== --- linux-work.orig/drivers/video/chipsfb.c 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/drivers/video/chipsfb.c 2005-05-30 10:52:32.000000000 +1000 @@ -28,22 +28,17 @@ #include #include #include +#include #include #ifdef CONFIG_PMAC_BACKLIGHT #include #endif -#ifdef CONFIG_PMAC_PBOOK -#include -#include -#endif /* * Since we access the display with inb/outb to fixed port numbers, * we can only handle one 6555x chip. -- paulus */ -static struct fb_info chipsfb_info; - #define write_ind(num, val, ap, dp) do { \ outb((num), (ap)); outb((val), (dp)); \ } while (0) @@ -74,14 +69,6 @@ inb(0x3da); read_ind(num, var, 0x3c0, 0x3c1); \ } while (0) -#ifdef CONFIG_PMAC_PBOOK -static unsigned char *save_framebuffer; -int chips_sleep_notify(struct pmu_sleep_notifier *self, int when); -static struct pmu_sleep_notifier chips_sleep_notifier = { - chips_sleep_notify, SLEEP_LEVEL_VIDEO, -}; -#endif - /* * Exported functions */ @@ -356,6 +343,8 @@ static void __init init_chips(struct fb_info *p, unsigned long addr) { + memset(p->screen_base, 0, 0x100000); + p->fix = chipsfb_fix; p->fix.smem_start = addr; @@ -366,34 +355,41 @@ fb_alloc_cmap(&p->cmap, 256, 0); - if (register_framebuffer(p) < 0) { - printk(KERN_ERR "C&T 65550 framebuffer failed to register\n"); - return; - } - - printk(KERN_INFO "fb%d: Chips 65550 frame buffer (%dK RAM detected)\n", - p->node, p->fix.smem_len / 1024); - chips_hw_init(); } static int __devinit chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) { - struct fb_info *p = &chipsfb_info; + struct fb_info *p; unsigned long addr, size; unsigned short cmd; + int rc = -ENODEV; + + if (pci_enable_device(dp) < 0) { + dev_err(&dp->dev, "Cannot enable PCI device\n"); + goto err_out; + } if ((dp->resource[0].flags & IORESOURCE_MEM) == 0) - return -ENODEV; + goto err_disable; addr = pci_resource_start(dp, 0); size = pci_resource_len(dp, 0); if (addr == 0) - return -ENODEV; - if (p->screen_base != 0) - return -EBUSY; - if (!request_mem_region(addr, size, "chipsfb")) - return -EBUSY; + goto err_disable; + + p = framebuffer_alloc(0, &dp->dev); + if (p == NULL) { + dev_err(&dp->dev, "Cannot allocate framebuffer structure\n"); + rc = -ENOMEM; + goto err_disable; + } + + if (pci_request_region(dp, 0, "chipsfb") != 0) { + dev_err(&dp->dev, "Cannot request framebuffer\n"); + rc = -EBUSY; + goto err_release_fb; + } #ifdef __BIG_ENDIAN addr += 0x800000; // Use big-endian aperture @@ -411,40 +407,87 @@ set_backlight_enable(1); #endif /* CONFIG_PMAC_BACKLIGHT */ +#ifdef CONFIG_PPC p->screen_base = __ioremap(addr, 0x200000, _PAGE_NO_CACHE); +#else + p->screen_base = ioremap(addr, 0x200000); +#endif if (p->screen_base == NULL) { - release_mem_region(addr, size); - return -ENOMEM; + dev_err(&dp->dev, "Cannot map framebuffer\n"); + rc = -ENOMEM; + goto err_release_pci; } - p->device = &dp->dev; + + pci_set_drvdata(dp, p); + init_chips(p, addr); -#ifdef CONFIG_PMAC_PBOOK - pmu_register_sleep_notifier(&chips_sleep_notifier); -#endif /* CONFIG_PMAC_PBOOK */ + if (register_framebuffer(p) < 0) { + dev_err(&dp->dev,"C&T 65550 framebuffer failed to register\n"); + goto err_unmap; + } - /* Clear the entire framebuffer */ - memset(p->screen_base, 0, 0x100000); + dev_info(&dp->dev,"fb%d: Chips 65550 frame buffer" + " (%dK RAM detected)\n", + p->node, p->fix.smem_len / 1024); - pci_set_drvdata(dp, p); return 0; + + err_unmap: + iounmap(p->screen_base); + err_release_pci: + pci_release_region(dp, 0); + err_release_fb: + framebuffer_release(p); + err_disable: +#if 0 + pci_disable_device(dp); +#endif + err_out: + return rc; } static void __devexit chipsfb_remove(struct pci_dev *dp) { struct fb_info *p = pci_get_drvdata(dp); - if (p != &chipsfb_info || p->screen_base == NULL) - return; unregister_framebuffer(p); iounmap(p->screen_base); - p->screen_base = NULL; - release_mem_region(pci_resource_start(dp, 0), pci_resource_len(dp, 0)); + pci_release_region(dp, 0); +} + +#ifdef CONFIG_PM +static int chipsfb_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct fb_info *p = pci_get_drvdata(pdev); + + if (state == pdev->dev.power.power_state) + return 0; + if (state != PM_SUSPEND_MEM) + goto done; + + acquire_console_sem(); + chipsfb_blank(1, p); + fb_set_suspend(p, 1); + release_console_sem(); + done: + pdev->dev.power.power_state = state; + return 0; +} + +static int chipsfb_pci_resume(struct pci_dev *pdev) +{ + struct fb_info *p = pci_get_drvdata(pdev); + + acquire_console_sem(); + fb_set_suspend(p, 0); + chipsfb_blank(0, p); + release_console_sem(); -#ifdef CONFIG_PMAC_PBOOK - pmu_unregister_sleep_notifier(&chips_sleep_notifier); -#endif /* CONFIG_PMAC_PBOOK */ + pdev->dev.power.power_state = PMSG_ON; + return 0; } +#endif /* CONFIG_PM */ static struct pci_device_id chipsfb_pci_tbl[] = { { PCI_VENDOR_ID_CT, PCI_DEVICE_ID_CT_65550, PCI_ANY_ID, PCI_ANY_ID }, @@ -458,6 +501,10 @@ .id_table = chipsfb_pci_tbl, .probe = chipsfb_pci_init, .remove = __devexit_p(chipsfb_remove), +#ifdef CONFIG_PM + .suspend = chipsfb_pci_suspend, + .resume = chipsfb_pci_resume, +#endif }; int __init chips_init(void) @@ -475,48 +522,4 @@ pci_unregister_driver(&chipsfb_driver); } -#ifdef CONFIG_PMAC_PBOOK -/* - * Save the contents of the frame buffer when we go to sleep, - * and restore it when we wake up again. - */ -int -chips_sleep_notify(struct pmu_sleep_notifier *self, int when) -{ - struct fb_info *p = &chipsfb_info; - int nb = p->var.yres * p->fix.line_length; - - if (p->screen_base == NULL) - return PBOOK_SLEEP_OK; - - switch (when) { - case PBOOK_SLEEP_REQUEST: - save_framebuffer = vmalloc(nb); - if (save_framebuffer == NULL) - return PBOOK_SLEEP_REFUSE; - break; - case PBOOK_SLEEP_REJECT: - if (save_framebuffer) { - vfree(save_framebuffer); - save_framebuffer = NULL; - } - break; - case PBOOK_SLEEP_NOW: - chipsfb_blank(1, p); - if (save_framebuffer) - memcpy(save_framebuffer, p->screen_base, nb); - break; - case PBOOK_WAKE: - if (save_framebuffer) { - memcpy(p->screen_base, save_framebuffer, nb); - vfree(save_framebuffer); - save_framebuffer = NULL; - } - chipsfb_blank(0, p); - break; - } - return PBOOK_SLEEP_OK; -} -#endif /* CONFIG_PMAC_PBOOK */ - MODULE_LICENSE("GPL"); Index: linux-work/sound/oss/dmasound/dmasound_awacs.c =================================================================== --- linux-work.orig/sound/oss/dmasound/dmasound_awacs.c 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/sound/oss/dmasound/dmasound_awacs.c 2005-05-30 10:52:32.000000000 +1000 @@ -255,7 +255,7 @@ static volatile struct dbdma_cmd *emergency_dbdma_cmd; -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM /* * Stuff for restoring after a sleep. */ @@ -263,7 +263,7 @@ struct pmu_sleep_notifier awacs_sleep_notifier = { awacs_sleep_notify, SLEEP_LEVEL_SOUND, }; -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PM */ /* for (soft) sample rate translations */ int expand_bal; /* Balance factor for expanding (not volume!) */ @@ -679,7 +679,7 @@ kfree(beep_dbdma_cmd_space); if (beep_buf) kfree(beep_buf); -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM pmu_unregister_sleep_notifier(&awacs_sleep_notifier); #endif } @@ -1419,7 +1419,7 @@ } } -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM /* * Save state when going to sleep, restore it afterwards. */ @@ -1555,7 +1555,7 @@ } return PBOOK_SLEEP_OK; } -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PM */ /* All the burgundy functions: */ @@ -3059,9 +3059,9 @@ if ((res=setup_beep())) return res ; -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM pmu_register_sleep_notifier(&awacs_sleep_notifier); -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PM */ /* Powerbooks have odd ways of enabling inputs such as an expansion-bay CD or sound from an internal modem Index: linux-work/sound/ppc/awacs.c =================================================================== --- linux-work.orig/sound/ppc/awacs.c 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/sound/ppc/awacs.c 2005-05-30 10:52:32.000000000 +1000 @@ -90,7 +90,7 @@ snd_pmac_awacs_write(chip, val | (reg << 12)); } -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM /* Recalibrate chip */ static void screamer_recalibrate(pmac_t *chip) { @@ -642,7 +642,7 @@ } } -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM static void snd_pmac_awacs_suspend(pmac_t *chip) { snd_pmac_awacs_write_noreg(chip, 1, (chip->awacs_reg[1] @@ -676,7 +676,7 @@ } #endif } -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PM */ #ifdef PMAC_SUPPORT_AUTOMUTE /* @@ -883,7 +883,7 @@ * set lowlevel callbacks */ chip->set_format = snd_pmac_awacs_set_format; -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM chip->suspend = snd_pmac_awacs_suspend; chip->resume = snd_pmac_awacs_resume; #endif Index: linux-work/sound/ppc/daca.c =================================================================== --- linux-work.orig/sound/ppc/daca.c 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/sound/ppc/daca.c 2005-05-30 10:52:32.000000000 +1000 @@ -218,7 +218,7 @@ }; -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM static void daca_resume(pmac_t *chip) { pmac_daca_t *mix = chip->mixer_data; @@ -227,7 +227,7 @@ mix->amp_on ? 0x05 : 0x04); daca_set_volume(mix); } -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PM */ static void daca_cleanup(pmac_t *chip) @@ -275,7 +275,7 @@ return err; } -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM chip->resume = daca_resume; #endif Index: linux-work/sound/ppc/pmac.c =================================================================== --- linux-work.orig/sound/ppc/pmac.c 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/sound/ppc/pmac.c 2005-05-30 10:52:32.000000000 +1000 @@ -36,7 +36,7 @@ #include -#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) +#ifdef CONFIG_PM static int snd_pmac_register_sleep_notifier(pmac_t *chip); static int snd_pmac_unregister_sleep_notifier(pmac_t *chip); static int snd_pmac_suspend(snd_card_t *card, pm_message_t state); @@ -782,7 +782,7 @@ } snd_pmac_sound_feature(chip, 0); -#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) +#ifdef CONFIG_PM snd_pmac_unregister_sleep_notifier(chip); #endif @@ -1292,7 +1292,7 @@ /* Reset dbdma channels */ snd_pmac_dbdma_reset(chip); -#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) +#ifdef CONFIG_PM /* add sleep notifier */ if (! snd_pmac_register_sleep_notifier(chip)) snd_card_set_pm_callback(chip->card, snd_pmac_suspend, snd_pmac_resume, chip); @@ -1316,7 +1316,7 @@ * sleep notify for powerbook */ -#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) +#ifdef CONFIG_PM /* * Save state when going to sleep, restore it afterwards. @@ -1414,4 +1414,5 @@ return 0; } -#endif /* CONFIG_PM && CONFIG_PMAC_PBOOK */ +#endif /* CONFIG_PM */ + Index: linux-work/sound/ppc/pmac.h =================================================================== --- linux-work.orig/sound/ppc/pmac.h 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/sound/ppc/pmac.h 2005-05-30 10:52:32.000000000 +1000 @@ -167,7 +167,7 @@ void (*set_format)(pmac_t *chip); void (*update_automute)(pmac_t *chip, int do_notify); int (*detect_headphone)(pmac_t *chip); -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM void (*suspend)(pmac_t *chip); void (*resume)(pmac_t *chip); #endif Index: linux-work/sound/ppc/tumbler.c =================================================================== --- linux-work.orig/sound/ppc/tumbler.c 2005-05-30 10:52:26.000000000 +1000 +++ linux-work/sound/ppc/tumbler.c 2005-05-30 10:52:32.000000000 +1000 @@ -1128,7 +1128,7 @@ } } -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM /* suspend mixer */ static void tumbler_suspend(pmac_t *chip) { @@ -1370,7 +1370,7 @@ if ((err = snd_ctl_add(chip->card, chip->drc_sw_ctl)) < 0) return err; -#ifdef CONFIG_PMAC_PBOOK +#ifdef CONFIG_PM chip->suspend = tumbler_suspend; chip->resume = tumbler_resume; #endif Index: linux-work/kernel/power/swsusp.c =================================================================== --- linux-work.orig/kernel/power/swsusp.c 2005-05-02 10:50:34.000000000 +1000 +++ linux-work/kernel/power/swsusp.c 2005-05-30 13:08:19.000000000 +1000 @@ -730,10 +730,13 @@ void swsusp_free(void) { + if (pagedir_save == NULL) + return; BUG_ON(PageNosave(virt_to_page(pagedir_save))); BUG_ON(PageNosaveFree(virt_to_page(pagedir_save))); free_image_pages(); free_pagedir(pagedir_save); + pagedir_save = NULL; }