1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * ISA Plug & Play support |
4 | * Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
5 | * |
6 | * Changelog: |
7 | * 2000-01-01 Added quirks handling for buggy hardware |
8 | * Peter Denison <peterd@pnd-pc.demon.co.uk> |
9 | * 2000-06-14 Added isapnp_probe_devs() and isapnp_activate_dev() |
10 | * Christoph Hellwig <hch@infradead.org> |
11 | * 2001-06-03 Added release_region calls to correspond with |
12 | * request_region calls when a failure occurs. Also |
13 | * added KERN_* constants to printk() calls. |
14 | * 2001-11-07 Added isapnp_{,un}register_driver calls along the lines |
15 | * of the pci driver interface |
16 | * Kai Germaschewski <kai.germaschewski@gmx.de> |
17 | * 2002-06-06 Made the use of dma channel 0 configurable |
18 | * Gerald Teschl <gerald.teschl@univie.ac.at> |
19 | * 2002-10-06 Ported to PnP Layer - Adam Belay <ambx1@neo.rr.com> |
20 | * 2003-08-11 Resource Management Updates - Adam Belay <ambx1@neo.rr.com> |
21 | */ |
22 | |
23 | #include <linux/moduleparam.h> |
24 | #include <linux/kernel.h> |
25 | #include <linux/errno.h> |
26 | #include <linux/delay.h> |
27 | #include <linux/init.h> |
28 | #include <linux/isapnp.h> |
29 | #include <linux/mutex.h> |
30 | #include <asm/io.h> |
31 | |
32 | #include "../base.h" |
33 | |
34 | #if 0 |
35 | #define ISAPNP_REGION_OK |
36 | #endif |
37 | |
38 | int isapnp_disable; /* Disable ISA PnP */ |
39 | static int isapnp_rdp; /* Read Data Port */ |
40 | static int isapnp_reset = 1; /* reset all PnP cards (deactivate) */ |
41 | static int isapnp_verbose = 1; /* verbose mode */ |
42 | |
43 | module_param(isapnp_disable, int, 0); |
44 | MODULE_PARM_DESC(isapnp_disable, "ISA Plug & Play disable" ); |
45 | module_param(isapnp_rdp, int, 0); |
46 | MODULE_PARM_DESC(isapnp_rdp, "ISA Plug & Play read data port" ); |
47 | module_param(isapnp_reset, int, 0); |
48 | MODULE_PARM_DESC(isapnp_reset, "ISA Plug & Play reset all cards" ); |
49 | module_param(isapnp_verbose, int, 0); |
50 | MODULE_PARM_DESC(isapnp_verbose, "ISA Plug & Play verbose mode" ); |
51 | |
52 | #define _PIDXR 0x279 |
53 | #define _PNPWRP 0xa79 |
54 | |
55 | /* short tags */ |
56 | #define _STAG_PNPVERNO 0x01 |
57 | #define _STAG_LOGDEVID 0x02 |
58 | #define _STAG_COMPATDEVID 0x03 |
59 | #define _STAG_IRQ 0x04 |
60 | #define _STAG_DMA 0x05 |
61 | #define _STAG_STARTDEP 0x06 |
62 | #define _STAG_ENDDEP 0x07 |
63 | #define _STAG_IOPORT 0x08 |
64 | #define _STAG_FIXEDIO 0x09 |
65 | #define _STAG_VENDOR 0x0e |
66 | #define _STAG_END 0x0f |
67 | /* long tags */ |
68 | #define _LTAG_MEMRANGE 0x81 |
69 | #define _LTAG_ANSISTR 0x82 |
70 | #define _LTAG_UNICODESTR 0x83 |
71 | #define _LTAG_VENDOR 0x84 |
72 | #define _LTAG_MEM32RANGE 0x85 |
73 | #define _LTAG_FIXEDMEM32RANGE 0x86 |
74 | |
75 | /* Logical device control and configuration registers */ |
76 | |
77 | #define ISAPNP_CFG_ACTIVATE 0x30 /* byte */ |
78 | #define ISAPNP_CFG_MEM 0x40 /* 4 * dword */ |
79 | #define ISAPNP_CFG_PORT 0x60 /* 8 * word */ |
80 | #define ISAPNP_CFG_IRQ 0x70 /* 2 * word */ |
81 | #define ISAPNP_CFG_DMA 0x74 /* 2 * byte */ |
82 | |
83 | /* |
84 | * Sizes of ISAPNP logical device configuration register sets. |
85 | * See PNP-ISA-v1.0a.pdf, Appendix A. |
86 | */ |
87 | #define ISAPNP_MAX_MEM 4 |
88 | #define ISAPNP_MAX_PORT 8 |
89 | #define ISAPNP_MAX_IRQ 2 |
90 | #define ISAPNP_MAX_DMA 2 |
91 | |
92 | static unsigned char isapnp_checksum_value; |
93 | static DEFINE_MUTEX(isapnp_cfg_mutex); |
94 | static int isapnp_csn_count; |
95 | |
96 | /* some prototypes */ |
97 | |
98 | static inline void write_data(unsigned char x) |
99 | { |
100 | outb(value: x, _PNPWRP); |
101 | } |
102 | |
103 | static inline void write_address(unsigned char x) |
104 | { |
105 | outb(value: x, _PIDXR); |
106 | udelay(20); |
107 | } |
108 | |
109 | static inline unsigned char read_data(void) |
110 | { |
111 | unsigned char val = inb(port: isapnp_rdp); |
112 | return val; |
113 | } |
114 | |
115 | unsigned char isapnp_read_byte(unsigned char idx) |
116 | { |
117 | write_address(x: idx); |
118 | return read_data(); |
119 | } |
120 | |
121 | static unsigned short isapnp_read_word(unsigned char idx) |
122 | { |
123 | unsigned short val; |
124 | |
125 | val = isapnp_read_byte(idx); |
126 | val = (val << 8) + isapnp_read_byte(idx: idx + 1); |
127 | return val; |
128 | } |
129 | |
130 | void isapnp_write_byte(unsigned char idx, unsigned char val) |
131 | { |
132 | write_address(x: idx); |
133 | write_data(x: val); |
134 | } |
135 | |
136 | static void isapnp_write_word(unsigned char idx, unsigned short val) |
137 | { |
138 | isapnp_write_byte(idx, val: val >> 8); |
139 | isapnp_write_byte(idx: idx + 1, val); |
140 | } |
141 | |
142 | static void isapnp_key(void) |
143 | { |
144 | unsigned char code = 0x6a, msb; |
145 | int i; |
146 | |
147 | mdelay(1); |
148 | write_address(x: 0x00); |
149 | write_address(x: 0x00); |
150 | |
151 | write_address(x: code); |
152 | |
153 | for (i = 1; i < 32; i++) { |
154 | msb = ((code & 0x01) ^ ((code & 0x02) >> 1)) << 7; |
155 | code = (code >> 1) | msb; |
156 | write_address(x: code); |
157 | } |
158 | } |
159 | |
160 | /* place all pnp cards in wait-for-key state */ |
161 | static void isapnp_wait(void) |
162 | { |
163 | isapnp_write_byte(idx: 0x02, val: 0x02); |
164 | } |
165 | |
166 | static void isapnp_wake(unsigned char csn) |
167 | { |
168 | isapnp_write_byte(idx: 0x03, val: csn); |
169 | } |
170 | |
171 | static void isapnp_device(unsigned char logdev) |
172 | { |
173 | isapnp_write_byte(idx: 0x07, val: logdev); |
174 | } |
175 | |
176 | static void isapnp_activate(unsigned char logdev) |
177 | { |
178 | isapnp_device(logdev); |
179 | isapnp_write_byte(ISAPNP_CFG_ACTIVATE, val: 1); |
180 | udelay(250); |
181 | } |
182 | |
183 | static void isapnp_deactivate(unsigned char logdev) |
184 | { |
185 | isapnp_device(logdev); |
186 | isapnp_write_byte(ISAPNP_CFG_ACTIVATE, val: 0); |
187 | udelay(500); |
188 | } |
189 | |
190 | static void __init isapnp_peek(unsigned char *data, int bytes) |
191 | { |
192 | int i, j; |
193 | unsigned char d = 0; |
194 | |
195 | for (i = 1; i <= bytes; i++) { |
196 | for (j = 0; j < 20; j++) { |
197 | d = isapnp_read_byte(idx: 0x05); |
198 | if (d & 1) |
199 | break; |
200 | udelay(100); |
201 | } |
202 | if (!(d & 1)) { |
203 | if (data != NULL) |
204 | *data++ = 0xff; |
205 | continue; |
206 | } |
207 | d = isapnp_read_byte(idx: 0x04); /* PRESDI */ |
208 | isapnp_checksum_value += d; |
209 | if (data != NULL) |
210 | *data++ = d; |
211 | } |
212 | } |
213 | |
214 | #define RDP_STEP 32 /* minimum is 4 */ |
215 | |
216 | static int isapnp_next_rdp(void) |
217 | { |
218 | int rdp = isapnp_rdp; |
219 | static int old_rdp = 0; |
220 | |
221 | if (old_rdp) { |
222 | release_region(old_rdp, 1); |
223 | old_rdp = 0; |
224 | } |
225 | while (rdp <= 0x3ff) { |
226 | /* |
227 | * We cannot use NE2000 probe spaces for ISAPnP or we |
228 | * will lock up machines. |
229 | */ |
230 | if ((rdp < 0x280 || rdp > 0x380) |
231 | && request_region(rdp, 1, "ISAPnP" )) { |
232 | isapnp_rdp = rdp; |
233 | old_rdp = rdp; |
234 | return 0; |
235 | } |
236 | rdp += RDP_STEP; |
237 | } |
238 | return -1; |
239 | } |
240 | |
241 | /* Set read port address */ |
242 | static inline void isapnp_set_rdp(void) |
243 | { |
244 | isapnp_write_byte(idx: 0x00, val: isapnp_rdp >> 2); |
245 | udelay(100); |
246 | } |
247 | |
248 | /* |
249 | * Perform an isolation. The port selection code now tries to avoid |
250 | * "dangerous to read" ports. |
251 | */ |
252 | static int __init isapnp_isolate_rdp_select(void) |
253 | { |
254 | isapnp_wait(); |
255 | isapnp_key(); |
256 | |
257 | /* Control: reset CSN and conditionally everything else too */ |
258 | isapnp_write_byte(idx: 0x02, val: isapnp_reset ? 0x05 : 0x04); |
259 | mdelay(2); |
260 | |
261 | isapnp_wait(); |
262 | isapnp_key(); |
263 | isapnp_wake(csn: 0x00); |
264 | |
265 | if (isapnp_next_rdp() < 0) { |
266 | isapnp_wait(); |
267 | return -1; |
268 | } |
269 | |
270 | isapnp_set_rdp(); |
271 | udelay(1000); |
272 | write_address(x: 0x01); |
273 | udelay(1000); |
274 | return 0; |
275 | } |
276 | |
277 | /* |
278 | * Isolate (assign uniqued CSN) to all ISA PnP devices. |
279 | */ |
280 | static int __init isapnp_isolate(void) |
281 | { |
282 | unsigned char checksum = 0x6a; |
283 | unsigned char chksum = 0x00; |
284 | unsigned char bit = 0x00; |
285 | int data; |
286 | int csn = 0; |
287 | int i; |
288 | int iteration = 1; |
289 | |
290 | isapnp_rdp = 0x213; |
291 | if (isapnp_isolate_rdp_select() < 0) |
292 | return -1; |
293 | |
294 | while (1) { |
295 | for (i = 1; i <= 64; i++) { |
296 | data = read_data() << 8; |
297 | udelay(250); |
298 | data = data | read_data(); |
299 | udelay(250); |
300 | if (data == 0x55aa) |
301 | bit = 0x01; |
302 | checksum = |
303 | ((((checksum ^ (checksum >> 1)) & 0x01) ^ bit) << 7) |
304 | | (checksum >> 1); |
305 | bit = 0x00; |
306 | } |
307 | for (i = 65; i <= 72; i++) { |
308 | data = read_data() << 8; |
309 | udelay(250); |
310 | data = data | read_data(); |
311 | udelay(250); |
312 | if (data == 0x55aa) |
313 | chksum |= (1 << (i - 65)); |
314 | } |
315 | if (checksum != 0x00 && checksum == chksum) { |
316 | csn++; |
317 | |
318 | isapnp_write_byte(idx: 0x06, val: csn); |
319 | udelay(250); |
320 | iteration++; |
321 | isapnp_wake(csn: 0x00); |
322 | isapnp_set_rdp(); |
323 | udelay(1000); |
324 | write_address(x: 0x01); |
325 | udelay(1000); |
326 | goto __next; |
327 | } |
328 | if (iteration == 1) { |
329 | isapnp_rdp += RDP_STEP; |
330 | if (isapnp_isolate_rdp_select() < 0) |
331 | return -1; |
332 | } else if (iteration > 1) { |
333 | break; |
334 | } |
335 | __next: |
336 | if (csn == 255) |
337 | break; |
338 | checksum = 0x6a; |
339 | chksum = 0x00; |
340 | bit = 0x00; |
341 | } |
342 | isapnp_wait(); |
343 | isapnp_csn_count = csn; |
344 | return csn; |
345 | } |
346 | |
347 | /* |
348 | * Read one tag from stream. |
349 | */ |
350 | static int __init isapnp_read_tag(unsigned char *type, unsigned short *size) |
351 | { |
352 | unsigned char tag, tmp[2]; |
353 | |
354 | isapnp_peek(data: &tag, bytes: 1); |
355 | if (tag == 0) /* invalid tag */ |
356 | return -1; |
357 | if (tag & 0x80) { /* large item */ |
358 | *type = tag; |
359 | isapnp_peek(data: tmp, bytes: 2); |
360 | *size = (tmp[1] << 8) | tmp[0]; |
361 | } else { |
362 | *type = (tag >> 3) & 0x0f; |
363 | *size = tag & 0x07; |
364 | } |
365 | if (*type == 0xff && *size == 0xffff) /* probably invalid data */ |
366 | return -1; |
367 | return 0; |
368 | } |
369 | |
370 | /* |
371 | * Skip specified number of bytes from stream. |
372 | */ |
373 | static void __init isapnp_skip_bytes(int count) |
374 | { |
375 | isapnp_peek(NULL, bytes: count); |
376 | } |
377 | |
378 | /* |
379 | * Parse logical device tag. |
380 | */ |
381 | static struct pnp_dev *__init isapnp_parse_device(struct pnp_card *card, |
382 | int size, int number) |
383 | { |
384 | unsigned char tmp[6]; |
385 | struct pnp_dev *dev; |
386 | u32 eisa_id; |
387 | char id[8]; |
388 | |
389 | isapnp_peek(data: tmp, bytes: size); |
390 | eisa_id = tmp[0] | tmp[1] << 8 | tmp[2] << 16 | tmp[3] << 24; |
391 | pnp_eisa_id_to_string(id: eisa_id, str: id); |
392 | |
393 | dev = pnp_alloc_dev(&isapnp_protocol, id: number, pnpid: id); |
394 | if (!dev) |
395 | return NULL; |
396 | |
397 | dev->card = card; |
398 | dev->capabilities |= PNP_CONFIGURABLE; |
399 | dev->capabilities |= PNP_READ; |
400 | dev->capabilities |= PNP_WRITE; |
401 | dev->capabilities |= PNP_DISABLE; |
402 | pnp_init_resources(dev); |
403 | return dev; |
404 | } |
405 | |
406 | /* |
407 | * Add IRQ resource to resources list. |
408 | */ |
409 | static void __init isapnp_parse_irq_resource(struct pnp_dev *dev, |
410 | unsigned int option_flags, |
411 | int size) |
412 | { |
413 | unsigned char tmp[3]; |
414 | unsigned long bits; |
415 | pnp_irq_mask_t map; |
416 | unsigned char flags = IORESOURCE_IRQ_HIGHEDGE; |
417 | |
418 | isapnp_peek(data: tmp, bytes: size); |
419 | bits = (tmp[1] << 8) | tmp[0]; |
420 | |
421 | bitmap_zero(dst: map.bits, PNP_IRQ_NR); |
422 | bitmap_copy(dst: map.bits, src: &bits, nbits: 16); |
423 | |
424 | if (size > 2) |
425 | flags = tmp[2]; |
426 | |
427 | pnp_register_irq_resource(dev, option_flags, map: &map, flags); |
428 | } |
429 | |
430 | /* |
431 | * Add DMA resource to resources list. |
432 | */ |
433 | static void __init isapnp_parse_dma_resource(struct pnp_dev *dev, |
434 | unsigned int option_flags, |
435 | int size) |
436 | { |
437 | unsigned char tmp[2]; |
438 | |
439 | isapnp_peek(data: tmp, bytes: size); |
440 | pnp_register_dma_resource(dev, option_flags, map: tmp[0], flags: tmp[1]); |
441 | } |
442 | |
443 | /* |
444 | * Add port resource to resources list. |
445 | */ |
446 | static void __init isapnp_parse_port_resource(struct pnp_dev *dev, |
447 | unsigned int option_flags, |
448 | int size) |
449 | { |
450 | unsigned char tmp[7]; |
451 | resource_size_t min, max, align, len; |
452 | unsigned char flags; |
453 | |
454 | isapnp_peek(data: tmp, bytes: size); |
455 | min = (tmp[2] << 8) | tmp[1]; |
456 | max = (tmp[4] << 8) | tmp[3]; |
457 | align = tmp[5]; |
458 | len = tmp[6]; |
459 | flags = tmp[0] ? IORESOURCE_IO_16BIT_ADDR : 0; |
460 | pnp_register_port_resource(dev, option_flags, |
461 | min, max, align, size: len, flags); |
462 | } |
463 | |
464 | /* |
465 | * Add fixed port resource to resources list. |
466 | */ |
467 | static void __init isapnp_parse_fixed_port_resource(struct pnp_dev *dev, |
468 | unsigned int option_flags, |
469 | int size) |
470 | { |
471 | unsigned char tmp[3]; |
472 | resource_size_t base, len; |
473 | |
474 | isapnp_peek(data: tmp, bytes: size); |
475 | base = (tmp[1] << 8) | tmp[0]; |
476 | len = tmp[2]; |
477 | pnp_register_port_resource(dev, option_flags, min: base, max: base, align: 0, size: len, |
478 | IORESOURCE_IO_FIXED); |
479 | } |
480 | |
481 | /* |
482 | * Add memory resource to resources list. |
483 | */ |
484 | static void __init isapnp_parse_mem_resource(struct pnp_dev *dev, |
485 | unsigned int option_flags, |
486 | int size) |
487 | { |
488 | unsigned char tmp[9]; |
489 | resource_size_t min, max, align, len; |
490 | unsigned char flags; |
491 | |
492 | isapnp_peek(data: tmp, bytes: size); |
493 | min = ((tmp[2] << 8) | tmp[1]) << 8; |
494 | max = ((tmp[4] << 8) | tmp[3]) << 8; |
495 | align = (tmp[6] << 8) | tmp[5]; |
496 | len = ((tmp[8] << 8) | tmp[7]) << 8; |
497 | flags = tmp[0]; |
498 | pnp_register_mem_resource(dev, option_flags, |
499 | min, max, align, size: len, flags); |
500 | } |
501 | |
502 | /* |
503 | * Add 32-bit memory resource to resources list. |
504 | */ |
505 | static void __init isapnp_parse_mem32_resource(struct pnp_dev *dev, |
506 | unsigned int option_flags, |
507 | int size) |
508 | { |
509 | unsigned char tmp[17]; |
510 | resource_size_t min, max, align, len; |
511 | unsigned char flags; |
512 | |
513 | isapnp_peek(data: tmp, bytes: size); |
514 | min = (tmp[4] << 24) | (tmp[3] << 16) | (tmp[2] << 8) | tmp[1]; |
515 | max = (tmp[8] << 24) | (tmp[7] << 16) | (tmp[6] << 8) | tmp[5]; |
516 | align = (tmp[12] << 24) | (tmp[11] << 16) | (tmp[10] << 8) | tmp[9]; |
517 | len = (tmp[16] << 24) | (tmp[15] << 16) | (tmp[14] << 8) | tmp[13]; |
518 | flags = tmp[0]; |
519 | pnp_register_mem_resource(dev, option_flags, |
520 | min, max, align, size: len, flags); |
521 | } |
522 | |
523 | /* |
524 | * Add 32-bit fixed memory resource to resources list. |
525 | */ |
526 | static void __init isapnp_parse_fixed_mem32_resource(struct pnp_dev *dev, |
527 | unsigned int option_flags, |
528 | int size) |
529 | { |
530 | unsigned char tmp[9]; |
531 | resource_size_t base, len; |
532 | unsigned char flags; |
533 | |
534 | isapnp_peek(data: tmp, bytes: size); |
535 | base = (tmp[4] << 24) | (tmp[3] << 16) | (tmp[2] << 8) | tmp[1]; |
536 | len = (tmp[8] << 24) | (tmp[7] << 16) | (tmp[6] << 8) | tmp[5]; |
537 | flags = tmp[0]; |
538 | pnp_register_mem_resource(dev, option_flags, min: base, max: base, align: 0, size: len, flags); |
539 | } |
540 | |
541 | /* |
542 | * Parse card name for ISA PnP device. |
543 | */ |
544 | static void __init |
545 | isapnp_parse_name(char *name, unsigned int name_max, unsigned short *size) |
546 | { |
547 | if (name[0] == '\0') { |
548 | unsigned short size1 = |
549 | *size >= name_max ? (name_max - 1) : *size; |
550 | isapnp_peek(data: name, bytes: size1); |
551 | name[size1] = '\0'; |
552 | *size -= size1; |
553 | |
554 | /* clean whitespace from end of string */ |
555 | while (size1 > 0 && name[--size1] == ' ') |
556 | name[size1] = '\0'; |
557 | } |
558 | } |
559 | |
560 | /* |
561 | * Parse resource map for logical device. |
562 | */ |
563 | static int __init isapnp_create_device(struct pnp_card *card, |
564 | unsigned short size) |
565 | { |
566 | int number = 0, skip = 0, priority, compat = 0; |
567 | unsigned char type, tmp[17]; |
568 | unsigned int option_flags; |
569 | struct pnp_dev *dev; |
570 | u32 eisa_id; |
571 | char id[8]; |
572 | |
573 | if ((dev = isapnp_parse_device(card, size, number: number++)) == NULL) |
574 | return 1; |
575 | option_flags = 0; |
576 | pnp_add_card_device(card, dev); |
577 | |
578 | while (1) { |
579 | if (isapnp_read_tag(type: &type, size: &size) < 0) |
580 | return 1; |
581 | if (skip && type != _STAG_LOGDEVID && type != _STAG_END) |
582 | goto __skip; |
583 | switch (type) { |
584 | case _STAG_LOGDEVID: |
585 | if (size >= 5 && size <= 6) { |
586 | if ((dev = |
587 | isapnp_parse_device(card, size, |
588 | number: number++)) == NULL) |
589 | return 1; |
590 | size = 0; |
591 | skip = 0; |
592 | option_flags = 0; |
593 | pnp_add_card_device(card, dev); |
594 | } else { |
595 | skip = 1; |
596 | } |
597 | compat = 0; |
598 | break; |
599 | case _STAG_COMPATDEVID: |
600 | if (size == 4 && compat < DEVICE_COUNT_COMPATIBLE) { |
601 | isapnp_peek(data: tmp, bytes: 4); |
602 | eisa_id = tmp[0] | tmp[1] << 8 | |
603 | tmp[2] << 16 | tmp[3] << 24; |
604 | pnp_eisa_id_to_string(id: eisa_id, str: id); |
605 | pnp_add_id(dev, id); |
606 | compat++; |
607 | size = 0; |
608 | } |
609 | break; |
610 | case _STAG_IRQ: |
611 | if (size < 2 || size > 3) |
612 | goto __skip; |
613 | isapnp_parse_irq_resource(dev, option_flags, size); |
614 | size = 0; |
615 | break; |
616 | case _STAG_DMA: |
617 | if (size != 2) |
618 | goto __skip; |
619 | isapnp_parse_dma_resource(dev, option_flags, size); |
620 | size = 0; |
621 | break; |
622 | case _STAG_STARTDEP: |
623 | if (size > 1) |
624 | goto __skip; |
625 | priority = PNP_RES_PRIORITY_ACCEPTABLE; |
626 | if (size > 0) { |
627 | isapnp_peek(data: tmp, bytes: size); |
628 | priority = tmp[0]; |
629 | size = 0; |
630 | } |
631 | option_flags = pnp_new_dependent_set(dev, priority); |
632 | break; |
633 | case _STAG_ENDDEP: |
634 | if (size != 0) |
635 | goto __skip; |
636 | option_flags = 0; |
637 | break; |
638 | case _STAG_IOPORT: |
639 | if (size != 7) |
640 | goto __skip; |
641 | isapnp_parse_port_resource(dev, option_flags, size); |
642 | size = 0; |
643 | break; |
644 | case _STAG_FIXEDIO: |
645 | if (size != 3) |
646 | goto __skip; |
647 | isapnp_parse_fixed_port_resource(dev, option_flags, |
648 | size); |
649 | size = 0; |
650 | break; |
651 | case _STAG_VENDOR: |
652 | break; |
653 | case _LTAG_MEMRANGE: |
654 | if (size != 9) |
655 | goto __skip; |
656 | isapnp_parse_mem_resource(dev, option_flags, size); |
657 | size = 0; |
658 | break; |
659 | case _LTAG_ANSISTR: |
660 | isapnp_parse_name(name: dev->name, name_max: sizeof(dev->name), size: &size); |
661 | break; |
662 | case _LTAG_UNICODESTR: |
663 | /* silently ignore */ |
664 | /* who use unicode for hardware identification? */ |
665 | break; |
666 | case _LTAG_VENDOR: |
667 | break; |
668 | case _LTAG_MEM32RANGE: |
669 | if (size != 17) |
670 | goto __skip; |
671 | isapnp_parse_mem32_resource(dev, option_flags, size); |
672 | size = 0; |
673 | break; |
674 | case _LTAG_FIXEDMEM32RANGE: |
675 | if (size != 9) |
676 | goto __skip; |
677 | isapnp_parse_fixed_mem32_resource(dev, option_flags, |
678 | size); |
679 | size = 0; |
680 | break; |
681 | case _STAG_END: |
682 | if (size > 0) |
683 | isapnp_skip_bytes(count: size); |
684 | return 1; |
685 | default: |
686 | dev_err(&dev->dev, "unknown tag %#x (card %i), " |
687 | "ignored\n" , type, card->number); |
688 | } |
689 | __skip: |
690 | if (size > 0) |
691 | isapnp_skip_bytes(count: size); |
692 | } |
693 | return 0; |
694 | } |
695 | |
696 | /* |
697 | * Parse resource map for ISA PnP card. |
698 | */ |
699 | static void __init isapnp_parse_resource_map(struct pnp_card *card) |
700 | { |
701 | unsigned char type, tmp[17]; |
702 | unsigned short size; |
703 | |
704 | while (1) { |
705 | if (isapnp_read_tag(type: &type, size: &size) < 0) |
706 | return; |
707 | switch (type) { |
708 | case _STAG_PNPVERNO: |
709 | if (size != 2) |
710 | goto __skip; |
711 | isapnp_peek(data: tmp, bytes: 2); |
712 | card->pnpver = tmp[0]; |
713 | card->productver = tmp[1]; |
714 | size = 0; |
715 | break; |
716 | case _STAG_LOGDEVID: |
717 | if (size >= 5 && size <= 6) { |
718 | if (isapnp_create_device(card, size) == 1) |
719 | return; |
720 | size = 0; |
721 | } |
722 | break; |
723 | case _STAG_VENDOR: |
724 | break; |
725 | case _LTAG_ANSISTR: |
726 | isapnp_parse_name(name: card->name, name_max: sizeof(card->name), |
727 | size: &size); |
728 | break; |
729 | case _LTAG_UNICODESTR: |
730 | /* silently ignore */ |
731 | /* who use unicode for hardware identification? */ |
732 | break; |
733 | case _LTAG_VENDOR: |
734 | break; |
735 | case _STAG_END: |
736 | if (size > 0) |
737 | isapnp_skip_bytes(count: size); |
738 | return; |
739 | default: |
740 | dev_err(&card->dev, "unknown tag %#x, ignored\n" , |
741 | type); |
742 | } |
743 | __skip: |
744 | if (size > 0) |
745 | isapnp_skip_bytes(count: size); |
746 | } |
747 | } |
748 | |
749 | /* |
750 | * Build device list for all present ISA PnP devices. |
751 | */ |
752 | static int __init isapnp_build_device_list(void) |
753 | { |
754 | int csn; |
755 | unsigned char [9]; |
756 | struct pnp_card *card; |
757 | u32 eisa_id; |
758 | char id[8]; |
759 | |
760 | isapnp_wait(); |
761 | isapnp_key(); |
762 | for (csn = 1; csn <= isapnp_csn_count; csn++) { |
763 | isapnp_wake(csn); |
764 | isapnp_peek(data: header, bytes: 9); |
765 | eisa_id = header[0] | header[1] << 8 | |
766 | header[2] << 16 | header[3] << 24; |
767 | pnp_eisa_id_to_string(id: eisa_id, str: id); |
768 | card = pnp_alloc_card(&isapnp_protocol, id: csn, pnpid: id); |
769 | if (!card) |
770 | continue; |
771 | |
772 | INIT_LIST_HEAD(list: &card->devices); |
773 | card->serial = |
774 | (header[7] << 24) | (header[6] << 16) | (header[5] << 8) | |
775 | header[4]; |
776 | isapnp_checksum_value = 0x00; |
777 | isapnp_parse_resource_map(card); |
778 | if (isapnp_checksum_value != 0x00) |
779 | dev_err(&card->dev, "invalid checksum %#x\n" , |
780 | isapnp_checksum_value); |
781 | card->checksum = isapnp_checksum_value; |
782 | |
783 | pnp_add_card(card); |
784 | } |
785 | isapnp_wait(); |
786 | return 0; |
787 | } |
788 | |
789 | /* |
790 | * Basic configuration routines. |
791 | */ |
792 | |
793 | int isapnp_present(void) |
794 | { |
795 | struct pnp_card *card; |
796 | |
797 | pnp_for_each_card(card) { |
798 | if (card->protocol == &isapnp_protocol) |
799 | return 1; |
800 | } |
801 | return 0; |
802 | } |
803 | |
804 | int isapnp_cfg_begin(int csn, int logdev) |
805 | { |
806 | if (csn < 1 || csn > isapnp_csn_count || logdev > 10) |
807 | return -EINVAL; |
808 | mutex_lock(&isapnp_cfg_mutex); |
809 | isapnp_wait(); |
810 | isapnp_key(); |
811 | isapnp_wake(csn); |
812 | #if 0 |
813 | /* to avoid malfunction when the isapnptools package is used */ |
814 | /* we must set RDP to our value again */ |
815 | /* it is possible to set RDP only in the isolation phase */ |
816 | /* Jens Thoms Toerring <Jens.Toerring@physik.fu-berlin.de> */ |
817 | isapnp_write_byte(0x02, 0x04); /* clear CSN of card */ |
818 | mdelay(2); /* is this necessary? */ |
819 | isapnp_wake(csn); /* bring card into sleep state */ |
820 | isapnp_wake(0); /* bring card into isolation state */ |
821 | isapnp_set_rdp(); /* reset the RDP port */ |
822 | udelay(1000); /* delay 1000us */ |
823 | isapnp_write_byte(0x06, csn); /* reset CSN to previous value */ |
824 | udelay(250); /* is this necessary? */ |
825 | #endif |
826 | if (logdev >= 0) |
827 | isapnp_device(logdev); |
828 | return 0; |
829 | } |
830 | |
831 | int isapnp_cfg_end(void) |
832 | { |
833 | isapnp_wait(); |
834 | mutex_unlock(lock: &isapnp_cfg_mutex); |
835 | return 0; |
836 | } |
837 | |
838 | /* |
839 | * Initialization. |
840 | */ |
841 | |
842 | EXPORT_SYMBOL(isapnp_protocol); |
843 | EXPORT_SYMBOL(isapnp_present); |
844 | EXPORT_SYMBOL(isapnp_cfg_begin); |
845 | EXPORT_SYMBOL(isapnp_cfg_end); |
846 | EXPORT_SYMBOL(isapnp_write_byte); |
847 | |
848 | static int isapnp_get_resources(struct pnp_dev *dev) |
849 | { |
850 | int i, ret; |
851 | |
852 | pnp_dbg(&dev->dev, "get resources\n" ); |
853 | pnp_init_resources(dev); |
854 | isapnp_cfg_begin(dev->card->number, dev->number); |
855 | dev->active = isapnp_read_byte(ISAPNP_CFG_ACTIVATE); |
856 | if (!dev->active) |
857 | goto __end; |
858 | |
859 | for (i = 0; i < ISAPNP_MAX_PORT; i++) { |
860 | ret = isapnp_read_word(ISAPNP_CFG_PORT + (i << 1)); |
861 | pnp_add_io_resource(dev, start: ret, end: ret, |
862 | flags: ret == 0 ? IORESOURCE_DISABLED : 0); |
863 | } |
864 | for (i = 0; i < ISAPNP_MAX_MEM; i++) { |
865 | ret = isapnp_read_word(ISAPNP_CFG_MEM + (i << 3)) << 8; |
866 | pnp_add_mem_resource(dev, start: ret, end: ret, |
867 | flags: ret == 0 ? IORESOURCE_DISABLED : 0); |
868 | } |
869 | for (i = 0; i < ISAPNP_MAX_IRQ; i++) { |
870 | ret = isapnp_read_word(ISAPNP_CFG_IRQ + (i << 1)) >> 8; |
871 | pnp_add_irq_resource(dev, irq: ret, |
872 | flags: ret == 0 ? IORESOURCE_DISABLED : 0); |
873 | } |
874 | for (i = 0; i < ISAPNP_MAX_DMA; i++) { |
875 | ret = isapnp_read_byte(ISAPNP_CFG_DMA + i); |
876 | pnp_add_dma_resource(dev, dma: ret, |
877 | flags: ret == 4 ? IORESOURCE_DISABLED : 0); |
878 | } |
879 | |
880 | __end: |
881 | isapnp_cfg_end(); |
882 | return 0; |
883 | } |
884 | |
885 | static int isapnp_set_resources(struct pnp_dev *dev) |
886 | { |
887 | struct resource *res; |
888 | int tmp; |
889 | |
890 | pnp_dbg(&dev->dev, "set resources\n" ); |
891 | isapnp_cfg_begin(dev->card->number, dev->number); |
892 | dev->active = 1; |
893 | for (tmp = 0; tmp < ISAPNP_MAX_PORT; tmp++) { |
894 | res = pnp_get_resource(dev, IORESOURCE_IO, num: tmp); |
895 | if (pnp_resource_enabled(res)) { |
896 | pnp_dbg(&dev->dev, " set io %d to %#llx\n" , |
897 | tmp, (unsigned long long) res->start); |
898 | isapnp_write_word(ISAPNP_CFG_PORT + (tmp << 1), |
899 | val: res->start); |
900 | } |
901 | } |
902 | for (tmp = 0; tmp < ISAPNP_MAX_IRQ; tmp++) { |
903 | res = pnp_get_resource(dev, IORESOURCE_IRQ, num: tmp); |
904 | if (pnp_resource_enabled(res)) { |
905 | int irq = res->start; |
906 | if (irq == 2) |
907 | irq = 9; |
908 | pnp_dbg(&dev->dev, " set irq %d to %d\n" , tmp, irq); |
909 | isapnp_write_byte(ISAPNP_CFG_IRQ + (tmp << 1), irq); |
910 | } |
911 | } |
912 | for (tmp = 0; tmp < ISAPNP_MAX_DMA; tmp++) { |
913 | res = pnp_get_resource(dev, IORESOURCE_DMA, num: tmp); |
914 | if (pnp_resource_enabled(res)) { |
915 | pnp_dbg(&dev->dev, " set dma %d to %lld\n" , |
916 | tmp, (unsigned long long) res->start); |
917 | isapnp_write_byte(ISAPNP_CFG_DMA + tmp, res->start); |
918 | } |
919 | } |
920 | for (tmp = 0; tmp < ISAPNP_MAX_MEM; tmp++) { |
921 | res = pnp_get_resource(dev, IORESOURCE_MEM, num: tmp); |
922 | if (pnp_resource_enabled(res)) { |
923 | pnp_dbg(&dev->dev, " set mem %d to %#llx\n" , |
924 | tmp, (unsigned long long) res->start); |
925 | isapnp_write_word(ISAPNP_CFG_MEM + (tmp << 3), |
926 | val: (res->start >> 8) & 0xffff); |
927 | } |
928 | } |
929 | /* FIXME: We aren't handling 32bit mems properly here */ |
930 | isapnp_activate(logdev: dev->number); |
931 | isapnp_cfg_end(); |
932 | return 0; |
933 | } |
934 | |
935 | static int isapnp_disable_resources(struct pnp_dev *dev) |
936 | { |
937 | if (!dev->active) |
938 | return -EINVAL; |
939 | isapnp_cfg_begin(dev->card->number, dev->number); |
940 | isapnp_deactivate(logdev: dev->number); |
941 | dev->active = 0; |
942 | isapnp_cfg_end(); |
943 | return 0; |
944 | } |
945 | |
946 | struct pnp_protocol isapnp_protocol = { |
947 | .name = "ISA Plug and Play" , |
948 | .get = isapnp_get_resources, |
949 | .set = isapnp_set_resources, |
950 | .disable = isapnp_disable_resources, |
951 | }; |
952 | |
953 | static int __init isapnp_init(void) |
954 | { |
955 | int cards; |
956 | struct pnp_card *card; |
957 | struct pnp_dev *dev; |
958 | |
959 | if (isapnp_disable) { |
960 | printk(KERN_INFO "isapnp: ISA Plug & Play support disabled\n" ); |
961 | return 0; |
962 | } |
963 | #ifdef CONFIG_PPC |
964 | if (check_legacy_ioport(_PIDXR) || check_legacy_ioport(_PNPWRP)) |
965 | return -EINVAL; |
966 | #endif |
967 | #ifdef ISAPNP_REGION_OK |
968 | if (!request_region(_PIDXR, 1, "isapnp index" )) { |
969 | printk(KERN_ERR "isapnp: Index Register 0x%x already used\n" , |
970 | _PIDXR); |
971 | return -EBUSY; |
972 | } |
973 | #endif |
974 | if (!request_region(_PNPWRP, 1, "isapnp write" )) { |
975 | printk(KERN_ERR |
976 | "isapnp: Write Data Register 0x%x already used\n" , |
977 | _PNPWRP); |
978 | #ifdef ISAPNP_REGION_OK |
979 | release_region(_PIDXR, 1); |
980 | #endif |
981 | return -EBUSY; |
982 | } |
983 | |
984 | if (pnp_register_protocol(protocol: &isapnp_protocol) < 0) |
985 | return -EBUSY; |
986 | |
987 | /* |
988 | * Print a message. The existing ISAPnP code is hanging machines |
989 | * so let the user know where. |
990 | */ |
991 | |
992 | printk(KERN_INFO "isapnp: Scanning for PnP cards...\n" ); |
993 | if (isapnp_rdp >= 0x203 && isapnp_rdp <= 0x3ff) { |
994 | isapnp_rdp |= 3; |
995 | if (!request_region(isapnp_rdp, 1, "isapnp read" )) { |
996 | printk(KERN_ERR |
997 | "isapnp: Read Data Register 0x%x already used\n" , |
998 | isapnp_rdp); |
999 | #ifdef ISAPNP_REGION_OK |
1000 | release_region(_PIDXR, 1); |
1001 | #endif |
1002 | release_region(_PNPWRP, 1); |
1003 | return -EBUSY; |
1004 | } |
1005 | isapnp_set_rdp(); |
1006 | } |
1007 | if (isapnp_rdp < 0x203 || isapnp_rdp > 0x3ff) { |
1008 | cards = isapnp_isolate(); |
1009 | if (cards < 0 || (isapnp_rdp < 0x203 || isapnp_rdp > 0x3ff)) { |
1010 | #ifdef ISAPNP_REGION_OK |
1011 | release_region(_PIDXR, 1); |
1012 | #endif |
1013 | release_region(_PNPWRP, 1); |
1014 | printk(KERN_INFO |
1015 | "isapnp: No Plug & Play device found\n" ); |
1016 | return 0; |
1017 | } |
1018 | request_region(isapnp_rdp, 1, "isapnp read" ); |
1019 | } |
1020 | isapnp_build_device_list(); |
1021 | cards = 0; |
1022 | |
1023 | protocol_for_each_card(&isapnp_protocol, card) { |
1024 | cards++; |
1025 | if (isapnp_verbose) { |
1026 | dev_info(&card->dev, "card '%s'\n" , |
1027 | card->name[0] ? card->name : "unknown" ); |
1028 | if (isapnp_verbose < 2) |
1029 | continue; |
1030 | card_for_each_dev(card, dev) { |
1031 | dev_info(&card->dev, "device '%s'\n" , |
1032 | dev->name[0] ? dev->name : "unknown" ); |
1033 | } |
1034 | } |
1035 | } |
1036 | if (cards) |
1037 | printk(KERN_INFO |
1038 | "isapnp: %i Plug & Play card%s detected total\n" , cards, |
1039 | cards > 1 ? "s" : "" ); |
1040 | else |
1041 | printk(KERN_INFO "isapnp: No Plug & Play card found\n" ); |
1042 | |
1043 | isapnp_proc_init(); |
1044 | return 0; |
1045 | } |
1046 | |
1047 | device_initcall(isapnp_init); |
1048 | |
1049 | /* format is: noisapnp */ |
1050 | |
1051 | static int __init isapnp_setup_disable(char *str) |
1052 | { |
1053 | isapnp_disable = 1; |
1054 | return 1; |
1055 | } |
1056 | |
1057 | __setup("noisapnp" , isapnp_setup_disable); |
1058 | |
1059 | /* format is: isapnp=rdp,reset,skip_pci_scan,verbose */ |
1060 | |
1061 | static int __init isapnp_setup_isapnp(char *str) |
1062 | { |
1063 | (void)((get_option(str: &str, pint: &isapnp_rdp) == 2) && |
1064 | (get_option(str: &str, pint: &isapnp_reset) == 2) && |
1065 | (get_option(str: &str, pint: &isapnp_verbose) == 2)); |
1066 | return 1; |
1067 | } |
1068 | |
1069 | __setup("isapnp=" , isapnp_setup_isapnp); |
1070 | |