pnpbus.c revision 1.8 1 /* $NetBSD: pnpbus.c,v 1.8 2008/04/28 19:01:45 garbled Exp $ */
2
3 /*-
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Tim Rightnour
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: pnpbus.c,v 1.8 2008/04/28 19:01:45 garbled Exp $");
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/device.h>
45 #include <sys/extent.h>
46 #include <sys/malloc.h>
47
48 #include <machine/bus.h>
49 #include <machine/pio.h>
50 #include <machine/intr.h>
51 #include <machine/platform.h>
52 #include <machine/residual.h>
53 #include <machine/pnp.h>
54 #include <machine/isa_machdep.h>
55 #include <machine/chpidpnp.h>
56
57 #include <dev/isa/isareg.h>
58
59 #include <prep/pnpbus/pnpbusvar.h>
60
61 #include "isadma.h"
62
63 static int pnpbus_match(struct device *, struct cfdata *, void *);
64 static void pnpbus_attach(struct device *, struct device *, void *);
65 static int pnpbus_print(void *, const char *);
66 static int pnpbus_search(struct device *, struct cfdata *,
67 const int *, void *);
68
69 CFATTACH_DECL(pnpbus, sizeof(struct pnpbus_softc),
70 pnpbus_match, pnpbus_attach, NULL, NULL);
71
72 struct pnpbus_softc *pnpbus_softc;
73 extern struct cfdriver pnpbus_cd;
74
75 static int
76 pnpbus_match(struct device *parent, struct cfdata *cf, void *aux)
77 {
78 struct pnpbus_attach_args *paa = aux;
79
80 if (paa->paa_name != NULL && strcmp(paa->paa_name, "pnpbus") == 0)
81 return 1;
82 return 0;
83 }
84
85 static void
86 pnpbus_attach(struct device *parent, struct device *self, void *aux)
87 {
88 struct pnpbus_softc *sc = (struct pnpbus_softc *)self;
89 struct pnpbus_attach_args *paa = aux;
90
91 aprint_normal("\n");
92
93 pnpbus_softc = sc;
94 sc->sc_ic = paa->paa_ic;
95 sc->sc_iot = paa->paa_iot;
96 sc->sc_memt = paa->paa_memt;
97 sc->sc_dmat = paa->paa_dmat;
98
99 #if NISADMA > 0
100 isa_dmainit(sc->sc_ic, sc->sc_iot, sc->sc_dmat, self);
101 #endif
102
103 (void)config_search_ia(pnpbus_search, self, "pnpbus", aux);
104 }
105
106 static int
107 pnp_newirq(void *v, struct pnpresources *r, int size)
108 {
109 struct _S4_Pack *p = v;
110 struct pnpbus_irq *irq;
111
112 irq = malloc(sizeof(struct pnpbus_irq), M_DEVBUF, M_NOWAIT);
113
114 irq->mask = le16dec(&p->IRQMask[0]);
115
116 if (size > 2)
117 irq->flags = p->IRQInfo;
118 else
119 irq->flags = 0x1;
120
121 SIMPLEQ_INSERT_TAIL(&r->irq, irq, next);
122 r->numirq++;
123
124 return 0;
125 }
126
127 static int
128 pnp_newdma(void *v, struct pnpresources *r, int size)
129 {
130 struct _S5_Pack *p = v;
131 struct pnpbus_dma *dma;
132
133 dma = malloc(sizeof(struct pnpbus_dma), M_DEVBUF, M_NOWAIT);
134
135 dma->mask = le16dec(&p->DMAMask);
136 if (size > 2)
137 dma->flags = p->DMAInfo;
138 else
139 dma->flags = 0x01;
140
141 SIMPLEQ_INSERT_TAIL(&r->dma, dma, next);
142 r->numdma++;
143
144 return 0;
145 }
146
147 static int
148 pnp_newioport(void *v, struct pnpresources *r, int size)
149 {
150 struct _S8_Pack *p = v;
151 struct pnpbus_io *io;
152 uint16_t mask;
153
154 io = malloc(sizeof(struct pnpbus_io), M_DEVBUF, M_NOWAIT);
155 mask = p->IOInfo & ISAAddr16bit ? 0xffff : 0x03ff;
156 io->minbase = (p->RangeMin[0] | (p->RangeMin[1] << 8)) & mask;
157 io->maxbase = (p->RangeMax[0] | (p->RangeMax[1] << 8)) & mask;
158 io->align = p->IOAlign;
159 io->len = p->IONum;
160 io->flags = p->IOInfo;
161
162 SIMPLEQ_INSERT_TAIL(&r->io, io, next);
163 r->numio++;
164
165 return 0;
166 }
167
168 static int
169 pnp_newfixedioport(void *v, struct pnpresources *r, int size)
170 {
171 struct _S9_Pack *p = v;
172 struct pnpbus_io *io;
173
174 io = malloc(sizeof(struct pnpbus_io), M_DEVBUF, M_NOWAIT);
175 io->minbase = (p->Range[0] | (p->Range[1] << 8)) & 0x3ff;
176 io->len = p->IONum;
177 io->maxbase = -1;
178 io->flags = 0;
179 io->align = 1;
180
181 SIMPLEQ_INSERT_TAIL(&r->io, io, next);
182 r->numio++;
183
184 return 0;
185 }
186
187 static int
188 pnp_newiomem(void *v, struct pnpresources *r, int size)
189 {
190 struct pnpbus_mem *mem;
191 struct _L1_Pack *pack = v;
192
193 if (pack->Count0 >= 0x9) {
194 mem = malloc(sizeof(struct pnpbus_mem), M_DEVBUF, M_NOWAIT);
195 mem->minbase = (pack->Data[2] << 16) | (pack->Data[1] << 8);
196 mem->maxbase = (pack->Data[4] << 16) | (pack->Data[3] << 8);
197 mem->align = (pack->Data[6] << 8) | pack->Data[5];
198 mem->len = (pack->Data[8] << 16) | (pack->Data[7] << 8);
199 mem->flags = pack->Data[0];
200 SIMPLEQ_INSERT_TAIL(&r->iomem, mem, next);
201 r->numiomem++;
202 return 0;
203 }
204 return -1;
205 }
206
207 static int
208 pnp_newaddr(void *v, struct pnpresources *r, int size)
209 {
210 struct pnpbus_io *io;
211 struct pnpbus_mem *mem;
212 struct _L4_Pack *pack = v;
213 struct _L4_PPCPack *p = &pack->L4_Data.L4_PPCPack;
214
215 if (p->PPCData[0] == 1) {/* type IO */
216 io = malloc(sizeof(struct pnpbus_io), M_DEVBUF, M_NOWAIT);
217 io->minbase = (uint16_t)le64dec(&p->PPCData[4]);
218 io->maxbase = -1;
219 io->align = p->PPCData[1];
220 io->len = (uint16_t)le64dec(&p->PPCData[12]);
221 io->flags = 0;
222 SIMPLEQ_INSERT_TAIL(&r->io, io, next);
223 r->numio++;
224
225 return 0;
226 } else if (p->PPCData[0] == 2) {
227 mem = malloc(sizeof(struct pnpbus_mem), M_DEVBUF, M_NOWAIT);
228 mem->minbase = (uint32_t)le64dec(&p->PPCData[4]);
229 mem->maxbase = -1;
230 mem->align = p->PPCData[1];
231 mem->len = (uint32_t)le64dec(&p->PPCData[12]);
232 mem->flags = 0;
233 SIMPLEQ_INSERT_TAIL(&r->mem, mem, next);
234 r->nummem++;
235
236 return 0;
237 } else
238 return -1;
239 }
240
241 static int
242 pnp_newcompatid(void *v, struct pnpresources *r, int size)
243 {
244 struct _S3_Pack *p = v;
245 struct pnpbus_compatid *id;
246 uint32_t cid;
247
248 id = malloc(sizeof(*id), M_DEVBUF, M_NOWAIT);
249 cid = le32dec(p->CompatId);
250 pnp_devid_to_string(cid, id->idstr);
251 id->next = r->compatids;
252 r->compatids = id;
253
254 return 0;
255 }
256
257 /*
258 * Call if match succeeds. This way we don't allocate lots of ram
259 * for structures we never use if the device isn't attached.
260 */
261
262 int
263 pnpbus_scan(struct pnpbus_dev_attach_args *pna, PPC_DEVICE *dev)
264 {
265 struct pnpresources *r = &pna->pna_res;
266 uint32_t l;
267 uint8_t *p, *q;
268 void *v;
269 int tag, size, item;
270
271 l = be32toh(dev->AllocatedOffset);
272 p = res->DevicePnPHeap + l;
273
274 if (p == NULL)
275 return -1;
276
277 for (; p[0] != END_TAG; p += size) {
278 tag = *p;
279 v = p;
280 if (tag_type(p[0]) == PNP_SMALL) {
281 size = tag_small_count(tag) + 1;
282 item = tag_small_item_name(tag);
283 switch (item) {
284 case IRQFormat:
285 pnp_newirq(v, r, size);
286 break;
287 case DMAFormat:
288 pnp_newdma(v, r, size);
289 break;
290 case IOPort:
291 pnp_newioport(v, r, size);
292 break;
293 case FixedIOPort:
294 pnp_newfixedioport(v, r, size);
295 break;
296 }
297 } else {
298 struct _L4_Pack *pack = v;
299 struct _L4_PPCPack *pa = &pack->L4_Data.L4_PPCPack;
300
301 q = p;
302 size = (q[1] | (q[2] << 8)) + 3 /* tag + length */;
303 item = tag_large_item_name(tag);
304 if (item == LargeVendorItem &&
305 pa->Type == LV_GenericAddress)
306 pnp_newaddr(v, r, size);
307 else if (item == MemoryRange)
308 pnp_newiomem(v, r, size);
309 }
310 }
311
312 /* scan for compatid's */
313
314 l = be32toh(dev->CompatibleOffset);
315 p = res->DevicePnPHeap + l;
316
317 if (p == NULL)
318 return -1;
319
320 for (; p[0] != END_TAG; p += size) {
321 tag = *p;
322 v = p;
323 if (tag_type(p[0]) == PNP_SMALL) {
324 size = tag_small_count(tag) + 1;
325 item = tag_small_item_name(tag);
326 switch (item) {
327 case CompatibleDevice:
328 pnp_newcompatid(v, r, size);
329 break;
330 }
331 } else {
332 q = p;
333 size = (q[1] | (q[2] << 8)) + 3 /* tag + length */;
334 }
335 }
336 return 0;
337 }
338
339 /*
340 * Setup the basic pna structure.
341 */
342
343 static void
344 pnp_getpna(struct pnpbus_dev_attach_args *pna, struct pnpbus_attach_args *paa,
345 PPC_DEVICE *dev)
346 {
347 DEVICE_ID *id = &dev->DeviceId;
348 struct pnpresources *r = &pna->pna_res;
349 ChipIDPack *pack;
350 uint32_t l;
351 uint8_t *p;
352 void *v;
353 int tag, size, item;
354
355 l = be32toh(dev->AllocatedOffset);
356 p = res->DevicePnPHeap + l;
357
358 pna->pna_iot = paa->paa_iot;
359 pna->pna_memt = paa->paa_memt;
360 pna->pna_ic = paa->paa_ic;
361 pna->pna_dmat = paa->paa_dmat;
362 pnp_devid_to_string(id->DevId, pna->pna_devid);
363 pna->basetype = id->BaseType;
364 pna->subtype = id->SubType;
365 pna->interface = id->Interface;
366 pna->pna_ppc_dev = dev;
367 memset(r, 0, sizeof(*r));
368 SIMPLEQ_INIT(&r->mem);
369 SIMPLEQ_INIT(&r->io);
370 SIMPLEQ_INIT(&r->irq);
371 SIMPLEQ_INIT(&r->dma);
372 SIMPLEQ_INIT(&r->iomem);
373 if (p == NULL)
374 return;
375 /* otherwise, we start looking for chipid's */
376 for (; p[0] != END_TAG; p += size) {
377 tag = *p;
378 v = p;
379 if (tag_type(p[0]) == PNP_SMALL) {
380 size = tag_small_count(tag) + 1;
381 item = tag_small_item_name(tag);
382 if (item != SmallVendorItem || p[1] != 1)
383 continue;
384 pack = v;
385 pna->chipid = le16dec(&pack->Name[0]);
386 pna->chipmfg0 = pack->VendorID0;
387 pna->chipmfg1 = pack->VendorID1;
388 break;
389 } else {
390 /* Large */
391 size = (p[1] | (p[2] << 8)) + 3 /* tag + length */;
392 }
393 }
394 }
395
396 static int
397 pnpbus_search(struct device *parent, struct cfdata *cf,
398 const int *ldesc, void *aux)
399 {
400 struct pnpbus_dev_attach_args pna;
401 struct pnpbus_attach_args *paa = aux;
402 PPC_DEVICE *ppc_dev;
403 int i;
404 uint32_t ndev;
405
406 ndev = be32toh(res->ActualNumDevices);
407 ppc_dev = res->Devices;
408
409 for (i = 0; i < ((ndev > MAX_DEVICES) ? MAX_DEVICES : ndev); i++) {
410 pnp_getpna(&pna, paa, &ppc_dev[i]);
411 if (config_match(parent, cf, &pna) > 0)
412 config_attach(parent, cf, &pna, pnpbus_print);
413 }
414
415 return 0;
416 }
417
418 static void
419 pnpbus_printres(struct pnpresources *r)
420 {
421 struct pnpbus_io *io;
422 struct pnpbus_mem *mem;
423 struct pnpbus_irq *irq;
424 struct pnpbus_dma *dma;
425 int p = 0;
426
427 if (!SIMPLEQ_EMPTY(&r->mem)) {
428 aprint_normal("mem");
429 SIMPLEQ_FOREACH(mem, &r->mem, next) {
430 aprint_normal(" 0x%x", mem->minbase);
431 if (mem->len > 1)
432 aprint_normal("-0x%x",
433 mem->minbase + mem->len - 1);
434 }
435 p++;
436 }
437 if (!SIMPLEQ_EMPTY(&r->io)) {
438 if (p++)
439 aprint_normal(", ");
440 aprint_normal("port");
441 SIMPLEQ_FOREACH(io, &r->io, next) {
442 aprint_normal(" 0x%x", io->minbase);
443 if (io->len > 1)
444 aprint_normal("-0x%x",
445 io->minbase + io->len - 1);
446 }
447 }
448 if (!SIMPLEQ_EMPTY(&r->iomem)) {
449 if (p++)
450 aprint_normal(", ");
451 aprint_normal("iomem");
452 SIMPLEQ_FOREACH(mem, &r->iomem, next) {
453 aprint_normal(" 0x%x", mem->minbase);
454 if (mem->len > 1)
455 aprint_normal("-0x%x",
456 mem->minbase + mem->len - 1);
457 }
458 p++;
459 }
460 if (!SIMPLEQ_EMPTY(&r->irq)) {
461 if (p++)
462 aprint_normal(", ");
463 aprint_normal("irq");
464 SIMPLEQ_FOREACH(irq, &r->irq, next) {
465 aprint_normal(" %d", ffs(irq->mask) - 1);
466 }
467 }
468 if (!SIMPLEQ_EMPTY(&r->dma)) {
469 if (p++)
470 aprint_normal(", ");
471 aprint_normal("DMA");
472 SIMPLEQ_FOREACH(dma, &r->dma, next) {
473 aprint_normal(" %d", ffs(dma->mask) - 1);
474 }
475 }
476 }
477
478 void
479 pnpbus_print_devres(struct pnpbus_dev_attach_args *pna)
480 {
481 aprint_normal(": ");
482 pnpbus_printres(&pna->pna_res);
483 }
484
485 static int
486 pnpbus_print(void *args, const char *name)
487 {
488 struct pnpbus_dev_attach_args *pna = args;
489
490 pnpbus_print_devres(pna);
491 return (UNCONF);
492 }
493
494 /*
495 * Set up an interrupt handler to start being called.
496 */
497 void *
498 pnpbus_intr_establish(int idx, int level, int tover, int (*ih_fun)(void *),
499 void *ih_arg, struct pnpresources *r)
500 {
501 struct pnpbus_irq *irq;
502 int irqnum, type;
503
504 if (idx >= r->numirq)
505 return 0;
506
507 irq = SIMPLEQ_FIRST(&r->irq);
508 while (idx--)
509 irq = SIMPLEQ_NEXT(irq, next);
510
511 irqnum = ffs(irq->mask) - 1;
512 type = (irq->flags & 0x0c) ? IST_LEVEL : IST_EDGE;
513 if (tover != IST_PNP)
514 type = tover;
515
516 return (void *)intr_establish(irqnum, type, level, ih_fun, ih_arg);
517 }
518
519 /*
520 * Deregister an interrupt handler.
521 */
522 void
523 pnpbus_intr_disestablish(void *arg)
524 {
525
526 intr_disestablish(arg);
527 }
528
529 int
530 pnpbus_getirqnum(struct pnpresources *r, int idx, int *irqp, int *istp)
531 {
532 struct pnpbus_irq *irq;
533
534 if (idx >= r->numirq)
535 return EINVAL;
536
537 irq = SIMPLEQ_FIRST(&r->irq);
538 while (idx--)
539 irq = SIMPLEQ_NEXT(irq, next);
540
541 if (irqp != NULL)
542 *irqp = ffs(irq->mask) - 1;
543 if (istp != NULL)
544 *istp = (irq->flags &0x0c) ? IST_LEVEL : IST_EDGE;
545 return 0;
546 }
547
548 int
549 pnpbus_getdmachan(struct pnpresources *r, int idx, int *chanp)
550 {
551 struct pnpbus_dma *dma;
552
553 if (idx >= r->numdma)
554 return EINVAL;
555
556 dma = SIMPLEQ_FIRST(&r->dma);
557 while (idx--)
558 dma = SIMPLEQ_NEXT(dma, next);
559
560 if (chanp != NULL)
561 *chanp = ffs(dma->mask) - 1;
562 return 0;
563 }
564
565 int
566 pnpbus_getioport(struct pnpresources *r, int idx, int *basep, int *sizep)
567 {
568 struct pnpbus_io *io;
569
570 if (idx >= r->numio)
571 return EINVAL;
572
573 io = SIMPLEQ_FIRST(&r->io);
574 while (idx--)
575 io = SIMPLEQ_NEXT(io, next);
576
577 if (basep)
578 *basep = io->minbase;
579 if (sizep)
580 *sizep = io->len;
581 return 0;
582 }
583
584 int
585 pnpbus_io_map(struct pnpresources *r, int idx, bus_space_tag_t *tagp,
586 bus_space_handle_t *hdlp)
587 {
588 struct pnpbus_io *io;
589
590 if (idx >= r->numio)
591 return EINVAL;
592
593 io = SIMPLEQ_FIRST(&r->io);
594 while (idx--)
595 io = SIMPLEQ_NEXT(io, next);
596
597 *tagp = &genppc_isa_io_space_tag;
598 return (bus_space_map(&genppc_isa_io_space_tag, io->minbase, io->len,
599 0, hdlp));
600 }
601
602 void
603 pnpbus_io_unmap(struct pnpresources *r, int idx, bus_space_tag_t tag,
604 bus_space_handle_t hdl)
605 {
606 struct pnpbus_io *io;
607
608 if (idx >= r->numio)
609 return;
610
611 io = SIMPLEQ_FIRST(&r->io);
612 while (idx--)
613 io = SIMPLEQ_NEXT(io, next);
614
615 bus_space_unmap(tag, hdl, io->len);
616 }
617
618 int
619 pnpbus_getiomem(struct pnpresources *r, int idx, int *basep, int *sizep)
620 {
621 struct pnpbus_mem *mem;
622
623 if (idx >= r->numiomem)
624 return EINVAL;
625
626 mem = SIMPLEQ_FIRST(&r->iomem);
627 while (idx--)
628 mem = SIMPLEQ_NEXT(mem, next);
629
630 if (basep)
631 *basep = mem->minbase;
632 if (sizep)
633 *sizep = mem->len;
634 return 0;
635 }
636
637 int
638 pnpbus_iomem_map(struct pnpresources *r, int idx, bus_space_tag_t *tagp,
639 bus_space_handle_t *hdlp)
640 {
641 struct pnpbus_mem *mem;
642
643 if (idx >= r->numiomem)
644 return EINVAL;
645
646 mem = SIMPLEQ_FIRST(&r->iomem);
647 while (idx--)
648 mem = SIMPLEQ_NEXT(mem, next);
649
650 *tagp = &genppc_isa_mem_space_tag;
651 return (bus_space_map(&genppc_isa_mem_space_tag, mem->minbase, mem->len,
652 0, hdlp));
653 }
654
655 void
656 pnpbus_iomem_unmap(struct pnpresources *r, int idx, bus_space_tag_t tag,
657 bus_space_handle_t hdl)
658 {
659 struct pnpbus_mem *mem;
660
661 if (idx >= r->numiomem)
662 return;
663
664 mem = SIMPLEQ_FIRST(&r->mem);
665 while (idx--)
666 mem = SIMPLEQ_NEXT(mem, next);
667
668 bus_space_unmap(tag, hdl, mem->len);
669 }
670