svn commit: r222128 - in projects/pseries: conf powerpc/conf
powerpc/include powerpc/ofw powerpc/pseries
Nathan Whitehorn
nwhitehorn at FreeBSD.org
Fri May 20 14:22:11 UTC 2011
Author: nwhitehorn
Date: Fri May 20 14:22:10 2011
New Revision: 222128
URL: http://svn.freebsd.org/changeset/base/222128
Log:
Add an RTAS (Runtime Abstraction Services) interface layer and an
RTAS-based PCI bus driver. RTAS is tested and working on both 32 and 64-bit
systems, but the RTAS PCI driver has been tested only on a 32-bit system
(Powermac G3) due to lack of RTAS-enabled 64-bit hardware on my end.
Added:
projects/pseries/powerpc/include/rtas.h
projects/pseries/powerpc/ofw/rtas.c
projects/pseries/powerpc/pseries/rtas_pci.c
Modified:
projects/pseries/conf/files.powerpc
projects/pseries/powerpc/conf/GENERIC
projects/pseries/powerpc/ofw/ofw_machdep.c
projects/pseries/powerpc/ofw/ofwcall32.S
projects/pseries/powerpc/ofw/ofwcall64.S
Modified: projects/pseries/conf/files.powerpc
==============================================================================
--- projects/pseries/conf/files.powerpc Fri May 20 14:07:08 2011 (r222127)
+++ projects/pseries/conf/files.powerpc Fri May 20 14:22:10 2011 (r222128)
@@ -138,6 +138,7 @@ powerpc/ofw/ofw_syscons.c optional sc ai
powerpc/ofw/ofwcall32.S optional aim powerpc
powerpc/ofw/ofwcall64.S optional aim powerpc64
powerpc/ofw/ofwmagic.S optional aim
+powerpc/ofw/rtas.c optional aim
powerpc/powermac/ata_kauai.c optional powermac ata | powermac atamacio
powerpc/powermac/ata_macio.c optional powermac ata | powermac atamacio
powerpc/powermac/ata_dbdma.c optional powermac ata | powermac atamacio
@@ -210,6 +211,7 @@ powerpc/ps3/ps3-hvcall.S optional ps3 sc
powerpc/pseries/phyp-hvcall.S optional pseries powerpc64
powerpc/pseries/mmu_phyp.c optional pseries powerpc64
powerpc/pseries/platform_chrp.c optional pseries
+powerpc/pseries/rtas_pci.c optional pseries
powerpc/psim/iobus.c optional psim
powerpc/psim/ata_iobus.c optional ata psim
powerpc/psim/openpic_iobus.c optional psim
Modified: projects/pseries/powerpc/conf/GENERIC
==============================================================================
--- projects/pseries/powerpc/conf/GENERIC Fri May 20 14:07:08 2011 (r222127)
+++ projects/pseries/powerpc/conf/GENERIC Fri May 20 14:22:10 2011 (r222128)
@@ -29,6 +29,7 @@ makeoptions DEBUG=-g #Build kernel with
options POWERMAC #NewWorld Apple PowerMacs
options PSIM #GDB PSIM ppc simulator
options MAMBO #IBM Mambo Full System Simulator
+options PSERIES #PAPR-compliant systems
options SCHED_ULE #ULE scheduler
options INET #InterNETworking
Added: projects/pseries/powerpc/include/rtas.h
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ projects/pseries/powerpc/include/rtas.h Fri May 20 14:22:10 2011 (r222128)
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 2011 Nathan Whitehorn
+ * All rights reserved.
+ *
+ * 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 AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef _MACHINE_RTAS_H_
+#define _MACHINE_RTAS_H_
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <dev/ofw/openfirm.h>
+
+/*
+ * RTAS functions are defined by 32-bit integer tokens. These vary from
+ * system to system, and can be looked up from their standardized names
+ * using rtas_token_lookup(). If RTAS is not available, rtas_token_lookup()
+ * and rtas_call_method() return -1; this can be checked in advance using
+ * rtas_exists(). Otherwise, rtas_call_method() returns one of the RTAS
+ * status codes from the bottom of this file.
+ */
+
+int rtas_exists(void);
+int rtas_call_method(cell_t token, int nargs, int nreturns, ...);
+cell_t rtas_token_lookup(const char *method);
+
+/* RTAS Status Codes: see CHRP or PAPR specification */
+#define RTAS_OK 0
+#define RTAS_HW_ERROR -1
+#define RTAS_BUSY -2
+#define RTAS_PARAM_ERROR -3
+#define RTAS_STATE_CHANGE -7
+#define RTAS_VENDOR_BEGIN 9000
+#define RTAS_EXTENDED_DELAY 9900
+#define RTAS_ISOLATION_ERROR -9000
+#define RTAS_VENDOR_ERROR_BEGIN -9004
+
+#endif /* _MACHINE_RTAS_H_ */
+
Modified: projects/pseries/powerpc/ofw/ofw_machdep.c
==============================================================================
--- projects/pseries/powerpc/ofw/ofw_machdep.c Fri May 20 14:07:08 2011 (r222127)
+++ projects/pseries/powerpc/ofw/ofw_machdep.c Fri May 20 14:22:10 2011 (r222128)
@@ -70,7 +70,6 @@ static void *fdt;
int ofw_real_mode;
int ofwcall(void *);
-int rtascall(void *);
static void ofw_quiesce(void);
static int openfirmware(void *args);
Modified: projects/pseries/powerpc/ofw/ofwcall32.S
==============================================================================
--- projects/pseries/powerpc/ofw/ofwcall32.S Fri May 20 14:07:08 2011 (r222127)
+++ projects/pseries/powerpc/ofw/ofwcall32.S Fri May 20 14:22:10 2011 (r222128)
@@ -46,8 +46,10 @@ GLOBAL(rtas_entry)
.long 0 /* RTAS entry point */
.align 4
-GLOBAL(ofwstk)
- .space OFWSTKSZ
+ofwstk:
+ .space OFWSTKSZ
+rtas_regsave:
+ .space 4
/*
* Open Firmware Entry Point. May need to enter real mode.
@@ -118,9 +120,10 @@ ASENTRY(rtascall)
mflr %r0
stw %r0,4(%r1)
- /* Record the old MSR just below the bottom of the stack */
+ /* Record the old MSR to real-mode-accessible area */
mfmsr %r0
- stw %r0,-4(%r1)
+ lis %r5,rtas_regsave at ha
+ stw %r0,rtas_regsave at l(%r5)
/* read client interface handler */
lis %r5,rtas_entry at ha
@@ -137,7 +140,8 @@ ASENTRY(rtascall)
bctrl
/* Now set the MSR back */
- lwz %r6,-4(%r1)
+ lis %r6,rtas_regsave at ha
+ lwz %r6,rtas_regsave at l(%r6)
mtmsr %r6
isync
Modified: projects/pseries/powerpc/ofw/ofwcall64.S
==============================================================================
--- projects/pseries/powerpc/ofw/ofwcall64.S Fri May 20 14:07:08 2011 (r222127)
+++ projects/pseries/powerpc/ofw/ofwcall64.S Fri May 20 14:22:10 2011 (r222128)
@@ -37,9 +37,9 @@
*/
.data
.align 4
-GLOBAL(ofwstk)
+ofwstk:
.space OFWSTKSZ
-GLOBAL(rtas_regsave)
+rtas_regsave:
.space 24 /* 3 * sizeof(register_t) */
GLOBAL(ofmsr)
.llong 0, 0, 0, 0, 0 /* msr/sprg0-3 used in Open Firmware */
@@ -170,7 +170,7 @@ ASENTRY(ofwcall)
* RTAS 32-bit Entry Point. Similar to the OF one, but simpler (no separate
* stack)
*
- * C prototype: int rtas_32bit_entry(void *callbuffer, void *rtas_privdat);
+ * C prototype: int rtascall(void *callbuffer, void *rtas_privdat);
*/
ASENTRY(rtascall)
Added: projects/pseries/powerpc/ofw/rtas.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ projects/pseries/powerpc/ofw/rtas.c Fri May 20 14:22:10 2011 (r222128)
@@ -0,0 +1,234 @@
+/*-
+ * Copyright (c) 2011 Nathan Whitehorn
+ * All rights reserved.
+ *
+ * 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 AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: projects/pseries/powerpc/ofw/ofw_real.c 222059 2011-05-18 15:07:36Z nwhitehorn $");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/systm.h>
+
+#include <vm/vm.h>
+#include <vm/vm_page.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/md_var.h>
+#include <machine/pmap.h>
+#include <machine/rtas.h>
+#include <machine/stdarg.h>
+
+#include <dev/ofw/openfirm.h>
+
+MALLOC_DEFINE(M_RTAS, "rtas", "Run Time Abstraction Service");
+
+static vm_offset_t rtas_bounce_phys;
+static caddr_t rtas_bounce_virt;
+static off_t rtas_bounce_offset;
+static size_t rtas_bounce_size;
+static uintptr_t rtas_private_data;
+static struct mtx rtas_mtx;
+static phandle_t rtas;
+
+/* From ofwcall.S */
+int rtascall(vm_offset_t callbuffer, uintptr_t rtas_privdat);
+extern uintptr_t rtas_entry;
+extern register_t rtasmsr;
+
+/*
+ * After the VM is up, allocate RTAS memory and instantiate it
+ */
+
+static void rtas_setup(void *);
+
+SYSINIT(rtas_setup, SI_SUB_KMEM, SI_ORDER_ANY, rtas_setup, NULL);
+
+static void
+rtas_setup(void *junk)
+{
+ ihandle_t rtasi;
+ cell_t rtas_size = 0, rtas_ptr;
+ int result;
+
+ rtasi = OF_open("/rtas");
+ rtas = OF_finddevice("/rtas");
+ if (rtasi == 0 || rtas == -1)
+ return;
+
+ mtx_init(&rtas_mtx, "RTAS", MTX_DEF, 0);
+
+ /* RTAS must be called with everything turned off in MSR */
+ rtasmsr = mfmsr();
+ rtasmsr &= ~(PSL_IR | PSL_DR | PSL_EE | PSL_SE);
+ #ifdef __powerpc64__
+ rtasmsr &= ~PSL_SF;
+ #endif
+
+ /*
+ * Allocate rtas_size + one page of contiguous, wired physical memory
+ * that can fit into a 32-bit address space and accessed from real mode.
+ * This is used both to bounce arguments and for RTAS private data.
+ *
+ * It must be 4KB-aligned and not cross a 256 MB boundary.
+ */
+
+ OF_getprop(rtas, "rtas-size", &rtas_size, sizeof(rtas_size));
+ rtas_size = round_page(rtas_size);
+ rtas_bounce_virt = contigmalloc(rtas_size + PAGE_SIZE, M_RTAS, 0, 0,
+ ulmin(platform_real_maxaddr(), BUS_SPACE_MAXADDR_32BIT),
+ 4096, 256*1024*1024);
+
+ rtas_private_data = vtophys(rtas_bounce_virt);
+ rtas_bounce_virt += rtas_size; /* Actual bounce area */
+ rtas_bounce_phys = vtophys(rtas_bounce_virt);
+ rtas_bounce_size = PAGE_SIZE;
+
+ /*
+ * Instantiate RTAS. We always use the 32-bit version.
+ */
+
+ result = OF_call_method("instantiate-rtas", rtasi, 1, 1,
+ (cell_t)rtas_private_data, &rtas_ptr);
+ OF_close(rtasi);
+
+ if (result == -1) {
+ rtas = 0;
+ rtas_ptr = 0;
+ printf("Error initializing RTAS\n");
+ return;
+ }
+
+ rtas_entry = (uintptr_t)(rtas_ptr);
+}
+
+static cell_t
+rtas_real_map(const void *buf, size_t len)
+{
+ cell_t phys;
+
+ mtx_assert(&rtas_mtx, MA_OWNED);
+
+ /*
+ * Make sure the bounce page offset satisfies any reasonable
+ * alignment constraint.
+ */
+ rtas_bounce_offset += sizeof(register_t) -
+ (rtas_bounce_offset % sizeof(register_t));
+
+ if (rtas_bounce_offset + len > rtas_bounce_size) {
+ panic("Oversize RTAS call!");
+ return 0;
+ }
+
+ if (buf != NULL)
+ memcpy(rtas_bounce_virt + rtas_bounce_offset, buf, len);
+ else
+ return (0);
+
+ phys = rtas_bounce_phys + rtas_bounce_offset;
+ rtas_bounce_offset += len;
+
+ return (phys);
+}
+
+static void
+rtas_real_unmap(cell_t physaddr, void *buf, size_t len)
+{
+ mtx_assert(&rtas_mtx, MA_OWNED);
+
+ if (physaddr == 0)
+ return;
+
+ memcpy(buf, rtas_bounce_virt + (physaddr - rtas_bounce_phys), len);
+}
+
+/* Check if we have RTAS */
+int
+rtas_exists(void)
+{
+ return (rtas != 0);
+}
+
+/* Call an RTAS method by token */
+int
+rtas_call_method(cell_t token, int nargs, int nreturns, ...)
+{
+ vm_offset_t argsptr;
+ va_list ap;
+ struct {
+ cell_t token;
+ cell_t nargs;
+ cell_t nreturns;
+ cell_t args_n_results[12];
+ } args;
+ int n, result;
+
+ if (!rtas_exists() || nargs > 6)
+ return (-1);
+
+ args.token = token;
+ va_start(ap, nreturns);
+
+ mtx_lock(&rtas_mtx);
+ rtas_bounce_offset = 0;
+
+ args.nargs = nargs;
+ args.nreturns = nreturns;
+
+ for (n = 0; n < nargs; n++)
+ args.args_n_results[n] = va_arg(ap, cell_t);
+
+ argsptr = rtas_real_map(&args, sizeof(args));
+ result = rtascall(argsptr, rtas_private_data);
+ rtas_real_unmap(argsptr, &args, sizeof(args));
+ mtx_unlock(&rtas_mtx);
+
+ if (result < 0)
+ return (result);
+
+ for (n = nargs; n < nargs + nreturns; n++)
+ *va_arg(ap, cell_t *) = args.args_n_results[n];
+ return (result);
+}
+
+/* Look up an RTAS token */
+cell_t
+rtas_token_lookup(const char *method)
+{
+ cell_t token;
+
+ if (!rtas_exists())
+ return (-1);
+
+ if (OF_getprop(rtas, method, &token, sizeof(token)) == -1)
+ return (-1);
+
+ return (token);
+}
+
+
Added: projects/pseries/powerpc/pseries/rtas_pci.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ projects/pseries/powerpc/pseries/rtas_pci.c Fri May 20 14:22:10 2011 (r222128)
@@ -0,0 +1,579 @@
+/*-
+ * Copyright (c) 2011 Nathan Whitehorn
+ * All rights reserved.
+ *
+ * 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 AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: projects/pseries/powerpc/ofw/ofw_real.c 222059 2011-05-18 15:07:36Z nwhitehorn $");
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_pci.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+
+#include <machine/bus.h>
+#include <machine/intr_machdep.h>
+#include <machine/md_var.h>
+#include <machine/pio.h>
+#include <machine/resource.h>
+#include <machine/rtas.h>
+
+#include <sys/rman.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include "pcib_if.h"
+
+/*
+ * Device interface.
+ */
+static int rtaspci_probe(device_t);
+static int rtaspci_attach(device_t);
+
+/*
+ * Bus interface.
+ */
+static int rtaspci_read_ivar(device_t, device_t, int,
+ uintptr_t *);
+static struct resource * rtaspci_alloc_resource(device_t bus,
+ device_t child, int type, int *rid, u_long start,
+ u_long end, u_long count, u_int flags);
+static int rtaspci_release_resource(device_t bus, device_t child,
+ int type, int rid, struct resource *res);
+static int rtaspci_activate_resource(device_t bus, device_t child,
+ int type, int rid, struct resource *res);
+static int rtaspci_deactivate_resource(device_t bus,
+ device_t child, int type, int rid,
+ struct resource *res);
+
+
+/*
+ * pcib interface.
+ */
+static int rtaspci_maxslots(device_t);
+static u_int32_t rtaspci_read_config(device_t, u_int, u_int, u_int,
+ u_int, int);
+static void rtaspci_write_config(device_t, u_int, u_int, u_int,
+ u_int, u_int32_t, int);
+static int rtaspci_route_interrupt(device_t, device_t, int);
+
+/*
+ * ofw_bus interface
+ */
+static phandle_t rtaspci_get_node(device_t bus, device_t dev);
+
+/*
+ * local methods
+ */
+
+struct ofw_pci_range {
+ uint32_t pci_hi;
+ uint32_t pci_mid;
+ uint32_t pci_lo;
+ uint64_t host;
+ uint32_t size_hi;
+ uint32_t size_lo;
+};
+
+static int rtaspci_fill_ranges(phandle_t node, struct ofw_pci_range **ranges,
+ int *nranges);
+
+/*
+ * Driver methods.
+ */
+static device_method_t rtaspci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, rtaspci_probe),
+ DEVMETHOD(device_attach, rtaspci_attach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_read_ivar, rtaspci_read_ivar),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+ DEVMETHOD(bus_alloc_resource, rtaspci_alloc_resource),
+ DEVMETHOD(bus_release_resource, rtaspci_release_resource),
+ DEVMETHOD(bus_activate_resource, rtaspci_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, rtaspci_deactivate_resource),
+
+ /* pcib interface */
+ DEVMETHOD(pcib_maxslots, rtaspci_maxslots),
+ DEVMETHOD(pcib_read_config, rtaspci_read_config),
+ DEVMETHOD(pcib_write_config, rtaspci_write_config),
+ DEVMETHOD(pcib_route_interrupt, rtaspci_route_interrupt),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, rtaspci_get_node),
+
+ { 0, 0 }
+};
+
+struct rtaspci_softc {
+ device_t sc_dev;
+ phandle_t sc_node;
+ int sc_bus;
+
+ cell_t read_pci_config, write_pci_config;
+ cell_t ex_read_pci_config, ex_write_pci_config;
+ int sc_extended_config;
+ struct ofw_pci_register pcir;
+
+ struct ofw_pci_range *sc_range;
+ int sc_nrange;
+
+ struct rman sc_io_rman;
+ struct rman sc_mem_rman;
+ bus_space_tag_t sc_memt;
+ bus_dma_tag_t sc_dmat;
+ vm_offset_t sc_iostart;
+
+ struct ofw_bus_iinfo sc_pci_iinfo;
+};
+
+static driver_t rtaspci_driver = {
+ "pcib",
+ rtaspci_methods,
+ sizeof(struct rtaspci_softc)
+};
+
+static devclass_t rtaspci_devclass;
+
+DRIVER_MODULE(rtaspci, nexus, rtaspci_driver, rtaspci_devclass, 0, 0);
+
+static int
+rtaspci_probe(device_t dev)
+{
+ const char *type;
+
+ if (!rtas_exists())
+ return (ENXIO);
+
+ type = ofw_bus_get_type(dev);
+
+ if (OF_getproplen(ofw_bus_get_node(dev), "used-by-rtas") < 0)
+ return (ENXIO);
+ if (type == NULL || strcmp(type, "pci") != 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "RTAS Host-PCI bridge");
+ return (BUS_PROBE_GENERIC);
+}
+
+static int
+rtaspci_attach(device_t dev)
+{
+ struct rtaspci_softc *sc;
+ phandle_t node;
+ u_int32_t busrange[2];
+ struct ofw_pci_range *rp, *io, *mem[5];
+ int nmem, i, error;
+
+ node = ofw_bus_get_node(dev);
+ sc = device_get_softc(dev);
+
+ sc->read_pci_config = rtas_token_lookup("read-pci-config");
+ sc->write_pci_config = rtas_token_lookup("write-pci-config");
+ sc->ex_read_pci_config = rtas_token_lookup("ibm,read-pci-config");
+ sc->ex_write_pci_config = rtas_token_lookup("ibm,write-pci-config");
+ if (OF_getprop(node, "reg", &sc->pcir, sizeof(sc->pcir)) == -1)
+ return (ENXIO);
+
+ sc->sc_extended_config = 0;
+ OF_getprop(node, "ibm,pci-config-space-type", &sc->sc_extended_config,
+ sizeof(sc->sc_extended_config));
+
+ if (OF_getprop(node, "bus-range", busrange, sizeof(busrange)) != 8)
+ return (ENXIO);
+
+ sc->sc_dev = dev;
+ sc->sc_node = node;
+ sc->sc_bus = busrange[0];
+
+ if (rtaspci_fill_ranges(node, &sc->sc_range, &sc->sc_nrange) < 0) {
+ device_printf(dev, "could not get ranges\n");
+ return (ENXIO);
+ }
+
+ io = NULL;
+ nmem = 0;
+
+ for (rp = sc->sc_range; rp < sc->sc_range + sc->sc_nrange &&
+ rp->pci_hi != 0; rp++) {
+ switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) {
+ case OFW_PCI_PHYS_HI_SPACE_CONFIG:
+ break;
+ case OFW_PCI_PHYS_HI_SPACE_IO:
+ io = rp;
+ break;
+ case OFW_PCI_PHYS_HI_SPACE_MEM32:
+ mem[nmem] = rp;
+ nmem++;
+ break;
+ case OFW_PCI_PHYS_HI_SPACE_MEM64:
+ break;
+ }
+ }
+
+ if (io == NULL) {
+ device_printf(dev, "can't find io range\n");
+ return (ENXIO);
+ }
+ sc->sc_io_rman.rm_type = RMAN_ARRAY;
+ sc->sc_io_rman.rm_descr = "PCI I/O Ports";
+ sc->sc_iostart = io->host;
+ if (rman_init(&sc->sc_io_rman) != 0 ||
+ rman_manage_region(&sc->sc_io_rman, io->pci_lo,
+ io->pci_lo + io->size_lo) != 0) {
+ panic("rtaspci_attach: failed to set up I/O rman");
+ }
+
+ if (nmem == 0) {
+ device_printf(dev, "can't find mem ranges\n");
+ return (ENXIO);
+ }
+ sc->sc_mem_rman.rm_type = RMAN_ARRAY;
+ sc->sc_mem_rman.rm_descr = "PCI Memory";
+ error = rman_init(&sc->sc_mem_rman);
+ if (error) {
+ device_printf(dev, "rman_init() failed. error = %d\n", error);
+ return (error);
+ }
+ for (i = 0; i < nmem; i++) {
+ error = rman_manage_region(&sc->sc_mem_rman, mem[i]->pci_lo,
+ mem[i]->pci_lo + mem[i]->size_lo);
+ if (error) {
+ device_printf(dev,
+ "rman_manage_region() failed. error = %d\n", error);
+ return (error);
+ }
+ }
+
+ ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(cell_t));
+
+ device_add_child(dev, "pci", device_get_unit(dev));
+ return (bus_generic_attach(dev));
+}
+
+static int
+rtaspci_maxslots(device_t dev)
+{
+
+ return (PCI_SLOTMAX);
+}
+
+static uint32_t
+rtaspci_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg,
+ int width)
+{
+ struct rtaspci_softc *sc;
+ uint32_t retval = 0xffffffff;
+ uint32_t config_addr;
+ int error, pcierror;
+
+ sc = device_get_softc(dev);
+
+ config_addr = ((bus & 0xff) << 16) | ((slot & 0x1f) << 11) |
+ ((func & 0x7) << 8) | (reg & 0xff);
+ if (sc->sc_extended_config)
+ config_addr |= (reg & 0xf00) << 16;
+
+ if (sc->ex_read_pci_config != -1)
+ error = rtas_call_method(sc->ex_read_pci_config, 4, 2,
+ config_addr, sc->pcir.phys_hi, sc->pcir.phys_mid,
+ width, &pcierror, &retval);
+ else
+ error = rtas_call_method(sc->read_pci_config, 2, 2,
+ config_addr, width, &pcierror, &retval);
+
+ /* Sign-extend output */
+ switch (width) {
+ case 1:
+ retval = (int32_t)(int8_t)(retval);
+ break;
+ case 2:
+ retval = (int32_t)(int16_t)(retval);
+ break;
+ }
+
+ if (error < 0 || pcierror != 0)
+ retval = 0xffffffff;
+
+ return (retval);
+}
+
+static void
+rtaspci_write_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, uint32_t val, int width)
+{
+ struct rtaspci_softc *sc;
+ uint32_t config_addr;
+ int pcierror;
+
+ sc = device_get_softc(dev);
+
+ config_addr = ((bus & 0xff) << 16) | ((slot & 0x1f) << 11) |
+ ((func & 0x7) << 8) | (reg & 0xff);
+ if (sc->sc_extended_config)
+ config_addr |= (reg & 0xf00) << 16;
+
+ if (sc->ex_write_pci_config != -1)
+ rtas_call_method(sc->ex_write_pci_config, 5, 1, config_addr,
+ sc->pcir.phys_hi, sc->pcir.phys_mid, width, val,
+ &pcierror);
+ else
+ rtas_call_method(sc->write_pci_config, 3, 1, config_addr,
+ width, val, &pcierror);
+}
+
+static int
+rtaspci_route_interrupt(device_t bus, device_t dev, int pin)
+{
+ struct rtaspci_softc *sc;
+ struct ofw_pci_register reg;
+ uint32_t pintr, mintr;
+ phandle_t iparent;
+ uint8_t maskbuf[sizeof(reg) + sizeof(pintr)];
+
+ sc = device_get_softc(bus);
+ pintr = pin;
+ if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo, ®,
+ sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr),
+ &iparent, maskbuf))
+ return (MAP_IRQ(iparent, mintr));
+
+ /* Maybe it's a real interrupt, not an intpin */
+ if (pin > 4)
+ return (pin);
+
+ device_printf(bus, "could not route pin %d for device %d.%d\n",
+ pin, pci_get_slot(dev), pci_get_function(dev));
+ return (PCI_INVALID_IRQ);
+}
+
+static int
+rtaspci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
+{
+ struct rtaspci_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ switch (which) {
+ case PCIB_IVAR_DOMAIN:
+ *result = 0;
+ return (0);
+ case PCIB_IVAR_BUS:
+ *result = sc->sc_bus;
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+static struct resource *
+rtaspci_alloc_resource(device_t bus, device_t child, int type, int *rid,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+ struct rtaspci_softc *sc;
+ struct resource *rv;
+ struct rman *rm;
+ int needactivate;
+
+ needactivate = flags & RF_ACTIVE;
+ flags &= ~RF_ACTIVE;
+
+ sc = device_get_softc(bus);
+
+ switch (type) {
+ case SYS_RES_MEMORY:
+ rm = &sc->sc_mem_rman;
+ break;
+
+ case SYS_RES_IOPORT:
+ rm = &sc->sc_io_rman;
+ break;
+
+ case SYS_RES_IRQ:
+ return (bus_alloc_resource(bus, type, rid, start, end, count,
+ flags));
+
+ default:
+ device_printf(bus, "unknown resource request from %s\n",
+ device_get_nameunit(child));
+ return (NULL);
+ }
+
+ rv = rman_reserve_resource(rm, start, end, count, flags, child);
+ if (rv == NULL) {
+ device_printf(bus, "failed to reserve resource for %s\n",
+ device_get_nameunit(child));
+ return (NULL);
+ }
+
+ rman_set_rid(rv, *rid);
+
+ if (needactivate) {
+ if (bus_activate_resource(child, type, *rid, rv) != 0) {
+ device_printf(bus,
+ "failed to activate resource for %s\n",
+ device_get_nameunit(child));
+ rman_release_resource(rv);
+ return (NULL);
+ }
+ }
+
+ return (rv);
+}
+
+static int
+rtaspci_release_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *res)
+{
+ if (rman_get_flags(res) & RF_ACTIVE) {
+ int error = bus_deactivate_resource(child, type, rid, res);
+ if (error)
+ return error;
+ }
+
+ return (rman_release_resource(res));
+}
+
+static int
+rtaspci_activate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *res)
+{
+ struct rtaspci_softc *sc;
+ void *p;
+
+ sc = device_get_softc(bus);
+
+ if (type == SYS_RES_IRQ) {
+ return (bus_activate_resource(bus, type, rid, res));
+ }
+ if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) {
+ vm_offset_t start;
+
+ start = (vm_offset_t)rman_get_start(res);
+
+ /*
+ * Some bridges have I/O ports relative to the start of I/O
+ * space, so adjust if we are under that.
+ */
+ if (type == SYS_RES_IOPORT && start < sc->sc_iostart)
+ start += sc->sc_iostart;
+
+ if (bootverbose)
+ printf("rtaspci mapdev: start %zx, len %ld\n", start,
+ rman_get_size(res));
+
+ p = pmap_mapdev(start, (vm_size_t)rman_get_size(res));
+ if (p == NULL)
+ return (ENOMEM);
+
+ rman_set_virtual(res, p);
+ rman_set_bustag(res, &bs_le_tag);
+ rman_set_bushandle(res, (u_long)p);
+ }
+
+ return (rman_activate_resource(res));
+}
+
+static int
+rtaspci_deactivate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *res)
+{
+ /*
+ * If this is a memory resource, unmap it.
+ */
+ if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) {
+ u_int32_t psize;
+
+ psize = rman_get_size(res);
+ pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize);
+ }
+
+ return (rman_deactivate_resource(res));
+}
+
+static phandle_t
+rtaspci_get_node(device_t bus, device_t dev)
+{
+ struct rtaspci_softc *sc;
+
+ sc = device_get_softc(bus);
+ /* We only have one child, the PCI bus, which needs our own node. */
+
+ return (sc->sc_node);
+}
+
+static int
+rtaspci_fill_ranges(phandle_t node, struct ofw_pci_range **ranges,
+ int *nranges)
+{
+ int address_cells = 1;
+ cell_t *base_ranges;
+ ssize_t nbase_ranges;
+ int i, j;
+
+ OF_getprop(OF_parent(node), "#address-cells", &address_cells,
+ sizeof(address_cells));
+ if (address_cells > 2)
+ panic("RTAS PCI: Addresses too wide (%d)", address_cells);
+
+ nbase_ranges = OF_getproplen(node, "ranges");
+ if (nbase_ranges <= 0)
+ return (-1);
+
+ base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
+ OF_getprop(node, "ranges", base_ranges, nbase_ranges);
+ *nranges = nbase_ranges / sizeof(cell_t) / (5 + address_cells);
+ *ranges = malloc(*nranges * sizeof(struct ofw_pci_range), M_DEVBUF,
+ M_WAITOK);
+
+ for (i = 0, j = 0; i < *nranges; i++) {
+ (*ranges)[i].pci_hi = base_ranges[j++];
+ (*ranges)[i].pci_mid = base_ranges[j++];
+ (*ranges)[i].pci_lo = base_ranges[j++];
+ (*ranges)[i].host = base_ranges[j++];
+ if (address_cells == 2) {
+ (*ranges)[i].host <<= 32;
+ (*ranges)[i].host |= base_ranges[j++];
+ }
+ (*ranges)[i].size_hi = base_ranges[j++];
+ (*ranges)[i].size_lo = base_ranges[j++];
+ }
+
+ free(base_ranges, M_DEVBUF);
+ return (0);
+}
+
More information about the svn-src-projects
mailing list