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/parport/daisy.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/parport/daisy.c')
-rw-r--r-- | drivers/parport/daisy.c | 512 |
1 files changed, 512 insertions, 0 deletions
diff --git a/drivers/parport/daisy.c b/drivers/parport/daisy.c new file mode 100644 index 000000000000..075c7eb5c85d --- /dev/null +++ b/drivers/parport/daisy.c | |||
@@ -0,0 +1,512 @@ | |||
1 | /* | ||
2 | * IEEE 1284.3 Parallel port daisy chain and multiplexor code | ||
3 | * | ||
4 | * Copyright (C) 1999, 2000 Tim Waugh <tim@cyberelk.demon.co.uk> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | * | ||
11 | * ??-12-1998: Initial implementation. | ||
12 | * 31-01-1999: Make port-cloning transparent. | ||
13 | * 13-02-1999: Move DeviceID technique from parport_probe. | ||
14 | * 13-03-1999: Get DeviceID from non-IEEE 1284.3 devices too. | ||
15 | * 22-02-2000: Count devices that are actually detected. | ||
16 | * | ||
17 | * Any part of this program may be used in documents licensed under | ||
18 | * the GNU Free Documentation License, Version 1.1 or any later version | ||
19 | * published by the Free Software Foundation. | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/parport.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/sched.h> | ||
26 | |||
27 | #include <asm/current.h> | ||
28 | #include <asm/uaccess.h> | ||
29 | |||
30 | #undef DEBUG | ||
31 | |||
32 | #ifdef DEBUG | ||
33 | #define DPRINTK(stuff...) printk (stuff) | ||
34 | #else | ||
35 | #define DPRINTK(stuff...) | ||
36 | #endif | ||
37 | |||
38 | static struct daisydev { | ||
39 | struct daisydev *next; | ||
40 | struct parport *port; | ||
41 | int daisy; | ||
42 | int devnum; | ||
43 | } *topology = NULL; | ||
44 | static DEFINE_SPINLOCK(topology_lock); | ||
45 | |||
46 | static int numdevs = 0; | ||
47 | |||
48 | /* Forward-declaration of lower-level functions. */ | ||
49 | static int mux_present (struct parport *port); | ||
50 | static int num_mux_ports (struct parport *port); | ||
51 | static int select_port (struct parport *port); | ||
52 | static int assign_addrs (struct parport *port); | ||
53 | |||
54 | /* Add a device to the discovered topology. */ | ||
55 | static void add_dev (int devnum, struct parport *port, int daisy) | ||
56 | { | ||
57 | struct daisydev *newdev, **p; | ||
58 | newdev = kmalloc (sizeof (struct daisydev), GFP_KERNEL); | ||
59 | if (newdev) { | ||
60 | newdev->port = port; | ||
61 | newdev->daisy = daisy; | ||
62 | newdev->devnum = devnum; | ||
63 | spin_lock(&topology_lock); | ||
64 | for (p = &topology; *p && (*p)->devnum<devnum; p = &(*p)->next) | ||
65 | ; | ||
66 | newdev->next = *p; | ||
67 | *p = newdev; | ||
68 | spin_unlock(&topology_lock); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | /* Clone a parport (actually, make an alias). */ | ||
73 | static struct parport *clone_parport (struct parport *real, int muxport) | ||
74 | { | ||
75 | struct parport *extra = parport_register_port (real->base, | ||
76 | real->irq, | ||
77 | real->dma, | ||
78 | real->ops); | ||
79 | if (extra) { | ||
80 | extra->portnum = real->portnum; | ||
81 | extra->physport = real; | ||
82 | extra->muxport = muxport; | ||
83 | real->slaves[muxport-1] = extra; | ||
84 | } | ||
85 | |||
86 | return extra; | ||
87 | } | ||
88 | |||
89 | /* Discover the IEEE1284.3 topology on a port -- muxes and daisy chains. | ||
90 | * Return value is number of devices actually detected. */ | ||
91 | int parport_daisy_init (struct parport *port) | ||
92 | { | ||
93 | int detected = 0; | ||
94 | char *deviceid; | ||
95 | static const char *th[] = { /*0*/"th", "st", "nd", "rd", "th" }; | ||
96 | int num_ports; | ||
97 | int i; | ||
98 | int last_try = 0; | ||
99 | |||
100 | again: | ||
101 | /* Because this is called before any other devices exist, | ||
102 | * we don't have to claim exclusive access. */ | ||
103 | |||
104 | /* If mux present on normal port, need to create new | ||
105 | * parports for each extra port. */ | ||
106 | if (port->muxport < 0 && mux_present (port) && | ||
107 | /* don't be fooled: a mux must have 2 or 4 ports. */ | ||
108 | ((num_ports = num_mux_ports (port)) == 2 || num_ports == 4)) { | ||
109 | /* Leave original as port zero. */ | ||
110 | port->muxport = 0; | ||
111 | printk (KERN_INFO | ||
112 | "%s: 1st (default) port of %d-way multiplexor\n", | ||
113 | port->name, num_ports); | ||
114 | for (i = 1; i < num_ports; i++) { | ||
115 | /* Clone the port. */ | ||
116 | struct parport *extra = clone_parport (port, i); | ||
117 | if (!extra) { | ||
118 | if (signal_pending (current)) | ||
119 | break; | ||
120 | |||
121 | schedule (); | ||
122 | continue; | ||
123 | } | ||
124 | |||
125 | printk (KERN_INFO | ||
126 | "%s: %d%s port of %d-way multiplexor on %s\n", | ||
127 | extra->name, i + 1, th[i + 1], num_ports, | ||
128 | port->name); | ||
129 | |||
130 | /* Analyse that port too. We won't recurse | ||
131 | forever because of the 'port->muxport < 0' | ||
132 | test above. */ | ||
133 | parport_daisy_init(extra); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | if (port->muxport >= 0) | ||
138 | select_port (port); | ||
139 | |||
140 | parport_daisy_deselect_all (port); | ||
141 | detected += assign_addrs (port); | ||
142 | |||
143 | /* Count the potential legacy device at the end. */ | ||
144 | add_dev (numdevs++, port, -1); | ||
145 | |||
146 | /* Find out the legacy device's IEEE 1284 device ID. */ | ||
147 | deviceid = kmalloc (1000, GFP_KERNEL); | ||
148 | if (deviceid) { | ||
149 | if (parport_device_id (numdevs - 1, deviceid, 1000) > 2) | ||
150 | detected++; | ||
151 | |||
152 | kfree (deviceid); | ||
153 | } | ||
154 | |||
155 | if (!detected && !last_try) { | ||
156 | /* No devices were detected. Perhaps they are in some | ||
157 | funny state; let's try to reset them and see if | ||
158 | they wake up. */ | ||
159 | parport_daisy_fini (port); | ||
160 | parport_write_control (port, PARPORT_CONTROL_SELECT); | ||
161 | udelay (50); | ||
162 | parport_write_control (port, | ||
163 | PARPORT_CONTROL_SELECT | | ||
164 | PARPORT_CONTROL_INIT); | ||
165 | udelay (50); | ||
166 | last_try = 1; | ||
167 | goto again; | ||
168 | } | ||
169 | |||
170 | return detected; | ||
171 | } | ||
172 | |||
173 | /* Forget about devices on a physical port. */ | ||
174 | void parport_daisy_fini (struct parport *port) | ||
175 | { | ||
176 | struct daisydev **p; | ||
177 | |||
178 | spin_lock(&topology_lock); | ||
179 | p = &topology; | ||
180 | while (*p) { | ||
181 | struct daisydev *dev = *p; | ||
182 | if (dev->port != port) { | ||
183 | p = &dev->next; | ||
184 | continue; | ||
185 | } | ||
186 | *p = dev->next; | ||
187 | kfree(dev); | ||
188 | } | ||
189 | |||
190 | /* Gaps in the numbering could be handled better. How should | ||
191 | someone enumerate through all IEEE1284.3 devices in the | ||
192 | topology?. */ | ||
193 | if (!topology) numdevs = 0; | ||
194 | spin_unlock(&topology_lock); | ||
195 | return; | ||
196 | } | ||
197 | |||
198 | /** | ||
199 | * parport_open - find a device by canonical device number | ||
200 | * @devnum: canonical device number | ||
201 | * @name: name to associate with the device | ||
202 | * @pf: preemption callback | ||
203 | * @kf: kick callback | ||
204 | * @irqf: interrupt handler | ||
205 | * @flags: registration flags | ||
206 | * @handle: driver data | ||
207 | * | ||
208 | * This function is similar to parport_register_device(), except | ||
209 | * that it locates a device by its number rather than by the port | ||
210 | * it is attached to. | ||
211 | * | ||
212 | * All parameters except for @devnum are the same as for | ||
213 | * parport_register_device(). The return value is the same as | ||
214 | * for parport_register_device(). | ||
215 | **/ | ||
216 | |||
217 | struct pardevice *parport_open (int devnum, const char *name, | ||
218 | int (*pf) (void *), void (*kf) (void *), | ||
219 | void (*irqf) (int, void *, struct pt_regs *), | ||
220 | int flags, void *handle) | ||
221 | { | ||
222 | struct daisydev *p = topology; | ||
223 | struct parport *port; | ||
224 | struct pardevice *dev; | ||
225 | int daisy; | ||
226 | |||
227 | spin_lock(&topology_lock); | ||
228 | while (p && p->devnum != devnum) | ||
229 | p = p->next; | ||
230 | |||
231 | if (!p) { | ||
232 | spin_unlock(&topology_lock); | ||
233 | return NULL; | ||
234 | } | ||
235 | |||
236 | daisy = p->daisy; | ||
237 | port = parport_get_port(p->port); | ||
238 | spin_unlock(&topology_lock); | ||
239 | |||
240 | dev = parport_register_device (port, name, pf, kf, | ||
241 | irqf, flags, handle); | ||
242 | parport_put_port(port); | ||
243 | if (!dev) | ||
244 | return NULL; | ||
245 | |||
246 | dev->daisy = daisy; | ||
247 | |||
248 | /* Check that there really is a device to select. */ | ||
249 | if (daisy >= 0) { | ||
250 | int selected; | ||
251 | parport_claim_or_block (dev); | ||
252 | selected = port->daisy; | ||
253 | parport_release (dev); | ||
254 | |||
255 | if (selected != port->daisy) { | ||
256 | /* No corresponding device. */ | ||
257 | parport_unregister_device (dev); | ||
258 | return NULL; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | return dev; | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * parport_close - close a device opened with parport_open() | ||
267 | * @dev: device to close | ||
268 | * | ||
269 | * This is to parport_open() as parport_unregister_device() is to | ||
270 | * parport_register_device(). | ||
271 | **/ | ||
272 | |||
273 | void parport_close (struct pardevice *dev) | ||
274 | { | ||
275 | parport_unregister_device (dev); | ||
276 | } | ||
277 | |||
278 | /** | ||
279 | * parport_device_num - convert device coordinates | ||
280 | * @parport: parallel port number | ||
281 | * @mux: multiplexor port number (-1 for no multiplexor) | ||
282 | * @daisy: daisy chain address (-1 for no daisy chain address) | ||
283 | * | ||
284 | * This tries to locate a device on the given parallel port, | ||
285 | * multiplexor port and daisy chain address, and returns its | ||
286 | * device number or -NXIO if no device with those coordinates | ||
287 | * exists. | ||
288 | **/ | ||
289 | |||
290 | int parport_device_num (int parport, int mux, int daisy) | ||
291 | { | ||
292 | int res = -ENXIO; | ||
293 | struct daisydev *dev; | ||
294 | |||
295 | spin_lock(&topology_lock); | ||
296 | dev = topology; | ||
297 | while (dev && dev->port->portnum != parport && | ||
298 | dev->port->muxport != mux && dev->daisy != daisy) | ||
299 | dev = dev->next; | ||
300 | if (dev) | ||
301 | res = dev->devnum; | ||
302 | spin_unlock(&topology_lock); | ||
303 | |||
304 | return res; | ||
305 | } | ||
306 | |||
307 | /* Send a daisy-chain-style CPP command packet. */ | ||
308 | static int cpp_daisy (struct parport *port, int cmd) | ||
309 | { | ||
310 | unsigned char s; | ||
311 | |||
312 | parport_data_forward (port); | ||
313 | parport_write_data (port, 0xaa); udelay (2); | ||
314 | parport_write_data (port, 0x55); udelay (2); | ||
315 | parport_write_data (port, 0x00); udelay (2); | ||
316 | parport_write_data (port, 0xff); udelay (2); | ||
317 | s = parport_read_status (port) & (PARPORT_STATUS_BUSY | ||
318 | | PARPORT_STATUS_PAPEROUT | ||
319 | | PARPORT_STATUS_SELECT | ||
320 | | PARPORT_STATUS_ERROR); | ||
321 | if (s != (PARPORT_STATUS_BUSY | ||
322 | | PARPORT_STATUS_PAPEROUT | ||
323 | | PARPORT_STATUS_SELECT | ||
324 | | PARPORT_STATUS_ERROR)) { | ||
325 | DPRINTK (KERN_DEBUG "%s: cpp_daisy: aa5500ff(%02x)\n", | ||
326 | port->name, s); | ||
327 | return -ENXIO; | ||
328 | } | ||
329 | |||
330 | parport_write_data (port, 0x87); udelay (2); | ||
331 | s = parport_read_status (port) & (PARPORT_STATUS_BUSY | ||
332 | | PARPORT_STATUS_PAPEROUT | ||
333 | | PARPORT_STATUS_SELECT | ||
334 | | PARPORT_STATUS_ERROR); | ||
335 | if (s != (PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR)) { | ||
336 | DPRINTK (KERN_DEBUG "%s: cpp_daisy: aa5500ff87(%02x)\n", | ||
337 | port->name, s); | ||
338 | return -ENXIO; | ||
339 | } | ||
340 | |||
341 | parport_write_data (port, 0x78); udelay (2); | ||
342 | parport_write_data (port, cmd); udelay (2); | ||
343 | parport_frob_control (port, | ||
344 | PARPORT_CONTROL_STROBE, | ||
345 | PARPORT_CONTROL_STROBE); | ||
346 | udelay (1); | ||
347 | parport_frob_control (port, PARPORT_CONTROL_STROBE, 0); | ||
348 | udelay (1); | ||
349 | s = parport_read_status (port); | ||
350 | parport_write_data (port, 0xff); udelay (2); | ||
351 | |||
352 | return s; | ||
353 | } | ||
354 | |||
355 | /* Send a mux-style CPP command packet. */ | ||
356 | static int cpp_mux (struct parport *port, int cmd) | ||
357 | { | ||
358 | unsigned char s; | ||
359 | int rc; | ||
360 | |||
361 | parport_data_forward (port); | ||
362 | parport_write_data (port, 0xaa); udelay (2); | ||
363 | parport_write_data (port, 0x55); udelay (2); | ||
364 | parport_write_data (port, 0xf0); udelay (2); | ||
365 | parport_write_data (port, 0x0f); udelay (2); | ||
366 | parport_write_data (port, 0x52); udelay (2); | ||
367 | parport_write_data (port, 0xad); udelay (2); | ||
368 | parport_write_data (port, cmd); udelay (2); | ||
369 | |||
370 | s = parport_read_status (port); | ||
371 | if (!(s & PARPORT_STATUS_ACK)) { | ||
372 | DPRINTK (KERN_DEBUG "%s: cpp_mux: aa55f00f52ad%02x(%02x)\n", | ||
373 | port->name, cmd, s); | ||
374 | return -EIO; | ||
375 | } | ||
376 | |||
377 | rc = (((s & PARPORT_STATUS_SELECT ? 1 : 0) << 0) | | ||
378 | ((s & PARPORT_STATUS_PAPEROUT ? 1 : 0) << 1) | | ||
379 | ((s & PARPORT_STATUS_BUSY ? 0 : 1) << 2) | | ||
380 | ((s & PARPORT_STATUS_ERROR ? 0 : 1) << 3)); | ||
381 | |||
382 | return rc; | ||
383 | } | ||
384 | |||
385 | void parport_daisy_deselect_all (struct parport *port) | ||
386 | { | ||
387 | cpp_daisy (port, 0x30); | ||
388 | } | ||
389 | |||
390 | int parport_daisy_select (struct parport *port, int daisy, int mode) | ||
391 | { | ||
392 | switch (mode) | ||
393 | { | ||
394 | // For these modes we should switch to EPP mode: | ||
395 | case IEEE1284_MODE_EPP: | ||
396 | case IEEE1284_MODE_EPPSL: | ||
397 | case IEEE1284_MODE_EPPSWE: | ||
398 | return (cpp_daisy (port, 0x20 + daisy) & | ||
399 | PARPORT_STATUS_ERROR); | ||
400 | |||
401 | // For these modes we should switch to ECP mode: | ||
402 | case IEEE1284_MODE_ECP: | ||
403 | case IEEE1284_MODE_ECPRLE: | ||
404 | case IEEE1284_MODE_ECPSWE: | ||
405 | return (cpp_daisy (port, 0xd0 + daisy) & | ||
406 | PARPORT_STATUS_ERROR); | ||
407 | |||
408 | // Nothing was told for BECP in Daisy chain specification. | ||
409 | // May be it's wise to use ECP? | ||
410 | case IEEE1284_MODE_BECP: | ||
411 | // Others use compat mode | ||
412 | case IEEE1284_MODE_NIBBLE: | ||
413 | case IEEE1284_MODE_BYTE: | ||
414 | case IEEE1284_MODE_COMPAT: | ||
415 | default: | ||
416 | return (cpp_daisy (port, 0xe0 + daisy) & | ||
417 | PARPORT_STATUS_ERROR); | ||
418 | } | ||
419 | } | ||
420 | |||
421 | static int mux_present (struct parport *port) | ||
422 | { | ||
423 | return cpp_mux (port, 0x51) == 3; | ||
424 | } | ||
425 | |||
426 | static int num_mux_ports (struct parport *port) | ||
427 | { | ||
428 | return cpp_mux (port, 0x58); | ||
429 | } | ||
430 | |||
431 | static int select_port (struct parport *port) | ||
432 | { | ||
433 | int muxport = port->muxport; | ||
434 | return cpp_mux (port, 0x60 + muxport) == muxport; | ||
435 | } | ||
436 | |||
437 | static int assign_addrs (struct parport *port) | ||
438 | { | ||
439 | unsigned char s, last_dev; | ||
440 | unsigned char daisy; | ||
441 | int thisdev = numdevs; | ||
442 | int detected; | ||
443 | char *deviceid; | ||
444 | |||
445 | parport_data_forward (port); | ||
446 | parport_write_data (port, 0xaa); udelay (2); | ||
447 | parport_write_data (port, 0x55); udelay (2); | ||
448 | parport_write_data (port, 0x00); udelay (2); | ||
449 | parport_write_data (port, 0xff); udelay (2); | ||
450 | s = parport_read_status (port) & (PARPORT_STATUS_BUSY | ||
451 | | PARPORT_STATUS_PAPEROUT | ||
452 | | PARPORT_STATUS_SELECT | ||
453 | | PARPORT_STATUS_ERROR); | ||
454 | if (s != (PARPORT_STATUS_BUSY | ||
455 | | PARPORT_STATUS_PAPEROUT | ||
456 | | PARPORT_STATUS_SELECT | ||
457 | | PARPORT_STATUS_ERROR)) { | ||
458 | DPRINTK (KERN_DEBUG "%s: assign_addrs: aa5500ff(%02x)\n", | ||
459 | port->name, s); | ||
460 | return 0; | ||
461 | } | ||
462 | |||
463 | parport_write_data (port, 0x87); udelay (2); | ||
464 | s = parport_read_status (port) & (PARPORT_STATUS_BUSY | ||
465 | | PARPORT_STATUS_PAPEROUT | ||
466 | | PARPORT_STATUS_SELECT | ||
467 | | PARPORT_STATUS_ERROR); | ||
468 | if (s != (PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR)) { | ||
469 | DPRINTK (KERN_DEBUG "%s: assign_addrs: aa5500ff87(%02x)\n", | ||
470 | port->name, s); | ||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | parport_write_data (port, 0x78); udelay (2); | ||
475 | last_dev = 0; /* We've just been speaking to a device, so we | ||
476 | know there must be at least _one_ out there. */ | ||
477 | |||
478 | for (daisy = 0; daisy < 4; daisy++) { | ||
479 | parport_write_data (port, daisy); | ||
480 | udelay (2); | ||
481 | parport_frob_control (port, | ||
482 | PARPORT_CONTROL_STROBE, | ||
483 | PARPORT_CONTROL_STROBE); | ||
484 | udelay (1); | ||
485 | parport_frob_control (port, PARPORT_CONTROL_STROBE, 0); | ||
486 | udelay (1); | ||
487 | |||
488 | if (last_dev) | ||
489 | /* No more devices. */ | ||
490 | break; | ||
491 | |||
492 | last_dev = !(parport_read_status (port) | ||
493 | & PARPORT_STATUS_BUSY); | ||
494 | |||
495 | add_dev (numdevs++, port, daisy); | ||
496 | } | ||
497 | |||
498 | parport_write_data (port, 0xff); udelay (2); | ||
499 | detected = numdevs - thisdev; | ||
500 | DPRINTK (KERN_DEBUG "%s: Found %d daisy-chained devices\n", port->name, | ||
501 | detected); | ||
502 | |||
503 | /* Ask the new devices to introduce themselves. */ | ||
504 | deviceid = kmalloc (1000, GFP_KERNEL); | ||
505 | if (!deviceid) return 0; | ||
506 | |||
507 | for (daisy = 0; thisdev < numdevs; thisdev++, daisy++) | ||
508 | parport_device_id (thisdev, deviceid, 1000); | ||
509 | |||
510 | kfree (deviceid); | ||
511 | return detected; | ||
512 | } | ||