[POWERPC] powermac: Use generic suspend code
This adds platform_suspend_ops for PMU based machines, directly in the PMU driver. This allows suspending via /sys/power/state on powerbooks. The patch also replaces the PMU ioctl with a simple call to pm_suspend(PM_SUSPEND_MEM). Additionally, it cleans up some debug code. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
This commit is contained in:
committed by
Paul Mackerras
parent
887ef35ae4
commit
f91266edba
@@ -10,13 +10,11 @@
|
|||||||
*
|
*
|
||||||
* Copyright (C) 1998 Paul Mackerras and Fabio Riccardi.
|
* Copyright (C) 1998 Paul Mackerras and Fabio Riccardi.
|
||||||
* Copyright (C) 2001-2002 Benjamin Herrenschmidt
|
* Copyright (C) 2001-2002 Benjamin Herrenschmidt
|
||||||
|
* Copyright (C) 2006-2007 Johannes Berg
|
||||||
*
|
*
|
||||||
* THIS DRIVER IS BECOMING A TOTAL MESS !
|
* THIS DRIVER IS BECOMING A TOTAL MESS !
|
||||||
* - Cleanup atomically disabling reply to PMU events after
|
* - Cleanup atomically disabling reply to PMU events after
|
||||||
* a sleep or a freq. switch
|
* 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>
|
#include <stdarg.h>
|
||||||
@@ -64,7 +62,7 @@
|
|||||||
#include "via-pmu-event.h"
|
#include "via-pmu-event.h"
|
||||||
|
|
||||||
/* Some compile options */
|
/* Some compile options */
|
||||||
#define DEBUG_SLEEP
|
#undef DEBUG_SLEEP
|
||||||
|
|
||||||
/* Misc minor number allocated for /dev/pmu */
|
/* Misc minor number allocated for /dev/pmu */
|
||||||
#define PMU_MINOR 154
|
#define PMU_MINOR 154
|
||||||
@@ -149,12 +147,9 @@ static spinlock_t pmu_lock;
|
|||||||
static u8 pmu_intr_mask;
|
static u8 pmu_intr_mask;
|
||||||
static int pmu_version;
|
static int pmu_version;
|
||||||
static int drop_interrupts;
|
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;
|
static int option_lid_wakeup = 1;
|
||||||
#endif /* CONFIG_PM_SLEEP && CONFIG_PPC32 */
|
#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */
|
||||||
#if (defined(CONFIG_PM_SLEEP)&&defined(CONFIG_PPC32))||defined(CONFIG_PMAC_BACKLIGHT_LEGACY)
|
|
||||||
static int sleep_in_progress;
|
|
||||||
#endif
|
|
||||||
static unsigned long async_req_locks;
|
static unsigned long async_req_locks;
|
||||||
static unsigned int pmu_irq_stats[11];
|
static unsigned int pmu_irq_stats[11];
|
||||||
|
|
||||||
@@ -226,7 +221,7 @@ extern void enable_kernel_fp(void);
|
|||||||
|
|
||||||
#ifdef DEBUG_SLEEP
|
#ifdef DEBUG_SLEEP
|
||||||
int pmu_polled_request(struct adb_request *req);
|
int pmu_polled_request(struct adb_request *req);
|
||||||
int pmu_wink(struct adb_request *req);
|
void pmu_blink(int n);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -881,7 +876,7 @@ proc_read_options(char *page, char **start, off_t off,
|
|||||||
{
|
{
|
||||||
char *p = page;
|
char *p = page;
|
||||||
|
|
||||||
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
|
#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
|
||||||
if (pmu_kind == PMU_KEYLARGO_BASED &&
|
if (pmu_kind == PMU_KEYLARGO_BASED &&
|
||||||
pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0)
|
pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0)
|
||||||
p += sprintf(p, "lid_wakeup=%d\n", option_lid_wakeup);
|
p += sprintf(p, "lid_wakeup=%d\n", option_lid_wakeup);
|
||||||
@@ -922,7 +917,7 @@ proc_write_options(struct file *file, const char __user *buffer,
|
|||||||
*(val++) = 0;
|
*(val++) = 0;
|
||||||
while(*val == ' ')
|
while(*val == ' ')
|
||||||
val++;
|
val++;
|
||||||
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
|
#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
|
||||||
if (pmu_kind == PMU_KEYLARGO_BASED &&
|
if (pmu_kind == PMU_KEYLARGO_BASED &&
|
||||||
pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0)
|
pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0)
|
||||||
if (!strcmp(label, "lid_wakeup"))
|
if (!strcmp(label, "lid_wakeup"))
|
||||||
@@ -1728,44 +1723,7 @@ pmu_present(void)
|
|||||||
return via != 0;
|
return via != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
|
#if defined(CONFIG_SUSPEND) && 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
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Put the powerbook to sleep.
|
* Put the powerbook to sleep.
|
||||||
*/
|
*/
|
||||||
@@ -1802,122 +1760,6 @@ restore_via_state(void)
|
|||||||
|
|
||||||
extern void pmu_backlight_set_sleep(int sleep);
|
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_PM (1<<7)
|
||||||
#define GRACKLE_DOZE (1<<5)
|
#define GRACKLE_DOZE (1<<5)
|
||||||
#define GRACKLE_NAP (1<<4)
|
#define GRACKLE_NAP (1<<4)
|
||||||
@@ -1928,19 +1770,12 @@ static int powerbook_sleep_grackle(void)
|
|||||||
unsigned long save_l2cr;
|
unsigned long save_l2cr;
|
||||||
unsigned short pmcr1;
|
unsigned short pmcr1;
|
||||||
struct adb_request req;
|
struct adb_request req;
|
||||||
int ret;
|
|
||||||
struct pci_dev *grackle;
|
struct pci_dev *grackle;
|
||||||
|
|
||||||
grackle = pci_get_bus_and_slot(0, 0);
|
grackle = pci_get_bus_and_slot(0, 0);
|
||||||
if (!grackle)
|
if (!grackle)
|
||||||
return -ENODEV;
|
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... */
|
/* 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_request(&req, NULL, 2, PMU_POWER_CTRL0, PMU_POW0_OFF|PMU_POW0_HARD_DRIVE);
|
||||||
pmu_wait_complete(&req);
|
pmu_wait_complete(&req);
|
||||||
@@ -2003,8 +1838,6 @@ static int powerbook_sleep_grackle(void)
|
|||||||
PMU_POW_ON|PMU_POW_BACKLIGHT|PMU_POW_CHARGER|PMU_POW_IRLED|PMU_POW_MEDIABAY);
|
PMU_POW_ON|PMU_POW_BACKLIGHT|PMU_POW_CHARGER|PMU_POW_IRLED|PMU_POW_MEDIABAY);
|
||||||
pmu_wait_complete(&req);
|
pmu_wait_complete(&req);
|
||||||
|
|
||||||
pmac_wakeup_devices();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2014,7 +1847,6 @@ powerbook_sleep_Core99(void)
|
|||||||
unsigned long save_l2cr;
|
unsigned long save_l2cr;
|
||||||
unsigned long save_l3cr;
|
unsigned long save_l3cr;
|
||||||
struct adb_request req;
|
struct adb_request req;
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) < 0) {
|
if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) < 0) {
|
||||||
printk(KERN_ERR "Sleep mode not supported on this machine\n");
|
printk(KERN_ERR "Sleep mode not supported on this machine\n");
|
||||||
@@ -2024,12 +1856,6 @@ powerbook_sleep_Core99(void)
|
|||||||
if (num_online_cpus() > 1 || cpu_is_offline(0))
|
if (num_online_cpus() > 1 || cpu_is_offline(0))
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
|
||||||
ret = pmac_suspend_devices();
|
|
||||||
if (ret) {
|
|
||||||
printk(KERN_ERR "Sleep rejected by devices\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop environment and ADB interrupts */
|
/* Stop environment and ADB interrupts */
|
||||||
pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0);
|
pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0);
|
||||||
pmu_wait_complete(&req);
|
pmu_wait_complete(&req);
|
||||||
@@ -2100,8 +1926,6 @@ powerbook_sleep_Core99(void)
|
|||||||
/* Restore LPJ, cpufreq will adjust the cpu frequency */
|
/* Restore LPJ, cpufreq will adjust the cpu frequency */
|
||||||
loops_per_jiffy /= 2;
|
loops_per_jiffy /= 2;
|
||||||
|
|
||||||
pmac_wakeup_devices();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2120,7 +1944,7 @@ static void powerbook_sleep_init_3400(void)
|
|||||||
|
|
||||||
static int powerbook_sleep_3400(void)
|
static int powerbook_sleep_3400(void)
|
||||||
{
|
{
|
||||||
int ret, i, x;
|
int i, x;
|
||||||
unsigned int hid0;
|
unsigned int hid0;
|
||||||
unsigned long msr;
|
unsigned long msr;
|
||||||
struct adb_request sleep_req;
|
struct adb_request sleep_req;
|
||||||
@@ -2130,12 +1954,6 @@ static int powerbook_sleep_3400(void)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
mem_ctrl_sleep = pb3400_mem_ctrl + PB3400_MEM_CTRL_SLEEP;
|
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
|
/* Set the memory controller to keep the memory refreshed
|
||||||
while we're asleep */
|
while we're asleep */
|
||||||
for (i = 0x403f; i >= 0x4000; --i) {
|
for (i = 0x403f; i >= 0x4000; --i) {
|
||||||
@@ -2173,12 +1991,10 @@ static int powerbook_sleep_3400(void)
|
|||||||
out_be32(mem_ctrl_sleep, 0x3f);
|
out_be32(mem_ctrl_sleep, 0x3f);
|
||||||
pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 0);
|
pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 0);
|
||||||
|
|
||||||
pmac_wakeup_devices();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_PM_SLEEP && CONFIG_PPC32 */
|
#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Support for /dev/pmu device
|
* Support for /dev/pmu device
|
||||||
@@ -2351,21 +2167,55 @@ pmu_release(struct inode *inode, struct file *file)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
|
||||||
pmu_ioctl(struct inode * inode, struct file *filp,
|
/*
|
||||||
u_int cmd, u_long arg)
|
* overrides the weak arch_suspend_disable_irqs in kernel/power/main.c
|
||||||
|
*
|
||||||
|
* XXX: Once Scott Wood's patch is merged, this needs to use the ppc_md
|
||||||
|
* hooks that patch adds!
|
||||||
|
*/
|
||||||
|
void arch_suspend_disable_irqs(void)
|
||||||
{
|
{
|
||||||
__u32 __user *argp = (__u32 __user *)arg;
|
#ifdef CONFIG_PMAC_BACKLIGHT
|
||||||
int error = -EINVAL;
|
/* 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));
|
||||||
|
|
||||||
|
local_irq_disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (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) {
|
switch (pmu_kind) {
|
||||||
case PMU_OHARE_BASED:
|
case PMU_OHARE_BASED:
|
||||||
error = powerbook_sleep_3400();
|
error = powerbook_sleep_3400();
|
||||||
@@ -2378,16 +2228,85 @@ pmu_ioctl(struct inode * inode, struct file *filp,
|
|||||||
error = powerbook_sleep_Core99();
|
error = powerbook_sleep_Core99();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error = -ENOSYS;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
sleep_in_progress = 0;
|
|
||||||
break;
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
mdelay(100);
|
||||||
|
|
||||||
|
#ifdef CONFIG_PMAC_BACKLIGHT
|
||||||
|
/* Tell backlight code it can use the chip again */
|
||||||
|
pmu_backlight_set_sleep(0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* overrides the weak arch_suspend_enable_irqs in kernel/power/main.c
|
||||||
|
*
|
||||||
|
* XXX: Once Scott Wood's patch is merged, this needs to use the ppc_md
|
||||||
|
* hooks that patch adds!
|
||||||
|
*/
|
||||||
|
void arch_suspend_enable_irqs(void)
|
||||||
|
{
|
||||||
|
/* 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
__u32 __user *argp = (__u32 __user *)arg;
|
||||||
|
int error = -EINVAL;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case PMU_IOC_SLEEP:
|
||||||
|
if (!capable(CAP_SYS_ADMIN))
|
||||||
|
return -EACCES;
|
||||||
|
return pm_suspend(PM_SUSPEND_MEM);
|
||||||
case PMU_IOC_CAN_SLEEP:
|
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);
|
return put_user(0, argp);
|
||||||
else
|
else
|
||||||
return put_user(1, argp);
|
return put_user(1, argp);
|
||||||
#endif /* CONFIG_PM_SLEEP && CONFIG_PPC32 */
|
|
||||||
|
|
||||||
#ifdef CONFIG_PMAC_BACKLIGHT_LEGACY
|
#ifdef CONFIG_PMAC_BACKLIGHT_LEGACY
|
||||||
/* Compatibility ioctl's for backlight */
|
/* Compatibility ioctl's for backlight */
|
||||||
@@ -2395,9 +2314,6 @@ pmu_ioctl(struct inode * inode, struct file *filp,
|
|||||||
{
|
{
|
||||||
int brightness;
|
int brightness;
|
||||||
|
|
||||||
if (sleep_in_progress)
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
brightness = pmac_backlight_get_legacy_brightness();
|
brightness = pmac_backlight_get_legacy_brightness();
|
||||||
if (brightness < 0)
|
if (brightness < 0)
|
||||||
return brightness;
|
return brightness;
|
||||||
@@ -2409,9 +2325,6 @@ pmu_ioctl(struct inode * inode, struct file *filp,
|
|||||||
{
|
{
|
||||||
int brightness;
|
int brightness;
|
||||||
|
|
||||||
if (sleep_in_progress)
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
error = get_user(brightness, argp);
|
error = get_user(brightness, argp);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
@@ -2536,15 +2449,43 @@ pmu_polled_request(struct adb_request *req)
|
|||||||
local_irq_restore(flags);
|
local_irq_restore(flags);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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 /* DEBUG_SLEEP */
|
#endif /* DEBUG_SLEEP */
|
||||||
|
|
||||||
|
#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
|
||||||
/* FIXME: This is a temporary set of callbacks to enable us
|
|
||||||
* to do suspend-to-disk.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
|
|
||||||
|
|
||||||
int pmu_sys_suspended;
|
int pmu_sys_suspended;
|
||||||
|
|
||||||
static int pmu_sys_suspend(struct sys_device *sysdev, pm_message_t state)
|
static int pmu_sys_suspend(struct sys_device *sysdev, pm_message_t state)
|
||||||
@@ -2578,7 +2519,7 @@ static int pmu_sys_resume(struct sys_device *sysdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_PM_SLEEP && CONFIG_PPC32 */
|
#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */
|
||||||
|
|
||||||
static struct sysdev_class pmu_sysclass = {
|
static struct sysdev_class pmu_sysclass = {
|
||||||
set_kset_name("pmu"),
|
set_kset_name("pmu"),
|
||||||
@@ -2589,10 +2530,10 @@ static struct sys_device device_pmu = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static struct sysdev_driver driver_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,
|
.suspend = &pmu_sys_suspend,
|
||||||
.resume = &pmu_sys_resume,
|
.resume = &pmu_sys_resume,
|
||||||
#endif /* CONFIG_PM_SLEEP && CONFIG_PPC32 */
|
#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init init_pmu_sysfs(void)
|
static int __init init_pmu_sysfs(void)
|
||||||
@@ -2627,10 +2568,10 @@ EXPORT_SYMBOL(pmu_wait_complete);
|
|||||||
EXPORT_SYMBOL(pmu_suspend);
|
EXPORT_SYMBOL(pmu_suspend);
|
||||||
EXPORT_SYMBOL(pmu_resume);
|
EXPORT_SYMBOL(pmu_resume);
|
||||||
EXPORT_SYMBOL(pmu_unlock);
|
EXPORT_SYMBOL(pmu_unlock);
|
||||||
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)
|
#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
|
||||||
EXPORT_SYMBOL(pmu_enable_irled);
|
EXPORT_SYMBOL(pmu_enable_irled);
|
||||||
EXPORT_SYMBOL(pmu_battery_count);
|
EXPORT_SYMBOL(pmu_battery_count);
|
||||||
EXPORT_SYMBOL(pmu_batteries);
|
EXPORT_SYMBOL(pmu_batteries);
|
||||||
EXPORT_SYMBOL(pmu_power_flags);
|
EXPORT_SYMBOL(pmu_power_flags);
|
||||||
#endif /* CONFIG_PM_SLEEP && CONFIG_PPC32 */
|
#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user