diff options
Diffstat (limited to 'drivers/macintosh/via-pmu.c')
| -rw-r--r-- | drivers/macintosh/via-pmu.c | 1277 |
1 files changed, 477 insertions, 800 deletions
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index 6eb93e45fcd..dee88e59f0d 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -10,18 +10,15 @@ * * 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 - * - Move backlight code out as well - * - Save/Restore PCI space properly * */ #include <stdarg.h> -#include <linux/config.h> +#include <linux/mutex.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/kernel.h> @@ -35,23 +32,26 @@ #include <linux/adb.h> #include <linux/pmu.h> #include <linux/cuda.h> -#include <linux/smp_lock.h> #include <linux/module.h> #include <linux/spinlock.h> #include <linux/pm.h> #include <linux/proc_fs.h> +#include <linux/seq_file.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/device.h> -#include <linux/sysdev.h> -#include <linux/suspend.h> +#include <linux/syscore_ops.h> +#include <linux/freezer.h> #include <linux/syscalls.h> +#include <linux/suspend.h> #include <linux/cpu.h> +#include <linux/compat.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <asm/prom.h> #include <asm/machdep.h> #include <asm/io.h> #include <asm/pgtable.h> -#include <asm/system.h> #include <asm/sections.h> #include <asm/irq.h> #include <asm/pmac_feature.h> @@ -61,18 +61,12 @@ #include <asm/mmu_context.h> #include <asm/cputable.h> #include <asm/time.h> -#ifdef CONFIG_PMAC_BACKLIGHT #include <asm/backlight.h> -#endif -#ifdef CONFIG_PPC32 -#include <asm/open_pic.h> -#endif +#include "via-pmu-event.h" /* Some compile options */ -#undef SUSPEND_USES_PMU -#define DEBUG_SLEEP -#undef HACKED_PCI_SAVE +#undef DEBUG_SLEEP /* Misc minor number allocated for /dev/pmu */ #define PMU_MINOR 154 @@ -80,6 +74,7 @@ /* How many iterations between battery polls */ #define BATTERY_POLLING_COUNT 2 +static DEFINE_MUTEX(pmu_info_proc_mutex); static volatile unsigned char __iomem *via; /* VIA registers - spaced 0x200 bytes apart */ @@ -144,24 +139,22 @@ static int data_index; static int data_len; static volatile int adb_int_pending; static volatile int disable_poll; -static struct adb_request bright_req_1, bright_req_2; static struct device_node *vias; static int pmu_kind = PMU_UNKNOWN; -static int pmu_fully_inited = 0; +static int pmu_fully_inited; static int pmu_has_adb; static struct device_node *gpio_node; -static unsigned char __iomem *gpio_reg = NULL; -static int gpio_irq = -1; +static unsigned char __iomem *gpio_reg; +static int gpio_irq = NO_IRQ; static int gpio_irq_enabled = -1; -static volatile int pmu_suspended = 0; +static volatile int pmu_suspended; static spinlock_t pmu_lock; static u8 pmu_intr_mask; static int pmu_version; static int drop_interrupts; -#if defined(CONFIG_PM) && defined(CONFIG_PPC32) +#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32) static int option_lid_wakeup = 1; -#endif /* CONFIG_PM && CONFIG_PPC32 */ -static int sleep_in_progress; +#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */ static unsigned long async_req_locks; static unsigned int pmu_irq_stats[11]; @@ -173,22 +166,17 @@ static int option_server_mode; int pmu_battery_count; int pmu_cur_battery; -unsigned int pmu_power_flags; +unsigned int pmu_power_flags = PMU_PWR_AC_PRESENT; struct pmu_battery_info pmu_batteries[PMU_MAX_BATTERIES]; 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]; -#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 -static int adb_dev_map = 0; +static int adb_dev_map; static int pmu_adb_flags; static int pmu_probe(void); @@ -200,23 +188,13 @@ static int pmu_adb_reset_bus(void); static int init_pmu(void); static void pmu_start(void); -static irqreturn_t via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs); -static irqreturn_t gpio1_interrupt(int irq, void *arg, struct pt_regs *regs); -static int proc_get_info(char *page, char **start, off_t off, - int count, int *eof, void *data); -static int proc_get_irqstats(char *page, char **start, off_t off, - int count, int *eof, void *data); -#ifdef CONFIG_PMAC_BACKLIGHT -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 */ +static irqreturn_t via_pmu_interrupt(int irq, void *arg); +static irqreturn_t gpio1_interrupt(int irq, void *arg); +static const struct file_operations pmu_info_proc_fops; +static const struct file_operations pmu_irqstats_proc_fops; 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); -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, - unsigned long count, void *data); +static const struct file_operations pmu_battery_proc_fops; +static const struct file_operations pmu_options_proc_fops; #ifdef CONFIG_ADB struct adb_driver via_pmu_driver = { @@ -236,7 +214,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 /* @@ -290,17 +268,10 @@ static char *pbook_type[] = { "Core99" }; -#ifdef CONFIG_PMAC_BACKLIGHT -static struct backlight_controller pmu_backlight_controller = { - pmu_set_backlight_enable, - pmu_set_backlight_level -}; -#endif /* CONFIG_PMAC_BACKLIGHT */ - int __init find_via_pmu(void) { u64 taddr; - u32 *reg; + const u32 *reg; if (via != 0) return 1; @@ -308,7 +279,7 @@ int __init find_via_pmu(void) if (vias == NULL) return 0; - reg = (u32 *)get_property(vias, "reg", NULL); + reg = of_get_property(vias, "reg", NULL); if (reg == NULL) { printk(KERN_ERR "via-pmu: No \"reg\" property !\n"); goto fail; @@ -329,19 +300,22 @@ int __init find_via_pmu(void) PMU_INT_TICK; if (vias->parent->name && ((strcmp(vias->parent->name, "ohare") == 0) - || device_is_compatible(vias->parent, "ohare"))) + || of_device_is_compatible(vias->parent, "ohare"))) pmu_kind = PMU_OHARE_BASED; - else if (device_is_compatible(vias->parent, "paddington")) + else if (of_device_is_compatible(vias->parent, "paddington")) pmu_kind = PMU_PADDINGTON_BASED; - else if (device_is_compatible(vias->parent, "heathrow")) + else if (of_device_is_compatible(vias->parent, "heathrow")) pmu_kind = PMU_HEATHROW_BASED; - else if (device_is_compatible(vias->parent, "Keylargo") - || device_is_compatible(vias->parent, "K2-Keylargo")) { + else if (of_device_is_compatible(vias->parent, "Keylargo") + || of_device_is_compatible(vias->parent, "K2-Keylargo")) { struct device_node *gpiop; + struct device_node *adbp; u64 gaddr = OF_BAD_ADDR; pmu_kind = PMU_KEYLARGO_BASED; - pmu_has_adb = (find_type_devices("adb") != NULL); + adbp = of_find_node_by_type(NULL, "adb"); + pmu_has_adb = (adbp != NULL); + of_node_put(adbp); pmu_intr_mask = PMU_INT_PCEJECT | PMU_INT_SNDBRT | PMU_INT_ADB | @@ -350,14 +324,16 @@ int __init find_via_pmu(void) gpiop = of_find_node_by_name(NULL, "gpio"); if (gpiop) { - reg = (u32 *)get_property(gpiop, "reg", NULL); + reg = of_get_property(gpiop, "reg", NULL); if (reg) gaddr = of_translate_address(gpiop, reg); if (gaddr != OF_BAD_ADDR) gpio_reg = ioremap(gaddr, 0x10); } - if (gpio_reg == NULL) + if (gpio_reg == NULL) { printk(KERN_ERR "via-pmu: Can't find GPIO reg !\n"); + goto fail_gpio; + } } else pmu_kind = PMU_UNKNOWN; @@ -385,6 +361,9 @@ int __init find_via_pmu(void) return 1; fail: of_node_put(vias); + iounmap(gpio_reg); + gpio_reg = NULL; + fail_gpio: vias = NULL; return 0; } @@ -412,24 +391,26 @@ static int __init pmu_init(void) */ static int __init via_pmu_start(void) { + unsigned int irq; + if (vias == NULL) return -ENODEV; - bright_req_1.complete = 1; - bright_req_2.complete = 1; batt_req.complete = 1; -#ifndef CONFIG_PPC_MERGE - if (pmu_kind == PMU_KEYLARGO_BASED) - openpic_set_irq_priority(vias->intrs[0].line, - OPENPIC_PRIORITY_DEFAULT + 1); -#endif - - if (request_irq(vias->intrs[0].line, via_pmu_interrupt, 0, "VIA-PMU", - (void *)0)) { - printk(KERN_ERR "VIA-PMU: can't get irq %d\n", - vias->intrs[0].line); - return -EAGAIN; + irq = irq_of_parse_and_map(vias, 0); + if (irq == NO_IRQ) { + printk(KERN_ERR "via-pmu: can't map interrupt\n"); + return -ENODEV; + } + /* We set IRQF_NO_SUSPEND because we don't want the interrupt + * to be disabled between the 2 passes of driver suspend, we + * control our own disabling for that one + */ + if (request_irq(irq, via_pmu_interrupt, IRQF_NO_SUSPEND, + "VIA-PMU", (void *)0)) { + printk(KERN_ERR "via-pmu: can't request irq %d\n", irq); + return -ENODEV; } if (pmu_kind == PMU_KEYLARGO_BASED) { @@ -437,11 +418,11 @@ static int __init via_pmu_start(void) if (gpio_node == NULL) gpio_node = of_find_node_by_name(NULL, "pmu-interrupt"); - if (gpio_node && gpio_node->n_intrs > 0) - gpio_irq = gpio_node->intrs[0].line; + if (gpio_node) + gpio_irq = irq_of_parse_and_map(gpio_node, 0); - if (gpio_irq != -1) { - if (request_irq(gpio_irq, gpio1_interrupt, 0, + if (gpio_irq != NO_IRQ) { + if (request_irq(gpio_irq, gpio1_interrupt, IRQF_TIMER, "GPIO1 ADB", (void *)0)) printk(KERN_ERR "pmu: can't get irq %d" " (GPIO1)\n", gpio_irq); @@ -481,13 +462,13 @@ static int __init via_pmu_dev_init(void) return -ENODEV; #ifdef CONFIG_PMAC_BACKLIGHT - /* Enable backlight */ - register_backlight_controller(&pmu_backlight_controller, NULL, "pmu"); -#endif /* CONFIG_PMAC_BACKLIGHT */ + /* Initialize backlight */ + pmu_backlight_init(); +#endif #ifdef CONFIG_PPC32 - if (machine_is_compatible("AAPL,3400/2400") || - machine_is_compatible("AAPL,3500")) { + if (of_machine_is_compatible("AAPL,3400/2400") || + of_machine_is_compatible("AAPL,3500")) { int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO, NULL, PMAC_MB_INFO_MODEL, 0); pmu_battery_count = 1; @@ -495,16 +476,17 @@ static int __init via_pmu_dev_init(void) pmu_batteries[0].flags |= PMU_BATT_TYPE_COMET; else pmu_batteries[0].flags |= PMU_BATT_TYPE_HOOPER; - } else if (machine_is_compatible("AAPL,PowerBook1998") || - machine_is_compatible("PowerBook1,1")) { + } else if (of_machine_is_compatible("AAPL,PowerBook1998") || + of_machine_is_compatible("PowerBook1,1")) { pmu_battery_count = 2; pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART; pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART; } else { - struct device_node* prim = find_devices("power-mgt"); - u32 *prim_info = NULL; + struct device_node* prim = + of_find_node_by_name(NULL, "power-mgt"); + const u32 *prim_info = NULL; if (prim) - prim_info = (u32 *)get_property(prim, "prim-info", NULL); + prim_info = of_get_property(prim, "prim-info", NULL); if (prim_info) { /* Other stuffs here yet unknown */ pmu_battery_count = (prim_info[6] >> 16) & 0xff; @@ -512,6 +494,7 @@ static int __init via_pmu_dev_init(void) if (pmu_battery_count > 1) pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART; } + of_node_put(prim); } #endif /* CONFIG_PPC32 */ @@ -523,20 +506,15 @@ static int __init via_pmu_dev_init(void) for (i=0; i<pmu_battery_count; i++) { char title[16]; sprintf(title, "battery_%ld", i); - proc_pmu_batt[i] = create_proc_read_entry(title, 0, proc_pmu_root, - proc_get_batt, (void *)i); + proc_pmu_batt[i] = proc_create_data(title, 0, proc_pmu_root, + &pmu_battery_proc_fops, (void *)i); } - proc_pmu_info = create_proc_read_entry("info", 0, proc_pmu_root, - proc_get_info, NULL); - proc_pmu_irqstats = create_proc_read_entry("interrupts", 0, proc_pmu_root, - proc_get_irqstats, NULL); - proc_pmu_options = create_proc_entry("options", 0600, proc_pmu_root); - if (proc_pmu_options) { - proc_pmu_options->nlink = 1; - proc_pmu_options->read_proc = proc_read_options; - proc_pmu_options->write_proc = proc_write_options; - } + proc_pmu_info = proc_create("info", 0, proc_pmu_root, &pmu_info_proc_fops); + proc_pmu_irqstats = proc_create("interrupts", 0, proc_pmu_root, + &pmu_irqstats_proc_fops); + proc_pmu_options = proc_create("options", 0600, proc_pmu_root, + &pmu_options_proc_fops); } return 0; } @@ -573,7 +551,7 @@ init_pmu(void) } if (pmu_state == idle) adb_int_pending = 1; - via_pmu_interrupt(0, NULL, NULL); + via_pmu_interrupt(0, NULL); udelay(10); } @@ -774,8 +752,9 @@ done_battery_state_smart(struct adb_request* req) voltage = (req->reply[8] << 8) | req->reply[9]; break; default: - printk(KERN_WARNING "pmu.c : unrecognized battery info, len: %d, %02x %02x %02x %02x\n", - req->reply_len, req->reply[0], req->reply[1], req->reply[2], req->reply[3]); + pr_warn("pmu.c: unrecognized battery info, " + "len: %d, %4ph\n", req->reply_len, + req->reply); break; } } @@ -816,27 +795,33 @@ query_battery_state(void) 2, PMU_SMART_BATTERY_STATE, pmu_cur_battery+1); } -static int -proc_get_info(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int pmu_info_proc_show(struct seq_file *m, void *v) { - char* p = page; + seq_printf(m, "PMU driver version : %d\n", PMU_DRIVER_VERSION); + seq_printf(m, "PMU firmware version : %02x\n", pmu_version); + seq_printf(m, "AC Power : %d\n", + ((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0) || pmu_battery_count == 0); + seq_printf(m, "Battery count : %d\n", pmu_battery_count); - p += sprintf(p, "PMU driver version : %d\n", PMU_DRIVER_VERSION); - p += sprintf(p, "PMU firmware version : %02x\n", pmu_version); - p += sprintf(p, "AC Power : %d\n", - ((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0)); - p += sprintf(p, "Battery count : %d\n", pmu_battery_count); + return 0; +} - return p - page; +static int pmu_info_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, pmu_info_proc_show, NULL); } -static int -proc_get_irqstats(char *page, char **start, off_t off, - int count, int *eof, void *data) +static const struct file_operations pmu_info_proc_fops = { + .owner = THIS_MODULE, + .open = pmu_info_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int pmu_irqstats_proc_show(struct seq_file *m, void *v) { int i; - char* p = page; static const char *irq_names[] = { "Total CB1 triggered events", "Total GPIO1 triggered events", @@ -852,60 +837,76 @@ proc_get_irqstats(char *page, char **start, off_t off, }; for (i=0; i<11; i++) { - p += sprintf(p, " %2u: %10u (%s)\n", + seq_printf(m, " %2u: %10u (%s)\n", i, pmu_irq_stats[i], irq_names[i]); } - return p - page; + return 0; } -static int -proc_get_batt(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int pmu_irqstats_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, pmu_irqstats_proc_show, NULL); +} + +static const struct file_operations pmu_irqstats_proc_fops = { + .owner = THIS_MODULE, + .open = pmu_irqstats_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int pmu_battery_proc_show(struct seq_file *m, void *v) { - long batnum = (long)data; - char *p = page; + long batnum = (long)m->private; - p += sprintf(p, "\n"); - p += sprintf(p, "flags : %08x\n", - pmu_batteries[batnum].flags); - p += sprintf(p, "charge : %d\n", - pmu_batteries[batnum].charge); - p += sprintf(p, "max_charge : %d\n", - pmu_batteries[batnum].max_charge); - p += sprintf(p, "current : %d\n", - pmu_batteries[batnum].amperage); - p += sprintf(p, "voltage : %d\n", - pmu_batteries[batnum].voltage); - p += sprintf(p, "time rem. : %d\n", - pmu_batteries[batnum].time_remaining); - - return p - page; + seq_putc(m, '\n'); + seq_printf(m, "flags : %08x\n", pmu_batteries[batnum].flags); + seq_printf(m, "charge : %d\n", pmu_batteries[batnum].charge); + seq_printf(m, "max_charge : %d\n", pmu_batteries[batnum].max_charge); + seq_printf(m, "current : %d\n", pmu_batteries[batnum].amperage); + seq_printf(m, "voltage : %d\n", pmu_batteries[batnum].voltage); + seq_printf(m, "time rem. : %d\n", pmu_batteries[batnum].time_remaining); + return 0; } -static int -proc_read_options(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int pmu_battery_proc_open(struct inode *inode, struct file *file) { - char *p = page; + return single_open(file, pmu_battery_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations pmu_battery_proc_fops = { + .owner = THIS_MODULE, + .open = pmu_battery_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; -#if defined(CONFIG_PM) && defined(CONFIG_PPC32) +static int pmu_options_proc_show(struct seq_file *m, void *v) +{ +#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); + seq_printf(m, "lid_wakeup=%d\n", option_lid_wakeup); #endif if (pmu_kind == PMU_KEYLARGO_BASED) - p += sprintf(p, "server_mode=%d\n", option_server_mode); + seq_printf(m, "server_mode=%d\n", option_server_mode); - return p - page; + return 0; } - -static int -proc_write_options(struct file *file, const char __user *buffer, - unsigned long count, void *data) + +static int pmu_options_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, pmu_options_proc_show, NULL); +} + +static ssize_t pmu_options_proc_write(struct file *file, + const char __user *buffer, size_t count, loff_t *pos) { char tmp[33]; char *label, *val; - unsigned long fcount = count; + size_t fcount = count; if (!count) return -EINVAL; @@ -929,7 +930,7 @@ proc_write_options(struct file *file, const char __user *buffer, *(val++) = 0; while(*val == ' ') val++; -#if defined(CONFIG_PM) && 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")) @@ -944,10 +945,18 @@ proc_write_options(struct file *file, const char __user *buffer, return fcount; } +static const struct file_operations pmu_options_proc_fops = { + .owner = THIS_MODULE, + .open = pmu_options_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = pmu_options_proc_write, +}; + #ifdef CONFIG_ADB /* Send an ADB command */ -static int -pmu_send_request(struct adb_request *req, int sync) +static int pmu_send_request(struct adb_request *req, int sync) { int i, ret; @@ -1026,16 +1035,11 @@ pmu_send_request(struct adb_request *req, int sync) } /* Enable/disable autopolling */ -static int -pmu_adb_autopoll(int devs) +static int __pmu_adb_autopoll(int devs) { struct adb_request req; - if ((vias == NULL) || (!pmu_fully_inited) || !pmu_has_adb) - return -ENXIO; - if (devs) { - adb_dev_map = devs; pmu_request(&req, NULL, 5, PMU_ADB_CMD, 0, 0x86, adb_dev_map >> 8, adb_dev_map); pmu_adb_flags = 2; @@ -1048,9 +1052,17 @@ pmu_adb_autopoll(int devs) return 0; } +static int pmu_adb_autopoll(int devs) +{ + if ((vias == NULL) || (!pmu_fully_inited) || !pmu_has_adb) + return -ENXIO; + + adb_dev_map = devs; + return __pmu_adb_autopoll(devs); +} + /* Reset the ADB bus */ -static int -pmu_adb_reset_bus(void) +static int pmu_adb_reset_bus(void) { struct adb_request req; int save_autopoll = adb_dev_map; @@ -1059,13 +1071,13 @@ pmu_adb_reset_bus(void) return -ENXIO; /* anyone got a better idea?? */ - pmu_adb_autopoll(0); + __pmu_adb_autopoll(0); - req.nbytes = 5; + req.nbytes = 4; req.done = NULL; req.data[0] = PMU_ADB_CMD; - req.data[1] = 0; - req.data[2] = ADB_BUSRESET; + req.data[1] = ADB_BUSRESET; + req.data[2] = 0; req.data[3] = 0; req.data[4] = 0; req.reply_len = 0; @@ -1077,7 +1089,7 @@ pmu_adb_reset_bus(void) pmu_wait_complete(&req); if (save_autopoll != 0) - pmu_adb_autopoll(save_autopoll); + __pmu_adb_autopoll(save_autopoll); return 0; } @@ -1233,7 +1245,7 @@ pmu_poll(void) return; if (disable_poll) return; - via_pmu_interrupt(0, NULL, NULL); + via_pmu_interrupt(0, NULL); } void @@ -1246,7 +1258,7 @@ pmu_poll_adb(void) /* Kicks ADB read when PMU is suspended */ adb_int_pending = 1; do { - via_pmu_interrupt(0, NULL, NULL); + via_pmu_interrupt(0, NULL); } while (pmu_suspended && (adb_int_pending || pmu_state != idle || req_awaiting_reply)); } @@ -1257,7 +1269,7 @@ pmu_wait_complete(struct adb_request *req) if (!via) return; while((pmu_state != idle && pmu_state != locked) || !req->complete) - via_pmu_interrupt(0, NULL, NULL); + via_pmu_interrupt(0, NULL); } /* This function loops until the PMU is idle and prevents it from @@ -1269,9 +1281,7 @@ void pmu_suspend(void) { unsigned long flags; -#ifdef SUSPEND_USES_PMU - struct adb_request *req; -#endif + if (!via) return; @@ -1286,20 +1296,13 @@ pmu_suspend(void) spin_unlock_irqrestore(&pmu_lock, flags); if (req_awaiting_reply) adb_int_pending = 1; - via_pmu_interrupt(0, NULL, NULL); + via_pmu_interrupt(0, NULL); spin_lock_irqsave(&pmu_lock, flags); if (!adb_int_pending && pmu_state == idle && !req_awaiting_reply) { -#ifdef SUSPEND_USES_PMU - pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0); - spin_unlock_irqrestore(&pmu_lock, flags); - while(!req.complete) - pmu_poll(); -#else /* SUSPEND_USES_PMU */ if (gpio_irq >= 0) disable_irq_nosync(gpio_irq); out_8(&via[IER], CB1_INT | IER_CLR); spin_unlock_irqrestore(&pmu_lock, flags); -#endif /* SUSPEND_USES_PMU */ break; } } while (1); @@ -1320,23 +1323,16 @@ pmu_resume(void) return; } adb_int_pending = 1; -#ifdef SUSPEND_USES_PMU - pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask); - spin_unlock_irqrestore(&pmu_lock, flags); - while(!req.complete) - pmu_poll(); -#else /* SUSPEND_USES_PMU */ if (gpio_irq >= 0) enable_irq(gpio_irq); out_8(&via[IER], CB1_INT | IER_SET); spin_unlock_irqrestore(&pmu_lock, flags); pmu_poll(); -#endif /* SUSPEND_USES_PMU */ } /* Interrupt data could be the result data from an ADB cmd */ static void -pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs) +pmu_handle_data(unsigned char *data, int len) { unsigned char ints, pirq; int i = 0; @@ -1411,7 +1407,7 @@ next: if (!(pmu_kind == PMU_OHARE_BASED && len == 4 && data[1] == 0x2c && data[3] == 0xff && (data[2] & ~1) == 0xf4)) - adb_input(data+1, len-1, regs, 1); + adb_input(data+1, len-1, 1); #endif /* CONFIG_ADB */ } } @@ -1419,11 +1415,8 @@ next: else if ((1 << pirq) & PMU_INT_SNDBRT) { #ifdef CONFIG_PMAC_BACKLIGHT if (len == 3) -#ifdef CONFIG_INPUT_ADBHID - if (!disable_kernel_backlight) -#endif /* CONFIG_INPUT_ADBHID */ - set_backlight_level(data[1] >> 4); -#endif /* CONFIG_PMAC_BACKLIGHT */ + pmac_backlight_set_legacy_brightness_pmu(data[1] >> 4); +#endif } /* Tick interrupt */ else if ((1 << pirq) & PMU_INT_TICK) { @@ -1439,6 +1432,12 @@ next: if (pmu_battery_count) query_battery_state(); pmu_pass_intr(data, len); + /* len == 6 is probably a bad check. But how do I + * know what PMU versions send what events here? */ + if (len == 6) { + via_pmu_event(PMU_EVT_POWER, !!(data[1]&8)); + via_pmu_event(PMU_EVT_LID, data[1]&1); + } } else { pmu_pass_intr(data, len); } @@ -1446,7 +1445,7 @@ next: } static struct adb_request* -pmu_sr_intr(struct pt_regs *regs) +pmu_sr_intr(void) { struct adb_request *req; int bite = 0; @@ -1531,7 +1530,7 @@ pmu_sr_intr(struct pt_regs *regs) req = current_req; /* * For PMU sleep and freq change requests, we lock the - * PMU until it's explicitely unlocked. This avoids any + * PMU until it's explicitly unlocked. This avoids any * spurrious event polling getting in */ current_req = req->next; @@ -1552,7 +1551,7 @@ pmu_sr_intr(struct pt_regs *regs) } static irqreturn_t -via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs) +via_pmu_interrupt(int irq, void *arg) { unsigned long flags; int intr; @@ -1582,7 +1581,7 @@ via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs) pmu_irq_stats[0]++; } if (intr & SR_INT) { - req = pmu_sr_intr(regs); + req = pmu_sr_intr(); if (req) break; } @@ -1628,7 +1627,7 @@ no_free_slot: /* Deal with interrupt datas outside of the lock */ if (int_data >= 0) { - pmu_handle_data(interrupt_data[int_data], interrupt_data_len[int_data], regs); + pmu_handle_data(interrupt_data[int_data], interrupt_data_len[int_data]); spin_lock_irqsave(&pmu_lock, flags); ++disable_poll; int_data_state[int_data] = int_data_empty; @@ -1653,7 +1652,7 @@ pmu_unlock(void) static irqreturn_t -gpio1_interrupt(int irq, void *arg, struct pt_regs *regs) +gpio1_interrupt(int irq, void *arg) { unsigned long flags; @@ -1666,67 +1665,12 @@ gpio1_interrupt(int irq, void *arg, struct pt_regs *regs) pmu_irq_stats[1]++; adb_int_pending = 1; spin_unlock_irqrestore(&pmu_lock, flags); - via_pmu_interrupt(0, NULL, NULL); + via_pmu_interrupt(0, NULL); return IRQ_HANDLED; } return IRQ_NONE; } -#ifdef CONFIG_PMAC_BACKLIGHT -static int backlight_to_bright[] = { - 0x7f, 0x46, 0x42, 0x3e, 0x3a, 0x36, 0x32, 0x2e, - 0x2a, 0x26, 0x22, 0x1e, 0x1a, 0x16, 0x12, 0x0e -}; - -static int -pmu_set_backlight_enable(int on, int level, void* data) -{ - struct adb_request req; - - if (vias == NULL) - return -ENODEV; - - if (on) { - pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT, - backlight_to_bright[level]); - pmu_wait_complete(&req); - } - pmu_request(&req, NULL, 2, PMU_POWER_CTRL, - PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF)); - pmu_wait_complete(&req); - - return 0; -} - -static void -pmu_bright_complete(struct adb_request *req) -{ - if (req == &bright_req_1) - clear_bit(1, &async_req_locks); - if (req == &bright_req_2) - clear_bit(2, &async_req_locks); -} - -static int -pmu_set_backlight_level(int level, void* data) -{ - if (vias == NULL) - return -ENODEV; - - if (test_and_set_bit(1, &async_req_locks)) - return -EAGAIN; - pmu_request(&bright_req_1, pmu_bright_complete, 2, PMU_BACKLIGHT_BRIGHT, - backlight_to_bright[level]); - if (test_and_set_bit(2, &async_req_locks)) - return -EAGAIN; - pmu_request(&bright_req_2, pmu_bright_complete, 2, PMU_POWER_CTRL, - PMU_POW_BACKLIGHT | (level > BACKLIGHT_OFF ? - PMU_POW_ON : PMU_POW_OFF)); - - return 0; -} -#endif /* CONFIG_PMAC_BACKLIGHT */ - void pmu_enable_irled(int on) { @@ -1803,239 +1747,7 @@ pmu_present(void) return via != 0; } -#ifdef CONFIG_PM - -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; -} -EXPORT_SYMBOL(pmu_register_sleep_notifier); - -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; -} -EXPORT_SYMBOL(pmu_unregister_sleep_notifier); -#endif /* CONFIG_PM */ - -#if defined(CONFIG_PM) && defined(CONFIG_PPC32) - -/* Sleep is broadcast last-to-first */ -static int -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 -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 -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 -pbook_free_pci_save(void) -{ - if (pbook_pci_saves == NULL) - return; - kfree(pbook_pci_saves); - pbook_pci_saves = NULL; - pbook_npci_saves = 0; -} - -static void -pbook_pci_save(void) -{ - struct pci_save *ps = pbook_pci_saves; - struct pci_dev *pd = NULL; - int npci = pbook_npci_saves; - - if (ps == NULL) - return; - - while ((pd = pci_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 -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 -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. */ @@ -2070,156 +1782,22 @@ restore_via_state(void) out_8(&via[IER], IER_SET | SR_INT | CB1_INT); } -static int -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; - } - - /* 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(); - 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_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(); - - /* Call platform functions marked "on wake" */ - pmac_pfunc_base_resume(); - pmac_pfunc_i2c_resume(); - - /* 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 -powerbook_sleep_grackle(void) +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_find_slot(0, 0); + 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); @@ -2258,6 +1836,8 @@ powerbook_sleep_grackle(void) pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP); pci_write_config_word(grackle, 0x70, pmcr1); + pci_dev_put(grackle); + /* Make sure the PMU is idle */ pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0); restore_via_state(); @@ -2267,7 +1847,7 @@ powerbook_sleep_grackle(void) _set_L2CR(save_l2cr); /* Restore userland MMU context */ - set_context(current->active_mm->context, current->active_mm->pgd); + switch_mmu_context(NULL, current->active_mm); /* Power things up */ pmu_unlock(); @@ -2280,8 +1860,6 @@ 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; } @@ -2291,7 +1869,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"); @@ -2301,12 +1878,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); @@ -2365,7 +1936,7 @@ powerbook_sleep_Core99(void) _set_L3CR(save_l3cr); /* Restore userland MMU context */ - set_context(current->active_mm->context, current->active_mm->pgd); + switch_mmu_context(NULL, current->active_mm); /* Tell PMU we are ready */ pmu_unlock(); @@ -2377,44 +1948,33 @@ powerbook_sleep_Core99(void) /* Restore LPJ, cpufreq will adjust the cpu frequency */ loops_per_jiffy /= 2; - pmac_wakeup_devices(); - return 0; } #define PB3400_MEM_CTRL 0xf8000000 #define PB3400_MEM_CTRL_SLEEP 0x70 -static int -powerbook_sleep_3400(void) +static void __iomem *pb3400_mem_ctrl; + +static void powerbook_sleep_init_3400(void) +{ + /* map in the memory controller registers */ + pb3400_mem_ctrl = ioremap(PB3400_MEM_CTRL, 0x100); + if (pb3400_mem_ctrl == NULL) + printk(KERN_WARNING "ioremap failed: sleep won't be possible"); +} + +static int powerbook_sleep_3400(void) { - int ret, i, x; + int i, x; unsigned int hid0; - unsigned long p; + unsigned long msr; struct adb_request sleep_req; - void __iomem *mem_ctrl; unsigned int __iomem *mem_ctrl_sleep; - /* first map in the memory controller registers */ - mem_ctrl = ioremap(PB3400_MEM_CTRL, 0x100); - if (mem_ctrl == NULL) { - printk("powerbook_sleep_3400: ioremap failed\n"); + if (pb3400_mem_ctrl == NULL) return -ENOMEM; - } - mem_ctrl_sleep = mem_ctrl + PB3400_MEM_CTRL_SLEEP; - - /* Allocate room for PCI save */ - pbook_alloc_pci_save(); - - 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(); + mem_ctrl_sleep = pb3400_mem_ctrl + PB3400_MEM_CTRL_SLEEP; /* Set the memory controller to keep the memory refreshed while we're asleep */ @@ -2429,41 +1989,34 @@ powerbook_sleep_3400(void) /* Ask the PMU to put us to sleep */ pmu_request(&sleep_req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); - while (!sleep_req.complete) - mb(); + pmu_wait_complete(&sleep_req); + pmu_unlock(); - pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1); + pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 1); - /* displacement-flush the L2 cache - necessary? */ - for (p = KERNELBASE; p < KERNELBASE + 0x100000; p += 0x1000) - i = *(volatile int *)p; asleep = 1; /* Put the CPU into sleep mode */ hid0 = mfspr(SPRN_HID0); hid0 = (hid0 & ~(HID0_NAP | HID0_DOZE)) | HID0_SLEEP; mtspr(SPRN_HID0, hid0); - mtmsr(mfmsr() | MSR_POW | MSR_EE); - udelay(10); + local_irq_enable(); + msr = mfmsr() | MSR_POW; + while (asleep) { + mb(); + mtmsr(msr); + isync(); + } + local_irq_disable(); /* OK, we're awake again, start restoring things */ out_be32(mem_ctrl_sleep, 0x3f); - pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0); - pbook_pci_restore(); - pmu_unlock(); - - /* wait for the PMU interrupt sequence to complete */ - while (asleep) - mb(); - - pmac_wakeup_devices(); - pbook_free_pci_save(); - iounmap(mem_ctrl); + pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 0); return 0; } -#endif /* CONFIG_PM && CONFIG_PPC32 */ +#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */ /* * Support for /dev/pmu device @@ -2481,7 +2034,7 @@ struct pmu_private { spinlock_t lock; #if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) int backlight_locker; -#endif /* defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) */ +#endif }; static LIST_HEAD(all_pmu_pvt); @@ -2528,13 +2081,15 @@ pmu_open(struct inode *inode, struct file *file) pp->rb_get = pp->rb_put = 0; spin_lock_init(&pp->lock); init_waitqueue_head(&pp->wait); + mutex_lock(&pmu_info_proc_mutex); spin_lock_irqsave(&all_pvt_lock, flags); #if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) pp->backlight_locker = 0; -#endif /* defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) */ +#endif list_add(&pp->list, &all_pmu_pvt); spin_unlock_irqrestore(&all_pvt_lock, flags); file->private_data = pp; + mutex_unlock(&pmu_info_proc_mutex); return 0; } @@ -2620,99 +2175,164 @@ pmu_release(struct inode *inode, struct file *file) struct pmu_private *pp = file->private_data; unsigned long flags; - lock_kernel(); if (pp != 0) { file->private_data = NULL; spin_lock_irqsave(&all_pvt_lock, flags); list_del(&pp->list); spin_unlock_irqrestore(&all_pvt_lock, flags); + #if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) - if (pp->backlight_locker) { - spin_lock_irqsave(&pmu_lock, flags); - disable_kernel_backlight--; - spin_unlock_irqrestore(&pmu_lock, flags); - } -#endif /* defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) */ + if (pp->backlight_locker) + pmac_backlight_enable(); +#endif + kfree(pp); } - unlock_kernel(); return 0; } -static int -pmu_ioctl(struct inode * inode, struct file *filp, +#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 const 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 file *filp, u_int cmd, u_long arg) { __u32 __user *argp = (__u32 __user *)arg; int error = -EINVAL; switch (cmd) { -#if defined(CONFIG_PM) && 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 && CONFIG_PPC32 */ -#ifdef CONFIG_PMAC_BACKLIGHT - /* Backlight should have its own device or go via - * the fbdev - */ +#ifdef CONFIG_PMAC_BACKLIGHT_LEGACY + /* Compatibility ioctl's for backlight */ case PMU_IOC_GET_BACKLIGHT: - if (sleep_in_progress) - return -EBUSY; - error = get_backlight_level(); - if (error < 0) - return error; - return put_user(error, argp); + { + int brightness; + + brightness = pmac_backlight_get_legacy_brightness(); + if (brightness < 0) + return brightness; + else + return put_user(brightness, argp); + + } 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); - break; + int brightness; + + error = get_user(brightness, argp); + if (error) + return error; + + return pmac_backlight_set_legacy_brightness(brightness); } #ifdef CONFIG_INPUT_ADBHID case PMU_IOC_GRAB_BACKLIGHT: { struct pmu_private *pp = filp->private_data; - unsigned long flags; if (pp->backlight_locker) return 0; + pp->backlight_locker = 1; - spin_lock_irqsave(&pmu_lock, flags); - disable_kernel_backlight++; - spin_unlock_irqrestore(&pmu_lock, flags); + pmac_backlight_disable(); + return 0; } #endif /* CONFIG_INPUT_ADBHID */ -#endif /* CONFIG_PMAC_BACKLIGHT */ +#endif /* CONFIG_PMAC_BACKLIGHT_LEGACY */ + case PMU_IOC_GET_MODEL: return put_user(pmu_kind, argp); case PMU_IOC_HAS_ADB: @@ -2721,13 +2341,67 @@ pmu_ioctl(struct inode * inode, struct file *filp, return error; } -static struct file_operations pmu_device_fops = { +static long pmu_unlocked_ioctl(struct file *filp, + u_int cmd, u_long arg) +{ + int ret; + + mutex_lock(&pmu_info_proc_mutex); + ret = pmu_ioctl(filp, cmd, arg); + mutex_unlock(&pmu_info_proc_mutex); + + return ret; +} + +#ifdef CONFIG_COMPAT +#define PMU_IOC_GET_BACKLIGHT32 _IOR('B', 1, compat_size_t) +#define PMU_IOC_SET_BACKLIGHT32 _IOW('B', 2, compat_size_t) +#define PMU_IOC_GET_MODEL32 _IOR('B', 3, compat_size_t) +#define PMU_IOC_HAS_ADB32 _IOR('B', 4, compat_size_t) +#define PMU_IOC_CAN_SLEEP32 _IOR('B', 5, compat_size_t) +#define PMU_IOC_GRAB_BACKLIGHT32 _IOR('B', 6, compat_size_t) + +static long compat_pmu_ioctl (struct file *filp, u_int cmd, u_long arg) +{ + switch (cmd) { + case PMU_IOC_SLEEP: + break; + case PMU_IOC_GET_BACKLIGHT32: + cmd = PMU_IOC_GET_BACKLIGHT; + break; + case PMU_IOC_SET_BACKLIGHT32: + cmd = PMU_IOC_SET_BACKLIGHT; + break; + case PMU_IOC_GET_MODEL32: + cmd = PMU_IOC_GET_MODEL; + break; + case PMU_IOC_HAS_ADB32: + cmd = PMU_IOC_HAS_ADB; + break; + case PMU_IOC_CAN_SLEEP32: + cmd = PMU_IOC_CAN_SLEEP; + break; + case PMU_IOC_GRAB_BACKLIGHT32: + cmd = PMU_IOC_GRAB_BACKLIGHT; + break; + default: + return -ENOIOCTLCMD; + } + return pmu_unlocked_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static const struct file_operations pmu_device_fops = { .read = pmu_read, .write = pmu_write, .poll = pmu_fpoll, - .ioctl = pmu_ioctl, + .unlocked_ioctl = pmu_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = compat_pmu_ioctl, +#endif .open = pmu_open, .release = pmu_release, + .llseek = noop_llseek, }; static struct miscdevice pmu_device = { @@ -2816,89 +2490,92 @@ 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) && 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 */ -static int pmu_sys_suspended = 0; +#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32) +int pmu_sys_suspended; -static int pmu_sys_suspend(struct sys_device *sysdev, pm_message_t state) +static int pmu_syscore_suspend(void) { - if (state.event != PM_EVENT_SUSPEND || pmu_sys_suspended) - return 0; - /* 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; } -static int pmu_sys_resume(struct sys_device *sysdev) +static void pmu_syscore_resume(void) { struct adb_request req; if (!pmu_sys_suspended) - return 0; + return; /* Tell PMU we are ready */ 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 && CONFIG_PPC32 */ - -static struct sysdev_class pmu_sysclass = { - set_kset_name("pmu"), -}; - -static struct sys_device device_pmu = { - .id = 0, - .cls = &pmu_sysclass, -}; - -static struct sysdev_driver driver_pmu = { -#if defined(CONFIG_PM) && defined(CONFIG_PPC32) - .suspend = &pmu_sys_suspend, - .resume = &pmu_sys_resume, -#endif /* CONFIG_PM && CONFIG_PPC32 */ +static struct syscore_ops pmu_syscore_ops = { + .suspend = pmu_syscore_suspend, + .resume = pmu_syscore_resume, }; -static int __init init_pmu_sysfs(void) +static int pmu_syscore_register(void) { - int rc; + register_syscore_ops(&pmu_syscore_ops); - rc = sysdev_class_register(&pmu_sysclass); - if (rc) { - printk(KERN_ERR "Failed registering PMU sys class\n"); - return -ENODEV; - } - rc = sysdev_register(&device_pmu); - if (rc) { - printk(KERN_ERR "Failed registering PMU sys device\n"); - return -ENODEV; - } - rc = sysdev_driver_register(&pmu_sysclass, &driver_pmu); - if (rc) { - printk(KERN_ERR "Failed registering PMU sys driver\n"); - return -ENODEV; - } return 0; } - -subsys_initcall(init_pmu_sysfs); +subsys_initcall(pmu_syscore_register); +#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */ EXPORT_SYMBOL(pmu_request); EXPORT_SYMBOL(pmu_queue_request); @@ -2908,10 +2585,10 @@ EXPORT_SYMBOL(pmu_wait_complete); EXPORT_SYMBOL(pmu_suspend); EXPORT_SYMBOL(pmu_resume); EXPORT_SYMBOL(pmu_unlock); -#if defined(CONFIG_PM) && 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 && CONFIG_PPC32 */ +#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */ |
