diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/input/serio/hp_sdc_mlc.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/input/serio/hp_sdc_mlc.c')
-rw-r--r-- | drivers/input/serio/hp_sdc_mlc.c | 358 |
1 files changed, 358 insertions, 0 deletions
diff --git a/drivers/input/serio/hp_sdc_mlc.c b/drivers/input/serio/hp_sdc_mlc.c new file mode 100644 index 000000000000..e3c44ffae674 --- /dev/null +++ b/drivers/input/serio/hp_sdc_mlc.c | |||
@@ -0,0 +1,358 @@ | |||
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); | ||