Date: Fri, 20 May 2011 14:22:10 +0000 (UTC) From: Nathan Whitehorn <nwhitehorn@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r222128 - in projects/pseries: conf powerpc/conf powerpc/include powerpc/ofw powerpc/pseries Message-ID: <201105201422.p4KEMAAO087880@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
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@ha + stw %r0,rtas_regsave@l(%r5) /* read client interface handler */ lis %r5,rtas_entry@ha @@ -137,7 +140,8 @@ ASENTRY(rtascall) bctrl /* Now set the MSR back */ - lwz %r6,-4(%r1) + lis %r6,rtas_regsave@ha + lwz %r6,rtas_regsave@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); +} +
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201105201422.p4KEMAAO087880>