Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 17 Jan 2018 03:34:26 +0000 (UTC)
From:      "Landon J. Fuller" <landonf@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r328071 - in head/sys/dev/bhnd: bhndb cores/pci
Message-ID:  <201801170334.w0H3YQsk032602@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: landonf
Date: Wed Jan 17 03:34:26 2018
New Revision: 328071
URL: https://svnweb.freebsd.org/changeset/base/328071

Log:
  bhndb_pci(4): fix incorrect BHND_PCI_SRSH_PI workaround
  
  On a SPROM-less device, the PCI(e) bridge core will be initialized with its
  power-on-reset defaults; this can leave the SPROM-derived BHND_PCI_SRSH_PI
  value pointing to the wrong backplane address. This value is used by the
  PCI core when performing address translation between the static register
  windows in BAR0 that map the PCI core's register block, and backplane
  address space.
  
  Previously, bhndb_pci(4) incorrectly used the potentially invalid static
  BAR0 PCI register windows when attempting to correct the BHND_PCI_SRSH_PI
  value in the PCI core's SPROM shadow.
  
  Instead, we now read/update BHND_PCI_SRSH_PI by fetching the PCI core's
  backplane address from the core enumeration table, and then using a dynamic
  register window to explicitly map the PCI core's register block into BAR0.
  
  Sponsored by:	The FreeBSD Foundation

Modified:
  head/sys/dev/bhnd/bhndb/bhndb_pci.c
  head/sys/dev/bhnd/bhndb/bhndb_pcivar.h
  head/sys/dev/bhnd/cores/pci/bhnd_pcireg.h

Modified: head/sys/dev/bhnd/bhndb/bhndb_pci.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_pci.c	Tue Jan 16 23:19:57 2018	(r328070)
+++ head/sys/dev/bhnd/bhndb/bhndb_pci.c	Wed Jan 17 03:34:26 2018	(r328071)
@@ -75,13 +75,11 @@ __FBSDID("$FreeBSD$");
 #include "bhndb_private.h"
 
 struct bhndb_pci_eio;
+struct bhndb_pci_probe;
 
 static int		bhndb_pci_alloc_msi(struct bhndb_pci_softc *sc,
 			    int *msi_count);
-static int		bhndb_pci_read_core_table(device_t dev,
-			    struct bhnd_chipid *chipid,
-			    struct bhnd_core_info **cores, u_int *ncores,
-			    bhnd_erom_class_t **eromcls);
+
 static int		bhndb_pci_add_children(struct bhndb_pci_softc *sc);
 
 static bhnd_devclass_t	bhndb_expected_pci_devclass(device_t dev);
@@ -101,15 +99,30 @@ static void		bhndb_pci_write_core(struct bhndb_pci_sof
 static uint32_t		bhndb_pci_read_core(struct bhndb_pci_softc *sc,
 			    bus_size_t offset, u_int width);
 
-static void		bhndb_init_sromless_pci_config(
-			    struct bhndb_pci_softc *sc);
+static int		bhndb_pci_srsh_pi_war(struct bhndb_pci_softc *sc,
+			    struct bhndb_pci_probe *probe);
 
 static bus_addr_t	bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc);
 static bus_size_t	bhndb_pci_sprom_size(struct bhndb_pci_softc *sc);
 
