diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /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')
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; +} + |