--- zzzz-none-000/linux-2.4.17/drivers/macintosh/via-pmu.c 2001-09-08 19:38:42.000000000 +0000 +++ sangam-fb-322/linux-2.4.17/drivers/macintosh/via-pmu.c 2004-11-24 13:23:25.000000000 +0000 @@ -9,9 +9,9 @@ * and the RTC (real time clock) chip. * * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi. + * Copyright (C) 2001 Benjamin Herrenschmidt * - * todo: - Check this driver for smp safety (new Core99 motherboards). - * - Cleanup synchro between VIA interrupt and GPIO-based PMU + * todo: - Cleanup synchro between VIA interrupt and GPIO-based PMU * interrupt. * * @@ -45,10 +45,12 @@ #include #include #include -#include +#include #include #include +#include #include +#include #ifdef CONFIG_PMAC_BACKLIGHT #include #endif @@ -56,9 +58,13 @@ /* Some compile options */ #undef SUSPEND_USES_PMU #define DEBUG_SLEEP +#undef HACKED_PCI_SAVE /* Misc minor number allocated for /dev/pmu */ -#define PMU_MINOR 154 +#define PMU_MINOR 154 + +/* How many iterations between battery polls */ +#define BATTERY_POLLING_COUNT 2 static volatile unsigned char *via; @@ -108,7 +114,7 @@ static struct adb_request *current_req; static struct adb_request *last_req; static struct adb_request *req_awaiting_reply; -static unsigned char interrupt_data[32]; +static unsigned char interrupt_data[256]; /* Made bigger: I've been told that might happen */ static unsigned char *reply_ptr; static int data_index; static int data_len; @@ -126,9 +132,26 @@ static int pmu_version; static int drop_interrupts; #ifdef CONFIG_PMAC_PBOOK +static int option_lid_wakeup = 1; static int sleep_in_progress; +static int can_sleep; +#endif /* CONFIG_PMAC_PBOOK */ + +static struct proc_dir_entry *proc_pmu_root; +static struct proc_dir_entry *proc_pmu_info; +static struct proc_dir_entry *proc_pmu_options; + +#ifdef CONFIG_PMAC_PBOOK +int pmu_battery_count; +int pmu_cur_battery; +unsigned int pmu_power_flags; +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]; #endif /* CONFIG_PMAC_PBOOK */ +int __fake_sleep; int asleep; struct notifier_block *sleep_notifier_list; @@ -153,15 +176,22 @@ static void pmu_done(struct adb_request *req); static void pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs); -static void set_volume(int level); static void 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); #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 */ #ifdef CONFIG_PMAC_PBOOK static void pmu_pass_intr(unsigned char *data, int len); -#endif +static int proc_get_batt(char *page, char **start, off_t off, + int count, int *eof, void *data); +#endif /* CONFIG_PMAC_PBOOK */ +static int proc_read_options(char *page, char **start, off_t off, + int count, int *eof, void *data); +static int proc_write_options(struct file *file, const char *buffer, + unsigned long count, void *data); #ifdef CONFIG_ADB struct adb_driver via_pmu_driver = { @@ -309,6 +339,10 @@ } else pmu_kind = PMU_UNKNOWN; +#ifdef CONFIG_PMAC_PBOOK + if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0) + can_sleep = 1; +#endif /* CONFIG_PMAC_PBOOK */ via = (volatile unsigned char *) ioremap(vias->addrs->address, 0x2000); out_8(&via[IER], IER_CLR | 0x7f); /* disable all intrs */ @@ -362,9 +396,14 @@ if (vias == NULL) return -ENODEV; + request_OF_resource(vias, 0, NULL); + bright_req_1.complete = 1; bright_req_2.complete = 1; bright_req_3.complete = 1; +#ifdef CONFIG_PMAC_PBOOK + batt_req.complete = 1; +#endif if (request_irq(vias->intrs[0].line, via_pmu_interrupt, 0, "VIA-PMU", (void *)0)) { @@ -388,6 +427,46 @@ register_backlight_controller(&pmu_backlight_controller, NULL, "pmu"); #endif /* CONFIG_PMAC_BACKLIGHT */ +#ifdef CONFIG_PMAC_PBOOK + if (machine_is_compatible("AAPL,3400/2400") || + machine_is_compatible("AAPL,3500")) + pmu_battery_count = 1; + else if (machine_is_compatible("AAPL,PowerBook1998") || + machine_is_compatible("PowerBook1,1")) + pmu_battery_count = 2; + else { + struct device_node* prim = find_devices("power-mgt"); + u32 *prim_info = NULL; + if (prim) + prim_info = (u32 *)get_property(prim, "prim-info", NULL); + if (prim_info) { + /* Other stuffs here yet unknown */ + pmu_battery_count = (prim_info[6] >> 16) & 0xff; + } + } +#endif /# CONFIG_PMAC_PBOOK */ + /* Create /proc/pmu */ + proc_pmu_root = proc_mkdir("pmu", 0); + if (proc_pmu_root) { + int i; + proc_pmu_info = create_proc_read_entry("info", 0, proc_pmu_root, + proc_get_info, NULL); +#ifdef CONFIG_PMAC_PBOOK + for (i=0; inlink = 1; + proc_pmu_options->read_proc = proc_read_options; + proc_pmu_options->write_proc = proc_write_options; + } + } + /* Make sure PMU settle down before continuing. This is _very_ important * since the IDE probe may shut interrupts down for quite a bit of time. If * a PMU communication is pending while this happens, the PMU may timeout @@ -448,8 +527,8 @@ pmu_request(&req, NULL, 1, PMU_GET_VERSION); while (!req.complete) pmu_poll(); - if (req.reply_len > 1) - pmu_version = req.reply[1]; + if (req.reply_len > 0) + pmu_version = req.reply[0]; return 1; } @@ -460,6 +539,263 @@ return pmu_kind; } +#ifdef CONFIG_PMAC_PBOOK + +/* + * WARNING ! This code probably needs some debugging... -- BenH. + */ +static void __pmac +done_battery_state_ohare(struct adb_request* req) +{ + unsigned int bat_flags = 0; + int current = 0; + unsigned int capa, max, voltage, time; + int lrange[] = { 0, 275, 850, 1680, 2325, + 2765, 3160, 3500, 3830, 4115, + 4360, 4585, 4795, 4990, 5170, + 5340, 5510, 5710, 5930, 6150, + 6370, 6500 + }; + + if (req->reply[0] & 0x01) + pmu_power_flags |= PMU_PWR_AC_PRESENT; + else + pmu_power_flags &= ~PMU_PWR_AC_PRESENT; + + if (req->reply[0] & 0x04) { + int vb, i, j, charge, pcharge; + bat_flags |= PMU_BATT_PRESENT; + vb = (req->reply[1] << 8) | req->reply[2]; + voltage = ((vb * 2650) + 726650)/100; + current = *((signed char *)&req->reply[5]); + if ((req->reply[0] & 0x01) == 0 && (current > 200)) + vb += (current - 200) * 15; + else if (req->reply[0] & 0x02) + vb = (vb - 10) * 100; + i = (33000 - vb) / 10; + j = i - (i % 100); + if (j <= 0) + charge = 0; + else if (j >= 21) + charge = 650000; + else + charge = (lrange[j + 1] - lrange[j]) * (i - j) + (lrange[j] * 100); + charge = (1000 - charge / 650) / 10; + if (req->reply[0] & 0x40) { + pcharge = (req->reply[6] << 8) + req->reply[7]; + if (pcharge > 6500) + pcharge = 6500; + pcharge *= 100; + pcharge = (1000 - pcharge / 650) / 10; + if (pcharge < charge) + charge = pcharge; + } + capa = charge; + max = 100; + time = (charge * 274) / current; + current = -current; + + } else + capa = max = current = voltage = time = 0; + + if ((req->reply[0] & 0x02) && (current > 0)) + bat_flags |= PMU_BATT_CHARGING; + if (req->reply[0] & 0x04) /* CHECK THIS ONE */ + bat_flags |= PMU_BATT_PRESENT; + + pmu_batteries[pmu_cur_battery].flags = bat_flags; + pmu_batteries[pmu_cur_battery].charge = capa; + pmu_batteries[pmu_cur_battery].max_charge = max; + pmu_batteries[pmu_cur_battery].current = current; + pmu_batteries[pmu_cur_battery].voltage = voltage; + pmu_batteries[pmu_cur_battery].time_remaining = time; +} + +static void __pmac +done_battery_state_smart(struct adb_request* req) +{ + /* format: + * [0] : format of this structure (known: 3,4,5) + * [1] : flags + * + * format 3 & 4: + * + * [2] : charge + * [3] : max charge + * [4] : current + * [5] : voltage + * + * format 5: + * + * [2][3] : charge + * [4][5] : max charge + * [6][7] : current + * [8][9] : voltage + */ + + unsigned int bat_flags = 0; + int current; + unsigned int capa, max, voltage; + + if (req->reply[1] & 0x01) + pmu_power_flags |= PMU_PWR_AC_PRESENT; + else + pmu_power_flags &= ~PMU_PWR_AC_PRESENT; + + + if (req->reply[1] & 0x04) { + bat_flags |= PMU_BATT_PRESENT; + switch(req->reply[0]) { + case 3: + case 4: capa = req->reply[2]; + max = req->reply[3]; + current = *((signed char *)&req->reply[4]); + voltage = req->reply[5]; + break; + case 5: capa = (req->reply[2] << 8) | req->reply[3]; + max = (req->reply[4] << 8) | req->reply[5]; + current = *((signed short *)&req->reply[6]); + 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]); + break; + } + } else + capa = max = current = voltage = 0; + + if ((req->reply[1] & 0x01) && (current > 0)) + bat_flags |= PMU_BATT_CHARGING; + + pmu_batteries[pmu_cur_battery].flags = bat_flags; + pmu_batteries[pmu_cur_battery].charge = capa; + pmu_batteries[pmu_cur_battery].max_charge = max; + pmu_batteries[pmu_cur_battery].current = current; + pmu_batteries[pmu_cur_battery].voltage = voltage; + if (current) { + if ((req->reply[1] & 0x01) && (current > 0)) + pmu_batteries[pmu_cur_battery].time_remaining + = ((max-capa) * 3600) / current; + else + pmu_batteries[pmu_cur_battery].time_remaining + = (capa * 3600) / (-current); + } else + pmu_batteries[pmu_cur_battery].time_remaining = 0; + + pmu_cur_battery = (pmu_cur_battery + 1) % pmu_battery_count; +} + +static void __pmac +query_battery_state(void) +{ + if (!batt_req.complete) + return; + if (pmu_kind == PMU_OHARE_BASED) + pmu_request(&batt_req, done_battery_state_ohare, + 1, PMU_BATTERY_STATE); + else + pmu_request(&batt_req, done_battery_state_smart, + 2, PMU_SMART_BATTERY_STATE, pmu_cur_battery+1); +} + +#endif /* CONFIG_PMAC_PBOOK */ + +static int +proc_get_info(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char* p = page; + + p += sprintf(p, "PMU driver version : %d\n", PMU_DRIVER_VERSION); + p += sprintf(p, "PMU firmware version : %02x\n", pmu_version); +#ifdef CONFIG_PMAC_PBOOK + p += sprintf(p, "AC Power : %d\n", + ((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0)); + p += sprintf(p, "Battery count : %d\n", pmu_battery_count); +#endif /* CONFIG_PMAC_PBOOK */ + + return p - page; +} + +#ifdef CONFIG_PMAC_PBOOK +static int +proc_get_batt(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int batnum = (int)data; + char *p = page; + + 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].current); + 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; +} +#endif /* CONFIG_PMAC_PBOOK */ + +static int +proc_read_options(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + +#ifdef CONFIG_PMAC_PBOOK + if (pmu_kind == PMU_KEYLARGO_BASED && can_sleep) + p += sprintf(p, "lid_wakeup=%d\n", option_lid_wakeup); +#endif /* CONFIG_PMAC_PBOOK */ + + return p - page; +} + +static int +proc_write_options(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + char tmp[33]; + char *label, *val; + unsigned long fcount = count; + + if (!count) + return -EINVAL; + if (count > 32) + count = 32; + if (copy_from_user(tmp, buffer, count)) + return -EFAULT; + tmp[count] = 0; + + label = tmp; + while(*label == ' ') + label++; + val = label; + while(*val && (*val != '=')) { + if (*val == ' ') + *val = 0; + val++; + } + if ((*val) == 0) + return -EINVAL; + *(val++) = 0; + while(*val == ' ') + val++; +#ifdef CONFIG_PMAC_PBOOK + if (pmu_kind == PMU_KEYLARGO_BASED && can_sleep) + if (!strcmp(label, "lid_wakeup")) + option_lid_wakeup = ((*val) == '1'); +#endif /* CONFIG_PMAC_PBOOK */ + return fcount; +} + #ifdef CONFIG_ADB /* Send an ADB command */ static int __openfirmware @@ -622,11 +958,7 @@ for (i = 0; i < nbytes; ++i) req->data[i] = va_arg(list, int); va_end(list); - if (pmu_data_len[req->data[0]][1] != 0) { - req->reply[0] = ADB_RET_OK; - req->reply_len = 1; - } else - req->reply_len = 0; + req->reply_len = 0; req->reply_expected = 0; return pmu_queue_request(req); } @@ -835,10 +1167,14 @@ spin_lock_irqsave(&pmu_lock, flags); ++disable_poll; - while ((intr = in_8(&via[IFR])) != 0) { + for (;;) { + intr = in_8(&via[IFR]) & (SR_INT | CB1_INT); + if (intr == 0) + break; if (++nloop > 1000) { printk(KERN_DEBUG "PMU: stuck in intr loop, " - "intr=%x pmu_state=%d\n", intr, pmu_state); + "intr=%x, ier=%x pmu_state=%d\n", + intr, in_8(&via[IER]), pmu_state); break; } out_8(&via[IFR], intr); @@ -847,15 +1183,6 @@ else if (intr & CB1_INT) adb_int_pending = 1; } - /* This is not necessary except if synchronous ADB requests are done - * with interrupts off, which should not happen. Since I'm not sure - * this "wiring" will remain, I'm commenting it out for now. Please do - * not remove. -- BenH. - */ -#if 0 - if (gpio_reg && !pmu_suspended && (in_8(gpio_reg + 0x9) & 0x02) == 0) - adb_int_pending = 1; -#endif if (pmu_state == idle) { if (adb_int_pending) { @@ -866,9 +1193,8 @@ wait_for_ack(); send_byte(PMU_INT_ACK); adb_int_pending = 0; - } else if (current_req) { + } else if (current_req) pmu_start(); - } } --disable_poll; @@ -878,8 +1204,10 @@ static void __openfirmware gpio1_interrupt(int irq, void *arg, struct pt_regs *regs) { - adb_int_pending = 1; - via_pmu_interrupt(0, 0, 0); + if ((in_8(gpio_reg + 0x9) & 0x02) == 0) { + adb_int_pending = 1; + via_pmu_interrupt(0, 0, 0); + } } static void __openfirmware @@ -1040,16 +1368,25 @@ adb_input(data+1, len-1, regs, 1); #endif /* CONFIG_ADB */ } - } else if (data[0] == 0x08 && len == 3) { - /* sound/brightness buttons pressed */ + } else { + /* Sound/brightness button pressed */ + if ((data[0] & PMU_INT_SNDBRT) && len == 3) { #ifdef CONFIG_PMAC_BACKLIGHT - set_backlight_level(data[1] >> 4); + set_backlight_level(data[1] >> 4); #endif - set_volume(data[2]); - } else { + } #ifdef CONFIG_PMAC_PBOOK - pmu_pass_intr(data, len); -#endif + /* Environement or tick interrupt, query batteries */ + if (pmu_battery_count && (data[0] & PMU_INT_TICK)) { + if ((--query_batt_timer) == 0) { + query_battery_state(); + query_batt_timer = BATTERY_POLLING_COUNT; + } + } else if (pmu_battery_count && (data[0] & PMU_INT_ENVIRONMENT)) + query_battery_state(); + if (data[0]) + pmu_pass_intr(data, len); +#endif /* CONFIG_PMAC_PBOOK */ } } @@ -1116,11 +1453,6 @@ pmu_poll(); } -static void __openfirmware -set_volume(int level) -{ -} - void __openfirmware pmu_restart(void) { @@ -1266,10 +1598,14 @@ * 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 n_pbook_pci_saves; @@ -1293,14 +1629,24 @@ return; pci_for_each_dev(pd) { +#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 __openfirmware pbook_pci_restore(void) { @@ -1310,7 +1656,13 @@ int j; pci_for_each_dev(pd) { +#ifdef HACKED_PCI_SAVE + int i; 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 (ps->command == 0) continue; pci_read_config_word(pd, PCI_COMMAND, &cmd); @@ -1330,8 +1682,8 @@ ps->intr); pci_write_config_word(pd, PCI_COMMAND, ps->command); break; - /* other header types not restored at present */ } +#endif } } @@ -1370,7 +1722,7 @@ } mdelay(50); } -#endif /* DEBUG_SLEEP */ +#endif /* * Put the powerbook to sleep. @@ -1403,6 +1755,15 @@ out_8(&via[IER], IER_SET | SR_INT | CB1_INT); } +static inline void wakeup_decrementer(void) +{ + set_dec(tb_ticks_per_jiffy); + /* No currently-supported powerbook has a 601, + * so use get_tbl, not native + */ + last_jiffy_stamp(0) = tb_last_stamp = get_tbl(); +} + #define GRACKLE_PM (1<<7) #define GRACKLE_DOZE (1<<5) #define GRACKLE_NAP (1<<4) @@ -1436,6 +1797,10 @@ * vmalloc's are done before actual sleep of block drivers */ fsync_dev(0); + /* Give the disks a little time to actually finish writing */ + for (wait = jiffies + (HZ/2); time_before(jiffies, wait); ) + mb(); + /* 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) { @@ -1443,13 +1808,9 @@ return -EBUSY; } - /* Give the disks a little time to actually finish writing */ - for (wait = jiffies + (HZ/2); time_before(jiffies, wait); ) - mb(); - /* Wait for completion of async backlight requests */ while (!bright_req_1.complete || !bright_req_2.complete || - !bright_req_3.complete) + !bright_req_3.complete || !batt_req.complete) pmu_poll(); /* Turn off various things. Darwin does some retry tests here... */ @@ -1495,7 +1856,7 @@ /* The VIA is supposed not to be restored correctly*/ save_via_state(); /* We shut down some HW */ - feature_prepare_for_sleep(); + pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1); pci_read_config_word(grackle, 0x70, &pmcr1); /* Apparently, MacOS uses NAP mode for Grackle ??? */ @@ -1512,7 +1873,7 @@ pci_write_config_word(grackle, 0x70, pmcr1); /* Make sure the PMU is idle */ - feature_wake_up(); + pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0); restore_via_state(); /* Restore L2 cache */ @@ -1522,11 +1883,6 @@ /* Restore userland MMU context */ set_context(current->active_mm->context, current->active_mm->pgd); - /* Re-enable DEC interrupts and kick DEC */ - asm volatile("mtdec %0" : : "r" (0x7fffffff)); - sti(); - asm volatile("mtdec %0" : : "r" (0x10000000)); - /* Power things up */ pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0xfc); while (!req.complete) @@ -1558,6 +1914,10 @@ /* Leave some time for HW to settle down */ mdelay(100); + /* Restart jiffies & scheduling */ + wakeup_decrementer(); + sti(); + /* Notify drivers */ broadcast_wake(); @@ -1571,7 +1931,7 @@ struct adb_request req; int ret, timeout; - if (!feature_can_sleep()) { + if (!can_sleep) { printk(KERN_ERR "Sleep mode not supported on this machine\n"); return -ENOSYS; } @@ -1591,20 +1951,19 @@ * vmalloc's are done before actual sleep of block drivers */ fsync_dev(0); + /* Give the disks a little time to actually finish writing */ + for (wait = jiffies + HZ; time_before(jiffies, wait); ) + mb(); + /* 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("pmu: sleep failed\n"); return -EBUSY; } - - /* Give the disks a little time to actually finish writing */ - for (wait = jiffies + HZ; time_before(jiffies, wait); ) - mb(); - /* Wait for completion of async backlight requests */ while (!bright_req_1.complete || !bright_req_2.complete || - !bright_req_3.complete) + !bright_req_3.complete || !batt_req.complete) pmu_poll(); /* Tell PMU what events will wake us up */ @@ -1614,7 +1973,8 @@ pmu_poll(); pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_SET_WAKEUP_EVENTS, - 0, PMU_PWR_WAKEUP_KEY | PMU_PWR_WAKEUP_LID_OPEN); + 0, PMU_PWR_WAKEUP_KEY | + (option_lid_wakeup ? PMU_PWR_WAKEUP_LID_OPEN : 0)); while (!req.complete) pmu_poll(); @@ -1651,10 +2011,12 @@ /* Save the state of PCI config space for some slots */ //pbook_pci_save(); - /* Ask the PMU to put us to sleep */ - pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); - while (!req.complete && pmu_state != idle) - pmu_poll(); + if (!__fake_sleep) { + /* Ask the PMU to put us to sleep */ + pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); + while (!req.complete && pmu_state != idle) + pmu_poll(); + } out_8(&via[B], in_8(&via[B]) | TREQ); wait_for_ack(); @@ -1666,13 +2028,16 @@ * talk to the PMU after this, so I moved it to _after_ sending the * sleep command to it. Still need to be checked. */ - feature_prepare_for_sleep(); + pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1); /* Call low-level ASM sleep handler */ - low_sleep_handler(); + if (__fake_sleep) + mdelay(5000); + else + low_sleep_handler(); /* Restore Apple core ASICs state */ - feature_wake_up(); + pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0); /* Restore VIA */ restore_via_state(); @@ -1694,11 +2059,6 @@ /* Restore userland MMU context */ set_context(current->active_mm->context, current->active_mm->pgd); - /* Re-enable DEC interrupts and kick DEC */ - asm volatile("mtdec %0" : : "r" (0x7fffffff)); - sti(); - asm volatile("mtdec %0" : : "r" (0x10000000)); - /* Tell PMU we are ready */ pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2); while (!req.complete) @@ -1725,6 +2085,10 @@ /* Leave some time for HW to settle down */ mdelay(100); + /* Restart jiffies & scheduling */ + wakeup_decrementer(); + sti(); + /* Notify drivers */ broadcast_wake(); @@ -1766,6 +2130,10 @@ * vmalloc's are done before actual sleep of block drivers */ fsync_dev(0); + /* Give the disks a little time to actually finish writing */ + for (wait = jiffies + (HZ/4); time_before(jiffies, wait); ) + mb(); + /* 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) { @@ -1773,13 +2141,9 @@ return -EBUSY; } - /* Give the disks a little time to actually finish writing */ - for (wait = jiffies + (HZ/4); time_before(jiffies, wait); ) - mb(); - /* Wait for completion of async backlight requests */ while (!bright_req_1.complete || !bright_req_2.complete || - !bright_req_3.complete) + !bright_req_3.complete || !batt_req.complete) pmu_poll(); /* Disable all interrupts except pmu */ @@ -1811,6 +2175,8 @@ while (!sleep_req.complete) mb(); + pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1); + /* displacement-flush the L2 cache - necessary? */ for (p = KERNELBASE; p < KERNELBASE + 0x100000; p += 0x1000) i = *(volatile int *)p; @@ -1825,20 +2191,23 @@ /* 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(); /* wait for the PMU interrupt sequence to complete */ while (asleep) mb(); - /* Re-enable DEC interrupts and kick DEC */ - asm volatile("mtdec %0" : : "r" (0x7fffffff)); - sti(); - asm volatile("mtdec %0" : : "r" (0x10000000)); - /* reenable interrupts */ pmac_sleep_restore_intrs(); + /* Leave some time for HW to settle down */ + mdelay(100); + + /* Restart jiffies & scheduling */ + wakeup_decrementer(); + sti(); + /* Notify drivers */ broadcast_wake(); @@ -1877,6 +2246,7 @@ spin_lock_irqsave(&all_pvt_lock, flags); for (list = &all_pmu_pvt; (list = list->next) != &all_pmu_pvt; ) { pp = list_entry(list, struct pmu_private, list); + spin_lock(&pp->lock); i = pp->rb_put + 1; if (i >= RB_SIZE) i = 0; @@ -1887,6 +2257,7 @@ pp->rb_put = i; wake_up_interruptible(&pp->wait); } + spin_unlock(&pp->lock); } spin_unlock_irqrestore(&all_pvt_lock, flags); } @@ -1914,6 +2285,7 @@ { struct pmu_private *pp = file->private_data; DECLARE_WAITQUEUE(wait, current); + unsigned long flags; int ret; if (count < 1 || pp == 0) @@ -1922,38 +2294,41 @@ if (ret) return ret; + spin_lock_irqsave(&pp->lock, flags); add_wait_queue(&pp->wait, &wait); current->state = TASK_INTERRUPTIBLE; for (;;) { ret = -EAGAIN; - spin_lock(&pp->lock); if (pp->rb_get != pp->rb_put) { int i = pp->rb_get; struct rb_entry *rp = &pp->rb_buf[i]; ret = rp->len; + spin_unlock_irqrestore(&pp->lock, flags); if (ret > count) ret = count; if (ret > 0 && copy_to_user(buf, rp->data, ret)) ret = -EFAULT; if (++i >= RB_SIZE) i = 0; + spin_lock_irqsave(&pp->lock, flags); pp->rb_get = i; } - spin_unlock(&pp->lock); if (ret >= 0) break; - if (file->f_flags & O_NONBLOCK) break; ret = -ERESTARTSYS; if (signal_pending(current)) break; + spin_unlock_irqrestore(&pp->lock, flags); schedule(); + spin_lock_irqsave(&pp->lock, flags); } current->state = TASK_RUNNING; remove_wait_queue(&pp->wait, &wait); - + spin_unlock_irqrestore(&pp->lock, flags); + return ret; } @@ -1967,14 +2342,15 @@ { struct pmu_private *pp = filp->private_data; unsigned int mask = 0; - + unsigned long flags; + if (pp == 0) return 0; poll_wait(filp, &pp->wait, wait); - spin_lock(&pp->lock); + spin_lock_irqsave(&pp->lock, flags); if (pp->rb_get != pp->rb_put) mask |= POLLIN; - spin_unlock(&pp->lock); + spin_unlock_irqrestore(&pp->lock, flags); return mask; } @@ -2025,7 +2401,7 @@ sleep_in_progress = 0; return error; case PMU_IOC_CAN_SLEEP: - return put_user(feature_can_sleep(), (__u32 *)arg); + return put_user((u32)can_sleep, (__u32 *)arg); #ifdef CONFIG_PMAC_BACKLIGHT /* Backlight should have its own device or go via @@ -2155,5 +2531,8 @@ EXPORT_SYMBOL(pmu_register_sleep_notifier); EXPORT_SYMBOL(pmu_unregister_sleep_notifier); EXPORT_SYMBOL(pmu_enable_irled); +EXPORT_SYMBOL(pmu_battery_count); +EXPORT_SYMBOL(pmu_batteries); +EXPORT_SYMBOL(pmu_power_flags); #endif /* CONFIG_PMAC_PBOOK */