pnpbus.c revision 1.5 1 /* $NetBSD: pnpbus.c,v 1.5 2006/06/23 03:08:41 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.5 2006/06/23 03:08:41 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 static int pnpbus_match(struct device *, struct cfdata *, void *);
62 static void pnpbus_attach(struct device *, struct device *, void *);
63 static int pnpbus_print(void *, const char *);
64 static int pnpbus_search(struct device *, struct cfdata *,
65 const int *, void *);
66
67 CFATTACH_DECL(pnpbus, sizeof(struct pnpbus_softc),
68 pnpbus_match, pnpbus_attach, NULL, NULL);
69
70 struct pnpbus_softc *pnpbus_softc;
71 extern struct cfdriver pnpbus_cd;
72
73 static int
74 pnpbus_match(struct device *parent, struct cfdata *cf, void *aux)
75 {
76 return 1;
77 }
78
79 static void
80 pnpbus_attach(struct device *parent, struct device *self, void *aux)
81 {
82 struct pnpbus_softc *sc = (struct pnpbus_softc *)self;
83 struct pnpbus_attach_args *paa = aux;
84
85 printf("\n");
86
87 pnpbus_softc = sc;
88 sc->sc_ic = paa->paa_ic;
89 sc->sc_iot = paa->paa_iot;
90 sc->sc_memt = paa->paa_memt;
91
92 (void)config_search_ia(pnpbus_search, self, "pnpbus", aux);
93 }
94
95 static int
96 pnp_newirq(void *v, struct pnpresources *r, int size)
97 {
98 struct _S4_Pack *p = v;
99 struct pnpbus_irq *irq;
100
101 irq = malloc(sizeof(struct pnpbus_irq), M_DEVBUF, M_NOWAIT);
102
103 irq->mask = le16dec(&p->IRQMask[0]);
104
105 if (size > 2)
106 irq->flags = p->IRQInfo;
107 else
108 irq->flags = 0x1;
109
110 SIMPLEQ_INSERT_TAIL(&r->irq, irq, next);
111 r->numirq++;
112
113 return 0;
114 }
115
116 static int
117 pnp_newdma(void *v, struct pnpresources *r, int size)
118 {
119 struct _S5_Pack *p = v;
120 struct pnpbus_dma *dma;
121
122 dma = malloc(sizeof(struct pnpbus_dma), M_DEVBUF, M_NOWAIT);
123
124 dma->mask = le16dec(&p->DMAMask);
125 if (size > 2)
126 dma->flags = p->DMAInfo;
127 else
128 dma->flags = 0x01;
129
130 SIMPLEQ_INSERT_TAIL(&r->dma, dma, next);
131 r->numdma++;
132
133 return 0;
134 }
135
136 static int
137 pnp_newioport(void *v, struct pnpresources *r, int size)
138 {
139 struct _S8_Pack *p = v;
140 struct pnpbus_io *io;
141 uint16_t mask;
142
143 io = malloc(sizeof(struct pnpbus_io), M_DEVBUF, M_NOWAIT);
144 mask = p->IOInfo & ISAAddr16bit ? 0xffff : 0x03ff;
145 io->minbase = (p->RangeMin[0] | (p->RangeMin[1] << 8)) & mask;
146 io->maxbase = (p->RangeMax[0] | (p->RangeMax[1] << 8)) & mask;
147 io->align = p->IOAlign;
148 io->len = p->IONum;
149 io->flags = p->IOInfo;
150
151 SIMPLEQ_INSERT_TAIL(&r->io, io, next);
152 r->numio++;
153
154 return 0;
155 }
156
157 static int
158 pnp_newfixedioport(void *v, struct pnpresources *r, int size)
159 {
160 struct _S9_Pack *p = v;
161 struct pnpbus_io *io;
162
163 io = malloc(sizeof(struct pnpbus_io), M_DEVBUF, M_NOWAIT);
164 io->minbase = (p->Range[0] | (p->Range[1] << 8)) & 0x3ff;
165 io->len = p->IONum;
166 io->maxbase = -1;
167 io->flags = 0;
168 io->align = 1;
169
170 SIMPLEQ_INSERT_TAIL(&r->io, io, next);
171 r->numio++;
172
173 return 0;
174 }
175
176 static int
177 pnp_newiomem(void *v, struct pnpresources *r, int size)
178 {
179 struct pnpbus_mem *mem;
180 struct _L1_Pack *pack = v;
181
182 if (pack->Count0 >= 0x9) {
183 mem = malloc(sizeof(struct pnpbus_mem), M_DEVBUF, M_NOWAIT);
184 mem->minbase = (pack->Data[2] << 16) | (pack->Data[1] << 8);
185 mem->maxbase = (pack->Data[4] << 16) | (pack->Data[3] << 8);
186 mem->align = (pack->Data[6] << 8) | pack->Data[5];
187 mem->len = (pack->Data[8] << 16) | (pack->Data[7] << 8);
188 mem->flags = pack->Data[0];
189 SIMPLEQ_INSERT_TAIL(&r->iomem, mem, next);
190 r->numiomem++;
191 return 0;
192 }
193 return -1;
194 }
195
196 static int
197 pnp_newaddr(void *v, struct pnpresources *r, int size)
198 {
199 struct pnpbus_io *io;
200 struct pnpbus_mem *mem;
201 struct _L4_Pack *pack = v;
202 struct _L4_PPCPack *p = &pack->L4_Data.L4_PPCPack;
203
204 if (p->PPCData[0] == 1) {/* type IO */
205 io = malloc(sizeof(struct pnpbus_io), M_DEVBUF, M_NOWAIT);
206 io->minbase = (uint16_t)le64dec(&p->PPCData[4]);
207 io->maxbase = -1;
208 io->align = p->PPCData[1];
209 io->len = (uint16_t)le64dec(&p->PPCData[12]);
210 io->flags = 0;
211 SIMPLEQ_INSERT_TAIL(&r->io, io, next);
212 r->numio++;
213
214 return 0;
215 } else if (p->PPCData[0] == 2) {
216 mem = malloc(sizeof(struct pnpbus_mem), M_DEVBUF, M_NOWAIT);
217 mem->minbase = (uint32_t)le64dec(&p->PPCData[4]);
218 mem->maxbase = -1;
219 mem->align = p->PPCData[1];
220 mem->len = (uint32_t)le64dec(&p->PPCData[12]);
221 mem->flags = 0;
222 SIMPLEQ_INSERT_TAIL(&r->mem, mem, next);
223 r->nummem++;
224
225 return 0;
226 } else
227 return -1;
228 }
229
230 static int
231 pnp_newcompatid(void *v, struct pnpresources *r, int size)
232 {
233 struct _S3_Pack *p = v;
234 struct pnpbus_compatid *id;
235 uint32_t cid;
236
237 id = malloc(sizeof(*id), M_DEVBUF, M_NOWAIT);
238 cid = le32dec(p->CompatId);
239 pnp_devid_to_string(cid, id->idstr);
240 id->next = r->compatids;
241 r->compatids = id;
242
243 return 0;
244 }
245
246 /*
247 * Call if match succeeds. This way we don't allocate lots of ram
248 * for structures we never use if the device isn't attached.
249 */
250
251 int
252 pnpbus_scan(struct pnpbus_dev_attach_args *pna, PPC_DEVICE *dev)
253 {
254 struct pnpresources *r = &pna->pna_res;
255 uint32_t l;
256 uint8_t *p, *q;
257 void *v;
258 int tag, size, item;
259
260 l = be32toh(dev->AllocatedOffset);
261 p = res->DevicePnPHeap + l;
262
263 if (p == NULL)
264 return -1;
265
266 for (; p[0] != END_TAG; p += size) {
267 tag = *p;
268 v = p;
269 if (tag_type(p[0]) == PNP_SMALL) {
270 size = tag_small_count(tag) + 1;
271 item = tag_small_item_name(tag);
272 switch (item) {
273 case IRQFormat:
274 pnp_newirq(v, r, size);
275 break;
276 case DMAFormat:
277 pnp_newdma(v, r, size);
278 break;
279 case IOPort:
280 pnp_newioport(v, r, size);
281 break;
282 case FixedIOPort:
283 pnp_newfixedioport(v, r, size);
284 break;
285 }
286 } else {
287 struct _L4_Pack *pack = v;
288 struct _L4_PPCPack *pa = &pack->L4_Data.L4_PPCPack;
289
290 q = p;
291 size = (q[1] | (q[2] << 8)) + 3 /* tag + length */;
292 item = tag_large_item_name(tag);
293 if (item == LargeVendorItem &&
294 pa->Type == LV_GenericAddress)
295 pnp_newaddr(v, r, size);
296 else if (item == MemoryRange)
297 pnp_newiomem(v, r, size);
298 }
299 }
300
301 /* scan for compatid's */
302
303 l = be32toh(dev->CompatibleOffset);
304 p = res->DevicePnPHeap + l;
305
306 if (p == NULL)
307 return -1;
308
309 for (; p[0] != END_TAG; p += size) {
310 tag = *p;
311 v = p;
312 if (tag_type(p[0]) == PNP_SMALL) {
313 size = tag_small_count(tag) + 1;
314 item = tag_small_item_name(tag);
315 switch (item) {
316 case CompatibleDevice:
317 pnp_newcompatid(v, r, size);
318 break;
319 }
320 } else {
321 q = p;
322 size = (q[1] | (q[2] << 8)) + 3 /* tag + length */;
323 }
324 }
325 return 0;
326 }
327
328 /*
329 * Setup the basic pna structure.
330 */
331
332 static void
333 pnp_getpna(struct pnpbus_dev_attach_args *pna, struct pnpbus_attach_args *paa,
334 PPC_DEVICE *dev)
335 {
336 DEVICE_ID *id = &dev->DeviceId;
337 struct pnpresources *r = &pna->pna_res;
338 ChipIDPack *pack;
339 uint32_t l;
340 uint8_t *p;
341 void *v;
342 int tag, size, item;
343
344 l = be32toh(dev->AllocatedOffset);
345 p = res->DevicePnPHeap + l;
346
347 pna->pna_iot = paa->paa_iot;
348 pna->pna_memt = paa->paa_memt;
349 pna->pna_ic = paa->paa_ic;
350 pnp_devid_to_string(id->DevId, pna->pna_devid);
351 pna->basetype = id->BaseType;
352 pna->subtype = id->SubType;
353 pna->interface = id->Interface;
354 pna->pna_ppc_dev = dev;
355 memset(r, 0, sizeof(*r));
356 SIMPLEQ_INIT(&r->mem);
357 SIMPLEQ_INIT(&r->io);
358 SIMPLEQ_INIT(&r->irq);
359 SIMPLEQ_INIT(&r->dma);
360 SIMPLEQ_INIT(&r->iomem);
361 if (p == NULL)
362 return;
363 /* otherwise, we start looking for chipid's */
364 for (; p[0] != END_TAG; p += size) {
365 tag = *p;
366 v = p;
367 if (tag_type(p[0]) == PNP_SMALL) {
368 size = tag_small_count(tag) + 1;
369 item = tag_small_item_name(tag);
370 if (item != SmallVendorItem || p[1] != 1)
371 continue;
372 pack = v;
373 pna->chipid = le16dec(&pack->Name[0]);
374 pna->chipmfg0 = pack->VendorID0;
375 pna->chipmfg1 = pack->VendorID1;
376 break;
377 } else {
378 /* Large */
379 size = (p[1] | (p[2] << 8)) + 3 /* tag + length */;
380 }
381 }
382 }
383
384 static int
385 pnpbus_search(struct device *parent, struct cfdata *cf,
386 const int *ldesc, void *aux)
387 {
388 struct pnpbus_dev_attach_args pna;
389 struct pnpbus_attach_args *paa = aux;
390 PPC_DEVICE *ppc_dev;
391 int i;
392 uint32_t ndev;
393
394 ndev = be32toh(res->ActualNumDevices);
395 ppc_dev = res->Devices;
396
397 for (i = 0; i < ((ndev > MAX_DEVICES) ? MAX_DEVICES : ndev); i++) {
398 pnp_getpna(&pna, paa, &ppc_dev[i]);
399 if (config_match(parent, cf, &pna) > 0)
400 config_attach(parent, cf, &pna, pnpbus_print);
401 }
402
403 return 0;
404 }
405
406 static void
407 pnpbus_printres(struct pnpresources *r)
408 {
409 struct pnpbus_io *io;
410 struct pnpbus_mem *mem;
411 struct pnpbus_irq *irq;
412 struct pnpbus_dma *dma;
413 int p = 0;
414
415 if (!SIMPLEQ_EMPTY(&r->mem)) {
416 aprint_normal("mem");
417 SIMPLEQ_FOREACH(mem, &r->mem, next) {
418 aprint_normal(" 0x%x", mem->minbase);
419 if (mem->len > 1)
420 aprint_normal("-0x%x",
421 mem->minbase + mem->len - 1);
422 }
423 p++;
424 }
425 if (!SIMPLEQ_EMPTY(&r->io)) {
426 if (p++)
427 aprint_normal(", ");
428 aprint_normal("port");
429 SIMPLEQ_FOREACH(io, &r->io, next) {
430 aprint_normal(" 0x%x", io->minbase);
431 if (io->len > 1)
432 aprint_normal("-0x%x",
433 io->minbase + io->len - 1);
434 }
435 }
436 if (!SIMPLEQ_EMPTY(&r->iomem)) {
437 if (p++)
438 aprint_normal(", ");
439 aprint_normal("iomem");
440 SIMPLEQ_FOREACH(mem, &r->iomem, next) {
441 aprint_normal(" 0x%x", mem->minbase);
442 if (mem->len > 1)
443 aprint_normal("-0x%x",
444 mem->minbase + mem->len - 1);
445 }
446 p++;
447 }
448 if (!SIMPLEQ_EMPTY(&r->irq)) {
449 if (p++)
450 aprint_normal(", ");
451 aprint_normal("irq");
452 SIMPLEQ_FOREACH(irq, &r->irq, next) {
453 aprint_normal(" %d", ffs(irq->mask) - 1);
454 }
455 }
456 if (!SIMPLEQ_EMPTY(&r->dma)) {
457 if (p++)
458 aprint_normal(", ");
459 aprint_normal("DMA");
460 SIMPLEQ_FOREACH(dma, &r->dma, next) {
461 aprint_normal(" %d", ffs(dma->mask) - 1);
462 }
463 }
464 }
465
466 void
467 pnpbus_print_devres(struct pnpbus_dev_attach_args *pna)
468 {
469 aprint_normal(": ");
470 pnpbus_printres(&pna->pna_res);
471 }
472
473 static int
474 pnpbus_print(void *args, const char *name)
475 {
476 struct pnpbus_dev_attach_args *pna = args;
477
478 pnpbus_print_devres(pna);
479 return (UNCONF);
480 }
481
482 /*
483 * Set up an interrupt handler to start being called.
484 */
485 void *
486 pnpbus_intr_establish(int idx, int level, int (*ih_fun)(void *),
487 void *ih_arg, struct pnpresources *r)
488 {
489 struct pnpbus_irq *irq;
490 int irqnum, type;
491
492 if (idx >= r->numirq)
493 return 0;
494
495 irq = SIMPLEQ_FIRST(&r->irq);
496 while (idx--)
497 irq = SIMPLEQ_NEXT(irq, next);
498
499 irqnum = ffs(irq->mask) - 1;
500 type = (irq->flags & 0x0c) ? IST_LEVEL : IST_EDGE;
501
502 return (void *)intr_establish(irqnum, type, level, ih_fun, ih_arg);
503 }
504
505 /*
506 * Deregister an interrupt handler.
507 */
508 void
509 pnpbus_intr_disestablish(void *arg)
510 {
511
512 intr_disestablish(arg);
513 }
514
515 int
516 pnpbus_getirqnum(struct pnpresources *r, int idx, int *irqp, int *istp)
517 {
518 struct pnpbus_irq *irq;
519
520 if (idx >= r->numirq)
521 return EINVAL;
522
523 irq = SIMPLEQ_FIRST(&r->irq);
524 while (idx--)
525 irq = SIMPLEQ_NEXT(irq, next);
526
527 if (irqp != NULL)
528 *irqp = ffs(irq->mask) - 1;
529 if (istp != NULL)
530 *istp = (irq->flags &0x0c) ? IST_LEVEL : IST_EDGE;
531 return 0;
532 }
533
534 int
535 pnpbus_getdmachan(struct pnpresources *r, int idx, int *chanp)
536 {
537 struct pnpbus_dma *dma;
538
539 if (idx >= r->numdma)
540 return EINVAL;
541
542 dma = SIMPLEQ_FIRST(&r->dma);
543 while (idx--)
544 dma = SIMPLEQ_NEXT(dma, next);
545
546 if (chanp != NULL)
547 *chanp = ffs(dma->mask) - 1;
548 return 0;
549 }
550
551 int
552 pnpbus_getioport(struct pnpresources *r, int idx, int *basep, int *sizep)
553 {
554 struct pnpbus_io *io;
555
556 if (idx >= r->numio)
557 return EINVAL;
558
559 io = SIMPLEQ_FIRST(&r->io);
560 while (idx--)
561 io = SIMPLEQ_NEXT(io, next);
562
563 if (basep)
564 *basep = io->minbase;
565 if (sizep)
566 *sizep = io->len;
567 return 0;
568 }
569
570 int
571 pnpbus_io_map(struct pnpresources *r, int idx, bus_space_tag_t *tagp,
572 bus_space_handle_t *hdlp)
573 {
574 struct pnpbus_io *io;
575
576 if (idx >= r->numio)
577 return EINVAL;
578
579 io = SIMPLEQ_FIRST(&r->io);
580 while (idx--)
581 io = SIMPLEQ_NEXT(io, next);
582
583 *tagp = &prep_isa_io_space_tag;
584 return (bus_space_map(&prep_isa_io_space_tag, io->minbase, io->len,
585 0, hdlp));
586 }
587
588 void
589 pnpbus_io_unmap(struct pnpresources *r, int idx, bus_space_tag_t tag,
590 bus_space_handle_t hdl)
591 {
592 struct pnpbus_io *io;
593
594 if (idx >= r->numio)
595 return;
596
597 io = SIMPLEQ_FIRST(&r->io);
598 while (idx--)
599 io = SIMPLEQ_NEXT(io, next);
600
601 bus_space_unmap(tag, hdl, io->len);
602 }
603
604 int
605 pnpbus_getiomem(struct pnpresources *r, int idx, int *basep, int *sizep)
606 {
607 struct pnpbus_mem *mem;
608
609 if (idx >= r->numiomem)
610 return EINVAL;
611
612 mem = SIMPLEQ_FIRST(&r->iomem);
613 while (idx--)
614 mem = SIMPLEQ_NEXT(mem, next);
615
616 if (basep)
617 *basep = mem->minbase;
618 if (sizep)
619 *sizep = mem->len;
620 return 0;
621 }
622
623 int
624 pnpbus_iomem_map(struct pnpresources *r, int idx, bus_space_tag_t *tagp,
625 bus_space_handle_t *hdlp)
626 {
627 struct pnpbus_mem *mem;
628
629 if (idx >= r->numiomem)
630 return EINVAL;
631
632 mem = SIMPLEQ_FIRST(&r->iomem);
633 while (idx--)
634 mem = SIMPLEQ_NEXT(mem, next);
635
636 *tagp = &prep_isa_mem_space_tag;
637 return (bus_space_map(&prep_isa_mem_space_tag, mem->minbase, mem->len,
638 0, hdlp));
639 }
640
641 void
642 pnpbus_iomem_unmap(struct pnpresources *r, int idx, bus_space_tag_t tag,
643 bus_space_handle_t hdl)
644 {
645 struct pnpbus_mem *mem;
646
647 if (idx >= r->numiomem)
648 return;
649
650 mem = SIMPLEQ_FIRST(&r->mem);
651 while (idx--)
652 mem = SIMPLEQ_NEXT(mem, next);
653
654 bus_space_unmap(tag, hdl, mem->len);
655 }
656