]> err.no Git - linux-2.6/blobdiff - drivers/macintosh/via-pmu.c
Merge branch 'for-neil' of git://git.kernel.org/pub/scm/linux/kernel/git/djbw/md...
[linux-2.6] / drivers / macintosh / via-pmu.c
index 0e233185ad1483d93b7f10d910ca2e1c741c69bc..d6365a9f0637407f020b901423dd1d06d851edeb 100644 (file)
  *
  * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi.
  * Copyright (C) 2001-2002 Benjamin Herrenschmidt
+ * Copyright (C) 2006-2007 Johannes Berg
  *
  * 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
- *  - Save/Restore PCI space properly
  *
  */
 #include <stdarg.h>
@@ -64,7 +62,7 @@
 #include "via-pmu-event.h"
 
 /* Some compile options */
-#define DEBUG_SLEEP
+#undef DEBUG_SLEEP
 
 /* Misc minor number allocated for /dev/pmu */
 #define PMU_MINOR              154
@@ -149,12 +147,9 @@ static spinlock_t pmu_lock;
 static u8 pmu_intr_mask;
 static int pmu_version;
 static int drop_interrupts;
-#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
+#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
 static int option_lid_wakeup = 1;
-#endif /* CONFIG_PM_SLEEP && CONFIG_PPC32 */
-#if (defined(CONFIG_PM_SLEEP)&&defined(CONFIG_PPC32))||defined(CONFIG_PMAC_BACKLIGHT_LEGACY)
-static int sleep_in_progress;
-#endif
+#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */
 static unsigned long async_req_locks;
 static unsigned int pmu_irq_stats[11];
 
