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