1 /* $NetBSD: pnpbus.c,v 1.8 2008/04/28 19:01:45 garbled Exp $ */
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: pnpbus.c,v 1.8 2008/04/28 19:01:45 garbled Exp $");
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/device.h>
38 #include <sys/extent.h>
39 #include <sys/malloc.h>
41 #include <machine/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>
50 #include <dev/isa/isareg.h>
52 #include <prep/pnpbus/pnpbusvar.h>
56 static int pnpbus_match(struct device
*, struct cfdata
*, void *);
57 static void pnpbus_attach(struct device
*, struct device
*, void *);
58 static int pnpbus_print(void *, const char *);
59 static int pnpbus_search(struct device
*, struct cfdata
*,
62 CFATTACH_DECL(pnpbus
, sizeof(struct pnpbus_softc
),
63 pnpbus_match
, pnpbus_attach
, NULL
, NULL
);
65 struct pnpbus_softc
*pnpbus_softc
;
66 extern struct cfdriver pnpbus_cd
;
69 pnpbus_match(struct device
*parent
, struct cfdata
*cf
, void *aux
)
71 struct pnpbus_attach_args
*paa
= aux
;
73 if (paa
->paa_name
!= NULL
&& strcmp(paa
->paa_name
, "pnpbus") == 0)
79 pnpbus_attach(struct device
*parent
, struct device
*self
, void *aux
)
81 struct pnpbus_softc
*sc
= (struct pnpbus_softc
*)self
;
82 struct pnpbus_attach_args
*paa
= aux
;
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
;
93 isa_dmainit(sc
->sc_ic
, sc
->sc_iot
, sc
->sc_dmat
, self
);
96 (void)config_search_ia(pnpbus_search
, self
, "pnpbus", aux
);
100 pnp_newirq(void *v
, struct pnpresources
*r
, int size
)
102 struct _S4_Pack
*p
= v
;
103 struct pnpbus_irq
*irq
;
105 irq
= malloc(sizeof(struct pnpbus_irq
), M_DEVBUF
, M_NOWAIT
);
107 irq
->mask
= le16dec(&p
->IRQMask
[0]);
110 irq
->flags
= p
->IRQInfo
;
114 SIMPLEQ_INSERT_TAIL(&r
->irq
, irq
, next
);
121 pnp_newdma(void *v
, struct pnpresources
*r
, int size
)
123 struct _S5_Pack
*p
= v
;
124 struct pnpbus_dma
*dma
;
126 dma
= malloc(sizeof(struct pnpbus_dma
), M_DEVBUF
, M_NOWAIT
);
128 dma
->mask
= le16dec(&p
->DMAMask
);
130 dma
->flags
= p
->DMAInfo
;
134 SIMPLEQ_INSERT_TAIL(&r
->dma
, dma
, next
);
141 pnp_newioport(void *v
, struct pnpresources
*r
, int size
)
143 struct _S8_Pack
*p
= v
;
144 struct pnpbus_io
*io
;
147 io
= malloc(sizeof(struct pnpbus_io
), M_DEVBUF
, M_NOWAIT
);
148 mask
= p
->IOInfo
& ISAAddr16bit
? 0xffff : 0x03ff;
149 io
->minbase
= (p
->RangeMin
[0] | (p
->RangeMin
[1] << 8)) & mask
;
150 io
->maxbase
= (p
->RangeMax
[0] | (p
->RangeMax
[1] << 8)) & mask
;
151 io
->align
= p
->IOAlign
;
153 io
->flags
= p
->IOInfo
;
155 SIMPLEQ_INSERT_TAIL(&r
->io
, io
, next
);
162 pnp_newfixedioport(void *v
, struct pnpresources
*r
, int size
)
164 struct _S9_Pack
*p
= v
;
165 struct pnpbus_io
*io
;
167 io
= malloc(sizeof(struct pnpbus_io
), M_DEVBUF
, M_NOWAIT
);
168 io
->minbase
= (p
->Range
[0] | (p
->Range
[1] << 8)) & 0x3ff;
174 SIMPLEQ_INSERT_TAIL(&r
->io
, io
, next
);
181 pnp_newiomem(void *v
, struct pnpresources
*r
, int size
)
183 struct pnpbus_mem
*mem
;
184 struct _L1_Pack
*pack
= v
;
186 if (pack
->Count0
>= 0x9) {
187 mem
= malloc(sizeof(struct pnpbus_mem
), M_DEVBUF
, M_NOWAIT
);
188 mem
->minbase
= (pack
->Data
[2] << 16) | (pack
->Data
[1] << 8);
189 mem
->maxbase
= (pack
->Data
[4] << 16) | (pack
->Data
[3] << 8);
190 mem
->align
= (pack
->Data
[6] << 8) | pack
->Data
[5];
191 mem
->len
= (pack
->Data
[8] << 16) | (pack
->Data
[7] << 8);
192 mem
->flags
= pack
->Data
[0];
193 SIMPLEQ_INSERT_TAIL(&r
->iomem
, mem
, next
);
201 pnp_newaddr(void *v
, struct pnpresources
*r
, int size
)
203 struct pnpbus_io
*io
;
204 struct pnpbus_mem
*mem
;
205 struct _L4_Pack
*pack
= v
;
206 struct _L4_PPCPack
*p
= &pack
->L4_Data
.L4_PPCPack
;
208 if (p
->PPCData
[0] == 1) {/* type IO */
209 io
= malloc(sizeof(struct pnpbus_io
), M_DEVBUF
, M_NOWAIT
);
210 io
->minbase
= (uint16_t)le64dec(&p
->PPCData
[4]);
212 io
->align
= p
->PPCData
[1];
213 io
->len
= (uint16_t)le64dec(&p
->PPCData
[12]);
215 SIMPLEQ_INSERT_TAIL(&r
->io
, io
, next
);
219 } else if (p
->PPCData
[0] == 2) {
220 mem
= malloc(sizeof(struct pnpbus_mem
), M_DEVBUF
, M_NOWAIT
);
221 mem
->minbase
= (uint32_t)le64dec(&p
->PPCData
[4]);
223 mem
->align
= p
->PPCData
[1];
224 mem
->len
= (uint32_t)le64dec(&p
->PPCData
[12]);
226 SIMPLEQ_INSERT_TAIL(&r
->mem
, mem
, next
);
235 pnp_newcompatid(void *v
, struct pnpresources
*r
, int size
)
237 struct _S3_Pack
*p
= v
;
238 struct pnpbus_compatid
*id
;
241 id
= malloc(sizeof(*id
), M_DEVBUF
, M_NOWAIT
);
242 cid
= le32dec(p
->CompatId
);
243 pnp_devid_to_string(cid
, id
->idstr
);
244 id
->next
= r
->compatids
;
251 * Call if match succeeds. This way we don't allocate lots of ram
252 * for structures we never use if the device isn't attached.
256 pnpbus_scan(struct pnpbus_dev_attach_args
*pna
, PPC_DEVICE
*dev
)
258 struct pnpresources
*r
= &pna
->pna_res
;
264 l
= be32toh(dev
->AllocatedOffset
);
265 p
= res
->DevicePnPHeap
+ l
;
270 for (; p
[0] != END_TAG
; p
+= size
) {
273 if (tag_type(p
[0]) == PNP_SMALL
) {
274 size
= tag_small_count(tag
) + 1;
275 item
= tag_small_item_name(tag
);
278 pnp_newirq(v
, r
, size
);
281 pnp_newdma(v
, r
, size
);
284 pnp_newioport(v
, r
, size
);
287 pnp_newfixedioport(v
, r
, size
);
291 struct _L4_Pack
*pack
= v
;
292 struct _L4_PPCPack
*pa
= &pack
->L4_Data
.L4_PPCPack
;
295 size
= (q
[1] | (q
[2] << 8)) + 3 /* tag + length */;
296 item
= tag_large_item_name(tag
);
297 if (item
== LargeVendorItem
&&
298 pa
->Type
== LV_GenericAddress
)
299 pnp_newaddr(v
, r
, size
);
300 else if (item
== MemoryRange
)
301 pnp_newiomem(v
, r
, size
);
305 /* scan for compatid's */
307 l
= be32toh(dev
->CompatibleOffset
);
308 p
= res
->DevicePnPHeap
+ l
;
313 for (; p
[0] != END_TAG
; p
+= size
) {
316 if (tag_type(p
[0]) == PNP_SMALL
) {
317 size
= tag_small_count(tag
) + 1;
318 item
= tag_small_item_name(tag
);
320 case CompatibleDevice
:
321 pnp_newcompatid(v
, r
, size
);
326 size
= (q
[1] | (q
[2] << 8)) + 3 /* tag + length */;
333 * Setup the basic pna structure.
337 pnp_getpna(struct pnpbus_dev_attach_args
*pna
, struct pnpbus_attach_args
*paa
,
340 DEVICE_ID
*id
= &dev
->DeviceId
;
341 struct pnpresources
*r
= &pna
->pna_res
;
348 l
= be32toh(dev
->AllocatedOffset
);
349 p
= res
->DevicePnPHeap
+ l
;
351 pna
->pna_iot
= paa
->paa_iot
;
352 pna
->pna_memt
= paa
->paa_memt
;
353 pna
->pna_ic
= paa
->paa_ic
;
354 pna
->pna_dmat
= paa
->paa_dmat
;
355 pnp_devid_to_string(id
->DevId
, pna
->pna_devid
);
356 pna
->basetype
= id
->BaseType
;
357 pna
->subtype
= id
->SubType
;
358 pna
->interface
= id
->Interface
;
359 pna
->pna_ppc_dev
= dev
;
360 memset(r
, 0, sizeof(*r
));
361 SIMPLEQ_INIT(&r
->mem
);
362 SIMPLEQ_INIT(&r
->io
);
363 SIMPLEQ_INIT(&r
->irq
);
364 SIMPLEQ_INIT(&r
->dma
);
365 SIMPLEQ_INIT(&r
->iomem
);
368 /* otherwise, we start looking for chipid's */
369 for (; p
[0] != END_TAG
; p
+= size
) {
372 if (tag_type(p
[0]) == PNP_SMALL
) {
373 size
= tag_small_count(tag
) + 1;
374 item
= tag_small_item_name(tag
);
375 if (item
!= SmallVendorItem
|| p
[1] != 1)
378 pna
->chipid
= le16dec(&pack
->Name
[0]);
379 pna
->chipmfg0
= pack
->VendorID0
;
380 pna
->chipmfg1
= pack
->VendorID1
;
384 size
= (p
[1] | (p
[2] << 8)) + 3 /* tag + length */;
390 pnpbus_search(struct device
*parent
, struct cfdata
*cf
,
391 const int *ldesc
, void *aux
)
393 struct pnpbus_dev_attach_args pna
;
394 struct pnpbus_attach_args
*paa
= aux
;
399 ndev
= be32toh(res
->ActualNumDevices
);
400 ppc_dev
= res
->Devices
;
402 for (i
= 0; i
< ((ndev
> MAX_DEVICES
) ? MAX_DEVICES
: ndev
); i
++) {
403 pnp_getpna(&pna
, paa
, &ppc_dev
[i
]);
404 if (config_match(parent
, cf
, &pna
) > 0)
405 config_attach(parent
, cf
, &pna
, pnpbus_print
);
412 pnpbus_printres(struct pnpresources
*r
)
414 struct pnpbus_io
*io
;
415 struct pnpbus_mem
*mem
;
416 struct pnpbus_irq
*irq
;
417 struct pnpbus_dma
*dma
;
420 if (!SIMPLEQ_EMPTY(&r
->mem
)) {
421 aprint_normal("mem");
422 SIMPLEQ_FOREACH(mem
, &r
->mem
, next
) {
423 aprint_normal(" 0x%x", mem
->minbase
);
425 aprint_normal("-0x%x",
426 mem
->minbase
+ mem
->len
- 1);
430 if (!SIMPLEQ_EMPTY(&r
->io
)) {
433 aprint_normal("port");
434 SIMPLEQ_FOREACH(io
, &r
->io
, next
) {
435 aprint_normal(" 0x%x", io
->minbase
);
437 aprint_normal("-0x%x",
438 io
->minbase
+ io
->len
- 1);
441 if (!SIMPLEQ_EMPTY(&r
->iomem
)) {
444 aprint_normal("iomem");
445 SIMPLEQ_FOREACH(mem
, &r
->iomem
, next
) {
446 aprint_normal(" 0x%x", mem
->minbase
);
448 aprint_normal("-0x%x",
449 mem
->minbase
+ mem
->len
- 1);
453 if (!SIMPLEQ_EMPTY(&r
->irq
)) {
456 aprint_normal("irq");
457 SIMPLEQ_FOREACH(irq
, &r
->irq
, next
) {
458 aprint_normal(" %d", ffs(irq
->mask
) - 1);
461 if (!SIMPLEQ_EMPTY(&r
->dma
)) {
464 aprint_normal("DMA");
465 SIMPLEQ_FOREACH(dma
, &r
->dma
, next
) {
466 aprint_normal(" %d", ffs(dma
->mask
) - 1);
472 pnpbus_print_devres(struct pnpbus_dev_attach_args
*pna
)
475 pnpbus_printres(&pna
->pna_res
);
479 pnpbus_print(void *args
, const char *name
)
481 struct pnpbus_dev_attach_args
*pna
= args
;
483 pnpbus_print_devres(pna
);
488 * Set up an interrupt handler to start being called.
491 pnpbus_intr_establish(int idx
, int level
, int tover
, int (*ih_fun
)(void *),
492 void *ih_arg
, struct pnpresources
*r
)
494 struct pnpbus_irq
*irq
;
497 if (idx
>= r
->numirq
)
500 irq
= SIMPLEQ_FIRST(&r
->irq
);
502 irq
= SIMPLEQ_NEXT(irq
, next
);
504 irqnum
= ffs(irq
->mask
) - 1;
505 type
= (irq
->flags
& 0x0c) ? IST_LEVEL
: IST_EDGE
;
506 if (tover
!= IST_PNP
)
509 return (void *)intr_establish(irqnum
, type
, level
, ih_fun
, ih_arg
);
513 * Deregister an interrupt handler.
516 pnpbus_intr_disestablish(void *arg
)
519 intr_disestablish(arg
);
523 pnpbus_getirqnum(struct pnpresources
*r
, int idx
, int *irqp
, int *istp
)
525 struct pnpbus_irq
*irq
;
527 if (idx
>= r
->numirq
)
530 irq
= SIMPLEQ_FIRST(&r
->irq
);
532 irq
= SIMPLEQ_NEXT(irq
, next
);
535 *irqp
= ffs(irq
->mask
) - 1;
537 *istp
= (irq
->flags
&0x0c) ? IST_LEVEL
: IST_EDGE
;
542 pnpbus_getdmachan(struct pnpresources
*r
, int idx
, int *chanp
)
544 struct pnpbus_dma
*dma
;
546 if (idx
>= r
->numdma
)
549 dma
= SIMPLEQ_FIRST(&r
->dma
);
551 dma
= SIMPLEQ_NEXT(dma
, next
);
554 *chanp
= ffs(dma
->mask
) - 1;
559 pnpbus_getioport(struct pnpresources
*r
, int idx
, int *basep
, int *sizep
)
561 struct pnpbus_io
*io
;
566 io
= SIMPLEQ_FIRST(&r
->io
);
568 io
= SIMPLEQ_NEXT(io
, next
);
571 *basep
= io
->minbase
;
578 pnpbus_io_map(struct pnpresources
*r
, int idx
, bus_space_tag_t
*tagp
,
579 bus_space_handle_t
*hdlp
)
581 struct pnpbus_io
*io
;
586 io
= SIMPLEQ_FIRST(&r
->io
);
588 io
= SIMPLEQ_NEXT(io
, next
);
590 *tagp
= &genppc_isa_io_space_tag
;
591 return (bus_space_map(&genppc_isa_io_space_tag
, io
->minbase
, io
->len
,
596 pnpbus_io_unmap(struct pnpresources
*r
, int idx
, bus_space_tag_t tag
,
597 bus_space_handle_t hdl
)
599 struct pnpbus_io
*io
;
604 io
= SIMPLEQ_FIRST(&r
->io
);
606 io
= SIMPLEQ_NEXT(io
, next
);
608 bus_space_unmap(tag
, hdl
, io
->len
);
612 pnpbus_getiomem(struct pnpresources
*r
, int idx
, int *basep
, int *sizep
)
614 struct pnpbus_mem
*mem
;
616 if (idx
>= r
->numiomem
)
619 mem
= SIMPLEQ_FIRST(&r
->iomem
);
621 mem
= SIMPLEQ_NEXT(mem
, next
);
624 *basep
= mem
->minbase
;
631 pnpbus_iomem_map(struct pnpresources
*r
, int idx
, bus_space_tag_t
*tagp
,
632 bus_space_handle_t
*hdlp
)
634 struct pnpbus_mem
*mem
;
636 if (idx
>= r
->numiomem
)
639 mem
= SIMPLEQ_FIRST(&r
->iomem
);
641 mem
= SIMPLEQ_NEXT(mem
, next
);
643 *tagp
= &genppc_isa_mem_space_tag
;
644 return (bus_space_map(&genppc_isa_mem_space_tag
, mem
->minbase
, mem
->len
,
649 pnpbus_iomem_unmap(struct pnpresources
*r
, int idx
, bus_space_tag_t tag
,
650 bus_space_handle_t hdl
)
652 struct pnpbus_mem
*mem
;
654 if (idx
>= r
->numiomem
)
657 mem
= SIMPLEQ_FIRST(&r
->mem
);
659 mem
= SIMPLEQ_NEXT(mem
, next
);
661 bus_space_unmap(tag
, hdl
, mem
->len
);