patch-2.4.0-test9 linux/drivers/macintosh/via-pmu.c
Next file: linux/drivers/md/Config.in
Previous file: linux/drivers/macintosh/via-cuda.c
Back to the patch index
Back to the overall index
- Lines: 993
- Date:
Sun Sep 17 09:48:05 2000
- Orig file:
v2.4.0-test8/linux/drivers/macintosh/via-pmu.c
- Orig date:
Wed Jul 12 21:58:42 2000
diff -u --recursive --new-file v2.4.0-test8/linux/drivers/macintosh/via-pmu.c linux/drivers/macintosh/via-pmu.c
@@ -32,6 +32,7 @@
#include <linux/pmu.h>
#include <linux/cuda.h>
#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
@@ -39,10 +40,16 @@
#include <asm/system.h>
#include <asm/init.h>
#include <asm/irq.h>
+#include <asm/hardirq.h>
#include <asm/feature.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
-#include <asm/heathrow.h>
+#ifdef CONFIG_PMAC_BACKLIGHT
+#include <asm/backlight.h>
+#endif
+
+/* Some compile options */
+#undef SUSPEND_USES_PMU
/* Misc minor number allocated for /dev/pmu */
#define PMU_MINOR 154
@@ -84,7 +91,7 @@
#define CB2_INT 0x08
#define CB1_INT 0x10 /* transition on CB1 input */
-static enum pmu_state {
+static volatile enum pmu_state {
idle,
sending,
intack,
@@ -95,7 +102,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;
@@ -106,22 +113,27 @@
static struct device_node *vias;
static int pmu_kind = PMU_UNKNOWN;
static int pmu_fully_inited = 0;
-static int pmu_has_adb, pmu_has_backlight;
+static int pmu_has_adb;
static unsigned char *gpio_reg = NULL;
-static int gpio_irq;
+static int gpio_irq = -1;
+static volatile int pmu_suspended = 0;
+static spinlock_t pmu_lock;
int asleep;
struct notifier_block *sleep_notifier_list;
+#ifdef CONFIG_ADB
static int pmu_probe(void);
static int pmu_init(void);
+static int pmu_send_request(struct adb_request *req, int sync);
+static int pmu_adb_autopoll(int devs);
+static int pmu_adb_reset_bus(void);
+#endif /* CONFIG_ADB */
+
static int init_pmu(void);
static int pmu_queue_request(struct adb_request *req);
static void pmu_start(void);
static void via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs);
-static int pmu_send_request(struct adb_request *req, int sync);
-static int pmu_adb_autopoll(int devs);
-static int pmu_adb_reset_bus(void);
static void send_byte(int x);
static void recv_byte(void);
static void pmu_sr_intr(struct pt_regs *regs);
@@ -130,20 +142,25 @@
struct pt_regs *regs);
static void set_volume(int level);
static void gpio1_interrupt(int irq, void *arg, struct pt_regs *regs);
+#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
+#ifdef CONFIG_ADB
struct adb_driver via_pmu_driver = {
"PMU",
pmu_probe,
pmu_init,
pmu_send_request,
- /*pmu_queue_request,*/
pmu_adb_autopoll,
pmu_poll,
pmu_adb_reset_bus
};
+#endif /* CONFIG_ADB */
extern void low_sleep_handler(void);
extern void sleep_save_intrs(int);
@@ -206,6 +223,13 @@
"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 __openfirmware
find_via_pmu()
{
@@ -216,17 +240,6 @@
return 0;
if (vias->next != 0)
printk(KERN_WARNING "Warning: only using 1st via-pmu\n");
-#if 0
- { int i;
-
- printk("find_via_pmu: node = %p, addrs =", vias->node);
- for (i = 0; i < vias->n_addrs; ++i)
- printk(" %x(%x)", vias->addrs[i].address, vias->addrs[i].size);
- printk(", intrs =");
- for (i = 0; i < vias->n_intrs; ++i)
- printk(" %x", vias->intrs[i].line);
- printk("\n"); }
-#endif
if (vias->n_addrs < 1 || vias->n_intrs < 1) {
printk(KERN_ERR "via-pmu: %d addresses, %d interrupts!\n",
@@ -235,8 +248,9 @@
return 0;
}
+ spin_lock_init(&pmu_lock);
+
pmu_has_adb = 1;
- pmu_has_backlight = 1;
if (vias->parent->name && ((strcmp(vias->parent->name, "ohare") == 0)
|| device_is_compatible(vias->parent, "ohare")))
@@ -246,9 +260,18 @@
else if (device_is_compatible(vias->parent, "heathrow"))
pmu_kind = PMU_HEATHROW_BASED;
else if (device_is_compatible(vias->parent, "Keylargo")) {
+ struct device_node *gpio, *gpiop;
+
pmu_kind = PMU_KEYLARGO_BASED;
pmu_has_adb = (find_type_devices("adb") != NULL);
- pmu_has_backlight = (find_type_devices("backlight") != NULL);
+
+ gpiop = find_devices("gpio");
+ if (gpiop && gpiop->n_addrs) {
+ gpio_reg = ioremap(gpiop->addrs->address, 0x10);
+ gpio = find_devices("extint-gpio1");
+ if (gpio && gpio->parent == gpiop && gpio->n_intrs)
+ gpio_irq = gpio->intrs[0].line;
+ }
} else
pmu_kind = PMU_UNKNOWN;
@@ -266,10 +289,13 @@
printk(KERN_INFO "PMU driver initialized for %s\n",
pbook_type[pmu_kind]);
+
sys_ctrler = SYS_CTRLER_PMU;
+
return 1;
}
+#ifdef CONFIG_ADB
static int __openfirmware
pmu_probe()
{
@@ -280,9 +306,10 @@
pmu_init(void)
{
if (vias == NULL)
- return -ENXIO;
+ return -ENODEV;
return 0;
}
+#endif /* CONFIG_ADB */
/*
* We can't wait until pmu_init gets called, that happens too late.
@@ -291,10 +318,10 @@
* turned us off.
* This is called from arch/ppc/kernel/pmac_setup.c:pmac_init2().
*/
-void via_pmu_start(void)
+int via_pmu_start(void)
{
if (vias == NULL)
- return;
+ return -ENODEV;
bright_req_1.complete = 1;
bright_req_2.complete = 1;
@@ -304,24 +331,12 @@
(void *)0)) {
printk(KERN_ERR "VIA-PMU: can't get irq %d\n",
vias->intrs[0].line);
- return;
+ return -EAGAIN;
}
- if (pmu_kind == PMU_KEYLARGO_BASED) {
- struct device_node *gpio, *gpiop;
-
- gpiop = find_devices("gpio");
- if (gpiop && gpiop->n_addrs) {
- gpio_reg = ioremap(gpiop->addrs->address, 0x10);
- gpio = find_devices("extint-gpio1");
- if (gpio && gpio->parent == gpiop && gpio->n_intrs) {
- gpio_irq = gpio->intrs[0].line;
- if (request_irq(gpio_irq, gpio1_interrupt, 0,
- "GPIO1/ADB", (void *)0))
- printk(KERN_ERR "pmu: can't get irq %d (GPIO1)\n",
- gpio->intrs[0].line);
- }
- }
+ if (pmu_kind == PMU_KEYLARGO_BASED && gpio_irq != -1) {
+ if (request_irq(gpio_irq, gpio1_interrupt, 0, "GPIO1/ADB", (void *)0))
+ printk(KERN_ERR "pmu: can't get irq %d (GPIO1)\n", gpio_irq);
}
/* Enable interrupts */
@@ -329,8 +344,24 @@
pmu_fully_inited = 1;
+#ifdef CONFIG_PMAC_BACKLIGHT
/* Enable backlight */
- pmu_enable_backlight(1);
+ register_backlight_controller(&pmu_backlight_controller, NULL, "pmu");
+#endif /* CONFIG_PMAC_BACKLIGHT */
+
+ /* 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
+ * Not that on Core99 machines, the PMU keeps sending us environement
+ * messages, we should find a way to either fix IDE or make it call
+ * pmu_suspend() before masking interrupts. This can also happens while
+ * scolling with some fbdevs.
+ */
+ do {
+ pmu_poll();
+ } while (pmu_state != idle);
+
+ return 0;
}
static int __openfirmware
@@ -342,7 +373,7 @@
out_8(&via[B], via[B] | TREQ); /* negate TREQ */
out_8(&via[DIRB], (via[DIRB] | TREQ) & ~TACK); /* TACK in, TREQ out */
- pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0xff);
+ pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0xfc);
timeout = 100000;
while (!req.complete) {
if (--timeout < 0) {
@@ -367,6 +398,13 @@
udelay(10);
}
+ /* Tell PMU we are ready. Which PMU support this ? */
+ if (pmu_kind == PMU_KEYLARGO_BASED) {
+ pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
+ while (!req.complete)
+ pmu_poll();
+ }
+
return 1;
}
@@ -376,6 +414,7 @@
return pmu_kind;
}
+#ifdef CONFIG_ADB
/* Send an ADB command */
static int __openfirmware
pmu_send_request(struct adb_request *req, int sync)
@@ -513,6 +552,7 @@
return 0;
}
+#endif /* CONFIG_ADB */
/* Construct and send a pmu request */
int __openfirmware
@@ -568,8 +608,8 @@
req->next = 0;
req->sent = 0;
req->complete = 0;
- save_flags(flags); cli();
+ spin_lock_irqsave(&pmu_lock, flags);
if (current_req != 0) {
last_req->next = req;
last_req = req;
@@ -579,11 +619,27 @@
if (pmu_state == idle)
pmu_start();
}
+ spin_unlock_irqrestore(&pmu_lock, flags);
- restore_flags(flags);
return 0;
}
+static void __openfirmware
+wait_for_ack(void)
+{
+ /* Sightly increased the delay, I had one occurence of the message
+ * reported
+ */
+ int timeout = 4000;
+ while ((in_8(&via[B]) & TACK) == 0) {
+ if (--timeout < 0) {
+ printk(KERN_ERR "PMU not responding (!ack)\n");
+ return;
+ }
+ udelay(10);
+ }
+}
+
/* New PMU seems to be very sensitive to those timings, so we make sure
* PCI is flushed immediately */
static void __openfirmware
@@ -613,57 +669,126 @@
static void __openfirmware
pmu_start()
{
- unsigned long flags;
struct adb_request *req;
/* assert pmu_state == idle */
/* get the packet to send */
- save_flags(flags); cli();
req = current_req;
if (req == 0 || pmu_state != idle
- || (req->reply_expected && req_awaiting_reply))
- goto out;
+ || (/*req->reply_expected && */req_awaiting_reply))
+ return;
pmu_state = sending;
data_index = 1;
data_len = pmu_data_len[req->data[0]][0];
+ /* Sounds safer to make sure ACK is high before writing. This helped
+ * kill a problem with ADB and some iBooks
+ */
+ wait_for_ack();
/* set the shift register to shift out and send a byte */
- ++disable_poll;
send_byte(req->data[0]);
- --disable_poll;
-
-out:
- restore_flags(flags);
}
void __openfirmware
pmu_poll()
{
- unsigned long flags;
-
+ if (!via)
+ return;
if (disable_poll)
return;
- save_flags(flags);
- cli();
- if ((via[IFR] & (SR_INT | CB1_INT)) ||
- (gpio_reg && (in_8(gpio_reg + 0x9) & 0x02) == 0))
+ /* Kicks ADB read when PMU is suspended */
+ if (pmu_suspended)
+ adb_int_pending = 1;
+ do {
+ via_pmu_interrupt(0, 0, 0);
+ } while (pmu_suspended && (adb_int_pending || pmu_state != idle
+ || req_awaiting_reply));
+}
+
+/* This function loops until the PMU is idle and prevents it from
+ * anwsering to ADB interrupts. pmu_request can still be called.
+ * This is done to avoid spurrious shutdowns when we know we'll have
+ * interrupts switched off for a long time
+ */
+void __openfirmware
+pmu_suspend(void)
+{
+ unsigned long flags;
+#ifdef SUSPEND_USES_PMU
+ struct adb_request *req;
+#endif
+ if (!via)
+ return;
+
+ spin_lock_irqsave(&pmu_lock, flags);
+ pmu_suspended++;
+ if (pmu_suspended > 1) {
+ spin_unlock_irqrestore(&pmu_lock, flags);
+ return;
+ }
+
+ do {
+ spin_unlock(&pmu_lock);
via_pmu_interrupt(0, 0, 0);
- restore_flags(flags);
+ spin_lock(&pmu_lock);
+ 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(gpio_irq);
+ out_8(&via[IER], CB1_INT | IER_CLR);
+ spin_unlock_irqrestore(&pmu_lock, flags);
+#endif /* SUSPEND_USES_PMU */
+ break;
+ }
+ } while (1);
+}
+
+void __openfirmware
+pmu_resume(void)
+{
+ unsigned long flags;
+
+ if (!via || (pmu_suspended < 1))
+ return;
+
+ spin_lock_irqsave(&pmu_lock, flags);
+ pmu_suspended--;
+ if (pmu_suspended > 0) {
+ spin_unlock_irqrestore(&pmu_lock, flags);
+ return;
+ }
+ adb_int_pending = 1;
+#ifdef SUSPEND_USES_PMU
+ pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0xfc);
+ 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 */
}
static void __openfirmware
via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs)
{
+ unsigned long flags;
int intr;
int nloop = 0;
- unsigned long flags;
- /* Currently, we use brute-force cli() for syncing with GPIO
- * interrupt. I'll make this smarter later, along with some
- * spinlocks for SMP */
- save_flags(flags);cli();
+ /* This is a bit brutal, we can probably do better */
+ spin_lock_irqsave(&pmu_lock, flags);
++disable_poll;
+
while ((intr = in_8(&via[IFR])) != 0) {
if (++nloop > 1000) {
printk(KERN_DEBUG "PMU: stuck in intr loop, "
@@ -681,25 +806,38 @@
out_8(&via[IFR], intr);
}
}
- if (gpio_reg && (in_8(gpio_reg + 0x9) & 0x02) == 0)
+ /* 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) {
pmu_state = intack;
+ /* Sounds safer to make sure ACK is high before writing.
+ * This helped kill a problem with ADB and some iBooks
+ */
+ wait_for_ack();
send_byte(PMU_INT_ACK);
adb_int_pending = 0;
} else if (current_req) {
pmu_start();
}
}
+
--disable_poll;
- restore_flags(flags);
+ spin_unlock_irqrestore(&pmu_lock, flags);
}
static void __openfirmware
gpio1_interrupt(int irq, void *arg, struct pt_regs *regs)
{
+ adb_int_pending = 1;
via_pmu_interrupt(0, 0, 0);
}
@@ -707,7 +845,7 @@
pmu_sr_intr(struct pt_regs *regs)
{
struct adb_request *req;
- int bite, timeout;
+ int bite;
if (via[B] & TREQ) {
printk(KERN_ERR "PMU: spurious SR intr (%x)\n", via[B]);
@@ -720,26 +858,16 @@
if (via[B] & TACK) {
while ((in_8(&via[B]) & TACK) != 0)
;
-#if 0
- printk(KERN_ERR "PMU: sr_intr but ack still high! (%x)\n",
- via[B]);
-#endif
}
/* reset TREQ and wait for TACK to go high */
out_8(&via[B], in_8(&via[B]) | TREQ);
- timeout = 3200;
- while ((in_8(&via[B]) & TACK) == 0) {
- if (--timeout < 0) {
- printk(KERN_ERR "PMU not responding (!ack)\n");
- return;
- }
- udelay(10);
- }
+ wait_for_ack();
/* if reading grab the byte, and reset the interrupt */
if (pmu_state == reading || pmu_state == reading_intr)
bite = in_8(&via[SR]);
+
out_8(&via[IFR], SR_INT);
switch (pmu_state) {
@@ -761,8 +889,11 @@
current_req = req->next;
if (req->reply_expected)
req_awaiting_reply = req;
- else
+ else {
+ spin_unlock(&pmu_lock);
pmu_done(req);
+ spin_lock(&pmu_lock);
+ }
} else {
pmu_state = reading;
data_index = 0;
@@ -795,12 +926,16 @@
}
if (pmu_state == reading_intr) {
+ spin_unlock(&pmu_lock);
pmu_handle_data(interrupt_data, data_index, regs);
+ spin_lock(&pmu_lock);
} else {
req = current_req;
current_req = req->next;
req->reply_len += data_index;
+ spin_unlock(&pmu_lock);
pmu_done(req);
+ spin_lock(&pmu_lock);
}
pmu_state = idle;
@@ -826,6 +961,7 @@
{
asleep = 0;
if (len < 1) {
+// xmon_printk("empty ADB\n");
adb_int_pending = 0;
return;
}
@@ -854,6 +990,7 @@
}
}
#endif /* CONFIG_XMON */
+#ifdef CONFIG_ADB
/*
* XXX On the [23]400 the PMU gives us an up
* event for keycodes 0x74 or 0x75 when the PC
@@ -864,10 +1001,13 @@
&& data[1] == 0x2c && data[3] == 0xff
&& (data[2] & ~1) == 0xf4))
adb_input(data+1, len-1, regs, 1);
+#endif /* CONFIG_ADB */
}
} else if (data[0] == 0x08 && len == 3) {
/* sound/brightness buttons pressed */
- pmu_set_brightness(data[1] >> 3);
+#ifdef CONFIG_PMAC_BACKLIGHT
+ set_backlight_level(data[1] >> 4);
+#endif
set_volume(data[2]);
} else {
#ifdef CONFIG_PMAC_PBOOK
@@ -876,53 +1016,23 @@
}
}
-int backlight_level = -1;
-int backlight_enabled = 0;
-
-#define LEVEL_TO_BRIGHT(lev) ((lev) < 1? 0x7f: 0x4a - ((lev) << 1))
-
-void __openfirmware
-pmu_enable_backlight(int on)
+#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 __openfirmware
+pmu_set_backlight_enable(int on, int level, void* data)
{
struct adb_request req;
+
+ if (vias == NULL)
+ return -ENODEV;
- if ((vias == NULL) || !pmu_has_backlight)
- return;
-
- /* first call: get current backlight value */
- if (on && backlight_level < 0) {
- switch (pmu_kind) {
- case PMU_OHARE_BASED:
- pmu_request(&req, NULL, 2, 0xd9, 0);
- while (!req.complete)
- pmu_poll();
- backlight_level = req.reply[1] >> 3;
- break;
- case PMU_HEATHROW_BASED:
- /* We cannot use nvram_read_byte here (not yet initialized) */
- pmu_request(&req, NULL, 3, PMU_READ_NVRAM, 0x14, 0xe);
- while (!req.complete)
- pmu_poll();
- backlight_level = req.reply[1];
- printk(KERN_DEBUG "pmu: nvram returned bright: %d\n", backlight_level);
- break;
- case PMU_PADDINGTON_BASED:
- case PMU_KEYLARGO_BASED:
- /* the G3 PB 1999 has a backlight node
- and chrp-structured nvram */
- /* XXX should read macos's "blkt" property in nvram
- for this node. For now this ensures that the
- backlight doesn't go off as soon as linux boots. */
- backlight_level = 20;
- break;
- default:
- backlight_enabled = 0;
- return;
- }
- }
if (on) {
pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT,
- LEVEL_TO_BRIGHT(backlight_level));
+ backlight_to_bright[level]);
while (!req.complete)
pmu_poll();
}
@@ -930,35 +1040,28 @@
PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF));
while (!req.complete)
pmu_poll();
- backlight_enabled = on;
+
+ return 0;
}
-void __openfirmware
-pmu_set_brightness(int level)
+static int __openfirmware
+pmu_set_backlight_level(int level, void* data)
{
- int bright;
+ if (vias == NULL)
+ return -ENODEV;
- if ((vias == NULL) || !pmu_has_backlight)
- return ;
+ if (!bright_req_1.complete)
+ return -EAGAIN;
+ pmu_request(&bright_req_1, NULL, 2, PMU_BACKLIGHT_BRIGHT,
+ backlight_to_bright[level]);
+ if (!bright_req_2.complete)
+ return -EAGAIN;
+ pmu_request(&bright_req_2, NULL, 2, PMU_POWER_CTRL, PMU_POW_BACKLIGHT
+ | (level > BACKLIGHT_OFF ? PMU_POW_ON : PMU_POW_OFF));
- backlight_level = level;
- bright = LEVEL_TO_BRIGHT(level);
- if (!backlight_enabled)
- return;
- if (bright_req_1.complete)
- pmu_request(&bright_req_1, NULL, 2, PMU_BACKLIGHT_BRIGHT,
- bright);
- if (bright_req_2.complete)
- pmu_request(&bright_req_2, NULL, 2, PMU_POWER_CTRL,
- PMU_POW_BACKLIGHT | (bright < 0x7f ? PMU_POW_ON : PMU_POW_OFF));
-
- /* XXX nvram address is hard-coded and looks ok on wallstreet, please
- test on your machine. Note that newer MacOS system software may break
- the nvram layout. */
- if ((pmu_kind == PMU_HEATHROW_BASED) && bright_req_3.complete)
- pmu_request(&bright_req_3, NULL, 4, PMU_WRITE_NVRAM,
- 0x14, 0xe, level);
+ return 0;
}
+#endif /* CONFIG_PMAC_BACKLIGHT */
void __openfirmware
pmu_enable_irled(int on)
@@ -967,6 +1070,8 @@
if (vias == NULL)
return ;
+ if (pmu_kind == PMU_KEYLARGO_BASED)
+ return ;
pmu_request(&req, NULL, 2, PMU_POWER_CTRL, PMU_POW_IRLED |
(on ? PMU_POW_ON : PMU_POW_OFF));
@@ -1201,17 +1306,9 @@
{
int ret;
unsigned long save_l2cr;
- unsigned long save_fcr;
unsigned long wait;
unsigned short pmcr1;
- struct adb_request sleep_req;
- struct device_node *macio;
- unsigned long macio_base = 0;
-
- macio = find_devices("mac-io");
- if (macio != 0 && macio->n_addrs > 0)
- macio_base = (unsigned long)
- ioremap(macio->addrs[0].address, 0x40);
+ struct adb_request req;
/* Notify device drivers */
ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, PBOOK_SLEEP_REJECT);
@@ -1245,14 +1342,13 @@
/* Make sure the decrementer won't interrupt us */
asm volatile("mtdec %0" : : "r" (0x7fffffff));
+ feature_prepare_for_sleep();
+
/* For 750, save backside cache setting and disable it */
save_l2cr = _get_L2CR(); /* (returns 0 if not 750) */
if (save_l2cr)
_set_L2CR(0);
- if (macio_base != 0)
- save_fcr = in_le32(FEATURE_CTRL(macio_base));
-
if (current->thread.regs && (current->thread.regs->msr & MSR_FP) != 0)
giveup_fpu(current);
@@ -1263,17 +1359,14 @@
grackle_pcibios_write_config_word(0, 0, 0x70, pmcr1);
/* Ask the PMU to put us to sleep */
- pmu_request(&sleep_req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
- while (!sleep_req.complete)
+ pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
+ while (!req.complete)
pmu_poll();
cli();
while (pmu_state != idle)
pmu_poll();
- /* clear IOBUS enable */
- out_le32(FEATURE_CTRL(macio_base), save_fcr & ~HRW_IOBUS_ENABLE);
-
/* Call low-level ASM sleep handler */
low_sleep_handler();
@@ -1282,15 +1375,14 @@
pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP);
grackle_pcibios_write_config_word(0, 0, 0x70, pmcr1);
- /* reenable IOBUS */
- out_le32(FEATURE_CTRL(macio_base), save_fcr | HRW_IOBUS_ENABLE);
-
/* Make sure the PMU is idle */
while (pmu_state != idle)
pmu_poll();
sti();
+ feature_wake_up();
+
/* The PGD is only a placeholder until Dan finds a way to make
* this work properly on the 8xx processors. It is only used on
* 8xx processors, it is ignored here.
@@ -1304,6 +1396,116 @@
/* reenable interrupts */
sleep_restore_intrs();
+ /* Tell PMU we are ready */
+ pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
+ while (!req.complete)
+ pmu_poll();
+
+ /* Notify drivers */
+ mdelay(10);
+ broadcast_wake();
+
+ return 0;
+}
+
+/* Not finished yet */
+int __openfirmware powerbook_sleep_Core99(void)
+{
+ int ret;
+ unsigned long save_l2cr;
+ unsigned long wait;
+ struct adb_request req;
+
+ /* Notify device drivers */
+ ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, PBOOK_SLEEP_REJECT);
+ if (ret != PBOOK_SLEEP_OK) {
+ printk("pmu: sleep rejected\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.
+ * BenH: Moved to _after_ sleep request and changed video
+ * drivers to vmalloc() during sleep request. This way, all
+ * vmalloc's are done before actual sleep of block drivers */
+ fsync_dev(0);
+
+ /* 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/4); time_before(jiffies, wait); )
+ mb();
+
+ /* Tell PMU what events will wake us up */
+ pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_CLR_WAKEUP_EVENTS,
+ 0xff, 0xff);
+ while (!req.complete)
+ 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);
+ while (!req.complete)
+ pmu_poll();
+
+ /* Disable all interrupts except pmu */
+ sleep_save_intrs(vias->intrs[0].line);
+
+ /* Make sure the decrementer won't interrupt us */
+ asm volatile("mtdec %0" : : "r" (0x7fffffff));
+
+ /* Save the state of PCI config space for some slots */
+ pbook_pci_save();
+
+ feature_prepare_for_sleep();
+
+ /* For 750, save backside cache setting and disable it */
+ save_l2cr = _get_L2CR(); /* (returns 0 if not 750) */
+ if (save_l2cr)
+ _set_L2CR(0);
+
+ if (current->thread.regs && (current->thread.regs->msr & MSR_FP) != 0)
+ giveup_fpu(current);
+
+ /* Ask the PMU to put us to sleep */
+ pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
+ while (!req.complete)
+ mb();
+
+ cli();
+ while (pmu_state != idle)
+ pmu_poll();
+
+ /* Call low-level ASM sleep handler */
+ low_sleep_handler();
+
+ /* Make sure the PMU is idle */
+ while (pmu_state != idle)
+ pmu_poll();
+
+ sti();
+
+ feature_wake_up();
+ pbook_pci_restore();
+
+ set_context(current->mm->context, current->mm->pgd);
+
+ /* Restore L2 cache */
+ if (save_l2cr)
+ _set_L2CR(save_l2cr | 0x200000); /* set invalidate bit */
+
+ /* reenable interrupts */
+ sleep_restore_intrs();
+
+ /* Tell PMU we are ready */
+ pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
+ while (!req.complete)
+ pmu_poll();
+
/* Notify drivers */
mdelay(10);
broadcast_wake();
@@ -1557,7 +1759,6 @@
u_int cmd, u_long arg)
{
int error;
- __u32 value;
switch (cmd) {
case PMU_IOC_SLEEP:
@@ -1569,21 +1770,33 @@
case PMU_PADDINGTON_BASED:
error = powerbook_sleep_G3();
break;
+#if 0 /* Not ready yet */
+ case PMU_KEYLARGO_BASED:
+ error = powerbook_sleep_Core99();
+ break;
+#endif
default:
error = -ENOSYS;
}
return error;
+#ifdef CONFIG_PMAC_BACKLIGHT
+ /* Backlight should have its own device or go via
+ * the fbdev
+ */
case PMU_IOC_GET_BACKLIGHT:
- if (!pmu_has_backlight)
- return -ENOSYS;
- return put_user(backlight_level, (__u32 *)arg);
+ error = get_backlight_level();
+ if (error < 0)
+ return error;
+ return put_user(error, (__u32 *)arg);
case PMU_IOC_SET_BACKLIGHT:
- if (!pmu_has_backlight)
- return -ENOSYS;
+ {
+ __u32 value;
error = get_user(value, (__u32 *)arg);
if (!error)
- pmu_set_brightness(value);
+ error = set_backlight_level(value);
return error;
+ }
+#endif /* CONFIG_PMAC_BACKLIGHT */
case PMU_IOC_GET_MODEL:
return put_user(pmu_kind, (__u32 *)arg);
case PMU_IOC_HAS_ADB:
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)