Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig
new file mode 100644
index 0000000..8a7117a
--- /dev/null
+++ b/drivers/macintosh/Kconfig
@@ -0,0 +1,198 @@
+
+menu "Macintosh device drivers"
+	depends on PPC || MAC
+
+config ADB
+	bool "Apple Desktop Bus (ADB) support"
+	depends on MAC || PPC_PMAC
+	help
+	  Apple Desktop Bus (ADB) support is for support of devices which
+	  are connected to an ADB port.  ADB devices tend to have 4 pins.
+	  If you have an Apple Macintosh prior to the iMac, an iBook or
+	  PowerBook, or a "Blue and White G3", you probably want to say Y
+	  here.  Otherwise say N.
+
+config ADB_MACII
+	bool "Include Mac II ADB driver"
+	depends on ADB && MAC
+	help
+	  Say Y here if want your kernel to support Macintosh systems that use
+	  the Mac II style ADB.  This includes the II, IIx, IIcx, SE/30, IIci,
+	  Quadra 610, Quadra 650, Quadra 700, Quadra 800, Centris 610 and
+	  Centris 650.
+
+config ADB_MACIISI
+	bool "Include Mac IIsi ADB driver"
+	depends on ADB && MAC
+	help
+	  Say Y here if want your kernel to support Macintosh systems that use
+	  the Mac IIsi style ADB.  This includes the IIsi, IIvi, IIvx, Classic
+	  II, LC, LC II, LC III, Performa 460, and the Performa 600.
+
+config ADB_IOP
+	bool "Include IOP (IIfx/Quadra 9x0) ADB driver"
+	depends on ADB && MAC
+	help
+	  The I/O Processor (IOP) is an Apple custom IC designed to provide
+	  intelligent support for I/O controllers.  It is described at
+	  <http://www.angelfire.com/ca2/dev68k/iopdesc.html> to enable direct
+	  support for it, say 'Y' here.
+
+config ADB_PMU68K
+	bool "Include PMU (Powerbook) ADB driver"
+	depends on ADB && MAC
+	help
+	  Say Y here if want your kernel to support the m68k based Powerbooks.
+	  This includes the PowerBook 140, PowerBook 145, PowerBook 150,
+	  PowerBook 160, PowerBook 165, PowerBook 165c, PowerBook 170,
+	  PowerBook 180, PowerBook, 180c, PowerBook 190cs, PowerBook 520,
+	  PowerBook Duo 210, PowerBook Duo 230, PowerBook Duo 250,
+	  PowerBook Duo 270c, PowerBook Duo 280 and PowerBook Duo 280c.
+
+# we want to change this to something like CONFIG_SYSCTRL_CUDA/PMU
+config ADB_CUDA
+	bool "Support for CUDA based Macs and PowerMacs"
+	depends on (ADB || PPC_PMAC) && !PPC_PMAC64
+	help
+	  This provides support for CUDA based Macintosh and Power Macintosh
+	  systems.  This includes many m68k based Macs (Color Classic, Mac TV,
+	  Performa 475, Performa 520, Performa 550, Performa 575,
+	  Performa 588, Quadra 605, Quadra 630, Quadra/Centris 660AV, and
+	  Quadra 840AV), most OldWorld PowerMacs, the first generation iMacs,
+	  the Blue&White G3 and the "Yikes" G4 (PCI Graphics).  All later
+	  models should use CONFIG_ADB_PMU instead.  It is safe to say Y here
+	  even if your machine doesn't have a CUDA.
+
+	  If unsure say Y.
+
+config ADB_PMU
+	bool "Support for PMU  based PowerMacs"
+	depends on PPC_PMAC
+	help
+	  On PowerBooks, iBooks, and recent iMacs and Power Macintoshes, the
+	  PMU is an embedded microprocessor whose primary function is to
+	  control system power, and battery charging on the portable models.
+	  The PMU also controls the ADB (Apple Desktop Bus) which connects to
+	  the keyboard and mouse on some machines, as well as the non-volatile
+	  RAM and the RTC (real time clock) chip.  Say Y to enable support for
+	  this device; you should do so if your machine is one of those
+	  mentioned above.
+
+config PMAC_SMU
+	bool "Support for SMU  based PowerMacs"
+	depends on PPC_PMAC64
+	help
+	  This option adds support for the newer G5 iMacs and PowerMacs based
+	  on the "SMU" system control chip which replaces the old PMU.
+	  If you don't know, say Y.
+
+config PMAC_PBOOK
+	bool "Power management support for PowerBooks"
+	depends on ADB_PMU
+	---help---
+	  This provides support for putting a PowerBook to sleep; it also
+	  enables media bay support.  Power management works on the
+	  PB2400/3400/3500, Wallstreet, Lombard, and Bronze PowerBook G3 and
+	  the Titanium Powerbook G4, as well as the iBooks.  You should get
+	  the power management daemon, pmud, to make it work and you must have
+	  the /dev/pmu device (see the pmud README).
+
+	  Get pmud from <ftp://ftp.samba.org/pub/ppclinux/pmud/>.
+
+	  If you have a PowerBook, you should say Y here.
+
+	  You may also want to compile the dma sound driver as a module and
+	  have it autoloaded. The act of removing the module shuts down the
+	  sound hardware for more power savings.
+
+config PM
+	bool
+	depends on PPC_PMAC && ADB_PMU && PMAC_PBOOK
+	default y
+
+config PMAC_APM_EMU
+	tristate "APM emulation"
+	depends on PMAC_PBOOK
+
+# made a separate option since backlight may end up beeing used
+# on non-powerbook machines (but only on PMU based ones AFAIK)
+config PMAC_BACKLIGHT
+	bool "Backlight control for LCD screens"
+	depends on ADB_PMU
+	help
+	  Say Y here to build in code to manage the LCD backlight on a
+	  Macintosh PowerBook.  With this code, the backlight will be turned
+	  on and off appropriately on power-management and lid-open/lid-closed
+	  events; also, the PowerBook button device will be enabled so you can
+	  change the screen brightness.
+
+config MAC_SERIAL
+	tristate "Support for PowerMac serial ports (OBSOLETE DRIVER)"
+	depends on PPC_PMAC && BROKEN
+	help
+	  This driver is obsolete. Use CONFIG_SERIAL_PMACZILOG in
+	  "Character devices --> Serial drivers --> PowerMac z85c30" option.
+
+config ADB_MACIO
+	bool "Include MacIO (CHRP) ADB driver"
+	depends on ADB && PPC_CHRP && !PPC_PMAC64
+	help
+	  Say Y here to include direct support for the ADB controller in the
+	  Hydra chip used on PowerPC Macintoshes of the CHRP type.  (The Hydra
+	  also includes a MESH II SCSI controller, DBDMA controller, VIA chip,
+	  OpenPIC controller and two RS422/Geoports.)
+
+config INPUT_ADBHID
+	bool "Support for ADB input devices (keyboard, mice, ...)"
+	depends on ADB && INPUT=y
+	help
+	  Say Y here if you want to have ADB (Apple Desktop Bus) HID devices
+	  such as keyboards, mice, joysticks, trackpads  or graphic tablets
+	  handled by the input layer.  If you say Y here, make sure to say Y to
+	  the corresponding drivers "Keyboard support" (CONFIG_INPUT_KEYBDEV),
+	  "Mouse Support" (CONFIG_INPUT_MOUSEDEV) and "Event interface
+	  support" (CONFIG_INPUT_EVDEV) as well.
+
+	  If unsure, say Y.
+
+config MAC_EMUMOUSEBTN
+	bool "Support for mouse button 2+3 emulation"
+	depends on INPUT_ADBHID
+	help
+	  This provides generic support for emulating the 2nd and 3rd mouse
+	  button with keypresses.  If you say Y here, the emulation is still
+	  disabled by default.  The emulation is controlled by these sysctl
+	  entries:
+	  /proc/sys/dev/mac_hid/mouse_button_emulation
+	  /proc/sys/dev/mac_hid/mouse_button2_keycode
+	  /proc/sys/dev/mac_hid/mouse_button3_keycode
+
+	  If you have an Apple machine with a 1-button mouse, say Y here.
+
+config THERM_WINDTUNNEL
+	tristate "Support for thermal management on Windtunnel G4s"
+	depends on I2C && I2C_KEYWEST && PPC_PMAC && !PPC_PMAC64
+	help
+	  This driver provides some thermostat and fan control for the desktop
+	  G4 "Windtunnel"
+
+config THERM_ADT746X
+	tristate "Support for thermal mgmnt on laptops with ADT 746x chipset"
+	depends on I2C && I2C_KEYWEST && PPC_PMAC && !PPC_PMAC64
+	help
+	  This driver provides some thermostat and fan control for the
+          iBook G4, and the ATI based aluminium PowerBooks, allowing slighlty
+	  better fan behaviour by default, and some manual control.
+
+config THERM_PM72
+	tristate "Support for thermal management on PowerMac G5"
+	depends on I2C && I2C_KEYWEST && PPC_PMAC64
+	help
+	  This driver provides thermostat and fan control for the desktop
+	  G5 machines. 
+
+config ANSLCD
+	tristate "Support for ANS LCD display"
+	depends on ADB_CUDA && PPC_PMAC
+
+endmenu
diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile
new file mode 100644
index 0000000..c3a4705
--- /dev/null
+++ b/drivers/macintosh/Makefile
@@ -0,0 +1,29 @@
+#
+# Makefile for the Macintosh-specific device drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_PPC_PMAC)		+= macio_asic.o
+
+obj-$(CONFIG_PMAC_PBOOK)	+= mediabay.o
+obj-$(CONFIG_MAC_SERIAL)	+= macserial.o
+obj-$(CONFIG_MAC_EMUMOUSEBTN)	+= mac_hid.o
+obj-$(CONFIG_INPUT_ADBHID)	+= adbhid.o
+obj-$(CONFIG_ANSLCD)		+= ans-lcd.o
+
+obj-$(CONFIG_ADB_PMU)		+= via-pmu.o
+obj-$(CONFIG_ADB_CUDA)		+= via-cuda.o
+obj-$(CONFIG_PMAC_APM_EMU)	+= apm_emu.o
+obj-$(CONFIG_PMAC_SMU)		+= smu.o
+
+obj-$(CONFIG_ADB)		+= adb.o
+obj-$(CONFIG_ADB_MACII)		+= via-macii.o
+obj-$(CONFIG_ADB_MACIISI)	+= via-maciisi.o
+obj-$(CONFIG_ADB_IOP)		+= adb-iop.o
+obj-$(CONFIG_ADB_PMU68K)	+= via-pmu68k.o
+obj-$(CONFIG_ADB_MACIO)		+= macio-adb.o
+
+obj-$(CONFIG_THERM_PM72)	+= therm_pm72.o
+obj-$(CONFIG_THERM_WINDTUNNEL)	+= therm_windtunnel.o
+obj-$(CONFIG_THERM_ADT746X)	+= therm_adt746x.o
diff --git a/drivers/macintosh/adb-iop.c b/drivers/macintosh/adb-iop.c
new file mode 100644
index 0000000..71aeb91
--- /dev/null
+++ b/drivers/macintosh/adb-iop.c
@@ -0,0 +1,287 @@
+/*
+ * I/O Processor (IOP) ADB Driver
+ * Written and (C) 1999 by Joshua M. Thompson (funaho@jurai.org)
+ * Based on via-cuda.c by Paul Mackerras.
+ *
+ * 1999-07-01 (jmt) - First implementation for new driver architecture.
+ *
+ * 1999-07-31 (jmt) - First working version.
+ *
+ * TODO:
+ *
+ * o Implement SRQ handling.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+
+#include <asm/bootinfo.h> 
+#include <asm/macintosh.h> 
+#include <asm/macints.h> 
+#include <asm/mac_iop.h>
+#include <asm/mac_oss.h>
+#include <asm/adb_iop.h>
+
+#include <linux/adb.h> 
+
+/*#define DEBUG_ADB_IOP*/
+
+extern void iop_ism_irq(int, void *, struct pt_regs *);
+
+static struct adb_request *current_req;
+static struct adb_request *last_req;
+#if 0
+static unsigned char reply_buff[16];
+static unsigned char *reply_ptr;
+#endif
+
+static enum adb_iop_state {
+    idle,
+    sending,
+    awaiting_reply
+} adb_iop_state;
+
+static void adb_iop_start(void);
+static int adb_iop_probe(void);
+static int adb_iop_init(void);
+static int adb_iop_send_request(struct adb_request *, int);
+static int adb_iop_write(struct adb_request *);
+static int adb_iop_autopoll(int);
+static void adb_iop_poll(void);
+static int adb_iop_reset_bus(void);
+
+struct adb_driver adb_iop_driver = {
+	"ISM IOP",
+	adb_iop_probe,
+	adb_iop_init,
+	adb_iop_send_request,
+	adb_iop_autopoll,
+	adb_iop_poll,
+	adb_iop_reset_bus
+};
+
+static void adb_iop_end_req(struct adb_request *req, int state)
+{
+	req->complete = 1;
+	current_req = req->next;
+	if (req->done) (*req->done)(req);
+	adb_iop_state = state;
+}
+
+/*
+ * Completion routine for ADB commands sent to the IOP.
+ *
+ * This will be called when a packet has been successfully sent.
+ */
+
+static void adb_iop_complete(struct iop_msg *msg, struct pt_regs *regs)
+{
+	struct adb_request *req;
+	uint flags;
+
+	local_irq_save(flags);
+
+	req = current_req;
+	if ((adb_iop_state == sending) && req && req->reply_expected) {
+		adb_iop_state = awaiting_reply;
+	}
+
+	local_irq_restore(flags);
+}
+
+/*
+ * Listen for ADB messages from the IOP.
+ *
+ * This will be called when unsolicited messages (usually replies to TALK
+ * commands or autopoll packets) are received.
+ */
+
+static void adb_iop_listen(struct iop_msg *msg, struct pt_regs *regs)
+{
+	struct adb_iopmsg *amsg = (struct adb_iopmsg *) msg->message;
+	struct adb_request *req;
+	uint flags;
+#ifdef DEBUG_ADB_IOP
+	int i;
+#endif
+
+	local_irq_save(flags);
+
+	req = current_req;
+
+#ifdef DEBUG_ADB_IOP
+	printk("adb_iop_listen %p: rcvd packet, %d bytes: %02X %02X", req,
+		(uint) amsg->count + 2, (uint) amsg->flags, (uint) amsg->cmd);
+	for (i = 0; i < amsg->count; i++)
+		printk(" %02X", (uint) amsg->data[i]);
+	printk("\n");
+#endif
+
+	/* Handle a timeout. Timeout packets seem to occur even after */
+	/* we've gotten a valid reply to a TALK, so I'm assuming that */
+	/* a "timeout" is actually more like an "end-of-data" signal. */
+	/* We need to send back a timeout packet to the IOP to shut   */
+	/* it up, plus complete the current request, if any.          */
+
+	if (amsg->flags & ADB_IOP_TIMEOUT) {
+		msg->reply[0] = ADB_IOP_TIMEOUT | ADB_IOP_AUTOPOLL;
+		msg->reply[1] = 0;
+		msg->reply[2] = 0;
+		if (req && (adb_iop_state != idle)) {
+			adb_iop_end_req(req, idle);
+		}
+	} else {
+		/* TODO: is it possible for more than one chunk of data  */
+		/*       to arrive before the timeout? If so we need to */
+		/*       use reply_ptr here like the other drivers do.  */
+		if ((adb_iop_state == awaiting_reply) &&
+		    (amsg->flags & ADB_IOP_EXPLICIT)) {
+			req->reply_len = amsg->count + 1;
+			memcpy(req->reply, &amsg->cmd, req->reply_len);
+		} else {
+			adb_input(&amsg->cmd, amsg->count + 1, regs,
+				  amsg->flags & ADB_IOP_AUTOPOLL);
+		}
+		memcpy(msg->reply, msg->message, IOP_MSG_LEN);
+	}
+	iop_complete_message(msg);
+	local_irq_restore(flags);
+}
+
+/*
+ * Start sending an ADB packet, IOP style
+ *
+ * There isn't much to do other than hand the packet over to the IOP
+ * after encapsulating it in an adb_iopmsg.
+ */
+
+static void adb_iop_start(void)
+{
+	unsigned long flags;
+	struct adb_request *req;
+	struct adb_iopmsg amsg;
+#ifdef DEBUG_ADB_IOP
+	int i;
+#endif
+
+	/* get the packet to send */
+	req = current_req;
+	if (!req) return;
+
+	local_irq_save(flags);
+
+#ifdef DEBUG_ADB_IOP
+	printk("adb_iop_start %p: sending packet, %d bytes:", req, req->nbytes);
+	for (i = 0 ; i < req->nbytes ; i++)
+		printk(" %02X", (uint) req->data[i]);
+	printk("\n");
+#endif
+
+	/* The IOP takes MacII-style packets, so */
+	/* strip the initial ADB_PACKET byte.    */
+
+	amsg.flags = ADB_IOP_EXPLICIT;
+	amsg.count = req->nbytes - 2;
+
+	/* amsg.data immediately follows amsg.cmd, effectively making */
+	/* amsg.cmd a pointer to the beginning of a full ADB packet.  */
+	memcpy(&amsg.cmd, req->data + 1, req->nbytes - 1);
+
+	req->sent = 1;
+	adb_iop_state = sending;
+	local_irq_restore(flags);
+
+	/* Now send it. The IOP manager will call adb_iop_complete */
+	/* when the packet has been sent.                          */
+
+	iop_send_message(ADB_IOP, ADB_CHAN, req,
+			 sizeof(amsg), (__u8 *) &amsg, adb_iop_complete);
+}
+
+int adb_iop_probe(void)
+{
+	if (!iop_ism_present) return -ENODEV;
+	return 0;
+}
+
+int adb_iop_init(void)
+{
+	printk("adb: IOP ISM driver v0.4 for Unified ADB.\n");
+	iop_listen(ADB_IOP, ADB_CHAN, adb_iop_listen, "ADB");
+	return 0;
+}
+
+int adb_iop_send_request(struct adb_request *req, int sync)
+{
+	int err;
+
+	err = adb_iop_write(req);
+	if (err) return err;
+
+	if (sync) {
+		while (!req->complete) adb_iop_poll();
+	}
+	return 0;
+}
+
+static int adb_iop_write(struct adb_request *req)
+{
+	unsigned long flags;
+
+	if ((req->nbytes < 2) || (req->data[0] != ADB_PACKET)) {
+		req->complete = 1;
+		return -EINVAL;
+	}
+
+	local_irq_save(flags);
+
+	req->next = 0;
+	req->sent = 0;
+	req->complete = 0;
+	req->reply_len = 0;
+
+	if (current_req != 0) {
+		last_req->next = req;
+		last_req = req;
+	} else {
+		current_req = req;
+		last_req = req;
+	}
+
+	local_irq_restore(flags);
+	if (adb_iop_state == idle) adb_iop_start();
+	return 0;
+}
+
+int adb_iop_autopoll(int devs)
+{
+	/* TODO: how do we enable/disable autopoll? */
+	return 0;
+}
+
+void adb_iop_poll(void)
+{
+	if (adb_iop_state == idle) adb_iop_start();
+	iop_ism_irq(0, (void *) ADB_IOP, NULL);
+}
+
+int adb_iop_reset_bus(void)
+{
+	struct adb_request req = {
+		.reply_expected = 0,
+		.nbytes = 2,
+		.data = { ADB_PACKET, 0 },
+	};
+
+	adb_iop_write(&req);
+	while (!req.complete) {
+		adb_iop_poll();
+		schedule();
+	}
+
+	return 0;
+}
diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c
new file mode 100644
index 0000000..7297c77
--- /dev/null
+++ b/drivers/macintosh/adb.c
@@ -0,0 +1,910 @@
+/*
+ * Device driver for the Apple Desktop Bus
+ * and the /dev/adb device on macintoshes.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ *
+ * Modified to declare controllers as structures, added
+ * client notification of bus reset and handles PowerBook
+ * sleep, by Benjamin Herrenschmidt.
+ *
+ * To do:
+ *
+ * - /sys/bus/adb to list the devices and infos
+ * - more /dev/adb to allow userland to receive the
+ *   flow of auto-polling datas from a given device.
+ * - move bus probe to a kernel thread
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/adb.h>
+#include <linux/cuda.h>
+#include <linux/pmu.h>
+#include <linux/notifier.h>
+#include <linux/wait.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/devfs_fs_kernel.h>
+
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#ifdef CONFIG_PPC
+#include <asm/prom.h>
+#endif
+
+
+EXPORT_SYMBOL(adb_controller);
+EXPORT_SYMBOL(adb_client_list);
+
+extern struct adb_driver via_macii_driver;
+extern struct adb_driver via_maciisi_driver;
+extern struct adb_driver via_cuda_driver;
+extern struct adb_driver adb_iop_driver;
+extern struct adb_driver via_pmu_driver;
+extern struct adb_driver macio_adb_driver;
+
+static struct adb_driver *adb_driver_list[] = {
+#ifdef CONFIG_ADB_MACII
+	&via_macii_driver,
+#endif
+#ifdef CONFIG_ADB_MACIISI
+	&via_maciisi_driver,
+#endif
+#ifdef CONFIG_ADB_CUDA
+	&via_cuda_driver,
+#endif
+#ifdef CONFIG_ADB_IOP
+	&adb_iop_driver,
+#endif
+#if defined(CONFIG_ADB_PMU) || defined(CONFIG_ADB_PMU68K)
+	&via_pmu_driver,
+#endif
+#ifdef CONFIG_ADB_MACIO
+	&macio_adb_driver,
+#endif
+	NULL
+};
+
+static struct class_simple *adb_dev_class;
+
+struct adb_driver *adb_controller;
+struct notifier_block *adb_client_list = NULL;
+static int adb_got_sleep;
+static int adb_inited;
+static pid_t adb_probe_task_pid;
+static DECLARE_MUTEX(adb_probe_mutex);
+static struct completion adb_probe_task_comp;
+static int sleepy_trackpad;
+static int autopoll_devs;
+int __adb_probe_sync;
+
+#ifdef CONFIG_PMAC_PBOOK
+static int adb_notify_sleep(struct pmu_sleep_notifier *self, int when);
+static struct pmu_sleep_notifier adb_sleep_notifier = {
+	adb_notify_sleep,
+	SLEEP_LEVEL_ADB,
+};
+#endif
+
+static int adb_scan_bus(void);
+static int do_adb_reset_bus(void);
+static void adbdev_init(void);
+static int try_handler_change(int, int);
+
+static struct adb_handler {
+	void (*handler)(unsigned char *, int, struct pt_regs *, int);
+	int original_address;
+	int handler_id;
+	int busy;
+} adb_handler[16];
+
+/*
+ * The adb_handler_sem mutex protects all accesses to the original_address
+ * and handler_id fields of adb_handler[i] for all i, and changes to the
+ * handler field.
+ * Accesses to the handler field are protected by the adb_handler_lock
+ * rwlock.  It is held across all calls to any handler, so that by the
+ * time adb_unregister returns, we know that the old handler isn't being
+ * called.
+ */
+static DECLARE_MUTEX(adb_handler_sem);
+static DEFINE_RWLOCK(adb_handler_lock);
+
+#if 0
+static void printADBreply(struct adb_request *req)
+{
+        int i;
+
+        printk("adb reply (%d)", req->reply_len);
+        for(i = 0; i < req->reply_len; i++)
+                printk(" %x", req->reply[i]);
+        printk("\n");
+
+}
+#endif
+
+
+static __inline__ void adb_wait_ms(unsigned int ms)
+{
+	if (current->pid && adb_probe_task_pid &&
+	  adb_probe_task_pid == current->pid)
+		msleep(ms);
+	else
+		mdelay(ms);
+}
+
+static int adb_scan_bus(void)
+{
+	int i, highFree=0, noMovement;
+	int devmask = 0;
+	struct adb_request req;
+	
+	/* assumes adb_handler[] is all zeroes at this point */
+	for (i = 1; i < 16; i++) {
+		/* see if there is anything at address i */
+		adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+                            (i << 4) | 0xf);
+		if (req.reply_len > 1)
+			/* one or more devices at this address */
+			adb_handler[i].original_address = i;
+		else if (i > highFree)
+			highFree = i;
+	}
+
+	/* Note we reset noMovement to 0 each time we move a device */
+	for (noMovement = 1; noMovement < 2 && highFree > 0; noMovement++) {
+		for (i = 1; i < 16; i++) {
+			if (adb_handler[i].original_address == 0)
+				continue;
+			/*
+			 * Send a "talk register 3" command to address i
+			 * to provoke a collision if there is more than
+			 * one device at this address.
+			 */
+			adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+				    (i << 4) | 0xf);
+			/*
+			 * Move the device(s) which didn't detect a
+			 * collision to address `highFree'.  Hopefully
+			 * this only moves one device.
+			 */
+			adb_request(&req, NULL, ADBREQ_SYNC, 3,
+				    (i<< 4) | 0xb, (highFree | 0x60), 0xfe);
+			/*
+			 * See if anybody actually moved. This is suggested
+			 * by HW TechNote 01:
+			 *
+			 * http://developer.apple.com/technotes/hw/hw_01.html
+			 */
+			adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+				    (highFree << 4) | 0xf);
+			if (req.reply_len <= 1) continue;
+			/*
+			 * Test whether there are any device(s) left
+			 * at address i.
+			 */
+			adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+				    (i << 4) | 0xf);
+			if (req.reply_len > 1) {
+				/*
+				 * There are still one or more devices
+				 * left at address i.  Register the one(s)
+				 * we moved to `highFree', and find a new
+				 * value for highFree.
+				 */
+				adb_handler[highFree].original_address =
+					adb_handler[i].original_address;
+				while (highFree > 0 &&
+				       adb_handler[highFree].original_address)
+					highFree--;
+				if (highFree <= 0)
+					break;
+
+				noMovement = 0;
+			}
+			else {
+				/*
+				 * No devices left at address i; move the
+				 * one(s) we moved to `highFree' back to i.
+				 */
+				adb_request(&req, NULL, ADBREQ_SYNC, 3,
+					    (highFree << 4) | 0xb,
+					    (i | 0x60), 0xfe);
+			}
+		}	
+	}
+
+	/* Now fill in the handler_id field of the adb_handler entries. */
+	printk(KERN_DEBUG "adb devices:");
+	for (i = 1; i < 16; i++) {
+		if (adb_handler[i].original_address == 0)
+			continue;
+		adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+			    (i << 4) | 0xf);
+		adb_handler[i].handler_id = req.reply[2];
+		printk(" [%d]: %d %x", i, adb_handler[i].original_address,
+		       adb_handler[i].handler_id);
+		devmask |= 1 << i;
+	}
+	printk("\n");
+	return devmask;
+}
+
+/*
+ * This kernel task handles ADB probing. It dies once probing is
+ * completed.
+ */
+static int
+adb_probe_task(void *x)
+{
+	sigset_t blocked;
+
+	strcpy(current->comm, "kadbprobe");
+
+	sigfillset(&blocked);
+	sigprocmask(SIG_BLOCK, &blocked, NULL);
+	flush_signals(current);
+
+	printk(KERN_INFO "adb: starting probe task...\n");
+	do_adb_reset_bus();
+	printk(KERN_INFO "adb: finished probe task...\n");
+	
+	adb_probe_task_pid = 0;
+	up(&adb_probe_mutex);
+	
+	return 0;
+}
+
+static void
+__adb_probe_task(void *data)
+{
+	adb_probe_task_pid = kernel_thread(adb_probe_task, NULL, SIGCHLD | CLONE_KERNEL);
+}
+
+static DECLARE_WORK(adb_reset_work, __adb_probe_task, NULL);
+
+int
+adb_reset_bus(void)
+{
+	if (__adb_probe_sync) {
+		do_adb_reset_bus();
+		return 0;
+	}
+
+	down(&adb_probe_mutex);
+	schedule_work(&adb_reset_work);
+	return 0;
+}
+
+int __init adb_init(void)
+{
+	struct adb_driver *driver;
+	int i;
+
+#ifdef CONFIG_PPC32
+	if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) )
+		return 0;
+#endif
+#ifdef CONFIG_MAC
+	if (!MACH_IS_MAC)
+		return 0;
+#endif
+
+	/* xmon may do early-init */
+	if (adb_inited)
+		return 0;
+	adb_inited = 1;
+		
+	adb_controller = NULL;
+
+	i = 0;
+	while ((driver = adb_driver_list[i++]) != NULL) {
+		if (!driver->probe()) {
+			adb_controller = driver;
+			break;
+		}
+	}
+	if ((adb_controller == NULL) || adb_controller->init()) {
+		printk(KERN_WARNING "Warning: no ADB interface detected\n");
+		adb_controller = NULL;
+	} else {
+#ifdef CONFIG_PMAC_PBOOK
+		pmu_register_sleep_notifier(&adb_sleep_notifier);
+#endif /* CONFIG_PMAC_PBOOK */
+#ifdef CONFIG_PPC
+		if (machine_is_compatible("AAPL,PowerBook1998") ||
+			machine_is_compatible("PowerBook1,1"))
+			sleepy_trackpad = 1;
+#endif /* CONFIG_PPC */
+		init_completion(&adb_probe_task_comp);
+		adbdev_init();
+		adb_reset_bus();
+	}
+	return 0;
+}
+
+__initcall(adb_init);
+
+#ifdef CONFIG_PMAC_PBOOK
+/*
+ * notify clients before sleep and reset bus afterwards
+ */
+int
+adb_notify_sleep(struct pmu_sleep_notifier *self, int when)
+{
+	int ret;
+	
+	switch (when) {
+	case PBOOK_SLEEP_REQUEST:
+		adb_got_sleep = 1;
+		/* We need to get a lock on the probe thread */
+		down(&adb_probe_mutex);
+		/* Stop autopoll */
+		if (adb_controller->autopoll)
+			adb_controller->autopoll(0);
+		ret = notifier_call_chain(&adb_client_list, ADB_MSG_POWERDOWN, NULL);
+		if (ret & NOTIFY_STOP_MASK) {
+			up(&adb_probe_mutex);
+			return PBOOK_SLEEP_REFUSE;
+		}
+		break;
+	case PBOOK_SLEEP_REJECT:
+		if (adb_got_sleep) {
+			adb_got_sleep = 0;
+			up(&adb_probe_mutex);
+			adb_reset_bus();
+		}
+		break;
+		
+	case PBOOK_SLEEP_NOW:
+		break;
+	case PBOOK_WAKE:
+		adb_got_sleep = 0;
+		up(&adb_probe_mutex);
+		adb_reset_bus();
+		break;
+	}
+	return PBOOK_SLEEP_OK;
+}
+#endif /* CONFIG_PMAC_PBOOK */
+
+static int
+do_adb_reset_bus(void)
+{
+	int ret, nret;
+	
+	if (adb_controller == NULL)
+		return -ENXIO;
+		
+	if (adb_controller->autopoll)
+		adb_controller->autopoll(0);
+
+	nret = notifier_call_chain(&adb_client_list, ADB_MSG_PRE_RESET, NULL);
+	if (nret & NOTIFY_STOP_MASK) {
+		if (adb_controller->autopoll)
+			adb_controller->autopoll(autopoll_devs);
+		return -EBUSY;
+	}
+
+	if (sleepy_trackpad) {
+		/* Let the trackpad settle down */
+		adb_wait_ms(500);
+	}
+
+	down(&adb_handler_sem);
+	write_lock_irq(&adb_handler_lock);
+	memset(adb_handler, 0, sizeof(adb_handler));
+	write_unlock_irq(&adb_handler_lock);
+
+	/* That one is still a bit synchronous, oh well... */
+	if (adb_controller->reset_bus)
+		ret = adb_controller->reset_bus();
+	else
+		ret = 0;
+
+	if (sleepy_trackpad) {
+		/* Let the trackpad settle down */
+		adb_wait_ms(1500);
+	}
+
+	if (!ret) {
+		autopoll_devs = adb_scan_bus();
+		if (adb_controller->autopoll)
+			adb_controller->autopoll(autopoll_devs);
+	}
+	up(&adb_handler_sem);
+
+	nret = notifier_call_chain(&adb_client_list, ADB_MSG_POST_RESET, NULL);
+	if (nret & NOTIFY_STOP_MASK)
+		return -EBUSY;
+	
+	return ret;
+}
+
+void
+adb_poll(void)
+{
+	if ((adb_controller == NULL)||(adb_controller->poll == NULL))
+		return;
+	adb_controller->poll();
+}
+
+static void
+adb_probe_wakeup(struct adb_request *req)
+{
+	complete(&adb_probe_task_comp);
+}
+
+/* Static request used during probe */
+static struct adb_request adb_sreq;
+static unsigned long adb_sreq_lock; // Use semaphore ! */ 
+
+int
+adb_request(struct adb_request *req, void (*done)(struct adb_request *),
+	    int flags, int nbytes, ...)
+{
+	va_list list;
+	int i, use_sreq;
+	int rc;
+
+	if ((adb_controller == NULL) || (adb_controller->send_request == NULL))
+		return -ENXIO;
+	if (nbytes < 1)
+		return -EINVAL;
+	if (req == NULL && (flags & ADBREQ_NOSEND))
+		return -EINVAL;
+	
+	if (req == NULL) {
+		if (test_and_set_bit(0,&adb_sreq_lock)) {
+			printk("adb.c: Warning: contention on static request !\n");
+			return -EPERM;
+		}
+		req = &adb_sreq;
+		flags |= ADBREQ_SYNC;
+		use_sreq = 1;
+	} else
+		use_sreq = 0;
+	req->nbytes = nbytes+1;
+	req->done = done;
+	req->reply_expected = flags & ADBREQ_REPLY;
+	req->data[0] = ADB_PACKET;
+	va_start(list, nbytes);
+	for (i = 0; i < nbytes; ++i)
+		req->data[i+1] = va_arg(list, int);
+	va_end(list);
+
+	if (flags & ADBREQ_NOSEND)
+		return 0;
+
+	/* Synchronous requests send from the probe thread cause it to
+	 * block. Beware that the "done" callback will be overriden !
+	 */
+	if ((flags & ADBREQ_SYNC) &&
+	    (current->pid && adb_probe_task_pid &&
+	    adb_probe_task_pid == current->pid)) {
+		req->done = adb_probe_wakeup;
+		rc = adb_controller->send_request(req, 0);
+		if (rc || req->complete)
+			goto bail;
+		wait_for_completion(&adb_probe_task_comp);
+		rc = 0;
+		goto bail;
+	}
+
+	rc = adb_controller->send_request(req, flags & ADBREQ_SYNC);
+bail:
+	if (use_sreq)
+		clear_bit(0, &adb_sreq_lock);
+
+	return rc;
+}
+
+ /* Ultimately this should return the number of devices with
+    the given default id.
+    And it does it now ! Note: changed behaviour: This function
+    will now register if default_id _and_ handler_id both match
+    but handler_id can be left to 0 to match with default_id only.
+    When handler_id is set, this function will try to adjust
+    the handler_id id it doesn't match. */
+int
+adb_register(int default_id, int handler_id, struct adb_ids *ids,
+	     void (*handler)(unsigned char *, int, struct pt_regs *, int))
+{
+	int i;
+
+	down(&adb_handler_sem);
+	ids->nids = 0;
+	for (i = 1; i < 16; i++) {
+		if ((adb_handler[i].original_address == default_id) &&
+		    (!handler_id || (handler_id == adb_handler[i].handler_id) || 
+		    try_handler_change(i, handler_id))) {
+			if (adb_handler[i].handler != 0) {
+				printk(KERN_ERR
+				       "Two handlers for ADB device %d\n",
+				       default_id);
+				continue;
+			}
+			write_lock_irq(&adb_handler_lock);
+			adb_handler[i].handler = handler;
+			write_unlock_irq(&adb_handler_lock);
+			ids->id[ids->nids++] = i;
+		}
+	}
+	up(&adb_handler_sem);
+	return ids->nids;
+}
+
+int
+adb_unregister(int index)
+{
+	int ret = -ENODEV;
+
+	down(&adb_handler_sem);
+	write_lock_irq(&adb_handler_lock);
+	if (adb_handler[index].handler) {
+		while(adb_handler[index].busy) {
+			write_unlock_irq(&adb_handler_lock);
+			yield();
+			write_lock_irq(&adb_handler_lock);
+		}
+		ret = 0;
+		adb_handler[index].handler = NULL;
+	}
+	write_unlock_irq(&adb_handler_lock);
+	up(&adb_handler_sem);
+	return ret;
+}
+
+void
+adb_input(unsigned char *buf, int nb, struct pt_regs *regs, int autopoll)
+{
+	int i, id;
+	static int dump_adb_input = 0;
+	unsigned long flags;
+	
+	void (*handler)(unsigned char *, int, struct pt_regs *, int);
+
+	/* We skip keystrokes and mouse moves when the sleep process
+	 * has been started. We stop autopoll, but this is another security
+	 */
+	if (adb_got_sleep)
+		return;
+		
+	id = buf[0] >> 4;
+	if (dump_adb_input) {
+		printk(KERN_INFO "adb packet: ");
+		for (i = 0; i < nb; ++i)
+			printk(" %x", buf[i]);
+		printk(", id = %d\n", id);
+	}
+	write_lock_irqsave(&adb_handler_lock, flags);
+	handler = adb_handler[id].handler;
+	if (handler != NULL)
+		adb_handler[id].busy = 1;
+	write_unlock_irqrestore(&adb_handler_lock, flags);
+	if (handler != NULL) {
+		(*handler)(buf, nb, regs, autopoll);
+		wmb();
+		adb_handler[id].busy = 0;
+	}
+		
+}
+
+/* Try to change handler to new_id. Will return 1 if successful. */
+static int try_handler_change(int address, int new_id)
+{
+	struct adb_request req;
+
+	if (adb_handler[address].handler_id == new_id)
+	    return 1;
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	    ADB_WRITEREG(address, 3), address | 0x20, new_id);
+	adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+	    ADB_READREG(address, 3));
+	if (req.reply_len < 2)
+	    return 0;
+	if (req.reply[2] != new_id)
+	    return 0;
+	adb_handler[address].handler_id = req.reply[2];
+
+	return 1;
+}
+
+int
+adb_try_handler_change(int address, int new_id)
+{
+	int ret;
+
+	down(&adb_handler_sem);
+	ret = try_handler_change(address, new_id);
+	up(&adb_handler_sem);
+	return ret;
+}
+
+int
+adb_get_infos(int address, int *original_address, int *handler_id)
+{
+	down(&adb_handler_sem);
+	*original_address = adb_handler[address].original_address;
+	*handler_id = adb_handler[address].handler_id;
+	up(&adb_handler_sem);
+
+	return (*original_address != 0);
+}
+
+
+/*
+ * /dev/adb device driver.
+ */
+
+#define ADB_MAJOR	56	/* major number for /dev/adb */
+
+struct adbdev_state {
+	spinlock_t	lock;
+	atomic_t	n_pending;
+	struct adb_request *completed;
+  	wait_queue_head_t wait_queue;
+	int		inuse;
+};
+
+static void adb_write_done(struct adb_request *req)
+{
+	struct adbdev_state *state = (struct adbdev_state *) req->arg;
+	unsigned long flags;
+
+	if (!req->complete) {
+		req->reply_len = 0;
+		req->complete = 1;
+	}
+	spin_lock_irqsave(&state->lock, flags);
+	atomic_dec(&state->n_pending);
+	if (!state->inuse) {
+		kfree(req);
+		if (atomic_read(&state->n_pending) == 0) {
+			spin_unlock_irqrestore(&state->lock, flags);
+			kfree(state);
+			return;
+		}
+	} else {
+		struct adb_request **ap = &state->completed;
+		while (*ap != NULL)
+			ap = &(*ap)->next;
+		req->next = NULL;
+		*ap = req;
+		wake_up_interruptible(&state->wait_queue);
+	}
+	spin_unlock_irqrestore(&state->lock, flags);
+}
+
+static int
+do_adb_query(struct adb_request *req)
+{
+	int	ret = -EINVAL;
+
+	switch(req->data[1])
+	{
+	case ADB_QUERY_GETDEVINFO:
+		if (req->nbytes < 3)
+			break;
+		down(&adb_handler_sem);
+		req->reply[0] = adb_handler[req->data[2]].original_address;
+		req->reply[1] = adb_handler[req->data[2]].handler_id;
+		up(&adb_handler_sem);
+		req->complete = 1;
+		req->reply_len = 2;
+		adb_write_done(req);
+		ret = 0;
+		break;
+	}
+	return ret;
+}
+
+static int adb_open(struct inode *inode, struct file *file)
+{
+	struct adbdev_state *state;
+
+	if (iminor(inode) > 0 || adb_controller == NULL)
+		return -ENXIO;
+	state = kmalloc(sizeof(struct adbdev_state), GFP_KERNEL);
+	if (state == 0)
+		return -ENOMEM;
+	file->private_data = state;
+	spin_lock_init(&state->lock);
+	atomic_set(&state->n_pending, 0);
+	state->completed = NULL;
+	init_waitqueue_head(&state->wait_queue);
+	state->inuse = 1;
+
+	return 0;
+}
+
+static int adb_release(struct inode *inode, struct file *file)
+{
+	struct adbdev_state *state = file->private_data;
+	unsigned long flags;
+
+	lock_kernel();
+	if (state) {
+		file->private_data = NULL;
+		spin_lock_irqsave(&state->lock, flags);
+		if (atomic_read(&state->n_pending) == 0
+		    && state->completed == NULL) {
+			spin_unlock_irqrestore(&state->lock, flags);
+			kfree(state);
+		} else {
+			state->inuse = 0;
+			spin_unlock_irqrestore(&state->lock, flags);
+		}
+	}
+	unlock_kernel();
+	return 0;
+}
+
+static ssize_t adb_read(struct file *file, char __user *buf,
+			size_t count, loff_t *ppos)
+{
+	int ret = 0;
+	struct adbdev_state *state = file->private_data;
+	struct adb_request *req;
+	wait_queue_t wait = __WAITQUEUE_INITIALIZER(wait,current);
+	unsigned long flags;
+
+	if (count < 2)
+		return -EINVAL;
+	if (count > sizeof(req->reply))
+		count = sizeof(req->reply);
+	if (!access_ok(VERIFY_WRITE, buf, count))
+		return -EFAULT;
+
+	req = NULL;
+	spin_lock_irqsave(&state->lock, flags);
+	add_wait_queue(&state->wait_queue, &wait);
+	current->state = TASK_INTERRUPTIBLE;
+
+	for (;;) {
+		req = state->completed;
+		if (req != NULL)
+			state->completed = req->next;
+		else if (atomic_read(&state->n_pending) == 0)
+			ret = -EIO;
+		if (req != NULL || ret != 0)
+			break;
+		
+		if (file->f_flags & O_NONBLOCK) {
+			ret = -EAGAIN;
+			break;
+		}
+		if (signal_pending(current)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+		spin_unlock_irqrestore(&state->lock, flags);
+		schedule();
+		spin_lock_irqsave(&state->lock, flags);
+	}
+
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&state->wait_queue, &wait);
+	spin_unlock_irqrestore(&state->lock, flags);
+	
+	if (ret)
+		return ret;
+
+	ret = req->reply_len;
+	if (ret > count)
+		ret = count;
+	if (ret > 0 && copy_to_user(buf, req->reply, ret))
+		ret = -EFAULT;
+
+	kfree(req);
+	return ret;
+}
+
+static ssize_t adb_write(struct file *file, const char __user *buf,
+			 size_t count, loff_t *ppos)
+{
+	int ret/*, i*/;
+	struct adbdev_state *state = file->private_data;
+	struct adb_request *req;
+
+	if (count < 2 || count > sizeof(req->data))
+		return -EINVAL;
+	if (adb_controller == NULL)
+		return -ENXIO;
+	if (!access_ok(VERIFY_READ, buf, count))
+		return -EFAULT;
+
+	req = (struct adb_request *) kmalloc(sizeof(struct adb_request),
+					     GFP_KERNEL);
+	if (req == NULL)
+		return -ENOMEM;
+
+	req->nbytes = count;
+	req->done = adb_write_done;
+	req->arg = (void *) state;
+	req->complete = 0;
+	
+	ret = -EFAULT;
+	if (copy_from_user(req->data, buf, count))
+		goto out;
+
+	atomic_inc(&state->n_pending);
+
+	/* If a probe is in progress or we are sleeping, wait for it to complete */
+	down(&adb_probe_mutex);
+
+	/* Queries are special requests sent to the ADB driver itself */
+	if (req->data[0] == ADB_QUERY) {
+		if (count > 1)
+			ret = do_adb_query(req);
+		else
+			ret = -EINVAL;
+		up(&adb_probe_mutex);
+	}
+	/* Special case for ADB_BUSRESET request, all others are sent to
+	   the controller */
+	else if ((req->data[0] == ADB_PACKET)&&(count > 1)
+		&&(req->data[1] == ADB_BUSRESET)) {
+		ret = do_adb_reset_bus();
+		up(&adb_probe_mutex);
+		atomic_dec(&state->n_pending);
+		if (ret == 0)
+			ret = count;
+		goto out;
+	} else {	
+		req->reply_expected = ((req->data[1] & 0xc) == 0xc);
+		if (adb_controller && adb_controller->send_request)
+			ret = adb_controller->send_request(req, 0);
+		else
+			ret = -ENXIO;
+		up(&adb_probe_mutex);
+	}
+
+	if (ret != 0) {
+		atomic_dec(&state->n_pending);
+		goto out;
+	}
+	return count;
+
+out:
+	kfree(req);
+	return ret;
+}
+
+static struct file_operations adb_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.read		= adb_read,
+	.write		= adb_write,
+	.open		= adb_open,
+	.release	= adb_release,
+};
+
+static void
+adbdev_init(void)
+{
+	if (register_chrdev(ADB_MAJOR, "adb", &adb_fops)) {
+		printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR);
+		return;
+	}
+
+	devfs_mk_cdev(MKDEV(ADB_MAJOR, 0), S_IFCHR | S_IRUSR | S_IWUSR, "adb");
+
+	adb_dev_class = class_simple_create(THIS_MODULE, "adb");
+	if (IS_ERR(adb_dev_class)) {
+		return;
+	}
+	class_simple_device_add(adb_dev_class, MKDEV(ADB_MAJOR, 0), NULL, "adb");
+}
diff --git a/drivers/macintosh/adbhid.c b/drivers/macintosh/adbhid.c
new file mode 100644
index 0000000..8f93d01
--- /dev/null
+++ b/drivers/macintosh/adbhid.c
@@ -0,0 +1,1177 @@
+/*
+ * drivers/input/adbhid.c
+ *
+ * ADB HID driver for Power Macintosh computers.
+ *
+ * Adapted from drivers/macintosh/mac_keyb.c by Franz Sirl.
+ * drivers/macintosh/mac_keyb.c was Copyright (C) 1996 Paul Mackerras
+ * with considerable contributions from Ben Herrenschmidt and others.
+ *
+ * Copyright (C) 2000 Franz Sirl.
+ *
+ * Adapted to ADB changes and support for more devices by
+ * Benjamin Herrenschmidt. Adapted from code in MkLinux
+ * and reworked.
+ * 
+ * Supported devices:
+ *
+ * - Standard 1 button mouse
+ * - All standard Apple Extended protocol (handler ID 4)
+ * - mouseman and trackman mice & trackballs 
+ * - PowerBook Trackpad (default setup: enable tapping)
+ * - MicroSpeed mouse & trackball (needs testing)
+ * - CH Products Trackball Pro (needs testing)
+ * - Contour Design (Contour Mouse)
+ * - Hunter digital (NoHandsMouse)
+ * - Kensignton TurboMouse 5 (needs testing)
+ * - Mouse Systems A3 mice and trackballs <aidan@kublai.com>
+ * - MacAlly 2-buttons mouse (needs testing) <pochini@denise.shiny.it>
+ *
+ * To do:
+ *
+ * Improve Kensington support.
+ * Split mouse/kbd
+ * Move to syfs
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/notifier.h>
+#include <linux/input.h>
+
+#include <linux/adb.h>
+#include <linux/cuda.h>
+#include <linux/pmu.h>
+
+#include <asm/machdep.h>
+#ifdef CONFIG_PPC_PMAC
+#include <asm/pmac_feature.h>
+#endif
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+#include <asm/backlight.h>
+#endif
+
+MODULE_AUTHOR("Franz Sirl <Franz.Sirl-kernel@lauterbach.com>");
+
+#define KEYB_KEYREG	0	/* register # for key up/down data */
+#define KEYB_LEDREG	2	/* register # for leds on ADB keyboard */
+#define MOUSE_DATAREG	0	/* reg# for movement/button codes from mouse */
+
+static int adb_message_handler(struct notifier_block *, unsigned long, void *);
+static struct notifier_block adbhid_adb_notifier = {
+	.notifier_call	= adb_message_handler,
+};
+
+/* Some special keys */
+#define ADB_KEY_DEL		0x33
+#define ADB_KEY_CMD		0x37
+#define ADB_KEY_CAPSLOCK	0x39
+#define ADB_KEY_FN		0x3f
+#define ADB_KEY_FWDEL		0x75
+#define ADB_KEY_POWER_OLD	0x7e
+#define ADB_KEY_POWER		0x7f
+
+u8 adb_to_linux_keycodes[128] = {
+	/* 0x00 */ KEY_A, 		/*  30 */
+	/* 0x01 */ KEY_S, 		/*  31 */
+	/* 0x02 */ KEY_D,		/*  32 */
+	/* 0x03 */ KEY_F,		/*  33 */
+	/* 0x04 */ KEY_H,		/*  35 */
+	/* 0x05 */ KEY_G,		/*  34 */
+	/* 0x06 */ KEY_Z,		/*  44 */
+	/* 0x07 */ KEY_X,		/*  45 */
+	/* 0x08 */ KEY_C,		/*  46 */
+	/* 0x09 */ KEY_V,		/*  47 */
+	/* 0x0a */ KEY_102ND,		/*  86 */
+	/* 0x0b */ KEY_B,		/*  48 */
+	/* 0x0c */ KEY_Q,		/*  16 */
+	/* 0x0d */ KEY_W,		/*  17 */
+	/* 0x0e */ KEY_E,		/*  18 */
+	/* 0x0f */ KEY_R,		/*  19 */
+	/* 0x10 */ KEY_Y,		/*  21 */
+	/* 0x11 */ KEY_T,		/*  20 */
+	/* 0x12 */ KEY_1,		/*   2 */
+	/* 0x13 */ KEY_2,		/*   3 */
+	/* 0x14 */ KEY_3,		/*   4 */
+	/* 0x15 */ KEY_4,		/*   5 */
+	/* 0x16 */ KEY_6,		/*   7 */
+	/* 0x17 */ KEY_5,		/*   6 */
+	/* 0x18 */ KEY_EQUAL,		/*  13 */
+	/* 0x19 */ KEY_9,		/*  10 */
+	/* 0x1a */ KEY_7,		/*   8 */
+	/* 0x1b */ KEY_MINUS,		/*  12 */
+	/* 0x1c */ KEY_8,		/*   9 */
+	/* 0x1d */ KEY_0,		/*  11 */
+	/* 0x1e */ KEY_RIGHTBRACE,	/*  27 */
+	/* 0x1f */ KEY_O,		/*  24 */
+	/* 0x20 */ KEY_U,		/*  22 */
+	/* 0x21 */ KEY_LEFTBRACE,	/*  26 */
+	/* 0x22 */ KEY_I,		/*  23 */
+	/* 0x23 */ KEY_P,		/*  25 */
+	/* 0x24 */ KEY_ENTER,		/*  28 */
+	/* 0x25 */ KEY_L,		/*  38 */
+	/* 0x26 */ KEY_J,		/*  36 */
+	/* 0x27 */ KEY_APOSTROPHE,	/*  40 */
+	/* 0x28 */ KEY_K,		/*  37 */
+	/* 0x29 */ KEY_SEMICOLON,	/*  39 */
+	/* 0x2a */ KEY_BACKSLASH,	/*  43 */
+	/* 0x2b */ KEY_COMMA,		/*  51 */
+	/* 0x2c */ KEY_SLASH,		/*  53 */
+	/* 0x2d */ KEY_N,		/*  49 */
+	/* 0x2e */ KEY_M,		/*  50 */
+	/* 0x2f */ KEY_DOT,		/*  52 */
+	/* 0x30 */ KEY_TAB,		/*  15 */
+	/* 0x31 */ KEY_SPACE,		/*  57 */
+	/* 0x32 */ KEY_GRAVE,		/*  41 */
+	/* 0x33 */ KEY_BACKSPACE,	/*  14 */
+	/* 0x34 */ KEY_KPENTER,		/*  96 */
+	/* 0x35 */ KEY_ESC,		/*   1 */
+	/* 0x36 */ KEY_LEFTCTRL,	/*  29 */
+	/* 0x37 */ KEY_LEFTMETA,	/* 125 */
+	/* 0x38 */ KEY_LEFTSHIFT,	/*  42 */
+	/* 0x39 */ KEY_CAPSLOCK,	/*  58 */
+	/* 0x3a */ KEY_LEFTALT,		/*  56 */
+	/* 0x3b */ KEY_LEFT,		/* 105 */
+	/* 0x3c */ KEY_RIGHT,		/* 106 */
+	/* 0x3d */ KEY_DOWN,		/* 108 */
+	/* 0x3e */ KEY_UP,		/* 103 */
+	/* 0x3f */ 0,
+	/* 0x40 */ 0,
+	/* 0x41 */ KEY_KPDOT,		/*  83 */
+	/* 0x42 */ 0,
+	/* 0x43 */ KEY_KPASTERISK,	/*  55 */
+	/* 0x44 */ 0,
+	/* 0x45 */ KEY_KPPLUS,		/*  78 */
+	/* 0x46 */ 0,
+	/* 0x47 */ KEY_NUMLOCK,		/*  69 */
+	/* 0x48 */ 0,
+	/* 0x49 */ 0,
+	/* 0x4a */ 0,
+	/* 0x4b */ KEY_KPSLASH,		/*  98 */
+	/* 0x4c */ KEY_KPENTER,		/*  96 */
+	/* 0x4d */ 0,
+	/* 0x4e */ KEY_KPMINUS,		/*  74 */
+	/* 0x4f */ 0,
+	/* 0x50 */ 0,
+	/* 0x51 */ KEY_KPEQUAL,		/* 117 */
+	/* 0x52 */ KEY_KP0,		/*  82 */
+	/* 0x53 */ KEY_KP1,		/*  79 */
+	/* 0x54 */ KEY_KP2,		/*  80 */
+	/* 0x55 */ KEY_KP3,		/*  81 */
+	/* 0x56 */ KEY_KP4,		/*  75 */
+	/* 0x57 */ KEY_KP5,		/*  76 */
+	/* 0x58 */ KEY_KP6,		/*  77 */
+	/* 0x59 */ KEY_KP7,		/*  71 */
+	/* 0x5a */ 0,
+	/* 0x5b */ KEY_KP8,		/*  72 */
+	/* 0x5c */ KEY_KP9,		/*  73 */
+	/* 0x5d */ KEY_YEN,		/* 124 */
+	/* 0x5e */ KEY_RO,		/*  89 */
+	/* 0x5f */ KEY_KPCOMMA,		/* 121 */
+	/* 0x60 */ KEY_F5,		/*  63 */
+	/* 0x61 */ KEY_F6,		/*  64 */
+	/* 0x62 */ KEY_F7,		/*  65 */
+	/* 0x63 */ KEY_F3,		/*  61 */
+	/* 0x64 */ KEY_F8,		/*  66 */
+	/* 0x65 */ KEY_F9,		/*  67 */
+	/* 0x66 */ KEY_HANJA,		/* 123 */
+	/* 0x67 */ KEY_F11,		/*  87 */
+	/* 0x68 */ KEY_HANGUEL,		/* 122 */
+	/* 0x69 */ KEY_SYSRQ,		/*  99 */
+	/* 0x6a */ 0,
+	/* 0x6b */ KEY_SCROLLLOCK,	/*  70 */
+	/* 0x6c */ 0,
+	/* 0x6d */ KEY_F10,		/*  68 */
+	/* 0x6e */ KEY_COMPOSE,		/* 127 */
+	/* 0x6f */ KEY_F12,		/*  88 */
+	/* 0x70 */ 0,
+	/* 0x71 */ KEY_PAUSE,		/* 119 */
+	/* 0x72 */ KEY_INSERT,		/* 110 */
+	/* 0x73 */ KEY_HOME,		/* 102 */
+	/* 0x74 */ KEY_PAGEUP,		/* 104 */
+	/* 0x75 */ KEY_DELETE,		/* 111 */
+	/* 0x76 */ KEY_F4,		/*  62 */
+	/* 0x77 */ KEY_END,		/* 107 */
+	/* 0x78 */ KEY_F2,		/*  60 */
+	/* 0x79 */ KEY_PAGEDOWN,	/* 109 */
+	/* 0x7a */ KEY_F1,		/*  59 */
+	/* 0x7b */ KEY_RIGHTSHIFT,	/*  54 */
+	/* 0x7c */ KEY_RIGHTALT,	/* 100 */
+	/* 0x7d */ KEY_RIGHTCTRL,	/*  97 */
+	/* 0x7e */ KEY_RIGHTMETA,	/* 126 */
+	/* 0x7f */ KEY_POWER,		/* 116 */
+};
+
+struct adbhid {
+	struct input_dev input;
+	int id;
+	int default_id;
+	int original_handler_id;
+	int current_handler_id;
+	int mouse_kind;
+	unsigned char *keycode;
+	char name[64];
+	char phys[32];
+	int flags;
+};
+
+#define FLAG_FN_KEY_PRESSED	0x00000001
+#define FLAG_POWER_FROM_FN	0x00000002
+#define FLAG_EMU_FWDEL_DOWN	0x00000004
+
+static struct adbhid *adbhid[16];
+
+static void adbhid_probe(void);
+
+static void adbhid_input_keycode(int, int, int, struct pt_regs *);
+
+static void init_trackpad(int id);
+static void init_trackball(int id);
+static void init_turbomouse(int id);
+static void init_microspeed(int id);
+static void init_ms_a3(int id);
+
+static struct adb_ids keyboard_ids;
+static struct adb_ids mouse_ids;
+static struct adb_ids buttons_ids;
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+/* Exported to via-pmu.c */
+int disable_kernel_backlight = 0;
+#endif /* CONFIG_PMAC_BACKLIGHT */
+
+/* Kind of keyboard, see Apple technote 1152  */
+#define ADB_KEYBOARD_UNKNOWN	0
+#define ADB_KEYBOARD_ANSI	0x0100
+#define ADB_KEYBOARD_ISO	0x0200
+#define ADB_KEYBOARD_JIS	0x0300
+
+/* Kind of mouse  */
+#define ADBMOUSE_STANDARD_100	0	/* Standard 100cpi mouse (handler 1) */
+#define ADBMOUSE_STANDARD_200	1	/* Standard 200cpi mouse (handler 2) */
+#define ADBMOUSE_EXTENDED	2	/* Apple Extended mouse (handler 4) */
+#define ADBMOUSE_TRACKBALL	3	/* TrackBall (handler 4) */
+#define ADBMOUSE_TRACKPAD       4	/* Apple's PowerBook trackpad (handler 4) */
+#define ADBMOUSE_TURBOMOUSE5    5	/* Turbomouse 5 (previously req. mousehack) */
+#define ADBMOUSE_MICROSPEED	6	/* Microspeed mouse (&trackball ?), MacPoint */
+#define ADBMOUSE_TRACKBALLPRO	7	/* Trackball Pro (special buttons) */
+#define ADBMOUSE_MS_A3		8	/* Mouse systems A3 trackball (handler 3) */
+#define ADBMOUSE_MACALLY2	9	/* MacAlly 2-button mouse */
+
+static void
+adbhid_keyboard_input(unsigned char *data, int nb, struct pt_regs *regs, int apoll)
+{
+	int id = (data[0] >> 4) & 0x0f;
+
+	if (!adbhid[id]) {
+		printk(KERN_ERR "ADB HID on ID %d not yet registered, packet %#02x, %#02x, %#02x, %#02x\n",
+		       id, data[0], data[1], data[2], data[3]);
+		return;
+	}
+
+	/* first check this is from register 0 */
+	if (nb != 3 || (data[0] & 3) != KEYB_KEYREG)
+		return;		/* ignore it */
+	adbhid_input_keycode(id, data[1], 0, regs);
+	if (!(data[2] == 0xff || (data[2] == 0x7f && data[1] == 0x7f)))
+		adbhid_input_keycode(id, data[2], 0, regs);
+}
+
+static void
+adbhid_input_keycode(int id, int keycode, int repeat, struct pt_regs *regs)
+{
+	struct adbhid *ahid = adbhid[id];
+	int up_flag;
+
+	up_flag = (keycode & 0x80);
+	keycode &= 0x7f;
+
+	switch (keycode) {
+	case ADB_KEY_CAPSLOCK: /* Generate down/up events for CapsLock everytime. */
+		input_regs(&ahid->input, regs);
+		input_report_key(&ahid->input, KEY_CAPSLOCK, 1);
+		input_report_key(&ahid->input, KEY_CAPSLOCK, 0);
+		input_sync(&ahid->input);
+		return;
+#ifdef CONFIG_PPC_PMAC
+	case ADB_KEY_POWER_OLD: /* Power key on PBook 3400 needs remapping */
+		switch(pmac_call_feature(PMAC_FTR_GET_MB_INFO,
+			NULL, PMAC_MB_INFO_MODEL, 0)) {
+		case PMAC_TYPE_COMET:
+		case PMAC_TYPE_HOOPER:
+		case PMAC_TYPE_KANGA:
+			keycode = ADB_KEY_POWER;
+		}
+		break;
+	case ADB_KEY_POWER: 
+		/* Fn + Command will produce a bogus "power" keycode */
+		if (ahid->flags & FLAG_FN_KEY_PRESSED) {
+			keycode = ADB_KEY_CMD;
+			if (up_flag)
+				ahid->flags &= ~FLAG_POWER_FROM_FN;
+			else
+				ahid->flags |= FLAG_POWER_FROM_FN;
+		} else if (ahid->flags & FLAG_POWER_FROM_FN) {
+			keycode = ADB_KEY_CMD;
+			ahid->flags &= ~FLAG_POWER_FROM_FN;
+		}
+		break;
+	case ADB_KEY_FN:
+		/* Keep track of the Fn key state */
+		if (up_flag) {
+			ahid->flags &= ~FLAG_FN_KEY_PRESSED;
+			/* Emulate Fn+delete = forward delete */
+			if (ahid->flags & FLAG_EMU_FWDEL_DOWN) {
+				ahid->flags &= ~FLAG_EMU_FWDEL_DOWN;
+				keycode = ADB_KEY_FWDEL;
+				break;
+			}
+		} else
+			ahid->flags |= FLAG_FN_KEY_PRESSED;
+		/* Swallow the key press */
+		return;
+	case ADB_KEY_DEL:
+		/* Emulate Fn+delete = forward delete */
+		if (ahid->flags & FLAG_FN_KEY_PRESSED) {
+			keycode = ADB_KEY_FWDEL;
+			if (up_flag)
+				ahid->flags &= ~FLAG_EMU_FWDEL_DOWN;
+			else
+				ahid->flags |= FLAG_EMU_FWDEL_DOWN;
+		}
+		break;
+#endif /* CONFIG_PPC_PMAC */
+	}
+
+	if (adbhid[id]->keycode[keycode]) {
+		input_regs(&adbhid[id]->input, regs);
+		input_report_key(&adbhid[id]->input,
+				 adbhid[id]->keycode[keycode], !up_flag);
+		input_sync(&adbhid[id]->input);
+	} else
+		printk(KERN_INFO "Unhandled ADB key (scancode %#02x) %s.\n", keycode,
+		       up_flag ? "released" : "pressed");
+
+}
+
+static void
+adbhid_mouse_input(unsigned char *data, int nb, struct pt_regs *regs, int autopoll)
+{
+	int id = (data[0] >> 4) & 0x0f;
+
+	if (!adbhid[id]) {
+		printk(KERN_ERR "ADB HID on ID %d not yet registered\n", id);
+		return;
+	}
+
+  /*
+    Handler 1 -- 100cpi original Apple mouse protocol.
+    Handler 2 -- 200cpi original Apple mouse protocol.
+
+    For Apple's standard one-button mouse protocol the data array will
+    contain the following values:
+
+                BITS    COMMENTS
+    data[0] = dddd 1100 ADB command: Talk, register 0, for device dddd.
+    data[1] = bxxx xxxx First button and x-axis motion.
+    data[2] = byyy yyyy Second button and y-axis motion.
+
+    Handler 4 -- Apple Extended mouse protocol.
+
+    For Apple's 3-button mouse protocol the data array will contain the
+    following values:
+
+		BITS    COMMENTS
+    data[0] = dddd 1100 ADB command: Talk, register 0, for device dddd.
+    data[1] = bxxx xxxx Left button and x-axis motion.
+    data[2] = byyy yyyy Second button and y-axis motion.
+    data[3] = byyy bxxx Third button and fourth button.  Y is additional
+	      high bits of y-axis motion.  XY is additional
+	      high bits of x-axis motion.
+
+    MacAlly 2-button mouse protocol.
+
+    For MacAlly 2-button mouse protocol the data array will contain the
+    following values:
+
+		BITS    COMMENTS
+    data[0] = dddd 1100 ADB command: Talk, register 0, for device dddd.
+    data[1] = bxxx xxxx Left button and x-axis motion.
+    data[2] = byyy yyyy Right button and y-axis motion.
+    data[3] = ???? ???? unknown
+    data[4] = ???? ???? unknown
+
+  */
+
+	/* If it's a trackpad, we alias the second button to the first.
+	   NOTE: Apple sends an ADB flush command to the trackpad when
+	         the first (the real) button is released. We could do
+		 this here using async flush requests.
+	*/
+	switch (adbhid[id]->mouse_kind)
+	{
+	    case ADBMOUSE_TRACKPAD:
+		data[1] = (data[1] & 0x7f) | ((data[1] & data[2]) & 0x80);
+		data[2] = data[2] | 0x80;
+		break;
+	    case ADBMOUSE_MICROSPEED:
+		data[1] = (data[1] & 0x7f) | ((data[3] & 0x01) << 7);
+		data[2] = (data[2] & 0x7f) | ((data[3] & 0x02) << 6);
+		data[3] = (data[3] & 0x77) | ((data[3] & 0x04) << 5)
+			| (data[3] & 0x08);
+		break;
+	    case ADBMOUSE_TRACKBALLPRO:
+		data[1] = (data[1] & 0x7f) | (((data[3] & 0x04) << 5)
+			& ((data[3] & 0x08) << 4));
+		data[2] = (data[2] & 0x7f) | ((data[3] & 0x01) << 7);
+		data[3] = (data[3] & 0x77) | ((data[3] & 0x02) << 6);
+		break;
+	    case ADBMOUSE_MS_A3:
+		data[1] = (data[1] & 0x7f) | ((data[3] & 0x01) << 7);
+		data[2] = (data[2] & 0x7f) | ((data[3] & 0x02) << 6);
+		data[3] = ((data[3] & 0x04) << 5);
+		break;
+            case ADBMOUSE_MACALLY2:
+		data[3] = (data[2] & 0x80) ? 0x80 : 0x00;
+		data[2] |= 0x80;  /* Right button is mapped as button 3 */
+		nb=4;
+                break;
+	}
+
+	input_regs(&adbhid[id]->input, regs);
+
+	input_report_key(&adbhid[id]->input, BTN_LEFT,   !((data[1] >> 7) & 1));
+	input_report_key(&adbhid[id]->input, BTN_MIDDLE, !((data[2] >> 7) & 1));
+
+	if (nb >= 4 && adbhid[id]->mouse_kind != ADBMOUSE_TRACKPAD)
+		input_report_key(&adbhid[id]->input, BTN_RIGHT,  !((data[3] >> 7) & 1));
+
+	input_report_rel(&adbhid[id]->input, REL_X,
+			 ((data[2]&0x7f) < 64 ? (data[2]&0x7f) : (data[2]&0x7f)-128 ));
+	input_report_rel(&adbhid[id]->input, REL_Y,
+			 ((data[1]&0x7f) < 64 ? (data[1]&0x7f) : (data[1]&0x7f)-128 ));
+
+	input_sync(&adbhid[id]->input);
+}
+
+static void
+adbhid_buttons_input(unsigned char *data, int nb, struct pt_regs *regs, int autopoll)
+{
+	int id = (data[0] >> 4) & 0x0f;
+
+	if (!adbhid[id]) {
+		printk(KERN_ERR "ADB HID on ID %d not yet registered\n", id);
+		return;
+	}
+
+	input_regs(&adbhid[id]->input, regs);
+
+	switch (adbhid[id]->original_handler_id) {
+	default:
+	case 0x02: /* Adjustable keyboard button device */
+	  {
+		int down = (data[1] == (data[1] & 0xf));
+
+		switch (data[1] & 0x0f) {
+		case 0x0:	/* microphone */
+			input_report_key(&adbhid[id]->input, KEY_SOUND, down);
+			break;
+
+		case 0x1:	/* mute */
+			input_report_key(&adbhid[id]->input, KEY_MUTE, down);
+			break;
+
+		case 0x2:	/* volume decrease */
+			input_report_key(&adbhid[id]->input, KEY_VOLUMEDOWN, down);
+			break;
+
+		case 0x3:	/* volume increase */
+			input_report_key(&adbhid[id]->input, KEY_VOLUMEUP, down);
+			break;
+
+		default:
+			printk(KERN_INFO "Unhandled ADB_MISC event %02x, %02x, %02x, %02x\n",
+			       data[0], data[1], data[2], data[3]);
+			break;
+		}
+	  }
+	  break;
+
+	case 0x1f: /* Powerbook button device */
+	  {
+		int down = (data[1] == (data[1] & 0xf));
+#ifdef CONFIG_PMAC_BACKLIGHT
+		int backlight = get_backlight_level();
+#endif
+		/*
+		 * XXX: Where is the contrast control for the passive?
+		 *  -- Cort
+		 */
+
+		switch (data[1] & 0x0f) {
+		case 0x8:	/* mute */
+			input_report_key(&adbhid[id]->input, KEY_MUTE, down);
+			break;
+
+		case 0x7:	/* volume decrease */
+			input_report_key(&adbhid[id]->input, KEY_VOLUMEDOWN, down);
+			break;
+
+		case 0x6:	/* volume increase */
+			input_report_key(&adbhid[id]->input, KEY_VOLUMEUP, down);
+			break;
+
+		case 0xb:	/* eject */
+			input_report_key(&adbhid[id]->input, KEY_EJECTCD, down);
+			break;
+
+		case 0xa:	/* brightness decrease */
+#ifdef CONFIG_PMAC_BACKLIGHT
+			if (!disable_kernel_backlight) {
+				if (down && backlight >= 0) {
+					if (backlight > BACKLIGHT_OFF)
+						set_backlight_level(backlight-1);
+					else
+						set_backlight_level(BACKLIGHT_OFF);
+				}
+			}
+#endif /* CONFIG_PMAC_BACKLIGHT */
+			input_report_key(&adbhid[id]->input, KEY_BRIGHTNESSDOWN, down);
+			break;
+
+		case 0x9:	/* brightness increase */
+#ifdef CONFIG_PMAC_BACKLIGHT
+			if (!disable_kernel_backlight) {
+				if (down && backlight >= 0) {
+					if (backlight < BACKLIGHT_MAX)
+						set_backlight_level(backlight+1);
+					else 
+						set_backlight_level(BACKLIGHT_MAX);
+				}
+			}
+#endif /* CONFIG_PMAC_BACKLIGHT */
+			input_report_key(&adbhid[id]->input, KEY_BRIGHTNESSUP, down);
+			break;
+		}
+	  }
+	  break;
+	}
+
+	input_sync(&adbhid[id]->input);
+}
+
+static struct adb_request led_request;
+static int leds_pending[16];
+static int leds_req_pending;
+static int pending_devs[16];
+static int pending_led_start=0;
+static int pending_led_end=0;
+static DEFINE_SPINLOCK(leds_lock);
+
+static void leds_done(struct adb_request *req)
+{
+	int leds = 0, device = 0, pending = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&leds_lock, flags);
+
+	if (pending_led_start != pending_led_end) {
+		device = pending_devs[pending_led_start];
+		leds = leds_pending[device] & 0xff;
+		leds_pending[device] = 0;
+		pending_led_start++;
+		pending_led_start = (pending_led_start < 16) ? pending_led_start : 0;
+		pending = leds_req_pending;
+	} else
+		leds_req_pending = 0;
+	spin_unlock_irqrestore(&leds_lock, flags);
+	if (pending)
+		adb_request(&led_request, leds_done, 0, 3,
+			    ADB_WRITEREG(device, KEYB_LEDREG), 0xff, ~leds);
+}
+
+static void real_leds(unsigned char leds, int device)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&leds_lock, flags);
+	if (!leds_req_pending) {
+		leds_req_pending = 1;
+		spin_unlock_irqrestore(&leds_lock, flags);	       
+		adb_request(&led_request, leds_done, 0, 3,
+			    ADB_WRITEREG(device, KEYB_LEDREG), 0xff, ~leds);
+		return;
+	} else {
+		if (!(leds_pending[device] & 0x100)) {
+			pending_devs[pending_led_end] = device;
+			pending_led_end++;
+			pending_led_end = (pending_led_end < 16) ? pending_led_end : 0;
+		}
+		leds_pending[device] = leds | 0x100;
+	}
+	spin_unlock_irqrestore(&leds_lock, flags);	       
+}
+
+/*
+ * Event callback from the input module. Events that change the state of
+ * the hardware are processed here.
+ */
+static int adbhid_kbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	struct adbhid *adbhid = dev->private;
+	unsigned char leds;
+
+	switch (type) {
+	case EV_LED:
+	  leds = (test_bit(LED_SCROLLL, dev->led) ? 4 : 0)
+	       | (test_bit(LED_NUML,    dev->led) ? 1 : 0)
+	       | (test_bit(LED_CAPSL,   dev->led) ? 2 : 0);
+	  real_leds(leds, adbhid->id);
+	  return 0;
+	}
+
+	return -1;
+}
+
+static int
+adb_message_handler(struct notifier_block *this, unsigned long code, void *x)
+{
+	switch (code) {
+	case ADB_MSG_PRE_RESET:
+	case ADB_MSG_POWERDOWN:
+	    	/* Stop the repeat timer. Autopoll is already off at this point */
+		{
+			int i;
+			for (i = 1; i < 16; i++) {
+				if (adbhid[i])
+					del_timer_sync(&adbhid[i]->input.timer);
+			}
+		}
+
+		/* Stop pending led requests */
+		while(leds_req_pending)
+			adb_poll();
+		break;
+
+	case ADB_MSG_POST_RESET:
+		adbhid_probe();
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static void
+adbhid_input_register(int id, int default_id, int original_handler_id,
+		      int current_handler_id, int mouse_kind)
+{
+	int i;
+
+	if (adbhid[id]) {
+		printk(KERN_ERR "Trying to reregister ADB HID on ID %d\n", id);
+		return;
+	}
+
+	if (!(adbhid[id] = kmalloc(sizeof(struct adbhid), GFP_KERNEL)))
+		return;
+
+	memset(adbhid[id], 0, sizeof(struct adbhid));
+	sprintf(adbhid[id]->phys, "adb%d:%d.%02x/input", id, default_id, original_handler_id);
+
+	init_input_dev(&adbhid[id]->input);
+
+	adbhid[id]->id = default_id;
+	adbhid[id]->original_handler_id = original_handler_id;
+	adbhid[id]->current_handler_id = current_handler_id;
+	adbhid[id]->mouse_kind = mouse_kind;
+	adbhid[id]->flags = 0;
+	adbhid[id]->input.private = adbhid[id];
+	adbhid[id]->input.name = adbhid[id]->name;
+	adbhid[id]->input.phys = adbhid[id]->phys;
+	adbhid[id]->input.id.bustype = BUS_ADB;
+	adbhid[id]->input.id.vendor = 0x0001;
+	adbhid[id]->input.id.product = (id << 12) | (default_id << 8) | original_handler_id;
+	adbhid[id]->input.id.version = 0x0100;
+
+	switch (default_id) {
+	case ADB_KEYBOARD:
+		if (!(adbhid[id]->keycode = kmalloc(sizeof(adb_to_linux_keycodes), GFP_KERNEL))) {
+			kfree(adbhid[id]);
+			return;
+		}
+
+		sprintf(adbhid[id]->name, "ADB keyboard");
+
+		memcpy(adbhid[id]->keycode, adb_to_linux_keycodes, sizeof(adb_to_linux_keycodes));
+
+		printk(KERN_INFO "Detected ADB keyboard, type ");
+		switch (original_handler_id) {
+		default:
+			printk("<unknown>.\n");
+			adbhid[id]->input.id.version = ADB_KEYBOARD_UNKNOWN;
+			break;
+
+		case 0x01: case 0x02: case 0x03: case 0x06: case 0x08:
+		case 0x0C: case 0x10: case 0x18: case 0x1B: case 0x1C:
+		case 0xC0: case 0xC3: case 0xC6:
+			printk("ANSI.\n");
+			adbhid[id]->input.id.version = ADB_KEYBOARD_ANSI;
+			break;
+
+		case 0x04: case 0x05: case 0x07: case 0x09: case 0x0D:
+		case 0x11: case 0x14: case 0x19: case 0x1D: case 0xC1:
+		case 0xC4: case 0xC7:
+			printk("ISO, swapping keys.\n");
+			adbhid[id]->input.id.version = ADB_KEYBOARD_ISO;
+			i = adbhid[id]->keycode[10];
+			adbhid[id]->keycode[10] = adbhid[id]->keycode[50];
+			adbhid[id]->keycode[50] = i;
+			break;
+
+		case 0x12: case 0x15: case 0x16: case 0x17: case 0x1A:
+		case 0x1E: case 0xC2: case 0xC5: case 0xC8: case 0xC9:
+			printk("JIS.\n");
+			adbhid[id]->input.id.version = ADB_KEYBOARD_JIS;
+			break;
+		}
+
+		for (i = 0; i < 128; i++)
+			if (adbhid[id]->keycode[i])
+				set_bit(adbhid[id]->keycode[i], adbhid[id]->input.keybit);
+
+		adbhid[id]->input.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
+		adbhid[id]->input.ledbit[0] = BIT(LED_SCROLLL) | BIT(LED_CAPSL) | BIT(LED_NUML);
+		adbhid[id]->input.event = adbhid_kbd_event;
+		adbhid[id]->input.keycodemax = 127;
+		adbhid[id]->input.keycodesize = 1;
+		break;
+
+	case ADB_MOUSE:
+		sprintf(adbhid[id]->name, "ADB mouse");
+
+		adbhid[id]->input.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+		adbhid[id]->input.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
+		adbhid[id]->input.relbit[0] = BIT(REL_X) | BIT(REL_Y);
+		break;
+
+	case ADB_MISC:
+		switch (original_handler_id) {
+		case 0x02: /* Adjustable keyboard button device */
+			sprintf(adbhid[id]->name, "ADB adjustable keyboard buttons");
+			adbhid[id]->input.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+			set_bit(KEY_SOUND, adbhid[id]->input.keybit);
+			set_bit(KEY_MUTE, adbhid[id]->input.keybit);
+			set_bit(KEY_VOLUMEUP, adbhid[id]->input.keybit);
+			set_bit(KEY_VOLUMEDOWN, adbhid[id]->input.keybit);
+			break;
+		case 0x1f: /* Powerbook button device */
+			sprintf(adbhid[id]->name, "ADB Powerbook buttons");
+			adbhid[id]->input.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+			set_bit(KEY_MUTE, adbhid[id]->input.keybit);
+			set_bit(KEY_VOLUMEUP, adbhid[id]->input.keybit);
+			set_bit(KEY_VOLUMEDOWN, adbhid[id]->input.keybit);
+			set_bit(KEY_BRIGHTNESSUP, adbhid[id]->input.keybit);
+			set_bit(KEY_BRIGHTNESSDOWN, adbhid[id]->input.keybit);
+			set_bit(KEY_EJECTCD, adbhid[id]->input.keybit);
+			break;
+		}
+		if (adbhid[id]->name[0])
+			break;
+		/* else fall through */
+
+	default:
+		printk(KERN_INFO "Trying to register unknown ADB device to input layer.\n");
+		kfree(adbhid[id]);
+		return;
+	}
+
+	adbhid[id]->input.keycode = adbhid[id]->keycode;
+
+	input_register_device(&adbhid[id]->input);
+
+	printk(KERN_INFO "input: %s on %s\n",
+	       adbhid[id]->name, adbhid[id]->phys);
+
+	if (default_id == ADB_KEYBOARD) {
+		/* HACK WARNING!! This should go away as soon there is an utility
+		 * to control that for event devices.
+		 */
+		adbhid[id]->input.rep[REP_DELAY] = 500;   /* input layer default: 250 */
+		adbhid[id]->input.rep[REP_PERIOD] = 66; /* input layer default: 33 */
+	}
+}
+
+static void adbhid_input_unregister(int id)
+{
+	input_unregister_device(&adbhid[id]->input);
+	if (adbhid[id]->keycode)
+		kfree(adbhid[id]->keycode);
+	kfree(adbhid[id]);
+	adbhid[id] = NULL;
+}
+
+
+static u16
+adbhid_input_reregister(int id, int default_id, int org_handler_id,
+			int cur_handler_id, int mk)
+{
+	if (adbhid[id]) {
+		if (adbhid[id]->input.id.product !=
+		    ((id << 12)|(default_id << 8)|org_handler_id)) {
+			adbhid_input_unregister(id);
+			adbhid_input_register(id, default_id, org_handler_id,
+					      cur_handler_id, mk);
+		}
+	} else
+		adbhid_input_register(id, default_id, org_handler_id,
+				      cur_handler_id, mk);
+	return 1<<id;
+}
+
+static void
+adbhid_input_devcleanup(u16 exist)
+{
+	int i;
+	for(i=1; i<16; i++)
+		if (adbhid[i] && !(exist&(1<<i)))
+			adbhid_input_unregister(i);
+}
+
+static void
+adbhid_probe(void)
+{
+	struct adb_request req;
+	int i, default_id, org_handler_id, cur_handler_id;
+	u16 reg = 0;
+
+	adb_register(ADB_MOUSE, 0, &mouse_ids, adbhid_mouse_input);
+	adb_register(ADB_KEYBOARD, 0, &keyboard_ids, adbhid_keyboard_input);
+	adb_register(ADB_MISC, 0, &buttons_ids, adbhid_buttons_input);
+
+	for (i = 0; i < keyboard_ids.nids; i++) {
+		int id = keyboard_ids.id[i];
+
+		adb_get_infos(id, &default_id, &org_handler_id);
+
+		/* turn off all leds */
+		adb_request(&req, NULL, ADBREQ_SYNC, 3,
+			    ADB_WRITEREG(id, KEYB_LEDREG), 0xff, 0xff);
+
+		/* Enable full feature set of the keyboard
+		   ->get it to send separate codes for left and right shift,
+		   control, option keys */
+#if 0		/* handler 5 doesn't send separate codes for R modifiers */
+		if (adb_try_handler_change(id, 5))
+			printk("ADB keyboard at %d, handler set to 5\n", id);
+		else
+#endif
+		if (adb_try_handler_change(id, 3))
+			printk("ADB keyboard at %d, handler set to 3\n", id);
+		else
+			printk("ADB keyboard at %d, handler 1\n", id);
+
+		adb_get_infos(id, &default_id, &cur_handler_id);
+		reg |= adbhid_input_reregister(id, default_id, org_handler_id,
+					       cur_handler_id, 0);
+	}
+
+	for (i = 0; i < buttons_ids.nids; i++) {
+		int id = buttons_ids.id[i];
+
+		adb_get_infos(id, &default_id, &org_handler_id);
+		reg |= adbhid_input_reregister(id, default_id, org_handler_id,
+					       org_handler_id, 0);
+	}
+
+	/* Try to switch all mice to handler 4, or 2 for three-button
+	   mode and full resolution. */
+	for (i = 0; i < mouse_ids.nids; i++) {
+		int id = mouse_ids.id[i];
+		int mouse_kind;
+
+		adb_get_infos(id, &default_id, &org_handler_id);
+
+		if (adb_try_handler_change(id, 4)) {
+			printk("ADB mouse at %d, handler set to 4", id);
+			mouse_kind = ADBMOUSE_EXTENDED;
+		}
+		else if (adb_try_handler_change(id, 0x2F)) {
+			printk("ADB mouse at %d, handler set to 0x2F", id);
+			mouse_kind = ADBMOUSE_MICROSPEED;
+		}
+		else if (adb_try_handler_change(id, 0x42)) {
+			printk("ADB mouse at %d, handler set to 0x42", id);
+			mouse_kind = ADBMOUSE_TRACKBALLPRO;
+		}
+		else if (adb_try_handler_change(id, 0x66)) {
+			printk("ADB mouse at %d, handler set to 0x66", id);
+			mouse_kind = ADBMOUSE_MICROSPEED;
+		}
+		else if (adb_try_handler_change(id, 0x5F)) {
+			printk("ADB mouse at %d, handler set to 0x5F", id);
+			mouse_kind = ADBMOUSE_MICROSPEED;
+		}
+		else if (adb_try_handler_change(id, 3)) {
+			printk("ADB mouse at %d, handler set to 3", id);
+			mouse_kind = ADBMOUSE_MS_A3;
+		}
+		else if (adb_try_handler_change(id, 2)) {
+			printk("ADB mouse at %d, handler set to 2", id);
+			mouse_kind = ADBMOUSE_STANDARD_200;
+		}
+		else {
+			printk("ADB mouse at %d, handler 1", id);
+			mouse_kind = ADBMOUSE_STANDARD_100;
+		}
+
+		if ((mouse_kind == ADBMOUSE_TRACKBALLPRO)
+		    || (mouse_kind == ADBMOUSE_MICROSPEED)) {
+			init_microspeed(id);
+		} else if (mouse_kind == ADBMOUSE_MS_A3) {
+			init_ms_a3(id);
+		} else if (mouse_kind ==  ADBMOUSE_EXTENDED) {
+			/*
+			 * Register 1 is usually used for device
+			 * identification.  Here, we try to identify
+			 * a known device and call the appropriate
+			 * init function.
+			 */
+			adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+				    ADB_READREG(id, 1));
+
+			if ((req.reply_len) &&
+			    (req.reply[1] == 0x9a) && ((req.reply[2] == 0x21)
+			    	|| (req.reply[2] == 0x20))) {
+				mouse_kind = ADBMOUSE_TRACKBALL;
+				init_trackball(id);
+			}
+			else if ((req.reply_len >= 4) &&
+			    (req.reply[1] == 0x74) && (req.reply[2] == 0x70) &&
+			    (req.reply[3] == 0x61) && (req.reply[4] == 0x64)) {
+				mouse_kind = ADBMOUSE_TRACKPAD;
+				init_trackpad(id);
+			}
+			else if ((req.reply_len >= 4) &&
+			    (req.reply[1] == 0x4b) && (req.reply[2] == 0x4d) &&
+			    (req.reply[3] == 0x4c) && (req.reply[4] == 0x31)) {
+				mouse_kind = ADBMOUSE_TURBOMOUSE5;
+				init_turbomouse(id);
+			}
+			else if ((req.reply_len == 9) &&
+			    (req.reply[1] == 0x4b) && (req.reply[2] == 0x4f) &&
+			    (req.reply[3] == 0x49) && (req.reply[4] == 0x54)) {
+				if (adb_try_handler_change(id, 0x42)) {
+					printk("\nADB MacAlly 2-button mouse at %d, handler set to 0x42", id);
+					mouse_kind = ADBMOUSE_MACALLY2;
+				}
+			}
+		}
+		printk("\n");
+
+		adb_get_infos(id, &default_id, &cur_handler_id);
+		reg |= adbhid_input_reregister(id, default_id, org_handler_id,
+					       cur_handler_id, mouse_kind);
+	}
+	adbhid_input_devcleanup(reg);
+}
+
+static void 
+init_trackpad(int id)
+{
+	struct adb_request req;
+	unsigned char r1_buffer[8];
+
+	printk(" (trackpad)");
+
+	adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+		    ADB_READREG(id,1));
+	if (req.reply_len < 8)
+	    printk("bad length for reg. 1\n");
+	else
+	{
+	    memcpy(r1_buffer, &req.reply[1], 8);
+
+	    adb_request(&req, NULL, ADBREQ_SYNC, 9,
+	        ADB_WRITEREG(id,1),
+	            r1_buffer[0],
+	            r1_buffer[1],
+	            r1_buffer[2],
+	            r1_buffer[3],
+	            r1_buffer[4],
+	            r1_buffer[5],
+	            0x0d,
+	            r1_buffer[7]);
+
+            adb_request(&req, NULL, ADBREQ_SYNC, 9,
+	        ADB_WRITEREG(id,2),
+	    	    0x99,
+	    	    0x94,
+	    	    0x19,
+	    	    0xff,
+	    	    0xb2,
+	    	    0x8a,
+	    	    0x1b,
+	    	    0x50);
+
+	    adb_request(&req, NULL, ADBREQ_SYNC, 9,
+	        ADB_WRITEREG(id,1),
+	            r1_buffer[0],
+	            r1_buffer[1],
+	            r1_buffer[2],
+	            r1_buffer[3],
+	            r1_buffer[4],
+	            r1_buffer[5],
+	            0x03, /*r1_buffer[6],*/
+	            r1_buffer[7]);
+
+	    /* Without this flush, the trackpad may be locked up */
+	    adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(id));
+        }
+}
+
+static void 
+init_trackball(int id)
+{
+	struct adb_request req;
+
+	printk(" (trackman/mouseman)");
+
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	ADB_WRITEREG(id,1), 00,0x81);
+
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	ADB_WRITEREG(id,1), 01,0x81);
+
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	ADB_WRITEREG(id,1), 02,0x81);
+
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	ADB_WRITEREG(id,1), 03,0x38);
+
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	ADB_WRITEREG(id,1), 00,0x81);
+
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	ADB_WRITEREG(id,1), 01,0x81);
+
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	ADB_WRITEREG(id,1), 02,0x81);
+
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	ADB_WRITEREG(id,1), 03,0x38);
+}
+
+static void
+init_turbomouse(int id)
+{
+	struct adb_request req;
+
+        printk(" (TurboMouse 5)");
+
+	adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(id));
+
+	adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(3));
+
+	adb_request(&req, NULL, ADBREQ_SYNC, 9,
+	ADB_WRITEREG(3,2),
+	    0xe7,
+	    0x8c,
+	    0,
+	    0,
+	    0,
+	    0xff,
+	    0xff,
+	    0x94);
+
+	adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(3));
+
+	adb_request(&req, NULL, ADBREQ_SYNC, 9,
+	ADB_WRITEREG(3,2),
+	    0xa5,
+	    0x14,
+	    0,
+	    0,
+	    0x69,
+	    0xff,
+	    0xff,
+	    0x27);
+}
+
+static void
+init_microspeed(int id)
+{
+	struct adb_request req;
+
+        printk(" (Microspeed/MacPoint or compatible)");
+
+	adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(id));
+
+	/* This will initialize mice using the Microspeed, MacPoint and
+	   other compatible firmware. Bit 12 enables extended protocol.
+	   
+	   Register 1 Listen (4 Bytes)
+            0 -  3     Button is mouse (set also for double clicking!!!)
+            4 -  7     Button is locking (affects change speed also)
+            8 - 11     Button changes speed
+           12          1 = Extended mouse mode, 0 = normal mouse mode
+           13 - 15     unused 0
+           16 - 23     normal speed
+           24 - 31     changed speed
+
+       Register 1 talk holds version and product identification information.
+       Register 1 Talk (4 Bytes):
+            0 -  7     Product code
+            8 - 23     undefined, reserved
+           24 - 31     Version number
+        
+       Speed 0 is max. 1 to 255 set speed in increments of 1/256 of max.
+ */
+	adb_request(&req, NULL, ADBREQ_SYNC, 5,
+	ADB_WRITEREG(id,1),
+	    0x20,	/* alt speed = 0x20 (rather slow) */
+	    0x00,	/* norm speed = 0x00 (fastest) */
+	    0x10,	/* extended protocol, no speed change */
+	    0x07);	/* all buttons enabled as mouse buttons, no locking */
+
+
+	adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(id));
+}
+
+static void
+init_ms_a3(int id)
+{
+	struct adb_request req;
+
+	printk(" (Mouse Systems A3 Mouse, or compatible)");
+	adb_request(&req, NULL, ADBREQ_SYNC, 3,
+	ADB_WRITEREG(id, 0x2),
+	    0x00,
+	    0x07);
+ 
+ 	adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(id));
+}
+
+static int __init adbhid_init(void)
+{
+#ifndef CONFIG_MAC
+	if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) )
+	    return 0;
+#endif
+
+	led_request.complete = 1;
+
+	adbhid_probe();
+
+	notifier_chain_register(&adb_client_list, &adbhid_adb_notifier);
+
+	return 0;
+}
+
+static void __exit adbhid_exit(void)
+{
+}
+ 
+module_init(adbhid_init);
+module_exit(adbhid_exit);
diff --git a/drivers/macintosh/ans-lcd.c b/drivers/macintosh/ans-lcd.c
new file mode 100644
index 0000000..5e0811d
--- /dev/null
+++ b/drivers/macintosh/ans-lcd.c
@@ -0,0 +1,186 @@
+/*
+ * /dev/lcd driver for Apple Network Servers.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+
+#include <asm/uaccess.h>
+#include <asm/sections.h>
+#include <asm/prom.h>
+#include <asm/ans-lcd.h>
+#include <asm/io.h>
+
+#define ANSLCD_ADDR		0xf301c000
+#define ANSLCD_CTRL_IX 0x00
+#define ANSLCD_DATA_IX 0x10
+
+static unsigned long anslcd_short_delay = 80;
+static unsigned long anslcd_long_delay = 3280;
+static volatile unsigned char __iomem *anslcd_ptr;
+
+#undef DEBUG
+
+static void __pmac
+anslcd_write_byte_ctrl ( unsigned char c )
+{
+#ifdef DEBUG
+	printk(KERN_DEBUG "LCD: CTRL byte: %02x\n",c);
+#endif
+	out_8(anslcd_ptr + ANSLCD_CTRL_IX, c);
+	switch(c) {
+		case 1:
+		case 2:
+		case 3:
+			udelay(anslcd_long_delay); break;
+		default: udelay(anslcd_short_delay);
+	}
+}
+
+static void __pmac
+anslcd_write_byte_data ( unsigned char c )
+{
+	out_8(anslcd_ptr + ANSLCD_DATA_IX, c);
+	udelay(anslcd_short_delay);
+}
+
+static ssize_t __pmac
+anslcd_write( struct file * file, const char __user * buf, 
+				size_t count, loff_t *ppos )
+{
+	const char __user *p = buf;
+	int i;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "LCD: write\n");
+#endif
+
+	if (!access_ok(VERIFY_READ, buf, count))
+		return -EFAULT;
+	for ( i = *ppos; count > 0; ++i, ++p, --count ) 
+	{
+		char c;
+		__get_user(c, p);
+		anslcd_write_byte_data( c );
+	}
+	*ppos = i;
+	return p - buf;
+}
+
+static int __pmac
+anslcd_ioctl( struct inode * inode, struct file * file,
+				unsigned int cmd, unsigned long arg )
+{
+	char ch, __user *temp;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg);
+#endif
+
+	switch ( cmd )
+	{
+	case ANSLCD_CLEAR:
+		anslcd_write_byte_ctrl ( 0x38 );
+		anslcd_write_byte_ctrl ( 0x0f );
+		anslcd_write_byte_ctrl ( 0x06 );
+		anslcd_write_byte_ctrl ( 0x01 );
+		anslcd_write_byte_ctrl ( 0x02 );
+		return 0;
+	case ANSLCD_SENDCTRL:
+		temp = (char __user *) arg;
+		__get_user(ch, temp);
+		for (; ch; temp++) { /* FIXME: This is ugly, but should work, as a \0 byte is not a valid command code */
+			anslcd_write_byte_ctrl ( ch );
+			__get_user(ch, temp);
+		}
+		return 0;
+	case ANSLCD_SETSHORTDELAY:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+		anslcd_short_delay=arg;
+		return 0;
+	case ANSLCD_SETLONGDELAY:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+		anslcd_long_delay=arg;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int __pmac
+anslcd_open( struct inode * inode, struct file * file )
+{
+	return 0;
+}
+
+struct file_operations anslcd_fops = {
+	.write	= anslcd_write,
+	.ioctl	= anslcd_ioctl,
+	.open	= anslcd_open,
+};
+
+static struct miscdevice anslcd_dev = {
+	ANSLCD_MINOR,
+	"anslcd",
+	&anslcd_fops
+};
+
+const char anslcd_logo[] =	"********************"  /* Line #1 */
+				"*      LINUX!      *"  /* Line #3 */
+				"*    Welcome to    *"  /* Line #2 */
+				"********************"; /* Line #4 */
+
+static int __init
+anslcd_init(void)
+{
+	int a;
+	int retval;
+	struct device_node* node;
+
+	node = find_devices("lcd");
+	if (!node || !node->parent)
+		return -ENODEV;
+	if (strcmp(node->parent->name, "gc"))
+		return -ENODEV;
+
+	anslcd_ptr = ioremap(ANSLCD_ADDR, 0x20);
+	
+	retval = misc_register(&anslcd_dev);
+	if(retval < 0){
+		printk(KERN_INFO "LCD: misc_register failed\n");
+		iounmap(anslcd_ptr);
+		return retval;
+	}
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "LCD: init\n");
+#endif
+
+	anslcd_write_byte_ctrl ( 0x38 );
+	anslcd_write_byte_ctrl ( 0x0c );
+	anslcd_write_byte_ctrl ( 0x06 );
+	anslcd_write_byte_ctrl ( 0x01 );
+	anslcd_write_byte_ctrl ( 0x02 );
+	for(a=0;a<80;a++) {
+		anslcd_write_byte_data(anslcd_logo[a]);
+	}
+	return 0;
+}
+
+static void __exit
+anslcd_exit(void)
+{
+	misc_deregister(&anslcd_dev);
+	iounmap(anslcd_ptr);
+}
+
+module_init(anslcd_init);
+module_exit(anslcd_exit);
diff --git a/drivers/macintosh/apm_emu.c b/drivers/macintosh/apm_emu.c
new file mode 100644
index 0000000..19d3e05
--- /dev/null
+++ b/drivers/macintosh/apm_emu.c
@@ -0,0 +1,553 @@
+/* APM emulation layer for PowerMac
+ * 
+ * Copyright 2001 Benjamin Herrenschmidt (benh@kernel.crashing.org)
+ *
+ * Lots of code inherited from apm.c, see appropriate notice in
+ *  arch/i386/kernel/apm.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/poll.h>
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include <linux/fcntl.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+#include <linux/miscdevice.h>
+#include <linux/apm_bios.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/pm.h>
+#include <linux/kernel.h>
+#include <linux/smp_lock.h>
+
+#include <linux/adb.h>
+#include <linux/pmu.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/machdep.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(args...) printk(KERN_DEBUG args)
+//#define DBG(args...) xmon_printf(args)
+#else
+#define DBG(args...) do { } while (0)
+#endif
+
+/*
+ * The apm_bios device is one of the misc char devices.
+ * This is its minor number.
+ */
+#define	APM_MINOR_DEV	134
+
+/*
+ * Maximum number of events stored
+ */
+#define APM_MAX_EVENTS		20
+
+#define FAKE_APM_BIOS_VERSION	0x0101
+
+#define APM_USER_NOTIFY_TIMEOUT	(5*HZ)
+
+/*
+ * The per-file APM data
+ */
+struct apm_user {
+	int		magic;
+	struct apm_user *	next;
+	int		suser: 1;
+	int		suspend_waiting: 1;
+	int		suspends_pending;
+	int		suspends_read;
+	int		event_head;
+	int		event_tail;
+	apm_event_t	events[APM_MAX_EVENTS];
+};
+
+/*
+ * The magic number in apm_user
+ */
+#define APM_BIOS_MAGIC		0x4101
+
+/*
+ * Local variables
+ */
+static int			suspends_pending;
+
+static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
+static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
+static struct apm_user *	user_list;
+
+static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when);
+static struct pmu_sleep_notifier apm_sleep_notifier = {
+	apm_notify_sleep,
+	SLEEP_LEVEL_USERLAND,
+};
+
+static char			driver_version[] = "0.5";	/* no spaces */
+
+#ifdef DEBUG
+static char *	apm_event_name[] = {
+	"system standby",
+	"system suspend",
+	"normal resume",
+	"critical resume",
+	"low battery",
+	"power status change",
+	"update time",
+	"critical suspend",
+	"user standby",
+	"user suspend",
+	"system standby resume",
+	"capabilities change"
+};
+#define NR_APM_EVENT_NAME	\
+		(sizeof(apm_event_name) / sizeof(apm_event_name[0]))
+
+#endif
+
+static int queue_empty(struct apm_user *as)
+{
+	return as->event_head == as->event_tail;
+}
+
+static apm_event_t get_queued_event(struct apm_user *as)
+{
+	as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
+	return as->events[as->event_tail];
+}
+
+static void queue_event(apm_event_t event, struct apm_user *sender)
+{
+	struct apm_user *	as;
+
+	DBG("apm_emu: queue_event(%s)\n", apm_event_name[event-1]);
+	if (user_list == NULL)
+		return;
+	for (as = user_list; as != NULL; as = as->next) {
+		if (as == sender)
+			continue;
+		as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
+		if (as->event_head == as->event_tail) {
+			static int notified;
+
+			if (notified++ == 0)
+			    printk(KERN_ERR "apm_emu: an event queue overflowed\n");
+			as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
+		}
+		as->events[as->event_head] = event;
+		if (!as->suser)
+			continue;
+		switch (event) {
+		case APM_SYS_SUSPEND:
+		case APM_USER_SUSPEND:
+			as->suspends_pending++;
+			suspends_pending++;
+			break;
+		case APM_NORMAL_RESUME:
+			as->suspend_waiting = 0;
+			break;
+		}
+	}
+	wake_up_interruptible(&apm_waitqueue);
+}
+
+static int check_apm_user(struct apm_user *as, const char *func)
+{
+	if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
+		printk(KERN_ERR "apm_emu: %s passed bad filp\n", func);
+		return 1;
+	}
+	return 0;
+}
+
+static ssize_t do_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos)
+{
+	struct apm_user *	as;
+	size_t			i;
+	apm_event_t		event;
+	DECLARE_WAITQUEUE(wait, current);
+
+	as = fp->private_data;
+	if (check_apm_user(as, "read"))
+		return -EIO;
+	if (count < sizeof(apm_event_t))
+		return -EINVAL;
+	if (queue_empty(as)) {
+		if (fp->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		add_wait_queue(&apm_waitqueue, &wait);
+repeat:
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (queue_empty(as) && !signal_pending(current)) {
+			schedule();
+			goto repeat;
+		}
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&apm_waitqueue, &wait);
+	}
+	i = count;
+	while ((i >= sizeof(event)) && !queue_empty(as)) {
+		event = get_queued_event(as);
+		DBG("apm_emu: do_read, returning: %s\n", apm_event_name[event-1]);
+		if (copy_to_user(buf, &event, sizeof(event))) {
+			if (i < count)
+				break;
+			return -EFAULT;
+		}
+		switch (event) {
+		case APM_SYS_SUSPEND:
+		case APM_USER_SUSPEND:
+			as->suspends_read++;
+			break;
+		}
+		buf += sizeof(event);
+		i -= sizeof(event);
+	}
+	if (i < count)
+		return count - i;
+	if (signal_pending(current))
+		return -ERESTARTSYS;
+	return 0;
+}
+
+static unsigned int do_poll(struct file *fp, poll_table * wait)
+{
+	struct apm_user * as;
+
+	as = fp->private_data;
+	if (check_apm_user(as, "poll"))
+		return 0;
+	poll_wait(fp, &apm_waitqueue, wait);
+	if (!queue_empty(as))
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static int do_ioctl(struct inode * inode, struct file *filp,
+		    u_int cmd, u_long arg)
+{
+	struct apm_user *	as;
+	DECLARE_WAITQUEUE(wait, current);
+
+	as = filp->private_data;
+	if (check_apm_user(as, "ioctl"))
+		return -EIO;
+	if (!as->suser)
+		return -EPERM;
+	switch (cmd) {
+	case APM_IOC_SUSPEND:
+		/* If a suspend message was sent to userland, we
+		 * consider this as a confirmation message
+		 */
+		if (as->suspends_read > 0) {
+			as->suspends_read--;
+			as->suspends_pending--;
+			suspends_pending--;
+		} else {
+			// Route to PMU suspend ?
+			break;
+		}
+		as->suspend_waiting = 1;
+		add_wait_queue(&apm_waitqueue, &wait);
+		DBG("apm_emu: ioctl waking up sleep waiter !\n");
+		wake_up(&apm_suspend_waitqueue);
+		mb();
+		while(as->suspend_waiting && !signal_pending(current)) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+		}
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&apm_waitqueue, &wait);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int do_release(struct inode * inode, struct file * filp)
+{
+	struct apm_user *	as;
+
+	as = filp->private_data;
+	if (check_apm_user(as, "release"))
+		return 0;
+	filp->private_data = NULL;
+	lock_kernel();
+	if (as->suspends_pending > 0) {
+		suspends_pending -= as->suspends_pending;
+		if (suspends_pending <= 0)
+			wake_up(&apm_suspend_waitqueue);
+	}
+	if (user_list == as)
+		user_list = as->next;
+	else {
+		struct apm_user *	as1;
+
+		for (as1 = user_list;
+		     (as1 != NULL) && (as1->next != as);
+		     as1 = as1->next)
+			;
+		if (as1 == NULL)
+			printk(KERN_ERR "apm: filp not in user list\n");
+		else
+			as1->next = as->next;
+	}
+	unlock_kernel();
+	kfree(as);
+	return 0;
+}
+
+static int do_open(struct inode * inode, struct file * filp)
+{
+	struct apm_user *	as;
+
+	as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL);
+	if (as == NULL) {
+		printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n",
+		       sizeof(*as));
+		return -ENOMEM;
+	}
+	as->magic = APM_BIOS_MAGIC;
+	as->event_tail = as->event_head = 0;
+	as->suspends_pending = 0;
+	as->suspends_read = 0;
+	/*
+	 * XXX - this is a tiny bit broken, when we consider BSD
+         * process accounting. If the device is opened by root, we
+	 * instantly flag that we used superuser privs. Who knows,
+	 * we might close the device immediately without doing a
+	 * privileged operation -- cevans
+	 */
+	as->suser = capable(CAP_SYS_ADMIN);
+	as->next = user_list;
+	user_list = as;
+	filp->private_data = as;
+
+	DBG("apm_emu: opened by %s, suser: %d\n", current->comm, (int)as->suser);
+
+	return 0;
+}
+
+/* Wait for all clients to ack the suspend request. APM API
+ * doesn't provide a way to NAK, but this could be added
+ * here.
+ */
+static int wait_all_suspend(void)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	add_wait_queue(&apm_suspend_waitqueue, &wait);
+	DBG("apm_emu: wait_all_suspend(), suspends_pending: %d\n", suspends_pending);
+	while(suspends_pending > 0) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&apm_suspend_waitqueue, &wait);
+
+	DBG("apm_emu: wait_all_suspend() - complete !\n");
+	
+	return 1;
+}
+
+static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when)
+{
+	switch(when) {
+		case PBOOK_SLEEP_REQUEST:
+			queue_event(APM_SYS_SUSPEND, NULL);
+			if (!wait_all_suspend())
+				return PBOOK_SLEEP_REFUSE;
+			break;
+		case PBOOK_SLEEP_REJECT:
+		case PBOOK_WAKE:
+			queue_event(APM_NORMAL_RESUME, NULL);
+			break;
+	}
+	return PBOOK_SLEEP_OK;
+}
+
+#define APM_CRITICAL		10
+#define APM_LOW			30
+
+static int apm_emu_get_info(char *buf, char **start, off_t fpos, int length)
+{
+	/* Arguments, with symbols from linux/apm_bios.h.  Information is
+	   from the Get Power Status (0x0a) call unless otherwise noted.
+
+	   0) Linux driver version (this will change if format changes)
+	   1) APM BIOS Version.  Usually 1.0, 1.1 or 1.2.
+	   2) APM flags from APM Installation Check (0x00):
+	      bit 0: APM_16_BIT_SUPPORT
+	      bit 1: APM_32_BIT_SUPPORT
+	      bit 2: APM_IDLE_SLOWS_CLOCK
+	      bit 3: APM_BIOS_DISABLED
+	      bit 4: APM_BIOS_DISENGAGED
+	   3) AC line status
+	      0x00: Off-line
+	      0x01: On-line
+	      0x02: On backup power (BIOS >= 1.1 only)
+	      0xff: Unknown
+	   4) Battery status
+	      0x00: High
+	      0x01: Low
+	      0x02: Critical
+	      0x03: Charging
+	      0x04: Selected battery not present (BIOS >= 1.2 only)
+	      0xff: Unknown
+	   5) Battery flag
+	      bit 0: High
+	      bit 1: Low
+	      bit 2: Critical
+	      bit 3: Charging
+	      bit 7: No system battery
+	      0xff: Unknown
+	   6) Remaining battery life (percentage of charge):
+	      0-100: valid
+	      -1: Unknown
+	   7) Remaining battery life (time units):
+	      Number of remaining minutes or seconds
+	      -1: Unknown
+	   8) min = minutes; sec = seconds */
+
+	unsigned short  ac_line_status = 0xff;
+	unsigned short  battery_status = 0xff;
+	unsigned short  battery_flag   = 0xff;
+	int		percentage     = -1;
+	int             time_units     = -1;
+	int		real_count     = 0;
+	int		i;
+	char *		p = buf;
+	char		charging       = 0;
+	long		charge	       = -1;
+	long		amperage       = 0;
+	unsigned long	btype          = 0;
+
+	ac_line_status = ((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0);
+	for (i=0; i<pmu_battery_count; i++) {
+		if (pmu_batteries[i].flags & PMU_BATT_PRESENT) {
+			if (percentage < 0)
+				percentage = 0;
+			if (charge < 0)
+				charge = 0;
+			percentage += (pmu_batteries[i].charge * 100) /
+				pmu_batteries[i].max_charge;
+			charge += pmu_batteries[i].charge;
+			amperage += pmu_batteries[i].amperage;
+			if (btype == 0)
+				btype = (pmu_batteries[i].flags & PMU_BATT_TYPE_MASK);
+			real_count++;
+			if ((pmu_batteries[i].flags & PMU_BATT_CHARGING))
+				charging++;
+		}
+	}
+	if (real_count) {
+		if (amperage < 0) {
+			if (btype == PMU_BATT_TYPE_SMART)
+				time_units = (charge * 59) / (amperage * -1);
+			else
+				time_units = (charge * 16440) / (amperage * -60);
+		}
+		percentage /= real_count;
+		if (charging > 0) {
+			battery_status = 0x03;
+			battery_flag = 0x08;
+		} else if (percentage <= APM_CRITICAL) {
+			battery_status = 0x02;
+			battery_flag = 0x04;
+		} else if (percentage <= APM_LOW) {
+			battery_status = 0x01;
+			battery_flag = 0x02;
+		} else {
+			battery_status = 0x00;
+			battery_flag = 0x01;
+		}
+	}
+	p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
+		     driver_version,
+		     (FAKE_APM_BIOS_VERSION >> 8) & 0xff,
+		     FAKE_APM_BIOS_VERSION & 0xff,
+		     0,
+		     ac_line_status,
+		     battery_status,
+		     battery_flag,
+		     percentage,
+		     time_units,
+		     "min");
+
+	return p - buf;
+}
+
+static struct file_operations apm_bios_fops = {
+	.owner		= THIS_MODULE,
+	.read		= do_read,
+	.poll		= do_poll,
+	.ioctl		= do_ioctl,
+	.open		= do_open,
+	.release	= do_release,
+};
+
+static struct miscdevice apm_device = {
+	APM_MINOR_DEV,
+	"apm_bios",
+	&apm_bios_fops
+};
+
+static int __init apm_emu_init(void)
+{
+	struct proc_dir_entry *apm_proc;
+
+	if (sys_ctrler != SYS_CTRLER_PMU) {
+		printk(KERN_INFO "apm_emu: Requires a machine with a PMU.\n");
+		return -ENODEV;
+	}
+		
+	apm_proc = create_proc_info_entry("apm", 0, NULL, apm_emu_get_info);
+	if (apm_proc)
+		apm_proc->owner = THIS_MODULE;
+
+	misc_register(&apm_device);
+
+	pmu_register_sleep_notifier(&apm_sleep_notifier);
+
+	printk(KERN_INFO "apm_emu: APM Emulation %s initialized.\n", driver_version);
+
+	return 0;
+}
+
+static void __exit apm_emu_exit(void)
+{
+	pmu_unregister_sleep_notifier(&apm_sleep_notifier);
+	misc_deregister(&apm_device);
+	remove_proc_entry("apm", NULL);
+
+	printk(KERN_INFO "apm_emu: APM Emulation removed.\n");
+}
+
+module_init(apm_emu_init);
+module_exit(apm_emu_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt");
+MODULE_DESCRIPTION("APM emulation layer for PowerMac");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/mac_hid.c b/drivers/macintosh/mac_hid.c
new file mode 100644
index 0000000..5ad3a5a
--- /dev/null
+++ b/drivers/macintosh/mac_hid.c
@@ -0,0 +1,140 @@
+/*
+ * drivers/macintosh/mac_hid.c
+ *
+ * HID support stuff for Macintosh computers.
+ *
+ * Copyright (C) 2000 Franz Sirl.
+ *
+ * This file will soon be removed in favor of an uinput userspace tool.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/sysctl.h>
+#include <linux/input.h>
+#include <linux/module.h>
+
+
+static struct input_dev emumousebtn;
+static void emumousebtn_input_register(void);
+static int mouse_emulate_buttons = 0;
+static int mouse_button2_keycode = KEY_RIGHTCTRL;	/* right control key */
+static int mouse_button3_keycode = KEY_RIGHTALT;	/* right option key */
+static int mouse_last_keycode = 0;
+
+#if defined(CONFIG_SYSCTL)
+/* file(s) in /proc/sys/dev/mac_hid */
+ctl_table mac_hid_files[] = {
+	{
+		.ctl_name	= DEV_MAC_HID_MOUSE_BUTTON_EMULATION,
+		.procname	= "mouse_button_emulation",
+		.data		= &mouse_emulate_buttons,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
+		.ctl_name	= DEV_MAC_HID_MOUSE_BUTTON2_KEYCODE,
+		.procname	= "mouse_button2_keycode",
+		.data		= &mouse_button2_keycode,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
+		.ctl_name	= DEV_MAC_HID_MOUSE_BUTTON3_KEYCODE,
+		.procname	= "mouse_button3_keycode",
+		.data		= &mouse_button3_keycode,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{ .ctl_name = 0 }
+};
+
+/* dir in /proc/sys/dev */
+ctl_table mac_hid_dir[] = {
+	{
+		.ctl_name	= DEV_MAC_HID,
+		.procname	= "mac_hid",
+		.maxlen		= 0,
+		.mode		= 0555,
+		.child		= mac_hid_files,
+	},
+	{ .ctl_name = 0 }
+};
+
+/* /proc/sys/dev itself, in case that is not there yet */
+ctl_table mac_hid_root_dir[] = {
+	{
+		.ctl_name	= CTL_DEV,
+		.procname	= "dev",
+		.maxlen		= 0,
+		.mode		= 0555,
+		.child		= mac_hid_dir,
+	},
+	{ .ctl_name = 0 }
+};
+
+static struct ctl_table_header *mac_hid_sysctl_header;
+
+#endif /* endif CONFIG_SYSCTL */
+
+int mac_hid_mouse_emulate_buttons(int caller, unsigned int keycode, int down)
+{
+	switch (caller) {
+	case 1:
+		/* Called from keyboard.c */
+		if (mouse_emulate_buttons
+		    && (keycode == mouse_button2_keycode
+			|| keycode == mouse_button3_keycode)) {
+			if (mouse_emulate_buttons == 1) {
+			 	input_report_key(&emumousebtn,
+						 keycode == mouse_button2_keycode ? BTN_MIDDLE : BTN_RIGHT,
+						 down);
+				input_sync(&emumousebtn);
+				return 1;
+			}
+			mouse_last_keycode = down ? keycode : 0;
+		}
+		break;
+	}
+	return 0;
+}
+
+EXPORT_SYMBOL(mac_hid_mouse_emulate_buttons);
+
+static void emumousebtn_input_register(void)
+{
+	emumousebtn.name = "Macintosh mouse button emulation";
+
+	init_input_dev(&emumousebtn);
+
+	emumousebtn.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+	emumousebtn.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
+	emumousebtn.relbit[0] = BIT(REL_X) | BIT(REL_Y);
+
+	emumousebtn.id.bustype = BUS_ADB;
+	emumousebtn.id.vendor = 0x0001;
+	emumousebtn.id.product = 0x0001;
+	emumousebtn.id.version = 0x0100;
+
+	input_register_device(&emumousebtn);
+
+	printk(KERN_INFO "input: Macintosh mouse button emulation\n");
+}
+
+int __init mac_hid_init(void)
+{
+
+	emumousebtn_input_register();
+
+#if defined(CONFIG_SYSCTL)
+	mac_hid_sysctl_header = register_sysctl_table(mac_hid_root_dir, 1);
+#endif /* CONFIG_SYSCTL */
+
+	return 0;
+}
+
+device_initcall(mac_hid_init);
diff --git a/drivers/macintosh/macio-adb.c b/drivers/macintosh/macio-adb.c
new file mode 100644
index 0000000..cf6a6f2
--- /dev/null
+++ b/drivers/macintosh/macio-adb.c
@@ -0,0 +1,284 @@
+/*
+ * Driver for the ADB controller in the Mac I/O (Hydra) chip.
+ */
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <asm/prom.h>
+#include <linux/adb.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/hydra.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <linux/init.h>
+
+struct preg {
+	unsigned char r;
+	char pad[15];
+};
+
+struct adb_regs {
+	struct preg intr;
+	struct preg data[9];
+	struct preg intr_enb;
+	struct preg dcount;
+	struct preg error;
+	struct preg ctrl;
+	struct preg autopoll;
+	struct preg active_hi;
+	struct preg active_lo;
+	struct preg test;
+};
+
+/* Bits in intr and intr_enb registers */
+#define DFB	1		/* data from bus */
+#define TAG	2		/* transfer access grant */
+
+/* Bits in dcount register */
+#define HMB	0x0f		/* how many bytes */
+#define APD	0x10		/* auto-poll data */
+
+/* Bits in error register */
+#define NRE	1		/* no response error */
+#define DLE	2		/* data lost error */
+
+/* Bits in ctrl register */
+#define TAR	1		/* transfer access request */
+#define DTB	2		/* data to bus */
+#define CRE	4		/* command response expected */
+#define ADB_RST	8		/* ADB reset */
+
+/* Bits in autopoll register */
+#define APE	1		/* autopoll enable */
+
+static volatile struct adb_regs __iomem *adb;
+static struct adb_request *current_req, *last_req;
+static DEFINE_SPINLOCK(macio_lock);
+
+static int macio_probe(void);
+static int macio_init(void);
+static irqreturn_t macio_adb_interrupt(int irq, void *arg, struct pt_regs *regs);
+static int macio_send_request(struct adb_request *req, int sync);
+static int macio_adb_autopoll(int devs);
+static void macio_adb_poll(void);
+static int macio_adb_reset_bus(void);
+
+struct adb_driver macio_adb_driver = {
+	"MACIO",
+	macio_probe,
+	macio_init,
+	macio_send_request,
+	/*macio_write,*/
+	macio_adb_autopoll,
+	macio_adb_poll,
+	macio_adb_reset_bus
+};
+
+int macio_probe(void)
+{
+	return find_compatible_devices("adb", "chrp,adb0")? 0: -ENODEV;
+}
+
+int macio_init(void)
+{
+	struct device_node *adbs;
+
+	adbs = find_compatible_devices("adb", "chrp,adb0");
+	if (adbs == 0)
+		return -ENXIO;
+
+#if 0
+	{ int i;
+
+	printk("macio_adb_init: node = %p, addrs =", adbs->node);
+	for (i = 0; i < adbs->n_addrs; ++i)
+		printk(" %x(%x)", adbs->addrs[i].address, adbs->addrs[i].size);
+	printk(", intrs =");
+	for (i = 0; i < adbs->n_intrs; ++i)
+		printk(" %x", adbs->intrs[i].line);
+	printk("\n"); }
+#endif
+	
+	adb = ioremap(adbs->addrs->address, sizeof(struct adb_regs));
+
+	out_8(&adb->ctrl.r, 0);
+	out_8(&adb->intr.r, 0);
+	out_8(&adb->error.r, 0);
+	out_8(&adb->active_hi.r, 0xff); /* for now, set all devices active */
+	out_8(&adb->active_lo.r, 0xff);
+	out_8(&adb->autopoll.r, APE);
+
+	if (request_irq(adbs->intrs[0].line, macio_adb_interrupt,
+			0, "ADB", (void *)0)) {
+		printk(KERN_ERR "ADB: can't get irq %d\n",
+		       adbs->intrs[0].line);
+		return -EAGAIN;
+	}
+	out_8(&adb->intr_enb.r, DFB | TAG);
+
+	printk("adb: mac-io driver 1.0 for unified ADB\n");
+
+	return 0;
+}
+
+static int macio_adb_autopoll(int devs)
+{
+	unsigned long flags;
+	
+	spin_lock_irqsave(&macio_lock, flags);
+	out_8(&adb->active_hi.r, devs >> 8);
+	out_8(&adb->active_lo.r, devs);
+	out_8(&adb->autopoll.r, devs? APE: 0);
+	spin_unlock_irqrestore(&macio_lock, flags);
+	return 0;
+}
+
+static int macio_adb_reset_bus(void)
+{
+	unsigned long flags;
+	int timeout = 1000000;
+
+	/* Hrm... we may want to not lock interrupts for so
+	 * long ... oh well, who uses that chip anyway ? :)
+	 * That function will be seldomly used during boot
+	 * on rare machines, so...
+	 */
+	spin_lock_irqsave(&macio_lock, flags);
+	out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | ADB_RST);
+	while ((in_8(&adb->ctrl.r) & ADB_RST) != 0) {
+		if (--timeout == 0) {
+			out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) & ~ADB_RST);
+			return -1;
+		}
+	}
+	spin_unlock_irqrestore(&macio_lock, flags);
+	return 0;
+}
+
+/* Send an ADB command */
+static int macio_send_request(struct adb_request *req, int sync)
+{
+	unsigned long flags;
+	int i;
+	
+	if (req->data[0] != ADB_PACKET)
+		return -EINVAL;
+	
+	for (i = 0; i < req->nbytes - 1; ++i)
+		req->data[i] = req->data[i+1];
+	--req->nbytes;
+	
+	req->next = NULL;
+	req->sent = 0;
+	req->complete = 0;
+	req->reply_len = 0;
+
+	spin_lock_irqsave(&macio_lock, flags);
+	if (current_req != 0) {
+		last_req->next = req;
+		last_req = req;
+	} else {
+		current_req = last_req = req;
+		out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR);
+	}
+	spin_unlock_irqrestore(&macio_lock, flags);
+	
+	if (sync) {
+		while (!req->complete)
+			macio_adb_poll();
+	}
+
+	return 0;
+}
+
+static irqreturn_t macio_adb_interrupt(int irq, void *arg,
+				       struct pt_regs *regs)
+{
+	int i, n, err;
+	struct adb_request *req = NULL;
+	unsigned char ibuf[16];
+	int ibuf_len = 0;
+	int complete = 0;
+	int autopoll = 0;
+	int handled = 0;
+
+	spin_lock(&macio_lock);
+	if (in_8(&adb->intr.r) & TAG) {
+		handled = 1;
+		if ((req = current_req) != 0) {
+			/* put the current request in */
+			for (i = 0; i < req->nbytes; ++i)
+				out_8(&adb->data[i].r, req->data[i]);
+			out_8(&adb->dcount.r, req->nbytes & HMB);
+			req->sent = 1;
+			if (req->reply_expected) {
+				out_8(&adb->ctrl.r, DTB + CRE);
+			} else {
+				out_8(&adb->ctrl.r, DTB);
+				current_req = req->next;
+				complete = 1;
+				if (current_req)
+					out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR);
+			}
+		}
+		out_8(&adb->intr.r, 0);
+	}
+
+	if (in_8(&adb->intr.r) & DFB) {
+		handled = 1;
+		err = in_8(&adb->error.r);
+		if (current_req && current_req->sent) {
+			/* this is the response to a command */
+			req = current_req;
+			if (err == 0) {
+				req->reply_len = in_8(&adb->dcount.r) & HMB;
+				for (i = 0; i < req->reply_len; ++i)
+					req->reply[i] = in_8(&adb->data[i].r);
+			}
+			current_req = req->next;
+			complete = 1;
+			if (current_req)
+				out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR);
+		} else if (err == 0) {
+			/* autopoll data */
+			n = in_8(&adb->dcount.r) & HMB;
+			for (i = 0; i < n; ++i)
+				ibuf[i] = in_8(&adb->data[i].r);
+			ibuf_len = n;
+			autopoll = (in_8(&adb->dcount.r) & APD) != 0;
+		}
+		out_8(&adb->error.r, 0);
+		out_8(&adb->intr.r, 0);
+	}
+	spin_unlock(&macio_lock);
+	if (complete && req) {
+	    void (*done)(struct adb_request *) = req->done;
+	    mb();
+	    req->complete = 1;
+	    /* Here, we assume that if the request has a done member, the
+    	     * struct request will survive to setting req->complete to 1
+	     */
+	    if (done)
+		(*done)(req);
+	}
+	if (ibuf_len)
+		adb_input(ibuf, ibuf_len, regs, autopoll);
+
+	return IRQ_RETVAL(handled);
+}
+
+static void macio_adb_poll(void)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (in_8(&adb->intr.r) != 0)
+		macio_adb_interrupt(0, NULL, NULL);
+	local_irq_restore(flags);
+}
diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c
new file mode 100644
index 0000000..3a609ec
--- /dev/null
+++ b/drivers/macintosh/macio_asic.c
@@ -0,0 +1,636 @@
+/*
+ * Bus & driver management routines for devices within
+ * a MacIO ASIC. Interface to new driver model mostly
+ * stolen from the PCI version.
+ * 
+ * TODO:
+ * 
+ *  - Don't probe below media bay by default, but instead provide
+ *    some hooks for media bay to dynamically add/remove it's own
+ *    sub-devices.
+ */
+ 
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <asm/machdep.h>
+#include <asm/macio.h>
+#include <asm/pmac_feature.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+
+#undef DEBUG
+
+#define MAX_NODE_NAME_SIZE (BUS_ID_SIZE - 12)
+
+static struct macio_chip      *macio_on_hold;
+
+static int macio_bus_match(struct device *dev, struct device_driver *drv) 
+{
+	struct macio_dev * macio_dev = to_macio_device(dev);
+	struct macio_driver * macio_drv = to_macio_driver(drv);
+	const struct of_match * matches = macio_drv->match_table;
+
+	if (!matches) 
+		return 0;
+
+	return of_match_device(matches, &macio_dev->ofdev) != NULL;
+}
+
+struct macio_dev *macio_dev_get(struct macio_dev *dev)
+{
+	struct device *tmp;
+
+	if (!dev)
+		return NULL;
+	tmp = get_device(&dev->ofdev.dev);
+	if (tmp)
+		return to_macio_device(tmp);
+	else
+		return NULL;
+}
+
+void macio_dev_put(struct macio_dev *dev)
+{
+	if (dev)
+		put_device(&dev->ofdev.dev);
+}
+
+
+static int macio_device_probe(struct device *dev)
+{
+	int error = -ENODEV;
+	struct macio_driver *drv;
+	struct macio_dev *macio_dev;
+	const struct of_match *match;
+
+	drv = to_macio_driver(dev->driver);
+	macio_dev = to_macio_device(dev);
+
+	if (!drv->probe)
+		return error;
+
+	macio_dev_get(macio_dev);
+
+	match = of_match_device(drv->match_table, &macio_dev->ofdev);
+	if (match)
+		error = drv->probe(macio_dev, match);
+	if (error)
+		macio_dev_put(macio_dev);
+
+	return error;
+}
+
+static int macio_device_remove(struct device *dev)
+{
+	struct macio_dev * macio_dev = to_macio_device(dev);
+	struct macio_driver * drv = to_macio_driver(dev->driver);
+
+	if (dev->driver && drv->remove)
+		drv->remove(macio_dev);
+	macio_dev_put(macio_dev);
+
+	return 0;
+}
+
+static void macio_device_shutdown(struct device *dev)
+{
+	struct macio_dev * macio_dev = to_macio_device(dev);
+	struct macio_driver * drv = to_macio_driver(dev->driver);
+
+	if (dev->driver && drv->shutdown)
+		drv->shutdown(macio_dev);
+}
+
+static int macio_device_suspend(struct device *dev, u32 state)
+{
+	struct macio_dev * macio_dev = to_macio_device(dev);
+	struct macio_driver * drv = to_macio_driver(dev->driver);
+
+	if (dev->driver && drv->suspend)
+		return drv->suspend(macio_dev, state);
+	return 0;
+}
+
+static int macio_device_resume(struct device * dev)
+{
+	struct macio_dev * macio_dev = to_macio_device(dev);
+	struct macio_driver * drv = to_macio_driver(dev->driver);
+
+	if (dev->driver && drv->resume)
+		return drv->resume(macio_dev);
+	return 0;
+}
+
+struct bus_type macio_bus_type = {
+       .name	= "macio",
+       .match	= macio_bus_match,
+       .suspend	= macio_device_suspend,
+       .resume	= macio_device_resume,
+};
+
+static int __init macio_bus_driver_init(void)
+{
+	return bus_register(&macio_bus_type);
+}
+
+postcore_initcall(macio_bus_driver_init);
+
+
+/**
+ * macio_release_dev - free a macio device structure when all users of it are finished.
+ * @dev: device that's been disconnected
+ *
+ * Will be called only by the device core when all users of this macio device are
+ * done. This currently means never as we don't hot remove any macio device yet,
+ * though that will happen with mediabay based devices in a later implementation.
+ */
+static void macio_release_dev(struct device *dev)
+{
+	struct macio_dev *mdev;
+
+        mdev = to_macio_device(dev);
+	kfree(mdev);
+}
+
+/**
+ * macio_resource_quirks - tweak or skip some resources for a device
+ * @np: pointer to the device node
+ * @res: resulting resource
+ * @index: index of resource in node
+ *
+ * If this routine returns non-null, then the resource is completely
+ * skipped.
+ */
+static int macio_resource_quirks(struct device_node *np, struct resource *res, int index)
+{
+	if (res->flags & IORESOURCE_MEM) {
+		/* Grand Central has too large resource 0 on some machines */
+		if (index == 0 && !strcmp(np->name, "gc")) {
+			np->addrs[0].size = 0x20000;
+			res->end = res->start + 0x1ffff;
+		}
+		/* Airport has bogus resource 2 */
+		if (index >= 2 && !strcmp(np->name, "radio"))
+			return 1;
+		/* DBDMAs may have bogus sizes */
+		if ((res->start & 0x0001f000) == 0x00008000) {
+			np->addrs[index].size = 0x100;
+			res->end = res->start + 0xff;
+		}
+		/* ESCC parent eats child resources. We could have added a level of hierarchy,
+		 * but I don't really feel the need for it */
+		if (!strcmp(np->name, "escc"))
+			return 1;
+		/* ESCC has bogus resources >= 3 */
+		if (index >= 3 && !(strcmp(np->name, "ch-a") && strcmp(np->name, "ch-b")))
+			return 1;
+		/* Media bay has too many resources, keep only first one */
+		if (index > 0 && !strcmp(np->name, "media-bay"))
+			return 1;
+		/* Some older IDE resources have bogus sizes */
+		if (!(strcmp(np->name, "IDE") && strcmp(np->name, "ATA") &&
+		      strcmp(np->type, "ide") && strcmp(np->type, "ata"))) {
+			if (index == 0 && np->addrs[0].size > 0x1000) {
+				np->addrs[0].size = 0x1000;
+				res->end = res->start + 0xfff;
+			}
+			if (index == 1 && np->addrs[1].size > 0x100) {
+				np->addrs[1].size = 0x100;
+				res->end = res->start + 0xff;
+			}
+		}
+	}
+	return 0;
+}
+
+
+/**
+ * macio_add_one_device - Add one device from OF node to the device tree
+ * @chip: pointer to the macio_chip holding the device
+ * @np: pointer to the device node in the OF tree
+ * @in_bay: set to 1 if device is part of a media-bay
+ *
+ * When media-bay is changed to hotswap drivers, this function will
+ * be exposed to the bay driver some way...
+ */
+static struct macio_dev * macio_add_one_device(struct macio_chip *chip, struct device *parent,
+					       struct device_node *np, struct macio_dev *in_bay,
+					       struct resource *parent_res)
+{
+	struct macio_dev *dev;
+	int i, j;
+	u32 *reg;
+	
+	if (np == NULL)
+		return NULL;
+
+	dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return NULL;
+	memset(dev, 0, sizeof(*dev));
+
+	dev->bus = &chip->lbus;
+	dev->media_bay = in_bay;
+	dev->ofdev.node = np;
+	dev->ofdev.dma_mask = 0xffffffffUL;
+	dev->ofdev.dev.dma_mask = &dev->ofdev.dma_mask;
+	dev->ofdev.dev.parent = parent;
+	dev->ofdev.dev.bus = &macio_bus_type;
+	dev->ofdev.dev.release = macio_release_dev;
+
+#ifdef DEBUG
+	printk("preparing mdev @%p, ofdev @%p, dev @%p, kobj @%p\n",
+	       dev, &dev->ofdev, &dev->ofdev.dev, &dev->ofdev.dev.kobj);
+#endif
+
+	/* MacIO itself has a different reg, we use it's PCI base */
+	if (np == chip->of_node) {
+		sprintf(dev->ofdev.dev.bus_id, "%1d.%08lx:%.*s", chip->lbus.index,
+#ifdef CONFIG_PCI
+			pci_resource_start(chip->lbus.pdev, 0),
+#else
+			0, /* NuBus may want to do something better here */
+#endif
+			MAX_NODE_NAME_SIZE, np->name);
+	} else {
+		reg = (u32 *)get_property(np, "reg", NULL);
+		sprintf(dev->ofdev.dev.bus_id, "%1d.%08x:%.*s", chip->lbus.index,
+			reg ? *reg : 0, MAX_NODE_NAME_SIZE, np->name);
+	}
+
+	/* For now, we use pre-parsed entries in the device-tree for
+	 * interrupt routing and addresses, but we should change that
+	 * to dynamically parsed entries and so get rid of most of the
+	 * clutter in struct device_node
+	 */
+	for (i = j = 0; i < np->n_intrs; i++) {
+		struct resource *res = &dev->interrupt[j];
+
+		if (j >= MACIO_DEV_COUNT_IRQS)
+			break;
+		res->start = np->intrs[i].line;
+		res->flags = IORESOURCE_IO;
+		if (np->intrs[j].sense)
+			res->flags |= IORESOURCE_IRQ_LOWLEVEL;
+		else
+			res->flags |= IORESOURCE_IRQ_HIGHEDGE;
+		res->name = dev->ofdev.dev.bus_id;
+		if (macio_resource_quirks(np, res, i))
+			memset(res, 0, sizeof(struct resource));
+		else
+			j++;
+	}
+	dev->n_interrupts = j;
+	for (i = j = 0; i < np->n_addrs; i++) {
+		struct resource *res = &dev->resource[j];
+		
+		if (j >= MACIO_DEV_COUNT_RESOURCES)
+			break;
+		res->start = np->addrs[i].address;
+		res->end = np->addrs[i].address + np->addrs[i].size - 1;
+		res->flags = IORESOURCE_MEM;
+		res->name = dev->ofdev.dev.bus_id;
+		if (macio_resource_quirks(np, res, i))
+			memset(res, 0, sizeof(struct resource));
+		else {
+			j++;
+			/* Currently, we consider failure as harmless, this may
+			 * change in the future, once I've found all the device
+			 * tree bugs in older machines & worked around them
+			 */
+			if (insert_resource(parent_res, res))
+       				printk(KERN_WARNING "Can't request resource %d for MacIO"
+				       " device %s\n", i, dev->ofdev.dev.bus_id);
+		}
+	}
+	dev->n_resources = j;
+
+	if (of_device_register(&dev->ofdev) != 0) {
+		printk(KERN_DEBUG"macio: device registration error for %s!\n",
+		       dev->ofdev.dev.bus_id);
+		kfree(dev);
+		return NULL;
+	}
+
+	return dev;
+}
+
+static int macio_skip_device(struct device_node *np)
+{
+	if (strncmp(np->name, "battery", 7) == 0)
+		return 1;
+	if (strncmp(np->name, "escc-legacy", 11) == 0)
+		return 1;
+	return 0;
+}
+
+/**
+ * macio_pci_add_devices - Adds sub-devices of mac-io to the device tree
+ * @chip: pointer to the macio_chip holding the devices
+ * 
+ * This function will do the job of extracting devices from the
+ * Open Firmware device tree, build macio_dev structures and add
+ * them to the Linux device tree.
+ * 
+ * For now, childs of media-bay are added now as well. This will
+ * change rsn though.
+ */
+static void macio_pci_add_devices(struct macio_chip *chip)
+{
+	struct device_node *np, *pnode;
+	struct macio_dev *rdev, *mdev, *mbdev = NULL, *sdev = NULL;
+	struct device *parent = NULL;
+	struct resource *root_res = &iomem_resource;
+	
+	/* Add a node for the macio bus itself */
+#ifdef CONFIG_PCI
+	if (chip->lbus.pdev) {
+		parent = &chip->lbus.pdev->dev;
+		root_res = &chip->lbus.pdev->resource[0];
+	}
+#endif
+	pnode = of_node_get(chip->of_node);
+	if (pnode == NULL)
+		return;
+
+	/* Add macio itself to hierarchy */
+	rdev = macio_add_one_device(chip, parent, pnode, NULL, root_res);
+	if (rdev == NULL)
+		return;
+	root_res = &rdev->resource[0];
+
+	/* First scan 1st level */
+	for (np = NULL; (np = of_get_next_child(pnode, np)) != NULL;) {
+		if (!macio_skip_device(np)) {
+			of_node_get(np);
+			mdev = macio_add_one_device(chip, &rdev->ofdev.dev, np, NULL, root_res);
+			if (mdev == NULL)
+				of_node_put(np);
+			else if (strncmp(np->name, "media-bay", 9) == 0)
+				mbdev = mdev;
+			else if (strncmp(np->name, "escc", 4) == 0)
+				sdev = mdev;
+		}
+	}
+
+	/* Add media bay devices if any */
+	if (mbdev)
+		for (np = NULL; (np = of_get_next_child(mbdev->ofdev.node, np)) != NULL;)
+			if (!macio_skip_device(np)) {
+				of_node_get(np);
+				if (macio_add_one_device(chip, &mbdev->ofdev.dev, np, mbdev,
+							 root_res) == NULL)
+					of_node_put(np);
+			}
+	/* Add serial ports if any */
+	if (sdev) {
+		for (np = NULL; (np = of_get_next_child(sdev->ofdev.node, np)) != NULL;)
+			if (!macio_skip_device(np)) {
+				of_node_get(np);
+				if (macio_add_one_device(chip, &sdev->ofdev.dev, np, NULL,
+							 root_res) == NULL)
+					of_node_put(np);
+			}
+	}
+}
+
+
+/**
+ * macio_register_driver - Registers a new MacIO device driver
+ * @drv: pointer to the driver definition structure
+ */
+int macio_register_driver(struct macio_driver *drv)
+{
+	int count = 0;
+
+	/* initialize common driver fields */
+	drv->driver.name = drv->name;
+	drv->driver.bus = &macio_bus_type;
+	drv->driver.probe = macio_device_probe;
+	drv->driver.remove = macio_device_remove;
+	drv->driver.shutdown = macio_device_shutdown;
+
+	/* register with core */
+	count = driver_register(&drv->driver);
+	return count ? count : 1;
+}
+
+/**
+ * macio_unregister_driver - Unregisters a new MacIO device driver
+ * @drv: pointer to the driver definition structure
+ */
+void macio_unregister_driver(struct macio_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+
+/**
+ *	macio_request_resource - Request an MMIO resource
+ * 	@dev: pointer to the device holding the resource
+ *	@resource_no: resource number to request
+ *	@name: resource name
+ *
+ *	Mark  memory region number @resource_no associated with MacIO
+ *	device @dev as being reserved by owner @name.  Do not access
+ *	any address inside the memory regions unless this call returns
+ *	successfully.
+ *
+ *	Returns 0 on success, or %EBUSY on error.  A warning
+ *	message is also printed on failure.
+ */
+int macio_request_resource(struct macio_dev *dev, int resource_no, const char *name)
+{
+	if (macio_resource_len(dev, resource_no) == 0)
+		return 0;
+		
+	if (!request_mem_region(macio_resource_start(dev, resource_no),
+				macio_resource_len(dev, resource_no),
+				name))
+		goto err_out;
+	
+	return 0;
+
+err_out:
+	printk (KERN_WARNING "MacIO: Unable to reserve resource #%d:%lx@%lx"
+		" for device %s\n",
+		resource_no,
+		macio_resource_len(dev, resource_no),
+		macio_resource_start(dev, resource_no),
+		dev->ofdev.dev.bus_id);
+	return -EBUSY;
+}
+
+/**
+ * macio_release_resource - Release an MMIO resource
+ * @dev: pointer to the device holding the resource
+ * @resource_no: resource number to release
+ */
+void macio_release_resource(struct macio_dev *dev, int resource_no)
+{
+	if (macio_resource_len(dev, resource_no) == 0)
+		return;
+	release_mem_region(macio_resource_start(dev, resource_no),
+			   macio_resource_len(dev, resource_no));
+}
+
+/**
+ *	macio_request_resources - Reserve all memory resources
+ *	@dev: MacIO device whose resources are to be reserved
+ *	@name: Name to be associated with resource.
+ *
+ *	Mark all memory regions associated with MacIO device @dev as
+ *	being reserved by owner @name.  Do not access any address inside
+ *	the memory regions unless this call returns successfully.
+ *
+ *	Returns 0 on success, or %EBUSY on error.  A warning
+ *	message is also printed on failure.
+ */
+int macio_request_resources(struct macio_dev *dev, const char *name)
+{
+	int i;
+	
+	for (i = 0; i < dev->n_resources; i++)
+		if (macio_request_resource(dev, i, name))
+			goto err_out;
+	return 0;
+
+err_out:
+	while(--i >= 0)
+		macio_release_resource(dev, i);
+		
+	return -EBUSY;
+}
+
+/**
+ *	macio_release_resources - Release reserved memory resources
+ *	@dev: MacIO device whose resources were previously reserved
+ */
+
+void macio_release_resources(struct macio_dev *dev)
+{
+	int i;
+	
+	for (i = 0; i < dev->n_resources; i++)
+		macio_release_resource(dev, i);
+}
+
+
+#ifdef CONFIG_PCI
+
+static int __devinit macio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	struct device_node* np;
+	struct macio_chip* chip;
+	
+	if (ent->vendor != PCI_VENDOR_ID_APPLE)
+		return -ENODEV;
+
+	/* Note regarding refcounting: We assume pci_device_to_OF_node() is ported
+	 * to new OF APIs and returns a node with refcount incremented. This isn't
+	 * the case today, but on the other hand ppc32 doesn't do refcounting. This
+	 * will have to be fixed when going to ppc64. --BenH.
+	 */
+	np = pci_device_to_OF_node(pdev);
+	if (np == NULL)
+		return -ENODEV;
+
+	/* This assumption is wrong, fix that here for now until I fix the arch */
+	of_node_get(np);
+
+	/* We also assume that pmac_feature will have done a get() on nodes stored
+	 * in the macio chips array
+	 */
+	chip = macio_find(np, macio_unknown);
+       	of_node_put(np);
+	if (chip == NULL)
+		return -ENODEV;
+
+	/* XXX Need locking ??? */
+	if (chip->lbus.pdev == NULL) {
+		chip->lbus.pdev = pdev;
+		chip->lbus.chip = chip;
+		pci_set_drvdata(pdev, &chip->lbus);
+		pci_set_master(pdev);
+	}
+
+	printk(KERN_INFO "MacIO PCI driver attached to %s chipset\n",
+		chip->name);
+
+	/*
+	 * HACK ALERT: The WallStreet PowerBook and some OHare based machines
+	 * have 2 macio ASICs. I must probe the "main" one first or IDE ordering
+	 * will be incorrect. So I put on "hold" the second one since it seem to
+	 * appear first on PCI
+	 */
+	if (chip->type == macio_gatwick || chip->type == macio_ohareII)
+		if (macio_chips[0].lbus.pdev == NULL) {
+			macio_on_hold = chip;
+			return 0;
+		}
+
+	macio_pci_add_devices(chip);
+	if (macio_on_hold && macio_chips[0].lbus.pdev != NULL) {
+		macio_pci_add_devices(macio_on_hold);
+		macio_on_hold = NULL;
+	}
+
+	return 0;
+}
+
+static void __devexit macio_pci_remove(struct pci_dev* pdev)
+{
+	panic("removing of macio-asic not supported !\n");
+}
+
+/*
+ * MacIO is matched against any Apple ID, it's probe() function
+ * will then decide wether it applies or not
+ */
+static const struct pci_device_id __devinitdata pci_ids [] = { {
+	.vendor		= PCI_VENDOR_ID_APPLE,
+	.device		= PCI_ANY_ID,
+	.subvendor	= PCI_ANY_ID,
+	.subdevice	= PCI_ANY_ID,
+
+	}, { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE (pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver macio_pci_driver = {
+	.name		= (char *) "macio",
+	.id_table	= pci_ids,
+
+	.probe		= macio_pci_probe,
+	.remove		= macio_pci_remove,
+};
+
+#endif /* CONFIG_PCI */
+
+static int __init macio_module_init (void) 
+{
+#ifdef CONFIG_PCI
+	int rc;
+
+	rc = pci_register_driver(&macio_pci_driver);
+	if (rc)
+		return rc;
+#endif /* CONFIG_PCI */
+	return 0;
+}
+
+module_init(macio_module_init);
+
+EXPORT_SYMBOL(macio_register_driver);
+EXPORT_SYMBOL(macio_unregister_driver);
+EXPORT_SYMBOL(macio_dev_get);
+EXPORT_SYMBOL(macio_dev_put);
+EXPORT_SYMBOL(macio_request_resource);
+EXPORT_SYMBOL(macio_release_resource);
+EXPORT_SYMBOL(macio_request_resources);
+EXPORT_SYMBOL(macio_release_resources);
diff --git a/drivers/macintosh/macserial.c b/drivers/macintosh/macserial.c
new file mode 100644
index 0000000..0be3ac6
--- /dev/null
+++ b/drivers/macintosh/macserial.c
@@ -0,0 +1,3036 @@
+/*
+ * macserial.c: Serial port driver for Power Macintoshes.
+ *
+ * Derived from drivers/sbus/char/sunserial.c by Paul Mackerras.
+ *
+ * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * Receive DMA code by Takashi Oe <toe@unlserve.unl.edu>.
+ *
+ * $Id: macserial.c,v 1.24.2.4 1999/10/19 04:36:42 paulus Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#ifdef CONFIG_SERIAL_CONSOLE
+#include <linux/console.h>
+#endif
+#include <linux/slab.h>
+#include <linux/bitops.h>
+
+#include <asm/sections.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+#include <asm/prom.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#ifdef CONFIG_KGDB
+#include <asm/kgdb.h>
+#endif
+#include <asm/dbdma.h>
+
+#include "macserial.h"
+
+#ifdef CONFIG_PMAC_PBOOK
+static int serial_notify_sleep(struct pmu_sleep_notifier *self, int when);
+static struct pmu_sleep_notifier serial_sleep_notifier = {
+	serial_notify_sleep,
+	SLEEP_LEVEL_MISC,
+};
+#endif
+
+#define SUPPORT_SERIAL_DMA
+#define MACSERIAL_VERSION	"2.0"
+
+/*
+ * It would be nice to dynamically allocate everything that
+ * depends on NUM_SERIAL, so we could support any number of
+ * Z8530s, but for now...
+ */
+#define NUM_SERIAL	2		/* Max number of ZS chips supported */
+#define NUM_CHANNELS	(NUM_SERIAL * 2)	/* 2 channels per chip */
+
+/* On PowerMacs, the hardware takes care of the SCC recovery time,
+   but we need the eieio to make sure that the accesses occur
+   in the order we want. */
+#define RECOVERY_DELAY	eieio()
+
+static struct tty_driver *serial_driver;
+
+struct mac_zschannel zs_channels[NUM_CHANNELS];
+
+struct mac_serial zs_soft[NUM_CHANNELS];
+int zs_channels_found;
+struct mac_serial *zs_chain;	/* list of all channels */
+
+struct tty_struct zs_ttys[NUM_CHANNELS];
+
+static int is_powerbook;
+
+#ifdef CONFIG_SERIAL_CONSOLE
+static struct console sercons;
+#endif
+
+#ifdef CONFIG_KGDB
+struct mac_zschannel *zs_kgdbchan;
+static unsigned char scc_inittab[] = {
+	9,  0x80,	/* reset A side (CHRA) */
+	13, 0,		/* set baud rate divisor */
+	12, 1,
+	14, 1,		/* baud rate gen enable, src=rtxc (BRENABL) */
+	11, 0x50,	/* clocks = br gen (RCBR | TCBR) */
+	5,  0x6a,	/* tx 8 bits, assert RTS (Tx8 | TxENAB | RTS) */
+	4,  0x44,	/* x16 clock, 1 stop (SB1 | X16CLK)*/
+	3,  0xc1,	/* rx enable, 8 bits (RxENABLE | Rx8)*/
+};
+#endif
+#define ZS_CLOCK         3686400 	/* Z8530 RTxC input clock rate */
+
+/* serial subtype definitions */
+#define SERIAL_TYPE_NORMAL	1
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/*
+ * Debugging.
+ */
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_POWER
+#undef SERIAL_DEBUG_THROTTLE
+#undef SERIAL_DEBUG_STOP
+#undef SERIAL_DEBUG_BAUDS
+
+#define RS_STROBE_TIME 10
+#define RS_ISR_PASS_LIMIT 256
+
+#define _INLINE_ inline
+
+#ifdef SERIAL_DEBUG_OPEN
+#define OPNDBG(fmt, arg...)	printk(KERN_DEBUG fmt , ## arg)
+#else
+#define OPNDBG(fmt, arg...)	do { } while (0)
+#endif
+#ifdef SERIAL_DEBUG_POWER
+#define PWRDBG(fmt, arg...)	printk(KERN_DEBUG fmt , ## arg)
+#else
+#define PWRDBG(fmt, arg...)	do { } while (0)
+#endif
+#ifdef SERIAL_DEBUG_BAUDS
+#define BAUDBG(fmt, arg...)	printk(fmt , ## arg)
+#else
+#define BAUDBG(fmt, arg...)	do { } while (0)
+#endif
+
+static void probe_sccs(void);
+static void change_speed(struct mac_serial *info, struct termios *old);
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
+static int set_scc_power(struct mac_serial * info, int state);
+static int setup_scc(struct mac_serial * info);
+static void dbdma_reset(volatile struct dbdma_regs *dma);
+static void dbdma_flush(volatile struct dbdma_regs *dma);
+static irqreturn_t rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs);
+static void dma_init(struct mac_serial * info);
+static void rxdma_start(struct mac_serial * info, int curr);
+static void rxdma_to_tty(struct mac_serial * info);
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write.  We need to
+ * lock it in case the copy_from_user blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf;
+static DECLARE_MUTEX(tmp_buf_sem);
+
+
+static inline int __pmac
+serial_paranoia_check(struct mac_serial *info,
+		      char *name, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+	static const char badmagic[] = KERN_WARNING
+		"Warning: bad magic number for serial struct %s in %s\n";
+	static const char badinfo[] = KERN_WARNING
+		"Warning: null mac_serial for %s in %s\n";
+
+	if (!info) {
+		printk(badinfo, name, routine);
+		return 1;
+	}
+	if (info->magic != SERIAL_MAGIC) {
+		printk(badmagic, name, routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+/* 
+ * Reading and writing Z8530 registers.
+ */
+static inline unsigned char __pmac read_zsreg(struct mac_zschannel *channel,
+					      unsigned char reg)
+{
+	unsigned char retval;
+	unsigned long flags;
+
+	/*
+	 * We have to make this atomic.
+	 */
+	spin_lock_irqsave(&channel->lock, flags);
+	if (reg != 0) {
+		*channel->control = reg;
+		RECOVERY_DELAY;
+	}
+	retval = *channel->control;
+	RECOVERY_DELAY;
+	spin_unlock_irqrestore(&channel->lock, flags);
+	return retval;
+}
+
+static inline void __pmac write_zsreg(struct mac_zschannel *channel,
+				      unsigned char reg, unsigned char value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&channel->lock, flags);
+	if (reg != 0) {
+		*channel->control = reg;
+		RECOVERY_DELAY;
+	}
+	*channel->control = value;
+	RECOVERY_DELAY;
+	spin_unlock_irqrestore(&channel->lock, flags);
+	return;
+}
+
+static inline unsigned char __pmac read_zsdata(struct mac_zschannel *channel)
+{
+	unsigned char retval;
+
+	retval = *channel->data;
+	RECOVERY_DELAY;
+	return retval;
+}
+
+static inline void write_zsdata(struct mac_zschannel *channel,
+				unsigned char value)
+{
+	*channel->data = value;
+	RECOVERY_DELAY;
+	return;
+}
+
+static inline void load_zsregs(struct mac_zschannel *channel,
+			       unsigned char *regs)
+{
+	ZS_CLEARERR(channel);
+	ZS_CLEARFIFO(channel);
+	/* Load 'em up */
+	write_zsreg(channel, R4, regs[R4]);
+	write_zsreg(channel, R10, regs[R10]);
+	write_zsreg(channel, R3, regs[R3] & ~RxENABLE);
+	write_zsreg(channel, R5, regs[R5] & ~TxENAB);
+	write_zsreg(channel, R1, regs[R1]);
+	write_zsreg(channel, R9, regs[R9]);
+	write_zsreg(channel, R11, regs[R11]);
+	write_zsreg(channel, R12, regs[R12]);
+	write_zsreg(channel, R13, regs[R13]);
+	write_zsreg(channel, R14, regs[R14]);
+	write_zsreg(channel, R15, regs[R15]);
+	write_zsreg(channel, R3, regs[R3]);
+	write_zsreg(channel, R5, regs[R5]);
+	return;
+}
+
+/* Sets or clears DTR/RTS on the requested line */
+static inline void zs_rtsdtr(struct mac_serial *ss, int set)
+{
+	if (set)
+		ss->curregs[5] |= (RTS | DTR);
+	else
+		ss->curregs[5] &= ~(RTS | DTR);
+	write_zsreg(ss->zs_channel, 5, ss->curregs[5]);
+	return;
+}
+
+/* Utility routines for the Zilog */
+static inline int get_zsbaud(struct mac_serial *ss)
+{
+	struct mac_zschannel *channel = ss->zs_channel;
+	int brg;
+
+	if ((ss->curregs[R11] & TCBR) == 0) {
+		/* higher rates don't use the baud rate generator */
+		return (ss->curregs[R4] & X32CLK)? ZS_CLOCK/32: ZS_CLOCK/16;
+	}
+	/* The baud rate is split up between two 8-bit registers in
+	 * what is termed 'BRG time constant' format in my docs for
+	 * the chip, it is a function of the clk rate the chip is
+	 * receiving which happens to be constant.
+	 */
+	brg = (read_zsreg(channel, 13) << 8);
+	brg |= read_zsreg(channel, 12);
+	return BRG_TO_BPS(brg, (ZS_CLOCK/(ss->clk_divisor)));
+}
+
+/* On receive, this clears errors and the receiver interrupts */
+static inline void rs_recv_clear(struct mac_zschannel *zsc)
+{
+	write_zsreg(zsc, 0, ERR_RES);
+	write_zsreg(zsc, 0, RES_H_IUS); /* XXX this is unnecessary */
+}
+
+/*
+ * Reset a Descriptor-Based DMA channel.
+ */
+static void dbdma_reset(volatile struct dbdma_regs *dma)
+{
+	int i;
+
+	out_le32(&dma->control, (WAKE|FLUSH|PAUSE|RUN) << 16);
+
+	/*
+	 * Yes this looks peculiar, but apparently it needs to be this
+	 * way on some machines.  (We need to make sure the DBDMA
+	 * engine has actually got the write above and responded
+	 * to it. - paulus)
+	 */
+	for (i = 200; i > 0; --i)
+		if (ld_le32(&dma->status) & RUN)
+			udelay(1);
+}
+
+/*
+ * Tells a DBDMA channel to stop and write any buffered data
+ * it might have to memory.
+ */
+static _INLINE_ void dbdma_flush(volatile struct dbdma_regs *dma)
+{
+	int i = 0;
+
+	out_le32(&dma->control, (FLUSH << 16) | FLUSH);
+	while (((in_le32(&dma->status) & FLUSH) != 0) && (i++ < 100))
+		udelay(1);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines.  All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt().  They were separated out for readability's sake.
+ *
+ * 				- Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static _INLINE_ void rs_sched_event(struct mac_serial *info,
+				  int event)
+{
+	info->event |= 1 << event;
+	schedule_work(&info->tqueue);
+}
+
+/* Work out the flag value for a z8530 status value. */
+static _INLINE_ int stat_to_flag(int stat)
+{
+	int flag;
+
+	if (stat & Rx_OVR) {
+		flag = TTY_OVERRUN;
+	} else if (stat & FRM_ERR) {
+		flag = TTY_FRAME;
+	} else if (stat & PAR_ERR) {
+		flag = TTY_PARITY;
+	} else
+		flag = 0;
+	return flag;
+}
+
+static _INLINE_ void receive_chars(struct mac_serial *info,
+				   struct pt_regs *regs)
+{
+	struct tty_struct *tty = info->tty;
+	unsigned char ch, stat, flag;
+
+	while ((read_zsreg(info->zs_channel, 0) & Rx_CH_AV) != 0) {
+
+		stat = read_zsreg(info->zs_channel, R1);
+		ch = read_zsdata(info->zs_channel);
+
+#ifdef CONFIG_KGDB
+		if (info->kgdb_channel) {
+			if (ch == 0x03 || ch == '$')
+				breakpoint();
+			if (stat & (Rx_OVR|FRM_ERR|PAR_ERR))
+				write_zsreg(info->zs_channel, 0, ERR_RES);
+			return;
+		}
+#endif
+		if (!tty)
+			continue;
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+			tty_flip_buffer_push(tty);
+
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+			static int flip_buf_ovf;
+			if (++flip_buf_ovf <= 1)
+				printk(KERN_WARNING "FB. overflow: %d\n",
+						    flip_buf_ovf);
+			break;
+		}
+		tty->flip.count++;
+		{
+			static int flip_max_cnt;
+			if (flip_max_cnt < tty->flip.count)
+				flip_max_cnt = tty->flip.count;
+		}
+		flag = stat_to_flag(stat);
+		if (flag)
+			/* reset the error indication */
+			write_zsreg(info->zs_channel, 0, ERR_RES);
+		*tty->flip.flag_buf_ptr++ = flag;
+		*tty->flip.char_buf_ptr++ = ch;
+	}
+	if (tty)
+		tty_flip_buffer_push(tty);
+}
+
+static void transmit_chars(struct mac_serial *info)
+{
+	if ((read_zsreg(info->zs_channel, 0) & Tx_BUF_EMP) == 0)
+		return;
+	info->tx_active = 0;
+
+	if (info->x_char && !info->power_wait) {
+		/* Send next char */
+		write_zsdata(info->zs_channel, info->x_char);
+		info->x_char = 0;
+		info->tx_active = 1;
+		return;
+	}
+
+	if ((info->xmit_cnt <= 0) || info->tty->stopped || info->tx_stopped
+	    || info->power_wait) {
+		write_zsreg(info->zs_channel, 0, RES_Tx_P);
+		return;
+	}
+
+	/* Send char */
+	write_zsdata(info->zs_channel, info->xmit_buf[info->xmit_tail++]);
+	info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+	info->xmit_cnt--;
+	info->tx_active = 1;
+
+	if (info->xmit_cnt < WAKEUP_CHARS)
+		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+}
+
+static void powerup_done(unsigned long data)
+{
+	struct mac_serial *info = (struct mac_serial *) data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&info->lock, flags);
+	info->power_wait = 0;
+	transmit_chars(info);
+	spin_unlock_irqrestore(&info->lock, flags);
+}
+
+static _INLINE_ void status_handle(struct mac_serial *info)
+{
+	unsigned char status;
+
+	/* Get status from Read Register 0 */
+	status = read_zsreg(info->zs_channel, 0);
+
+	/* Check for DCD transitions */
+	if (((status ^ info->read_reg_zero) & DCD) != 0
+	    && info->tty && !C_CLOCAL(info->tty)) {
+		if (status & DCD) {
+			wake_up_interruptible(&info->open_wait);
+		} else {
+			if (info->tty)
+				tty_hangup(info->tty);
+		}
+	}
+
+	/* Check for CTS transitions */
+	if (info->tty && C_CRTSCTS(info->tty)) {
+		/*
+		 * For some reason, on the Power Macintosh,
+		 * it seems that the CTS bit is 1 when CTS is
+		 * *negated* and 0 when it is asserted.
+		 * The DCD bit doesn't seem to be inverted
+		 * like this.
+		 */
+		if ((status & CTS) == 0) {
+			if (info->tx_stopped) {
+#ifdef SERIAL_DEBUG_FLOW
+				printk(KERN_DEBUG "CTS up\n");
+#endif
+				info->tx_stopped = 0;
+				if (!info->tx_active)
+					transmit_chars(info);
+			}
+		} else {
+#ifdef SERIAL_DEBUG_FLOW
+			printk(KERN_DEBUG "CTS down\n");
+#endif
+			info->tx_stopped = 1;
+		}
+	}
+
+	/* Clear status condition... */
+	write_zsreg(info->zs_channel, 0, RES_EXT_INT);
+	info->read_reg_zero = status;
+}
+
+static _INLINE_ void receive_special_dma(struct mac_serial *info)
+{
+	unsigned char stat, flag;
+	volatile struct dbdma_regs *rd = &info->rx->dma;
+	int where = RX_BUF_SIZE;
+
+	spin_lock(&info->rx_dma_lock);
+	if ((ld_le32(&rd->status) & ACTIVE) != 0)
+		dbdma_flush(rd);
+	if (in_le32(&rd->cmdptr)
+	    == virt_to_bus(info->rx_cmds[info->rx_cbuf] + 1))
+		where -= in_le16(&info->rx->res_count);
+	where--;
+
+	stat = read_zsreg(info->zs_channel, R1);
+
+	flag = stat_to_flag(stat);
+	if (flag) {
+		info->rx_flag_buf[info->rx_cbuf][where] = flag;
+		/* reset the error indication */
+		write_zsreg(info->zs_channel, 0, ERR_RES);
+	}
+
+	spin_unlock(&info->rx_dma_lock);
+}
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+static irqreturn_t rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct mac_serial *info = (struct mac_serial *) dev_id;
+	unsigned char zs_intreg;
+	int shift;
+	unsigned long flags;
+	int handled = 0;
+
+	if (!(info->flags & ZILOG_INITIALIZED)) {
+		printk(KERN_WARNING "rs_interrupt: irq %d, port not "
+				    "initialized\n", irq);
+		disable_irq(irq);
+		return IRQ_NONE;
+	}
+
+	/* NOTE: The read register 3, which holds the irq status,
+	 *       does so for both channels on each chip.  Although
+	 *       the status value itself must be read from the A
+	 *       channel and is only valid when read from channel A.
+	 *       Yes... broken hardware...
+	 */
+#define CHAN_IRQMASK (CHBRxIP | CHBTxIP | CHBEXT)
+
+	if (info->zs_chan_a == info->zs_channel)
+		shift = 3;	/* Channel A */
+	else
+		shift = 0;	/* Channel B */
+
+	spin_lock_irqsave(&info->lock, flags);
+	for (;;) {
+		zs_intreg = read_zsreg(info->zs_chan_a, 3) >> shift;
+#ifdef SERIAL_DEBUG_INTR
+		printk(KERN_DEBUG "rs_interrupt: irq %d, zs_intreg 0x%x\n",
+		       irq, (int)zs_intreg);
+#endif
+
+		if ((zs_intreg & CHAN_IRQMASK) == 0)
+			break;
+		handled = 1;
+
+		if (zs_intreg & CHBRxIP) {
+			/* If we are doing DMA, we only ask for interrupts
+			   on characters with errors or special conditions. */
+			if (info->dma_initted)
+				receive_special_dma(info);
+			else
+				receive_chars(info, regs);
+		}
+		if (zs_intreg & CHBTxIP)
+			transmit_chars(info);
+		if (zs_intreg & CHBEXT)
+			status_handle(info);
+	}
+	spin_unlock_irqrestore(&info->lock, flags);
+	return IRQ_RETVAL(handled);
+}
+
+/* Transmit DMA interrupt - not used at present */
+static irqreturn_t rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	return IRQ_HANDLED;
+}
+
+/*
+ * Receive DMA interrupt.
+ */
+static irqreturn_t rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct mac_serial *info = (struct mac_serial *) dev_id;
+	volatile struct dbdma_cmd *cd;
+
+	if (!info->dma_initted)
+		return IRQ_NONE;
+	spin_lock(&info->rx_dma_lock);
+	/* First, confirm that this interrupt is, indeed, coming */
+	/* from Rx DMA */
+	cd = info->rx_cmds[info->rx_cbuf] + 2;
+	if ((in_le16(&cd->xfer_status) & (RUN | ACTIVE)) != (RUN | ACTIVE)) {
+		spin_unlock(&info->rx_dma_lock);
+		return IRQ_NONE;
+	}
+	if (info->rx_fbuf != RX_NO_FBUF) {
+		info->rx_cbuf = info->rx_fbuf;
+		if (++info->rx_fbuf == info->rx_nbuf)
+			info->rx_fbuf = 0;
+		if (info->rx_fbuf == info->rx_ubuf)
+			info->rx_fbuf = RX_NO_FBUF;
+	}
+	spin_unlock(&info->rx_dma_lock);
+	return IRQ_HANDLED;
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * ------------------------------------------------------------
+ */
+static void rs_stop(struct tty_struct *tty)
+{
+	struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+
+#ifdef SERIAL_DEBUG_STOP
+	printk(KERN_DEBUG "rs_stop %ld....\n",
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (serial_paranoia_check(info, tty->name, "rs_stop"))
+		return;
+
+#if 0
+	spin_lock_irqsave(&info->lock, flags);
+	if (info->curregs[5] & TxENAB) {
+		info->curregs[5] &= ~TxENAB;
+		info->pendregs[5] &= ~TxENAB;
+		write_zsreg(info->zs_channel, 5, info->curregs[5]);
+	}
+	spin_unlock_irqrestore(&info->lock, flags);
+#endif
+}
+
+static void rs_start(struct tty_struct *tty)
+{
+	struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+	unsigned long flags;
+
+#ifdef SERIAL_DEBUG_STOP
+	printk(KERN_DEBUG "rs_start %ld....\n", 
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (serial_paranoia_check(info, tty->name, "rs_start"))
+		return;
+
+	spin_lock_irqsave(&info->lock, flags);
+#if 0
+	if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) {
+		info->curregs[5] |= TxENAB;
+		info->pendregs[5] = info->curregs[5];
+		write_zsreg(info->zs_channel, 5, info->curregs[5]);
+	}
+#else
+	if (info->xmit_cnt && info->xmit_buf && !info->tx_active) {
+		transmit_chars(info);
+	}
+#endif
+	spin_unlock_irqrestore(&info->lock, flags);
+}
+
+static void do_softint(void *private_)
+{
+	struct mac_serial	*info = (struct mac_serial *) private_;
+	struct tty_struct	*tty;
+
+	tty = info->tty;
+	if (!tty)
+		return;
+
+	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event))
+		tty_wakeup(tty);
+}
+
+static int startup(struct mac_serial * info)
+{
+	int delay;
+
+	OPNDBG("startup() (ttyS%d, irq %d)\n", info->line, info->irq);
+ 
+	if (info->flags & ZILOG_INITIALIZED) {
+		OPNDBG(" -> already inited\n");
+ 		return 0;
+	}
+
+	if (!info->xmit_buf) {
+		info->xmit_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL);
+		if (!info->xmit_buf)
+			return -ENOMEM;
+	}
+
+	OPNDBG("starting up ttyS%d (irq %d)...\n", info->line, info->irq);
+
+	delay = set_scc_power(info, 1);
+
+	setup_scc(info);
+
+	if (delay) {
+		unsigned long flags;
+
+		/* delay is in ms */
+		spin_lock_irqsave(&info->lock, flags);
+		info->power_wait = 1;
+		mod_timer(&info->powerup_timer,
+			  jiffies + (delay * HZ + 999) / 1000);
+		spin_unlock_irqrestore(&info->lock, flags);
+	}
+
+	OPNDBG("enabling IRQ on ttyS%d (irq %d)...\n", info->line, info->irq);
+
+	info->flags |= ZILOG_INITIALIZED;
+	enable_irq(info->irq);
+	if (info->dma_initted) {
+		enable_irq(info->rx_dma_irq);
+	}
+
+	return 0;
+}
+
+static _INLINE_ void rxdma_start(struct mac_serial * info, int curr)
+{
+	volatile struct dbdma_regs *rd = &info->rx->dma;
+	volatile struct dbdma_cmd *cd = info->rx_cmds[curr];
+
+//printk(KERN_DEBUG "SCC: rxdma_start\n");
+
+	st_le32(&rd->cmdptr, virt_to_bus(cd));
+	out_le32(&rd->control, (RUN << 16) | RUN);
+}
+
+static void rxdma_to_tty(struct mac_serial *info)
+{
+	struct tty_struct	*tty = info->tty;
+	volatile struct dbdma_regs *rd = &info->rx->dma;
+	unsigned long flags;
+	int residue, available, space, do_queue;
+
+	if (!tty)
+		return;
+
+	do_queue = 0;
+	spin_lock_irqsave(&info->rx_dma_lock, flags);
+more:
+	space = TTY_FLIPBUF_SIZE - tty->flip.count;
+	if (!space) {
+		do_queue++;
+		goto out;
+	}
+	residue = 0;
+	if (info->rx_ubuf == info->rx_cbuf) {
+		if ((ld_le32(&rd->status) & ACTIVE) != 0) {
+			dbdma_flush(rd);
+			if (in_le32(&rd->cmdptr)
+			    == virt_to_bus(info->rx_cmds[info->rx_cbuf]+1))
+				residue = in_le16(&info->rx->res_count);
+		}
+	}
+	available = RX_BUF_SIZE - residue - info->rx_done_bytes;
+	if (available > space)
+		available = space;
+	if (available) {
+		memcpy(tty->flip.char_buf_ptr,
+		       info->rx_char_buf[info->rx_ubuf] + info->rx_done_bytes,
+		       available);
+		memcpy(tty->flip.flag_buf_ptr,
+		       info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes,
+		       available);
+		tty->flip.char_buf_ptr += available;
+		tty->flip.count += available;
+		tty->flip.flag_buf_ptr += available;
+		memset(info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes,
+		       0, available);
+		info->rx_done_bytes += available;
+		do_queue++;
+	}
+	if (info->rx_done_bytes == RX_BUF_SIZE) {
+		volatile struct dbdma_cmd *cd = info->rx_cmds[info->rx_ubuf];
+
+		if (info->rx_ubuf == info->rx_cbuf)
+			goto out;
+		/* mark rx_char_buf[rx_ubuf] free */
+		st_le16(&cd->command, DBDMA_NOP);
+		cd++;
+		st_le32(&cd->cmd_dep, 0);
+		st_le32((unsigned int *)&cd->res_count, 0);
+		cd++;
+		st_le16(&cd->xfer_status, 0);
+
+		if (info->rx_fbuf == RX_NO_FBUF) {
+			info->rx_fbuf = info->rx_ubuf;
+			if (!(ld_le32(&rd->status) & ACTIVE)) {
+				dbdma_reset(&info->rx->dma);
+				rxdma_start(info, info->rx_ubuf);
+				info->rx_cbuf = info->rx_ubuf;
+			}
+		}
+		info->rx_done_bytes = 0;
+		if (++info->rx_ubuf == info->rx_nbuf)
+			info->rx_ubuf = 0;
+		if (info->rx_fbuf == info->rx_ubuf)
+			info->rx_fbuf = RX_NO_FBUF;
+		goto more;
+	}
+out:
+	spin_unlock_irqrestore(&info->rx_dma_lock, flags);
+	if (do_queue)
+		tty_flip_buffer_push(tty);
+}
+
+static void poll_rxdma(unsigned long private_)
+{
+	struct mac_serial	*info = (struct mac_serial *) private_;
+	unsigned long flags;
+
+	rxdma_to_tty(info);
+	spin_lock_irqsave(&info->rx_dma_lock, flags);
+	mod_timer(&info->poll_dma_timer, RX_DMA_TIMER);
+	spin_unlock_irqrestore(&info->rx_dma_lock, flags);
+}
+
+static void dma_init(struct mac_serial * info)
+{
+	int i, size;
+	volatile struct dbdma_cmd *cd;
+	unsigned char *p;
+
+	info->rx_nbuf = 8;
+
+	/* various mem set up */
+	size = sizeof(struct dbdma_cmd) * (3 * info->rx_nbuf + 2)
+		+ (RX_BUF_SIZE * 2 + sizeof(*info->rx_cmds)
+		   + sizeof(*info->rx_char_buf) + sizeof(*info->rx_flag_buf))
+		* info->rx_nbuf;
+	info->dma_priv = kmalloc(size, GFP_KERNEL | GFP_DMA);
+	if (info->dma_priv == NULL)
+		return;
+	memset(info->dma_priv, 0, size);
+
+	info->rx_cmds = (volatile struct dbdma_cmd **)info->dma_priv;
+	info->rx_char_buf = (unsigned char **) (info->rx_cmds + info->rx_nbuf);
+	info->rx_flag_buf = info->rx_char_buf + info->rx_nbuf;
+	p = (unsigned char *) (info->rx_flag_buf + info->rx_nbuf);
+	for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE)
+		info->rx_char_buf[i] = p;
+	for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE)
+		info->rx_flag_buf[i] = p;
+
+	/* a bit of DMA programming */
+	cd = info->rx_cmds[0] = (volatile struct dbdma_cmd *) DBDMA_ALIGN(p);
+	st_le16(&cd->command, DBDMA_NOP);
+	cd++;
+	st_le16(&cd->req_count, RX_BUF_SIZE);
+	st_le16(&cd->command, INPUT_MORE);
+	st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[0]));
+	cd++;
+	st_le16(&cd->req_count, 4);
+	st_le16(&cd->command, STORE_WORD | INTR_ALWAYS);
+	st_le32(&cd->phy_addr, virt_to_bus(cd-2));
+	st_le32(&cd->cmd_dep, DBDMA_STOP);
+	for (i = 1; i < info->rx_nbuf; i++) {
+		info->rx_cmds[i] = ++cd;
+		st_le16(&cd->command, DBDMA_NOP);
+		cd++;
+		st_le16(&cd->req_count, RX_BUF_SIZE);
+		st_le16(&cd->command, INPUT_MORE);
+		st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[i]));
+		cd++;
+		st_le16(&cd->req_count, 4);
+		st_le16(&cd->command, STORE_WORD | INTR_ALWAYS);
+		st_le32(&cd->phy_addr, virt_to_bus(cd-2));
+		st_le32(&cd->cmd_dep, DBDMA_STOP);
+	}
+	cd++;
+	st_le16(&cd->command, DBDMA_NOP | BR_ALWAYS);
+	st_le32(&cd->cmd_dep, virt_to_bus(info->rx_cmds[0]));
+
+	/* setup DMA to our liking */
+	dbdma_reset(&info->rx->dma);
+	st_le32(&info->rx->dma.intr_sel, 0x10001);
+	st_le32(&info->rx->dma.br_sel, 0x10001);
+	out_le32(&info->rx->dma.wait_sel, 0x10001);
+
+	/* set various flags */
+	info->rx_ubuf = 0;
+	info->rx_cbuf = 0;
+	info->rx_fbuf = info->rx_ubuf + 1;
+	if (info->rx_fbuf == info->rx_nbuf)
+		info->rx_fbuf = RX_NO_FBUF;
+	info->rx_done_bytes = 0;
+
+	/* setup polling */
+	init_timer(&info->poll_dma_timer);
+	info->poll_dma_timer.function = (void *)&poll_rxdma;
+	info->poll_dma_timer.data = (unsigned long)info;
+
+	info->dma_initted = 1;
+}
+
+/*
+ * FixZeroBug....Works around a bug in the SCC receving channel.
+ * Taken from Darwin code, 15 Sept. 2000  -DanM
+ *
+ * The following sequence prevents a problem that is seen with O'Hare ASICs
+ * (most versions -- also with some Heathrow and Hydra ASICs) where a zero
+ * at the input to the receiver becomes 'stuck' and locks up the receiver.
+ * This problem can occur as a result of a zero bit at the receiver input
+ * coincident with any of the following events:
+ *
+ *	The SCC is initialized (hardware or software).
+ *	A framing error is detected.
+ *	The clocking option changes from synchronous or X1 asynchronous
+ *		clocking to X16, X32, or X64 asynchronous clocking.
+ *	The decoding mode is changed among NRZ, NRZI, FM0, or FM1.
+ *
+ * This workaround attempts to recover from the lockup condition by placing
+ * the SCC in synchronous loopback mode with a fast clock before programming
+ * any of the asynchronous modes.
+ */
+static void fix_zero_bug_scc(struct mac_serial * info)
+{
+	write_zsreg(info->zs_channel, 9,
+		    (info->zs_channel == info->zs_chan_a? CHRA: CHRB));
+	udelay(10);
+	write_zsreg(info->zs_channel, 9,
+		    ((info->zs_channel == info->zs_chan_a? CHRA: CHRB) | NV));
+
+	write_zsreg(info->zs_channel, 4, (X1CLK | EXTSYNC));
+
+	/* I think this is wrong....but, I just copying code....
+	*/
+	write_zsreg(info->zs_channel, 3, (8 & ~RxENABLE));
+
+	write_zsreg(info->zs_channel, 5, (8 & ~TxENAB));
+	write_zsreg(info->zs_channel, 9, NV);	/* Didn't we already do this? */
+	write_zsreg(info->zs_channel, 11, (RCBR | TCBR));
+	write_zsreg(info->zs_channel, 12, 0);
+	write_zsreg(info->zs_channel, 13, 0);
+	write_zsreg(info->zs_channel, 14, (LOOPBAK | SSBR));
+	write_zsreg(info->zs_channel, 14, (LOOPBAK | SSBR | BRENABL));
+	write_zsreg(info->zs_channel, 3, (8 | RxENABLE));
+	write_zsreg(info->zs_channel, 0, RES_EXT_INT);
+	write_zsreg(info->zs_channel, 0, RES_EXT_INT);	/* to kill some time */
+
+	/* The channel should be OK now, but it is probably receiving
+	 * loopback garbage.
+	 * Switch to asynchronous mode, disable the receiver,
+	 * and discard everything in the receive buffer.
+	 */
+	write_zsreg(info->zs_channel, 9, NV);
+	write_zsreg(info->zs_channel, 4, PAR_ENA);
+	write_zsreg(info->zs_channel, 3, (8 & ~RxENABLE));
+
+	while (read_zsreg(info->zs_channel, 0) & Rx_CH_AV) {
+		(void)read_zsreg(info->zs_channel, 8);
+		write_zsreg(info->zs_channel, 0, RES_EXT_INT);
+		write_zsreg(info->zs_channel, 0, ERR_RES);
+	}
+}
+
+static int setup_scc(struct mac_serial * info)
+{
+	unsigned long flags;
+
+	OPNDBG("setting up ttyS%d SCC...\n", info->line);
+
+	spin_lock_irqsave(&info->lock, flags);
+
+	/* Nice buggy HW ... */
+	fix_zero_bug_scc(info);
+
+	/*
+	 * Reset the chip.
+	 */
+	write_zsreg(info->zs_channel, 9,
+		    (info->zs_channel == info->zs_chan_a? CHRA: CHRB));
+	udelay(10);
+	write_zsreg(info->zs_channel, 9, 0);
+
+	/*
+	 * Clear the receive FIFO.
+	 */
+	ZS_CLEARFIFO(info->zs_channel);
+	info->xmit_fifo_size = 1;
+
+	/*
+	 * Reset DMAs
+	 */
+	if (info->has_dma)
+		dma_init(info);
+
+	/*
+	 * Clear the interrupt registers.
+	 */
+	write_zsreg(info->zs_channel, 0, ERR_RES);
+	write_zsreg(info->zs_channel, 0, RES_H_IUS);
+
+	/*
+	 * Turn on RTS and DTR.
+	 */
+	if (!info->is_irda)
+		zs_rtsdtr(info, 1);
+
+	/*
+	 * Finally, enable sequencing and interrupts
+	 */
+	if (!info->dma_initted) {
+		/* interrupt on ext/status changes, all received chars,
+		   transmit ready */
+		info->curregs[1] = (info->curregs[1] & ~0x18)
+				| (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB);
+	} else {
+		/* interrupt on ext/status changes, W/Req pin is
+		   receive DMA request */
+		info->curregs[1] = (info->curregs[1] & ~(0x18 | TxINT_ENAB))
+				| (EXT_INT_ENAB | WT_RDY_RT | WT_FN_RDYFN);
+		write_zsreg(info->zs_channel, 1, info->curregs[1]);
+		/* enable W/Req pin */
+		info->curregs[1] |= WT_RDY_ENAB;
+		write_zsreg(info->zs_channel, 1, info->curregs[1]);
+		/* enable interrupts on transmit ready and receive errors */
+		info->curregs[1] |= INT_ERR_Rx | TxINT_ENAB;
+	}
+	info->pendregs[1] = info->curregs[1];
+	info->curregs[3] |= (RxENABLE | Rx8);
+	info->pendregs[3] = info->curregs[3];
+	info->curregs[5] |= (TxENAB | Tx8);
+	info->pendregs[5] = info->curregs[5];
+	info->curregs[9] |= (NV | MIE);
+	info->pendregs[9] = info->curregs[9];
+	write_zsreg(info->zs_channel, 3, info->curregs[3]);
+	write_zsreg(info->zs_channel, 5, info->curregs[5]);
+	write_zsreg(info->zs_channel, 9, info->curregs[9]);
+
+	if (info->tty)
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	/*
+	 * Set the speed of the serial port
+	 */
+	change_speed(info, 0);
+
+	/* Save the current value of RR0 */
+	info->read_reg_zero = read_zsreg(info->zs_channel, 0);
+
+	if (info->dma_initted) {
+		spin_lock_irqsave(&info->rx_dma_lock, flags);
+		rxdma_start(info, 0);
+		info->poll_dma_timer.expires = RX_DMA_TIMER;
+		add_timer(&info->poll_dma_timer);
+		spin_unlock_irqrestore(&info->rx_dma_lock, flags);
+	}
+
+	return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(struct mac_serial * info)
+{
+	OPNDBG("Shutting down serial port %d (irq %d)....\n", info->line,
+	       info->irq);
+
+	if (!(info->flags & ZILOG_INITIALIZED)) {
+		OPNDBG("(already shutdown)\n");
+		return;
+	}
+
+	if (info->has_dma) {
+		del_timer(&info->poll_dma_timer);
+		dbdma_reset(info->tx_dma);
+		dbdma_reset(&info->rx->dma);
+		disable_irq(info->tx_dma_irq);
+		disable_irq(info->rx_dma_irq);
+	}
+	disable_irq(info->irq);
+
+	info->pendregs[1] = info->curregs[1] = 0;
+	write_zsreg(info->zs_channel, 1, 0);	/* no interrupts */
+
+	info->curregs[3] &= ~RxENABLE;
+	info->pendregs[3] = info->curregs[3];
+	write_zsreg(info->zs_channel, 3, info->curregs[3]);
+
+	info->curregs[5] &= ~TxENAB;
+	if (!info->tty || C_HUPCL(info->tty))
+		info->curregs[5] &= ~DTR;
+	info->pendregs[5] = info->curregs[5];
+	write_zsreg(info->zs_channel, 5, info->curregs[5]);
+
+	if (info->tty)
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	set_scc_power(info, 0);
+
+	if (info->xmit_buf) {
+		free_page((unsigned long) info->xmit_buf);
+		info->xmit_buf = 0;
+	}
+
+	if (info->has_dma && info->dma_priv) {
+		kfree(info->dma_priv);
+		info->dma_priv = NULL;
+		info->dma_initted = 0;
+	}
+
+	memset(info->curregs, 0, sizeof(info->curregs));
+	memset(info->pendregs, 0, sizeof(info->pendregs));
+
+	info->flags &= ~ZILOG_INITIALIZED;
+}
+
+/*
+ * Turn power on or off to the SCC and associated stuff
+ * (port drivers, modem, IR port, etc.)
+ * Returns the number of milliseconds we should wait before
+ * trying to use the port.
+ */
+static int set_scc_power(struct mac_serial * info, int state)
+{
+	int delay = 0;
+
+	if (state) {
+		PWRDBG("ttyS%d: powering up hardware\n", info->line);
+		pmac_call_feature(
+			PMAC_FTR_SCC_ENABLE,
+			info->dev_node, info->port_type, 1);
+		if (info->is_internal_modem) {
+			pmac_call_feature(
+				PMAC_FTR_MODEM_ENABLE,
+				info->dev_node, 0, 1);
+			delay = 2500;	/* wait for 2.5s before using */
+		} else if (info->is_irda)
+			mdelay(50);	/* Do better here once the problems
+			                 * with blocking have been ironed out
+			                 */
+	} else {
+		/* TODO: Make that depend on a timer, don't power down
+		 * immediately
+		 */
+		PWRDBG("ttyS%d: shutting down hardware\n", info->line);
+		if (info->is_internal_modem) {
+			PWRDBG("ttyS%d: shutting down modem\n", info->line);
+			pmac_call_feature(
+				PMAC_FTR_MODEM_ENABLE,
+				info->dev_node, 0, 0);
+		}
+		pmac_call_feature(
+			PMAC_FTR_SCC_ENABLE,
+			info->dev_node, info->port_type, 0);
+	}
+	return delay;
+}
+
+static void irda_rts_pulses(struct mac_serial *info, int w)
+{
+	udelay(w);
+	write_zsreg(info->zs_channel, 5, Tx8 | TxENAB);
+	udelay(2);
+	write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS);
+	udelay(8);
+	write_zsreg(info->zs_channel, 5, Tx8 | TxENAB);
+	udelay(4);
+	write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS);
+}
+
+/*
+ * Set the irda codec on the imac to the specified baud rate.
+ */
+static void irda_setup(struct mac_serial *info)
+{
+	int code, speed, t;
+
+	speed = info->tty->termios->c_cflag & CBAUD;
+	if (speed < B2400 || speed > B115200)
+		return;
+	code = 0x4d + B115200 - speed;
+
+	/* disable serial interrupts and receive DMA */
+	write_zsreg(info->zs_channel, 1, info->curregs[1] & ~0x9f);
+
+	/* wait for transmitter to drain */
+	t = 10000;
+	while ((read_zsreg(info->zs_channel, 0) & Tx_BUF_EMP) == 0
+	       || (read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) {
+		if (--t <= 0) {
+			printk(KERN_ERR "transmitter didn't drain\n");
+			return;
+		}
+		udelay(10);
+	}
+	udelay(100);
+
+	/* set to 8 bits, no parity, 19200 baud, RTS on, DTR off */
+	write_zsreg(info->zs_channel, 4, X16CLK | SB1);
+	write_zsreg(info->zs_channel, 11, TCBR | RCBR);
+	t = BPS_TO_BRG(19200, ZS_CLOCK/16);
+	write_zsreg(info->zs_channel, 12, t);
+	write_zsreg(info->zs_channel, 13, t >> 8);
+	write_zsreg(info->zs_channel, 14, BRENABL);
+	write_zsreg(info->zs_channel, 3, Rx8 | RxENABLE);
+	write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS);
+
+	/* set TxD low for ~104us and pulse RTS */
+	udelay(1000);
+	write_zsdata(info->zs_channel, 0xfe);
+	irda_rts_pulses(info, 150);
+	irda_rts_pulses(info, 180);
+	irda_rts_pulses(info, 50);
+	udelay(100);
+
+	/* assert DTR, wait 30ms, talk to the chip */
+	write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS | DTR);
+	mdelay(30);
+	while (read_zsreg(info->zs_channel, 0) & Rx_CH_AV)
+		read_zsdata(info->zs_channel);
+
+	write_zsdata(info->zs_channel, 1);
+	t = 1000;
+	while ((read_zsreg(info->zs_channel, 0) & Rx_CH_AV) == 0) {
+		if (--t <= 0) {
+			printk(KERN_ERR "irda_setup timed out on 1st byte\n");
+			goto out;
+		}
+		udelay(10);
+	}
+	t = read_zsdata(info->zs_channel);
+	if (t != 4)
+		printk(KERN_ERR "irda_setup 1st byte = %x\n", t);
+
+	write_zsdata(info->zs_channel, code);
+	t = 1000;
+	while ((read_zsreg(info->zs_channel, 0) & Rx_CH_AV) == 0) {
+		if (--t <= 0) {
+			printk(KERN_ERR "irda_setup timed out on 2nd byte\n");
+			goto out;
+		}
+		udelay(10);
+	}
+	t = read_zsdata(info->zs_channel);
+	if (t != code)
+		printk(KERN_ERR "irda_setup 2nd byte = %x (%x)\n", t, code);
+
+	/* Drop DTR again and do some more RTS pulses */
+ out:
+	udelay(100);
+	write_zsreg(info->zs_channel, 5, Tx8 | TxENAB | RTS);
+	irda_rts_pulses(info, 80);
+
+	/* We should be right to go now.  We assume that load_zsregs
+	   will get called soon to load up the correct baud rate etc. */
+	info->curregs[5] = (info->curregs[5] | RTS) & ~DTR;
+	info->pendregs[5] = info->curregs[5];
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct mac_serial *info, struct termios *old_termios)
+{
+	unsigned cflag;
+	int	bits;
+	int	brg, baud;
+	unsigned long flags;
+
+	if (!info->tty || !info->tty->termios)
+		return;
+
+	cflag = info->tty->termios->c_cflag;
+	baud = tty_get_baud_rate(info->tty);
+	if (baud == 0) {
+		if (old_termios) {
+			info->tty->termios->c_cflag &= ~CBAUD;
+			info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);
+			cflag = info->tty->termios->c_cflag;
+			baud = tty_get_baud_rate(info->tty);
+		}
+		else
+			baud = info->zs_baud;
+	}
+	if (baud > 230400)
+		baud = 230400;
+	else if (baud == 0)
+		baud = 38400;
+
+	spin_lock_irqsave(&info->lock, flags);
+	info->zs_baud = baud;
+	info->clk_divisor = 16;
+
+	BAUDBG(KERN_DEBUG "set speed to %d bds, ", baud);
+
+	switch (baud) {
+	case ZS_CLOCK/16:	/* 230400 */
+		info->curregs[4] = X16CLK;
+		info->curregs[11] = 0;
+		break;
+	case ZS_CLOCK/32:	/* 115200 */
+		info->curregs[4] = X32CLK;
+		info->curregs[11] = 0;
+		break;
+	default:
+		info->curregs[4] = X16CLK;
+		info->curregs[11] = TCBR | RCBR;
+		brg = BPS_TO_BRG(baud, ZS_CLOCK/info->clk_divisor);
+		info->curregs[12] = (brg & 255);
+		info->curregs[13] = ((brg >> 8) & 255);
+		info->curregs[14] = BRENABL;
+	}
+
+	/* byte size and parity */
+	info->curregs[3] &= ~RxNBITS_MASK;
+	info->curregs[5] &= ~TxNBITS_MASK;
+	switch (cflag & CSIZE) {
+	case CS5:
+		info->curregs[3] |= Rx5;
+		info->curregs[5] |= Tx5;
+		BAUDBG("5 bits, ");
+		bits = 7;
+		break;
+	case CS6:
+		info->curregs[3] |= Rx6;
+		info->curregs[5] |= Tx6;
+		BAUDBG("6 bits, ");
+		bits = 8;
+		break;
+	case CS7:
+		info->curregs[3] |= Rx7;
+		info->curregs[5] |= Tx7;
+		BAUDBG("7 bits, ");
+		bits = 9;
+		break;
+	case CS8:
+	default: /* defaults to 8 bits */
+		info->curregs[3] |= Rx8;
+		info->curregs[5] |= Tx8;
+		BAUDBG("8 bits, ");
+		bits = 10;
+		break;
+	}
+	info->pendregs[3] = info->curregs[3];
+	info->pendregs[5] = info->curregs[5];
+
+	info->curregs[4] &= ~(SB_MASK | PAR_ENA | PAR_EVEN);
+	if (cflag & CSTOPB) {
+		info->curregs[4] |= SB2;
+		bits++;
+		BAUDBG("2 stop, ");
+	} else {
+		info->curregs[4] |= SB1;
+		BAUDBG("1 stop, ");
+	}
+	if (cflag & PARENB) {
+		bits++;
+ 		info->curregs[4] |= PAR_ENA;
+		BAUDBG("parity, ");
+	}
+	if (!(cflag & PARODD)) {
+		info->curregs[4] |= PAR_EVEN;
+	}
+	info->pendregs[4] = info->curregs[4];
+
+	if (!(cflag & CLOCAL)) {
+		if (!(info->curregs[15] & DCDIE))
+			info->read_reg_zero = read_zsreg(info->zs_channel, 0);
+		info->curregs[15] |= DCDIE;
+	} else
+		info->curregs[15] &= ~DCDIE;
+	if (cflag & CRTSCTS) {
+		info->curregs[15] |= CTSIE;
+		if ((read_zsreg(info->zs_channel, 0) & CTS) != 0)
+			info->tx_stopped = 1;
+	} else {
+		info->curregs[15] &= ~CTSIE;
+		info->tx_stopped = 0;
+	}
+	info->pendregs[15] = info->curregs[15];
+
+	/* Calc timeout value. This is pretty broken with high baud rates with HZ=100.
+	   This code would love a larger HZ and a >1 fifo size, but this is not
+	   a priority. The resulting value must be >HZ/2
+	 */
+	info->timeout = ((info->xmit_fifo_size*HZ*bits) / baud);
+	info->timeout += HZ/50+1;	/* Add .02 seconds of slop */
+
+	BAUDBG("timeout=%d/%ds, base:%d\n", (int)info->timeout, (int)HZ,
+	       (int)info->baud_base);
+
+	/* set the irda codec to the right rate */
+	if (info->is_irda)
+		irda_setup(info);
+
+	/* Load up the new values */
+	load_zsregs(info->zs_channel, info->curregs);
+
+	spin_unlock_irqrestore(&info->lock, flags);
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
+{
+	struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->name, "rs_flush_chars"))
+		return;
+
+	spin_lock_irqsave(&info->lock, flags);
+	if (!(info->xmit_cnt <= 0 || tty->stopped || info->tx_stopped ||
+	      !info->xmit_buf))
+		/* Enable transmitter */
+		transmit_chars(info);
+	spin_unlock_irqrestore(&info->lock, flags);
+}
+
+static int rs_write(struct tty_struct * tty,
+		    const unsigned char *buf, int count)
+{
+	int	c, ret = 0;
+	struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->name, "rs_write"))
+		return 0;
+
+	if (!tty || !info->xmit_buf || !tmp_buf)
+		return 0;
+
+	while (1) {
+		spin_lock_irqsave(&info->lock, flags);
+		c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+					  SERIAL_XMIT_SIZE - info->xmit_head));
+		if (c <= 0) {
+			spin_unlock_irqrestore(&info->lock, flags);
+			break;
+		}
+		memcpy(info->xmit_buf + info->xmit_head, buf, c);
+		info->xmit_head = ((info->xmit_head + c) &
+				   (SERIAL_XMIT_SIZE-1));
+		info->xmit_cnt += c;
+		spin_unlock_irqrestore(&info->lock, flags);
+		buf += c;
+		count -= c;
+		ret += c;
+	}
+	spin_lock_irqsave(&info->lock, flags);
+	if (info->xmit_cnt && !tty->stopped && !info->tx_stopped
+	    && !info->tx_active)
+		transmit_chars(info);
+	spin_unlock_irqrestore(&info->lock, flags);
+	return ret;
+}
+
+static int rs_write_room(struct tty_struct *tty)
+{
+	struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+	int	ret;
+
+	if (serial_paranoia_check(info, tty->name, "rs_write_room"))
+		return 0;
+	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+	if (ret < 0)
+		ret = 0;
+	return ret;
+}
+
+static int rs_chars_in_buffer(struct tty_struct *tty)
+{
+	struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer"))
+		return 0;
+	return info->xmit_cnt;
+}
+
+static void rs_flush_buffer(struct tty_struct *tty)
+{
+	struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->name, "rs_flush_buffer"))
+		return;
+	spin_lock_irqsave(&info->lock, flags);
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	spin_unlock_irqrestore(&info->lock, flags);
+	tty_wakeup(tty);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ * 
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void rs_throttle(struct tty_struct * tty)
+{
+	struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+	unsigned long flags;
+#ifdef SERIAL_DEBUG_THROTTLE
+	printk(KERN_DEBUG "throttle %ld....\n",tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (serial_paranoia_check(info, tty->name, "rs_throttle"))
+		return;
+
+	if (I_IXOFF(tty)) {
+		spin_lock_irqsave(&info->lock, flags);
+		info->x_char = STOP_CHAR(tty);
+		if (!info->tx_active)
+			transmit_chars(info);
+		spin_unlock_irqrestore(&info->lock, flags);
+	}
+
+	if (C_CRTSCTS(tty)) {
+		/*
+		 * Here we want to turn off the RTS line.  On Macintoshes,
+		 * the external serial ports using a DIN-8 or DIN-9
+		 * connector only have the DTR line (which is usually
+		 * wired to both RTS and DTR on an external modem in
+		 * the cable).  RTS doesn't go out to the serial port
+		 * socket, it acts as an output enable for the transmit
+		 * data line.  So in this case we don't drop RTS.
+		 *
+		 * Macs with internal modems generally do have both RTS
+		 * and DTR wired to the modem, so in that case we do
+		 * drop RTS.
+		 */
+		if (info->is_internal_modem) {
+			spin_lock_irqsave(&info->lock, flags);
+			info->curregs[5] &= ~RTS;
+			info->pendregs[5] &= ~RTS;
+			write_zsreg(info->zs_channel, 5, info->curregs[5]);
+			spin_unlock_irqrestore(&info->lock, flags);
+		}
+	}
+	
+#ifdef CDTRCTS
+	if (tty->termios->c_cflag & CDTRCTS) {
+		spin_lock_irqsave(&info->lock, flags);
+		info->curregs[5] &= ~DTR;
+		info->pendregs[5] &= ~DTR;
+		write_zsreg(info->zs_channel, 5, info->curregs[5]);
+		spin_unlock_irqrestore(&info->lock, flags);
+	}
+#endif /* CDTRCTS */
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+	struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+	unsigned long flags;
+#ifdef SERIAL_DEBUG_THROTTLE
+	printk(KERN_DEBUG "unthrottle %s: %d....\n",
+			tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (serial_paranoia_check(info, tty->name, "rs_unthrottle"))
+		return;
+
+	if (I_IXOFF(tty)) {
+		spin_lock_irqsave(&info->lock, flags);
+		if (info->x_char)
+			info->x_char = 0;
+		else {
+			info->x_char = START_CHAR(tty);
+			if (!info->tx_active)
+				transmit_chars(info);
+		}
+		spin_unlock_irqrestore(&info->lock, flags);
+	}
+
+	if (C_CRTSCTS(tty) && info->is_internal_modem) {
+		/* Assert RTS line */
+		spin_lock_irqsave(&info->lock, flags);
+		info->curregs[5] |= RTS;
+		info->pendregs[5] |= RTS;
+		write_zsreg(info->zs_channel, 5, info->curregs[5]);
+		spin_unlock_irqrestore(&info->lock, flags);
+	}
+
+#ifdef CDTRCTS
+	if (tty->termios->c_cflag & CDTRCTS) {
+		/* Assert DTR line */
+		spin_lock_irqsave(&info->lock, flags);
+		info->curregs[5] |= DTR;
+		info->pendregs[5] |= DTR;
+		write_zsreg(info->zs_channel, 5, info->curregs[5]);
+		spin_unlock_irqrestore(&info->lock, flags);
+	}
+#endif
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct mac_serial * info,
+			   struct serial_struct __user * retinfo)
+{
+	struct serial_struct tmp;
+  
+	if (!retinfo)
+		return -EFAULT;
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.type = info->type;
+	tmp.line = info->line;
+	tmp.port = info->port;
+	tmp.irq = info->irq;
+	tmp.flags = info->flags;
+	tmp.baud_base = info->baud_base;
+	tmp.close_delay = info->close_delay;
+	tmp.closing_wait = info->closing_wait;
+	tmp.custom_divisor = info->custom_divisor;
+	if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
+}
+
+static int set_serial_info(struct mac_serial * info,
+			   struct serial_struct __user * new_info)
+{
+	struct serial_struct new_serial;
+	struct mac_serial old_info;
+	int 			retval = 0;
+
+	if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
+		return -EFAULT;
+	old_info = *info;
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		if ((new_serial.baud_base != info->baud_base) ||
+		    (new_serial.type != info->type) ||
+		    (new_serial.close_delay != info->close_delay) ||
+		    ((new_serial.flags & ~ZILOG_USR_MASK) !=
+		     (info->flags & ~ZILOG_USR_MASK)))
+			return -EPERM;
+		info->flags = ((info->flags & ~ZILOG_USR_MASK) |
+			       (new_serial.flags & ZILOG_USR_MASK));
+		info->custom_divisor = new_serial.custom_divisor;
+		goto check_and_exit;
+	}
+
+	if (info->count > 1)
+		return -EBUSY;
+
+	/*
+	 * OK, past this point, all the error checking has been done.
+	 * At this point, we start making changes.....
+	 */
+
+	info->baud_base = new_serial.baud_base;
+	info->flags = ((info->flags & ~ZILOG_FLAGS) |
+			(new_serial.flags & ZILOG_FLAGS));
+	info->type = new_serial.type;
+	info->close_delay = new_serial.close_delay;
+	info->closing_wait = new_serial.closing_wait;
+
+check_and_exit:
+	if (info->flags & ZILOG_INITIALIZED)
+		retval = setup_scc(info);
+	return retval;
+}
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * 	    is emptied.  On bus types like RS485, the transmitter must
+ * 	    release the bus after transmitting. This must be done when
+ * 	    the transmit shift register is empty, not be done when the
+ * 	    transmit holding register is empty.  This functionality
+ * 	    allows an RS485 driver to be written in user space. 
+ */
+static int get_lsr_info(struct mac_serial * info, unsigned int *value)
+{
+	unsigned char status;
+	unsigned long flags;
+
+	spin_lock_irqsave(&info->lock, flags);
+	status = read_zsreg(info->zs_channel, 0);
+	spin_unlock_irqrestore(&info->lock, flags);
+	status = (status & Tx_BUF_EMP)? TIOCSER_TEMT: 0;
+	return put_user(status,value);
+}
+
+static int rs_tiocmget(struct tty_struct *tty, struct file *file)
+{
+	struct mac_serial * info = (struct mac_serial *)tty->driver_data;
+	unsigned char control, status;
+	unsigned long flags;
+
+#ifdef CONFIG_KGDB
+	if (info->kgdb_channel)
+		return -ENODEV;
+#endif
+	if (serial_paranoia_check(info, tty->name, __FUNCTION__))
+		return -ENODEV;
+
+	if (tty->flags & (1 << TTY_IO_ERROR))
+		return -EIO;
+
+	spin_lock_irqsave(&info->lock, flags);
+	control = info->curregs[5];
+	status = read_zsreg(info->zs_channel, 0);
+	spin_unlock_irqrestore(&info->lock, flags);
+	return    ((control & RTS) ? TIOCM_RTS: 0)
+		| ((control & DTR) ? TIOCM_DTR: 0)
+		| ((status  & DCD) ? TIOCM_CAR: 0)
+		| ((status  & CTS) ? 0: TIOCM_CTS);
+}
+
+static int rs_tiocmset(struct tty_struct *tty, struct file *file,
+		       unsigned int set, unsigned int clear)
+{
+	struct mac_serial * info = (struct mac_serial *)tty->driver_data;
+	unsigned int arg, bits;
+	unsigned long flags;
+
+#ifdef CONFIG_KGDB
+	if (info->kgdb_channel)
+		return -ENODEV;
+#endif
+	if (serial_paranoia_check(info, tty->name, __FUNCTION__))
+		return -ENODEV;
+
+	if (tty->flags & (1 << TTY_IO_ERROR))
+		return -EIO;
+
+	spin_lock_irqsave(&info->lock, flags);
+	if (set & TIOCM_RTS)
+		info->curregs[5] |= RTS;
+	if (set & TIOCM_DTR)
+		info->curregs[5] |= DTR;
+	if (clear & TIOCM_RTS)
+		info->curregs[5] &= ~RTS;
+	if (clear & TIOCM_DTR)
+		info->curregs[5] &= ~DTR;
+
+	info->pendregs[5] = info->curregs[5];
+	write_zsreg(info->zs_channel, 5, info->curregs[5]);
+	spin_unlock_irqrestore(&info->lock, flags);
+	return 0;
+}
+
+/*
+ * rs_break - turn transmit break condition on/off
+ */
+static void rs_break(struct tty_struct *tty, int break_state)
+{
+	struct mac_serial *info = (struct mac_serial *) tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->name, "rs_break"))
+		return;
+
+	spin_lock_irqsave(&info->lock, flags);
+	if (break_state == -1)
+		info->curregs[5] |= SND_BRK;
+	else
+		info->curregs[5] &= ~SND_BRK;
+	write_zsreg(info->zs_channel, 5, info->curregs[5]);
+	spin_unlock_irqrestore(&info->lock, flags);
+}
+
+static int rs_ioctl(struct tty_struct *tty, struct file * file,
+		    unsigned int cmd, unsigned long arg)
+{
+	struct mac_serial * info = (struct mac_serial *)tty->driver_data;
+
+#ifdef CONFIG_KGDB
+	if (info->kgdb_channel)
+		return -ENODEV;
+#endif
+	if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
+		return -ENODEV;
+
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+		    return -EIO;
+	}
+
+	switch (cmd) {
+		case TIOCGSERIAL:
+			return get_serial_info(info,
+					(struct serial_struct __user *) arg);
+		case TIOCSSERIAL:
+			return set_serial_info(info,
+					(struct serial_struct __user *) arg);
+		case TIOCSERGETLSR: /* Get line status register */
+			return get_lsr_info(info, (unsigned int *) arg);
+
+		case TIOCSERGSTRUCT:
+			if (copy_to_user((struct mac_serial __user *) arg,
+					 info, sizeof(struct mac_serial)))
+				return -EFAULT;
+			return 0;
+
+		default:
+			return -ENOIOCTLCMD;
+		}
+	return 0;
+}
+
+static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+	int was_stopped;
+
+	if (tty->termios->c_cflag == old_termios->c_cflag)
+		return;
+	was_stopped = info->tx_stopped;
+
+	change_speed(info, old_termios);
+
+	if (was_stopped && !info->tx_stopped) {
+		tty->hw_stopped = 0;
+		rs_start(tty);
+	}
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ * 
+ * This routine is called when the serial port gets closed.
+ * Wait for the last remaining data to be sent.
+ * ------------------------------------------------------------
+ */
+static void rs_close(struct tty_struct *tty, struct file * filp)
+{
+	struct mac_serial * info = (struct mac_serial *)tty->driver_data;
+	unsigned long flags;
+
+	if (!info || serial_paranoia_check(info, tty->name, "rs_close"))
+		return;
+
+	spin_lock_irqsave(&info->lock, flags);
+
+	if (tty_hung_up_p(filp)) {
+		spin_unlock_irqrestore(&info->lock, flags);
+		return;
+	}
+
+	OPNDBG("rs_close ttyS%d, count = %d\n", info->line, info->count);
+	if ((tty->count == 1) && (info->count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  Info->count should always
+		 * be one in these conditions.  If it's greater than
+		 * one, we've got real problems, since it means the
+		 * serial port won't be shutdown.
+		 */
+		printk(KERN_ERR "rs_close: bad serial port count; tty->count "
+				"is 1, info->count is %d\n", info->count);
+		info->count = 1;
+	}
+	if (--info->count < 0) {
+		printk(KERN_ERR "rs_close: bad serial port count for "
+				"ttyS%d: %d\n", info->line, info->count);
+		info->count = 0;
+	}
+	if (info->count) {
+		spin_unlock_irqrestore(&info->lock, flags);
+		return;
+	}
+	info->flags |= ZILOG_CLOSING;
+	/*
+	 * Now we wait for the transmit buffer to clear; and we notify 
+	 * the line discipline to only process XON/XOFF characters.
+	 */
+	OPNDBG("waiting end of Tx... (timeout:%d)\n", info->closing_wait);
+	tty->closing = 1;
+	if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE) {
+		spin_unlock_irqrestore(&info->lock, flags);
+		tty_wait_until_sent(tty, info->closing_wait);
+		spin_lock_irqsave(&info->lock, flags);
+	}
+
+	/*
+	 * At this point we stop accepting input.  To do this, we
+	 * disable the receiver and receive interrupts.
+	 */
+	info->curregs[3] &= ~RxENABLE;
+	info->pendregs[3] = info->curregs[3];
+	write_zsreg(info->zs_channel, 3, info->curregs[3]);
+	info->curregs[1] &= ~(0x18);	/* disable any rx ints */
+	info->pendregs[1] = info->curregs[1];
+	write_zsreg(info->zs_channel, 1, info->curregs[1]);
+	ZS_CLEARFIFO(info->zs_channel);
+	if (info->flags & ZILOG_INITIALIZED) {
+		/*
+		 * Before we drop DTR, make sure the SCC transmitter
+		 * has completely drained.
+		 */
+		OPNDBG("waiting end of Rx...\n");
+		spin_unlock_irqrestore(&info->lock, flags);
+		rs_wait_until_sent(tty, info->timeout);
+		spin_lock_irqsave(&info->lock, flags);
+	}
+
+	shutdown(info);
+	/* restore flags now since shutdown() will have disabled this port's
+	   specific irqs */
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	if (tty->driver->flush_buffer)
+		tty->driver->flush_buffer(tty);
+	tty_ldisc_flush(tty);
+	tty->closing = 0;
+	info->event = 0;
+	info->tty = 0;
+
+	if (info->blocked_open) {
+		if (info->close_delay) {
+			msleep_interruptible(jiffies_to_msecs(info->close_delay));
+		}
+		wake_up_interruptible(&info->open_wait);
+	}
+	info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CLOSING);
+	wake_up_interruptible(&info->close_wait);
+}
+
+/*
+ * rs_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	struct mac_serial *info = (struct mac_serial *) tty->driver_data;
+	unsigned long orig_jiffies, char_time;
+
+	if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent"))
+		return;
+
+/*	printk("rs_wait_until_sent, timeout:%d, tty_stopped:%d, tx_stopped:%d\n",
+			timeout, tty->stopped, info->tx_stopped);
+*/
+	orig_jiffies = jiffies;
+	/*
+	 * Set the check interval to be 1/5 of the estimated time to
+	 * send a single character, and make it at least 1.  The check
+	 * interval should also be less than the timeout.
+	 */
+	if (info->timeout <= HZ/50) {
+		printk(KERN_INFO "macserial: invalid info->timeout=%d\n",
+				    info->timeout);
+		info->timeout = HZ/50+1;
+	}
+
+	char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
+	char_time = char_time / 5;
+	if (char_time > HZ) {
+		printk(KERN_WARNING "macserial: char_time %ld >HZ !!!\n",
+				    char_time);
+		char_time = 1;
+	} else if (char_time == 0)
+		char_time = 1;
+	if (timeout)
+		char_time = min_t(unsigned long, char_time, timeout);
+	while ((read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) {
+		msleep_interruptible(jiffies_to_msecs(char_time));
+		if (signal_pending(current))
+			break;
+		if (timeout && time_after(jiffies, orig_jiffies + timeout))
+			break;
+	}
+	current->state = TASK_RUNNING;
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void rs_hangup(struct tty_struct *tty)
+{
+	struct mac_serial * info = (struct mac_serial *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->name, "rs_hangup"))
+		return;
+
+	rs_flush_buffer(tty);
+	shutdown(info);
+	info->event = 0;
+	info->count = 0;
+	info->flags &= ~ZILOG_NORMAL_ACTIVE;
+	info->tty = 0;
+	wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+			   struct mac_serial *info)
+{
+	DECLARE_WAITQUEUE(wait,current);
+	int		retval;
+	int		do_clocal = 0;
+
+	/*
+	 * If the device is in the middle of being closed, then block
+	 * until it's done, and then try again.
+	 */
+	if (info->flags & ZILOG_CLOSING) {
+		interruptible_sleep_on(&info->close_wait);
+		return -EAGAIN;
+	}
+
+	/*
+	 * If non-blocking mode is set, or the port is not enabled,
+	 * then make the check up front and then exit.
+	 */
+	if ((filp->f_flags & O_NONBLOCK) ||
+	    (tty->flags & (1 << TTY_IO_ERROR))) {
+		info->flags |= ZILOG_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (tty->termios->c_cflag & CLOCAL)
+		do_clocal = 1;
+
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, info->count is dropped by one, so that
+	 * rs_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+	add_wait_queue(&info->open_wait, &wait);
+	OPNDBG("block_til_ready before block: ttyS%d, count = %d\n",
+	       info->line, info->count);
+	spin_lock_irq(&info->lock);
+	if (!tty_hung_up_p(filp)) 
+		info->count--;
+	spin_unlock_irq(&info->lock);
+	info->blocked_open++;
+	while (1) {
+		spin_lock_irq(&info->lock);
+		if ((tty->termios->c_cflag & CBAUD) &&
+		    !info->is_irda)
+			zs_rtsdtr(info, 1);
+		spin_unlock_irq(&info->lock);
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (tty_hung_up_p(filp) ||
+		    !(info->flags & ZILOG_INITIALIZED)) {
+			retval = -EAGAIN;
+			break;
+		}
+		if (!(info->flags & ZILOG_CLOSING) &&
+		    (do_clocal || (read_zsreg(info->zs_channel, 0) & DCD)))
+			break;
+		if (signal_pending(current)) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+		OPNDBG("block_til_ready blocking: ttyS%d, count = %d\n",
+		       info->line, info->count);
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&info->open_wait, &wait);
+	if (!tty_hung_up_p(filp))
+		info->count++;
+	info->blocked_open--;
+	OPNDBG("block_til_ready after blocking: ttyS%d, count = %d\n",
+	       info->line, info->count);
+	if (retval)
+		return retval;
+	info->flags |= ZILOG_NORMAL_ACTIVE;
+	return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its ZILOG structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int rs_open(struct tty_struct *tty, struct file * filp)
+{
+	struct mac_serial	*info;
+	int 			retval, line;
+	unsigned long		page;
+
+	line = tty->index;
+	if ((line < 0) || (line >= zs_channels_found)) {
+		return -ENODEV;
+	}
+	info = zs_soft + line;
+
+#ifdef CONFIG_KGDB
+	if (info->kgdb_channel) {
+		return -ENODEV;
+	}
+#endif
+	if (serial_paranoia_check(info, tty->name, "rs_open"))
+		return -ENODEV;
+	OPNDBG("rs_open %s, count = %d, tty=%p\n", tty->name,
+	       info->count, tty);
+
+	info->count++;
+	tty->driver_data = info;
+	info->tty = tty;
+
+	if (!tmp_buf) {
+		page = get_zeroed_page(GFP_KERNEL);
+		if (!page)
+			return -ENOMEM;
+		if (tmp_buf)
+			free_page(page);
+		else
+			tmp_buf = (unsigned char *) page;
+	}
+
+	/*
+	 * If the port is the middle of closing, bail out now
+	 */
+	if (tty_hung_up_p(filp) ||
+	    (info->flags & ZILOG_CLOSING)) {
+		if (info->flags & ZILOG_CLOSING)
+			interruptible_sleep_on(&info->close_wait);
+		return -EAGAIN;
+	}
+
+	/*
+	 * Start up serial port
+	 */
+
+	retval = startup(info);
+	if (retval)
+		return retval;
+
+	retval = block_til_ready(tty, filp, info);
+	if (retval) {
+		OPNDBG("rs_open returning after block_til_ready with %d\n",
+			retval);
+		return retval;
+	}
+
+#ifdef CONFIG_SERIAL_CONSOLE
+	if (sercons.cflag && sercons.index == line) {
+		tty->termios->c_cflag = sercons.cflag;
+		sercons.cflag = 0;
+		change_speed(info, 0);
+	}
+#endif
+
+	OPNDBG("rs_open %s successful...\n", tty->name);
+	return 0;
+}
+
+/* Finally, routines used to initialize the serial driver. */
+
+static void show_serial_version(void)
+{
+	printk(KERN_INFO "PowerMac Z8530 serial driver version " MACSERIAL_VERSION "\n");
+}
+
+/*
+ * Initialize one channel, both the mac_serial and mac_zschannel
+ * structs.  We use the dev_node field of the mac_serial struct.
+ */
+static int
+chan_init(struct mac_serial *zss, struct mac_zschannel *zs_chan,
+	  struct mac_zschannel *zs_chan_a)
+{
+	struct device_node *ch = zss->dev_node;
+	char *conn;
+	int len;
+	struct slot_names_prop {
+		int	count;
+		char	name[1];
+	} *slots;
+
+	zss->irq = ch->intrs[0].line;
+	zss->has_dma = 0;
+#if !defined(CONFIG_KGDB) && defined(SUPPORT_SERIAL_DMA)
+	if (ch->n_addrs >= 3 && ch->n_intrs == 3)
+		zss->has_dma = 1;
+#endif
+	zss->dma_initted = 0;
+
+	zs_chan->control = (volatile unsigned char *)
+		ioremap(ch->addrs[0].address, 0x1000);
+	zs_chan->data = zs_chan->control + 0x10;
+	spin_lock_init(&zs_chan->lock);
+	zs_chan->parent = zss;
+	zss->zs_channel = zs_chan;
+	zss->zs_chan_a = zs_chan_a;
+
+	/* setup misc varariables */
+	zss->kgdb_channel = 0;
+
+	/* For now, we assume you either have a slot-names property
+	 * with "Modem" in it, or your channel is compatible with
+	 * "cobalt". Might need additional fixups
+	 */
+	zss->is_internal_modem = device_is_compatible(ch, "cobalt");
+	conn = get_property(ch, "AAPL,connector", &len);
+	zss->is_irda = conn && (strcmp(conn, "infrared") == 0);
+	zss->port_type = PMAC_SCC_ASYNC;
+	/* 1999 Powerbook G3 has slot-names property instead */
+	slots = (struct slot_names_prop *)get_property(ch, "slot-names", &len);
+	if (slots && slots->count > 0) {
+		if (strcmp(slots->name, "IrDA") == 0)
+			zss->is_irda = 1;
+		else if (strcmp(slots->name, "Modem") == 0)
+			zss->is_internal_modem = 1;
+	}
+	if (zss->is_irda)
+		zss->port_type = PMAC_SCC_IRDA;
+	if (zss->is_internal_modem) {
+		struct device_node* i2c_modem = find_devices("i2c-modem");
+		if (i2c_modem) {
+			char* mid = get_property(i2c_modem, "modem-id", NULL);
+			if (mid) switch(*mid) {
+			case 0x04 :
+			case 0x05 :
+			case 0x07 :
+			case 0x08 :
+			case 0x0b :
+			case 0x0c :
+				zss->port_type = PMAC_SCC_I2S1;
+			}
+			printk(KERN_INFO "macserial: i2c-modem detected, id: %d\n",
+				mid ? (*mid) : 0);
+		} else {
+			printk(KERN_INFO "macserial: serial modem detected\n");
+		}
+	}
+
+	while (zss->has_dma) {
+		zss->dma_priv = NULL;
+		/* it seems that the last two addresses are the
+		   DMA controllers */
+		zss->tx_dma = (volatile struct dbdma_regs *)
+			ioremap(ch->addrs[ch->n_addrs - 2].address, 0x100);
+		zss->rx = (volatile struct mac_dma *)
+			ioremap(ch->addrs[ch->n_addrs - 1].address, 0x100);
+		zss->tx_dma_irq = ch->intrs[1].line;
+		zss->rx_dma_irq = ch->intrs[2].line;
+		spin_lock_init(&zss->rx_dma_lock);
+		break;
+	}
+
+	init_timer(&zss->powerup_timer);
+	zss->powerup_timer.function = powerup_done;
+	zss->powerup_timer.data = (unsigned long) zss;
+	return 0;
+}
+
+/*
+ * /proc fs routines. TODO: Add status lines & error stats
+ */
+static inline int
+line_info(char *buf, struct mac_serial *info)
+{
+	int		ret=0;
+	unsigned char* connector;
+	int lenp;
+
+	ret += sprintf(buf, "%d: port:0x%X irq:%d", info->line, info->port, info->irq);
+
+	connector = get_property(info->dev_node, "AAPL,connector", &lenp);
+	if (connector)
+		ret+=sprintf(buf+ret," con:%s ", connector);
+	if (info->is_internal_modem) {
+		if (!connector)
+			ret+=sprintf(buf+ret," con:");
+		ret+=sprintf(buf+ret,"%s", " (internal modem)");
+	}
+	if (info->is_irda) {
+		if (!connector)
+			ret+=sprintf(buf+ret," con:");
+		ret+=sprintf(buf+ret,"%s", " (IrDA)");
+	}
+	ret+=sprintf(buf+ret,"\n");
+
+	return ret;
+}
+
+int macserial_read_proc(char *page, char **start, off_t off, int count,
+		 int *eof, void *data)
+{
+	int l, len = 0;
+	off_t	begin = 0;
+	struct mac_serial *info;
+
+	len += sprintf(page, "serinfo:1.0 driver:" MACSERIAL_VERSION "\n");
+	for (info = zs_chain; info && len < 4000; info = info->zs_next) {
+		l = line_info(page + len, info);
+		len += l;
+		if (len+begin > off+count)
+			goto done;
+		if (len+begin < off) {
+			begin += len;
+			len = 0;
+		}
+	}
+	*eof = 1;
+done:
+	if (off >= len+begin)
+		return 0;
+	*start = page + (off-begin);
+	return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/* Ask the PROM how many Z8530s we have and initialize their zs_channels */
+static void
+probe_sccs(void)
+{
+	struct device_node *dev, *ch;
+	struct mac_serial **pp;
+	int n, chip, nchan;
+	struct mac_zschannel *zs_chan;
+	int chan_a_index;
+
+	n = 0;
+	pp = &zs_chain;
+	zs_chan = zs_channels;
+	for (dev = find_devices("escc"); dev != 0; dev = dev->next) {
+		nchan = 0;
+		chip = n;
+		if (n >= NUM_CHANNELS) {
+			printk(KERN_WARNING "Sorry, can't use %s: no more "
+					    "channels\n", dev->full_name);
+			continue;
+		}
+		chan_a_index = 0;
+		for (ch = dev->child; ch != 0; ch = ch->sibling) {
+			if (nchan >= 2) {
+				printk(KERN_WARNING "SCC: Only 2 channels per "
+					"chip are supported\n");
+				break;
+			}
+			if (ch->n_addrs < 1 || (ch ->n_intrs < 1)) {
+				printk("Can't use %s: %d addrs %d intrs\n",
+				      ch->full_name, ch->n_addrs, ch->n_intrs);
+				continue;
+			}
+
+			/* The channel with the higher address
+			   will be the A side. */
+			if (nchan > 0 &&
+			    ch->addrs[0].address
+			    > zs_soft[n-1].dev_node->addrs[0].address)
+				chan_a_index = 1;
+
+			/* minimal initialization for now */
+			zs_soft[n].dev_node = ch;
+			*pp = &zs_soft[n];
+			pp = &zs_soft[n].zs_next;
+			++nchan;
+			++n;
+		}
+		if (nchan == 0)
+			continue;
+
+		/* set up A side */
+		if (chan_init(&zs_soft[chip + chan_a_index], zs_chan, zs_chan))
+			continue;
+		++zs_chan;
+
+		/* set up B side, if it exists */
+		if (nchan > 1)
+			if (chan_init(&zs_soft[chip + 1 - chan_a_index],
+				  zs_chan, zs_chan - 1))
+				continue;
+		++zs_chan;
+	}
+	*pp = 0;
+
+	zs_channels_found = n;
+#ifdef CONFIG_PMAC_PBOOK
+	if (n)
+		pmu_register_sleep_notifier(&serial_sleep_notifier);
+#endif /* CONFIG_PMAC_PBOOK */
+}
+
+static struct tty_operations serial_ops = {
+	.open = rs_open,
+	.close = rs_close,
+	.write = rs_write,
+	.flush_chars = rs_flush_chars,
+	.write_room = rs_write_room,
+	.chars_in_buffer = rs_chars_in_buffer,
+	.flush_buffer = rs_flush_buffer,
+	.ioctl = rs_ioctl,
+	.throttle = rs_throttle,
+	.unthrottle = rs_unthrottle,
+	.set_termios = rs_set_termios,
+	.stop = rs_stop,
+	.start = rs_start,
+	.hangup = rs_hangup,
+	.break_ctl = rs_break,
+	.wait_until_sent = rs_wait_until_sent,
+	.read_proc = macserial_read_proc,
+	.tiocmget = rs_tiocmget,
+	.tiocmset = rs_tiocmset,
+};
+
+static int macserial_init(void)
+{
+	int channel, i;
+	struct mac_serial *info;
+
+	/* Find out how many Z8530 SCCs we have */
+	if (zs_chain == 0)
+		probe_sccs();
+
+	serial_driver = alloc_tty_driver(zs_channels_found);
+	if (!serial_driver)
+		return -ENOMEM;
+
+	/* XXX assume it's a powerbook if we have a via-pmu
+	 * 
+	 * This is OK for core99 machines as well.
+	 */
+	is_powerbook = find_devices("via-pmu") != 0;
+
+	/* Register the interrupt handler for each one
+	 * We also request the OF resources here as probe_sccs()
+	 * might be called too early for that
+	 */
+	for (i = 0; i < zs_channels_found; ++i) {
+		struct device_node* ch = zs_soft[i].dev_node;
+		if (!request_OF_resource(ch, 0, NULL)) {
+			printk(KERN_ERR "macserial: can't request IO resource !\n");
+			put_tty_driver(serial_driver);
+			return -ENODEV;
+		}
+		if (zs_soft[i].has_dma) {
+			if (!request_OF_resource(ch, ch->n_addrs - 2, " (tx dma)")) {
+				printk(KERN_ERR "macserial: can't request TX DMA resource !\n");
+				zs_soft[i].has_dma = 0;
+				goto no_dma;
+			}
+			if (!request_OF_resource(ch, ch->n_addrs - 1, " (rx dma)")) {
+				release_OF_resource(ch, ch->n_addrs - 2);
+				printk(KERN_ERR "macserial: can't request RX DMA resource !\n");
+				zs_soft[i].has_dma = 0;
+				goto no_dma;
+			}
+			if (request_irq(zs_soft[i].tx_dma_irq, rs_txdma_irq, 0,
+					"SCC-txdma", &zs_soft[i]))
+				printk(KERN_ERR "macserial: can't get irq %d\n",
+				       zs_soft[i].tx_dma_irq);
+			disable_irq(zs_soft[i].tx_dma_irq);
+			if (request_irq(zs_soft[i].rx_dma_irq, rs_rxdma_irq, 0,
+					"SCC-rxdma", &zs_soft[i]))
+				printk(KERN_ERR "macserial: can't get irq %d\n",
+				       zs_soft[i].rx_dma_irq);
+			disable_irq(zs_soft[i].rx_dma_irq);
+		}
+no_dma:		
+		if (request_irq(zs_soft[i].irq, rs_interrupt, 0,
+				"SCC", &zs_soft[i]))
+			printk(KERN_ERR "macserial: can't get irq %d\n",
+			       zs_soft[i].irq);
+		disable_irq(zs_soft[i].irq);
+	}
+
+	show_serial_version();
+
+	/* Initialize the tty_driver structure */
+	/* Not all of this is exactly right for us. */
+
+	serial_driver->owner = THIS_MODULE;
+	serial_driver->driver_name = "macserial";
+	serial_driver->devfs_name = "tts/";
+	serial_driver->name = "ttyS";
+	serial_driver->major = TTY_MAJOR;
+	serial_driver->minor_start = 64;
+	serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
+	serial_driver->subtype = SERIAL_TYPE_NORMAL;
+	serial_driver->init_termios = tty_std_termios;
+	serial_driver->init_termios.c_cflag =
+		B38400 | CS8 | CREAD | HUPCL | CLOCAL;
+	serial_driver->flags = TTY_DRIVER_REAL_RAW;
+	tty_set_operations(serial_driver, &serial_ops);
+
+	if (tty_register_driver(serial_driver))
+		printk(KERN_ERR "Error: couldn't register serial driver\n");
+
+	for (channel = 0; channel < zs_channels_found; ++channel) {
+#ifdef CONFIG_KGDB
+		if (zs_soft[channel].kgdb_channel) {
+			kgdb_interruptible(1);
+			continue;
+		}
+#endif
+		zs_soft[channel].clk_divisor = 16;
+/* -- we are not sure the SCC is powered ON at this point
+ 		zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]);
+*/
+		zs_soft[channel].zs_baud = 38400;
+
+		/* If console serial line, then enable interrupts. */
+		if (zs_soft[channel].is_cons) {
+			printk(KERN_INFO "macserial: console line, enabling "
+					"interrupt %d\n", zs_soft[channel].irq);
+			panic("macserial: console not supported yet !");
+			write_zsreg(zs_soft[channel].zs_channel, R1,
+				    (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB));
+			write_zsreg(zs_soft[channel].zs_channel, R9,
+				    (NV | MIE));
+		}
+	}
+
+	for (info = zs_chain, i = 0; info; info = info->zs_next, i++)
+	{
+		unsigned char* connector;
+		int lenp;
+
+#ifdef CONFIG_KGDB
+		if (info->kgdb_channel) {
+			continue;
+		}
+#endif
+		info->magic = SERIAL_MAGIC;
+		info->port = (int) info->zs_channel->control;
+		info->line = i;
+		info->tty = 0;
+		info->custom_divisor = 16;
+		info->timeout = 0;
+		info->close_delay = 50;
+		info->closing_wait = 3000;
+		info->x_char = 0;
+		info->event = 0;
+		info->count = 0;
+		info->blocked_open = 0;
+		INIT_WORK(&info->tqueue, do_softint, info);
+		spin_lock_init(&info->lock);
+		init_waitqueue_head(&info->open_wait);
+		init_waitqueue_head(&info->close_wait);
+		info->timeout = HZ;
+		printk(KERN_INFO "tty%02d at 0x%08x (irq = %d)", info->line, 
+			info->port, info->irq);
+		printk(" is a Z8530 ESCC");
+		connector = get_property(info->dev_node, "AAPL,connector", &lenp);
+		if (connector)
+			printk(", port = %s", connector);
+		if (info->is_internal_modem)
+			printk(" (internal modem)");
+		if (info->is_irda)
+			printk(" (IrDA)");
+		printk("\n");
+ 	}
+	tmp_buf = 0;
+
+	return 0;
+}
+
+void macserial_cleanup(void)
+{
+	int i;
+	unsigned long flags;
+	struct mac_serial *info;
+
+	for (info = zs_chain, i = 0; info; info = info->zs_next, i++)
+		set_scc_power(info, 0);
+	spin_lock_irqsave(&info->lock, flags);
+	for (i = 0; i < zs_channels_found; ++i) {
+		free_irq(zs_soft[i].irq, &zs_soft[i]);
+		if (zs_soft[i].has_dma) {
+			free_irq(zs_soft[i].tx_dma_irq, &zs_soft[i]);
+			free_irq(zs_soft[i].rx_dma_irq, &zs_soft[i]);
+		}
+		release_OF_resource(zs_soft[i].dev_node, 0);
+		if (zs_soft[i].has_dma) {
+			struct device_node* ch = zs_soft[i].dev_node;
+			release_OF_resource(ch, ch->n_addrs - 2);
+			release_OF_resource(ch, ch->n_addrs - 1);
+		}
+	}
+	spin_unlock_irqrestore(&info->lock, flags);
+	tty_unregister_driver(serial_driver);
+	put_tty_driver(serial_driver);
+
+	if (tmp_buf) {
+		free_page((unsigned long) tmp_buf);
+		tmp_buf = 0;
+	}
+
+#ifdef CONFIG_PMAC_PBOOK
+	if (zs_channels_found)
+		pmu_unregister_sleep_notifier(&serial_sleep_notifier);
+#endif /* CONFIG_PMAC_PBOOK */
+}
+
+module_init(macserial_init);
+module_exit(macserial_cleanup);
+MODULE_LICENSE("GPL");
+
+#if 0
+/*
+ * register_serial and unregister_serial allows for serial ports to be
+ * configured at run-time, to support PCMCIA modems.
+ */
+/* PowerMac: Unused at this time, just here to make things link. */
+int register_serial(struct serial_struct *req)
+{
+	return -1;
+}
+
+void unregister_serial(int line)
+{
+	return;
+}
+#endif
+
+/*
+ * ------------------------------------------------------------
+ * Serial console driver
+ * ------------------------------------------------------------
+ */
+#ifdef CONFIG_SERIAL_CONSOLE
+
+/*
+ *	Print a string to the serial port trying not to disturb
+ *	any possible real use of the port...
+ */
+static void serial_console_write(struct console *co, const char *s,
+				 unsigned count)
+{
+	struct mac_serial *info = zs_soft + co->index;
+	int i;
+
+	/* Turn of interrupts and enable the transmitter. */
+	write_zsreg(info->zs_channel, R1, info->curregs[1] & ~TxINT_ENAB);
+	write_zsreg(info->zs_channel, R5, info->curregs[5] | TxENAB | RTS | DTR);
+
+	for (i=0; i<count; i++) {
+		/* Wait for the transmit buffer to empty. */
+		while ((read_zsreg(info->zs_channel, 0) & Tx_BUF_EMP) == 0) {
+			eieio();
+		}
+
+		write_zsdata(info->zs_channel, s[i]);
+		if (s[i] == 10) {
+			while ((read_zsreg(info->zs_channel, 0) & Tx_BUF_EMP)
+                                == 0)
+				eieio();
+
+			write_zsdata(info->zs_channel, 13);
+		}
+	}
+
+	/* Restore the values in the registers. */
+	write_zsreg(info->zs_channel, R1, info->curregs[1]);
+	/* Don't disable the transmitter. */
+}
+
+static struct tty_driver *serial_driver;
+
+static struct tty_driver *serial_console_device(struct console *c, int *index)
+{
+	*index = c->index;
+	return serial_driver;
+}
+
+/*
+ *	Setup initial baud/bits/parity. We do two things here:
+ *	- construct a cflag setting for the first rs_open()
+ *	- initialize the serial port
+ *	Return non-zero if we didn't find a serial port.
+ */
+static int __init serial_console_setup(struct console *co, char *options)
+{
+	struct mac_serial *info;
+	int	baud = 38400;
+	int	bits = 8;
+	int	parity = 'n';
+	int	cflag = CREAD | HUPCL | CLOCAL;
+	int	brg;
+	char	*s;
+	long	flags;
+
+	/* Find out how many Z8530 SCCs we have */
+	if (zs_chain == 0)
+		probe_sccs();
+
+	if (zs_chain == 0)
+		return -1;
+
+	/* Do we have the device asked for? */
+	if (co->index >= zs_channels_found)
+		return -1;
+	info = zs_soft + co->index;
+
+	set_scc_power(info, 1);
+
+	/* Reset the channel */
+	write_zsreg(info->zs_channel, R9, CHRA);
+
+	if (options) {
+		baud = simple_strtoul(options, NULL, 10);
+		s = options;
+		while(*s >= '0' && *s <= '9')
+			s++;
+		if (*s)
+			parity = *s++;
+		if (*s)
+			bits   = *s - '0';
+	}
+
+	/*
+	 *	Now construct a cflag setting.
+	 */
+	switch(baud) {
+	case 1200:
+		cflag |= B1200;
+		break;
+	case 2400:
+		cflag |= B2400;
+		break;
+	case 4800:
+		cflag |= B4800;
+		break;
+	case 9600:
+		cflag |= B9600;
+		break;
+	case 19200:
+		cflag |= B19200;
+		break;
+	case 57600:
+		cflag |= B57600;
+		break;
+	case 115200:
+		cflag |= B115200;
+		break;
+	case 38400:
+	default:
+		cflag |= B38400;
+		break;
+	}
+	switch(bits) {
+	case 7:
+		cflag |= CS7;
+		break;
+	default:
+	case 8:
+		cflag |= CS8;
+		break;
+	}
+	switch(parity) {
+	case 'o': case 'O':
+		cflag |= PARENB | PARODD;
+		break;
+	case 'e': case 'E':
+		cflag |= PARENB;
+		break;
+	}
+	co->cflag = cflag;
+
+	spin_lock_irqsave(&info->lock, flags);
+        memset(info->curregs, 0, sizeof(info->curregs));
+
+	info->zs_baud = baud;
+	info->clk_divisor = 16;
+	switch (info->zs_baud) {
+	case ZS_CLOCK/16:	/* 230400 */
+		info->curregs[4] = X16CLK;
+		info->curregs[11] = 0;
+		break;
+	case ZS_CLOCK/32:	/* 115200 */
+		info->curregs[4] = X32CLK;
+		info->curregs[11] = 0;
+		break;
+	default:
+		info->curregs[4] = X16CLK;
+		info->curregs[11] = TCBR | RCBR;
+		brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor);
+		info->curregs[12] = (brg & 255);
+		info->curregs[13] = ((brg >> 8) & 255);
+		info->curregs[14] = BRENABL;
+	}
+
+	/* byte size and parity */
+	info->curregs[3] &= ~RxNBITS_MASK;
+	info->curregs[5] &= ~TxNBITS_MASK;
+	switch (cflag & CSIZE) {
+	case CS5:
+		info->curregs[3] |= Rx5;
+		info->curregs[5] |= Tx5;
+		break;
+	case CS6:
+		info->curregs[3] |= Rx6;
+		info->curregs[5] |= Tx6;
+		break;
+	case CS7:
+		info->curregs[3] |= Rx7;
+		info->curregs[5] |= Tx7;
+		break;
+	case CS8:
+	default: /* defaults to 8 bits */
+		info->curregs[3] |= Rx8;
+		info->curregs[5] |= Tx8;
+		break;
+	}
+        info->curregs[5] |= TxENAB | RTS | DTR;
+	info->pendregs[3] = info->curregs[3];
+	info->pendregs[5] = info->curregs[5];
+
+	info->curregs[4] &= ~(SB_MASK | PAR_ENA | PAR_EVEN);
+	if (cflag & CSTOPB) {
+		info->curregs[4] |= SB2;
+	} else {
+		info->curregs[4] |= SB1;
+	}
+	if (cflag & PARENB) {
+		info->curregs[4] |= PAR_ENA;
+		if (!(cflag & PARODD)) {
+			info->curregs[4] |= PAR_EVEN;
+		}
+	}
+	info->pendregs[4] = info->curregs[4];
+
+	if (!(cflag & CLOCAL)) {
+		if (!(info->curregs[15] & DCDIE))
+			info->read_reg_zero = read_zsreg(info->zs_channel, 0);
+		info->curregs[15] |= DCDIE;
+	} else
+		info->curregs[15] &= ~DCDIE;
+	if (cflag & CRTSCTS) {
+		info->curregs[15] |= CTSIE;
+		if ((read_zsreg(info->zs_channel, 0) & CTS) != 0)
+			info->tx_stopped = 1;
+	} else {
+		info->curregs[15] &= ~CTSIE;
+		info->tx_stopped = 0;
+	}
+	info->pendregs[15] = info->curregs[15];
+
+	/* Load up the new values */
+	load_zsregs(info->zs_channel, info->curregs);
+
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	return 0;
+}
+
+static struct console sercons = {
+	.name		= "ttyS",
+	.write		= serial_console_write,
+	.device		= serial_console_device,
+	.setup		= serial_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+};
+
+/*
+ *	Register console.
+ */
+static void __init mac_scc_console_init(void)
+{
+	register_console(&sercons);
+}
+console_initcall(mac_scc_console_init);
+
+#endif /* ifdef CONFIG_SERIAL_CONSOLE */
+
+#ifdef CONFIG_KGDB
+/* These are for receiving and sending characters under the kgdb
+ * source level kernel debugger.
+ */
+void putDebugChar(char kgdb_char)
+{
+	struct mac_zschannel *chan = zs_kgdbchan;
+	while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0)
+		udelay(5);
+	write_zsdata(chan, kgdb_char);
+}
+
+char getDebugChar(void)
+{
+	struct mac_zschannel *chan = zs_kgdbchan;
+	while((read_zsreg(chan, 0) & Rx_CH_AV) == 0)
+		eieio(); /*barrier();*/
+	return read_zsdata(chan);
+}
+
+void kgdb_interruptible(int yes)
+{
+	struct mac_zschannel *chan = zs_kgdbchan;
+	int one, nine;
+	nine = read_zsreg(chan, 9);
+	if (yes == 1) {
+		one = EXT_INT_ENAB|INT_ALL_Rx;
+		nine |= MIE;
+		printk("turning serial ints on\n");
+	} else {
+		one = RxINT_DISAB;
+		nine &= ~MIE;
+		printk("turning serial ints off\n");
+	}
+	write_zsreg(chan, 1, one);
+	write_zsreg(chan, 9, nine);
+}
+
+/* This sets up the serial port we're using, and turns on
+ * interrupts for that channel, so kgdb is usable once we're done.
+ */
+static inline void kgdb_chaninit(struct mac_zschannel *ms, int intson, int bps)
+{
+	int brg;
+	int i, x;
+	volatile char *sccc = ms->control;
+	brg = BPS_TO_BRG(bps, ZS_CLOCK/16);
+	printk("setting bps on kgdb line to %d [brg=%x]\n", bps, brg);
+	for (i = 20000; i != 0; --i) {
+		x = *sccc; eieio();
+	}
+	for (i = 0; i < sizeof(scc_inittab); ++i) {
+		write_zsreg(ms, scc_inittab[i], scc_inittab[i+1]);
+		i++;
+	}
+}
+
+/* This is called at boot time to prime the kgdb serial debugging
+ * serial line.  The 'tty_num' argument is 0 for /dev/ttya and 1
+ * for /dev/ttyb which is determined in setup_arch() from the
+ * boot command line flags.
+ * XXX at the moment probably only channel A will work
+ */
+void __init zs_kgdb_hook(int tty_num)
+{
+	/* Find out how many Z8530 SCCs we have */
+	if (zs_chain == 0)
+		probe_sccs();
+
+	set_scc_power(&zs_soft[tty_num], 1);
+
+	zs_kgdbchan = zs_soft[tty_num].zs_channel;
+	zs_soft[tty_num].change_needed = 0;
+	zs_soft[tty_num].clk_divisor = 16;
+	zs_soft[tty_num].zs_baud = 38400;
+	zs_soft[tty_num].kgdb_channel = 1;     /* This runs kgdb */
+
+	/* Turn on transmitter/receiver at 8-bits/char */
+        kgdb_chaninit(zs_soft[tty_num].zs_channel, 1, 38400);
+	printk("KGDB: on channel %d initialized\n", tty_num);
+	set_debug_traps(); /* init stub */
+}
+#endif /* ifdef CONFIG_KGDB */
+
+#ifdef CONFIG_PMAC_PBOOK
+/*
+ * notify clients before sleep and reset bus afterwards
+ */
+int
+serial_notify_sleep(struct pmu_sleep_notifier *self, int when)
+{
+	int i;
+
+	switch (when) {
+	case PBOOK_SLEEP_REQUEST:
+	case PBOOK_SLEEP_REJECT:
+		break;
+
+	case PBOOK_SLEEP_NOW:
+		for (i=0; i<zs_channels_found; i++) {
+			struct mac_serial *info = &zs_soft[i];
+			if (info->flags & ZILOG_INITIALIZED) {
+				shutdown(info);
+				info->flags |= ZILOG_SLEEPING;
+			}
+		}
+		break;
+	case PBOOK_WAKE:
+		for (i=0; i<zs_channels_found; i++) {
+			struct mac_serial *info = &zs_soft[i];
+			if (info->flags & ZILOG_SLEEPING) {
+				info->flags &= ~ZILOG_SLEEPING;
+				startup(info);
+			}
+		}
+		break;
+	}
+	return PBOOK_SLEEP_OK;
+}
+#endif /* CONFIG_PMAC_PBOOK */
diff --git a/drivers/macintosh/macserial.h b/drivers/macintosh/macserial.h
new file mode 100644
index 0000000..bade11a
--- /dev/null
+++ b/drivers/macintosh/macserial.h
@@ -0,0 +1,461 @@
+/*
+ * macserial.h: Definitions for the Macintosh Z8530 serial driver.
+ *
+ * Adapted from drivers/sbus/char/sunserial.h by Paul Mackerras.
+ *
+ * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+#ifndef _MACSERIAL_H
+#define _MACSERIAL_H
+
+#include <linux/spinlock.h>
+
+#define NUM_ZSREGS    16
+
+struct serial_struct {
+	int	type;
+	int	line;
+	int	port;
+	int	irq;
+	int	flags;
+	int	xmit_fifo_size;
+	int	custom_divisor;
+	int	baud_base;
+	unsigned short	close_delay;
+	char	reserved_char[2];
+	int	hub6;
+	unsigned short	closing_wait; /* time to wait before closing */
+	unsigned short	closing_wait2; /* no longer used... */
+	int	reserved[4];
+};
+
+/*
+ * For the close wait times, 0 means wait forever for serial port to
+ * flush its output.  65535 means don't wait at all.
+ */
+#define ZILOG_CLOSING_WAIT_INF	0
+#define ZILOG_CLOSING_WAIT_NONE	65535
+
+/*
+ * Definitions for ZILOG_struct (and serial_struct) flags field
+ */
+#define ZILOG_HUP_NOTIFY	0x0001	/* Notify getty on hangups and closes 
+				   	 * on the callout port */
+#define ZILOG_FOURPORT 		0x0002	/* Set OU1, OUT2 per AST Fourport settings */
+#define ZILOG_SAK		0x0004	/* Secure Attention Key (Orange book) */
+#define ZILOG_SPLIT_TERMIOS	0x0008	/* Separate termios for dialin/callout */
+
+#define ZILOG_SPD_MASK		0x0030
+#define ZILOG_SPD_HI		0x0010	/* Use 56000 instead of 38400 bps */
+
+#define ZILOG_SPD_VHI		0x0020  /* Use 115200 instead of 38400 bps */
+#define ZILOG_SPD_CUST		0x0030  /* Use user-specified divisor */
+
+#define ZILOG_SKIP_TEST		0x0040	/* Skip UART test during autoconfiguration */
+#define ZILOG_AUTO_IRQ 		0x0080	/* Do automatic IRQ during autoconfiguration */
+#define ZILOG_SESSION_LOCKOUT	0x0100	/* Lock out cua opens based on session */
+#define ZILOG_PGRP_LOCKOUT	0x0200	/* Lock out cua opens based on pgrp */
+#define ZILOG_CALLOUT_NOHUP	0x0400	/* Don't do hangups for cua device */
+
+#define ZILOG_FLAGS		0x0FFF	/* Possible legal ZILOG flags */
+#define ZILOG_USR_MASK		0x0430	/* Legal flags that non-privileged
+					 * users can set or reset */
+
+/* Internal flags used only by kernel/chr_drv/serial.c */
+#define ZILOG_INITIALIZED	0x80000000 /* Serial port was initialized */
+#define ZILOG_CALLOUT_ACTIVE	0x40000000 /* Call out device is active */
+#define ZILOG_NORMAL_ACTIVE	0x20000000 /* Normal device is active */
+#define ZILOG_BOOT_AUTOCONF	0x10000000 /* Autoconfigure port on bootup */
+#define ZILOG_CLOSING		0x08000000 /* Serial port is closing */
+#define ZILOG_CTS_FLOW		0x04000000 /* Do CTS flow control */
+#define ZILOG_CHECK_CD		0x02000000 /* i.e., CLOCAL */
+#define ZILOG_SLEEPING		0x01000000 /* have shut it down for sleep */
+
+/* Software state per channel */
+
+#ifdef __KERNEL__
+/*
+ * This is our internal structure for each serial port's state.
+ * 
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+struct mac_serial;
+
+struct mac_zschannel {
+	volatile unsigned char*	control;
+	volatile unsigned char*	data;
+	spinlock_t		lock;
+	/* Used for debugging */
+	struct mac_serial*	parent;
+};
+
+struct mac_dma {
+	volatile struct dbdma_regs	dma;
+	volatile unsigned short		res_count;
+	volatile unsigned short		command;
+	volatile unsigned int		buf_addr;
+};
+
+struct mac_serial {
+	struct mac_serial *zs_next;	/* For IRQ servicing chain */
+	struct mac_zschannel *zs_channel; /* Channel registers */
+	struct mac_zschannel *zs_chan_a;	/* A side registers */
+	unsigned char read_reg_zero;
+	struct device_node* dev_node;
+	spinlock_t lock;
+
+	char soft_carrier;  /* Use soft carrier on this channel */
+	char break_abort;   /* Is serial console in, so process brk/abrt */
+	char kgdb_channel;  /* Kgdb is running on this channel */
+	char is_cons;       /* Is this our console. */
+	char is_internal_modem; /* is connected to an internal modem */
+	char is_irda;		/* is connected to an IrDA codec */
+	int port_type;		/* Port type for pmac_feature */
+	unsigned char tx_active; /* character is being xmitted */
+	unsigned char tx_stopped; /* output is suspended */
+	unsigned char power_wait; /* waiting for power-up delay to expire */
+
+	/* We need to know the current clock divisor
+	 * to read the bps rate the chip has currently
+	 * loaded.
+	 */
+	unsigned char clk_divisor;  /* May be 1, 16, 32, or 64 */
+	int zs_baud;
+
+	/* Current write register values */
+	unsigned char curregs[NUM_ZSREGS];
+
+	/* Values we need to set next opportunity */
+	unsigned char pendregs[NUM_ZSREGS];
+
+	char change_needed;
+
+	int			magic;
+	int			baud_base;
+	int			port;
+	int			irq;
+	int			flags; 		/* defined in tty.h */
+	int			type; 		/* UART type */
+	struct tty_struct 	*tty;
+	int			read_status_mask;
+	int			ignore_status_mask;
+	int			timeout;
+	int			xmit_fifo_size;
+	int			custom_divisor;
+	int			x_char;	/* xon/xoff character */
+	int			close_delay;
+	unsigned short		closing_wait;
+	unsigned short		closing_wait2;
+	unsigned long		event;
+	unsigned long		last_active;
+	int			line;
+	int			count;	    /* # of fd on device */
+	int			blocked_open; /* # of blocked opens */
+	unsigned char 		*xmit_buf;
+	int			xmit_head;
+	int			xmit_tail;
+	int			xmit_cnt;
+	struct work_struct	tqueue;
+	wait_queue_head_t	open_wait;
+	wait_queue_head_t	close_wait;
+
+	volatile struct dbdma_regs *tx_dma;
+	int			tx_dma_irq;
+	volatile struct dbdma_cmd *tx_cmds;
+	volatile struct mac_dma *rx;
+	int 			rx_dma_irq;
+	volatile struct dbdma_cmd **rx_cmds;
+	unsigned char		**rx_char_buf;
+	unsigned char		**rx_flag_buf;
+#define	RX_BUF_SIZE	256
+	int			rx_nbuf;
+	int			rx_done_bytes;
+	int			rx_ubuf;
+	int			rx_fbuf;
+#define	RX_NO_FBUF	(-1)
+	int			rx_cbuf;
+	spinlock_t		rx_dma_lock;
+	int			has_dma;
+	int			dma_initted;
+	void			*dma_priv;
+	struct timer_list	poll_dma_timer;
+#define RX_DMA_TIMER	(jiffies + 10*HZ/1000)
+
+	struct timer_list	powerup_timer;
+};
+
+
+#define SERIAL_MAGIC 0x5301
+
+/*
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
+ */
+#define SERIAL_XMIT_SIZE 4096
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP	0
+
+#endif /* __KERNEL__ */
+
+/* Conversion routines to/from brg time constants from/to bits
+ * per second.
+ */
+#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
+#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
+
+/* The Zilog register set */
+
+#define	FLAG	0x7e
+
+/* Write Register 0 */
+#define	R0	0		/* Register selects */
+#define	R1	1
+#define	R2	2
+#define	R3	3
+#define	R4	4
+#define	R5	5
+#define	R6	6
+#define	R7	7
+#define	R8	8
+#define	R9	9
+#define	R10	10
+#define	R11	11
+#define	R12	12
+#define	R13	13
+#define	R14	14
+#define	R15	15
+
+#define	NULLCODE	0	/* Null Code */
+#define	POINT_HIGH	0x8	/* Select upper half of registers */
+#define	RES_EXT_INT	0x10	/* Reset Ext. Status Interrupts */
+#define	SEND_ABORT	0x18	/* HDLC Abort */
+#define	RES_RxINT_FC	0x20	/* Reset RxINT on First Character */
+#define	RES_Tx_P	0x28	/* Reset TxINT Pending */
+#define	ERR_RES		0x30	/* Error Reset */
+#define	RES_H_IUS	0x38	/* Reset highest IUS */
+
+#define	RES_Rx_CRC	0x40	/* Reset Rx CRC Checker */
+#define	RES_Tx_CRC	0x80	/* Reset Tx CRC Checker */
+#define	RES_EOM_L	0xC0	/* Reset EOM latch */
+
+/* Write Register 1 */
+
+#define	EXT_INT_ENAB	0x1	/* Ext Int Enable */
+#define	TxINT_ENAB	0x2	/* Tx Int Enable */
+#define	PAR_SPEC	0x4	/* Parity is special condition */
+
+#define	RxINT_DISAB	0	/* Rx Int Disable */
+#define	RxINT_FCERR	0x8	/* Rx Int on First Character Only or Error */
+#define	INT_ALL_Rx	0x10	/* Int on all Rx Characters or error */
+#define	INT_ERR_Rx	0x18	/* Int on error only */
+
+#define	WT_RDY_RT	0x20	/* W/Req reflects recv if 1, xmit if 0 */
+#define	WT_FN_RDYFN	0x40	/* W/Req pin is DMA request if 1, wait if 0 */
+#define	WT_RDY_ENAB	0x80	/* Enable W/Req pin */
+
+/* Write Register #2 (Interrupt Vector) */
+
+/* Write Register 3 */
+
+#define	RxENABLE	0x1	/* Rx Enable */
+#define	SYNC_L_INH	0x2	/* Sync Character Load Inhibit */
+#define	ADD_SM		0x4	/* Address Search Mode (SDLC) */
+#define	RxCRC_ENAB	0x8	/* Rx CRC Enable */
+#define	ENT_HM		0x10	/* Enter Hunt Mode */
+#define	AUTO_ENAB	0x20	/* Auto Enables */
+#define	Rx5		0x0	/* Rx 5 Bits/Character */
+#define	Rx7		0x40	/* Rx 7 Bits/Character */
+#define	Rx6		0x80	/* Rx 6 Bits/Character */
+#define	Rx8		0xc0	/* Rx 8 Bits/Character */
+#define RxNBITS_MASK	0xc0
+
+/* Write Register 4 */
+
+#define	PAR_ENA		0x1	/* Parity Enable */
+#define	PAR_EVEN	0x2	/* Parity Even/Odd* */
+
+#define	SYNC_ENAB	0	/* Sync Modes Enable */
+#define	SB1		0x4	/* 1 stop bit/char */
+#define	SB15		0x8	/* 1.5 stop bits/char */
+#define	SB2		0xc	/* 2 stop bits/char */
+#define SB_MASK		0xc
+
+#define	MONSYNC		0	/* 8 Bit Sync character */
+#define	BISYNC		0x10	/* 16 bit sync character */
+#define	SDLC		0x20	/* SDLC Mode (01111110 Sync Flag) */
+#define	EXTSYNC		0x30	/* External Sync Mode */
+
+#define	X1CLK		0x0	/* x1 clock mode */
+#define	X16CLK		0x40	/* x16 clock mode */
+#define	X32CLK		0x80	/* x32 clock mode */
+#define	X64CLK		0xC0	/* x64 clock mode */
+#define XCLK_MASK	0xC0
+
+/* Write Register 5 */
+
+#define	TxCRC_ENAB	0x1	/* Tx CRC Enable */
+#define	RTS		0x2	/* RTS */
+#define	SDLC_CRC	0x4	/* SDLC/CRC-16 */
+#define	TxENAB		0x8	/* Tx Enable */
+#define	SND_BRK		0x10	/* Send Break */
+#define	Tx5		0x0	/* Tx 5 bits (or less)/character */
+#define	Tx7		0x20	/* Tx 7 bits/character */
+#define	Tx6		0x40	/* Tx 6 bits/character */
+#define	Tx8		0x60	/* Tx 8 bits/character */
+#define TxNBITS_MASK	0x60
+#define	DTR		0x80	/* DTR */
+
+/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
+
+/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
+
+/* Write Register 7' (Some enhanced feature control) */
+#define	ENEXREAD	0x40	/* Enable read of some write registers */
+
+/* Write Register 8 (transmit buffer) */
+
+/* Write Register 9 (Master interrupt control) */
+#define	VIS	1	/* Vector Includes Status */
+#define	NV	2	/* No Vector */
+#define	DLC	4	/* Disable Lower Chain */
+#define	MIE	8	/* Master Interrupt Enable */
+#define	STATHI	0x10	/* Status high */
+#define	NORESET	0	/* No reset on write to R9 */
+#define	CHRB	0x40	/* Reset channel B */
+#define	CHRA	0x80	/* Reset channel A */
+#define	FHWRES	0xc0	/* Force hardware reset */
+
+/* Write Register 10 (misc control bits) */
+#define	BIT6	1	/* 6 bit/8bit sync */
+#define	LOOPMODE 2	/* SDLC Loop mode */
+#define	ABUNDER	4	/* Abort/flag on SDLC xmit underrun */
+#define	MARKIDLE 8	/* Mark/flag on idle */
+#define	GAOP	0x10	/* Go active on poll */
+#define	NRZ	0	/* NRZ mode */
+#define	NRZI	0x20	/* NRZI mode */
+#define	FM1	0x40	/* FM1 (transition = 1) */
+#define	FM0	0x60	/* FM0 (transition = 0) */
+#define	CRCPS	0x80	/* CRC Preset I/O */
+
+/* Write Register 11 (Clock Mode control) */
+#define	TRxCXT	0	/* TRxC = Xtal output */
+#define	TRxCTC	1	/* TRxC = Transmit clock */
+#define	TRxCBR	2	/* TRxC = BR Generator Output */
+#define	TRxCDP	3	/* TRxC = DPLL output */
+#define	TRxCOI	4	/* TRxC O/I */
+#define	TCRTxCP	0	/* Transmit clock = RTxC pin */
+#define	TCTRxCP	8	/* Transmit clock = TRxC pin */
+#define	TCBR	0x10	/* Transmit clock = BR Generator output */
+#define	TCDPLL	0x18	/* Transmit clock = DPLL output */
+#define	RCRTxCP	0	/* Receive clock = RTxC pin */
+#define	RCTRxCP	0x20	/* Receive clock = TRxC pin */
+#define	RCBR	0x40	/* Receive clock = BR Generator output */
+#define	RCDPLL	0x60	/* Receive clock = DPLL output */
+#define	RTxCX	0x80	/* RTxC Xtal/No Xtal */
+
+/* Write Register 12 (lower byte of baud rate generator time constant) */
+
+/* Write Register 13 (upper byte of baud rate generator time constant) */
+
+/* Write Register 14 (Misc control bits) */
+#define	BRENABL	1	/* Baud rate generator enable */
+#define	BRSRC	2	/* Baud rate generator source */
+#define	DTRREQ	4	/* DTR/Request function */
+#define	AUTOECHO 8	/* Auto Echo */
+#define	LOOPBAK	0x10	/* Local loopback */
+#define	SEARCH	0x20	/* Enter search mode */
+#define	RMC	0x40	/* Reset missing clock */
+#define	DISDPLL	0x60	/* Disable DPLL */
+#define	SSBR	0x80	/* Set DPLL source = BR generator */
+#define	SSRTxC	0xa0	/* Set DPLL source = RTxC */
+#define	SFMM	0xc0	/* Set FM mode */
+#define	SNRZI	0xe0	/* Set NRZI mode */
+
+/* Write Register 15 (external/status interrupt control) */
+#define	EN85C30	1	/* Enable some 85c30-enhanced registers */
+#define	ZCIE	2	/* Zero count IE */
+#define	ENSTFIFO 4	/* Enable status FIFO (SDLC) */
+#define	DCDIE	8	/* DCD IE */
+#define	SYNCIE	0x10	/* Sync/hunt IE */
+#define	CTSIE	0x20	/* CTS IE */
+#define	TxUIE	0x40	/* Tx Underrun/EOM IE */
+#define	BRKIE	0x80	/* Break/Abort IE */
+
+
+/* Read Register 0 */
+#define	Rx_CH_AV	0x1	/* Rx Character Available */
+#define	ZCOUNT		0x2	/* Zero count */
+#define	Tx_BUF_EMP	0x4	/* Tx Buffer empty */
+#define	DCD		0x8	/* DCD */
+#define	SYNC_HUNT	0x10	/* Sync/hunt */
+#define	CTS		0x20	/* CTS */
+#define	TxEOM		0x40	/* Tx underrun */
+#define	BRK_ABRT	0x80	/* Break/Abort */
+
+/* Read Register 1 */
+#define	ALL_SNT		0x1	/* All sent */
+/* Residue Data for 8 Rx bits/char programmed */
+#define	RES3		0x8	/* 0/3 */
+#define	RES4		0x4	/* 0/4 */
+#define	RES5		0xc	/* 0/5 */
+#define	RES6		0x2	/* 0/6 */
+#define	RES7		0xa	/* 0/7 */
+#define	RES8		0x6	/* 0/8 */
+#define	RES18		0xe	/* 1/8 */
+#define	RES28		0x0	/* 2/8 */
+/* Special Rx Condition Interrupts */
+#define	PAR_ERR		0x10	/* Parity error */
+#define	Rx_OVR		0x20	/* Rx Overrun Error */
+#define	FRM_ERR		0x40	/* CRC/Framing Error */
+#define	END_FR		0x80	/* End of Frame (SDLC) */
+
+/* Read Register 2 (channel b only) - Interrupt vector */
+#define	CHB_Tx_EMPTY	0x00
+#define	CHB_EXT_STAT	0x02
+#define	CHB_Rx_AVAIL	0x04
+#define	CHB_SPECIAL	0x06
+#define	CHA_Tx_EMPTY	0x08
+#define	CHA_EXT_STAT	0x0a
+#define	CHA_Rx_AVAIL	0x0c
+#define	CHA_SPECIAL	0x0e
+#define	STATUS_MASK	0x06
+
+/* Read Register 3 (interrupt pending register) ch a only */
+#define	CHBEXT	0x1		/* Channel B Ext/Stat IP */
+#define	CHBTxIP	0x2		/* Channel B Tx IP */
+#define	CHBRxIP	0x4		/* Channel B Rx IP */
+#define	CHAEXT	0x8		/* Channel A Ext/Stat IP */
+#define	CHATxIP	0x10		/* Channel A Tx IP */
+#define	CHARxIP	0x20		/* Channel A Rx IP */
+
+/* Read Register 8 (receive data register) */
+
+/* Read Register 10  (misc status bits) */
+#define	ONLOOP	2		/* On loop */
+#define	LOOPSEND 0x10		/* Loop sending */
+#define	CLK2MIS	0x40		/* Two clocks missing */
+#define	CLK1MIS	0x80		/* One clock missing */
+
+/* Read Register 12 (lower byte of baud rate generator constant) */
+
+/* Read Register 13 (upper byte of baud rate generator constant) */
+
+/* Read Register 15 (value of WR 15) */
+
+/* Misc macros */
+#define ZS_CLEARERR(channel)    (write_zsreg(channel, 0, ERR_RES))
+#define ZS_CLEARFIFO(channel)   do { volatile unsigned char garbage; \
+				     garbage = read_zsdata(channel); \
+				     garbage = read_zsdata(channel); \
+				     garbage = read_zsdata(channel); \
+				} while(0)
+
+#endif /* !(_MACSERIAL_H) */
diff --git a/drivers/macintosh/mediabay.c b/drivers/macintosh/mediabay.c
new file mode 100644
index 0000000..b8b5a2f
--- /dev/null
+++ b/drivers/macintosh/mediabay.c
@@ -0,0 +1,851 @@
+/*
+ * Driver for the media bay on the PowerBook 3400 and 2400.
+ *
+ * Copyright (C) 1998 Paul Mackerras.
+ *
+ * Various evolutions by Benjamin Herrenschmidt & Henry Worth
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/hdreg.h>
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+#include <asm/prom.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/mediabay.h>
+#include <asm/sections.h>
+#include <asm/ohare.h>
+#include <asm/heathrow.h>
+#include <asm/keylargo.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+
+
+#define MB_DEBUG
+#define MB_IGNORE_SIGNALS
+
+#ifdef MB_DEBUG
+#define MBDBG(fmt, arg...)	printk(KERN_INFO fmt , ## arg)
+#else
+#define MBDBG(fmt, arg...)	do { } while (0)
+#endif
+
+#define MB_FCR32(bay, r)	((bay)->base + ((r) >> 2))
+#define MB_FCR8(bay, r)		(((volatile u8 __iomem *)((bay)->base)) + (r))
+
+#define MB_IN32(bay,r)		(in_le32(MB_FCR32(bay,r)))
+#define MB_OUT32(bay,r,v)	(out_le32(MB_FCR32(bay,r), (v)))
+#define MB_BIS(bay,r,v)		(MB_OUT32((bay), (r), MB_IN32((bay), r) | (v)))
+#define MB_BIC(bay,r,v)		(MB_OUT32((bay), (r), MB_IN32((bay), r) & ~(v)))
+#define MB_IN8(bay,r)		(in_8(MB_FCR8(bay,r)))
+#define MB_OUT8(bay,r,v)	(out_8(MB_FCR8(bay,r), (v)))
+
+struct media_bay_info;
+
+struct mb_ops {
+	char*	name;
+	void	(*init)(struct media_bay_info *bay);
+	u8	(*content)(struct media_bay_info *bay);
+	void	(*power)(struct media_bay_info *bay, int on_off);
+	int	(*setup_bus)(struct media_bay_info *bay, u8 device_id);
+	void	(*un_reset)(struct media_bay_info *bay);
+	void	(*un_reset_ide)(struct media_bay_info *bay);
+};
+
+struct media_bay_info {
+	u32 __iomem			*base;
+	int				content_id;
+	int				state;
+	int				last_value;
+	int				value_count;
+	int				timer;
+	struct macio_dev		*mdev;
+	struct mb_ops*			ops;
+	int				index;
+	int				cached_gpio;
+	int				sleeping;
+	struct semaphore		lock;
+#ifdef CONFIG_BLK_DEV_IDE
+	void __iomem			*cd_base;
+	int 				cd_index;
+	int				cd_irq;
+	int				cd_retry;
+#endif
+};
+
+#define MAX_BAYS	2
+
+static struct media_bay_info media_bays[MAX_BAYS];
+int media_bay_count = 0;
+
+#ifdef CONFIG_BLK_DEV_IDE
+/* check the busy bit in the media-bay ide interface
+   (assumes the media-bay contains an ide device) */
+#define MB_IDE_READY(i)	((readb(media_bays[i].cd_base + 0x70) & 0x80) == 0)
+#endif
+
+/*
+ * Wait that number of ms between each step in normal polling mode
+ */
+#define MB_POLL_DELAY	25
+
+/*
+ * Consider the media-bay ID value stable if it is the same for
+ * this number of milliseconds
+ */
+#define MB_STABLE_DELAY	100
+
+/* Wait after powering up the media bay this delay in ms
+ * timeout bumped for some powerbooks
+ */
+#define MB_POWER_DELAY	200
+
+/*
+ * Hold the media-bay reset signal true for this many ticks
+ * after a device is inserted before releasing it.
+ */
+#define MB_RESET_DELAY	50
+
+/*
+ * Wait this long after the reset signal is released and before doing
+ * further operations. After this delay, the IDE reset signal is released
+ * too for an IDE device
+ */
+#define MB_SETUP_DELAY	100
+
+/*
+ * Wait this many ticks after an IDE device (e.g. CD-ROM) is inserted
+ * (or until the device is ready) before waiting for busy bit to disappear
+ */
+#define MB_IDE_WAIT	1000
+
+/*
+ * Timeout waiting for busy bit of an IDE device to go down
+ */
+#define MB_IDE_TIMEOUT	5000
+
+/*
+ * Max retries of the full power up/down sequence for an IDE device
+ */
+#define MAX_CD_RETRIES	3
+
+/*
+ * States of a media bay
+ */
+enum {
+	mb_empty = 0,		/* Idle */
+	mb_powering_up,		/* power bit set, waiting MB_POWER_DELAY */
+	mb_enabling_bay,	/* enable bits set, waiting MB_RESET_DELAY */
+	mb_resetting,		/* reset bit unset, waiting MB_SETUP_DELAY */
+	mb_ide_resetting,	/* IDE reset bit unser, waiting MB_IDE_WAIT */
+	mb_ide_waiting,		/* Waiting for BUSY bit to go away until MB_IDE_TIMEOUT */
+	mb_up,			/* Media bay full */
+	mb_powering_down	/* Powering down (avoid too fast down/up) */
+};
+
+#define MB_POWER_SOUND		0x08
+#define MB_POWER_FLOPPY		0x04
+#define MB_POWER_ATA		0x02
+#define MB_POWER_PCI		0x01
+#define MB_POWER_OFF		0x00
+
+/*
+ * Functions for polling content of media bay
+ */
+ 
+static u8 __pmac
+ohare_mb_content(struct media_bay_info *bay)
+{
+	return (MB_IN32(bay, OHARE_MBCR) >> 12) & 7;
+}
+
+static u8 __pmac
+heathrow_mb_content(struct media_bay_info *bay)
+{
+	return (MB_IN32(bay, HEATHROW_MBCR) >> 12) & 7;
+}
+
+static u8 __pmac
+keylargo_mb_content(struct media_bay_info *bay)
+{
+	int new_gpio;
+
+	new_gpio = MB_IN8(bay, KL_GPIO_MEDIABAY_IRQ) & KEYLARGO_GPIO_INPUT_DATA;
+	if (new_gpio) {
+		bay->cached_gpio = new_gpio;
+		return MB_NO;
+	} else if (bay->cached_gpio != new_gpio) {
+		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_ENABLE);
+		(void)MB_IN32(bay, KEYLARGO_MBCR);
+		udelay(5);
+		MB_BIC(bay, KEYLARGO_MBCR, 0x0000000F);
+		(void)MB_IN32(bay, KEYLARGO_MBCR);
+		udelay(5);
+		bay->cached_gpio = new_gpio;
+	}
+	return (MB_IN32(bay, KEYLARGO_MBCR) >> 4) & 7;
+}
+
+/*
+ * Functions for powering up/down the bay, puts the bay device
+ * into reset state as well
+ */
+
+static void __pmac
+ohare_mb_power(struct media_bay_info* bay, int on_off)
+{
+	if (on_off) {
+		/* Power up device, assert it's reset line */
+		MB_BIC(bay, OHARE_FCR, OH_BAY_RESET_N);
+		MB_BIC(bay, OHARE_FCR, OH_BAY_POWER_N);
+	} else {
+		/* Disable all devices */
+		MB_BIC(bay, OHARE_FCR, OH_BAY_DEV_MASK);
+		MB_BIC(bay, OHARE_FCR, OH_FLOPPY_ENABLE);
+		/* Cut power from bay, release reset line */
+		MB_BIS(bay, OHARE_FCR, OH_BAY_POWER_N);
+		MB_BIS(bay, OHARE_FCR, OH_BAY_RESET_N);
+		MB_BIS(bay, OHARE_FCR, OH_IDE1_RESET_N);
+	}
+	MB_BIC(bay, OHARE_MBCR, 0x00000F00);
+}
+
+static void __pmac
+heathrow_mb_power(struct media_bay_info* bay, int on_off)
+{
+	if (on_off) {
+		/* Power up device, assert it's reset line */
+		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
+		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_POWER_N);
+	} else {
+		/* Disable all devices */
+		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_DEV_MASK);
+		MB_BIC(bay, HEATHROW_FCR, HRW_SWIM_ENABLE);
+		/* Cut power from bay, release reset line */
+		MB_BIS(bay, HEATHROW_FCR, HRW_BAY_POWER_N);
+		MB_BIS(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
+		MB_BIS(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
+	}
+	MB_BIC(bay, HEATHROW_MBCR, 0x00000F00);
+}
+
+static void __pmac
+keylargo_mb_power(struct media_bay_info* bay, int on_off)
+{
+	if (on_off) {
+		/* Power up device, assert it's reset line */
+            	MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
+            	MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_POWER);
+	} else {
+		/* Disable all devices */
+		MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_MASK);
+		MB_BIC(bay, KEYLARGO_FCR1, KL1_EIDE0_ENABLE);
+		/* Cut power from bay, release reset line */
+		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_POWER);
+		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
+		MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
+	}
+	MB_BIC(bay, KEYLARGO_MBCR, 0x0000000F);
+}
+
+/*
+ * Functions for configuring the media bay for a given type of device,
+ * enable the related busses
+ */
+
+static int __pmac
+ohare_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
+{
+	switch(device_id) {
+		case MB_FD:
+		case MB_FD1:
+			MB_BIS(bay, OHARE_FCR, OH_BAY_FLOPPY_ENABLE);
+			MB_BIS(bay, OHARE_FCR, OH_FLOPPY_ENABLE);
+			return 0;
+		case MB_CD:
+			MB_BIC(bay, OHARE_FCR, OH_IDE1_RESET_N);
+			MB_BIS(bay, OHARE_FCR, OH_BAY_IDE_ENABLE);
+			return 0;
+		case MB_PCI:
+			MB_BIS(bay, OHARE_FCR, OH_BAY_PCI_ENABLE);
+			return 0;
+	}
+	return -ENODEV;
+}
+
+static int __pmac
+heathrow_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
+{
+	switch(device_id) {
+		case MB_FD:
+		case MB_FD1:
+			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_FLOPPY_ENABLE);
+			MB_BIS(bay, HEATHROW_FCR, HRW_SWIM_ENABLE);
+			return 0;
+		case MB_CD:
+			MB_BIC(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
+			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_IDE_ENABLE);
+			return 0;
+		case MB_PCI:
+			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_PCI_ENABLE);
+			return 0;
+	}
+	return -ENODEV;
+}
+
+static int __pmac
+keylargo_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
+{
+	switch(device_id) {
+		case MB_CD:
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_IDE_ENABLE);
+			MB_BIC(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
+			MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_ENABLE);
+			return 0;
+		case MB_PCI:
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_PCI_ENABLE);
+			return 0;
+		case MB_SOUND:
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_SOUND_ENABLE);
+			return 0;
+	}
+	return -ENODEV;
+}
+
+/*
+ * Functions for tweaking resets
+ */
+
+static void __pmac
+ohare_mb_un_reset(struct media_bay_info* bay)
+{
+	MB_BIS(bay, OHARE_FCR, OH_BAY_RESET_N);
+}
+
+static void __pmac keylargo_mb_init(struct media_bay_info *bay)
+{
+	MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_ENABLE);
+}
+
+static void __pmac heathrow_mb_un_reset(struct media_bay_info* bay)
+{
+	MB_BIS(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
+}
+
+static void __pmac keylargo_mb_un_reset(struct media_bay_info* bay)
+{
+	MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
+}
+
+static void __pmac ohare_mb_un_reset_ide(struct media_bay_info* bay)
+{
+	MB_BIS(bay, OHARE_FCR, OH_IDE1_RESET_N);
+}
+
+static void __pmac heathrow_mb_un_reset_ide(struct media_bay_info* bay)
+{
+	MB_BIS(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
+}
+
+static void __pmac keylargo_mb_un_reset_ide(struct media_bay_info* bay)
+{
+	MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
+}
+
+static inline void __pmac set_mb_power(struct media_bay_info* bay, int onoff)
+{
+	/* Power up up and assert the bay reset line */
+	if (onoff) {
+		bay->ops->power(bay, 1);
+		bay->state = mb_powering_up;
+		MBDBG("mediabay%d: powering up\n", bay->index);
+	} else { 
+		/* Make sure everything is powered down & disabled */
+		bay->ops->power(bay, 0);
+		bay->state = mb_powering_down;
+		MBDBG("mediabay%d: powering down\n", bay->index);
+	}
+	bay->timer = msecs_to_jiffies(MB_POWER_DELAY);
+}
+
+static void __pmac poll_media_bay(struct media_bay_info* bay)
+{
+	int id = bay->ops->content(bay);
+
+	if (id == bay->last_value) {
+		if (id != bay->content_id) {
+			bay->value_count += msecs_to_jiffies(MB_POLL_DELAY);
+			if (bay->value_count >= msecs_to_jiffies(MB_STABLE_DELAY)) {
+				/* If the device type changes without going thru
+				 * "MB_NO", we force a pass by "MB_NO" to make sure
+				 * things are properly reset
+				 */
+				if ((id != MB_NO) && (bay->content_id != MB_NO)) {
+					id = MB_NO;
+					MBDBG("mediabay%d: forcing MB_NO\n", bay->index);
+				}
+				MBDBG("mediabay%d: switching to %d\n", bay->index, id);
+				set_mb_power(bay, id != MB_NO);
+				bay->content_id = id;
+				if (id == MB_NO) {
+#ifdef CONFIG_BLK_DEV_IDE
+					bay->cd_retry = 0;
+#endif
+					printk(KERN_INFO "media bay %d is empty\n", bay->index);
+				}
+			}
+		}
+	} else {
+		bay->last_value = id;
+		bay->value_count = 0;
+	}
+}
+
+int __pmac check_media_bay(struct device_node *which_bay, int what)
+{
+#ifdef CONFIG_BLK_DEV_IDE
+	int	i;
+
+	for (i=0; i<media_bay_count; i++)
+		if (media_bays[i].mdev && which_bay == media_bays[i].mdev->ofdev.node) {
+			if ((what == media_bays[i].content_id) && media_bays[i].state == mb_up)
+				return 0;
+			media_bays[i].cd_index = -1;
+			return -EINVAL;
+		}
+#endif /* CONFIG_BLK_DEV_IDE */
+	return -ENODEV;
+}
+EXPORT_SYMBOL(check_media_bay);
+
+int __pmac check_media_bay_by_base(unsigned long base, int what)
+{
+#ifdef CONFIG_BLK_DEV_IDE
+	int	i;
+
+	for (i=0; i<media_bay_count; i++)
+		if (media_bays[i].mdev && base == (unsigned long) media_bays[i].cd_base) {
+			if ((what == media_bays[i].content_id) && media_bays[i].state == mb_up)
+				return 0;
+			media_bays[i].cd_index = -1;
+			return -EINVAL;
+		} 
+#endif
+	
+	return -ENODEV;
+}
+
+int __pmac media_bay_set_ide_infos(struct device_node* which_bay, unsigned long base,
+	int irq, int index)
+{
+#ifdef CONFIG_BLK_DEV_IDE
+	int	i;
+
+	for (i=0; i<media_bay_count; i++) {
+		struct media_bay_info* bay = &media_bays[i];
+
+		if (bay->mdev && which_bay == bay->mdev->ofdev.node) {
+			int timeout = 5000;
+			
+			down(&bay->lock);
+
+ 			bay->cd_base	= (void __iomem *) base;
+			bay->cd_irq	= irq;
+
+			if ((MB_CD != bay->content_id) || bay->state != mb_up) {
+				up(&bay->lock);
+				return 0;
+			}
+			printk(KERN_DEBUG "Registered ide%d for media bay %d\n", index, i);
+			do {
+				if (MB_IDE_READY(i)) {
+					bay->cd_index	= index;
+					up(&bay->lock);
+					return 0;
+				}
+				mdelay(1);
+			} while(--timeout);
+			printk(KERN_DEBUG "Timeount waiting IDE in bay %d\n", i);
+			up(&bay->lock);
+			return -ENODEV;
+		}
+	}
+#endif /* CONFIG_BLK_DEV_IDE */
+	
+	return -ENODEV;
+}
+
+static void __pmac media_bay_step(int i)
+{
+	struct media_bay_info* bay = &media_bays[i];
+
+	/* We don't poll when powering down */
+	if (bay->state != mb_powering_down)
+	    poll_media_bay(bay);
+
+	/* If timer expired or polling IDE busy, run state machine */
+	if ((bay->state != mb_ide_waiting) && (bay->timer != 0)) {
+		bay->timer -= msecs_to_jiffies(MB_POLL_DELAY);
+		if (bay->timer > 0)
+			return;
+		bay->timer = 0;
+	}
+
+	switch(bay->state) {
+	case mb_powering_up:
+	    	if (bay->ops->setup_bus(bay, bay->last_value) < 0) {
+			MBDBG("mediabay%d: device not supported (kind:%d)\n", i, bay->content_id);
+	    		set_mb_power(bay, 0);
+	    		break;
+	    	}
+	    	bay->timer = msecs_to_jiffies(MB_RESET_DELAY);
+	    	bay->state = mb_enabling_bay;
+		MBDBG("mediabay%d: enabling (kind:%d)\n", i, bay->content_id);
+		break;
+	case mb_enabling_bay:
+		bay->ops->un_reset(bay);
+	    	bay->timer = msecs_to_jiffies(MB_SETUP_DELAY);
+	    	bay->state = mb_resetting;
+		MBDBG("mediabay%d: waiting reset (kind:%d)\n", i, bay->content_id);
+	    	break;
+	    
+	case mb_resetting:
+		if (bay->content_id != MB_CD) {
+			MBDBG("mediabay%d: bay is up (kind:%d)\n", i, bay->content_id);
+			bay->state = mb_up;
+			break;
+	    	}
+#ifdef CONFIG_BLK_DEV_IDE
+		MBDBG("mediabay%d: waiting IDE reset (kind:%d)\n", i, bay->content_id);
+		bay->ops->un_reset_ide(bay);
+	    	bay->timer = msecs_to_jiffies(MB_IDE_WAIT);
+	    	bay->state = mb_ide_resetting;
+#else
+		printk(KERN_DEBUG "media-bay %d is ide (not compiled in kernel)\n", i);
+		set_mb_power(bay, 0);
+#endif /* CONFIG_BLK_DEV_IDE */
+	    	break;
+	    
+#ifdef CONFIG_BLK_DEV_IDE
+	case mb_ide_resetting:
+	    	bay->timer = msecs_to_jiffies(MB_IDE_TIMEOUT);
+	    	bay->state = mb_ide_waiting;
+		MBDBG("mediabay%d: waiting IDE ready (kind:%d)\n", i, bay->content_id);
+	    	break;
+	    
+	case mb_ide_waiting:
+		if (bay->cd_base == NULL) {
+			bay->timer = 0;
+			bay->state = mb_up;
+			MBDBG("mediabay%d: up before IDE init\n", i);
+			break;
+		} else if (MB_IDE_READY(i)) {
+			bay->timer = 0;
+			bay->state = mb_up;
+			if (bay->cd_index < 0) {
+				hw_regs_t hw;
+
+				printk("mediabay %d, registering IDE...\n", i);
+				pmu_suspend();
+				ide_init_hwif_ports(&hw, (unsigned long) bay->cd_base, (unsigned long) 0, NULL);
+				hw.irq = bay->cd_irq;
+				hw.chipset = ide_pmac;
+				bay->cd_index = ide_register_hw(&hw, NULL);
+				pmu_resume();
+			}
+			if (bay->cd_index == -1) {
+				/* We eventually do a retry */
+				bay->cd_retry++;
+				printk("IDE register error\n");
+				set_mb_power(bay, 0);
+			} else {
+				printk(KERN_DEBUG "media-bay %d is ide%d\n", i, bay->cd_index);
+				MBDBG("mediabay %d IDE ready\n", i);
+			}
+			break;
+	    	} else if (bay->timer > 0)
+			bay->timer -= msecs_to_jiffies(MB_POLL_DELAY);
+	    	if (bay->timer <= 0) {
+			printk("\nIDE Timeout in bay %d !, IDE state is: 0x%02x\n",
+			       i, readb(bay->cd_base + 0x70));
+			MBDBG("mediabay%d: nIDE Timeout !\n", i);
+			set_mb_power(bay, 0);
+			bay->timer = 0;
+	    	}
+		break;
+#endif /* CONFIG_BLK_DEV_IDE */
+
+	case mb_powering_down:
+	    	bay->state = mb_empty;
+#ifdef CONFIG_BLK_DEV_IDE
+    	        if (bay->cd_index >= 0) {
+			printk(KERN_DEBUG "Unregistering mb %d ide, index:%d\n", i,
+			       bay->cd_index);
+			ide_unregister(bay->cd_index);
+			bay->cd_index = -1;
+		}
+	    	if (bay->cd_retry) {
+			if (bay->cd_retry > MAX_CD_RETRIES) {
+				/* Should add an error sound (sort of beep in dmasound) */
+				printk("\nmedia-bay %d, IDE device badly inserted or unrecognised\n", i);
+			} else {
+				/* Force a new power down/up sequence */
+				bay->content_id = MB_NO;
+			}
+	    	}
+#endif /* CONFIG_BLK_DEV_IDE */    
+		MBDBG("mediabay%d: end of power down\n", i);
+	    	break;
+	}
+}
+
+/*
+ * This procedure runs as a kernel thread to poll the media bay
+ * once each tick and register and unregister the IDE interface
+ * with the IDE driver.  It needs to be a thread because
+ * ide_register can't be called from interrupt context.
+ */
+static int __pmac media_bay_task(void *x)
+{
+	int	i;
+
+	strcpy(current->comm, "media-bay");
+#ifdef MB_IGNORE_SIGNALS
+	sigfillset(&current->blocked);
+#endif
+
+	for (;;) {
+		for (i = 0; i < media_bay_count; ++i) {
+			down(&media_bays[i].lock);
+			if (!media_bays[i].sleeping)
+				media_bay_step(i);
+			up(&media_bays[i].lock);
+		}
+
+		msleep_interruptible(MB_POLL_DELAY);
+		if (signal_pending(current))
+			return 0;
+	}
+}
+
+static int __devinit media_bay_attach(struct macio_dev *mdev, const struct of_match *match)
+{
+	struct media_bay_info* bay;
+	u32 __iomem *regbase;
+	struct device_node *ofnode;
+	int i;
+
+	ofnode = mdev->ofdev.node;
+
+	if (macio_resource_count(mdev) < 1)
+		return -ENODEV;
+	if (macio_request_resources(mdev, "media-bay"))
+		return -EBUSY;
+	/* Media bay registers are located at the beginning of the
+         * mac-io chip, we get the parent address for now (hrm...)
+         */
+	regbase = (u32 __iomem *)
+		ioremap(ofnode->parent->addrs[0].address, 0x100);
+	if (regbase == NULL) {
+		macio_release_resources(mdev);
+		return -ENOMEM;
+	}
+	
+	i = media_bay_count++;
+	bay = &media_bays[i];
+	bay->mdev = mdev;
+	bay->base = regbase;
+	bay->index = i;
+	bay->ops = match->data;
+	bay->sleeping = 0;
+	init_MUTEX(&bay->lock);
+
+	/* Init HW probing */
+	if (bay->ops->init)
+		bay->ops->init(bay);
+
+	printk(KERN_INFO "mediabay%d: Registered %s media-bay\n", i, bay->ops->name);
+
+	/* Force an immediate detect */
+	set_mb_power(bay, 0);
+	msleep(MB_POWER_DELAY);
+	bay->content_id = MB_NO;
+	bay->last_value = bay->ops->content(bay);
+	bay->value_count = msecs_to_jiffies(MB_STABLE_DELAY);
+	bay->state = mb_empty;
+	do {
+		msleep(MB_POLL_DELAY);
+		media_bay_step(i);
+	} while((bay->state != mb_empty) &&
+		(bay->state != mb_up));
+
+	/* Mark us ready by filling our mdev data */
+	macio_set_drvdata(mdev, bay);
+
+	/* Startup kernel thread */
+	if (i == 0)
+		kernel_thread(media_bay_task, NULL, CLONE_KERNEL);
+
+	return 0;
+
+}
+
+static int __pmac media_bay_suspend(struct macio_dev *mdev, u32 state)
+{
+	struct media_bay_info	*bay = macio_get_drvdata(mdev);
+
+	if (state != mdev->ofdev.dev.power.power_state && state == PM_SUSPEND_MEM) {
+		down(&bay->lock);
+		bay->sleeping = 1;
+		set_mb_power(bay, 0);
+		up(&bay->lock);
+		msleep(MB_POLL_DELAY);
+		mdev->ofdev.dev.power.power_state = state;
+	}
+	return 0;
+}
+
+static int __pmac media_bay_resume(struct macio_dev *mdev)
+{
+	struct media_bay_info	*bay = macio_get_drvdata(mdev);
+
+	if (mdev->ofdev.dev.power.power_state != 0) {
+		mdev->ofdev.dev.power.power_state = 0;
+
+	       	/* We re-enable the bay using it's previous content
+	       	   only if it did not change. Note those bozo timings,
+	       	   they seem to help the 3400 get it right.
+	       	 */
+	       	/* Force MB power to 0 */
+		down(&bay->lock);
+	       	set_mb_power(bay, 0);
+		msleep(MB_POWER_DELAY);
+	       	if (bay->ops->content(bay) != bay->content_id) {
+			printk("mediabay%d: content changed during sleep...\n", bay->index);
+			up(&bay->lock);
+	       		return 0;
+		}
+	       	set_mb_power(bay, 1);
+	       	bay->last_value = bay->content_id;
+	       	bay->value_count = msecs_to_jiffies(MB_STABLE_DELAY);
+	       	bay->timer = msecs_to_jiffies(MB_POWER_DELAY);
+#ifdef CONFIG_BLK_DEV_IDE
+	       	bay->cd_retry = 0;
+#endif
+	       	do {
+			msleep(MB_POLL_DELAY);
+	       		media_bay_step(bay->index);
+	       	} while((bay->state != mb_empty) &&
+	       		(bay->state != mb_up));
+		bay->sleeping = 0;
+		up(&bay->lock);
+	}
+	return 0;
+}
+
+
+/* Definitions of "ops" structures.
+ */
+static struct mb_ops ohare_mb_ops __pmacdata = {
+	.name		= "Ohare",
+	.content	= ohare_mb_content,
+	.power		= ohare_mb_power,
+	.setup_bus	= ohare_mb_setup_bus,
+	.un_reset	= ohare_mb_un_reset,
+	.un_reset_ide	= ohare_mb_un_reset_ide,
+};
+
+static struct mb_ops heathrow_mb_ops __pmacdata = {
+	.name		= "Heathrow",
+	.content	= heathrow_mb_content,
+	.power		= heathrow_mb_power,
+	.setup_bus	= heathrow_mb_setup_bus,
+	.un_reset	= heathrow_mb_un_reset,
+	.un_reset_ide	= heathrow_mb_un_reset_ide,
+};
+
+static struct mb_ops keylargo_mb_ops __pmacdata = {
+	.name		= "KeyLargo",
+	.init		= keylargo_mb_init,
+	.content	= keylargo_mb_content,
+	.power		= keylargo_mb_power,
+	.setup_bus	= keylargo_mb_setup_bus,
+	.un_reset	= keylargo_mb_un_reset,
+	.un_reset_ide	= keylargo_mb_un_reset_ide,
+};
+
+/*
+ * It seems that the bit for the media-bay interrupt in the IRQ_LEVEL
+ * register is always set when there is something in the media bay.
+ * This causes problems for the interrupt code if we attach an interrupt
+ * handler to the media-bay interrupt, because it tends to go into
+ * an infinite loop calling the media bay interrupt handler.
+ * Therefore we do it all by polling the media bay once each tick.
+ */
+
+static struct of_match media_bay_match[] =
+{
+	{
+	.name		= "media-bay",
+	.type		= OF_ANY_MATCH,
+	.compatible	= "keylargo-media-bay",
+	.data		= &keylargo_mb_ops,
+	},
+	{
+	.name		= "media-bay",
+	.type		= OF_ANY_MATCH,
+	.compatible	= "heathrow-media-bay",
+	.data		= &heathrow_mb_ops,
+	},
+	{
+	.name		= "media-bay",
+	.type		= OF_ANY_MATCH,
+	.compatible	= "ohare-media-bay",
+	.data		= &ohare_mb_ops,
+	},
+	{},
+};
+
+static struct macio_driver media_bay_driver =
+{
+	.name		= "media-bay",
+	.match_table	= media_bay_match,
+	.probe		= media_bay_attach,
+	.suspend	= media_bay_suspend,
+	.resume		= media_bay_resume
+};
+
+static int __init media_bay_init(void)
+{
+	int i;
+
+	for (i=0; i<MAX_BAYS; i++) {
+		memset((char *)&media_bays[i], 0, sizeof(struct media_bay_info));
+		media_bays[i].content_id	= -1;
+#ifdef CONFIG_BLK_DEV_IDE
+		media_bays[i].cd_index		= -1;
+#endif
+	}
+	if (_machine != _MACH_Pmac)
+		return -ENODEV;
+
+	macio_register_driver(&media_bay_driver);	
+
+	return 0;
+}
+
+device_initcall(media_bay_init);
diff --git a/drivers/macintosh/nvram.c b/drivers/macintosh/nvram.c
new file mode 100644
index 0000000..3079187
--- /dev/null
+++ b/drivers/macintosh/nvram.c
@@ -0,0 +1,131 @@
+/*
+ * /dev/nvram driver for Power Macintosh.
+ */
+
+#define NVRAM_VERSION "1.0"
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/fcntl.h>
+#include <linux/nvram.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <asm/uaccess.h>
+#include <asm/nvram.h>
+
+#define NVRAM_SIZE	8192
+
+static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
+{
+	lock_kernel();
+	switch (origin) {
+	case 1:
+		offset += file->f_pos;
+		break;
+	case 2:
+		offset += NVRAM_SIZE;
+		break;
+	}
+	if (offset < 0) {
+		unlock_kernel();
+		return -EINVAL;
+	}
+	file->f_pos = offset;
+	unlock_kernel();
+	return file->f_pos;
+}
+
+static ssize_t read_nvram(struct file *file, char __user *buf,
+			  size_t count, loff_t *ppos)
+{
+	unsigned int i;
+	char __user *p = buf;
+
+	if (!access_ok(VERIFY_WRITE, buf, count))
+		return -EFAULT;
+	if (*ppos >= NVRAM_SIZE)
+		return 0;
+	for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count)
+		if (__put_user(nvram_read_byte(i), p))
+			return -EFAULT;
+	*ppos = i;
+	return p - buf;
+}
+
+static ssize_t write_nvram(struct file *file, const char __user *buf,
+			   size_t count, loff_t *ppos)
+{
+	unsigned int i;
+	const char __user *p = buf;
+	char c;
+
+	if (!access_ok(VERIFY_READ, buf, count))
+		return -EFAULT;
+	if (*ppos >= NVRAM_SIZE)
+		return 0;
+	for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) {
+		if (__get_user(c, p))
+			return -EFAULT;
+		nvram_write_byte(c, i);
+	}
+	*ppos = i;
+	return p - buf;
+}
+
+static int nvram_ioctl(struct inode *inode, struct file *file,
+	unsigned int cmd, unsigned long arg)
+{
+	switch(cmd) {
+		case PMAC_NVRAM_GET_OFFSET:
+		{
+			int part, offset;
+			if (copy_from_user(&part, (void __user*)arg, sizeof(part)) != 0)
+				return -EFAULT;
+			if (part < pmac_nvram_OF || part > pmac_nvram_NR)
+				return -EINVAL;
+			offset = pmac_get_partition(part);
+			if (copy_to_user((void __user*)arg, &offset, sizeof(offset)) != 0)
+				return -EFAULT;
+			break;
+		}
+
+		default:
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+struct file_operations nvram_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= nvram_llseek,
+	.read		= read_nvram,
+	.write		= write_nvram,
+	.ioctl		= nvram_ioctl,
+};
+
+static struct miscdevice nvram_dev = {
+	NVRAM_MINOR,
+	"nvram",
+	&nvram_fops
+};
+
+int __init nvram_init(void)
+{
+	printk(KERN_INFO "Macintosh non-volatile memory driver v%s\n",
+		NVRAM_VERSION);
+	return misc_register(&nvram_dev);
+}
+
+void __exit nvram_cleanup(void)
+{
+        misc_deregister( &nvram_dev );
+}
+
+module_init(nvram_init);
+module_exit(nvram_cleanup);
+MODULE_LICENSE("GPL");
diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c
new file mode 100644
index 0000000..fb53573
--- /dev/null
+++ b/drivers/macintosh/smu.c
@@ -0,0 +1,364 @@
+/*
+ * PowerMac G5 SMU driver
+ *
+ * Copyright 2004 J. Mayer <l_indien@magic.fr>
+ * Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * Released under the term of the GNU GPL v2.
+ */
+
+/*
+ * For now, this driver includes:
+ * - RTC get & set
+ * - reboot & shutdown commands
+ * all synchronous with IRQ disabled (ugh)
+ *
+ * TODO:
+ *   rework in a way the PMU driver works, that is asynchronous
+ *   with a queue of commands. I'll do that as soon as I have an
+ *   SMU based machine at hand. Some more cleanup is needed too,
+ *   like maybe fitting it into a platform device, etc...
+ *   Also check what's up with cache coherency, and if we really
+ *   can't do better than flushing the cache, maybe build a table
+ *   of command len/reply len like the PMU driver to only flush
+ *   what is actually necessary.
+ *   --BenH.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/bootmem.h>
+#include <linux/vmalloc.h>
+#include <linux/highmem.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/smu.h>
+#include <asm/sections.h>
+#include <asm/abs_addr.h>
+
+#define DEBUG_SMU 1
+
+#ifdef DEBUG_SMU
+#define DPRINTK(fmt, args...) do { printk(KERN_DEBUG fmt , ##args); } while (0)
+#else
+#define DPRINTK(fmt, args...) do { } while (0)
+#endif
+
+/*
+ * This is the command buffer passed to the SMU hardware
+ */
+struct smu_cmd_buf {
+	u8 cmd;
+	u8 length;
+	u8 data[0x0FFE];
+};
+
+struct smu_device {
+	spinlock_t		lock;
+	struct device_node	*of_node;
+	int			db_ack;		/* doorbell ack GPIO */
+	int			db_req;		/* doorbell req GPIO */
+	u32 __iomem		*db_buf;	/* doorbell buffer */
+	struct smu_cmd_buf	*cmd_buf;	/* command buffer virtual */
+	u32			cmd_buf_abs;	/* command buffer absolute */
+};
+
+/*
+ * I don't think there will ever be more than one SMU, so
+ * for now, just hard code that
+ */
+static struct smu_device	*smu;
+
+/*
+ * SMU low level communication stuff
+ */
+static inline int smu_cmd_stat(struct smu_cmd_buf *cmd_buf, u8 cmd_ack)
+{
+	rmb();
+	return cmd_buf->cmd == cmd_ack && cmd_buf->length != 0;
+}
+
+static inline u8 smu_save_ack_cmd(struct smu_cmd_buf *cmd_buf)
+{
+	return (~cmd_buf->cmd) & 0xff;
+}
+
+static void smu_send_cmd(struct smu_device *dev)
+{
+	/* SMU command buf is currently cacheable, we need a physical
+	 * address. This isn't exactly a DMA mapping here, I suspect
+	 * the SMU is actually communicating with us via i2c to the
+	 * northbridge or the CPU to access RAM.
+	 */
+	writel(dev->cmd_buf_abs, dev->db_buf);
+
+	/* Ring the SMU doorbell */
+	pmac_do_feature_call(PMAC_FTR_WRITE_GPIO, NULL, dev->db_req, 4);
+	pmac_do_feature_call(PMAC_FTR_READ_GPIO, NULL, dev->db_req, 4);
+}
+
+static int smu_cmd_done(struct smu_device *dev)
+{
+	unsigned long wait = 0;
+	int gpio;
+
+	/* Check the SMU doorbell */
+	do  {
+		gpio = pmac_do_feature_call(PMAC_FTR_READ_GPIO,
+					    NULL, dev->db_ack);
+		if ((gpio & 7) == 7)
+			return 0;
+		udelay(100);
+	} while(++wait < 10000);
+
+	printk(KERN_ERR "SMU timeout !\n");
+	return -ENXIO;
+}
+
+static int smu_do_cmd(struct smu_device *dev)
+{
+	int rc;
+	u8 cmd_ack;
+
+	DPRINTK("SMU do_cmd %02x len=%d %02x\n",
+		dev->cmd_buf->cmd, dev->cmd_buf->length,
+		dev->cmd_buf->data[0]);
+
+	cmd_ack = smu_save_ack_cmd(dev->cmd_buf);
+
+	/* Clear cmd_buf cache lines */
+	flush_inval_dcache_range((unsigned long)dev->cmd_buf,
+				 ((unsigned long)dev->cmd_buf) +
+				 sizeof(struct smu_cmd_buf));
+	smu_send_cmd(dev);
+	rc = smu_cmd_done(dev);
+	if (rc == 0)
+		rc = smu_cmd_stat(dev->cmd_buf, cmd_ack) ? 0 : -1;
+
+	DPRINTK("SMU do_cmd %02x len=%d %02x => %d (%02x)\n",
+		dev->cmd_buf->cmd, dev->cmd_buf->length,
+		dev->cmd_buf->data[0], rc, cmd_ack);
+
+	return rc;
+}
+
+/* RTC low level commands */
+static inline int bcd2hex (int n)
+{
+	return (((n & 0xf0) >> 4) * 10) + (n & 0xf);
+}
+
+static inline int hex2bcd (int n)
+{
+	return ((n / 10) << 4) + (n % 10);
+}
+
+#if 0
+static inline void smu_fill_set_pwrup_timer_cmd(struct smu_cmd_buf *cmd_buf)
+{
+	cmd_buf->cmd = 0x8e;
+	cmd_buf->length = 8;
+	cmd_buf->data[0] = 0x00;
+	memset(cmd_buf->data + 1, 0, 7);
+}
+
+static inline void smu_fill_get_pwrup_timer_cmd(struct smu_cmd_buf *cmd_buf)
+{
+	cmd_buf->cmd = 0x8e;
+	cmd_buf->length = 1;
+	cmd_buf->data[0] = 0x01;
+}
+
+static inline void smu_fill_dis_pwrup_timer_cmd(struct smu_cmd_buf *cmd_buf)
+{
+	cmd_buf->cmd = 0x8e;
+	cmd_buf->length = 1;
+	cmd_buf->data[0] = 0x02;
+}
+#endif
+
+static inline void smu_fill_set_rtc_cmd(struct smu_cmd_buf *cmd_buf,
+					struct rtc_time *time)
+{
+	cmd_buf->cmd = 0x8e;
+	cmd_buf->length = 8;
+	cmd_buf->data[0] = 0x80;
+	cmd_buf->data[1] = hex2bcd(time->tm_sec);
+	cmd_buf->data[2] = hex2bcd(time->tm_min);
+	cmd_buf->data[3] = hex2bcd(time->tm_hour);
+	cmd_buf->data[4] = time->tm_wday;
+	cmd_buf->data[5] = hex2bcd(time->tm_mday);
+	cmd_buf->data[6] = hex2bcd(time->tm_mon) + 1;
+	cmd_buf->data[7] = hex2bcd(time->tm_year - 100);
+}
+
+static inline void smu_fill_get_rtc_cmd(struct smu_cmd_buf *cmd_buf)
+{
+	cmd_buf->cmd = 0x8e;
+	cmd_buf->length = 1;
+	cmd_buf->data[0] = 0x81;
+}
+
+static void smu_parse_get_rtc_reply(struct smu_cmd_buf *cmd_buf,
+				    struct rtc_time *time)
+{
+	time->tm_sec = bcd2hex(cmd_buf->data[0]);
+	time->tm_min = bcd2hex(cmd_buf->data[1]);
+	time->tm_hour = bcd2hex(cmd_buf->data[2]);
+	time->tm_wday = bcd2hex(cmd_buf->data[3]);
+	time->tm_mday = bcd2hex(cmd_buf->data[4]);
+	time->tm_mon = bcd2hex(cmd_buf->data[5]) - 1;
+	time->tm_year = bcd2hex(cmd_buf->data[6]) + 100;
+}
+
+int smu_get_rtc_time(struct rtc_time *time)
+{
+	unsigned long flags;
+	int rc;
+
+	if (smu == NULL)
+		return -ENODEV;
+
+	memset(time, 0, sizeof(struct rtc_time));
+	spin_lock_irqsave(&smu->lock, flags);
+	smu_fill_get_rtc_cmd(smu->cmd_buf);
+	rc = smu_do_cmd(smu);
+	if (rc == 0)
+		smu_parse_get_rtc_reply(smu->cmd_buf, time);
+	spin_unlock_irqrestore(&smu->lock, flags);
+
+	return rc;
+}
+
+int smu_set_rtc_time(struct rtc_time *time)
+{
+	unsigned long flags;
+	int rc;
+
+	if (smu == NULL)
+		return -ENODEV;
+
+	spin_lock_irqsave(&smu->lock, flags);
+	smu_fill_set_rtc_cmd(smu->cmd_buf, time);
+	rc = smu_do_cmd(smu);
+	spin_unlock_irqrestore(&smu->lock, flags);
+
+	return rc;
+}
+
+void smu_shutdown(void)
+{
+	const unsigned char *command = "SHUTDOWN";
+	unsigned long flags;
+
+	if (smu == NULL)
+		return;
+
+	spin_lock_irqsave(&smu->lock, flags);
+	smu->cmd_buf->cmd = 0xaa;
+	smu->cmd_buf->length = strlen(command);
+	strcpy(smu->cmd_buf->data, command);
+	smu_do_cmd(smu);
+	for (;;)
+		;
+	spin_unlock_irqrestore(&smu->lock, flags);
+}
+
+void smu_restart(void)
+{
+	const unsigned char *command = "RESTART";
+	unsigned long flags;
+
+	if (smu == NULL)
+		return;
+
+	spin_lock_irqsave(&smu->lock, flags);
+	smu->cmd_buf->cmd = 0xaa;
+	smu->cmd_buf->length = strlen(command);
+	strcpy(smu->cmd_buf->data, command);
+	smu_do_cmd(smu);
+	for (;;)
+		;
+	spin_unlock_irqrestore(&smu->lock, flags);
+}
+
+int smu_present(void)
+{
+	return smu != NULL;
+}
+
+
+int smu_init (void)
+{
+	struct device_node *np;
+	u32 *data;
+
+        np = of_find_node_by_type(NULL, "smu");
+        if (np == NULL)
+		return -ENODEV;
+
+	if (smu_cmdbuf_abs == 0) {
+		printk(KERN_ERR "SMU: Command buffer not allocated !\n");
+		return -EINVAL;
+	}
+
+	smu = alloc_bootmem(sizeof(struct smu_device));
+	if (smu == NULL)
+		return -ENOMEM;
+	memset(smu, 0, sizeof(*smu));
+
+	spin_lock_init(&smu->lock);
+	smu->of_node = np;
+	/* smu_cmdbuf_abs is in the low 2G of RAM, can be converted to a
+	 * 32 bits value safely
+	 */
+	smu->cmd_buf_abs = (u32)smu_cmdbuf_abs;
+	smu->cmd_buf = (struct smu_cmd_buf *)abs_to_virt(smu_cmdbuf_abs);
+
+	np = of_find_node_by_name(NULL, "smu-doorbell");
+	if (np == NULL) {
+		printk(KERN_ERR "SMU: Can't find doorbell GPIO !\n");
+		goto fail;
+	}
+	data = (u32 *)get_property(np, "reg", NULL);
+	of_node_put(np);
+	if (data == NULL) {
+		printk(KERN_ERR "SMU: Can't find doorbell GPIO address !\n");
+		goto fail;
+	}
+
+	/* Current setup has one doorbell GPIO that does both doorbell
+	 * and ack. GPIOs are at 0x50, best would be to find that out
+	 * in the device-tree though.
+	 */
+	smu->db_req = 0x50 + *data;
+	smu->db_ack = 0x50 + *data;
+
+	/* Doorbell buffer is currently hard-coded, I didn't find a proper
+	 * device-tree entry giving the address. Best would probably to use
+	 * an offset for K2 base though, but let's do it that way for now.
+	 */
+	smu->db_buf = ioremap(0x8000860c, 0x1000);
+	if (smu->db_buf == NULL) {
+		printk(KERN_ERR "SMU: Can't map doorbell buffer pointer !\n");
+		goto fail;
+	}
+
+	sys_ctrler = SYS_CTRLER_SMU;
+	return 0;
+
+ fail:
+	smu = NULL;
+	return -ENXIO;
+
+}
diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c
new file mode 100644
index 0000000..e0ac63e
--- /dev/null
+++ b/drivers/macintosh/therm_adt746x.c
@@ -0,0 +1,612 @@
+/*
+ * Device driver for the i2c thermostat found on the iBook G4, Albook G4
+ *
+ * Copyright (C) 2003, 2004 Colin Leroy, Rasmus Rohde, Benjamin Herrenschmidt
+ *
+ * Documentation from
+ * http://www.analog.com/UploadedFiles/Data_Sheets/115254175ADT7467_pra.pdf
+ * http://www.analog.com/UploadedFiles/Data_Sheets/3686221171167ADT7460_b.pdf
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/wait.h>
+#include <linux/suspend.h>
+#include <linux/kthread.h>
+#include <linux/moduleparam.h>
+
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/sections.h>
+#include <asm/of_device.h>
+
+#undef DEBUG
+
+#define CONFIG_REG   0x40
+#define MANUAL_MASK  0xe0
+#define AUTO_MASK    0x20
+
+static u8 TEMP_REG[3]    = {0x26, 0x25, 0x27}; /* local, cpu, gpu */
+static u8 LIMIT_REG[3]   = {0x6b, 0x6a, 0x6c}; /* local, cpu, gpu */
+static u8 MANUAL_MODE[2] = {0x5c, 0x5d};       
+static u8 REM_CONTROL[2] = {0x00, 0x40};
+static u8 FAN_SPEED[2]   = {0x28, 0x2a};
+static u8 FAN_SPD_SET[2] = {0x30, 0x31};
+
+static u8 default_limits_local[3] = {70, 50, 70};    /* local, cpu, gpu */
+static u8 default_limits_chip[3] = {80, 65, 80};    /* local, cpu, gpu */
+
+static int limit_adjust = 0;
+static int fan_speed = -1;
+
+MODULE_AUTHOR("Colin Leroy <colin@colino.net>");
+MODULE_DESCRIPTION("Driver for ADT746x thermostat in iBook G4 and "
+		   "Powerbook G4 Alu");
+MODULE_LICENSE("GPL");
+
+module_param(limit_adjust, int, 0644);
+MODULE_PARM_DESC(limit_adjust,"Adjust maximum temperatures (50 cpu, 70 gpu) "
+		 "by N degrees.");
+
+module_param(fan_speed, int, 0644);
+MODULE_PARM_DESC(fan_speed,"Specify starting fan speed (0-255) "
+		 "(default 64)");
+
+struct thermostat {
+	struct i2c_client	clt;
+	u8			temps[3];
+	u8			cached_temp[3];
+	u8			initial_limits[3];
+	u8			limits[3];
+	int			last_speed[2];
+	int			last_var[2];
+};
+
+static enum {ADT7460, ADT7467} therm_type;
+static int therm_bus, therm_address;
+static struct of_device * of_dev;
+static struct thermostat* thermostat;
+static struct task_struct *thread_therm = NULL;
+
+static int attach_one_thermostat(struct i2c_adapter *adapter, int addr,
+				 int busno);
+
+static void write_both_fan_speed(struct thermostat *th, int speed);
+static void write_fan_speed(struct thermostat *th, int speed, int fan);
+
+static int
+write_reg(struct thermostat* th, int reg, u8 data)
+{
+	u8 tmp[2];
+	int rc;
+	
+	tmp[0] = reg;
+	tmp[1] = data;
+	rc = i2c_master_send(&th->clt, (const char *)tmp, 2);
+	if (rc < 0)
+		return rc;
+	if (rc != 2)
+		return -ENODEV;
+	return 0;
+}
+
+static int
+read_reg(struct thermostat* th, int reg)
+{
+	u8 reg_addr, data;
+	int rc;
+
+	reg_addr = (u8)reg;
+	rc = i2c_master_send(&th->clt, &reg_addr, 1);
+	if (rc < 0)
+		return rc;
+	if (rc != 1)
+		return -ENODEV;
+	rc = i2c_master_recv(&th->clt, (char *)&data, 1);
+	if (rc < 0)
+		return rc;
+	return data;
+}
+
+static int
+attach_thermostat(struct i2c_adapter *adapter)
+{
+	unsigned long bus_no;
+
+	if (strncmp(adapter->name, "uni-n", 5))
+		return -ENODEV;
+	bus_no = simple_strtoul(adapter->name + 6, NULL, 10);
+	if (bus_no != therm_bus)
+		return -ENODEV;
+	return attach_one_thermostat(adapter, therm_address, bus_no);
+}
+
+static int
+detach_thermostat(struct i2c_adapter *adapter)
+{
+	struct thermostat* th;
+	int i;
+	
+	if (thermostat == NULL)
+		return 0;
+
+	th = thermostat;
+
+	if (thread_therm != NULL) {
+		kthread_stop(thread_therm);
+	}
+		
+	printk(KERN_INFO "adt746x: Putting max temperatures back from "
+			 "%d, %d, %d to %d, %d, %d\n",
+		th->limits[0], th->limits[1], th->limits[2],
+		th->initial_limits[0], th->initial_limits[1],
+		th->initial_limits[2]);
+	
+	for (i = 0; i < 3; i++)
+		write_reg(th, LIMIT_REG[i], th->initial_limits[i]);
+
+	write_both_fan_speed(th, -1);
+
+	i2c_detach_client(&th->clt);
+
+	thermostat = NULL;
+
+	kfree(th);
+
+	return 0;
+}
+
+static struct i2c_driver thermostat_driver = {  
+	.owner		= THIS_MODULE,
+	.name		= "therm_adt746x",
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= attach_thermostat,
+	.detach_adapter	= detach_thermostat,
+};
+
+static int read_fan_speed(struct thermostat *th, u8 addr)
+{
+	u8 tmp[2];
+	u16 res;
+	
+	/* should start with low byte */
+	tmp[1] = read_reg(th, addr);
+	tmp[0] = read_reg(th, addr + 1);
+	
+	res = tmp[1] + (tmp[0] << 8);
+	/* "a value of 0xffff means that the fan has stopped" */
+	return (res == 0xffff ? 0 : (90000*60)/res);
+}
+
+static void write_both_fan_speed(struct thermostat *th, int speed)
+{
+	write_fan_speed(th, speed, 0);
+	if (therm_type == ADT7460)
+		write_fan_speed(th, speed, 1);
+}
+
+static void write_fan_speed(struct thermostat *th, int speed, int fan)
+{
+	u8 manual;
+	
+	if (speed > 0xff) 
+		speed = 0xff;
+	else if (speed < -1) 
+		speed = 0;
+	
+	if (therm_type == ADT7467 && fan == 1)
+		return;
+	
+	if (th->last_speed[fan] != speed) {
+		if (speed == -1)
+			printk(KERN_DEBUG "adt746x: Setting speed to automatic "
+				"for %s fan.\n", fan?"GPU":"CPU");
+		else
+			printk(KERN_DEBUG "adt746x: Setting speed to %d "
+				"for %s fan.\n", speed, fan?"GPU":"CPU");
+	} else
+		return;
+	
+	if (speed >= 0) {
+		manual = read_reg(th, MANUAL_MODE[fan]);
+		write_reg(th, MANUAL_MODE[fan], manual|MANUAL_MASK);
+		write_reg(th, FAN_SPD_SET[fan], speed);
+	} else {
+		/* back to automatic */
+		if(therm_type == ADT7460) {
+			manual = read_reg(th,
+				MANUAL_MODE[fan]) & (~MANUAL_MASK);
+
+			write_reg(th,
+				MANUAL_MODE[fan], manual|REM_CONTROL[fan]);
+		} else {
+			manual = read_reg(th, MANUAL_MODE[fan]);
+			write_reg(th, MANUAL_MODE[fan], manual&(~AUTO_MASK));
+		}
+	}
+	
+	th->last_speed[fan] = speed;			
+}
+
+static void read_sensors(struct thermostat *th)
+{
+	int i = 0;
+
+	for (i = 0; i < 3; i++)
+		th->temps[i]  = read_reg(th, TEMP_REG[i]);
+}
+
+#ifdef DEBUG
+static void display_stats(struct thermostat *th)
+{
+	if (th->temps[0] != th->cached_temp[0]
+	||  th->temps[1] != th->cached_temp[1]
+	||  th->temps[2] != th->cached_temp[2]) {
+		printk(KERN_INFO "adt746x: Temperature infos:"
+				 " thermostats: %d,%d,%d;"
+				 " limits: %d,%d,%d;"
+				 " fan speed: %d RPM\n",
+				 th->temps[0], th->temps[1], th->temps[2],
+				 th->limits[0],  th->limits[1],  th->limits[2],
+				 read_fan_speed(th, FAN_SPEED[0]));
+	}
+	th->cached_temp[0] = th->temps[0];
+	th->cached_temp[1] = th->temps[1];
+	th->cached_temp[2] = th->temps[2];
+}
+#endif
+
+static void update_fans_speed (struct thermostat *th)
+{
+	int lastvar = 0; /* last variation, for iBook */
+	int i = 0;
+
+	/* we don't care about local sensor, so we start at sensor 1 */
+	for (i = 1; i < 3; i++) {
+		int started = 0;
+		int fan_number = (therm_type == ADT7460 && i == 2);
+		int var = th->temps[i] - th->limits[i];
+
+		if (var > -1) {
+			int step = (255 - fan_speed) / 7;
+			int new_speed = 0;
+
+			/* hysteresis : change fan speed only if variation is
+			 * more than two degrees */
+			if (abs(var - th->last_var[fan_number]) < 2)
+				continue;
+
+			started = 1;
+			new_speed = fan_speed + ((var-1)*step);
+
+			if (new_speed < fan_speed)
+				new_speed = fan_speed;
+			if (new_speed > 255)
+				new_speed = 255;
+
+			printk(KERN_DEBUG "adt746x: setting fans speed to %d "
+					 "(limit exceeded by %d on %s) \n",
+					new_speed, var,
+					fan_number?"GPU/pwr":"CPU");
+			write_both_fan_speed(th, new_speed);
+			th->last_var[fan_number] = var;
+		} else if (var < -2) {
+			/* don't stop fan if GPU/power is cold and CPU is not
+			 * so cold (lastvar >= -1) */
+			if (i == 2 && lastvar < -1) {
+				if (th->last_speed[fan_number] != 0)
+					printk(KERN_DEBUG "adt746x: Stopping "
+						"fans.\n");
+				write_both_fan_speed(th, 0);
+			}
+		}
+
+		lastvar = var;
+
+		if (started)
+			return; /* we don't want to re-stop the fan
+				* if CPU is heating and GPU/power is not */
+	}
+}
+
+static int monitor_task(void *arg)
+{
+	struct thermostat* th = arg;
+
+	while(!kthread_should_stop()) {
+		if (current->flags & PF_FREEZE)
+			refrigerator(PF_FREEZE);
+
+		msleep_interruptible(2000);
+
+#ifndef DEBUG
+		if (fan_speed != -1)
+			read_sensors(th);
+#else
+		read_sensors(th);
+#endif		
+
+		if (fan_speed != -1)
+			update_fans_speed(th);
+
+#ifdef DEBUG
+		display_stats(th);
+#endif
+
+	}
+
+	return 0;
+}
+
+static void set_limit(struct thermostat *th, int i)
+{
+		/* Set CPU limit higher to avoid powerdowns */ 
+		th->limits[i] = default_limits_chip[i] + limit_adjust;
+		write_reg(th, LIMIT_REG[i], th->limits[i]);
+		
+		/* set our limits to normal */
+		th->limits[i] = default_limits_local[i] + limit_adjust;
+}
+
+static int attach_one_thermostat(struct i2c_adapter *adapter, int addr,
+				 int busno)
+{
+	struct thermostat* th;
+	int rc;
+	int i;
+
+	if (thermostat)
+		return 0;
+
+	th = (struct thermostat *)
+		kmalloc(sizeof(struct thermostat), GFP_KERNEL);
+
+	if (!th)
+		return -ENOMEM;
+
+	memset(th, 0, sizeof(*th));
+	th->clt.addr = addr;
+	th->clt.adapter = adapter;
+	th->clt.driver = &thermostat_driver;
+	strcpy(th->clt.name, "thermostat");
+
+	rc = read_reg(th, 0);
+	if (rc < 0) {
+		printk(KERN_ERR "adt746x: Thermostat failed to read config "
+				"from bus %d !\n",
+				busno);
+		kfree(th);
+		return -ENODEV;
+	}
+
+	/* force manual control to start the fan quieter */
+	if (fan_speed == -1)
+		fan_speed = 64;
+	
+	if(therm_type == ADT7460) {
+		printk(KERN_INFO "adt746x: ADT7460 initializing\n");
+		/* The 7460 needs to be started explicitly */
+		write_reg(th, CONFIG_REG, 1);
+	} else
+		printk(KERN_INFO "adt746x: ADT7467 initializing\n");
+
+	for (i = 0; i < 3; i++) {
+		th->initial_limits[i] = read_reg(th, LIMIT_REG[i]);
+		set_limit(th, i);
+	}
+	
+	printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d"
+			 " to %d, %d, %d\n",
+			 th->initial_limits[0], th->initial_limits[1],
+			 th->initial_limits[2], th->limits[0], th->limits[1],
+			 th->limits[2]);
+
+	thermostat = th;
+
+	if (i2c_attach_client(&th->clt)) {
+		printk(KERN_INFO "adt746x: Thermostat failed to attach "
+				 "client !\n");
+		thermostat = NULL;
+		kfree(th);
+		return -ENODEV;
+	}
+
+	/* be sure to really write fan speed the first time */
+	th->last_speed[0] = -2;
+	th->last_speed[1] = -2;
+	th->last_var[0] = -80;
+	th->last_var[1] = -80;
+
+	if (fan_speed != -1) {
+		/* manual mode, stop fans */
+		write_both_fan_speed(th, 0);
+	} else {
+		/* automatic mode */
+		write_both_fan_speed(th, -1);
+	}
+	
+	thread_therm = kthread_run(monitor_task, th, "kfand");
+
+	if (thread_therm == ERR_PTR(-ENOMEM)) {
+		printk(KERN_INFO "adt746x: Kthread creation failed\n");
+		thread_therm = NULL;
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/* 
+ * Now, unfortunately, sysfs doesn't give us a nice void * we could
+ * pass around to the attribute functions, so we don't really have
+ * choice but implement a bunch of them...
+ *
+ */
+#define BUILD_SHOW_FUNC_INT(name, data)				\
+static ssize_t show_##name(struct device *dev, char *buf)	\
+{								\
+	return sprintf(buf, "%d\n", data);			\
+}
+
+#define BUILD_SHOW_FUNC_FAN(name, data)				\
+static ssize_t show_##name(struct device *dev, char *buf)       \
+{								\
+	return sprintf(buf, "%d (%d rpm)\n", 			\
+		thermostat->last_speed[data],			\
+		read_fan_speed(thermostat, FAN_SPEED[data])	\
+		);						\
+}
+
+#define BUILD_STORE_FUNC_DEG(name, data)			\
+static ssize_t store_##name(struct device *dev, const char *buf, size_t n) \
+{								\
+	int val;						\
+	int i;							\
+	val = simple_strtol(buf, NULL, 10);			\
+	printk(KERN_INFO "Adjusting limits by %d°C\n", val);	\
+	limit_adjust = val;					\
+	for (i=0; i < 3; i++)					\
+		set_limit(thermostat, i);			\
+	return n;						\
+}
+
+#define BUILD_STORE_FUNC_INT(name, data)			\
+static ssize_t store_##name(struct device *dev, const char *buf, size_t n) \
+{								\
+	u32 val;						\
+	val = simple_strtoul(buf, NULL, 10);			\
+	if (val < 0 || val > 255)				\
+		return -EINVAL;					\
+	printk(KERN_INFO "Setting specified fan speed to %d\n", val);	\
+	data = val;						\
+	return n;						\
+}
+
+BUILD_SHOW_FUNC_INT(cpu_temperature,	 (read_reg(thermostat, TEMP_REG[1])))
+BUILD_SHOW_FUNC_INT(gpu_temperature,	 (read_reg(thermostat, TEMP_REG[2])))
+BUILD_SHOW_FUNC_INT(cpu_limit,		 thermostat->limits[1])
+BUILD_SHOW_FUNC_INT(gpu_limit,		 thermostat->limits[2])
+
+BUILD_SHOW_FUNC_INT(specified_fan_speed, fan_speed)
+BUILD_SHOW_FUNC_FAN(cpu_fan_speed,	 0)
+BUILD_SHOW_FUNC_FAN(gpu_fan_speed,	 1)
+
+BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed)
+BUILD_SHOW_FUNC_INT(limit_adjust,	 limit_adjust)
+BUILD_STORE_FUNC_DEG(limit_adjust,	 thermostat)
+		
+static DEVICE_ATTR(cpu_temperature,	S_IRUGO,
+		   show_cpu_temperature,NULL);
+static DEVICE_ATTR(gpu_temperature,	S_IRUGO,
+		   show_gpu_temperature,NULL);
+static DEVICE_ATTR(cpu_limit,		S_IRUGO,
+		   show_cpu_limit,	NULL);
+static DEVICE_ATTR(gpu_limit,		S_IRUGO,
+		   show_gpu_limit,	NULL);
+
+static DEVICE_ATTR(specified_fan_speed,	S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
+		   show_specified_fan_speed,store_specified_fan_speed);
+
+static DEVICE_ATTR(cpu_fan_speed,	S_IRUGO,
+		   show_cpu_fan_speed,	NULL);
+static DEVICE_ATTR(gpu_fan_speed,	S_IRUGO,
+		   show_gpu_fan_speed,	NULL);
+
+static DEVICE_ATTR(limit_adjust,	S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
+		   show_limit_adjust,	store_limit_adjust);
+
+
+static int __init
+thermostat_init(void)
+{
+	struct device_node* np;
+	u32 *prop;
+	
+	np = of_find_node_by_name(NULL, "fan");
+	if (!np)
+		return -ENODEV;
+	if (device_is_compatible(np, "adt7460"))
+		therm_type = ADT7460;
+	else if (device_is_compatible(np, "adt7467"))
+		therm_type = ADT7467;
+	else
+		return -ENODEV;
+
+	prop = (u32 *)get_property(np, "reg", NULL);
+	if (!prop)
+		return -ENODEV;
+
+	/* look for bus either by path or using "reg" */
+	if (strstr(np->full_name, "/i2c-bus@") != NULL) {
+		const char *tmp_bus = (strstr(np->full_name, "/i2c-bus@") + 9);
+		therm_bus = tmp_bus[0]-'0';
+	} else {
+		therm_bus = ((*prop) >> 8) & 0x0f;
+	}
+
+	therm_address = ((*prop) & 0xff) >> 1;
+
+	printk(KERN_INFO "adt746x: Thermostat bus: %d, address: 0x%02x, "
+			 "limit_adjust: %d, fan_speed: %d\n",
+			 therm_bus, therm_address, limit_adjust, fan_speed);
+
+	of_dev = of_platform_device_create(np, "temperatures");
+	
+	if (of_dev == NULL) {
+		printk(KERN_ERR "Can't register temperatures device !\n");
+		return -ENODEV;
+	}
+	
+	device_create_file(&of_dev->dev, &dev_attr_cpu_temperature);
+	device_create_file(&of_dev->dev, &dev_attr_gpu_temperature);
+	device_create_file(&of_dev->dev, &dev_attr_cpu_limit);
+	device_create_file(&of_dev->dev, &dev_attr_gpu_limit);
+	device_create_file(&of_dev->dev, &dev_attr_limit_adjust);
+	device_create_file(&of_dev->dev, &dev_attr_specified_fan_speed);
+	device_create_file(&of_dev->dev, &dev_attr_cpu_fan_speed);
+	if(therm_type == ADT7460)
+		device_create_file(&of_dev->dev, &dev_attr_gpu_fan_speed);
+
+#ifndef CONFIG_I2C_KEYWEST
+	request_module("i2c-keywest");
+#endif
+
+	return i2c_add_driver(&thermostat_driver);
+}
+
+static void __exit
+thermostat_exit(void)
+{
+	if (of_dev) {
+		device_remove_file(&of_dev->dev, &dev_attr_cpu_temperature);
+		device_remove_file(&of_dev->dev, &dev_attr_gpu_temperature);
+		device_remove_file(&of_dev->dev, &dev_attr_cpu_limit);
+		device_remove_file(&of_dev->dev, &dev_attr_gpu_limit);
+		device_remove_file(&of_dev->dev, &dev_attr_limit_adjust);
+		device_remove_file(&of_dev->dev, &dev_attr_specified_fan_speed);
+		device_remove_file(&of_dev->dev, &dev_attr_cpu_fan_speed);
+
+		if(therm_type == ADT7460)
+			device_remove_file(&of_dev->dev,
+					   &dev_attr_gpu_fan_speed);
+
+		of_device_unregister(of_dev);
+	}
+	i2c_del_driver(&thermostat_driver);
+}
+
+module_init(thermostat_init);
+module_exit(thermostat_exit);
diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c
new file mode 100644
index 0000000..82336a5
--- /dev/null
+++ b/drivers/macintosh/therm_pm72.c
@@ -0,0 +1,2080 @@
+/*
+ * Device driver for the thermostats & fan controller of  the
+ * Apple G5 "PowerMac7,2" desktop machines.
+ *
+ * (c) Copyright IBM Corp. 2003-2004
+ *
+ * Maintained by: Benjamin Herrenschmidt
+ *                <benh@kernel.crashing.org>
+ * 
+ *
+ * The algorithm used is the PID control algorithm, used the same
+ * way the published Darwin code does, using the same values that
+ * are present in the Darwin 7.0 snapshot property lists.
+ *
+ * As far as the CPUs control loops are concerned, I use the
+ * calibration & PID constants provided by the EEPROM,
+ * I do _not_ embed any value from the property lists, as the ones
+ * provided by Darwin 7.0 seem to always have an older version that
+ * what I've seen on the actual computers.
+ * It would be interesting to verify that though. Darwin has a
+ * version code of 1.0.0d11 for all control loops it seems, while
+ * so far, the machines EEPROMs contain a dataset versioned 1.0.0f
+ *
+ * Darwin doesn't provide source to all parts, some missing
+ * bits like the AppleFCU driver or the actual scale of some
+ * of the values returned by sensors had to be "guessed" some
+ * way... or based on what Open Firmware does.
+ *
+ * I didn't yet figure out how to get the slots power consumption
+ * out of the FCU, so that part has not been implemented yet and
+ * the slots fan is set to a fixed 50% PWM, hoping this value is
+ * safe enough ...
+ *
+ * Note: I have observed strange oscillations of the CPU control
+ * loop on a dual G5 here. When idle, the CPU exhaust fan tend to
+ * oscillates slowly (over several minutes) between the minimum
+ * of 300RPMs and approx. 1000 RPMs. I don't know what is causing
+ * this, it could be some incorrect constant or an error in the
+ * way I ported the algorithm, or it could be just normal. I
+ * don't have full understanding on the way Apple tweaked the PID
+ * algorithm for the CPU control, it is definitely not a standard
+ * implementation...
+ *
+ * TODO:  - Check MPU structure version/signature
+ *        - Add things like /sbin/overtemp for non-critical
+ *          overtemp conditions so userland can take some policy
+ *          decisions, like slewing down CPUs
+ *	  - Deal with fan and i2c failures in a better way
+ *	  - Maybe do a generic PID based on params used for
+ *	    U3 and Drives ? Definitely need to factor code a bit
+ *          bettter... also make sensor detection more robust using
+ *          the device-tree to probe for them
+ *        - Figure out how to get the slots consumption and set the
+ *          slots fan accordingly
+ *
+ * History:
+ *
+ *  Nov. 13, 2003 : 0.5
+ *	- First release
+ *
+ *  Nov. 14, 2003 : 0.6
+ *	- Read fan speed from FCU, low level fan routines now deal
+ *	  with errors & check fan status, though higher level don't
+ *	  do much.
+ *	- Move a bunch of definitions to .h file
+ *
+ *  Nov. 18, 2003 : 0.7
+ *	- Fix build on ppc64 kernel
+ *	- Move back statics definitions to .c file
+ *	- Avoid calling schedule_timeout with a negative number
+ *
+ *  Dec. 18, 2003 : 0.8
+ *	- Fix typo when reading back fan speed on 2 CPU machines
+ *
+ *  Mar. 11, 2004 : 0.9
+ *	- Rework code accessing the ADC chips, make it more robust and
+ *	  closer to the chip spec. Also make sure it is configured properly,
+ *        I've seen yet unexplained cases where on startup, I would have stale
+ *        values in the configuration register
+ *	- Switch back to use of target fan speed for PID, thus lowering
+ *        pressure on i2c
+ *
+ *  Oct. 20, 2004 : 1.1
+ *	- Add device-tree lookup for fan IDs, should detect liquid cooling
+ *        pumps when present
+ *	- Enable driver for PowerMac7,3 machines
+ *	- Split the U3/Backside cooling on U3 & U3H versions as Darwin does
+ *	- Add new CPU cooling algorithm for machines with liquid cooling
+ *	- Workaround for some PowerMac7,3 with empty "fan" node in the devtree
+ *	- Fix a signed/unsigned compare issue in some PID loops
+ *
+ *  Mar. 10, 2005 : 1.2
+ *	- Add basic support for Xserve G5
+ *	- Retreive pumps min/max from EEPROM image in device-tree (broken)
+ *	- Use min/max macros here or there
+ *	- Latest darwin updated U3H min fan speed to 20% PWM
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/wait.h>
+#include <linux/reboot.h>
+#include <linux/kmod.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/sections.h>
+#include <asm/of_device.h>
+
+#include "therm_pm72.h"
+
+#define VERSION "1.2b2"
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(args...)	printk(args)
+#else
+#define DBG(args...)	do { } while(0)
+#endif
+
+
+/*
+ * Driver statics
+ */
+
+static struct of_device *		of_dev;
+static struct i2c_adapter *		u3_0;
+static struct i2c_adapter *		u3_1;
+static struct i2c_adapter *		k2;
+static struct i2c_client *		fcu;
+static struct cpu_pid_state		cpu_state[2];
+static struct basckside_pid_params	backside_params;
+static struct backside_pid_state	backside_state;
+static struct drives_pid_state		drives_state;
+static struct dimm_pid_state		dimms_state;
+static int				state;
+static int				cpu_count;
+static int				cpu_pid_type;
+static pid_t				ctrl_task;
+static struct completion		ctrl_complete;
+static int				critical_state;
+static int				rackmac;
+static s32				dimm_output_clamp;
+
+static DECLARE_MUTEX(driver_lock);
+
+/*
+ * We have 3 types of CPU PID control. One is "split" old style control
+ * for intake & exhaust fans, the other is "combined" control for both
+ * CPUs that also deals with the pumps when present. To be "compatible"
+ * with OS X at this point, we only use "COMBINED" on the machines that
+ * are identified as having the pumps (though that identification is at
+ * least dodgy). Ultimately, we could probably switch completely to this
+ * algorithm provided we hack it to deal with the UP case
+ */
+#define CPU_PID_TYPE_SPLIT	0
+#define CPU_PID_TYPE_COMBINED	1
+#define CPU_PID_TYPE_RACKMAC	2
+
+/*
+ * This table describes all fans in the FCU. The "id" and "type" values
+ * are defaults valid for all earlier machines. Newer machines will
+ * eventually override the table content based on the device-tree
+ */
+struct fcu_fan_table
+{
+	char*	loc;	/* location code */
+	int	type;	/* 0 = rpm, 1 = pwm, 2 = pump */
+	int	id;	/* id or -1 */
+};
+
+#define FCU_FAN_RPM		0
+#define FCU_FAN_PWM		1
+
+#define FCU_FAN_ABSENT_ID	-1
+
+#define FCU_FAN_COUNT		ARRAY_SIZE(fcu_fans)
+
+struct fcu_fan_table	fcu_fans[] = {
+	[BACKSIDE_FAN_PWM_INDEX] = {
+		.loc	= "BACKSIDE,SYS CTRLR FAN",
+		.type	= FCU_FAN_PWM,
+		.id	= BACKSIDE_FAN_PWM_DEFAULT_ID,
+	},
+	[DRIVES_FAN_RPM_INDEX] = {
+		.loc	= "DRIVE BAY",
+		.type	= FCU_FAN_RPM,
+		.id	= DRIVES_FAN_RPM_DEFAULT_ID,
+	},
+	[SLOTS_FAN_PWM_INDEX] = {
+		.loc	= "SLOT,PCI FAN",
+		.type	= FCU_FAN_PWM,
+		.id	= SLOTS_FAN_PWM_DEFAULT_ID,
+	},
+	[CPUA_INTAKE_FAN_RPM_INDEX] = {
+		.loc	= "CPU A INTAKE",
+		.type	= FCU_FAN_RPM,
+		.id	= CPUA_INTAKE_FAN_RPM_DEFAULT_ID,
+	},
+	[CPUA_EXHAUST_FAN_RPM_INDEX] = {
+		.loc	= "CPU A EXHAUST",
+		.type	= FCU_FAN_RPM,
+		.id	= CPUA_EXHAUST_FAN_RPM_DEFAULT_ID,
+	},
+	[CPUB_INTAKE_FAN_RPM_INDEX] = {
+		.loc	= "CPU B INTAKE",
+		.type	= FCU_FAN_RPM,
+		.id	= CPUB_INTAKE_FAN_RPM_DEFAULT_ID,
+	},
+	[CPUB_EXHAUST_FAN_RPM_INDEX] = {
+		.loc	= "CPU B EXHAUST",
+		.type	= FCU_FAN_RPM,
+		.id	= CPUB_EXHAUST_FAN_RPM_DEFAULT_ID,
+	},
+	/* pumps aren't present by default, have to be looked up in the
+	 * device-tree
+	 */
+	[CPUA_PUMP_RPM_INDEX] = {
+		.loc	= "CPU A PUMP",
+		.type	= FCU_FAN_RPM,		
+		.id	= FCU_FAN_ABSENT_ID,
+	},
+	[CPUB_PUMP_RPM_INDEX] = {
+		.loc	= "CPU B PUMP",
+		.type	= FCU_FAN_RPM,
+		.id	= FCU_FAN_ABSENT_ID,
+	},
+	/* Xserve fans */
+	[CPU_A1_FAN_RPM_INDEX] = {
+		.loc	= "CPU A 1",
+		.type	= FCU_FAN_RPM,
+		.id	= FCU_FAN_ABSENT_ID,
+	},
+	[CPU_A2_FAN_RPM_INDEX] = {
+		.loc	= "CPU A 2",
+		.type	= FCU_FAN_RPM,
+		.id	= FCU_FAN_ABSENT_ID,
+	},
+	[CPU_A3_FAN_RPM_INDEX] = {
+		.loc	= "CPU A 3",
+		.type	= FCU_FAN_RPM,
+		.id	= FCU_FAN_ABSENT_ID,
+	},
+	[CPU_B1_FAN_RPM_INDEX] = {
+		.loc	= "CPU B 1",
+		.type	= FCU_FAN_RPM,
+		.id	= FCU_FAN_ABSENT_ID,
+	},
+	[CPU_B2_FAN_RPM_INDEX] = {
+		.loc	= "CPU B 2",
+		.type	= FCU_FAN_RPM,
+		.id	= FCU_FAN_ABSENT_ID,
+	},
+	[CPU_B3_FAN_RPM_INDEX] = {
+		.loc	= "CPU B 3",
+		.type	= FCU_FAN_RPM,
+		.id	= FCU_FAN_ABSENT_ID,
+	},
+};
+
+/*
+ * i2c_driver structure to attach to the host i2c controller
+ */
+
+static int therm_pm72_attach(struct i2c_adapter *adapter);
+static int therm_pm72_detach(struct i2c_adapter *adapter);
+
+static struct i2c_driver therm_pm72_driver =
+{
+	.owner		= THIS_MODULE,
+	.name		= "therm_pm72",
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= therm_pm72_attach,
+	.detach_adapter	= therm_pm72_detach,
+};
+
+/*
+ * Utility function to create an i2c_client structure and
+ * attach it to one of u3 adapters
+ */
+static struct i2c_client *attach_i2c_chip(int id, const char *name)
+{
+	struct i2c_client *clt;
+	struct i2c_adapter *adap;
+
+	if (id & 0x200)
+		adap = k2;
+	else if (id & 0x100)
+		adap = u3_1;
+	else
+		adap = u3_0;
+	if (adap == NULL)
+		return NULL;
+
+	clt = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+	if (clt == NULL)
+		return NULL;
+	memset(clt, 0, sizeof(struct i2c_client));
+
+	clt->addr = (id >> 1) & 0x7f;
+	clt->adapter = adap;
+	clt->driver = &therm_pm72_driver;
+	strncpy(clt->name, name, I2C_NAME_SIZE-1);
+
+	if (i2c_attach_client(clt)) {
+		printk(KERN_ERR "therm_pm72: Failed to attach to i2c ID 0x%x\n", id);
+		kfree(clt);
+		return NULL;
+	}
+	return clt;
+}
+
+/*
+ * Utility function to get rid of the i2c_client structure
+ * (will also detach from the adapter hopepfully)
+ */
+static void detach_i2c_chip(struct i2c_client *clt)
+{
+	i2c_detach_client(clt);
+	kfree(clt);
+}
+
+/*
+ * Here are the i2c chip access wrappers
+ */
+
+static void initialize_adc(struct cpu_pid_state *state)
+{
+	int rc;
+	u8 buf[2];
+
+	/* Read ADC the configuration register and cache it. We
+	 * also make sure Config2 contains proper values, I've seen
+	 * cases where we got stale grabage in there, thus preventing
+	 * proper reading of conv. values
+	 */
+
+	/* Clear Config2 */
+	buf[0] = 5;
+	buf[1] = 0;
+	i2c_master_send(state->monitor, buf, 2);
+
+	/* Read & cache Config1 */
+	buf[0] = 1;
+	rc = i2c_master_send(state->monitor, buf, 1);
+	if (rc > 0) {
+		rc = i2c_master_recv(state->monitor, buf, 1);
+		if (rc > 0) {
+			state->adc_config = buf[0];
+			DBG("ADC config reg: %02x\n", state->adc_config);
+			/* Disable shutdown mode */
+		       	state->adc_config &= 0xfe;
+			buf[0] = 1;
+			buf[1] = state->adc_config;
+			rc = i2c_master_send(state->monitor, buf, 2);
+		}
+	}
+	if (rc <= 0)
+		printk(KERN_ERR "therm_pm72: Error reading ADC config"
+		       " register !\n");
+}
+
+static int read_smon_adc(struct cpu_pid_state *state, int chan)
+{
+	int rc, data, tries = 0;
+	u8 buf[2];
+
+	for (;;) {
+		/* Set channel */
+		buf[0] = 1;
+		buf[1] = (state->adc_config & 0x1f) | (chan << 5);
+		rc = i2c_master_send(state->monitor, buf, 2);
+		if (rc <= 0)
+			goto error;
+		/* Wait for convertion */
+		msleep(1);
+		/* Switch to data register */
+		buf[0] = 4;
+		rc = i2c_master_send(state->monitor, buf, 1);
+		if (rc <= 0)
+			goto error;
+		/* Read result */
+		rc = i2c_master_recv(state->monitor, buf, 2);
+		if (rc < 0)
+			goto error;
+		data = ((u16)buf[0]) << 8 | (u16)buf[1];
+		return data >> 6;
+	error:
+		DBG("Error reading ADC, retrying...\n");
+		if (++tries > 10) {
+			printk(KERN_ERR "therm_pm72: Error reading ADC !\n");
+			return -1;
+		}
+		msleep(10);
+	}
+}
+
+static int read_lm87_reg(struct i2c_client * chip, int reg)
+{
+	int rc, tries = 0;
+	u8 buf;
+
+	for (;;) {
+		/* Set address */
+		buf = (u8)reg;
+		rc = i2c_master_send(chip, &buf, 1);
+		if (rc <= 0)
+			goto error;
+		rc = i2c_master_recv(chip, &buf, 1);
+		if (rc <= 0)
+			goto error;
+		return (int)buf;
+	error:
+		DBG("Error reading LM87, retrying...\n");
+		if (++tries > 10) {
+			printk(KERN_ERR "therm_pm72: Error reading LM87 !\n");
+			return -1;
+		}
+		msleep(10);
+	}
+}
+
+static int fan_read_reg(int reg, unsigned char *buf, int nb)
+{
+	int tries, nr, nw;
+
+	buf[0] = reg;
+	tries = 0;
+	for (;;) {
+		nw = i2c_master_send(fcu, buf, 1);
+		if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100)
+			break;
+		msleep(10);
+		++tries;
+	}
+	if (nw <= 0) {
+		printk(KERN_ERR "Failure writing address to FCU: %d", nw);
+		return -EIO;
+	}
+	tries = 0;
+	for (;;) {
+		nr = i2c_master_recv(fcu, buf, nb);
+		if (nr > 0 || (nr < 0 && nr != ENODEV) || tries >= 100)
+			break;
+		msleep(10);
+		++tries;
+	}
+	if (nr <= 0)
+		printk(KERN_ERR "Failure reading data from FCU: %d", nw);
+	return nr;
+}
+
+static int fan_write_reg(int reg, const unsigned char *ptr, int nb)
+{
+	int tries, nw;
+	unsigned char buf[16];
+
+	buf[0] = reg;
+	memcpy(buf+1, ptr, nb);
+	++nb;
+	tries = 0;
+	for (;;) {
+		nw = i2c_master_send(fcu, buf, nb);
+		if (nw > 0 || (nw < 0 && nw != EIO) || tries >= 100)
+			break;
+		msleep(10);
+		++tries;
+	}
+	if (nw < 0)
+		printk(KERN_ERR "Failure writing to FCU: %d", nw);
+	return nw;
+}
+
+static int start_fcu(void)
+{
+	unsigned char buf = 0xff;
+	int rc;
+
+	rc = fan_write_reg(0xe, &buf, 1);
+	if (rc < 0)
+		return -EIO;
+	rc = fan_write_reg(0x2e, &buf, 1);
+	if (rc < 0)
+		return -EIO;
+	return 0;
+}
+
+static int set_rpm_fan(int fan_index, int rpm)
+{
+	unsigned char buf[2];
+	int rc, id;
+
+	if (fcu_fans[fan_index].type != FCU_FAN_RPM)
+		return -EINVAL;
+	id = fcu_fans[fan_index].id; 
+	if (id == FCU_FAN_ABSENT_ID)
+		return -EINVAL;
+
+	if (rpm < 300)
+		rpm = 300;
+	else if (rpm > 8191)
+		rpm = 8191;
+	buf[0] = rpm >> 5;
+	buf[1] = rpm << 3;
+	rc = fan_write_reg(0x10 + (id * 2), buf, 2);
+	if (rc < 0)
+		return -EIO;
+	return 0;
+}
+
+static int get_rpm_fan(int fan_index, int programmed)
+{
+	unsigned char failure;
+	unsigned char active;
+	unsigned char buf[2];
+	int rc, id, reg_base;
+
+	if (fcu_fans[fan_index].type != FCU_FAN_RPM)
+		return -EINVAL;
+	id = fcu_fans[fan_index].id; 
+	if (id == FCU_FAN_ABSENT_ID)
+		return -EINVAL;
+
+	rc = fan_read_reg(0xb, &failure, 1);
+	if (rc != 1)
+		return -EIO;
+	if ((failure & (1 << id)) != 0)
+		return -EFAULT;
+	rc = fan_read_reg(0xd, &active, 1);
+	if (rc != 1)
+		return -EIO;
+	if ((active & (1 << id)) == 0)
+		return -ENXIO;
+
+	/* Programmed value or real current speed */
+	reg_base = programmed ? 0x10 : 0x11;
+	rc = fan_read_reg(reg_base + (id * 2), buf, 2);
+	if (rc != 2)
+		return -EIO;
+
+	return (buf[0] << 5) | buf[1] >> 3;
+}
+
+static int set_pwm_fan(int fan_index, int pwm)
+{
+	unsigned char buf[2];
+	int rc, id;
+
+	if (fcu_fans[fan_index].type != FCU_FAN_PWM)
+		return -EINVAL;
+	id = fcu_fans[fan_index].id; 
+	if (id == FCU_FAN_ABSENT_ID)
+		return -EINVAL;
+
+	if (pwm < 10)
+		pwm = 10;
+	else if (pwm > 100)
+		pwm = 100;
+	pwm = (pwm * 2559) / 1000;
+	buf[0] = pwm;
+	rc = fan_write_reg(0x30 + (id * 2), buf, 1);
+	if (rc < 0)
+		return rc;
+	return 0;
+}
+
+static int get_pwm_fan(int fan_index)
+{
+	unsigned char failure;
+	unsigned char active;
+	unsigned char buf[2];
+	int rc, id;
+
+	if (fcu_fans[fan_index].type != FCU_FAN_PWM)
+		return -EINVAL;
+	id = fcu_fans[fan_index].id; 
+	if (id == FCU_FAN_ABSENT_ID)
+		return -EINVAL;
+
+	rc = fan_read_reg(0x2b, &failure, 1);
+	if (rc != 1)
+		return -EIO;
+	if ((failure & (1 << id)) != 0)
+		return -EFAULT;
+	rc = fan_read_reg(0x2d, &active, 1);
+	if (rc != 1)
+		return -EIO;
+	if ((active & (1 << id)) == 0)
+		return -ENXIO;
+
+	/* Programmed value or real current speed */
+	rc = fan_read_reg(0x30 + (id * 2), buf, 1);
+	if (rc != 1)
+		return -EIO;
+
+	return (buf[0] * 1000) / 2559;
+}
+
+/*
+ * Utility routine to read the CPU calibration EEPROM data
+ * from the device-tree
+ */
+static int read_eeprom(int cpu, struct mpu_data *out)
+{
+	struct device_node *np;
+	char nodename[64];
+	u8 *data;
+	int len;
+
+	/* prom.c routine for finding a node by path is a bit brain dead
+	 * and requires exact @xxx unit numbers. This is a bit ugly but
+	 * will work for these machines
+	 */
+	sprintf(nodename, "/u3@0,f8000000/i2c@f8001000/cpuid@a%d", cpu ? 2 : 0);
+	np = of_find_node_by_path(nodename);
+	if (np == NULL) {
+		printk(KERN_ERR "therm_pm72: Failed to retreive cpuid node from device-tree\n");
+		return -ENODEV;
+	}
+	data = (u8 *)get_property(np, "cpuid", &len);
+	if (data == NULL) {
+		printk(KERN_ERR "therm_pm72: Failed to retreive cpuid property from device-tree\n");
+		of_node_put(np);
+		return -ENODEV;
+	}
+	memcpy(out, data, sizeof(struct mpu_data));
+	of_node_put(np);
+	
+	return 0;
+}
+
+static void fetch_cpu_pumps_minmax(void)
+{
+	struct cpu_pid_state *state0 = &cpu_state[0];
+	struct cpu_pid_state *state1 = &cpu_state[1];
+	u16 pump_min = 0, pump_max = 0xffff;
+	u16 tmp[4];
+
+	/* Try to fetch pumps min/max infos from eeprom */
+
+	memcpy(&tmp, &state0->mpu.processor_part_num, 8);
+	if (tmp[0] != 0xffff && tmp[1] != 0xffff) {
+		pump_min = max(pump_min, tmp[0]);
+		pump_max = min(pump_max, tmp[1]);
+	}
+	if (tmp[2] != 0xffff && tmp[3] != 0xffff) {
+		pump_min = max(pump_min, tmp[2]);
+		pump_max = min(pump_max, tmp[3]);
+	}
+
+	/* Double check the values, this _IS_ needed as the EEPROM on
+	 * some dual 2.5Ghz G5s seem, at least, to have both min & max
+	 * same to the same value ... (grrrr)
+	 */
+	if (pump_min == pump_max || pump_min == 0 || pump_max == 0xffff) {
+		pump_min = CPU_PUMP_OUTPUT_MIN;
+		pump_max = CPU_PUMP_OUTPUT_MAX;
+	}
+
+	state0->pump_min = state1->pump_min = pump_min;
+	state0->pump_max = state1->pump_max = pump_max;
+}
+
+/* 
+ * Now, unfortunately, sysfs doesn't give us a nice void * we could
+ * pass around to the attribute functions, so we don't really have
+ * choice but implement a bunch of them...
+ *
+ * That sucks a bit, we take the lock because FIX32TOPRINT evaluates
+ * the input twice... I accept patches :)
+ */
+#define BUILD_SHOW_FUNC_FIX(name, data)				\
+static ssize_t show_##name(struct device *dev, char *buf)	\
+{								\
+	ssize_t r;						\
+	down(&driver_lock);					\
+	r = sprintf(buf, "%d.%03d", FIX32TOPRINT(data));	\
+	up(&driver_lock);					\
+	return r;						\
+}
+#define BUILD_SHOW_FUNC_INT(name, data)				\
+static ssize_t show_##name(struct device *dev, char *buf)	\
+{								\
+	return sprintf(buf, "%d", data);			\
+}
+
+BUILD_SHOW_FUNC_FIX(cpu0_temperature, cpu_state[0].last_temp)
+BUILD_SHOW_FUNC_FIX(cpu0_voltage, cpu_state[0].voltage)
+BUILD_SHOW_FUNC_FIX(cpu0_current, cpu_state[0].current_a)
+BUILD_SHOW_FUNC_INT(cpu0_exhaust_fan_rpm, cpu_state[0].rpm)
+BUILD_SHOW_FUNC_INT(cpu0_intake_fan_rpm, cpu_state[0].intake_rpm)
+
+BUILD_SHOW_FUNC_FIX(cpu1_temperature, cpu_state[1].last_temp)
+BUILD_SHOW_FUNC_FIX(cpu1_voltage, cpu_state[1].voltage)
+BUILD_SHOW_FUNC_FIX(cpu1_current, cpu_state[1].current_a)
+BUILD_SHOW_FUNC_INT(cpu1_exhaust_fan_rpm, cpu_state[1].rpm)
+BUILD_SHOW_FUNC_INT(cpu1_intake_fan_rpm, cpu_state[1].intake_rpm)
+
+BUILD_SHOW_FUNC_FIX(backside_temperature, backside_state.last_temp)
+BUILD_SHOW_FUNC_INT(backside_fan_pwm, backside_state.pwm)
+
+BUILD_SHOW_FUNC_FIX(drives_temperature, drives_state.last_temp)
+BUILD_SHOW_FUNC_INT(drives_fan_rpm, drives_state.rpm)
+
+BUILD_SHOW_FUNC_FIX(dimms_temperature, dimms_state.last_temp)
+
+static DEVICE_ATTR(cpu0_temperature,S_IRUGO,show_cpu0_temperature,NULL);
+static DEVICE_ATTR(cpu0_voltage,S_IRUGO,show_cpu0_voltage,NULL);
+static DEVICE_ATTR(cpu0_current,S_IRUGO,show_cpu0_current,NULL);
+static DEVICE_ATTR(cpu0_exhaust_fan_rpm,S_IRUGO,show_cpu0_exhaust_fan_rpm,NULL);
+static DEVICE_ATTR(cpu0_intake_fan_rpm,S_IRUGO,show_cpu0_intake_fan_rpm,NULL);
+
+static DEVICE_ATTR(cpu1_temperature,S_IRUGO,show_cpu1_temperature,NULL);
+static DEVICE_ATTR(cpu1_voltage,S_IRUGO,show_cpu1_voltage,NULL);
+static DEVICE_ATTR(cpu1_current,S_IRUGO,show_cpu1_current,NULL);
+static DEVICE_ATTR(cpu1_exhaust_fan_rpm,S_IRUGO,show_cpu1_exhaust_fan_rpm,NULL);
+static DEVICE_ATTR(cpu1_intake_fan_rpm,S_IRUGO,show_cpu1_intake_fan_rpm,NULL);
+
+static DEVICE_ATTR(backside_temperature,S_IRUGO,show_backside_temperature,NULL);
+static DEVICE_ATTR(backside_fan_pwm,S_IRUGO,show_backside_fan_pwm,NULL);
+
+static DEVICE_ATTR(drives_temperature,S_IRUGO,show_drives_temperature,NULL);
+static DEVICE_ATTR(drives_fan_rpm,S_IRUGO,show_drives_fan_rpm,NULL);
+
+static DEVICE_ATTR(dimms_temperature,S_IRUGO,show_dimms_temperature,NULL);
+
+/*
+ * CPUs fans control loop
+ */
+
+static int do_read_one_cpu_values(struct cpu_pid_state *state, s32 *temp, s32 *power)
+{
+	s32 ltemp, volts, amps;
+	int index, rc = 0;
+
+	/* Default (in case of error) */
+	*temp = state->cur_temp;
+	*power = state->cur_power;
+
+	if (cpu_pid_type == CPU_PID_TYPE_RACKMAC)
+		index = (state->index == 0) ?
+			CPU_A1_FAN_RPM_INDEX : CPU_B1_FAN_RPM_INDEX;
+	else
+		index = (state->index == 0) ?
+			CPUA_EXHAUST_FAN_RPM_INDEX : CPUB_EXHAUST_FAN_RPM_INDEX;
+
+	/* Read current fan status */
+	rc = get_rpm_fan(index, !RPM_PID_USE_ACTUAL_SPEED);
+	if (rc < 0) {
+		/* XXX What do we do now ? Nothing for now, keep old value, but
+		 * return error upstream
+		 */
+		DBG("  cpu %d, fan reading error !\n", state->index);
+	} else {
+		state->rpm = rc;
+		DBG("  cpu %d, exhaust RPM: %d\n", state->index, state->rpm);
+	}
+
+	/* Get some sensor readings and scale it */
+	ltemp = read_smon_adc(state, 1);
+	if (ltemp == -1) {
+		/* XXX What do we do now ? */
+		state->overtemp++;
+		if (rc == 0)
+			rc = -EIO;
+		DBG("  cpu %d, temp reading error !\n", state->index);
+	} else {
+		/* Fixup temperature according to diode calibration
+		 */
+		DBG("  cpu %d, temp raw: %04x, m_diode: %04x, b_diode: %04x\n",
+		    state->index,
+		    ltemp, state->mpu.mdiode, state->mpu.bdiode);
+		*temp = ((s32)ltemp * (s32)state->mpu.mdiode + ((s32)state->mpu.bdiode << 12)) >> 2;
+		state->last_temp = *temp;
+		DBG("  temp: %d.%03d\n", FIX32TOPRINT((*temp)));
+	}
+
+	/*
+	 * Read voltage & current and calculate power
+	 */
+	volts = read_smon_adc(state, 3);
+	amps = read_smon_adc(state, 4);
+
+	/* Scale voltage and current raw sensor values according to fixed scales
+	 * obtained in Darwin and calculate power from I and V
+	 */
+	volts *= ADC_CPU_VOLTAGE_SCALE;
+	amps *= ADC_CPU_CURRENT_SCALE;
+	*power = (((u64)volts) * ((u64)amps)) >> 16;
+	state->voltage = volts;
+	state->current_a = amps;
+	state->last_power = *power;
+
+	DBG("  cpu %d, current: %d.%03d, voltage: %d.%03d, power: %d.%03d W\n",
+	    state->index, FIX32TOPRINT(state->current_a),
+	    FIX32TOPRINT(state->voltage), FIX32TOPRINT(*power));
+
+	return 0;
+}
+
+static void do_cpu_pid(struct cpu_pid_state *state, s32 temp, s32 power)
+{
+	s32 power_target, integral, derivative, proportional, adj_in_target, sval;
+	s64 integ_p, deriv_p, prop_p, sum; 
+	int i;
+
+	/* Calculate power target value (could be done once for all)
+	 * and convert to a 16.16 fp number
+	 */
+	power_target = ((u32)(state->mpu.pmaxh - state->mpu.padjmax)) << 16;
+	DBG("  power target: %d.%03d, error: %d.%03d\n",
+	    FIX32TOPRINT(power_target), FIX32TOPRINT(power_target - power));
+
+	/* Store temperature and power in history array */
+	state->cur_temp = (state->cur_temp + 1) % CPU_TEMP_HISTORY_SIZE;
+	state->temp_history[state->cur_temp] = temp;
+	state->cur_power = (state->cur_power + 1) % state->count_power;
+	state->power_history[state->cur_power] = power;
+	state->error_history[state->cur_power] = power_target - power;
+	
+	/* If first loop, fill the history table */
+	if (state->first) {
+		for (i = 0; i < (state->count_power - 1); i++) {
+			state->cur_power = (state->cur_power + 1) % state->count_power;
+			state->power_history[state->cur_power] = power;
+			state->error_history[state->cur_power] = power_target - power;
+		}
+		for (i = 0; i < (CPU_TEMP_HISTORY_SIZE - 1); i++) {
+			state->cur_temp = (state->cur_temp + 1) % CPU_TEMP_HISTORY_SIZE;
+			state->temp_history[state->cur_temp] = temp;			
+		}
+		state->first = 0;
+	}
+
+	/* Calculate the integral term normally based on the "power" values */
+	sum = 0;
+	integral = 0;
+	for (i = 0; i < state->count_power; i++)
+		integral += state->error_history[i];
+	integral *= CPU_PID_INTERVAL;
+	DBG("  integral: %08x\n", integral);
+
+	/* Calculate the adjusted input (sense value).
+	 *   G_r is 12.20
+	 *   integ is 16.16
+	 *   so the result is 28.36
+	 *
+	 * input target is mpu.ttarget, input max is mpu.tmax
+	 */
+	integ_p = ((s64)state->mpu.pid_gr) * (s64)integral;
+	DBG("   integ_p: %d\n", (int)(integ_p >> 36));
+	sval = (state->mpu.tmax << 16) - ((integ_p >> 20) & 0xffffffff);
+	adj_in_target = (state->mpu.ttarget << 16);
+	if (adj_in_target > sval)
+		adj_in_target = sval;
+	DBG("   adj_in_target: %d.%03d, ttarget: %d\n", FIX32TOPRINT(adj_in_target),
+	    state->mpu.ttarget);
+
+	/* Calculate the derivative term */
+	derivative = state->temp_history[state->cur_temp] -
+		state->temp_history[(state->cur_temp + CPU_TEMP_HISTORY_SIZE - 1)
+				    % CPU_TEMP_HISTORY_SIZE];
+	derivative /= CPU_PID_INTERVAL;
+	deriv_p = ((s64)state->mpu.pid_gd) * (s64)derivative;
+	DBG("   deriv_p: %d\n", (int)(deriv_p >> 36));
+	sum += deriv_p;
+
+	/* Calculate the proportional term */
+	proportional = temp - adj_in_target;
+	prop_p = ((s64)state->mpu.pid_gp) * (s64)proportional;
+	DBG("   prop_p: %d\n", (int)(prop_p >> 36));
+	sum += prop_p;
+
+	/* Scale sum */
+	sum >>= 36;
+
+	DBG("   sum: %d\n", (int)sum);
+	state->rpm += (s32)sum;
+}
+
+static void do_monitor_cpu_combined(void)
+{
+	struct cpu_pid_state *state0 = &cpu_state[0];
+	struct cpu_pid_state *state1 = &cpu_state[1];
+	s32 temp0, power0, temp1, power1;
+	s32 temp_combi, power_combi;
+	int rc, intake, pump;
+
+	rc = do_read_one_cpu_values(state0, &temp0, &power0);
+	if (rc < 0) {
+		/* XXX What do we do now ? */
+	}
+	state1->overtemp = 0;
+	rc = do_read_one_cpu_values(state1, &temp1, &power1);
+	if (rc < 0) {
+		/* XXX What do we do now ? */
+	}
+	if (state1->overtemp)
+		state0->overtemp++;
+
+	temp_combi = max(temp0, temp1);
+	power_combi = max(power0, power1);
+
+	/* Check tmax, increment overtemp if we are there. At tmax+8, we go
+	 * full blown immediately and try to trigger a shutdown
+	 */
+	if (temp_combi >= ((state0->mpu.tmax + 8) << 16)) {
+		printk(KERN_WARNING "Warning ! Temperature way above maximum (%d) !\n",
+		       temp_combi >> 16);
+		state0->overtemp = CPU_MAX_OVERTEMP;
+	} else if (temp_combi > (state0->mpu.tmax << 16))
+		state0->overtemp++;
+	else
+		state0->overtemp = 0;
+	if (state0->overtemp >= CPU_MAX_OVERTEMP)
+		critical_state = 1;
+	if (state0->overtemp > 0) {
+		state0->rpm = state0->mpu.rmaxn_exhaust_fan;
+		state0->intake_rpm = intake = state0->mpu.rmaxn_intake_fan;
+		pump = state0->pump_min;
+		goto do_set_fans;
+	}
+
+	/* Do the PID */
+	do_cpu_pid(state0, temp_combi, power_combi);
+
+	/* Range check */
+	state0->rpm = max(state0->rpm, (int)state0->mpu.rminn_exhaust_fan);
+	state0->rpm = min(state0->rpm, (int)state0->mpu.rmaxn_exhaust_fan);
+
+	/* Calculate intake fan speed */
+	intake = (state0->rpm * CPU_INTAKE_SCALE) >> 16;
+	intake = max(intake, (int)state0->mpu.rminn_intake_fan);
+	intake = min(intake, (int)state0->mpu.rmaxn_intake_fan);
+	state0->intake_rpm = intake;
+
+	/* Calculate pump speed */
+	pump = (state0->rpm * state0->pump_max) /
+		state0->mpu.rmaxn_exhaust_fan;
+	pump = min(pump, state0->pump_max);
+	pump = max(pump, state0->pump_min);
+	
+ do_set_fans:
+	/* We copy values from state 0 to state 1 for /sysfs */
+	state1->rpm = state0->rpm;
+	state1->intake_rpm = state0->intake_rpm;
+
+	DBG("** CPU %d RPM: %d Ex, %d, Pump: %d, In, overtemp: %d\n",
+	    state1->index, (int)state1->rpm, intake, pump, state1->overtemp);
+
+	/* We should check for errors, shouldn't we ? But then, what
+	 * do we do once the error occurs ? For FCU notified fan
+	 * failures (-EFAULT) we probably want to notify userland
+	 * some way...
+	 */
+	set_rpm_fan(CPUA_INTAKE_FAN_RPM_INDEX, intake);
+	set_rpm_fan(CPUA_EXHAUST_FAN_RPM_INDEX, state0->rpm);
+	set_rpm_fan(CPUB_INTAKE_FAN_RPM_INDEX, intake);
+	set_rpm_fan(CPUB_EXHAUST_FAN_RPM_INDEX, state0->rpm);
+
+	if (fcu_fans[CPUA_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID)
+		set_rpm_fan(CPUA_PUMP_RPM_INDEX, pump);
+	if (fcu_fans[CPUB_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID)
+		set_rpm_fan(CPUB_PUMP_RPM_INDEX, pump);
+}
+
+static void do_monitor_cpu_split(struct cpu_pid_state *state)
+{
+	s32 temp, power;
+	int rc, intake;
+
+	/* Read current fan status */
+	rc = do_read_one_cpu_values(state, &temp, &power);
+	if (rc < 0) {
+		/* XXX What do we do now ? */
+	}
+
+	/* Check tmax, increment overtemp if we are there. At tmax+8, we go
+	 * full blown immediately and try to trigger a shutdown
+	 */
+	if (temp >= ((state->mpu.tmax + 8) << 16)) {
+		printk(KERN_WARNING "Warning ! CPU %d temperature way above maximum"
+		       " (%d) !\n",
+		       state->index, temp >> 16);
+		state->overtemp = CPU_MAX_OVERTEMP;
+	} else if (temp > (state->mpu.tmax << 16))
+		state->overtemp++;
+	else
+		state->overtemp = 0;
+	if (state->overtemp >= CPU_MAX_OVERTEMP)
+		critical_state = 1;
+	if (state->overtemp > 0) {
+		state->rpm = state->mpu.rmaxn_exhaust_fan;
+		state->intake_rpm = intake = state->mpu.rmaxn_intake_fan;
+		goto do_set_fans;
+	}
+
+	/* Do the PID */
+	do_cpu_pid(state, temp, power);
+
+	/* Range check */
+	state->rpm = max(state->rpm, (int)state->mpu.rminn_exhaust_fan);
+	state->rpm = min(state->rpm, (int)state->mpu.rmaxn_exhaust_fan);
+
+	/* Calculate intake fan */
+	intake = (state->rpm * CPU_INTAKE_SCALE) >> 16;
+	intake = max(intake, (int)state->mpu.rminn_intake_fan);
+	intake = min(intake, (int)state->mpu.rmaxn_intake_fan);
+	state->intake_rpm = intake;
+
+ do_set_fans:
+	DBG("** CPU %d RPM: %d Ex, %d In, overtemp: %d\n",
+	    state->index, (int)state->rpm, intake, state->overtemp);
+
+	/* We should check for errors, shouldn't we ? But then, what
+	 * do we do once the error occurs ? For FCU notified fan
+	 * failures (-EFAULT) we probably want to notify userland
+	 * some way...
+	 */
+	if (state->index == 0) {
+		set_rpm_fan(CPUA_INTAKE_FAN_RPM_INDEX, intake);
+		set_rpm_fan(CPUA_EXHAUST_FAN_RPM_INDEX, state->rpm);
+	} else {
+		set_rpm_fan(CPUB_INTAKE_FAN_RPM_INDEX, intake);
+		set_rpm_fan(CPUB_EXHAUST_FAN_RPM_INDEX, state->rpm);
+	}
+}
+
+static void do_monitor_cpu_rack(struct cpu_pid_state *state)
+{
+	s32 temp, power, fan_min;
+	int rc;
+
+	/* Read current fan status */
+	rc = do_read_one_cpu_values(state, &temp, &power);
+	if (rc < 0) {
+		/* XXX What do we do now ? */
+	}
+
+	/* Check tmax, increment overtemp if we are there. At tmax+8, we go
+	 * full blown immediately and try to trigger a shutdown
+	 */
+	if (temp >= ((state->mpu.tmax + 8) << 16)) {
+		printk(KERN_WARNING "Warning ! CPU %d temperature way above maximum"
+		       " (%d) !\n",
+		       state->index, temp >> 16);
+		state->overtemp = CPU_MAX_OVERTEMP;
+	} else if (temp > (state->mpu.tmax << 16))
+		state->overtemp++;
+	else
+		state->overtemp = 0;
+	if (state->overtemp >= CPU_MAX_OVERTEMP)
+		critical_state = 1;
+	if (state->overtemp > 0) {
+		state->rpm = state->intake_rpm = state->mpu.rmaxn_intake_fan;
+		goto do_set_fans;
+	}
+
+	/* Do the PID */
+	do_cpu_pid(state, temp, power);
+
+	/* Check clamp from dimms */
+	fan_min = dimm_output_clamp;
+	fan_min = max(fan_min, (int)state->mpu.rminn_intake_fan);
+
+	state->rpm = max(state->rpm, (int)fan_min);
+	state->rpm = min(state->rpm, (int)state->mpu.rmaxn_intake_fan);
+	state->intake_rpm = state->rpm;
+
+ do_set_fans:
+	DBG("** CPU %d RPM: %d overtemp: %d\n",
+	    state->index, (int)state->rpm, state->overtemp);
+
+	/* We should check for errors, shouldn't we ? But then, what
+	 * do we do once the error occurs ? For FCU notified fan
+	 * failures (-EFAULT) we probably want to notify userland
+	 * some way...
+	 */
+	if (state->index == 0) {
+		set_rpm_fan(CPU_A1_FAN_RPM_INDEX, state->rpm);
+		set_rpm_fan(CPU_A2_FAN_RPM_INDEX, state->rpm);
+		set_rpm_fan(CPU_A3_FAN_RPM_INDEX, state->rpm);
+	} else {
+		set_rpm_fan(CPU_B1_FAN_RPM_INDEX, state->rpm);
+		set_rpm_fan(CPU_B2_FAN_RPM_INDEX, state->rpm);
+		set_rpm_fan(CPU_B3_FAN_RPM_INDEX, state->rpm);
+	}
+}
+
+/*
+ * Initialize the state structure for one CPU control loop
+ */
+static int init_cpu_state(struct cpu_pid_state *state, int index)
+{
+	state->index = index;
+	state->first = 1;
+	state->rpm = (cpu_pid_type == CPU_PID_TYPE_RACKMAC) ? 4000 : 1000;
+	state->overtemp = 0;
+	state->adc_config = 0x00;
+
+
+	if (index == 0)
+		state->monitor = attach_i2c_chip(SUPPLY_MONITOR_ID, "CPU0_monitor");
+	else if (index == 1)
+		state->monitor = attach_i2c_chip(SUPPLY_MONITORB_ID, "CPU1_monitor");
+	if (state->monitor == NULL)
+		goto fail;
+
+	if (read_eeprom(index, &state->mpu))
+		goto fail;
+
+	state->count_power = state->mpu.tguardband;
+	if (state->count_power > CPU_POWER_HISTORY_SIZE) {
+		printk(KERN_WARNING "Warning ! too many power history slots\n");
+		state->count_power = CPU_POWER_HISTORY_SIZE;
+	}
+	DBG("CPU %d Using %d power history entries\n", index, state->count_power);
+
+	if (index == 0) {
+		device_create_file(&of_dev->dev, &dev_attr_cpu0_temperature);
+		device_create_file(&of_dev->dev, &dev_attr_cpu0_voltage);
+		device_create_file(&of_dev->dev, &dev_attr_cpu0_current);
+		device_create_file(&of_dev->dev, &dev_attr_cpu0_exhaust_fan_rpm);
+		device_create_file(&of_dev->dev, &dev_attr_cpu0_intake_fan_rpm);
+	} else {
+		device_create_file(&of_dev->dev, &dev_attr_cpu1_temperature);
+		device_create_file(&of_dev->dev, &dev_attr_cpu1_voltage);
+		device_create_file(&of_dev->dev, &dev_attr_cpu1_current);
+		device_create_file(&of_dev->dev, &dev_attr_cpu1_exhaust_fan_rpm);
+		device_create_file(&of_dev->dev, &dev_attr_cpu1_intake_fan_rpm);
+	}
+
+	return 0;
+ fail:
+	if (state->monitor)
+		detach_i2c_chip(state->monitor);
+	state->monitor = NULL;
+	
+	return -ENODEV;
+}
+
+/*
+ * Dispose of the state data for one CPU control loop
+ */
+static void dispose_cpu_state(struct cpu_pid_state *state)
+{
+	if (state->monitor == NULL)
+		return;
+
+	if (state->index == 0) {
+		device_remove_file(&of_dev->dev, &dev_attr_cpu0_temperature);
+		device_remove_file(&of_dev->dev, &dev_attr_cpu0_voltage);
+		device_remove_file(&of_dev->dev, &dev_attr_cpu0_current);
+		device_remove_file(&of_dev->dev, &dev_attr_cpu0_exhaust_fan_rpm);
+		device_remove_file(&of_dev->dev, &dev_attr_cpu0_intake_fan_rpm);
+	} else {
+		device_remove_file(&of_dev->dev, &dev_attr_cpu1_temperature);
+		device_remove_file(&of_dev->dev, &dev_attr_cpu1_voltage);
+		device_remove_file(&of_dev->dev, &dev_attr_cpu1_current);
+		device_remove_file(&of_dev->dev, &dev_attr_cpu1_exhaust_fan_rpm);
+		device_remove_file(&of_dev->dev, &dev_attr_cpu1_intake_fan_rpm);
+	}
+
+	detach_i2c_chip(state->monitor);
+	state->monitor = NULL;
+}
+
+/*
+ * Motherboard backside & U3 heatsink fan control loop
+ */
+static void do_monitor_backside(struct backside_pid_state *state)
+{
+	s32 temp, integral, derivative, fan_min;
+	s64 integ_p, deriv_p, prop_p, sum; 
+	int i, rc;
+
+	if (--state->ticks != 0)
+		return;
+	state->ticks = backside_params.interval;
+
+	DBG("backside:\n");
+
+	/* Check fan status */
+	rc = get_pwm_fan(BACKSIDE_FAN_PWM_INDEX);
+	if (rc < 0) {
+		printk(KERN_WARNING "Error %d reading backside fan !\n", rc);
+		/* XXX What do we do now ? */
+	} else
+		state->pwm = rc;
+	DBG("  current pwm: %d\n", state->pwm);
+
+	/* Get some sensor readings */
+	temp = i2c_smbus_read_byte_data(state->monitor, MAX6690_EXT_TEMP) << 16;
+	state->last_temp = temp;
+	DBG("  temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp),
+	    FIX32TOPRINT(backside_params.input_target));
+
+	/* Store temperature and error in history array */
+	state->cur_sample = (state->cur_sample + 1) % BACKSIDE_PID_HISTORY_SIZE;
+	state->sample_history[state->cur_sample] = temp;
+	state->error_history[state->cur_sample] = temp - backside_params.input_target;
+	
+	/* If first loop, fill the history table */
+	if (state->first) {
+		for (i = 0; i < (BACKSIDE_PID_HISTORY_SIZE - 1); i++) {
+			state->cur_sample = (state->cur_sample + 1) %
+				BACKSIDE_PID_HISTORY_SIZE;
+			state->sample_history[state->cur_sample] = temp;
+			state->error_history[state->cur_sample] =
+				temp - backside_params.input_target;
+		}
+		state->first = 0;
+	}
+
+	/* Calculate the integral term */
+	sum = 0;
+	integral = 0;
+	for (i = 0; i < BACKSIDE_PID_HISTORY_SIZE; i++)
+		integral += state->error_history[i];
+	integral *= backside_params.interval;
+	DBG("  integral: %08x\n", integral);
+	integ_p = ((s64)backside_params.G_r) * (s64)integral;
+	DBG("   integ_p: %d\n", (int)(integ_p >> 36));
+	sum += integ_p;
+
+	/* Calculate the derivative term */
+	derivative = state->error_history[state->cur_sample] -
+		state->error_history[(state->cur_sample + BACKSIDE_PID_HISTORY_SIZE - 1)
+				    % BACKSIDE_PID_HISTORY_SIZE];
+	derivative /= backside_params.interval;
+	deriv_p = ((s64)backside_params.G_d) * (s64)derivative;
+	DBG("   deriv_p: %d\n", (int)(deriv_p >> 36));
+	sum += deriv_p;
+
+	/* Calculate the proportional term */
+	prop_p = ((s64)backside_params.G_p) * (s64)(state->error_history[state->cur_sample]);
+	DBG("   prop_p: %d\n", (int)(prop_p >> 36));
+	sum += prop_p;
+
+	/* Scale sum */
+	sum >>= 36;
+
+	DBG("   sum: %d\n", (int)sum);
+	if (backside_params.additive)
+		state->pwm += (s32)sum;
+	else
+		state->pwm = sum;
+
+	/* Check for clamp */
+	fan_min = (dimm_output_clamp * 100) / 14000;
+	fan_min = max(fan_min, backside_params.output_min);
+
+	state->pwm = max(state->pwm, fan_min);
+	state->pwm = min(state->pwm, backside_params.output_max);
+
+	DBG("** BACKSIDE PWM: %d\n", (int)state->pwm);
+	set_pwm_fan(BACKSIDE_FAN_PWM_INDEX, state->pwm);
+}
+
+/*
+ * Initialize the state structure for the backside fan control loop
+ */
+static int init_backside_state(struct backside_pid_state *state)
+{
+	struct device_node *u3;
+	int u3h = 1; /* conservative by default */
+
+	/*
+	 * There are different PID params for machines with U3 and machines
+	 * with U3H, pick the right ones now
+	 */
+	u3 = of_find_node_by_path("/u3@0,f8000000");
+	if (u3 != NULL) {
+		u32 *vers = (u32 *)get_property(u3, "device-rev", NULL);
+		if (vers)
+			if (((*vers) & 0x3f) < 0x34)
+				u3h = 0;
+		of_node_put(u3);
+	}
+
+	if (rackmac) {
+		backside_params.G_d = BACKSIDE_PID_RACK_G_d;
+		backside_params.input_target = BACKSIDE_PID_RACK_INPUT_TARGET;
+		backside_params.output_min = BACKSIDE_PID_U3H_OUTPUT_MIN;
+		backside_params.interval = BACKSIDE_PID_RACK_INTERVAL;
+		backside_params.G_p = BACKSIDE_PID_RACK_G_p;
+		backside_params.G_r = BACKSIDE_PID_G_r;
+		backside_params.output_max = BACKSIDE_PID_OUTPUT_MAX;
+		backside_params.additive = 0;
+	} else if (u3h) {
+		backside_params.G_d = BACKSIDE_PID_U3H_G_d;
+		backside_params.input_target = BACKSIDE_PID_U3H_INPUT_TARGET;
+		backside_params.output_min = BACKSIDE_PID_U3H_OUTPUT_MIN;
+		backside_params.interval = BACKSIDE_PID_INTERVAL;
+		backside_params.G_p = BACKSIDE_PID_G_p;
+		backside_params.G_r = BACKSIDE_PID_G_r;
+		backside_params.output_max = BACKSIDE_PID_OUTPUT_MAX;
+		backside_params.additive = 1;
+	} else {
+		backside_params.G_d = BACKSIDE_PID_U3_G_d;
+		backside_params.input_target = BACKSIDE_PID_U3_INPUT_TARGET;
+		backside_params.output_min = BACKSIDE_PID_U3_OUTPUT_MIN;
+		backside_params.interval = BACKSIDE_PID_INTERVAL;
+		backside_params.G_p = BACKSIDE_PID_G_p;
+		backside_params.G_r = BACKSIDE_PID_G_r;
+		backside_params.output_max = BACKSIDE_PID_OUTPUT_MAX;
+		backside_params.additive = 1;
+	}
+
+	state->ticks = 1;
+	state->first = 1;
+	state->pwm = 50;
+
+	state->monitor = attach_i2c_chip(BACKSIDE_MAX_ID, "backside_temp");
+	if (state->monitor == NULL)
+		return -ENODEV;
+
+	device_create_file(&of_dev->dev, &dev_attr_backside_temperature);
+	device_create_file(&of_dev->dev, &dev_attr_backside_fan_pwm);
+
+	return 0;
+}
+
+/*
+ * Dispose of the state data for the backside control loop
+ */
+static void dispose_backside_state(struct backside_pid_state *state)
+{
+	if (state->monitor == NULL)
+		return;
+
+	device_remove_file(&of_dev->dev, &dev_attr_backside_temperature);
+	device_remove_file(&of_dev->dev, &dev_attr_backside_fan_pwm);
+
+	detach_i2c_chip(state->monitor);
+	state->monitor = NULL;
+}
+ 
+/*
+ * Drives bay fan control loop
+ */
+static void do_monitor_drives(struct drives_pid_state *state)
+{
+	s32 temp, integral, derivative;
+	s64 integ_p, deriv_p, prop_p, sum; 
+	int i, rc;
+
+	if (--state->ticks != 0)
+		return;
+	state->ticks = DRIVES_PID_INTERVAL;
+
+	DBG("drives:\n");
+
+	/* Check fan status */
+	rc = get_rpm_fan(DRIVES_FAN_RPM_INDEX, !RPM_PID_USE_ACTUAL_SPEED);
+	if (rc < 0) {
+		printk(KERN_WARNING "Error %d reading drives fan !\n", rc);
+		/* XXX What do we do now ? */
+	} else
+		state->rpm = rc;
+	DBG("  current rpm: %d\n", state->rpm);
+
+	/* Get some sensor readings */
+	temp = le16_to_cpu(i2c_smbus_read_word_data(state->monitor, DS1775_TEMP)) << 8;
+	state->last_temp = temp;
+	DBG("  temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp),
+	    FIX32TOPRINT(DRIVES_PID_INPUT_TARGET));
+
+	/* Store temperature and error in history array */
+	state->cur_sample = (state->cur_sample + 1) % DRIVES_PID_HISTORY_SIZE;
+	state->sample_history[state->cur_sample] = temp;
+	state->error_history[state->cur_sample] = temp - DRIVES_PID_INPUT_TARGET;
+	
+	/* If first loop, fill the history table */
+	if (state->first) {
+		for (i = 0; i < (DRIVES_PID_HISTORY_SIZE - 1); i++) {
+			state->cur_sample = (state->cur_sample + 1) %
+				DRIVES_PID_HISTORY_SIZE;
+			state->sample_history[state->cur_sample] = temp;
+			state->error_history[state->cur_sample] =
+				temp - DRIVES_PID_INPUT_TARGET;
+		}
+		state->first = 0;
+	}
+
+	/* Calculate the integral term */
+	sum = 0;
+	integral = 0;
+	for (i = 0; i < DRIVES_PID_HISTORY_SIZE; i++)
+		integral += state->error_history[i];
+	integral *= DRIVES_PID_INTERVAL;
+	DBG("  integral: %08x\n", integral);
+	integ_p = ((s64)DRIVES_PID_G_r) * (s64)integral;
+	DBG("   integ_p: %d\n", (int)(integ_p >> 36));
+	sum += integ_p;
+
+	/* Calculate the derivative term */
+	derivative = state->error_history[state->cur_sample] -
+		state->error_history[(state->cur_sample + DRIVES_PID_HISTORY_SIZE - 1)
+				    % DRIVES_PID_HISTORY_SIZE];
+	derivative /= DRIVES_PID_INTERVAL;
+	deriv_p = ((s64)DRIVES_PID_G_d) * (s64)derivative;
+	DBG("   deriv_p: %d\n", (int)(deriv_p >> 36));
+	sum += deriv_p;
+
+	/* Calculate the proportional term */
+	prop_p = ((s64)DRIVES_PID_G_p) * (s64)(state->error_history[state->cur_sample]);
+	DBG("   prop_p: %d\n", (int)(prop_p >> 36));
+	sum += prop_p;
+
+	/* Scale sum */
+	sum >>= 36;
+
+	DBG("   sum: %d\n", (int)sum);
+	state->rpm += (s32)sum;
+
+	state->rpm = max(state->rpm, DRIVES_PID_OUTPUT_MIN);
+	state->rpm = min(state->rpm, DRIVES_PID_OUTPUT_MAX);
+
+	DBG("** DRIVES RPM: %d\n", (int)state->rpm);
+	set_rpm_fan(DRIVES_FAN_RPM_INDEX, state->rpm);
+}
+
+/*
+ * Initialize the state structure for the drives bay fan control loop
+ */
+static int init_drives_state(struct drives_pid_state *state)
+{
+	state->ticks = 1;
+	state->first = 1;
+	state->rpm = 1000;
+
+	state->monitor = attach_i2c_chip(DRIVES_DALLAS_ID, "drives_temp");
+	if (state->monitor == NULL)
+		return -ENODEV;
+
+	device_create_file(&of_dev->dev, &dev_attr_drives_temperature);
+	device_create_file(&of_dev->dev, &dev_attr_drives_fan_rpm);
+
+	return 0;
+}
+
+/*
+ * Dispose of the state data for the drives control loop
+ */
+static void dispose_drives_state(struct drives_pid_state *state)
+{
+	if (state->monitor == NULL)
+		return;
+
+	device_remove_file(&of_dev->dev, &dev_attr_drives_temperature);
+	device_remove_file(&of_dev->dev, &dev_attr_drives_fan_rpm);
+
+	detach_i2c_chip(state->monitor);
+	state->monitor = NULL;
+}
+
+/*
+ * DIMMs temp control loop
+ */
+static void do_monitor_dimms(struct dimm_pid_state *state)
+{
+	s32 temp, integral, derivative, fan_min;
+	s64 integ_p, deriv_p, prop_p, sum;
+	int i;
+
+	if (--state->ticks != 0)
+		return;
+	state->ticks = DIMM_PID_INTERVAL;
+
+	DBG("DIMM:\n");
+
+	DBG("  current value: %d\n", state->output);
+
+	temp = read_lm87_reg(state->monitor, LM87_INT_TEMP);
+	if (temp < 0)
+		return;
+	temp <<= 16;
+	state->last_temp = temp;
+	DBG("  temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp),
+	    FIX32TOPRINT(DIMM_PID_INPUT_TARGET));
+
+	/* Store temperature and error in history array */
+	state->cur_sample = (state->cur_sample + 1) % DIMM_PID_HISTORY_SIZE;
+	state->sample_history[state->cur_sample] = temp;
+	state->error_history[state->cur_sample] = temp - DIMM_PID_INPUT_TARGET;
+
+	/* If first loop, fill the history table */
+	if (state->first) {
+		for (i = 0; i < (DIMM_PID_HISTORY_SIZE - 1); i++) {
+			state->cur_sample = (state->cur_sample + 1) %
+				DIMM_PID_HISTORY_SIZE;
+			state->sample_history[state->cur_sample] = temp;
+			state->error_history[state->cur_sample] =
+				temp - DIMM_PID_INPUT_TARGET;
+		}
+		state->first = 0;
+	}
+
+	/* Calculate the integral term */
+	sum = 0;
+	integral = 0;
+	for (i = 0; i < DIMM_PID_HISTORY_SIZE; i++)
+		integral += state->error_history[i];
+	integral *= DIMM_PID_INTERVAL;
+	DBG("  integral: %08x\n", integral);
+	integ_p = ((s64)DIMM_PID_G_r) * (s64)integral;
+	DBG("   integ_p: %d\n", (int)(integ_p >> 36));
+	sum += integ_p;
+
+	/* Calculate the derivative term */
+	derivative = state->error_history[state->cur_sample] -
+		state->error_history[(state->cur_sample + DIMM_PID_HISTORY_SIZE - 1)
+				    % DIMM_PID_HISTORY_SIZE];
+	derivative /= DIMM_PID_INTERVAL;
+	deriv_p = ((s64)DIMM_PID_G_d) * (s64)derivative;
+	DBG("   deriv_p: %d\n", (int)(deriv_p >> 36));
+	sum += deriv_p;
+
+	/* Calculate the proportional term */
+	prop_p = ((s64)DIMM_PID_G_p) * (s64)(state->error_history[state->cur_sample]);
+	DBG("   prop_p: %d\n", (int)(prop_p >> 36));
+	sum += prop_p;
+
+	/* Scale sum */
+	sum >>= 36;
+
+	DBG("   sum: %d\n", (int)sum);
+	state->output = (s32)sum;
+	state->output = max(state->output, DIMM_PID_OUTPUT_MIN);
+	state->output = min(state->output, DIMM_PID_OUTPUT_MAX);
+	dimm_output_clamp = state->output;
+
+	DBG("** DIMM clamp value: %d\n", (int)state->output);
+
+	/* Backside PID is only every 5 seconds, force backside fan clamping now */
+	fan_min = (dimm_output_clamp * 100) / 14000;
+	fan_min = max(fan_min, backside_params.output_min);
+	if (backside_state.pwm < fan_min) {
+		backside_state.pwm = fan_min;
+		DBG(" -> applying clamp to backside fan now: %d  !\n", fan_min);
+		set_pwm_fan(BACKSIDE_FAN_PWM_INDEX, fan_min);
+	}
+}
+
+/*
+ * Initialize the state structure for the DIMM temp control loop
+ */
+static int init_dimms_state(struct dimm_pid_state *state)
+{
+	state->ticks = 1;
+	state->first = 1;
+	state->output = 4000;
+
+	state->monitor = attach_i2c_chip(XSERVE_DIMMS_LM87, "dimms_temp");
+	if (state->monitor == NULL)
+		return -ENODEV;
+
+       	device_create_file(&of_dev->dev, &dev_attr_dimms_temperature);
+
+	return 0;
+}
+
+/*
+ * Dispose of the state data for the drives control loop
+ */
+static void dispose_dimms_state(struct dimm_pid_state *state)
+{
+	if (state->monitor == NULL)
+		return;
+
+	device_remove_file(&of_dev->dev, &dev_attr_dimms_temperature);
+
+	detach_i2c_chip(state->monitor);
+	state->monitor = NULL;
+}
+
+static int call_critical_overtemp(void)
+{
+	char *argv[] = { critical_overtemp_path, NULL };
+	static char *envp[] = { "HOME=/",
+				"TERM=linux",
+				"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
+				NULL };
+
+	return call_usermodehelper(critical_overtemp_path, argv, envp, 0);
+}
+
+
+/*
+ * Here's the kernel thread that calls the various control loops
+ */
+static int main_control_loop(void *x)
+{
+	daemonize("kfand");
+
+	DBG("main_control_loop started\n");
+
+	down(&driver_lock);
+
+	if (start_fcu() < 0) {
+		printk(KERN_ERR "kfand: failed to start FCU\n");
+		up(&driver_lock);
+		goto out;
+	}
+
+	/* Set the PCI fan once for now */
+	set_pwm_fan(SLOTS_FAN_PWM_INDEX, SLOTS_FAN_DEFAULT_PWM);
+
+	/* Initialize ADCs */
+	initialize_adc(&cpu_state[0]);
+	if (cpu_state[1].monitor != NULL)
+		initialize_adc(&cpu_state[1]);
+
+	up(&driver_lock);
+
+	while (state == state_attached) {
+		unsigned long elapsed, start;
+
+		start = jiffies;
+
+		down(&driver_lock);
+
+		/* First, we always calculate the new DIMMs state on an Xserve */
+		if (rackmac)
+			do_monitor_dimms(&dimms_state);
+
+		/* Then, the CPUs */
+		if (cpu_pid_type == CPU_PID_TYPE_COMBINED)
+			do_monitor_cpu_combined();
+		else if (cpu_pid_type == CPU_PID_TYPE_RACKMAC) {
+			do_monitor_cpu_rack(&cpu_state[0]);
+			if (cpu_state[1].monitor != NULL)
+				do_monitor_cpu_rack(&cpu_state[1]);
+			// better deal with UP
+		} else {
+			do_monitor_cpu_split(&cpu_state[0]);
+			if (cpu_state[1].monitor != NULL)
+				do_monitor_cpu_split(&cpu_state[1]);
+			// better deal with UP
+		}
+		/* Then, the rest */
+		do_monitor_backside(&backside_state);
+		if (!rackmac)
+			do_monitor_drives(&drives_state);
+		up(&driver_lock);
+
+		if (critical_state == 1) {
+			printk(KERN_WARNING "Temperature control detected a critical condition\n");
+			printk(KERN_WARNING "Attempting to shut down...\n");
+			if (call_critical_overtemp()) {
+				printk(KERN_WARNING "Can't call %s, power off now!\n",
+				       critical_overtemp_path);
+				machine_power_off();
+			}
+		}
+		if (critical_state > 0)
+			critical_state++;
+		if (critical_state > MAX_CRITICAL_STATE) {
+			printk(KERN_WARNING "Shutdown timed out, power off now !\n");
+			machine_power_off();
+		}
+
+		// FIXME: Deal with signals
+		set_current_state(TASK_INTERRUPTIBLE);
+		elapsed = jiffies - start;
+		if (elapsed < HZ)
+			schedule_timeout(HZ - elapsed);
+	}
+
+ out:
+	DBG("main_control_loop ended\n");
+
+	ctrl_task = 0;
+	complete_and_exit(&ctrl_complete, 0);
+}
+
+/*
+ * Dispose the control loops when tearing down
+ */
+static void dispose_control_loops(void)
+{
+	dispose_cpu_state(&cpu_state[0]);
+	dispose_cpu_state(&cpu_state[1]);
+	dispose_backside_state(&backside_state);
+	dispose_drives_state(&drives_state);
+	dispose_dimms_state(&dimms_state);
+}
+
+/*
+ * Create the control loops. U3-0 i2c bus is up, so we can now
+ * get to the various sensors
+ */
+static int create_control_loops(void)
+{
+	struct device_node *np;
+
+	/* Count CPUs from the device-tree, we don't care how many are
+	 * actually used by Linux
+	 */
+	cpu_count = 0;
+	for (np = NULL; NULL != (np = of_find_node_by_type(np, "cpu"));)
+		cpu_count++;
+
+	DBG("counted %d CPUs in the device-tree\n", cpu_count);
+
+	/* Decide the type of PID algorithm to use based on the presence of
+	 * the pumps, though that may not be the best way, that is good enough
+	 * for now
+	 */
+	if (rackmac)
+		cpu_pid_type = CPU_PID_TYPE_RACKMAC;
+	else if (machine_is_compatible("PowerMac7,3")
+	    && (cpu_count > 1)
+	    && fcu_fans[CPUA_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID
+	    && fcu_fans[CPUB_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID) {
+		printk(KERN_INFO "Liquid cooling pumps detected, using new algorithm !\n");
+		cpu_pid_type = CPU_PID_TYPE_COMBINED;
+	} else
+		cpu_pid_type = CPU_PID_TYPE_SPLIT;
+
+	/* Create control loops for everything. If any fail, everything
+	 * fails
+	 */
+	if (init_cpu_state(&cpu_state[0], 0))
+		goto fail;
+	if (cpu_pid_type == CPU_PID_TYPE_COMBINED)
+		fetch_cpu_pumps_minmax();
+
+	if (cpu_count > 1 && init_cpu_state(&cpu_state[1], 1))
+		goto fail;
+	if (init_backside_state(&backside_state))
+		goto fail;
+	if (rackmac && init_dimms_state(&dimms_state))
+		goto fail;
+	if (!rackmac && init_drives_state(&drives_state))
+		goto fail;
+
+	DBG("all control loops up !\n");
+
+	return 0;
+	
+ fail:
+	DBG("failure creating control loops, disposing\n");
+
+	dispose_control_loops();
+
+	return -ENODEV;
+}
+
+/*
+ * Start the control loops after everything is up, that is create
+ * the thread that will make them run
+ */
+static void start_control_loops(void)
+{
+	init_completion(&ctrl_complete);
+
+	ctrl_task = kernel_thread(main_control_loop, NULL, SIGCHLD | CLONE_KERNEL);
+}
+
+/*
+ * Stop the control loops when tearing down
+ */
+static void stop_control_loops(void)
+{
+	if (ctrl_task != 0)
+		wait_for_completion(&ctrl_complete);
+}
+
+/*
+ * Attach to the i2c FCU after detecting U3-1 bus
+ */
+static int attach_fcu(void)
+{
+	fcu = attach_i2c_chip(FAN_CTRLER_ID, "fcu");
+	if (fcu == NULL)
+		return -ENODEV;
+
+	DBG("FCU attached\n");
+
+	return 0;
+}
+
+/*
+ * Detach from the i2c FCU when tearing down
+ */
+static void detach_fcu(void)
+{
+	if (fcu)
+		detach_i2c_chip(fcu);
+	fcu = NULL;
+}
+
+/*
+ * Attach to the i2c controller. We probe the various chips based
+ * on the device-tree nodes and build everything for the driver to
+ * run, we then kick the driver monitoring thread
+ */
+static int therm_pm72_attach(struct i2c_adapter *adapter)
+{
+	down(&driver_lock);
+
+	/* Check state */
+	if (state == state_detached)
+		state = state_attaching;
+	if (state != state_attaching) {
+		up(&driver_lock);
+		return 0;
+	}
+
+	/* Check if we are looking for one of these */
+	if (u3_0 == NULL && !strcmp(adapter->name, "u3 0")) {
+		u3_0 = adapter;
+		DBG("found U3-0\n");
+		if (k2 || !rackmac)
+			if (create_control_loops())
+				u3_0 = NULL;
+	} else if (u3_1 == NULL && !strcmp(adapter->name, "u3 1")) {
+		u3_1 = adapter;
+		DBG("found U3-1, attaching FCU\n");
+		if (attach_fcu())
+			u3_1 = NULL;
+	} else if (k2 == NULL && !strcmp(adapter->name, "mac-io 0")) {
+		k2 = adapter;
+		DBG("Found K2\n");
+		if (u3_0 && rackmac)
+			if (create_control_loops())
+				k2 = NULL;
+	}
+	/* We got all we need, start control loops */
+	if (u3_0 != NULL && u3_1 != NULL && (k2 || !rackmac)) {
+		DBG("everything up, starting control loops\n");
+		state = state_attached;
+		start_control_loops();
+	}
+	up(&driver_lock);
+
+	return 0;
+}
+
+/*
+ * Called on every adapter when the driver or the i2c controller
+ * is going away.
+ */
+static int therm_pm72_detach(struct i2c_adapter *adapter)
+{
+	down(&driver_lock);
+
+	if (state != state_detached)
+		state = state_detaching;
+
+	/* Stop control loops if any */
+	DBG("stopping control loops\n");
+	up(&driver_lock);
+	stop_control_loops();
+	down(&driver_lock);
+
+	if (u3_0 != NULL && !strcmp(adapter->name, "u3 0")) {
+		DBG("lost U3-0, disposing control loops\n");
+		dispose_control_loops();
+		u3_0 = NULL;
+	}
+	
+	if (u3_1 != NULL && !strcmp(adapter->name, "u3 1")) {
+		DBG("lost U3-1, detaching FCU\n");
+		detach_fcu();
+		u3_1 = NULL;
+	}
+	if (u3_0 == NULL && u3_1 == NULL)
+		state = state_detached;
+
+	up(&driver_lock);
+
+	return 0;
+}
+
+static int fan_check_loc_match(const char *loc, int fan)
+{
+	char	tmp[64];
+	char	*c, *e;
+
+	strlcpy(tmp, fcu_fans[fan].loc, 64);
+
+	c = tmp;
+	for (;;) {
+		e = strchr(c, ',');
+		if (e)
+			*e = 0;
+		if (strcmp(loc, c) == 0)
+			return 1;
+		if (e == NULL)
+			break;
+		c = e + 1;
+	}
+	return 0;
+}
+
+static void fcu_lookup_fans(struct device_node *fcu_node)
+{
+	struct device_node *np = NULL;
+	int i;
+
+	/* The table is filled by default with values that are suitable
+	 * for the old machines without device-tree informations. We scan
+	 * the device-tree and override those values with whatever is
+	 * there
+	 */
+
+	DBG("Looking up FCU controls in device-tree...\n");
+
+	while ((np = of_get_next_child(fcu_node, np)) != NULL) {
+		int type = -1;
+		char *loc;
+		u32 *reg;
+
+		DBG(" control: %s, type: %s\n", np->name, np->type);
+
+		/* Detect control type */
+		if (!strcmp(np->type, "fan-rpm-control") ||
+		    !strcmp(np->type, "fan-rpm"))
+			type = FCU_FAN_RPM;
+		if (!strcmp(np->type, "fan-pwm-control") ||
+		    !strcmp(np->type, "fan-pwm"))
+			type = FCU_FAN_PWM;
+		/* Only care about fans for now */
+		if (type == -1)
+			continue;
+
+		/* Lookup for a matching location */
+		loc = (char *)get_property(np, "location", NULL);
+		reg = (u32 *)get_property(np, "reg", NULL);
+		if (loc == NULL || reg == NULL)
+			continue;
+		DBG(" matching location: %s, reg: 0x%08x\n", loc, *reg);
+
+		for (i = 0; i < FCU_FAN_COUNT; i++) {
+			int fan_id;
+
+			if (!fan_check_loc_match(loc, i))
+				continue;
+			DBG(" location match, index: %d\n", i);
+			fcu_fans[i].id = FCU_FAN_ABSENT_ID;
+			if (type != fcu_fans[i].type) {
+				printk(KERN_WARNING "therm_pm72: Fan type mismatch "
+				       "in device-tree for %s\n", np->full_name);
+				break;
+			}
+			if (type == FCU_FAN_RPM)
+				fan_id = ((*reg) - 0x10) / 2;
+			else
+				fan_id = ((*reg) - 0x30) / 2;
+			if (fan_id > 7) {
+				printk(KERN_WARNING "therm_pm72: Can't parse "
+				       "fan ID in device-tree for %s\n", np->full_name);
+				break;
+			}
+			DBG(" fan id -> %d, type -> %d\n", fan_id, type);
+			fcu_fans[i].id = fan_id;
+		}
+	}
+
+	/* Now dump the array */
+	printk(KERN_INFO "Detected fan controls:\n");
+	for (i = 0; i < FCU_FAN_COUNT; i++) {
+		if (fcu_fans[i].id == FCU_FAN_ABSENT_ID)
+			continue;
+		printk(KERN_INFO "  %d: %s fan, id %d, location: %s\n", i,
+		       fcu_fans[i].type == FCU_FAN_RPM ? "RPM" : "PWM",
+		       fcu_fans[i].id, fcu_fans[i].loc);
+	}
+}
+
+static int fcu_of_probe(struct of_device* dev, const struct of_match *match)
+{
+	int rc;
+
+	state = state_detached;
+
+	/* Lookup the fans in the device tree */
+	fcu_lookup_fans(dev->node);
+
+	/* Add the driver */
+	rc = i2c_add_driver(&therm_pm72_driver);
+	if (rc < 0)
+		return rc;
+	return 0;
+}
+
+static int fcu_of_remove(struct of_device* dev)
+{
+	i2c_del_driver(&therm_pm72_driver);
+
+	return 0;
+}
+
+static struct of_match fcu_of_match[] = 
+{
+	{
+	.name 		= OF_ANY_MATCH,
+	.type		= "fcu",
+	.compatible	= OF_ANY_MATCH
+	},
+	{},
+};
+
+static struct of_platform_driver fcu_of_platform_driver = 
+{
+	.name 		= "temperature",
+	.match_table	= fcu_of_match,
+	.probe		= fcu_of_probe,
+	.remove		= fcu_of_remove
+};
+
+/*
+ * Check machine type, attach to i2c controller
+ */
+static int __init therm_pm72_init(void)
+{
+	struct device_node *np;
+
+	rackmac = machine_is_compatible("RackMac3,1");
+
+	if (!machine_is_compatible("PowerMac7,2") &&
+	    !machine_is_compatible("PowerMac7,3") &&
+	    !rackmac)
+	    	return -ENODEV;
+
+	printk(KERN_INFO "PowerMac G5 Thermal control driver %s\n", VERSION);
+
+	np = of_find_node_by_type(NULL, "fcu");
+	if (np == NULL) {
+		/* Some machines have strangely broken device-tree */
+		np = of_find_node_by_path("/u3@0,f8000000/i2c@f8001000/fan@15e");
+		if (np == NULL) {
+			    printk(KERN_ERR "Can't find FCU in device-tree !\n");
+			    return -ENODEV;
+		}
+	}
+	of_dev = of_platform_device_create(np, "temperature");
+	if (of_dev == NULL) {
+		printk(KERN_ERR "Can't register FCU platform device !\n");
+		return -ENODEV;
+	}
+
+	of_register_driver(&fcu_of_platform_driver);
+	
+	return 0;
+}
+
+static void __exit therm_pm72_exit(void)
+{
+	of_unregister_driver(&fcu_of_platform_driver);
+
+	if (of_dev)
+		of_device_unregister(of_dev);
+}
+
+module_init(therm_pm72_init);
+module_exit(therm_pm72_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("Driver for Apple's PowerMac G5 thermal control");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/therm_pm72.h b/drivers/macintosh/therm_pm72.h
new file mode 100644
index 0000000..c17e61f
--- /dev/null
+++ b/drivers/macintosh/therm_pm72.h
@@ -0,0 +1,297 @@
+#ifndef __THERM_PMAC_7_2_H__
+#define __THERM_PMAC_7_2_H__
+
+typedef unsigned short fu16;
+typedef int fs32;
+typedef short fs16;
+
+struct mpu_data
+{
+	u8	signature;		/* 0x00 - EEPROM sig. */
+	u8	bytes_used;		/* 0x01 - Bytes used in eeprom (160 ?) */
+	u8	size;			/* 0x02 - EEPROM size (256 ?) */
+	u8	version;		/* 0x03 - EEPROM version */
+	u32	data_revision;		/* 0x04 - Dataset revision */
+	u8	processor_bin_code[3];	/* 0x08 - Processor BIN code */
+	u8	bin_code_expansion;	/* 0x0b - ??? (padding ?) */
+	u8	processor_num;		/* 0x0c - Number of CPUs on this MPU */
+	u8	input_mul_bus_div;	/* 0x0d - Clock input multiplier/bus divider */
+	u8	reserved1[2];		/* 0x0e - */
+	u32	input_clk_freq_high;	/* 0x10 - Input clock frequency high */
+	u8	cpu_nb_target_cycles;	/* 0x14 - ??? */
+	u8	cpu_statlat;		/* 0x15 - ??? */
+	u8	cpu_snooplat;		/* 0x16 - ??? */
+	u8	cpu_snoopacc;		/* 0x17 - ??? */
+	u8	nb_paamwin;		/* 0x18 - ??? */
+	u8	nb_statlat;		/* 0x19 - ??? */
+	u8	nb_snooplat;		/* 0x1a - ??? */
+	u8	nb_snoopwin;		/* 0x1b - ??? */
+	u8	api_bus_mode;		/* 0x1c - ??? */
+	u8	reserved2[3];		/* 0x1d - */
+	u32	input_clk_freq_low;	/* 0x20 - Input clock frequency low */
+	u8	processor_card_slot;	/* 0x24 - Processor card slot number */
+	u8	reserved3[2];		/* 0x25 - */
+	u8	padjmax;       		/* 0x27 - Max power adjustment (Not in OF!) */
+	u8	ttarget;		/* 0x28 - Target temperature */
+	u8	tmax;			/* 0x29 - Max temperature */
+	u8	pmaxh;			/* 0x2a - Max power */
+	u8	tguardband;		/* 0x2b - Guardband temp ??? Hist. len in OSX */
+	fs32	pid_gp;			/* 0x2c - PID proportional gain */
+	fs32	pid_gr;			/* 0x30 - PID reset gain */
+	fs32	pid_gd;			/* 0x34 - PID derivative gain */
+	fu16	voph;			/* 0x38 - Vop High */
+	fu16	vopl;			/* 0x3a - Vop Low */
+	fs16	nactual_die;		/* 0x3c - nActual Die */
+	fs16	nactual_heatsink;	/* 0x3e - nActual Heatsink */
+	fs16	nactual_system;		/* 0x40 - nActual System */
+	u16	calibration_flags;	/* 0x42 - Calibration flags */
+	fu16	mdiode;			/* 0x44 - Diode M value (scaling factor) */
+	fs16	bdiode;			/* 0x46 - Diode B value (offset) */
+	fs32	theta_heat_sink;	/* 0x48 - Theta heat sink */
+	u16	rminn_intake_fan;	/* 0x4c - Intake fan min RPM */
+	u16	rmaxn_intake_fan;	/* 0x4e - Intake fan max RPM */
+	u16	rminn_exhaust_fan;	/* 0x50 - Exhaust fan min RPM */
+	u16	rmaxn_exhaust_fan;	/* 0x52 - Exhaust fan max RPM */
+	u8	processor_part_num[8];	/* 0x54 - Processor part number XX pumps min/max */
+	u32	processor_lot_num;	/* 0x5c - Processor lot number */
+	u8	orig_card_sernum[0x10];	/* 0x60 - Card original serial number */
+	u8	curr_card_sernum[0x10];	/* 0x70 - Card current serial number */
+	u8	mlb_sernum[0x18];	/* 0x80 - MLB serial number */
+	u32	checksum1;		/* 0x98 - */
+	u32	checksum2;		/* 0x9c - */	
+}; /* Total size = 0xa0 */
+
+/* Display a 16.16 fixed point value */
+#define FIX32TOPRINT(f)	((f) >> 16),((((f) & 0xffff) * 1000) >> 16)
+
+/*
+ * Maximum number of seconds to be in critical state (after a
+ * normal shutdown attempt). If the machine isn't down after
+ * this counter elapses, we force an immediate machine power
+ * off.
+ */
+#define MAX_CRITICAL_STATE			30
+static char * critical_overtemp_path = "/sbin/critical_overtemp";
+
+/*
+ * This option is "weird" :) Basically, if you define this to 1
+ * the control loop for the RPMs fans (not PWMs) will apply the
+ * correction factor obtained from the PID to the _actual_ RPM
+ * speed read from the FCU.
+ * If you define the below constant to 0, then it will be
+ * applied to the setpoint RPM speed, that is basically the
+ * speed we proviously "asked" for.
+ *
+ * I'm not sure which of these Apple's algorithm is supposed
+ * to use
+ */
+#define RPM_PID_USE_ACTUAL_SPEED		0
+
+/*
+ * i2c IDs. Currently, we hard code those and assume that
+ * the FCU is on U3 bus 1 while all sensors are on U3 bus
+ * 0. This appear to be safe enough for this first version
+ * of the driver, though I would accept any clean patch
+ * doing a better use of the device-tree without turning the
+ * while i2c registration mecanism into a racy mess
+ *
+ * Note: Xserve changed this. We have some bits on the K2 bus,
+ * which I arbitrarily set to 0x200. Ultimately, we really want
+ * too lookup these in the device-tree though
+ */
+#define FAN_CTRLER_ID		0x15e
+#define SUPPLY_MONITOR_ID      	0x58
+#define SUPPLY_MONITORB_ID     	0x5a
+#define DRIVES_DALLAS_ID	0x94
+#define BACKSIDE_MAX_ID		0x98
+#define XSERVE_DIMMS_LM87	0x25a
+
+/*
+ * Some MAX6690, DS1775, LM87 register definitions
+ */
+#define MAX6690_INT_TEMP	0
+#define MAX6690_EXT_TEMP	1
+#define DS1775_TEMP		0
+#define LM87_INT_TEMP		0x27
+
+/*
+ * Scaling factors for the AD7417 ADC converters (except
+ * for the CPU diode which is obtained from the EEPROM).
+ * Those values are obtained from the property list of
+ * the darwin driver
+ */
+#define ADC_12V_CURRENT_SCALE	0x0320	/* _AD2 */
+#define ADC_CPU_VOLTAGE_SCALE	0x00a0	/* _AD3 */
+#define ADC_CPU_CURRENT_SCALE	0x1f40	/* _AD4 */
+
+/*
+ * PID factors for the U3/Backside fan control loop. We have 2 sets
+ * of values here, one set for U3 and one set for U3H
+ */
+#define BACKSIDE_FAN_PWM_DEFAULT_ID	1
+#define BACKSIDE_FAN_PWM_INDEX		0
+#define BACKSIDE_PID_U3_G_d		0x02800000
+#define BACKSIDE_PID_U3H_G_d		0x01400000
+#define BACKSIDE_PID_RACK_G_d		0x00500000
+#define BACKSIDE_PID_G_p		0x00500000
+#define BACKSIDE_PID_RACK_G_p		0x0004cccc
+#define BACKSIDE_PID_G_r		0x00000000
+#define BACKSIDE_PID_U3_INPUT_TARGET	0x00410000
+#define BACKSIDE_PID_U3H_INPUT_TARGET	0x004b0000
+#define BACKSIDE_PID_RACK_INPUT_TARGET	0x00460000
+#define BACKSIDE_PID_INTERVAL		5
+#define BACKSIDE_PID_RACK_INTERVAL	1
+#define BACKSIDE_PID_OUTPUT_MAX		100
+#define BACKSIDE_PID_U3_OUTPUT_MIN	20
+#define BACKSIDE_PID_U3H_OUTPUT_MIN	20
+#define BACKSIDE_PID_HISTORY_SIZE	2
+
+struct basckside_pid_params
+{
+	s32			G_d;
+	s32			G_p;
+	s32			G_r;
+	s32			input_target;
+	s32			output_min;
+	s32			output_max;
+	s32			interval;
+	int			additive;
+};
+
+struct backside_pid_state
+{
+	int			ticks;
+	struct i2c_client *	monitor;
+	s32		       	sample_history[BACKSIDE_PID_HISTORY_SIZE];
+	s32			error_history[BACKSIDE_PID_HISTORY_SIZE];
+	int			cur_sample;
+	s32			last_temp;
+	int			pwm;
+	int			first;
+};
+
+/*
+ * PID factors for the Drive Bay fan control loop
+ */
+#define DRIVES_FAN_RPM_DEFAULT_ID	2
+#define DRIVES_FAN_RPM_INDEX		1
+#define DRIVES_PID_G_d			0x01e00000
+#define DRIVES_PID_G_p			0x00500000
+#define DRIVES_PID_G_r			0x00000000
+#define DRIVES_PID_INPUT_TARGET		0x00280000
+#define DRIVES_PID_INTERVAL    		5
+#define DRIVES_PID_OUTPUT_MAX		4000
+#define DRIVES_PID_OUTPUT_MIN		300
+#define DRIVES_PID_HISTORY_SIZE		2
+
+struct drives_pid_state
+{
+	int			ticks;
+	struct i2c_client *	monitor;
+	s32	       		sample_history[BACKSIDE_PID_HISTORY_SIZE];
+	s32			error_history[BACKSIDE_PID_HISTORY_SIZE];
+	int			cur_sample;
+	s32			last_temp;
+	int			rpm;
+	int			first;
+};
+
+#define SLOTS_FAN_PWM_DEFAULT_ID	2
+#define SLOTS_FAN_PWM_INDEX		2
+#define	SLOTS_FAN_DEFAULT_PWM		50 /* Do better here ! */
+
+
+/*
+ * PID factors for the Xserve DIMM control loop
+ */
+#define DIMM_PID_G_d			0
+#define DIMM_PID_G_p			0
+#define DIMM_PID_G_r			0x6553600
+#define DIMM_PID_INPUT_TARGET		3276800
+#define DIMM_PID_INTERVAL    		1
+#define DIMM_PID_OUTPUT_MAX		14000
+#define DIMM_PID_OUTPUT_MIN		4000
+#define DIMM_PID_HISTORY_SIZE		20
+
+struct dimm_pid_state
+{
+	int			ticks;
+	struct i2c_client *	monitor;
+	s32	       		sample_history[DIMM_PID_HISTORY_SIZE];
+	s32			error_history[DIMM_PID_HISTORY_SIZE];
+	int			cur_sample;
+	s32			last_temp;
+	int			first;
+	int			output;
+};
+
+
+
+/* Desktops */
+
+#define CPUA_INTAKE_FAN_RPM_DEFAULT_ID	3
+#define CPUA_EXHAUST_FAN_RPM_DEFAULT_ID	4
+#define CPUB_INTAKE_FAN_RPM_DEFAULT_ID	5
+#define CPUB_EXHAUST_FAN_RPM_DEFAULT_ID	6
+
+#define CPUA_INTAKE_FAN_RPM_INDEX	3
+#define CPUA_EXHAUST_FAN_RPM_INDEX	4
+#define CPUB_INTAKE_FAN_RPM_INDEX	5
+#define CPUB_EXHAUST_FAN_RPM_INDEX	6
+
+#define CPU_INTAKE_SCALE		0x0000f852
+#define CPU_TEMP_HISTORY_SIZE		2
+#define CPU_POWER_HISTORY_SIZE		10
+#define CPU_PID_INTERVAL		1
+#define CPU_MAX_OVERTEMP		30
+
+#define CPUA_PUMP_RPM_INDEX		7
+#define CPUB_PUMP_RPM_INDEX		8
+#define CPU_PUMP_OUTPUT_MAX		3200
+#define CPU_PUMP_OUTPUT_MIN		1250
+
+/* Xserve */
+#define CPU_A1_FAN_RPM_INDEX		9
+#define CPU_A2_FAN_RPM_INDEX		10
+#define CPU_A3_FAN_RPM_INDEX		11
+#define CPU_B1_FAN_RPM_INDEX		12
+#define CPU_B2_FAN_RPM_INDEX		13
+#define CPU_B3_FAN_RPM_INDEX		14
+
+
+struct cpu_pid_state
+{
+	int			index;
+	struct i2c_client *	monitor;
+	struct mpu_data		mpu;
+	int			overtemp;
+	s32	       		temp_history[CPU_TEMP_HISTORY_SIZE];
+	int			cur_temp;
+	s32			power_history[CPU_POWER_HISTORY_SIZE];
+	s32			error_history[CPU_POWER_HISTORY_SIZE];
+	int			cur_power;
+	int			count_power;
+	int			rpm;
+	int			intake_rpm;
+	s32			voltage;
+	s32			current_a;
+	s32			last_temp;
+	s32			last_power;
+	int			first;
+	u8			adc_config;
+	s32			pump_min;
+	s32			pump_max;
+};
+
+/*
+ * Driver state
+ */
+enum {
+	state_detached,
+	state_attaching,
+	state_attached,
+	state_detaching,
+};
+
+
+#endif /* __THERM_PMAC_7_2_H__ */
diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c
new file mode 100644
index 0000000..c153699
--- /dev/null
+++ b/drivers/macintosh/therm_windtunnel.c
@@ -0,0 +1,531 @@
+/* 
+ *   Creation Date: <2003/03/14 20:54:13 samuel>
+ *   Time-stamp: <2004/03/20 14:20:59 samuel>
+ *   
+ *	<therm_windtunnel.c>
+ *	
+ *	The G4 "windtunnel" has a single fan controlled by an
+ *	ADM1030 fan controller and a DS1775 thermostat.
+ *
+ *	The fan controller is equipped with a temperature sensor
+ *	which measures the case temperature. The DS1775 sensor
+ *	measures the CPU temperature. This driver tunes the
+ *	behavior of the fan. It is based upon empirical observations
+ *	of the 'AppleFan' driver under Mac OS X.
+ *
+ *	WARNING: This driver has only been testen on Apple's
+ *	1.25 MHz Dual G4 (March 03). It is tuned for a CPU
+ *	temperatur around 57 C.
+ *
+ *   Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
+ *
+ *   Loosely based upon 'thermostat.c' written by Benjamin Herrenschmidt
+ *   
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation
+ *   
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/sections.h>
+#include <asm/of_device.h>
+
+#define LOG_TEMP		0			/* continously log temperature */
+
+#define I2C_DRIVERID_G4FAN	0x9001			/* fixme */
+
+static int 			do_probe( struct i2c_adapter *adapter, int addr, int kind);
+
+/* scan 0x48-0x4f (DS1775) and 0x2c-2x2f (ADM1030) */
+static unsigned short		normal_i2c[] = { 0x49, 0x2c, I2C_CLIENT_END };
+static unsigned short		normal_i2c_range[] = { 0x48, 0x4f, 0x2c, 0x2f, I2C_CLIENT_END };
+
+I2C_CLIENT_INSMOD;
+
+static struct {
+	volatile int		running;
+	struct completion	completion;
+	pid_t			poll_task;
+	
+	struct semaphore 	lock;
+	struct of_device	*of_dev;
+	
+	struct i2c_client	*thermostat;
+	struct i2c_client	*fan;
+
+	int			overheat_temp;		/* 100% fan at this temp */
+	int			overheat_hyst;
+	int			temp;
+	int			casetemp;
+	int			fan_level;		/* active fan_table setting */
+
+	int			downind;
+	int			upind;
+
+	int			r0, r1, r20, r23, r25;	/* saved register */
+} x;
+
+#define T(x,y)			(((x)<<8) | (y)*0x100/10 )
+
+static struct {
+	int			fan_down_setting;
+	int			temp;
+	int			fan_up_setting;
+} fan_table[] = {
+	{ 11, T(0,0),  11 },	/* min fan */
+	{ 11, T(55,0), 11 },
+	{  6, T(55,3), 11 },
+	{  7, T(56,0), 11 },
+	{  8, T(57,0), 8 },
+	{  7, T(58,3), 7 },
+	{  6, T(58,8), 6 },
+	{  5, T(59,2), 5 },
+	{  4, T(59,6), 4 },
+	{  3, T(59,9), 3 },
+	{  2, T(60,1), 2 },
+	{  1, 0xfffff, 1 }	/* on fire */
+};
+
+static void
+print_temp( const char *s, int temp )
+{
+	printk("%s%d.%d C", s ? s : "", temp>>8, (temp & 255)*10/256 );
+}
+
+static ssize_t
+show_cpu_temperature( struct device *dev, char *buf )
+{
+	return sprintf(buf, "%d.%d\n", x.temp>>8, (x.temp & 255)*10/256 );
+}
+
+static ssize_t
+show_case_temperature( struct device *dev, char *buf )
+{
+	return sprintf(buf, "%d.%d\n", x.casetemp>>8, (x.casetemp & 255)*10/256 );
+}
+
+static DEVICE_ATTR(cpu_temperature, S_IRUGO, show_cpu_temperature, NULL );
+static DEVICE_ATTR(case_temperature, S_IRUGO, show_case_temperature, NULL );
+
+
+
+/************************************************************************/
+/*	controller thread						*/
+/************************************************************************/
+
+static int
+write_reg( struct i2c_client *cl, int reg, int data, int len )
+{
+	u8 tmp[3];
+
+	if( len < 1 || len > 2 || data < 0 )
+		return -EINVAL;
+
+	tmp[0] = reg;
+	tmp[1] = (len == 1) ? data : (data >> 8);
+	tmp[2] = data;
+	len++;
+	
+	if( i2c_master_send(cl, tmp, len) != len )
+		return -ENODEV;
+	return 0;
+}
+
+static int
+read_reg( struct i2c_client *cl, int reg, int len )
+{
+	u8 buf[2];
+
+	if( len != 1 && len != 2 )
+		return -EINVAL;
+	buf[0] = reg;
+	if( i2c_master_send(cl, buf, 1) != 1 )
+		return -ENODEV;
+	if( i2c_master_recv(cl, buf, len) != len )
+		return -ENODEV;
+	return (len == 2)? ((unsigned int)buf[0] << 8) | buf[1] : buf[0];
+}
+
+static void
+tune_fan( int fan_setting )
+{
+	int val = (fan_setting << 3) | 7;
+
+	/* write_reg( x.fan, 0x24, val, 1 ); */
+	write_reg( x.fan, 0x25, val, 1 );
+	write_reg( x.fan, 0x20, 0, 1 );
+	print_temp("CPU-temp: ", x.temp );
+	if( x.casetemp )
+		print_temp(", Case: ", x.casetemp );
+	printk(",  Fan: %d (tuned %+d)\n", 11-fan_setting, x.fan_level-fan_setting );
+
+	x.fan_level = fan_setting;
+}
+
+static void
+poll_temp( void )
+{
+	int temp, i, level, casetemp;
+
+	temp = read_reg( x.thermostat, 0, 2 );
+
+	/* this actually occurs when the computer is loaded */
+	if( temp < 0 )
+		return;
+
+	casetemp = read_reg(x.fan, 0x0b, 1) << 8;
+	casetemp |= (read_reg(x.fan, 0x06, 1) & 0x7) << 5;
+
+	if( LOG_TEMP && x.temp != temp ) {
+		print_temp("CPU-temp: ", temp );
+		print_temp(", Case: ", casetemp );
+		printk(",  Fan: %d\n", 11-x.fan_level );
+	}
+	x.temp = temp;
+	x.casetemp = casetemp;
+
+	level = -1;
+	for( i=0; (temp & 0xffff) > fan_table[i].temp ; i++ )
+		;
+	if( i < x.downind )
+		level = fan_table[i].fan_down_setting;
+	x.downind = i;
+
+	for( i=0; (temp & 0xffff) >= fan_table[i+1].temp ; i++ )
+		;
+	if( x.upind < i )
+		level = fan_table[i].fan_up_setting;
+	x.upind = i;
+
+	if( level >= 0 )
+		tune_fan( level );
+}
+
+
+static void
+setup_hardware( void )
+{
+	int val;
+
+	/* save registers (if we unload the module) */
+	x.r0 = read_reg( x.fan, 0x00, 1 );
+	x.r1 = read_reg( x.fan, 0x01, 1 );
+	x.r20 = read_reg( x.fan, 0x20, 1 );
+	x.r23 = read_reg( x.fan, 0x23, 1 );
+	x.r25 = read_reg( x.fan, 0x25, 1 );
+
+	/* improve measurement resolution (convergence time 1.5s) */
+	if( (val=read_reg(x.thermostat, 1, 1)) >= 0 ) {
+		val |= 0x60;
+		if( write_reg( x.thermostat, 1, val, 1 ) )
+			printk("Failed writing config register\n");
+	}
+	/* disable interrupts and TAC input */
+	write_reg( x.fan, 0x01, 0x01, 1 );
+	/* enable filter */
+	write_reg( x.fan, 0x23, 0x91, 1 );
+	/* remote temp. controls fan */
+	write_reg( x.fan, 0x00, 0x95, 1 );
+
+	/* The thermostat (which besides measureing temperature controls
+	 * has a THERM output which puts the fan on 100%) is usually
+	 * set to kick in at 80 C (chip default). We reduce this a bit
+	 * to be on the safe side (OSX doesn't)...
+	 */
+	if( x.overheat_temp == (80 << 8) ) {
+		x.overheat_temp = 65 << 8;
+		x.overheat_hyst = 60 << 8;
+		write_reg( x.thermostat, 2, x.overheat_hyst, 2 );
+		write_reg( x.thermostat, 3, x.overheat_temp, 2 );
+
+		print_temp("Reducing overheating limit to ", x.overheat_temp );
+		print_temp(" (Hyst: ", x.overheat_hyst );
+		printk(")\n");
+	}
+
+	/* set an initial fan setting */
+	x.downind = 0xffff;
+	x.upind = -1;
+	/* tune_fan( fan_up_table[x.upind].fan_setting ); */
+
+	device_create_file( &x.of_dev->dev, &dev_attr_cpu_temperature );
+	device_create_file( &x.of_dev->dev, &dev_attr_case_temperature );
+}
+
+static void
+restore_regs( void )
+{
+	device_remove_file( &x.of_dev->dev, &dev_attr_cpu_temperature );
+	device_remove_file( &x.of_dev->dev, &dev_attr_case_temperature );
+
+	write_reg( x.fan, 0x01, x.r1, 1 );
+	write_reg( x.fan, 0x20, x.r20, 1 );
+	write_reg( x.fan, 0x23, x.r23, 1 );
+	write_reg( x.fan, 0x25, x.r25, 1 );
+	write_reg( x.fan, 0x00, x.r0, 1 );
+}
+
+static int
+control_loop( void *dummy )
+{
+	daemonize("g4fand");
+
+	down( &x.lock );
+	setup_hardware();
+
+	while( x.running ) {
+		up( &x.lock );
+
+		msleep_interruptible(8000);
+		
+		down( &x.lock );
+		poll_temp();
+	}
+
+	restore_regs();
+	up( &x.lock );
+
+	complete_and_exit( &x.completion, 0 );
+}
+
+
+/************************************************************************/
+/*	i2c probing and setup						*/
+/************************************************************************/
+
+static int
+do_attach( struct i2c_adapter *adapter )
+{
+	int ret = 0;
+
+	if( strncmp(adapter->name, "uni-n", 5) )
+		return 0;
+
+	if( !x.running ) {
+		ret = i2c_probe( adapter, &addr_data, &do_probe );
+		if( x.thermostat && x.fan ) {
+			x.running = 1;
+			init_completion( &x.completion );
+			x.poll_task = kernel_thread( control_loop, NULL, SIGCHLD | CLONE_KERNEL );
+		}
+	}
+	return ret;
+}
+
+static int
+do_detach( struct i2c_client *client )
+{
+	int err;
+
+	if( (err=i2c_detach_client(client)) )
+		printk(KERN_ERR "failed to detach thermostat client\n");
+	else {
+		if( x.running ) {
+			x.running = 0;
+			wait_for_completion( &x.completion );
+		}
+		if( client == x.thermostat )
+			x.thermostat = NULL;
+		else if( client == x.fan )
+			x.fan = NULL;
+		else {
+			printk(KERN_ERR "g4fan: bad client\n");
+		}
+		kfree( client );
+	}
+	return err;
+}
+
+static struct i2c_driver g4fan_driver = {  
+	.owner		= THIS_MODULE,
+	.name		= "therm_windtunnel",
+	.id		= I2C_DRIVERID_G4FAN,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter = do_attach,
+	.detach_client	= do_detach,
+};
+
+static int
+attach_fan( struct i2c_client *cl )
+{
+	if( x.fan )
+		goto out;
+
+	/* check that this is an ADM1030 */
+	if( read_reg(cl, 0x3d, 1) != 0x30 || read_reg(cl, 0x3e, 1) != 0x41 )
+		goto out;
+	printk("ADM1030 fan controller [@%02x]\n", cl->addr );
+
+	strlcpy( cl->name, "ADM1030 fan controller", sizeof(cl->name) );
+
+	if( !i2c_attach_client(cl) )
+		x.fan = cl;
+ out:
+	if( cl != x.fan )
+		kfree( cl );
+	return 0;
+}
+
+static int
+attach_thermostat( struct i2c_client *cl ) 
+{
+	int hyst_temp, os_temp, temp;
+
+	if( x.thermostat )
+		goto out;
+
+	if( (temp=read_reg(cl, 0, 2)) < 0 )
+		goto out;
+	
+	/* temperature sanity check */
+	if( temp < 0x1600 || temp > 0x3c00 )
+		goto out;
+	hyst_temp = read_reg(cl, 2, 2);
+	os_temp = read_reg(cl, 3, 2);
+	if( hyst_temp < 0 || os_temp < 0 )
+		goto out;
+
+	printk("DS1775 digital thermometer [@%02x]\n", cl->addr );
+	print_temp("Temp: ", temp );
+	print_temp("  Hyst: ", hyst_temp );
+	print_temp("  OS: ", os_temp );
+	printk("\n");
+
+	x.temp = temp;
+	x.overheat_temp = os_temp;
+	x.overheat_hyst = hyst_temp;
+	
+	strlcpy( cl->name, "DS1775 thermostat", sizeof(cl->name) );
+
+	if( !i2c_attach_client(cl) )
+		x.thermostat = cl;
+out:
+	if( cl != x.thermostat )
+		kfree( cl );
+	return 0;
+}
+
+static int
+do_probe( struct i2c_adapter *adapter, int addr, int kind )
+{
+	struct i2c_client *cl;
+
+	if( !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA
+				     | I2C_FUNC_SMBUS_WRITE_BYTE) )
+		return 0;
+
+	if( !(cl=kmalloc(sizeof(*cl), GFP_KERNEL)) )
+		return -ENOMEM;
+	memset( cl, 0, sizeof(struct i2c_client) );
+
+	cl->addr = addr;
+	cl->adapter = adapter;
+	cl->driver = &g4fan_driver;
+	cl->flags = 0;
+
+	if( addr < 0x48 )
+		return attach_fan( cl );
+	return attach_thermostat( cl );
+}
+
+
+/************************************************************************/
+/*	initialization / cleanup					*/
+/************************************************************************/
+
+static int
+therm_of_probe( struct of_device *dev, const struct of_match *match )
+{
+	return i2c_add_driver( &g4fan_driver );
+}
+
+static int
+therm_of_remove( struct of_device *dev )
+{
+	return i2c_del_driver( &g4fan_driver );
+}
+
+static struct of_match therm_of_match[] = {{
+	.name		= "fan",
+	.type		= OF_ANY_MATCH,
+	.compatible	= "adm1030"
+    }, {}
+};
+
+static struct of_platform_driver therm_of_driver = {
+	.name		= "temperature",
+	.match_table	= therm_of_match,
+	.probe		= therm_of_probe,
+	.remove		= therm_of_remove,
+};
+
+struct apple_thermal_info {
+	u8		id;			/* implementation ID */
+	u8		fan_count;		/* number of fans */
+	u8		thermostat_count;	/* number of thermostats */
+	u8		unused;
+};
+
+static int __init
+g4fan_init( void )
+{
+	struct apple_thermal_info *info;
+	struct device_node *np;
+
+	init_MUTEX( &x.lock );
+
+	if( !(np=of_find_node_by_name(NULL, "power-mgt")) )
+		return -ENODEV;
+	info = (struct apple_thermal_info*)get_property(np, "thermal-info", NULL);
+	of_node_put(np);
+
+	if( !info || !machine_is_compatible("PowerMac3,6") )
+		return -ENODEV;
+
+	if( info->id != 3 ) {
+		printk(KERN_ERR "therm_windtunnel: unsupported thermal design %d\n", info->id );
+		return -ENODEV;
+	}
+	if( !(np=of_find_node_by_name(NULL, "fan")) )
+		return -ENODEV;
+	x.of_dev = of_platform_device_create( np, "temperature" );
+	of_node_put( np );
+
+	if( !x.of_dev ) {
+		printk(KERN_ERR "Can't register fan controller!\n");
+		return -ENODEV;
+	}
+
+	of_register_driver( &therm_of_driver );
+	return 0;
+}
+
+static void __exit
+g4fan_exit( void )
+{
+	of_unregister_driver( &therm_of_driver );
+
+	if( x.of_dev )
+		of_device_unregister( x.of_dev );
+}
+
+module_init(g4fan_init);
+module_exit(g4fan_exit);
+
+MODULE_AUTHOR("Samuel Rydh <samuel@ibrium.se>");
+MODULE_DESCRIPTION("Apple G4 (windtunnel) fan controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c
new file mode 100644
index 0000000..417deb5
--- /dev/null
+++ b/drivers/macintosh/via-cuda.c
@@ -0,0 +1,628 @@
+/*
+ * Device driver for the via-cuda on Apple Powermacs.
+ *
+ * The VIA (versatile interface adapter) interfaces to the CUDA,
+ * a 6805 microprocessor core which controls the ADB (Apple Desktop
+ * Bus) which connects to the keyboard and mouse.  The CUDA also
+ * controls system power and the RTC (real time clock) chip.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+#include <stdarg.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/adb.h>
+#include <linux/cuda.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#ifdef CONFIG_PPC
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#else
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/machw.h>
+#include <asm/mac_via.h>
+#endif
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/init.h>
+
+static volatile unsigned char __iomem *via;
+static DEFINE_SPINLOCK(cuda_lock);
+
+#ifdef CONFIG_MAC
+#define CUDA_IRQ IRQ_MAC_ADB
+#define __openfirmware
+#define eieio()
+#else
+#define CUDA_IRQ vias->intrs[0].line
+#endif
+
+/* VIA registers - spaced 0x200 bytes apart */
+#define RS		0x200		/* skip between registers */
+#define B		0		/* B-side data */
+#define A		RS		/* A-side data */
+#define DIRB		(2*RS)		/* B-side direction (1=output) */
+#define DIRA		(3*RS)		/* A-side direction (1=output) */
+#define T1CL		(4*RS)		/* Timer 1 ctr/latch (low 8 bits) */
+#define T1CH		(5*RS)		/* Timer 1 counter (high 8 bits) */
+#define T1LL		(6*RS)		/* Timer 1 latch (low 8 bits) */
+#define T1LH		(7*RS)		/* Timer 1 latch (high 8 bits) */
+#define T2CL		(8*RS)		/* Timer 2 ctr/latch (low 8 bits) */
+#define T2CH		(9*RS)		/* Timer 2 counter (high 8 bits) */
+#define SR		(10*RS)		/* Shift register */
+#define ACR		(11*RS)		/* Auxiliary control register */
+#define PCR		(12*RS)		/* Peripheral control register */
+#define IFR		(13*RS)		/* Interrupt flag register */
+#define IER		(14*RS)		/* Interrupt enable register */
+#define ANH		(15*RS)		/* A-side data, no handshake */
+
+/* Bits in B data register: all active low */
+#define TREQ		0x08		/* Transfer request (input) */
+#define TACK		0x10		/* Transfer acknowledge (output) */
+#define TIP		0x20		/* Transfer in progress (output) */
+
+/* Bits in ACR */
+#define SR_CTRL		0x1c		/* Shift register control bits */
+#define SR_EXT		0x0c		/* Shift on external clock */
+#define SR_OUT		0x10		/* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define IER_SET		0x80		/* set bits in IER */
+#define IER_CLR		0		/* clear bits in IER */
+#define SR_INT		0x04		/* Shift register full/empty */
+
+static enum cuda_state {
+    idle,
+    sent_first_byte,
+    sending,
+    reading,
+    read_done,
+    awaiting_reply
+} cuda_state;
+
+static struct adb_request *current_req;
+static struct adb_request *last_req;
+static unsigned char cuda_rbuf[16];
+static unsigned char *reply_ptr;
+static int reading_reply;
+static int data_index;
+#ifdef CONFIG_PPC
+static struct device_node *vias;
+#endif
+static int cuda_fully_inited = 0;
+
+#ifdef CONFIG_ADB
+static int cuda_probe(void);
+static int cuda_init(void);
+static int cuda_send_request(struct adb_request *req, int sync);
+static int cuda_adb_autopoll(int devs);
+static int cuda_reset_adb_bus(void);
+#endif /* CONFIG_ADB */
+
+static int cuda_init_via(void);
+static void cuda_start(void);
+static irqreturn_t cuda_interrupt(int irq, void *arg, struct pt_regs *regs);
+static void cuda_input(unsigned char *buf, int nb, struct pt_regs *regs);
+void cuda_poll(void);
+static int cuda_write(struct adb_request *req);
+
+int cuda_request(struct adb_request *req,
+		 void (*done)(struct adb_request *), int nbytes, ...);
+
+#ifdef CONFIG_ADB
+struct adb_driver via_cuda_driver = {
+	"CUDA",
+	cuda_probe,
+	cuda_init,
+	cuda_send_request,
+	cuda_adb_autopoll,
+	cuda_poll,
+	cuda_reset_adb_bus
+};
+#endif /* CONFIG_ADB */
+
+#ifdef CONFIG_PPC
+int __init
+find_via_cuda(void)
+{
+    int err;
+    struct adb_request req;
+
+    if (vias != 0)
+	return 1;
+    vias = find_devices("via-cuda");
+    if (vias == 0)
+	return 0;
+    if (vias->next != 0)
+	printk(KERN_WARNING "Warning: only using 1st via-cuda\n");
+
+#if 0
+    { int i;
+
+    printk("find_via_cuda: 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-cuda: expecting 1 address (%d) and 1 interrupt (%d)\n",
+	       vias->n_addrs, vias->n_intrs);
+	if (vias->n_addrs < 1 || vias->n_intrs < 1)
+	    return 0;
+    }
+    via = ioremap(vias->addrs->address, 0x2000);
+
+    cuda_state = idle;
+    sys_ctrler = SYS_CTRLER_CUDA;
+
+    err = cuda_init_via();
+    if (err) {
+	printk(KERN_ERR "cuda_init_via() failed\n");
+	via = NULL;
+	return 0;
+    }
+
+    /* Clear and enable interrupts, but only on PPC. On 68K it's done  */
+    /* for us by the main VIA driver in arch/m68k/mac/via.c        */
+
+#ifndef CONFIG_MAC
+    out_8(&via[IFR], 0x7f);	/* clear interrupts by writing 1s */
+    out_8(&via[IER], IER_SET|SR_INT); /* enable interrupt from SR */
+#endif
+
+    /* enable autopoll */
+    cuda_request(&req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, 1);
+    while (!req.complete)
+	cuda_poll();
+
+    return 1;
+}
+#endif /* CONFIG_PPC */
+
+static int __init via_cuda_start(void)
+{
+    if (via == NULL)
+	return -ENODEV;
+
+#ifdef CONFIG_PPC
+    request_OF_resource(vias, 0, NULL);
+#endif
+
+    if (request_irq(CUDA_IRQ, cuda_interrupt, 0, "ADB", cuda_interrupt)) {
+	printk(KERN_ERR "cuda_init: can't get irq %d\n", CUDA_IRQ);
+	return -EAGAIN;
+    }
+
+    printk("Macintosh CUDA driver v0.5 for Unified ADB.\n");
+
+    cuda_fully_inited = 1;
+    return 0;
+}
+
+device_initcall(via_cuda_start);
+
+#ifdef CONFIG_ADB
+static int
+cuda_probe(void)
+{
+#ifdef CONFIG_PPC
+    if (sys_ctrler != SYS_CTRLER_CUDA)
+	return -ENODEV;
+#else
+    if (macintosh_config->adb_type != MAC_ADB_CUDA)
+	return -ENODEV;
+    via = via1;
+#endif
+    return 0;
+}
+
+static int __init
+cuda_init(void)
+{
+#ifdef CONFIG_PPC
+    if (via == NULL)
+	return -ENODEV;
+    return 0;
+#else 
+    int err = cuda_init_via();
+    if (err) {
+	printk(KERN_ERR "cuda_init_via() failed\n");
+	return -ENODEV;
+    }
+
+    return via_cuda_start();
+#endif
+}
+#endif /* CONFIG_ADB */
+
+#define WAIT_FOR(cond, what)					\
+    do {                                                        \
+    	int x;							\
+	for (x = 1000; !(cond); --x) {				\
+	    if (x == 0) {					\
+		printk("Timeout waiting for " what "\n");	\
+		return -ENXIO;					\
+	    }							\
+	    udelay(100);					\
+	}							\
+    } while (0)
+
+static int
+cuda_init_via(void)
+{
+    out_8(&via[DIRB], (in_8(&via[DIRB]) | TACK | TIP) & ~TREQ);	/* TACK & TIP out */
+    out_8(&via[B], in_8(&via[B]) | TACK | TIP);			/* negate them */
+    out_8(&via[ACR] ,(in_8(&via[ACR]) & ~SR_CTRL) | SR_EXT);	/* SR data in */
+    (void)in_8(&via[SR]);						/* clear any left-over data */
+#ifndef CONFIG_MAC
+    out_8(&via[IER], 0x7f);					/* disable interrupts from VIA */
+    (void)in_8(&via[IER]);
+#endif
+
+    /* delay 4ms and then clear any pending interrupt */
+    mdelay(4);
+    (void)in_8(&via[SR]);
+    out_8(&via[IFR], in_8(&via[IFR]) & 0x7f);
+
+    /* sync with the CUDA - assert TACK without TIP */
+    out_8(&via[B], in_8(&via[B]) & ~TACK);
+
+    /* wait for the CUDA to assert TREQ in response */
+    WAIT_FOR((in_8(&via[B]) & TREQ) == 0, "CUDA response to sync");
+
+    /* wait for the interrupt and then clear it */
+    WAIT_FOR(in_8(&via[IFR]) & SR_INT, "CUDA response to sync (2)");
+    (void)in_8(&via[SR]);
+    out_8(&via[IFR], in_8(&via[IFR]) & 0x7f);
+
+    /* finish the sync by negating TACK */
+    out_8(&via[B], in_8(&via[B]) | TACK);
+
+    /* wait for the CUDA to negate TREQ and the corresponding interrupt */
+    WAIT_FOR(in_8(&via[B]) & TREQ, "CUDA response to sync (3)");
+    WAIT_FOR(in_8(&via[IFR]) & SR_INT, "CUDA response to sync (4)");
+    (void)in_8(&via[SR]);
+    out_8(&via[IFR], in_8(&via[IFR]) & 0x7f);
+    out_8(&via[B], in_8(&via[B]) | TIP);	/* should be unnecessary */
+
+    return 0;
+}
+
+#ifdef CONFIG_ADB
+/* Send an ADB command */
+static int
+cuda_send_request(struct adb_request *req, int sync)
+{
+    int i;
+
+    if ((via == NULL) || !cuda_fully_inited) {
+	req->complete = 1;
+	return -ENXIO;
+    }
+  
+    req->reply_expected = 1;
+
+    i = cuda_write(req);
+    if (i)
+	return i;
+
+    if (sync) {
+	while (!req->complete)
+	    cuda_poll();
+    }
+    return 0;
+}
+
+
+/* Enable/disable autopolling */
+static int
+cuda_adb_autopoll(int devs)
+{
+    struct adb_request req;
+
+    if ((via == NULL) || !cuda_fully_inited)
+	return -ENXIO;
+
+    cuda_request(&req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, (devs? 1: 0));
+    while (!req.complete)
+	cuda_poll();
+    return 0;
+}
+
+/* Reset adb bus - how do we do this?? */
+static int
+cuda_reset_adb_bus(void)
+{
+    struct adb_request req;
+
+    if ((via == NULL) || !cuda_fully_inited)
+	return -ENXIO;
+
+    cuda_request(&req, NULL, 2, ADB_PACKET, 0);		/* maybe? */
+    while (!req.complete)
+	cuda_poll();
+    return 0;
+}
+#endif /* CONFIG_ADB */
+/* Construct and send a cuda request */
+int
+cuda_request(struct adb_request *req, void (*done)(struct adb_request *),
+	     int nbytes, ...)
+{
+    va_list list;
+    int i;
+
+    if (via == NULL) {
+	req->complete = 1;
+	return -ENXIO;
+    }
+
+    req->nbytes = nbytes;
+    req->done = done;
+    va_start(list, nbytes);
+    for (i = 0; i < nbytes; ++i)
+	req->data[i] = va_arg(list, int);
+    va_end(list);
+    req->reply_expected = 1;
+    return cuda_write(req);
+}
+
+static int
+cuda_write(struct adb_request *req)
+{
+    unsigned long flags;
+
+    if (req->nbytes < 2 || req->data[0] > CUDA_PACKET) {
+	req->complete = 1;
+	return -EINVAL;
+    }
+    req->next = NULL;
+    req->sent = 0;
+    req->complete = 0;
+    req->reply_len = 0;
+
+    spin_lock_irqsave(&cuda_lock, flags);
+    if (current_req != 0) {
+	last_req->next = req;
+	last_req = req;
+    } else {
+	current_req = req;
+	last_req = req;
+	if (cuda_state == idle)
+	    cuda_start();
+    }
+    spin_unlock_irqrestore(&cuda_lock, flags);
+
+    return 0;
+}
+
+static void
+cuda_start(void)
+{
+    struct adb_request *req;
+
+    /* assert cuda_state == idle */
+    /* get the packet to send */
+    req = current_req;
+    if (req == 0)
+	return;
+    if ((in_8(&via[B]) & TREQ) == 0)
+	return;			/* a byte is coming in from the CUDA */
+
+    /* set the shift register to shift out and send a byte */
+    out_8(&via[ACR], in_8(&via[ACR]) | SR_OUT);
+    out_8(&via[SR], req->data[0]);
+    out_8(&via[B], in_8(&via[B]) & ~TIP);
+    cuda_state = sent_first_byte;
+}
+
+void
+cuda_poll(void)
+{
+    unsigned long flags;
+
+    /* cuda_interrupt only takes a normal lock, we disable
+     * interrupts here to avoid re-entering and thus deadlocking.
+     * An option would be to disable only the IRQ source with
+     * disable_irq(), would that work on m68k ? --BenH
+     */
+    local_irq_save(flags);
+    cuda_interrupt(0, NULL, NULL);
+    local_irq_restore(flags);
+}
+
+static irqreturn_t
+cuda_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+    int status;
+    struct adb_request *req = NULL;
+    unsigned char ibuf[16];
+    int ibuf_len = 0;
+    int complete = 0;
+    unsigned char virq;
+    
+    spin_lock(&cuda_lock);
+
+    virq = in_8(&via[IFR]) & 0x7f;
+    out_8(&via[IFR], virq);   
+    if ((virq & SR_INT) == 0) {
+        spin_unlock(&cuda_lock);
+	return IRQ_NONE;
+    }
+    
+    status = (~in_8(&via[B]) & (TIP|TREQ)) | (in_8(&via[ACR]) & SR_OUT);
+    /* printk("cuda_interrupt: state=%d status=%x\n", cuda_state, status); */
+    switch (cuda_state) {
+    case idle:
+	/* CUDA has sent us the first byte of data - unsolicited */
+	if (status != TREQ)
+	    printk("cuda: state=idle, status=%x\n", status);
+	(void)in_8(&via[SR]);
+	out_8(&via[B], in_8(&via[B]) & ~TIP);
+	cuda_state = reading;
+	reply_ptr = cuda_rbuf;
+	reading_reply = 0;
+	break;
+
+    case awaiting_reply:
+	/* CUDA has sent us the first byte of data of a reply */
+	if (status != TREQ)
+	    printk("cuda: state=awaiting_reply, status=%x\n", status);
+	(void)in_8(&via[SR]);
+	out_8(&via[B], in_8(&via[B]) & ~TIP);
+	cuda_state = reading;
+	reply_ptr = current_req->reply;
+	reading_reply = 1;
+	break;
+
+    case sent_first_byte:
+	if (status == TREQ + TIP + SR_OUT) {
+	    /* collision */
+	    out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT);
+	    (void)in_8(&via[SR]);
+	    out_8(&via[B], in_8(&via[B]) | TIP | TACK);
+	    cuda_state = idle;
+	} else {
+	    /* assert status == TIP + SR_OUT */
+	    if (status != TIP + SR_OUT)
+		printk("cuda: state=sent_first_byte status=%x\n", status);
+	    out_8(&via[SR], current_req->data[1]);
+	    out_8(&via[B], in_8(&via[B]) ^ TACK);
+	    data_index = 2;
+	    cuda_state = sending;
+	}
+	break;
+
+    case sending:
+	req = current_req;
+	if (data_index >= req->nbytes) {
+	    out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT);
+	    (void)in_8(&via[SR]);
+	    out_8(&via[B], in_8(&via[B]) | TACK | TIP);
+	    req->sent = 1;
+	    if (req->reply_expected) {
+		cuda_state = awaiting_reply;
+	    } else {
+		current_req = req->next;
+		complete = 1;
+		/* not sure about this */
+		cuda_state = idle;
+		cuda_start();
+	    }
+	} else {
+	    out_8(&via[SR], req->data[data_index++]);
+	    out_8(&via[B], in_8(&via[B]) ^ TACK);
+	}
+	break;
+
+    case reading:
+	*reply_ptr++ = in_8(&via[SR]);
+	if (status == TIP) {
+	    /* that's all folks */
+	    out_8(&via[B], in_8(&via[B]) | TACK | TIP);
+	    cuda_state = read_done;
+	} else {
+	    /* assert status == TIP | TREQ */
+	    if (status != TIP + TREQ)
+		printk("cuda: state=reading status=%x\n", status);
+	    out_8(&via[B], in_8(&via[B]) ^ TACK);
+	}
+	break;
+
+    case read_done:
+	(void)in_8(&via[SR]);
+	if (reading_reply) {
+	    req = current_req;
+	    req->reply_len = reply_ptr - req->reply;
+	    if (req->data[0] == ADB_PACKET) {
+		/* Have to adjust the reply from ADB commands */
+		if (req->reply_len <= 2 || (req->reply[1] & 2) != 0) {
+		    /* the 0x2 bit indicates no response */
+		    req->reply_len = 0;
+		} else {
+		    /* leave just the command and result bytes in the reply */
+		    req->reply_len -= 2;
+		    memmove(req->reply, req->reply + 2, req->reply_len);
+		}
+	    }
+	    current_req = req->next;
+	    complete = 1;
+	} else {
+	    /* This is tricky. We must break the spinlock to call
+	     * cuda_input. However, doing so means we might get
+	     * re-entered from another CPU getting an interrupt
+	     * or calling cuda_poll(). I ended up using the stack
+	     * (it's only for 16 bytes) and moving the actual
+	     * call to cuda_input to outside of the lock.
+	     */
+	    ibuf_len = reply_ptr - cuda_rbuf;
+	    memcpy(ibuf, cuda_rbuf, ibuf_len);
+	}
+	if (status == TREQ) {
+	    out_8(&via[B], in_8(&via[B]) & ~TIP);
+	    cuda_state = reading;
+	    reply_ptr = cuda_rbuf;
+	    reading_reply = 0;
+	} else {
+	    cuda_state = idle;
+	    cuda_start();
+	}
+	break;
+
+    default:
+	printk("cuda_interrupt: unknown cuda_state %d?\n", cuda_state);
+    }
+    spin_unlock(&cuda_lock);
+    if (complete && req) {
+    	void (*done)(struct adb_request *) = req->done;
+    	mb();
+    	req->complete = 1;
+    	/* Here, we assume that if the request has a done member, the
+    	 * struct request will survive to setting req->complete to 1
+    	 */
+    	if (done)
+		(*done)(req);
+    }
+    if (ibuf_len)
+	cuda_input(ibuf, ibuf_len, regs);
+    return IRQ_HANDLED;
+}
+
+static void
+cuda_input(unsigned char *buf, int nb, struct pt_regs *regs)
+{
+    int i;
+
+    switch (buf[0]) {
+    case ADB_PACKET:
+#ifdef CONFIG_XMON
+	if (nb == 5 && buf[2] == 0x2c) {
+	    extern int xmon_wants_key, xmon_adb_keycode;
+	    if (xmon_wants_key) {
+		xmon_adb_keycode = buf[3];
+		return;
+	    }
+	}
+#endif /* CONFIG_XMON */
+#ifdef CONFIG_ADB
+	adb_input(buf+2, nb-2, regs, buf[1] & 0x40);
+#endif /* CONFIG_ADB */
+	break;
+
+    default:
+	printk("data from cuda (%d bytes):", nb);
+	for (i = 0; i < nb; ++i)
+	    printk(" %.2x", buf[i]);
+	printk("\n");
+    }
+}
diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c
new file mode 100644
index 0000000..e9a159a
--- /dev/null
+++ b/drivers/macintosh/via-macii.c
@@ -0,0 +1,653 @@
+/*
+ * Device driver for the via ADB on (many) Mac II-class machines
+ *
+ * Based on the original ADB keyboard handler Copyright (c) 1997 Alan Cox
+ * Also derived from code Copyright (C) 1996 Paul Mackerras.
+ *
+ * With various updates provided over the years by Michael Schmitz,
+ * Guideo Koerber and others.
+ *
+ * Rewrite for Unified ADB by Joshua M. Thompson (funaho@jurai.org)
+ *
+ * 1999-08-02 (jmt) - Initial rewrite for Unified ADB.
+ * 2000-03-29 Tony Mantler <tonym@mac.linux-m68k.org>
+ * 				- Big overhaul, should actually work now.
+ */
+ 
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/adb.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/machw.h>
+#include <asm/mac_via.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+static volatile unsigned char *via;
+
+/* VIA registers - spaced 0x200 bytes apart */
+#define RS		0x200		/* skip between registers */
+#define B		0		/* B-side data */
+#define A		RS		/* A-side data */
+#define DIRB		(2*RS)		/* B-side direction (1=output) */
+#define DIRA		(3*RS)		/* A-side direction (1=output) */
+#define T1CL		(4*RS)		/* Timer 1 ctr/latch (low 8 bits) */
+#define T1CH		(5*RS)		/* Timer 1 counter (high 8 bits) */
+#define T1LL		(6*RS)		/* Timer 1 latch (low 8 bits) */
+#define T1LH		(7*RS)		/* Timer 1 latch (high 8 bits) */
+#define T2CL		(8*RS)		/* Timer 2 ctr/latch (low 8 bits) */
+#define T2CH		(9*RS)		/* Timer 2 counter (high 8 bits) */
+#define SR		(10*RS)		/* Shift register */
+#define ACR		(11*RS)		/* Auxiliary control register */
+#define PCR		(12*RS)		/* Peripheral control register */
+#define IFR		(13*RS)		/* Interrupt flag register */
+#define IER		(14*RS)		/* Interrupt enable register */
+#define ANH		(15*RS)		/* A-side data, no handshake */
+
+/* Bits in B data register: all active low */
+#define TREQ		0x08		/* Transfer request (input) */
+#define TACK		0x10		/* Transfer acknowledge (output) */
+#define TIP		0x20		/* Transfer in progress (output) */
+#define ST_MASK		0x30		/* mask for selecting ADB state bits */
+
+/* Bits in ACR */
+#define SR_CTRL		0x1c		/* Shift register control bits */
+#define SR_EXT		0x0c		/* Shift on external clock */
+#define SR_OUT		0x10		/* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define IER_SET		0x80		/* set bits in IER */
+#define IER_CLR		0		/* clear bits in IER */
+#define SR_INT		0x04		/* Shift register full/empty */
+#define SR_DATA		0x08		/* Shift register data */
+#define SR_CLOCK	0x10		/* Shift register clock */
+
+/* ADB transaction states according to GMHW */
+#define ST_CMD		0x00		/* ADB state: command byte */
+#define ST_EVEN		0x10		/* ADB state: even data byte */
+#define ST_ODD		0x20		/* ADB state: odd data byte */
+#define ST_IDLE		0x30		/* ADB state: idle, nothing to send */
+
+static int  macii_init_via(void);
+static void macii_start(void);
+static irqreturn_t macii_interrupt(int irq, void *arg, struct pt_regs *regs);
+static void macii_retransmit(int);
+static void macii_queue_poll(void);
+
+static int macii_probe(void);
+static int macii_init(void);
+static int macii_send_request(struct adb_request *req, int sync);
+static int macii_write(struct adb_request *req);
+static int macii_autopoll(int devs);
+static void macii_poll(void);
+static int macii_reset_bus(void);
+
+struct adb_driver via_macii_driver = {
+	"Mac II",
+	macii_probe,
+	macii_init,
+	macii_send_request,
+	macii_autopoll,
+	macii_poll,
+	macii_reset_bus
+};
+
+static enum macii_state {
+	idle,
+	sending,
+	reading,
+	read_done,
+	awaiting_reply
+} macii_state;
+
+static int need_poll    = 0;
+static int command_byte = 0;
+static int last_reply   = 0;
+static int last_active  = 0;
+
+static struct adb_request *current_req;
+static struct adb_request *last_req;
+static struct adb_request *retry_req;
+static unsigned char reply_buf[16];
+static unsigned char *reply_ptr;
+static int reply_len;
+static int reading_reply;
+static int data_index;
+static int first_byte;
+static int prefix_len;
+static int status = ST_IDLE|TREQ;
+static int last_status;
+static int driver_running = 0;
+
+/* debug level 10 required for ADB logging (should be && debug_adb, ideally) */
+
+/* Check for MacII style ADB */
+static int macii_probe(void)
+{
+	if (macintosh_config->adb_type != MAC_ADB_II) return -ENODEV;
+
+	via = via1;
+
+	printk("adb: Mac II ADB Driver v1.0 for Unified ADB\n");
+	return 0;
+}
+
+/* Initialize the driver */
+int macii_init(void)
+{
+	unsigned long flags;
+	int err;
+	
+	local_irq_save(flags);
+	
+	err = macii_init_via();
+	if (err) return err;
+
+	err = request_irq(IRQ_MAC_ADB, macii_interrupt, IRQ_FLG_LOCK, "ADB",
+			  macii_interrupt);
+	if (err) return err;
+
+	macii_state = idle;
+	local_irq_restore(flags);
+	return 0;
+}
+
+/* initialize the hardware */	
+static int macii_init_via(void)
+{
+	unsigned char x;
+
+	/* Set the lines up. We want TREQ as input TACK|TIP as output */
+	via[DIRB] = (via[DIRB] | TACK | TIP) & ~TREQ;
+
+	/* Set up state: idle */
+	via[B] |= ST_IDLE;
+	last_status = via[B] & (ST_MASK|TREQ);
+
+	/* Shift register on input */
+	via[ACR] = (via[ACR] & ~SR_CTRL) | SR_EXT;
+
+	/* Wipe any pending data and int */
+	x = via[SR];
+
+	return 0;
+}
+
+/* Send an ADB poll (Talk Register 0 command, tagged on the front of the request queue) */
+static void macii_queue_poll(void)
+{
+	static int device = 0;
+	static int in_poll=0;
+	static struct adb_request req;
+	unsigned long flags;
+	
+	if (in_poll) printk("macii_queue_poll: double poll!\n");
+
+	in_poll++;
+	if (++device > 15) device = 1;
+
+	adb_request(&req, NULL, ADBREQ_REPLY|ADBREQ_NOSEND, 1,
+		    ADB_READREG(device, 0));
+
+	local_irq_save(flags);
+
+	req.next = current_req;
+	current_req = &req;
+
+	local_irq_restore(flags);
+	macii_start();
+	in_poll--;
+}
+
+/* Send an ADB retransmit (Talk, appended to the request queue) */
+static void macii_retransmit(int device)
+{
+	static int in_retransmit = 0;
+	static struct adb_request rt;
+	unsigned long flags;
+	
+	if (in_retransmit) printk("macii_retransmit: double retransmit!\n");
+
+	in_retransmit++;
+
+	adb_request(&rt, NULL, ADBREQ_REPLY|ADBREQ_NOSEND, 1,
+		    ADB_READREG(device, 0));
+
+	local_irq_save(flags);
+
+	if (current_req != NULL) {
+		last_req->next = &rt;
+		last_req = &rt;
+	} else {
+		current_req = &rt;
+		last_req = &rt;
+	}
+
+	if (macii_state == idle) macii_start();
+
+	local_irq_restore(flags);
+	in_retransmit--;
+}
+
+/* Send an ADB request; if sync, poll out the reply 'till it's done */
+static int macii_send_request(struct adb_request *req, int sync)
+{
+	int i;
+
+	i = macii_write(req);
+	if (i) return i;
+
+	if (sync) {
+		while (!req->complete) macii_poll();
+	}
+	return 0;
+}
+
+/* Send an ADB request */
+static int macii_write(struct adb_request *req)
+{
+	unsigned long flags;
+
+	if (req->nbytes < 2 || req->data[0] != ADB_PACKET || req->nbytes > 15) {
+		req->complete = 1;
+		return -EINVAL;
+	}
+	
+	req->next = 0;
+	req->sent = 0;
+	req->complete = 0;
+	req->reply_len = 0;
+
+	local_irq_save(flags);
+
+	if (current_req != NULL) {
+		last_req->next = req;
+		last_req = req;
+	} else {
+		current_req = req;
+		last_req = req;
+		if (macii_state == idle) macii_start();
+	}
+
+	local_irq_restore(flags);
+	return 0;
+}
+
+/* Start auto-polling */
+static int macii_autopoll(int devs)
+{
+	/* Just ping a random default address */
+	if (!(current_req || retry_req))
+		macii_retransmit( (last_active < 16 && last_active > 0) ? last_active : 3);
+	return 0;
+}
+
+/* Prod the chip without interrupts */
+static void macii_poll(void)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (via[IFR] & SR_INT) macii_interrupt(0, 0, 0);
+	local_irq_restore(flags);
+}
+
+/* Reset the bus */
+static int macii_reset_bus(void)
+{
+	static struct adb_request req;
+	
+	/* Command = 0, Address = ignored */
+	adb_request(&req, NULL, 0, 1, ADB_BUSRESET);
+
+	return 0;
+}
+
+/* Start sending ADB packet */
+static void macii_start(void)
+{
+	unsigned long flags;
+	struct adb_request *req;
+
+	req = current_req;
+	if (!req) return;
+	
+	/* assert macii_state == idle */
+	if (macii_state != idle) {
+		printk("macii_start: called while driver busy (%p %x %x)!\n",
+			req, macii_state, (uint) via1[B] & (ST_MASK|TREQ));
+		return;
+	}
+
+	local_irq_save(flags);
+	
+	/* 
+	 * IRQ signaled ?? (means ADB controller wants to send, or might 
+	 * be end of packet if we were reading)
+	 */
+#if 0 /* FIXME: This is broke broke broke, for some reason */
+	if ((via[B] & TREQ) == 0) {
+		printk("macii_start: weird poll stuff. huh?\n");
+		/*
+		 *	FIXME - we need to restart this on a timer
+		 *	or a collision at boot hangs us.
+		 *	Never set macii_state to idle here, or macii_start 
+		 *	won't be called again from send_request!
+		 *	(need to re-check other cases ...)
+		 */
+		/*
+		 * if the interrupt handler set the need_poll
+		 * flag, it's hopefully a SRQ poll or re-Talk
+		 * so we try to send here anyway
+		 */
+		if (!need_poll) {
+			if (console_loglevel == 10)
+				printk("macii_start: device busy - retry %p state %d status %x!\n", 
+					req, macii_state,
+					(uint) via[B] & (ST_MASK|TREQ));
+			retry_req = req;
+			/* set ADB status here ? */
+			local_irq_restore(flags);
+			return;
+		} else {
+			need_poll = 0;
+		}
+	}
+#endif
+	/*
+	 * Another retry pending? (sanity check)
+	 */
+	if (retry_req) {
+		retry_req = NULL;
+	}
+
+	/* Now send it. Be careful though, that first byte of the request */
+	/* is actually ADB_PACKET; the real data begins at index 1!	  */
+	
+	/* store command byte */
+	command_byte = req->data[1];
+	/* Output mode */
+	via[ACR] |= SR_OUT;
+	/* Load data */
+	via[SR] = req->data[1];
+	/* set ADB state to 'command' */
+	via[B] = (via[B] & ~ST_MASK) | ST_CMD;
+
+	macii_state = sending;
+	data_index = 2;
+
+	local_irq_restore(flags);
+}
+
+/*
+ * The notorious ADB interrupt handler - does all of the protocol handling, 
+ * except for starting new send operations. Relies heavily on the ADB 
+ * controller sending and receiving data, thereby generating SR interrupts
+ * for us. This means there has to be always activity on the ADB bus, otherwise
+ * the whole process dies and has to be re-kicked by sending TALK requests ...
+ * CUDA-based Macs seem to solve this with the autopoll option, for MacII-type
+ * ADB the problem isn't solved yet (retransmit of the latest active TALK seems
+ * a good choice; either on timeout or on a timer interrupt).
+ *
+ * The basic ADB state machine was left unchanged from the original MacII code
+ * by Alan Cox, which was based on the CUDA driver for PowerMac. 
+ * The syntax of the ADB status lines seems to be totally different on MacII, 
+ * though. MacII uses the states Command -> Even -> Odd -> Even ->...-> Idle for
+ * sending, and Idle -> Even -> Odd -> Even ->...-> Idle for receiving. Start 
+ * and end of a receive packet are signaled by asserting /IRQ on the interrupt
+ * line. Timeouts are signaled by a sequence of 4 0xFF, with /IRQ asserted on 
+ * every other byte. SRQ is probably signaled by 3 or more 0xFF tacked on the 
+ * end of a packet. (Thanks to Guido Koerber for eavesdropping on the ADB 
+ * protocol with a logic analyzer!!)
+ *
+ * Note: As of 21/10/97, the MacII ADB part works including timeout detection
+ * and retransmit (Talk to the last active device).
+ */
+static irqreturn_t macii_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+	int x, adbdir;
+	unsigned long flags;
+	struct adb_request *req;
+
+	last_status = status;
+
+	/* prevent races due to SCSI enabling ints */
+	local_irq_save(flags);
+
+	if (driver_running) {
+		local_irq_restore(flags);
+		return IRQ_NONE;
+	}
+
+	driver_running = 1;
+	
+	status = via[B] & (ST_MASK|TREQ);
+	adbdir = via[ACR] & SR_OUT;
+
+	switch (macii_state) {
+		case idle:
+			x = via[SR];
+			first_byte = x;
+			/* set ADB state = even for first data byte */
+			via[B] = (via[B] & ~ST_MASK) | ST_EVEN;
+
+			reply_buf[0] = first_byte; /* was command_byte?? */
+			reply_ptr = reply_buf + 1;
+			reply_len = 1;
+			prefix_len = 1;
+			reading_reply = 0;
+			
+			macii_state = reading;
+			break;
+
+		case awaiting_reply:
+			/* handshake etc. for II ?? */
+			x = via[SR];
+			first_byte = x;
+			/* set ADB state = even for first data byte */
+			via[B] = (via[B] & ~ST_MASK) | ST_EVEN;
+
+			current_req->reply[0] = first_byte;
+			reply_ptr = current_req->reply + 1;
+			reply_len = 1;
+			prefix_len = 1;
+			reading_reply = 1;
+
+			macii_state = reading;			
+			break;
+
+		case sending:
+			req = current_req;
+			if (data_index >= req->nbytes) {
+				/* print an error message if a listen command has no data */
+				if (((command_byte & 0x0C) == 0x08)
+				 /* && (console_loglevel == 10) */
+				    && (data_index == 2))
+					printk("MacII ADB: listen command with no data: %x!\n", 
+						command_byte);
+				/* reset to shift in */
+				via[ACR] &= ~SR_OUT;
+				x = via[SR];
+				/* set ADB state idle - might get SRQ */
+				via[B] = (via[B] & ~ST_MASK) | ST_IDLE;
+
+				req->sent = 1;
+
+				if (req->reply_expected) {
+					macii_state = awaiting_reply;
+				} else {
+					req->complete = 1;
+					current_req = req->next;
+					if (req->done) (*req->done)(req);
+					macii_state = idle;
+					if (current_req || retry_req)
+						macii_start();
+					else
+						macii_retransmit((command_byte & 0xF0) >> 4);
+				}
+			} else {
+				via[SR] = req->data[data_index++];
+
+				if ( (via[B] & ST_MASK) == ST_CMD ) {
+					/* just sent the command byte, set to EVEN */
+					via[B] = (via[B] & ~ST_MASK) | ST_EVEN;
+				} else {
+					/* invert state bits, toggle ODD/EVEN */
+					via[B] ^= ST_MASK;
+				}
+			}
+			break;
+
+		case reading:
+
+			/* timeout / SRQ handling for II hw */
+			if( (first_byte == 0xFF && (reply_len-prefix_len)==2 
+			     && memcmp(reply_ptr-2,"\xFF\xFF",2)==0) || 
+			    ((reply_len-prefix_len)==3 
+			     && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0))
+			{
+				/*
+				 * possible timeout (in fact, most probably a 
+				 * timeout, since SRQ can't be signaled without
+				 * transfer on the bus).
+				 * The last three bytes seen were FF, together 
+				 * with the starting byte (in case we started
+				 * on 'idle' or 'awaiting_reply') this probably
+				 * makes four. So this is mostl likely #5!
+				 * The timeout signal is a pattern 1 0 1 0 0..
+				 * on /INT, meaning we missed it :-(
+				 */
+				x = via[SR];
+				if (x != 0xFF) printk("MacII ADB: mistaken timeout/SRQ!\n");
+
+				if ((status & TREQ) == (last_status & TREQ)) {
+					/* Not a timeout. Unsolicited SRQ? weird. */
+					/* Terminate the SRQ packet and poll */
+					need_poll = 1;
+				}
+				/* There's no packet to get, so reply is blank */
+				via[B] ^= ST_MASK;
+				reply_ptr -= (reply_len-prefix_len);
+				reply_len = prefix_len;
+				macii_state = read_done;
+				break;
+			} /* end timeout / SRQ handling for II hw. */
+
+			if((reply_len-prefix_len)>3
+				&& memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0)
+			{
+				/* SRQ tacked on data packet */
+				/* Terminate the packet (SRQ never ends) */
+				x = via[SR];
+				macii_state = read_done;
+				reply_len -= 3;
+				reply_ptr -= 3;
+				need_poll = 1;
+				/* need to continue; next byte not seen else */
+			} else {
+				/* Sanity check */
+				if (reply_len > 15) reply_len = 0;
+				/* read byte */
+				x = via[SR];
+				*reply_ptr = x;
+				reply_ptr++;
+				reply_len++;
+			}
+			/* The usual handshake ... */
+
+			/*
+			 * NetBSD hints that the next to last byte 
+			 * is sent with IRQ !! 
+			 * Guido found out it's the last one (0x0),
+			 * but IRQ should be asserted already.
+			 * Problem with timeout detection: First
+			 * transition to /IRQ might be second 
+			 * byte of timeout packet! 
+			 * Timeouts are signaled by 4x FF.
+			 */
+			if (((status & TREQ) == 0) && (x == 0x00)) { /* != 0xFF */
+				/* invert state bits, toggle ODD/EVEN */
+				via[B] ^= ST_MASK;
+
+				/* adjust packet length */
+				reply_len--;
+				reply_ptr--;
+				macii_state = read_done;
+			} else {
+				/* not caught: ST_CMD */
+				/* required for re-entry 'reading'! */
+				if ((status & ST_MASK) == ST_IDLE) {
+					/* (in)sanity check - set even */
+					via[B] = (via[B] & ~ST_MASK) | ST_EVEN;
+				} else {
+					/* invert state bits */
+					via[B] ^= ST_MASK;
+				}
+			}
+			break;
+
+		case read_done:
+			x = via[SR];
+			if (reading_reply) {
+				req = current_req;
+				req->reply_len = reply_ptr - req->reply;
+				req->complete = 1;
+				current_req = req->next;
+				if (req->done) (*req->done)(req);
+			} else {
+				adb_input(reply_buf, reply_ptr - reply_buf,
+					  regs, 0);
+			}
+
+			/*
+			 * remember this device ID; it's the latest we got a 
+			 * reply from!
+			 */
+			last_reply = command_byte;
+			last_active = (command_byte & 0xF0) >> 4;
+
+			/* SRQ seen before, initiate poll now */
+			if (need_poll) {
+				macii_state = idle;
+				macii_queue_poll();
+				need_poll = 0;
+				break;
+			}
+			
+			/* set ADB state to idle */
+			via[B] = (via[B] & ~ST_MASK) | ST_IDLE;
+			
+			/* /IRQ seen, so the ADB controller has data for us */
+			if ((via[B] & TREQ) != 0) {
+				macii_state = reading;
+
+				reply_buf[0] = command_byte;
+				reply_ptr = reply_buf + 1;
+				reply_len = 1;
+				prefix_len = 1;
+				reading_reply = 0;
+			} else {
+				/* no IRQ, send next packet or wait */
+				macii_state = idle;
+				if (current_req)
+					macii_start();
+				else
+					macii_retransmit(last_active);
+			}
+			break;
+
+		default:
+		break;
+	}
+	/* reset mutex and interrupts */
+	driver_running = 0;
+	local_irq_restore(flags);
+	return IRQ_HANDLED;
+}
diff --git a/drivers/macintosh/via-maciisi.c b/drivers/macintosh/via-maciisi.c
new file mode 100644
index 0000000..a196697
--- /dev/null
+++ b/drivers/macintosh/via-maciisi.c
@@ -0,0 +1,661 @@
+/*
+ * Device driver for the IIsi-style ADB on some Mac LC and II-class machines
+ *
+ * Based on via-cuda.c and via-macii.c, as well as the original
+ * adb-bus.c, which in turn is somewhat influenced by (but uses no
+ * code from) the NetBSD HWDIRECT ADB code.  Original IIsi driver work
+ * was done by Robert Thompson and integrated into the old style
+ * driver by Michael Schmitz.
+ *
+ * Original sources (c) Alan Cox, Paul Mackerras, and others.
+ *
+ * Rewritten for Unified ADB by David Huggins-Daines <dhd@debian.org>
+ * 
+ * 7/13/2000- extensive changes by Andrew McPherson <andrew@macduff.dhs.org>
+ * Works about 30% of the time now.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/adb.h>
+#include <linux/cuda.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/machw.h>
+#include <asm/mac_via.h>
+
+static volatile unsigned char *via;
+
+/* VIA registers - spaced 0x200 bytes apart - only the ones we actually use */
+#define RS		0x200		/* skip between registers */
+#define B		0		/* B-side data */
+#define A		RS		/* A-side data */
+#define DIRB		(2*RS)		/* B-side direction (1=output) */
+#define DIRA		(3*RS)		/* A-side direction (1=output) */
+#define SR		(10*RS)		/* Shift register */
+#define ACR		(11*RS)		/* Auxiliary control register */
+#define IFR		(13*RS)		/* Interrupt flag register */
+#define IER		(14*RS)		/* Interrupt enable register */
+
+/* Bits in B data register: all active low */
+#define TREQ		0x08		/* Transfer request (input) */
+#define TACK		0x10		/* Transfer acknowledge (output) */
+#define TIP		0x20		/* Transfer in progress (output) */
+#define ST_MASK		0x30		/* mask for selecting ADB state bits */
+
+/* Bits in ACR */
+#define SR_CTRL		0x1c		/* Shift register control bits */
+#define SR_EXT		0x0c		/* Shift on external clock */
+#define SR_OUT		0x10		/* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define IER_SET		0x80		/* set bits in IER */
+#define IER_CLR		0		/* clear bits in IER */
+#define SR_INT		0x04		/* Shift register full/empty */
+#define SR_DATA		0x08		/* Shift register data */
+#define SR_CLOCK	0x10		/* Shift register clock */
+
+#define ADB_DELAY 150
+
+#undef DEBUG_MACIISI_ADB
+
+static struct adb_request* current_req = NULL;
+static struct adb_request* last_req = NULL;
+static unsigned char maciisi_rbuf[16];
+static unsigned char *reply_ptr = NULL;
+static int data_index;
+static int reading_reply;
+static int reply_len;
+static int tmp;
+static int need_sync;
+
+static enum maciisi_state {
+    idle,
+    sending,
+    reading,
+} maciisi_state;
+
+static int maciisi_probe(void);
+static int maciisi_init(void);
+static int maciisi_send_request(struct adb_request* req, int sync);
+static void maciisi_sync(struct adb_request *req);
+static int maciisi_write(struct adb_request* req);
+static irqreturn_t maciisi_interrupt(int irq, void* arg, struct pt_regs* regs);
+static void maciisi_input(unsigned char *buf, int nb, struct pt_regs *regs);
+static int maciisi_init_via(void);
+static void maciisi_poll(void);
+static int maciisi_start(void);
+
+struct adb_driver via_maciisi_driver = {
+	"Mac IIsi",
+	maciisi_probe,
+	maciisi_init,
+	maciisi_send_request,
+	NULL, /* maciisi_adb_autopoll, */
+	maciisi_poll,
+	NULL /* maciisi_reset_adb_bus */
+};
+
+static int
+maciisi_probe(void)
+{
+	if (macintosh_config->adb_type != MAC_ADB_IISI)
+		return -ENODEV;
+
+	via = via1;
+	return 0;
+}
+
+static int
+maciisi_init(void)
+{
+	int err;
+
+	if (via == NULL)
+		return -ENODEV;
+
+	if ((err = maciisi_init_via())) {
+		printk(KERN_ERR "maciisi_init: maciisi_init_via() failed, code %d\n", err);
+		via = NULL;
+		return err;
+	}
+
+	if (request_irq(IRQ_MAC_ADB, maciisi_interrupt, IRQ_FLG_LOCK | IRQ_FLG_FAST, 
+			"ADB", maciisi_interrupt)) {
+		printk(KERN_ERR "maciisi_init: can't get irq %d\n", IRQ_MAC_ADB);
+		return -EAGAIN;
+	}
+
+	printk("adb: Mac IIsi driver v0.2 for Unified ADB.\n");
+	return 0;
+}
+
+/* Flush data from the ADB controller */
+static void
+maciisi_stfu(void)
+{
+	int status = via[B] & (TIP|TREQ);
+
+	if (status & TREQ) {
+#ifdef DEBUG_MACIISI_ADB
+		printk (KERN_DEBUG "maciisi_stfu called with TREQ high!\n");
+#endif
+		return;
+	}
+	
+	udelay(ADB_DELAY);
+	via[ACR] &= ~SR_OUT;
+	via[IER] = IER_CLR | SR_INT;
+
+	udelay(ADB_DELAY);
+
+	status = via[B] & (TIP|TREQ);
+
+	if (!(status & TREQ))
+	{
+		via[B] |= TIP;
+
+		while(1)
+		{
+			int poll_timeout = ADB_DELAY * 5;
+			/* Poll for SR interrupt */
+			while (!(via[IFR] & SR_INT) && poll_timeout-- > 0)
+				status = via[B] & (TIP|TREQ);
+
+			tmp = via[SR]; /* Clear shift register */
+#ifdef DEBUG_MACIISI_ADB
+			printk(KERN_DEBUG "maciisi_stfu: status %x timeout %d data %x\n",
+			       status, poll_timeout, tmp);
+#endif	
+			if(via[B] & TREQ)
+				break;
+	
+			/* ACK on-off */
+			via[B] |= TACK;
+			udelay(ADB_DELAY);
+			via[B] &= ~TACK;
+		}
+
+		/* end frame */
+		via[B] &= ~TIP;
+		udelay(ADB_DELAY);
+	}
+
+	via[IER] = IER_SET | SR_INT;	
+}
+
+/* All specifically VIA-related initialization goes here */
+static int
+maciisi_init_via(void)
+{
+	int	i;
+	
+	/* Set the lines up. We want TREQ as input TACK|TIP as output */
+	via[DIRB] = (via[DIRB] | TACK | TIP) & ~TREQ;
+	/* Shift register on input */
+	via[ACR]  = (via[ACR] & ~SR_CTRL) | SR_EXT;
+#ifdef DEBUG_MACIISI_ADB
+	printk(KERN_DEBUG "maciisi_init_via: initial status %x\n", via[B] & (TIP|TREQ));
+#endif
+	/* Wipe any pending data and int */
+	tmp = via[SR];
+	/* Enable keyboard interrupts */
+	via[IER] = IER_SET | SR_INT;
+	/* Set initial state: idle */
+	via[B] &= ~(TACK|TIP);
+	/* Clear interrupt bit */
+	via[IFR] = SR_INT;
+
+	for(i = 0; i < 60; i++) {
+		udelay(ADB_DELAY);
+		maciisi_stfu();
+		udelay(ADB_DELAY);
+		if(via[B] & TREQ)
+			break;
+	}
+	if (i == 60)
+		printk(KERN_ERR "maciisi_init_via: bus jam?\n");
+
+	maciisi_state = idle;
+	need_sync = 0;
+
+	return 0;
+}
+
+/* Send a request, possibly waiting for a reply */
+static int
+maciisi_send_request(struct adb_request* req, int sync)
+{
+	int i;
+
+#ifdef DEBUG_MACIISI_ADB
+	static int dump_packet = 0;
+#endif
+
+	if (via == NULL) {
+		req->complete = 1;
+		return -ENXIO;
+	}
+
+#ifdef DEBUG_MACIISI_ADB
+	if (dump_packet) {
+		printk(KERN_DEBUG "maciisi_send_request:");
+		for (i = 0; i < req->nbytes; i++) {
+			printk(" %.2x", req->data[i]);
+		}
+		printk(" sync %d\n", sync);
+	}
+#endif
+
+	req->reply_expected = 1;
+	
+	i = maciisi_write(req);
+	if (i)
+	{
+		/* Normally, if a packet requires syncing, that happens at the end of
+		 * maciisi_send_request. But if the transfer fails, it will be restarted
+		 * by maciisi_interrupt(). We use need_sync to tell maciisi_interrupt
+		 * when to sync a packet that it sends out.
+		 * 
+		 * Suggestions on a better way to do this are welcome.
+		 */
+		if(i == -EBUSY && sync)
+			need_sync = 1;
+		else
+			need_sync = 0;
+		return i;
+	}
+	if(sync)
+		maciisi_sync(req);
+	
+	return 0;
+}
+
+/* Poll the ADB chip until the request completes */
+static void maciisi_sync(struct adb_request *req)
+{
+	int count = 0; 
+
+#ifdef DEBUG_MACIISI_ADB
+	printk(KERN_DEBUG "maciisi_sync called\n");
+#endif
+
+	/* If for some reason the ADB chip shuts up on us, we want to avoid an endless loop. */
+	while (!req->complete && count++ < 50) {
+		maciisi_poll();
+	}
+	/* This could be BAD... when the ADB controller doesn't respond
+	 * for this long, it's probably not coming back :-( */
+	if(count >= 50) /* Hopefully shouldn't happen */
+		printk(KERN_ERR "maciisi_send_request: poll timed out!\n");
+}
+
+/* Enqueue a request, and run the queue if possible */
+static int
+maciisi_write(struct adb_request* req)
+{
+	unsigned long flags;
+	int i;
+
+	/* We will accept CUDA packets - the VIA sends them to us, so
+           it figures that we should be able to send them to it */
+	if (req->nbytes < 2 || req->data[0] > CUDA_PACKET) {
+		printk(KERN_ERR "maciisi_write: packet too small or not an ADB or CUDA packet\n");
+		req->complete = 1;
+		return -EINVAL;
+	}
+	req->next = 0;
+	req->sent = 0;
+	req->complete = 0;
+	req->reply_len = 0;
+	
+	local_irq_save(flags);
+
+	if (current_req) {
+		last_req->next = req;
+		last_req = req;
+	} else {
+		current_req = req;
+		last_req = req;
+	}
+	if (maciisi_state == idle)
+	{
+		i = maciisi_start();
+		if(i != 0)
+		{
+			local_irq_restore(flags);
+			return i;
+		}
+	}
+	else
+	{
+#ifdef DEBUG_MACIISI_ADB
+		printk(KERN_DEBUG "maciisi_write: would start, but state is %d\n", maciisi_state);
+#endif
+		local_irq_restore(flags);
+		return -EBUSY;
+	}
+
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static int
+maciisi_start(void)
+{
+	struct adb_request* req;
+	int status;
+
+#ifdef DEBUG_MACIISI_ADB
+	status = via[B] & (TIP | TREQ);
+
+	printk(KERN_DEBUG "maciisi_start called, state=%d, status=%x, ifr=%x\n", maciisi_state, status, via[IFR]);
+#endif
+
+	if (maciisi_state != idle) {
+		/* shouldn't happen */
+		printk(KERN_ERR "maciisi_start: maciisi_start called when driver busy!\n");
+		return -EBUSY;
+	}
+
+	req = current_req;
+	if (req == NULL)
+		return -EINVAL;
+
+	status = via[B] & (TIP|TREQ);
+	if (!(status & TREQ)) {
+#ifdef DEBUG_MACIISI_ADB
+		printk(KERN_DEBUG "maciisi_start: bus busy - aborting\n");
+#endif
+		return -EBUSY;
+	}
+
+	/* Okay, send */
+#ifdef DEBUG_MACIISI_ADB
+	printk(KERN_DEBUG "maciisi_start: sending\n");
+#endif
+	/* Set state to active */
+	via[B] |= TIP;
+	/* ACK off */
+	via[B] &= ~TACK;
+	/* Delay */
+	udelay(ADB_DELAY);
+	/* Shift out and send */
+	via[ACR] |= SR_OUT;
+	via[SR] = req->data[0];
+	data_index = 1;
+	/* ACK on */
+	via[B] |= TACK;
+	maciisi_state = sending;
+
+	return 0;
+}
+
+void
+maciisi_poll(void)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (via[IFR] & SR_INT) {
+		maciisi_interrupt(0, 0, 0);
+	}
+	else /* avoid calling this function too quickly in a loop */
+		udelay(ADB_DELAY);
+
+	local_irq_restore(flags);
+}
+
+/* Shift register interrupt - this is *supposed* to mean that the
+   register is either full or empty. In practice, I have no idea what
+   it means :( */
+static irqreturn_t
+maciisi_interrupt(int irq, void* arg, struct pt_regs* regs)
+{
+	int status;
+	struct adb_request *req;
+#ifdef DEBUG_MACIISI_ADB
+	static int dump_reply = 0;
+#endif
+	int i;
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	status = via[B] & (TIP|TREQ);
+#ifdef DEBUG_MACIISI_ADB
+	printk(KERN_DEBUG "state %d status %x ifr %x\n", maciisi_state, status, via[IFR]);
+#endif
+
+	if (!(via[IFR] & SR_INT)) {
+		/* Shouldn't happen, we hope */
+		printk(KERN_ERR "maciisi_interrupt: called without interrupt flag set\n");
+		local_irq_restore(flags);
+		return IRQ_NONE;
+	}
+
+	/* Clear the interrupt */
+	/* via[IFR] = SR_INT; */
+
+ switch_start:
+	switch (maciisi_state) {
+	case idle:
+		if (status & TIP)
+			printk(KERN_ERR "maciisi_interrupt: state is idle but TIP asserted!\n");
+
+		if(!reading_reply)
+			udelay(ADB_DELAY);
+		/* Shift in */
+		via[ACR] &= ~SR_OUT;
+ 		/* Signal start of frame */
+		via[B] |= TIP;
+		/* Clear the interrupt (throw this value on the floor, it's useless) */
+		tmp = via[SR];
+		/* ACK adb chip, high-low */
+		via[B] |= TACK;
+		udelay(ADB_DELAY);
+		via[B] &= ~TACK;
+		reply_len = 0;
+		maciisi_state = reading;
+		if (reading_reply) {
+			reply_ptr = current_req->reply;
+		} else {
+			reply_ptr = maciisi_rbuf;
+		}
+		break;
+
+	case sending:
+		/* via[SR]; */
+		/* Set ACK off */
+		via[B] &= ~TACK;
+		req = current_req;
+
+		if (!(status & TREQ)) {
+			/* collision */
+			printk(KERN_ERR "maciisi_interrupt: send collision\n");
+			/* Set idle and input */
+			via[ACR] &= ~SR_OUT;
+			tmp = via[SR];
+			via[B] &= ~TIP;
+			/* Must re-send */
+			reading_reply = 0;
+			reply_len = 0;
+			maciisi_state = idle;
+			udelay(ADB_DELAY);
+			/* process this now, because the IFR has been cleared */
+			goto switch_start;
+		}
+
+		udelay(ADB_DELAY);
+
+		if (data_index >= req->nbytes) {
+			/* Sent the whole packet, put the bus back in idle state */
+			/* Shift in, we are about to read a reply (hopefully) */
+			via[ACR] &= ~SR_OUT;
+			tmp = via[SR];
+			/* End of frame */
+			via[B] &= ~TIP;
+			req->sent = 1;
+			maciisi_state = idle;
+			if (req->reply_expected) {
+				/* Note: only set this once we've
+                                   successfully sent the packet */
+				reading_reply = 1;
+			} else {
+				current_req = req->next;
+				if (req->done)
+					(*req->done)(req);
+				/* Do any queued requests now */
+				i = maciisi_start();
+				if(i == 0 && need_sync) {
+					/* Packet needs to be synced */
+					maciisi_sync(current_req);
+				}
+				if(i != -EBUSY)
+					need_sync = 0;
+			}
+		} else {
+			/* Sending more stuff */
+			/* Shift out */
+			via[ACR] |= SR_OUT;
+			/* Write */
+			via[SR] = req->data[data_index++];
+			/* Signal 'byte ready' */
+			via[B] |= TACK;
+		}
+		break;
+
+	case reading:
+		/* Shift in */
+		/* via[ACR] &= ~SR_OUT; */ /* Not in 2.2 */
+		if (reply_len++ > 16) {
+			printk(KERN_ERR "maciisi_interrupt: reply too long, aborting read\n");
+			via[B] |= TACK;
+			udelay(ADB_DELAY);
+			via[B] &= ~(TACK|TIP);
+			maciisi_state = idle;
+			i = maciisi_start();
+			if(i == 0 && need_sync) {
+				/* Packet needs to be synced */
+				maciisi_sync(current_req);
+			}
+			if(i != -EBUSY)
+				need_sync = 0;
+			break;
+		}
+		/* Read data */
+		*reply_ptr++ = via[SR];
+		status = via[B] & (TIP|TREQ);
+		/* ACK on/off */
+		via[B] |= TACK;
+		udelay(ADB_DELAY);
+		via[B] &= ~TACK;	
+		if (!(status & TREQ))
+			break; /* more stuff to deal with */
+		
+		/* end of frame */
+		via[B] &= ~TIP;
+		tmp = via[SR]; /* That's what happens in 2.2 */
+		udelay(ADB_DELAY); /* Give controller time to recover */
+
+		/* end of packet, deal with it */
+		if (reading_reply) {
+			req = current_req;
+			req->reply_len = reply_ptr - req->reply;
+			if (req->data[0] == ADB_PACKET) {
+				/* Have to adjust the reply from ADB commands */
+				if (req->reply_len <= 2 || (req->reply[1] & 2) != 0) {
+					/* the 0x2 bit indicates no response */
+					req->reply_len = 0;
+				} else {
+					/* leave just the command and result bytes in the reply */
+					req->reply_len -= 2;
+					memmove(req->reply, req->reply + 2, req->reply_len);
+				}
+			}
+#ifdef DEBUG_MACIISI_ADB
+			if (dump_reply) {
+				int i;
+				printk(KERN_DEBUG "maciisi_interrupt: reply is ");
+				for (i = 0; i < req->reply_len; ++i)
+					printk(" %.2x", req->reply[i]);
+				printk("\n");
+			}
+#endif
+			req->complete = 1;
+			current_req = req->next;
+			if (req->done)
+				(*req->done)(req);
+			/* Obviously, we got it */
+			reading_reply = 0;
+		} else {
+			maciisi_input(maciisi_rbuf, reply_ptr - maciisi_rbuf, regs);
+		}
+		maciisi_state = idle;
+		status = via[B] & (TIP|TREQ);
+		if (!(status & TREQ)) {
+			/* Timeout?! More likely, another packet coming in already */
+#ifdef DEBUG_MACIISI_ADB
+			printk(KERN_DEBUG "extra data after packet: status %x ifr %x\n",
+			       status, via[IFR]);
+#endif
+#if 0
+			udelay(ADB_DELAY);
+			via[B] |= TIP;
+
+			maciisi_state = reading;
+			reading_reply = 0;
+			reply_ptr = maciisi_rbuf;
+#else
+			/* Process the packet now */
+			reading_reply = 0;
+			goto switch_start;
+#endif
+			/* We used to do this... but the controller might actually have data for us */
+			/* maciisi_stfu(); */
+		}
+		else {
+			/* Do any queued requests now if possible */
+			i = maciisi_start();
+			if(i == 0 && need_sync) {
+				/* Packet needs to be synced */
+				maciisi_sync(current_req);
+			}
+			if(i != -EBUSY)
+				need_sync = 0;
+		}
+		break;
+
+	default:
+		printk("maciisi_interrupt: unknown maciisi_state %d?\n", maciisi_state);
+	}
+	local_irq_restore(flags);
+	return IRQ_HANDLED;
+}
+
+static void
+maciisi_input(unsigned char *buf, int nb, struct pt_regs *regs)
+{
+#ifdef DEBUG_MACIISI_ADB
+    int i;
+#endif
+
+    switch (buf[0]) {
+    case ADB_PACKET:
+	    adb_input(buf+2, nb-2, regs, buf[1] & 0x40);
+	    break;
+    default:
+#ifdef DEBUG_MACIISI_ADB
+	    printk(KERN_DEBUG "data from IIsi ADB (%d bytes):", nb);
+	    for (i = 0; i < nb; ++i)
+		    printk(" %.2x", buf[i]);
+	    printk("\n");
+#endif
+	    break;
+    }
+}
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
new file mode 100644
index 0000000..cea1e75
--- /dev/null
+++ b/drivers/macintosh/via-pmu.c
@@ -0,0 +1,3147 @@
+/*
+ * Device driver for the via-pmu on Apple Powermacs.
+ *
+ * The VIA (versatile interface adapter) interfaces to the PMU,
+ * a 6805 microprocessor core whose primary function is to control
+ * battery charging and system power on the PowerBook 3400 and 2400.
+ * The PMU also controls the ADB (Apple Desktop Bus) which connects
+ * to the keyboard and mouse, as well as the non-volatile RAM
+ * and the RTC (real time clock) chip.
+ *
+ * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi.
+ * Copyright (C) 2001-2002 Benjamin Herrenschmidt
+ *
+ * THIS DRIVER IS BECOMING A TOTAL MESS !
+ *  - Cleanup atomically disabling reply to PMU events after
+ *    a sleep or a freq. switch
+ *  - Move sleep code out of here to pmac_pm, merge into new
+ *    common PM infrastructure
+ *  - Move backlight code out as well
+ *  - Save/Restore PCI space properly
+ *
+ */
+#include <stdarg.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/blkdev.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#include <linux/cuda.h>
+#include <linux/smp_lock.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/pm.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+#include <linux/suspend.h>
+#include <linux/syscalls.h>
+#include <linux/cpu.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/sections.h>
+#include <asm/irq.h>
+#include <asm/pmac_feature.h>
+#include <asm/uaccess.h>
+#include <asm/mmu_context.h>
+#include <asm/cputable.h>
+#include <asm/time.h>
+#ifdef CONFIG_PMAC_BACKLIGHT
+#include <asm/backlight.h>
+#endif
+
+/* 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
+
+/* How many iterations between battery polls */
+#define BATTERY_POLLING_COUNT	2
+
+static volatile unsigned char __iomem *via;
+
+/* VIA registers - spaced 0x200 bytes apart */
+#define RS		0x200		/* skip between registers */
+#define B		0		/* B-side data */
+#define A		RS		/* A-side data */
+#define DIRB		(2*RS)		/* B-side direction (1=output) */
+#define DIRA		(3*RS)		/* A-side direction (1=output) */
+#define T1CL		(4*RS)		/* Timer 1 ctr/latch (low 8 bits) */
+#define T1CH		(5*RS)		/* Timer 1 counter (high 8 bits) */
+#define T1LL		(6*RS)		/* Timer 1 latch (low 8 bits) */
+#define T1LH		(7*RS)		/* Timer 1 latch (high 8 bits) */
+#define T2CL		(8*RS)		/* Timer 2 ctr/latch (low 8 bits) */
+#define T2CH		(9*RS)		/* Timer 2 counter (high 8 bits) */
+#define SR		(10*RS)		/* Shift register */
+#define ACR		(11*RS)		/* Auxiliary control register */
+#define PCR		(12*RS)		/* Peripheral control register */
+#define IFR		(13*RS)		/* Interrupt flag register */
+#define IER		(14*RS)		/* Interrupt enable register */
+#define ANH		(15*RS)		/* A-side data, no handshake */
+
+/* Bits in B data register: both active low */
+#define TACK		0x08		/* Transfer acknowledge (input) */
+#define TREQ		0x10		/* Transfer request (output) */
+
+/* Bits in ACR */
+#define SR_CTRL		0x1c		/* Shift register control bits */
+#define SR_EXT		0x0c		/* Shift on external clock */
+#define SR_OUT		0x10		/* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define IER_SET		0x80		/* set bits in IER */
+#define IER_CLR		0		/* clear bits in IER */
+#define SR_INT		0x04		/* Shift register full/empty */
+#define CB2_INT		0x08
+#define CB1_INT		0x10		/* transition on CB1 input */
+
+static volatile enum pmu_state {
+	idle,
+	sending,
+	intack,
+	reading,
+	reading_intr,
+	locked,
+} pmu_state;
+
+static volatile enum int_data_state {
+	int_data_empty,
+	int_data_fill,
+	int_data_ready,
+	int_data_flush
+} int_data_state[2] = { int_data_empty, int_data_empty };
+
+static struct adb_request *current_req;
+static struct adb_request *last_req;
+static struct adb_request *req_awaiting_reply;
+static unsigned char interrupt_data[2][32];
+static int interrupt_data_len[2];
+static int int_data_last;
+static unsigned char *reply_ptr;
+static int data_index;
+static int data_len;
+static volatile int adb_int_pending;
+static volatile int disable_poll;
+static struct adb_request bright_req_1, bright_req_2;
+static struct device_node *vias;
+static int pmu_kind = PMU_UNKNOWN;
+static int pmu_fully_inited = 0;
+static int pmu_has_adb;
+static unsigned char __iomem *gpio_reg = NULL;
+static int gpio_irq = -1;
+static int gpio_irq_enabled = -1;
+static volatile int pmu_suspended = 0;
+static spinlock_t pmu_lock;
+static u8 pmu_intr_mask;
+static int pmu_version;
+static int drop_interrupts;
+#ifdef CONFIG_PMAC_PBOOK
+static int option_lid_wakeup = 1;
+static int sleep_in_progress;
+#endif /* CONFIG_PMAC_PBOOK */
+static unsigned long async_req_locks;
+static unsigned int pmu_irq_stats[11];
+
+static struct proc_dir_entry *proc_pmu_root;
+static struct proc_dir_entry *proc_pmu_info;
+static struct proc_dir_entry *proc_pmu_irqstats;
+static struct proc_dir_entry *proc_pmu_options;
+static int option_server_mode;
+
+#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 */
+
+#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT)
+extern int disable_kernel_backlight;
+#endif /* defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) */
+
+int __fake_sleep;
+int asleep;
+struct notifier_block *sleep_notifier_list;
+
+#ifdef CONFIG_ADB
+static int adb_dev_map = 0;
+static int pmu_adb_flags;
+
+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 irqreturn_t via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs);
+static irqreturn_t gpio1_interrupt(int irq, void *arg, struct pt_regs *regs);
+static int proc_get_info(char *page, char **start, off_t off,
+			  int count, int *eof, void *data);
+static int proc_get_irqstats(char *page, char **start, off_t off,
+			  int count, int *eof, void *data);
+#ifdef CONFIG_PMAC_BACKLIGHT
+static int pmu_set_backlight_level(int level, void* data);
+static int pmu_set_backlight_enable(int on, int level, void* data);
+#endif /* CONFIG_PMAC_BACKLIGHT */
+#ifdef CONFIG_PMAC_PBOOK
+static void pmu_pass_intr(unsigned char *data, int len);
+static int proc_get_batt(char *page, char **start, off_t off,
+			int count, int *eof, void *data);
+#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 __user *buffer,
+			unsigned long count, void *data);
+
+#ifdef CONFIG_ADB
+struct adb_driver via_pmu_driver = {
+	"PMU",
+	pmu_probe,
+	pmu_init,
+	pmu_send_request,
+	pmu_adb_autopoll,
+	pmu_poll_adb,
+	pmu_adb_reset_bus
+};
+#endif /* CONFIG_ADB */
+
+extern void low_sleep_handler(void);
+extern void enable_kernel_altivec(void);
+extern void enable_kernel_fp(void);
+
+#ifdef DEBUG_SLEEP
+int pmu_polled_request(struct adb_request *req);
+int pmu_wink(struct adb_request *req);
+#endif
+
+/*
+ * This table indicates for each PMU opcode:
+ * - the number of data bytes to be sent with the command, or -1
+ *   if a length byte should be sent,
+ * - the number of response bytes which the PMU will return, or
+ *   -1 if it will send a length byte.
+ */
+static const s8 pmu_data_len[256][2] __openfirmwaredata = {
+/*	   0	   1	   2	   3	   4	   5	   6	   7  */
+/*00*/	{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*08*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*10*/	{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*18*/	{ 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0, 0},
+/*20*/	{-1, 0},{ 0, 0},{ 2, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*28*/	{ 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0,-1},
+/*30*/	{ 4, 0},{20, 0},{-1, 0},{ 3, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*38*/	{ 0, 4},{ 0,20},{ 2,-1},{ 2, 1},{ 3,-1},{-1,-1},{-1,-1},{ 4, 0},
+/*40*/	{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*48*/	{ 0, 1},{ 0, 1},{-1,-1},{ 1, 0},{ 1, 0},{-1,-1},{-1,-1},{-1,-1},
+/*50*/	{ 1, 0},{ 0, 0},{ 2, 0},{ 2, 0},{-1, 0},{ 1, 0},{ 3, 0},{ 1, 0},
+/*58*/	{ 0, 1},{ 1, 0},{ 0, 2},{ 0, 2},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},
+/*60*/	{ 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*68*/	{ 0, 3},{ 0, 3},{ 0, 2},{ 0, 8},{ 0,-1},{ 0,-1},{-1,-1},{-1,-1},
+/*70*/	{ 1, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*78*/	{ 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{ 5, 1},{ 4, 1},{ 4, 1},
+/*80*/	{ 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*88*/	{ 0, 5},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*90*/	{ 1, 0},{ 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*98*/	{ 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*a0*/	{ 2, 0},{ 2, 0},{ 2, 0},{ 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},
+/*a8*/	{ 1, 1},{ 1, 0},{ 3, 0},{ 2, 0},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*b0*/	{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*b8*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*c0*/	{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*c8*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*d0*/	{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*d8*/	{ 1, 1},{ 1, 1},{-1,-1},{-1,-1},{ 0, 1},{ 0,-1},{-1,-1},{-1,-1},
+/*e0*/	{-1, 0},{ 4, 0},{ 0, 1},{-1, 0},{-1, 0},{ 4, 0},{-1, 0},{-1, 0},
+/*e8*/	{ 3,-1},{-1,-1},{ 0, 1},{-1,-1},{ 0,-1},{-1,-1},{-1,-1},{ 0, 0},
+/*f0*/	{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*f8*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+};
+
+static char *pbook_type[] = {
+	"Unknown PowerBook",
+	"PowerBook 2400/3400/3500(G3)",
+	"PowerBook G3 Series",
+	"1999 PowerBook G3",
+	"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(void)
+{
+	if (via != 0)
+		return 1;
+	vias = find_devices("via-pmu");
+	if (vias == 0)
+		return 0;
+	if (vias->next != 0)
+		printk(KERN_WARNING "Warning: only using 1st via-pmu\n");
+
+	if (vias->n_addrs < 1 || vias->n_intrs < 1) {
+		printk(KERN_ERR "via-pmu: %d addresses, %d interrupts!\n",
+		       vias->n_addrs, vias->n_intrs);
+		if (vias->n_addrs < 1 || vias->n_intrs < 1)
+			return 0;
+	}
+
+	spin_lock_init(&pmu_lock);
+
+	pmu_has_adb = 1;
+
+	pmu_intr_mask =	PMU_INT_PCEJECT |
+			PMU_INT_SNDBRT |
+			PMU_INT_ADB |
+			PMU_INT_TICK;
+	
+	if (vias->parent->name && ((strcmp(vias->parent->name, "ohare") == 0)
+	    || device_is_compatible(vias->parent, "ohare")))
+		pmu_kind = PMU_OHARE_BASED;
+	else if (device_is_compatible(vias->parent, "paddington"))
+		pmu_kind = PMU_PADDINGTON_BASED;
+	else if (device_is_compatible(vias->parent, "heathrow"))
+		pmu_kind = PMU_HEATHROW_BASED;
+	else if (device_is_compatible(vias->parent, "Keylargo")
+		 || device_is_compatible(vias->parent, "K2-Keylargo")) {
+		struct device_node *gpio, *gpiop;
+
+		pmu_kind = PMU_KEYLARGO_BASED;
+		pmu_has_adb = (find_type_devices("adb") != NULL);
+		pmu_intr_mask =	PMU_INT_PCEJECT |
+				PMU_INT_SNDBRT |
+				PMU_INT_ADB |
+				PMU_INT_TICK |
+				PMU_INT_ENVIRONMENT;
+		
+		gpiop = find_devices("gpio");
+		if (gpiop && gpiop->n_addrs) {
+			gpio_reg = ioremap(gpiop->addrs->address, 0x10);
+			gpio = find_devices("extint-gpio1");
+			if (gpio == NULL)
+				gpio = find_devices("pmu-interrupt");
+			if (gpio && gpio->parent == gpiop && gpio->n_intrs)
+				gpio_irq = gpio->intrs[0].line;
+		}
+	} else
+		pmu_kind = PMU_UNKNOWN;
+
+	via = ioremap(vias->addrs->address, 0x2000);
+	
+	out_8(&via[IER], IER_CLR | 0x7f);	/* disable all intrs */
+	out_8(&via[IFR], 0x7f);			/* clear IFR */
+
+	pmu_state = idle;
+
+	if (!init_pmu()) {
+		via = NULL;
+		return 0;
+	}
+
+	printk(KERN_INFO "PMU driver %d initialized for %s, firmware: %02x\n",
+	       PMU_DRIVER_VERSION, pbook_type[pmu_kind], pmu_version);
+	       
+	sys_ctrler = SYS_CTRLER_PMU;
+	
+	return 1;
+}
+
+#ifdef CONFIG_ADB
+static int __openfirmware
+pmu_probe(void)
+{
+	return vias == NULL? -ENODEV: 0;
+}
+
+static int __init
+pmu_init(void)
+{
+	if (vias == NULL)
+		return -ENODEV;
+	return 0;
+}
+#endif /* CONFIG_ADB */
+
+/*
+ * We can't wait until pmu_init gets called, that happens too late.
+ * It happens after IDE and SCSI initialization, which can take a few
+ * seconds, and by that time the PMU could have given up on us and
+ * turned us off.
+ * Thus this is called with arch_initcall rather than device_initcall.
+ */
+static int __init via_pmu_start(void)
+{
+	if (vias == NULL)
+		return -ENODEV;
+
+	bright_req_1.complete = 1;
+	bright_req_2.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)) {
+		printk(KERN_ERR "VIA-PMU: can't get irq %d\n",
+		       vias->intrs[0].line);
+		return -EAGAIN;
+	}
+
+	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);
+		gpio_irq_enabled = 1;
+	}
+
+	/* Enable interrupts */
+	out_8(&via[IER], IER_SET | SR_INT | CB1_INT);
+
+	pmu_fully_inited = 1;
+
+	/* 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;
+}
+
+arch_initcall(via_pmu_start);
+
+/*
+ * This has to be done after pci_init, which is a subsys_initcall.
+ */
+static int __init via_pmu_dev_init(void)
+{
+	if (vias == NULL)
+		return -ENODEV;
+
+#ifndef CONFIG_PPC64
+	request_OF_resource(vias, 0, NULL);
+#endif
+#ifdef CONFIG_PMAC_BACKLIGHT
+	/* Enable backlight */
+	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")) {
+		int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO,
+			NULL, PMAC_MB_INFO_MODEL, 0);
+		pmu_battery_count = 1;
+		if (mb == PMAC_TYPE_COMET)
+			pmu_batteries[0].flags |= PMU_BATT_TYPE_COMET;
+		else
+			pmu_batteries[0].flags |= PMU_BATT_TYPE_HOOPER;
+	} else if (machine_is_compatible("AAPL,PowerBook1998") ||
+		machine_is_compatible("PowerBook1,1")) {
+		pmu_battery_count = 2;
+		pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART;
+		pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART;
+	} else {
+		struct device_node* prim = find_devices("power-mgt");
+		u32 *prim_info = NULL;
+		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;
+			pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART;
+			if (pmu_battery_count > 1)
+				pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART;
+		}
+	}
+#endif /* CONFIG_PMAC_PBOOK */
+	/* Create /proc/pmu */
+	proc_pmu_root = proc_mkdir("pmu", NULL);
+	if (proc_pmu_root) {
+#ifdef CONFIG_PMAC_PBOOK
+		int i;
+
+		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_info = create_proc_read_entry("info", 0, proc_pmu_root,
+					proc_get_info, NULL);
+		proc_pmu_irqstats = create_proc_read_entry("interrupts", 0, proc_pmu_root,
+					proc_get_irqstats, NULL);
+		proc_pmu_options = create_proc_entry("options", 0600, proc_pmu_root);
+		if (proc_pmu_options) {
+			proc_pmu_options->nlink = 1;
+			proc_pmu_options->read_proc = proc_read_options;
+			proc_pmu_options->write_proc = proc_write_options;
+		}
+	}
+	return 0;
+}
+
+device_initcall(via_pmu_dev_init);
+
+static int __openfirmware
+init_pmu(void)
+{
+	int timeout;
+	struct adb_request req;
+
+	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, pmu_intr_mask);
+	timeout =  100000;
+	while (!req.complete) {
+		if (--timeout < 0) {
+			printk(KERN_ERR "init_pmu: no response from PMU\n");
+			return 0;
+		}
+		udelay(10);
+		pmu_poll();
+	}
+
+	/* ack all pending interrupts */
+	timeout = 100000;
+	interrupt_data[0][0] = 1;
+	while (interrupt_data[0][0] || pmu_state != idle) {
+		if (--timeout < 0) {
+			printk(KERN_ERR "init_pmu: timed out acking intrs\n");
+			return 0;
+		}
+		if (pmu_state == idle)
+			adb_int_pending = 1;
+		via_pmu_interrupt(0, NULL, NULL);
+		udelay(10);
+	}
+
+	/* Tell PMU we are ready.  */
+	if (pmu_kind == PMU_KEYLARGO_BASED) {
+		pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
+		while (!req.complete)
+			pmu_poll();
+	}
+
+	/* Read PMU version */
+	pmu_request(&req, NULL, 1, PMU_GET_VERSION);
+	pmu_wait_complete(&req);
+	if (req.reply_len > 0)
+		pmu_version = req.reply[0];
+	
+	/* Read server mode setting */
+	if (pmu_kind == PMU_KEYLARGO_BASED) {
+		pmu_request(&req, NULL, 2, PMU_POWER_EVENTS,
+			    PMU_PWR_GET_POWERUP_EVENTS);
+		pmu_wait_complete(&req);
+		if (req.reply_len == 2) {
+			if (req.reply[1] & PMU_PWR_WAKEUP_AC_INSERT)
+				option_server_mode = 1;
+			printk(KERN_INFO "via-pmu: Server Mode is %s\n",
+			       option_server_mode ? "enabled" : "disabled");
+		}
+	}
+	return 1;
+}
+
+int
+pmu_get_model(void)
+{
+	return pmu_kind;
+}
+
+#ifndef CONFIG_PPC64
+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();
+}
+#endif
+
+static void pmu_set_server_mode(int server_mode)
+{
+	struct adb_request req;
+
+	if (pmu_kind != PMU_KEYLARGO_BASED)
+		return;
+
+	option_server_mode = server_mode;
+	pmu_request(&req, NULL, 2, PMU_POWER_EVENTS, PMU_PWR_GET_POWERUP_EVENTS);
+	pmu_wait_complete(&req);
+	if (req.reply_len < 2)
+		return;
+	if (server_mode)
+		pmu_request(&req, NULL, 4, PMU_POWER_EVENTS,
+			    PMU_PWR_SET_POWERUP_EVENTS,
+			    req.reply[0], PMU_PWR_WAKEUP_AC_INSERT); 
+	else
+		pmu_request(&req, NULL, 4, PMU_POWER_EVENTS,
+			    PMU_PWR_CLR_POWERUP_EVENTS,
+			    req.reply[0], PMU_PWR_WAKEUP_AC_INSERT); 
+	pmu_wait_complete(&req);
+}
+
+#ifdef CONFIG_PMAC_PBOOK
+
+/* This new version of the code for 2400/3400/3500 powerbooks
+ * is inspired from the implementation in gkrellm-pmu
+ */
+static void __pmac
+done_battery_state_ohare(struct adb_request* req)
+{
+	/* format:
+	 *  [0]    :  flags
+	 *    0x01 :  AC indicator
+	 *    0x02 :  charging
+	 *    0x04 :  battery exist
+	 *    0x08 :  
+	 *    0x10 :  
+	 *    0x20 :  full charged
+	 *    0x40 :  pcharge reset
+	 *    0x80 :  battery exist
+	 *
+	 *  [1][2] :  battery voltage
+	 *  [3]    :  CPU temperature
+	 *  [4]    :  battery temperature
+	 *  [5]    :  current
+	 *  [6][7] :  pcharge
+	 *              --tkoba
+	 */
+	unsigned int bat_flags = PMU_BATT_TYPE_HOOPER;
+	long pcharge, charge, vb, vmax, lmax;
+	long vmax_charging, vmax_charged;
+	long amperage, voltage, time, max;
+	int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO,
+			NULL, PMAC_MB_INFO_MODEL, 0);
+
+	if (req->reply[0] & 0x01)
+		pmu_power_flags |= PMU_PWR_AC_PRESENT;
+	else
+		pmu_power_flags &= ~PMU_PWR_AC_PRESENT;
+	
+	if (mb == PMAC_TYPE_COMET) {
+		vmax_charged = 189;
+		vmax_charging = 213;
+		lmax = 6500;
+	} else {
+		vmax_charged = 330;
+		vmax_charging = 330;
+		lmax = 6500;
+	}
+	vmax = vmax_charged;
+
+	/* If battery installed */
+	if (req->reply[0] & 0x04) {
+		bat_flags |= PMU_BATT_PRESENT;
+		if (req->reply[0] & 0x02)
+			bat_flags |= PMU_BATT_CHARGING;
+		vb = (req->reply[1] << 8) | req->reply[2];
+		voltage = (vb * 265 + 72665) / 10;
+		amperage = req->reply[5];
+		if ((req->reply[0] & 0x01) == 0) {
+			if (amperage > 200)
+				vb += ((amperage - 200) * 15)/100;
+		} else if (req->reply[0] & 0x02) {
+			vb = (vb * 97) / 100;
+			vmax = vmax_charging;
+		}
+		charge = (100 * vb) / vmax;
+		if (req->reply[0] & 0x40) {
+			pcharge = (req->reply[6] << 8) + req->reply[7];
+			if (pcharge > lmax)
+				pcharge = lmax;
+			pcharge *= 100;
+			pcharge = 100 - pcharge / lmax;
+			if (pcharge < charge)
+				charge = pcharge;
+		}
+		if (amperage > 0)
+			time = (charge * 16440) / amperage;
+		else
+			time = 0;
+		max = 100;
+		amperage = -amperage;
+	} else
+		charge = max = amperage = voltage = time = 0;
+
+	pmu_batteries[pmu_cur_battery].flags = bat_flags;
+	pmu_batteries[pmu_cur_battery].charge = charge;
+	pmu_batteries[pmu_cur_battery].max_charge = max;
+	pmu_batteries[pmu_cur_battery].amperage = amperage;
+	pmu_batteries[pmu_cur_battery].voltage = voltage;
+	pmu_batteries[pmu_cur_battery].time_remaining = time;
+
+	clear_bit(0, &async_req_locks);
+}
+
+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 = PMU_BATT_TYPE_SMART;
+	int amperage;
+	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;
+
+
+	capa = max = amperage = voltage = 0;
+	
+	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];
+				amperage = *((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];
+				amperage = *((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;
+		}
+	}
+
+	if ((req->reply[1] & 0x01) && (amperage > 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].amperage = amperage;
+	pmu_batteries[pmu_cur_battery].voltage = voltage;
+	if (amperage) {
+		if ((req->reply[1] & 0x01) && (amperage > 0))
+			pmu_batteries[pmu_cur_battery].time_remaining
+				= ((max-capa) * 3600) / amperage;
+		else
+			pmu_batteries[pmu_cur_battery].time_remaining
+				= (capa * 3600) / (-amperage);
+	} else
+		pmu_batteries[pmu_cur_battery].time_remaining = 0;
+
+	pmu_cur_battery = (pmu_cur_battery + 1) % pmu_battery_count;
+
+	clear_bit(0, &async_req_locks);
+}
+
+static void __pmac
+query_battery_state(void)
+{
+	if (test_and_set_bit(0, &async_req_locks))
+		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 __pmac
+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;
+}
+
+static int __pmac
+proc_get_irqstats(char *page, char **start, off_t off,
+		  int count, int *eof, void *data)
+{
+	int i;
+	char* p = page;
+	static const char *irq_names[] = {
+		"Total CB1 triggered events",
+		"Total GPIO1 triggered events",
+		"PC-Card eject button",
+		"Sound/Brightness button",
+		"ADB message",
+		"Battery state change",
+		"Environment interrupt",
+		"Tick timer",
+		"Ghost interrupt (zero len)",
+		"Empty interrupt (empty mask)",
+		"Max irqs in a row"
+        };
+
+	for (i=0; i<11; i++) {
+		p += sprintf(p, " %2u: %10u (%s)\n",
+			     i, pmu_irq_stats[i], irq_names[i]);
+	}
+	return p - page;
+}
+
+#ifdef CONFIG_PMAC_PBOOK
+static int __pmac
+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].amperage);
+	p += sprintf(p, "voltage    : %d\n",
+		pmu_batteries[batnum].voltage);
+	p += sprintf(p, "time rem.  : %d\n",
+		pmu_batteries[batnum].time_remaining);
+
+	return p - page;
+}
+#endif /* CONFIG_PMAC_PBOOK */
+
+static int __pmac
+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 &&
+	    pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0)
+		p += sprintf(p, "lid_wakeup=%d\n", option_lid_wakeup);
+#endif /* CONFIG_PMAC_PBOOK */
+	if (pmu_kind == PMU_KEYLARGO_BASED)
+		p += sprintf(p, "server_mode=%d\n", option_server_mode);
+
+	return p - page;
+}
+			
+static int __pmac
+proc_write_options(struct file *file, const char __user *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 &&
+	    pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0)
+		if (!strcmp(label, "lid_wakeup"))
+			option_lid_wakeup = ((*val) == '1');
+#endif /* CONFIG_PMAC_PBOOK */
+	if (pmu_kind == PMU_KEYLARGO_BASED && !strcmp(label, "server_mode")) {
+		int new_value;
+		new_value = ((*val) == '1');
+		if (new_value != option_server_mode)
+			pmu_set_server_mode(new_value);
+	}
+	return fcount;
+}
+
+#ifdef CONFIG_ADB
+/* Send an ADB command */
+static int __pmac
+pmu_send_request(struct adb_request *req, int sync)
+{
+	int i, ret;
+
+	if ((vias == NULL) || (!pmu_fully_inited)) {
+		req->complete = 1;
+		return -ENXIO;
+	}
+
+	ret = -EINVAL;
+
+	switch (req->data[0]) {
+	case PMU_PACKET:
+		for (i = 0; i < req->nbytes - 1; ++i)
+			req->data[i] = req->data[i+1];
+		--req->nbytes;
+		if (pmu_data_len[req->data[0]][1] != 0) {
+			req->reply[0] = ADB_RET_OK;
+			req->reply_len = 1;
+		} else
+			req->reply_len = 0;
+		ret = pmu_queue_request(req);
+		break;
+	case CUDA_PACKET:
+		switch (req->data[1]) {
+		case CUDA_GET_TIME:
+			if (req->nbytes != 2)
+				break;
+			req->data[0] = PMU_READ_RTC;
+			req->nbytes = 1;
+			req->reply_len = 3;
+			req->reply[0] = CUDA_PACKET;
+			req->reply[1] = 0;
+			req->reply[2] = CUDA_GET_TIME;
+			ret = pmu_queue_request(req);
+			break;
+		case CUDA_SET_TIME:
+			if (req->nbytes != 6)
+				break;
+			req->data[0] = PMU_SET_RTC;
+			req->nbytes = 5;
+			for (i = 1; i <= 4; ++i)
+				req->data[i] = req->data[i+1];
+			req->reply_len = 3;
+			req->reply[0] = CUDA_PACKET;
+			req->reply[1] = 0;
+			req->reply[2] = CUDA_SET_TIME;
+			ret = pmu_queue_request(req);
+			break;
+		}
+		break;
+	case ADB_PACKET:
+	    	if (!pmu_has_adb)
+    			return -ENXIO;
+		for (i = req->nbytes - 1; i > 1; --i)
+			req->data[i+2] = req->data[i];
+		req->data[3] = req->nbytes - 2;
+		req->data[2] = pmu_adb_flags;
+		/*req->data[1] = req->data[1];*/
+		req->data[0] = PMU_ADB_CMD;
+		req->nbytes += 2;
+		req->reply_expected = 1;
+		req->reply_len = 0;
+		ret = pmu_queue_request(req);
+		break;
+	}
+	if (ret) {
+		req->complete = 1;
+		return ret;
+	}
+
+	if (sync)
+		while (!req->complete)
+			pmu_poll();
+
+	return 0;
+}
+
+/* Enable/disable autopolling */
+static int __pmac
+pmu_adb_autopoll(int devs)
+{
+	struct adb_request req;
+
+	if ((vias == NULL) || (!pmu_fully_inited) || !pmu_has_adb)
+		return -ENXIO;
+
+	if (devs) {
+		adb_dev_map = devs;
+		pmu_request(&req, NULL, 5, PMU_ADB_CMD, 0, 0x86,
+			    adb_dev_map >> 8, adb_dev_map);
+		pmu_adb_flags = 2;
+	} else {
+		pmu_request(&req, NULL, 1, PMU_ADB_POLL_OFF);
+		pmu_adb_flags = 0;
+	}
+	while (!req.complete)
+		pmu_poll();
+	return 0;
+}
+
+/* Reset the ADB bus */
+static int __pmac
+pmu_adb_reset_bus(void)
+{
+	struct adb_request req;
+	int save_autopoll = adb_dev_map;
+
+	if ((vias == NULL) || (!pmu_fully_inited) || !pmu_has_adb)
+		return -ENXIO;
+
+	/* anyone got a better idea?? */
+	pmu_adb_autopoll(0);
+
+	req.nbytes = 5;
+	req.done = NULL;
+	req.data[0] = PMU_ADB_CMD;
+	req.data[1] = 0;
+	req.data[2] = ADB_BUSRESET;
+	req.data[3] = 0;
+	req.data[4] = 0;
+	req.reply_len = 0;
+	req.reply_expected = 1;
+	if (pmu_queue_request(&req) != 0) {
+		printk(KERN_ERR "pmu_adb_reset_bus: pmu_queue_request failed\n");
+		return -EIO;
+	}
+	pmu_wait_complete(&req);
+
+	if (save_autopoll != 0)
+		pmu_adb_autopoll(save_autopoll);
+
+	return 0;
+}
+#endif /* CONFIG_ADB */
+
+/* Construct and send a pmu request */
+int __openfirmware
+pmu_request(struct adb_request *req, void (*done)(struct adb_request *),
+	    int nbytes, ...)
+{
+	va_list list;
+	int i;
+
+	if (vias == NULL)
+		return -ENXIO;
+
+	if (nbytes < 0 || nbytes > 32) {
+		printk(KERN_ERR "pmu_request: bad nbytes (%d)\n", nbytes);
+		req->complete = 1;
+		return -EINVAL;
+	}
+	req->nbytes = nbytes;
+	req->done = done;
+	va_start(list, nbytes);
+	for (i = 0; i < nbytes; ++i)
+		req->data[i] = va_arg(list, int);
+	va_end(list);
+	req->reply_len = 0;
+	req->reply_expected = 0;
+	return pmu_queue_request(req);
+}
+
+int __pmac
+pmu_queue_request(struct adb_request *req)
+{
+	unsigned long flags;
+	int nsend;
+
+	if (via == NULL) {
+		req->complete = 1;
+		return -ENXIO;
+	}
+	if (req->nbytes <= 0) {
+		req->complete = 1;
+		return 0;
+	}
+	nsend = pmu_data_len[req->data[0]][0];
+	if (nsend >= 0 && req->nbytes != nsend + 1) {
+		req->complete = 1;
+		return -EINVAL;
+	}
+
+	req->next = NULL;
+	req->sent = 0;
+	req->complete = 0;
+
+	spin_lock_irqsave(&pmu_lock, flags);
+	if (current_req != 0) {
+		last_req->next = req;
+		last_req = req;
+	} else {
+		current_req = req;
+		last_req = req;
+		if (pmu_state == idle)
+			pmu_start();
+	}
+	spin_unlock_irqrestore(&pmu_lock, flags);
+
+	return 0;
+}
+
+static inline void
+wait_for_ack(void)
+{
+	/* Sightly increased the delay, I had one occurrence 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 inline void
+send_byte(int x)
+{
+	volatile unsigned char __iomem *v = via;
+
+	out_8(&v[ACR], in_8(&v[ACR]) | SR_OUT | SR_EXT);
+	out_8(&v[SR], x);
+	out_8(&v[B], in_8(&v[B]) & ~TREQ);		/* assert TREQ */
+	(void)in_8(&v[B]);
+}
+
+static inline void
+recv_byte(void)
+{
+	volatile unsigned char __iomem *v = via;
+
+	out_8(&v[ACR], (in_8(&v[ACR]) & ~SR_OUT) | SR_EXT);
+	in_8(&v[SR]);		/* resets SR */
+	out_8(&v[B], in_8(&v[B]) & ~TREQ);
+	(void)in_8(&v[B]);
+}
+
+static inline void
+pmu_done(struct adb_request *req)
+{
+	void (*done)(struct adb_request *) = req->done;
+	mb();
+	req->complete = 1;
+    	/* Here, we assume that if the request has a done member, the
+    	 * struct request will survive to setting req->complete to 1
+    	 */
+	if (done)
+		(*done)(req);
+}
+
+static void __pmac
+pmu_start(void)
+{
+	struct adb_request *req;
+
+	/* assert pmu_state == idle */
+	/* get the packet to send */
+	req = current_req;
+	if (req == 0 || pmu_state != idle
+	    || (/*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 */
+	send_byte(req->data[0]);
+}
+
+void __openfirmware
+pmu_poll(void)
+{
+	if (!via)
+		return;
+	if (disable_poll)
+		return;
+	via_pmu_interrupt(0, NULL, NULL);
+}
+
+void __openfirmware
+pmu_poll_adb(void)
+{
+	if (!via)
+		return;
+	if (disable_poll)
+		return;
+	/* Kicks ADB read when PMU is suspended */
+	adb_int_pending = 1;
+	do {
+		via_pmu_interrupt(0, NULL, NULL);
+	} while (pmu_suspended && (adb_int_pending || pmu_state != idle
+		|| req_awaiting_reply));
+}
+
+void __openfirmware
+pmu_wait_complete(struct adb_request *req)
+{
+	if (!via)
+		return;
+	while((pmu_state != idle && pmu_state != locked) || !req->complete)
+		via_pmu_interrupt(0, NULL, NULL);
+}
+
+/* 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_irqrestore(&pmu_lock, flags);
+		if (req_awaiting_reply)
+			adb_int_pending = 1;
+		via_pmu_interrupt(0, NULL, NULL);
+		spin_lock_irqsave(&pmu_lock, flags);
+		if (!adb_int_pending && pmu_state == idle && !req_awaiting_reply) {
+#ifdef SUSPEND_USES_PMU
+			pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0);
+			spin_unlock_irqrestore(&pmu_lock, flags);
+			while(!req.complete)
+				pmu_poll();
+#else /* SUSPEND_USES_PMU */
+			if (gpio_irq >= 0)
+				disable_irq_nosync(gpio_irq);
+			out_8(&via[IER], CB1_INT | IER_CLR);
+			spin_unlock_irqrestore(&pmu_lock, flags);
+#endif /* SUSPEND_USES_PMU */
+			break;
+		}
+	} while (1);
+}
+
+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, pmu_intr_mask);
+	spin_unlock_irqrestore(&pmu_lock, flags);
+	while(!req.complete)
+		pmu_poll();
+#else /* SUSPEND_USES_PMU */
+	if (gpio_irq >= 0)
+		enable_irq(gpio_irq);
+	out_8(&via[IER], CB1_INT | IER_SET);
+	spin_unlock_irqrestore(&pmu_lock, flags);
+	pmu_poll();
+#endif /* SUSPEND_USES_PMU */
+}
+
+/* Interrupt data could be the result data from an ADB cmd */
+static void __pmac
+pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs)
+{
+	unsigned char ints, pirq;
+	int i = 0;
+
+	asleep = 0;
+	if (drop_interrupts || len < 1) {
+		adb_int_pending = 0;
+		pmu_irq_stats[8]++;
+		return;
+	}
+
+	/* Get PMU interrupt mask */
+	ints = data[0];
+
+	/* Record zero interrupts for stats */
+	if (ints == 0)
+		pmu_irq_stats[9]++;
+
+	/* Hack to deal with ADB autopoll flag */
+	if (ints & PMU_INT_ADB)
+		ints &= ~(PMU_INT_ADB_AUTO | PMU_INT_AUTO_SRQ_POLL);
+
+next:
+
+	if (ints == 0) {
+		if (i > pmu_irq_stats[10])
+			pmu_irq_stats[10] = i;
+		return;
+	}
+
+	for (pirq = 0; pirq < 8; pirq++)
+		if (ints & (1 << pirq))
+			break;
+	pmu_irq_stats[pirq]++;
+	i++;
+	ints &= ~(1 << pirq);
+
+	/* Note: for some reason, we get an interrupt with len=1,
+	 * data[0]==0 after each normal ADB interrupt, at least
+	 * on the Pismo. Still investigating...  --BenH
+	 */
+	if ((1 << pirq) & PMU_INT_ADB) {
+		if ((data[0] & PMU_INT_ADB_AUTO) == 0) {
+			struct adb_request *req = req_awaiting_reply;
+			if (req == 0) {
+				printk(KERN_ERR "PMU: extra ADB reply\n");
+				return;
+			}
+			req_awaiting_reply = NULL;
+			if (len <= 2)
+				req->reply_len = 0;
+			else {
+				memcpy(req->reply, data + 1, len - 1);
+				req->reply_len = len - 1;
+			}
+			pmu_done(req);
+		} else {
+#if defined(CONFIG_XMON) && !defined(CONFIG_PPC64)
+			if (len == 4 && data[1] == 0x2c) {
+				extern int xmon_wants_key, xmon_adb_keycode;
+				if (xmon_wants_key) {
+					xmon_adb_keycode = data[2];
+					return;
+				}
+			}
+#endif /* defined(CONFIG_XMON) && !defined(CONFIG_PPC64) */
+#ifdef CONFIG_ADB
+			/*
+			 * XXX On the [23]400 the PMU gives us an up
+			 * event for keycodes 0x74 or 0x75 when the PC
+			 * card eject buttons are released, so we
+			 * ignore those events.
+			 */
+			if (!(pmu_kind == PMU_OHARE_BASED && len == 4
+			      && data[1] == 0x2c && data[3] == 0xff
+			      && (data[2] & ~1) == 0xf4))
+				adb_input(data+1, len-1, regs, 1);
+#endif /* CONFIG_ADB */		
+		}
+	}
+	/* Sound/brightness button pressed */
+	else if ((1 << pirq) & PMU_INT_SNDBRT) {
+#ifdef CONFIG_PMAC_BACKLIGHT
+		if (len == 3)
+#ifdef CONFIG_INPUT_ADBHID
+			if (!disable_kernel_backlight)
+#endif /* CONFIG_INPUT_ADBHID */
+				set_backlight_level(data[1] >> 4);
+#endif /* CONFIG_PMAC_BACKLIGHT */
+	}
+	/* Tick interrupt */
+	else if ((1 << pirq) & PMU_INT_TICK) {
+#ifdef CONFIG_PMAC_PBOOK
+		/* Environement or tick interrupt, query batteries */
+		if (pmu_battery_count) {
+			if ((--query_batt_timer) == 0) {
+				query_battery_state();
+				query_batt_timer = BATTERY_POLLING_COUNT;
+			}
+		}
+        }
+	else if ((1 << pirq) & PMU_INT_ENVIRONMENT) {
+		if (pmu_battery_count)
+			query_battery_state();
+		pmu_pass_intr(data, len);
+	} else {
+	       pmu_pass_intr(data, len);
+#endif /* CONFIG_PMAC_PBOOK */
+	}
+	goto next;
+}
+
+static struct adb_request* __pmac
+pmu_sr_intr(struct pt_regs *regs)
+{
+	struct adb_request *req;
+	int bite = 0;
+
+	if (via[B] & TREQ) {
+		printk(KERN_ERR "PMU: spurious SR intr (%x)\n", via[B]);
+		out_8(&via[IFR], SR_INT);
+		return NULL;
+	}
+	/* The ack may not yet be low when we get the interrupt */
+	while ((in_8(&via[B]) & TACK) != 0)
+			;
+
+	/* if reading grab the byte, and reset the interrupt */
+	if (pmu_state == reading || pmu_state == reading_intr)
+		bite = in_8(&via[SR]);
+
+	/* reset TREQ and wait for TACK to go high */
+	out_8(&via[B], in_8(&via[B]) | TREQ);
+	wait_for_ack();
+
+	switch (pmu_state) {
+	case sending:
+		req = current_req;
+		if (data_len < 0) {
+			data_len = req->nbytes - 1;
+			send_byte(data_len);
+			break;
+		}
+		if (data_index <= data_len) {
+			send_byte(req->data[data_index++]);
+			break;
+		}
+		req->sent = 1;
+		data_len = pmu_data_len[req->data[0]][1];
+		if (data_len == 0) {
+			pmu_state = idle;
+			current_req = req->next;
+			if (req->reply_expected)
+				req_awaiting_reply = req;
+			else
+				return req;
+		} else {
+			pmu_state = reading;
+			data_index = 0;
+			reply_ptr = req->reply + req->reply_len;
+			recv_byte();
+		}
+		break;
+
+	case intack:
+		data_index = 0;
+		data_len = -1;
+		pmu_state = reading_intr;
+		reply_ptr = interrupt_data[int_data_last];
+		recv_byte();
+		if (gpio_irq >= 0 && !gpio_irq_enabled) {
+			enable_irq(gpio_irq);
+			gpio_irq_enabled = 1;
+		}
+		break;
+
+	case reading:
+	case reading_intr:
+		if (data_len == -1) {
+			data_len = bite;
+			if (bite > 32)
+				printk(KERN_ERR "PMU: bad reply len %d\n", bite);
+		} else if (data_index < 32) {
+			reply_ptr[data_index++] = bite;
+		}
+		if (data_index < data_len) {
+			recv_byte();
+			break;
+		}
+
+		if (pmu_state == reading_intr) {
+			pmu_state = idle;
+			int_data_state[int_data_last] = int_data_ready;
+			interrupt_data_len[int_data_last] = data_len;
+		} else {
+			req = current_req;
+			/* 
+			 * For PMU sleep and freq change requests, we lock the
+			 * PMU until it's explicitely unlocked. This avoids any
+			 * spurrious event polling getting in
+			 */
+			current_req = req->next;
+			req->reply_len += data_index;
+			if (req->data[0] == PMU_SLEEP || req->data[0] == PMU_CPU_SPEED)
+				pmu_state = locked;
+			else
+				pmu_state = idle;
+			return req;
+		}
+		break;
+
+	default:
+		printk(KERN_ERR "via_pmu_interrupt: unknown state %d?\n",
+		       pmu_state);
+	}
+	return NULL;
+}
+
+static irqreturn_t __pmac
+via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+	unsigned long flags;
+	int intr;
+	int nloop = 0;
+	int int_data = -1;
+	struct adb_request *req = NULL;
+	int handled = 0;
+
+	/* This is a bit brutal, we can probably do better */
+	spin_lock_irqsave(&pmu_lock, flags);
+	++disable_poll;
+	
+	for (;;) {
+		intr = in_8(&via[IFR]) & (SR_INT | CB1_INT);
+		if (intr == 0)
+			break;
+		handled = 1;
+		if (++nloop > 1000) {
+			printk(KERN_DEBUG "PMU: stuck in intr loop, "
+			       "intr=%x, ier=%x pmu_state=%d\n",
+			       intr, in_8(&via[IER]), pmu_state);
+			break;
+		}
+		out_8(&via[IFR], intr);
+		if (intr & CB1_INT) {
+			adb_int_pending = 1;
+			pmu_irq_stats[0]++;
+		}
+		if (intr & SR_INT) {
+			req = pmu_sr_intr(regs);
+			if (req)
+				break;
+		}
+	}
+
+recheck:
+	if (pmu_state == idle) {
+		if (adb_int_pending) {
+			if (int_data_state[0] == int_data_empty)
+				int_data_last = 0;
+			else if (int_data_state[1] == int_data_empty)
+				int_data_last = 1;
+			else
+				goto no_free_slot;
+			pmu_state = intack;
+			int_data_state[int_data_last] = int_data_fill;
+			/* 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();
+	}
+no_free_slot:			
+	/* Mark the oldest buffer for flushing */
+	if (int_data_state[!int_data_last] == int_data_ready) {
+		int_data_state[!int_data_last] = int_data_flush;
+		int_data = !int_data_last;
+	} else if (int_data_state[int_data_last] == int_data_ready) {
+		int_data_state[int_data_last] = int_data_flush;
+		int_data = int_data_last;
+	}
+	--disable_poll;
+	spin_unlock_irqrestore(&pmu_lock, flags);
+
+	/* Deal with completed PMU requests outside of the lock */
+	if (req) {
+		pmu_done(req);
+		req = NULL;
+	}
+		
+	/* Deal with interrupt datas outside of the lock */
+	if (int_data >= 0) {
+		pmu_handle_data(interrupt_data[int_data], interrupt_data_len[int_data], regs);
+		spin_lock_irqsave(&pmu_lock, flags);
+		++disable_poll;
+		int_data_state[int_data] = int_data_empty;
+		int_data = -1;
+		goto recheck;
+	}
+
+	return IRQ_RETVAL(handled);
+}
+
+void __pmac
+pmu_unlock(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&pmu_lock, flags);
+	if (pmu_state == locked)
+		pmu_state = idle;
+	adb_int_pending = 1;
+	spin_unlock_irqrestore(&pmu_lock, flags);
+}
+
+
+static irqreturn_t __pmac
+gpio1_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+	unsigned long flags;
+
+	if ((in_8(gpio_reg + 0x9) & 0x02) == 0) {
+		spin_lock_irqsave(&pmu_lock, flags);
+		if (gpio_irq_enabled > 0) {
+			disable_irq_nosync(gpio_irq);
+			gpio_irq_enabled = 0;
+		}
+		pmu_irq_stats[1]++;
+		adb_int_pending = 1;
+		spin_unlock_irqrestore(&pmu_lock, flags);
+		via_pmu_interrupt(0, NULL, NULL);
+		return IRQ_HANDLED;
+	}
+	return IRQ_NONE;
+}
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+static int backlight_to_bright[] __pmacdata = {
+	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 (on) {
+		pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT,
+			    backlight_to_bright[level]);
+		pmu_wait_complete(&req);
+	}
+	pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
+		    PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF));
+       	pmu_wait_complete(&req);
+
+	return 0;
+}
+
+static void __openfirmware
+pmu_bright_complete(struct adb_request *req)
+{
+	if (req == &bright_req_1)
+		clear_bit(1, &async_req_locks);
+	if (req == &bright_req_2)
+		clear_bit(2, &async_req_locks);
+}
+
+static int __openfirmware
+pmu_set_backlight_level(int level, void* data)
+{
+	if (vias == NULL)
+		return -ENODEV;
+
+	if (test_and_set_bit(1, &async_req_locks))
+		return -EAGAIN;
+	pmu_request(&bright_req_1, pmu_bright_complete, 2, PMU_BACKLIGHT_BRIGHT,
+		backlight_to_bright[level]);
+	if (test_and_set_bit(2, &async_req_locks))
+		return -EAGAIN;
+	pmu_request(&bright_req_2, pmu_bright_complete, 2, PMU_POWER_CTRL,
+		    PMU_POW_BACKLIGHT | (level > BACKLIGHT_OFF ?
+					 PMU_POW_ON : PMU_POW_OFF));
+
+	return 0;
+}
+#endif /* CONFIG_PMAC_BACKLIGHT */
+
+void __pmac
+pmu_enable_irled(int on)
+{
+	struct adb_request req;
+
+	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));
+	pmu_wait_complete(&req);
+}
+
+void __pmac
+pmu_restart(void)
+{
+	struct adb_request req;
+
+	if (via == NULL)
+		return;
+
+	local_irq_disable();
+
+	drop_interrupts = 1;
+	
+	if (pmu_kind != PMU_KEYLARGO_BASED) {
+		pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB |
+						PMU_INT_TICK );
+		while(!req.complete)
+			pmu_poll();
+	}
+
+	pmu_request(&req, NULL, 1, PMU_RESET);
+	pmu_wait_complete(&req);
+	for (;;)
+		;
+}
+
+void __pmac
+pmu_shutdown(void)
+{
+	struct adb_request req;
+
+	if (via == NULL)
+		return;
+
+	local_irq_disable();
+
+	drop_interrupts = 1;
+
+	if (pmu_kind != PMU_KEYLARGO_BASED) {
+		pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB |
+						PMU_INT_TICK );
+		pmu_wait_complete(&req);
+	} else {
+		/* Disable server mode on shutdown or we'll just
+		 * wake up again
+		 */
+		pmu_set_server_mode(0);
+	}
+
+	pmu_request(&req, NULL, 5, PMU_SHUTDOWN,
+		    'M', 'A', 'T', 'T');
+	pmu_wait_complete(&req);
+	for (;;)
+		;
+}
+
+int
+pmu_present(void)
+{
+	return via != 0;
+}
+
+struct pmu_i2c_hdr {
+	u8	bus;
+	u8	mode;
+	u8	bus2;
+	u8	address;
+	u8	sub_addr;
+	u8	comb_addr;
+	u8	count;
+};
+
+int
+pmu_i2c_combined_read(int bus, int addr, int subaddr,  u8* data, int len)
+{
+	struct adb_request	req;
+	struct pmu_i2c_hdr	*hdr = (struct pmu_i2c_hdr *)&req.data[1];
+	int retry;
+	int rc;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		hdr->bus = bus;
+		hdr->address = addr & 0xfe;
+		hdr->mode = PMU_I2C_MODE_COMBINED;
+		hdr->bus2 = 0;
+		hdr->sub_addr = subaddr;
+		hdr->comb_addr = addr | 1;
+		hdr->count = len;
+		
+		req.nbytes = sizeof(struct pmu_i2c_hdr) + 1;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.data[0] = PMU_I2C_CMD;
+		req.reply[0] = 0xff;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_OK)
+			break;
+		mdelay(15);
+	}
+	if (req.reply[0] != PMU_I2C_STATUS_OK)
+		return -1;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		mdelay(15);
+
+		hdr->bus = PMU_I2C_BUS_STATUS;
+		req.reply[0] = 0xff;
+		
+		req.nbytes = 2;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.data[0] = PMU_I2C_CMD;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_DATAREAD) {
+			memcpy(data, &req.reply[1], req.reply_len - 1);
+			return req.reply_len - 1;
+		}
+	}
+	return -1;
+}
+
+int
+pmu_i2c_stdsub_write(int bus, int addr, int subaddr,  u8* data, int len)
+{
+	struct adb_request	req;
+	struct pmu_i2c_hdr	*hdr = (struct pmu_i2c_hdr *)&req.data[1];
+	int retry;
+	int rc;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		hdr->bus = bus;
+		hdr->address = addr & 0xfe;
+		hdr->mode = PMU_I2C_MODE_STDSUB;
+		hdr->bus2 = 0;
+		hdr->sub_addr = subaddr;
+		hdr->comb_addr = addr & 0xfe;
+		hdr->count = len;
+
+		req.data[0] = PMU_I2C_CMD;
+		memcpy(&req.data[sizeof(struct pmu_i2c_hdr) + 1], data, len);
+		req.nbytes = sizeof(struct pmu_i2c_hdr) + len + 1;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.reply[0] = 0xff;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_OK)
+			break;
+		mdelay(15);
+	}
+	if (req.reply[0] != PMU_I2C_STATUS_OK)
+		return -1;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		mdelay(15);
+
+		hdr->bus = PMU_I2C_BUS_STATUS;
+		req.reply[0] = 0xff;
+		
+		req.nbytes = 2;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.data[0] = PMU_I2C_CMD;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_OK)
+			return len;
+	}
+	return -1;
+}
+
+int
+pmu_i2c_simple_read(int bus, int addr,  u8* data, int len)
+{
+	struct adb_request	req;
+	struct pmu_i2c_hdr	*hdr = (struct pmu_i2c_hdr *)&req.data[1];
+	int retry;
+	int rc;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		hdr->bus = bus;
+		hdr->address = addr | 1;
+		hdr->mode = PMU_I2C_MODE_SIMPLE;
+		hdr->bus2 = 0;
+		hdr->sub_addr = 0;
+		hdr->comb_addr = 0;
+		hdr->count = len;
+
+		req.data[0] = PMU_I2C_CMD;
+		req.nbytes = sizeof(struct pmu_i2c_hdr) + 1;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.reply[0] = 0xff;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_OK)
+			break;
+		mdelay(15);
+	}
+	if (req.reply[0] != PMU_I2C_STATUS_OK)
+		return -1;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		mdelay(15);
+
+		hdr->bus = PMU_I2C_BUS_STATUS;
+		req.reply[0] = 0xff;
+		
+		req.nbytes = 2;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.data[0] = PMU_I2C_CMD;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_DATAREAD) {
+			memcpy(data, &req.reply[1], req.reply_len - 1);
+			return req.reply_len - 1;
+		}
+	}
+	return -1;
+}
+
+int
+pmu_i2c_simple_write(int bus, int addr,  u8* data, int len)
+{
+	struct adb_request	req;
+	struct pmu_i2c_hdr	*hdr = (struct pmu_i2c_hdr *)&req.data[1];
+	int retry;
+	int rc;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		hdr->bus = bus;
+		hdr->address = addr & 0xfe;
+		hdr->mode = PMU_I2C_MODE_SIMPLE;
+		hdr->bus2 = 0;
+		hdr->sub_addr = 0;
+		hdr->comb_addr = 0;
+		hdr->count = len;
+
+		req.data[0] = PMU_I2C_CMD;
+		memcpy(&req.data[sizeof(struct pmu_i2c_hdr) + 1], data, len);
+		req.nbytes = sizeof(struct pmu_i2c_hdr) + len + 1;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.reply[0] = 0xff;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_OK)
+			break;
+		mdelay(15);
+	}
+	if (req.reply[0] != PMU_I2C_STATUS_OK)
+		return -1;
+
+	for (retry=0; retry<16; retry++) {
+		memset(&req, 0, sizeof(req));
+
+		mdelay(15);
+
+		hdr->bus = PMU_I2C_BUS_STATUS;
+		req.reply[0] = 0xff;
+		
+		req.nbytes = 2;
+		req.reply_expected = 0;
+		req.reply_len = 0;
+		req.data[0] = PMU_I2C_CMD;
+		rc = pmu_queue_request(&req);
+		if (rc)
+			return rc;
+		while(!req.complete)
+			pmu_poll();
+		if (req.reply[0] == PMU_I2C_STATUS_OK)
+			return len;
+	}
+	return -1;
+}
+
+#ifdef CONFIG_PMAC_PBOOK
+
+static LIST_HEAD(sleep_notifiers);
+
+int
+pmu_register_sleep_notifier(struct pmu_sleep_notifier *n)
+{
+	struct list_head *list;
+	struct pmu_sleep_notifier *notifier;
+
+	for (list = sleep_notifiers.next; list != &sleep_notifiers;
+	     list = list->next) {
+		notifier = list_entry(list, struct pmu_sleep_notifier, list);
+		if (n->priority > notifier->priority)
+			break;
+	}
+	__list_add(&n->list, list->prev, list);
+	return 0;
+}
+
+int
+pmu_unregister_sleep_notifier(struct pmu_sleep_notifier* n)
+{
+	if (n->list.next == 0)
+		return -ENOENT;
+	list_del(&n->list);
+	n->list.next = NULL;
+	return 0;
+}
+
+/* Sleep is broadcast last-to-first */
+static int __pmac
+broadcast_sleep(int when, int fallback)
+{
+	int ret = PBOOK_SLEEP_OK;
+	struct list_head *list;
+	struct pmu_sleep_notifier *notifier;
+
+	for (list = sleep_notifiers.prev; list != &sleep_notifiers;
+	     list = list->prev) {
+		notifier = list_entry(list, struct pmu_sleep_notifier, list);
+		ret = notifier->notifier_call(notifier, when);
+		if (ret != PBOOK_SLEEP_OK) {
+			printk(KERN_DEBUG "sleep %d rejected by %p (%p)\n",
+			       when, notifier, notifier->notifier_call);
+			for (; list != &sleep_notifiers; list = list->next) {
+				notifier = list_entry(list, struct pmu_sleep_notifier, list);
+				notifier->notifier_call(notifier, fallback);
+			}
+			return ret;
+		}
+	}
+	return ret;
+}
+
+/* Wake is broadcast first-to-last */
+static int __pmac
+broadcast_wake(void)
+{
+	int ret = PBOOK_SLEEP_OK;
+	struct list_head *list;
+	struct pmu_sleep_notifier *notifier;
+
+	for (list = sleep_notifiers.next; list != &sleep_notifiers;
+	     list = list->next) {
+		notifier = list_entry(list, struct pmu_sleep_notifier, list);
+		notifier->notifier_call(notifier, PBOOK_WAKE);
+	}
+	return ret;
+}
+
+/*
+ * This struct is used to store config register values for
+ * PCI devices which may get powered off when we sleep.
+ */
+static struct pci_save {
+#ifndef HACKED_PCI_SAVE
+	u16	command;
+	u16	cache_lat;
+	u16	intr;
+	u32	rom_address;
+#else
+	u32	config[16];
+#endif	
+} *pbook_pci_saves;
+static int pbook_npci_saves;
+
+static void __pmac
+pbook_alloc_pci_save(void)
+{
+	int npci;
+	struct pci_dev *pd = NULL;
+
+	npci = 0;
+	while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
+		++npci;
+	}
+	if (npci == 0)
+		return;
+	pbook_pci_saves = (struct pci_save *)
+		kmalloc(npci * sizeof(struct pci_save), GFP_KERNEL);
+	pbook_npci_saves = npci;
+}
+
+static void __pmac
+pbook_free_pci_save(void)
+{
+	if (pbook_pci_saves == NULL)
+		return;
+	kfree(pbook_pci_saves);
+	pbook_pci_saves = NULL;
+	pbook_npci_saves = 0;
+}
+
+static void __pmac
+pbook_pci_save(void)
+{
+	struct pci_save *ps = pbook_pci_saves;
+	struct pci_dev *pd = NULL;
+	int npci = pbook_npci_saves;
+	
+	if (ps == NULL)
+		return;
+
+	while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
+		if (npci-- == 0)
+			return;
+#ifndef HACKED_PCI_SAVE
+		pci_read_config_word(pd, PCI_COMMAND, &ps->command);
+		pci_read_config_word(pd, PCI_CACHE_LINE_SIZE, &ps->cache_lat);
+		pci_read_config_word(pd, PCI_INTERRUPT_LINE, &ps->intr);
+		pci_read_config_dword(pd, PCI_ROM_ADDRESS, &ps->rom_address);
+#else
+		int i;
+		for (i=1;i<16;i++)
+			pci_read_config_dword(pd, i<<4, &ps->config[i]);
+#endif
+		++ps;
+	}
+}
+
+/* For this to work, we must take care of a few things: If gmac was enabled
+ * during boot, it will be in the pci dev list. If it's disabled at this point
+ * (and it will probably be), then you can't access it's config space.
+ */
+static void __pmac
+pbook_pci_restore(void)
+{
+	u16 cmd;
+	struct pci_save *ps = pbook_pci_saves - 1;
+	struct pci_dev *pd = NULL;
+	int npci = pbook_npci_saves;
+	int j;
+
+	while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
+#ifdef HACKED_PCI_SAVE
+		int i;
+		if (npci-- == 0)
+			return;
+		ps++;
+		for (i=2;i<16;i++)
+			pci_write_config_dword(pd, i<<4, ps->config[i]);
+		pci_write_config_dword(pd, 4, ps->config[1]);
+#else
+		if (npci-- == 0)
+			return;
+		ps++;
+		if (ps->command == 0)
+			continue;
+		pci_read_config_word(pd, PCI_COMMAND, &cmd);
+		if ((ps->command & ~cmd) == 0)
+			continue;
+		switch (pd->hdr_type) {
+		case PCI_HEADER_TYPE_NORMAL:
+			for (j = 0; j < 6; ++j)
+				pci_write_config_dword(pd,
+					PCI_BASE_ADDRESS_0 + j*4,
+					pd->resource[j].start);
+			pci_write_config_dword(pd, PCI_ROM_ADDRESS,
+				ps->rom_address);
+			pci_write_config_word(pd, PCI_CACHE_LINE_SIZE,
+				ps->cache_lat);
+			pci_write_config_word(pd, PCI_INTERRUPT_LINE,
+				ps->intr);
+			pci_write_config_word(pd, PCI_COMMAND, ps->command);
+			break;
+		}
+#endif	
+	}
+}
+
+#ifdef DEBUG_SLEEP
+/* N.B. This doesn't work on the 3400 */
+void  __pmac
+pmu_blink(int n)
+{
+	struct adb_request req;
+
+	memset(&req, 0, sizeof(req));
+
+	for (; n > 0; --n) {
+		req.nbytes = 4;
+		req.done = NULL;
+		req.data[0] = 0xee;
+		req.data[1] = 4;
+		req.data[2] = 0;
+		req.data[3] = 1;
+		req.reply[0] = ADB_RET_OK;
+		req.reply_len = 1;
+		req.reply_expected = 0;
+		pmu_polled_request(&req);
+		mdelay(50);
+		req.nbytes = 4;
+		req.done = NULL;
+		req.data[0] = 0xee;
+		req.data[1] = 4;
+		req.data[2] = 0;
+		req.data[3] = 0;
+		req.reply[0] = ADB_RET_OK;
+		req.reply_len = 1;
+		req.reply_expected = 0;
+		pmu_polled_request(&req);
+		mdelay(50);
+	}
+	mdelay(50);
+}
+#endif
+
+/*
+ * Put the powerbook to sleep.
+ */
+ 
+static u32 save_via[8] __pmacdata;
+
+static void __pmac
+save_via_state(void)
+{
+	save_via[0] = in_8(&via[ANH]);
+	save_via[1] = in_8(&via[DIRA]);
+	save_via[2] = in_8(&via[B]);
+	save_via[3] = in_8(&via[DIRB]);
+	save_via[4] = in_8(&via[PCR]);
+	save_via[5] = in_8(&via[ACR]);
+	save_via[6] = in_8(&via[T1CL]);
+	save_via[7] = in_8(&via[T1CH]);
+}
+static void __pmac
+restore_via_state(void)
+{
+	out_8(&via[ANH], save_via[0]);
+	out_8(&via[DIRA], save_via[1]);
+	out_8(&via[B], save_via[2]);
+	out_8(&via[DIRB], save_via[3]);
+	out_8(&via[PCR], save_via[4]);
+	out_8(&via[ACR], save_via[5]);
+	out_8(&via[T1CL], save_via[6]);
+	out_8(&via[T1CH], save_via[7]);
+	out_8(&via[IER], IER_CLR | 0x7f);	/* disable all intrs */
+	out_8(&via[IFR], 0x7f);				/* clear IFR */
+	out_8(&via[IER], IER_SET | SR_INT | CB1_INT);
+}
+
+static int __pmac
+pmac_suspend_devices(void)
+{
+	int ret;
+
+	pm_prepare_console();
+	
+	/* Notify old-style device drivers & userland */
+	ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, PBOOK_SLEEP_REJECT);
+	if (ret != PBOOK_SLEEP_OK) {
+		printk(KERN_ERR "Sleep rejected by drivers\n");
+		return -EBUSY;
+	}
+
+	/* Sync the disks. */
+	/* XXX It would be nice to have some way to ensure that
+	 * nobody is dirtying any new buffers while we wait. That
+	 * could be achieved using the refrigerator for processes
+	 * that swsusp uses
+	 */
+	sys_sync();
+
+	/* Sleep can fail now. May not be very robust but useful for debugging */
+	ret = broadcast_sleep(PBOOK_SLEEP_NOW, PBOOK_WAKE);
+	if (ret != PBOOK_SLEEP_OK) {
+		printk(KERN_ERR "Driver sleep failed\n");
+		return -EBUSY;
+	}
+
+	/* Send suspend call to devices, hold the device core's dpm_sem */
+	ret = device_suspend(PMSG_SUSPEND);
+	if (ret) {
+		broadcast_wake();
+		printk(KERN_ERR "Driver sleep failed\n");
+		return -EBUSY;
+	}
+
+	preempt_disable();
+
+	/* Make sure the decrementer won't interrupt us */
+	asm volatile("mtdec %0" : : "r" (0x7fffffff));
+	/* Make sure any pending DEC interrupt occurring while we did
+	 * the above didn't re-enable the DEC */
+	mb();
+	asm volatile("mtdec %0" : : "r" (0x7fffffff));
+
+	/* We can now disable MSR_EE. This code of course works properly only
+	 * on UP machines... For SMP, if we ever implement sleep, we'll have to
+	 * stop the "other" CPUs way before we do all that stuff.
+	 */
+	local_irq_disable();
+
+	/* Broadcast power down irq
+	 * This isn't that useful in most cases (only directly wired devices can
+	 * use this but still... This will take care of sysdev's as well, so
+	 * we exit from here with local irqs disabled and PIC off.
+	 */
+	ret = device_power_down(PM_SUSPEND_MEM);
+	if (ret) {
+		wakeup_decrementer();
+		local_irq_enable();
+		preempt_enable();
+		device_resume();
+		broadcast_wake();
+		printk(KERN_ERR "Driver powerdown failed\n");
+		return -EBUSY;
+	}
+
+	/* Wait for completion of async backlight requests */
+	while (!bright_req_1.complete || !bright_req_2.complete ||
+			!batt_req.complete)
+		pmu_poll();
+
+	/* Giveup the lazy FPU & vec so we don't have to back them
+	 * up from the low level code
+	 */
+	enable_kernel_fp();
+
+#ifdef CONFIG_ALTIVEC
+	if (cpu_has_feature(CPU_FTR_ALTIVEC))
+		enable_kernel_altivec();
+#endif /* CONFIG_ALTIVEC */
+
+	return 0;
+}
+
+static int __pmac
+pmac_wakeup_devices(void)
+{
+	mdelay(100);
+
+	/* Power back up system devices (including the PIC) */
+	device_power_up();
+
+	/* Force a poll of ADB interrupts */
+	adb_int_pending = 1;
+	via_pmu_interrupt(0, NULL, NULL);
+
+	/* Restart jiffies & scheduling */
+	wakeup_decrementer();
+
+	/* Re-enable local CPU interrupts */
+	local_irq_enable();
+
+	mdelay(100);
+
+	preempt_enable();
+
+	/* Resume devices */
+	device_resume();
+
+	/* Notify old style drivers */
+	broadcast_wake();
+
+	pm_restore_console();
+
+	return 0;
+}
+
+#define	GRACKLE_PM	(1<<7)
+#define GRACKLE_DOZE	(1<<5)
+#define	GRACKLE_NAP	(1<<4)
+#define	GRACKLE_SLEEP	(1<<3)
+
+int __pmac
+powerbook_sleep_grackle(void)
+{
+	unsigned long save_l2cr;
+	unsigned short pmcr1;
+	struct adb_request req;
+	int ret;
+	struct pci_dev *grackle;
+
+	grackle = pci_find_slot(0, 0);
+	if (!grackle)
+		return -ENODEV;
+
+	ret = pmac_suspend_devices();
+	if (ret) {
+		printk(KERN_ERR "Sleep rejected by devices\n");
+		return ret;
+	}
+	
+	/* Turn off various things. Darwin does some retry tests here... */
+	pmu_request(&req, NULL, 2, PMU_POWER_CTRL0, PMU_POW0_OFF|PMU_POW0_HARD_DRIVE);
+	pmu_wait_complete(&req);
+	pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
+		PMU_POW_OFF|PMU_POW_BACKLIGHT|PMU_POW_IRLED|PMU_POW_MEDIABAY);
+	pmu_wait_complete(&req);
+
+	/* For 750, save backside cache setting and disable it */
+	save_l2cr = _get_L2CR();	/* (returns -1 if not available) */
+
+	if (!__fake_sleep) {
+		/* Ask the PMU to put us to sleep */
+		pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
+		pmu_wait_complete(&req);
+	}
+
+	/* The VIA is supposed not to be restored correctly*/
+	save_via_state();
+	/* We shut down some HW */
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1);
+
+	pci_read_config_word(grackle, 0x70, &pmcr1);
+	/* Apparently, MacOS uses NAP mode for Grackle ??? */
+	pmcr1 &= ~(GRACKLE_DOZE|GRACKLE_SLEEP); 
+	pmcr1 |= GRACKLE_PM|GRACKLE_NAP;
+	pci_write_config_word(grackle, 0x70, pmcr1);
+
+	/* Call low-level ASM sleep handler */
+	if (__fake_sleep)
+		mdelay(5000);
+	else
+		low_sleep_handler();
+
+	/* We're awake again, stop grackle PM */
+	pci_read_config_word(grackle, 0x70, &pmcr1);
+	pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP); 
+	pci_write_config_word(grackle, 0x70, pmcr1);
+
+	/* Make sure the PMU is idle */
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
+	restore_via_state();
+	
+	/* Restore L2 cache */
+	if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0)
+ 		_set_L2CR(save_l2cr);
+	
+	/* Restore userland MMU context */
+	set_context(current->active_mm->context, current->active_mm->pgd);
+
+	/* Power things up */
+	pmu_unlock();
+	pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask);
+	pmu_wait_complete(&req);
+	pmu_request(&req, NULL, 2, PMU_POWER_CTRL0,
+			PMU_POW0_ON|PMU_POW0_HARD_DRIVE);
+	pmu_wait_complete(&req);
+	pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
+			PMU_POW_ON|PMU_POW_BACKLIGHT|PMU_POW_CHARGER|PMU_POW_IRLED|PMU_POW_MEDIABAY);
+	pmu_wait_complete(&req);
+
+	pmac_wakeup_devices();
+
+	return 0;
+}
+
+static int __pmac
+powerbook_sleep_Core99(void)
+{
+	unsigned long save_l2cr;
+	unsigned long save_l3cr;
+	struct adb_request req;
+	int ret;
+	
+	if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) < 0) {
+		printk(KERN_ERR "Sleep mode not supported on this machine\n");
+		return -ENOSYS;
+	}
+
+	if (num_online_cpus() > 1 || cpu_is_offline(0))
+		return -EAGAIN;
+
+	ret = pmac_suspend_devices();
+	if (ret) {
+		printk(KERN_ERR "Sleep rejected by devices\n");
+		return ret;
+	}
+
+	printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1));
+
+	/* Tell PMU what events will wake us up */
+	pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_CLR_WAKEUP_EVENTS,
+		0xff, 0xff);
+	pmu_wait_complete(&req);
+	pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_SET_WAKEUP_EVENTS,
+		0, PMU_PWR_WAKEUP_KEY |
+		(option_lid_wakeup ? PMU_PWR_WAKEUP_LID_OPEN : 0));
+	pmu_wait_complete(&req);
+
+	/* Save the state of the L2 and L3 caches */
+	save_l3cr = _get_L3CR();	/* (returns -1 if not available) */
+	save_l2cr = _get_L2CR();	/* (returns -1 if not available) */
+
+	if (!__fake_sleep) {
+		/* Ask the PMU to put us to sleep */
+		pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
+		pmu_wait_complete(&req);
+	}
+
+	/* The VIA is supposed not to be restored correctly*/
+	save_via_state();
+
+	/* Shut down various ASICs. There's a chance that we can no longer
+	 * talk to the PMU after this, so I moved it to _after_ sending the
+	 * sleep command to it. Still need to be checked.
+	 */
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 1);
+
+	/* Call low-level ASM sleep handler */
+	if (__fake_sleep)
+		mdelay(5000);
+	else
+		low_sleep_handler();
+
+	/* Restore Apple core ASICs state */
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, 0);
+
+	/* Restore VIA */
+	restore_via_state();
+
+	/* Restore video */
+	pmac_call_early_video_resume();
+
+	/* Restore L2 cache */
+	if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0)
+ 		_set_L2CR(save_l2cr);
+	/* Restore L3 cache */
+	if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0)
+ 		_set_L3CR(save_l3cr);
+	
+	/* Restore userland MMU context */
+	set_context(current->active_mm->context, current->active_mm->pgd);
+
+	/* Tell PMU we are ready */
+	pmu_unlock();
+	pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
+	pmu_wait_complete(&req);
+	pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask);
+	pmu_wait_complete(&req);
+
+	printk(KERN_DEBUG "HID1, after: %x\n", mfspr(SPRN_HID1));
+
+	pmac_wakeup_devices();
+
+	return 0;
+}
+
+#define PB3400_MEM_CTRL		0xf8000000
+#define PB3400_MEM_CTRL_SLEEP	0x70
+
+static int __pmac
+powerbook_sleep_3400(void)
+{
+	int ret, i, x;
+	unsigned int hid0;
+	unsigned long p;
+	struct adb_request sleep_req;
+	void __iomem *mem_ctrl;
+	unsigned int __iomem *mem_ctrl_sleep;
+
+	/* first map in the memory controller registers */
+	mem_ctrl = ioremap(PB3400_MEM_CTRL, 0x100);
+	if (mem_ctrl == NULL) {
+		printk("powerbook_sleep_3400: ioremap failed\n");
+		return -ENOMEM;
+	}
+	mem_ctrl_sleep = mem_ctrl + PB3400_MEM_CTRL_SLEEP;
+
+	/* Allocate room for PCI save */
+	pbook_alloc_pci_save();
+
+	ret = pmac_suspend_devices();
+	if (ret) {
+		pbook_free_pci_save();
+		printk(KERN_ERR "Sleep rejected by devices\n");
+		return ret;
+	}
+
+	/* Save the state of PCI config space for some slots */
+	pbook_pci_save();
+
+	/* Set the memory controller to keep the memory refreshed
+	   while we're asleep */
+	for (i = 0x403f; i >= 0x4000; --i) {
+		out_be32(mem_ctrl_sleep, i);
+		do {
+			x = (in_be32(mem_ctrl_sleep) >> 16) & 0x3ff;
+		} while (x == 0);
+		if (x >= 0x100)
+			break;
+	}
+
+	/* Ask the PMU to put us to sleep */
+	pmu_request(&sleep_req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
+	while (!sleep_req.complete)
+		mb();
+
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1);
+
+	/* displacement-flush the L2 cache - necessary? */
+	for (p = KERNELBASE; p < KERNELBASE + 0x100000; p += 0x1000)
+		i = *(volatile int *)p;
+	asleep = 1;
+
+	/* Put the CPU into sleep mode */
+	asm volatile("mfspr %0,1008" : "=r" (hid0) :);
+	hid0 = (hid0 & ~(HID0_NAP | HID0_DOZE)) | HID0_SLEEP;
+	asm volatile("mtspr 1008,%0" : : "r" (hid0));
+	_nmask_and_or_msr(0, MSR_POW | MSR_EE);
+	udelay(10);
+
+	/* OK, we're awake again, start restoring things */
+	out_be32(mem_ctrl_sleep, 0x3f);
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
+	pbook_pci_restore();
+	pmu_unlock();
+
+	/* wait for the PMU interrupt sequence to complete */
+	while (asleep)
+		mb();
+
+	pmac_wakeup_devices();
+	pbook_free_pci_save();
+	iounmap(mem_ctrl);
+
+	return 0;
+}
+
+/*
+ * Support for /dev/pmu device
+ */
+#define RB_SIZE		0x10
+struct pmu_private {
+	struct list_head list;
+	int	rb_get;
+	int	rb_put;
+	struct rb_entry {
+		unsigned short len;
+		unsigned char data[16];
+	}	rb_buf[RB_SIZE];
+	wait_queue_head_t wait;
+	spinlock_t lock;
+#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT)
+	int	backlight_locker;
+#endif /* defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) */	
+};
+
+static LIST_HEAD(all_pmu_pvt);
+static DEFINE_SPINLOCK(all_pvt_lock __pmacdata);
+
+static void __pmac
+pmu_pass_intr(unsigned char *data, int len)
+{
+	struct pmu_private *pp;
+	struct list_head *list;
+	int i;
+	unsigned long flags;
+
+	if (len > sizeof(pp->rb_buf[0].data))
+		len = sizeof(pp->rb_buf[0].data);
+	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;
+		if (i != pp->rb_get) {
+			struct rb_entry *rp = &pp->rb_buf[pp->rb_put];
+			rp->len = len;
+			memcpy(rp->data, data, len);
+			pp->rb_put = i;
+			wake_up_interruptible(&pp->wait);
+		}
+		spin_unlock(&pp->lock);
+	}
+	spin_unlock_irqrestore(&all_pvt_lock, flags);
+}
+
+static int __pmac
+pmu_open(struct inode *inode, struct file *file)
+{
+	struct pmu_private *pp;
+	unsigned long flags;
+
+	pp = kmalloc(sizeof(struct pmu_private), GFP_KERNEL);
+	if (pp == 0)
+		return -ENOMEM;
+	pp->rb_get = pp->rb_put = 0;
+	spin_lock_init(&pp->lock);
+	init_waitqueue_head(&pp->wait);
+	spin_lock_irqsave(&all_pvt_lock, flags);
+#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT)
+	pp->backlight_locker = 0;
+#endif /* defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) */	
+	list_add(&pp->list, &all_pmu_pvt);
+	spin_unlock_irqrestore(&all_pvt_lock, flags);
+	file->private_data = pp;
+	return 0;
+}
+
+static ssize_t  __pmac
+pmu_read(struct file *file, char __user *buf,
+			size_t count, loff_t *ppos)
+{
+	struct pmu_private *pp = file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	int ret = 0;
+
+	if (count < 1 || pp == 0)
+		return -EINVAL;
+	if (!access_ok(VERIFY_WRITE, buf, count))
+		return -EFAULT;
+
+	spin_lock_irqsave(&pp->lock, flags);
+	add_wait_queue(&pp->wait, &wait);
+	current->state = TASK_INTERRUPTIBLE;
+
+	for (;;) {
+		ret = -EAGAIN;
+		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;
+		}
+		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;
+}
+
+static ssize_t __pmac
+pmu_write(struct file *file, const char __user *buf,
+			 size_t count, loff_t *ppos)
+{
+	return 0;
+}
+
+static unsigned int __pmac
+pmu_fpoll(struct file *filp, poll_table *wait)
+{
+	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_irqsave(&pp->lock, flags);
+	if (pp->rb_get != pp->rb_put)
+		mask |= POLLIN;
+	spin_unlock_irqrestore(&pp->lock, flags);
+	return mask;
+}
+
+static int __pmac
+pmu_release(struct inode *inode, struct file *file)
+{
+	struct pmu_private *pp = file->private_data;
+	unsigned long flags;
+
+	lock_kernel();
+	if (pp != 0) {
+		file->private_data = NULL;
+		spin_lock_irqsave(&all_pvt_lock, flags);
+		list_del(&pp->list);
+		spin_unlock_irqrestore(&all_pvt_lock, flags);
+#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT)
+		if (pp->backlight_locker) {
+			spin_lock_irqsave(&pmu_lock, flags);
+			disable_kernel_backlight--;
+			spin_unlock_irqrestore(&pmu_lock, flags);
+		}
+#endif /* defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT) */
+		kfree(pp);
+	}
+	unlock_kernel();
+	return 0;
+}
+
+/* Note: removed __openfirmware here since it causes link errors */
+static int __pmac
+pmu_ioctl(struct inode * inode, struct file *filp,
+		     u_int cmd, u_long arg)
+{
+	struct pmu_private *pp = filp->private_data;
+	__u32 __user *argp = (__u32 __user *)arg;
+	int error;
+
+	switch (cmd) {
+	case PMU_IOC_SLEEP:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+		if (sleep_in_progress)
+			return -EBUSY;
+		sleep_in_progress = 1;
+		switch (pmu_kind) {
+		case PMU_OHARE_BASED:
+			error = powerbook_sleep_3400();
+			break;
+		case PMU_HEATHROW_BASED:
+		case PMU_PADDINGTON_BASED:
+			error = powerbook_sleep_grackle();
+			break;
+		case PMU_KEYLARGO_BASED:
+			error = powerbook_sleep_Core99();
+			break;
+		default:
+			error = -ENOSYS;
+		}
+		sleep_in_progress = 0;
+		return error;
+	case PMU_IOC_CAN_SLEEP:
+		if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) < 0)
+			return put_user(0, argp);
+		else
+			return put_user(1, argp);
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+	/* Backlight should have its own device or go via
+	 * the fbdev
+	 */
+	case PMU_IOC_GET_BACKLIGHT:
+		if (sleep_in_progress)
+			return -EBUSY;
+		error = get_backlight_level();
+		if (error < 0)
+			return error;
+		return put_user(error, argp);
+	case PMU_IOC_SET_BACKLIGHT:
+	{
+		__u32 value;
+		if (sleep_in_progress)
+			return -EBUSY;
+		error = get_user(value, argp);
+		if (!error)
+			error = set_backlight_level(value);
+		return error;
+	}
+#ifdef CONFIG_INPUT_ADBHID
+	case PMU_IOC_GRAB_BACKLIGHT: {
+		unsigned long flags;
+		if (pp->backlight_locker)
+			return 0;
+		pp->backlight_locker = 1;
+		spin_lock_irqsave(&pmu_lock, flags);
+		disable_kernel_backlight++;
+		spin_unlock_irqrestore(&pmu_lock, flags);
+		return 0;
+	}
+#endif /* CONFIG_INPUT_ADBHID */
+#endif /* CONFIG_PMAC_BACKLIGHT */
+	case PMU_IOC_GET_MODEL:
+	    	return put_user(pmu_kind, argp);
+	case PMU_IOC_HAS_ADB:
+		return put_user(pmu_has_adb, argp);
+	}
+	return -EINVAL;
+}
+
+static struct file_operations pmu_device_fops __pmacdata = {
+	.read		= pmu_read,
+	.write		= pmu_write,
+	.poll		= pmu_fpoll,
+	.ioctl		= pmu_ioctl,
+	.open		= pmu_open,
+	.release	= pmu_release,
+};
+
+static struct miscdevice pmu_device __pmacdata = {
+	PMU_MINOR, "pmu", &pmu_device_fops
+};
+
+void pmu_device_init(void)
+{
+	if (!via)
+		return;
+	if (misc_register(&pmu_device) < 0)
+		printk(KERN_ERR "via-pmu: cannot register misc device.\n");
+}
+#endif /* CONFIG_PMAC_PBOOK */
+
+#ifdef DEBUG_SLEEP
+static inline void  __pmac
+polled_handshake(volatile unsigned char __iomem *via)
+{
+	via[B] &= ~TREQ; eieio();
+	while ((via[B] & TACK) != 0)
+		;
+	via[B] |= TREQ; eieio();
+	while ((via[B] & TACK) == 0)
+		;
+}
+
+static inline void  __pmac
+polled_send_byte(volatile unsigned char __iomem *via, int x)
+{
+	via[ACR] |= SR_OUT | SR_EXT; eieio();
+	via[SR] = x; eieio();
+	polled_handshake(via);
+}
+
+static inline int __pmac
+polled_recv_byte(volatile unsigned char __iomem *via)
+{
+	int x;
+
+	via[ACR] = (via[ACR] & ~SR_OUT) | SR_EXT; eieio();
+	x = via[SR]; eieio();
+	polled_handshake(via);
+	x = via[SR]; eieio();
+	return x;
+}
+
+int __pmac
+pmu_polled_request(struct adb_request *req)
+{
+	unsigned long flags;
+	int i, l, c;
+	volatile unsigned char __iomem *v = via;
+
+	req->complete = 1;
+	c = req->data[0];
+	l = pmu_data_len[c][0];
+	if (l >= 0 && req->nbytes != l + 1)
+		return -EINVAL;
+
+	local_irq_save(flags);
+	while (pmu_state != idle)
+		pmu_poll();
+
+	while ((via[B] & TACK) == 0)
+		;
+	polled_send_byte(v, c);
+	if (l < 0) {
+		l = req->nbytes - 1;
+		polled_send_byte(v, l);
+	}
+	for (i = 1; i <= l; ++i)
+		polled_send_byte(v, req->data[i]);
+
+	l = pmu_data_len[c][1];
+	if (l < 0)
+		l = polled_recv_byte(v);
+	for (i = 0; i < l; ++i)
+		req->reply[i + req->reply_len] = polled_recv_byte(v);
+
+	if (req->done)
+		(*req->done)(req);
+
+	local_irq_restore(flags);
+	return 0;
+}
+#endif /* DEBUG_SLEEP */
+
+
+/* FIXME: This is a temporary set of callbacks to enable us
+ * to do suspend-to-disk.
+ */
+
+#ifdef CONFIG_PM
+
+static int pmu_sys_suspended = 0;
+
+static int pmu_sys_suspend(struct sys_device *sysdev, u32 state)
+{
+	if (state != PM_SUSPEND_DISK || pmu_sys_suspended)
+		return 0;
+
+	/* Suspend PMU event interrupts */
+	pmu_suspend();
+
+	pmu_sys_suspended = 1;
+	return 0;
+}
+
+static int pmu_sys_resume(struct sys_device *sysdev)
+{
+	struct adb_request req;
+
+	if (!pmu_sys_suspended)
+		return 0;
+
+	/* Tell PMU we are ready */
+	pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
+	pmu_wait_complete(&req);
+
+	/* Resume PMU event interrupts */
+	pmu_resume();
+
+	pmu_sys_suspended = 0;
+
+	return 0;
+}
+
+#endif /* CONFIG_PM */
+
+static struct sysdev_class pmu_sysclass = {
+	set_kset_name("pmu"),
+};
+
+static struct sys_device device_pmu = {
+	.id		= 0,
+	.cls		= &pmu_sysclass,
+};
+
+static struct sysdev_driver driver_pmu = {
+#ifdef CONFIG_PM
+	.suspend	= &pmu_sys_suspend,
+	.resume		= &pmu_sys_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init init_pmu_sysfs(void)
+{
+	int rc;
+
+	rc = sysdev_class_register(&pmu_sysclass);
+	if (rc) {
+		printk(KERN_ERR "Failed registering PMU sys class\n");
+		return -ENODEV;
+	}
+	rc = sysdev_register(&device_pmu);
+	if (rc) {
+		printk(KERN_ERR "Failed registering PMU sys device\n");
+		return -ENODEV;
+	}
+	rc = sysdev_driver_register(&pmu_sysclass, &driver_pmu);
+	if (rc) {
+		printk(KERN_ERR "Failed registering PMU sys driver\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+subsys_initcall(init_pmu_sysfs);
+
+EXPORT_SYMBOL(pmu_request);
+EXPORT_SYMBOL(pmu_poll);
+EXPORT_SYMBOL(pmu_poll_adb);
+EXPORT_SYMBOL(pmu_wait_complete);
+EXPORT_SYMBOL(pmu_suspend);
+EXPORT_SYMBOL(pmu_resume);
+EXPORT_SYMBOL(pmu_unlock);
+EXPORT_SYMBOL(pmu_i2c_combined_read);
+EXPORT_SYMBOL(pmu_i2c_stdsub_write);
+EXPORT_SYMBOL(pmu_i2c_simple_read);
+EXPORT_SYMBOL(pmu_i2c_simple_write);
+#ifdef CONFIG_PMAC_PBOOK
+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 */
+
diff --git a/drivers/macintosh/via-pmu68k.c b/drivers/macintosh/via-pmu68k.c
new file mode 100644
index 0000000..820dc52
--- /dev/null
+++ b/drivers/macintosh/via-pmu68k.c
@@ -0,0 +1,1063 @@
+/*
+ * Device driver for the PMU on 68K-based Apple PowerBooks
+ *
+ * The VIA (versatile interface adapter) interfaces to the PMU,
+ * a 6805 microprocessor core whose primary function is to control
+ * battery charging and system power on the PowerBooks.
+ * The PMU also controls the ADB (Apple Desktop Bus) which connects
+ * to the keyboard and mouse, as well as the non-volatile RAM
+ * and the RTC (real time clock) chip.
+ *
+ * Adapted for 68K PMU by Joshua M. Thompson
+ *
+ * Based largely on the PowerMac PMU code by Paul Mackerras and
+ * Fabio Riccardi.
+ *
+ * Also based on the PMU driver from MkLinux by Apple Computer, Inc.
+ * and the Open Software Foundation, Inc.
+ */
+
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/blkdev.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#include <linux/cuda.h>
+
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/machw.h>
+#include <asm/mac_via.h>
+
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/* Misc minor number allocated for /dev/pmu */
+#define PMU_MINOR	154
+
+/* VIA registers - spaced 0x200 bytes apart */
+#define RS		0x200		/* skip between registers */
+#define B		0		/* B-side data */
+#define A		RS		/* A-side data */
+#define DIRB		(2*RS)		/* B-side direction (1=output) */
+#define DIRA		(3*RS)		/* A-side direction (1=output) */
+#define T1CL		(4*RS)		/* Timer 1 ctr/latch (low 8 bits) */
+#define T1CH		(5*RS)		/* Timer 1 counter (high 8 bits) */
+#define T1LL		(6*RS)		/* Timer 1 latch (low 8 bits) */
+#define T1LH		(7*RS)		/* Timer 1 latch (high 8 bits) */
+#define T2CL		(8*RS)		/* Timer 2 ctr/latch (low 8 bits) */
+#define T2CH		(9*RS)		/* Timer 2 counter (high 8 bits) */
+#define SR		(10*RS)		/* Shift register */
+#define ACR		(11*RS)		/* Auxiliary control register */
+#define PCR		(12*RS)		/* Peripheral control register */
+#define IFR		(13*RS)		/* Interrupt flag register */
+#define IER		(14*RS)		/* Interrupt enable register */
+#define ANH		(15*RS)		/* A-side data, no handshake */
+
+/* Bits in B data register: both active low */
+#define TACK		0x02		/* Transfer acknowledge (input) */
+#define TREQ		0x04		/* Transfer request (output) */
+
+/* Bits in ACR */
+#define SR_CTRL		0x1c		/* Shift register control bits */
+#define SR_EXT		0x0c		/* Shift on external clock */
+#define SR_OUT		0x10		/* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define SR_INT		0x04		/* Shift register full/empty */
+#define CB1_INT		0x10		/* transition on CB1 input */
+
+static enum pmu_state {
+	idle,
+	sending,
+	intack,
+	reading,
+	reading_intr,
+} pmu_state;
+
+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 *reply_ptr;
+static int data_index;
+static int data_len;
+static int adb_int_pending;
+static int pmu_adb_flags;
+static int adb_dev_map = 0;
+static struct adb_request bright_req_1, bright_req_2, bright_req_3;
+static int pmu_kind = PMU_UNKNOWN;
+static int pmu_fully_inited = 0;
+
+int asleep;
+struct notifier_block *sleep_notifier_list;
+
+static int pmu_probe(void);
+static int pmu_init(void);
+static void pmu_start(void);
+static irqreturn_t pmu_interrupt(int irq, void *arg, struct pt_regs *regs);
+static int pmu_send_request(struct adb_request *req, int sync);
+static int pmu_autopoll(int devs);
+void pmu_poll(void);
+static int pmu_reset_bus(void);
+static int pmu_queue_request(struct adb_request *req);
+
+static void pmu_start(void);
+static void send_byte(int x);
+static void recv_byte(void);
+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 pmu_enable_backlight(int on);
+static void pmu_set_brightness(int level);
+
+struct adb_driver via_pmu_driver = {
+	"68K PMU",
+	pmu_probe,
+	pmu_init,
+	pmu_send_request,
+	pmu_autopoll,
+	pmu_poll,
+	pmu_reset_bus
+};
+
+/*
+ * This table indicates for each PMU opcode:
+ * - the number of data bytes to be sent with the command, or -1
+ *   if a length byte should be sent,
+ * - the number of response bytes which the PMU will return, or
+ *   -1 if it will send a length byte.
+ */
+static s8 pmu_data_len[256][2] = {
+/*	   0	   1	   2	   3	   4	   5	   6	   7  */
+/*00*/	{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*08*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*10*/	{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*18*/	{ 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0, 0},
+/*20*/	{-1, 0},{ 0, 0},{ 2, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*28*/	{ 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0,-1},
+/*30*/	{ 4, 0},{20, 0},{-1, 0},{ 3, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*38*/	{ 0, 4},{ 0,20},{ 2,-1},{ 2, 1},{ 3,-1},{-1,-1},{-1,-1},{ 4, 0},
+/*40*/	{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*48*/	{ 0, 1},{ 0, 1},{-1,-1},{ 1, 0},{ 1, 0},{-1,-1},{-1,-1},{-1,-1},
+/*50*/	{ 1, 0},{ 0, 0},{ 2, 0},{ 2, 0},{-1, 0},{ 1, 0},{ 3, 0},{ 1, 0},
+/*58*/	{ 0, 1},{ 1, 0},{ 0, 2},{ 0, 2},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},
+/*60*/	{ 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*68*/	{ 0, 3},{ 0, 3},{ 0, 2},{ 0, 8},{ 0,-1},{ 0,-1},{-1,-1},{-1,-1},
+/*70*/	{ 1, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*78*/	{ 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{ 5, 1},{ 4, 1},{ 4, 1},
+/*80*/	{ 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*88*/	{ 0, 5},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*90*/	{ 1, 0},{ 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*98*/	{ 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*a0*/	{ 2, 0},{ 2, 0},{ 2, 0},{ 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},
+/*a8*/	{ 1, 1},{ 1, 0},{ 3, 0},{ 2, 0},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*b0*/	{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*b8*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*c0*/	{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*c8*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*d0*/	{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*d8*/	{ 1, 1},{ 1, 1},{-1,-1},{-1,-1},{ 0, 1},{ 0,-1},{-1,-1},{-1,-1},
+/*e0*/	{-1, 0},{ 4, 0},{ 0, 1},{-1, 0},{-1, 0},{ 4, 0},{-1, 0},{-1, 0},
+/*e8*/	{ 3,-1},{-1,-1},{ 0, 1},{-1,-1},{ 0,-1},{-1,-1},{-1,-1},{ 0, 0},
+/*f0*/	{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*f8*/	{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+};
+
+int pmu_probe(void)
+{
+	if (macintosh_config->adb_type == MAC_ADB_PB1) {
+		pmu_kind = PMU_68K_V1;
+	} else if (macintosh_config->adb_type == MAC_ADB_PB2) {
+		pmu_kind = PMU_68K_V2;
+	} else {
+		return -ENODEV;
+	}
+
+	pmu_state = idle;
+
+	return 0;
+}
+
+static int 
+pmu_init(void)
+{
+	int timeout;
+	volatile struct adb_request req;
+
+	via2[B] |= TREQ;				/* negate TREQ */
+	via2[DIRB] = (via2[DIRB] | TREQ) & ~TACK;	/* TACK in, TREQ out */
+
+	pmu_request((struct adb_request *) &req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB);
+	timeout =  100000;
+	while (!req.complete) {
+		if (--timeout < 0) {
+			printk(KERN_ERR "pmu_init: no response from PMU\n");
+			return -EAGAIN;
+		}
+		udelay(10);
+		pmu_poll();
+	}
+
+	/* ack all pending interrupts */
+	timeout = 100000;
+	interrupt_data[0] = 1;
+	while (interrupt_data[0] || pmu_state != idle) {
+		if (--timeout < 0) {
+			printk(KERN_ERR "pmu_init: timed out acking intrs\n");
+			return -EAGAIN;
+		}
+		if (pmu_state == idle) {
+			adb_int_pending = 1;
+			pmu_interrupt(0, NULL, NULL);
+		}
+		pmu_poll();
+		udelay(10);
+	}
+
+	pmu_request((struct adb_request *) &req, NULL, 2, PMU_SET_INTR_MASK,
+			PMU_INT_ADB_AUTO|PMU_INT_SNDBRT|PMU_INT_ADB);
+	timeout =  100000;
+	while (!req.complete) {
+		if (--timeout < 0) {
+			printk(KERN_ERR "pmu_init: no response from PMU\n");
+			return -EAGAIN;
+		}
+		udelay(10);
+		pmu_poll();
+	}
+
+	bright_req_1.complete = 1;
+	bright_req_2.complete = 1;
+	bright_req_3.complete = 1;
+
+	if (request_irq(IRQ_MAC_ADB_SR, pmu_interrupt, 0, "pmu-shift",
+			pmu_interrupt)) {
+		printk(KERN_ERR "pmu_init: can't get irq %d\n",
+			IRQ_MAC_ADB_SR);
+		return -EAGAIN;
+	}
+	if (request_irq(IRQ_MAC_ADB_CL, pmu_interrupt, 0, "pmu-clock",
+			pmu_interrupt)) {
+		printk(KERN_ERR "pmu_init: can't get irq %d\n",
+			IRQ_MAC_ADB_CL);
+		free_irq(IRQ_MAC_ADB_SR, pmu_interrupt);
+		return -EAGAIN;
+	}
+
+	pmu_fully_inited = 1;
+	
+	/* Enable backlight */
+	pmu_enable_backlight(1);
+
+	printk("adb: PMU 68K driver v0.5 for Unified ADB.\n");
+
+	return 0;
+}
+
+int
+pmu_get_model(void)
+{
+	return pmu_kind;
+}
+
+/* Send an ADB command */
+static int 
+pmu_send_request(struct adb_request *req, int sync)
+{
+    int i, ret;
+
+    if (!pmu_fully_inited)
+    {
+ 	req->complete = 1;
+   	return -ENXIO;
+   }
+
+    ret = -EINVAL;
+	
+    switch (req->data[0]) {
+    case PMU_PACKET:
+		for (i = 0; i < req->nbytes - 1; ++i)
+			req->data[i] = req->data[i+1];
+		--req->nbytes;
+		if (pmu_data_len[req->data[0]][1] != 0) {
+			req->reply[0] = ADB_RET_OK;
+			req->reply_len = 1;
+		} else
+			req->reply_len = 0;
+		ret = pmu_queue_request(req);
+		break;
+    case CUDA_PACKET:
+		switch (req->data[1]) {
+		case CUDA_GET_TIME:
+			if (req->nbytes != 2)
+				break;
+			req->data[0] = PMU_READ_RTC;
+			req->nbytes = 1;
+			req->reply_len = 3;
+			req->reply[0] = CUDA_PACKET;
+			req->reply[1] = 0;
+			req->reply[2] = CUDA_GET_TIME;
+			ret = pmu_queue_request(req);
+			break;
+		case CUDA_SET_TIME:
+			if (req->nbytes != 6)
+				break;
+			req->data[0] = PMU_SET_RTC;
+			req->nbytes = 5;
+			for (i = 1; i <= 4; ++i)
+				req->data[i] = req->data[i+1];
+			req->reply_len = 3;
+			req->reply[0] = CUDA_PACKET;
+			req->reply[1] = 0;
+			req->reply[2] = CUDA_SET_TIME;
+			ret = pmu_queue_request(req);
+			break;
+		case CUDA_GET_PRAM:
+			if (req->nbytes != 4)
+				break;
+			req->data[0] = PMU_READ_NVRAM;
+			req->data[1] = req->data[2];
+			req->data[2] = req->data[3];
+			req->nbytes = 3;
+			req->reply_len = 3;
+			req->reply[0] = CUDA_PACKET;
+			req->reply[1] = 0;
+			req->reply[2] = CUDA_GET_PRAM;
+			ret = pmu_queue_request(req);
+			break;
+		case CUDA_SET_PRAM:
+			if (req->nbytes != 5)
+				break;
+			req->data[0] = PMU_WRITE_NVRAM;
+			req->data[1] = req->data[2];
+			req->data[2] = req->data[3];
+			req->data[3] = req->data[4];
+			req->nbytes = 4;
+			req->reply_len = 3;
+			req->reply[0] = CUDA_PACKET;
+			req->reply[1] = 0;
+			req->reply[2] = CUDA_SET_PRAM;
+			ret = pmu_queue_request(req);
+			break;
+		}
+		break;
+    case ADB_PACKET:
+		for (i = req->nbytes - 1; i > 1; --i)
+			req->data[i+2] = req->data[i];
+		req->data[3] = req->nbytes - 2;
+		req->data[2] = pmu_adb_flags;
+		/*req->data[1] = req->data[1];*/
+		req->data[0] = PMU_ADB_CMD;
+		req->nbytes += 2;
+		req->reply_expected = 1;
+		req->reply_len = 0;
+		ret = pmu_queue_request(req);
+		break;
+    }
+    if (ret)
+    {
+    	req->complete = 1;
+    	return ret;
+    }
+    	
+    if (sync) {
+	while (!req->complete)
+		pmu_poll();
+    }
+
+    return 0;
+}
+
+/* Enable/disable autopolling */
+static int 
+pmu_autopoll(int devs)
+{
+	struct adb_request req;
+
+	if (!pmu_fully_inited) return -ENXIO;
+
+	if (devs) {
+		adb_dev_map = devs;
+		pmu_request(&req, NULL, 5, PMU_ADB_CMD, 0, 0x86,
+			    adb_dev_map >> 8, adb_dev_map);
+		pmu_adb_flags = 2;
+	} else {
+		pmu_request(&req, NULL, 1, PMU_ADB_POLL_OFF);
+		pmu_adb_flags = 0;
+	}
+	while (!req.complete)
+		pmu_poll();
+	return 0;
+}
+
+/* Reset the ADB bus */
+static int 
+pmu_reset_bus(void)
+{
+	struct adb_request req;
+	long timeout;
+	int save_autopoll = adb_dev_map;
+
+	if (!pmu_fully_inited) return -ENXIO;
+
+	/* anyone got a better idea?? */
+	pmu_autopoll(0);
+
+	req.nbytes = 5;
+	req.done = NULL;
+	req.data[0] = PMU_ADB_CMD;
+	req.data[1] = 0;
+	req.data[2] = 3; /* ADB_BUSRESET ??? */
+	req.data[3] = 0;
+	req.data[4] = 0;
+	req.reply_len = 0;
+	req.reply_expected = 1;
+	if (pmu_queue_request(&req) != 0)
+	{
+		printk(KERN_ERR "pmu_adb_reset_bus: pmu_queue_request failed\n");
+		return -EIO;
+	}
+	while (!req.complete)
+		pmu_poll();
+	timeout = 100000;
+	while (!req.complete) {
+		if (--timeout < 0) {
+			printk(KERN_ERR "pmu_adb_reset_bus (reset): no response from PMU\n");
+			return -EIO;
+		}
+		udelay(10);
+		pmu_poll();
+	}
+
+	if (save_autopoll != 0)
+		pmu_autopoll(save_autopoll);
+		
+	return 0;
+}
+
+/* Construct and send a pmu request */
+int 
+pmu_request(struct adb_request *req, void (*done)(struct adb_request *),
+	    int nbytes, ...)
+{
+	va_list list;
+	int i;
+
+	if (nbytes < 0 || nbytes > 32) {
+		printk(KERN_ERR "pmu_request: bad nbytes (%d)\n", nbytes);
+		req->complete = 1;
+		return -EINVAL;
+	}
+	req->nbytes = nbytes;
+	req->done = done;
+	va_start(list, nbytes);
+	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_expected = 0;
+	return pmu_queue_request(req);
+}
+
+static int 
+pmu_queue_request(struct adb_request *req)
+{
+	unsigned long flags;
+	int nsend;
+
+	if (req->nbytes <= 0) {
+		req->complete = 1;
+		return 0;
+	}
+	nsend = pmu_data_len[req->data[0]][0];
+	if (nsend >= 0 && req->nbytes != nsend + 1) {
+		req->complete = 1;
+		return -EINVAL;
+	}
+
+	req->next = 0;
+	req->sent = 0;
+	req->complete = 0;
+	local_irq_save(flags);
+
+	if (current_req != 0) {
+		last_req->next = req;
+		last_req = req;
+	} else {
+		current_req = req;
+		last_req = req;
+		if (pmu_state == idle)
+			pmu_start();
+	}
+
+	local_irq_restore(flags);
+	return 0;
+}
+
+static void 
+send_byte(int x)
+{
+	via1[ACR] |= SR_CTRL;
+	via1[SR] = x;
+	via2[B] &= ~TREQ;		/* assert TREQ */
+}
+
+static void 
+recv_byte(void)
+{
+	char c;
+
+	via1[ACR] = (via1[ACR] | SR_EXT) & ~SR_OUT;
+	c = via1[SR];		/* resets SR */
+	via2[B] &= ~TREQ;
+}
+
+static void 
+pmu_start(void)
+{
+	unsigned long flags;
+	struct adb_request *req;
+
+	/* assert pmu_state == idle */
+	/* get the packet to send */
+	local_irq_save(flags);
+	req = current_req;
+	if (req == 0 || pmu_state != idle
+	    || (req->reply_expected && req_awaiting_reply))
+		goto out;
+
+	pmu_state = sending;
+	data_index = 1;
+	data_len = pmu_data_len[req->data[0]][0];
+
+	/* set the shift register to shift out and send a byte */
+	send_byte(req->data[0]);
+
+out:
+	local_irq_restore(flags);
+}
+
+void 
+pmu_poll(void)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (via1[IFR] & SR_INT) {
+		via1[IFR] = SR_INT;
+		pmu_interrupt(IRQ_MAC_ADB_SR, NULL, NULL);
+	}
+	if (via1[IFR] & CB1_INT) {
+		via1[IFR] = CB1_INT;
+		pmu_interrupt(IRQ_MAC_ADB_CL, NULL, NULL);
+	}
+	local_irq_restore(flags);
+}
+
+static irqreturn_t
+pmu_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct adb_request *req;
+	int timeout, bite = 0;	/* to prevent compiler warning */
+
+#if 0
+	printk("pmu_interrupt: irq %d state %d acr %02X, b %02X data_index %d/%d adb_int_pending %d\n",
+		irq, pmu_state, (uint) via1[ACR], (uint) via2[B], data_index, data_len, adb_int_pending);
+#endif
+
+	if (irq == IRQ_MAC_ADB_CL) {		/* CB1 interrupt */
+		adb_int_pending = 1;
+	} else if (irq == IRQ_MAC_ADB_SR) {	/* SR interrupt  */
+		if (via2[B] & TACK) {
+			printk(KERN_DEBUG "PMU: SR_INT but ack still high! (%x)\n", via2[B]);
+		}
+
+		/* if reading grab the byte */
+		if ((via1[ACR] & SR_OUT) == 0) bite = via1[SR];
+
+		/* reset TREQ and wait for TACK to go high */
+		via2[B] |= TREQ;
+		timeout = 3200;
+		while (!(via2[B] & TACK)) {
+			if (--timeout < 0) {
+				printk(KERN_ERR "PMU not responding (!ack)\n");
+				goto finish;
+			}
+			udelay(10);
+		}
+
+		switch (pmu_state) {
+		case sending:
+			req = current_req;
+			if (data_len < 0) {
+				data_len = req->nbytes - 1;
+				send_byte(data_len);
+				break;
+			}
+			if (data_index <= data_len) {
+				send_byte(req->data[data_index++]);
+				break;
+			}
+			req->sent = 1;
+			data_len = pmu_data_len[req->data[0]][1];
+			if (data_len == 0) {
+				pmu_state = idle;
+				current_req = req->next;
+				if (req->reply_expected)
+					req_awaiting_reply = req;
+				else
+					pmu_done(req);
+			} else {
+				pmu_state = reading;
+				data_index = 0;
+				reply_ptr = req->reply + req->reply_len;
+				recv_byte();
+			}
+			break;
+
+		case intack:
+			data_index = 0;
+			data_len = -1;
+			pmu_state = reading_intr;
+			reply_ptr = interrupt_data;
+			recv_byte();
+			break;
+
+		case reading:
+		case reading_intr:
+			if (data_len == -1) {
+				data_len = bite;
+				if (bite > 32)
+					printk(KERN_ERR "PMU: bad reply len %d\n",
+					       bite);
+			} else {
+				reply_ptr[data_index++] = bite;
+			}
+			if (data_index < data_len) {
+				recv_byte();
+				break;
+			}
+
+			if (pmu_state == reading_intr) {
+				pmu_handle_data(interrupt_data, data_index, regs);
+			} else {
+				req = current_req;
+				current_req = req->next;
+				req->reply_len += data_index;
+				pmu_done(req);
+			}
+			pmu_state = idle;
+
+			break;
+
+		default:
+			printk(KERN_ERR "pmu_interrupt: unknown state %d?\n",
+			       pmu_state);
+		}
+	}
+finish:
+	if (pmu_state == idle) {
+		if (adb_int_pending) {
+			pmu_state = intack;
+			send_byte(PMU_INT_ACK);
+			adb_int_pending = 0;
+		} else if (current_req) {
+			pmu_start();
+		}
+	}
+
+#if 0
+	printk("pmu_interrupt: exit state %d acr %02X, b %02X data_index %d/%d adb_int_pending %d\n",
+		pmu_state, (uint) via1[ACR], (uint) via2[B], data_index, data_len, adb_int_pending);
+#endif
+	return IRQ_HANDLED;
+}
+
+static void 
+pmu_done(struct adb_request *req)
+{
+	req->complete = 1;
+	if (req->done)
+		(*req->done)(req);
+}
+
+/* Interrupt data could be the result data from an ADB cmd */
+static void 
+pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs)
+{
+	static int show_pmu_ints = 1;
+
+	asleep = 0;
+	if (len < 1) {
+		adb_int_pending = 0;
+		return;
+	}
+	if (data[0] & PMU_INT_ADB) {
+		if ((data[0] & PMU_INT_ADB_AUTO) == 0) {
+			struct adb_request *req = req_awaiting_reply;
+			if (req == 0) {
+				printk(KERN_ERR "PMU: extra ADB reply\n");
+				return;
+			}
+			req_awaiting_reply = 0;
+			if (len <= 2)
+				req->reply_len = 0;
+			else {
+				memcpy(req->reply, data + 1, len - 1);
+				req->reply_len = len - 1;
+			}
+			pmu_done(req);
+		} else {
+			adb_input(data+1, len-1, regs, 1);
+		}
+	} else {
+		if (data[0] == 0x08 && len == 3) {
+			/* sound/brightness buttons pressed */
+			pmu_set_brightness(data[1] >> 3);
+			set_volume(data[2]);
+		} else if (show_pmu_ints
+			   && !(data[0] == PMU_INT_TICK && len == 1)) {
+			int i;
+			printk(KERN_DEBUG "pmu intr");
+			for (i = 0; i < len; ++i)
+				printk(" %.2x", data[i]);
+			printk("\n");
+		}
+	}
+}
+
+int backlight_level = -1;
+int backlight_enabled = 0;
+
+#define LEVEL_TO_BRIGHT(lev)	((lev) < 1? 0x7f: 0x4a - ((lev) << 1))
+
+static void 
+pmu_enable_backlight(int on)
+{
+	struct adb_request req;
+
+	if (on) {
+	    /* first call: get current backlight value */
+	    if (backlight_level < 0) {
+		switch(pmu_kind) {
+		    case PMU_68K_V1:
+		    case PMU_68K_V2:
+			pmu_request(&req, NULL, 3, PMU_READ_NVRAM, 0x14, 0xe);
+			while (!req.complete)
+				pmu_poll();
+			printk(KERN_DEBUG "pmu: nvram returned bright: %d\n", (int)req.reply[1]);
+			backlight_level = req.reply[1];
+			break;
+		    default:
+		        backlight_enabled = 0;
+		        return;
+		}
+	    }
+	    pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT,
+	    	LEVEL_TO_BRIGHT(backlight_level));
+	    while (!req.complete)
+		pmu_poll();
+	}
+	pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
+	    PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF));
+	while (!req.complete)
+		pmu_poll();
+	backlight_enabled = on;
+}
+
+static void 
+pmu_set_brightness(int level)
+{
+	int bright;
+
+	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));
+}
+
+void 
+pmu_enable_irled(int on)
+{
+	struct adb_request req;
+
+	pmu_request(&req, NULL, 2, PMU_POWER_CTRL, PMU_POW_IRLED |
+	    (on ? PMU_POW_ON : PMU_POW_OFF));
+	while (!req.complete)
+		pmu_poll();
+}
+
+static void 
+set_volume(int level)
+{
+}
+
+int
+pmu_present(void)
+{
+	return (pmu_kind != PMU_UNKNOWN);
+}
+
+#if 0 /* needs some work for 68K */
+
+/*
+ * This struct is used to store config register values for
+ * PCI devices which may get powered off when we sleep.
+ */
+static struct pci_save {
+	u16	command;
+	u16	cache_lat;
+	u16	intr;
+} *pbook_pci_saves;
+static int n_pbook_pci_saves;
+
+static inline void __openfirmware
+pbook_pci_save(void)
+{
+	int npci;
+	struct pci_dev *pd = NULL;
+	struct pci_save *ps;
+
+	npci = 0;
+	while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL)
+		++npci;
+	n_pbook_pci_saves = npci;
+	if (npci == 0)
+		return;
+	ps = (struct pci_save *) kmalloc(npci * sizeof(*ps), GFP_KERNEL);
+	pbook_pci_saves = ps;
+	if (ps == NULL)
+		return;
+
+	pd = NULL;
+	while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
+		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);
+		++ps;
+		--npci;
+	}
+}
+
+static inline void __openfirmware
+pbook_pci_restore(void)
+{
+	u16 cmd;
+	struct pci_save *ps = pbook_pci_saves;
+	struct pci_dev *pd = NULL;
+	int j;
+
+	while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
+		if (ps->command == 0)
+			continue;
+		pci_read_config_word(pd, PCI_COMMAND, &cmd);
+		if ((ps->command & ~cmd) == 0)
+			continue;
+		switch (pd->hdr_type) {
+		case PCI_HEADER_TYPE_NORMAL:
+			for (j = 0; j < 6; ++j)
+				pci_write_config_dword(pd,
+					PCI_BASE_ADDRESS_0 + j*4,
+					pd->resource[j].start);
+			pci_write_config_dword(pd, PCI_ROM_ADDRESS,
+			       pd->resource[PCI_ROM_RESOURCE].start);
+			pci_write_config_word(pd, PCI_CACHE_LINE_SIZE,
+				ps->cache_lat);
+			pci_write_config_word(pd, PCI_INTERRUPT_LINE,
+				ps->intr);
+			pci_write_config_word(pd, PCI_COMMAND, ps->command);
+			break;
+			/* other header types not restored at present */
+		}
+	}
+}
+
+/*
+ * Put the powerbook to sleep.
+ */
+#define IRQ_ENABLE	((unsigned int *)0xf3000024)
+#define MEM_CTRL	((unsigned int *)0xf8000070)
+
+int __openfirmware powerbook_sleep(void)
+{
+	int ret, i, x;
+	static int save_backlight;
+	static unsigned int save_irqen;
+	unsigned long msr;
+	unsigned int hid0;
+	unsigned long p, wait;
+	struct adb_request sleep_req;
+
+	/* Notify device drivers */
+	ret = notifier_call_chain(&sleep_notifier_list, PBOOK_SLEEP, NULL);
+	if (ret & NOTIFY_STOP_MASK)
+		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. */
+	sys_sync();
+
+	/* Turn off the display backlight */
+	save_backlight = backlight_enabled;
+	if (save_backlight)
+		pmu_enable_backlight(0);
+
+	/* Give the disks a little time to actually finish writing */
+	for (wait = jiffies + (HZ/4); time_before(jiffies, wait); )
+		mb();
+
+	/* Disable all interrupts except pmu */
+	save_irqen = in_le32(IRQ_ENABLE);
+	for (i = 0; i < 32; ++i)
+		if (i != vias->intrs[0].line && (save_irqen & (1 << i)))
+			disable_irq(i);
+	asm volatile("mtdec %0" : : "r" (0x7fffffff));
+
+	/* Save the state of PCI config space for some slots */
+	pbook_pci_save();
+
+	/* Set the memory controller to keep the memory refreshed
+	   while we're asleep */
+	for (i = 0x403f; i >= 0x4000; --i) {
+		out_be32(MEM_CTRL, i);
+		do {
+			x = (in_be32(MEM_CTRL) >> 16) & 0x3ff;
+		} while (x == 0);
+		if (x >= 0x100)
+			break;
+	}
+
+	/* Ask the PMU to put us to sleep */
+	pmu_request(&sleep_req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
+	while (!sleep_req.complete)
+		mb();
+	/* displacement-flush the L2 cache - necessary? */
+	for (p = KERNELBASE; p < KERNELBASE + 0x100000; p += 0x1000)
+		i = *(volatile int *)p;
+	asleep = 1;
+
+	/* Put the CPU into sleep mode */
+	asm volatile("mfspr %0,1008" : "=r" (hid0) :);
+	hid0 = (hid0 & ~(HID0_NAP | HID0_DOZE)) | HID0_SLEEP;
+	asm volatile("mtspr 1008,%0" : : "r" (hid0));
+	local_save_flags(msr);
+	msr |= MSR_POW | MSR_EE;
+	local_irq_restore(msr);
+	udelay(10);
+
+	/* OK, we're awake again, start restoring things */
+	out_be32(MEM_CTRL, 0x3f);
+	pbook_pci_restore();
+
+	/* wait for the PMU interrupt sequence to complete */
+	while (asleep)
+		mb();
+
+	/* reenable interrupts */
+	for (i = 0; i < 32; ++i)
+		if (i != vias->intrs[0].line && (save_irqen & (1 << i)))
+			enable_irq(i);
+
+	/* Notify drivers */
+	notifier_call_chain(&sleep_notifier_list, PBOOK_WAKE, NULL);
+
+	/* reenable ADB autopoll */
+	pmu_adb_autopoll(adb_dev_map);
+
+	/* Turn on the screen backlight, if it was on before */
+	if (save_backlight)
+		pmu_enable_backlight(1);
+
+	/* Wait for the hard disk to spin up */
+
+	return 0;
+}
+
+/*
+ * Support for /dev/pmu device
+ */
+static int __openfirmware pmu_open(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static ssize_t __openfirmware pmu_read(struct file *file, char *buf,
+			size_t count, loff_t *ppos)
+{
+	return 0;
+}
+
+static ssize_t __openfirmware pmu_write(struct file *file, const char *buf,
+			 size_t count, loff_t *ppos)
+{
+	return 0;
+}
+
+/* Note: removed __openfirmware here since it causes link errors */
+static int /*__openfirmware*/ pmu_ioctl(struct inode * inode, struct file *filp,
+		     u_int cmd, u_long arg)
+{
+	int error;
+	__u32 value;
+
+	switch (cmd) {
+	    case PMU_IOC_SLEEP:
+	    	return -ENOSYS;
+	    case PMU_IOC_GET_BACKLIGHT:
+		return put_user(backlight_level, (__u32 *)arg);
+	    case PMU_IOC_SET_BACKLIGHT:
+		error = get_user(value, (__u32 *)arg);
+		if (!error)
+			pmu_set_brightness(value);
+		return error;
+	    case PMU_IOC_GET_MODEL:
+	    	return put_user(pmu_kind, (__u32 *)arg);
+	}
+	return -EINVAL;
+}
+
+static struct file_operations pmu_device_fops = {
+	.read		= pmu_read,
+	.write		= pmu_write,
+	.ioctl		= pmu_ioctl,
+	.open		= pmu_open,
+};
+
+static struct miscdevice pmu_device = {
+	PMU_MINOR, "pmu", &pmu_device_fops
+};
+
+void pmu_device_init(void)
+{
+	if (!via)
+		return;
+	if (misc_register(&pmu_device) < 0)
+		printk(KERN_ERR "via-pmu68k: cannot register misc device.\n");
+}
+#endif /* CONFIG_PMAC_PBOOK */
+