1*1da177e4SLinus Torvalds /* 2*1da177e4SLinus Torvalds * HP i8042-based System Device Controller driver. 3*1da177e4SLinus Torvalds * 4*1da177e4SLinus Torvalds * Copyright (c) 2001 Brian S. Julin 5*1da177e4SLinus Torvalds * All rights reserved. 6*1da177e4SLinus Torvalds * 7*1da177e4SLinus Torvalds * Redistribution and use in source and binary forms, with or without 8*1da177e4SLinus Torvalds * modification, are permitted provided that the following conditions 9*1da177e4SLinus Torvalds * are met: 10*1da177e4SLinus Torvalds * 1. Redistributions of source code must retain the above copyright 11*1da177e4SLinus Torvalds * notice, this list of conditions, and the following disclaimer, 12*1da177e4SLinus Torvalds * without modification. 13*1da177e4SLinus Torvalds * 2. The name of the author may not be used to endorse or promote products 14*1da177e4SLinus Torvalds * derived from this software without specific prior written permission. 15*1da177e4SLinus Torvalds * 16*1da177e4SLinus Torvalds * Alternatively, this software may be distributed under the terms of the 17*1da177e4SLinus Torvalds * GNU General Public License ("GPL"). 18*1da177e4SLinus Torvalds * 19*1da177e4SLinus Torvalds * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20*1da177e4SLinus Torvalds * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21*1da177e4SLinus Torvalds * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22*1da177e4SLinus Torvalds * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 23*1da177e4SLinus Torvalds * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24*1da177e4SLinus Torvalds * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25*1da177e4SLinus Torvalds * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26*1da177e4SLinus Torvalds * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27*1da177e4SLinus Torvalds * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28*1da177e4SLinus Torvalds * 29*1da177e4SLinus Torvalds * References: 30*1da177e4SLinus Torvalds * System Device Controller Microprocessor Firmware Theory of Operation 31*1da177e4SLinus Torvalds * for Part Number 1820-4784 Revision B. Dwg No. A-1820-4784-2 32*1da177e4SLinus Torvalds * Helge Deller's original hilkbd.c port for PA-RISC. 33*1da177e4SLinus Torvalds * 34*1da177e4SLinus Torvalds * 35*1da177e4SLinus Torvalds * Driver theory of operation: 36*1da177e4SLinus Torvalds * 37*1da177e4SLinus Torvalds * hp_sdc_put does all writing to the SDC. ISR can run on a different 38*1da177e4SLinus Torvalds * CPU than hp_sdc_put, but only one CPU runs hp_sdc_put at a time 39*1da177e4SLinus Torvalds * (it cannot really benefit from SMP anyway.) A tasket fit this perfectly. 40*1da177e4SLinus Torvalds * 41*1da177e4SLinus Torvalds * All data coming back from the SDC is sent via interrupt and can be read 42*1da177e4SLinus Torvalds * fully in the ISR, so there are no latency/throughput problems there. 43*1da177e4SLinus Torvalds * The problem is with output, due to the slow clock speed of the SDC 44*1da177e4SLinus Torvalds * compared to the CPU. This should not be too horrible most of the time, 45*1da177e4SLinus Torvalds * but if used with HIL devices that support the multibyte transfer command, 46*1da177e4SLinus Torvalds * keeping outbound throughput flowing at the 6500KBps that the HIL is 47*1da177e4SLinus Torvalds * capable of is more than can be done at HZ=100. 48*1da177e4SLinus Torvalds * 49*1da177e4SLinus Torvalds * Busy polling for IBF clear wastes CPU cycles and bus cycles. hp_sdc.ibf 50*1da177e4SLinus Torvalds * is set to 0 when the IBF flag in the status register has cleared. ISR 51*1da177e4SLinus Torvalds * may do this, and may also access the parts of queued transactions related 52*1da177e4SLinus Torvalds * to reading data back from the SDC, but otherwise will not touch the 53*1da177e4SLinus Torvalds * hp_sdc state. Whenever a register is written hp_sdc.ibf is set to 1. 54*1da177e4SLinus Torvalds * 55*1da177e4SLinus Torvalds * The i8042 write index and the values in the 4-byte input buffer 56*1da177e4SLinus Torvalds * starting at 0x70 are kept track of in hp_sdc.wi, and .r7[], respectively, 57*1da177e4SLinus Torvalds * to minimize the amount of IO needed to the SDC. However these values 58*1da177e4SLinus Torvalds * do not need to be locked since they are only ever accessed by hp_sdc_put. 59*1da177e4SLinus Torvalds * 60*1da177e4SLinus Torvalds * A timer task schedules the tasklet once per second just to make 61*1da177e4SLinus Torvalds * sure it doesn't freeze up and to allow for bad reads to time out. 62*1da177e4SLinus Torvalds */ 63*1da177e4SLinus Torvalds 64*1da177e4SLinus Torvalds #include <linux/hp_sdc.h> 65*1da177e4SLinus Torvalds #include <linux/sched.h> 66*1da177e4SLinus Torvalds #include <linux/errno.h> 67*1da177e4SLinus Torvalds #include <linux/init.h> 68*1da177e4SLinus Torvalds #include <linux/module.h> 69*1da177e4SLinus Torvalds #include <linux/ioport.h> 70*1da177e4SLinus Torvalds #include <linux/time.h> 71*1da177e4SLinus Torvalds #include <linux/slab.h> 72*1da177e4SLinus Torvalds #include <linux/hil.h> 73*1da177e4SLinus Torvalds #include <asm/io.h> 74*1da177e4SLinus Torvalds #include <asm/system.h> 75*1da177e4SLinus Torvalds 76*1da177e4SLinus Torvalds /* Machine-specific abstraction */ 77*1da177e4SLinus Torvalds 78*1da177e4SLinus Torvalds #if defined(__hppa__) 79*1da177e4SLinus Torvalds # include <asm/parisc-device.h> 80*1da177e4SLinus Torvalds # define sdc_readb(p) gsc_readb(p) 81*1da177e4SLinus Torvalds # define sdc_writeb(v,p) gsc_writeb((v),(p)) 82*1da177e4SLinus Torvalds #elif defined(__mc68000__) 83*1da177e4SLinus Torvalds # include <asm/uaccess.h> 84*1da177e4SLinus Torvalds # define sdc_readb(p) in_8(p) 85*1da177e4SLinus Torvalds # define sdc_writeb(v,p) out_8((p),(v)) 86*1da177e4SLinus Torvalds #else 87*1da177e4SLinus Torvalds # error "HIL is not supported on this platform" 88*1da177e4SLinus Torvalds #endif 89*1da177e4SLinus Torvalds 90*1da177e4SLinus Torvalds #define PREFIX "HP SDC: " 91*1da177e4SLinus Torvalds 92*1da177e4SLinus Torvalds MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>"); 93*1da177e4SLinus Torvalds MODULE_DESCRIPTION("HP i8042-based SDC Driver"); 94*1da177e4SLinus Torvalds MODULE_LICENSE("Dual BSD/GPL"); 95*1da177e4SLinus Torvalds 96*1da177e4SLinus Torvalds EXPORT_SYMBOL(hp_sdc_request_timer_irq); 97*1da177e4SLinus Torvalds EXPORT_SYMBOL(hp_sdc_request_hil_irq); 98*1da177e4SLinus Torvalds EXPORT_SYMBOL(hp_sdc_request_cooked_irq); 99*1da177e4SLinus Torvalds 100*1da177e4SLinus Torvalds EXPORT_SYMBOL(hp_sdc_release_timer_irq); 101*1da177e4SLinus Torvalds EXPORT_SYMBOL(hp_sdc_release_hil_irq); 102*1da177e4SLinus Torvalds EXPORT_SYMBOL(hp_sdc_release_cooked_irq); 103*1da177e4SLinus Torvalds 104*1da177e4SLinus Torvalds EXPORT_SYMBOL(hp_sdc_enqueue_transaction); 105*1da177e4SLinus Torvalds EXPORT_SYMBOL(hp_sdc_dequeue_transaction); 106*1da177e4SLinus Torvalds 107*1da177e4SLinus Torvalds static hp_i8042_sdc hp_sdc; /* All driver state is kept in here. */ 108*1da177e4SLinus Torvalds 109*1da177e4SLinus Torvalds /*************** primitives for use in any context *********************/ 110*1da177e4SLinus Torvalds static inline uint8_t hp_sdc_status_in8 (void) { 111*1da177e4SLinus Torvalds uint8_t status; 112*1da177e4SLinus Torvalds unsigned long flags; 113*1da177e4SLinus Torvalds 114*1da177e4SLinus Torvalds write_lock_irqsave(&hp_sdc.ibf_lock, flags); 115*1da177e4SLinus Torvalds status = sdc_readb(hp_sdc.status_io); 116*1da177e4SLinus Torvalds if (!(status & HP_SDC_STATUS_IBF)) hp_sdc.ibf = 0; 117*1da177e4SLinus Torvalds write_unlock_irqrestore(&hp_sdc.ibf_lock, flags); 118*1da177e4SLinus Torvalds 119*1da177e4SLinus Torvalds return status; 120*1da177e4SLinus Torvalds } 121*1da177e4SLinus Torvalds 122*1da177e4SLinus Torvalds static inline uint8_t hp_sdc_data_in8 (void) { 123*1da177e4SLinus Torvalds return sdc_readb(hp_sdc.data_io); 124*1da177e4SLinus Torvalds } 125*1da177e4SLinus Torvalds 126*1da177e4SLinus Torvalds static inline void hp_sdc_status_out8 (uint8_t val) { 127*1da177e4SLinus Torvalds unsigned long flags; 128*1da177e4SLinus Torvalds 129*1da177e4SLinus Torvalds write_lock_irqsave(&hp_sdc.ibf_lock, flags); 130*1da177e4SLinus Torvalds hp_sdc.ibf = 1; 131*1da177e4SLinus Torvalds if ((val & 0xf0) == 0xe0) hp_sdc.wi = 0xff; 132*1da177e4SLinus Torvalds sdc_writeb(val, hp_sdc.status_io); 133*1da177e4SLinus Torvalds write_unlock_irqrestore(&hp_sdc.ibf_lock, flags); 134*1da177e4SLinus Torvalds } 135*1da177e4SLinus Torvalds 136*1da177e4SLinus Torvalds static inline void hp_sdc_data_out8 (uint8_t val) { 137*1da177e4SLinus Torvalds unsigned long flags; 138*1da177e4SLinus Torvalds 139*1da177e4SLinus Torvalds write_lock_irqsave(&hp_sdc.ibf_lock, flags); 140*1da177e4SLinus Torvalds hp_sdc.ibf = 1; 141*1da177e4SLinus Torvalds sdc_writeb(val, hp_sdc.data_io); 142*1da177e4SLinus Torvalds write_unlock_irqrestore(&hp_sdc.ibf_lock, flags); 143*1da177e4SLinus Torvalds } 144*1da177e4SLinus Torvalds 145*1da177e4SLinus Torvalds /* Care must be taken to only invoke hp_sdc_spin_ibf when 146*1da177e4SLinus Torvalds * absolutely needed, or in rarely invoked subroutines. 147*1da177e4SLinus Torvalds * Not only does it waste CPU cycles, it also wastes bus cycles. 148*1da177e4SLinus Torvalds */ 149*1da177e4SLinus Torvalds static inline void hp_sdc_spin_ibf(void) { 150*1da177e4SLinus Torvalds unsigned long flags; 151*1da177e4SLinus Torvalds rwlock_t *lock; 152*1da177e4SLinus Torvalds 153*1da177e4SLinus Torvalds lock = &hp_sdc.ibf_lock; 154*1da177e4SLinus Torvalds 155*1da177e4SLinus Torvalds read_lock_irqsave(lock, flags); 156*1da177e4SLinus Torvalds if (!hp_sdc.ibf) { 157*1da177e4SLinus Torvalds read_unlock_irqrestore(lock, flags); 158*1da177e4SLinus Torvalds return; 159*1da177e4SLinus Torvalds } 160*1da177e4SLinus Torvalds read_unlock(lock); 161*1da177e4SLinus Torvalds write_lock(lock); 162*1da177e4SLinus Torvalds while (sdc_readb(hp_sdc.status_io) & HP_SDC_STATUS_IBF) {}; 163*1da177e4SLinus Torvalds hp_sdc.ibf = 0; 164*1da177e4SLinus Torvalds write_unlock_irqrestore(lock, flags); 165*1da177e4SLinus Torvalds } 166*1da177e4SLinus Torvalds 167*1da177e4SLinus Torvalds 168*1da177e4SLinus Torvalds /************************ Interrupt context functions ************************/ 169*1da177e4SLinus Torvalds static void hp_sdc_take (int irq, void *dev_id, uint8_t status, uint8_t data) { 170*1da177e4SLinus Torvalds hp_sdc_transaction *curr; 171*1da177e4SLinus Torvalds 172*1da177e4SLinus Torvalds read_lock(&hp_sdc.rtq_lock); 173*1da177e4SLinus Torvalds if (hp_sdc.rcurr < 0) { 174*1da177e4SLinus Torvalds read_unlock(&hp_sdc.rtq_lock); 175*1da177e4SLinus Torvalds return; 176*1da177e4SLinus Torvalds } 177*1da177e4SLinus Torvalds curr = hp_sdc.tq[hp_sdc.rcurr]; 178*1da177e4SLinus Torvalds read_unlock(&hp_sdc.rtq_lock); 179*1da177e4SLinus Torvalds 180*1da177e4SLinus Torvalds curr->seq[curr->idx++] = status; 181*1da177e4SLinus Torvalds curr->seq[curr->idx++] = data; 182*1da177e4SLinus Torvalds hp_sdc.rqty -= 2; 183*1da177e4SLinus Torvalds do_gettimeofday(&hp_sdc.rtv); 184*1da177e4SLinus Torvalds 185*1da177e4SLinus Torvalds if (hp_sdc.rqty <= 0) { 186*1da177e4SLinus Torvalds /* All data has been gathered. */ 187*1da177e4SLinus Torvalds if(curr->seq[curr->actidx] & HP_SDC_ACT_SEMAPHORE) { 188*1da177e4SLinus Torvalds if (curr->act.semaphore) up(curr->act.semaphore); 189*1da177e4SLinus Torvalds } 190*1da177e4SLinus Torvalds if(curr->seq[curr->actidx] & HP_SDC_ACT_CALLBACK) { 191*1da177e4SLinus Torvalds if (curr->act.irqhook) 192*1da177e4SLinus Torvalds curr->act.irqhook(irq, dev_id, status, data); 193*1da177e4SLinus Torvalds } 194*1da177e4SLinus Torvalds curr->actidx = curr->idx; 195*1da177e4SLinus Torvalds curr->idx++; 196*1da177e4SLinus Torvalds /* Return control of this transaction */ 197*1da177e4SLinus Torvalds write_lock(&hp_sdc.rtq_lock); 198*1da177e4SLinus Torvalds hp_sdc.rcurr = -1; 199*1da177e4SLinus Torvalds hp_sdc.rqty = 0; 200*1da177e4SLinus Torvalds write_unlock(&hp_sdc.rtq_lock); 201*1da177e4SLinus Torvalds tasklet_schedule(&hp_sdc.task); 202*1da177e4SLinus Torvalds } 203*1da177e4SLinus Torvalds } 204*1da177e4SLinus Torvalds 205*1da177e4SLinus Torvalds static irqreturn_t hp_sdc_isr(int irq, void *dev_id, struct pt_regs * regs) { 206*1da177e4SLinus Torvalds uint8_t status, data; 207*1da177e4SLinus Torvalds 208*1da177e4SLinus Torvalds status = hp_sdc_status_in8(); 209*1da177e4SLinus Torvalds /* Read data unconditionally to advance i8042. */ 210*1da177e4SLinus Torvalds data = hp_sdc_data_in8(); 211*1da177e4SLinus Torvalds 212*1da177e4SLinus Torvalds /* For now we are ignoring these until we get the SDC to behave. */ 213*1da177e4SLinus Torvalds if (((status & 0xf1) == 0x51) && data == 0x82) { 214*1da177e4SLinus Torvalds return IRQ_HANDLED; 215*1da177e4SLinus Torvalds } 216*1da177e4SLinus Torvalds 217*1da177e4SLinus Torvalds switch(status & HP_SDC_STATUS_IRQMASK) { 218*1da177e4SLinus Torvalds case 0: /* This case is not documented. */ 219*1da177e4SLinus Torvalds break; 220*1da177e4SLinus Torvalds case HP_SDC_STATUS_USERTIMER: 221*1da177e4SLinus Torvalds case HP_SDC_STATUS_PERIODIC: 222*1da177e4SLinus Torvalds case HP_SDC_STATUS_TIMER: 223*1da177e4SLinus Torvalds read_lock(&hp_sdc.hook_lock); 224*1da177e4SLinus Torvalds if (hp_sdc.timer != NULL) 225*1da177e4SLinus Torvalds hp_sdc.timer(irq, dev_id, status, data); 226*1da177e4SLinus Torvalds read_unlock(&hp_sdc.hook_lock); 227*1da177e4SLinus Torvalds break; 228*1da177e4SLinus Torvalds case HP_SDC_STATUS_REG: 229*1da177e4SLinus Torvalds hp_sdc_take(irq, dev_id, status, data); 230*1da177e4SLinus Torvalds break; 231*1da177e4SLinus Torvalds case HP_SDC_STATUS_HILCMD: 232*1da177e4SLinus Torvalds case HP_SDC_STATUS_HILDATA: 233*1da177e4SLinus Torvalds read_lock(&hp_sdc.hook_lock); 234*1da177e4SLinus Torvalds if (hp_sdc.hil != NULL) 235*1da177e4SLinus Torvalds hp_sdc.hil(irq, dev_id, status, data); 236*1da177e4SLinus Torvalds read_unlock(&hp_sdc.hook_lock); 237*1da177e4SLinus Torvalds break; 238*1da177e4SLinus Torvalds case HP_SDC_STATUS_PUP: 239*1da177e4SLinus Torvalds read_lock(&hp_sdc.hook_lock); 240*1da177e4SLinus Torvalds if (hp_sdc.pup != NULL) 241*1da177e4SLinus Torvalds hp_sdc.pup(irq, dev_id, status, data); 242*1da177e4SLinus Torvalds else printk(KERN_INFO PREFIX "HP SDC reports successful PUP.\n"); 243*1da177e4SLinus Torvalds read_unlock(&hp_sdc.hook_lock); 244*1da177e4SLinus Torvalds break; 245*1da177e4SLinus Torvalds default: 246*1da177e4SLinus Torvalds read_lock(&hp_sdc.hook_lock); 247*1da177e4SLinus Torvalds if (hp_sdc.cooked != NULL) 248*1da177e4SLinus Torvalds hp_sdc.cooked(irq, dev_id, status, data); 249*1da177e4SLinus Torvalds read_unlock(&hp_sdc.hook_lock); 250*1da177e4SLinus Torvalds break; 251*1da177e4SLinus Torvalds } 252*1da177e4SLinus Torvalds return IRQ_HANDLED; 253*1da177e4SLinus Torvalds } 254*1da177e4SLinus Torvalds 255*1da177e4SLinus Torvalds 256*1da177e4SLinus Torvalds static irqreturn_t hp_sdc_nmisr(int irq, void *dev_id, struct pt_regs * regs) { 257*1da177e4SLinus Torvalds int status; 258*1da177e4SLinus Torvalds 259*1da177e4SLinus Torvalds status = hp_sdc_status_in8(); 260*1da177e4SLinus Torvalds printk(KERN_WARNING PREFIX "NMI !\n"); 261*1da177e4SLinus Torvalds 262*1da177e4SLinus Torvalds #if 0 263*1da177e4SLinus Torvalds if (status & HP_SDC_NMISTATUS_FHS) { 264*1da177e4SLinus Torvalds read_lock(&hp_sdc.hook_lock); 265*1da177e4SLinus Torvalds if (hp_sdc.timer != NULL) 266*1da177e4SLinus Torvalds hp_sdc.timer(irq, dev_id, status, 0); 267*1da177e4SLinus Torvalds read_unlock(&hp_sdc.hook_lock); 268*1da177e4SLinus Torvalds } 269*1da177e4SLinus Torvalds else { 270*1da177e4SLinus Torvalds /* TODO: pass this on to the HIL handler, or do SAK here? */ 271*1da177e4SLinus Torvalds printk(KERN_WARNING PREFIX "HIL NMI\n"); 272*1da177e4SLinus Torvalds } 273*1da177e4SLinus Torvalds #endif 274*1da177e4SLinus Torvalds return IRQ_HANDLED; 275*1da177e4SLinus Torvalds } 276*1da177e4SLinus Torvalds 277*1da177e4SLinus Torvalds 278*1da177e4SLinus Torvalds /***************** Kernel (tasklet) context functions ****************/ 279*1da177e4SLinus Torvalds 280*1da177e4SLinus Torvalds unsigned long hp_sdc_put(void); 281*1da177e4SLinus Torvalds 282*1da177e4SLinus Torvalds static void hp_sdc_tasklet(unsigned long foo) { 283*1da177e4SLinus Torvalds 284*1da177e4SLinus Torvalds write_lock_irq(&hp_sdc.rtq_lock); 285*1da177e4SLinus Torvalds if (hp_sdc.rcurr >= 0) { 286*1da177e4SLinus Torvalds struct timeval tv; 287*1da177e4SLinus Torvalds do_gettimeofday(&tv); 288*1da177e4SLinus Torvalds if (tv.tv_sec > hp_sdc.rtv.tv_sec) tv.tv_usec += 1000000; 289*1da177e4SLinus Torvalds if (tv.tv_usec - hp_sdc.rtv.tv_usec > HP_SDC_MAX_REG_DELAY) { 290*1da177e4SLinus Torvalds hp_sdc_transaction *curr; 291*1da177e4SLinus Torvalds uint8_t tmp; 292*1da177e4SLinus Torvalds 293*1da177e4SLinus Torvalds curr = hp_sdc.tq[hp_sdc.rcurr]; 294*1da177e4SLinus Torvalds /* If this turns out to be a normal failure mode 295*1da177e4SLinus Torvalds * we'll need to figure out a way to communicate 296*1da177e4SLinus Torvalds * it back to the application. and be less verbose. 297*1da177e4SLinus Torvalds */ 298*1da177e4SLinus Torvalds printk(KERN_WARNING PREFIX "read timeout (%ius)!\n", 299*1da177e4SLinus Torvalds tv.tv_usec - hp_sdc.rtv.tv_usec); 300*1da177e4SLinus Torvalds curr->idx += hp_sdc.rqty; 301*1da177e4SLinus Torvalds hp_sdc.rqty = 0; 302*1da177e4SLinus Torvalds tmp = curr->seq[curr->actidx]; 303*1da177e4SLinus Torvalds curr->seq[curr->actidx] |= HP_SDC_ACT_DEAD; 304*1da177e4SLinus Torvalds if(tmp & HP_SDC_ACT_SEMAPHORE) { 305*1da177e4SLinus Torvalds if (curr->act.semaphore) 306*1da177e4SLinus Torvalds up(curr->act.semaphore); 307*1da177e4SLinus Torvalds } 308*1da177e4SLinus Torvalds if(tmp & HP_SDC_ACT_CALLBACK) { 309*1da177e4SLinus Torvalds /* Note this means that irqhooks may be called 310*1da177e4SLinus Torvalds * in tasklet/bh context. 311*1da177e4SLinus Torvalds */ 312*1da177e4SLinus Torvalds if (curr->act.irqhook) 313*1da177e4SLinus Torvalds curr->act.irqhook(0, 0, 0, 0); 314*1da177e4SLinus Torvalds } 315*1da177e4SLinus Torvalds curr->actidx = curr->idx; 316*1da177e4SLinus Torvalds curr->idx++; 317*1da177e4SLinus Torvalds hp_sdc.rcurr = -1; 318*1da177e4SLinus Torvalds } 319*1da177e4SLinus Torvalds } 320*1da177e4SLinus Torvalds write_unlock_irq(&hp_sdc.rtq_lock); 321*1da177e4SLinus Torvalds hp_sdc_put(); 322*1da177e4SLinus Torvalds } 323*1da177e4SLinus Torvalds 324*1da177e4SLinus Torvalds unsigned long hp_sdc_put(void) { 325*1da177e4SLinus Torvalds hp_sdc_transaction *curr; 326*1da177e4SLinus Torvalds uint8_t act; 327*1da177e4SLinus Torvalds int idx, curridx; 328*1da177e4SLinus Torvalds 329*1da177e4SLinus Torvalds int limit = 0; 330*1da177e4SLinus Torvalds 331*1da177e4SLinus Torvalds write_lock(&hp_sdc.lock); 332*1da177e4SLinus Torvalds 333*1da177e4SLinus Torvalds /* If i8042 buffers are full, we cannot do anything that 334*1da177e4SLinus Torvalds requires output, so we skip to the administrativa. */ 335*1da177e4SLinus Torvalds if (hp_sdc.ibf) { 336*1da177e4SLinus Torvalds hp_sdc_status_in8(); 337*1da177e4SLinus Torvalds if (hp_sdc.ibf) goto finish; 338*1da177e4SLinus Torvalds } 339*1da177e4SLinus Torvalds 340*1da177e4SLinus Torvalds anew: 341*1da177e4SLinus Torvalds /* See if we are in the middle of a sequence. */ 342*1da177e4SLinus Torvalds if (hp_sdc.wcurr < 0) hp_sdc.wcurr = 0; 343*1da177e4SLinus Torvalds read_lock_irq(&hp_sdc.rtq_lock); 344*1da177e4SLinus Torvalds if (hp_sdc.rcurr == hp_sdc.wcurr) hp_sdc.wcurr++; 345*1da177e4SLinus Torvalds read_unlock_irq(&hp_sdc.rtq_lock); 346*1da177e4SLinus Torvalds if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0; 347*1da177e4SLinus Torvalds curridx = hp_sdc.wcurr; 348*1da177e4SLinus Torvalds 349*1da177e4SLinus Torvalds if (hp_sdc.tq[curridx] != NULL) goto start; 350*1da177e4SLinus Torvalds 351*1da177e4SLinus Torvalds while (++curridx != hp_sdc.wcurr) { 352*1da177e4SLinus Torvalds if (curridx >= HP_SDC_QUEUE_LEN) { 353*1da177e4SLinus Torvalds curridx = -1; /* Wrap to top */ 354*1da177e4SLinus Torvalds continue; 355*1da177e4SLinus Torvalds } 356*1da177e4SLinus Torvalds read_lock_irq(&hp_sdc.rtq_lock); 357*1da177e4SLinus Torvalds if (hp_sdc.rcurr == curridx) { 358*1da177e4SLinus Torvalds read_unlock_irq(&hp_sdc.rtq_lock); 359*1da177e4SLinus Torvalds continue; 360*1da177e4SLinus Torvalds } 361*1da177e4SLinus Torvalds read_unlock_irq(&hp_sdc.rtq_lock); 362*1da177e4SLinus Torvalds if (hp_sdc.tq[curridx] != NULL) break; /* Found one. */ 363*1da177e4SLinus Torvalds } 364*1da177e4SLinus Torvalds if (curridx == hp_sdc.wcurr) { /* There's nothing queued to do. */ 365*1da177e4SLinus Torvalds curridx = -1; 366*1da177e4SLinus Torvalds } 367*1da177e4SLinus Torvalds hp_sdc.wcurr = curridx; 368*1da177e4SLinus Torvalds 369*1da177e4SLinus Torvalds start: 370*1da177e4SLinus Torvalds 371*1da177e4SLinus Torvalds /* Check to see if the interrupt mask needs to be set. */ 372*1da177e4SLinus Torvalds if (hp_sdc.set_im) { 373*1da177e4SLinus Torvalds hp_sdc_status_out8(hp_sdc.im | HP_SDC_CMD_SET_IM); 374*1da177e4SLinus Torvalds hp_sdc.set_im = 0; 375*1da177e4SLinus Torvalds goto finish; 376*1da177e4SLinus Torvalds } 377*1da177e4SLinus Torvalds 378*1da177e4SLinus Torvalds if (hp_sdc.wcurr == -1) goto done; 379*1da177e4SLinus Torvalds 380*1da177e4SLinus Torvalds curr = hp_sdc.tq[curridx]; 381*1da177e4SLinus Torvalds idx = curr->actidx; 382*1da177e4SLinus Torvalds 383*1da177e4SLinus Torvalds if (curr->actidx >= curr->endidx) { 384*1da177e4SLinus Torvalds hp_sdc.tq[curridx] = NULL; 385*1da177e4SLinus Torvalds /* Interleave outbound data between the transactions. */ 386*1da177e4SLinus Torvalds hp_sdc.wcurr++; 387*1da177e4SLinus Torvalds if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0; 388*1da177e4SLinus Torvalds goto finish; 389*1da177e4SLinus Torvalds } 390*1da177e4SLinus Torvalds 391*1da177e4SLinus Torvalds act = curr->seq[idx]; 392*1da177e4SLinus Torvalds idx++; 393*1da177e4SLinus Torvalds 394*1da177e4SLinus Torvalds if (curr->idx >= curr->endidx) { 395*1da177e4SLinus Torvalds if (act & HP_SDC_ACT_DEALLOC) kfree(curr); 396*1da177e4SLinus Torvalds hp_sdc.tq[curridx] = NULL; 397*1da177e4SLinus Torvalds /* Interleave outbound data between the transactions. */ 398*1da177e4SLinus Torvalds hp_sdc.wcurr++; 399*1da177e4SLinus Torvalds if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0; 400*1da177e4SLinus Torvalds goto finish; 401*1da177e4SLinus Torvalds } 402*1da177e4SLinus Torvalds 403*1da177e4SLinus Torvalds while (act & HP_SDC_ACT_PRECMD) { 404*1da177e4SLinus Torvalds if (curr->idx != idx) { 405*1da177e4SLinus Torvalds idx++; 406*1da177e4SLinus Torvalds act &= ~HP_SDC_ACT_PRECMD; 407*1da177e4SLinus Torvalds break; 408*1da177e4SLinus Torvalds } 409*1da177e4SLinus Torvalds hp_sdc_status_out8(curr->seq[idx]); 410*1da177e4SLinus Torvalds curr->idx++; 411*1da177e4SLinus Torvalds /* act finished? */ 412*1da177e4SLinus Torvalds if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_PRECMD) 413*1da177e4SLinus Torvalds goto actdone; 414*1da177e4SLinus Torvalds /* skip quantity field if data-out sequence follows. */ 415*1da177e4SLinus Torvalds if (act & HP_SDC_ACT_DATAOUT) curr->idx++; 416*1da177e4SLinus Torvalds goto finish; 417*1da177e4SLinus Torvalds } 418*1da177e4SLinus Torvalds if (act & HP_SDC_ACT_DATAOUT) { 419*1da177e4SLinus Torvalds int qty; 420*1da177e4SLinus Torvalds 421*1da177e4SLinus Torvalds qty = curr->seq[idx]; 422*1da177e4SLinus Torvalds idx++; 423*1da177e4SLinus Torvalds if (curr->idx - idx < qty) { 424*1da177e4SLinus Torvalds hp_sdc_data_out8(curr->seq[curr->idx]); 425*1da177e4SLinus Torvalds curr->idx++; 426*1da177e4SLinus Torvalds /* act finished? */ 427*1da177e4SLinus Torvalds if ((curr->idx - idx >= qty) && 428*1da177e4SLinus Torvalds ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAOUT)) 429*1da177e4SLinus Torvalds goto actdone; 430*1da177e4SLinus Torvalds goto finish; 431*1da177e4SLinus Torvalds } 432*1da177e4SLinus Torvalds idx += qty; 433*1da177e4SLinus Torvalds act &= ~HP_SDC_ACT_DATAOUT; 434*1da177e4SLinus Torvalds } 435*1da177e4SLinus Torvalds else while (act & HP_SDC_ACT_DATAREG) { 436*1da177e4SLinus Torvalds int mask; 437*1da177e4SLinus Torvalds uint8_t w7[4]; 438*1da177e4SLinus Torvalds 439*1da177e4SLinus Torvalds mask = curr->seq[idx]; 440*1da177e4SLinus Torvalds if (idx != curr->idx) { 441*1da177e4SLinus Torvalds idx++; 442*1da177e4SLinus Torvalds idx += !!(mask & 1); 443*1da177e4SLinus Torvalds idx += !!(mask & 2); 444*1da177e4SLinus Torvalds idx += !!(mask & 4); 445*1da177e4SLinus Torvalds idx += !!(mask & 8); 446*1da177e4SLinus Torvalds act &= ~HP_SDC_ACT_DATAREG; 447*1da177e4SLinus Torvalds break; 448*1da177e4SLinus Torvalds } 449*1da177e4SLinus Torvalds 450*1da177e4SLinus Torvalds w7[0] = (mask & 1) ? curr->seq[++idx] : hp_sdc.r7[0]; 451*1da177e4SLinus Torvalds w7[1] = (mask & 2) ? curr->seq[++idx] : hp_sdc.r7[1]; 452*1da177e4SLinus Torvalds w7[2] = (mask & 4) ? curr->seq[++idx] : hp_sdc.r7[2]; 453*1da177e4SLinus Torvalds w7[3] = (mask & 8) ? curr->seq[++idx] : hp_sdc.r7[3]; 454*1da177e4SLinus Torvalds 455*1da177e4SLinus Torvalds if (hp_sdc.wi > 0x73 || hp_sdc.wi < 0x70 || 456*1da177e4SLinus Torvalds w7[hp_sdc.wi-0x70] == hp_sdc.r7[hp_sdc.wi-0x70]) { 457*1da177e4SLinus Torvalds int i = 0; 458*1da177e4SLinus Torvalds 459*1da177e4SLinus Torvalds /* Need to point the write index register */ 460*1da177e4SLinus Torvalds while ((i < 4) && w7[i] == hp_sdc.r7[i]) i++; 461*1da177e4SLinus Torvalds if (i < 4) { 462*1da177e4SLinus Torvalds hp_sdc_status_out8(HP_SDC_CMD_SET_D0 + i); 463*1da177e4SLinus Torvalds hp_sdc.wi = 0x70 + i; 464*1da177e4SLinus Torvalds goto finish; 465*1da177e4SLinus Torvalds } 466*1da177e4SLinus Torvalds idx++; 467*1da177e4SLinus Torvalds if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAREG) 468*1da177e4SLinus Torvalds goto actdone; 469*1da177e4SLinus Torvalds curr->idx = idx; 470*1da177e4SLinus Torvalds act &= ~HP_SDC_ACT_DATAREG; 471*1da177e4SLinus Torvalds break; 472*1da177e4SLinus Torvalds } 473*1da177e4SLinus Torvalds 474*1da177e4SLinus Torvalds hp_sdc_data_out8(w7[hp_sdc.wi - 0x70]); 475*1da177e4SLinus Torvalds hp_sdc.r7[hp_sdc.wi - 0x70] = w7[hp_sdc.wi - 0x70]; 476*1da177e4SLinus Torvalds hp_sdc.wi++; /* write index register autoincrements */ 477*1da177e4SLinus Torvalds { 478*1da177e4SLinus Torvalds int i = 0; 479*1da177e4SLinus Torvalds 480*1da177e4SLinus Torvalds while ((i < 4) && w7[i] == hp_sdc.r7[i]) i++; 481*1da177e4SLinus Torvalds if (i >= 4) { 482*1da177e4SLinus Torvalds curr->idx = idx + 1; 483*1da177e4SLinus Torvalds if ((act & HP_SDC_ACT_DURING) == 484*1da177e4SLinus Torvalds HP_SDC_ACT_DATAREG) 485*1da177e4SLinus Torvalds goto actdone; 486*1da177e4SLinus Torvalds } 487*1da177e4SLinus Torvalds } 488*1da177e4SLinus Torvalds goto finish; 489*1da177e4SLinus Torvalds } 490*1da177e4SLinus Torvalds /* We don't go any further in the command if there is a pending read, 491*1da177e4SLinus Torvalds because we don't want interleaved results. */ 492*1da177e4SLinus Torvalds read_lock_irq(&hp_sdc.rtq_lock); 493*1da177e4SLinus Torvalds if (hp_sdc.rcurr >= 0) { 494*1da177e4SLinus Torvalds read_unlock_irq(&hp_sdc.rtq_lock); 495*1da177e4SLinus Torvalds goto finish; 496*1da177e4SLinus Torvalds } 497*1da177e4SLinus Torvalds read_unlock_irq(&hp_sdc.rtq_lock); 498*1da177e4SLinus Torvalds 499*1da177e4SLinus Torvalds 500*1da177e4SLinus Torvalds if (act & HP_SDC_ACT_POSTCMD) { 501*1da177e4SLinus Torvalds uint8_t postcmd; 502*1da177e4SLinus Torvalds 503*1da177e4SLinus Torvalds /* curr->idx should == idx at this point. */ 504*1da177e4SLinus Torvalds postcmd = curr->seq[idx]; 505*1da177e4SLinus Torvalds curr->idx++; 506*1da177e4SLinus Torvalds if (act & HP_SDC_ACT_DATAIN) { 507*1da177e4SLinus Torvalds 508*1da177e4SLinus Torvalds /* Start a new read */ 509*1da177e4SLinus Torvalds hp_sdc.rqty = curr->seq[curr->idx]; 510*1da177e4SLinus Torvalds do_gettimeofday(&hp_sdc.rtv); 511*1da177e4SLinus Torvalds curr->idx++; 512*1da177e4SLinus Torvalds /* Still need to lock here in case of spurious irq. */ 513*1da177e4SLinus Torvalds write_lock_irq(&hp_sdc.rtq_lock); 514*1da177e4SLinus Torvalds hp_sdc.rcurr = curridx; 515*1da177e4SLinus Torvalds write_unlock_irq(&hp_sdc.rtq_lock); 516*1da177e4SLinus Torvalds hp_sdc_status_out8(postcmd); 517*1da177e4SLinus Torvalds goto finish; 518*1da177e4SLinus Torvalds } 519*1da177e4SLinus Torvalds hp_sdc_status_out8(postcmd); 520*1da177e4SLinus Torvalds goto actdone; 521*1da177e4SLinus Torvalds } 522*1da177e4SLinus Torvalds 523*1da177e4SLinus Torvalds actdone: 524*1da177e4SLinus Torvalds if (act & HP_SDC_ACT_SEMAPHORE) { 525*1da177e4SLinus Torvalds up(curr->act.semaphore); 526*1da177e4SLinus Torvalds } 527*1da177e4SLinus Torvalds else if (act & HP_SDC_ACT_CALLBACK) { 528*1da177e4SLinus Torvalds curr->act.irqhook(0,0,0,0); 529*1da177e4SLinus Torvalds } 530*1da177e4SLinus Torvalds if (curr->idx >= curr->endidx) { /* This transaction is over. */ 531*1da177e4SLinus Torvalds if (act & HP_SDC_ACT_DEALLOC) kfree(curr); 532*1da177e4SLinus Torvalds hp_sdc.tq[curridx] = NULL; 533*1da177e4SLinus Torvalds } 534*1da177e4SLinus Torvalds else { 535*1da177e4SLinus Torvalds curr->actidx = idx + 1; 536*1da177e4SLinus Torvalds curr->idx = idx + 2; 537*1da177e4SLinus Torvalds } 538*1da177e4SLinus Torvalds /* Interleave outbound data between the transactions. */ 539*1da177e4SLinus Torvalds hp_sdc.wcurr++; 540*1da177e4SLinus Torvalds if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0; 541*1da177e4SLinus Torvalds 542*1da177e4SLinus Torvalds finish: 543*1da177e4SLinus Torvalds /* If by some quirk IBF has cleared and our ISR has run to 544*1da177e4SLinus Torvalds see that that has happened, do it all again. */ 545*1da177e4SLinus Torvalds if (!hp_sdc.ibf && limit++ < 20) goto anew; 546*1da177e4SLinus Torvalds 547*1da177e4SLinus Torvalds done: 548*1da177e4SLinus Torvalds if (hp_sdc.wcurr >= 0) tasklet_schedule(&hp_sdc.task); 549*1da177e4SLinus Torvalds write_unlock(&hp_sdc.lock); 550*1da177e4SLinus Torvalds return 0; 551*1da177e4SLinus Torvalds } 552*1da177e4SLinus Torvalds 553*1da177e4SLinus Torvalds /******* Functions called in either user or kernel context ****/ 554*1da177e4SLinus Torvalds int hp_sdc_enqueue_transaction(hp_sdc_transaction *this) { 555*1da177e4SLinus Torvalds unsigned long flags; 556*1da177e4SLinus Torvalds int i; 557*1da177e4SLinus Torvalds 558*1da177e4SLinus Torvalds if (this == NULL) { 559*1da177e4SLinus Torvalds tasklet_schedule(&hp_sdc.task); 560*1da177e4SLinus Torvalds return -EINVAL; 561*1da177e4SLinus Torvalds }; 562*1da177e4SLinus Torvalds 563*1da177e4SLinus Torvalds write_lock_irqsave(&hp_sdc.lock, flags); 564*1da177e4SLinus Torvalds 565*1da177e4SLinus Torvalds /* Can't have same transaction on queue twice */ 566*1da177e4SLinus Torvalds for (i=0; i < HP_SDC_QUEUE_LEN; i++) 567*1da177e4SLinus Torvalds if (hp_sdc.tq[i] == this) goto fail; 568*1da177e4SLinus Torvalds 569*1da177e4SLinus Torvalds this->actidx = 0; 570*1da177e4SLinus Torvalds this->idx = 1; 571*1da177e4SLinus Torvalds 572*1da177e4SLinus Torvalds /* Search for empty slot */ 573*1da177e4SLinus Torvalds for (i=0; i < HP_SDC_QUEUE_LEN; i++) { 574*1da177e4SLinus Torvalds if (hp_sdc.tq[i] == NULL) { 575*1da177e4SLinus Torvalds hp_sdc.tq[i] = this; 576*1da177e4SLinus Torvalds write_unlock_irqrestore(&hp_sdc.lock, flags); 577*1da177e4SLinus Torvalds tasklet_schedule(&hp_sdc.task); 578*1da177e4SLinus Torvalds return 0; 579*1da177e4SLinus Torvalds } 580*1da177e4SLinus Torvalds } 581*1da177e4SLinus Torvalds write_unlock_irqrestore(&hp_sdc.lock, flags); 582*1da177e4SLinus Torvalds printk(KERN_WARNING PREFIX "No free slot to add transaction.\n"); 583*1da177e4SLinus Torvalds return -EBUSY; 584*1da177e4SLinus Torvalds 585*1da177e4SLinus Torvalds fail: 586*1da177e4SLinus Torvalds write_unlock_irqrestore(&hp_sdc.lock,flags); 587*1da177e4SLinus Torvalds printk(KERN_WARNING PREFIX "Transaction add failed: transaction already queued?\n"); 588*1da177e4SLinus Torvalds return -EINVAL; 589*1da177e4SLinus Torvalds } 590*1da177e4SLinus Torvalds 591*1da177e4SLinus Torvalds int hp_sdc_dequeue_transaction(hp_sdc_transaction *this) { 592*1da177e4SLinus Torvalds unsigned long flags; 593*1da177e4SLinus Torvalds int i; 594*1da177e4SLinus Torvalds 595*1da177e4SLinus Torvalds write_lock_irqsave(&hp_sdc.lock, flags); 596*1da177e4SLinus Torvalds 597*1da177e4SLinus Torvalds /* TODO: don't remove it if it's not done. */ 598*1da177e4SLinus Torvalds 599*1da177e4SLinus Torvalds for (i=0; i < HP_SDC_QUEUE_LEN; i++) 600*1da177e4SLinus Torvalds if (hp_sdc.tq[i] == this) hp_sdc.tq[i] = NULL; 601*1da177e4SLinus Torvalds 602*1da177e4SLinus Torvalds write_unlock_irqrestore(&hp_sdc.lock, flags); 603*1da177e4SLinus Torvalds return 0; 604*1da177e4SLinus Torvalds } 605*1da177e4SLinus Torvalds 606*1da177e4SLinus Torvalds 607*1da177e4SLinus Torvalds 608*1da177e4SLinus Torvalds /********************** User context functions **************************/ 609*1da177e4SLinus Torvalds int hp_sdc_request_timer_irq(hp_sdc_irqhook *callback) { 610*1da177e4SLinus Torvalds 611*1da177e4SLinus Torvalds if (callback == NULL || hp_sdc.dev == NULL) { 612*1da177e4SLinus Torvalds return -EINVAL; 613*1da177e4SLinus Torvalds } 614*1da177e4SLinus Torvalds write_lock_irq(&hp_sdc.hook_lock); 615*1da177e4SLinus Torvalds if (hp_sdc.timer != NULL) { 616*1da177e4SLinus Torvalds write_unlock_irq(&hp_sdc.hook_lock); 617*1da177e4SLinus Torvalds return -EBUSY; 618*1da177e4SLinus Torvalds } 619*1da177e4SLinus Torvalds 620*1da177e4SLinus Torvalds hp_sdc.timer = callback; 621*1da177e4SLinus Torvalds /* Enable interrupts from the timers */ 622*1da177e4SLinus Torvalds hp_sdc.im &= ~HP_SDC_IM_FH; 623*1da177e4SLinus Torvalds hp_sdc.im &= ~HP_SDC_IM_PT; 624*1da177e4SLinus Torvalds hp_sdc.im &= ~HP_SDC_IM_TIMERS; 625*1da177e4SLinus Torvalds hp_sdc.set_im = 1; 626*1da177e4SLinus Torvalds write_unlock_irq(&hp_sdc.hook_lock); 627*1da177e4SLinus Torvalds 628*1da177e4SLinus Torvalds tasklet_schedule(&hp_sdc.task); 629*1da177e4SLinus Torvalds 630*1da177e4SLinus Torvalds return 0; 631*1da177e4SLinus Torvalds } 632*1da177e4SLinus Torvalds 633*1da177e4SLinus Torvalds int hp_sdc_request_hil_irq(hp_sdc_irqhook *callback) { 634*1da177e4SLinus Torvalds 635*1da177e4SLinus Torvalds if (callback == NULL || hp_sdc.dev == NULL) { 636*1da177e4SLinus Torvalds return -EINVAL; 637*1da177e4SLinus Torvalds } 638*1da177e4SLinus Torvalds write_lock_irq(&hp_sdc.hook_lock); 639*1da177e4SLinus Torvalds if (hp_sdc.hil != NULL) { 640*1da177e4SLinus Torvalds write_unlock_irq(&hp_sdc.hook_lock); 641*1da177e4SLinus Torvalds return -EBUSY; 642*1da177e4SLinus Torvalds } 643*1da177e4SLinus Torvalds 644*1da177e4SLinus Torvalds hp_sdc.hil = callback; 645*1da177e4SLinus Torvalds hp_sdc.im &= ~(HP_SDC_IM_HIL | HP_SDC_IM_RESET); 646*1da177e4SLinus Torvalds hp_sdc.set_im = 1; 647*1da177e4SLinus Torvalds write_unlock_irq(&hp_sdc.hook_lock); 648*1da177e4SLinus Torvalds 649*1da177e4SLinus Torvalds tasklet_schedule(&hp_sdc.task); 650*1da177e4SLinus Torvalds 651*1da177e4SLinus Torvalds return 0; 652*1da177e4SLinus Torvalds } 653*1da177e4SLinus Torvalds 654*1da177e4SLinus Torvalds int hp_sdc_request_cooked_irq(hp_sdc_irqhook *callback) { 655*1da177e4SLinus Torvalds 656*1da177e4SLinus Torvalds if (callback == NULL || hp_sdc.dev == NULL) { 657*1da177e4SLinus Torvalds return -EINVAL; 658*1da177e4SLinus Torvalds } 659*1da177e4SLinus Torvalds write_lock_irq(&hp_sdc.hook_lock); 660*1da177e4SLinus Torvalds if (hp_sdc.cooked != NULL) { 661*1da177e4SLinus Torvalds write_unlock_irq(&hp_sdc.hook_lock); 662*1da177e4SLinus Torvalds return -EBUSY; 663*1da177e4SLinus Torvalds } 664*1da177e4SLinus Torvalds 665*1da177e4SLinus Torvalds /* Enable interrupts from the HIL MLC */ 666*1da177e4SLinus Torvalds hp_sdc.cooked = callback; 667*1da177e4SLinus Torvalds hp_sdc.im &= ~(HP_SDC_IM_HIL | HP_SDC_IM_RESET); 668*1da177e4SLinus Torvalds hp_sdc.set_im = 1; 669*1da177e4SLinus Torvalds write_unlock_irq(&hp_sdc.hook_lock); 670*1da177e4SLinus Torvalds 671*1da177e4SLinus Torvalds tasklet_schedule(&hp_sdc.task); 672*1da177e4SLinus Torvalds 673*1da177e4SLinus Torvalds return 0; 674*1da177e4SLinus Torvalds } 675*1da177e4SLinus Torvalds 676*1da177e4SLinus Torvalds int hp_sdc_release_timer_irq(hp_sdc_irqhook *callback) { 677*1da177e4SLinus Torvalds 678*1da177e4SLinus Torvalds 679*1da177e4SLinus Torvalds write_lock_irq(&hp_sdc.hook_lock); 680*1da177e4SLinus Torvalds if ((callback != hp_sdc.timer) || 681*1da177e4SLinus Torvalds (hp_sdc.timer == NULL)) { 682*1da177e4SLinus Torvalds write_unlock_irq(&hp_sdc.hook_lock); 683*1da177e4SLinus Torvalds return -EINVAL; 684*1da177e4SLinus Torvalds } 685*1da177e4SLinus Torvalds 686*1da177e4SLinus Torvalds /* Disable interrupts from the timers */ 687*1da177e4SLinus Torvalds hp_sdc.timer = NULL; 688*1da177e4SLinus Torvalds hp_sdc.im |= HP_SDC_IM_TIMERS; 689*1da177e4SLinus Torvalds hp_sdc.im |= HP_SDC_IM_FH; 690*1da177e4SLinus Torvalds hp_sdc.im |= HP_SDC_IM_PT; 691*1da177e4SLinus Torvalds hp_sdc.set_im = 1; 692*1da177e4SLinus Torvalds write_unlock_irq(&hp_sdc.hook_lock); 693*1da177e4SLinus Torvalds tasklet_schedule(&hp_sdc.task); 694*1da177e4SLinus Torvalds 695*1da177e4SLinus Torvalds return 0; 696*1da177e4SLinus Torvalds } 697*1da177e4SLinus Torvalds 698*1da177e4SLinus Torvalds int hp_sdc_release_hil_irq(hp_sdc_irqhook *callback) { 699*1da177e4SLinus Torvalds 700*1da177e4SLinus Torvalds write_lock_irq(&hp_sdc.hook_lock); 701*1da177e4SLinus Torvalds if ((callback != hp_sdc.hil) || 702*1da177e4SLinus Torvalds (hp_sdc.hil == NULL)) { 703*1da177e4SLinus Torvalds write_unlock_irq(&hp_sdc.hook_lock); 704*1da177e4SLinus Torvalds return -EINVAL; 705*1da177e4SLinus Torvalds } 706*1da177e4SLinus Torvalds 707*1da177e4SLinus Torvalds hp_sdc.hil = NULL; 708*1da177e4SLinus Torvalds /* Disable interrupts from HIL only if there is no cooked driver. */ 709*1da177e4SLinus Torvalds if(hp_sdc.cooked == NULL) { 710*1da177e4SLinus Torvalds hp_sdc.im |= (HP_SDC_IM_HIL | HP_SDC_IM_RESET); 711*1da177e4SLinus Torvalds hp_sdc.set_im = 1; 712*1da177e4SLinus Torvalds } 713*1da177e4SLinus Torvalds write_unlock_irq(&hp_sdc.hook_lock); 714*1da177e4SLinus Torvalds tasklet_schedule(&hp_sdc.task); 715*1da177e4SLinus Torvalds 716*1da177e4SLinus Torvalds return 0; 717*1da177e4SLinus Torvalds } 718*1da177e4SLinus Torvalds 719*1da177e4SLinus Torvalds int hp_sdc_release_cooked_irq(hp_sdc_irqhook *callback) { 720*1da177e4SLinus Torvalds 721*1da177e4SLinus Torvalds write_lock_irq(&hp_sdc.hook_lock); 722*1da177e4SLinus Torvalds if ((callback != hp_sdc.cooked) || 723*1da177e4SLinus Torvalds (hp_sdc.cooked == NULL)) { 724*1da177e4SLinus Torvalds write_unlock_irq(&hp_sdc.hook_lock); 725*1da177e4SLinus Torvalds return -EINVAL; 726*1da177e4SLinus Torvalds } 727*1da177e4SLinus Torvalds 728*1da177e4SLinus Torvalds hp_sdc.cooked = NULL; 729*1da177e4SLinus Torvalds /* Disable interrupts from HIL only if there is no raw HIL driver. */ 730*1da177e4SLinus Torvalds if(hp_sdc.hil == NULL) { 731*1da177e4SLinus Torvalds hp_sdc.im |= (HP_SDC_IM_HIL | HP_SDC_IM_RESET); 732*1da177e4SLinus Torvalds hp_sdc.set_im = 1; 733*1da177e4SLinus Torvalds } 734*1da177e4SLinus Torvalds write_unlock_irq(&hp_sdc.hook_lock); 735*1da177e4SLinus Torvalds tasklet_schedule(&hp_sdc.task); 736*1da177e4SLinus Torvalds 737*1da177e4SLinus Torvalds return 0; 738*1da177e4SLinus Torvalds } 739*1da177e4SLinus Torvalds 740*1da177e4SLinus Torvalds /************************* Keepalive timer task *********************/ 741*1da177e4SLinus Torvalds 742*1da177e4SLinus Torvalds void hp_sdc_kicker (unsigned long data) { 743*1da177e4SLinus Torvalds tasklet_schedule(&hp_sdc.task); 744*1da177e4SLinus Torvalds /* Re-insert the periodic task. */ 745*1da177e4SLinus Torvalds mod_timer(&hp_sdc.kicker, jiffies + HZ); 746*1da177e4SLinus Torvalds } 747*1da177e4SLinus Torvalds 748*1da177e4SLinus Torvalds /************************** Module Initialization ***************************/ 749*1da177e4SLinus Torvalds 750*1da177e4SLinus Torvalds #if defined(__hppa__) 751*1da177e4SLinus Torvalds 752*1da177e4SLinus Torvalds static struct parisc_device_id hp_sdc_tbl[] = { 753*1da177e4SLinus Torvalds { 754*1da177e4SLinus Torvalds .hw_type = HPHW_FIO, 755*1da177e4SLinus Torvalds .hversion_rev = HVERSION_REV_ANY_ID, 756*1da177e4SLinus Torvalds .hversion = HVERSION_ANY_ID, 757*1da177e4SLinus Torvalds .sversion = 0x73, 758*1da177e4SLinus Torvalds }, 759*1da177e4SLinus Torvalds { 0, } 760*1da177e4SLinus Torvalds }; 761*1da177e4SLinus Torvalds 762*1da177e4SLinus Torvalds MODULE_DEVICE_TABLE(parisc, hp_sdc_tbl); 763*1da177e4SLinus Torvalds 764*1da177e4SLinus Torvalds static int __init hp_sdc_init_hppa(struct parisc_device *d); 765*1da177e4SLinus Torvalds 766*1da177e4SLinus Torvalds static struct parisc_driver hp_sdc_driver = { 767*1da177e4SLinus Torvalds .name = "HP SDC", 768*1da177e4SLinus Torvalds .id_table = hp_sdc_tbl, 769*1da177e4SLinus Torvalds .probe = hp_sdc_init_hppa, 770*1da177e4SLinus Torvalds }; 771*1da177e4SLinus Torvalds 772*1da177e4SLinus Torvalds #endif /* __hppa__ */ 773*1da177e4SLinus Torvalds 774*1da177e4SLinus Torvalds static int __init hp_sdc_init(void) 775*1da177e4SLinus Torvalds { 776*1da177e4SLinus Torvalds int i; 777*1da177e4SLinus Torvalds char *errstr; 778*1da177e4SLinus Torvalds hp_sdc_transaction t_sync; 779*1da177e4SLinus Torvalds uint8_t ts_sync[6]; 780*1da177e4SLinus Torvalds struct semaphore s_sync; 781*1da177e4SLinus Torvalds 782*1da177e4SLinus Torvalds rwlock_init(&hp_sdc.lock); 783*1da177e4SLinus Torvalds rwlock_init(&hp_sdc.ibf_lock); 784*1da177e4SLinus Torvalds rwlock_init(&hp_sdc.rtq_lock); 785*1da177e4SLinus Torvalds rwlock_init(&hp_sdc.hook_lock); 786*1da177e4SLinus Torvalds 787*1da177e4SLinus Torvalds hp_sdc.timer = NULL; 788*1da177e4SLinus Torvalds hp_sdc.hil = NULL; 789*1da177e4SLinus Torvalds hp_sdc.pup = NULL; 790*1da177e4SLinus Torvalds hp_sdc.cooked = NULL; 791*1da177e4SLinus Torvalds hp_sdc.im = HP_SDC_IM_MASK; /* Mask maskable irqs */ 792*1da177e4SLinus Torvalds hp_sdc.set_im = 1; 793*1da177e4SLinus Torvalds hp_sdc.wi = 0xff; 794*1da177e4SLinus Torvalds hp_sdc.r7[0] = 0xff; 795*1da177e4SLinus Torvalds hp_sdc.r7[1] = 0xff; 796*1da177e4SLinus Torvalds hp_sdc.r7[2] = 0xff; 797*1da177e4SLinus Torvalds hp_sdc.r7[3] = 0xff; 798*1da177e4SLinus Torvalds hp_sdc.ibf = 1; 799*1da177e4SLinus Torvalds 800*1da177e4SLinus Torvalds for (i = 0; i < HP_SDC_QUEUE_LEN; i++) hp_sdc.tq[i] = NULL; 801*1da177e4SLinus Torvalds hp_sdc.wcurr = -1; 802*1da177e4SLinus Torvalds hp_sdc.rcurr = -1; 803*1da177e4SLinus Torvalds hp_sdc.rqty = 0; 804*1da177e4SLinus Torvalds 805*1da177e4SLinus Torvalds hp_sdc.dev_err = -ENODEV; 806*1da177e4SLinus Torvalds 807*1da177e4SLinus Torvalds errstr = "IO not found for"; 808*1da177e4SLinus Torvalds if (!hp_sdc.base_io) goto err0; 809*1da177e4SLinus Torvalds 810*1da177e4SLinus Torvalds errstr = "IRQ not found for"; 811*1da177e4SLinus Torvalds if (!hp_sdc.irq) goto err0; 812*1da177e4SLinus Torvalds 813*1da177e4SLinus Torvalds hp_sdc.dev_err = -EBUSY; 814*1da177e4SLinus Torvalds 815*1da177e4SLinus Torvalds #if defined(__hppa__) 816*1da177e4SLinus Torvalds errstr = "IO not available for"; 817*1da177e4SLinus Torvalds if (request_region(hp_sdc.data_io, 2, hp_sdc_driver.name)) goto err0; 818*1da177e4SLinus Torvalds #endif 819*1da177e4SLinus Torvalds 820*1da177e4SLinus Torvalds errstr = "IRQ not available for"; 821*1da177e4SLinus Torvalds if(request_irq(hp_sdc.irq, &hp_sdc_isr, 0, "HP SDC", 822*1da177e4SLinus Torvalds (void *) hp_sdc.base_io)) goto err1; 823*1da177e4SLinus Torvalds 824*1da177e4SLinus Torvalds errstr = "NMI not available for"; 825*1da177e4SLinus Torvalds if (request_irq(hp_sdc.nmi, &hp_sdc_nmisr, 0, "HP SDC NMI", 826*1da177e4SLinus Torvalds (void *) hp_sdc.base_io)) goto err2; 827*1da177e4SLinus Torvalds 828*1da177e4SLinus Torvalds printk(KERN_INFO PREFIX "HP SDC at 0x%p, IRQ %d (NMI IRQ %d)\n", 829*1da177e4SLinus Torvalds (void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi); 830*1da177e4SLinus Torvalds 831*1da177e4SLinus Torvalds hp_sdc_status_in8(); 832*1da177e4SLinus Torvalds hp_sdc_data_in8(); 833*1da177e4SLinus Torvalds 834*1da177e4SLinus Torvalds tasklet_init(&hp_sdc.task, hp_sdc_tasklet, 0); 835*1da177e4SLinus Torvalds 836*1da177e4SLinus Torvalds /* Sync the output buffer registers, thus scheduling hp_sdc_tasklet. */ 837*1da177e4SLinus Torvalds t_sync.actidx = 0; 838*1da177e4SLinus Torvalds t_sync.idx = 1; 839*1da177e4SLinus Torvalds t_sync.endidx = 6; 840*1da177e4SLinus Torvalds t_sync.seq = ts_sync; 841*1da177e4SLinus Torvalds ts_sync[0] = HP_SDC_ACT_DATAREG | HP_SDC_ACT_SEMAPHORE; 842*1da177e4SLinus Torvalds ts_sync[1] = 0x0f; 843*1da177e4SLinus Torvalds ts_sync[2] = ts_sync[3] = ts_sync[4] = ts_sync[5] = 0; 844*1da177e4SLinus Torvalds t_sync.act.semaphore = &s_sync; 845*1da177e4SLinus Torvalds init_MUTEX_LOCKED(&s_sync); 846*1da177e4SLinus Torvalds hp_sdc_enqueue_transaction(&t_sync); 847*1da177e4SLinus Torvalds down(&s_sync); /* Wait for t_sync to complete */ 848*1da177e4SLinus Torvalds 849*1da177e4SLinus Torvalds /* Create the keepalive task */ 850*1da177e4SLinus Torvalds init_timer(&hp_sdc.kicker); 851*1da177e4SLinus Torvalds hp_sdc.kicker.expires = jiffies + HZ; 852*1da177e4SLinus Torvalds hp_sdc.kicker.function = &hp_sdc_kicker; 853*1da177e4SLinus Torvalds add_timer(&hp_sdc.kicker); 854*1da177e4SLinus Torvalds 855*1da177e4SLinus Torvalds hp_sdc.dev_err = 0; 856*1da177e4SLinus Torvalds return 0; 857*1da177e4SLinus Torvalds err2: 858*1da177e4SLinus Torvalds free_irq(hp_sdc.irq, NULL); 859*1da177e4SLinus Torvalds err1: 860*1da177e4SLinus Torvalds release_region(hp_sdc.data_io, 2); 861*1da177e4SLinus Torvalds err0: 862*1da177e4SLinus Torvalds printk(KERN_WARNING PREFIX ": %s SDC IO=0x%p IRQ=0x%x NMI=0x%x\n", 863*1da177e4SLinus Torvalds errstr, (void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi); 864*1da177e4SLinus Torvalds hp_sdc.dev = NULL; 865*1da177e4SLinus Torvalds return hp_sdc.dev_err; 866*1da177e4SLinus Torvalds } 867*1da177e4SLinus Torvalds 868*1da177e4SLinus Torvalds #if defined(__hppa__) 869*1da177e4SLinus Torvalds 870*1da177e4SLinus Torvalds static int __init hp_sdc_init_hppa(struct parisc_device *d) 871*1da177e4SLinus Torvalds { 872*1da177e4SLinus Torvalds if (!d) return 1; 873*1da177e4SLinus Torvalds if (hp_sdc.dev != NULL) return 1; /* We only expect one SDC */ 874*1da177e4SLinus Torvalds 875*1da177e4SLinus Torvalds hp_sdc.dev = d; 876*1da177e4SLinus Torvalds hp_sdc.irq = d->irq; 877*1da177e4SLinus Torvalds hp_sdc.nmi = d->aux_irq; 878*1da177e4SLinus Torvalds hp_sdc.base_io = d->hpa; 879*1da177e4SLinus Torvalds hp_sdc.data_io = d->hpa + 0x800; 880*1da177e4SLinus Torvalds hp_sdc.status_io = d->hpa + 0x801; 881*1da177e4SLinus Torvalds 882*1da177e4SLinus Torvalds return hp_sdc_init(); 883*1da177e4SLinus Torvalds } 884*1da177e4SLinus Torvalds 885*1da177e4SLinus Torvalds #endif /* __hppa__ */ 886*1da177e4SLinus Torvalds 887*1da177e4SLinus Torvalds #if !defined(__mc68000__) /* Link error on m68k! */ 888*1da177e4SLinus Torvalds static void __exit hp_sdc_exit(void) 889*1da177e4SLinus Torvalds #else 890*1da177e4SLinus Torvalds static void hp_sdc_exit(void) 891*1da177e4SLinus Torvalds #endif 892*1da177e4SLinus Torvalds { 893*1da177e4SLinus Torvalds write_lock_irq(&hp_sdc.lock); 894*1da177e4SLinus Torvalds 895*1da177e4SLinus Torvalds /* Turn off all maskable "sub-function" irq's. */ 896*1da177e4SLinus Torvalds hp_sdc_spin_ibf(); 897*1da177e4SLinus Torvalds sdc_writeb(HP_SDC_CMD_SET_IM | HP_SDC_IM_MASK, hp_sdc.status_io); 898*1da177e4SLinus Torvalds 899*1da177e4SLinus Torvalds /* Wait until we know this has been processed by the i8042 */ 900*1da177e4SLinus Torvalds hp_sdc_spin_ibf(); 901*1da177e4SLinus Torvalds 902*1da177e4SLinus Torvalds free_irq(hp_sdc.nmi, NULL); 903*1da177e4SLinus Torvalds free_irq(hp_sdc.irq, NULL); 904*1da177e4SLinus Torvalds write_unlock_irq(&hp_sdc.lock); 905*1da177e4SLinus Torvalds 906*1da177e4SLinus Torvalds del_timer(&hp_sdc.kicker); 907*1da177e4SLinus Torvalds 908*1da177e4SLinus Torvalds tasklet_kill(&hp_sdc.task); 909*1da177e4SLinus Torvalds 910*1da177e4SLinus Torvalds /* release_region(hp_sdc.data_io, 2); */ 911*1da177e4SLinus Torvalds 912*1da177e4SLinus Torvalds #if defined(__hppa__) 913*1da177e4SLinus Torvalds if (unregister_parisc_driver(&hp_sdc_driver)) 914*1da177e4SLinus Torvalds printk(KERN_WARNING PREFIX "Error unregistering HP SDC"); 915*1da177e4SLinus Torvalds #endif 916*1da177e4SLinus Torvalds } 917*1da177e4SLinus Torvalds 918*1da177e4SLinus Torvalds static int __init hp_sdc_register(void) 919*1da177e4SLinus Torvalds { 920*1da177e4SLinus Torvalds hp_sdc_transaction tq_init; 921*1da177e4SLinus Torvalds uint8_t tq_init_seq[5]; 922*1da177e4SLinus Torvalds struct semaphore tq_init_sem; 923*1da177e4SLinus Torvalds #if defined(__mc68000__) 924*1da177e4SLinus Torvalds mm_segment_t fs; 925*1da177e4SLinus Torvalds unsigned char i; 926*1da177e4SLinus Torvalds #endif 927*1da177e4SLinus Torvalds 928*1da177e4SLinus Torvalds hp_sdc.dev = NULL; 929*1da177e4SLinus Torvalds hp_sdc.dev_err = 0; 930*1da177e4SLinus Torvalds #if defined(__hppa__) 931*1da177e4SLinus Torvalds if (register_parisc_driver(&hp_sdc_driver)) { 932*1da177e4SLinus Torvalds printk(KERN_WARNING PREFIX "Error registering SDC with system bus tree.\n"); 933*1da177e4SLinus Torvalds return -ENODEV; 934*1da177e4SLinus Torvalds } 935*1da177e4SLinus Torvalds #elif defined(__mc68000__) 936*1da177e4SLinus Torvalds if (!MACH_IS_HP300) 937*1da177e4SLinus Torvalds return -ENODEV; 938*1da177e4SLinus Torvalds 939*1da177e4SLinus Torvalds hp_sdc.irq = 1; 940*1da177e4SLinus Torvalds hp_sdc.nmi = 7; 941*1da177e4SLinus Torvalds hp_sdc.base_io = (unsigned long) 0xf0428000; 942*1da177e4SLinus Torvalds hp_sdc.data_io = (unsigned long) hp_sdc.base_io + 1; 943*1da177e4SLinus Torvalds hp_sdc.status_io = (unsigned long) hp_sdc.base_io + 3; 944*1da177e4SLinus Torvalds fs = get_fs(); 945*1da177e4SLinus Torvalds set_fs(KERNEL_DS); 946*1da177e4SLinus Torvalds if (!get_user(i, (unsigned char *)hp_sdc.data_io)) 947*1da177e4SLinus Torvalds hp_sdc.dev = (void *)1; 948*1da177e4SLinus Torvalds set_fs(fs); 949*1da177e4SLinus Torvalds hp_sdc.dev_err = hp_sdc_init(); 950*1da177e4SLinus Torvalds #endif 951*1da177e4SLinus Torvalds if (hp_sdc.dev == NULL) { 952*1da177e4SLinus Torvalds printk(KERN_WARNING PREFIX "No SDC found.\n"); 953*1da177e4SLinus Torvalds return hp_sdc.dev_err; 954*1da177e4SLinus Torvalds } 955*1da177e4SLinus Torvalds 956*1da177e4SLinus Torvalds init_MUTEX_LOCKED(&tq_init_sem); 957*1da177e4SLinus Torvalds 958*1da177e4SLinus Torvalds tq_init.actidx = 0; 959*1da177e4SLinus Torvalds tq_init.idx = 1; 960*1da177e4SLinus Torvalds tq_init.endidx = 5; 961*1da177e4SLinus Torvalds tq_init.seq = tq_init_seq; 962*1da177e4SLinus Torvalds tq_init.act.semaphore = &tq_init_sem; 963*1da177e4SLinus Torvalds 964*1da177e4SLinus Torvalds tq_init_seq[0] = 965*1da177e4SLinus Torvalds HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE; 966*1da177e4SLinus Torvalds tq_init_seq[1] = HP_SDC_CMD_READ_KCC; 967*1da177e4SLinus Torvalds tq_init_seq[2] = 1; 968*1da177e4SLinus Torvalds tq_init_seq[3] = 0; 969*1da177e4SLinus Torvalds tq_init_seq[4] = 0; 970*1da177e4SLinus Torvalds 971*1da177e4SLinus Torvalds hp_sdc_enqueue_transaction(&tq_init); 972*1da177e4SLinus Torvalds 973*1da177e4SLinus Torvalds down(&tq_init_sem); 974*1da177e4SLinus Torvalds up(&tq_init_sem); 975*1da177e4SLinus Torvalds 976*1da177e4SLinus Torvalds if ((tq_init_seq[0] & HP_SDC_ACT_DEAD) == HP_SDC_ACT_DEAD) { 977*1da177e4SLinus Torvalds printk(KERN_WARNING PREFIX "Error reading config byte.\n"); 978*1da177e4SLinus Torvalds hp_sdc_exit(); 979*1da177e4SLinus Torvalds return -ENODEV; 980*1da177e4SLinus Torvalds } 981*1da177e4SLinus Torvalds hp_sdc.r11 = tq_init_seq[4]; 982*1da177e4SLinus Torvalds if (hp_sdc.r11 & HP_SDC_CFG_NEW) { 983*1da177e4SLinus Torvalds char *str; 984*1da177e4SLinus Torvalds printk(KERN_INFO PREFIX "New style SDC\n"); 985*1da177e4SLinus Torvalds tq_init_seq[1] = HP_SDC_CMD_READ_XTD; 986*1da177e4SLinus Torvalds tq_init.actidx = 0; 987*1da177e4SLinus Torvalds tq_init.idx = 1; 988*1da177e4SLinus Torvalds down(&tq_init_sem); 989*1da177e4SLinus Torvalds hp_sdc_enqueue_transaction(&tq_init); 990*1da177e4SLinus Torvalds down(&tq_init_sem); 991*1da177e4SLinus Torvalds up(&tq_init_sem); 992*1da177e4SLinus Torvalds if ((tq_init_seq[0] & HP_SDC_ACT_DEAD) == HP_SDC_ACT_DEAD) { 993*1da177e4SLinus Torvalds printk(KERN_WARNING PREFIX "Error reading extended config byte.\n"); 994*1da177e4SLinus Torvalds return -ENODEV; 995*1da177e4SLinus Torvalds } 996*1da177e4SLinus Torvalds hp_sdc.r7e = tq_init_seq[4]; 997*1da177e4SLinus Torvalds HP_SDC_XTD_REV_STRINGS(hp_sdc.r7e & HP_SDC_XTD_REV, str) 998*1da177e4SLinus Torvalds printk(KERN_INFO PREFIX "Revision: %s\n", str); 999*1da177e4SLinus Torvalds if (hp_sdc.r7e & HP_SDC_XTD_BEEPER) { 1000*1da177e4SLinus Torvalds printk(KERN_INFO PREFIX "TI SN76494 beeper present\n"); 1001*1da177e4SLinus Torvalds } 1002*1da177e4SLinus Torvalds if (hp_sdc.r7e & HP_SDC_XTD_BBRTC) { 1003*1da177e4SLinus Torvalds printk(KERN_INFO PREFIX "OKI MSM-58321 BBRTC present\n"); 1004*1da177e4SLinus Torvalds } 1005*1da177e4SLinus Torvalds printk(KERN_INFO PREFIX "Spunking the self test register to force PUP " 1006*1da177e4SLinus Torvalds "on next firmware reset.\n"); 1007*1da177e4SLinus Torvalds tq_init_seq[0] = HP_SDC_ACT_PRECMD | 1008*1da177e4SLinus Torvalds HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE; 1009*1da177e4SLinus Torvalds tq_init_seq[1] = HP_SDC_CMD_SET_STR; 1010*1da177e4SLinus Torvalds tq_init_seq[2] = 1; 1011*1da177e4SLinus Torvalds tq_init_seq[3] = 0; 1012*1da177e4SLinus Torvalds tq_init.actidx = 0; 1013*1da177e4SLinus Torvalds tq_init.idx = 1; 1014*1da177e4SLinus Torvalds tq_init.endidx = 4; 1015*1da177e4SLinus Torvalds down(&tq_init_sem); 1016*1da177e4SLinus Torvalds hp_sdc_enqueue_transaction(&tq_init); 1017*1da177e4SLinus Torvalds down(&tq_init_sem); 1018*1da177e4SLinus Torvalds up(&tq_init_sem); 1019*1da177e4SLinus Torvalds } 1020*1da177e4SLinus Torvalds else { 1021*1da177e4SLinus Torvalds printk(KERN_INFO PREFIX "Old style SDC (1820-%s).\n", 1022*1da177e4SLinus Torvalds (hp_sdc.r11 & HP_SDC_CFG_REV) ? "3300" : "2564/3087"); 1023*1da177e4SLinus Torvalds } 1024*1da177e4SLinus Torvalds 1025*1da177e4SLinus Torvalds return 0; 1026*1da177e4SLinus Torvalds } 1027*1da177e4SLinus Torvalds 1028*1da177e4SLinus Torvalds module_init(hp_sdc_register); 1029*1da177e4SLinus Torvalds module_exit(hp_sdc_exit); 1030*1da177e4SLinus Torvalds 1031*1da177e4SLinus Torvalds /* Timing notes: These measurements taken on my 64MHz 7100-LC (715/64) 1032*1da177e4SLinus Torvalds * cycles cycles-adj time 1033*1da177e4SLinus Torvalds * between two consecutive mfctl(16)'s: 4 n/a 63ns 1034*1da177e4SLinus Torvalds * hp_sdc_spin_ibf when idle: 119 115 1.7us 1035*1da177e4SLinus Torvalds * gsc_writeb status register: 83 79 1.2us 1036*1da177e4SLinus Torvalds * IBF to clear after sending SET_IM: 6204 6006 93us 1037*1da177e4SLinus Torvalds * IBF to clear after sending LOAD_RT: 4467 4352 68us 1038*1da177e4SLinus Torvalds * IBF to clear after sending two LOAD_RTs: 18974 18859 295us 1039*1da177e4SLinus Torvalds * READ_T1, read status/data, IRQ, call handler: 35564 n/a 556us 1040*1da177e4SLinus Torvalds * cmd to ~IBF READ_T1 2nd time right after: 5158403 n/a 81ms 1041*1da177e4SLinus Torvalds * between IRQ received and ~IBF for above: 2578877 n/a 40ms 1042*1da177e4SLinus Torvalds * 1043*1da177e4SLinus Torvalds * Performance stats after a run of this module configuring HIL and 1044*1da177e4SLinus Torvalds * receiving a few mouse events: 1045*1da177e4SLinus Torvalds * 1046*1da177e4SLinus Torvalds * status in8 282508 cycles 7128 calls 1047*1da177e4SLinus Torvalds * status out8 8404 cycles 341 calls 1048*1da177e4SLinus Torvalds * data out8 1734 cycles 78 calls 1049*1da177e4SLinus Torvalds * isr 174324 cycles 617 calls (includes take) 1050*1da177e4SLinus Torvalds * take 1241 cycles 2 calls 1051*1da177e4SLinus Torvalds * put 1411504 cycles 6937 calls 1052*1da177e4SLinus Torvalds * task 1655209 cycles 6937 calls (includes put) 1053*1da177e4SLinus Torvalds * 1054*1da177e4SLinus Torvalds */ 1055