This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / drivers / input / serio / hp_sdc_mlc.c
1 /*
2  * Access to HP-HIL MLC through HP System Device Controller.
3  *
4  * Copyright (c) 2001 Brian S. Julin
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions, and the following disclaimer,
12  *    without modification.
13  * 2. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * Alternatively, this software may be distributed under the terms of the
17  * GNU General Public License ("GPL").
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  *
29  * References:
30  * HP-HIL Technical Reference Manual.  Hewlett Packard Product No. 45918A
31  * System Device Controller Microprocessor Firmware Theory of Operation
32  *      for Part Number 1820-4784 Revision B.  Dwg No. A-1820-4784-2
33  *
34  */
35
36 #include <linux/hil_mlc.h>
37 #include <linux/hp_sdc.h>
38 #include <linux/errno.h>
39 #include <linux/kernel.h>
40 #include <linux/module.h>
41 #include <linux/init.h>
42 #include <linux/string.h>
43
44 #define PREFIX "HP SDC MLC: "
45
46 static hil_mlc hp_sdc_mlc;
47
48 MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
49 MODULE_DESCRIPTION("Glue for onboard HIL MLC in HP-PARISC machines");
50 MODULE_LICENSE("Dual BSD/GPL");
51
52 struct hp_sdc_mlc_priv_s {
53         int emtestmode;
54         hp_sdc_transaction trans;
55         u8 tseq[16];
56         int got5x;
57 } hp_sdc_mlc_priv;
58
59 /************************* Interrupt context ******************************/
60 static void hp_sdc_mlc_isr (int irq, void *dev_id, 
61                             uint8_t status, uint8_t data) {
62         int idx;
63         hil_mlc *mlc = &hp_sdc_mlc;
64
65         write_lock(&(mlc->lock));
66         if (mlc->icount < 0) {
67                 printk(KERN_WARNING PREFIX "HIL Overflow!\n");
68                 up(&mlc->isem);
69                 goto out;
70         }
71         idx = 15 - mlc->icount;
72         if ((status & HP_SDC_STATUS_IRQMASK) == HP_SDC_STATUS_HILDATA) {
73                 mlc->ipacket[idx] |= data | HIL_ERR_INT;
74                 mlc->icount--;
75                 if (hp_sdc_mlc_priv.got5x) goto check;
76                 if (!idx) goto check;
77                 if ((mlc->ipacket[idx-1] & HIL_PKT_ADDR_MASK) !=
78                     (mlc->ipacket[idx] & HIL_PKT_ADDR_MASK)) {
79                         mlc->ipacket[idx] &= ~HIL_PKT_ADDR_MASK;
80                         mlc->ipacket[idx] |= (mlc->ipacket[idx-1] 
81                                                     & HIL_PKT_ADDR_MASK);
82                 }
83                 goto check;
84         }
85         /* We know status is 5X */
86         if (data & HP_SDC_HIL_ISERR) goto err;
87         mlc->ipacket[idx] = 
88                 (data & HP_SDC_HIL_R1MASK) << HIL_PKT_ADDR_SHIFT;
89         hp_sdc_mlc_priv.got5x = 1;
90         goto out;
91
92  check:
93         hp_sdc_mlc_priv.got5x = 0;
94         if (mlc->imatch == 0) goto done;
95         if ((mlc->imatch == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) 
96             && (mlc->ipacket[idx] == (mlc->imatch | idx))) goto done;
97         if (mlc->ipacket[idx] == mlc->imatch) goto done;
98         goto out;
99
100  err:                           
101         printk(KERN_DEBUG PREFIX "err code %x\n", data);
102         switch (data) {
103         case HP_SDC_HIL_RC_DONE:
104                 printk(KERN_WARNING PREFIX "Bastard SDC reconfigured loop!\n");
105                 break;
106         case HP_SDC_HIL_ERR:
107                 mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_PERR | 
108                   HIL_ERR_FERR | HIL_ERR_FOF;
109                 break;
110         case HP_SDC_HIL_TO:
111                 mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_LERR;
112                 break;
113         case HP_SDC_HIL_RC:
114                 printk(KERN_WARNING PREFIX "Bastard SDC decided to reconfigure loop!\n");
115                 break;
116         default:
117                 printk(KERN_WARNING PREFIX "Unkown HIL Error status (%x)!\n", data);
118                 break;
119         }
120         /* No more data will be coming due to an error. */
121  done:
122         tasklet_schedule(mlc->tasklet);
123         up(&(mlc->isem));
124  out:
125         write_unlock(&(mlc->lock));
126 }
127
128
129 /******************** Tasklet or userspace context functions ****************/
130
131 static int hp_sdc_mlc_in (hil_mlc *mlc, suseconds_t timeout) {
132         unsigned long flags;
133         struct hp_sdc_mlc_priv_s *priv;
134         int rc = 2;
135
136         priv = mlc->priv;
137
138         write_lock_irqsave(&(mlc->lock), flags);
139
140         /* Try to down the semaphore */
141         if (down_trylock(&(mlc->isem))) {
142                 struct timeval tv;
143                 if (priv->emtestmode) {
144                         mlc->ipacket[0] = 
145                                 HIL_ERR_INT | (mlc->opacket & 
146                                                (HIL_PKT_CMD | 
147                                                 HIL_PKT_ADDR_MASK | 
148                                                 HIL_PKT_DATA_MASK));
149                         mlc->icount = 14;
150                         /* printk(KERN_DEBUG PREFIX ">[%x]\n", mlc->ipacket[0]); */
151                         goto wasup;
152                 }
153                 do_gettimeofday(&tv);
154                 tv.tv_usec += 1000000 * (tv.tv_sec - mlc->instart.tv_sec);
155                 if (tv.tv_usec - mlc->instart.tv_usec > mlc->intimeout) {
156                   /*              printk("!%i %i", 
157                                   tv.tv_usec - mlc->instart.tv_usec, 
158                                   mlc->intimeout);
159                   */
160                         rc = 1;
161                         up(&(mlc->isem));
162                 }
163                 goto done;
164         }
165  wasup:
166         up(&(mlc->isem));
167         rc = 0;
168         goto done;
169  done:
170         write_unlock_irqrestore(&(mlc->lock), flags);
171         return rc;
172 }
173
174 static int hp_sdc_mlc_cts (hil_mlc *mlc) {
175         struct hp_sdc_mlc_priv_s *priv;
176         unsigned long flags;
177
178         priv = mlc->priv;       
179
180         write_lock_irqsave(&(mlc->lock), flags);
181
182         /* Try to down the semaphores -- they should be up. */
183         if (down_trylock(&(mlc->isem))) {
184                 BUG();
185                 goto busy;
186         }
187         if (down_trylock(&(mlc->osem))) {
188                 BUG();
189                 up(&(mlc->isem));
190                 goto busy;
191         }
192         up(&(mlc->isem));
193         up(&(mlc->osem));
194
195         if (down_trylock(&(mlc->csem))) {
196                 if (priv->trans.act.semaphore != &(mlc->csem)) goto poll;
197                 goto busy;
198         }
199         if (!(priv->tseq[4] & HP_SDC_USE_LOOP)) goto done;
200
201  poll:
202         priv->trans.act.semaphore = &(mlc->csem);
203         priv->trans.actidx = 0;
204         priv->trans.idx = 1;
205         priv->trans.endidx = 5;
206         priv->tseq[0] = 
207                 HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE;
208         priv->tseq[1] = HP_SDC_CMD_READ_USE;
209         priv->tseq[2] = 1;
210         priv->tseq[3] = 0;
211         priv->tseq[4] = 0;
212         hp_sdc_enqueue_transaction(&(priv->trans));
213  busy:
214         write_unlock_irqrestore(&(mlc->lock), flags);
215         return 1;
216  done:
217         priv->trans.act.semaphore = &(mlc->osem);
218         up(&(mlc->csem));
219         write_unlock_irqrestore(&(mlc->lock), flags);
220         return 0;
221 }
222
223 static void hp_sdc_mlc_out (hil_mlc *mlc) {
224         struct hp_sdc_mlc_priv_s *priv;
225         unsigned long flags;
226
227         priv = mlc->priv;
228
229         write_lock_irqsave(&(mlc->lock), flags);
230         
231         /* Try to down the semaphore -- it should be up. */
232         if (down_trylock(&(mlc->osem))) {
233                 BUG();
234                 goto done;
235         }
236
237         if (mlc->opacket & HIL_DO_ALTER_CTRL) goto do_control;
238
239  do_data:
240         if (priv->emtestmode) {
241                 up(&(mlc->osem));
242                 goto done;
243         }
244         /* Shouldn't be sending commands when loop may be busy */
245         if (down_trylock(&(mlc->csem))) {
246                 BUG();
247                 goto done;
248         }
249         up(&(mlc->csem));
250
251         priv->trans.actidx = 0;
252         priv->trans.idx = 1;
253         priv->trans.act.semaphore = &(mlc->osem);
254         priv->trans.endidx = 6;
255         priv->tseq[0] = 
256                 HP_SDC_ACT_DATAREG | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_SEMAPHORE;
257         priv->tseq[1] = 0x7;
258         priv->tseq[2] = 
259                 (mlc->opacket & 
260                  (HIL_PKT_ADDR_MASK | HIL_PKT_CMD))
261                    >> HIL_PKT_ADDR_SHIFT;
262         priv->tseq[3] = 
263                 (mlc->opacket & HIL_PKT_DATA_MASK) 
264                   >> HIL_PKT_DATA_SHIFT;
265         priv->tseq[4] = 0;  /* No timeout */
266         if (priv->tseq[3] == HIL_CMD_DHR) priv->tseq[4] = 1;
267         priv->tseq[5] = HP_SDC_CMD_DO_HIL;
268         goto enqueue;
269
270  do_control:
271         priv->emtestmode = mlc->opacket & HIL_CTRL_TEST;
272         if ((mlc->opacket & (HIL_CTRL_APE | HIL_CTRL_IPF)) == HIL_CTRL_APE) {
273                 BUG(); /* we cannot emulate this, it should not be used. */
274         }
275         if ((mlc->opacket & HIL_CTRL_ONLY) == HIL_CTRL_ONLY) goto control_only;
276         if (mlc->opacket & HIL_CTRL_APE) { 
277                 BUG(); /* Should not send command/data after engaging APE */
278                 goto done;
279         }
280         /* Disengaging APE this way would not be valid either since 
281          * the loop must be allowed to idle.
282          *
283          * So, it works out that we really never actually send control 
284          * and data when using SDC, we just send the data. 
285          */
286         goto do_data;
287
288  control_only:
289         priv->trans.actidx = 0;
290         priv->trans.idx = 1;
291         priv->trans.act.semaphore = &(mlc->osem);
292         priv->trans.endidx = 4;
293         priv->tseq[0] = 
294           HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE;
295         priv->tseq[1] = HP_SDC_CMD_SET_LPC;
296         priv->tseq[2] = 1;
297         //      priv->tseq[3] = (mlc->ddc + 1) | HP_SDC_LPS_ACSUCC;
298         priv->tseq[3] = 0;
299         if (mlc->opacket & HIL_CTRL_APE) {
300                 priv->tseq[3] |= HP_SDC_LPC_APE_IPF;
301                 down_trylock(&(mlc->csem));
302         } 
303  enqueue:
304         hp_sdc_enqueue_transaction(&(priv->trans));
305  done:
306         write_unlock_irqrestore(&(mlc->lock), flags);
307 }
308
309 static int __init hp_sdc_mlc_init(void)
310 {
311         hil_mlc *mlc = &hp_sdc_mlc;
312
313         printk(KERN_INFO PREFIX "Registering the System Domain Controller's HIL MLC.\n");
314
315         hp_sdc_mlc_priv.emtestmode = 0;
316         hp_sdc_mlc_priv.trans.seq = hp_sdc_mlc_priv.tseq;
317         hp_sdc_mlc_priv.trans.act.semaphore = &(mlc->osem);
318         hp_sdc_mlc_priv.got5x = 0;
319
320         mlc->cts                = &hp_sdc_mlc_cts;
321         mlc->in                 = &hp_sdc_mlc_in;
322         mlc->out                = &hp_sdc_mlc_out;
323
324         if (hil_mlc_register(mlc)) {
325                 printk(KERN_WARNING PREFIX "Failed to register MLC structure with hil_mlc\n");
326                 goto err0;
327         }
328         mlc->priv               = &hp_sdc_mlc_priv;
329
330         if (hp_sdc_request_hil_irq(&hp_sdc_mlc_isr)) {
331                 printk(KERN_WARNING PREFIX "Request for raw HIL ISR hook denied\n");
332                 goto err1;
333         }
334         return 0;
335  err1:
336         if (hil_mlc_unregister(mlc)) {
337                 printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n"
338                         "This is bad.  Could cause an oops.\n");
339         }
340  err0:
341         return -EBUSY;
342 }
343
344 static void __exit hp_sdc_mlc_exit(void)
345 {
346         hil_mlc *mlc = &hp_sdc_mlc;
347         if (hp_sdc_release_hil_irq(&hp_sdc_mlc_isr)) {
348                 printk(KERN_ERR PREFIX "Failed to release the raw HIL ISR hook.\n"
349                         "This is bad.  Could cause an oops.\n");
350         }
351         if (hil_mlc_unregister(mlc)) {
352                 printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n"
353                         "This is bad.  Could cause an oops.\n");
354         }
355 }
356
357 module_init(hp_sdc_mlc_init);
358 module_exit(hp_sdc_mlc_exit);