/*-
* Copyright (c) 2000, 2002 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Steve C. Woodford.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* If we have any offboard RAM (i.e. a VMEbus RAM board) then
* we need to record its details since it's effectively another
* VMEbus slave image as far as we're concerned.
* The chip-specific backend will have reserved sc->sc_slaves[0]
* for exactly this purpose.
*/
svr = sc->sc_slaves;
if (mem_cluster_cnt < 2) {
svr->vr_am = MVMEBUS_AM_DISABLED;
return;
}
/*
* Figure out which VMEbus master image the RAM is
* visible through. This will tell us the address
* modifier and datasizes it uses, as well as allowing
* us to calculate its `real' VMEbus address.
*
* XXX FIXME: This is broken if the RAM is mapped through
* a translated address space. For example, on mvme167 it's
* perfectly legal to set up the following A32 mapping:
*
* vr_locaddr == 0x80000000
* vr_vmestart == 0x10000000
* vr_vmeend == 0x10ffffff
*
* In this case, RAM at VMEbus address 0x10800000 will appear at local
* address 0x80800000, but we need to set the slave vr_vmestart to
* 0x10800000.
*/
for (i = 0, mvr = sc->sc_masters; i < sc->sc_nmasters; i++, mvr++) {
vme_addr_t vstart = mvr->vr_locstart + mvr->vr_vmestart;
if (start >= vstart &&
end <= vstart + (mvr->vr_vmeend - mvr->vr_vmestart))
break;
}
if (i == sc->sc_nmasters) {
svr->vr_am = MVMEBUS_AM_DISABLED;
#ifdef DEBUG
printf("%s: No VMEbus master mapping for offboard RAM!\n",
device_xname(sc->sc_dev));
#endif
return;
}
panic("Must use vme_dmamap_destroy() in place of bus_dmamap_destroy()");
}
#endif
/* ARGSUSED */
int
mvmebus_dmamap_create(
void *vsc,
vme_size_t len,
vme_am_t am,
vme_datasize_t datasize,
vme_swap_t swap,
int nsegs,
vme_size_t segsz,
vme_addr_t bound,
int flags,
bus_dmamap_t *mapp)
{
struct mvmebus_softc *sc = vsc;
struct mvmebus_dmamap *vmap;
struct mvmebus_range *vr;
vme_am_t cap, as;
int i, rv;
cap = MVMEBUS_AM2CAP(am);
as = am & VME_AM_ADRSIZEMASK;
/*
* Verify that we even stand a chance of satisfying
* the VMEbus address space and datasize requested.
*/
for (i = 0, vr = sc->sc_slaves; i < sc->sc_nslaves; i++, vr++) {
if (vr->vr_am == MVMEBUS_AM_DISABLED)
continue;
if (as == (vr->vr_am & VME_AM_ADRSIZEMASK) &&
cap == (vr->vr_am & cap) && datasize <= vr->vr_datasize &&
len <= (vr->vr_vmeend - vr->vr_vmestart))
break;
}
cap = MVMEBUS_AM2CAP(vmap->vm_am);
am = vmap->vm_am & VME_AM_ADRSIZEMASK;
/*
* Traverse the list of segments which make up this map, and
* convert the CPU-relative addresses therein to VMEbus addresses.
*/
for (ds = &map->dm_segs[0]; ds < &map->dm_segs[map->dm_nsegs]; ds++) {
/*
* First, see if this map's slave image can access the
* segment, otherwise we have to waste time scanning all
* the slave images.
*/
vr = vmap->vm_slave;
if (am == (vr->vr_am & VME_AM_ADRSIZEMASK) &&
cap == (vr->vr_am & cap) &&
vmap->vm_datasize <= vr->vr_datasize &&
ds->_ds_cpuaddr >= vr->vr_locstart &&
ds->ds_len <= (vr->vr_vmeend - vr->vr_vmestart))
goto found;
for (i = 0, vr = sc->sc_slaves; i < sc->sc_nslaves; i++, vr++) {
if (vr->vr_am == MVMEBUS_AM_DISABLED)
continue;
/*
* Filter out any slave images which don't have the
* same VMEbus address modifier and datasize as
* this DMA map, and those which don't cover the
* physical address region containing the segment.
*/
if (vr != vmap->vm_slave &&
am == (vr->vr_am & VME_AM_ADRSIZEMASK) &&
cap == (vr->vr_am & cap) &&
vmap->vm_datasize <= vr->vr_datasize &&
ds->_ds_cpuaddr >= vr->vr_locstart &&
ds->ds_len <= (vr->vr_vmeend - vr->vr_vmestart))
break;
}
/*
* Did we find an applicable slave image which covers this
* segment?
*/
if (i == sc->sc_nslaves) {
/*
* XXX TODO:
*
* Bounce this segment via a bounce buffer allocated
* from this DMA map.
*/
printf("mvmebus_dmamap_load_common: bounce needed!\n");
return (EINVAL);
}
found:
/*
* Generate the VMEbus address of this segment
*/
ds->ds_addr = (ds->_ds_cpuaddr - vr->vr_locstart) +
vr->vr_vmestart;
}
return (0);
}
int
mvmebus_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf, bus_size_t buflen, struct proc *p, int flags)
{
struct mvmebus_softc *sc = t->_cookie;
int rv;
int
mvmebus_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)
{
struct mvmebus_softc *sc = t->_cookie;
int rv;
/*
* mvmebus_dmamem_alloc() will ensure that the physical memory
* backing these segments is 100% accessible in at least one
* of the board's VMEbus slave images.
*/
rv = bus_dmamap_load_raw(sc->sc_dmat, map, segs, nsegs, size, flags);
if (rv != 0)
return rv;
panic("Must use vme_dmamem_free() in place of bus_dmamem_free()");
}
#endif
/* ARGSUSED */
int
mvmebus_dmamem_alloc(void *vsc, vme_size_t len, vme_am_t am, vme_datasize_t datasize, vme_swap_t swap, bus_dma_segment_t *segs, int nsegs, int *rsegs, int flags)
{
extern paddr_t avail_start;
struct mvmebus_softc *sc = vsc;
struct mvmebus_range *vr;
bus_addr_t low, high;
bus_size_t bound;
vme_am_t cap;
int i;
cap = MVMEBUS_AM2CAP(am);
am &= VME_AM_ADRSIZEMASK;
/*
* Find a slave mapping in the requested VMEbus address space.
*/
for (i = 0, vr = sc->sc_slaves; i < sc->sc_nslaves; i++, vr++) {
if (vr->vr_am == MVMEBUS_AM_DISABLED)
continue;
if (i == 0 && (flags & BUS_DMA_ONBOARD_RAM) != 0)
continue;
if (am == (vr->vr_am & VME_AM_ADRSIZEMASK) &&
cap == (vr->vr_am & cap) && datasize <= vr->vr_datasize &&
len <= (vr->vr_vmeend - vr->vr_vmestart))
break;
}
if (i == sc->sc_nslaves)
return (EINVAL);
/*
* Set up the constraints so we can allocate physical memory which
* is visible in the requested address space
*/
low = uimax(vr->vr_locstart, avail_start);
high = vr->vr_locstart + (vr->vr_vmeend - vr->vr_vmestart) + 1;
bound = (bus_size_t) vr->vr_mask + 1;
/*
* Allocate physical memory.
*
* Note: This fills in the segments with CPU-relative physical
* addresses. A further call to bus_dmamap_load_raw() (with a
* DMA map which specifies the same VMEbus address space and
* constraints as the call to here) must be made. The segments
* of the DMA map will then contain VMEbus-relative physical
* addresses of the memory allocated here.
*/
return _bus_dmamem_alloc_common(sc->sc_dmat, low, high,
len, 0, bound, segs, nsegs, rsegs, flags);
}