xref: /freebsd/sys/dev/bhnd/bhndb/bhndb_pci.c (revision 89294a783a51c4dfc6382017c2d86646c14fb6bc)
14ad7e9b0SAdrian Chadd /*-
2bb64eeccSAdrian Chadd  * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
3*89294a78SLandon J. Fuller  * Copyright (c) 2017 The FreeBSD Foundation
44ad7e9b0SAdrian Chadd  * All rights reserved.
54ad7e9b0SAdrian Chadd  *
6*89294a78SLandon J. Fuller  * Portions of this software were developed by Landon Fuller
7*89294a78SLandon J. Fuller  * under sponsorship from the FreeBSD Foundation.
8*89294a78SLandon J. Fuller  *
94ad7e9b0SAdrian Chadd  * Redistribution and use in source and binary forms, with or without
104ad7e9b0SAdrian Chadd  * modification, are permitted provided that the following conditions
114ad7e9b0SAdrian Chadd  * are met:
124ad7e9b0SAdrian Chadd  * 1. Redistributions of source code must retain the above copyright
134ad7e9b0SAdrian Chadd  *    notice, this list of conditions and the following disclaimer,
144ad7e9b0SAdrian Chadd  *    without modification.
154ad7e9b0SAdrian Chadd  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
164ad7e9b0SAdrian Chadd  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
174ad7e9b0SAdrian Chadd  *    redistribution must be conditioned upon including a substantially
184ad7e9b0SAdrian Chadd  *    similar Disclaimer requirement for further binary redistribution.
194ad7e9b0SAdrian Chadd  *
204ad7e9b0SAdrian Chadd  * NO WARRANTY
214ad7e9b0SAdrian Chadd  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
224ad7e9b0SAdrian Chadd  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
234ad7e9b0SAdrian Chadd  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
244ad7e9b0SAdrian Chadd  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
254ad7e9b0SAdrian Chadd  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
264ad7e9b0SAdrian Chadd  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
274ad7e9b0SAdrian Chadd  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
284ad7e9b0SAdrian Chadd  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
294ad7e9b0SAdrian Chadd  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
304ad7e9b0SAdrian Chadd  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
314ad7e9b0SAdrian Chadd  * THE POSSIBILITY OF SUCH DAMAGES.
324ad7e9b0SAdrian Chadd  */
334ad7e9b0SAdrian Chadd 
344ad7e9b0SAdrian Chadd #include <sys/cdefs.h>
354ad7e9b0SAdrian Chadd __FBSDID("$FreeBSD$");
364ad7e9b0SAdrian Chadd 
374ad7e9b0SAdrian Chadd /*
384ad7e9b0SAdrian Chadd  * PCI-specific implementation for the BHNDB bridge driver.
394ad7e9b0SAdrian Chadd  *
404ad7e9b0SAdrian Chadd  * Provides support for bridging from a PCI parent bus to a BHND-compatible
414ad7e9b0SAdrian Chadd  * bus (e.g. bcma or siba) via a Broadcom PCI core configured in end-point
424ad7e9b0SAdrian Chadd  * mode.
434ad7e9b0SAdrian Chadd  *
448ef24a0dSAdrian Chadd  * This driver handles all initial generic host-level PCI interactions with a
458ef24a0dSAdrian Chadd  * PCI/PCIe bridge core operating in endpoint mode. Once the bridged bhnd(4)
468ef24a0dSAdrian Chadd  * bus has been enumerated, this driver works in tandem with a core-specific
478ef24a0dSAdrian Chadd  * bhnd_pci_hostb driver to manage the PCI core.
484ad7e9b0SAdrian Chadd  */
494ad7e9b0SAdrian Chadd 
504ad7e9b0SAdrian Chadd #include <sys/param.h>
514ad7e9b0SAdrian Chadd #include <sys/kernel.h>
524ad7e9b0SAdrian Chadd #include <sys/bus.h>
534ad7e9b0SAdrian Chadd #include <sys/limits.h>
544ad7e9b0SAdrian Chadd #include <sys/malloc.h>
554ad7e9b0SAdrian Chadd #include <sys/module.h>
564ad7e9b0SAdrian Chadd #include <sys/systm.h>
574ad7e9b0SAdrian Chadd 
584ad7e9b0SAdrian Chadd #include <dev/pci/pcireg.h>
594ad7e9b0SAdrian Chadd #include <dev/pci/pcivar.h>
604ad7e9b0SAdrian Chadd 
614ad7e9b0SAdrian Chadd #include <dev/bhnd/bhnd.h>
62*89294a78SLandon J. Fuller #include <dev/bhnd/bhndreg.h>
63*89294a78SLandon J. Fuller 
64*89294a78SLandon J. Fuller #include <dev/bhnd/bhnd_erom.h>
65*89294a78SLandon J. Fuller #include <dev/bhnd/bhnd_eromvar.h>
664ad7e9b0SAdrian Chadd 
674ad7e9b0SAdrian Chadd #include <dev/bhnd/cores/pci/bhnd_pcireg.h>
684ad7e9b0SAdrian Chadd 
694ad7e9b0SAdrian Chadd #include "bhndb_pcireg.h"
704ad7e9b0SAdrian Chadd #include "bhndb_pcivar.h"
714ad7e9b0SAdrian Chadd #include "bhndb_private.h"
724ad7e9b0SAdrian Chadd 
73*89294a78SLandon J. Fuller struct bhndb_pci_eio;
74*89294a78SLandon J. Fuller 
75824b48efSLandon J. Fuller static int		bhndb_pci_init_msi(struct bhndb_pci_softc *sc);
76*89294a78SLandon J. Fuller static int		bhndb_pci_read_core_table(device_t dev,
77*89294a78SLandon J. Fuller 			    struct bhnd_chipid *chipid,
78*89294a78SLandon J. Fuller 			    struct bhnd_core_info **cores, u_int *ncores,
79*89294a78SLandon J. Fuller 			    bhnd_erom_class_t **eromcls);
80111d7cb2SLandon J. Fuller static int		bhndb_pci_add_children(struct bhndb_pci_softc *sc);
81111d7cb2SLandon J. Fuller 
82*89294a78SLandon J. Fuller static bool		bhndb_is_pcie_attached(device_t dev);
834ad7e9b0SAdrian Chadd 
84*89294a78SLandon J. Fuller static int		bhndb_enable_pci_clocks(device_t dev);
85*89294a78SLandon J. Fuller static int		bhndb_disable_pci_clocks(device_t dev);
86*89294a78SLandon J. Fuller 
87*89294a78SLandon J. Fuller static int		bhndb_pci_compat_setregwin(device_t dev,
88*89294a78SLandon J. Fuller 			    device_t pci_dev, const struct bhndb_regwin *,
89*89294a78SLandon J. Fuller 			    bhnd_addr_t);
90*89294a78SLandon J. Fuller static int		bhndb_pci_fast_setregwin(device_t dev, device_t pci_dev,
914ad7e9b0SAdrian Chadd 			    const struct bhndb_regwin *, bhnd_addr_t);
924ad7e9b0SAdrian Chadd 
93e83ce340SAdrian Chadd static void		bhndb_init_sromless_pci_config(
94e83ce340SAdrian Chadd 			    struct bhndb_pci_softc *sc);
95e83ce340SAdrian Chadd 
96e83ce340SAdrian Chadd static bus_addr_t	bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc);
97f9cf87a0SAdrian Chadd static bus_size_t	bhndb_pci_sprom_size(struct bhndb_pci_softc *sc);
984ad7e9b0SAdrian Chadd 
99*89294a78SLandon J. Fuller static int		bhndb_pci_eio_init(struct bhndb_pci_eio *pio,
100*89294a78SLandon J. Fuller 			    device_t dev, device_t pci_dev,
101*89294a78SLandon J. Fuller 			    struct bhndb_host_resources *hr);
102*89294a78SLandon J. Fuller static int		bhndb_pci_eio_map(struct bhnd_erom_io *eio,
103*89294a78SLandon J. Fuller 			    bhnd_addr_t addr, bhnd_size_t size);
104*89294a78SLandon J. Fuller static uint32_t		bhndb_pci_eio_read(struct bhnd_erom_io *eio,
105*89294a78SLandon J. Fuller 			    bhnd_size_t offset, u_int width);
106*89294a78SLandon J. Fuller 
107824b48efSLandon J. Fuller #define	BHNDB_PCI_MSI_COUNT	1
108824b48efSLandon J. Fuller 
109*89294a78SLandon J. Fuller /* bhndb_pci erom I/O implementation */
110*89294a78SLandon J. Fuller struct bhndb_pci_eio {
111*89294a78SLandon J. Fuller 	struct bhnd_erom_io		 eio;
112*89294a78SLandon J. Fuller 	device_t			 dev;		/**< bridge device */
113*89294a78SLandon J. Fuller 	device_t			 pci_dev;	/**< parent PCI device */
114*89294a78SLandon J. Fuller 	struct bhndb_host_resources	*hr;		/**< borrowed reference to host resources */
115*89294a78SLandon J. Fuller 	const struct bhndb_regwin	*win;		/**< mapped register window, or NULL */
116*89294a78SLandon J. Fuller 	struct resource			*res;		/**< resource containing the register window, or NULL if no window mapped */
117*89294a78SLandon J. Fuller 	bhnd_addr_t			 res_target;	/**< current target address (if mapped) */
118*89294a78SLandon J. Fuller 	bool				 mapped;	/**< true if a valid mapping exists, false otherwise */
119*89294a78SLandon J. Fuller 	bhnd_addr_t			 addr;		/**< mapped address */
120*89294a78SLandon J. Fuller 	bhnd_size_t			 size;		/**< mapped size */
121*89294a78SLandon J. Fuller };
122*89294a78SLandon J. Fuller 
1234ad7e9b0SAdrian Chadd /**
1244ad7e9b0SAdrian Chadd  * Default bhndb_pci implementation of device_probe().
1254ad7e9b0SAdrian Chadd  *
1264ad7e9b0SAdrian Chadd  * Verifies that the parent is a PCI/PCIe device.
1274ad7e9b0SAdrian Chadd  */
1284ad7e9b0SAdrian Chadd static int
1294ad7e9b0SAdrian Chadd bhndb_pci_probe(device_t dev)
1304ad7e9b0SAdrian Chadd {
1314ad7e9b0SAdrian Chadd 	device_t	parent;
1324ad7e9b0SAdrian Chadd 	devclass_t	parent_bus;
1334ad7e9b0SAdrian Chadd 	devclass_t	pci;
1344ad7e9b0SAdrian Chadd 
1354ad7e9b0SAdrian Chadd 	/* Our parent must be a PCI/PCIe device. */
1364ad7e9b0SAdrian Chadd 	pci = devclass_find("pci");
1374ad7e9b0SAdrian Chadd 	parent = device_get_parent(dev);
1384ad7e9b0SAdrian Chadd 	parent_bus = device_get_devclass(device_get_parent(parent));
1394ad7e9b0SAdrian Chadd 
1404ad7e9b0SAdrian Chadd 	if (parent_bus != pci)
1414ad7e9b0SAdrian Chadd 		return (ENXIO);
1424ad7e9b0SAdrian Chadd 
1434ad7e9b0SAdrian Chadd 	device_set_desc(dev, "PCI-BHND bridge");
1444ad7e9b0SAdrian Chadd 
1454ad7e9b0SAdrian Chadd 	return (BUS_PROBE_DEFAULT);
1464ad7e9b0SAdrian Chadd }
1474ad7e9b0SAdrian Chadd 
148824b48efSLandon J. Fuller /* Configure MSI interrupts */
149824b48efSLandon J. Fuller static int
150824b48efSLandon J. Fuller bhndb_pci_init_msi(struct bhndb_pci_softc *sc)
151824b48efSLandon J. Fuller {
152824b48efSLandon J. Fuller 	int error;
153824b48efSLandon J. Fuller 
154824b48efSLandon J. Fuller 	/* Is MSI available? */
155824b48efSLandon J. Fuller 	if (pci_msi_count(sc->parent) < BHNDB_PCI_MSI_COUNT)
156824b48efSLandon J. Fuller 		return (ENXIO);
157824b48efSLandon J. Fuller 
158824b48efSLandon J. Fuller 	/* Allocate expected message count */
159824b48efSLandon J. Fuller 	sc->intr.msi_count = BHNDB_PCI_MSI_COUNT;
160824b48efSLandon J. Fuller 	if ((error = pci_alloc_msi(sc->parent, &sc->intr.msi_count))) {
161824b48efSLandon J. Fuller 		device_printf(sc->dev, "failed to allocate MSI interrupts: "
162824b48efSLandon J. Fuller 		    "%d\n", error);
163824b48efSLandon J. Fuller 		return (error);
164824b48efSLandon J. Fuller 	}
165824b48efSLandon J. Fuller 
166824b48efSLandon J. Fuller 	if (sc->intr.msi_count < BHNDB_PCI_MSI_COUNT)
167824b48efSLandon J. Fuller 		return (ENXIO);
168824b48efSLandon J. Fuller 
169824b48efSLandon J. Fuller 	/* MSI uses resource IDs starting at 1 */
170824b48efSLandon J. Fuller 	sc->intr.intr_rid = 1;
171824b48efSLandon J. Fuller 
172824b48efSLandon J. Fuller 	return (0);
173824b48efSLandon J. Fuller }
174824b48efSLandon J. Fuller 
1754ad7e9b0SAdrian Chadd static int
1764ad7e9b0SAdrian Chadd bhndb_pci_attach(device_t dev)
1774ad7e9b0SAdrian Chadd {
1784ad7e9b0SAdrian Chadd 	struct bhndb_pci_softc	*sc;
179*89294a78SLandon J. Fuller 	struct bhnd_chipid	 cid;
180*89294a78SLandon J. Fuller 	struct bhnd_core_info	*cores, hostb_core;
181*89294a78SLandon J. Fuller 	bhnd_erom_class_t	*erom_class;
182*89294a78SLandon J. Fuller 	u_int			 ncores;
1834ad7e9b0SAdrian Chadd 	int			 error, reg;
1844ad7e9b0SAdrian Chadd 
1854ad7e9b0SAdrian Chadd 	sc = device_get_softc(dev);
1864ad7e9b0SAdrian Chadd 	sc->dev = dev;
187e83ce340SAdrian Chadd 	sc->parent = device_get_parent(dev);
188*89294a78SLandon J. Fuller 	sc->set_regwin = NULL;
189*89294a78SLandon J. Fuller 
190*89294a78SLandon J. Fuller 	cores = NULL;
1914ad7e9b0SAdrian Chadd 
192824b48efSLandon J. Fuller 	/* Enable PCI bus mastering */
193824b48efSLandon J. Fuller 	pci_enable_busmaster(sc->parent);
194824b48efSLandon J. Fuller 
195*89294a78SLandon J. Fuller 	/* Set up PCI interrupt handling */
196824b48efSLandon J. Fuller 	if (bhndb_pci_init_msi(sc) == 0) {
197824b48efSLandon J. Fuller 		device_printf(dev, "Using MSI interrupts on %s\n",
198824b48efSLandon J. Fuller 		    device_get_nameunit(sc->parent));
199824b48efSLandon J. Fuller 	} else {
200824b48efSLandon J. Fuller 		device_printf(dev, "Using INTx interrupts on %s\n",
201824b48efSLandon J. Fuller 		    device_get_nameunit(sc->parent));
202824b48efSLandon J. Fuller 		sc->intr.intr_rid = 0;
203824b48efSLandon J. Fuller 	}
204824b48efSLandon J. Fuller 
205824b48efSLandon J. Fuller 	/* Determine our bridge device class */
206824b48efSLandon J. Fuller 	sc->pci_devclass = BHND_DEVCLASS_PCI;
207111d7cb2SLandon J. Fuller 	if (pci_find_cap(sc->parent, PCIY_EXPRESS, &reg) == 0)
208111d7cb2SLandon J. Fuller 		sc->pci_devclass = BHND_DEVCLASS_PCIE;
209111d7cb2SLandon J. Fuller 	else
210111d7cb2SLandon J. Fuller 		sc->pci_devclass = BHND_DEVCLASS_PCI;
2114ad7e9b0SAdrian Chadd 
212111d7cb2SLandon J. Fuller 	/* Enable clocks (if required by this hardware) */
213*89294a78SLandon J. Fuller 	if ((error = bhndb_enable_pci_clocks(sc->dev)))
214111d7cb2SLandon J. Fuller 		goto cleanup;
215111d7cb2SLandon J. Fuller 
216*89294a78SLandon J. Fuller 	/* Identify the chip and enumerate the bridged cores */
217*89294a78SLandon J. Fuller 	error = bhndb_pci_read_core_table(dev, &cid, &cores, &ncores,
218*89294a78SLandon J. Fuller 	    &erom_class);
219*89294a78SLandon J. Fuller 	if (error)
220111d7cb2SLandon J. Fuller 		goto cleanup;
221111d7cb2SLandon J. Fuller 
222*89294a78SLandon J. Fuller 	/* Select the appropriate register window handler */
223*89294a78SLandon J. Fuller 	if (cid.chip_type == BHND_CHIPTYPE_SIBA) {
224*89294a78SLandon J. Fuller 		sc->set_regwin = bhndb_pci_compat_setregwin;
225*89294a78SLandon J. Fuller 	} else {
226*89294a78SLandon J. Fuller 		sc->set_regwin = bhndb_pci_fast_setregwin;
2274ad7e9b0SAdrian Chadd 	}
2284ad7e9b0SAdrian Chadd 
229*89294a78SLandon J. Fuller 	/* Determine our host bridge core */
230*89294a78SLandon J. Fuller 	error = bhndb_find_hostb_core(cores, ncores, sc->pci_devclass,
231*89294a78SLandon J. Fuller 	    &hostb_core);
232*89294a78SLandon J. Fuller 	if (error)
233*89294a78SLandon J. Fuller 		goto cleanup;
234*89294a78SLandon J. Fuller 
235*89294a78SLandon J. Fuller 	/* Perform bridge attach */
236*89294a78SLandon J. Fuller 	error = bhndb_attach(dev, &cid, cores, ncores, &hostb_core, erom_class);
237*89294a78SLandon J. Fuller 	if (error)
238*89294a78SLandon J. Fuller 		goto cleanup;
2394ad7e9b0SAdrian Chadd 
240bb64eeccSAdrian Chadd 	/* Fix-up power on defaults for SROM-less devices. */
2414ad7e9b0SAdrian Chadd 	bhndb_init_sromless_pci_config(sc);
2424ad7e9b0SAdrian Chadd 
243111d7cb2SLandon J. Fuller 	/* Add any additional child devices */
244111d7cb2SLandon J. Fuller 	if ((error = bhndb_pci_add_children(sc)))
245111d7cb2SLandon J. Fuller 		goto cleanup;
246111d7cb2SLandon J. Fuller 
247111d7cb2SLandon J. Fuller 	/* Probe and attach our children */
248111d7cb2SLandon J. Fuller 	if ((error = bus_generic_attach(dev)))
249111d7cb2SLandon J. Fuller 		goto cleanup;
250111d7cb2SLandon J. Fuller 
251*89294a78SLandon J. Fuller 	free(cores, M_BHND);
252*89294a78SLandon J. Fuller 
253111d7cb2SLandon J. Fuller 	return (0);
254111d7cb2SLandon J. Fuller 
255111d7cb2SLandon J. Fuller cleanup:
256111d7cb2SLandon J. Fuller 	device_delete_children(dev);
257*89294a78SLandon J. Fuller 	bhndb_disable_pci_clocks(sc->dev);
258*89294a78SLandon J. Fuller 
259824b48efSLandon J. Fuller 	if (sc->intr.msi_count > 0)
260824b48efSLandon J. Fuller 		pci_release_msi(dev);
261824b48efSLandon J. Fuller 
262*89294a78SLandon J. Fuller 	if (cores != NULL)
263*89294a78SLandon J. Fuller 		free(cores, M_BHND);
264*89294a78SLandon J. Fuller 
265111d7cb2SLandon J. Fuller 	pci_disable_busmaster(sc->parent);
266111d7cb2SLandon J. Fuller 
267111d7cb2SLandon J. Fuller 	return (error);
268111d7cb2SLandon J. Fuller }
269111d7cb2SLandon J. Fuller 
270111d7cb2SLandon J. Fuller static int
271111d7cb2SLandon J. Fuller bhndb_pci_detach(device_t dev)
272111d7cb2SLandon J. Fuller {
273111d7cb2SLandon J. Fuller 	struct bhndb_pci_softc	*sc;
274111d7cb2SLandon J. Fuller 	int			 error;
275111d7cb2SLandon J. Fuller 
276111d7cb2SLandon J. Fuller 	sc = device_get_softc(dev);
277111d7cb2SLandon J. Fuller 
278111d7cb2SLandon J. Fuller 	/* Attempt to detach our children */
279111d7cb2SLandon J. Fuller 	if ((error = bus_generic_detach(dev)))
280111d7cb2SLandon J. Fuller 		return (error);
281111d7cb2SLandon J. Fuller 
282111d7cb2SLandon J. Fuller 	/* Perform generic bridge detach */
283111d7cb2SLandon J. Fuller 	if ((error = bhndb_generic_detach(dev)))
284111d7cb2SLandon J. Fuller 		return (error);
285111d7cb2SLandon J. Fuller 
286111d7cb2SLandon J. Fuller 	/* Disable clocks (if required by this hardware) */
287*89294a78SLandon J. Fuller 	if ((error = bhndb_disable_pci_clocks(sc->dev)))
288111d7cb2SLandon J. Fuller 		return (error);
289111d7cb2SLandon J. Fuller 
290824b48efSLandon J. Fuller 	/* Release MSI interrupts */
291824b48efSLandon J. Fuller 	if (sc->intr.msi_count > 0)
292824b48efSLandon J. Fuller 		pci_release_msi(dev);
293824b48efSLandon J. Fuller 
294111d7cb2SLandon J. Fuller 	/* Disable PCI bus mastering */
295111d7cb2SLandon J. Fuller 	pci_disable_busmaster(sc->parent);
296111d7cb2SLandon J. Fuller 
297111d7cb2SLandon J. Fuller 	return (0);
298111d7cb2SLandon J. Fuller }
299111d7cb2SLandon J. Fuller 
300*89294a78SLandon J. Fuller /**
301*89294a78SLandon J. Fuller  * Use the generic PCI bridge hardware configuration to enumerate the bridged
302*89294a78SLandon J. Fuller  * bhnd(4) bus' core table.
303*89294a78SLandon J. Fuller  *
304*89294a78SLandon J. Fuller  * @note This function may be safely called prior to device attach, (e.g.
305*89294a78SLandon J. Fuller  * from DEVICE_PROBE).
306*89294a78SLandon J. Fuller  * @note This function requires exclusive ownership over allocating and
307*89294a78SLandon J. Fuller  * configuring host bridge resources, and should only be called prior to
308*89294a78SLandon J. Fuller  * completion of device attach and full configuration of the bridge.
309*89294a78SLandon J. Fuller  *
310*89294a78SLandon J. Fuller  * @param	dev		The bhndb_pci bridge device.
311*89294a78SLandon J. Fuller  * @param[out]	chipid		On success, the parsed chip identification.
312*89294a78SLandon J. Fuller  * @param[out]	cores		On success, the enumerated core table. The
313*89294a78SLandon J. Fuller  *				caller is responsible for freeing this table via
314*89294a78SLandon J. Fuller  *				bhndb_pci_free_core_table().
315*89294a78SLandon J. Fuller  * @param[out]	ncores		On success, the number of cores found in
316*89294a78SLandon J. Fuller  *				@p cores.
317*89294a78SLandon J. Fuller  * @param[out]	eromcls		On success, a pointer to the erom class used to
318*89294a78SLandon J. Fuller  *				parse the device enumeration table. This
319*89294a78SLandon J. Fuller  *				argument may be NULL if the class is not
320*89294a78SLandon J. Fuller  *				desired.
321*89294a78SLandon J. Fuller  *
322*89294a78SLandon J. Fuller  * @retval 0		success
323*89294a78SLandon J. Fuller  * @retval non-zero	if enumerating the bridged bhnd(4) bus fails, a regular
324*89294a78SLandon J. Fuller  * 			unix error code will be returned.
325*89294a78SLandon J. Fuller  */
326*89294a78SLandon J. Fuller static int
327*89294a78SLandon J. Fuller bhndb_pci_read_core_table(device_t dev, struct bhnd_chipid *chipid,
328*89294a78SLandon J. Fuller     struct bhnd_core_info **cores, u_int *ncores,
329*89294a78SLandon J. Fuller     bhnd_erom_class_t **eromcls)
330*89294a78SLandon J. Fuller {
331*89294a78SLandon J. Fuller 	const struct bhndb_hwcfg	*cfg;
332*89294a78SLandon J. Fuller 	struct bhndb_host_resources	*hr;
333*89294a78SLandon J. Fuller 	struct bhndb_pci_eio		 pio;
334*89294a78SLandon J. Fuller 	struct bhnd_core_info		*erom_cores;
335*89294a78SLandon J. Fuller 	const struct bhnd_chipid	*hint;
336*89294a78SLandon J. Fuller 	struct bhnd_chipid		 cid;
337*89294a78SLandon J. Fuller 	bhnd_erom_class_t		*erom_class;
338*89294a78SLandon J. Fuller 	bhnd_erom_t			*erom;
339*89294a78SLandon J. Fuller 	device_t			 parent_dev;
340*89294a78SLandon J. Fuller 	u_int				 erom_ncores;
341*89294a78SLandon J. Fuller 	int				 error;
342*89294a78SLandon J. Fuller 
343*89294a78SLandon J. Fuller 	parent_dev = device_get_parent(dev);
344*89294a78SLandon J. Fuller 	erom = NULL;
345*89294a78SLandon J. Fuller 	erom_cores = NULL;
346*89294a78SLandon J. Fuller 
347*89294a78SLandon J. Fuller 	/* Fetch our chipid hint (if any) and generic hardware configuration */
348*89294a78SLandon J. Fuller 	cfg = BHNDB_BUS_GET_GENERIC_HWCFG(parent_dev, dev);
349*89294a78SLandon J. Fuller 	hint = BHNDB_BUS_GET_CHIPID(parent_dev, dev);
350*89294a78SLandon J. Fuller 
351*89294a78SLandon J. Fuller 	/* Allocate our host resources */
352*89294a78SLandon J. Fuller 	if ((error = bhndb_alloc_host_resources(parent_dev, cfg, &hr)))
353*89294a78SLandon J. Fuller 		return (error);
354*89294a78SLandon J. Fuller 
355*89294a78SLandon J. Fuller 	/* Initialize our erom I/O state */
356*89294a78SLandon J. Fuller 	if ((error = bhndb_pci_eio_init(&pio, dev, parent_dev, hr)))
357*89294a78SLandon J. Fuller 		goto failed;
358*89294a78SLandon J. Fuller 
359*89294a78SLandon J. Fuller 	/* Map the first bus core from our bridged bhnd(4) bus */
360*89294a78SLandon J. Fuller 	error = bhndb_pci_eio_map(&pio.eio, BHND_DEFAULT_CHIPC_ADDR,
361*89294a78SLandon J. Fuller 	    BHND_DEFAULT_CORE_SIZE);
362*89294a78SLandon J. Fuller 	if (error)
363*89294a78SLandon J. Fuller 		goto failed;
364*89294a78SLandon J. Fuller 
365*89294a78SLandon J. Fuller 	/* Probe for a usable EROM class, and read the chip identifier */
366*89294a78SLandon J. Fuller 	erom_class = bhnd_erom_probe_driver_classes(device_get_devclass(dev),
367*89294a78SLandon J. Fuller 	    &pio.eio, hint, &cid);
368*89294a78SLandon J. Fuller 	if (erom_class == NULL) {
369*89294a78SLandon J. Fuller 		device_printf(dev, "device enumeration unsupported; no "
370*89294a78SLandon J. Fuller 		    "compatible driver found\n");
371*89294a78SLandon J. Fuller 
372*89294a78SLandon J. Fuller 		error = ENXIO;
373*89294a78SLandon J. Fuller 		goto failed;
374*89294a78SLandon J. Fuller 	}
375*89294a78SLandon J. Fuller 
376*89294a78SLandon J. Fuller 	/* Allocate EROM parser */
377*89294a78SLandon J. Fuller 	if ((erom = bhnd_erom_alloc(erom_class, &cid, &pio.eio)) == NULL) {
378*89294a78SLandon J. Fuller 		device_printf(dev, "failed to allocate device enumeration "
379*89294a78SLandon J. Fuller 		    "table parser\n");
380*89294a78SLandon J. Fuller 		error = ENXIO;
381*89294a78SLandon J. Fuller 		goto failed;
382*89294a78SLandon J. Fuller 	}
383*89294a78SLandon J. Fuller 
384*89294a78SLandon J. Fuller 	/* Read the full core table */
385*89294a78SLandon J. Fuller 	error = bhnd_erom_get_core_table(erom, &erom_cores, &erom_ncores);
386*89294a78SLandon J. Fuller 	if (error) {
387*89294a78SLandon J. Fuller 		device_printf(dev, "error fetching core table: %d\n", error);
388*89294a78SLandon J. Fuller 		goto failed;
389*89294a78SLandon J. Fuller 	}
390*89294a78SLandon J. Fuller 
391*89294a78SLandon J. Fuller 	/* Provide the results to our caller */
392*89294a78SLandon J. Fuller 	*cores = malloc(sizeof(erom_cores[0]) * erom_ncores, M_BHND, M_WAITOK);
393*89294a78SLandon J. Fuller 	memcpy(*cores, erom_cores, sizeof(erom_cores[0]) * erom_ncores);
394*89294a78SLandon J. Fuller 	*ncores = erom_ncores;
395*89294a78SLandon J. Fuller 
396*89294a78SLandon J. Fuller 	*chipid = cid;
397*89294a78SLandon J. Fuller 	if (eromcls != NULL)
398*89294a78SLandon J. Fuller 		*eromcls = erom_class;
399*89294a78SLandon J. Fuller 
400*89294a78SLandon J. Fuller 	/* Clean up */
401*89294a78SLandon J. Fuller 	bhnd_erom_free_core_table(erom, erom_cores);
402*89294a78SLandon J. Fuller 	bhnd_erom_free(erom);
403*89294a78SLandon J. Fuller 	bhndb_release_host_resources(hr);
404*89294a78SLandon J. Fuller 
405*89294a78SLandon J. Fuller 	return (0);
406*89294a78SLandon J. Fuller 
407*89294a78SLandon J. Fuller failed:
408*89294a78SLandon J. Fuller 	if (erom_cores != NULL)
409*89294a78SLandon J. Fuller 		bhnd_erom_free_core_table(erom, erom_cores);
410*89294a78SLandon J. Fuller 
411*89294a78SLandon J. Fuller 	if (erom != NULL)
412*89294a78SLandon J. Fuller 		bhnd_erom_free(erom);
413*89294a78SLandon J. Fuller 
414*89294a78SLandon J. Fuller 	bhndb_release_host_resources(hr);
415*89294a78SLandon J. Fuller 	return (error);
416*89294a78SLandon J. Fuller }
417*89294a78SLandon J. Fuller 
418111d7cb2SLandon J. Fuller static int
419111d7cb2SLandon J. Fuller bhndb_pci_add_children(struct bhndb_pci_softc *sc)
420111d7cb2SLandon J. Fuller {
421111d7cb2SLandon J. Fuller 	bus_size_t		 nv_sz;
422111d7cb2SLandon J. Fuller 	int			 error;
423111d7cb2SLandon J. Fuller 
424111d7cb2SLandon J. Fuller 	/**
425111d7cb2SLandon J. Fuller 	 * If SPROM is mapped directly into BAR0, add child NVRAM
426111d7cb2SLandon J. Fuller 	 * device.
427111d7cb2SLandon J. Fuller 	 */
428e83ce340SAdrian Chadd 	nv_sz = bhndb_pci_sprom_size(sc);
429e83ce340SAdrian Chadd 	if (nv_sz > 0) {
430e83ce340SAdrian Chadd 		struct bhndb_devinfo	*dinfo;
431111d7cb2SLandon J. Fuller 		device_t		 child;
432e83ce340SAdrian Chadd 
433e83ce340SAdrian Chadd 		if (bootverbose) {
434111d7cb2SLandon J. Fuller 			device_printf(sc->dev, "found SPROM (%ju bytes)\n",
435111d7cb2SLandon J. Fuller 			    (uintmax_t)nv_sz);
436e83ce340SAdrian Chadd 		}
437e83ce340SAdrian Chadd 
438111d7cb2SLandon J. Fuller 		/* Add sprom device, ordered early enough to be available
439111d7cb2SLandon J. Fuller 		 * before the bridged bhnd(4) bus is attached. */
440111d7cb2SLandon J. Fuller 		child = BUS_ADD_CHILD(sc->dev,
441111d7cb2SLandon J. Fuller 		    BHND_PROBE_ROOT + BHND_PROBE_ORDER_EARLY, "bhnd_nvram", -1);
442111d7cb2SLandon J. Fuller 		if (child == NULL) {
443111d7cb2SLandon J. Fuller 			device_printf(sc->dev, "failed to add sprom device\n");
444e83ce340SAdrian Chadd 			return (ENXIO);
445e83ce340SAdrian Chadd 		}
446e83ce340SAdrian Chadd 
447e83ce340SAdrian Chadd 		/* Initialize device address space and resource covering the
448e83ce340SAdrian Chadd 		 * BAR0 SPROM shadow. */
449111d7cb2SLandon J. Fuller 		dinfo = device_get_ivars(child);
450e83ce340SAdrian Chadd 		dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE;
451111d7cb2SLandon J. Fuller 
452111d7cb2SLandon J. Fuller 		error = bus_set_resource(child, SYS_RES_MEMORY, 0,
453e83ce340SAdrian Chadd 		    bhndb_pci_sprom_addr(sc), nv_sz);
454e83ce340SAdrian Chadd 		if (error) {
455111d7cb2SLandon J. Fuller 			device_printf(sc->dev,
456e83ce340SAdrian Chadd 			    "failed to register sprom resources\n");
457e83ce340SAdrian Chadd 			return (error);
458e83ce340SAdrian Chadd 		}
459e83ce340SAdrian Chadd 	}
460e83ce340SAdrian Chadd 
4614ad7e9b0SAdrian Chadd 	return (0);
4624ad7e9b0SAdrian Chadd }
4634ad7e9b0SAdrian Chadd 
464e83ce340SAdrian Chadd static const struct bhndb_regwin *
465e83ce340SAdrian Chadd bhndb_pci_sprom_regwin(struct bhndb_pci_softc *sc)
466e83ce340SAdrian Chadd {
467e83ce340SAdrian Chadd 	struct bhndb_resources		*bres;
468e83ce340SAdrian Chadd 	const struct bhndb_hwcfg	*cfg;
469e83ce340SAdrian Chadd 	const struct bhndb_regwin	*sprom_win;
470e83ce340SAdrian Chadd 
471e83ce340SAdrian Chadd 	bres = sc->bhndb.bus_res;
472e83ce340SAdrian Chadd 	cfg = bres->cfg;
473e83ce340SAdrian Chadd 
474e83ce340SAdrian Chadd 	sprom_win = bhndb_regwin_find_type(cfg->register_windows,
475e83ce340SAdrian Chadd 	    BHNDB_REGWIN_T_SPROM, BHNDB_PCI_V0_BAR0_SPROM_SIZE);
476e83ce340SAdrian Chadd 
477e83ce340SAdrian Chadd 	return (sprom_win);
478e83ce340SAdrian Chadd }
479e83ce340SAdrian Chadd 
480e83ce340SAdrian Chadd static bus_addr_t
481e83ce340SAdrian Chadd bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc)
482e83ce340SAdrian Chadd {
483e83ce340SAdrian Chadd 	const struct bhndb_regwin	*sprom_win;
484e83ce340SAdrian Chadd 	struct resource			*r;
485e83ce340SAdrian Chadd 
486e83ce340SAdrian Chadd 	/* Fetch the SPROM register window */
487e83ce340SAdrian Chadd 	sprom_win = bhndb_pci_sprom_regwin(sc);
488e83ce340SAdrian Chadd 	KASSERT(sprom_win != NULL, ("requested sprom address on PCI_V2+"));
489e83ce340SAdrian Chadd 
490e83ce340SAdrian Chadd 	/* Fetch the associated resource */
491*89294a78SLandon J. Fuller 	r = bhndb_host_resource_for_regwin(sc->bhndb.bus_res->res, sprom_win);
492e83ce340SAdrian Chadd 	KASSERT(r != NULL, ("missing resource for sprom window\n"));
493e83ce340SAdrian Chadd 
494e83ce340SAdrian Chadd 	return (rman_get_start(r) + sprom_win->win_offset);
495e83ce340SAdrian Chadd }
496e83ce340SAdrian Chadd 
497e83ce340SAdrian Chadd static bus_size_t
498e83ce340SAdrian Chadd bhndb_pci_sprom_size(struct bhndb_pci_softc *sc)
499e83ce340SAdrian Chadd {
500e83ce340SAdrian Chadd 	const struct bhndb_regwin	*sprom_win;
501e83ce340SAdrian Chadd 	uint32_t			 sctl;
502e83ce340SAdrian Chadd 	bus_size_t			 sprom_sz;
503e83ce340SAdrian Chadd 
504e83ce340SAdrian Chadd 	sprom_win = bhndb_pci_sprom_regwin(sc);
505e83ce340SAdrian Chadd 
506e83ce340SAdrian Chadd 	/* PCI_V2 and later devices map SPROM/OTP via ChipCommon */
507e83ce340SAdrian Chadd 	if (sprom_win == NULL)
508e83ce340SAdrian Chadd 		return (0);
509e83ce340SAdrian Chadd 
510e83ce340SAdrian Chadd 	/* Determine SPROM size */
511e83ce340SAdrian Chadd 	sctl = pci_read_config(sc->parent, BHNDB_PCI_SPROM_CONTROL, 4);
512e83ce340SAdrian Chadd 	if (sctl & BHNDB_PCI_SPROM_BLANK)
513e83ce340SAdrian Chadd 		return (0);
514e83ce340SAdrian Chadd 
515e83ce340SAdrian Chadd 	switch (sctl & BHNDB_PCI_SPROM_SZ_MASK) {
516e83ce340SAdrian Chadd 	case BHNDB_PCI_SPROM_SZ_1KB:
517e83ce340SAdrian Chadd 		sprom_sz = (1 * 1024);
518e83ce340SAdrian Chadd 		break;
519e83ce340SAdrian Chadd 
520e83ce340SAdrian Chadd 	case BHNDB_PCI_SPROM_SZ_4KB:
521e83ce340SAdrian Chadd 		sprom_sz = (4 * 1024);
522e83ce340SAdrian Chadd 		break;
523e83ce340SAdrian Chadd 
524e83ce340SAdrian Chadd 	case BHNDB_PCI_SPROM_SZ_16KB:
525e83ce340SAdrian Chadd 		sprom_sz = (16 * 1024);
526e83ce340SAdrian Chadd 		break;
527e83ce340SAdrian Chadd 
528e83ce340SAdrian Chadd 	case BHNDB_PCI_SPROM_SZ_RESERVED:
529e83ce340SAdrian Chadd 	default:
530e83ce340SAdrian Chadd 		device_printf(sc->dev, "invalid PCI sprom size 0x%x\n", sctl);
531e83ce340SAdrian Chadd 		return (0);
532e83ce340SAdrian Chadd 	}
533e83ce340SAdrian Chadd 
534e83ce340SAdrian Chadd 	if (sprom_sz > sprom_win->win_size) {
535e83ce340SAdrian Chadd 		device_printf(sc->dev,
536e83ce340SAdrian Chadd 		    "PCI sprom size (0x%x) overruns defined register window\n",
537e83ce340SAdrian Chadd 		    sctl);
538e83ce340SAdrian Chadd 		return (0);
539e83ce340SAdrian Chadd 	}
540e83ce340SAdrian Chadd 
541e83ce340SAdrian Chadd 	return (sprom_sz);
542e83ce340SAdrian Chadd }
543e83ce340SAdrian Chadd 
5444ad7e9b0SAdrian Chadd /*
5454ad7e9b0SAdrian Chadd  * On devices without a SROM, the PCI(e) cores will be initialized with
546bb64eeccSAdrian Chadd  * their Power-on-Reset defaults; this can leave two of the BAR0 PCI windows
547bb64eeccSAdrian Chadd  * mapped to the wrong core.
5484ad7e9b0SAdrian Chadd  *
549bb64eeccSAdrian Chadd  * This function updates the SROM shadow to point the BAR0 windows at the
5504ad7e9b0SAdrian Chadd  * current PCI core.
5514ad7e9b0SAdrian Chadd  *
552bb64eeccSAdrian Chadd  * Applies to all PCI/PCIe revisions.
5534ad7e9b0SAdrian Chadd  */
5544ad7e9b0SAdrian Chadd static void
5554ad7e9b0SAdrian Chadd bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc)
5564ad7e9b0SAdrian Chadd {
557bb64eeccSAdrian Chadd 	struct bhndb_resources		*bres;
558bb64eeccSAdrian Chadd 	const struct bhndb_hwcfg	*cfg;
559bb64eeccSAdrian Chadd 	const struct bhndb_regwin	*win;
560111d7cb2SLandon J. Fuller 	struct bhnd_core_info		 hostb_core;
561bb64eeccSAdrian Chadd 	struct resource			*core_regs;
562bb64eeccSAdrian Chadd 	bus_size_t			 srom_offset;
563bb64eeccSAdrian Chadd 	u_int				 pci_cidx, sprom_cidx;
5644ad7e9b0SAdrian Chadd 	uint16_t			 val;
565111d7cb2SLandon J. Fuller 	int				 error;
5664ad7e9b0SAdrian Chadd 
567bb64eeccSAdrian Chadd 	bres = sc->bhndb.bus_res;
568bb64eeccSAdrian Chadd 	cfg = bres->cfg;
569bb64eeccSAdrian Chadd 
570111d7cb2SLandon J. Fuller 	/* Find our hostb core */
571111d7cb2SLandon J. Fuller 	error = BHNDB_GET_HOSTB_CORE(sc->dev, sc->bhndb.bus_dev, &hostb_core);
572111d7cb2SLandon J. Fuller 	if (error) {
573111d7cb2SLandon J. Fuller 		device_printf(sc->dev, "no host bridge device found\n");
574111d7cb2SLandon J. Fuller 		return;
575111d7cb2SLandon J. Fuller 	}
576111d7cb2SLandon J. Fuller 
577111d7cb2SLandon J. Fuller 	if (hostb_core.vendor != BHND_MFGID_BCM)
578bb64eeccSAdrian Chadd 		return;
579bb64eeccSAdrian Chadd 
580111d7cb2SLandon J. Fuller 	switch (hostb_core.device) {
581bb64eeccSAdrian Chadd 	case BHND_COREID_PCI:
582bb64eeccSAdrian Chadd 		srom_offset = BHND_PCI_SRSH_PI_OFFSET;
583bb64eeccSAdrian Chadd 		break;
584bb64eeccSAdrian Chadd 	case BHND_COREID_PCIE:
585bb64eeccSAdrian Chadd 		srom_offset = BHND_PCIE_SRSH_PI_OFFSET;
586bb64eeccSAdrian Chadd 		break;
587bb64eeccSAdrian Chadd 	default:
588bb64eeccSAdrian Chadd 		device_printf(sc->dev, "unsupported PCI host bridge device\n");
589bb64eeccSAdrian Chadd 		return;
590bb64eeccSAdrian Chadd 	}
591bb64eeccSAdrian Chadd 
592bb64eeccSAdrian Chadd 	/* Locate the static register window mapping the PCI core */
593bb64eeccSAdrian Chadd 	win = bhndb_regwin_find_core(cfg->register_windows, sc->pci_devclass,
594bb64eeccSAdrian Chadd 	    0, BHND_PORT_DEVICE, 0, 0);
595bb64eeccSAdrian Chadd 	if (win == NULL) {
596bb64eeccSAdrian Chadd 		device_printf(sc->dev, "missing PCI core register window\n");
597bb64eeccSAdrian Chadd 		return;
598bb64eeccSAdrian Chadd 	}
599bb64eeccSAdrian Chadd 
600bb64eeccSAdrian Chadd 	/* Fetch the resource containing the register window */
601*89294a78SLandon J. Fuller 	core_regs = bhndb_host_resource_for_regwin(bres->res, win);
602bb64eeccSAdrian Chadd 	if (core_regs == NULL) {
603bb64eeccSAdrian Chadd 		device_printf(sc->dev, "missing PCI core register resource\n");
604bb64eeccSAdrian Chadd 		return;
605bb64eeccSAdrian Chadd 	}
606bb64eeccSAdrian Chadd 
6074ad7e9b0SAdrian Chadd 	/* Fetch the SPROM's configured core index */
608bb64eeccSAdrian Chadd 	val = bus_read_2(core_regs, win->win_offset + srom_offset);
609bb64eeccSAdrian Chadd 	sprom_cidx = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT;
6104ad7e9b0SAdrian Chadd 
6114ad7e9b0SAdrian Chadd 	/* If it doesn't match host bridge's core index, update the index
6124ad7e9b0SAdrian Chadd 	 * value */
613111d7cb2SLandon J. Fuller 	pci_cidx = hostb_core.core_idx;
614bb64eeccSAdrian Chadd 	if (sprom_cidx != pci_cidx) {
615bb64eeccSAdrian Chadd 		val &= ~BHND_PCI_SRSH_PI_MASK;
616bb64eeccSAdrian Chadd 		val |= (pci_cidx << BHND_PCI_SRSH_PI_SHIFT);
617bb64eeccSAdrian Chadd 		bus_write_2(core_regs,
618bb64eeccSAdrian Chadd 		    win->win_offset + srom_offset, val);
6194ad7e9b0SAdrian Chadd 	}
6204ad7e9b0SAdrian Chadd }
6214ad7e9b0SAdrian Chadd 
6224ad7e9b0SAdrian Chadd static int
6234ad7e9b0SAdrian Chadd bhndb_pci_resume(device_t dev)
6244ad7e9b0SAdrian Chadd {
6254ad7e9b0SAdrian Chadd 	struct bhndb_pci_softc	*sc;
6264ad7e9b0SAdrian Chadd 	int			 error;
6274ad7e9b0SAdrian Chadd 
6284ad7e9b0SAdrian Chadd 	sc = device_get_softc(dev);
6294ad7e9b0SAdrian Chadd 
630bb64eeccSAdrian Chadd 	/* Enable clocks (if supported by this hardware) */
631*89294a78SLandon J. Fuller 	if ((error = bhndb_enable_pci_clocks(sc->dev)))
6324ad7e9b0SAdrian Chadd 		return (error);
6334ad7e9b0SAdrian Chadd 
634bb64eeccSAdrian Chadd 	/* Perform resume */
635bb64eeccSAdrian Chadd 	return (bhndb_generic_resume(dev));
636bb64eeccSAdrian Chadd }
637bb64eeccSAdrian Chadd 
638bb64eeccSAdrian Chadd static int
639bb64eeccSAdrian Chadd bhndb_pci_suspend(device_t dev)
640bb64eeccSAdrian Chadd {
641bb64eeccSAdrian Chadd 	struct bhndb_pci_softc	*sc;
642bb64eeccSAdrian Chadd 	int			 error;
643bb64eeccSAdrian Chadd 
644bb64eeccSAdrian Chadd 	sc = device_get_softc(dev);
645bb64eeccSAdrian Chadd 
646bb64eeccSAdrian Chadd 	/* Disable clocks (if supported by this hardware) */
647*89294a78SLandon J. Fuller 	if ((error = bhndb_disable_pci_clocks(sc->dev)))
6484ad7e9b0SAdrian Chadd 		return (error);
6494ad7e9b0SAdrian Chadd 
650bb64eeccSAdrian Chadd 	/* Perform suspend */
651bb64eeccSAdrian Chadd 	return (bhndb_generic_suspend(dev));
652bb64eeccSAdrian Chadd }
653bb64eeccSAdrian Chadd 
654bb64eeccSAdrian Chadd static int
6554ad7e9b0SAdrian Chadd bhndb_pci_set_window_addr(device_t dev, const struct bhndb_regwin *rw,
6564ad7e9b0SAdrian Chadd     bhnd_addr_t addr)
6574ad7e9b0SAdrian Chadd {
6584ad7e9b0SAdrian Chadd 	struct bhndb_pci_softc *sc = device_get_softc(dev);
659*89294a78SLandon J. Fuller 	return (sc->set_regwin(sc->dev, sc->parent, rw, addr));
6604ad7e9b0SAdrian Chadd }
6614ad7e9b0SAdrian Chadd 
6624ad7e9b0SAdrian Chadd /**
6634ad7e9b0SAdrian Chadd  * A siba(4) and bcma(4)-compatible bhndb_set_window_addr implementation.
6644ad7e9b0SAdrian Chadd  *
6654ad7e9b0SAdrian Chadd  * On siba(4) devices, it's possible that writing a PCI window register may
6664ad7e9b0SAdrian Chadd  * not succeed; it's necessary to immediately read the configuration register
6674ad7e9b0SAdrian Chadd  * and retry if not set to the desired value.
6684ad7e9b0SAdrian Chadd  *
6694ad7e9b0SAdrian Chadd  * This is not necessary on bcma(4) devices, but other than the overhead of
6704ad7e9b0SAdrian Chadd  * validating the register, there's no harm in performing the verification.
6714ad7e9b0SAdrian Chadd  */
6724ad7e9b0SAdrian Chadd static int
673*89294a78SLandon J. Fuller bhndb_pci_compat_setregwin(device_t dev, device_t pci_dev,
6744ad7e9b0SAdrian Chadd     const struct bhndb_regwin *rw, bhnd_addr_t addr)
6754ad7e9b0SAdrian Chadd {
6764ad7e9b0SAdrian Chadd 	int		error;
677e83ce340SAdrian Chadd 	int		reg;
6784ad7e9b0SAdrian Chadd 
6794ad7e9b0SAdrian Chadd 	if (rw->win_type != BHNDB_REGWIN_T_DYN)
6804ad7e9b0SAdrian Chadd 		return (ENODEV);
6814ad7e9b0SAdrian Chadd 
682e83ce340SAdrian Chadd 	reg = rw->d.dyn.cfg_offset;
6834ad7e9b0SAdrian Chadd 	for (u_int i = 0; i < BHNDB_PCI_BARCTRL_WRITE_RETRY; i++) {
684*89294a78SLandon J. Fuller 		if ((error = bhndb_pci_fast_setregwin(dev, pci_dev, rw, addr)))
6854ad7e9b0SAdrian Chadd 			return (error);
6864ad7e9b0SAdrian Chadd 
687*89294a78SLandon J. Fuller 		if (pci_read_config(pci_dev, reg, 4) == addr)
6884ad7e9b0SAdrian Chadd 			return (0);
6894ad7e9b0SAdrian Chadd 
6904ad7e9b0SAdrian Chadd 		DELAY(10);
6914ad7e9b0SAdrian Chadd 	}
6924ad7e9b0SAdrian Chadd 
6934ad7e9b0SAdrian Chadd 	/* Unable to set window */
6944ad7e9b0SAdrian Chadd 	return (ENODEV);
6954ad7e9b0SAdrian Chadd }
6964ad7e9b0SAdrian Chadd 
6974ad7e9b0SAdrian Chadd /**
6984ad7e9b0SAdrian Chadd  * A bcma(4)-only bhndb_set_window_addr implementation.
6994ad7e9b0SAdrian Chadd  */
7004ad7e9b0SAdrian Chadd static int
701*89294a78SLandon J. Fuller bhndb_pci_fast_setregwin(device_t dev, device_t pci_dev,
7024ad7e9b0SAdrian Chadd     const struct bhndb_regwin *rw, bhnd_addr_t addr)
7034ad7e9b0SAdrian Chadd {
7044ad7e9b0SAdrian Chadd 	/* The PCI bridge core only supports 32-bit addressing, regardless
7054ad7e9b0SAdrian Chadd 	 * of the bus' support for 64-bit addressing */
7064ad7e9b0SAdrian Chadd 	if (addr > UINT32_MAX)
7074ad7e9b0SAdrian Chadd 		return (ERANGE);
7084ad7e9b0SAdrian Chadd 
7094ad7e9b0SAdrian Chadd 	switch (rw->win_type) {
7104ad7e9b0SAdrian Chadd 	case BHNDB_REGWIN_T_DYN:
7114ad7e9b0SAdrian Chadd 		/* Addresses must be page aligned */
7124ad7e9b0SAdrian Chadd 		if (addr % rw->win_size != 0)
7134ad7e9b0SAdrian Chadd 			return (EINVAL);
7144ad7e9b0SAdrian Chadd 
715*89294a78SLandon J. Fuller 		pci_write_config(pci_dev, rw->d.dyn.cfg_offset, addr, 4);
7164ad7e9b0SAdrian Chadd 		break;
7174ad7e9b0SAdrian Chadd 	default:
7184ad7e9b0SAdrian Chadd 		return (ENODEV);
7194ad7e9b0SAdrian Chadd 	}
7204ad7e9b0SAdrian Chadd 
7214ad7e9b0SAdrian Chadd 	return (0);
7224ad7e9b0SAdrian Chadd }
7234ad7e9b0SAdrian Chadd 
724d567592bSAdrian Chadd static int
725d567592bSAdrian Chadd bhndb_pci_populate_board_info(device_t dev, device_t child,
726d567592bSAdrian Chadd     struct bhnd_board_info *info)
727d567592bSAdrian Chadd {
728d567592bSAdrian Chadd 	struct bhndb_pci_softc	*sc;
729d567592bSAdrian Chadd 
730d567592bSAdrian Chadd 	sc = device_get_softc(dev);
731d567592bSAdrian Chadd 
7328ef24a0dSAdrian Chadd 	/*
7338ef24a0dSAdrian Chadd 	 * On a subset of Apple BCM4360 modules, always prefer the
7348ef24a0dSAdrian Chadd 	 * PCI subdevice to the SPROM-supplied boardtype.
7358ef24a0dSAdrian Chadd 	 *
7368ef24a0dSAdrian Chadd 	 * TODO:
7378ef24a0dSAdrian Chadd 	 *
7388ef24a0dSAdrian Chadd 	 * Broadcom's own drivers implement this override, and then later use
7398ef24a0dSAdrian Chadd 	 * the remapped BCM4360 board type to determine the required
7408ef24a0dSAdrian Chadd 	 * board-specific workarounds.
7418ef24a0dSAdrian Chadd 	 *
7428ef24a0dSAdrian Chadd 	 * Without access to this hardware, it's unclear why this mapping
7438ef24a0dSAdrian Chadd 	 * is done, and we must do the same. If we can survey the hardware
7448ef24a0dSAdrian Chadd 	 * in question, it may be possible to replace this behavior with
7458ef24a0dSAdrian Chadd 	 * explicit references to the SPROM-supplied boardtype(s) in our
7468ef24a0dSAdrian Chadd 	 * quirk definitions.
7478ef24a0dSAdrian Chadd 	 */
7488ef24a0dSAdrian Chadd 	if (pci_get_subvendor(sc->parent) == PCI_VENDOR_APPLE) {
7498ef24a0dSAdrian Chadd 		switch (info->board_type) {
7508ef24a0dSAdrian Chadd 		case BHND_BOARD_BCM94360X29C:
7518ef24a0dSAdrian Chadd 		case BHND_BOARD_BCM94360X29CP2:
7528ef24a0dSAdrian Chadd 		case BHND_BOARD_BCM94360X51:
7538ef24a0dSAdrian Chadd 		case BHND_BOARD_BCM94360X51P2:
7548ef24a0dSAdrian Chadd 			info->board_type = 0;	/* allow override below */
7558ef24a0dSAdrian Chadd 			break;
7568ef24a0dSAdrian Chadd 		default:
7578ef24a0dSAdrian Chadd 			break;
7588ef24a0dSAdrian Chadd 		}
7598ef24a0dSAdrian Chadd 	}
7608ef24a0dSAdrian Chadd 
761d567592bSAdrian Chadd 	/* If NVRAM did not supply vendor/type info, provide the PCI
762d567592bSAdrian Chadd 	 * subvendor/subdevice values. */
763d567592bSAdrian Chadd 	if (info->board_vendor == 0)
764d567592bSAdrian Chadd 		info->board_vendor = pci_get_subvendor(sc->parent);
765d567592bSAdrian Chadd 
766d567592bSAdrian Chadd 	if (info->board_type == 0)
767d567592bSAdrian Chadd 		info->board_type = pci_get_subdevice(sc->parent);
768d567592bSAdrian Chadd 
769d567592bSAdrian Chadd 	return (0);
770d567592bSAdrian Chadd }
771d567592bSAdrian Chadd 
7724ad7e9b0SAdrian Chadd /**
773*89294a78SLandon J. Fuller  * Return true if the bridge device @p bhndb is attached via PCIe,
774*89294a78SLandon J. Fuller  * false otherwise.
775*89294a78SLandon J. Fuller  *
776*89294a78SLandon J. Fuller  * @param dev The bhndb bridge device
777*89294a78SLandon J. Fuller  */
778*89294a78SLandon J. Fuller static bool
779*89294a78SLandon J. Fuller bhndb_is_pcie_attached(device_t dev)
780*89294a78SLandon J. Fuller {
781*89294a78SLandon J. Fuller 	int reg;
782*89294a78SLandon J. Fuller 
783*89294a78SLandon J. Fuller 	if (pci_find_cap(device_get_parent(dev), PCIY_EXPRESS, &reg) == 0)
784*89294a78SLandon J. Fuller 		return (true);
785*89294a78SLandon J. Fuller 
786*89294a78SLandon J. Fuller 	return (false);
787*89294a78SLandon J. Fuller }
788*89294a78SLandon J. Fuller 
789*89294a78SLandon J. Fuller /**
790bb64eeccSAdrian Chadd  * Enable externally managed clocks, if required.
7914ad7e9b0SAdrian Chadd  *
792bb64eeccSAdrian Chadd  * Some PCI chipsets (BCM4306, possibly others) chips do not support
793bb64eeccSAdrian Chadd  * the idle low-power clock. Clocking must be bootstrapped at
794bb64eeccSAdrian Chadd  * attach/resume by directly adjusting GPIO registers exposed in the
795bb64eeccSAdrian Chadd  * PCI config space, and correspondingly, explicitly shutdown at
796bb64eeccSAdrian Chadd  * detach/suspend.
7974ad7e9b0SAdrian Chadd  *
798*89294a78SLandon J. Fuller  * @note This function may be safely called prior to device attach, (e.g.
799*89294a78SLandon J. Fuller  * from DEVICE_PROBE).
800*89294a78SLandon J. Fuller  *
801*89294a78SLandon J. Fuller  * @param dev The bhndb bridge device
8024ad7e9b0SAdrian Chadd  */
8034ad7e9b0SAdrian Chadd static int
804*89294a78SLandon J. Fuller bhndb_enable_pci_clocks(device_t dev)
8054ad7e9b0SAdrian Chadd {
806*89294a78SLandon J. Fuller 	device_t		pci_dev;
8074ad7e9b0SAdrian Chadd 	uint32_t		gpio_in, gpio_out, gpio_en;
8084ad7e9b0SAdrian Chadd 	uint32_t		gpio_flags;
8094ad7e9b0SAdrian Chadd 	uint16_t		pci_status;
8104ad7e9b0SAdrian Chadd 
811*89294a78SLandon J. Fuller 	pci_dev = device_get_parent(dev);
812*89294a78SLandon J. Fuller 
813bb64eeccSAdrian Chadd 	/* Only supported and required on PCI devices */
814*89294a78SLandon J. Fuller 	if (!bhndb_is_pcie_attached(dev))
815bb64eeccSAdrian Chadd 		return (0);
8164ad7e9b0SAdrian Chadd 
8174ad7e9b0SAdrian Chadd 	/* Read state of XTAL pin */
818*89294a78SLandon J. Fuller 	gpio_in = pci_read_config(pci_dev, BHNDB_PCI_GPIO_IN, 4);
8194ad7e9b0SAdrian Chadd 	if (gpio_in & BHNDB_PCI_GPIO_XTAL_ON)
8204ad7e9b0SAdrian Chadd 		return (0); /* already enabled */
8214ad7e9b0SAdrian Chadd 
8224ad7e9b0SAdrian Chadd 	/* Fetch current config */
823*89294a78SLandon J. Fuller 	gpio_out = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUT, 4);
824*89294a78SLandon J. Fuller 	gpio_en = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, 4);
8254ad7e9b0SAdrian Chadd 
8264ad7e9b0SAdrian Chadd 	/* Set PLL_OFF/XTAL_ON pins to HIGH and enable both pins */
8274ad7e9b0SAdrian Chadd 	gpio_flags = (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON);
8284ad7e9b0SAdrian Chadd 	gpio_out |= gpio_flags;
8294ad7e9b0SAdrian Chadd 	gpio_en |= gpio_flags;
8304ad7e9b0SAdrian Chadd 
831*89294a78SLandon J. Fuller 	pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
832*89294a78SLandon J. Fuller 	pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4);
8334ad7e9b0SAdrian Chadd 	DELAY(1000);
8344ad7e9b0SAdrian Chadd 
8354ad7e9b0SAdrian Chadd 	/* Reset PLL_OFF */
8364ad7e9b0SAdrian Chadd 	gpio_out &= ~BHNDB_PCI_GPIO_PLL_OFF;
837*89294a78SLandon J. Fuller 	pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
8384ad7e9b0SAdrian Chadd 	DELAY(5000);
8394ad7e9b0SAdrian Chadd 
8404ad7e9b0SAdrian Chadd 	/* Clear any PCI 'sent target-abort' flag. */
841*89294a78SLandon J. Fuller 	pci_status = pci_read_config(pci_dev, PCIR_STATUS, 2);
8424ad7e9b0SAdrian Chadd 	pci_status &= ~PCIM_STATUS_STABORT;
843*89294a78SLandon J. Fuller 	pci_write_config(pci_dev, PCIR_STATUS, pci_status, 2);
8444ad7e9b0SAdrian Chadd 
8454ad7e9b0SAdrian Chadd 	return (0);
8464ad7e9b0SAdrian Chadd }
8474ad7e9b0SAdrian Chadd 
8484ad7e9b0SAdrian Chadd /**
849bb64eeccSAdrian Chadd  * Disable externally managed clocks, if required.
8504ad7e9b0SAdrian Chadd  *
851*89294a78SLandon J. Fuller  * This function may be safely called prior to device attach, (e.g.
852*89294a78SLandon J. Fuller  * from DEVICE_PROBE).
853*89294a78SLandon J. Fuller  *
854*89294a78SLandon J. Fuller  * @param dev The bhndb bridge device
8554ad7e9b0SAdrian Chadd  */
8564ad7e9b0SAdrian Chadd static int
857*89294a78SLandon J. Fuller bhndb_disable_pci_clocks(device_t dev)
8584ad7e9b0SAdrian Chadd {
859*89294a78SLandon J. Fuller 	device_t	pci_dev;
8604ad7e9b0SAdrian Chadd 	uint32_t	gpio_out, gpio_en;
8614ad7e9b0SAdrian Chadd 
862*89294a78SLandon J. Fuller 	pci_dev = device_get_parent(dev);
863*89294a78SLandon J. Fuller 
864bb64eeccSAdrian Chadd 	/* Only supported and required on PCI devices */
865*89294a78SLandon J. Fuller 	if (bhndb_is_pcie_attached(dev))
866bb64eeccSAdrian Chadd 		return (0);
8674ad7e9b0SAdrian Chadd 
8684ad7e9b0SAdrian Chadd 	/* Fetch current config */
869*89294a78SLandon J. Fuller 	gpio_out = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUT, 4);
870*89294a78SLandon J. Fuller 	gpio_en = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, 4);
8714ad7e9b0SAdrian Chadd 
8724ad7e9b0SAdrian Chadd 	/* Set PLL_OFF to HIGH, XTAL_ON to LOW. */
8734ad7e9b0SAdrian Chadd 	gpio_out &= ~BHNDB_PCI_GPIO_XTAL_ON;
8744ad7e9b0SAdrian Chadd 	gpio_out |= BHNDB_PCI_GPIO_PLL_OFF;
875*89294a78SLandon J. Fuller 	pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
8764ad7e9b0SAdrian Chadd 
8774ad7e9b0SAdrian Chadd 	/* Enable both output pins */
8784ad7e9b0SAdrian Chadd 	gpio_en |= (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON);
879*89294a78SLandon J. Fuller 	pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4);
8804ad7e9b0SAdrian Chadd 
8814ad7e9b0SAdrian Chadd 	return (0);
8824ad7e9b0SAdrian Chadd }
8834ad7e9b0SAdrian Chadd 
884f90f4b65SLandon J. Fuller static bhnd_clksrc
885f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_get_clksrc(device_t dev, device_t child,
886f90f4b65SLandon J. Fuller 	bhnd_clock clock)
887f90f4b65SLandon J. Fuller {
888f90f4b65SLandon J. Fuller 	struct bhndb_pci_softc	*sc;
889f90f4b65SLandon J. Fuller 	uint32_t		 gpio_out;
890f90f4b65SLandon J. Fuller 
891f90f4b65SLandon J. Fuller 	sc = device_get_softc(dev);
892f90f4b65SLandon J. Fuller 
893f90f4b65SLandon J. Fuller 	/* Only supported on PCI devices */
894*89294a78SLandon J. Fuller 	if (bhndb_is_pcie_attached(sc->dev))
895f90f4b65SLandon J. Fuller 		return (ENODEV);
896f90f4b65SLandon J. Fuller 
897f90f4b65SLandon J. Fuller 	/* Only ILP is supported */
898f90f4b65SLandon J. Fuller 	if (clock != BHND_CLOCK_ILP)
899f90f4b65SLandon J. Fuller 		return (ENXIO);
900f90f4b65SLandon J. Fuller 
901f90f4b65SLandon J. Fuller 	gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4);
902f90f4b65SLandon J. Fuller 	if (gpio_out & BHNDB_PCI_GPIO_SCS)
903f90f4b65SLandon J. Fuller 		return (BHND_CLKSRC_PCI);
904f90f4b65SLandon J. Fuller 	else
905f90f4b65SLandon J. Fuller 		return (BHND_CLKSRC_XTAL);
906f90f4b65SLandon J. Fuller }
907f90f4b65SLandon J. Fuller 
908f90f4b65SLandon J. Fuller static int
909f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_gate_clock(device_t dev, device_t child,
910f90f4b65SLandon J. Fuller 	bhnd_clock clock)
911f90f4b65SLandon J. Fuller {
912f90f4b65SLandon J. Fuller 	struct bhndb_pci_softc *sc = device_get_softc(dev);
913f90f4b65SLandon J. Fuller 
914f90f4b65SLandon J. Fuller 	/* Only supported on PCI devices */
915*89294a78SLandon J. Fuller 	if (bhndb_is_pcie_attached(sc->dev))
916f90f4b65SLandon J. Fuller 		return (ENODEV);
917f90f4b65SLandon J. Fuller 
918f90f4b65SLandon J. Fuller 	/* Only HT is supported */
919f90f4b65SLandon J. Fuller 	if (clock != BHND_CLOCK_HT)
920f90f4b65SLandon J. Fuller 		return (ENXIO);
921f90f4b65SLandon J. Fuller 
922*89294a78SLandon J. Fuller 	return (bhndb_disable_pci_clocks(sc->dev));
923f90f4b65SLandon J. Fuller }
924f90f4b65SLandon J. Fuller 
925f90f4b65SLandon J. Fuller static int
926f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_ungate_clock(device_t dev, device_t child,
927f90f4b65SLandon J. Fuller 	bhnd_clock clock)
928f90f4b65SLandon J. Fuller {
929f90f4b65SLandon J. Fuller 	struct bhndb_pci_softc *sc = device_get_softc(dev);
930f90f4b65SLandon J. Fuller 
931f90f4b65SLandon J. Fuller 	/* Only supported on PCI devices */
932*89294a78SLandon J. Fuller 	if (bhndb_is_pcie_attached(sc->dev))
933f90f4b65SLandon J. Fuller 		return (ENODEV);
934f90f4b65SLandon J. Fuller 
935f90f4b65SLandon J. Fuller 	/* Only HT is supported */
936f90f4b65SLandon J. Fuller 	if (clock != BHND_CLOCK_HT)
937f90f4b65SLandon J. Fuller 		return (ENXIO);
938f90f4b65SLandon J. Fuller 
939*89294a78SLandon J. Fuller 	return (bhndb_enable_pci_clocks(sc->dev));
940f90f4b65SLandon J. Fuller }
941f90f4b65SLandon J. Fuller 
942824b48efSLandon J. Fuller static int
943824b48efSLandon J. Fuller bhndb_pci_assign_intr(device_t dev, device_t child, int rid)
944824b48efSLandon J. Fuller {
945824b48efSLandon J. Fuller 	struct bhndb_pci_softc	*sc;
946824b48efSLandon J. Fuller 	rman_res_t		 start, count;
947824b48efSLandon J. Fuller 	int			 error;
948824b48efSLandon J. Fuller 
949824b48efSLandon J. Fuller 	sc = device_get_softc(dev);
950824b48efSLandon J. Fuller 
951824b48efSLandon J. Fuller 	/* Is the rid valid? */
952824b48efSLandon J. Fuller 	if (rid >= bhnd_get_intr_count(child))
953824b48efSLandon J. Fuller 		return (EINVAL);
954824b48efSLandon J. Fuller 
955824b48efSLandon J. Fuller 	/* Fetch our common PCI interrupt's start/count. */
956824b48efSLandon J. Fuller 	error = bus_get_resource(sc->parent, SYS_RES_IRQ, sc->intr.intr_rid,
957824b48efSLandon J. Fuller 	    &start, &count);
958824b48efSLandon J. Fuller 	if (error)
959824b48efSLandon J. Fuller 		return (error);
960824b48efSLandon J. Fuller 
961824b48efSLandon J. Fuller 	/* Add to child's resource list */
962824b48efSLandon J. Fuller         return (bus_set_resource(child, SYS_RES_IRQ, rid, start, count));
963824b48efSLandon J. Fuller }
964824b48efSLandon J. Fuller 
965*89294a78SLandon J. Fuller /**
966*89294a78SLandon J. Fuller  * Initialize a new bhndb PCI bridge EROM I/O instance. This EROM I/O
967*89294a78SLandon J. Fuller  * implementation supports mapping of the device enumeration table via the
968*89294a78SLandon J. Fuller  * @p hr host resources.
969*89294a78SLandon J. Fuller  *
970*89294a78SLandon J. Fuller  * @param pio		The instance to be initialized.
971*89294a78SLandon J. Fuller  * @param dev		The bridge device.
972*89294a78SLandon J. Fuller  * @param pci_dev	The bridge's parent PCI device.
973*89294a78SLandon J. Fuller  * @param hr		The host resources to be used to map the device
974*89294a78SLandon J. Fuller  *			enumeration table.
975*89294a78SLandon J. Fuller  */
976*89294a78SLandon J. Fuller static int
977*89294a78SLandon J. Fuller bhndb_pci_eio_init(struct bhndb_pci_eio *pio, device_t dev, device_t pci_dev,
978*89294a78SLandon J. Fuller     struct bhndb_host_resources *hr)
979*89294a78SLandon J. Fuller {
980*89294a78SLandon J. Fuller 	memset(&pio->eio, sizeof(pio->eio), 0);
981*89294a78SLandon J. Fuller 	pio->eio.map = bhndb_pci_eio_map;
982*89294a78SLandon J. Fuller 	pio->eio.read = bhndb_pci_eio_read;
983*89294a78SLandon J. Fuller 	pio->eio.fini = NULL;
984*89294a78SLandon J. Fuller 
985*89294a78SLandon J. Fuller 	pio->dev = dev;
986*89294a78SLandon J. Fuller 	pio->pci_dev = pci_dev;
987*89294a78SLandon J. Fuller 	pio->hr = hr;
988*89294a78SLandon J. Fuller 	pio->win = NULL;
989*89294a78SLandon J. Fuller 	pio->res = NULL;
990*89294a78SLandon J. Fuller 
991*89294a78SLandon J. Fuller 	return (0);
992*89294a78SLandon J. Fuller }
993*89294a78SLandon J. Fuller 
994*89294a78SLandon J. Fuller /**
995*89294a78SLandon J. Fuller  * Attempt to adjust the dynamic register window backing @p pio to permit
996*89294a78SLandon J. Fuller  * reading @p size bytes at @p addr.
997*89294a78SLandon J. Fuller  *
998*89294a78SLandon J. Fuller  * If @p addr or @p size fall outside the existing mapped range, or if
999*89294a78SLandon J. Fuller  * @p pio is not backed by a dynamic register window, ENXIO will be returned.
1000*89294a78SLandon J. Fuller  *
1001*89294a78SLandon J. Fuller  * @param pio	The bhndb PCI erom I/O state to be modified.
1002*89294a78SLandon J. Fuller  * @param addr	The address to be include
1003*89294a78SLandon J. Fuller  */
1004*89294a78SLandon J. Fuller static int
1005*89294a78SLandon J. Fuller bhndb_pci_eio_adjust_mapping(struct bhndb_pci_eio *pio, bhnd_addr_t addr,
1006*89294a78SLandon J. Fuller     bhnd_size_t size)
1007*89294a78SLandon J. Fuller {
1008*89294a78SLandon J. Fuller 	bhnd_addr_t	 target;
1009*89294a78SLandon J. Fuller 	bhnd_size_t	 offset;
1010*89294a78SLandon J. Fuller 	int		 error;
1011*89294a78SLandon J. Fuller 
1012*89294a78SLandon J. Fuller 
1013*89294a78SLandon J. Fuller 	KASSERT(pio->win != NULL, ("missing register window"));
1014*89294a78SLandon J. Fuller 	KASSERT(pio->res != NULL, ("missing regwin resource"));
1015*89294a78SLandon J. Fuller 	KASSERT(pio->win->win_type == BHNDB_REGWIN_T_DYN,
1016*89294a78SLandon J. Fuller 	    ("unexpected window type %d", pio->win->win_type));
1017*89294a78SLandon J. Fuller 
1018*89294a78SLandon J. Fuller 	/* The requested subrange must fall within the total mapped range */
1019*89294a78SLandon J. Fuller 	if (addr < pio->addr || (addr - pio->addr) > pio->size ||
1020*89294a78SLandon J. Fuller 	    size > pio->size || (addr - pio->addr) - pio->size < size)
1021*89294a78SLandon J. Fuller 	{
1022*89294a78SLandon J. Fuller 		return (ENXIO);
1023*89294a78SLandon J. Fuller 	}
1024*89294a78SLandon J. Fuller 
1025*89294a78SLandon J. Fuller 	/* Do we already have a useable mapping? */
1026*89294a78SLandon J. Fuller 	if (addr >= pio->res_target &&
1027*89294a78SLandon J. Fuller 	    addr <= pio->res_target + pio->win->win_size &&
1028*89294a78SLandon J. Fuller 	    (pio->res_target + pio->win->win_size) - addr >= size)
1029*89294a78SLandon J. Fuller 	{
1030*89294a78SLandon J. Fuller 		return (0);
1031*89294a78SLandon J. Fuller 	}
1032*89294a78SLandon J. Fuller 
1033*89294a78SLandon J. Fuller 	/* Page-align the target address */
1034*89294a78SLandon J. Fuller 	offset = addr % pio->win->win_size;
1035*89294a78SLandon J. Fuller 	target = addr - offset;
1036*89294a78SLandon J. Fuller 
1037*89294a78SLandon J. Fuller 	/* Configure the register window */
1038*89294a78SLandon J. Fuller 	error = bhndb_pci_compat_setregwin(pio->dev, pio->pci_dev, pio->win,
1039*89294a78SLandon J. Fuller 	    target);
1040*89294a78SLandon J. Fuller 	if (error) {
1041*89294a78SLandon J. Fuller 		device_printf(pio->dev, "failed to configure dynamic register "
1042*89294a78SLandon J. Fuller 		    "window: %d\n", error);
1043*89294a78SLandon J. Fuller 		return (error);
1044*89294a78SLandon J. Fuller 	}
1045*89294a78SLandon J. Fuller 
1046*89294a78SLandon J. Fuller 	pio->res_target = target;
1047*89294a78SLandon J. Fuller 	return (0);
1048*89294a78SLandon J. Fuller }
1049*89294a78SLandon J. Fuller 
1050*89294a78SLandon J. Fuller /* bhnd_erom_io_map() implementation */
1051*89294a78SLandon J. Fuller static int
1052*89294a78SLandon J. Fuller bhndb_pci_eio_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
1053*89294a78SLandon J. Fuller     bhnd_size_t size)
1054*89294a78SLandon J. Fuller {
1055*89294a78SLandon J. Fuller 	struct bhndb_pci_eio		*pio;
1056*89294a78SLandon J. Fuller 	const struct bhndb_regwin	*regwin;
1057*89294a78SLandon J. Fuller 	struct resource			*r;
1058*89294a78SLandon J. Fuller 	bhnd_addr_t			 target;
1059*89294a78SLandon J. Fuller 	bhnd_size_t			 offset;
1060*89294a78SLandon J. Fuller 	int				 error;
1061*89294a78SLandon J. Fuller 
1062*89294a78SLandon J. Fuller 	pio = (struct bhndb_pci_eio *)eio;
1063*89294a78SLandon J. Fuller 
1064*89294a78SLandon J. Fuller 	/* Locate a useable dynamic register window */
1065*89294a78SLandon J. Fuller 	regwin = bhndb_regwin_find_type(pio->hr->cfg->register_windows,
1066*89294a78SLandon J. Fuller 	    BHNDB_REGWIN_T_DYN, MIN(size, BHND_DEFAULT_CORE_SIZE));
1067*89294a78SLandon J. Fuller 	if (regwin == NULL) {
1068*89294a78SLandon J. Fuller 		device_printf(pio->dev, "unable to map %#jx+%#jx; no "
1069*89294a78SLandon J. Fuller 		    "usable dynamic register window found\n", addr, size);
1070*89294a78SLandon J. Fuller 		return (ENXIO);
1071*89294a78SLandon J. Fuller 	}
1072*89294a78SLandon J. Fuller 
1073*89294a78SLandon J. Fuller 	/* Locate the host resource mapping our register window */
1074*89294a78SLandon J. Fuller 	if ((r = bhndb_host_resource_for_regwin(pio->hr, regwin)) == NULL) {
1075*89294a78SLandon J. Fuller 		device_printf(pio->dev, "unable to map %#jx+%#jx; no "
1076*89294a78SLandon J. Fuller 		    "usable register resource found\n", addr, size);
1077*89294a78SLandon J. Fuller 		return (ENXIO);
1078*89294a78SLandon J. Fuller 	}
1079*89294a78SLandon J. Fuller 
1080*89294a78SLandon J. Fuller 	/* Page-align the target address */
1081*89294a78SLandon J. Fuller 	offset = addr % regwin->win_size;
1082*89294a78SLandon J. Fuller 	target = addr - offset;
1083*89294a78SLandon J. Fuller 
1084*89294a78SLandon J. Fuller 	/* Configure the register window */
1085*89294a78SLandon J. Fuller 	error = bhndb_pci_compat_setregwin(pio->dev, pio->pci_dev, regwin,
1086*89294a78SLandon J. Fuller 	    target);
1087*89294a78SLandon J. Fuller 	if (error) {
1088*89294a78SLandon J. Fuller 		device_printf(pio->dev, "failed to configure dynamic register "
1089*89294a78SLandon J. Fuller 		    "window: %d\n", error);
1090*89294a78SLandon J. Fuller 		return (error);
1091*89294a78SLandon J. Fuller 	}
1092*89294a78SLandon J. Fuller 
1093*89294a78SLandon J. Fuller 	/* Update our mapping state */
1094*89294a78SLandon J. Fuller 	pio->win = regwin;
1095*89294a78SLandon J. Fuller 	pio->res = r;
1096*89294a78SLandon J. Fuller 	pio->addr = addr;
1097*89294a78SLandon J. Fuller 	pio->size = size;
1098*89294a78SLandon J. Fuller 	pio->res_target = target;
1099*89294a78SLandon J. Fuller 
1100*89294a78SLandon J. Fuller 	return (0);
1101*89294a78SLandon J. Fuller }
1102*89294a78SLandon J. Fuller 
1103*89294a78SLandon J. Fuller /* bhnd_erom_io_read() implementation */
1104*89294a78SLandon J. Fuller static uint32_t
1105*89294a78SLandon J. Fuller bhndb_pci_eio_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
1106*89294a78SLandon J. Fuller {
1107*89294a78SLandon J. Fuller 	struct bhndb_pci_eio		*pio;
1108*89294a78SLandon J. Fuller 	bhnd_addr_t			 addr;
1109*89294a78SLandon J. Fuller 	bus_size_t			 res_offset;
1110*89294a78SLandon J. Fuller 	int				 error;
1111*89294a78SLandon J. Fuller 
1112*89294a78SLandon J. Fuller 	pio = (struct bhndb_pci_eio *)eio;
1113*89294a78SLandon J. Fuller 
1114*89294a78SLandon J. Fuller 	/* Calculate absolute address */
1115*89294a78SLandon J. Fuller 	if (BHND_SIZE_MAX - offset < pio->addr) {
1116*89294a78SLandon J. Fuller 		device_printf(pio->dev, "invalid offset %#jx+%#jx\n", pio->addr,
1117*89294a78SLandon J. Fuller 		    offset);
1118*89294a78SLandon J. Fuller 		return (UINT32_MAX);
1119*89294a78SLandon J. Fuller 	}
1120*89294a78SLandon J. Fuller 
1121*89294a78SLandon J. Fuller 	addr = pio->addr + offset;
1122*89294a78SLandon J. Fuller 
1123*89294a78SLandon J. Fuller 	/* Adjust the mapping for our read */
1124*89294a78SLandon J. Fuller 	if ((error = bhndb_pci_eio_adjust_mapping(pio, addr, width))) {
1125*89294a78SLandon J. Fuller 		device_printf(pio->dev, "failed to adjust register mapping: "
1126*89294a78SLandon J. Fuller 		    "%d\n", error);
1127*89294a78SLandon J. Fuller 		return (UINT32_MAX);
1128*89294a78SLandon J. Fuller 	}
1129*89294a78SLandon J. Fuller 
1130*89294a78SLandon J. Fuller 	KASSERT(pio->res_target <= addr, ("invalid mapping (%#jx vs. %#jx)",
1131*89294a78SLandon J. Fuller 	    pio->res_target, addr));
1132*89294a78SLandon J. Fuller 
1133*89294a78SLandon J. Fuller 	/* Determine the actual read offset within our register window
1134*89294a78SLandon J. Fuller 	 * resource */
1135*89294a78SLandon J. Fuller 	res_offset = (addr - pio->res_target) + pio->win->win_offset;
1136*89294a78SLandon J. Fuller 
1137*89294a78SLandon J. Fuller 	/* Perform our read */
1138*89294a78SLandon J. Fuller 	switch (width) {
1139*89294a78SLandon J. Fuller 	case 1:
1140*89294a78SLandon J. Fuller 		return (bus_read_1(pio->res, res_offset));
1141*89294a78SLandon J. Fuller 	case 2:
1142*89294a78SLandon J. Fuller 		return (bus_read_2(pio->res, res_offset));
1143*89294a78SLandon J. Fuller 	case 4:
1144*89294a78SLandon J. Fuller 		return (bus_read_4(pio->res, res_offset));
1145*89294a78SLandon J. Fuller 	default:
1146*89294a78SLandon J. Fuller 		panic("unsupported width: %u", width);
1147*89294a78SLandon J. Fuller 	}
1148*89294a78SLandon J. Fuller }
1149*89294a78SLandon J. Fuller 
11504ad7e9b0SAdrian Chadd static device_method_t bhndb_pci_methods[] = {
11514ad7e9b0SAdrian Chadd 	/* Device interface */
11524ad7e9b0SAdrian Chadd 	DEVMETHOD(device_probe,			bhndb_pci_probe),
11534ad7e9b0SAdrian Chadd 	DEVMETHOD(device_attach,		bhndb_pci_attach),
11544ad7e9b0SAdrian Chadd 	DEVMETHOD(device_resume,		bhndb_pci_resume),
1155bb64eeccSAdrian Chadd 	DEVMETHOD(device_suspend,		bhndb_pci_suspend),
1156bb64eeccSAdrian Chadd 	DEVMETHOD(device_detach,		bhndb_pci_detach),
11574ad7e9b0SAdrian Chadd 
1158f90f4b65SLandon J. Fuller 	/* BHND interface */
1159824b48efSLandon J. Fuller 	DEVMETHOD(bhnd_bus_assign_intr,		bhndb_pci_assign_intr),
1160824b48efSLandon J. Fuller 
1161f90f4b65SLandon J. Fuller 	DEVMETHOD(bhnd_bus_pwrctl_get_clksrc,	bhndb_pci_pwrctl_get_clksrc),
1162f90f4b65SLandon J. Fuller 	DEVMETHOD(bhnd_bus_pwrctl_gate_clock,	bhndb_pci_pwrctl_gate_clock),
1163f90f4b65SLandon J. Fuller 	DEVMETHOD(bhnd_bus_pwrctl_ungate_clock,	bhndb_pci_pwrctl_ungate_clock),
1164f90f4b65SLandon J. Fuller 
11654ad7e9b0SAdrian Chadd 	/* BHNDB interface */
11664ad7e9b0SAdrian Chadd 	DEVMETHOD(bhndb_set_window_addr,	bhndb_pci_set_window_addr),
1167d567592bSAdrian Chadd 	DEVMETHOD(bhndb_populate_board_info,	bhndb_pci_populate_board_info),
11684ad7e9b0SAdrian Chadd 
11694ad7e9b0SAdrian Chadd 	DEVMETHOD_END
11704ad7e9b0SAdrian Chadd };
11714ad7e9b0SAdrian Chadd 
11724ad7e9b0SAdrian Chadd DEFINE_CLASS_1(bhndb, bhndb_pci_driver, bhndb_pci_methods,
11734ad7e9b0SAdrian Chadd     sizeof(struct bhndb_pci_softc), bhndb_driver);
11744ad7e9b0SAdrian Chadd 
11754ad7e9b0SAdrian Chadd MODULE_VERSION(bhndb_pci, 1);
1176148ed571SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhnd_pci_hostb, 1, 1, 1);
11774ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb_pci, pci, 1, 1, 1);
11784ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhndb, 1, 1, 1);
1179148ed571SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhnd, 1, 1, 1);
1180