@@ -202,12 +197,6 @@ static int proc_read_options(char *page, char **start, off_t off,
 static int proc_write_options(struct file *file, const char __user *buffer,
                        unsigned long count, void *data);
 
-#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
-static void powerbook_sleep_init_3400(void);
-#else
-#define powerbook_sleep_init_3400()    do { } while (0)
-#endif
-
 #ifdef CONFIG_ADB
 struct adb_driver via_pmu_driver = {
        "PMU",
@@ -226,7 +215,7 @@ extern void enable_kernel_fp(void);
 
 #ifdef DEBUG_SLEEP
 int pmu_polled_request(struct adb_request *req);
-int pmu_wink(struct adb_request *req);
+void pmu_blink(int n);
 #endif
 
 /*
@@ -455,10 +444,6 @@ static int __init via_pmu_start(void)
                pmu_poll();
        } while (pmu_state != idle);
 
-       /* Do allocations and ioremaps that will be needed for sleep */
-       if (pmu_kind == PMU_OHARE_BASED)
-               powerbook_sleep_init_3400();
-
        return 0;
 }
 
@@ -881,7 +866,7 @@ proc_read_options(char *page, char **start, off_t off,
 {
        char *p = page;
 
-#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
+#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
        if (pmu_kind == PMU_KEYLARGO_BASED &&
            pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0)
                p += sprintf(p, "lid_wakeup=%d\n", option_lid_wakeup);
@@ -922,7 +907,7 @@ proc_write_options(struct file *file, const char __user *buffer,
        *(val++) = 0;
        while(*val == ' ')
                val++;
-#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
+#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
        if (pmu_kind == PMU_KEYLARGO_BASED &&
            pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0)
                if (!strcmp(label, "lid_wakeup"))
@@ -1728,44 +1713,7 @@ pmu_present(void)
        return via != 0;
 }
 
-#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
-#ifdef DEBUG_SLEEP
-/* N.B. This doesn't work on the 3400 */
-void 
-pmu_blink(int n)
-{
-       struct adb_request req;
-
-       memset(&req, 0, sizeof(req));
-
-       for (; n > 0; --n) {
-               req.nbytes = 4;
-               req.done = NULL;
-               req.data[0] = 0xee;
-               req.data[1] = 4;
-               req.data[2] = 0;
-               req.data[3] = 1;
-               req.reply[0] = ADB_RET_OK;
-               req.reply_len = 1;
-               req.reply_expected = 0;
-               pmu_polled_request(&req);
-               mdelay(50);
-               req.nbytes = 4;
-               req.done = NULL;
-               req.data[0] = 0xee;
-               req.data[1] = 4;
-               req.data[2] = 0;
-               req.data[3] = 0;
-               req.reply[0] = ADB_RET_OK;
-               req.reply_len = 1;
-               req.reply_expected = 0;
-               pmu_polled_request(&req);
-               mdelay(50);
-       }
-       mdelay(50);
-}
-#endif
-
+#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
 /*
  * Put the powerbook to sleep.
  */
@@ -1800,124 +1748,6 @@ restore_via_state(void)
        out_8(&via[IER], IER_SET | SR_INT | CB1_INT);
 }
 
-extern void pmu_backlight_set_sleep(int sleep);
-
-static int
-pmac_suspend_devices(void)
-{
-       int ret;
-
-       pm_prepare_console();
-       
-       /* 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();
-
-       /* Send suspend call to devices, hold the device core's dpm_sem */
-       ret = device_suspend(PMSG_SUSPEND);
-       if (ret) {
-               printk(KERN_ERR "Driver sleep failed\n");
-               return -EBUSY;
-       }
-
-#ifdef CONFIG_PMAC_BACKLIGHT
-       /* Tell backlight code not to muck around with the chip anymore */
-       pmu_backlight_set_sleep(1);
-#endif
-
-       /* Call platform functions marked "on sleep" */
-       pmac_pfunc_i2c_suspend();
-       pmac_pfunc_base_suspend();
-
-       /* 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();
-               printk(KERN_ERR "Driver powerdown failed\n");
-               return -EBUSY;
-       }
-
-       /* Wait for completion of async requests */
-       while (!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_wakeup_devices(void)
-{
-       mdelay(100);
-
-#ifdef CONFIG_PMAC_BACKLIGHT
-       /* Tell backlight code it can use the chip again */
-       pmu_backlight_set_sleep(0);
-#endif
-
-       /* 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);
-
-       /* Restart jiffies & scheduling */
-       wakeup_decrementer();
-
-       /* Re-enable local CPU interrupts */
-       local_irq_enable();
-       mdelay(10);
-       preempt_enable();
-
-       /* Call platform functions marked "on wake" */
-       pmac_pfunc_base_resume();
-       pmac_pfunc_i2c_resume();
-
-       /* Resume devices */
-       device_resume();
-
-       pm_restore_console();
-
-       return 0;
-}
-
 #define        GRACKLE_PM      (1<<7)
 #define GRACKLE_DOZE   (1<<5)
 #define        GRACKLE_NAP     (1<<4)
@@ -1928,19 +1758,12 @@ static int powerbook_sleep_grackle(void)
        unsigned long save_l2cr;
        unsigned short pmcr1;
        struct adb_request req;
-       int ret;
        struct pci_dev *grackle;
 
        grackle = pci_get_bus_and_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);
@@ -2003,8 +1826,6 @@ static int powerbook_sleep_grackle(void)
                        PMU_POW_ON|PMU_POW_BACKLIGHT|PMU_POW_CHARGER|PMU_POW_IRLED|PMU_POW_MEDIABAY);
        pmu_wait_complete(&req);
 
-       pmac_wakeup_devices();
-
        return 0;
 }
 
@@ -2014,7 +1835,6 @@ 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");
@@ -2024,12 +1844,6 @@ powerbook_sleep_Core99(void)
        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);
@@ -2100,8 +1914,6 @@ powerbook_sleep_Core99(void)
        /* Restore LPJ, cpufreq will adjust the cpu frequency */
        loops_per_jiffy /= 2;
 
-       pmac_wakeup_devices();
-
        return 0;
 }
 