-static int		bhndb_pci_eio_init(struct bhndb_pci_eio *pio,
-			    device_t dev, device_t pci_dev,
-			    struct bhndb_host_resources *hr);
+static int		bhndb_pci_probe_alloc(struct bhndb_pci_probe **probe,
+			    device_t dev, bhnd_devclass_t pci_devclass);
+static void		bhndb_pci_probe_free(struct bhndb_pci_probe *probe);
+
+static int		bhndb_pci_probe_copy_core_table(
+			    struct bhndb_pci_probe *probe,
+			    struct bhnd_core_info **cores, u_int *ncores);
+static void		bhndb_pci_probe_free_core_table(
+			    struct bhnd_core_info *cores);
+
+static void		bhndb_pci_probe_write(struct bhndb_pci_probe *sc,
+			    bhnd_addr_t addr, bhnd_size_t offset,
+			    uint32_t value, u_int width);
+static uint32_t		bhndb_pci_probe_read(struct bhndb_pci_probe *sc,
+			    bhnd_addr_t addr, bhnd_size_t offset, u_int width);
+
+static void		bhndb_pci_eio_init(struct bhndb_pci_eio *eio,
+			    struct bhndb_pci_probe *probe);
 static int		bhndb_pci_eio_map(struct bhnd_erom_io *eio,
 			    bhnd_addr_t addr, bhnd_size_t size);
 static uint32_t		bhndb_pci_eio_read(struct bhnd_erom_io *eio,
@@ -122,26 +135,50 @@ static struct bhndb_pci_quirk	bhndb_pcie_quirks[];
 static struct bhndb_pci_quirk	bhndb_pcie2_quirks[];
 
 static struct bhndb_pci_core bhndb_pci_cores[] = {
-	BHNDB_PCI_CORE(PCI,	BHND_PCI_SRSH_PI_OFFSET,	bhndb_pci_quirks),
-	BHNDB_PCI_CORE(PCIE,	BHND_PCIE_SRSH_PI_OFFSET,	bhndb_pcie_quirks),
-	BHNDB_PCI_CORE(PCIE2,	BHND_PCIE_SRSH_PI_OFFSET,	bhndb_pcie2_quirks),
+	BHNDB_PCI_CORE(PCI,	bhndb_pci_quirks),
+	BHNDB_PCI_CORE(PCIE,	bhndb_pcie_quirks),
+	BHNDB_PCI_CORE(PCIE2,	bhndb_pcie2_quirks),
 	BHNDB_PCI_CORE_END
 };
 
 /* bhndb_pci erom I/O instance state */
 struct bhndb_pci_eio {
 	struct bhnd_erom_io		 eio;
-	device_t			 dev;		/**< bridge device */
-	device_t			 pci_dev;	/**< parent PCI device */
-	struct bhndb_host_resources	*hr;		/**< borrowed reference to host resources */
-	const struct bhndb_regwin	*win;		/**< mapped register window, or NULL */
-	struct resource			*res;		/**< resource containing the register window, or NULL if no window mapped */
-	bhnd_addr_t			 res_target;	/**< current target address (if mapped) */
-	bool				 mapped;	/**< true if a valid mapping exists, false otherwise */
 	bhnd_addr_t			 addr;		/**< mapped address */
 	bhnd_size_t			 size;		/**< mapped size */
+	struct bhndb_pci_probe		*probe;		/**< borrowed probe reference */
 };
 
+/**
+ * Provides early bus access to the bridged device's cores and core enumeration
+ * table.
+ *
+ * May be safely used during probe or early device attach, prior to calling
+ * bhndb_attach().
+ */
+struct bhndb_pci_probe {
+	device_t			 dev;		/**< bridge device */
+	device_t			 pci_dev;	/**< parent PCI device */
+	struct bhnd_chipid		 cid;		/**< chip identification */
+	struct bhnd_core_info		 hostb_core;	/**< PCI bridge core info */
+
+	struct bhndb_pci_eio		 erom_io;	/**< erom I/O instance */
+	bhnd_erom_class_t		*erom_class;	/**< probed erom class */
+	bhnd_erom_t			*erom;		/**< erom parser */
+	struct bhnd_core_info		*cores;		/**< erom-owned core table */
+	u_int				 ncores;	/**< number of cores */
+
+	const struct bhndb_regwin	*m_win;		/**< mapped register window, or NULL if no mapping */
+	struct resource			*m_res;		/**< resource containing the register window, or NULL if no window mapped */
+	bhnd_addr_t			 m_target;	/**< base address mapped by m_win */
+	bhnd_addr_t			 m_addr;	/**< mapped address */
+	bhnd_size_t			 m_size;	/**< mapped size */
+	bool				 m_valid;	/**< true if a valid mapping exists, false otherwise */
+
+	struct bhndb_host_resources	*hr;		/**< backing host resources */
+};
+
+
 static struct bhndb_pci_quirk bhndb_pci_quirks[] = {
 	/* Backplane interrupt flags must be routed via siba-specific
 	 * SIBA_CFG0_INTVEC configuration register; the BHNDB_PCI_INT_MASK
@@ -162,12 +199,9 @@ static struct bhndb_pci_quirk bhndb_pcie_quirks[] = {
 };
 
 static struct bhndb_pci_quirk bhndb_pcie2_quirks[] = {
-	/* All PCIe-G2 core revisions require the SRSH work-around */
-	BHNDB_PCI_QUIRK(HWREV_ANY,	BHNDB_PCI_QUIRK_SRSH_WAR),
 	BHNDB_PCI_QUIRK_END
 };
 
-
 /**
  * Return the device table entry for @p ci, or NULL if none.
  */
@@ -227,16 +261,14 @@ bhndb_pci_get_core_quirks(struct bhnd_chipid *cid, str
 static int
 bhndb_pci_probe(device_t dev)
 {
-	struct bhnd_chipid	 cid;
-	struct bhnd_core_info	*cores, hostb_core;
+	struct bhndb_pci_probe	*probe;
 	struct bhndb_pci_core	*entry;
 	bhnd_devclass_t		 hostb_devclass;
-	u_int			 ncores;
 	device_t		 parent;
 	devclass_t		 parent_bus, pci;
 	int			 error;
 
-	cores = NULL;
+	probe = NULL;
 
 	/* Our parent must be a PCI/PCIe device. */
 	pci = devclass_find("pci");
@@ -251,19 +283,12 @@ bhndb_pci_probe(device_t dev)
 		return (error);
 
 	/* Identify the chip and enumerate the bridged cores */
-	error = bhndb_pci_read_core_table(dev, &cid, &cores, &ncores, NULL);
-	if (error)
-		goto cleanup;
-
-	/* Search our core table for the host bridge core */
 	hostb_devclass = bhndb_expected_pci_devclass(dev);
-	error = bhndb_find_hostb_core(cores, ncores, hostb_devclass,
-	    &hostb_core);
-	if (error)
+	if ((error = bhndb_pci_probe_alloc(&probe, dev, hostb_devclass)))
 		goto cleanup;
 
 	/* Look for a matching core table entry */
-	if ((entry = bhndb_pci_find_core(&hostb_core)) == NULL) {
+	if ((entry = bhndb_pci_find_core(&probe->hostb_core)) == NULL) {
 		error = ENXIO;
 		goto cleanup;
 	}
@@ -274,9 +299,10 @@ bhndb_pci_probe(device_t dev)
 	error = BUS_PROBE_DEFAULT;
 
 cleanup:
+	if (probe != NULL)
+		bhndb_pci_probe_free(probe);
+
 	bhndb_disable_pci_clocks(dev);
-	if (cores != NULL)
-		free(cores, M_BHND);
 
 	return (error);
 }
@@ -319,6 +345,7 @@ bhndb_pci_attach(device_t dev)
 	struct bhnd_chipid	 cid;
 	struct bhnd_core_info	*cores, hostb_core;
 	bhnd_erom_class_t	*erom_class;
+	struct bhndb_pci_probe	*probe;
 	u_int			 ncores;
 	int			 irq_rid;
 	int			 error;
@@ -332,11 +359,40 @@ bhndb_pci_attach(device_t dev)
 
 	BHNDB_PCI_LOCK_INIT(sc);
 
+	probe = NULL;
 	cores = NULL;
 
 	/* Enable PCI bus mastering */
 	pci_enable_busmaster(sc->parent);
 
+	/* Enable clocks (if required by this hardware) */
+	if ((error = bhndb_enable_pci_clocks(sc->dev)))
+		goto cleanup;
+
+	/* Identify the chip and enumerate the bridged cores */
+	error = bhndb_pci_probe_alloc(&probe, dev, sc->pci_devclass);
+	if (error)
+		goto cleanup;
+
+	sc->pci_quirks = bhndb_pci_get_core_quirks(&probe->cid,
+	    &probe->hostb_core);
+
+	/* Select the appropriate register window handler */
+	if (probe->cid.chip_type == BHND_CHIPTYPE_SIBA) {
+		sc->set_regwin = bhndb_pci_compat_setregwin;
+	} else {
+		sc->set_regwin = bhndb_pci_fast_setregwin;
+	}
+
+	/*
+	 * Fix up our PCI base address in the SPROM shadow, if necessary.
+	 * 
+	 * This must be done prior to accessing any static register windows
+	 * that map the PCI core.
+	 */
+	if ((error = bhndb_pci_srsh_pi_war(sc, probe)))
+		goto cleanup;
+
 	/* Set up PCI interrupt handling */
 	if (bhndb_pci_alloc_msi(sc, &sc->msi_count) == 0) {
 		/* MSI uses resource IDs starting at 1 */
@@ -361,39 +417,31 @@ bhndb_pci_attach(device_t dev)
 		goto cleanup;
 	}
 
-	/* Enable clocks (if required by this hardware) */
-	if ((error = bhndb_enable_pci_clocks(sc->dev)))
-		goto cleanup;
+	/*
+	 * Copy out the probe results and then free our probe state, releasing
+	 * its exclusive ownership of host bridge resources.
+	 * 
+	 * This must be done prior to full configuration of the bridge via
+	 * bhndb_attach().
+	 */
+	cid = probe->cid;
+	erom_class = probe->erom_class;
+	hostb_core = probe->hostb_core;
 
-	/* Identify the chip and enumerate the bridged cores */
-	error = bhndb_pci_read_core_table(dev, &cid, &cores, &ncores,
-	    &erom_class);
-	if (error)
+	error = bhndb_pci_probe_copy_core_table(probe, &cores, &ncores);
+	if (error) {
+		cores = NULL;
 		goto cleanup;
-
-	/* Select the appropriate register window handler */
-	if (cid.chip_type == BHND_CHIPTYPE_SIBA) {
-		sc->set_regwin = bhndb_pci_compat_setregwin;
-	} else {
-		sc->set_regwin = bhndb_pci_fast_setregwin;
 	}
 
-	/* Determine our host bridge core and populate our quirk flags */
-	error = bhndb_find_hostb_core(cores, ncores, sc->pci_devclass,
-	    &hostb_core);
-	if (error)
-		goto cleanup;
+	bhndb_pci_probe_free(probe);
+	probe = NULL;
 
-	sc->pci_quirks = bhndb_pci_get_core_quirks(&cid, &hostb_core);
-
 	/* Perform bridge attach */
 	error = bhndb_attach(dev, &cid, cores, ncores, &hostb_core, erom_class);
 	if (error)
 		goto cleanup;
 
-	/* Fix-up power on defaults for SROM-less devices. */
-	bhndb_init_sromless_pci_config(sc);
-
 	/* Add any additional child devices */
 	if ((error = bhndb_pci_add_children(sc)))
 		goto cleanup;
@@ -402,13 +450,12 @@ bhndb_pci_attach(device_t dev)
 	if ((error = bus_generic_attach(dev)))
 		goto cleanup;
 
-	free(cores, M_BHND);
+	bhndb_pci_probe_free_core_table(cores);
 
 	return (0);
 
 cleanup:
 	device_delete_children(dev);
-	bhndb_disable_pci_clocks(sc->dev);
 
 	if (sc->isrc != NULL)
 		bhndb_free_intr_isrc(sc->isrc);
@@ -417,8 +464,13 @@ cleanup:
 		pci_release_msi(sc->parent);
 
 	if (cores != NULL)
-		free(cores, M_BHND);
+		bhndb_pci_probe_free_core_table(cores);
 
+	if (probe != NULL)
+		bhndb_pci_probe_free(probe);
+
+	bhndb_disable_pci_clocks(sc->dev);
+
 	pci_disable_busmaster(sc->parent);
 
 	BHNDB_PCI_LOCK_DESTROY(sc);
@@ -461,125 +513,7 @@ bhndb_pci_detach(device_t dev)
 	return (0);
 }
 
-/**
- * Use the generic PCI bridge hardware configuration to enumerate the bridged
- * bhnd(4) bus' core table.
- * 
- * @note This function may be safely called prior to device attach, (e.g.
- * from DEVICE_PROBE).
- * @note This function requires exclusive ownership over allocating and 
- * configuring host bridge resources, and should only be called prior to
- * completion of device attach and full configuration of the bridge.
- * 
- * @param	dev		The bhndb_pci bridge device.
- * @param[out]	chipid		On success, the parsed chip identification.
- * @param[out]	cores		On success, the enumerated core table. The
- *				caller is responsible for freeing this table via
- *				bhndb_pci_free_core_table().
- * @param[out]	ncores		On success, the number of cores found in
- *				@p cores.
- * @param[out]	eromcls		On success, a pointer to the erom class used to
- *				parse the device enumeration table. This
- *				argument may be NULL if the class is not
- *				desired.
- * 
- * @retval 0		success
- * @retval non-zero	if enumerating the bridged bhnd(4) bus fails, a regular
- * 			unix error code will be returned.
- */
 static int
-bhndb_pci_read_core_table(device_t dev, struct bhnd_chipid *chipid,
-    struct bhnd_core_info **cores, u_int *ncores,
-    bhnd_erom_class_t **eromcls)
-{
-	const struct bhndb_hwcfg	*cfg;
-	struct bhndb_host_resources	*hr;
-	struct bhndb_pci_eio		 pio;
-	struct bhnd_core_info		*erom_cores;
-	const struct bhnd_chipid	*hint;
-	struct bhnd_chipid		 cid;
-	bhnd_erom_class_t		*erom_class;
-	bhnd_erom_t			*erom;
-	device_t			 parent_dev;
-	u_int				 erom_ncores;
-	int				 error;
-
-	parent_dev = device_get_parent(dev);
-	erom = NULL;
-	erom_cores = NULL;
-
-	/* Fetch our chipid hint (if any) and generic hardware configuration */
-	cfg = BHNDB_BUS_GET_GENERIC_HWCFG(parent_dev, dev);
-	hint = BHNDB_BUS_GET_CHIPID(parent_dev, dev);
-
-	/* Allocate our host resources */
-	if ((error = bhndb_alloc_host_resources(&hr, dev, parent_dev, cfg)))
-		return (error);
-
-	/* Initialize our erom I/O state */
-	if ((error = bhndb_pci_eio_init(&pio, dev, parent_dev, hr)))
-		goto failed;
-
-	/* Map the first bus core from our bridged bhnd(4) bus */
-	error = bhndb_pci_eio_map(&pio.eio, BHND_DEFAULT_CHIPC_ADDR,
-	    BHND_DEFAULT_CORE_SIZE);
-	if (error)
-		goto failed;
-
-	/* Probe for a usable EROM class, and read the chip identifier */
-	erom_class = bhnd_erom_probe_driver_classes(device_get_devclass(dev),
-	    &pio.eio, hint, &cid);
-	if (erom_class == NULL) {
-		device_printf(dev, "device enumeration unsupported; no "
-		    "compatible driver found\n");
-
-		error = ENXIO;
-		goto failed;
-	}
-
-	/* Allocate EROM parser */
-	if ((erom = bhnd_erom_alloc(erom_class, &cid, &pio.eio)) == NULL) {
-		device_printf(dev, "failed to allocate device enumeration "
-		    "table parser\n");
-		error = ENXIO;
-		goto failed;
-	}
-
-	/* Read the full core table */
-	error = bhnd_erom_get_core_table(erom, &erom_cores, &erom_ncores);
-	if (error) {
-		device_printf(dev, "error fetching core table: %d\n", error);
-		goto failed;
-	}
-
-	/* Provide the results to our caller */
-	*cores = malloc(sizeof(erom_cores[0]) * erom_ncores, M_BHND, M_WAITOK);
-	memcpy(*cores, erom_cores, sizeof(erom_cores[0]) * erom_ncores);
-	*ncores = erom_ncores;
-
-	*chipid = cid;
-	if (eromcls != NULL)
-		*eromcls = erom_class;
-
-	/* Clean up */
-	bhnd_erom_free_core_table(erom, erom_cores);
-	bhnd_erom_free(erom);
-	bhndb_release_host_resources(hr);
-
-	return (0);
-
-failed:
-	if (erom_cores != NULL)
-		bhnd_erom_free_core_table(erom, erom_cores);
-
-	if (erom != NULL)
-		bhnd_erom_free(erom);
-
-	bhndb_release_host_resources(hr);
-	return (error);
-}
-
-static int
 bhndb_pci_add_children(struct bhndb_pci_softc *sc)
 {
 	bus_size_t		 nv_sz;
@@ -822,45 +756,82 @@ bhndb_pci_read_core(struct bhndb_pci_softc *sc, bus_si
 	}
 }
 
-/*
- * On devices without a SROM, the PCI(e) cores will be initialized with
- * their Power-on-Reset defaults; this can leave two of the BAR0 PCI windows
- * mapped to the wrong core.
+/**
+ * Fix-up power on defaults for SPROM-less devices.
+ *
+ * On SPROM-less devices, the PCI(e) cores will be initialized with their their
+ * Power-on-Reset defaults; this can leave the BHND_PCI_SRSH_PI value pointing
+ * to the wrong backplane address. This value is used by the PCI core when
+ * performing address translation between static register windows in BAR0 that
+ * map the PCI core's register block, and backplane address space.
+ *
+ * When translating accesses via these BAR0 regions, the PCI bridge determines
+ * the base address of the PCI core by concatenating:
+ *
+ *	[bits]	[source]
+ *	31:16	bits [31:16] of the enumeration space address (e.g. 0x18000000)
+ *	15:12	value of BHND_PCI_SRSH_PI from the PCI core's SPROM shadow
+ *	11:0	bits [11:0] of the PCI bus address
+ *
+ * For example, on a PCI_V0 device, the following PCI core register offsets are
+ * mapped into BAR0:
+ *
+ *	[BAR0 offset]		[description]		[PCI core offset]
+ *	0x1000-0x17FF		sprom shadow		0x800-0xFFF
+ *	0x1800-0x1DFF		device registers	0x000-0x5FF
+ *	0x1E00+0x1FFF		siba config registers	0xE00-0xFFF
+ *
+ * This function checks -- and if necessary, corrects -- the BHND_PCI_SRSH_PI
+ * value in the SPROM shadow. 
+ *
+ * This workaround must applied prior to accessing any static register windows
+ * that map the PCI core.
  * 
- * This function updates the SROM shadow to point the BAR0 windows at the
- * current PCI core.
- * 
- * Applies to all PCI/PCIe revisions.
+ * Applies to all PCI and PCIe-G1 core revisions.
  */
-static void
-bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc)
+static int
+bhndb_pci_srsh_pi_war(struct bhndb_pci_softc *sc,
+    struct bhndb_pci_probe *probe)
 {
-	const struct bhndb_pci_core	*pci_core;
-	bus_size_t			 srsh_offset;
-	u_int				 pci_cidx, sprom_cidx;
-	uint16_t			 val;
+	struct bhnd_core_match	md;
+	bhnd_addr_t		pci_addr;
+	bhnd_size_t		pci_size;
+	bus_size_t		srsh_offset;
+	uint16_t		srsh_val, pci_val;
+	uint16_t		val;
+	int			error;
 
 	if ((sc->pci_quirks & BHNDB_PCI_QUIRK_SRSH_WAR) == 0)
-		return;
+		return (0);
 
-	/* Determine the correct register offset for our PCI core */
-	pci_core = bhndb_pci_find_core(&sc->bhndb.bridge_core);
-	KASSERT(pci_core != NULL, ("missing core table entry"));
+	/* Use an equality match descriptor to look up our PCI core's base
+	 * address in the EROM */
+	md = bhnd_core_get_match_desc(&probe->hostb_core);
+	error = bhnd_erom_lookup_core_addr(probe->erom, &md, BHND_PORT_DEVICE,
+	    0, 0, NULL, &pci_addr, &pci_size);
+	if (error) {
+		device_printf(sc->dev, "no base address found for the PCI host "
+		    "bridge core: %d\n", error);
+		return (error);
+	}
 
-	srsh_offset = pci_core->srsh_offset;
+	/* Fetch the SPROM SRSH_PI value */
+	srsh_offset = BHND_PCI_SPROM_SHADOW + BHND_PCI_SRSH_PI_OFFSET;
+	val = bhndb_pci_probe_read(probe, pci_addr, srsh_offset, sizeof(val));
+	srsh_val = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT;
 
-	/* Fetch the SPROM's configured core index */
-	val = bhndb_pci_read_core(sc, srsh_offset, sizeof(val));
-	sprom_cidx = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT;
-
-	/* If it doesn't match host bridge's core index, update the index
-	 * value */
-	pci_cidx = sc->bhndb.bridge_core.core_idx;
-	if (sprom_cidx != pci_cidx) {
+	/* If it doesn't match PCI core's base address, update the SPROM
+	 * shadow */
+	pci_val = (pci_addr & BHND_PCI_SRSH_PI_ADDR_MASK) >>
+	    BHND_PCI_SRSH_PI_ADDR_SHIFT;
+	if (srsh_val != pci_val) {
 		val &= ~BHND_PCI_SRSH_PI_MASK;
-		val |= (pci_cidx << BHND_PCI_SRSH_PI_SHIFT);
-		bhndb_pci_write_core(sc, srsh_offset, val, sizeof(val));
+		val |= (pci_val << BHND_PCI_SRSH_PI_SHIFT);
+		bhndb_pci_probe_write(probe, pci_addr, srsh_offset, val,
+		    sizeof(val));
 	}
+
+	return (0);
 }
 
 static int
@@ -1283,188 +1254,449 @@ bhndb_pci_route_interrupts(device_t dev, device_t chil
 }
 
 /**
- * Initialize a new bhndb PCI bridge EROM I/O instance. This EROM I/O
- * implementation supports mapping of the device enumeration table via the
- * @p hr host resources.
+ * Using the generic PCI bridge hardware configuration, allocate, initialize
+ * and return a new bhndb_pci probe state instance.
  * 
- * @param pio		The instance to be initialized.
- * @param dev		The bridge device.
- * @param pci_dev	The bridge's parent PCI device.
- * @param hr		The host resources to be used to map the device
- *			enumeration table.
+ * On success, the caller assumes ownership of the returned probe instance, and
+ * is responsible for releasing this reference using bhndb_pci_probe_free().
+ * 
+ * @param[out]	probe		On success, the newly allocated probe instance.
+ * @param	dev		The bhndb_pci bridge device.
+ * @param	hostb_devclass	The expected device class of the bridge core.
+ *
+ * @retval 0		success
+ * @retval non-zero	if allocating the probe state fails, a regular
+ * 			unix error code will be returned.
+ * 
+ * @note This function requires exclusive ownership over allocating and 
+ * configuring host bridge resources, and should only be called prior to
+ * completion of device attach and full configuration of the bridge.
  */
 static int
-bhndb_pci_eio_init(struct bhndb_pci_eio *pio, device_t dev, device_t pci_dev,
-    struct bhndb_host_resources *hr)
+bhndb_pci_probe_alloc(struct bhndb_pci_probe **probe, device_t dev,
+    bhnd_devclass_t hostb_devclass)
 {
-	memset(&pio->eio, 0, sizeof(pio->eio));
-	pio->eio.map = bhndb_pci_eio_map;
-	pio->eio.read = bhndb_pci_eio_read;
-	pio->eio.fini = NULL;
+	struct bhndb_pci_probe		*p;
+	struct bhnd_erom_io		*eio;
+	const struct bhndb_hwcfg	*hwcfg;
+	const struct bhnd_chipid	*hint;
+	device_t			 parent_dev;
+	int				 error;
 
-	pio->dev = dev;
-	pio->pci_dev = pci_dev;
-	pio->hr = hr;
-	pio->win = NULL;
-	pio->res = NULL;
+	parent_dev = device_get_parent(dev);
+	eio = NULL;
 
+	p = malloc(sizeof(*p), M_BHND, M_ZERO|M_WAITOK);
+	p->dev = dev;
+	p->pci_dev = parent_dev;
+
+	/* Our register window mapping state must be initialized at this point,
+	 * as bhndb_pci_eio will begin making calls into
+	 * bhndb_pci_probe_(read|write|get_mapping) */
+	p->m_win = NULL;
+	p->m_res = NULL;
+	p->m_valid = false;
+
+	bhndb_pci_eio_init(&p->erom_io, p);
+	eio = &p->erom_io.eio;
+
+	/* Fetch our chipid hint (if any) and generic hardware configuration */
+	hwcfg = BHNDB_BUS_GET_GENERIC_HWCFG(parent_dev, dev);
+	hint = BHNDB_BUS_GET_CHIPID(parent_dev, dev);
+
+	/* Allocate our host resources */
+	error = bhndb_alloc_host_resources(&p->hr, dev, parent_dev, hwcfg);
+	if (error) {
+		p->hr = NULL;
+		goto failed;
+	}
+
+	/* Map the first bus core from our bridged bhnd(4) bus */
+	error = bhnd_erom_io_map(eio, BHND_DEFAULT_CHIPC_ADDR,
+	    BHND_DEFAULT_CORE_SIZE);
+	if (error)
+		goto failed;
+
+	/* Probe for a usable EROM class, and read the chip identifier */
+	p->erom_class = bhnd_erom_probe_driver_classes(
+	    device_get_devclass(dev), eio, hint, &p->cid);
+	if (p->erom_class == NULL) {
+		device_printf(dev, "device enumeration unsupported; no "
+		    "compatible driver found\n");
+
+		error = ENXIO;
+		goto failed;
+	}
+
+	/* Allocate EROM parser */
+	p->erom = bhnd_erom_alloc(p->erom_class, &p->cid, eio);
+	if (p->erom == NULL) {
+		device_printf(dev, "failed to allocate device enumeration "
+		    "table parser\n");
+		error = ENXIO;
+		goto failed;
+	}
+
+	/* The EROM I/O instance is now owned by our EROM parser */
+	eio = NULL;
+
+	/* Read the full core table */
+	error = bhnd_erom_get_core_table(p->erom, &p->cores, &p->ncores);
+	if (error) {
+		device_printf(p->dev, "error fetching core table: %d\n",
+		    error);
+
+		p->cores = NULL;
+		goto failed;
+	}
+
+	/* Identify the host bridge core */
+	error = bhndb_find_hostb_core(p->cores, p->ncores, hostb_devclass,
+	    &p->hostb_core);
+	if (error) {
+		device_printf(dev, "failed to identify the host bridge "
+		    "core: %d\n", error);
+
+		goto failed;
+	}
+
+	*probe = p;
 	return (0);
+
+failed:
+	if (eio != NULL) {
+		KASSERT(p->erom == NULL, ("I/O instance will be freed by "
+		    "its owning parser"));
+
+		bhnd_erom_io_fini(eio);
+	}
+
+	if (p->erom != NULL) {
+		if (p->cores != NULL)
+			bhnd_erom_free_core_table(p->erom, p->cores);
+
+		bhnd_erom_free(p->erom);
+	} else {
+		KASSERT(p->cores == NULL, ("cannot free erom-owned core table "
+		    "without erom reference"));
+	}
+
+	if (p->hr != NULL)
+		bhndb_release_host_resources(p->hr);
+
+	free(p, M_BHND);
+
+	return (error);
 }
 
 /**
- * Attempt to adjust the dynamic register window backing @p pio to permit
- * reading @p size bytes at @p addr.
+ * Free the given @p probe instance and any associated host bridge resources.
+ */
+static void
+bhndb_pci_probe_free(struct bhndb_pci_probe *probe)
+{
+	bhnd_erom_free_core_table(probe->erom, probe->cores);
+	bhnd_erom_free(probe->erom);
+	bhndb_release_host_resources(probe->hr);
+	free(probe, M_BHND);
+}
+
+/**
+ * Return a copy of probed core table from @p probe.
  * 
- * If @p addr or @p size fall outside the existing mapped range, or if
- * @p pio is not backed by a dynamic register window, ENXIO will be returned.
+ * @param	probe		The probe instance.
+ * @param[out]	cores		On success, a copy of the probed core table. The
+ *				caller is responsible for freeing this table
+ *				bhndb_pci_probe_free_core_table().
+ * @param[out]	ncores		On success, the number of cores found in
+ *				@p cores.
  * 
- * @param pio	The bhndb PCI erom I/O state to be modified.
- * @param addr	The address to be include
+ * @retval 0		success
+ * @retval non-zero	if enumerating the bridged bhnd(4) bus fails, a regular
+ * 			unix error code will be returned.
  */
 static int
-bhndb_pci_eio_adjust_mapping(struct bhndb_pci_eio *pio, bhnd_addr_t addr,
-    bhnd_size_t size)
+bhndb_pci_probe_copy_core_table(struct bhndb_pci_probe *probe,
+    struct bhnd_core_info **cores, u_int *ncores)
 {
-	bhnd_addr_t	 target;
-	bhnd_size_t	 offset;
-	int		 error;
+	size_t len = sizeof(**cores) * probe->ncores;
 
+	*cores = malloc(len, M_BHND, M_WAITOK);
+	memcpy(*cores, probe->cores, len);
 
-	KASSERT(pio->win != NULL, ("missing register window"));
-	KASSERT(pio->res != NULL, ("missing regwin resource"));
-	KASSERT(pio->win->win_type == BHNDB_REGWIN_T_DYN,
-	    ("unexpected window type %d", pio->win->win_type));
+	*ncores = probe->ncores;
 
-	/* The requested subrange must fall within the total mapped range */
-	if (addr < pio->addr || (addr - pio->addr) > pio->size ||
-	    size > pio->size || (addr - pio->addr) - pio->size < size)
-	{
-		return (ENXIO);
-	}
+	return (0);
+}
 
-	/* Do we already have a useable mapping? */
-	if (addr >= pio->res_target &&
-	    addr <= pio->res_target + pio->win->win_size &&
-	    (pio->res_target + pio->win->win_size) - addr >= size)
-	{
-		return (0);
-	}
+/**
+ * Free a core table previously returned by bhndb_pci_probe_copy_core_table().
+ * 
+ * @param cores The core table to be freed.
+ */
+static void
+bhndb_pci_probe_free_core_table(struct bhnd_core_info *cores)
+{
+	free(cores, M_BHND);
+}
 
-	/* Page-align the target address */
-	offset = addr % pio->win->win_size;
-	target = addr - offset;
+/**
+ * Return true if @p addr and @p size are mapped by the dynamic register window
+ * backing @p probe. 
+ */
+static bool
+bhndb_pci_probe_has_mapping(struct bhndb_pci_probe *probe, bhnd_addr_t addr,
+    bhnd_size_t size)
+{
+	if (!probe->m_valid)
+		return (false);
 
-	/* Configure the register window */
-	error = bhndb_pci_compat_setregwin(pio->dev, pio->pci_dev, pio->win,
-	    target);
-	if (error) {
-		device_printf(pio->dev, "failed to configure dynamic register "
-		    "window: %d\n", error);
-		return (error);
-	}
+	KASSERT(probe->m_win != NULL, ("missing register window"));
+	KASSERT(probe->m_res != NULL, ("missing regwin resource"));
+	KASSERT(probe->m_win->win_type == BHNDB_REGWIN_T_DYN,
+	    ("unexpected window type %d", probe->m_win->win_type));
 
-	pio->res_target = target;
-	return (0);
+	if (addr < probe->m_target)
+		return (false);
+
+	if (addr >= probe->m_target + probe->m_win->win_size)
+		return (false);
+
+	if ((probe->m_target + probe->m_win->win_size) - addr < size)
+		return (false);
+
+	return (true);
 }
 
-/* bhnd_erom_io_map() implementation */
+/**
+ * Attempt to adjust the dynamic register window backing @p probe to permit
+ * accessing @p size bytes at @p addr.
+ * 
+ * @param	probe		The bhndb_pci probe state to be modified.
+ * @param	addr		The address at which @p size bytes will mapped.
+ * @param	size		The number of bytes to be mapped.
+ * @param[out]	res		On success, will be set to the host resource
+ *				mapping @p size bytes at @p addr.
+ * @param[out]	res_offset	On success, will be set to the offset of @addr
+ *				within @p res.
+ * 
+ * @retval 0		success
+ * @retval non-zero	if an error occurs adjusting the backing dynamic
+ *			register window.
+ */
 static int
-bhndb_pci_eio_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
-    bhnd_size_t size)
+bhndb_pci_probe_map(struct bhndb_pci_probe *probe, bhnd_addr_t addr,
+    bhnd_size_t offset, bhnd_size_t size, struct resource **res,
+    bus_size_t *res_offset)
 {
-	struct bhndb_pci_eio		*pio;
-	const struct bhndb_regwin	*regwin;
-	struct resource			*r;
+	const struct bhndb_regwin	*regwin, *regwin_table;
+	struct resource			*regwin_res;
 	bhnd_addr_t			 target;
-	bhnd_size_t			 offset;
 	int				 error;
 
-	pio = (struct bhndb_pci_eio *)eio;
+	/* Determine the absolute address */
+	if (BHND_SIZE_MAX - offset < addr) {
+		device_printf(probe->dev, "invalid offset %#jx+%#jx\n", addr,
+		    offset);
+		return (ENXIO);
+	}
 
+	addr += offset;
+
+	/* Can we use the existing mapping? */
+	if (bhndb_pci_probe_has_mapping(probe, addr, size)) {
+		*res = probe->m_res;
+		*res_offset = (addr - probe->m_target) +
+		    probe->m_win->win_offset;
+
+		return (0);
+	}
+
 	/* Locate a useable dynamic register window */
-	regwin = bhndb_regwin_find_type(pio->hr->cfg->register_windows,
-	    BHNDB_REGWIN_T_DYN, MIN(size, BHND_DEFAULT_CORE_SIZE));
+	regwin_table = probe->hr->cfg->register_windows;
+	regwin = bhndb_regwin_find_type(regwin_table,
+	    BHNDB_REGWIN_T_DYN, size);
 	if (regwin == NULL) {
-		device_printf(pio->dev, "unable to map %#jx+%#jx; no "
-		    "usable dynamic register window found\n", addr, size);
+		device_printf(probe->dev, "unable to map %#jx+%#jx; no "
+		    "usable dynamic register window found\n", addr,
+		    size);
 		return (ENXIO);
 	}
 
 	/* Locate the host resource mapping our register window */
-	if ((r = bhndb_host_resource_for_regwin(pio->hr, regwin)) == NULL) {
-		device_printf(pio->dev, "unable to map %#jx+%#jx; no "
+	regwin_res = bhndb_host_resource_for_regwin(probe->hr, regwin);
+	if (regwin_res == NULL) {
+		device_printf(probe->dev, "unable to map %#jx+%#jx; no "
 		    "usable register resource found\n", addr, size);
 		return (ENXIO);
 	}
 
 	/* Page-align the target address */
-	offset = addr % regwin->win_size;
-	target = addr - offset;
+	target = addr - (addr % regwin->win_size);
 
 	/* Configure the register window */
-	error = bhndb_pci_compat_setregwin(pio->dev, pio->pci_dev, regwin,
-	    target);
+	error = bhndb_pci_compat_setregwin(probe->dev, probe->pci_dev,
+	    regwin, target);
 	if (error) {
-		device_printf(pio->dev, "failed to configure dynamic register "
-		    "window: %d\n", error);
+		device_printf(probe->dev, "failed to configure dynamic "
+		    "register window: %d\n", error);
 		return (error);
 	}
 
 	/* Update our mapping state */
-	pio->win = regwin;
-	pio->res = r;
-	pio->addr = addr;
-	pio->size = size;
-	pio->res_target = target;
+	probe->m_win = regwin;
+	probe->m_res = regwin_res;
+	probe->m_addr = addr;
+	probe->m_size = size;
+	probe->m_target = target;
+	probe->m_valid = true;
 
+	*res = regwin_res;
+	*res_offset = (addr - target) + regwin->win_offset;
+
 	return (0);
 }
 
-/* bhnd_erom_io_read() implementation */
-static uint32_t
-bhndb_pci_eio_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
+/**
+ * Write a data item to the bridged address space at the given @p offset from
+ * @p addr.
+ *
+ * A dynamic register window will be used to map @p addr.
+ * 
+ * @param probe		The bhndb_pci probe state to be used to perform the
+ *			write.
+ * @param addr		The base address.
+ * @param offset	The offset from @p addr at which @p value will be
+ *			written.
+ * @param value		The data item to be written.
+ * @param width		The data item width (1, 2, or 4 bytes).
+ */
+static void
+bhndb_pci_probe_write(struct bhndb_pci_probe *probe, bhnd_addr_t addr,
+    bhnd_size_t offset, uint32_t value, u_int width)
 {
-	struct bhndb_pci_eio		*pio;
-	bhnd_addr_t			 addr;
-	bus_size_t			 res_offset;
-	int				 error;
+	struct resource	*r;
+	bus_size_t	 res_offset;
+	int		 error;
 
-	pio = (struct bhndb_pci_eio *)eio;
+	/* Map the target address */
+	error = bhndb_pci_probe_map(probe, addr, offset, width, &r,
+	    &res_offset);
+	if (error) {
+		device_printf(probe->dev, "error mapping %#jx+%#jx for "
+		    "writing: %d\n", addr, offset, error);
+		return;
+	}
 
-	/* Calculate absolute address */
-	if (BHND_SIZE_MAX - offset < pio->addr) {
-		device_printf(pio->dev, "invalid offset %#jx+%#jx\n", pio->addr,
-		    offset);
-		return (UINT32_MAX);
+	/* Perform write */
+	switch (width) {
+	case 1:
+		return (bus_write_1(r, res_offset, value));
+	case 2:
+		return (bus_write_2(r, res_offset, value));
+	case 4:
+		return (bus_write_4(r, res_offset, value));
+	default:
+		panic("unsupported width: %u", width);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201801170334.w0H3YQsk032602>