1 /* $NetBSD: btvmeii.c,v 1.19 2009/05/06 10:34:32 cegger Exp $ */
5 * Matthias Drochner. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions, and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * Driver for the Bit3/SBS PCI-VME adapter Model 2706.
31 * Uses the common Tundra Universe code.
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: btvmeii.c,v 1.19 2009/05/06 10:34:32 cegger Exp $");
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/device.h>
42 #include <dev/pci/pcireg.h>
43 #include <dev/pci/pcivar.h>
44 #include <dev/pci/pcidevs.h>
47 #include <sys/malloc.h>
48 #include <sys/extent.h>
50 #include <dev/pci/ppbreg.h>
52 #include <dev/vme/vmereg.h>
53 #include <dev/vme/vmevar.h>
55 #include <dev/pci/universe_pci_var.h>
57 static int b3_2706_match(device_t
, cfdata_t
, void *);
58 static void b3_2706_attach(device_t
, device_t
, void *);
60 /* exported via tag structs */
61 int b3_2706_map_vme(void *, vme_addr_t
, vme_size_t
,
62 vme_am_t
, vme_datasize_t
, vme_swap_t
,
63 bus_space_tag_t
*, bus_space_handle_t
*, vme_mapresc_t
*);
64 void b3_2706_unmap_vme(void *, vme_mapresc_t
);
66 int b3_2706_vme_probe(void *, vme_addr_t
, vme_size_t
, vme_am_t
,
68 int (*)(void *, bus_space_tag_t
, bus_space_handle_t
),
71 int b3_2706_map_vmeint(void *, int, int, vme_intr_handle_t
*);
72 void *b3_2706_establish_vmeint(void *, vme_intr_handle_t
, int,
73 int (*)(void *), void *);
74 void b3_2706_disestablish_vmeint(void *, void *);
75 void b3_2706_vmeint(void *, int, int);
77 int b3_2706_dmamap_create(void *, vme_size_t
,
78 vme_am_t
, vme_datasize_t
, vme_swap_t
,
79 int, vme_size_t
, vme_addr_t
,
81 void b3_2706_dmamap_destroy(void *, bus_dmamap_t
);
83 int b3_2706_dmamem_alloc(void *, vme_size_t
,
84 vme_am_t
, vme_datasize_t
, vme_swap_t
,
85 bus_dma_segment_t
*, int, int *, int);
86 void b3_2706_dmamem_free(void *, bus_dma_segment_t
*, int);
88 struct b3_2706_vmemaprescs
{
90 unsigned long pcibase
, maplen
;
91 bus_space_handle_t handle
;
95 struct b3_2706_vmeintrhand
{
96 TAILQ_ENTRY(b3_2706_vmeintrhand
) ih_next
;
105 struct b3_2706_softc
{
106 struct device sc_dev
;
107 struct univ_pci_data univdata
;
108 bus_space_tag_t swapt
, vmet
;
109 bus_space_handle_t swaph
;
113 struct b3_2706_vmemaprescs vmemaprescs
[8];
114 struct extent
*vmeext
;
115 char vmemap
[EXTENT_FIXED_STORAGE_SIZE(8)];
117 struct vme_chipset_tag sc_vct
;
119 /* list of VME interrupt handlers */
120 TAILQ_HEAD(, b3_2706_vmeintrhand
) intrhdls
;
124 CFATTACH_DECL(btvmeii
, sizeof(struct b3_2706_softc
),
125 b3_2706_match
, b3_2706_attach
, NULL
, NULL
);
128 * The adapter consists of a DEC PCI-PCI-bridge with two
129 * PCI devices behind it: A Tundra Universe as device 4 and
130 * some FPGA with glue logics as device 8.
131 * As long as the autoconf code doesn't provide more support
132 * for dependant devices, we have to duplicate a part of the
133 * "ppb" functions here.
137 b3_2706_match(device_t parent
, cfdata_t match
, void *aux
)
139 struct pci_attach_args
*pa
= aux
;
140 pci_chipset_tag_t pc
= pa
->pa_pc
;
145 if ((PCI_VENDOR(pa
->pa_id
) != PCI_VENDOR_DEC
)
146 || (PCI_PRODUCT(pa
->pa_id
) != PCI_PRODUCT_DEC_21152
))
149 secbus
= PPB_BUSINFO_SECONDARY(pci_conf_read(pc
, pa
->pa_tag
,
152 printf("b3_2706_match: ppb not configured\n");
156 tag
= pci_make_tag(pc
, secbus
, 4, 0);
157 id
= pci_conf_read(pc
, tag
, PCI_ID_REG
);
159 if ((PCI_VENDOR(id
) != PCI_VENDOR_NEWBRIDGE
)
160 || (PCI_PRODUCT(id
) != PCI_PRODUCT_NEWBRIDGE_CA91CX42
)) {
162 printf("b3_2706_match: no tundra\n");
167 tag
= pci_make_tag(pc
, secbus
, 8, 0);
168 id
= pci_conf_read(pc
, tag
, PCI_ID_REG
);
170 if ((PCI_VENDOR(id
) != PCI_VENDOR_BIT3
)
171 || (PCI_PRODUCT(id
) != PCI_PRODUCT_BIT3_PCIVME2706
)) {
173 printf("b3_2706_match: no bit3 chip\n");
178 return (5); /* beat "ppb" */
182 b3_2706_attach(device_t parent
, device_t self
, void *aux
)
184 struct b3_2706_softc
*sc
= device_private(self
);
185 struct pci_attach_args
*pa
= aux
;
186 pci_chipset_tag_t pc
= pa
->pa_pc
;
187 struct pci_attach_args aa
;
191 bus_addr_t swappbase
;
194 struct vmebus_attach_args vaa
;
196 aprint_naive(": VME bus adapter\n");
199 secbus
= PPB_BUSINFO_SECONDARY(pci_conf_read(pc
, pa
->pa_tag
,
202 memcpy(&aa
, pa
, sizeof(struct pci_attach_args
));
205 aa
.pa_tag
= pci_make_tag(pc
, secbus
, 4, 0);
207 intr
= pci_conf_read(pc
, aa
.pa_tag
, PCI_INTERRUPT_REG
);
209 * swizzle it based on the number of
210 * busses we're behind and our device
213 aa
.pa_intrpin
= ((1 + aa
.pa_intrswiz
- 1) % 4) + 1;
214 aa
.pa_intrline
= PCI_INTERRUPT_LINE(intr
);
216 if (univ_pci_attach(&sc
->univdata
, &aa
, device_xname(self
),
217 b3_2706_vmeint
, sc
)) {
218 aprint_error_dev(self
, "error initializing universe chip\n");
223 * don't waste KVM - the byteswap register is aliased in
224 * a 512k window, we need it only once
226 tag
= pci_make_tag(pc
, secbus
, 8, 0);
227 sc
->swapt
= pa
->pa_memt
;
228 if (pci_mapreg_info(pc
, tag
, 0x10,
229 PCI_MAPREG_TYPE_MEM
| PCI_MAPREG_MEM_TYPE_32BIT
,
231 bus_space_map(sc
->swapt
, swappbase
, 4, 0, &sc
->swaph
)) {
232 aprint_error_dev(self
, "can't map byteswap register\n");
236 * Set up cycle specific byteswap mode.
237 * XXX Readback yields "all-ones" for me, and it doesn't seem
238 * to matter what I write into the register - the data don't
239 * get swapped. Adapter fault or documentation bug?
241 bus_space_write_4(sc
->swapt
, sc
->swaph
, 0, 0x00000490);
243 /* VME space is mapped as needed */
244 sc
->vmet
= pa
->pa_memt
;
245 if (pci_mapreg_info(pc
, tag
, 0x14,
246 PCI_MAPREG_TYPE_MEM
| PCI_MAPREG_MEM_TYPE_32BIT
,
247 &sc
->vmepbase
, 0, 0)) {
248 aprint_error_dev(self
, "VME range not assigned\n");
252 aprint_debug_dev(self
, "VME window @%lx\n",
256 for (i
= 0; i
< 8; i
++) {
257 sc
->windowused
[i
] = 0;
259 sc
->vmeext
= extent_create("pcivme", sc
->vmepbase
,
260 sc
->vmepbase
+ 32*1024*1024 - 1, M_DEVBUF
,
261 sc
->vmemap
, sizeof(sc
->vmemap
),
264 sc
->sc_vct
.cookie
= self
;
265 sc
->sc_vct
.vct_probe
= b3_2706_vme_probe
;
266 sc
->sc_vct
.vct_map
= b3_2706_map_vme
;
267 sc
->sc_vct
.vct_unmap
= b3_2706_unmap_vme
;
268 sc
->sc_vct
.vct_int_map
= b3_2706_map_vmeint
;
269 sc
->sc_vct
.vct_int_establish
= b3_2706_establish_vmeint
;
270 sc
->sc_vct
.vct_int_disestablish
= b3_2706_disestablish_vmeint
;
271 sc
->sc_vct
.vct_dmamap_create
= b3_2706_dmamap_create
;
272 sc
->sc_vct
.vct_dmamap_destroy
= b3_2706_dmamap_destroy
;
273 sc
->sc_vct
.vct_dmamem_alloc
= b3_2706_dmamem_alloc
;
274 sc
->sc_vct
.vct_dmamem_free
= b3_2706_dmamem_free
;
276 vaa
.va_vct
= &(sc
->sc_vct
);
277 vaa
.va_bdt
= pa
->pa_dmat
; /* XXX */
278 vaa
.va_slaveconfig
= 0; /* XXX CSR window? */
280 config_found(self
, &vaa
, 0);
283 #define sc ((struct b3_2706_softc*)vsc)
286 b3_2706_map_vme(void *vsc
, vme_addr_t vmeaddr
, vme_size_t len
, vme_am_t am
, vme_datasize_t datasizes
, vme_swap_t swap
, bus_space_tag_t
*tag
, bus_space_handle_t
*handle
, vme_mapresc_t
*resc
)
288 int idx
, i
, wnd
, res
;
289 unsigned long boundary
, maplen
, pcibase
;
290 vme_addr_t vmebase
, vmeend
;
291 static int windoworder
[8] = {1, 2, 3, 5, 6, 7, 0, 4};
293 /* prefer windows with fine granularity for small mappings */
299 for (i
= 0; i
< 8; i
++) {
300 if (!sc
->windowused
[windoworder
[idx
]]) {
301 wnd
= windoworder
[idx
];
302 sc
->windowused
[wnd
] = 1;
310 boundary
= (wnd
& 3) ? 64*1024 : 4*1024;
312 /* first mapped address */
313 vmebase
= vmeaddr
& ~(boundary
- 1);
314 /* base of last mapped page */
315 vmeend
= (vmeaddr
+ len
- 1) & ~(boundary
- 1);
316 /* bytes in outgoing window required */
317 maplen
= vmeend
- vmebase
+ boundary
;
319 if (extent_alloc(sc
->vmeext
, maplen
, boundary
, 0, EX_FAST
, &pcibase
)) {
320 sc
->windowused
[wnd
] = 0;
324 res
= univ_pci_mapvme(&sc
->univdata
, wnd
, vmebase
, maplen
,
325 am
, datasizes
, pcibase
);
327 extent_free(sc
->vmeext
, pcibase
, maplen
, 0);
328 sc
->windowused
[wnd
] = 0;
332 res
= bus_space_map(sc
->vmet
, pcibase
+ (vmeaddr
- vmebase
), len
,
335 univ_pci_unmapvme(&sc
->univdata
, wnd
);
336 extent_free(sc
->vmeext
, pcibase
, maplen
, 0);
337 sc
->windowused
[wnd
] = 0;
344 * save all data needed for later unmapping
346 sc
->vmemaprescs
[wnd
].wnd
= wnd
;
347 sc
->vmemaprescs
[wnd
].pcibase
= pcibase
;
348 sc
->vmemaprescs
[wnd
].maplen
= maplen
;
349 sc
->vmemaprescs
[wnd
].handle
= *handle
;
350 sc
->vmemaprescs
[wnd
].len
= len
;
351 *resc
= &sc
->vmemaprescs
[wnd
];
356 b3_2706_unmap_vme(void *vsc
, vme_mapresc_t resc
)
358 struct b3_2706_vmemaprescs
*r
= resc
;
360 bus_space_unmap(sc
->vmet
, r
->handle
, r
->len
);
361 extent_free(sc
->vmeext
, r
->pcibase
, r
->maplen
, 0);
363 if (!sc
->windowused
[r
->wnd
])
364 panic("b3_2706_unmap_vme: bad window");
365 univ_pci_unmapvme(&sc
->univdata
, r
->wnd
);
366 sc
->windowused
[r
->wnd
] = 0;
370 b3_2706_vme_probe(void *vsc
, vme_addr_t addr
, vme_size_t len
, vme_am_t am
, vme_datasize_t datasize
, int (*callback
)(void *, bus_space_tag_t
, bus_space_handle_t
), void *cbarg
)
373 bus_space_handle_t handle
;
376 volatile u_int32_t dummy
;
378 res
= b3_2706_map_vme(vsc
, addr
, len
, am
, datasize
, 0,
379 &tag
, &handle
, &resc
);
383 if (univ_pci_vmebuserr(&sc
->univdata
, 1))
384 printf("b3_2706_vme_badaddr: TA bit not clean - reset\n");
387 res
= (*callback
)(cbarg
, tag
, handle
);
389 for (i
= 0; i
< len
;) {
392 dummy
= bus_space_read_1(tag
, handle
, i
);
396 dummy
= bus_space_read_2(tag
, handle
, i
);
400 dummy
= bus_space_read_4(tag
, handle
, i
);
404 panic("b3_2706_vme_probe: invalid datasize %x",
410 if (univ_pci_vmebuserr(&sc
->univdata
, 0)) {
412 printf("b3_2706_vme_badaddr: caught TA\n");
414 univ_pci_vmebuserr(&sc
->univdata
, 1);
418 b3_2706_unmap_vme(vsc
, resc
);
423 b3_2706_map_vmeint(void *vsc
, int level
, int vector
, vme_intr_handle_t
*handlep
)
426 *handlep
= (void *)(long)((level
<< 8) | vector
); /* XXX */
431 b3_2706_establish_vmeint(void *vsc
, vme_intr_handle_t handle
, int prior
, int (*func
)(void *), void *arg
)
433 struct b3_2706_vmeintrhand
*ih
;
437 /* no point in sleeping unless someone can free memory. */
438 ih
= malloc(sizeof *ih
, M_DEVBUF
, cold
? M_NOWAIT
: M_WAITOK
);
440 panic("b3_2706_map_vmeint: can't malloc handler info");
442 lv
= (long)handle
; /* XXX */
446 ih
->ih_level
= lv
>> 8;
447 ih
->ih_vector
= lv
& 0xff;
448 ih
->ih_prior
= prior
;
452 TAILQ_INSERT_TAIL(&(sc
->intrhdls
), ih
, ih_next
);
459 b3_2706_disestablish_vmeint(void *vsc
, void *cookie
)
461 struct b3_2706_vmeintrhand
*ih
= cookie
;
465 printf("b3_2706_unmap_vmeint: NULL arg\n");
470 TAILQ_REMOVE(&(sc
->intrhdls
), ih
, ih_next
);
477 b3_2706_vmeint(void *vsc
, int level
, int vector
)
479 struct b3_2706_vmeintrhand
*ih
;
483 printf("b3_2706_vmeint: VME IRQ %d, vec %x\n", level
, vector
);
487 for (ih
= sc
->intrhdls
.tqh_first
; ih
;
488 ih
= ih
->ih_next
.tqe_next
) {
489 if ((ih
->ih_level
== level
) &&
490 ((ih
->ih_vector
== -1) ||
491 (ih
->ih_vector
== vector
))) {
494 * We should raise the interrupt level
495 * to ih->ih_prior here. How to do this
496 * machine-independently?
497 * To be safe, raise to the maximum.
500 found
|= (res
= (*(ih
->ih_fun
))(ih
->ih_arg
));
513 b3_2706_dmamap_create(vsc
, len
, am
, datasize
, swap
,
519 vme_datasize_t datasize
;
531 b3_2706_dmamap_destroy(void *vsc
, bus_dmamap_t map
)
536 b3_2706_dmamem_alloc(void *vsc
, vme_size_t len
, vme_am_t am
, vme_datasize_t datasizes
, vme_swap_t swap
, bus_dma_segment_t
*segs
, int nsegs
, int *rsegs
, int flags
)
542 b3_2706_dmamem_free(void *vsc
, bus_dma_segment_t
*segs
, int nsegs
)