@@ -2120,7 +1932,7 @@ static void powerbook_sleep_init_3400(void)
 
 static int powerbook_sleep_3400(void)
 {
-       int ret, i, x;
+       int i, x;
        unsigned int hid0;
        unsigned long msr;
        struct adb_request sleep_req;
@@ -2130,12 +1942,6 @@ static int powerbook_sleep_3400(void)
                return -ENOMEM;
        mem_ctrl_sleep = pb3400_mem_ctrl + PB3400_MEM_CTRL_SLEEP;
 
-       ret = pmac_suspend_devices();
-       if (ret) {
-               printk(KERN_ERR "Sleep rejected by devices\n");
-               return ret;
-       }
-
        /* Set the memory controller to keep the memory refreshed
           while we're asleep */
        for (i = 0x403f; i >= 0x4000; --i) {
@@ -2173,12 +1979,10 @@ static int powerbook_sleep_3400(void)
        out_be32(mem_ctrl_sleep, 0x3f);
        pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 0);
 
-       pmac_wakeup_devices();
-
        return 0;
 }
 
-#endif /* CONFIG_PM_SLEEP && CONFIG_PPC32 */
+#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */
 
 /*
  * Support for /dev/pmu device
@@ -2351,6 +2155,93 @@ pmu_release(struct inode *inode, struct file *file)
        return 0;
 }
 
+#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
+static void pmac_suspend_disable_irqs(void)
+{
+       /* Call platform functions marked "on sleep" */
+       pmac_pfunc_i2c_suspend();
+       pmac_pfunc_base_suspend();
+}
+
+static int powerbook_sleep(suspend_state_t state)
+{
+       int error = 0;
+
+       /* Wait for completion of async requests */
+       while (!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 */
+
+       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:
+               return -ENOSYS;
+       }
+
+       if (error)
+               return error;
+
+       mdelay(100);
+
+       return 0;
+}
+
+static void pmac_suspend_enable_irqs(void)
+{
+       /* Force a poll of ADB interrupts */
+       adb_int_pending = 1;
+       via_pmu_interrupt(0, NULL);
+
+       mdelay(10);
+
+       /* Call platform functions marked "on wake" */
+       pmac_pfunc_base_resume();
+       pmac_pfunc_i2c_resume();
+}
+
+static int pmu_sleep_valid(suspend_state_t state)
+{
+       return state == PM_SUSPEND_MEM
+               && (pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, -1) >= 0);
+}
+
+static struct platform_suspend_ops pmu_pm_ops = {
+       .enter = powerbook_sleep,
+       .valid = pmu_sleep_valid,
+};
+
+static int register_pmu_pm_ops(void)
+{
+       if (pmu_kind == PMU_OHARE_BASED)
+               powerbook_sleep_init_3400();
+       ppc_md.suspend_disable_irqs = pmac_suspend_disable_irqs;
+       ppc_md.suspend_enable_irqs = pmac_suspend_enable_irqs;
+       suspend_set_ops(&pmu_pm_ops);
+
+       return 0;
+}
+
+device_initcall(register_pmu_pm_ops);
+#endif
+
 static int
 pmu_ioctl(struct inode * inode, struct file *filp,
                     u_int cmd, u_long arg)
@@ -2359,35 +2250,15 @@ pmu_ioctl(struct inode * inode, struct file *filp,
        int error = -EINVAL;
 
        switch (cmd) {
-#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
        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;
-               break;
+               return pm_suspend(PM_SUSPEND_MEM);
        case PMU_IOC_CAN_SLEEP:
-               if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) < 0)
+               if (pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, -1) < 0)
                        return put_user(0, argp);
                else
                        return put_user(1, argp);
-#endif /* CONFIG_PM_SLEEP && CONFIG_PPC32 */
 
 #ifdef CONFIG_PMAC_BACKLIGHT_LEGACY
        /* Compatibility ioctl's for backlight */
