1999-06-20 04:51:29 +04:00
|
|
|
/* $NetBSD: sbus.c,v 1.19 1999/06/20 00:51:30 eeh Exp $ */
|
1998-06-20 08:58:50 +04:00
|
|
|
|
|
|
|
/*-
|
|
|
|
* Copyright (c) 1998 The NetBSD Foundation, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
|
|
* by Paul Kranenburg.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
|
|
|
* This product includes software developed by the NetBSD
|
|
|
|
* Foundation, Inc. and its contributors.
|
|
|
|
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
|
|
|
* contributors may be used to endorse or promote products derived
|
|
|
|
* from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 1992, 1993
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
|
|
|
*
|
|
|
|
* This software was developed by the Computer Systems Engineering group
|
|
|
|
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
|
|
|
|
* contributed to Berkeley.
|
|
|
|
*
|
|
|
|
* All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
|
|
|
* This product includes software developed by the University of
|
|
|
|
* California, Lawrence Berkeley Laboratory.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
|
|
|
* This product includes software developed by the University of
|
|
|
|
* California, Berkeley and its contributors.
|
|
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
|
|
|
|
*
|
|
|
|
* @(#)sbus.c 8.1 (Berkeley) 6/11/93
|
|
|
|
*/
|
|
|
|
|
1999-06-07 09:28:03 +04:00
|
|
|
/*
|
|
|
|
* Copyright (c) 1999 Eduardo Horvath
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
1998-06-20 08:58:50 +04:00
|
|
|
/*
|
|
|
|
* Sbus stuff.
|
|
|
|
*/
|
1998-09-06 03:57:24 +04:00
|
|
|
#include "opt_ddb.h"
|
1998-06-20 08:58:50 +04:00
|
|
|
|
|
|
|
#include <sys/param.h>
|
1999-05-23 00:33:55 +04:00
|
|
|
#include <sys/extent.h>
|
1998-06-20 08:58:50 +04:00
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/device.h>
|
|
|
|
#include <vm/vm.h>
|
|
|
|
|
|
|
|
#include <machine/bus.h>
|
1998-07-07 07:05:02 +04:00
|
|
|
#include <sparc64/sparc64/vaddrs.h>
|
1999-05-23 11:24:02 +04:00
|
|
|
#include <sparc64/dev/iommureg.h>
|
1999-06-05 09:30:43 +04:00
|
|
|
#include <sparc64/dev/iommuvar.h>
|
1998-06-20 08:58:50 +04:00
|
|
|
#include <sparc64/dev/sbusreg.h>
|
1998-09-05 20:50:37 +04:00
|
|
|
#include <dev/sbus/sbusvar.h>
|
1998-06-20 08:58:50 +04:00
|
|
|
|
|
|
|
#include <machine/autoconf.h>
|
|
|
|
#include <machine/ctlreg.h>
|
|
|
|
#include <machine/cpu.h>
|
1998-09-06 03:57:24 +04:00
|
|
|
#include <machine/sparc64.h>
|
1998-06-20 08:58:50 +04:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
#define SDB_DVMA 0x1
|
|
|
|
#define SDB_INTR 0x2
|
|
|
|
int sbusdebug = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void sbusreset __P((int));
|
|
|
|
|
|
|
|
static bus_space_tag_t sbus_alloc_bustag __P((struct sbus_softc *));
|
|
|
|
static bus_dma_tag_t sbus_alloc_dmatag __P((struct sbus_softc *));
|
1998-08-13 06:10:37 +04:00
|
|
|
static int sbus_get_intr __P((struct sbus_softc *, int,
|
|
|
|
struct sbus_intr **, int *));
|
1998-06-20 08:58:50 +04:00
|
|
|
static int sbus_bus_mmap __P((bus_space_tag_t, bus_type_t, bus_addr_t,
|
|
|
|
int, bus_space_handle_t *));
|
|
|
|
static int _sbus_bus_map __P((
|
|
|
|
bus_space_tag_t,
|
|
|
|
bus_type_t,
|
|
|
|
bus_addr_t, /*offset*/
|
|
|
|
bus_size_t, /*size*/
|
|
|
|
int, /*flags*/
|
1998-08-13 06:10:37 +04:00
|
|
|
vaddr_t, /*preferred virtual address */
|
1998-06-20 08:58:50 +04:00
|
|
|
bus_space_handle_t *));
|
|
|
|
static void *sbus_intr_establish __P((
|
|
|
|
bus_space_tag_t,
|
|
|
|
int, /*level*/
|
|
|
|
int, /*flags*/
|
|
|
|
int (*) __P((void *)), /*handler*/
|
|
|
|
void *)); /*handler arg*/
|
|
|
|
|
|
|
|
|
|
|
|
/* autoconfiguration driver */
|
|
|
|
int sbus_match __P((struct device *, struct cfdata *, void *));
|
|
|
|
void sbus_attach __P((struct device *, struct device *, void *));
|
|
|
|
|
|
|
|
|
|
|
|
struct cfattach sbus_ca = {
|
|
|
|
sizeof(struct sbus_softc), sbus_match, sbus_attach
|
|
|
|
};
|
|
|
|
|
|
|
|
extern struct cfdriver sbus_cd;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* DVMA routines
|
|
|
|
*/
|
|
|
|
int sbus_dmamap_load __P((bus_dma_tag_t, bus_dmamap_t, void *,
|
|
|
|
bus_size_t, struct proc *, int));
|
|
|
|
void sbus_dmamap_unload __P((bus_dma_tag_t, bus_dmamap_t));
|
|
|
|
void sbus_dmamap_sync __P((bus_dma_tag_t, bus_dmamap_t, bus_addr_t,
|
|
|
|
bus_size_t, int));
|
|
|
|
int sbus_dmamem_alloc __P((bus_dma_tag_t tag, bus_size_t size,
|
|
|
|
bus_size_t alignment, bus_size_t boundary,
|
|
|
|
bus_dma_segment_t *segs, int nsegs, int *rsegs, int flags));
|
|
|
|
void sbus_dmamem_free __P((bus_dma_tag_t tag, bus_dma_segment_t *segs,
|
|
|
|
int nsegs));
|
1998-07-07 07:05:02 +04:00
|
|
|
int sbus_dmamem_map __P((bus_dma_tag_t tag, bus_dma_segment_t *segs,
|
|
|
|
int nsegs, size_t size, caddr_t *kvap, int flags));
|
|
|
|
void sbus_dmamem_unmap __P((bus_dma_tag_t tag, caddr_t kva,
|
|
|
|
size_t size));
|
1998-06-20 08:58:50 +04:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Child devices receive the Sbus interrupt level in their attach
|
|
|
|
* arguments. We translate these to CPU IPLs using the following
|
|
|
|
* tables. Note: obio bus interrupt levels are identical to the
|
|
|
|
* processor IPL.
|
|
|
|
*
|
|
|
|
* The second set of tables is used when the Sbus interrupt level
|
|
|
|
* cannot be had from the PROM as an `interrupt' property. We then
|
|
|
|
* fall back on the `intr' property which contains the CPU IPL.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Translate Sbus interrupt level to processor IPL */
|
|
|
|
static int intr_sbus2ipl_4c[] = {
|
|
|
|
0, 1, 2, 3, 5, 7, 8, 9
|
|
|
|
};
|
|
|
|
static int intr_sbus2ipl_4m[] = {
|
|
|
|
0, 2, 3, 5, 7, 9, 11, 13
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This value is or'ed into the attach args' interrupt level cookie
|
|
|
|
* if the interrupt level comes from an `intr' property, i.e. it is
|
|
|
|
* not an Sbus interrupt level.
|
|
|
|
*/
|
|
|
|
#define SBUS_INTR_COMPAT 0x80000000
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Print the location of some sbus-attached device (called just
|
|
|
|
* before attaching that device). If `sbus' is not NULL, the
|
|
|
|
* device was found but not configured; print the sbus as well.
|
|
|
|
* Return UNCONF (config_find ignores this if the device was configured).
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sbus_print(args, busname)
|
|
|
|
void *args;
|
|
|
|
const char *busname;
|
|
|
|
{
|
|
|
|
struct sbus_attach_args *sa = args;
|
1998-08-13 06:10:37 +04:00
|
|
|
int i;
|
1998-06-20 08:58:50 +04:00
|
|
|
|
|
|
|
if (busname)
|
|
|
|
printf("%s at %s", sa->sa_name, busname);
|
1998-09-06 03:57:24 +04:00
|
|
|
printf(" slot %ld offset 0x%lx", (long)sa->sa_slot,
|
|
|
|
(u_long)sa->sa_offset);
|
1998-08-13 06:10:37 +04:00
|
|
|
for (i=0; i<sa->sa_nintr; i++) {
|
|
|
|
struct sbus_intr *sbi = &sa->sa_intr[i];
|
1998-06-20 08:58:50 +04:00
|
|
|
|
1998-09-06 03:57:24 +04:00
|
|
|
printf(" vector %lx ipl %ld",
|
|
|
|
(u_long)sbi->sbi_vec,
|
|
|
|
(long)INTLEV(sbi->sbi_pri));
|
1998-06-20 08:58:50 +04:00
|
|
|
}
|
|
|
|
return (UNCONF);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sbus_match(parent, cf, aux)
|
|
|
|
struct device *parent;
|
|
|
|
struct cfdata *cf;
|
|
|
|
void *aux;
|
|
|
|
{
|
|
|
|
struct mainbus_attach_args *ma = aux;
|
|
|
|
|
|
|
|
return (strcmp(cf->cf_driver->cd_name, ma->ma_name) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Attach an Sbus.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
sbus_attach(parent, self, aux)
|
|
|
|
struct device *parent;
|
|
|
|
struct device *self;
|
|
|
|
void *aux;
|
|
|
|
{
|
1999-01-11 02:32:57 +03:00
|
|
|
struct sbus_softc *sc = (struct sbus_softc *)self;
|
1998-06-20 08:58:50 +04:00
|
|
|
struct mainbus_attach_args *ma = aux;
|
|
|
|
int node = ma->ma_node;
|
|
|
|
|
|
|
|
int node0, error;
|
|
|
|
bus_space_tag_t sbt;
|
|
|
|
struct sbus_attach_args sa;
|
|
|
|
char *busname = "sbus";
|
|
|
|
struct bootpath *bp = ma->ma_bp;
|
|
|
|
|
|
|
|
|
|
|
|
sc->sc_bustag = ma->ma_bustag;
|
|
|
|
sc->sc_dmatag = ma->ma_dmatag;
|
1998-09-06 03:57:24 +04:00
|
|
|
sc->sc_sysio = (struct sysioreg*)(u_long)ma->ma_address[0]; /* Use prom mapping for sysio. */
|
1998-06-20 08:58:50 +04:00
|
|
|
sc->sc_ign = ma->ma_interrupts[0] & INTMAP_IGN; /* Find interrupt group no */
|
|
|
|
|
|
|
|
/* Setup interrupt translation tables */
|
|
|
|
sc->sc_intr2ipl = CPU_ISSUN4C
|
|
|
|
? intr_sbus2ipl_4c
|
|
|
|
: intr_sbus2ipl_4m;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Record clock frequency for synchronous SCSI.
|
|
|
|
* IS THIS THE CORRECT DEFAULT??
|
|
|
|
*/
|
|
|
|
sc->sc_clockfreq = getpropint(node, "clock-frequency", 25*1000*1000);
|
|
|
|
printf(": clock = %s MHz\n", clockfreq(sc->sc_clockfreq));
|
|
|
|
|
|
|
|
sbt = sbus_alloc_bustag(sc);
|
|
|
|
sc->sc_dmatag = sbus_alloc_dmatag(sc);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the SBus burst transfer size if burst transfers are supported
|
|
|
|
*/
|
|
|
|
sc->sc_burst = getpropint(node, "burst-sizes", 0);
|
|
|
|
|
|
|
|
/* Propagate bootpath */
|
|
|
|
if (bp != NULL && strcmp(bp->name, busname) == 0)
|
|
|
|
bp++;
|
|
|
|
else
|
|
|
|
bp = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Collect address translations from the OBP.
|
|
|
|
*/
|
1998-09-05 20:23:08 +04:00
|
|
|
error = getprop(node, "ranges", sizeof(struct sbus_range),
|
1998-06-20 08:58:50 +04:00
|
|
|
&sc->sc_nrange, (void **)&sc->sc_range);
|
1999-05-31 04:14:00 +04:00
|
|
|
if (error)
|
1998-06-20 08:58:50 +04:00
|
|
|
panic("%s: error getting ranges property", sc->sc_dev.dv_xname);
|
|
|
|
|
1999-06-05 09:30:43 +04:00
|
|
|
/* initailise the IOMMU */
|
1998-06-20 08:58:50 +04:00
|
|
|
|
1999-06-05 09:30:43 +04:00
|
|
|
/* punch in our copies */
|
|
|
|
sc->sc_is.is_bustag = sc->sc_bustag;
|
|
|
|
sc->sc_is.is_iommu = &sc->sc_sysio->sys_iommu;
|
|
|
|
sc->sc_is.is_sb = &sc->sc_sysio->sys_strbuf;
|
1999-05-31 04:14:00 +04:00
|
|
|
|
1998-06-20 08:58:50 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (sbusdebug & SDB_DVMA)
|
1999-06-05 09:30:43 +04:00
|
|
|
printf("sysio base %p phys %p\n",
|
|
|
|
(long)sc->sc_sysio, (long)pmap_extract(pmap_kernel(), (vaddr_t)sc->sc_sysio));
|
1998-06-20 08:58:50 +04:00
|
|
|
#endif
|
|
|
|
|
1999-06-05 09:30:43 +04:00
|
|
|
/* XXX should have instance number */
|
|
|
|
iommu_init("SBus dvma", &sc->sc_is, 0);
|
1999-05-23 00:33:55 +04:00
|
|
|
|
1998-06-20 08:58:50 +04:00
|
|
|
/*
|
|
|
|
* Loop through ROM children, fixing any relative addresses
|
|
|
|
* and then configuring each device.
|
|
|
|
* `specials' is an array of device names that are treated
|
|
|
|
* specially:
|
|
|
|
*/
|
|
|
|
node0 = firstchild(node);
|
|
|
|
for (node = node0; node; node = nextsibling(node)) {
|
|
|
|
char *name = getpropstring(node, "name");
|
|
|
|
|
|
|
|
if (sbus_setup_attach_args(sc, sbt, sc->sc_dmatag,
|
|
|
|
node, bp, &sa) != 0) {
|
|
|
|
printf("sbus_attach: %s: incomplete\n", name);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
(void) config_found(&sc->sc_dev, (void *)&sa, sbus_print);
|
1998-08-13 06:10:37 +04:00
|
|
|
sbus_destroy_attach_args(&sa);
|
1998-06-20 08:58:50 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sbus_setup_attach_args(sc, bustag, dmatag, node, bp, sa)
|
|
|
|
struct sbus_softc *sc;
|
|
|
|
bus_space_tag_t bustag;
|
|
|
|
bus_dma_tag_t dmatag;
|
|
|
|
int node;
|
|
|
|
struct bootpath *bp;
|
|
|
|
struct sbus_attach_args *sa;
|
|
|
|
{
|
1998-08-13 06:10:37 +04:00
|
|
|
/*struct sbus_reg sbusreg;*/
|
|
|
|
/*int base;*/
|
1998-06-20 08:58:50 +04:00
|
|
|
int error;
|
1998-08-13 06:10:37 +04:00
|
|
|
int n;
|
1998-06-20 08:58:50 +04:00
|
|
|
|
|
|
|
bzero(sa, sizeof(struct sbus_attach_args));
|
1998-09-05 20:23:08 +04:00
|
|
|
error = getprop(node, "name", 1, &n, (void **)&sa->sa_name);
|
1998-08-13 06:10:37 +04:00
|
|
|
if (error != 0)
|
|
|
|
return (error);
|
|
|
|
sa->sa_name[n] = '\0';
|
|
|
|
|
1998-06-20 08:58:50 +04:00
|
|
|
sa->sa_bustag = bustag;
|
|
|
|
sa->sa_dmatag = dmatag;
|
|
|
|
sa->sa_node = node;
|
|
|
|
sa->sa_bp = bp;
|
|
|
|
|
1998-09-05 20:23:08 +04:00
|
|
|
error = getprop(node, "reg", sizeof(struct sbus_reg),
|
1998-08-13 06:10:37 +04:00
|
|
|
&sa->sa_nreg, (void **)&sa->sa_reg);
|
|
|
|
if (error != 0) {
|
|
|
|
char buf[32];
|
|
|
|
if (error != ENOENT ||
|
|
|
|
!node_has_property(node, "device_type") ||
|
|
|
|
strcmp(getpropstringA(node, "device_type", buf),
|
|
|
|
"hierarchical") != 0)
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
for (n = 0; n < sa->sa_nreg; n++) {
|
|
|
|
/* Convert to relative addressing, if necessary */
|
|
|
|
u_int32_t base = sa->sa_reg[n].sbr_offset;
|
|
|
|
if (SBUS_ABS(base)) {
|
|
|
|
sa->sa_reg[n].sbr_slot = SBUS_ABS_TO_SLOT(base);
|
|
|
|
sa->sa_reg[n].sbr_offset = SBUS_ABS_TO_OFFSET(base);
|
|
|
|
}
|
1998-06-20 08:58:50 +04:00
|
|
|
}
|
|
|
|
|
1998-08-13 06:10:37 +04:00
|
|
|
if ((error = sbus_get_intr(sc, node, &sa->sa_intr, &sa->sa_nintr)) != 0)
|
1998-06-20 08:58:50 +04:00
|
|
|
return (error);
|
|
|
|
|
1998-09-05 20:23:08 +04:00
|
|
|
error = getprop(node, "address", sizeof(u_int32_t),
|
1998-08-13 06:10:37 +04:00
|
|
|
&sa->sa_npromvaddrs, (void **)&sa->sa_promvaddrs);
|
|
|
|
if (error != 0 && error != ENOENT)
|
1998-06-20 08:58:50 +04:00
|
|
|
return (error);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1998-08-13 06:10:37 +04:00
|
|
|
void
|
|
|
|
sbus_destroy_attach_args(sa)
|
|
|
|
struct sbus_attach_args *sa;
|
|
|
|
{
|
|
|
|
if (sa->sa_name != NULL)
|
|
|
|
free(sa->sa_name, M_DEVBUF);
|
|
|
|
|
|
|
|
if (sa->sa_nreg != 0)
|
|
|
|
free(sa->sa_reg, M_DEVBUF);
|
|
|
|
|
|
|
|
if (sa->sa_intr)
|
|
|
|
free(sa->sa_intr, M_DEVBUF);
|
|
|
|
|
|
|
|
if (sa->sa_promvaddrs)
|
1998-09-06 03:57:24 +04:00
|
|
|
free((void *)sa->sa_promvaddrs, M_DEVBUF);
|
1998-08-13 06:10:37 +04:00
|
|
|
|
|
|
|
bzero(sa, sizeof(struct sbus_attach_args));/*DEBUG*/
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1998-06-20 08:58:50 +04:00
|
|
|
int
|
|
|
|
_sbus_bus_map(t, btype, offset, size, flags, vaddr, hp)
|
|
|
|
bus_space_tag_t t;
|
|
|
|
bus_type_t btype;
|
|
|
|
bus_addr_t offset;
|
|
|
|
bus_size_t size;
|
|
|
|
int flags;
|
1998-08-13 06:10:37 +04:00
|
|
|
vaddr_t vaddr;
|
1998-06-20 08:58:50 +04:00
|
|
|
bus_space_handle_t *hp;
|
|
|
|
{
|
|
|
|
struct sbus_softc *sc = t->cookie;
|
|
|
|
int64_t slot = btype;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < sc->sc_nrange; i++) {
|
|
|
|
bus_addr_t paddr;
|
|
|
|
|
|
|
|
if (sc->sc_range[i].cspace != slot)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* We've found the connection to the parent bus */
|
|
|
|
paddr = sc->sc_range[i].poffset + offset;
|
|
|
|
paddr |= ((bus_addr_t)sc->sc_range[i].pspace<<32);
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (sbusdebug & SDB_DVMA)
|
1998-09-06 03:57:24 +04:00
|
|
|
printf("\n_sbus_bus_map: mapping paddr slot %lx offset %lx poffset %lx paddr %lx\n",
|
|
|
|
(long)slot, (long)offset, (long)sc->sc_range[i].poffset, (long)paddr);
|
1998-06-20 08:58:50 +04:00
|
|
|
#endif
|
|
|
|
return (bus_space_map2(sc->sc_bustag, 0, paddr,
|
|
|
|
size, flags, vaddr, hp));
|
|
|
|
}
|
|
|
|
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sbus_bus_mmap(t, btype, paddr, flags, hp)
|
|
|
|
bus_space_tag_t t;
|
|
|
|
bus_type_t btype;
|
|
|
|
bus_addr_t paddr;
|
|
|
|
int flags;
|
|
|
|
bus_space_handle_t *hp;
|
|
|
|
{
|
|
|
|
bus_addr_t offset = paddr;
|
|
|
|
int slot = (paddr>>32);
|
|
|
|
struct sbus_softc *sc = t->cookie;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < sc->sc_nrange; i++) {
|
|
|
|
bus_addr_t paddr;
|
|
|
|
|
|
|
|
if (sc->sc_range[i].cspace != slot)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
paddr = sc->sc_range[i].poffset + offset;
|
|
|
|
paddr |= ((bus_addr_t)sc->sc_range[i].pspace<<32);
|
|
|
|
return (bus_space_mmap(sc->sc_bustag, 0, paddr,
|
|
|
|
flags, hp));
|
|
|
|
}
|
|
|
|
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Each attached device calls sbus_establish after it initializes
|
|
|
|
* its sbusdev portion.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
sbus_establish(sd, dev)
|
|
|
|
register struct sbusdev *sd;
|
|
|
|
register struct device *dev;
|
|
|
|
{
|
|
|
|
register struct sbus_softc *sc;
|
|
|
|
register struct device *curdev;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We have to look for the sbus by name, since it is not necessarily
|
|
|
|
* our immediate parent (i.e. sun4m /iommu/sbus/espdma/esp)
|
|
|
|
* We don't just use the device structure of the above-attached
|
|
|
|
* sbus, since we might (in the future) support multiple sbus's.
|
|
|
|
*/
|
|
|
|
for (curdev = dev->dv_parent; ; curdev = curdev->dv_parent) {
|
|
|
|
if (!curdev || !curdev->dv_xname)
|
|
|
|
panic("sbus_establish: can't find sbus parent for %s",
|
|
|
|
sd->sd_dev->dv_xname
|
|
|
|
? sd->sd_dev->dv_xname
|
|
|
|
: "<unknown>" );
|
|
|
|
|
|
|
|
if (strncmp(curdev->dv_xname, "sbus", 4) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sc = (struct sbus_softc *) curdev;
|
|
|
|
|
|
|
|
sd->sd_dev = dev;
|
|
|
|
sd->sd_bchain = sc->sc_sbdev;
|
|
|
|
sc->sc_sbdev = sd;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the given sbus. (???)
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
sbusreset(sbus)
|
|
|
|
int sbus;
|
|
|
|
{
|
|
|
|
register struct sbusdev *sd;
|
|
|
|
struct sbus_softc *sc = sbus_cd.cd_devs[sbus];
|
|
|
|
struct device *dev;
|
|
|
|
|
|
|
|
printf("reset %s:", sc->sc_dev.dv_xname);
|
|
|
|
for (sd = sc->sc_sbdev; sd != NULL; sd = sd->sd_bchain) {
|
|
|
|
if (sd->sd_reset) {
|
|
|
|
dev = sd->sd_dev;
|
|
|
|
(*sd->sd_reset)(dev);
|
|
|
|
printf(" %s", dev->dv_xname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Reload iommu regs */
|
1999-06-05 09:30:43 +04:00
|
|
|
iommu_reset(&sc->sc_is);
|
1998-06-20 08:58:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get interrupt attributes for an Sbus device.
|
|
|
|
*/
|
|
|
|
int
|
1998-08-13 06:10:37 +04:00
|
|
|
sbus_get_intr(sc, node, ipp, np)
|
1998-06-20 08:58:50 +04:00
|
|
|
struct sbus_softc *sc;
|
|
|
|
int node;
|
1998-08-13 06:10:37 +04:00
|
|
|
struct sbus_intr **ipp;
|
|
|
|
int *np;
|
1998-06-20 08:58:50 +04:00
|
|
|
{
|
|
|
|
int *ipl;
|
1998-08-13 06:10:37 +04:00
|
|
|
int i, n, error;
|
1998-06-20 08:58:50 +04:00
|
|
|
char buf[32];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The `interrupts' property contains the Sbus interrupt level.
|
|
|
|
*/
|
|
|
|
ipl = NULL;
|
1998-09-05 20:23:08 +04:00
|
|
|
if (getprop(node, "interrupts", sizeof(int), np, (void **)&ipl) == 0) {
|
1998-08-13 06:10:37 +04:00
|
|
|
/* Change format to an `struct sbus_intr' array */
|
|
|
|
struct sbus_intr *ip;
|
1999-03-18 06:23:53 +03:00
|
|
|
/* Default to interrupt level 2 -- otherwise unused */
|
|
|
|
int pri = INTLEVENCODE(2);
|
1998-08-13 06:10:37 +04:00
|
|
|
ip = malloc(*np * sizeof(struct sbus_intr), M_DEVBUF, M_NOWAIT);
|
|
|
|
if (ip == NULL)
|
|
|
|
return (ENOMEM);
|
1998-06-20 08:58:50 +04:00
|
|
|
/* Now things get ugly. We need to take this value which is
|
|
|
|
* the interrupt vector number and encode the IPL into it
|
|
|
|
* somehow. Luckily, the interrupt vector has lots of free
|
|
|
|
* space and we can easily stuff the IPL in there for a while.
|
|
|
|
*/
|
|
|
|
getpropstringA(node, "device_type", buf);
|
1999-03-18 06:23:53 +03:00
|
|
|
if (!buf[0]) {
|
|
|
|
getpropstringA(node, "name", buf);
|
|
|
|
}
|
1998-08-13 06:10:37 +04:00
|
|
|
for (i=0; intrmap[i].in_class; i++) {
|
|
|
|
if (strcmp(intrmap[i].in_class, buf) == 0) {
|
|
|
|
pri = INTLEVENCODE(intrmap[i].in_lev);
|
1998-06-20 08:58:50 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
1998-08-13 06:10:37 +04:00
|
|
|
for (n = 0; n < *np; n++) {
|
|
|
|
/*
|
|
|
|
* We encode vector and priority into sbi_pri so we
|
|
|
|
* can pass them as a unit. This will go away if
|
|
|
|
* sbus_establish ever takes an sbus_intr instead
|
|
|
|
* of an integer level.
|
|
|
|
* Stuff the real vector in sbi_vec.
|
|
|
|
*/
|
|
|
|
ip[n].sbi_pri = pri|ipl[n];
|
|
|
|
ip[n].sbi_vec = ipl[n];
|
|
|
|
}
|
1998-06-20 08:58:50 +04:00
|
|
|
free(ipl, M_DEVBUF);
|
1998-08-13 06:10:37 +04:00
|
|
|
*ipp = ip;
|
1998-06-20 08:58:50 +04:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We really don't support the following */
|
|
|
|
/* printf("\nWARNING: sbus_get_intr() \"interrupts\" not found -- using \"intr\"\n"); */
|
|
|
|
/* And some devices don't even have interrupts */
|
|
|
|
/*
|
|
|
|
* Fall back on `intr' property.
|
|
|
|
*/
|
1998-08-13 06:10:37 +04:00
|
|
|
*ipp = NULL;
|
1998-09-05 20:23:08 +04:00
|
|
|
error = getprop(node, "intr", sizeof(struct sbus_intr),
|
1998-08-13 06:10:37 +04:00
|
|
|
np, (void **)ipp);
|
|
|
|
switch (error) {
|
1998-06-20 08:58:50 +04:00
|
|
|
case 0:
|
1998-08-13 06:10:37 +04:00
|
|
|
for (n = *np; n-- > 0;) {
|
|
|
|
/*
|
|
|
|
* Move the interrupt vector into place.
|
|
|
|
* We could remap the level, but the SBUS priorities
|
|
|
|
* are probably good enough.
|
|
|
|
*/
|
|
|
|
(*ipp)[n].sbi_vec = (*ipp)[n].sbi_pri;
|
|
|
|
(*ipp)[n].sbi_pri |= INTLEVENCODE((*ipp)[n].sbi_pri);
|
1998-06-20 08:58:50 +04:00
|
|
|
}
|
1998-08-13 06:10:37 +04:00
|
|
|
break;
|
1998-06-20 08:58:50 +04:00
|
|
|
case ENOENT:
|
1998-08-13 06:10:37 +04:00
|
|
|
error = 0;
|
|
|
|
break;
|
1998-06-20 08:58:50 +04:00
|
|
|
}
|
|
|
|
|
1998-08-13 06:10:37 +04:00
|
|
|
return (error);
|
1998-06-20 08:58:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Install an interrupt handler for an Sbus device.
|
|
|
|
*/
|
|
|
|
void *
|
|
|
|
sbus_intr_establish(t, level, flags, handler, arg)
|
|
|
|
bus_space_tag_t t;
|
|
|
|
int level;
|
|
|
|
int flags;
|
|
|
|
int (*handler) __P((void *));
|
|
|
|
void *arg;
|
|
|
|
{
|
|
|
|
struct sbus_softc *sc = t->cookie;
|
|
|
|
struct intrhand *ih;
|
|
|
|
int ipl;
|
1998-09-06 03:57:24 +04:00
|
|
|
long vec = level;
|
1998-06-20 08:58:50 +04:00
|
|
|
|
|
|
|
ih = (struct intrhand *)
|
|
|
|
malloc(sizeof(struct intrhand), M_DEVBUF, M_NOWAIT);
|
|
|
|
if (ih == NULL)
|
|
|
|
return (NULL);
|
|
|
|
|
|
|
|
if ((flags & BUS_INTR_ESTABLISH_SOFTINTR) != 0)
|
1998-09-06 03:57:24 +04:00
|
|
|
ipl = vec;
|
|
|
|
else if ((vec & SBUS_INTR_COMPAT) != 0)
|
|
|
|
ipl = vec & ~SBUS_INTR_COMPAT;
|
1998-06-20 08:58:50 +04:00
|
|
|
else {
|
|
|
|
/* Decode and remove IPL */
|
1998-09-06 03:57:24 +04:00
|
|
|
ipl = INTLEV(vec);
|
|
|
|
vec = INTVEC(vec);
|
1998-06-20 08:58:50 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (sbusdebug & SDB_INTR) {
|
1998-09-06 03:57:24 +04:00
|
|
|
printf("\nsbus: intr[%ld]%lx: %lx\n", (long)ipl, (long)vec,
|
|
|
|
intrlev[vec]);
|
1998-06-20 08:58:50 +04:00
|
|
|
printf("Hunting for IRQ...\n");
|
|
|
|
}
|
|
|
|
#endif
|
1998-09-06 03:57:24 +04:00
|
|
|
if ((vec & INTMAP_OBIO) == 0) {
|
1998-06-20 08:58:50 +04:00
|
|
|
/* We're in an SBUS slot */
|
|
|
|
/* Register the map and clear intr registers */
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (sbusdebug & SDB_INTR) {
|
1998-09-06 03:57:24 +04:00
|
|
|
int64_t *intrptr = &(&sc->sc_sysio->sbus_slot0_int)[INTSLOT(vec)];
|
1998-06-20 08:58:50 +04:00
|
|
|
int64_t intrmap = *intrptr;
|
|
|
|
|
1998-09-06 03:57:24 +04:00
|
|
|
printf("Found SBUS %lx IRQ as %llx in slot %ld\n",
|
|
|
|
(long)vec, (long)intrmap,
|
|
|
|
(long)INTSLOT(vec));
|
1998-06-20 08:58:50 +04:00
|
|
|
}
|
|
|
|
#endif
|
1998-09-06 03:57:24 +04:00
|
|
|
ih->ih_map = &(&sc->sc_sysio->sbus_slot0_int)[INTSLOT(vec)];
|
|
|
|
ih->ih_clr = &sc->sc_sysio->sbus0_clr_int[INTVEC(vec)];
|
1998-06-20 08:58:50 +04:00
|
|
|
/* Enable the interrupt */
|
1998-09-06 03:57:24 +04:00
|
|
|
vec |= INTMAP_V;
|
1999-01-11 02:32:57 +03:00
|
|
|
/* Insert IGN */
|
|
|
|
vec |= sc->sc_ign;
|
1998-09-06 03:57:24 +04:00
|
|
|
bus_space_write_8(sc->sc_bustag, ih->ih_map, 0, vec);
|
1998-06-20 08:58:50 +04:00
|
|
|
} else {
|
|
|
|
int64_t *intrptr = &sc->sc_sysio->scsi_int_map;
|
|
|
|
int64_t intrmap = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Insert IGN */
|
1998-09-06 03:57:24 +04:00
|
|
|
vec |= sc->sc_ign;
|
1998-06-20 08:58:50 +04:00
|
|
|
for (i=0;
|
|
|
|
&intrptr[i] <= (int64_t *)&sc->sc_sysio->reserved_int_map &&
|
1998-09-06 03:57:24 +04:00
|
|
|
INTVEC(intrmap=intrptr[i]) != INTVEC(vec);
|
1998-06-20 08:58:50 +04:00
|
|
|
i++);
|
1998-09-06 03:57:24 +04:00
|
|
|
if (INTVEC(intrmap) == INTVEC(vec)) {
|
1998-06-20 08:58:50 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (sbusdebug & SDB_INTR)
|
1998-09-06 03:57:24 +04:00
|
|
|
printf("Found OBIO %lx IRQ as %lx in slot %d\n",
|
|
|
|
vec, (long)intrmap, i);
|
1998-06-20 08:58:50 +04:00
|
|
|
#endif
|
|
|
|
/* Register the map and clear intr registers */
|
|
|
|
ih->ih_map = &intrptr[i];
|
|
|
|
intrptr = (int64_t *)&sc->sc_sysio->scsi_clr_int;
|
|
|
|
ih->ih_clr = &intrptr[i];
|
|
|
|
/* Enable the interrupt */
|
|
|
|
intrmap |= INTMAP_V;
|
1998-09-06 03:57:24 +04:00
|
|
|
bus_space_write_8(sc->sc_bustag, ih->ih_map, 0, (u_long)intrmap);
|
1998-06-20 08:58:50 +04:00
|
|
|
} else panic("IRQ not found!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef DEBUG
|
1998-09-06 03:57:24 +04:00
|
|
|
if (sbusdebug & SDB_INTR) { long i; for (i=0; i<1400000000; i++); }
|
1998-06-20 08:58:50 +04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
ih->ih_fun = handler;
|
|
|
|
ih->ih_arg = arg;
|
1998-09-06 03:57:24 +04:00
|
|
|
ih->ih_number = vec;
|
1998-06-20 08:58:50 +04:00
|
|
|
ih->ih_pil = (1<<ipl);
|
1999-06-07 09:28:03 +04:00
|
|
|
intr_establish(ipl, ih);
|
1998-06-20 08:58:50 +04:00
|
|
|
return (ih);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bus_space_tag_t
|
|
|
|
sbus_alloc_bustag(sc)
|
|
|
|
struct sbus_softc *sc;
|
|
|
|
{
|
|
|
|
bus_space_tag_t sbt;
|
|
|
|
|
|
|
|
sbt = (bus_space_tag_t)
|
|
|
|
malloc(sizeof(struct sparc_bus_space_tag), M_DEVBUF, M_NOWAIT);
|
|
|
|
if (sbt == NULL)
|
|
|
|
return (NULL);
|
|
|
|
|
|
|
|
bzero(sbt, sizeof *sbt);
|
|
|
|
sbt->cookie = sc;
|
|
|
|
sbt->parent = sc->sc_bustag;
|
1999-05-23 00:33:55 +04:00
|
|
|
sbt->type = SBUS_BUS_SPACE;
|
1998-06-20 08:58:50 +04:00
|
|
|
sbt->sparc_bus_map = _sbus_bus_map;
|
|
|
|
sbt->sparc_bus_mmap = sbus_bus_mmap;
|
|
|
|
sbt->sparc_intr_establish = sbus_intr_establish;
|
|
|
|
return (sbt);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bus_dma_tag_t
|
|
|
|
sbus_alloc_dmatag(sc)
|
|
|
|
struct sbus_softc *sc;
|
|
|
|
{
|
|
|
|
bus_dma_tag_t sdt, psdt = sc->sc_dmatag;
|
|
|
|
|
|
|
|
sdt = (bus_dma_tag_t)
|
|
|
|
malloc(sizeof(struct sparc_bus_dma_tag), M_DEVBUF, M_NOWAIT);
|
|
|
|
if (sdt == NULL)
|
|
|
|
/* Panic? */
|
|
|
|
return (psdt);
|
|
|
|
|
|
|
|
sdt->_cookie = sc;
|
|
|
|
sdt->_parent = psdt;
|
|
|
|
#define PCOPY(x) sdt->x = psdt->x
|
|
|
|
PCOPY(_dmamap_create);
|
|
|
|
PCOPY(_dmamap_destroy);
|
|
|
|
sdt->_dmamap_load = sbus_dmamap_load;
|
|
|
|
PCOPY(_dmamap_load_mbuf);
|
|
|
|
PCOPY(_dmamap_load_uio);
|
|
|
|
PCOPY(_dmamap_load_raw);
|
|
|
|
sdt->_dmamap_unload = sbus_dmamap_unload;
|
|
|
|
sdt->_dmamap_sync = sbus_dmamap_sync;
|
|
|
|
sdt->_dmamem_alloc = sbus_dmamem_alloc;
|
|
|
|
sdt->_dmamem_free = sbus_dmamem_free;
|
1998-07-07 07:05:02 +04:00
|
|
|
sdt->_dmamem_map = sbus_dmamem_map;
|
|
|
|
sdt->_dmamem_unmap = sbus_dmamem_unmap;
|
1998-06-20 08:58:50 +04:00
|
|
|
PCOPY(_dmamem_mmap);
|
|
|
|
#undef PCOPY
|
|
|
|
sc->sc_dmatag = sdt;
|
|
|
|
return (sdt);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sbus_dmamap_load(t, map, buf, buflen, p, flags)
|
|
|
|
bus_dma_tag_t t;
|
|
|
|
bus_dmamap_t map;
|
|
|
|
void *buf;
|
|
|
|
bus_size_t buflen;
|
|
|
|
struct proc *p;
|
|
|
|
int flags;
|
|
|
|
{
|
1999-05-23 00:33:55 +04:00
|
|
|
int err, s;
|
1998-06-20 08:58:50 +04:00
|
|
|
bus_size_t sgsize;
|
1998-08-13 06:10:37 +04:00
|
|
|
paddr_t curaddr;
|
1999-05-23 00:33:55 +04:00
|
|
|
u_long dvmaddr;
|
|
|
|
vaddr_t vaddr = (vaddr_t)buf;
|
1998-06-20 08:58:50 +04:00
|
|
|
pmap_t pmap;
|
|
|
|
struct sbus_softc *sc = (struct sbus_softc *)t->_cookie;
|
|
|
|
|
1998-07-07 07:05:02 +04:00
|
|
|
if (map->dm_nsegs) {
|
|
|
|
/* Already in use?? */
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
printf("sbus_dmamap_load: map still in use\n");
|
|
|
|
#endif
|
|
|
|
bus_dmamap_unload(t, map);
|
|
|
|
}
|
1999-06-20 04:51:29 +04:00
|
|
|
|
1999-05-23 00:33:55 +04:00
|
|
|
/*
|
|
|
|
* Make sure that on error condition we return "no valid mappings".
|
|
|
|
*/
|
|
|
|
map->dm_nsegs = 0;
|
|
|
|
|
|
|
|
if (buflen > map->_dm_size)
|
|
|
|
#ifdef DEBUG
|
|
|
|
{
|
1999-05-31 04:14:00 +04:00
|
|
|
printf("sbus_dmamap_load(): error %d > %d -- map size exceeded!\n", buflen, map->_dm_size);
|
1999-05-23 00:33:55 +04:00
|
|
|
Debugger();
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
return (EINVAL);
|
|
|
|
#endif
|
|
|
|
sgsize = round_page(buflen + ((int)vaddr & PGOFSET));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX Need to implement "don't dma across this boundry".
|
|
|
|
*/
|
|
|
|
|
|
|
|
s = splhigh();
|
1999-06-05 09:30:43 +04:00
|
|
|
err = extent_alloc(sc->sc_is.is_dvmamap, sgsize, NBPG,
|
1999-05-23 00:33:55 +04:00
|
|
|
map->_dm_boundary, EX_NOWAIT, (u_long *)&dvmaddr);
|
|
|
|
splx(s);
|
|
|
|
|
|
|
|
if (err != 0)
|
1998-06-20 08:58:50 +04:00
|
|
|
return (err);
|
|
|
|
|
1999-05-23 00:33:55 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (dvmaddr == (bus_addr_t)-1)
|
|
|
|
{
|
1999-05-31 04:14:00 +04:00
|
|
|
printf("sbus_dmamap_load(): dvmamap_alloc(%d, %x) failed!\n", sgsize, flags);
|
1999-05-23 00:33:55 +04:00
|
|
|
Debugger();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (dvmaddr == (bus_addr_t)-1)
|
|
|
|
return (ENOMEM);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We always use just one segment.
|
|
|
|
*/
|
|
|
|
map->dm_mapsize = buflen;
|
|
|
|
map->dm_nsegs = 1;
|
|
|
|
map->dm_segs[0].ds_addr = dvmaddr + (vaddr & PGOFSET);
|
|
|
|
map->dm_segs[0].ds_len = sgsize;
|
|
|
|
|
1998-06-20 08:58:50 +04:00
|
|
|
if (p != NULL)
|
|
|
|
pmap = p->p_vmspace->vm_map.pmap;
|
|
|
|
else
|
|
|
|
pmap = pmap_kernel();
|
|
|
|
|
1998-07-07 07:05:02 +04:00
|
|
|
dvmaddr = trunc_page(map->dm_segs[0].ds_addr);
|
1998-06-20 08:58:50 +04:00
|
|
|
sgsize = round_page(buflen + ((int)vaddr & PGOFSET));
|
|
|
|
for (; buflen > 0; ) {
|
|
|
|
/*
|
|
|
|
* Get the physical address for this page.
|
|
|
|
*/
|
1998-08-13 06:10:37 +04:00
|
|
|
if ((curaddr = (bus_addr_t)pmap_extract(pmap, (vaddr_t)vaddr)) == NULL) {
|
1998-06-20 08:58:50 +04:00
|
|
|
bus_dmamap_unload(t, map);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compute the segment size, and adjust counts.
|
|
|
|
*/
|
|
|
|
sgsize = NBPG - ((u_long)vaddr & PGOFSET);
|
|
|
|
if (buflen < sgsize)
|
|
|
|
sgsize = buflen;
|
|
|
|
|
1998-07-07 07:05:02 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (sbusdebug & SDB_DVMA)
|
1998-09-06 03:57:24 +04:00
|
|
|
printf("sbus_dmamap_load: map %p loading va %lx at pa %lx\n",
|
|
|
|
map, (long)dvmaddr, (long)(curaddr & ~(NBPG-1)));
|
1998-07-07 07:05:02 +04:00
|
|
|
#endif
|
1999-06-20 04:51:29 +04:00
|
|
|
iommu_enter(&sc->sc_is, trunc_page(dvmaddr), trunc_page(curaddr), flags);
|
1998-06-20 08:58:50 +04:00
|
|
|
|
|
|
|
dvmaddr += PAGE_SIZE;
|
|
|
|
vaddr += sgsize;
|
|
|
|
buflen -= sgsize;
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sbus_dmamap_unload(t, map)
|
|
|
|
bus_dma_tag_t t;
|
|
|
|
bus_dmamap_t map;
|
|
|
|
{
|
1998-08-13 06:10:37 +04:00
|
|
|
vaddr_t addr;
|
1999-05-23 00:33:55 +04:00
|
|
|
int len, error, s;
|
|
|
|
bus_addr_t dvmaddr;
|
|
|
|
bus_size_t sgsize;
|
1998-06-20 08:58:50 +04:00
|
|
|
struct sbus_softc *sc = (struct sbus_softc *)t->_cookie;
|
|
|
|
|
|
|
|
if (map->dm_nsegs != 1)
|
1999-05-31 04:14:00 +04:00
|
|
|
panic("sbus_dmamap_unload: nsegs = %d", map->dm_nsegs);
|
1998-06-20 08:58:50 +04:00
|
|
|
|
1998-07-07 07:05:02 +04:00
|
|
|
addr = trunc_page(map->dm_segs[0].ds_addr);
|
1998-06-20 08:58:50 +04:00
|
|
|
len = map->dm_segs[0].ds_len;
|
|
|
|
|
1998-07-07 07:05:02 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (sbusdebug & SDB_DVMA)
|
1998-09-06 03:57:24 +04:00
|
|
|
printf("sbus_dmamap_unload: map %p removing va %lx size %lx\n",
|
|
|
|
map, (long)addr, (long)len);
|
1998-07-07 07:05:02 +04:00
|
|
|
#endif
|
1999-06-20 04:51:29 +04:00
|
|
|
iommu_remove(&sc->sc_is, addr, len);
|
1999-05-23 00:33:55 +04:00
|
|
|
dvmaddr = (map->dm_segs[0].ds_addr & ~PGOFSET);
|
|
|
|
sgsize = map->dm_segs[0].ds_len;
|
|
|
|
|
|
|
|
/* Mark the mappings as invalid. */
|
|
|
|
map->dm_mapsize = 0;
|
|
|
|
map->dm_nsegs = 0;
|
|
|
|
|
|
|
|
/* Unmapping is bus dependent */
|
|
|
|
s = splhigh();
|
1999-06-05 09:30:43 +04:00
|
|
|
error = extent_free(sc->sc_is.is_dvmamap, dvmaddr, sgsize, EX_NOWAIT);
|
1999-05-23 00:33:55 +04:00
|
|
|
splx(s);
|
|
|
|
if (error != 0)
|
|
|
|
printf("warning: %ld of DVMA space lost\n", (long)sgsize);
|
|
|
|
|
|
|
|
cache_flush((caddr_t)dvmaddr, (u_int) sgsize);
|
1998-06-20 08:58:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
sbus_dmamap_sync(t, map, offset, len, ops)
|
|
|
|
bus_dma_tag_t t;
|
|
|
|
bus_dmamap_t map;
|
|
|
|
bus_addr_t offset;
|
|
|
|
bus_size_t len;
|
|
|
|
int ops;
|
|
|
|
{
|
|
|
|
struct sbus_softc *sc = (struct sbus_softc *)t->_cookie;
|
1998-08-13 06:10:37 +04:00
|
|
|
vaddr_t va = map->dm_segs[0].ds_addr + offset;
|
1998-06-20 08:58:50 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We only support one DMA segment; supporting more makes this code
|
|
|
|
* too unweildy.
|
|
|
|
*/
|
|
|
|
|
1998-09-06 03:57:24 +04:00
|
|
|
if (ops&BUS_DMASYNC_PREREAD) {
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (sbusdebug & SDB_DVMA)
|
|
|
|
printf("sbus_dmamap_sync: syncing va %p len %lu BUS_DMASYNC_PREREAD\n",
|
|
|
|
(long)va, (u_long)len);
|
|
|
|
#endif
|
|
|
|
|
1998-06-20 08:58:50 +04:00
|
|
|
/* Nothing to do */;
|
1998-09-06 03:57:24 +04:00
|
|
|
}
|
1998-06-20 08:58:50 +04:00
|
|
|
if (ops&BUS_DMASYNC_POSTREAD) {
|
|
|
|
/*
|
|
|
|
* We should sync the IOMMU streaming caches here first.
|
|
|
|
*/
|
1998-09-06 03:57:24 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (sbusdebug & SDB_DVMA)
|
|
|
|
printf("sbus_dmamap_sync: syncing va %p len %lu BUS_DMASYNC_POSTREAD\n",
|
|
|
|
(long)va, (u_long)len);
|
|
|
|
#endif
|
1998-06-20 08:58:50 +04:00
|
|
|
while (len > 0) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Streaming buffer flushes:
|
|
|
|
*
|
|
|
|
* 1 Tell strbuf to flush by storing va to strbuf_pgflush
|
|
|
|
* If we're not on a cache line boundary (64-bits):
|
|
|
|
* 2 Store 0 in flag
|
|
|
|
* 3 Store pointer to flag in flushsync
|
|
|
|
* 4 wait till flushsync becomes 0x1
|
|
|
|
*
|
|
|
|
* If it takes more than .5 sec, something went wrong.
|
|
|
|
*/
|
1998-09-06 03:57:24 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (sbusdebug & SDB_DVMA)
|
|
|
|
printf("sbus_dmamap_sync: flushing va %p, %lu bytes left\n",
|
|
|
|
(long)va, (u_long)len);
|
|
|
|
#endif
|
1999-06-05 09:30:43 +04:00
|
|
|
bus_space_write_8(sc->sc_bustag, &sc->sc_is.is_sb->strbuf_pgflush, 0, va);
|
1998-06-20 08:58:50 +04:00
|
|
|
if (len <= NBPG) {
|
1999-06-20 04:51:29 +04:00
|
|
|
iommu_flush(&sc->sc_is);
|
1998-09-06 03:57:24 +04:00
|
|
|
len = 0;
|
|
|
|
} else
|
|
|
|
len -= NBPG;
|
1998-06-20 08:58:50 +04:00
|
|
|
va += NBPG;
|
|
|
|
}
|
|
|
|
}
|
1998-09-06 03:57:24 +04:00
|
|
|
if (ops&BUS_DMASYNC_PREWRITE) {
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (sbusdebug & SDB_DVMA)
|
|
|
|
printf("sbus_dmamap_sync: syncing va %p len %lu BUS_DMASYNC_PREWRITE\n",
|
|
|
|
(long)va, (u_long)len);
|
|
|
|
#endif
|
1998-06-20 08:58:50 +04:00
|
|
|
/* Nothing to do */;
|
1998-09-06 03:57:24 +04:00
|
|
|
}
|
|
|
|
if (ops&BUS_DMASYNC_POSTWRITE) {
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (sbusdebug & SDB_DVMA)
|
|
|
|
printf("sbus_dmamap_sync: syncing va %p len %lu BUS_DMASYNC_POSTWRITE\n",
|
|
|
|
(long)va, (u_long)len);
|
|
|
|
#endif
|
|
|
|
/* Nothing to do */;
|
|
|
|
}
|
1998-06-20 08:58:50 +04:00
|
|
|
bus_dmamap_sync(t->_parent, map, offset, len, ops);
|
|
|
|
}
|
|
|
|
|
1998-09-06 03:57:24 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Take memory allocated by our parent bus and generate DVMA mappings for it.
|
|
|
|
*/
|
1998-06-20 08:58:50 +04:00
|
|
|
int
|
|
|
|
sbus_dmamem_alloc(t, size, alignment, boundary, segs, nsegs, rsegs, flags)
|
|
|
|
bus_dma_tag_t t;
|
|
|
|
bus_size_t size, alignment, boundary;
|
|
|
|
bus_dma_segment_t *segs;
|
|
|
|
int nsegs;
|
|
|
|
int *rsegs;
|
|
|
|
int flags;
|
|
|
|
{
|
1998-08-13 06:10:37 +04:00
|
|
|
paddr_t curaddr;
|
1999-05-23 00:33:55 +04:00
|
|
|
u_long dvmaddr;
|
1998-06-20 08:58:50 +04:00
|
|
|
vm_page_t m;
|
|
|
|
struct pglist *mlist;
|
|
|
|
int error;
|
1999-05-23 00:33:55 +04:00
|
|
|
int n, s;
|
1998-06-20 08:58:50 +04:00
|
|
|
struct sbus_softc *sc = (struct sbus_softc *)t->_cookie;
|
|
|
|
|
|
|
|
if ((error = bus_dmamem_alloc(t->_parent, size, alignment,
|
|
|
|
boundary, segs, nsegs, rsegs, flags)))
|
|
|
|
return (error);
|
|
|
|
|
1998-09-06 03:57:24 +04:00
|
|
|
/*
|
|
|
|
* Allocate a DVMA mapping for our new memory.
|
|
|
|
*/
|
1999-06-05 09:30:43 +04:00
|
|
|
for (n = 0; n < *rsegs; n++) {
|
1999-05-23 00:33:55 +04:00
|
|
|
#if 1
|
|
|
|
s = splhigh();
|
1999-06-05 09:30:43 +04:00
|
|
|
if (extent_alloc(sc->sc_is.is_dvmamap, segs[0].ds_len, alignment,
|
1999-05-23 00:33:55 +04:00
|
|
|
boundary, EX_NOWAIT, (u_long *)&dvmaddr)) {
|
|
|
|
splx(s);
|
|
|
|
/* Free what we got and exit */
|
|
|
|
bus_dmamem_free(t->_parent, segs, nsegs);
|
|
|
|
return (ENOMEM);
|
|
|
|
}
|
|
|
|
splx(s);
|
|
|
|
#else
|
1998-09-06 03:57:24 +04:00
|
|
|
dvmaddr = dvmamap_alloc(segs[0].ds_len, flags);
|
|
|
|
if (dvmaddr == (bus_addr_t)-1) {
|
|
|
|
/* Free what we got and exit */
|
|
|
|
bus_dmamem_free(t->_parent, segs, nsegs);
|
|
|
|
return (ENOMEM);
|
|
|
|
}
|
1999-05-23 00:33:55 +04:00
|
|
|
#endif
|
1998-09-06 03:57:24 +04:00
|
|
|
segs[n].ds_addr = dvmaddr;
|
1998-06-20 08:58:50 +04:00
|
|
|
size = segs[n].ds_len;
|
|
|
|
mlist = segs[n]._ds_mlist;
|
|
|
|
|
|
|
|
/* Map memory into DVMA space */
|
|
|
|
for (m = mlist->tqh_first; m != NULL; m = m->pageq.tqe_next) {
|
|
|
|
curaddr = VM_PAGE_TO_PHYS(m);
|
1999-05-23 00:33:55 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (sbusdebug & SDB_DVMA)
|
|
|
|
printf("sbus_dmamem_alloc: map %p loading va %lx at pa %lx\n",
|
|
|
|
(long)m, (long)dvmaddr, (long)(curaddr & ~(NBPG-1)));
|
|
|
|
#endif
|
1999-06-20 04:51:29 +04:00
|
|
|
iommu_enter(&sc->sc_is, dvmaddr, curaddr, flags);
|
1998-06-20 08:58:50 +04:00
|
|
|
dvmaddr += PAGE_SIZE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sbus_dmamem_free(t, segs, nsegs)
|
|
|
|
bus_dma_tag_t t;
|
|
|
|
bus_dma_segment_t *segs;
|
|
|
|
int nsegs;
|
|
|
|
{
|
1998-08-13 06:10:37 +04:00
|
|
|
vaddr_t addr;
|
1998-07-07 07:05:02 +04:00
|
|
|
int len;
|
1999-05-23 00:33:55 +04:00
|
|
|
int n, s, error;
|
1998-06-20 08:58:50 +04:00
|
|
|
struct sbus_softc *sc = (struct sbus_softc *)t->_cookie;
|
|
|
|
|
|
|
|
|
|
|
|
for (n=0; n<nsegs; n++) {
|
|
|
|
addr = segs[n].ds_addr;
|
|
|
|
len = segs[n].ds_len;
|
1999-06-20 04:51:29 +04:00
|
|
|
iommu_remove(&sc->sc_is, addr, len);
|
1999-05-23 00:33:55 +04:00
|
|
|
#if 1
|
|
|
|
s = splhigh();
|
1999-06-05 09:30:43 +04:00
|
|
|
error = extent_free(sc->sc_is.is_dvmamap, addr, len, EX_NOWAIT);
|
1999-05-23 00:33:55 +04:00
|
|
|
splx(s);
|
|
|
|
if (error != 0)
|
|
|
|
printf("warning: %ld of DVMA space lost\n", (long)len);
|
|
|
|
#else
|
1998-09-06 03:57:24 +04:00
|
|
|
dvmamap_free(addr, len);
|
1999-05-23 00:33:55 +04:00
|
|
|
#endif
|
1998-06-20 08:58:50 +04:00
|
|
|
}
|
|
|
|
bus_dmamem_free(t->_parent, segs, nsegs);
|
|
|
|
}
|
|
|
|
|
1998-07-07 07:05:02 +04:00
|
|
|
/*
|
1998-09-06 03:57:24 +04:00
|
|
|
* Map the DVMA mappings into the kernel pmap.
|
1998-07-07 07:05:02 +04:00
|
|
|
* Check the flags to see whether we're streaming or coherent.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sbus_dmamem_map(t, segs, nsegs, size, kvap, flags)
|
|
|
|
bus_dma_tag_t t;
|
|
|
|
bus_dma_segment_t *segs;
|
|
|
|
int nsegs;
|
|
|
|
size_t size;
|
|
|
|
caddr_t *kvap;
|
|
|
|
int flags;
|
|
|
|
{
|
|
|
|
vm_page_t m;
|
1998-08-13 06:10:37 +04:00
|
|
|
vaddr_t va;
|
1998-07-07 07:05:02 +04:00
|
|
|
bus_addr_t addr;
|
|
|
|
struct pglist *mlist;
|
1998-08-13 06:10:37 +04:00
|
|
|
int cbit;
|
1998-07-07 07:05:02 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* digest flags:
|
|
|
|
*/
|
|
|
|
cbit = 0;
|
|
|
|
if (flags & BUS_DMA_COHERENT) /* Disable vcache */
|
|
|
|
cbit |= PMAP_NVC;
|
1998-08-13 06:10:37 +04:00
|
|
|
if (flags & BUS_DMA_NOCACHE) /* sideffects */
|
1998-07-07 07:05:02 +04:00
|
|
|
cbit |= PMAP_NC;
|
|
|
|
/*
|
1998-09-06 03:57:24 +04:00
|
|
|
* Now take this and map it into the CPU since it should already
|
|
|
|
* be in the the IOMMU.
|
1998-07-07 07:05:02 +04:00
|
|
|
*/
|
1998-09-06 03:57:24 +04:00
|
|
|
*kvap = (caddr_t)va = segs[0].ds_addr;
|
1998-07-07 07:05:02 +04:00
|
|
|
mlist = segs[0]._ds_mlist;
|
|
|
|
for (m = mlist->tqh_first; m != NULL; m = m->pageq.tqe_next) {
|
|
|
|
|
|
|
|
if (size == 0)
|
|
|
|
panic("_bus_dmamem_map: size botch");
|
|
|
|
|
|
|
|
addr = VM_PAGE_TO_PHYS(m);
|
|
|
|
pmap_enter(pmap_kernel(), va, addr | cbit,
|
bus_dmamem_map() maps DMA safe memory, which is usually one or more
managed pages, into KVA space. Since the pages are managed, we should
use pmap_enter(), not pmap_kenter_pa().
Also, when entering the mappings, enter with an access_type of
VM_PROT_READ | VM_PROT_WRITE. We do this for a couple of reasons:
(1) On systems that have H/W mod/ref attributes, the hardware
may not be able to track mod/ref done by a bus master.
(2) On systems that have to do mod/ref emulation, this prevents
a mod/ref page fault from potentially happening while in an
interrupt context, which can be problematic.
This latter change is fairly important if we ever want to be able to
transfer DMA-safe memory pages to anonymous memory objects; we will need
to know that the pages are modified, or else data could be lost!
Note that while the pages are unowned (i.e. "just DMA-safe memory pages"),
they won't consume any swap resources, as the mappings are wired, and
the pages aren't on the active or inactive queues.
1999-05-26 03:14:03 +04:00
|
|
|
VM_PROT_READ | VM_PROT_WRITE, TRUE,
|
|
|
|
VM_PROT_READ | VM_PROT_WRITE);
|
1998-07-07 07:05:02 +04:00
|
|
|
va += PAGE_SIZE;
|
|
|
|
size -= PAGE_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1998-09-06 03:57:24 +04:00
|
|
|
* Unmap DVMA mappings from kernel
|
1998-07-07 07:05:02 +04:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
sbus_dmamem_unmap(t, kva, size)
|
|
|
|
bus_dma_tag_t t;
|
|
|
|
caddr_t kva;
|
|
|
|
size_t size;
|
|
|
|
{
|
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if ((u_long)kva & PGOFSET)
|
|
|
|
panic("_bus_dmamem_unmap");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
size = round_page(size);
|
1998-09-06 03:57:24 +04:00
|
|
|
pmap_remove(pmap_kernel(), (vaddr_t)kva, size);
|
1998-07-07 07:05:02 +04:00
|
|
|
}
|