diff options
author | Martin Pieuchot <mpi@cvs.openbsd.org> | 2015-09-01 08:44:35 +0000 |
---|---|---|
committer | Martin Pieuchot <mpi@cvs.openbsd.org> | 2015-09-01 08:44:35 +0000 |
commit | a0bddd91c5f29af3fdaef087cbed805c2ac13e8c (patch) | |
tree | 0bd39ea648a1ab569a6ba9072fb219dc68a6416a /sys | |
parent | 801a7ee36a22caa535ee1cd85edb8b30889449a5 (diff) |
Sync bus_dmamap_load_raw(9) with amd64/i386 in order to respect the
segement size constraint.
Make xhci(4) work on my G5.
ok dlg@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/powerpc/powerpc/bus_dma.c | 88 |
1 files changed, 71 insertions, 17 deletions
diff --git a/sys/arch/powerpc/powerpc/bus_dma.c b/sys/arch/powerpc/powerpc/bus_dma.c index ec77838eb3b..c3441e37943 100644 --- a/sys/arch/powerpc/powerpc/bus_dma.c +++ b/sys/arch/powerpc/powerpc/bus_dma.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bus_dma.c,v 1.2 2015/01/24 20:59:42 kettenis Exp $ */ +/* $OpenBSD: bus_dma.c,v 1.3 2015/09/01 08:44:34 mpi Exp $ */ /* $NetBSD: machdep.c,v 1.214 1996/11/10 03:16:17 thorpej Exp $ */ /*- @@ -326,28 +326,82 @@ int _dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags) { - if (nsegs > map->_dm_segcnt || size > map->_dm_size) - return (EINVAL); + bus_addr_t paddr, baddr, bmask, lastaddr = 0; + bus_size_t plen, sgsize, mapsize; + int first = 1; + int i, seg = 0; /* - * Make sure we don't cross any boundaries. + * Make sure that on error condition we return "no valid mappings". */ - if (map->_dm_boundary) { - bus_addr_t bmask = ~(map->_dm_boundary - 1); - int i; - - for (i = 0; i < nsegs; i++) { - if (segs[i].ds_len > map->_dm_maxsegsz) - return (EINVAL); - if ((segs[i].ds_addr & bmask) != - ((segs[i].ds_addr + segs[i].ds_len - 1) & bmask)) - return (EINVAL); + map->dm_mapsize = 0; + map->dm_nsegs = 0; + + if (nsegs > map->_dm_segcnt || size > map->_dm_size) + return (EINVAL); + + mapsize = size; + bmask = ~(map->_dm_boundary - 1); + + for (i = 0; i < nsegs && size > 0; i++) { + paddr = segs[i].ds_addr; + plen = MIN(segs[i].ds_len, size); + + while (plen > 0) { + /* + * Compute the segment size, and adjust counts. + */ + sgsize = PAGE_SIZE - ((u_long)paddr & PGOFSET); + if (plen < sgsize) + sgsize = plen; + + if (paddr > dma_constraint.ucr_high) + panic("Non dma-reachable buffer at paddr %#lx(raw)", + paddr); + + /* + * Make sure we don't cross any boundaries. + */ + if (map->_dm_boundary > 0) { + baddr = (paddr + map->_dm_boundary) & bmask; + if (sgsize > (baddr - paddr)) + sgsize = (baddr - paddr); + } + + /* + * Insert chunk into a segment, coalescing with + * previous segment if possible. + */ + if (first) { + map->dm_segs[seg].ds_addr = paddr; + map->dm_segs[seg].ds_len = sgsize; + first = 0; + } else { + if (paddr == lastaddr && + (map->dm_segs[seg].ds_len + sgsize) <= + map->_dm_maxsegsz && + (map->_dm_boundary == 0 || + (map->dm_segs[seg].ds_addr & bmask) == + (paddr & bmask))) + map->dm_segs[seg].ds_len += sgsize; + else { + if (++seg >= map->_dm_segcnt) + return (EINVAL); + map->dm_segs[seg].ds_addr = paddr; + map->dm_segs[seg].ds_len = sgsize; + } + } + + paddr += sgsize; + plen -= sgsize; + size -= sgsize; + + lastaddr = paddr; } } - bcopy(segs, map->dm_segs, nsegs * sizeof(*segs)); - map->dm_nsegs = nsegs; - map->dm_mapsize = size; + map->dm_mapsize = mapsize; + map->dm_nsegs = seg + 1; return (0); } |