summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/arch/powerpc64/dev/phb.c165
1 files changed, 155 insertions, 10 deletions
diff --git a/sys/arch/powerpc64/dev/phb.c b/sys/arch/powerpc64/dev/phb.c
index d56f19c4edf..e9f91920972 100644
--- a/sys/arch/powerpc64/dev/phb.c
+++ b/sys/arch/powerpc64/dev/phb.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: phb.c,v 1.2 2020/06/07 20:13:07 kettenis Exp $ */
+/* $OpenBSD: phb.c,v 1.3 2020/06/08 19:06:47 kettenis Exp $ */
/*
* Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
*
@@ -30,6 +30,10 @@
#include <dev/ofw/openfirm.h>
#include <dev/ofw/fdt.h>
+extern paddr_t physmax; /* machdep.c */
+
+#define IODA_TVE_SELECT (1ULL << 59)
+
struct phb_range {
uint32_t flags;
uint64_t pci_base;
@@ -40,9 +44,9 @@ struct phb_range {
struct phb_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
+ bus_dma_tag_t sc_dmat;
int sc_node;
- uint64_t sc_phb_id;
int sc_acells;
int sc_scells;
int sc_pacells;
@@ -50,8 +54,12 @@ struct phb_softc {
struct phb_range *sc_ranges;
int sc_nranges;
+ uint64_t sc_phb_id;
+ uint64_t sc_pe_number;
+
struct bus_space sc_bus_iot;
struct bus_space sc_bus_memt;
+ struct machine_bus_dma_tag sc_bus_dmat;
struct ppc64_pci_chipset sc_pc;
int sc_bus;
@@ -87,6 +95,8 @@ int phb_bs_iomap(bus_space_tag_t, bus_addr_t, bus_size_t, int,
bus_space_handle_t *);
int phb_bs_memmap(bus_space_tag_t, bus_addr_t, bus_size_t, int,
bus_space_handle_t *);
+int phb_dmamap_load_buffer(bus_dma_tag_t, bus_dmamap_t, void *,
+ bus_size_t, struct proc *, int, paddr_t *, int *, int);
int
phb_match(struct device *parent, void *match, void *aux)
@@ -103,7 +113,11 @@ phb_attach(struct device *parent, struct device *self, void *aux)
struct fdt_attach_args *faa = aux;
struct pcibus_attach_args pba;
uint32_t *ranges;
+ uint32_t m64window[6];
+ uint32_t m64ranges[2];
int i, j, nranges, rangeslen;
+ int32_t window;
+ int64_t error;
if (faa->fa_nreg < 1) {
printf(": no registers\n");
@@ -111,8 +125,47 @@ phb_attach(struct device *parent, struct device *self, void *aux)
}
sc->sc_iot = faa->fa_iot;
+ sc->sc_dmat = faa->fa_dmat;
sc->sc_node = faa->fa_node;
sc->sc_phb_id = OF_getpropint64(sc->sc_node, "ibm,opal-phbid", 0);
+ sc->sc_pe_number = 0;
+
+ /*
+ * Reset the IODA tables. Should clear any gunk left behind
+ * by Linux.
+ */
+ error = opal_pci_reset(sc->sc_phb_id, OPAL_RESET_PCI_IODA_TABLE,
+ OPAL_ASSERT_RESET);
+ if (error != OPAL_SUCCESS) {
+ printf(": can't reset IODA table\n");
+ return;
+ }
+
+ /*
+ * Keep things simple and use a single PE for everything below
+ * this host bridge.
+ */
+ error = opal_pci_set_pe(sc->sc_phb_id, sc->sc_pe_number, 0,
+ OPAL_IGNORE_RID_BUS_NUMBER, OPAL_IGNORE_RID_DEVICE_NUMBER,
+ OPAL_IGNORE_RID_FUNCTION_NUMBER, OPAL_MAP_PE);
+ if (error != OPAL_SUCCESS) {
+ printf(": can't map PHB PE\n");
+ return;
+ }
+
+ /* Enable bypass mode. */
+ error = opal_pci_map_pe_dma_window_real(sc->sc_phb_id,
+ sc->sc_pe_number, (sc->sc_pe_number << 1) | 1,
+ IODA_TVE_SELECT, physmax);
+ if (error != OPAL_SUCCESS) {
+ printf(": can't enable DMA bypass\n");
+ return;
+ }
+
+ /*
+ * Parse address ranges such that we can do the appropriate
+ * address translations.
+ */
sc->sc_acells = OF_getpropint(sc->sc_node, "#address-cells",
faa->fa_acells);
@@ -133,11 +186,15 @@ phb_attach(struct device *parent, struct device *self, void *aux)
OF_getpropintarray(sc->sc_node, "ranges", ranges,
rangeslen);
+ /*
+ * Reserve an extra slot here and make sure it is filled
+ * with zeroes.
+ */
nranges = (rangeslen / sizeof(uint32_t)) /
(sc->sc_acells + sc->sc_pacells + sc->sc_scells);
- sc->sc_ranges = mallocarray(nranges,
- sizeof(struct phb_range), M_TEMP, M_WAITOK);
- sc->sc_nranges = nranges;
+ sc->sc_ranges = mallocarray(nranges + 1,
+ sizeof(struct phb_range), M_DEVBUF, M_ZERO | M_WAITOK);
+ sc->sc_nranges = nranges + 1;
for (i = 0, j = 0; i < sc->sc_nranges; i++) {
sc->sc_ranges[i].flags = ranges[j++];
@@ -160,6 +217,57 @@ phb_attach(struct device *parent, struct device *self, void *aux)
free(ranges, M_TEMP, rangeslen);
+ /*
+ * IBM has chosen a non-standard way to encode 64-bit mmio
+ * ranges. Stick the information into the slot we reserved
+ * above.
+ */
+ if (OF_getpropintarray(sc->sc_node, "ibm,opal-m64-window",
+ m64window, sizeof(m64window)) == sizeof(m64window)) {
+ sc->sc_ranges[sc->sc_nranges - 1].flags = 0x03000000;
+ sc->sc_ranges[sc->sc_nranges - 1].pci_base =
+ (uint64_t)m64window[0] << 32 | m64window[1];
+ sc->sc_ranges[sc->sc_nranges - 1].phys_base =
+ (uint64_t)m64window[2] << 32 | m64window[3];
+ sc->sc_ranges[sc->sc_nranges - 1].size =
+ (uint64_t)m64window[4] << 32 | m64window[5];
+ }
+
+ /*
+ * Enable all the 64-bit mmio windows we found.
+ */
+ m64ranges[0] = 1; m64ranges[1] = 0;
+ OF_getpropintarray(sc->sc_node, "ibm,opal-available-m64-ranges",
+ m64ranges, sizeof(m64ranges));
+ window = m64ranges[0];
+ for (i = 0; i < sc->sc_nranges; i++) {
+ /* Skip non-64-bit ranges. */
+ if ((sc->sc_ranges[i].flags & 0x03000000) != 0x03000000)
+ continue;
+
+ /* Bail if we're out of 64-bit mmio windows. */
+ if (window > m64ranges[1]) {
+ printf(": no 64-bit mmio window available\n");
+ return;
+ }
+
+ error = opal_pci_set_phb_mem_window(sc->sc_phb_id,
+ OPAL_M64_WINDOW_TYPE, window, sc->sc_ranges[i].phys_base,
+ sc->sc_ranges[i].pci_base, sc->sc_ranges[i].size);
+ if (error != OPAL_SUCCESS) {
+ printf(": can't set 64-bit mmio window\n");
+ return;
+ }
+ error = opal_pci_phb_mmio_enable(sc->sc_phb_id,
+ OPAL_M64_WINDOW_TYPE, window, OPAL_ENABLE_M64_SPLIT);
+ if (error != OPAL_SUCCESS) {
+ printf(": can't enable 64-bit mmio window\n");
+ return;
+ }
+
+ window++;
+ }
+
printf("\n");
memcpy(&sc->sc_bus_iot, sc->sc_iot, sizeof(sc->sc_bus_iot));
@@ -181,6 +289,10 @@ phb_attach(struct device *parent, struct device *self, void *aux)
sc->sc_bus_memt._space_write_4 = little_space_write_4;
sc->sc_bus_memt._space_write_8 = little_space_write_8;
+ memcpy(&sc->sc_bus_dmat, sc->sc_dmat, sizeof(sc->sc_bus_dmat));
+ sc->sc_bus_dmat._cookie = sc;
+ sc->sc_bus_dmat._dmamap_load_buffer = phb_dmamap_load_buffer;
+
sc->sc_pc.pc_conf_v = sc;
sc->sc_pc.pc_attach_hook = phb_attach_hook;
sc->sc_pc.pc_bus_maxdevs = phb_bus_maxdevs;
@@ -202,7 +314,7 @@ phb_attach(struct device *parent, struct device *self, void *aux)
pba.pba_busname = "pci";
pba.pba_iot = &sc->sc_bus_iot;
pba.pba_memt = &sc->sc_bus_memt;
- pba.pba_dmat = faa->fa_dmat;
+ pba.pba_dmat = &sc->sc_bus_dmat;
pba.pba_pc = &sc->sc_pc;
pba.pba_domain = pci_ndomains++;
pba.pba_bus = sc->sc_bus;
@@ -257,11 +369,23 @@ phb_conf_read(void *v, pcitag_t tag, int reg)
struct phb_softc *sc = v;
int64_t error;
uint32_t data;
+ uint16_t pci_error_state;
+ uint8_t freeze_state;
error = opal_pci_config_read_word(sc->sc_phb_id, tag, reg, &data);
- if (error == OPAL_SUCCESS)
+ if (error == OPAL_SUCCESS && data != 0xffffffff)
return data;
+ /*
+ * Probing hardware that isn't there may ut the host bridge in
+ * an error state. Clear the error.
+ */
+ error = opal_pci_eeh_freeze_status(sc->sc_phb_id, sc->sc_pe_number,
+ &freeze_state, &pci_error_state, NULL);
+ if (freeze_state)
+ opal_pci_eeh_freeze_clear(sc->sc_phb_id, sc->sc_pe_number,
+ OPAL_EEH_ACTION_CLEAR_FREEZE_ALL);
+
return 0xffffffff;
}
@@ -309,7 +433,7 @@ void *
phb_intr_establish(void *v, pci_intr_handle_t ih, int level,
int (*func)(void *), void *arg, char *name)
{
- return v;
+ return NULL;
}
void
@@ -351,12 +475,33 @@ phb_bs_memmap(bus_space_tag_t t, bus_addr_t addr, bus_size_t size,
uint64_t pci_end = pci_start + sc->sc_ranges[i].size;
uint64_t phys_start = sc->sc_ranges[i].phys_base;
- if ((sc->sc_ranges[i].flags & 0x03000000) == 0x02000000 &&
+ if ((sc->sc_ranges[i].flags & 0x02000000) == 0x02000000 &&
addr >= pci_start && addr + size <= pci_end) {
return bus_space_map(sc->sc_iot,
addr - pci_start + phys_start, size, flags, bshp);
}
}
-
+
return ENXIO;
}
+
+int
+phb_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf,
+ bus_size_t buflen, struct proc *p, int flags, paddr_t *lastaddrp,
+ int *segp, int first)
+{
+ struct phb_softc *sc = t->_cookie;
+ int seg, firstseg = *segp;
+ int error;
+
+ error = sc->sc_dmat->_dmamap_load_buffer(sc->sc_dmat, map, buf, buflen,
+ p, flags, lastaddrp, segp, first);
+ if (error)
+ return error;
+
+ /* For each segment. */
+ for (seg = firstseg; seg <= *segp; seg++)
+ map->dm_segs[seg].ds_addr |= IODA_TVE_SELECT;
+
+ return 0;
+}