@@ -2395,9 +2266,6 @@ pmu_ioctl(struct inode * inode, struct file *filp,
        {
                int brightness;
 
-               if (sleep_in_progress)
-                       return -EBUSY;
-
                brightness = pmac_backlight_get_legacy_brightness();
                if (brightness < 0)
                        return brightness;
@@ -2409,9 +2277,6 @@ pmu_ioctl(struct inode * inode, struct file *filp,
        {
                int brightness;
 
-               if (sleep_in_progress)
-                       return -EBUSY;
-
                error = get_user(brightness, argp);
                if (error)
                        return error;
@@ -2536,15 +2401,43 @@ pmu_polled_request(struct adb_request *req)
        local_irq_restore(flags);
        return 0;
 }
-#endif /* DEBUG_SLEEP */
 
+/* N.B. This doesn't work on the 3400 */
+void pmu_blink(int n)
+{
+       struct adb_request req;
 
-/* FIXME: This is a temporary set of callbacks to enable us
- * to do suspend-to-disk.
- */
+       memset(&req, 0, sizeof(req));
 
-#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
+       for (; n > 0; --n) {
+               req.nbytes = 4;
+               req.done = NULL;
+               req.data[0] = 0xee;
+               req.data[1] = 4;
+               req.data[2] = 0;
+               req.data[3] = 1;
+               req.reply[0] = ADB_RET_OK;
+               req.reply_len = 1;
+               req.reply_expected = 0;
+               pmu_polled_request(&req);
+               mdelay(50);
+               req.nbytes = 4;
+               req.done = NULL;
+               req.data[0] = 0xee;
+               req.data[1] = 4;
+               req.data[2] = 0;
+               req.data[3] = 0;
+               req.reply[0] = ADB_RET_OK;
+               req.reply_len = 1;
+               req.reply_expected = 0;
+               pmu_polled_request(&req);
+               mdelay(50);
+       }
+       mdelay(50);
+}
+#endif /* DEBUG_SLEEP */
 
+#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
 int pmu_sys_suspended;
 
 static int pmu_sys_suspend(struct sys_device *sysdev, pm_message_t state)
@@ -2552,10 +2445,15 @@ static int pmu_sys_suspend(struct sys_device *sysdev, pm_message_t state)
        if (state.event != PM_EVENT_SUSPEND || pmu_sys_suspended)
                return 0;
 
-       /* Suspend PMU event interrupts */
+       /* Suspend PMU event interrupts */\
        pmu_suspend();
-
        pmu_sys_suspended = 1;
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+       /* Tell backlight code not to muck around with the chip anymore */
+       pmu_backlight_set_sleep(1);
+#endif
+
        return 0;
 }
 
@@ -2570,18 +2468,21 @@ static int pmu_sys_resume(struct sys_device *sysdev)
        pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
        pmu_wait_complete(&req);
 
+#ifdef CONFIG_PMAC_BACKLIGHT
+       /* Tell backlight code it can use the chip again */
+       pmu_backlight_set_sleep(0);
+#endif
        /* Resume PMU event interrupts */
        pmu_resume();
-
        pmu_sys_suspended = 0;
 
        return 0;
 }
 
-#endif /* CONFIG_PM_SLEEP && CONFIG_PPC32 */
+#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */
 
 static struct sysdev_class pmu_sysclass = {
-       set_kset_name("pmu"),
+       .name = "pmu",
 };
 
 static struct sys_device device_pmu = {
@@ -2589,10 +2490,10 @@ static struct sys_device device_pmu = {
 };
 
 static struct sysdev_driver driver_pmu = {
-#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
+#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
        .suspend        = &pmu_sys_suspend,
        .resume         = &pmu_sys_resume,
-#endif /* CONFIG_PM_SLEEP && CONFIG_PPC32 */
+#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */
 };
 
 static int __init init_pmu_sysfs(void)
@@ -2627,10 +2528,10 @@ EXPORT_SYMBOL(pmu_wait_complete);
 EXPORT_SYMBOL(pmu_suspend);
 EXPORT_SYMBOL(pmu_resume);
 EXPORT_SYMBOL(pmu_unlock);
-#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
+#if defined(CONFIG_PPC32)
 EXPORT_SYMBOL(pmu_enable_irled);
 EXPORT_SYMBOL(pmu_battery_count);
 EXPORT_SYMBOL(pmu_batteries);
 EXPORT_SYMBOL(pmu_power_flags);
-#endif /* CONFIG_PM_SLEEP && CONFIG_PPC32 */
+#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */