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

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)