aboutsummaryrefslogtreecommitdiff
path: root/sys/powerpc/pseries/rtas_pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/powerpc/pseries/rtas_pci.c')
-rw-r--r--sys/powerpc/pseries/rtas_pci.c208
1 files changed, 208 insertions, 0 deletions
diff --git a/sys/powerpc/pseries/rtas_pci.c b/sys/powerpc/pseries/rtas_pci.c
new file mode 100644
index 000000000000..02d1a559e7dd
--- /dev/null
+++ b/sys/powerpc/pseries/rtas_pci.c
@@ -0,0 +1,208 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/rman.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/ofw/ofwpci.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 <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <powerpc/pseries/plpar_iommu.h>
+
+#include "pcib_if.h"
+#include "iommu_if.h"
+
+/*
+ * Device interface.
+ */
+static int rtaspci_probe(device_t);
+static int rtaspci_attach(device_t);
+
+/*
+ * pcib interface.
+ */
+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);
+
+/*
+ * Driver methods.
+ */
+static device_method_t rtaspci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, rtaspci_probe),
+ DEVMETHOD(device_attach, rtaspci_attach),
+
+ /* pcib interface */
+ DEVMETHOD(pcib_read_config, rtaspci_read_config),
+ DEVMETHOD(pcib_write_config, rtaspci_write_config),
+
+ DEVMETHOD_END
+};
+
+struct rtaspci_softc {
+ struct ofw_pci_softc pci_sc;
+
+ struct ofw_pci_register sc_pcir;
+
+ cell_t read_pci_config, write_pci_config;
+ cell_t ex_read_pci_config, ex_write_pci_config;
+ int sc_extended_config;
+};
+
+DEFINE_CLASS_1(pcib, rtaspci_driver, rtaspci_methods,
+ sizeof(struct rtaspci_softc), ofw_pcib_driver);
+DRIVER_MODULE(rtaspci, ofwbus, rtaspci_driver, 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;
+
+ sc = device_get_softc(dev);
+
+ if (OF_getencprop(ofw_bus_get_node(dev), "reg", (pcell_t *)&sc->sc_pcir,
+ sizeof(sc->sc_pcir)) == -1)
+ return (ENXIO);
+
+ 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");
+
+ sc->sc_extended_config = 0;
+ OF_getencprop(ofw_bus_get_node(dev), "ibm,pci-config-space-type",
+ &sc->sc_extended_config, sizeof(sc->sc_extended_config));
+
+ return (ofw_pcib_attach(dev));
+}
+
+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->sc_pcir.phys_hi,
+ sc->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->sc_pcir.phys_hi, sc->sc_pcir.phys_mid,
+ width, val, &pcierror);
+ else
+ rtas_call_method(sc->write_pci_config, 3, 1, config_addr,
+ width, val, &pcierror);
+}