1 /* $NetBSD: apbus.c,v 1.20 2005/12/11 12:18:24 christos Exp $ */
4 * Copyright (C) 1999 SHIMIZU Ryo. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: apbus.c,v 1.20 2005/12/11 12:18:24 christos Exp $");
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/malloc.h>
35 #include <sys/device.h>
38 #include <uvm/uvm_extern.h>
40 #include <machine/adrsmap.h>
41 #include <machine/autoconf.h>
42 #define _NEWSMIPS_BUS_DMA_PRIVATE
43 #include <machine/bus.h>
44 #include <machine/intr.h>
45 #include <newsmips/apbus/apbusvar.h>
47 static int apbusmatch(device_t
, cfdata_t
, void *);
48 static void apbusattach(device_t
, device_t
, void *);
49 static int apbusprint(void *, const char *);
51 static void *aptokseg0 (void *);
53 static void apbus_dma_unmapped(bus_dma_tag_t
, bus_dmamap_t
);
54 static int apbus_dma_mapalloc(bus_dma_tag_t
, bus_dmamap_t
, int);
55 static void apbus_dma_mapfree(bus_dma_tag_t
, bus_dmamap_t
);
56 static void apbus_dma_mapset(bus_dma_tag_t
, bus_dmamap_t
);
57 static int apbus_dmamap_create(bus_dma_tag_t
, bus_size_t
, int, bus_size_t
,
58 bus_size_t
, int, bus_dmamap_t
*);
59 static void apbus_dmamap_destroy(bus_dma_tag_t
, bus_dmamap_t
);
60 static int apbus_dmamap_load(bus_dma_tag_t
, bus_dmamap_t
, void *, bus_size_t
,
62 static int apbus_dmamap_load_mbuf(bus_dma_tag_t
, bus_dmamap_t
, struct mbuf
*,
64 static int apbus_dmamap_load_uio(bus_dma_tag_t
, bus_dmamap_t
, struct uio
*,
66 static int apbus_dmamap_load_raw(bus_dma_tag_t
, bus_dmamap_t
,
67 bus_dma_segment_t
*, int, bus_size_t
, int);
68 static void apbus_dmamap_sync(bus_dma_tag_t
, bus_dmamap_t
, bus_addr_t
,
71 #define MAXAPDEVNUM 32
73 CFATTACH_DECL_NEW(ap
, 0,
74 apbusmatch
, apbusattach
, NULL
, NULL
);
77 static struct newsmips_intr apintr_tab
[NLEVEL
];
80 apbusmatch(device_t parent
, cfdata_t cf
, void *aux
)
82 struct confargs
*ca
= aux
;
84 if (strcmp(ca
->ca_name
, "ap") != 0)
92 apbusattach(device_t parent
, device_t self
, void *aux
)
94 struct apbus_attach_args child
;
95 struct apbus_dev
*apdev
;
96 struct apbus_ctl
*apctl
;
97 struct newsmips_intr
*ip
;
100 *(volatile uint32_t *)(NEWS5000_APBUS_INTST
) = 0xffffffff;
101 *(volatile uint32_t *)(NEWS5000_APBUS_INTMSK
) = 0xffffffff;
102 *(volatile uint32_t *)(NEWS5000_APBUS_CTRL
) = 0x00000004;
103 *(volatile uint32_t *)(NEWS5000_APBUS_DMA
) = 0xffffffff;
107 for (i
= 0; i
< NLEVEL
; i
++) {
109 LIST_INIT(&ip
->intr_q
);
113 * get first ap-device
115 apdev
= apbus_lookupdev(NULL
);
121 apctl
= apdev
->apbd_ctl
;
124 * probe physical device only
127 if (apctl
&& apctl
->apbc_hwbase
) {
132 /* make apbus_attach_args for devices */
133 child
.apa_name
= apdev
->apbd_name
;
134 child
.apa_ctlnum
= apctl
->apbc_ctlno
;
135 child
.apa_slotno
= apctl
->apbc_sl
;
136 child
.apa_hwbase
= apctl
->apbc_hwbase
;
138 config_found(self
, &child
, apbusprint
);
140 apctl
= apctl
->apbc_link
;
144 apdev
= apdev
->apbd_link
;
149 apbusprint(void *aux
, const char *pnp
)
151 struct apbus_attach_args
*a
= aux
;
154 aprint_normal("%s at %s slot%d addr 0x%lx",
155 a
->apa_name
, pnp
, a
->apa_slotno
, a
->apa_hwbase
);
164 vaddr_t addr
= (vaddr_t
)va
;
166 if (addr
>= 0xfff00000) {
168 addr
+= physmem
<< PGSHIFT
;
179 volatile int32_t *wbflush
= (uint32_t *)NEWS5000_WBFLUSH
;
185 * called by hardware interrupt routine
188 apbus_intr_dispatch(int level
, int stat
)
190 struct newsmips_intr
*ip
;
191 struct newsmips_intrhand
*ih
;
194 ip
= &apintr_tab
[level
];
197 LIST_FOREACH(ih
, &ip
->intr_q
, ih_q
) {
198 if (ih
->ih_mask
& stat
)
199 nintr
+= (*ih
->ih_func
)(ih
->ih_arg
);
205 * register device interrupt routine
208 apbus_intr_establish(int level
, int mask
, int priority
, int (*func
)(void *),
209 void *arg
, const char *name
, int ctlno
)
211 struct newsmips_intr
*ip
;
212 struct newsmips_intrhand
*ih
, *curih
;
213 volatile uint32_t *inten0
, *inten1
;
215 ip
= &apintr_tab
[level
];
217 ih
= malloc(sizeof(*ih
), M_DEVBUF
, M_NOWAIT
);
219 panic("%s: can't malloc handler info", __func__
);
221 ih
->ih_priority
= priority
;
225 if (LIST_EMPTY(&ip
->intr_q
)) {
226 LIST_INSERT_HEAD(&ip
->intr_q
, ih
, ih_q
);
230 for (curih
= LIST_FIRST(&ip
->intr_q
);
231 LIST_NEXT(curih
, ih_q
) != NULL
;
232 curih
= LIST_NEXT(curih
, ih_q
)) {
233 if (ih
->ih_priority
> curih
->ih_priority
) {
234 LIST_INSERT_BEFORE(curih
, ih
, ih_q
);
239 LIST_INSERT_AFTER(curih
, ih
, ih_q
);
244 inten0
= (volatile uint32_t *)NEWS5000_INTEN0
;
248 inten1
= (volatile uint32_t *)NEWS5000_INTEN1
;
257 apbus_dma_unmapped(bus_dma_tag_t t
, bus_dmamap_t map
)
261 for (seg
= 0; seg
< map
->dm_nsegs
; seg
++) {
263 * set MSB to indicate unmapped DMA.
264 * also need bit 30 for memory over 256MB.
266 if ((map
->dm_segs
[seg
].ds_addr
& 0x30000000) == 0)
267 map
->dm_segs
[seg
].ds_addr
|= 0x80000000;
269 map
->dm_segs
[seg
].ds_addr
|= 0xc0000000;
273 #define APBUS_NDMAMAP (NEWS5000_APBUS_MAPSIZE / NEWS5000_APBUS_MAPENT)
274 #define APBUS_MAPTBL(n, v) \
275 (*(volatile uint32_t *)(NEWS5000_APBUS_DMAMAP + \
276 NEWS5000_APBUS_MAPENT * (n) + 1) = (v))
277 static uint8_t apbus_dma_maptbl
[APBUS_NDMAMAP
];
280 apbus_dma_mapalloc(bus_dma_tag_t t
, bus_dmamap_t map
, int flags
)
284 cnt
= round_page(map
->_dm_size
) / PAGE_SIZE
;
287 for (i
= 0; i
< APBUS_NDMAMAP
; i
+= j
+ 1) {
288 for (j
= 0; j
< cnt
; j
++) {
289 if (apbus_dma_maptbl
[i
+ j
])
293 for (j
= 0; j
< cnt
; j
++)
294 apbus_dma_maptbl
[i
+ j
] = 1;
296 map
->_dm_maptblcnt
= cnt
;
300 if ((flags
& BUS_DMA_NOWAIT
) == 0) {
301 tsleep(&apbus_dma_maptbl
, PRIBIO
, "apdmat", 0);
308 apbus_dma_mapfree(bus_dma_tag_t t
, bus_dmamap_t map
)
312 if (map
->_dm_maptblcnt
> 0) {
314 for (i
= 0; i
< map
->_dm_maptblcnt
; i
++, n
++) {
316 if (apbus_dma_maptbl
[n
] == 0)
317 panic("freeing free DMA map");
318 APBUS_MAPTBL(n
, 0xffffffff); /* causes DMA error */
320 apbus_dma_maptbl
[n
] = 0;
322 wakeup(&apbus_dma_maptbl
);
323 map
->_dm_maptblcnt
= 0;
328 apbus_dma_mapset(bus_dma_tag_t t
, bus_dmamap_t map
)
331 bus_addr_t addr
, eaddr
;
333 bus_dma_segment_t
*segs
;
336 for (seg
= 0; seg
< map
->dm_nsegs
; seg
++) {
337 segs
= &map
->dm_segs
[seg
];
338 for (addr
= segs
->ds_addr
, eaddr
= addr
+ segs
->ds_len
;
339 addr
< eaddr
; addr
+= PAGE_SIZE
, i
++) {
341 if (i
>= map
->_dm_maptblcnt
)
342 panic("DMA map table overflow");
344 APBUS_MAPTBL(map
->_dm_maptbl
+ i
,
345 NEWS5000_APBUS_MAP_VALID
|
346 NEWS5000_APBUS_MAP_COHERENT
|
350 map
->dm_segs
[0].ds_addr
= map
->_dm_maptbl
<< PGSHIFT
;
351 map
->dm_segs
[0].ds_len
= map
->dm_mapsize
;
356 apbus_dmamap_create(bus_dma_tag_t t
, bus_size_t size
, int nsegments
,
357 bus_size_t maxsegsz
, bus_size_t boundary
, int flags
, bus_dmamap_t
*dmamp
)
361 if (flags
& NEWSMIPS_DMAMAP_MAPTBL
)
362 nsegments
= round_page(size
) / PAGE_SIZE
;
363 error
= _bus_dmamap_create(t
, size
, nsegments
, maxsegsz
, boundary
,
365 if (error
== 0 && (flags
& NEWSMIPS_DMAMAP_MAPTBL
)) {
366 error
= apbus_dma_mapalloc(t
, *dmamp
, flags
);
368 _bus_dmamap_destroy(t
, *dmamp
);
376 apbus_dmamap_destroy(bus_dma_tag_t t
, bus_dmamap_t map
)
379 if (map
->_dm_flags
& NEWSMIPS_DMAMAP_MAPTBL
)
380 apbus_dma_mapfree(t
, map
);
381 _bus_dmamap_destroy(t
, map
);
385 apbus_dmamap_load(bus_dma_tag_t t
, bus_dmamap_t map
, void *buf
,
386 bus_size_t buflen
, struct proc
*p
, int flags
)
390 error
= _bus_dmamap_load(t
, map
, buf
, buflen
, p
, flags
);
392 if (map
->_dm_flags
& NEWSMIPS_DMAMAP_MAPTBL
)
393 apbus_dma_mapset(t
, map
);
395 apbus_dma_unmapped(t
, map
);
401 apbus_dmamap_load_mbuf(bus_dma_tag_t t
, bus_dmamap_t map
, struct mbuf
*m0
,
406 error
= _bus_dmamap_load_mbuf(t
, map
, m0
, flags
);
408 if (map
->_dm_flags
& NEWSMIPS_DMAMAP_MAPTBL
)
409 apbus_dma_mapset(t
, map
);
411 apbus_dma_unmapped(t
, map
);
417 apbus_dmamap_load_uio(bus_dma_tag_t t
, bus_dmamap_t map
, struct uio
*uio
,
422 error
= _bus_dmamap_load_uio(t
, map
, uio
, flags
);
424 if (map
->_dm_flags
& NEWSMIPS_DMAMAP_MAPTBL
)
425 apbus_dma_mapset(t
, map
);
427 apbus_dma_unmapped(t
, map
);
433 apbus_dmamap_load_raw(bus_dma_tag_t t
, bus_dmamap_t map
,
434 bus_dma_segment_t
*segs
, int nsegs
, bus_size_t size
, int flags
)
438 error
= _bus_dmamap_load_raw(t
, map
, segs
, nsegs
, size
, flags
);
440 if (map
->_dm_flags
& NEWSMIPS_DMAMAP_MAPTBL
)
441 apbus_dma_mapset(t
, map
);
443 apbus_dma_unmapped(t
, map
);
449 apbus_dmamap_sync(bus_dma_tag_t t
, bus_dmamap_t map
, bus_addr_t offset
,
450 bus_size_t len
, int ops
)
454 * Flush DMA cache by issuing IO read for the AProm of specified slot.
456 bus_space_read_4(t
->_slotbaset
, t
->_slotbaseh
, 0);
458 bus_dmamap_sync(&newsmips_default_bus_dma_tag
, map
, offset
, len
, ops
);
461 struct newsmips_bus_dma_tag apbus_dma_tag
= {
463 apbus_dmamap_destroy
,
465 apbus_dmamap_load_mbuf
,
466 apbus_dmamap_load_uio
,
467 apbus_dmamap_load_raw
,
477 struct newsmips_bus_dma_tag
*
478 apbus_dmatag_init(struct apbus_attach_args
*apa
)
480 struct newsmips_bus_dma_tag
*dmat
;
482 dmat
= malloc(sizeof(*dmat
), M_DEVBUF
, M_NOWAIT
);
484 memcpy(dmat
, &apbus_dma_tag
, sizeof(*dmat
));
485 dmat
->_slotno
= apa
->apa_slotno
;
486 dmat
->_slotbaset
= 0;
487 dmat
->_slotbaseh
= apa
->apa_hwbase
;