aboutsummaryrefslogtreecommitdiff
path: root/drivers/macintosh
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/macintosh
Linux-2.6.12-rc2v2.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!
Diffstat (limited to 'drivers/macintosh')
-rw-r--r--drivers/macintosh/Kconfig198
-rw-r--r--drivers/macintosh/Makefile29
-rw-r--r--drivers/macintosh/adb-iop.c287
-rw-r--r--drivers/macintosh/adb.c910
-rw-r--r--drivers/macintosh/adbhid.c1177
-rw-r--r--drivers/macintosh/ans-lcd.c186
-rw-r--r--drivers/macintosh/apm_emu.c553
-rw-r--r--drivers/macintosh/mac_hid.c140
-rw-r--r--drivers/macintosh/macio-adb.c284
-rw-r--r--drivers/macintosh/macio_asic.c636
-rw-r--r--drivers/macintosh/macserial.c3036
-rw-r--r--drivers/macintosh/macserial.h461
-rw-r--r--drivers/macintosh/mediabay.c851
-rw-r--r--drivers/macintosh/nvram.c131
-rw-r--r--drivers/macintosh/smu.c364
-rw-r--r--drivers/macintosh/therm_adt746x.c612
-rw-r--r--drivers/macintosh/therm_pm72.c2080
-rw-r--r--drivers/macintosh/therm_pm72.h297
-rw-r--r--drivers/macintosh/therm_windtunnel.c531
-rw-r--r--drivers/macintosh/via-cuda.c628
-rw-r--r--drivers/macintosh/via-macii.c653
-rw-r--r--drivers/macintosh/via-maciisi.c661
-rw-r--r--drivers/macintosh/via-pmu.c3147
-rw-r--r--drivers/macintosh/via-pmu68k.c1063
24 files changed, 18915 insertions, 0 deletions
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig
new file mode 100644
index 00000000000..8a7117a08cf
--- /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 00000000000..c3a4705a829
--- /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 00000000000..71aeb912ec6
--- /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 00000000000..7297c77f99c
--- /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;
+}
+