xref: /openbmc/linux/drivers/input/serio/hp_sdc.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
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