Messages in this thread Patch in this message | | | From | Paul Mackerras <> | Date | Thu, 7 Nov 2002 15:00:58 +1100 | Subject | [PATCH] Update ADB drivers in 2.5 |
| |
This patch updates the ADB driver and the three low-level ADB bus adaptor drivers used on powermacs. The files affected are all in drivers/macintosh; they are adb.c, macio-adb.c, via-cuda.c and via-pmu.c.
The main changes in this patch are:
- Remove the use of global cli/sti and replace them with local cli/sti, spinlocks and semaphores as appropriate. - Use DECLARE_WORK/schedule_work instead of tq_struct/schedule_task. - Improvements to the PMU interrupt handling and sleep/wakeup code.
Linus, please apply.
Thanks, Paul.
diff -urN linux-2.5/drivers/macintosh/adb.c pmac-2.5/drivers/macintosh/adb.c --- linux-2.5/drivers/macintosh/adb.c 2002-10-31 02:09:29.000000000 +1100 +++ pmac-2.5/drivers/macintosh/adb.c 2002-11-07 14:38:03.000000000 +1100 @@ -34,8 +34,10 @@ #include <linux/wait.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/spinlock.h> #include <linux/completion.h> #include <asm/uaccess.h> +#include <asm/semaphore.h> #ifdef CONFIG_PPC #include <asm/prom.h> #include <asm/hydra.h> @@ -75,8 +77,8 @@ struct adb_driver *adb_controller; struct notifier_block *adb_client_list = NULL; -static int adb_got_sleep = 0; -static int adb_inited = 0; +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; @@ -94,7 +96,7 @@ 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); @@ -102,6 +104,18 @@ int handler_id; } 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 rwlock_t adb_handler_lock = RW_LOCK_UNLOCKED; + #if 0 static void printADBreply(struct adb_request *req) { @@ -254,25 +268,18 @@ SIGCHLD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND); } +static DECLARE_WORK(adb_reset_work, __adb_probe_task, NULL); + int adb_reset_bus(void) { - static struct tq_struct tqs = { - routine: __adb_probe_task, - }; - if (__adb_probe_sync) { do_adb_reset_bus(); return 0; } down(&adb_probe_mutex); - - /* Create probe thread as a child of keventd */ - if (current_is_keventd()) - __adb_probe_task(NULL); - else - schedule_task(&tqs); + schedule_work(&adb_reset_work); return 0; } @@ -372,7 +379,6 @@ do_adb_reset_bus(void) { int ret, nret, devs; - unsigned long flags; if (adb_controller == NULL) return -ENXIO; @@ -391,11 +397,11 @@ /* Let the trackpad settle down */ adb_wait_ms(500); } - - save_flags(flags); - cli(); + + down(&adb_handler_sem); + write_lock_irq(&adb_handler_lock); memset(adb_handler, 0, sizeof(adb_handler)); - restore_flags(flags); + write_unlock_irq(&adb_handler_lock); /* That one is still a bit synchronous, oh well... */ if (adb_controller->reset_bus) @@ -413,6 +419,7 @@ if (adb_controller->autopoll) adb_controller->autopoll(devs); } + up(&adb_handler_sem); nret = notifier_call_chain(&adb_client_list, ADB_MSG_POST_RESET, NULL); if (nret & NOTIFY_STOP_MASK) @@ -512,30 +519,41 @@ { 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) || - adb_try_handler_change(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) { - if (!adb_handler[index].handler) - return -ENODEV; - adb_handler[index].handler = 0; + int ret = -ENODEV; + + down(&adb_handler_sem); + write_lock_irq(&adb_handler_lock); + if (adb_handler[index].handler) { + ret = 0; + adb_handler[index].handler = 0; + } + write_unlock_irq(&adb_handler_lock); + up(&adb_handler_sem); return 0; } @@ -544,6 +562,7 @@ { int i, id; static int dump_adb_input = 0; + 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 @@ -558,14 +577,15 @@ printk(" %x", buf[i]); printk(", id = %d\n", id); } - if (adb_handler[id].handler != 0) { - (*adb_handler[id].handler)(buf, nb, regs, autopoll); - } + read_lock(&adb_handler_lock); + handler = adb_handler[id].handler; + if (handler != 0) + (*handler)(buf, nb, regs, autopoll); + read_unlock(&adb_handler_lock); } -/* Try to change handler to new_id. Will return 1 if successful */ -int -adb_try_handler_change(int address, int new_id) +/* 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; @@ -585,11 +605,24 @@ } 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); } diff -urN linux-2.5/drivers/macintosh/macio-adb.c pmac-2.5/drivers/macintosh/macio-adb.c --- linux-2.5/drivers/macintosh/macio-adb.c 2002-02-06 04:40:16.000000000 +1100 +++ pmac-2.5/drivers/macintosh/macio-adb.c 2002-10-13 15:00:41.000000000 +1000 @@ -7,6 +7,7 @@ #include <linux/kernel.h> #include <linux/delay.h> #include <linux/sched.h> +#include <linux/spinlock.h> #include <asm/prom.h> #include <linux/adb.h> #include <asm/io.h> @@ -57,7 +58,7 @@ static volatile struct adb_regs *adb; static struct adb_request *current_req, *last_req; -static unsigned char adb_rbuf[16]; +static spinlock_t macio_lock = SPIN_LOCK_UNLOCKED; static int macio_probe(void); static int macio_init(void); @@ -66,7 +67,6 @@ static int macio_adb_autopoll(int devs); static void macio_adb_poll(void); static int macio_adb_reset_bus(void); -static void completed(void); struct adb_driver macio_adb_driver = { "MACIO", @@ -107,19 +107,19 @@ adb = (volatile struct adb_regs *) ioremap(adbs->addrs->address, sizeof(struct adb_regs)); - 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->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"); @@ -129,16 +129,27 @@ 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) { @@ -146,13 +157,14 @@ 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 mflags; + unsigned long flags; int i; if (req->data[0] != ADB_PACKET) @@ -167,8 +179,7 @@ req->complete = 0; req->reply_len = 0; - save_flags(mflags); - cli(); + spin_lock_irqsave(&macio_lock, flags); if (current_req != 0) { last_req->next = req; last_req = req; @@ -176,7 +187,7 @@ current_req = last_req = req; out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR); } - restore_flags(mflags); + spin_unlock_irqrestore(&macio_lock, flags); if (sync) { while (!req->complete) @@ -190,7 +201,12 @@ { int i, n, err; struct adb_request *req; - + unsigned char ibuf[16]; + int ibuf_len = 0; + int complete = 0; + int autopoll = 0; + + spin_lock(&macio_lock); if (in_8(&adb->intr.r) & TAG) { if ((req = current_req) != 0) { /* put the current request in */ @@ -202,7 +218,10 @@ out_8(&adb->ctrl.r, DTB + CRE); } else { out_8(&adb->ctrl.r, DTB); - completed(); + 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); @@ -218,39 +237,42 @@ for (i = 0; i < req->reply_len; ++i) req->reply[i] = in_8(&adb->data[i].r); } - completed(); + 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) - adb_rbuf[i] = in_8(&adb->data[i].r); - adb_input(adb_rbuf, n, regs, - in_8(&adb->dcount.r) & APD); + 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); } -} - -static void completed(void) -{ - struct adb_request *req = current_req; - - req->complete = 1; - current_req = req->next; - if (current_req) - out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR); - if (req->done) - (*req->done)(req); + 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); } static void macio_adb_poll(void) { unsigned long flags; - save_flags(flags); - cli(); + local_irq_save(flags); if (in_8(&adb->intr.r) != 0) macio_adb_interrupt(0, 0, 0); - restore_flags(flags); + local_irq_restore(flags); } diff -urN linux-2.5/drivers/macintosh/via-cuda.c pmac-2.5/drivers/macintosh/via-cuda.c --- linux-2.5/drivers/macintosh/via-cuda.c 2002-07-24 17:12:04.000000000 +1000 +++ pmac-2.5/drivers/macintosh/via-cuda.c 2002-10-13 15:00:41.000000000 +1000 @@ -17,6 +17,7 @@ #include <linux/sched.h> #include <linux/adb.h> #include <linux/cuda.h> +#include <linux/spinlock.h> #ifdef CONFIG_PPC #include <asm/prom.h> #include <asm/machdep.h> @@ -31,6 +32,7 @@ #include <linux/init.h> static volatile unsigned char *via; +static spinlock_t cuda_lock = SPIN_LOCK_UNLOCKED; #ifdef CONFIG_MAC #define CUDA_IRQ IRQ_MAC_ADB @@ -386,8 +388,8 @@ req->sent = 0; req->complete = 0; req->reply_len = 0; - save_flags(flags); cli(); + spin_lock_irqsave(&cuda_lock, flags); if (current_req != 0) { last_req->next = req; last_req = req; @@ -397,15 +399,14 @@ if (cuda_state == idle) cuda_start(); } + spin_unlock_irqrestore(&cuda_lock, flags); - restore_flags(flags); return 0; } static void cuda_start() { - unsigned long flags; struct adb_request *req; /* assert cuda_state == idle */ @@ -413,41 +414,46 @@ req = current_req; if (req == 0) return; - save_flags(flags); cli(); - if ((via[B] & TREQ) == 0) { - restore_flags(flags); + if ((via[B] & TREQ) == 0) return; /* a byte is coming in from the CUDA */ - } /* set the shift register to shift out and send a byte */ via[ACR] |= SR_OUT; eieio(); via[SR] = req->data[0]; eieio(); via[B] &= ~TIP; cuda_state = sent_first_byte; - restore_flags(flags); } void cuda_poll() { - unsigned long flags; + if (via[IFR] & SR_INT) { + unsigned long flags; - save_flags(flags); - cli(); - if (via[IFR] & SR_INT) + /* 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, 0, 0); - restore_flags(flags); + local_irq_restore(flags); + } } static void cuda_interrupt(int irq, void *arg, struct pt_regs *regs) { int x, status; - struct adb_request *req; - + struct adb_request *req = NULL; + unsigned char ibuf[16]; + int ibuf_len = 0; + int complete = 0; + if ((via[IFR] & SR_INT) == 0) return; + spin_lock(&cuda_lock); status = (~via[B] & (TIP|TREQ)) | (via[ACR] & SR_OUT); eieio(); /* printk("cuda_interrupt: state=%d status=%x\n", cuda_state, status); */ switch (cuda_state) { @@ -502,8 +508,7 @@ cuda_state = awaiting_reply; } else { current_req = req->next; - if (req->done) - (*req->done)(req); + complete = 1; /* not sure about this */ cuda_state = idle; cuda_start(); @@ -544,12 +549,18 @@ memmove(req->reply, req->reply + 2, req->reply_len); } } - req->complete = 1; current_req = req->next; - if (req->done) - (*req->done)(req); + complete = 1; } else { - cuda_input(cuda_rbuf, reply_ptr - cuda_rbuf, regs); + /* 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) { via[B] &= ~TIP; eieio(); @@ -565,6 +576,19 @@ 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); } static void diff -urN linux-2.5/drivers/macintosh/via-pmu.c pmac-2.5/drivers/macintosh/via-pmu.c --- linux-2.5/drivers/macintosh/via-pmu.c 2002-08-29 09:08:29.000000000 +1000 +++ pmac-2.5/drivers/macintosh/via-pmu.c 2002-10-13 15:00:41.000000000 +1000 @@ -9,11 +9,7 @@ * and the RTC (real time clock) chip. * * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi. - * Copyright (C) 2001 Benjamin Herrenschmidt - * - * todo: - Cleanup synchro between VIA interrupt and GPIO-based PMU - * interrupt. - * + * Copyright (C) 2001-2002 Benjamin Herrenschmidt * */ #include <stdarg.h> @@ -111,14 +107,24 @@ reading_intr, } 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[256]; /* Made bigger: I've been told that might happen */ +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, bright_req_3; static struct device_node *vias; static int pmu_kind = PMU_UNKNOWN; @@ -174,12 +180,6 @@ static int pmu_queue_request(struct adb_request *req); static void pmu_start(void); static void via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs); -static void send_byte(int x); -static void recv_byte(void); -static void pmu_sr_intr(struct pt_regs *regs); -static void pmu_done(struct adb_request *req); -static void pmu_handle_data(unsigned char *data, int len, - struct pt_regs *regs); static void 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); @@ -521,8 +521,8 @@ /* ack all pending interrupts */ timeout = 100000; - interrupt_data[0] = 1; - while (interrupt_data[0] || pmu_state != idle) { + 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; @@ -1176,7 +1176,7 @@ return 0; } -static void __openfirmware +static inline void wait_for_ack(void) { /* Sightly increased the delay, I had one occurence of the message @@ -1194,7 +1194,7 @@ /* New PMU seems to be very sensitive to those timings, so we make sure * PCI is flushed immediately */ -static void __openfirmware +static inline void send_byte(int x) { volatile unsigned char *v = via; @@ -1205,8 +1205,8 @@ (void)in_8(&v[B]); } -static void __openfirmware -recv_byte() +static inline void +recv_byte(void) { volatile unsigned char *v = via; @@ -1216,7 +1216,18 @@ (void)in_8(&v[B]); } -static volatile int disable_poll; +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 __openfirmware pmu_start() @@ -1281,9 +1292,9 @@ } do { - spin_unlock(&pmu_lock); + spin_unlock_irqrestore(&pmu_lock, flags); via_pmu_interrupt(0, 0, 0); - spin_lock(&pmu_lock); + 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); @@ -1330,61 +1341,83 @@ #endif /* SUSPEND_USES_PMU */ } +/* Interrupt data could be the result data from an ADB cmd */ static void __openfirmware -via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs) +pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs) { - unsigned long flags; - int intr; - int nloop = 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; - 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 & SR_INT) - pmu_sr_intr(regs); - if (intr & CB1_INT) - adb_int_pending = 1; + asleep = 0; + if (drop_interrupts || len < 1) { + adb_int_pending = 0; + return; } - - if (pmu_state == idle) { - if (adb_int_pending) { - pmu_state = intack; - /* Sounds safer to make sure ACK is high before writing. - * This helped kill a problem with ADB and some iBooks + /* 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 (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 { +#ifdef CONFIG_XMON + 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 /* CONFIG_XMON */ +#ifdef CONFIG_ADB + /* + * XXX On the [23]400 the PMU gives us an up + * event for keycodes 0x74 or 0x75 when the PC + * card eject buttons are released, so we + * ignore those events. */ - wait_for_ack(); - send_byte(PMU_INT_ACK); - adb_int_pending = 0; - } else if (current_req) - pmu_start(); - } - - --disable_poll; - spin_unlock_irqrestore(&pmu_lock, flags); -} - -static void __openfirmware -gpio1_interrupt(int irq, void *arg, struct pt_regs *regs) -{ - if ((in_8(gpio_reg + 0x9) & 0x02) == 0) { - adb_int_pending = 1; - via_pmu_interrupt(0, 0, 0); + 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 */ + } + } else { + /* Sound/brightness button pressed */ + if ((data[0] & PMU_INT_SNDBRT) && len == 3) { +#ifdef CONFIG_PMAC_BACKLIGHT +#ifdef CONFIG_INPUT_ADBHID + if (!disable_kernel_backlight) +#endif /* CONFIG_INPUT_ADBHID */ + set_backlight_level(data[1] >> 4); +#endif /* CONFIG_PMAC_BACKLIGHT */ + } +#ifdef CONFIG_PMAC_PBOOK + /* Environement or tick interrupt, query batteries */ + if (pmu_battery_count && (data[0] & PMU_INT_TICK)) { + if ((--query_batt_timer) == 0) { + query_battery_state(); + query_batt_timer = BATTERY_POLLING_COUNT; + } + } else if (pmu_battery_count && (data[0] & PMU_INT_ENVIRONMENT)) + query_battery_state(); + if (data[0]) + pmu_pass_intr(data, len); +#endif /* CONFIG_PMAC_PBOOK */ } } -static void __openfirmware +static struct adb_request* __openfirmware pmu_sr_intr(struct pt_regs *regs) { struct adb_request *req; @@ -1393,7 +1426,7 @@ if (via[B] & TREQ) { printk(KERN_ERR "PMU: spurious SR intr (%x)\n", via[B]); out_8(&via[IFR], SR_INT); - return; + return NULL; } /* The ack may not yet be low when we get the interrupt */ while ((in_8(&via[B]) & TACK) != 0) @@ -1426,11 +1459,8 @@ current_req = req->next; if (req->reply_expected) req_awaiting_reply = req; - else { - spin_unlock(&pmu_lock); - pmu_done(req); - spin_lock(&pmu_lock); - } + else + return req; } else { pmu_state = reading; data_index = 0; @@ -1443,7 +1473,7 @@ data_index = 0; data_len = -1; pmu_state = reading_intr; - reply_ptr = interrupt_data; + reply_ptr = interrupt_data[int_data_last]; recv_byte(); break; @@ -1462,108 +1492,113 @@ } if (pmu_state == reading_intr) { - spin_unlock(&pmu_lock); - pmu_handle_data(interrupt_data, data_index, regs); - spin_lock(&pmu_lock); + pmu_state = idle; + int_data_state[int_data_last] = int_data_ready; + interrupt_data_len[int_data_last] = data_len; } else { req = current_req; current_req = req->next; req->reply_len += data_index; - spin_unlock(&pmu_lock); - pmu_done(req); - spin_lock(&pmu_lock); + pmu_state = idle; + return req; } - pmu_state = idle; - break; default: printk(KERN_ERR "via_pmu_interrupt: unknown state %d?\n", pmu_state); } + return NULL; } static void __openfirmware -pmu_done(struct adb_request *req) +via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs) { - req->complete = 1; - if (req->done) - (*req->done)(req); + unsigned long flags; + int intr; + int nloop = 0; + int int_data = -1; + struct adb_request *req = NULL; + + /* 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; + 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; + 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; +no_free_slot: + } else if (current_req) + pmu_start(); + } + /* 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; + } } -/* Interrupt data could be the result data from an ADB cmd */ static void __openfirmware -pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs) +gpio1_interrupt(int irq, void *arg, struct pt_regs *regs) { - asleep = 0; - if (drop_interrupts || len < 1) { - adb_int_pending = 0; - return; - } - /* 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 (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 { -#ifdef CONFIG_XMON - 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 /* CONFIG_XMON */ -#ifdef CONFIG_ADB - /* - * XXX On the [23]400 the PMU gives us an up - * event for keycodes 0x74 or 0x75 when the PC - * 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 */ - } - } else { - /* Sound/brightness button pressed */ - if ((data[0] & PMU_INT_SNDBRT) && len == 3) { -#ifdef CONFIG_PMAC_BACKLIGHT -#ifdef CONFIG_INPUT_ADBHID - if (!disable_kernel_backlight) -#endif /* CONFIG_INPUT_ADBHID */ - set_backlight_level(data[1] >> 4); -#endif /* CONFIG_PMAC_BACKLIGHT */ - } -#ifdef CONFIG_PMAC_PBOOK - /* Environement or tick interrupt, query batteries */ - if (pmu_battery_count && (data[0] & PMU_INT_TICK)) { - if ((--query_batt_timer) == 0) { - query_battery_state(); - query_batt_timer = BATTERY_POLLING_COUNT; - } - } else if (pmu_battery_count && (data[0] & PMU_INT_ENVIRONMENT)) - query_battery_state(); - if (data[0]) - pmu_pass_intr(data, len); -#endif /* CONFIG_PMAC_PBOOK */ + if ((in_8(gpio_reg + 0x9) & 0x02) == 0) { + adb_int_pending = 1; + via_pmu_interrupt(0, 0, 0); } } @@ -1635,7 +1670,7 @@ { struct adb_request req; - cli(); + local_irq_disable(); drop_interrupts = 1; @@ -1658,7 +1693,7 @@ { struct adb_request req; - cli(); + local_irq_disable(); drop_interrupts = 1; @@ -1979,7 +2014,7 @@ unsigned long save_l2cr; unsigned short pmcr1; struct adb_request req; - int ret, timeout; + int ret; struct pci_dev *grackle; grackle = pci_find_slot(0, 0); @@ -2036,17 +2071,16 @@ mb(); asm volatile("mtdec %0" : : "r" (0x7fffffff)); - /* Giveup the FPU */ - if (current->thread.regs && (current->thread.regs->msr & MSR_FP) != 0) - giveup_fpu(current); - /* We can now disable MSR_EE */ - cli(); + local_irq_disable(); + + /* Giveup the FPU */ + enable_kernel_fp(); /* For 750, save backside cache setting and disable it */ - save_l2cr = _get_L2CR(); /* (returns 0 if not 750) */ - if (save_l2cr) - _set_L2CR(0); + save_l2cr = _get_L2CR(); /* (returns -1 if not available) */ + if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0) + _set_L2CR(save_l2cr & 0x7fffffff); /* Ask the PMU to put us to sleep */ pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); @@ -2077,7 +2111,7 @@ restore_via_state(); /* Restore L2 cache */ - if (save_l2cr) + if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0) _set_L2CR(save_l2cr); /* Restore userland MMU context */ @@ -2096,18 +2130,6 @@ while (!req.complete) pmu_poll(); - /* ack all pending interrupts */ - timeout = 100000; - interrupt_data[0] = 1; - while (interrupt_data[0] || pmu_state != idle) { - if (--timeout < 0) - break; - if (pmu_state == idle) - adb_int_pending = 1; - via_pmu_interrupt(0, 0, 0); - udelay(10); - } - /* reenable interrupt controller */ pmac_sleep_restore_intrs(); @@ -2116,7 +2138,13 @@ /* Restart jiffies & scheduling */ wakeup_decrementer(); - sti(); + + /* Force a poll of ADB interrupts */ + adb_int_pending = 1; + via_pmu_interrupt(0, 0, 0); + + /* Re-enable local CPU interrupts */ + local_irq_enable(); /* Notify drivers */ broadcast_wake(); @@ -2127,8 +2155,9 @@ int __openfirmware powerbook_sleep_Core99(void) { unsigned long save_l2cr; + unsigned long save_l3cr; struct adb_request req; - int ret, timeout; + int ret; if (!can_sleep) { printk(KERN_ERR "Sleep mode not supported on this machine\n"); @@ -2187,6 +2216,9 @@ mb(); asm volatile("mtdec %0" : : "r" (0x7fffffff)); + /* We can now disable MSR_EE */ + local_irq_disable(); + /* Giveup the FPU & vec */ enable_kernel_fp(); @@ -2195,12 +2227,12 @@ enable_kernel_altivec(); #endif /* CONFIG_ALTIVEC */ - /* We can now disable MSR_EE */ - cli(); - - /* For 750, save backside cache setting and disable it */ - save_l2cr = _get_L2CR(); /* (returns 0 if not 750) */ - if (save_l2cr) + /* Save & disable L2 and L3 caches*/ + save_l3cr = _get_L3CR(); /* (returns -1 if not available) */ + save_l2cr = _get_L2CR(); /* (returns -1 if not available) */ + if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0) + _set_L3CR(save_l3cr & 0x7fffffff); + if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0) _set_L2CR(save_l2cr & 0x7fffffff); /* Save the state of PCI config space for some slots */ @@ -2248,8 +2280,11 @@ pmu_blink(2); /* Restore L2 cache */ - if (save_l2cr) + 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); @@ -2262,18 +2297,6 @@ while (!req.complete) pmu_poll(); - /* ack all pending interrupts */ - timeout = 100000; - interrupt_data[0] = 1; - while (interrupt_data[0] || pmu_state != idle) { - if (--timeout < 0) - break; - if (pmu_state == idle) - adb_int_pending = 1; - via_pmu_interrupt(0, 0, 0); - udelay(10); - } - /* reenable interrupt controller */ openpic_sleep_restore_intrs(); @@ -2282,7 +2305,13 @@ /* Restart jiffies & scheduling */ wakeup_decrementer(); - sti(); + + /* Force a poll of ADB interrupts */ + adb_int_pending = 1; + via_pmu_interrupt(0, 0, 0); + + /* Re-enable local CPU interrupts */ + local_irq_enable(); /* Notify drivers */ broadcast_wake(); @@ -2402,7 +2431,9 @@ /* Restart jiffies & scheduling */ wakeup_decrementer(); - sti(); + + /* Re-enable local CPU interrupts */ + local_irq_enable(); /* Notify drivers */ broadcast_wake(); @@ -2718,7 +2749,7 @@ if (l >= 0 && req->nbytes != l + 1) return -EINVAL; - save_flags(flags); cli(); + local_irq_save(flags); while (pmu_state != idle) pmu_poll(); @@ -2741,7 +2772,7 @@ if (req->done) (*req->done)(req); - restore_flags(flags); + local_irq_restore(flags); return 0; } #endif /* DEBUG_SLEEP */ - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |