patch-2.4.18 linux/drivers/macintosh/via-pmu.c
Next file: linux/drivers/md/lvm-fs.c
Previous file: linux/drivers/macintosh/via-cuda.c
Back to the patch index
Back to the overall index
- Lines: 999
- Date:
Mon Jan 21 18:49:12 2002
- Orig file:
linux.orig/drivers/macintosh/via-pmu.c
- Orig date:
Mon Feb 18 20:18:39 2002
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/via-pmu.c linux/drivers/macintosh/via-pmu.c
@@ -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 <asm/sections.h>
#include <asm/irq.h>
#include <asm/hardirq.h>
-#include <asm/feature.h>
+#include <asm/pmac_feature.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
+#include <asm/sections.h>
#include <asm/cputable.h>
+#include <asm/time.h>
#ifdef CONFIG_PMAC_BACKLIGHT
#include <asm/backlight.h>
#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; i<pmu_battery_count; i++) {
+ char title[16];
+ sprintf(title, "battery_%d", i);
+ proc_pmu_batt[i] = create_proc_read_entry(title, 0, proc_pmu_root,
+ proc_get_batt, (void *)i);
+ }
+#endif /* CONFIG_PMAC_PBOOK */
+ 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;
+ }
+ }
+
/* 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,27 +1167,22 @@
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);
if (intr & SR_INT)
pmu_sr_intr(regs);
- else if (intr & CB1_INT)
+ 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 */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)