xref: /freebsd/sys/dev/bhnd/bhndb/bhndb_pci.c (revision caeff9a3c2626660d3e080d4d3b35bc53ec4417f)
14ad7e9b0SAdrian Chadd /*-
2bb64eeccSAdrian Chadd  * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
389294a78SLandon J. Fuller  * Copyright (c) 2017 The FreeBSD Foundation
44ad7e9b0SAdrian Chadd  * All rights reserved.
54ad7e9b0SAdrian Chadd  *
689294a78SLandon J. Fuller  * Portions of this software were developed by Landon Fuller
789294a78SLandon J. Fuller  * under sponsorship from the FreeBSD Foundation.
889294a78SLandon 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>
6289294a78SLandon J. Fuller #include <dev/bhnd/bhndreg.h>
6389294a78SLandon J. Fuller 
6489294a78SLandon J. Fuller #include <dev/bhnd/bhnd_erom.h>
6589294a78SLandon J. Fuller #include <dev/bhnd/bhnd_eromvar.h>
664ad7e9b0SAdrian Chadd 
67*caeff9a3SLandon J. Fuller #include <dev/bhnd/siba/sibareg.h>
68*caeff9a3SLandon J. Fuller 
694ad7e9b0SAdrian Chadd #include <dev/bhnd/cores/pci/bhnd_pcireg.h>
704ad7e9b0SAdrian Chadd 
714ad7e9b0SAdrian Chadd #include "bhndb_pcireg.h"
724ad7e9b0SAdrian Chadd #include "bhndb_pcivar.h"
734ad7e9b0SAdrian Chadd #include "bhndb_private.h"
744ad7e9b0SAdrian Chadd 
7589294a78SLandon J. Fuller struct bhndb_pci_eio;
7689294a78SLandon J. Fuller 
77*caeff9a3SLandon J. Fuller static int		bhndb_pci_alloc_msi(struct bhndb_pci_softc *sc,
78*caeff9a3SLandon J. Fuller 			    int *msi_count);
7989294a78SLandon J. Fuller static int		bhndb_pci_read_core_table(device_t dev,
8089294a78SLandon J. Fuller 			    struct bhnd_chipid *chipid,
8189294a78SLandon J. Fuller 			    struct bhnd_core_info **cores, u_int *ncores,
8289294a78SLandon J. Fuller 			    bhnd_erom_class_t **eromcls);
83111d7cb2SLandon J. Fuller static int		bhndb_pci_add_children(struct bhndb_pci_softc *sc);
84111d7cb2SLandon J. Fuller 
85*caeff9a3SLandon J. Fuller static bhnd_devclass_t	bhndb_expected_pci_devclass(device_t dev);
8689294a78SLandon J. Fuller static bool		bhndb_is_pcie_attached(device_t dev);
874ad7e9b0SAdrian Chadd 
8889294a78SLandon J. Fuller static int		bhndb_enable_pci_clocks(device_t dev);
8989294a78SLandon J. Fuller static int		bhndb_disable_pci_clocks(device_t dev);
9089294a78SLandon J. Fuller 
9189294a78SLandon J. Fuller static int		bhndb_pci_compat_setregwin(device_t dev,
9289294a78SLandon J. Fuller 			    device_t pci_dev, const struct bhndb_regwin *,
9389294a78SLandon J. Fuller 			    bhnd_addr_t);
9489294a78SLandon J. Fuller static int		bhndb_pci_fast_setregwin(device_t dev, device_t pci_dev,
954ad7e9b0SAdrian Chadd 			    const struct bhndb_regwin *, bhnd_addr_t);
964ad7e9b0SAdrian Chadd 
97*caeff9a3SLandon J. Fuller static void		bhndb_pci_write_core(struct bhndb_pci_softc *sc,
98*caeff9a3SLandon J. Fuller 			    bus_size_t offset, uint32_t value, u_int width);
99*caeff9a3SLandon J. Fuller static uint32_t		bhndb_pci_read_core(struct bhndb_pci_softc *sc,
100*caeff9a3SLandon J. Fuller 			    bus_size_t offset, u_int width);
101*caeff9a3SLandon J. Fuller 
102e83ce340SAdrian Chadd static void		bhndb_init_sromless_pci_config(
103e83ce340SAdrian Chadd 			    struct bhndb_pci_softc *sc);
104e83ce340SAdrian Chadd 
105e83ce340SAdrian Chadd static bus_addr_t	bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc);
106f9cf87a0SAdrian Chadd static bus_size_t	bhndb_pci_sprom_size(struct bhndb_pci_softc *sc);
1074ad7e9b0SAdrian Chadd 
10889294a78SLandon J. Fuller static int		bhndb_pci_eio_init(struct bhndb_pci_eio *pio,
10989294a78SLandon J. Fuller 			    device_t dev, device_t pci_dev,
11089294a78SLandon J. Fuller 			    struct bhndb_host_resources *hr);
11189294a78SLandon J. Fuller static int		bhndb_pci_eio_map(struct bhnd_erom_io *eio,
11289294a78SLandon J. Fuller 			    bhnd_addr_t addr, bhnd_size_t size);
11389294a78SLandon J. Fuller static uint32_t		bhndb_pci_eio_read(struct bhnd_erom_io *eio,
11489294a78SLandon J. Fuller 			    bhnd_size_t offset, u_int width);
11589294a78SLandon J. Fuller 
116824b48efSLandon J. Fuller #define	BHNDB_PCI_MSI_COUNT	1
117824b48efSLandon J. Fuller 
118*caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk	bhndb_pci_quirks[];
119*caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk	bhndb_pcie_quirks[];
120*caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk	bhndb_pcie2_quirks[];
121*caeff9a3SLandon J. Fuller 
122*caeff9a3SLandon J. Fuller static struct bhndb_pci_core bhndb_pci_cores[] = {
123*caeff9a3SLandon J. Fuller 	BHNDB_PCI_CORE(PCI,	BHND_PCI_SRSH_PI_OFFSET,	bhndb_pci_quirks),
124*caeff9a3SLandon J. Fuller 	BHNDB_PCI_CORE(PCIE,	BHND_PCIE_SRSH_PI_OFFSET,	bhndb_pcie_quirks),
125*caeff9a3SLandon J. Fuller 	BHNDB_PCI_CORE(PCIE2,	BHND_PCIE_SRSH_PI_OFFSET,	bhndb_pcie2_quirks),
126*caeff9a3SLandon J. Fuller 	BHNDB_PCI_CORE_END
127*caeff9a3SLandon J. Fuller };
128*caeff9a3SLandon J. Fuller 
129*caeff9a3SLandon J. Fuller /* bhndb_pci erom I/O instance state */
13089294a78SLandon J. Fuller struct bhndb_pci_eio {
13189294a78SLandon J. Fuller 	struct bhnd_erom_io		 eio;
13289294a78SLandon J. Fuller 	device_t			 dev;		/**< bridge device */
13389294a78SLandon J. Fuller 	device_t			 pci_dev;	/**< parent PCI device */
13489294a78SLandon J. Fuller 	struct bhndb_host_resources	*hr;		/**< borrowed reference to host resources */
13589294a78SLandon J. Fuller 	const struct bhndb_regwin	*win;		/**< mapped register window, or NULL */
13689294a78SLandon J. Fuller 	struct resource			*res;		/**< resource containing the register window, or NULL if no window mapped */
13789294a78SLandon J. Fuller 	bhnd_addr_t			 res_target;	/**< current target address (if mapped) */
13889294a78SLandon J. Fuller 	bool				 mapped;	/**< true if a valid mapping exists, false otherwise */
13989294a78SLandon J. Fuller 	bhnd_addr_t			 addr;		/**< mapped address */
14089294a78SLandon J. Fuller 	bhnd_size_t			 size;		/**< mapped size */
14189294a78SLandon J. Fuller };
14289294a78SLandon J. Fuller 
143*caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk bhndb_pci_quirks[] = {
144*caeff9a3SLandon J. Fuller 	/* Backplane interrupt flags must be routed via siba-specific
145*caeff9a3SLandon J. Fuller 	 * SIBA_CFG0_INTVEC configuration register; the BHNDB_PCI_INT_MASK
146*caeff9a3SLandon J. Fuller 	 * PCI configuration register is unsupported. */
147*caeff9a3SLandon J. Fuller 	{{ BHND_MATCH_CHIP_TYPE		(SIBA) },
148*caeff9a3SLandon J. Fuller 	 { BHND_MATCH_CORE_REV		(HWREV_LTE(5)) },
149*caeff9a3SLandon J. Fuller 		BHNDB_PCI_QUIRK_SIBA_INTVEC },
150*caeff9a3SLandon J. Fuller 
151*caeff9a3SLandon J. Fuller 	/* All PCI core revisions require the SRSH work-around */
152*caeff9a3SLandon J. Fuller 	BHNDB_PCI_QUIRK(HWREV_ANY,	BHNDB_PCI_QUIRK_SRSH_WAR),
153*caeff9a3SLandon J. Fuller 	BHNDB_PCI_QUIRK_END
154*caeff9a3SLandon J. Fuller };
155*caeff9a3SLandon J. Fuller 
156*caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk bhndb_pcie_quirks[] = {
157*caeff9a3SLandon J. Fuller 	/* All PCIe-G1 core revisions require the SRSH work-around */
158*caeff9a3SLandon J. Fuller 	BHNDB_PCI_QUIRK(HWREV_ANY,	BHNDB_PCI_QUIRK_SRSH_WAR),
159*caeff9a3SLandon J. Fuller 	BHNDB_PCI_QUIRK_END
160*caeff9a3SLandon J. Fuller };
161*caeff9a3SLandon J. Fuller 
162*caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk bhndb_pcie2_quirks[] = {
163*caeff9a3SLandon J. Fuller 	/* All PCIe-G2 core revisions require the SRSH work-around */
164*caeff9a3SLandon J. Fuller 	BHNDB_PCI_QUIRK(HWREV_ANY,	BHNDB_PCI_QUIRK_SRSH_WAR),
165*caeff9a3SLandon J. Fuller 	BHNDB_PCI_QUIRK_END
166*caeff9a3SLandon J. Fuller };
167*caeff9a3SLandon J. Fuller 
168*caeff9a3SLandon J. Fuller 
169*caeff9a3SLandon J. Fuller /**
170*caeff9a3SLandon J. Fuller  * Return the device table entry for @p ci, or NULL if none.
171*caeff9a3SLandon J. Fuller  */
172*caeff9a3SLandon J. Fuller static struct bhndb_pci_core *
173*caeff9a3SLandon J. Fuller bhndb_pci_find_core(struct bhnd_core_info *ci)
174*caeff9a3SLandon J. Fuller {
175*caeff9a3SLandon J. Fuller 	for (size_t i = 0; !BHNDB_PCI_IS_CORE_END(&bhndb_pci_cores[i]); i++) {
176*caeff9a3SLandon J. Fuller 		struct bhndb_pci_core *entry = &bhndb_pci_cores[i];
177*caeff9a3SLandon J. Fuller 
178*caeff9a3SLandon J. Fuller 		if (bhnd_core_matches(ci, &entry->match))
179*caeff9a3SLandon J. Fuller 			return (entry);
180*caeff9a3SLandon J. Fuller 	}
181*caeff9a3SLandon J. Fuller 
182*caeff9a3SLandon J. Fuller 	return (NULL);
183*caeff9a3SLandon J. Fuller }
184*caeff9a3SLandon J. Fuller 
185*caeff9a3SLandon J. Fuller /**
186*caeff9a3SLandon J. Fuller  * Return all quirk flags for the given @p cid and @p ci.
187*caeff9a3SLandon J. Fuller  */
188*caeff9a3SLandon J. Fuller static uint32_t
189*caeff9a3SLandon J. Fuller bhndb_pci_get_core_quirks(struct bhnd_chipid *cid, struct bhnd_core_info *ci)
190*caeff9a3SLandon J. Fuller {
191*caeff9a3SLandon J. Fuller 	struct bhndb_pci_core	*entry;
192*caeff9a3SLandon J. Fuller 	struct bhndb_pci_quirk	*qtable;
193*caeff9a3SLandon J. Fuller 	uint32_t		 quirks;
194*caeff9a3SLandon J. Fuller 
195*caeff9a3SLandon J. Fuller 	quirks = 0;
196*caeff9a3SLandon J. Fuller 
197*caeff9a3SLandon J. Fuller 	/* No core entry? */
198*caeff9a3SLandon J. Fuller 	if ((entry = bhndb_pci_find_core(ci)) == NULL)
199*caeff9a3SLandon J. Fuller 		return (quirks);
200*caeff9a3SLandon J. Fuller 
201*caeff9a3SLandon J. Fuller 	/* No quirks? */
202*caeff9a3SLandon J. Fuller 	if ((qtable = entry->quirks) == NULL)
203*caeff9a3SLandon J. Fuller 		return (quirks);
204*caeff9a3SLandon J. Fuller 
205*caeff9a3SLandon J. Fuller 	for (size_t i = 0; !BHNDB_PCI_IS_QUIRK_END(&qtable[i]); i++) {
206*caeff9a3SLandon J. Fuller 		struct bhndb_pci_quirk *q = &qtable[i];
207*caeff9a3SLandon J. Fuller 
208*caeff9a3SLandon J. Fuller 		if (!bhnd_chip_matches(cid, &q->chip_desc))
209*caeff9a3SLandon J. Fuller 			continue;
210*caeff9a3SLandon J. Fuller 
211*caeff9a3SLandon J. Fuller 		if (!bhnd_core_matches(ci, &q->core_desc))
212*caeff9a3SLandon J. Fuller 			continue;
213*caeff9a3SLandon J. Fuller 
214*caeff9a3SLandon J. Fuller 		quirks |= q->quirks;
215*caeff9a3SLandon J. Fuller 	}
216*caeff9a3SLandon J. Fuller 
217*caeff9a3SLandon J. Fuller 	return (quirks);
218*caeff9a3SLandon J. Fuller }
219*caeff9a3SLandon J. Fuller 
2204ad7e9b0SAdrian Chadd /**
2214ad7e9b0SAdrian Chadd  * Default bhndb_pci implementation of device_probe().
2224ad7e9b0SAdrian Chadd  *
2234ad7e9b0SAdrian Chadd  * Verifies that the parent is a PCI/PCIe device.
2244ad7e9b0SAdrian Chadd  */
2254ad7e9b0SAdrian Chadd static int
2264ad7e9b0SAdrian Chadd bhndb_pci_probe(device_t dev)
2274ad7e9b0SAdrian Chadd {
228*caeff9a3SLandon J. Fuller 	struct bhnd_chipid	 cid;
229*caeff9a3SLandon J. Fuller 	struct bhnd_core_info	*cores, hostb_core;
230*caeff9a3SLandon J. Fuller 	struct bhndb_pci_core	*entry;
231*caeff9a3SLandon J. Fuller 	bhnd_devclass_t		 hostb_devclass;
232*caeff9a3SLandon J. Fuller 	u_int			 ncores;
2334ad7e9b0SAdrian Chadd 	device_t		 parent;
234*caeff9a3SLandon J. Fuller 	devclass_t		 parent_bus, pci;
235*caeff9a3SLandon J. Fuller 	int			 error;
236*caeff9a3SLandon J. Fuller 
237*caeff9a3SLandon J. Fuller 	cores = NULL;
2384ad7e9b0SAdrian Chadd 
2394ad7e9b0SAdrian Chadd 	/* Our parent must be a PCI/PCIe device. */
2404ad7e9b0SAdrian Chadd 	pci = devclass_find("pci");
2414ad7e9b0SAdrian Chadd 	parent = device_get_parent(dev);
2424ad7e9b0SAdrian Chadd 	parent_bus = device_get_devclass(device_get_parent(parent));
2434ad7e9b0SAdrian Chadd 
2444ad7e9b0SAdrian Chadd 	if (parent_bus != pci)
2454ad7e9b0SAdrian Chadd 		return (ENXIO);
2464ad7e9b0SAdrian Chadd 
247*caeff9a3SLandon J. Fuller 	/* Enable clocks */
248*caeff9a3SLandon J. Fuller 	if ((error = bhndb_enable_pci_clocks(dev)))
249*caeff9a3SLandon J. Fuller 		return (error);
2504ad7e9b0SAdrian Chadd 
251*caeff9a3SLandon J. Fuller 	/* Identify the chip and enumerate the bridged cores */
252*caeff9a3SLandon J. Fuller 	error = bhndb_pci_read_core_table(dev, &cid, &cores, &ncores, NULL);
253*caeff9a3SLandon J. Fuller 	if (error)
254*caeff9a3SLandon J. Fuller 		goto cleanup;
255*caeff9a3SLandon J. Fuller 
256*caeff9a3SLandon J. Fuller 	/* Search our core table for the host bridge core */
257*caeff9a3SLandon J. Fuller 	hostb_devclass = bhndb_expected_pci_devclass(dev);
258*caeff9a3SLandon J. Fuller 	error = bhndb_find_hostb_core(cores, ncores, hostb_devclass,
259*caeff9a3SLandon J. Fuller 	    &hostb_core);
260*caeff9a3SLandon J. Fuller 	if (error)
261*caeff9a3SLandon J. Fuller 		goto cleanup;
262*caeff9a3SLandon J. Fuller 
263*caeff9a3SLandon J. Fuller 	/* Look for a matching core table entry */
264*caeff9a3SLandon J. Fuller 	if ((entry = bhndb_pci_find_core(&hostb_core)) == NULL) {
265*caeff9a3SLandon J. Fuller 		error = ENXIO;
266*caeff9a3SLandon J. Fuller 		goto cleanup;
2674ad7e9b0SAdrian Chadd 	}
2684ad7e9b0SAdrian Chadd 
269*caeff9a3SLandon J. Fuller 	device_set_desc(dev, "PCI-BHND bridge");
270*caeff9a3SLandon J. Fuller 
271*caeff9a3SLandon J. Fuller 	/* fall-through */
272*caeff9a3SLandon J. Fuller 	error = BUS_PROBE_DEFAULT;
273*caeff9a3SLandon J. Fuller 
274*caeff9a3SLandon J. Fuller cleanup:
275*caeff9a3SLandon J. Fuller 	bhndb_disable_pci_clocks(dev);
276*caeff9a3SLandon J. Fuller 	if (cores != NULL)
277*caeff9a3SLandon J. Fuller 		free(cores, M_BHND);
278*caeff9a3SLandon J. Fuller 
279*caeff9a3SLandon J. Fuller 	return (error);
280*caeff9a3SLandon J. Fuller }
281*caeff9a3SLandon J. Fuller 
282*caeff9a3SLandon J. Fuller /**
283*caeff9a3SLandon J. Fuller  * Attempt to allocate MSI interrupts, returning the count in @p msi_count
284*caeff9a3SLandon J. Fuller  * on success.
285*caeff9a3SLandon J. Fuller  */
286824b48efSLandon J. Fuller static int
287*caeff9a3SLandon J. Fuller bhndb_pci_alloc_msi(struct bhndb_pci_softc *sc, int *msi_count)
288824b48efSLandon J. Fuller {
289*caeff9a3SLandon J. Fuller 	int error, count;
290824b48efSLandon J. Fuller 
291824b48efSLandon J. Fuller 	/* Is MSI available? */
292824b48efSLandon J. Fuller 	if (pci_msi_count(sc->parent) < BHNDB_PCI_MSI_COUNT)
293824b48efSLandon J. Fuller 		return (ENXIO);
294824b48efSLandon J. Fuller 
295824b48efSLandon J. Fuller 	/* Allocate expected message count */
296*caeff9a3SLandon J. Fuller 	count = BHNDB_PCI_MSI_COUNT;
297*caeff9a3SLandon J. Fuller 	if ((error = pci_alloc_msi(sc->parent, &count))) {
298824b48efSLandon J. Fuller 		device_printf(sc->dev, "failed to allocate MSI interrupts: "
299824b48efSLandon J. Fuller 		    "%d\n", error);
300*caeff9a3SLandon J. Fuller 
301824b48efSLandon J. Fuller 		return (error);
302824b48efSLandon J. Fuller 	}
303824b48efSLandon J. Fuller 
304*caeff9a3SLandon J. Fuller 	if (count < BHNDB_PCI_MSI_COUNT)
305824b48efSLandon J. Fuller 		return (ENXIO);
306824b48efSLandon J. Fuller 
307*caeff9a3SLandon J. Fuller 	*msi_count = count;
308824b48efSLandon J. Fuller 	return (0);
309824b48efSLandon J. Fuller }
310824b48efSLandon J. Fuller 
3114ad7e9b0SAdrian Chadd static int
3124ad7e9b0SAdrian Chadd bhndb_pci_attach(device_t dev)
3134ad7e9b0SAdrian Chadd {
3144ad7e9b0SAdrian Chadd 	struct bhndb_pci_softc	*sc;
31589294a78SLandon J. Fuller 	struct bhnd_chipid	 cid;
31689294a78SLandon J. Fuller 	struct bhnd_core_info	*cores, hostb_core;
31789294a78SLandon J. Fuller 	bhnd_erom_class_t	*erom_class;
31889294a78SLandon J. Fuller 	u_int			 ncores;
319*caeff9a3SLandon J. Fuller 	int			 irq_rid;
320*caeff9a3SLandon J. Fuller 	int			 error;
3214ad7e9b0SAdrian Chadd 
3224ad7e9b0SAdrian Chadd 	sc = device_get_softc(dev);
3234ad7e9b0SAdrian Chadd 	sc->dev = dev;
324e83ce340SAdrian Chadd 	sc->parent = device_get_parent(dev);
325*caeff9a3SLandon J. Fuller 	sc->pci_devclass = bhndb_expected_pci_devclass(dev);
326*caeff9a3SLandon J. Fuller 	sc->pci_quirks = 0;
32789294a78SLandon J. Fuller 	sc->set_regwin = NULL;
32889294a78SLandon J. Fuller 
329*caeff9a3SLandon J. Fuller 	BHNDB_PCI_LOCK_INIT(sc);
330*caeff9a3SLandon J. Fuller 
33189294a78SLandon J. Fuller 	cores = NULL;
3324ad7e9b0SAdrian Chadd 
333824b48efSLandon J. Fuller 	/* Enable PCI bus mastering */
334824b48efSLandon J. Fuller 	pci_enable_busmaster(sc->parent);
335824b48efSLandon J. Fuller 
33689294a78SLandon J. Fuller 	/* Set up PCI interrupt handling */
337*caeff9a3SLandon J. Fuller 	if (bhndb_pci_alloc_msi(sc, &sc->msi_count) == 0) {
338*caeff9a3SLandon J. Fuller 		/* MSI uses resource IDs starting at 1 */
339*caeff9a3SLandon J. Fuller 		irq_rid = 1;
340*caeff9a3SLandon J. Fuller 
341824b48efSLandon J. Fuller 		device_printf(dev, "Using MSI interrupts on %s\n",
342824b48efSLandon J. Fuller 		    device_get_nameunit(sc->parent));
343824b48efSLandon J. Fuller 	} else {
344*caeff9a3SLandon J. Fuller 		sc->msi_count = 0;
345*caeff9a3SLandon J. Fuller 		irq_rid = 0;
346*caeff9a3SLandon J. Fuller 
347824b48efSLandon J. Fuller 		device_printf(dev, "Using INTx interrupts on %s\n",
348824b48efSLandon J. Fuller 		    device_get_nameunit(sc->parent));
349824b48efSLandon J. Fuller 	}
350824b48efSLandon J. Fuller 
351*caeff9a3SLandon J. Fuller 	sc->isrc = bhndb_alloc_intr_isrc(sc->parent, irq_rid, 0, RM_MAX_END, 1,
352*caeff9a3SLandon J. Fuller 	    RF_SHAREABLE | RF_ACTIVE);
353*caeff9a3SLandon J. Fuller 	if (sc->isrc == NULL) {
354*caeff9a3SLandon J. Fuller 		device_printf(sc->dev, "failed to allocate interrupt "
355*caeff9a3SLandon J. Fuller 		    "resource\n");
356*caeff9a3SLandon J. Fuller 		error = ENXIO;
357*caeff9a3SLandon J. Fuller 		goto cleanup;
358*caeff9a3SLandon J. Fuller 	}
3594ad7e9b0SAdrian Chadd 
360111d7cb2SLandon J. Fuller 	/* Enable clocks (if required by this hardware) */
36189294a78SLandon J. Fuller 	if ((error = bhndb_enable_pci_clocks(sc->dev)))
362111d7cb2SLandon J. Fuller 		goto cleanup;
363111d7cb2SLandon J. Fuller 
36489294a78SLandon J. Fuller 	/* Identify the chip and enumerate the bridged cores */
36589294a78SLandon J. Fuller 	error = bhndb_pci_read_core_table(dev, &cid, &cores, &ncores,
36689294a78SLandon J. Fuller 	    &erom_class);
36789294a78SLandon J. Fuller 	if (error)
368111d7cb2SLandon J. Fuller 		goto cleanup;
369111d7cb2SLandon J. Fuller 
37089294a78SLandon J. Fuller 	/* Select the appropriate register window handler */
37189294a78SLandon J. Fuller 	if (cid.chip_type == BHND_CHIPTYPE_SIBA) {
37289294a78SLandon J. Fuller 		sc->set_regwin = bhndb_pci_compat_setregwin;
37389294a78SLandon J. Fuller 	} else {
37489294a78SLandon J. Fuller 		sc->set_regwin = bhndb_pci_fast_setregwin;
3754ad7e9b0SAdrian Chadd 	}
3764ad7e9b0SAdrian Chadd 
377*caeff9a3SLandon J. Fuller 	/* Determine our host bridge core and populate our quirk flags */
37889294a78SLandon J. Fuller 	error = bhndb_find_hostb_core(cores, ncores, sc->pci_devclass,
37989294a78SLandon J. Fuller 	    &hostb_core);
38089294a78SLandon J. Fuller 	if (error)
38189294a78SLandon J. Fuller 		goto cleanup;
38289294a78SLandon J. Fuller 
383*caeff9a3SLandon J. Fuller 	sc->pci_quirks = bhndb_pci_get_core_quirks(&cid, &hostb_core);
384*caeff9a3SLandon J. Fuller 
38589294a78SLandon J. Fuller 	/* Perform bridge attach */
38689294a78SLandon J. Fuller 	error = bhndb_attach(dev, &cid, cores, ncores, &hostb_core, erom_class);
38789294a78SLandon J. Fuller 	if (error)
38889294a78SLandon J. Fuller 		goto cleanup;
3894ad7e9b0SAdrian Chadd 
390bb64eeccSAdrian Chadd 	/* Fix-up power on defaults for SROM-less devices. */
3914ad7e9b0SAdrian Chadd 	bhndb_init_sromless_pci_config(sc);
3924ad7e9b0SAdrian Chadd 
393111d7cb2SLandon J. Fuller 	/* Add any additional child devices */
394111d7cb2SLandon J. Fuller 	if ((error = bhndb_pci_add_children(sc)))
395111d7cb2SLandon J. Fuller 		goto cleanup;
396111d7cb2SLandon J. Fuller 
397111d7cb2SLandon J. Fuller 	/* Probe and attach our children */
398111d7cb2SLandon J. Fuller 	if ((error = bus_generic_attach(dev)))
399111d7cb2SLandon J. Fuller 		goto cleanup;
400111d7cb2SLandon J. Fuller 
40189294a78SLandon J. Fuller 	free(cores, M_BHND);
40289294a78SLandon J. Fuller 
403111d7cb2SLandon J. Fuller 	return (0);
404111d7cb2SLandon J. Fuller 
405111d7cb2SLandon J. Fuller cleanup:
406111d7cb2SLandon J. Fuller 	device_delete_children(dev);
40789294a78SLandon J. Fuller 	bhndb_disable_pci_clocks(sc->dev);
40889294a78SLandon J. Fuller 
409*caeff9a3SLandon J. Fuller 	if (sc->isrc != NULL)
410*caeff9a3SLandon J. Fuller 		bhndb_free_intr_isrc(sc->isrc);
411*caeff9a3SLandon J. Fuller 
412*caeff9a3SLandon J. Fuller 	if (sc->msi_count > 0)
413824b48efSLandon J. Fuller 		pci_release_msi(dev);
414824b48efSLandon J. Fuller 
41589294a78SLandon J. Fuller 	if (cores != NULL)
41689294a78SLandon J. Fuller 		free(cores, M_BHND);
41789294a78SLandon J. Fuller 
418111d7cb2SLandon J. Fuller 	pci_disable_busmaster(sc->parent);
419111d7cb2SLandon J. Fuller 
420*caeff9a3SLandon J. Fuller 	BHNDB_PCI_LOCK_DESTROY(sc);
421*caeff9a3SLandon J. Fuller 
422111d7cb2SLandon J. Fuller 	return (error);
423111d7cb2SLandon J. Fuller }
424111d7cb2SLandon J. Fuller 
425111d7cb2SLandon J. Fuller static int
426111d7cb2SLandon J. Fuller bhndb_pci_detach(device_t dev)
427111d7cb2SLandon J. Fuller {
428111d7cb2SLandon J. Fuller 	struct bhndb_pci_softc	*sc;
429111d7cb2SLandon J. Fuller 	int			 error;
430111d7cb2SLandon J. Fuller 
431111d7cb2SLandon J. Fuller 	sc = device_get_softc(dev);
432111d7cb2SLandon J. Fuller 
433111d7cb2SLandon J. Fuller 	/* Attempt to detach our children */
434111d7cb2SLandon J. Fuller 	if ((error = bus_generic_detach(dev)))
435111d7cb2SLandon J. Fuller 		return (error);
436111d7cb2SLandon J. Fuller 
437111d7cb2SLandon J. Fuller 	/* Perform generic bridge detach */
438111d7cb2SLandon J. Fuller 	if ((error = bhndb_generic_detach(dev)))
439111d7cb2SLandon J. Fuller 		return (error);
440111d7cb2SLandon J. Fuller 
441111d7cb2SLandon J. Fuller 	/* Disable clocks (if required by this hardware) */
44289294a78SLandon J. Fuller 	if ((error = bhndb_disable_pci_clocks(sc->dev)))
443111d7cb2SLandon J. Fuller 		return (error);
444111d7cb2SLandon J. Fuller 
445*caeff9a3SLandon J. Fuller 	/* Free our interrupt resources */
446*caeff9a3SLandon J. Fuller 	bhndb_free_intr_isrc(sc->isrc);
447*caeff9a3SLandon J. Fuller 
448824b48efSLandon J. Fuller 	/* Release MSI interrupts */
449*caeff9a3SLandon J. Fuller 	if (sc->msi_count > 0)
450824b48efSLandon J. Fuller 		pci_release_msi(dev);
451824b48efSLandon J. Fuller 
452111d7cb2SLandon J. Fuller 	/* Disable PCI bus mastering */
453111d7cb2SLandon J. Fuller 	pci_disable_busmaster(sc->parent);
454111d7cb2SLandon J. Fuller 
455*caeff9a3SLandon J. Fuller 	BHNDB_PCI_LOCK_DESTROY(sc);
456*caeff9a3SLandon J. Fuller 
457111d7cb2SLandon J. Fuller 	return (0);
458111d7cb2SLandon J. Fuller }
459111d7cb2SLandon J. Fuller 
46089294a78SLandon J. Fuller /**
46189294a78SLandon J. Fuller  * Use the generic PCI bridge hardware configuration to enumerate the bridged
46289294a78SLandon J. Fuller  * bhnd(4) bus' core table.
46389294a78SLandon J. Fuller  *
46489294a78SLandon J. Fuller  * @note This function may be safely called prior to device attach, (e.g.
46589294a78SLandon J. Fuller  * from DEVICE_PROBE).
46689294a78SLandon J. Fuller  * @note This function requires exclusive ownership over allocating and
46789294a78SLandon J. Fuller  * configuring host bridge resources, and should only be called prior to
46889294a78SLandon J. Fuller  * completion of device attach and full configuration of the bridge.
46989294a78SLandon J. Fuller  *
47089294a78SLandon J. Fuller  * @param	dev		The bhndb_pci bridge device.
47189294a78SLandon J. Fuller  * @param[out]	chipid		On success, the parsed chip identification.
47289294a78SLandon J. Fuller  * @param[out]	cores		On success, the enumerated core table. The
47389294a78SLandon J. Fuller  *				caller is responsible for freeing this table via
47489294a78SLandon J. Fuller  *				bhndb_pci_free_core_table().
47589294a78SLandon J. Fuller  * @param[out]	ncores		On success, the number of cores found in
47689294a78SLandon J. Fuller  *				@p cores.
47789294a78SLandon J. Fuller  * @param[out]	eromcls		On success, a pointer to the erom class used to
47889294a78SLandon J. Fuller  *				parse the device enumeration table. This
47989294a78SLandon J. Fuller  *				argument may be NULL if the class is not
48089294a78SLandon J. Fuller  *				desired.
48189294a78SLandon J. Fuller  *
48289294a78SLandon J. Fuller  * @retval 0		success
48389294a78SLandon J. Fuller  * @retval non-zero	if enumerating the bridged bhnd(4) bus fails, a regular
48489294a78SLandon J. Fuller  * 			unix error code will be returned.
48589294a78SLandon J. Fuller  */
48689294a78SLandon J. Fuller static int
48789294a78SLandon J. Fuller bhndb_pci_read_core_table(device_t dev, struct bhnd_chipid *chipid,
48889294a78SLandon J. Fuller     struct bhnd_core_info **cores, u_int *ncores,
48989294a78SLandon J. Fuller     bhnd_erom_class_t **eromcls)
49089294a78SLandon J. Fuller {
49189294a78SLandon J. Fuller 	const struct bhndb_hwcfg	*cfg;
49289294a78SLandon J. Fuller 	struct bhndb_host_resources	*hr;
49389294a78SLandon J. Fuller 	struct bhndb_pci_eio		 pio;
49489294a78SLandon J. Fuller 	struct bhnd_core_info		*erom_cores;
49589294a78SLandon J. Fuller 	const struct bhnd_chipid	*hint;
49689294a78SLandon J. Fuller 	struct bhnd_chipid		 cid;
49789294a78SLandon J. Fuller 	bhnd_erom_class_t		*erom_class;
49889294a78SLandon J. Fuller 	bhnd_erom_t			*erom;
49989294a78SLandon J. Fuller 	device_t			 parent_dev;
50089294a78SLandon J. Fuller 	u_int				 erom_ncores;
50189294a78SLandon J. Fuller 	int				 error;
50289294a78SLandon J. Fuller 
50389294a78SLandon J. Fuller 	parent_dev = device_get_parent(dev);
50489294a78SLandon J. Fuller 	erom = NULL;
50589294a78SLandon J. Fuller 	erom_cores = NULL;
50689294a78SLandon J. Fuller 
50789294a78SLandon J. Fuller 	/* Fetch our chipid hint (if any) and generic hardware configuration */
50889294a78SLandon J. Fuller 	cfg = BHNDB_BUS_GET_GENERIC_HWCFG(parent_dev, dev);
50989294a78SLandon J. Fuller 	hint = BHNDB_BUS_GET_CHIPID(parent_dev, dev);
51089294a78SLandon J. Fuller 
51189294a78SLandon J. Fuller 	/* Allocate our host resources */
51289294a78SLandon J. Fuller 	if ((error = bhndb_alloc_host_resources(parent_dev, cfg, &hr)))
51389294a78SLandon J. Fuller 		return (error);
51489294a78SLandon J. Fuller 
51589294a78SLandon J. Fuller 	/* Initialize our erom I/O state */
51689294a78SLandon J. Fuller 	if ((error = bhndb_pci_eio_init(&pio, dev, parent_dev, hr)))
51789294a78SLandon J. Fuller 		goto failed;
51889294a78SLandon J. Fuller 
51989294a78SLandon J. Fuller 	/* Map the first bus core from our bridged bhnd(4) bus */
52089294a78SLandon J. Fuller 	error = bhndb_pci_eio_map(&pio.eio, BHND_DEFAULT_CHIPC_ADDR,
52189294a78SLandon J. Fuller 	    BHND_DEFAULT_CORE_SIZE);
52289294a78SLandon J. Fuller 	if (error)
52389294a78SLandon J. Fuller 		goto failed;
52489294a78SLandon J. Fuller 
52589294a78SLandon J. Fuller 	/* Probe for a usable EROM class, and read the chip identifier */
52689294a78SLandon J. Fuller 	erom_class = bhnd_erom_probe_driver_classes(device_get_devclass(dev),
52789294a78SLandon J. Fuller 	    &pio.eio, hint, &cid);
52889294a78SLandon J. Fuller 	if (erom_class == NULL) {
52989294a78SLandon J. Fuller 		device_printf(dev, "device enumeration unsupported; no "
53089294a78SLandon J. Fuller 		    "compatible driver found\n");
53189294a78SLandon J. Fuller 
53289294a78SLandon J. Fuller 		error = ENXIO;
53389294a78SLandon J. Fuller 		goto failed;
53489294a78SLandon J. Fuller 	}
53589294a78SLandon J. Fuller 
53689294a78SLandon J. Fuller 	/* Allocate EROM parser */
53789294a78SLandon J. Fuller 	if ((erom = bhnd_erom_alloc(erom_class, &cid, &pio.eio)) == NULL) {
53889294a78SLandon J. Fuller 		device_printf(dev, "failed to allocate device enumeration "
53989294a78SLandon J. Fuller 		    "table parser\n");
54089294a78SLandon J. Fuller 		error = ENXIO;
54189294a78SLandon J. Fuller 		goto failed;
54289294a78SLandon J. Fuller 	}
54389294a78SLandon J. Fuller 
54489294a78SLandon J. Fuller 	/* Read the full core table */
54589294a78SLandon J. Fuller 	error = bhnd_erom_get_core_table(erom, &erom_cores, &erom_ncores);
54689294a78SLandon J. Fuller 	if (error) {
54789294a78SLandon J. Fuller 		device_printf(dev, "error fetching core table: %d\n", error);
54889294a78SLandon J. Fuller 		goto failed;
54989294a78SLandon J. Fuller 	}
55089294a78SLandon J. Fuller 
55189294a78SLandon J. Fuller 	/* Provide the results to our caller */
55289294a78SLandon J. Fuller 	*cores = malloc(sizeof(erom_cores[0]) * erom_ncores, M_BHND, M_WAITOK);
55389294a78SLandon J. Fuller 	memcpy(*cores, erom_cores, sizeof(erom_cores[0]) * erom_ncores);
55489294a78SLandon J. Fuller 	*ncores = erom_ncores;
55589294a78SLandon J. Fuller 
55689294a78SLandon J. Fuller 	*chipid = cid;
55789294a78SLandon J. Fuller 	if (eromcls != NULL)
55889294a78SLandon J. Fuller 		*eromcls = erom_class;
55989294a78SLandon J. Fuller 
56089294a78SLandon J. Fuller 	/* Clean up */
56189294a78SLandon J. Fuller 	bhnd_erom_free_core_table(erom, erom_cores);
56289294a78SLandon J. Fuller 	bhnd_erom_free(erom);
56389294a78SLandon J. Fuller 	bhndb_release_host_resources(hr);
56489294a78SLandon J. Fuller 
56589294a78SLandon J. Fuller 	return (0);
56689294a78SLandon J. Fuller 
56789294a78SLandon J. Fuller failed:
56889294a78SLandon J. Fuller 	if (erom_cores != NULL)
56989294a78SLandon J. Fuller 		bhnd_erom_free_core_table(erom, erom_cores);
57089294a78SLandon J. Fuller 
57189294a78SLandon J. Fuller 	if (erom != NULL)
57289294a78SLandon J. Fuller 		bhnd_erom_free(erom);
57389294a78SLandon J. Fuller 
57489294a78SLandon J. Fuller 	bhndb_release_host_resources(hr);
57589294a78SLandon J. Fuller 	return (error);
57689294a78SLandon J. Fuller }
57789294a78SLandon J. Fuller 
578111d7cb2SLandon J. Fuller static int
579111d7cb2SLandon J. Fuller bhndb_pci_add_children(struct bhndb_pci_softc *sc)
580111d7cb2SLandon J. Fuller {
581111d7cb2SLandon J. Fuller 	bus_size_t		 nv_sz;
582111d7cb2SLandon J. Fuller 	int			 error;
583111d7cb2SLandon J. Fuller 
584111d7cb2SLandon J. Fuller 	/**
585111d7cb2SLandon J. Fuller 	 * If SPROM is mapped directly into BAR0, add child NVRAM
586111d7cb2SLandon J. Fuller 	 * device.
587111d7cb2SLandon J. Fuller 	 */
588e83ce340SAdrian Chadd 	nv_sz = bhndb_pci_sprom_size(sc);
589e83ce340SAdrian Chadd 	if (nv_sz > 0) {
590e83ce340SAdrian Chadd 		struct bhndb_devinfo	*dinfo;
591111d7cb2SLandon J. Fuller 		device_t		 child;
592e83ce340SAdrian Chadd 
593e83ce340SAdrian Chadd 		if (bootverbose) {
594111d7cb2SLandon J. Fuller 			device_printf(sc->dev, "found SPROM (%ju bytes)\n",
595111d7cb2SLandon J. Fuller 			    (uintmax_t)nv_sz);
596e83ce340SAdrian Chadd 		}
597e83ce340SAdrian Chadd 
598111d7cb2SLandon J. Fuller 		/* Add sprom device, ordered early enough to be available
599111d7cb2SLandon J. Fuller 		 * before the bridged bhnd(4) bus is attached. */
600111d7cb2SLandon J. Fuller 		child = BUS_ADD_CHILD(sc->dev,
601111d7cb2SLandon J. Fuller 		    BHND_PROBE_ROOT + BHND_PROBE_ORDER_EARLY, "bhnd_nvram", -1);
602111d7cb2SLandon J. Fuller 		if (child == NULL) {
603111d7cb2SLandon J. Fuller 			device_printf(sc->dev, "failed to add sprom device\n");
604e83ce340SAdrian Chadd 			return (ENXIO);
605e83ce340SAdrian Chadd 		}
606e83ce340SAdrian Chadd 
607e83ce340SAdrian Chadd 		/* Initialize device address space and resource covering the
608e83ce340SAdrian Chadd 		 * BAR0 SPROM shadow. */
609111d7cb2SLandon J. Fuller 		dinfo = device_get_ivars(child);
610e83ce340SAdrian Chadd 		dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE;
611111d7cb2SLandon J. Fuller 
612111d7cb2SLandon J. Fuller 		error = bus_set_resource(child, SYS_RES_MEMORY, 0,
613e83ce340SAdrian Chadd 		    bhndb_pci_sprom_addr(sc), nv_sz);
614e83ce340SAdrian Chadd 		if (error) {
615111d7cb2SLandon J. Fuller 			device_printf(sc->dev,
616e83ce340SAdrian Chadd 			    "failed to register sprom resources\n");
617e83ce340SAdrian Chadd 			return (error);
618e83ce340SAdrian Chadd 		}
619e83ce340SAdrian Chadd 	}
620e83ce340SAdrian Chadd 
6214ad7e9b0SAdrian Chadd 	return (0);
6224ad7e9b0SAdrian Chadd }
6234ad7e9b0SAdrian Chadd 
624e83ce340SAdrian Chadd static const struct bhndb_regwin *
625e83ce340SAdrian Chadd bhndb_pci_sprom_regwin(struct bhndb_pci_softc *sc)
626e83ce340SAdrian Chadd {
627e83ce340SAdrian Chadd 	struct bhndb_resources		*bres;
628e83ce340SAdrian Chadd 	const struct bhndb_hwcfg	*cfg;
629e83ce340SAdrian Chadd 	const struct bhndb_regwin	*sprom_win;
630e83ce340SAdrian Chadd 
631e83ce340SAdrian Chadd 	bres = sc->bhndb.bus_res;
632e83ce340SAdrian Chadd 	cfg = bres->cfg;
633e83ce340SAdrian Chadd 
634e83ce340SAdrian Chadd 	sprom_win = bhndb_regwin_find_type(cfg->register_windows,
635e83ce340SAdrian Chadd 	    BHNDB_REGWIN_T_SPROM, BHNDB_PCI_V0_BAR0_SPROM_SIZE);
636e83ce340SAdrian Chadd 
637e83ce340SAdrian Chadd 	return (sprom_win);
638e83ce340SAdrian Chadd }
639e83ce340SAdrian Chadd 
640e83ce340SAdrian Chadd static bus_addr_t
641e83ce340SAdrian Chadd bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc)
642e83ce340SAdrian Chadd {
643e83ce340SAdrian Chadd 	const struct bhndb_regwin	*sprom_win;
644e83ce340SAdrian Chadd 	struct resource			*r;
645e83ce340SAdrian Chadd 
646e83ce340SAdrian Chadd 	/* Fetch the SPROM register window */
647e83ce340SAdrian Chadd 	sprom_win = bhndb_pci_sprom_regwin(sc);
648e83ce340SAdrian Chadd 	KASSERT(sprom_win != NULL, ("requested sprom address on PCI_V2+"));
649e83ce340SAdrian Chadd 
650e83ce340SAdrian Chadd 	/* Fetch the associated resource */
65189294a78SLandon J. Fuller 	r = bhndb_host_resource_for_regwin(sc->bhndb.bus_res->res, sprom_win);
652e83ce340SAdrian Chadd 	KASSERT(r != NULL, ("missing resource for sprom window\n"));
653e83ce340SAdrian Chadd 
654e83ce340SAdrian Chadd 	return (rman_get_start(r) + sprom_win->win_offset);
655e83ce340SAdrian Chadd }
656e83ce340SAdrian Chadd 
657e83ce340SAdrian Chadd static bus_size_t
658e83ce340SAdrian Chadd bhndb_pci_sprom_size(struct bhndb_pci_softc *sc)
659e83ce340SAdrian Chadd {
660e83ce340SAdrian Chadd 	const struct bhndb_regwin	*sprom_win;
661e83ce340SAdrian Chadd 	uint32_t			 sctl;
662e83ce340SAdrian Chadd 	bus_size_t			 sprom_sz;
663e83ce340SAdrian Chadd 
664e83ce340SAdrian Chadd 	sprom_win = bhndb_pci_sprom_regwin(sc);
665e83ce340SAdrian Chadd 
666e83ce340SAdrian Chadd 	/* PCI_V2 and later devices map SPROM/OTP via ChipCommon */
667e83ce340SAdrian Chadd 	if (sprom_win == NULL)
668e83ce340SAdrian Chadd 		return (0);
669e83ce340SAdrian Chadd 
670e83ce340SAdrian Chadd 	/* Determine SPROM size */
671e83ce340SAdrian Chadd 	sctl = pci_read_config(sc->parent, BHNDB_PCI_SPROM_CONTROL, 4);
672e83ce340SAdrian Chadd 	if (sctl & BHNDB_PCI_SPROM_BLANK)
673e83ce340SAdrian Chadd 		return (0);
674e83ce340SAdrian Chadd 
675e83ce340SAdrian Chadd 	switch (sctl & BHNDB_PCI_SPROM_SZ_MASK) {
676e83ce340SAdrian Chadd 	case BHNDB_PCI_SPROM_SZ_1KB:
677e83ce340SAdrian Chadd 		sprom_sz = (1 * 1024);
678e83ce340SAdrian Chadd 		break;
679e83ce340SAdrian Chadd 
680e83ce340SAdrian Chadd 	case BHNDB_PCI_SPROM_SZ_4KB:
681e83ce340SAdrian Chadd 		sprom_sz = (4 * 1024);
682e83ce340SAdrian Chadd 		break;
683e83ce340SAdrian Chadd 
684e83ce340SAdrian Chadd 	case BHNDB_PCI_SPROM_SZ_16KB:
685e83ce340SAdrian Chadd 		sprom_sz = (16 * 1024);
686e83ce340SAdrian Chadd 		break;
687e83ce340SAdrian Chadd 
688e83ce340SAdrian Chadd 	case BHNDB_PCI_SPROM_SZ_RESERVED:
689e83ce340SAdrian Chadd 	default:
690e83ce340SAdrian Chadd 		device_printf(sc->dev, "invalid PCI sprom size 0x%x\n", sctl);
691e83ce340SAdrian Chadd 		return (0);
692e83ce340SAdrian Chadd 	}
693e83ce340SAdrian Chadd 
694e83ce340SAdrian Chadd 	if (sprom_sz > sprom_win->win_size) {
695e83ce340SAdrian Chadd 		device_printf(sc->dev,
696e83ce340SAdrian Chadd 		    "PCI sprom size (0x%x) overruns defined register window\n",
697e83ce340SAdrian Chadd 		    sctl);
698e83ce340SAdrian Chadd 		return (0);
699e83ce340SAdrian Chadd 	}
700e83ce340SAdrian Chadd 
701e83ce340SAdrian Chadd 	return (sprom_sz);
702e83ce340SAdrian Chadd }
703e83ce340SAdrian Chadd 
704*caeff9a3SLandon J. Fuller /**
705*caeff9a3SLandon J. Fuller  * Return the host resource providing a static mapping of the PCI core's
706*caeff9a3SLandon J. Fuller  * registers.
707*caeff9a3SLandon J. Fuller  *
708*caeff9a3SLandon J. Fuller  * @param	sc	bhndb PCI driver state.
709*caeff9a3SLandon J. Fuller  * @param[out]	res	On success, the host resource containing our PCI
710*caeff9a3SLandon J. Fuller  *			core's register window.
711*caeff9a3SLandon J. Fuller  * @param[out]	offset	On success, the offset of the PCI core registers within
712*caeff9a3SLandon J. Fuller  * 			@p res.
713*caeff9a3SLandon J. Fuller  *
714*caeff9a3SLandon J. Fuller  * @retval 0		success
715*caeff9a3SLandon J. Fuller  * @retval ENXIO	if a valid static register window mapping the PCI core
716*caeff9a3SLandon J. Fuller  *			registers is not available.
717*caeff9a3SLandon J. Fuller  */
718*caeff9a3SLandon J. Fuller static int
719*caeff9a3SLandon J. Fuller bhndb_pci_get_core_regs(struct bhndb_pci_softc *sc, struct resource **res,
720*caeff9a3SLandon J. Fuller     bus_size_t *offset)
721*caeff9a3SLandon J. Fuller {
722*caeff9a3SLandon J. Fuller 	const struct bhndb_regwin	*win;
723*caeff9a3SLandon J. Fuller 	struct resource			*r;
724*caeff9a3SLandon J. Fuller 
725*caeff9a3SLandon J. Fuller 	/* Locate the static register window mapping the PCI core */
726*caeff9a3SLandon J. Fuller 	win = bhndb_regwin_find_core(sc->bhndb.bus_res->cfg->register_windows,
727*caeff9a3SLandon J. Fuller 	    sc->pci_devclass, 0, BHND_PORT_DEVICE, 0, 0);
728*caeff9a3SLandon J. Fuller 	if (win == NULL) {
729*caeff9a3SLandon J. Fuller 		device_printf(sc->dev, "missing PCI core register window\n");
730*caeff9a3SLandon J. Fuller 		return (ENXIO);
731*caeff9a3SLandon J. Fuller 	}
732*caeff9a3SLandon J. Fuller 
733*caeff9a3SLandon J. Fuller 	/* Fetch the resource containing the register window */
734*caeff9a3SLandon J. Fuller 	r = bhndb_host_resource_for_regwin(sc->bhndb.bus_res->res, win);
735*caeff9a3SLandon J. Fuller 	if (r == NULL) {
736*caeff9a3SLandon J. Fuller 		device_printf(sc->dev, "missing PCI core register resource\n");
737*caeff9a3SLandon J. Fuller 		return (ENXIO);
738*caeff9a3SLandon J. Fuller 	}
739*caeff9a3SLandon J. Fuller 
740*caeff9a3SLandon J. Fuller 	*res = r;
741*caeff9a3SLandon J. Fuller 	*offset = win->win_offset;
742*caeff9a3SLandon J. Fuller 
743*caeff9a3SLandon J. Fuller 	return (0);
744*caeff9a3SLandon J. Fuller }
745*caeff9a3SLandon J. Fuller 
746*caeff9a3SLandon J. Fuller /**
747*caeff9a3SLandon J. Fuller  * Write a 1, 2, or 4 byte data item to the PCI core's registers at @p offset.
748*caeff9a3SLandon J. Fuller  *
749*caeff9a3SLandon J. Fuller  * @param sc		bhndb PCI driver state.
750*caeff9a3SLandon J. Fuller  * @param offset	register write offset.
751*caeff9a3SLandon J. Fuller  * @param value		value to be written.
752*caeff9a3SLandon J. Fuller  * @param width		item width (1, 2, or 4 bytes).
753*caeff9a3SLandon J. Fuller  */
754*caeff9a3SLandon J. Fuller static void
755*caeff9a3SLandon J. Fuller bhndb_pci_write_core(struct bhndb_pci_softc *sc, bus_size_t offset,
756*caeff9a3SLandon J. Fuller     uint32_t value, u_int width)
757*caeff9a3SLandon J. Fuller {
758*caeff9a3SLandon J. Fuller 	struct resource	*r;
759*caeff9a3SLandon J. Fuller 	bus_size_t	 r_offset;
760*caeff9a3SLandon J. Fuller 	int		 error;
761*caeff9a3SLandon J. Fuller 
762*caeff9a3SLandon J. Fuller 	if ((error = bhndb_pci_get_core_regs(sc, &r, &r_offset)))
763*caeff9a3SLandon J. Fuller 		panic("no PCI core registers: %d", error);
764*caeff9a3SLandon J. Fuller 
765*caeff9a3SLandon J. Fuller 	switch (width) {
766*caeff9a3SLandon J. Fuller 	case 1:
767*caeff9a3SLandon J. Fuller 		bus_write_1(r, r_offset + offset, value);
768*caeff9a3SLandon J. Fuller 		break;
769*caeff9a3SLandon J. Fuller 	case 2:
770*caeff9a3SLandon J. Fuller 		bus_write_2(r, r_offset + offset, value);
771*caeff9a3SLandon J. Fuller 		break;
772*caeff9a3SLandon J. Fuller 	case 4:
773*caeff9a3SLandon J. Fuller 		bus_write_4(r, r_offset + offset, value);
774*caeff9a3SLandon J. Fuller 		break;
775*caeff9a3SLandon J. Fuller 	default:
776*caeff9a3SLandon J. Fuller 		panic("invalid width: %u", width);
777*caeff9a3SLandon J. Fuller 	}
778*caeff9a3SLandon J. Fuller }
779*caeff9a3SLandon J. Fuller 
780*caeff9a3SLandon J. Fuller /**
781*caeff9a3SLandon J. Fuller  * Read a 1, 2, or 4 byte data item from the PCI core's registers
782*caeff9a3SLandon J. Fuller  * at @p offset.
783*caeff9a3SLandon J. Fuller  *
784*caeff9a3SLandon J. Fuller  * @param sc		bhndb PCI driver state.
785*caeff9a3SLandon J. Fuller  * @param offset	register read offset.
786*caeff9a3SLandon J. Fuller  * @param width		item width (1, 2, or 4 bytes).
787*caeff9a3SLandon J. Fuller  */
788*caeff9a3SLandon J. Fuller static uint32_t
789*caeff9a3SLandon J. Fuller bhndb_pci_read_core(struct bhndb_pci_softc *sc, bus_size_t offset, u_int width)
790*caeff9a3SLandon J. Fuller {
791*caeff9a3SLandon J. Fuller 	struct resource	*r;
792*caeff9a3SLandon J. Fuller 	bus_size_t	 r_offset;
793*caeff9a3SLandon J. Fuller 	int		 error;
794*caeff9a3SLandon J. Fuller 
795*caeff9a3SLandon J. Fuller 	if ((error = bhndb_pci_get_core_regs(sc, &r, &r_offset)))
796*caeff9a3SLandon J. Fuller 		panic("no PCI core registers: %d", error);
797*caeff9a3SLandon J. Fuller 
798*caeff9a3SLandon J. Fuller 	switch (width) {
799*caeff9a3SLandon J. Fuller 	case 1:
800*caeff9a3SLandon J. Fuller 		return (bus_read_1(r, r_offset + offset));
801*caeff9a3SLandon J. Fuller 	case 2:
802*caeff9a3SLandon J. Fuller 		return (bus_read_2(r, r_offset + offset));
803*caeff9a3SLandon J. Fuller 	case 4:
804*caeff9a3SLandon J. Fuller 		return (bus_read_4(r, r_offset + offset));
805*caeff9a3SLandon J. Fuller 	default:
806*caeff9a3SLandon J. Fuller 		panic("invalid width: %u", width);
807*caeff9a3SLandon J. Fuller 	}
808*caeff9a3SLandon J. Fuller }
809*caeff9a3SLandon J. Fuller 
8104ad7e9b0SAdrian Chadd /*
8114ad7e9b0SAdrian Chadd  * On devices without a SROM, the PCI(e) cores will be initialized with
812bb64eeccSAdrian Chadd  * their Power-on-Reset defaults; this can leave two of the BAR0 PCI windows
813bb64eeccSAdrian Chadd  * mapped to the wrong core.
8144ad7e9b0SAdrian Chadd  *
815bb64eeccSAdrian Chadd  * This function updates the SROM shadow to point the BAR0 windows at the
8164ad7e9b0SAdrian Chadd  * current PCI core.
8174ad7e9b0SAdrian Chadd  *
818bb64eeccSAdrian Chadd  * Applies to all PCI/PCIe revisions.
8194ad7e9b0SAdrian Chadd  */
8204ad7e9b0SAdrian Chadd static void
8214ad7e9b0SAdrian Chadd bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc)
8224ad7e9b0SAdrian Chadd {
823*caeff9a3SLandon J. Fuller 	const struct bhndb_pci_core	*pci_core;
824*caeff9a3SLandon J. Fuller 	bus_size_t			 srsh_offset;
825bb64eeccSAdrian Chadd 	u_int				 pci_cidx, sprom_cidx;
8264ad7e9b0SAdrian Chadd 	uint16_t			 val;
8274ad7e9b0SAdrian Chadd 
828*caeff9a3SLandon J. Fuller 	if ((sc->pci_quirks & BHNDB_PCI_QUIRK_SRSH_WAR) == 0)
829bb64eeccSAdrian Chadd 		return;
830bb64eeccSAdrian Chadd 
831*caeff9a3SLandon J. Fuller 	/* Determine the correct register offset for our PCI core */
832*caeff9a3SLandon J. Fuller 	pci_core = bhndb_pci_find_core(&sc->bhndb.bridge_core);
833*caeff9a3SLandon J. Fuller 	KASSERT(pci_core != NULL, ("missing core table entry"));
834bb64eeccSAdrian Chadd 
835*caeff9a3SLandon J. Fuller 	srsh_offset = pci_core->srsh_offset;
836bb64eeccSAdrian Chadd 
8374ad7e9b0SAdrian Chadd 	/* Fetch the SPROM's configured core index */
838*caeff9a3SLandon J. Fuller 	val = bhndb_pci_read_core(sc, srsh_offset, sizeof(val));
839bb64eeccSAdrian Chadd 	sprom_cidx = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT;
8404ad7e9b0SAdrian Chadd 
8414ad7e9b0SAdrian Chadd 	/* If it doesn't match host bridge's core index, update the index
8424ad7e9b0SAdrian Chadd 	 * value */
843*caeff9a3SLandon J. Fuller 	pci_cidx = sc->bhndb.bridge_core.core_idx;
844bb64eeccSAdrian Chadd 	if (sprom_cidx != pci_cidx) {
845bb64eeccSAdrian Chadd 		val &= ~BHND_PCI_SRSH_PI_MASK;
846bb64eeccSAdrian Chadd 		val |= (pci_cidx << BHND_PCI_SRSH_PI_SHIFT);
847*caeff9a3SLandon J. Fuller 		bhndb_pci_write_core(sc, srsh_offset, val, sizeof(val));
8484ad7e9b0SAdrian Chadd 	}
8494ad7e9b0SAdrian Chadd }
8504ad7e9b0SAdrian Chadd 
8514ad7e9b0SAdrian Chadd static int
8524ad7e9b0SAdrian Chadd bhndb_pci_resume(device_t dev)
8534ad7e9b0SAdrian Chadd {
8544ad7e9b0SAdrian Chadd 	struct bhndb_pci_softc	*sc;
8554ad7e9b0SAdrian Chadd 	int			 error;
8564ad7e9b0SAdrian Chadd 
8574ad7e9b0SAdrian Chadd 	sc = device_get_softc(dev);
8584ad7e9b0SAdrian Chadd 
859bb64eeccSAdrian Chadd 	/* Enable clocks (if supported by this hardware) */
86089294a78SLandon J. Fuller 	if ((error = bhndb_enable_pci_clocks(sc->dev)))
8614ad7e9b0SAdrian Chadd 		return (error);
8624ad7e9b0SAdrian Chadd 
863bb64eeccSAdrian Chadd 	/* Perform resume */
864bb64eeccSAdrian Chadd 	return (bhndb_generic_resume(dev));
865bb64eeccSAdrian Chadd }
866bb64eeccSAdrian Chadd 
867bb64eeccSAdrian Chadd static int
868bb64eeccSAdrian Chadd bhndb_pci_suspend(device_t dev)
869bb64eeccSAdrian Chadd {
870bb64eeccSAdrian Chadd 	struct bhndb_pci_softc	*sc;
871bb64eeccSAdrian Chadd 	int			 error;
872bb64eeccSAdrian Chadd 
873bb64eeccSAdrian Chadd 	sc = device_get_softc(dev);
874bb64eeccSAdrian Chadd 
875bb64eeccSAdrian Chadd 	/* Disable clocks (if supported by this hardware) */
87689294a78SLandon J. Fuller 	if ((error = bhndb_disable_pci_clocks(sc->dev)))
8774ad7e9b0SAdrian Chadd 		return (error);
8784ad7e9b0SAdrian Chadd 
879bb64eeccSAdrian Chadd 	/* Perform suspend */
880bb64eeccSAdrian Chadd 	return (bhndb_generic_suspend(dev));
881bb64eeccSAdrian Chadd }
882bb64eeccSAdrian Chadd 
883bb64eeccSAdrian Chadd static int
8844ad7e9b0SAdrian Chadd bhndb_pci_set_window_addr(device_t dev, const struct bhndb_regwin *rw,
8854ad7e9b0SAdrian Chadd     bhnd_addr_t addr)
8864ad7e9b0SAdrian Chadd {
8874ad7e9b0SAdrian Chadd 	struct bhndb_pci_softc *sc = device_get_softc(dev);
88889294a78SLandon J. Fuller 	return (sc->set_regwin(sc->dev, sc->parent, rw, addr));
8894ad7e9b0SAdrian Chadd }
8904ad7e9b0SAdrian Chadd 
8914ad7e9b0SAdrian Chadd /**
8924ad7e9b0SAdrian Chadd  * A siba(4) and bcma(4)-compatible bhndb_set_window_addr implementation.
8934ad7e9b0SAdrian Chadd  *
8944ad7e9b0SAdrian Chadd  * On siba(4) devices, it's possible that writing a PCI window register may
8954ad7e9b0SAdrian Chadd  * not succeed; it's necessary to immediately read the configuration register
8964ad7e9b0SAdrian Chadd  * and retry if not set to the desired value.
8974ad7e9b0SAdrian Chadd  *
8984ad7e9b0SAdrian Chadd  * This is not necessary on bcma(4) devices, but other than the overhead of
8994ad7e9b0SAdrian Chadd  * validating the register, there's no harm in performing the verification.
9004ad7e9b0SAdrian Chadd  */
9014ad7e9b0SAdrian Chadd static int
90289294a78SLandon J. Fuller bhndb_pci_compat_setregwin(device_t dev, device_t pci_dev,
9034ad7e9b0SAdrian Chadd     const struct bhndb_regwin *rw, bhnd_addr_t addr)
9044ad7e9b0SAdrian Chadd {
9054ad7e9b0SAdrian Chadd 	int		error;
906e83ce340SAdrian Chadd 	int		reg;
9074ad7e9b0SAdrian Chadd 
9084ad7e9b0SAdrian Chadd 	if (rw->win_type != BHNDB_REGWIN_T_DYN)
9094ad7e9b0SAdrian Chadd 		return (ENODEV);
9104ad7e9b0SAdrian Chadd 
911e83ce340SAdrian Chadd 	reg = rw->d.dyn.cfg_offset;
9124ad7e9b0SAdrian Chadd 	for (u_int i = 0; i < BHNDB_PCI_BARCTRL_WRITE_RETRY; i++) {
91389294a78SLandon J. Fuller 		if ((error = bhndb_pci_fast_setregwin(dev, pci_dev, rw, addr)))
9144ad7e9b0SAdrian Chadd 			return (error);
9154ad7e9b0SAdrian Chadd 
91689294a78SLandon J. Fuller 		if (pci_read_config(pci_dev, reg, 4) == addr)
9174ad7e9b0SAdrian Chadd 			return (0);
9184ad7e9b0SAdrian Chadd 
9194ad7e9b0SAdrian Chadd 		DELAY(10);
9204ad7e9b0SAdrian Chadd 	}
9214ad7e9b0SAdrian Chadd 
9224ad7e9b0SAdrian Chadd 	/* Unable to set window */
9234ad7e9b0SAdrian Chadd 	return (ENODEV);
9244ad7e9b0SAdrian Chadd }
9254ad7e9b0SAdrian Chadd 
9264ad7e9b0SAdrian Chadd /**
9274ad7e9b0SAdrian Chadd  * A bcma(4)-only bhndb_set_window_addr implementation.
9284ad7e9b0SAdrian Chadd  */
9294ad7e9b0SAdrian Chadd static int
93089294a78SLandon J. Fuller bhndb_pci_fast_setregwin(device_t dev, device_t pci_dev,
9314ad7e9b0SAdrian Chadd     const struct bhndb_regwin *rw, bhnd_addr_t addr)
9324ad7e9b0SAdrian Chadd {
9334ad7e9b0SAdrian Chadd 	/* The PCI bridge core only supports 32-bit addressing, regardless
9344ad7e9b0SAdrian Chadd 	 * of the bus' support for 64-bit addressing */
9354ad7e9b0SAdrian Chadd 	if (addr > UINT32_MAX)
9364ad7e9b0SAdrian Chadd 		return (ERANGE);
9374ad7e9b0SAdrian Chadd 
9384ad7e9b0SAdrian Chadd 	switch (rw->win_type) {
9394ad7e9b0SAdrian Chadd 	case BHNDB_REGWIN_T_DYN:
9404ad7e9b0SAdrian Chadd 		/* Addresses must be page aligned */
9414ad7e9b0SAdrian Chadd 		if (addr % rw->win_size != 0)
9424ad7e9b0SAdrian Chadd 			return (EINVAL);
9434ad7e9b0SAdrian Chadd 
94489294a78SLandon J. Fuller 		pci_write_config(pci_dev, rw->d.dyn.cfg_offset, addr, 4);
9454ad7e9b0SAdrian Chadd 		break;
9464ad7e9b0SAdrian Chadd 	default:
9474ad7e9b0SAdrian Chadd 		return (ENODEV);
9484ad7e9b0SAdrian Chadd 	}
9494ad7e9b0SAdrian Chadd 
9504ad7e9b0SAdrian Chadd 	return (0);
9514ad7e9b0SAdrian Chadd }
9524ad7e9b0SAdrian Chadd 
953d567592bSAdrian Chadd static int
954d567592bSAdrian Chadd bhndb_pci_populate_board_info(device_t dev, device_t child,
955d567592bSAdrian Chadd     struct bhnd_board_info *info)
956d567592bSAdrian Chadd {
957d567592bSAdrian Chadd 	struct bhndb_pci_softc	*sc;
958d567592bSAdrian Chadd 
959d567592bSAdrian Chadd 	sc = device_get_softc(dev);
960d567592bSAdrian Chadd 
9618ef24a0dSAdrian Chadd 	/*
9628ef24a0dSAdrian Chadd 	 * On a subset of Apple BCM4360 modules, always prefer the
9638ef24a0dSAdrian Chadd 	 * PCI subdevice to the SPROM-supplied boardtype.
9648ef24a0dSAdrian Chadd 	 *
9658ef24a0dSAdrian Chadd 	 * TODO:
9668ef24a0dSAdrian Chadd 	 *
9678ef24a0dSAdrian Chadd 	 * Broadcom's own drivers implement this override, and then later use
9688ef24a0dSAdrian Chadd 	 * the remapped BCM4360 board type to determine the required
9698ef24a0dSAdrian Chadd 	 * board-specific workarounds.
9708ef24a0dSAdrian Chadd 	 *
9718ef24a0dSAdrian Chadd 	 * Without access to this hardware, it's unclear why this mapping
9728ef24a0dSAdrian Chadd 	 * is done, and we must do the same. If we can survey the hardware
9738ef24a0dSAdrian Chadd 	 * in question, it may be possible to replace this behavior with
9748ef24a0dSAdrian Chadd 	 * explicit references to the SPROM-supplied boardtype(s) in our
9758ef24a0dSAdrian Chadd 	 * quirk definitions.
9768ef24a0dSAdrian Chadd 	 */
9778ef24a0dSAdrian Chadd 	if (pci_get_subvendor(sc->parent) == PCI_VENDOR_APPLE) {
9788ef24a0dSAdrian Chadd 		switch (info->board_type) {
9798ef24a0dSAdrian Chadd 		case BHND_BOARD_BCM94360X29C:
9808ef24a0dSAdrian Chadd 		case BHND_BOARD_BCM94360X29CP2:
9818ef24a0dSAdrian Chadd 		case BHND_BOARD_BCM94360X51:
9828ef24a0dSAdrian Chadd 		case BHND_BOARD_BCM94360X51P2:
9838ef24a0dSAdrian Chadd 			info->board_type = 0;	/* allow override below */
9848ef24a0dSAdrian Chadd 			break;
9858ef24a0dSAdrian Chadd 		default:
9868ef24a0dSAdrian Chadd 			break;
9878ef24a0dSAdrian Chadd 		}
9888ef24a0dSAdrian Chadd 	}
9898ef24a0dSAdrian Chadd 
990d567592bSAdrian Chadd 	/* If NVRAM did not supply vendor/type info, provide the PCI
991d567592bSAdrian Chadd 	 * subvendor/subdevice values. */
992d567592bSAdrian Chadd 	if (info->board_vendor == 0)
993d567592bSAdrian Chadd 		info->board_vendor = pci_get_subvendor(sc->parent);
994d567592bSAdrian Chadd 
995d567592bSAdrian Chadd 	if (info->board_type == 0)
996d567592bSAdrian Chadd 		info->board_type = pci_get_subdevice(sc->parent);
997d567592bSAdrian Chadd 
998d567592bSAdrian Chadd 	return (0);
999d567592bSAdrian Chadd }
1000d567592bSAdrian Chadd 
10014ad7e9b0SAdrian Chadd /**
1002*caeff9a3SLandon J. Fuller  * Examine the bridge device @p dev and return the expected host bridge
1003*caeff9a3SLandon J. Fuller  * device class.
1004*caeff9a3SLandon J. Fuller  *
1005*caeff9a3SLandon J. Fuller  * @param dev The bhndb bridge device
1006*caeff9a3SLandon J. Fuller  */
1007*caeff9a3SLandon J. Fuller static bhnd_devclass_t
1008*caeff9a3SLandon J. Fuller bhndb_expected_pci_devclass(device_t dev)
1009*caeff9a3SLandon J. Fuller {
1010*caeff9a3SLandon J. Fuller 	if (bhndb_is_pcie_attached(dev))
1011*caeff9a3SLandon J. Fuller 		return (BHND_DEVCLASS_PCIE);
1012*caeff9a3SLandon J. Fuller 	else
1013*caeff9a3SLandon J. Fuller 		return (BHND_DEVCLASS_PCI);
1014*caeff9a3SLandon J. Fuller }
1015*caeff9a3SLandon J. Fuller 
1016*caeff9a3SLandon J. Fuller /**
1017*caeff9a3SLandon J. Fuller  * Return true if the bridge device @p dev is attached via PCIe,
101889294a78SLandon J. Fuller  * false otherwise.
101989294a78SLandon J. Fuller  *
102089294a78SLandon J. Fuller  * @param dev The bhndb bridge device
102189294a78SLandon J. Fuller  */
102289294a78SLandon J. Fuller static bool
102389294a78SLandon J. Fuller bhndb_is_pcie_attached(device_t dev)
102489294a78SLandon J. Fuller {
102589294a78SLandon J. Fuller 	int reg;
102689294a78SLandon J. Fuller 
102789294a78SLandon J. Fuller 	if (pci_find_cap(device_get_parent(dev), PCIY_EXPRESS, &reg) == 0)
102889294a78SLandon J. Fuller 		return (true);
102989294a78SLandon J. Fuller 
103089294a78SLandon J. Fuller 	return (false);
103189294a78SLandon J. Fuller }
103289294a78SLandon J. Fuller 
103389294a78SLandon J. Fuller /**
1034bb64eeccSAdrian Chadd  * Enable externally managed clocks, if required.
10354ad7e9b0SAdrian Chadd  *
1036bb64eeccSAdrian Chadd  * Some PCI chipsets (BCM4306, possibly others) chips do not support
1037bb64eeccSAdrian Chadd  * the idle low-power clock. Clocking must be bootstrapped at
1038bb64eeccSAdrian Chadd  * attach/resume by directly adjusting GPIO registers exposed in the
1039bb64eeccSAdrian Chadd  * PCI config space, and correspondingly, explicitly shutdown at
1040bb64eeccSAdrian Chadd  * detach/suspend.
10414ad7e9b0SAdrian Chadd  *
104289294a78SLandon J. Fuller  * @note This function may be safely called prior to device attach, (e.g.
104389294a78SLandon J. Fuller  * from DEVICE_PROBE).
104489294a78SLandon J. Fuller  *
104589294a78SLandon J. Fuller  * @param dev The bhndb bridge device
10464ad7e9b0SAdrian Chadd  */
10474ad7e9b0SAdrian Chadd static int
104889294a78SLandon J. Fuller bhndb_enable_pci_clocks(device_t dev)
10494ad7e9b0SAdrian Chadd {
105089294a78SLandon J. Fuller 	device_t		pci_dev;
10514ad7e9b0SAdrian Chadd 	uint32_t		gpio_in, gpio_out, gpio_en;
10524ad7e9b0SAdrian Chadd 	uint32_t		gpio_flags;
10534ad7e9b0SAdrian Chadd 	uint16_t		pci_status;
10544ad7e9b0SAdrian Chadd 
105589294a78SLandon J. Fuller 	pci_dev = device_get_parent(dev);
105689294a78SLandon J. Fuller 
1057bb64eeccSAdrian Chadd 	/* Only supported and required on PCI devices */
105889294a78SLandon J. Fuller 	if (!bhndb_is_pcie_attached(dev))
1059bb64eeccSAdrian Chadd 		return (0);
10604ad7e9b0SAdrian Chadd 
10614ad7e9b0SAdrian Chadd 	/* Read state of XTAL pin */
106289294a78SLandon J. Fuller 	gpio_in = pci_read_config(pci_dev, BHNDB_PCI_GPIO_IN, 4);
10634ad7e9b0SAdrian Chadd 	if (gpio_in & BHNDB_PCI_GPIO_XTAL_ON)
10644ad7e9b0SAdrian Chadd 		return (0); /* already enabled */
10654ad7e9b0SAdrian Chadd 
10664ad7e9b0SAdrian Chadd 	/* Fetch current config */
106789294a78SLandon J. Fuller 	gpio_out = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUT, 4);
106889294a78SLandon J. Fuller 	gpio_en = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, 4);
10694ad7e9b0SAdrian Chadd 
10704ad7e9b0SAdrian Chadd 	/* Set PLL_OFF/XTAL_ON pins to HIGH and enable both pins */
10714ad7e9b0SAdrian Chadd 	gpio_flags = (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON);
10724ad7e9b0SAdrian Chadd 	gpio_out |= gpio_flags;
10734ad7e9b0SAdrian Chadd 	gpio_en |= gpio_flags;
10744ad7e9b0SAdrian Chadd 
107589294a78SLandon J. Fuller 	pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
107689294a78SLandon J. Fuller 	pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4);
10774ad7e9b0SAdrian Chadd 	DELAY(1000);
10784ad7e9b0SAdrian Chadd 
10794ad7e9b0SAdrian Chadd 	/* Reset PLL_OFF */
10804ad7e9b0SAdrian Chadd 	gpio_out &= ~BHNDB_PCI_GPIO_PLL_OFF;
108189294a78SLandon J. Fuller 	pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
10824ad7e9b0SAdrian Chadd 	DELAY(5000);
10834ad7e9b0SAdrian Chadd 
10844ad7e9b0SAdrian Chadd 	/* Clear any PCI 'sent target-abort' flag. */
108589294a78SLandon J. Fuller 	pci_status = pci_read_config(pci_dev, PCIR_STATUS, 2);
10864ad7e9b0SAdrian Chadd 	pci_status &= ~PCIM_STATUS_STABORT;
108789294a78SLandon J. Fuller 	pci_write_config(pci_dev, PCIR_STATUS, pci_status, 2);
10884ad7e9b0SAdrian Chadd 
10894ad7e9b0SAdrian Chadd 	return (0);
10904ad7e9b0SAdrian Chadd }
10914ad7e9b0SAdrian Chadd 
10924ad7e9b0SAdrian Chadd /**
1093bb64eeccSAdrian Chadd  * Disable externally managed clocks, if required.
10944ad7e9b0SAdrian Chadd  *
109589294a78SLandon J. Fuller  * This function may be safely called prior to device attach, (e.g.
109689294a78SLandon J. Fuller  * from DEVICE_PROBE).
109789294a78SLandon J. Fuller  *
109889294a78SLandon J. Fuller  * @param dev The bhndb bridge device
10994ad7e9b0SAdrian Chadd  */
11004ad7e9b0SAdrian Chadd static int
110189294a78SLandon J. Fuller bhndb_disable_pci_clocks(device_t dev)
11024ad7e9b0SAdrian Chadd {
110389294a78SLandon J. Fuller 	device_t	pci_dev;
11044ad7e9b0SAdrian Chadd 	uint32_t	gpio_out, gpio_en;
11054ad7e9b0SAdrian Chadd 
110689294a78SLandon J. Fuller 	pci_dev = device_get_parent(dev);
110789294a78SLandon J. Fuller 
1108bb64eeccSAdrian Chadd 	/* Only supported and required on PCI devices */
110989294a78SLandon J. Fuller 	if (bhndb_is_pcie_attached(dev))
1110bb64eeccSAdrian Chadd 		return (0);
11114ad7e9b0SAdrian Chadd 
11124ad7e9b0SAdrian Chadd 	/* Fetch current config */
111389294a78SLandon J. Fuller 	gpio_out = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUT, 4);
111489294a78SLandon J. Fuller 	gpio_en = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, 4);
11154ad7e9b0SAdrian Chadd 
11164ad7e9b0SAdrian Chadd 	/* Set PLL_OFF to HIGH, XTAL_ON to LOW. */
11174ad7e9b0SAdrian Chadd 	gpio_out &= ~BHNDB_PCI_GPIO_XTAL_ON;
11184ad7e9b0SAdrian Chadd 	gpio_out |= BHNDB_PCI_GPIO_PLL_OFF;
111989294a78SLandon J. Fuller 	pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
11204ad7e9b0SAdrian Chadd 
11214ad7e9b0SAdrian Chadd 	/* Enable both output pins */
11224ad7e9b0SAdrian Chadd 	gpio_en |= (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON);
112389294a78SLandon J. Fuller 	pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4);
11244ad7e9b0SAdrian Chadd 
11254ad7e9b0SAdrian Chadd 	return (0);
11264ad7e9b0SAdrian Chadd }
11274ad7e9b0SAdrian Chadd 
1128f90f4b65SLandon J. Fuller static bhnd_clksrc
1129f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_get_clksrc(device_t dev, device_t child,
1130f90f4b65SLandon J. Fuller 	bhnd_clock clock)
1131f90f4b65SLandon J. Fuller {
1132f90f4b65SLandon J. Fuller 	struct bhndb_pci_softc	*sc;
1133f90f4b65SLandon J. Fuller 	uint32_t		 gpio_out;
1134f90f4b65SLandon J. Fuller 
1135f90f4b65SLandon J. Fuller 	sc = device_get_softc(dev);
1136f90f4b65SLandon J. Fuller 
1137f90f4b65SLandon J. Fuller 	/* Only supported on PCI devices */
113889294a78SLandon J. Fuller 	if (bhndb_is_pcie_attached(sc->dev))
1139f90f4b65SLandon J. Fuller 		return (ENODEV);
1140f90f4b65SLandon J. Fuller 
1141f90f4b65SLandon J. Fuller 	/* Only ILP is supported */
1142f90f4b65SLandon J. Fuller 	if (clock != BHND_CLOCK_ILP)
1143f90f4b65SLandon J. Fuller 		return (ENXIO);
1144f90f4b65SLandon J. Fuller 
1145f90f4b65SLandon J. Fuller 	gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4);
1146f90f4b65SLandon J. Fuller 	if (gpio_out & BHNDB_PCI_GPIO_SCS)
1147f90f4b65SLandon J. Fuller 		return (BHND_CLKSRC_PCI);
1148f90f4b65SLandon J. Fuller 	else
1149f90f4b65SLandon J. Fuller 		return (BHND_CLKSRC_XTAL);
1150f90f4b65SLandon J. Fuller }
1151f90f4b65SLandon J. Fuller 
1152f90f4b65SLandon J. Fuller static int
1153f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_gate_clock(device_t dev, device_t child,
1154f90f4b65SLandon J. Fuller 	bhnd_clock clock)
1155f90f4b65SLandon J. Fuller {
1156f90f4b65SLandon J. Fuller 	struct bhndb_pci_softc *sc = device_get_softc(dev);
1157f90f4b65SLandon J. Fuller 
1158f90f4b65SLandon J. Fuller 	/* Only supported on PCI devices */
115989294a78SLandon J. Fuller 	if (bhndb_is_pcie_attached(sc->dev))
1160f90f4b65SLandon J. Fuller 		return (ENODEV);
1161f90f4b65SLandon J. Fuller 
1162f90f4b65SLandon J. Fuller 	/* Only HT is supported */
1163f90f4b65SLandon J. Fuller 	if (clock != BHND_CLOCK_HT)
1164f90f4b65SLandon J. Fuller 		return (ENXIO);
1165f90f4b65SLandon J. Fuller 
116689294a78SLandon J. Fuller 	return (bhndb_disable_pci_clocks(sc->dev));
1167f90f4b65SLandon J. Fuller }
1168f90f4b65SLandon J. Fuller 
1169f90f4b65SLandon J. Fuller static int
1170f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_ungate_clock(device_t dev, device_t child,
1171f90f4b65SLandon J. Fuller 	bhnd_clock clock)
1172f90f4b65SLandon J. Fuller {
1173f90f4b65SLandon J. Fuller 	struct bhndb_pci_softc *sc = device_get_softc(dev);
1174f90f4b65SLandon J. Fuller 
1175f90f4b65SLandon J. Fuller 	/* Only supported on PCI devices */
117689294a78SLandon J. Fuller 	if (bhndb_is_pcie_attached(sc->dev))
1177f90f4b65SLandon J. Fuller 		return (ENODEV);
1178f90f4b65SLandon J. Fuller 
1179f90f4b65SLandon J. Fuller 	/* Only HT is supported */
1180f90f4b65SLandon J. Fuller 	if (clock != BHND_CLOCK_HT)
1181f90f4b65SLandon J. Fuller 		return (ENXIO);
1182f90f4b65SLandon J. Fuller 
118389294a78SLandon J. Fuller 	return (bhndb_enable_pci_clocks(sc->dev));
1184f90f4b65SLandon J. Fuller }
1185f90f4b65SLandon J. Fuller 
1186*caeff9a3SLandon J. Fuller /**
1187*caeff9a3SLandon J. Fuller  * BHNDB_MAP_INTR_ISRC()
1188*caeff9a3SLandon J. Fuller  */
1189824b48efSLandon J. Fuller static int
1190*caeff9a3SLandon J. Fuller bhndb_pci_map_intr_isrc(device_t dev, struct resource *irq,
1191*caeff9a3SLandon J. Fuller     struct bhndb_intr_isrc **isrc)
1192*caeff9a3SLandon J. Fuller {
1193*caeff9a3SLandon J. Fuller 	struct bhndb_pci_softc *sc = device_get_softc(dev);
1194*caeff9a3SLandon J. Fuller 
1195*caeff9a3SLandon J. Fuller 	/* There's only one bridged interrupt to choose from */
1196*caeff9a3SLandon J. Fuller 	*isrc = sc->isrc;
1197*caeff9a3SLandon J. Fuller 	return (0);
1198*caeff9a3SLandon J. Fuller }
1199*caeff9a3SLandon J. Fuller 
1200*caeff9a3SLandon J. Fuller /* siba-specific implementation of BHNDB_ROUTE_INTERRUPTS() */
1201*caeff9a3SLandon J. Fuller static int
1202*caeff9a3SLandon J. Fuller bhndb_pci_route_siba_interrupts(struct bhndb_pci_softc *sc, device_t child)
1203*caeff9a3SLandon J. Fuller {
1204*caeff9a3SLandon J. Fuller 	uint32_t	sbintvec;
1205*caeff9a3SLandon J. Fuller 	u_int		ivec;
1206*caeff9a3SLandon J. Fuller 	int		error;
1207*caeff9a3SLandon J. Fuller 
1208*caeff9a3SLandon J. Fuller 	KASSERT(sc->pci_quirks & BHNDB_PCI_QUIRK_SIBA_INTVEC,
1209*caeff9a3SLandon J. Fuller 	    ("route_siba_interrupts not supported by this hardware"));
1210*caeff9a3SLandon J. Fuller 
1211*caeff9a3SLandon J. Fuller 	/* Fetch the sbflag# for the child */
1212*caeff9a3SLandon J. Fuller 	if ((error = bhnd_get_intr_ivec(child, 0, &ivec)))
1213*caeff9a3SLandon J. Fuller 		return (error);
1214*caeff9a3SLandon J. Fuller 
1215*caeff9a3SLandon J. Fuller 	if (ivec > (sizeof(sbintvec)*8) - 1 /* aka '31' */) {
1216*caeff9a3SLandon J. Fuller 		/* This should never be an issue in practice */
1217*caeff9a3SLandon J. Fuller 		device_printf(sc->dev, "cannot route interrupts to high "
1218*caeff9a3SLandon J. Fuller 		    "sbflag# %u\n", ivec);
1219*caeff9a3SLandon J. Fuller 		return (ENXIO);
1220*caeff9a3SLandon J. Fuller 	}
1221*caeff9a3SLandon J. Fuller 
1222*caeff9a3SLandon J. Fuller 	BHNDB_PCI_LOCK(sc);
1223*caeff9a3SLandon J. Fuller 
1224*caeff9a3SLandon J. Fuller 	sbintvec = bhndb_pci_read_core(sc, SB0_REG_ABS(SIBA_CFG0_INTVEC), 4);
1225*caeff9a3SLandon J. Fuller 	sbintvec |= (1 << ivec);
1226*caeff9a3SLandon J. Fuller 	bhndb_pci_write_core(sc, SB0_REG_ABS(SIBA_CFG0_INTVEC), sbintvec, 4);
1227*caeff9a3SLandon J. Fuller 
1228*caeff9a3SLandon J. Fuller 	BHNDB_PCI_UNLOCK(sc);
1229*caeff9a3SLandon J. Fuller 
1230*caeff9a3SLandon J. Fuller 	return (0);
1231*caeff9a3SLandon J. Fuller }
1232*caeff9a3SLandon J. Fuller 
1233*caeff9a3SLandon J. Fuller /* BHNDB_ROUTE_INTERRUPTS() */
1234*caeff9a3SLandon J. Fuller static int
1235*caeff9a3SLandon J. Fuller bhndb_pci_route_interrupts(device_t dev, device_t child)
1236824b48efSLandon J. Fuller {
1237824b48efSLandon J. Fuller 	struct bhndb_pci_softc	*sc;
1238*caeff9a3SLandon J. Fuller 	struct bhnd_core_info	 core;
1239*caeff9a3SLandon J. Fuller 	uint32_t		 core_bit;
1240*caeff9a3SLandon J. Fuller 	uint32_t		 intmask;
1241824b48efSLandon J. Fuller 
1242824b48efSLandon J. Fuller 	sc = device_get_softc(dev);
1243824b48efSLandon J. Fuller 
1244*caeff9a3SLandon J. Fuller 	if (sc->pci_quirks & BHNDB_PCI_QUIRK_SIBA_INTVEC)
1245*caeff9a3SLandon J. Fuller 		return (bhndb_pci_route_siba_interrupts(sc, child));
1246824b48efSLandon J. Fuller 
1247*caeff9a3SLandon J. Fuller 	core = bhnd_get_core_info(child);
1248*caeff9a3SLandon J. Fuller 	if (core.core_idx > BHNDB_PCI_SBIM_COREIDX_MAX) {
1249*caeff9a3SLandon J. Fuller 		/* This should never be an issue in practice */
1250*caeff9a3SLandon J. Fuller 		device_printf(dev, "cannot route interrupts to high core "
1251*caeff9a3SLandon J. Fuller 		    "index %u\n", core.core_idx);
1252*caeff9a3SLandon J. Fuller 		return (ENXIO);
1253*caeff9a3SLandon J. Fuller 	}
1254824b48efSLandon J. Fuller 
1255*caeff9a3SLandon J. Fuller 	BHNDB_PCI_LOCK(sc);
1256*caeff9a3SLandon J. Fuller 
1257*caeff9a3SLandon J. Fuller 	core_bit = (1<<core.core_idx) << BHNDB_PCI_SBIM_SHIFT;
1258*caeff9a3SLandon J. Fuller 	intmask = pci_read_config(sc->parent, BHNDB_PCI_INT_MASK, 4);
1259*caeff9a3SLandon J. Fuller 	intmask |= core_bit;
1260*caeff9a3SLandon J. Fuller 	pci_write_config(sc->parent, BHNDB_PCI_INT_MASK, intmask, 4);
1261*caeff9a3SLandon J. Fuller 
1262*caeff9a3SLandon J. Fuller 	BHNDB_PCI_UNLOCK(sc);
1263*caeff9a3SLandon J. Fuller 
1264*caeff9a3SLandon J. Fuller 	return (0);
1265824b48efSLandon J. Fuller }
1266824b48efSLandon J. Fuller 
126789294a78SLandon J. Fuller /**
126889294a78SLandon J. Fuller  * Initialize a new bhndb PCI bridge EROM I/O instance. This EROM I/O
126989294a78SLandon J. Fuller  * implementation supports mapping of the device enumeration table via the
127089294a78SLandon J. Fuller  * @p hr host resources.
127189294a78SLandon J. Fuller  *
127289294a78SLandon J. Fuller  * @param pio		The instance to be initialized.
127389294a78SLandon J. Fuller  * @param dev		The bridge device.
127489294a78SLandon J. Fuller  * @param pci_dev	The bridge's parent PCI device.
127589294a78SLandon J. Fuller  * @param hr		The host resources to be used to map the device
127689294a78SLandon J. Fuller  *			enumeration table.
127789294a78SLandon J. Fuller  */
127889294a78SLandon J. Fuller static int
127989294a78SLandon J. Fuller bhndb_pci_eio_init(struct bhndb_pci_eio *pio, device_t dev, device_t pci_dev,
128089294a78SLandon J. Fuller     struct bhndb_host_resources *hr)
128189294a78SLandon J. Fuller {
128289294a78SLandon J. Fuller 	memset(&pio->eio, sizeof(pio->eio), 0);
128389294a78SLandon J. Fuller 	pio->eio.map = bhndb_pci_eio_map;
128489294a78SLandon J. Fuller 	pio->eio.read = bhndb_pci_eio_read;
128589294a78SLandon J. Fuller 	pio->eio.fini = NULL;
128689294a78SLandon J. Fuller 
128789294a78SLandon J. Fuller 	pio->dev = dev;
128889294a78SLandon J. Fuller 	pio->pci_dev = pci_dev;
128989294a78SLandon J. Fuller 	pio->hr = hr;
129089294a78SLandon J. Fuller 	pio->win = NULL;
129189294a78SLandon J. Fuller 	pio->res = NULL;
129289294a78SLandon J. Fuller 
129389294a78SLandon J. Fuller 	return (0);
129489294a78SLandon J. Fuller }
129589294a78SLandon J. Fuller 
129689294a78SLandon J. Fuller /**
129789294a78SLandon J. Fuller  * Attempt to adjust the dynamic register window backing @p pio to permit
129889294a78SLandon J. Fuller  * reading @p size bytes at @p addr.
129989294a78SLandon J. Fuller  *
130089294a78SLandon J. Fuller  * If @p addr or @p size fall outside the existing mapped range, or if
130189294a78SLandon J. Fuller  * @p pio is not backed by a dynamic register window, ENXIO will be returned.
130289294a78SLandon J. Fuller  *
130389294a78SLandon J. Fuller  * @param pio	The bhndb PCI erom I/O state to be modified.
130489294a78SLandon J. Fuller  * @param addr	The address to be include
130589294a78SLandon J. Fuller  */
130689294a78SLandon J. Fuller static int
130789294a78SLandon J. Fuller bhndb_pci_eio_adjust_mapping(struct bhndb_pci_eio *pio, bhnd_addr_t addr,
130889294a78SLandon J. Fuller     bhnd_size_t size)
130989294a78SLandon J. Fuller {
131089294a78SLandon J. Fuller 	bhnd_addr_t	 target;
131189294a78SLandon J. Fuller 	bhnd_size_t	 offset;
131289294a78SLandon J. Fuller 	int		 error;
131389294a78SLandon J. Fuller 
131489294a78SLandon J. Fuller 
131589294a78SLandon J. Fuller 	KASSERT(pio->win != NULL, ("missing register window"));
131689294a78SLandon J. Fuller 	KASSERT(pio->res != NULL, ("missing regwin resource"));
131789294a78SLandon J. Fuller 	KASSERT(pio->win->win_type == BHNDB_REGWIN_T_DYN,
131889294a78SLandon J. Fuller 	    ("unexpected window type %d", pio->win->win_type));
131989294a78SLandon J. Fuller 
132089294a78SLandon J. Fuller 	/* The requested subrange must fall within the total mapped range */
132189294a78SLandon J. Fuller 	if (addr < pio->addr || (addr - pio->addr) > pio->size ||
132289294a78SLandon J. Fuller 	    size > pio->size || (addr - pio->addr) - pio->size < size)
132389294a78SLandon J. Fuller 	{
132489294a78SLandon J. Fuller 		return (ENXIO);
132589294a78SLandon J. Fuller 	}
132689294a78SLandon J. Fuller 
132789294a78SLandon J. Fuller 	/* Do we already have a useable mapping? */
132889294a78SLandon J. Fuller 	if (addr >= pio->res_target &&
132989294a78SLandon J. Fuller 	    addr <= pio->res_target + pio->win->win_size &&
133089294a78SLandon J. Fuller 	    (pio->res_target + pio->win->win_size) - addr >= size)
133189294a78SLandon J. Fuller 	{
133289294a78SLandon J. Fuller 		return (0);
133389294a78SLandon J. Fuller 	}
133489294a78SLandon J. Fuller 
133589294a78SLandon J. Fuller 	/* Page-align the target address */
133689294a78SLandon J. Fuller 	offset = addr % pio->win->win_size;
133789294a78SLandon J. Fuller 	target = addr - offset;
133889294a78SLandon J. Fuller 
133989294a78SLandon J. Fuller 	/* Configure the register window */
134089294a78SLandon J. Fuller 	error = bhndb_pci_compat_setregwin(pio->dev, pio->pci_dev, pio->win,
134189294a78SLandon J. Fuller 	    target);
134289294a78SLandon J. Fuller 	if (error) {
134389294a78SLandon J. Fuller 		device_printf(pio->dev, "failed to configure dynamic register "
134489294a78SLandon J. Fuller 		    "window: %d\n", error);
134589294a78SLandon J. Fuller 		return (error);
134689294a78SLandon J. Fuller 	}
134789294a78SLandon J. Fuller 
134889294a78SLandon J. Fuller 	pio->res_target = target;
134989294a78SLandon J. Fuller 	return (0);
135089294a78SLandon J. Fuller }
135189294a78SLandon J. Fuller 
135289294a78SLandon J. Fuller /* bhnd_erom_io_map() implementation */
135389294a78SLandon J. Fuller static int
135489294a78SLandon J. Fuller bhndb_pci_eio_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
135589294a78SLandon J. Fuller     bhnd_size_t size)
135689294a78SLandon J. Fuller {
135789294a78SLandon J. Fuller 	struct bhndb_pci_eio		*pio;
135889294a78SLandon J. Fuller 	const struct bhndb_regwin	*regwin;
135989294a78SLandon J. Fuller 	struct resource			*r;
136089294a78SLandon J. Fuller 	bhnd_addr_t			 target;
136189294a78SLandon J. Fuller 	bhnd_size_t			 offset;
136289294a78SLandon J. Fuller 	int				 error;
136389294a78SLandon J. Fuller 
136489294a78SLandon J. Fuller 	pio = (struct bhndb_pci_eio *)eio;
136589294a78SLandon J. Fuller 
136689294a78SLandon J. Fuller 	/* Locate a useable dynamic register window */
136789294a78SLandon J. Fuller 	regwin = bhndb_regwin_find_type(pio->hr->cfg->register_windows,
136889294a78SLandon J. Fuller 	    BHNDB_REGWIN_T_DYN, MIN(size, BHND_DEFAULT_CORE_SIZE));
136989294a78SLandon J. Fuller 	if (regwin == NULL) {
137089294a78SLandon J. Fuller 		device_printf(pio->dev, "unable to map %#jx+%#jx; no "
137189294a78SLandon J. Fuller 		    "usable dynamic register window found\n", addr, size);
137289294a78SLandon J. Fuller 		return (ENXIO);
137389294a78SLandon J. Fuller 	}
137489294a78SLandon J. Fuller 
137589294a78SLandon J. Fuller 	/* Locate the host resource mapping our register window */
137689294a78SLandon J. Fuller 	if ((r = bhndb_host_resource_for_regwin(pio->hr, regwin)) == NULL) {
137789294a78SLandon J. Fuller 		device_printf(pio->dev, "unable to map %#jx+%#jx; no "
137889294a78SLandon J. Fuller 		    "usable register resource found\n", addr, size);
137989294a78SLandon J. Fuller 		return (ENXIO);
138089294a78SLandon J. Fuller 	}
138189294a78SLandon J. Fuller 
138289294a78SLandon J. Fuller 	/* Page-align the target address */
138389294a78SLandon J. Fuller 	offset = addr % regwin->win_size;
138489294a78SLandon J. Fuller 	target = addr - offset;
138589294a78SLandon J. Fuller 
138689294a78SLandon J. Fuller 	/* Configure the register window */
138789294a78SLandon J. Fuller 	error = bhndb_pci_compat_setregwin(pio->dev, pio->pci_dev, regwin,
138889294a78SLandon J. Fuller 	    target);
138989294a78SLandon J. Fuller 	if (error) {
139089294a78SLandon J. Fuller 		device_printf(pio->dev, "failed to configure dynamic register "
139189294a78SLandon J. Fuller 		    "window: %d\n", error);
139289294a78SLandon J. Fuller 		return (error);
139389294a78SLandon J. Fuller 	}
139489294a78SLandon J. Fuller 
139589294a78SLandon J. Fuller 	/* Update our mapping state */
139689294a78SLandon J. Fuller 	pio->win = regwin;
139789294a78SLandon J. Fuller 	pio->res = r;
139889294a78SLandon J. Fuller 	pio->addr = addr;
139989294a78SLandon J. Fuller 	pio->size = size;
140089294a78SLandon J. Fuller 	pio->res_target = target;
140189294a78SLandon J. Fuller 
140289294a78SLandon J. Fuller 	return (0);
140389294a78SLandon J. Fuller }
140489294a78SLandon J. Fuller 
140589294a78SLandon J. Fuller /* bhnd_erom_io_read() implementation */
140689294a78SLandon J. Fuller static uint32_t
140789294a78SLandon J. Fuller bhndb_pci_eio_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
140889294a78SLandon J. Fuller {
140989294a78SLandon J. Fuller 	struct bhndb_pci_eio		*pio;
141089294a78SLandon J. Fuller 	bhnd_addr_t			 addr;
141189294a78SLandon J. Fuller 	bus_size_t			 res_offset;
141289294a78SLandon J. Fuller 	int				 error;
141389294a78SLandon J. Fuller 
141489294a78SLandon J. Fuller 	pio = (struct bhndb_pci_eio *)eio;
141589294a78SLandon J. Fuller 
141689294a78SLandon J. Fuller 	/* Calculate absolute address */
141789294a78SLandon J. Fuller 	if (BHND_SIZE_MAX - offset < pio->addr) {
141889294a78SLandon J. Fuller 		device_printf(pio->dev, "invalid offset %#jx+%#jx\n", pio->addr,
141989294a78SLandon J. Fuller 		    offset);
142089294a78SLandon J. Fuller 		return (UINT32_MAX);
142189294a78SLandon J. Fuller 	}
142289294a78SLandon J. Fuller 
142389294a78SLandon J. Fuller 	addr = pio->addr + offset;
142489294a78SLandon J. Fuller 
142589294a78SLandon J. Fuller 	/* Adjust the mapping for our read */
142689294a78SLandon J. Fuller 	if ((error = bhndb_pci_eio_adjust_mapping(pio, addr, width))) {
142789294a78SLandon J. Fuller 		device_printf(pio->dev, "failed to adjust register mapping: "
142889294a78SLandon J. Fuller 		    "%d\n", error);
142989294a78SLandon J. Fuller 		return (UINT32_MAX);
143089294a78SLandon J. Fuller 	}
143189294a78SLandon J. Fuller 
143289294a78SLandon J. Fuller 	KASSERT(pio->res_target <= addr, ("invalid mapping (%#jx vs. %#jx)",
143389294a78SLandon J. Fuller 	    pio->res_target, addr));
143489294a78SLandon J. Fuller 
143589294a78SLandon J. Fuller 	/* Determine the actual read offset within our register window
143689294a78SLandon J. Fuller 	 * resource */
143789294a78SLandon J. Fuller 	res_offset = (addr - pio->res_target) + pio->win->win_offset;
143889294a78SLandon J. Fuller 
143989294a78SLandon J. Fuller 	/* Perform our read */
144089294a78SLandon J. Fuller 	switch (width) {
144189294a78SLandon J. Fuller 	case 1:
144289294a78SLandon J. Fuller 		return (bus_read_1(pio->res, res_offset));
144389294a78SLandon J. Fuller 	case 2:
144489294a78SLandon J. Fuller 		return (bus_read_2(pio->res, res_offset));
144589294a78SLandon J. Fuller 	case 4:
144689294a78SLandon J. Fuller 		return (bus_read_4(pio->res, res_offset));
144789294a78SLandon J. Fuller 	default:
144889294a78SLandon J. Fuller 		panic("unsupported width: %u", width);
144989294a78SLandon J. Fuller 	}
145089294a78SLandon J. Fuller }
145189294a78SLandon J. Fuller 
14524ad7e9b0SAdrian Chadd static device_method_t bhndb_pci_methods[] = {
14534ad7e9b0SAdrian Chadd 	/* Device interface */
14544ad7e9b0SAdrian Chadd 	DEVMETHOD(device_probe,			bhndb_pci_probe),
14554ad7e9b0SAdrian Chadd 	DEVMETHOD(device_attach,		bhndb_pci_attach),
14564ad7e9b0SAdrian Chadd 	DEVMETHOD(device_resume,		bhndb_pci_resume),
1457bb64eeccSAdrian Chadd 	DEVMETHOD(device_suspend,		bhndb_pci_suspend),
1458bb64eeccSAdrian Chadd 	DEVMETHOD(device_detach,		bhndb_pci_detach),
14594ad7e9b0SAdrian Chadd 
1460f90f4b65SLandon J. Fuller 	/* BHND interface */
1461f90f4b65SLandon J. Fuller 	DEVMETHOD(bhnd_bus_pwrctl_get_clksrc,	bhndb_pci_pwrctl_get_clksrc),
1462f90f4b65SLandon J. Fuller 	DEVMETHOD(bhnd_bus_pwrctl_gate_clock,	bhndb_pci_pwrctl_gate_clock),
1463f90f4b65SLandon J. Fuller 	DEVMETHOD(bhnd_bus_pwrctl_ungate_clock,	bhndb_pci_pwrctl_ungate_clock),
1464f90f4b65SLandon J. Fuller 
14654ad7e9b0SAdrian Chadd 	/* BHNDB interface */
14664ad7e9b0SAdrian Chadd 	DEVMETHOD(bhndb_set_window_addr,	bhndb_pci_set_window_addr),
1467d567592bSAdrian Chadd 	DEVMETHOD(bhndb_populate_board_info,	bhndb_pci_populate_board_info),
1468*caeff9a3SLandon J. Fuller 	DEVMETHOD(bhndb_map_intr_isrc,		bhndb_pci_map_intr_isrc),
1469*caeff9a3SLandon J. Fuller 	DEVMETHOD(bhndb_route_interrupts,	bhndb_pci_route_interrupts),
14704ad7e9b0SAdrian Chadd 
14714ad7e9b0SAdrian Chadd 	DEVMETHOD_END
14724ad7e9b0SAdrian Chadd };
14734ad7e9b0SAdrian Chadd 
14744ad7e9b0SAdrian Chadd DEFINE_CLASS_1(bhndb, bhndb_pci_driver, bhndb_pci_methods,
14754ad7e9b0SAdrian Chadd     sizeof(struct bhndb_pci_softc), bhndb_driver);
14764ad7e9b0SAdrian Chadd 
14774ad7e9b0SAdrian Chadd MODULE_VERSION(bhndb_pci, 1);
1478148ed571SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhnd_pci_hostb, 1, 1, 1);
14794ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb_pci, pci, 1, 1, 1);
14804ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhndb, 1, 1, 1);
1481148ed571SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhnd, 1, 1, 1);
1482