pnpbus.c revision 1.2 1 /* $NetBSD: pnpbus.c,v 1.2 2006/03/16 17:43:34 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.2 2006/03/16 17:43:34 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
56 #include <dev/isa/isareg.h>
57
58 #include <prep/pnpbus/pnpbusvar.h>
59
60 static int pnpbus_match(struct device *, struct cfdata *, void *);
61 static void pnpbus_attach(struct device *, struct device *, void *);
62 static int pnpbus_print(void *, const char *);
63 static int pnpbus_search(struct device *, struct cfdata *,
64 const int *, void *);
65
66 CFATTACH_DECL(pnpbus, sizeof(struct pnpbus_softc),
67 pnpbus_match, pnpbus_attach, NULL, NULL);
68
69 struct pnpbus_softc *pnpbus_softc;
70 extern struct cfdriver pnpbus_cd;
71
72 static int
73 pnpbus_match(struct device *parent, struct cfdata *cf, void *aux)
74 {
75 return 1;
76 }
77
78 static void
79 pnpbus_attach(struct device *parent, struct device *self, void *aux)
80 {
81 struct pnpbus_softc *sc = (struct pnpbus_softc *)self;
82 struct pnpbus_attach_args *paa = aux;
83
84 printf("\n");
85
86 pnpbus_softc = sc;
87 sc->sc_ic = paa->paa_ic;
88 sc->sc_iot = paa->paa_iot;
89 sc->sc_memt = paa->paa_memt;
90
91 (void)config_search_ia(pnpbus_search, self, "pnpbus", aux);
92 }
93
94 static int
95 pnp_newirq(void *v, struct pnpresources *r, int size)
96 {
97 struct _S4_Pack *p = v;
98 struct pnpbus_irq *irq;
99
100 irq = malloc(sizeof(struct pnpbus_irq), M_DEVBUF, M_NOWAIT);
101
102 irq->mask = le16dec(&p->IRQMask[0]);
103
104 /* XXX default to level, not edge, based on the powerstack E0 */
105 if (size > 2)
106 irq->flags = p->IRQInfo;
107 else
108 irq->flags = 0x0c;
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_newaddr(void *v, struct pnpresources *r, int size)
178 {
179 struct pnpbus_io *io;
180 struct pnpbus_mem *mem;
181 struct _L4_Pack *pack = v;
182 struct _L4_PPCPack *p = &pack->L4_Data.L4_PPCPack;
183
184 if (p->PPCData[0] == 1) {/* type IO */
185 io = malloc(sizeof(struct pnpbus_io), M_DEVBUF, M_NOWAIT);
186 io->minbase = (uint16_t)le64dec(&p->PPCData[4]);
187 io->maxbase = -1;
188 io->align = p->PPCData[1];
189 io->len = (uint16_t)le64dec(&p->PPCData[12]);
190 io->flags = 0;
191 SIMPLEQ_INSERT_TAIL(&r->io, io, next);
192 r->numio++;
193
194 return 0;
195 } else if (p->PPCData[0] == 2) {
196 mem = malloc(sizeof(struct pnpbus_mem), M_DEVBUF, M_NOWAIT);
197 mem->minbase = (uint32_t)le64dec(&p->PPCData[4]);
198 mem->maxbase = -1;
199 mem->align = p->PPCData[1];
200 mem->len = (uint32_t)le64dec(&p->PPCData[12]);
201 mem->flags = 0;
202 SIMPLEQ_INSERT_TAIL(&r->mem, mem, next);
203 r->nummem++;
204
205 return 0;
206 } else
207 return -1;
208 }
209
210 static int
211 pnp_newcompatid(void *v, struct pnpresources *r, int size)
212 {
213 struct _S3_Pack *p = v;
214 struct pnpbus_compatid *id;
215 uint32_t cid;
216
217 id = malloc(sizeof(*id), M_DEVBUF, M_NOWAIT);
218 cid = le32dec(p->CompatId);
219 pnp_devid_to_string(cid, id->idstr);
220 id->next = r->compatids;
221 r->compatids = id;
222
223 return 0;
224 }
225
226 /*
227 * Call if match succeeds. This way we don't allocate lots of ram
228 * for structures we never use if the device isn't attached.
229 */
230
231 int
232 pnpbus_scan(struct pnpbus_dev_attach_args *pna, PPC_DEVICE *dev)
233 {
234 struct pnpresources *r = &pna->pna_res;
235 uint32_t l;
236 uint8_t *p, *q;
237 void *v;
238 int tag, size, item;
239
240 l = be32toh(dev->AllocatedOffset);
241 p = res->DevicePnPHeap + l;
242
243 if (p == NULL)
244 return -1;
245
246 for (; p[0] != END_TAG; p += size) {
247 tag = *p;
248 v = p;
249 if (tag_type(p[0]) == PNP_SMALL) {
250 size = tag_small_count(tag) + 1;
251 item = tag_small_item_name(tag);
252 switch (item) {
253 case IRQFormat:
254 pnp_newirq(v, r, size);
255 break;
256 case DMAFormat:
257 pnp_newdma(v, r, size);
258 break;
259 case IOPort:
260 pnp_newioport(v, r, size);
261 break;
262 case FixedIOPort:
263 pnp_newfixedioport(v, r, size);
264 break;
265 }
266 } else {
267 struct _L4_Pack *pack = v;
268 struct _L4_PPCPack *pa = &pack->L4_Data.L4_PPCPack;
269
270 q = p;
271 size = (q[1] | (q[2] << 8)) + 3 /* tag + length */;
272 item = tag_large_item_name(tag);
273 if (item == LargeVendorItem &&
274 pa->Type == LV_GenericAddress)
275 pnp_newaddr(v, r, size);
276 }
277 }
278
279 /* scan for compatid's */
280
281 l = be32toh(dev->CompatibleOffset);
282 p = res->DevicePnPHeap + l;
283
284 if (p == NULL)
285 return -1;
286
287 for (; p[0] != END_TAG; p += size) {
288 tag = *p;
289 v = p;
290 if (tag_type(p[0]) == PNP_SMALL) {
291 size = tag_small_count(tag) + 1;
292 item = tag_small_item_name(tag);
293 switch (item) {
294 case CompatibleDevice:
295 pnp_newcompatid(v, r, size);
296 break;
297 }
298 } else {
299 q = p;
300 size = (q[1] | (q[2] << 8)) + 3 /* tag + length */;
301 }
302 }
303 return 0;
304 }
305
306 /*
307 * Setup the basic pna structure.
308 */
309
310 static void
311 pnp_getpna(struct pnpbus_dev_attach_args *pna, struct pnpbus_attach_args *paa,
312 PPC_DEVICE *dev)
313 {
314 uint32_t l;
315 DEVICE_ID *id = &dev->DeviceId;
316 struct pnpresources *r = &pna->pna_res;
317
318 l = be32toh(dev->AllocatedOffset);
319
320 pna->pna_iot = paa->paa_iot;
321 pna->pna_memt = paa->paa_memt;
322 pna->pna_ic = paa->paa_ic;
323 pnp_devid_to_string(id->DevId, pna->pna_devid);
324 pna->pna_ppc_dev = dev;
325 memset(r, 0, sizeof(*r));
326 SIMPLEQ_INIT(&r->mem);
327 SIMPLEQ_INIT(&r->io);
328 SIMPLEQ_INIT(&r->irq);
329 SIMPLEQ_INIT(&r->dma);
330 }
331
332 static int
333 pnpbus_search(struct device *parent, struct cfdata *cf,
334 const int *ldesc, void *aux)
335 {
336 struct pnpbus_dev_attach_args pna;
337 struct pnpbus_attach_args *paa = aux;
338 PPC_DEVICE *ppc_dev;
339 int i;
340 uint32_t ndev;
341
342 ndev = be32toh(res->ActualNumDevices);
343 ppc_dev = res->Devices;
344
345 for (i = 0; i < ((ndev > MAX_DEVICES) ? MAX_DEVICES : ndev); i++) {
346 pnp_getpna(&pna, paa, &ppc_dev[i]);
347 if (config_match(parent, cf, &pna) > 0)
348 config_attach(parent, cf, &pna, pnpbus_print);
349 }
350
351 return 0;
352 }
353
354 static void
355 pnpbus_printres(struct pnpresources *r)
356 {
357 struct pnpbus_io *io;
358 struct pnpbus_mem *mem;
359 struct pnpbus_irq *irq;
360 struct pnpbus_dma *dma;
361 int p = 0;
362
363 if (!SIMPLEQ_EMPTY(&r->mem)) {
364 aprint_normal("mem");
365 SIMPLEQ_FOREACH(mem, &r->mem, next) {
366 aprint_normal(" 0x%x", mem->minbase);
367 if (mem->len > 1)
368 aprint_normal("-0x%x",
369 mem->minbase + mem->len - 1);
370 }
371 p++;
372 }
373 if (!SIMPLEQ_EMPTY(&r->io)) {
374 if (p++)
375 aprint_normal(", ");
376 aprint_normal("port");
377 SIMPLEQ_FOREACH(io, &r->io, next) {
378 aprint_normal(" 0x%x", io->minbase);
379 if (io->len > 1)
380 aprint_normal("-0x%x",
381 io->minbase + io->len - 1);
382 }
383 }
384 if (!SIMPLEQ_EMPTY(&r->irq)) {
385 if (p++)
386 aprint_normal(", ");
387 aprint_normal("irq");
388 SIMPLEQ_FOREACH(irq, &r->irq, next) {
389 aprint_normal(" %d", ffs(irq->mask) - 1);
390 }
391 }
392 if (!SIMPLEQ_EMPTY(&r->dma)) {
393 if (p++)
394 aprint_normal(", ");
395 aprint_normal("DMA");
396 SIMPLEQ_FOREACH(dma, &r->dma, next) {
397 aprint_normal(" %d", ffs(dma->mask) - 1);
398 }
399 }
400 }
401
402 void
403 pnpbus_print_devres(struct pnpbus_dev_attach_args *pna)
404 {
405 aprint_normal(": ");
406 pnpbus_printres(&pna->pna_res);
407 aprint_normal("\n");
408 }
409
410 static int
411 pnpbus_print(void *args, const char *name)
412 {
413 struct pnpbus_dev_attach_args *pna = args;
414
415 pnpbus_print_devres(pna);
416 return (UNCONF);
417 }
418
419 /*
420 * Set up an interrupt handler to start being called.
421 */
422 void *
423 pnpbus_intr_establish(int idx, int level, int (*ih_fun)(void *),
424 void *ih_arg, struct pnpresources *r)
425 {
426 struct pnpbus_irq *irq;
427 int irqnum, type;
428
429 if (idx >= r->numirq)
430 return 0;
431
432 irq = SIMPLEQ_FIRST(&r->irq);
433 while (idx--)
434 irq = SIMPLEQ_NEXT(irq, next);
435
436 irqnum = ffs(irq->mask) - 1;
437 type = (irq->flags & 0x0c) ? IST_LEVEL : IST_EDGE;
438
439 return (void *)intr_establish(irqnum, type, level, ih_fun, ih_arg);
440 }
441
442 /*
443 * Deregister an interrupt handler.
444 */
445 void
446 pnpbus_intr_disestablish(void *arg)
447 {
448
449 intr_disestablish(arg);
450 }
451
452 int
453 pnpbus_getirqnum(struct pnpresources *r, int idx, int *irqp, int *istp)
454 {
455 struct pnpbus_irq *irq;
456
457 if (idx >= r->numirq)
458 return EINVAL;
459
460 irq = SIMPLEQ_FIRST(&r->irq);
461 while (idx--)
462 irq = SIMPLEQ_NEXT(irq, next);
463
464 if (irqp != NULL)
465 *irqp = ffs(irq->mask) - 1;
466 if (istp != NULL)
467 *istp = (irq->flags &0x0c) ? IST_LEVEL : IST_EDGE;
468 return 0;
469 }
470
471 int
472 pnpbus_getdmachan(struct pnpresources *r, int idx, int *chanp)
473 {
474 struct pnpbus_dma *dma;
475
476 if (idx >= r->numdma)
477 return EINVAL;
478
479 dma = SIMPLEQ_FIRST(&r->dma);
480 while (idx--)
481 dma = SIMPLEQ_NEXT(dma, next);
482
483 if (chanp != NULL)
484 *chanp = ffs(dma->mask) - 1;
485 return 0;
486 }
487
488 int
489 pnpbus_getioport(struct pnpresources *r, int idx, int *basep, int *sizep)
490 {
491 struct pnpbus_io *io;
492
493 if (idx >= r->numio)
494 return EINVAL;
495
496 io = SIMPLEQ_FIRST(&r->io);
497 while (idx--)
498 io = SIMPLEQ_NEXT(io, next);
499
500 if (basep)
501 *basep = io->minbase;
502 if (sizep)
503 *sizep = io->len;
504 return 0;
505 }
506
507 int
508 pnpbus_io_map(struct pnpresources *r, int idx, bus_space_tag_t *tagp,
509 bus_space_handle_t *hdlp)
510 {
511 struct pnpbus_io *io;
512
513 if (idx >= r->numio)
514 return EINVAL;
515
516 io = SIMPLEQ_FIRST(&r->io);
517 while (idx--)
518 io = SIMPLEQ_NEXT(io, next);
519
520 *tagp = &prep_isa_io_space_tag;
521 return (bus_space_map(&prep_isa_io_space_tag, io->minbase, io->len,
522 0, hdlp));
523 }
524
525 void
526 pnpbus_io_unmap(struct pnpresources *r, int idx, bus_space_tag_t tag,
527 bus_space_handle_t hdl)
528 {
529 struct pnpbus_io *io;
530
531 if (idx >= r->numio)
532 return;
533
534 io = SIMPLEQ_FIRST(&r->io);
535 while (idx--)
536 io = SIMPLEQ_NEXT(io, next);
537
538 bus_space_unmap(tag, hdl, io->len);
539 }
540