xref: /freebsd/sys/dev/bhnd/bhndb/bhndb_pci.c (revision 18250ec6c089c0c50cbd9fd87d78e03ff89916df)
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 /*
364ad7e9b0SAdrian Chadd  * PCI-specific implementation for the BHNDB bridge driver.
374ad7e9b0SAdrian Chadd  *
384ad7e9b0SAdrian Chadd  * Provides support for bridging from a PCI parent bus to a BHND-compatible
394ad7e9b0SAdrian Chadd  * bus (e.g. bcma or siba) via a Broadcom PCI core configured in end-point
404ad7e9b0SAdrian Chadd  * mode.
414ad7e9b0SAdrian Chadd  *
428ef24a0dSAdrian Chadd  * This driver handles all initial generic host-level PCI interactions with a
438ef24a0dSAdrian Chadd  * PCI/PCIe bridge core operating in endpoint mode. Once the bridged bhnd(4)
448ef24a0dSAdrian Chadd  * bus has been enumerated, this driver works in tandem with a core-specific
458ef24a0dSAdrian Chadd  * bhnd_pci_hostb driver to manage the PCI core.
464ad7e9b0SAdrian Chadd  */
474ad7e9b0SAdrian Chadd 
484ad7e9b0SAdrian Chadd #include <sys/param.h>
494ad7e9b0SAdrian Chadd #include <sys/kernel.h>
504ad7e9b0SAdrian Chadd #include <sys/bus.h>
514ad7e9b0SAdrian Chadd #include <sys/limits.h>
524ad7e9b0SAdrian Chadd #include <sys/malloc.h>
534ad7e9b0SAdrian Chadd #include <sys/module.h>
544ad7e9b0SAdrian Chadd #include <sys/systm.h>
554ad7e9b0SAdrian Chadd 
564ad7e9b0SAdrian Chadd #include <dev/pci/pcireg.h>
574ad7e9b0SAdrian Chadd #include <dev/pci/pcivar.h>
584ad7e9b0SAdrian Chadd 
594ad7e9b0SAdrian Chadd #include <dev/bhnd/bhnd.h>
6089294a78SLandon J. Fuller #include <dev/bhnd/bhndreg.h>
6189294a78SLandon J. Fuller 
6289294a78SLandon J. Fuller #include <dev/bhnd/bhnd_erom.h>
6389294a78SLandon J. Fuller #include <dev/bhnd/bhnd_eromvar.h>
644ad7e9b0SAdrian Chadd 
65caeff9a3SLandon J. Fuller #include <dev/bhnd/siba/sibareg.h>
66caeff9a3SLandon J. Fuller 
674ad7e9b0SAdrian Chadd #include <dev/bhnd/cores/pci/bhnd_pcireg.h>
684ad7e9b0SAdrian Chadd 
694e96bf3aSLandon J. Fuller #include "bhnd_pwrctl_hostb_if.h"
704e96bf3aSLandon J. Fuller 
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;
7684d6a5d4SLandon J. Fuller struct bhndb_pci_probe;
7789294a78SLandon J. Fuller 
78caeff9a3SLandon J. Fuller static int		bhndb_pci_alloc_msi(struct bhndb_pci_softc *sc,
79caeff9a3SLandon J. Fuller 			    int *msi_count);
8084d6a5d4SLandon J. Fuller 
81111d7cb2SLandon J. Fuller static int		bhndb_pci_add_children(struct bhndb_pci_softc *sc);
82111d7cb2SLandon J. Fuller 
83caeff9a3SLandon J. Fuller static bhnd_devclass_t	bhndb_expected_pci_devclass(device_t dev);
8489294a78SLandon J. Fuller static bool		bhndb_is_pcie_attached(device_t dev);
854ad7e9b0SAdrian Chadd 
8689294a78SLandon J. Fuller static int		bhndb_enable_pci_clocks(device_t dev);
8789294a78SLandon J. Fuller static int		bhndb_disable_pci_clocks(device_t dev);
8889294a78SLandon J. Fuller 
8989294a78SLandon J. Fuller static int		bhndb_pci_compat_setregwin(device_t dev,
9089294a78SLandon J. Fuller 			    device_t pci_dev, const struct bhndb_regwin *,
9189294a78SLandon J. Fuller 			    bhnd_addr_t);
9289294a78SLandon J. Fuller static int		bhndb_pci_fast_setregwin(device_t dev, device_t pci_dev,
934ad7e9b0SAdrian Chadd 			    const struct bhndb_regwin *, bhnd_addr_t);
944ad7e9b0SAdrian Chadd 
95caeff9a3SLandon J. Fuller static void		bhndb_pci_write_core(struct bhndb_pci_softc *sc,
96caeff9a3SLandon J. Fuller 			    bus_size_t offset, uint32_t value, u_int width);
97caeff9a3SLandon J. Fuller static uint32_t		bhndb_pci_read_core(struct bhndb_pci_softc *sc,
98caeff9a3SLandon J. Fuller 			    bus_size_t offset, u_int width);
99caeff9a3SLandon J. Fuller 
10084d6a5d4SLandon J. Fuller static int		bhndb_pci_srsh_pi_war(struct bhndb_pci_softc *sc,
10184d6a5d4SLandon J. Fuller 			    struct bhndb_pci_probe *probe);
102e83ce340SAdrian Chadd 
103e83ce340SAdrian Chadd static bus_addr_t	bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc);
104f9cf87a0SAdrian Chadd static bus_size_t	bhndb_pci_sprom_size(struct bhndb_pci_softc *sc);
1054ad7e9b0SAdrian Chadd 
10684d6a5d4SLandon J. Fuller static int		bhndb_pci_probe_alloc(struct bhndb_pci_probe **probe,
10784d6a5d4SLandon J. Fuller 			    device_t dev, bhnd_devclass_t pci_devclass);
10884d6a5d4SLandon J. Fuller static void		bhndb_pci_probe_free(struct bhndb_pci_probe *probe);
10984d6a5d4SLandon J. Fuller 
11084d6a5d4SLandon J. Fuller static int		bhndb_pci_probe_copy_core_table(
11184d6a5d4SLandon J. Fuller 			    struct bhndb_pci_probe *probe,
11284d6a5d4SLandon J. Fuller 			    struct bhnd_core_info **cores, u_int *ncores);
11384d6a5d4SLandon J. Fuller static void		bhndb_pci_probe_free_core_table(
11484d6a5d4SLandon J. Fuller 			    struct bhnd_core_info *cores);
11584d6a5d4SLandon J. Fuller 
11684d6a5d4SLandon J. Fuller static void		bhndb_pci_probe_write(struct bhndb_pci_probe *sc,
11784d6a5d4SLandon J. Fuller 			    bhnd_addr_t addr, bhnd_size_t offset,
11884d6a5d4SLandon J. Fuller 			    uint32_t value, u_int width);
11984d6a5d4SLandon J. Fuller static uint32_t		bhndb_pci_probe_read(struct bhndb_pci_probe *sc,
12084d6a5d4SLandon J. Fuller 			    bhnd_addr_t addr, bhnd_size_t offset, u_int width);
12184d6a5d4SLandon J. Fuller 
12284d6a5d4SLandon J. Fuller static void		bhndb_pci_eio_init(struct bhndb_pci_eio *eio,
12384d6a5d4SLandon J. Fuller 			    struct bhndb_pci_probe *probe);
12489294a78SLandon J. Fuller static int		bhndb_pci_eio_map(struct bhnd_erom_io *eio,
12589294a78SLandon J. Fuller 			    bhnd_addr_t addr, bhnd_size_t size);
126f3524ec8SLandon J. Fuller static int		bhndb_pci_eio_tell(struct bhnd_erom_io *eio,
127f3524ec8SLandon J. Fuller 			    bhnd_addr_t *addr, bhnd_size_t *size);
12889294a78SLandon J. Fuller static uint32_t		bhndb_pci_eio_read(struct bhnd_erom_io *eio,
12989294a78SLandon J. Fuller 			    bhnd_size_t offset, u_int width);
13089294a78SLandon J. Fuller 
131824b48efSLandon J. Fuller #define	BHNDB_PCI_MSI_COUNT	1
132824b48efSLandon J. Fuller 
133caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk	bhndb_pci_quirks[];
134caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk	bhndb_pcie_quirks[];
135caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk	bhndb_pcie2_quirks[];
136caeff9a3SLandon J. Fuller 
137caeff9a3SLandon J. Fuller static struct bhndb_pci_core bhndb_pci_cores[] = {
13884d6a5d4SLandon J. Fuller 	BHNDB_PCI_CORE(PCI,	bhndb_pci_quirks),
13984d6a5d4SLandon J. Fuller 	BHNDB_PCI_CORE(PCIE,	bhndb_pcie_quirks),
14084d6a5d4SLandon J. Fuller 	BHNDB_PCI_CORE(PCIE2,	bhndb_pcie2_quirks),
141caeff9a3SLandon J. Fuller 	BHNDB_PCI_CORE_END
142caeff9a3SLandon J. Fuller };
143caeff9a3SLandon J. Fuller 
144caeff9a3SLandon J. Fuller /* bhndb_pci erom I/O instance state */
14589294a78SLandon J. Fuller struct bhndb_pci_eio {
14689294a78SLandon J. Fuller 	struct bhnd_erom_io		 eio;
147f3524ec8SLandon J. Fuller 	bool				 mapped;	/**< true if a valid mapping exists */
14889294a78SLandon J. Fuller 	bhnd_addr_t			 addr;		/**< mapped address */
14989294a78SLandon J. Fuller 	bhnd_size_t			 size;		/**< mapped size */
15084d6a5d4SLandon J. Fuller 	struct bhndb_pci_probe		*probe;		/**< borrowed probe reference */
15189294a78SLandon J. Fuller };
15289294a78SLandon J. Fuller 
15384d6a5d4SLandon J. Fuller /**
15484d6a5d4SLandon J. Fuller  * Provides early bus access to the bridged device's cores and core enumeration
15584d6a5d4SLandon J. Fuller  * table.
15684d6a5d4SLandon J. Fuller  *
15784d6a5d4SLandon J. Fuller  * May be safely used during probe or early device attach, prior to calling
15884d6a5d4SLandon J. Fuller  * bhndb_attach().
15984d6a5d4SLandon J. Fuller  */
16084d6a5d4SLandon J. Fuller struct bhndb_pci_probe {
16184d6a5d4SLandon J. Fuller 	device_t			 dev;		/**< bridge device */
16284d6a5d4SLandon J. Fuller 	device_t			 pci_dev;	/**< parent PCI device */
16384d6a5d4SLandon J. Fuller 	struct bhnd_chipid		 cid;		/**< chip identification */
16484d6a5d4SLandon J. Fuller 	struct bhnd_core_info		 hostb_core;	/**< PCI bridge core info */
16584d6a5d4SLandon J. Fuller 
16684d6a5d4SLandon J. Fuller 	struct bhndb_pci_eio		 erom_io;	/**< erom I/O instance */
16784d6a5d4SLandon J. Fuller 	bhnd_erom_class_t		*erom_class;	/**< probed erom class */
16884d6a5d4SLandon J. Fuller 	bhnd_erom_t			*erom;		/**< erom parser */
16984d6a5d4SLandon J. Fuller 	struct bhnd_core_info		*cores;		/**< erom-owned core table */
17084d6a5d4SLandon J. Fuller 	u_int				 ncores;	/**< number of cores */
17184d6a5d4SLandon J. Fuller 
17284d6a5d4SLandon J. Fuller 	const struct bhndb_regwin	*m_win;		/**< mapped register window, or NULL if no mapping */
17384d6a5d4SLandon J. Fuller 	struct resource			*m_res;		/**< resource containing the register window, or NULL if no window mapped */
17484d6a5d4SLandon J. Fuller 	bhnd_addr_t			 m_target;	/**< base address mapped by m_win */
17584d6a5d4SLandon J. Fuller 	bhnd_addr_t			 m_addr;	/**< mapped address */
17684d6a5d4SLandon J. Fuller 	bhnd_size_t			 m_size;	/**< mapped size */
17784d6a5d4SLandon J. Fuller 	bool				 m_valid;	/**< true if a valid mapping exists, false otherwise */
17884d6a5d4SLandon J. Fuller 
17984d6a5d4SLandon J. Fuller 	struct bhndb_host_resources	*hr;		/**< backing host resources */
18084d6a5d4SLandon J. Fuller };
18184d6a5d4SLandon J. Fuller 
182caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk bhndb_pci_quirks[] = {
183caeff9a3SLandon J. Fuller 	/* Backplane interrupt flags must be routed via siba-specific
184caeff9a3SLandon J. Fuller 	 * SIBA_CFG0_INTVEC configuration register; the BHNDB_PCI_INT_MASK
185caeff9a3SLandon J. Fuller 	 * PCI configuration register is unsupported. */
186caeff9a3SLandon J. Fuller 	{{ BHND_MATCH_CHIP_TYPE		(SIBA) },
187caeff9a3SLandon J. Fuller 	 { BHND_MATCH_CORE_REV		(HWREV_LTE(5)) },
188caeff9a3SLandon J. Fuller 		BHNDB_PCI_QUIRK_SIBA_INTVEC },
189caeff9a3SLandon J. Fuller 
190caeff9a3SLandon J. Fuller 	/* All PCI core revisions require the SRSH work-around */
191caeff9a3SLandon J. Fuller 	BHNDB_PCI_QUIRK(HWREV_ANY,	BHNDB_PCI_QUIRK_SRSH_WAR),
192caeff9a3SLandon J. Fuller 	BHNDB_PCI_QUIRK_END
193caeff9a3SLandon J. Fuller };
194caeff9a3SLandon J. Fuller 
195caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk bhndb_pcie_quirks[] = {
196caeff9a3SLandon J. Fuller 	/* All PCIe-G1 core revisions require the SRSH work-around */
197caeff9a3SLandon J. Fuller 	BHNDB_PCI_QUIRK(HWREV_ANY,	BHNDB_PCI_QUIRK_SRSH_WAR),
198caeff9a3SLandon J. Fuller 	BHNDB_PCI_QUIRK_END
199caeff9a3SLandon J. Fuller };
200caeff9a3SLandon J. Fuller 
201caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk bhndb_pcie2_quirks[] = {
202caeff9a3SLandon J. Fuller 	BHNDB_PCI_QUIRK_END
203caeff9a3SLandon J. Fuller };
204caeff9a3SLandon J. Fuller 
205caeff9a3SLandon J. Fuller /**
206caeff9a3SLandon J. Fuller  * Return the device table entry for @p ci, or NULL if none.
207caeff9a3SLandon J. Fuller  */
208caeff9a3SLandon J. Fuller static struct bhndb_pci_core *
bhndb_pci_find_core(struct bhnd_core_info * ci)209caeff9a3SLandon J. Fuller bhndb_pci_find_core(struct bhnd_core_info *ci)
210caeff9a3SLandon J. Fuller {
211caeff9a3SLandon J. Fuller 	for (size_t i = 0; !BHNDB_PCI_IS_CORE_END(&bhndb_pci_cores[i]); i++) {
212caeff9a3SLandon J. Fuller 		struct bhndb_pci_core *entry = &bhndb_pci_cores[i];
213caeff9a3SLandon J. Fuller 
214caeff9a3SLandon J. Fuller 		if (bhnd_core_matches(ci, &entry->match))
215caeff9a3SLandon J. Fuller 			return (entry);
216caeff9a3SLandon J. Fuller 	}
217caeff9a3SLandon J. Fuller 
218caeff9a3SLandon J. Fuller 	return (NULL);
219caeff9a3SLandon J. Fuller }
220caeff9a3SLandon J. Fuller 
221caeff9a3SLandon J. Fuller /**
222caeff9a3SLandon J. Fuller  * Return all quirk flags for the given @p cid and @p ci.
223caeff9a3SLandon J. Fuller  */
224caeff9a3SLandon J. Fuller static uint32_t
bhndb_pci_get_core_quirks(struct bhnd_chipid * cid,struct bhnd_core_info * ci)225caeff9a3SLandon J. Fuller bhndb_pci_get_core_quirks(struct bhnd_chipid *cid, struct bhnd_core_info *ci)
226caeff9a3SLandon J. Fuller {
227caeff9a3SLandon J. Fuller 	struct bhndb_pci_core	*entry;
228caeff9a3SLandon J. Fuller 	struct bhndb_pci_quirk	*qtable;
229caeff9a3SLandon J. Fuller 	uint32_t		 quirks;
230caeff9a3SLandon J. Fuller 
231caeff9a3SLandon J. Fuller 	quirks = 0;
232caeff9a3SLandon J. Fuller 
233caeff9a3SLandon J. Fuller 	/* No core entry? */
234caeff9a3SLandon J. Fuller 	if ((entry = bhndb_pci_find_core(ci)) == NULL)
235caeff9a3SLandon J. Fuller 		return (quirks);
236caeff9a3SLandon J. Fuller 
237caeff9a3SLandon J. Fuller 	/* No quirks? */
238caeff9a3SLandon J. Fuller 	if ((qtable = entry->quirks) == NULL)
239caeff9a3SLandon J. Fuller 		return (quirks);
240caeff9a3SLandon J. Fuller 
241caeff9a3SLandon J. Fuller 	for (size_t i = 0; !BHNDB_PCI_IS_QUIRK_END(&qtable[i]); i++) {
242caeff9a3SLandon J. Fuller 		struct bhndb_pci_quirk *q = &qtable[i];
243caeff9a3SLandon J. Fuller 
244caeff9a3SLandon J. Fuller 		if (!bhnd_chip_matches(cid, &q->chip_desc))
245caeff9a3SLandon J. Fuller 			continue;
246caeff9a3SLandon J. Fuller 
247caeff9a3SLandon J. Fuller 		if (!bhnd_core_matches(ci, &q->core_desc))
248caeff9a3SLandon J. Fuller 			continue;
249caeff9a3SLandon J. Fuller 
250caeff9a3SLandon J. Fuller 		quirks |= q->quirks;
251caeff9a3SLandon J. Fuller 	}
252caeff9a3SLandon J. Fuller 
253caeff9a3SLandon J. Fuller 	return (quirks);
254caeff9a3SLandon J. Fuller }
255caeff9a3SLandon J. Fuller 
2564ad7e9b0SAdrian Chadd /**
2574ad7e9b0SAdrian Chadd  * Default bhndb_pci implementation of device_probe().
2584ad7e9b0SAdrian Chadd  *
2594ad7e9b0SAdrian Chadd  * Verifies that the parent is a PCI/PCIe device.
2604ad7e9b0SAdrian Chadd  */
2614ad7e9b0SAdrian Chadd static int
bhndb_pci_probe(device_t dev)2624ad7e9b0SAdrian Chadd bhndb_pci_probe(device_t dev)
2634ad7e9b0SAdrian Chadd {
26484d6a5d4SLandon J. Fuller 	struct bhndb_pci_probe	*probe;
265caeff9a3SLandon J. Fuller 	struct bhndb_pci_core	*entry;
266caeff9a3SLandon J. Fuller 	bhnd_devclass_t		 hostb_devclass;
267711221efSLandon J. Fuller 	device_t		 parent, parent_bus;
268711221efSLandon J. Fuller 	devclass_t		 pci, bus_devclass;
269caeff9a3SLandon J. Fuller 	int			 error;
270caeff9a3SLandon J. Fuller 
27184d6a5d4SLandon J. Fuller 	probe = NULL;
2724ad7e9b0SAdrian Chadd 
2734ad7e9b0SAdrian Chadd 	/* Our parent must be a PCI/PCIe device. */
2744ad7e9b0SAdrian Chadd 	pci = devclass_find("pci");
2754ad7e9b0SAdrian Chadd 	parent = device_get_parent(dev);
276711221efSLandon J. Fuller 	parent_bus = device_get_parent(parent);
277711221efSLandon J. Fuller 	if (parent_bus == NULL)
278711221efSLandon J. Fuller 		return (ENXIO);
2794ad7e9b0SAdrian Chadd 
280711221efSLandon J. Fuller 	/* The bus device class may inherit from 'pci' */
281711221efSLandon J. Fuller 	for (bus_devclass = device_get_devclass(parent_bus);
282711221efSLandon J. Fuller 	    bus_devclass != NULL;
283711221efSLandon J. Fuller 	    bus_devclass = devclass_get_parent(bus_devclass))
284711221efSLandon J. Fuller 	{
285711221efSLandon J. Fuller 		if (bus_devclass == pci)
286711221efSLandon J. Fuller 			break;
287711221efSLandon J. Fuller 	}
288711221efSLandon J. Fuller 
289711221efSLandon J. Fuller 	if (bus_devclass != pci)
2904ad7e9b0SAdrian Chadd 		return (ENXIO);
2914ad7e9b0SAdrian Chadd 
292caeff9a3SLandon J. Fuller 	/* Enable clocks */
293caeff9a3SLandon J. Fuller 	if ((error = bhndb_enable_pci_clocks(dev)))
294caeff9a3SLandon J. Fuller 		return (error);
2954ad7e9b0SAdrian Chadd 
296caeff9a3SLandon J. Fuller 	/* Identify the chip and enumerate the bridged cores */
297caeff9a3SLandon J. Fuller 	hostb_devclass = bhndb_expected_pci_devclass(dev);
29884d6a5d4SLandon J. Fuller 	if ((error = bhndb_pci_probe_alloc(&probe, dev, hostb_devclass)))
299caeff9a3SLandon J. Fuller 		goto cleanup;
300caeff9a3SLandon J. Fuller 
301caeff9a3SLandon J. Fuller 	/* Look for a matching core table entry */
30284d6a5d4SLandon J. Fuller 	if ((entry = bhndb_pci_find_core(&probe->hostb_core)) == NULL) {
303caeff9a3SLandon J. Fuller 		error = ENXIO;
304caeff9a3SLandon J. Fuller 		goto cleanup;
3054ad7e9b0SAdrian Chadd 	}
3064ad7e9b0SAdrian Chadd 
307caeff9a3SLandon J. Fuller 	device_set_desc(dev, "PCI-BHND bridge");
308caeff9a3SLandon J. Fuller 
309caeff9a3SLandon J. Fuller 	/* fall-through */
310caeff9a3SLandon J. Fuller 	error = BUS_PROBE_DEFAULT;
311caeff9a3SLandon J. Fuller 
312caeff9a3SLandon J. Fuller cleanup:
31384d6a5d4SLandon J. Fuller 	if (probe != NULL)
31484d6a5d4SLandon J. Fuller 		bhndb_pci_probe_free(probe);
31584d6a5d4SLandon J. Fuller 
316caeff9a3SLandon J. Fuller 	bhndb_disable_pci_clocks(dev);
317caeff9a3SLandon J. Fuller 
318caeff9a3SLandon J. Fuller 	return (error);
319caeff9a3SLandon J. Fuller }
320caeff9a3SLandon J. Fuller 
321caeff9a3SLandon J. Fuller /**
322caeff9a3SLandon J. Fuller  * Attempt to allocate MSI interrupts, returning the count in @p msi_count
323caeff9a3SLandon J. Fuller  * on success.
324caeff9a3SLandon J. Fuller  */
325824b48efSLandon J. Fuller static int
bhndb_pci_alloc_msi(struct bhndb_pci_softc * sc,int * msi_count)326caeff9a3SLandon J. Fuller bhndb_pci_alloc_msi(struct bhndb_pci_softc *sc, int *msi_count)
327824b48efSLandon J. Fuller {
328caeff9a3SLandon J. Fuller 	int error, count;
329824b48efSLandon J. Fuller 
330824b48efSLandon J. Fuller 	/* Is MSI available? */
331824b48efSLandon J. Fuller 	if (pci_msi_count(sc->parent) < BHNDB_PCI_MSI_COUNT)
332824b48efSLandon J. Fuller 		return (ENXIO);
333824b48efSLandon J. Fuller 
334824b48efSLandon J. Fuller 	/* Allocate expected message count */
335caeff9a3SLandon J. Fuller 	count = BHNDB_PCI_MSI_COUNT;
336caeff9a3SLandon J. Fuller 	if ((error = pci_alloc_msi(sc->parent, &count))) {
337824b48efSLandon J. Fuller 		device_printf(sc->dev, "failed to allocate MSI interrupts: "
338824b48efSLandon J. Fuller 		    "%d\n", error);
339caeff9a3SLandon J. Fuller 
340824b48efSLandon J. Fuller 		return (error);
341824b48efSLandon J. Fuller 	}
342824b48efSLandon J. Fuller 
343d16875a8SLandon J. Fuller 	if (count < BHNDB_PCI_MSI_COUNT) {
344d16875a8SLandon J. Fuller 		pci_release_msi(sc->parent);
345824b48efSLandon J. Fuller 		return (ENXIO);
346d16875a8SLandon J. Fuller 	}
347824b48efSLandon J. Fuller 
348caeff9a3SLandon J. Fuller 	*msi_count = count;
349824b48efSLandon J. Fuller 	return (0);
350824b48efSLandon J. Fuller }
351824b48efSLandon J. Fuller 
3524ad7e9b0SAdrian Chadd static int
bhndb_pci_attach(device_t dev)3534ad7e9b0SAdrian Chadd bhndb_pci_attach(device_t dev)
3544ad7e9b0SAdrian Chadd {
3554ad7e9b0SAdrian Chadd 	struct bhndb_pci_softc	*sc;
35689294a78SLandon J. Fuller 	struct bhnd_chipid	 cid;
35789294a78SLandon J. Fuller 	struct bhnd_core_info	*cores, hostb_core;
35889294a78SLandon J. Fuller 	bhnd_erom_class_t	*erom_class;
35984d6a5d4SLandon J. Fuller 	struct bhndb_pci_probe	*probe;
36089294a78SLandon J. Fuller 	u_int			 ncores;
361caeff9a3SLandon J. Fuller 	int			 irq_rid;
362caeff9a3SLandon J. Fuller 	int			 error;
3634ad7e9b0SAdrian Chadd 
3644ad7e9b0SAdrian Chadd 	sc = device_get_softc(dev);
3654ad7e9b0SAdrian Chadd 	sc->dev = dev;
366e83ce340SAdrian Chadd 	sc->parent = device_get_parent(dev);
367caeff9a3SLandon J. Fuller 	sc->pci_devclass = bhndb_expected_pci_devclass(dev);
368caeff9a3SLandon J. Fuller 	sc->pci_quirks = 0;
36989294a78SLandon J. Fuller 	sc->set_regwin = NULL;
37089294a78SLandon J. Fuller 
371caeff9a3SLandon J. Fuller 	BHNDB_PCI_LOCK_INIT(sc);
372caeff9a3SLandon J. Fuller 
37384d6a5d4SLandon J. Fuller 	probe = NULL;
37489294a78SLandon J. Fuller 	cores = NULL;
3754ad7e9b0SAdrian Chadd 
376824b48efSLandon J. Fuller 	/* Enable PCI bus mastering */
377824b48efSLandon J. Fuller 	pci_enable_busmaster(sc->parent);
378824b48efSLandon J. Fuller 
37984d6a5d4SLandon J. Fuller 	/* Enable clocks (if required by this hardware) */
38084d6a5d4SLandon J. Fuller 	if ((error = bhndb_enable_pci_clocks(sc->dev)))
38184d6a5d4SLandon J. Fuller 		goto cleanup;
38284d6a5d4SLandon J. Fuller 
38384d6a5d4SLandon J. Fuller 	/* Identify the chip and enumerate the bridged cores */
38484d6a5d4SLandon J. Fuller 	error = bhndb_pci_probe_alloc(&probe, dev, sc->pci_devclass);
38584d6a5d4SLandon J. Fuller 	if (error)
38684d6a5d4SLandon J. Fuller 		goto cleanup;
38784d6a5d4SLandon J. Fuller 
38884d6a5d4SLandon J. Fuller 	sc->pci_quirks = bhndb_pci_get_core_quirks(&probe->cid,
38984d6a5d4SLandon J. Fuller 	    &probe->hostb_core);
39084d6a5d4SLandon J. Fuller 
39184d6a5d4SLandon J. Fuller 	/* Select the appropriate register window handler */
39284d6a5d4SLandon J. Fuller 	if (probe->cid.chip_type == BHND_CHIPTYPE_SIBA) {
39384d6a5d4SLandon J. Fuller 		sc->set_regwin = bhndb_pci_compat_setregwin;
39484d6a5d4SLandon J. Fuller 	} else {
39584d6a5d4SLandon J. Fuller 		sc->set_regwin = bhndb_pci_fast_setregwin;
39684d6a5d4SLandon J. Fuller 	}
39784d6a5d4SLandon J. Fuller 
39884d6a5d4SLandon J. Fuller 	/*
39984d6a5d4SLandon J. Fuller 	 * Fix up our PCI base address in the SPROM shadow, if necessary.
40084d6a5d4SLandon J. Fuller 	 *
40184d6a5d4SLandon J. Fuller 	 * This must be done prior to accessing any static register windows
40284d6a5d4SLandon J. Fuller 	 * that map the PCI core.
40384d6a5d4SLandon J. Fuller 	 */
40484d6a5d4SLandon J. Fuller 	if ((error = bhndb_pci_srsh_pi_war(sc, probe)))
40584d6a5d4SLandon J. Fuller 		goto cleanup;
40684d6a5d4SLandon J. Fuller 
40789294a78SLandon J. Fuller 	/* Set up PCI interrupt handling */
408caeff9a3SLandon J. Fuller 	if (bhndb_pci_alloc_msi(sc, &sc->msi_count) == 0) {
409caeff9a3SLandon J. Fuller 		/* MSI uses resource IDs starting at 1 */
410caeff9a3SLandon J. Fuller 		irq_rid = 1;
411caeff9a3SLandon J. Fuller 
412824b48efSLandon J. Fuller 		device_printf(dev, "Using MSI interrupts on %s\n",
413824b48efSLandon J. Fuller 		    device_get_nameunit(sc->parent));
414824b48efSLandon J. Fuller 	} else {
415caeff9a3SLandon J. Fuller 		sc->msi_count = 0;
416caeff9a3SLandon J. Fuller 		irq_rid = 0;
417caeff9a3SLandon J. Fuller 
418824b48efSLandon J. Fuller 		device_printf(dev, "Using INTx interrupts on %s\n",
419824b48efSLandon J. Fuller 		    device_get_nameunit(sc->parent));
420824b48efSLandon J. Fuller 	}
421824b48efSLandon J. Fuller 
422caeff9a3SLandon J. Fuller 	sc->isrc = bhndb_alloc_intr_isrc(sc->parent, irq_rid, 0, RM_MAX_END, 1,
423caeff9a3SLandon J. Fuller 	    RF_SHAREABLE | RF_ACTIVE);
424caeff9a3SLandon J. Fuller 	if (sc->isrc == NULL) {
425caeff9a3SLandon J. Fuller 		device_printf(sc->dev, "failed to allocate interrupt "
426caeff9a3SLandon J. Fuller 		    "resource\n");
427caeff9a3SLandon J. Fuller 		error = ENXIO;
428caeff9a3SLandon J. Fuller 		goto cleanup;
429caeff9a3SLandon J. Fuller 	}
4304ad7e9b0SAdrian Chadd 
43184d6a5d4SLandon J. Fuller 	/*
43284d6a5d4SLandon J. Fuller 	 * Copy out the probe results and then free our probe state, releasing
43384d6a5d4SLandon J. Fuller 	 * its exclusive ownership of host bridge resources.
43484d6a5d4SLandon J. Fuller 	 *
43584d6a5d4SLandon J. Fuller 	 * This must be done prior to full configuration of the bridge via
43684d6a5d4SLandon J. Fuller 	 * bhndb_attach().
43784d6a5d4SLandon J. Fuller 	 */
43884d6a5d4SLandon J. Fuller 	cid = probe->cid;
43984d6a5d4SLandon J. Fuller 	erom_class = probe->erom_class;
44084d6a5d4SLandon J. Fuller 	hostb_core = probe->hostb_core;
441111d7cb2SLandon J. Fuller 
44284d6a5d4SLandon J. Fuller 	error = bhndb_pci_probe_copy_core_table(probe, &cores, &ncores);
44384d6a5d4SLandon J. Fuller 	if (error) {
44484d6a5d4SLandon J. Fuller 		cores = NULL;
445111d7cb2SLandon J. Fuller 		goto cleanup;
4464ad7e9b0SAdrian Chadd 	}
4474ad7e9b0SAdrian Chadd 
44884d6a5d4SLandon J. Fuller 	bhndb_pci_probe_free(probe);
44984d6a5d4SLandon J. Fuller 	probe = NULL;
450caeff9a3SLandon J. Fuller 
45189294a78SLandon J. Fuller 	/* Perform bridge attach */
45289294a78SLandon J. Fuller 	error = bhndb_attach(dev, &cid, cores, ncores, &hostb_core, erom_class);
45389294a78SLandon J. Fuller 	if (error)
45489294a78SLandon J. Fuller 		goto cleanup;
4554ad7e9b0SAdrian Chadd 
456111d7cb2SLandon J. Fuller 	/* Add any additional child devices */
457111d7cb2SLandon J. Fuller 	if ((error = bhndb_pci_add_children(sc)))
458111d7cb2SLandon J. Fuller 		goto cleanup;
459111d7cb2SLandon J. Fuller 
460111d7cb2SLandon J. Fuller 	/* Probe and attach our children */
461*18250ec6SJohn Baldwin 	bus_attach_children(dev);
462111d7cb2SLandon J. Fuller 
46384d6a5d4SLandon J. Fuller 	bhndb_pci_probe_free_core_table(cores);
46489294a78SLandon J. Fuller 
465111d7cb2SLandon J. Fuller 	return (0);
466111d7cb2SLandon J. Fuller 
467111d7cb2SLandon J. Fuller cleanup:
468111d7cb2SLandon J. Fuller 	device_delete_children(dev);
46989294a78SLandon J. Fuller 
470caeff9a3SLandon J. Fuller 	if (sc->isrc != NULL)
471caeff9a3SLandon J. Fuller 		bhndb_free_intr_isrc(sc->isrc);
472caeff9a3SLandon J. Fuller 
473caeff9a3SLandon J. Fuller 	if (sc->msi_count > 0)
474d16875a8SLandon J. Fuller 		pci_release_msi(sc->parent);
475824b48efSLandon J. Fuller 
47689294a78SLandon J. Fuller 	if (cores != NULL)
47784d6a5d4SLandon J. Fuller 		bhndb_pci_probe_free_core_table(cores);
47884d6a5d4SLandon J. Fuller 
47984d6a5d4SLandon J. Fuller 	if (probe != NULL)
48084d6a5d4SLandon J. Fuller 		bhndb_pci_probe_free(probe);
48184d6a5d4SLandon J. Fuller 
48284d6a5d4SLandon J. Fuller 	bhndb_disable_pci_clocks(sc->dev);
48389294a78SLandon J. Fuller 
484111d7cb2SLandon J. Fuller 	pci_disable_busmaster(sc->parent);
485111d7cb2SLandon J. Fuller 
486caeff9a3SLandon J. Fuller 	BHNDB_PCI_LOCK_DESTROY(sc);
487caeff9a3SLandon J. Fuller 
488111d7cb2SLandon J. Fuller 	return (error);
489111d7cb2SLandon J. Fuller }
490111d7cb2SLandon J. Fuller 
491111d7cb2SLandon J. Fuller static int
bhndb_pci_detach(device_t dev)492111d7cb2SLandon J. Fuller bhndb_pci_detach(device_t dev)
493111d7cb2SLandon J. Fuller {
494111d7cb2SLandon J. Fuller 	struct bhndb_pci_softc	*sc;
495111d7cb2SLandon J. Fuller 	int			 error;
496111d7cb2SLandon J. Fuller 
497111d7cb2SLandon J. Fuller 	sc = device_get_softc(dev);
498111d7cb2SLandon J. Fuller 
499111d7cb2SLandon J. Fuller 	/* Attempt to detach our children */
500111d7cb2SLandon J. Fuller 	if ((error = bus_generic_detach(dev)))
501111d7cb2SLandon J. Fuller 		return (error);
502111d7cb2SLandon J. Fuller 
503111d7cb2SLandon J. Fuller 	/* Perform generic bridge detach */
504111d7cb2SLandon J. Fuller 	if ((error = bhndb_generic_detach(dev)))
505111d7cb2SLandon J. Fuller 		return (error);
506111d7cb2SLandon J. Fuller 
507111d7cb2SLandon J. Fuller 	/* Disable clocks (if required by this hardware) */
50889294a78SLandon J. Fuller 	if ((error = bhndb_disable_pci_clocks(sc->dev)))
509111d7cb2SLandon J. Fuller 		return (error);
510111d7cb2SLandon J. Fuller 
511caeff9a3SLandon J. Fuller 	/* Free our interrupt resources */
512caeff9a3SLandon J. Fuller 	bhndb_free_intr_isrc(sc->isrc);
513caeff9a3SLandon J. Fuller 
514824b48efSLandon J. Fuller 	/* Release MSI interrupts */
515caeff9a3SLandon J. Fuller 	if (sc->msi_count > 0)
516d16875a8SLandon J. Fuller 		pci_release_msi(sc->parent);
517824b48efSLandon J. Fuller 
518111d7cb2SLandon J. Fuller 	/* Disable PCI bus mastering */
519111d7cb2SLandon J. Fuller 	pci_disable_busmaster(sc->parent);
520111d7cb2SLandon J. Fuller 
521caeff9a3SLandon J. Fuller 	BHNDB_PCI_LOCK_DESTROY(sc);
522caeff9a3SLandon J. Fuller 
523111d7cb2SLandon J. Fuller 	return (0);
524111d7cb2SLandon J. Fuller }
525111d7cb2SLandon J. Fuller 
526111d7cb2SLandon J. Fuller static int
bhndb_pci_add_children(struct bhndb_pci_softc * sc)527111d7cb2SLandon J. Fuller bhndb_pci_add_children(struct bhndb_pci_softc *sc)
528111d7cb2SLandon J. Fuller {
529111d7cb2SLandon J. Fuller 	bus_size_t		 nv_sz;
530111d7cb2SLandon J. Fuller 	int			 error;
531111d7cb2SLandon J. Fuller 
532111d7cb2SLandon J. Fuller 	/**
533111d7cb2SLandon J. Fuller 	 * If SPROM is mapped directly into BAR0, add child NVRAM
534111d7cb2SLandon J. Fuller 	 * device.
535111d7cb2SLandon J. Fuller 	 */
536e83ce340SAdrian Chadd 	nv_sz = bhndb_pci_sprom_size(sc);
537e83ce340SAdrian Chadd 	if (nv_sz > 0) {
538e83ce340SAdrian Chadd 		struct bhndb_devinfo	*dinfo;
539111d7cb2SLandon J. Fuller 		device_t		 child;
540e83ce340SAdrian Chadd 
541e83ce340SAdrian Chadd 		if (bootverbose) {
542111d7cb2SLandon J. Fuller 			device_printf(sc->dev, "found SPROM (%ju bytes)\n",
543111d7cb2SLandon J. Fuller 			    (uintmax_t)nv_sz);
544e83ce340SAdrian Chadd 		}
545e83ce340SAdrian Chadd 
546111d7cb2SLandon J. Fuller 		/* Add sprom device, ordered early enough to be available
547111d7cb2SLandon J. Fuller 		 * before the bridged bhnd(4) bus is attached. */
548111d7cb2SLandon J. Fuller 		child = BUS_ADD_CHILD(sc->dev,
549111d7cb2SLandon J. Fuller 		    BHND_PROBE_ROOT + BHND_PROBE_ORDER_EARLY, "bhnd_nvram", -1);
550111d7cb2SLandon J. Fuller 		if (child == NULL) {
551111d7cb2SLandon J. Fuller 			device_printf(sc->dev, "failed to add sprom device\n");
552e83ce340SAdrian Chadd 			return (ENXIO);
553e83ce340SAdrian Chadd 		}
554e83ce340SAdrian Chadd 
555e83ce340SAdrian Chadd 		/* Initialize device address space and resource covering the
556e83ce340SAdrian Chadd 		 * BAR0 SPROM shadow. */
557111d7cb2SLandon J. Fuller 		dinfo = device_get_ivars(child);
558e83ce340SAdrian Chadd 		dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE;
559111d7cb2SLandon J. Fuller 
560111d7cb2SLandon J. Fuller 		error = bus_set_resource(child, SYS_RES_MEMORY, 0,
561e83ce340SAdrian Chadd 		    bhndb_pci_sprom_addr(sc), nv_sz);
562e83ce340SAdrian Chadd 		if (error) {
563111d7cb2SLandon J. Fuller 			device_printf(sc->dev,
564e83ce340SAdrian Chadd 			    "failed to register sprom resources\n");
565e83ce340SAdrian Chadd 			return (error);
566e83ce340SAdrian Chadd 		}
567e83ce340SAdrian Chadd 	}
568e83ce340SAdrian Chadd 
5694ad7e9b0SAdrian Chadd 	return (0);
5704ad7e9b0SAdrian Chadd }
5714ad7e9b0SAdrian Chadd 
572e83ce340SAdrian Chadd static const struct bhndb_regwin *
bhndb_pci_sprom_regwin(struct bhndb_pci_softc * sc)573e83ce340SAdrian Chadd bhndb_pci_sprom_regwin(struct bhndb_pci_softc *sc)
574e83ce340SAdrian Chadd {
575e83ce340SAdrian Chadd 	struct bhndb_resources		*bres;
576e83ce340SAdrian Chadd 	const struct bhndb_hwcfg	*cfg;
577e83ce340SAdrian Chadd 	const struct bhndb_regwin	*sprom_win;
578e83ce340SAdrian Chadd 
579e83ce340SAdrian Chadd 	bres = sc->bhndb.bus_res;
580e83ce340SAdrian Chadd 	cfg = bres->cfg;
581e83ce340SAdrian Chadd 
582e83ce340SAdrian Chadd 	sprom_win = bhndb_regwin_find_type(cfg->register_windows,
583e83ce340SAdrian Chadd 	    BHNDB_REGWIN_T_SPROM, BHNDB_PCI_V0_BAR0_SPROM_SIZE);
584e83ce340SAdrian Chadd 
585e83ce340SAdrian Chadd 	return (sprom_win);
586e83ce340SAdrian Chadd }
587e83ce340SAdrian Chadd 
588e83ce340SAdrian Chadd static bus_addr_t
bhndb_pci_sprom_addr(struct bhndb_pci_softc * sc)589e83ce340SAdrian Chadd bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc)
590e83ce340SAdrian Chadd {
591e83ce340SAdrian Chadd 	const struct bhndb_regwin	*sprom_win;
592e83ce340SAdrian Chadd 	struct resource			*r;
593e83ce340SAdrian Chadd 
594e83ce340SAdrian Chadd 	/* Fetch the SPROM register window */
595e83ce340SAdrian Chadd 	sprom_win = bhndb_pci_sprom_regwin(sc);
596e83ce340SAdrian Chadd 	KASSERT(sprom_win != NULL, ("requested sprom address on PCI_V2+"));
597e83ce340SAdrian Chadd 
598e83ce340SAdrian Chadd 	/* Fetch the associated resource */
59989294a78SLandon J. Fuller 	r = bhndb_host_resource_for_regwin(sc->bhndb.bus_res->res, sprom_win);
600e83ce340SAdrian Chadd 	KASSERT(r != NULL, ("missing resource for sprom window\n"));
601e83ce340SAdrian Chadd 
602e83ce340SAdrian Chadd 	return (rman_get_start(r) + sprom_win->win_offset);
603e83ce340SAdrian Chadd }
604e83ce340SAdrian Chadd 
605e83ce340SAdrian Chadd static bus_size_t
bhndb_pci_sprom_size(struct bhndb_pci_softc * sc)606e83ce340SAdrian Chadd bhndb_pci_sprom_size(struct bhndb_pci_softc *sc)
607e83ce340SAdrian Chadd {
608e83ce340SAdrian Chadd 	const struct bhndb_regwin	*sprom_win;
609e83ce340SAdrian Chadd 	uint32_t			 sctl;
610e83ce340SAdrian Chadd 	bus_size_t			 sprom_sz;
611e83ce340SAdrian Chadd 
612e83ce340SAdrian Chadd 	sprom_win = bhndb_pci_sprom_regwin(sc);
613e83ce340SAdrian Chadd 
614e83ce340SAdrian Chadd 	/* PCI_V2 and later devices map SPROM/OTP via ChipCommon */
615e83ce340SAdrian Chadd 	if (sprom_win == NULL)
616e83ce340SAdrian Chadd 		return (0);
617e83ce340SAdrian Chadd 
618e83ce340SAdrian Chadd 	/* Determine SPROM size */
619e83ce340SAdrian Chadd 	sctl = pci_read_config(sc->parent, BHNDB_PCI_SPROM_CONTROL, 4);
620e83ce340SAdrian Chadd 	if (sctl & BHNDB_PCI_SPROM_BLANK)
621e83ce340SAdrian Chadd 		return (0);
622e83ce340SAdrian Chadd 
623e83ce340SAdrian Chadd 	switch (sctl & BHNDB_PCI_SPROM_SZ_MASK) {
624e83ce340SAdrian Chadd 	case BHNDB_PCI_SPROM_SZ_1KB:
625e83ce340SAdrian Chadd 		sprom_sz = (1 * 1024);
626e83ce340SAdrian Chadd 		break;
627e83ce340SAdrian Chadd 
628e83ce340SAdrian Chadd 	case BHNDB_PCI_SPROM_SZ_4KB:
629e83ce340SAdrian Chadd 		sprom_sz = (4 * 1024);
630e83ce340SAdrian Chadd 		break;
631e83ce340SAdrian Chadd 
632e83ce340SAdrian Chadd 	case BHNDB_PCI_SPROM_SZ_16KB:
633e83ce340SAdrian Chadd 		sprom_sz = (16 * 1024);
634e83ce340SAdrian Chadd 		break;
635e83ce340SAdrian Chadd 
636e83ce340SAdrian Chadd 	case BHNDB_PCI_SPROM_SZ_RESERVED:
637e83ce340SAdrian Chadd 	default:
638e83ce340SAdrian Chadd 		device_printf(sc->dev, "invalid PCI sprom size 0x%x\n", sctl);
639e83ce340SAdrian Chadd 		return (0);
640e83ce340SAdrian Chadd 	}
641e83ce340SAdrian Chadd 
642711221efSLandon J. Fuller 	/* If the device has a larger SPROM than can be addressed via our SPROM
643711221efSLandon J. Fuller 	 * register window, the SPROM image data will still be located within
644711221efSLandon J. Fuller 	 * the window's addressable range */
645711221efSLandon J. Fuller 	sprom_sz = MIN(sprom_sz, sprom_win->win_size);
646e83ce340SAdrian Chadd 
647e83ce340SAdrian Chadd 	return (sprom_sz);
648e83ce340SAdrian Chadd }
649e83ce340SAdrian Chadd 
650caeff9a3SLandon J. Fuller /**
651caeff9a3SLandon J. Fuller  * Return the host resource providing a static mapping of the PCI core's
652caeff9a3SLandon J. Fuller  * registers.
653caeff9a3SLandon J. Fuller  *
654caeff9a3SLandon J. Fuller  * @param	sc		bhndb PCI driver state.
655eaa5fb4bSLandon J. Fuller  * @param	offset		The required readable offset within the PCI core
656eaa5fb4bSLandon J. Fuller  *				register block.
657eaa5fb4bSLandon J. Fuller  * @param	size		The required readable size at @p offset.
658caeff9a3SLandon J. Fuller  * @param[out]	res		On success, the host resource containing our PCI
659caeff9a3SLandon J. Fuller  *				core's register window.
660eaa5fb4bSLandon J. Fuller  * @param[out]	res_offset	On success, the @p offset relative to @p res.
661caeff9a3SLandon J. Fuller  *
662caeff9a3SLandon J. Fuller  * @retval 0		success
663caeff9a3SLandon J. Fuller  * @retval ENXIO	if a valid static register window mapping the PCI core
664caeff9a3SLandon J. Fuller  *			registers is not available.
665caeff9a3SLandon J. Fuller  */
666caeff9a3SLandon J. Fuller static int
bhndb_pci_get_core_regs(struct bhndb_pci_softc * sc,bus_size_t offset,bus_size_t size,struct resource ** res,bus_size_t * res_offset)667eaa5fb4bSLandon J. Fuller bhndb_pci_get_core_regs(struct bhndb_pci_softc *sc, bus_size_t offset,
668eaa5fb4bSLandon J. Fuller     bus_size_t size, struct resource **res, bus_size_t *res_offset)
669caeff9a3SLandon J. Fuller {
670caeff9a3SLandon J. Fuller 	const struct bhndb_regwin	*win;
671caeff9a3SLandon J. Fuller 	struct resource			*r;
672caeff9a3SLandon J. Fuller 
673eaa5fb4bSLandon J. Fuller 	/* Locate the static register window mapping the requested offset */
674caeff9a3SLandon J. Fuller 	win = bhndb_regwin_find_core(sc->bhndb.bus_res->cfg->register_windows,
675eaa5fb4bSLandon J. Fuller 	    sc->pci_devclass, 0, BHND_PORT_DEVICE, 0, 0, offset, size);
676caeff9a3SLandon J. Fuller 	if (win == NULL) {
677caeff9a3SLandon J. Fuller 		device_printf(sc->dev, "missing PCI core register window\n");
678caeff9a3SLandon J. Fuller 		return (ENXIO);
679caeff9a3SLandon J. Fuller 	}
680caeff9a3SLandon J. Fuller 
681caeff9a3SLandon J. Fuller 	/* Fetch the resource containing the register window */
682caeff9a3SLandon J. Fuller 	r = bhndb_host_resource_for_regwin(sc->bhndb.bus_res->res, win);
683caeff9a3SLandon J. Fuller 	if (r == NULL) {
684caeff9a3SLandon J. Fuller 		device_printf(sc->dev, "missing PCI core register resource\n");
685caeff9a3SLandon J. Fuller 		return (ENXIO);
686caeff9a3SLandon J. Fuller 	}
687caeff9a3SLandon J. Fuller 
688eaa5fb4bSLandon J. Fuller 	KASSERT(offset >= win->d.core.offset, ("offset %#jx outside of "
689eaa5fb4bSLandon J. Fuller 	    "register window", (uintmax_t)offset));
690eaa5fb4bSLandon J. Fuller 
691caeff9a3SLandon J. Fuller 	*res = r;
692eaa5fb4bSLandon J. Fuller 	*res_offset = win->win_offset + (offset - win->d.core.offset);
693caeff9a3SLandon J. Fuller 
694caeff9a3SLandon J. Fuller 	return (0);
695caeff9a3SLandon J. Fuller }
696caeff9a3SLandon J. Fuller 
697caeff9a3SLandon J. Fuller /**
698caeff9a3SLandon J. Fuller  * Write a 1, 2, or 4 byte data item to the PCI core's registers at @p offset.
699caeff9a3SLandon J. Fuller  *
700caeff9a3SLandon J. Fuller  * @param sc		bhndb PCI driver state.
701caeff9a3SLandon J. Fuller  * @param offset	register write offset.
702caeff9a3SLandon J. Fuller  * @param value		value to be written.
703caeff9a3SLandon J. Fuller  * @param width		item width (1, 2, or 4 bytes).
704caeff9a3SLandon J. Fuller  */
705caeff9a3SLandon J. Fuller static void
bhndb_pci_write_core(struct bhndb_pci_softc * sc,bus_size_t offset,uint32_t value,u_int width)706caeff9a3SLandon J. Fuller bhndb_pci_write_core(struct bhndb_pci_softc *sc, bus_size_t offset,
707caeff9a3SLandon J. Fuller     uint32_t value, u_int width)
708caeff9a3SLandon J. Fuller {
709caeff9a3SLandon J. Fuller 	struct resource	*r;
710caeff9a3SLandon J. Fuller 	bus_size_t	 r_offset;
711caeff9a3SLandon J. Fuller 	int		 error;
712caeff9a3SLandon J. Fuller 
713eaa5fb4bSLandon J. Fuller 	error = bhndb_pci_get_core_regs(sc, offset, width, &r, &r_offset);
714eaa5fb4bSLandon J. Fuller 	if (error) {
715eaa5fb4bSLandon J. Fuller 		panic("no PCI register window mapping %#jx+%#x: %d",
716eaa5fb4bSLandon J. Fuller 		    (uintmax_t)offset, width, error);
717eaa5fb4bSLandon J. Fuller 	}
718caeff9a3SLandon J. Fuller 
719caeff9a3SLandon J. Fuller 	switch (width) {
720caeff9a3SLandon J. Fuller 	case 1:
721eaa5fb4bSLandon J. Fuller 		bus_write_1(r, r_offset, value);
722caeff9a3SLandon J. Fuller 		break;
723caeff9a3SLandon J. Fuller 	case 2:
724eaa5fb4bSLandon J. Fuller 		bus_write_2(r, r_offset, value);
725caeff9a3SLandon J. Fuller 		break;
726caeff9a3SLandon J. Fuller 	case 4:
727eaa5fb4bSLandon J. Fuller 		bus_write_4(r, r_offset, value);
728caeff9a3SLandon J. Fuller 		break;
729caeff9a3SLandon J. Fuller 	default:
730caeff9a3SLandon J. Fuller 		panic("invalid width: %u", width);
731caeff9a3SLandon J. Fuller 	}
732caeff9a3SLandon J. Fuller }
733caeff9a3SLandon J. Fuller 
734caeff9a3SLandon J. Fuller /**
735caeff9a3SLandon J. Fuller  * Read a 1, 2, or 4 byte data item from the PCI core's registers
736caeff9a3SLandon J. Fuller  * at @p offset.
737caeff9a3SLandon J. Fuller  *
738caeff9a3SLandon J. Fuller  * @param sc		bhndb PCI driver state.
739caeff9a3SLandon J. Fuller  * @param offset	register read offset.
740caeff9a3SLandon J. Fuller  * @param width		item width (1, 2, or 4 bytes).
741caeff9a3SLandon J. Fuller  */
742caeff9a3SLandon J. Fuller static uint32_t
bhndb_pci_read_core(struct bhndb_pci_softc * sc,bus_size_t offset,u_int width)743caeff9a3SLandon J. Fuller bhndb_pci_read_core(struct bhndb_pci_softc *sc, bus_size_t offset, u_int width)
744caeff9a3SLandon J. Fuller {
745caeff9a3SLandon J. Fuller 	struct resource	*r;
746caeff9a3SLandon J. Fuller 	bus_size_t	 r_offset;
747caeff9a3SLandon J. Fuller 	int		 error;
748caeff9a3SLandon J. Fuller 
749eaa5fb4bSLandon J. Fuller 	error = bhndb_pci_get_core_regs(sc, offset, width, &r, &r_offset);
750eaa5fb4bSLandon J. Fuller 	if (error) {
751eaa5fb4bSLandon J. Fuller 		panic("no PCI register window mapping %#jx+%#x: %d",
752eaa5fb4bSLandon J. Fuller 		    (uintmax_t)offset, width, error);
753eaa5fb4bSLandon J. Fuller 	}
754caeff9a3SLandon J. Fuller 
755caeff9a3SLandon J. Fuller 	switch (width) {
756caeff9a3SLandon J. Fuller 	case 1:
757eaa5fb4bSLandon J. Fuller 		return (bus_read_1(r, r_offset));
758caeff9a3SLandon J. Fuller 	case 2:
759eaa5fb4bSLandon J. Fuller 		return (bus_read_2(r, r_offset));
760caeff9a3SLandon J. Fuller 	case 4:
761eaa5fb4bSLandon J. Fuller 		return (bus_read_4(r, r_offset));
762caeff9a3SLandon J. Fuller 	default:
763caeff9a3SLandon J. Fuller 		panic("invalid width: %u", width);
764caeff9a3SLandon J. Fuller 	}
765caeff9a3SLandon J. Fuller }
766caeff9a3SLandon J. Fuller 
76784d6a5d4SLandon J. Fuller /**
76884d6a5d4SLandon J. Fuller  * Fix-up power on defaults for SPROM-less devices.
7694ad7e9b0SAdrian Chadd  *
77084d6a5d4SLandon J. Fuller  * On SPROM-less devices, the PCI(e) cores will be initialized with their their
77184d6a5d4SLandon J. Fuller  * Power-on-Reset defaults; this can leave the BHND_PCI_SRSH_PI value pointing
77284d6a5d4SLandon J. Fuller  * to the wrong backplane address. This value is used by the PCI core when
77384d6a5d4SLandon J. Fuller  * performing address translation between static register windows in BAR0 that
77484d6a5d4SLandon J. Fuller  * map the PCI core's register block, and backplane address space.
7754ad7e9b0SAdrian Chadd  *
77684d6a5d4SLandon J. Fuller  * When translating accesses via these BAR0 regions, the PCI bridge determines
77784d6a5d4SLandon J. Fuller  * the base address of the PCI core by concatenating:
77884d6a5d4SLandon J. Fuller  *
77984d6a5d4SLandon J. Fuller  *	[bits]	[source]
78084d6a5d4SLandon J. Fuller  *	31:16	bits [31:16] of the enumeration space address (e.g. 0x18000000)
78184d6a5d4SLandon J. Fuller  *	15:12	value of BHND_PCI_SRSH_PI from the PCI core's SPROM shadow
78284d6a5d4SLandon J. Fuller  *	11:0	bits [11:0] of the PCI bus address
78384d6a5d4SLandon J. Fuller  *
78484d6a5d4SLandon J. Fuller  * For example, on a PCI_V0 device, the following PCI core register offsets are
78584d6a5d4SLandon J. Fuller  * mapped into BAR0:
78684d6a5d4SLandon J. Fuller  *
78784d6a5d4SLandon J. Fuller  *	[BAR0 offset]		[description]		[PCI core offset]
78884d6a5d4SLandon J. Fuller  *	0x1000-0x17FF		sprom shadow		0x800-0xFFF
78984d6a5d4SLandon J. Fuller  *	0x1800-0x1DFF		device registers	0x000-0x5FF
79084d6a5d4SLandon J. Fuller  *	0x1E00+0x1FFF		siba config registers	0xE00-0xFFF
79184d6a5d4SLandon J. Fuller  *
79284d6a5d4SLandon J. Fuller  * This function checks -- and if necessary, corrects -- the BHND_PCI_SRSH_PI
79384d6a5d4SLandon J. Fuller  * value in the SPROM shadow.
79484d6a5d4SLandon J. Fuller  *
79584d6a5d4SLandon J. Fuller  * This workaround must applied prior to accessing any static register windows
79684d6a5d4SLandon J. Fuller  * that map the PCI core.
79784d6a5d4SLandon J. Fuller  *
79884d6a5d4SLandon J. Fuller  * Applies to all PCI and PCIe-G1 core revisions.
7994ad7e9b0SAdrian Chadd  */
80084d6a5d4SLandon J. Fuller static int
bhndb_pci_srsh_pi_war(struct bhndb_pci_softc * sc,struct bhndb_pci_probe * probe)80184d6a5d4SLandon J. Fuller bhndb_pci_srsh_pi_war(struct bhndb_pci_softc *sc,
80284d6a5d4SLandon J. Fuller     struct bhndb_pci_probe *probe)
8034ad7e9b0SAdrian Chadd {
80484d6a5d4SLandon J. Fuller 	struct bhnd_core_match	md;
80584d6a5d4SLandon J. Fuller 	bhnd_addr_t		pci_addr;
80684d6a5d4SLandon J. Fuller 	bhnd_size_t		pci_size;
807caeff9a3SLandon J. Fuller 	bus_size_t		srsh_offset;
80884d6a5d4SLandon J. Fuller 	uint16_t		srsh_val, pci_val;
8094ad7e9b0SAdrian Chadd 	uint16_t		val;
81084d6a5d4SLandon J. Fuller 	int			error;
8114ad7e9b0SAdrian Chadd 
812caeff9a3SLandon J. Fuller 	if ((sc->pci_quirks & BHNDB_PCI_QUIRK_SRSH_WAR) == 0)
81384d6a5d4SLandon J. Fuller 		return (0);
814bb64eeccSAdrian Chadd 
81584d6a5d4SLandon J. Fuller 	/* Use an equality match descriptor to look up our PCI core's base
81684d6a5d4SLandon J. Fuller 	 * address in the EROM */
81784d6a5d4SLandon J. Fuller 	md = bhnd_core_get_match_desc(&probe->hostb_core);
81884d6a5d4SLandon J. Fuller 	error = bhnd_erom_lookup_core_addr(probe->erom, &md, BHND_PORT_DEVICE,
81984d6a5d4SLandon J. Fuller 	    0, 0, NULL, &pci_addr, &pci_size);
82084d6a5d4SLandon J. Fuller 	if (error) {
82184d6a5d4SLandon J. Fuller 		device_printf(sc->dev, "no base address found for the PCI host "
82284d6a5d4SLandon J. Fuller 		    "bridge core: %d\n", error);
82384d6a5d4SLandon J. Fuller 		return (error);
8244ad7e9b0SAdrian Chadd 	}
82584d6a5d4SLandon J. Fuller 
82684d6a5d4SLandon J. Fuller 	/* Fetch the SPROM SRSH_PI value */
82784d6a5d4SLandon J. Fuller 	srsh_offset = BHND_PCI_SPROM_SHADOW + BHND_PCI_SRSH_PI_OFFSET;
82884d6a5d4SLandon J. Fuller 	val = bhndb_pci_probe_read(probe, pci_addr, srsh_offset, sizeof(val));
82984d6a5d4SLandon J. Fuller 	srsh_val = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT;
83084d6a5d4SLandon J. Fuller 
83184d6a5d4SLandon J. Fuller 	/* If it doesn't match PCI core's base address, update the SPROM
83284d6a5d4SLandon J. Fuller 	 * shadow */
83384d6a5d4SLandon J. Fuller 	pci_val = (pci_addr & BHND_PCI_SRSH_PI_ADDR_MASK) >>
83484d6a5d4SLandon J. Fuller 	    BHND_PCI_SRSH_PI_ADDR_SHIFT;
83584d6a5d4SLandon J. Fuller 	if (srsh_val != pci_val) {
83684d6a5d4SLandon J. Fuller 		val &= ~BHND_PCI_SRSH_PI_MASK;
83784d6a5d4SLandon J. Fuller 		val |= (pci_val << BHND_PCI_SRSH_PI_SHIFT);
83884d6a5d4SLandon J. Fuller 		bhndb_pci_probe_write(probe, pci_addr, srsh_offset, val,
83984d6a5d4SLandon J. Fuller 		    sizeof(val));
84084d6a5d4SLandon J. Fuller 	}
84184d6a5d4SLandon J. Fuller 
84284d6a5d4SLandon J. Fuller 	return (0);
8434ad7e9b0SAdrian Chadd }
8444ad7e9b0SAdrian Chadd 
8454ad7e9b0SAdrian Chadd static int
bhndb_pci_resume(device_t dev)8464ad7e9b0SAdrian Chadd bhndb_pci_resume(device_t dev)
8474ad7e9b0SAdrian Chadd {
8484ad7e9b0SAdrian Chadd 	struct bhndb_pci_softc	*sc;
8494ad7e9b0SAdrian Chadd 	int			 error;
8504ad7e9b0SAdrian Chadd 
8514ad7e9b0SAdrian Chadd 	sc = device_get_softc(dev);
8524ad7e9b0SAdrian Chadd 
853bb64eeccSAdrian Chadd 	/* Enable clocks (if supported by this hardware) */
85489294a78SLandon J. Fuller 	if ((error = bhndb_enable_pci_clocks(sc->dev)))
8554ad7e9b0SAdrian Chadd 		return (error);
8564ad7e9b0SAdrian Chadd 
857bb64eeccSAdrian Chadd 	/* Perform resume */
858bb64eeccSAdrian Chadd 	return (bhndb_generic_resume(dev));
859bb64eeccSAdrian Chadd }
860bb64eeccSAdrian Chadd 
861bb64eeccSAdrian Chadd static int
bhndb_pci_suspend(device_t dev)862bb64eeccSAdrian Chadd bhndb_pci_suspend(device_t dev)
863bb64eeccSAdrian Chadd {
864bb64eeccSAdrian Chadd 	struct bhndb_pci_softc	*sc;
865bb64eeccSAdrian Chadd 	int			 error;
866bb64eeccSAdrian Chadd 
867bb64eeccSAdrian Chadd 	sc = device_get_softc(dev);
868bb64eeccSAdrian Chadd 
869bb64eeccSAdrian Chadd 	/* Disable clocks (if supported by this hardware) */
87089294a78SLandon J. Fuller 	if ((error = bhndb_disable_pci_clocks(sc->dev)))
8714ad7e9b0SAdrian Chadd 		return (error);
8724ad7e9b0SAdrian Chadd 
873bb64eeccSAdrian Chadd 	/* Perform suspend */
874bb64eeccSAdrian Chadd 	return (bhndb_generic_suspend(dev));
875bb64eeccSAdrian Chadd }
876bb64eeccSAdrian Chadd 
877bb64eeccSAdrian Chadd static int
bhndb_pci_set_window_addr(device_t dev,const struct bhndb_regwin * rw,bhnd_addr_t addr)8784ad7e9b0SAdrian Chadd bhndb_pci_set_window_addr(device_t dev, const struct bhndb_regwin *rw,
8794ad7e9b0SAdrian Chadd     bhnd_addr_t addr)
8804ad7e9b0SAdrian Chadd {
8814ad7e9b0SAdrian Chadd 	struct bhndb_pci_softc *sc = device_get_softc(dev);
88289294a78SLandon J. Fuller 	return (sc->set_regwin(sc->dev, sc->parent, rw, addr));
8834ad7e9b0SAdrian Chadd }
8844ad7e9b0SAdrian Chadd 
8854ad7e9b0SAdrian Chadd /**
8864ad7e9b0SAdrian Chadd  * A siba(4) and bcma(4)-compatible bhndb_set_window_addr implementation.
8874ad7e9b0SAdrian Chadd  *
8884ad7e9b0SAdrian Chadd  * On siba(4) devices, it's possible that writing a PCI window register may
8894ad7e9b0SAdrian Chadd  * not succeed; it's necessary to immediately read the configuration register
8904ad7e9b0SAdrian Chadd  * and retry if not set to the desired value.
8914ad7e9b0SAdrian Chadd  *
8924ad7e9b0SAdrian Chadd  * This is not necessary on bcma(4) devices, but other than the overhead of
8934ad7e9b0SAdrian Chadd  * validating the register, there's no harm in performing the verification.
8944ad7e9b0SAdrian Chadd  */
8954ad7e9b0SAdrian Chadd static int
bhndb_pci_compat_setregwin(device_t dev,device_t pci_dev,const struct bhndb_regwin * rw,bhnd_addr_t addr)89689294a78SLandon J. Fuller bhndb_pci_compat_setregwin(device_t dev, device_t pci_dev,
8974ad7e9b0SAdrian Chadd     const struct bhndb_regwin *rw, bhnd_addr_t addr)
8984ad7e9b0SAdrian Chadd {
8994ad7e9b0SAdrian Chadd 	int		error;
900e83ce340SAdrian Chadd 	int		reg;
9014ad7e9b0SAdrian Chadd 
9024ad7e9b0SAdrian Chadd 	if (rw->win_type != BHNDB_REGWIN_T_DYN)
9034ad7e9b0SAdrian Chadd 		return (ENODEV);
9044ad7e9b0SAdrian Chadd 
905e83ce340SAdrian Chadd 	reg = rw->d.dyn.cfg_offset;
9064ad7e9b0SAdrian Chadd 	for (u_int i = 0; i < BHNDB_PCI_BARCTRL_WRITE_RETRY; i++) {
90789294a78SLandon J. Fuller 		if ((error = bhndb_pci_fast_setregwin(dev, pci_dev, rw, addr)))
9084ad7e9b0SAdrian Chadd 			return (error);
9094ad7e9b0SAdrian Chadd 
91089294a78SLandon J. Fuller 		if (pci_read_config(pci_dev, reg, 4) == addr)
9114ad7e9b0SAdrian Chadd 			return (0);
9124ad7e9b0SAdrian Chadd 
9134ad7e9b0SAdrian Chadd 		DELAY(10);
9144ad7e9b0SAdrian Chadd 	}
9154ad7e9b0SAdrian Chadd 
9164ad7e9b0SAdrian Chadd 	/* Unable to set window */
9174ad7e9b0SAdrian Chadd 	return (ENODEV);
9184ad7e9b0SAdrian Chadd }
9194ad7e9b0SAdrian Chadd 
9204ad7e9b0SAdrian Chadd /**
9214ad7e9b0SAdrian Chadd  * A bcma(4)-only bhndb_set_window_addr implementation.
9224ad7e9b0SAdrian Chadd  */
9234ad7e9b0SAdrian Chadd static int
bhndb_pci_fast_setregwin(device_t dev,device_t pci_dev,const struct bhndb_regwin * rw,bhnd_addr_t addr)92489294a78SLandon J. Fuller bhndb_pci_fast_setregwin(device_t dev, device_t pci_dev,
9254ad7e9b0SAdrian Chadd     const struct bhndb_regwin *rw, bhnd_addr_t addr)
9264ad7e9b0SAdrian Chadd {
9274ad7e9b0SAdrian Chadd 	/* The PCI bridge core only supports 32-bit addressing, regardless
9284ad7e9b0SAdrian Chadd 	 * of the bus' support for 64-bit addressing */
9294ad7e9b0SAdrian Chadd 	if (addr > UINT32_MAX)
9304ad7e9b0SAdrian Chadd 		return (ERANGE);
9314ad7e9b0SAdrian Chadd 
9324ad7e9b0SAdrian Chadd 	switch (rw->win_type) {
9334ad7e9b0SAdrian Chadd 	case BHNDB_REGWIN_T_DYN:
9344ad7e9b0SAdrian Chadd 		/* Addresses must be page aligned */
9354ad7e9b0SAdrian Chadd 		if (addr % rw->win_size != 0)
9364ad7e9b0SAdrian Chadd 			return (EINVAL);
9374ad7e9b0SAdrian Chadd 
93889294a78SLandon J. Fuller 		pci_write_config(pci_dev, rw->d.dyn.cfg_offset, addr, 4);
9394ad7e9b0SAdrian Chadd 		break;
9404ad7e9b0SAdrian Chadd 	default:
9414ad7e9b0SAdrian Chadd 		return (ENODEV);
9424ad7e9b0SAdrian Chadd 	}
9434ad7e9b0SAdrian Chadd 
9444ad7e9b0SAdrian Chadd 	return (0);
9454ad7e9b0SAdrian Chadd }
9464ad7e9b0SAdrian Chadd 
947d567592bSAdrian Chadd static int
bhndb_pci_populate_board_info(device_t dev,device_t child,struct bhnd_board_info * info)948d567592bSAdrian Chadd bhndb_pci_populate_board_info(device_t dev, device_t child,
949d567592bSAdrian Chadd     struct bhnd_board_info *info)
950d567592bSAdrian Chadd {
951d567592bSAdrian Chadd 	struct bhndb_pci_softc	*sc;
952d567592bSAdrian Chadd 
953d567592bSAdrian Chadd 	sc = device_get_softc(dev);
954d567592bSAdrian Chadd 
9558ef24a0dSAdrian Chadd 	/*
9568ef24a0dSAdrian Chadd 	 * On a subset of Apple BCM4360 modules, always prefer the
9578ef24a0dSAdrian Chadd 	 * PCI subdevice to the SPROM-supplied boardtype.
9588ef24a0dSAdrian Chadd 	 *
9598ef24a0dSAdrian Chadd 	 * TODO:
9608ef24a0dSAdrian Chadd 	 *
9618ef24a0dSAdrian Chadd 	 * Broadcom's own drivers implement this override, and then later use
9628ef24a0dSAdrian Chadd 	 * the remapped BCM4360 board type to determine the required
9638ef24a0dSAdrian Chadd 	 * board-specific workarounds.
9648ef24a0dSAdrian Chadd 	 *
9658ef24a0dSAdrian Chadd 	 * Without access to this hardware, it's unclear why this mapping
9668ef24a0dSAdrian Chadd 	 * is done, and we must do the same. If we can survey the hardware
9678ef24a0dSAdrian Chadd 	 * in question, it may be possible to replace this behavior with
9688ef24a0dSAdrian Chadd 	 * explicit references to the SPROM-supplied boardtype(s) in our
9698ef24a0dSAdrian Chadd 	 * quirk definitions.
9708ef24a0dSAdrian Chadd 	 */
9718ef24a0dSAdrian Chadd 	if (pci_get_subvendor(sc->parent) == PCI_VENDOR_APPLE) {
9728ef24a0dSAdrian Chadd 		switch (info->board_type) {
9738ef24a0dSAdrian Chadd 		case BHND_BOARD_BCM94360X29C:
9748ef24a0dSAdrian Chadd 		case BHND_BOARD_BCM94360X29CP2:
9758ef24a0dSAdrian Chadd 		case BHND_BOARD_BCM94360X51:
9768ef24a0dSAdrian Chadd 		case BHND_BOARD_BCM94360X51P2:
9778ef24a0dSAdrian Chadd 			info->board_type = 0;	/* allow override below */
9788ef24a0dSAdrian Chadd 			break;
9798ef24a0dSAdrian Chadd 		default:
9808ef24a0dSAdrian Chadd 			break;
9818ef24a0dSAdrian Chadd 		}
9828ef24a0dSAdrian Chadd 	}
9838ef24a0dSAdrian Chadd 
984566ca880SLandon J. Fuller 	/* If NVRAM did not supply vendor/type/devid info, provide the PCI
985566ca880SLandon J. Fuller 	 * subvendor/subdevice/device values. */
986d567592bSAdrian Chadd 	if (info->board_vendor == 0)
987d567592bSAdrian Chadd 		info->board_vendor = pci_get_subvendor(sc->parent);
988d567592bSAdrian Chadd 
989d567592bSAdrian Chadd 	if (info->board_type == 0)
990d567592bSAdrian Chadd 		info->board_type = pci_get_subdevice(sc->parent);
991d567592bSAdrian Chadd 
992566ca880SLandon J. Fuller 	if (info->board_devid == 0)
993566ca880SLandon J. Fuller 		info->board_devid = pci_get_device(sc->parent);
994566ca880SLandon J. Fuller 
995d567592bSAdrian Chadd 	return (0);
996d567592bSAdrian Chadd }
997d567592bSAdrian Chadd 
9984ad7e9b0SAdrian Chadd /**
999caeff9a3SLandon J. Fuller  * Examine the bridge device @p dev and return the expected host bridge
1000caeff9a3SLandon J. Fuller  * device class.
1001caeff9a3SLandon J. Fuller  *
1002caeff9a3SLandon J. Fuller  * @param dev The bhndb bridge device
1003caeff9a3SLandon J. Fuller  */
1004caeff9a3SLandon J. Fuller static bhnd_devclass_t
bhndb_expected_pci_devclass(device_t dev)1005caeff9a3SLandon J. Fuller bhndb_expected_pci_devclass(device_t dev)
1006caeff9a3SLandon J. Fuller {
1007caeff9a3SLandon J. Fuller 	if (bhndb_is_pcie_attached(dev))
1008caeff9a3SLandon J. Fuller 		return (BHND_DEVCLASS_PCIE);
1009caeff9a3SLandon J. Fuller 	else
1010caeff9a3SLandon J. Fuller 		return (BHND_DEVCLASS_PCI);
1011caeff9a3SLandon J. Fuller }
1012caeff9a3SLandon J. Fuller 
1013caeff9a3SLandon J. Fuller /**
1014caeff9a3SLandon J. Fuller  * Return true if the bridge device @p dev is attached via PCIe,
101589294a78SLandon J. Fuller  * false otherwise.
101689294a78SLandon J. Fuller  *
101789294a78SLandon J. Fuller  * @param dev The bhndb bridge device
101889294a78SLandon J. Fuller  */
101989294a78SLandon J. Fuller static bool
bhndb_is_pcie_attached(device_t dev)102089294a78SLandon J. Fuller bhndb_is_pcie_attached(device_t dev)
102189294a78SLandon J. Fuller {
102289294a78SLandon J. Fuller 	int reg;
102389294a78SLandon J. Fuller 
102489294a78SLandon J. Fuller 	if (pci_find_cap(device_get_parent(dev), PCIY_EXPRESS, &reg) == 0)
102589294a78SLandon J. Fuller 		return (true);
102689294a78SLandon J. Fuller 
102789294a78SLandon J. Fuller 	return (false);
102889294a78SLandon J. Fuller }
102989294a78SLandon J. Fuller 
103089294a78SLandon J. Fuller /**
1031bb64eeccSAdrian Chadd  * Enable externally managed clocks, if required.
10324ad7e9b0SAdrian Chadd  *
1033bb64eeccSAdrian Chadd  * Some PCI chipsets (BCM4306, possibly others) chips do not support
1034bb64eeccSAdrian Chadd  * the idle low-power clock. Clocking must be bootstrapped at
1035bb64eeccSAdrian Chadd  * attach/resume by directly adjusting GPIO registers exposed in the
1036bb64eeccSAdrian Chadd  * PCI config space, and correspondingly, explicitly shutdown at
1037bb64eeccSAdrian Chadd  * detach/suspend.
10384ad7e9b0SAdrian Chadd  *
103989294a78SLandon J. Fuller  * @note This function may be safely called prior to device attach, (e.g.
104089294a78SLandon J. Fuller  * from DEVICE_PROBE).
104189294a78SLandon J. Fuller  *
104289294a78SLandon J. Fuller  * @param dev The bhndb bridge device
10434ad7e9b0SAdrian Chadd  */
10444ad7e9b0SAdrian Chadd static int
bhndb_enable_pci_clocks(device_t dev)104589294a78SLandon J. Fuller bhndb_enable_pci_clocks(device_t dev)
10464ad7e9b0SAdrian Chadd {
104789294a78SLandon J. Fuller 	device_t		pci_dev;
10484ad7e9b0SAdrian Chadd 	uint32_t		gpio_in, gpio_out, gpio_en;
10494ad7e9b0SAdrian Chadd 	uint32_t		gpio_flags;
10504ad7e9b0SAdrian Chadd 	uint16_t		pci_status;
10514ad7e9b0SAdrian Chadd 
105289294a78SLandon J. Fuller 	pci_dev = device_get_parent(dev);
105389294a78SLandon J. Fuller 
1054bb64eeccSAdrian Chadd 	/* Only supported and required on PCI devices */
1055eaa5fb4bSLandon J. Fuller 	if (bhndb_is_pcie_attached(dev))
1056bb64eeccSAdrian Chadd 		return (0);
10574ad7e9b0SAdrian Chadd 
10584ad7e9b0SAdrian Chadd 	/* Read state of XTAL pin */
105989294a78SLandon J. Fuller 	gpio_in = pci_read_config(pci_dev, BHNDB_PCI_GPIO_IN, 4);
10604ad7e9b0SAdrian Chadd 	if (gpio_in & BHNDB_PCI_GPIO_XTAL_ON)
10614ad7e9b0SAdrian Chadd 		return (0); /* already enabled */
10624ad7e9b0SAdrian Chadd 
10634ad7e9b0SAdrian Chadd 	/* Fetch current config */
106489294a78SLandon J. Fuller 	gpio_out = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUT, 4);
106589294a78SLandon J. Fuller 	gpio_en = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, 4);
10664ad7e9b0SAdrian Chadd 
10674ad7e9b0SAdrian Chadd 	/* Set PLL_OFF/XTAL_ON pins to HIGH and enable both pins */
10684ad7e9b0SAdrian Chadd 	gpio_flags = (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON);
10694ad7e9b0SAdrian Chadd 	gpio_out |= gpio_flags;
10704ad7e9b0SAdrian Chadd 	gpio_en |= gpio_flags;
10714ad7e9b0SAdrian Chadd 
107289294a78SLandon J. Fuller 	pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
107389294a78SLandon J. Fuller 	pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4);
10744ad7e9b0SAdrian Chadd 	DELAY(1000);
10754ad7e9b0SAdrian Chadd 
10764ad7e9b0SAdrian Chadd 	/* Reset PLL_OFF */
10774ad7e9b0SAdrian Chadd 	gpio_out &= ~BHNDB_PCI_GPIO_PLL_OFF;
107889294a78SLandon J. Fuller 	pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
10794ad7e9b0SAdrian Chadd 	DELAY(5000);
10804ad7e9b0SAdrian Chadd 
10814ad7e9b0SAdrian Chadd 	/* Clear any PCI 'sent target-abort' flag. */
108289294a78SLandon J. Fuller 	pci_status = pci_read_config(pci_dev, PCIR_STATUS, 2);
10834ad7e9b0SAdrian Chadd 	pci_status &= ~PCIM_STATUS_STABORT;
108489294a78SLandon J. Fuller 	pci_write_config(pci_dev, PCIR_STATUS, pci_status, 2);
10854ad7e9b0SAdrian Chadd 
10864ad7e9b0SAdrian Chadd 	return (0);
10874ad7e9b0SAdrian Chadd }
10884ad7e9b0SAdrian Chadd 
10894ad7e9b0SAdrian Chadd /**
1090bb64eeccSAdrian Chadd  * Disable externally managed clocks, if required.
10914ad7e9b0SAdrian Chadd  *
109289294a78SLandon J. Fuller  * This function may be safely called prior to device attach, (e.g.
109389294a78SLandon J. Fuller  * from DEVICE_PROBE).
109489294a78SLandon J. Fuller  *
109589294a78SLandon J. Fuller  * @param dev The bhndb bridge device
10964ad7e9b0SAdrian Chadd  */
10974ad7e9b0SAdrian Chadd static int
bhndb_disable_pci_clocks(device_t dev)109889294a78SLandon J. Fuller bhndb_disable_pci_clocks(device_t dev)
10994ad7e9b0SAdrian Chadd {
110089294a78SLandon J. Fuller 	device_t	pci_dev;
11014ad7e9b0SAdrian Chadd 	uint32_t	gpio_out, gpio_en;
11024ad7e9b0SAdrian Chadd 
110389294a78SLandon J. Fuller 	pci_dev = device_get_parent(dev);
110489294a78SLandon J. Fuller 
1105bb64eeccSAdrian Chadd 	/* Only supported and required on PCI devices */
110689294a78SLandon J. Fuller 	if (bhndb_is_pcie_attached(dev))
1107bb64eeccSAdrian Chadd 		return (0);
11084ad7e9b0SAdrian Chadd 
11094ad7e9b0SAdrian Chadd 	/* Fetch current config */
111089294a78SLandon J. Fuller 	gpio_out = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUT, 4);
111189294a78SLandon J. Fuller 	gpio_en = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, 4);
11124ad7e9b0SAdrian Chadd 
11134ad7e9b0SAdrian Chadd 	/* Set PLL_OFF to HIGH, XTAL_ON to LOW. */
11144ad7e9b0SAdrian Chadd 	gpio_out &= ~BHNDB_PCI_GPIO_XTAL_ON;
11154ad7e9b0SAdrian Chadd 	gpio_out |= BHNDB_PCI_GPIO_PLL_OFF;
111689294a78SLandon J. Fuller 	pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
11174ad7e9b0SAdrian Chadd 
11184ad7e9b0SAdrian Chadd 	/* Enable both output pins */
11194ad7e9b0SAdrian Chadd 	gpio_en |= (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON);
112089294a78SLandon J. Fuller 	pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4);
11214ad7e9b0SAdrian Chadd 
11224ad7e9b0SAdrian Chadd 	return (0);
11234ad7e9b0SAdrian Chadd }
11244ad7e9b0SAdrian Chadd 
1125f90f4b65SLandon J. Fuller static bhnd_clksrc
bhndb_pci_pwrctl_get_clksrc(device_t dev,device_t child,bhnd_clock clock)1126f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_get_clksrc(device_t dev, device_t child,
1127f90f4b65SLandon J. Fuller 	bhnd_clock clock)
1128f90f4b65SLandon J. Fuller {
1129f90f4b65SLandon J. Fuller 	struct bhndb_pci_softc	*sc;
1130f90f4b65SLandon J. Fuller 	uint32_t		 gpio_out;
1131f90f4b65SLandon J. Fuller 
1132f90f4b65SLandon J. Fuller 	sc = device_get_softc(dev);
1133f90f4b65SLandon J. Fuller 
1134f90f4b65SLandon J. Fuller 	/* Only supported on PCI devices */
113589294a78SLandon J. Fuller 	if (bhndb_is_pcie_attached(sc->dev))
11364e96bf3aSLandon J. Fuller 		return (BHND_CLKSRC_UNKNOWN);
1137f90f4b65SLandon J. Fuller 
1138f90f4b65SLandon J. Fuller 	/* Only ILP is supported */
1139f90f4b65SLandon J. Fuller 	if (clock != BHND_CLOCK_ILP)
11404e96bf3aSLandon J. Fuller 		return (BHND_CLKSRC_UNKNOWN);
1141f90f4b65SLandon J. Fuller 
1142f90f4b65SLandon J. Fuller 	gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4);
1143f90f4b65SLandon J. Fuller 	if (gpio_out & BHNDB_PCI_GPIO_SCS)
1144f90f4b65SLandon J. Fuller 		return (BHND_CLKSRC_PCI);
1145f90f4b65SLandon J. Fuller 	else
1146f90f4b65SLandon J. Fuller 		return (BHND_CLKSRC_XTAL);
1147f90f4b65SLandon J. Fuller }
1148f90f4b65SLandon J. Fuller 
1149f90f4b65SLandon J. Fuller static int
bhndb_pci_pwrctl_gate_clock(device_t dev,device_t child,bhnd_clock clock)1150f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_gate_clock(device_t dev, device_t child,
1151f90f4b65SLandon J. Fuller 	bhnd_clock clock)
1152f90f4b65SLandon J. Fuller {
1153f90f4b65SLandon J. Fuller 	struct bhndb_pci_softc *sc = device_get_softc(dev);
1154f90f4b65SLandon J. Fuller 
1155f90f4b65SLandon J. Fuller 	/* Only supported on PCI devices */
115689294a78SLandon J. Fuller 	if (bhndb_is_pcie_attached(sc->dev))
1157f90f4b65SLandon J. Fuller 		return (ENODEV);
1158f90f4b65SLandon J. Fuller 
1159f90f4b65SLandon J. Fuller 	/* Only HT is supported */
1160f90f4b65SLandon J. Fuller 	if (clock != BHND_CLOCK_HT)
1161f90f4b65SLandon J. Fuller 		return (ENXIO);
1162f90f4b65SLandon J. Fuller 
116389294a78SLandon J. Fuller 	return (bhndb_disable_pci_clocks(sc->dev));
1164f90f4b65SLandon J. Fuller }
1165f90f4b65SLandon J. Fuller 
1166f90f4b65SLandon J. Fuller static int
bhndb_pci_pwrctl_ungate_clock(device_t dev,device_t child,bhnd_clock clock)1167f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_ungate_clock(device_t dev, device_t child,
1168f90f4b65SLandon J. Fuller 	bhnd_clock clock)
1169f90f4b65SLandon J. Fuller {
1170f90f4b65SLandon J. Fuller 	struct bhndb_pci_softc *sc = device_get_softc(dev);
1171f90f4b65SLandon J. Fuller 
1172f90f4b65SLandon J. Fuller 	/* Only supported on PCI devices */
117389294a78SLandon J. Fuller 	if (bhndb_is_pcie_attached(sc->dev))
1174f90f4b65SLandon J. Fuller 		return (ENODEV);
1175f90f4b65SLandon J. Fuller 
1176f90f4b65SLandon J. Fuller 	/* Only HT is supported */
1177f90f4b65SLandon J. Fuller 	if (clock != BHND_CLOCK_HT)
1178f90f4b65SLandon J. Fuller 		return (ENXIO);
1179f90f4b65SLandon J. Fuller 
118089294a78SLandon J. Fuller 	return (bhndb_enable_pci_clocks(sc->dev));
1181f90f4b65SLandon J. Fuller }
1182f90f4b65SLandon J. Fuller 
1183caeff9a3SLandon J. Fuller /**
1184caeff9a3SLandon J. Fuller  * BHNDB_MAP_INTR_ISRC()
1185caeff9a3SLandon J. Fuller  */
1186824b48efSLandon J. Fuller static int
bhndb_pci_map_intr_isrc(device_t dev,struct resource * irq,struct bhndb_intr_isrc ** isrc)1187caeff9a3SLandon J. Fuller bhndb_pci_map_intr_isrc(device_t dev, struct resource *irq,
1188caeff9a3SLandon J. Fuller     struct bhndb_intr_isrc **isrc)
1189caeff9a3SLandon J. Fuller {
1190caeff9a3SLandon J. Fuller 	struct bhndb_pci_softc *sc = device_get_softc(dev);
1191caeff9a3SLandon J. Fuller 
1192caeff9a3SLandon J. Fuller 	/* There's only one bridged interrupt to choose from */
1193caeff9a3SLandon J. Fuller 	*isrc = sc->isrc;
1194caeff9a3SLandon J. Fuller 	return (0);
1195caeff9a3SLandon J. Fuller }
1196caeff9a3SLandon J. Fuller 
1197caeff9a3SLandon J. Fuller /* siba-specific implementation of BHNDB_ROUTE_INTERRUPTS() */
1198caeff9a3SLandon J. Fuller static int
bhndb_pci_route_siba_interrupts(struct bhndb_pci_softc * sc,device_t child)1199caeff9a3SLandon J. Fuller bhndb_pci_route_siba_interrupts(struct bhndb_pci_softc *sc, device_t child)
1200caeff9a3SLandon J. Fuller {
1201caeff9a3SLandon J. Fuller 	uint32_t	sbintvec;
1202caeff9a3SLandon J. Fuller 	u_int		ivec;
1203caeff9a3SLandon J. Fuller 	int		error;
1204caeff9a3SLandon J. Fuller 
1205caeff9a3SLandon J. Fuller 	KASSERT(sc->pci_quirks & BHNDB_PCI_QUIRK_SIBA_INTVEC,
1206caeff9a3SLandon J. Fuller 	    ("route_siba_interrupts not supported by this hardware"));
1207caeff9a3SLandon J. Fuller 
1208caeff9a3SLandon J. Fuller 	/* Fetch the sbflag# for the child */
1209caeff9a3SLandon J. Fuller 	if ((error = bhnd_get_intr_ivec(child, 0, &ivec)))
1210caeff9a3SLandon J. Fuller 		return (error);
1211caeff9a3SLandon J. Fuller 
1212caeff9a3SLandon J. Fuller 	if (ivec > (sizeof(sbintvec)*8) - 1 /* aka '31' */) {
1213caeff9a3SLandon J. Fuller 		/* This should never be an issue in practice */
1214caeff9a3SLandon J. Fuller 		device_printf(sc->dev, "cannot route interrupts to high "
1215caeff9a3SLandon J. Fuller 		    "sbflag# %u\n", ivec);
1216caeff9a3SLandon J. Fuller 		return (ENXIO);
1217caeff9a3SLandon J. Fuller 	}
1218caeff9a3SLandon J. Fuller 
1219caeff9a3SLandon J. Fuller 	BHNDB_PCI_LOCK(sc);
1220caeff9a3SLandon J. Fuller 
1221caeff9a3SLandon J. Fuller 	sbintvec = bhndb_pci_read_core(sc, SB0_REG_ABS(SIBA_CFG0_INTVEC), 4);
1222caeff9a3SLandon J. Fuller 	sbintvec |= (1 << ivec);
1223caeff9a3SLandon J. Fuller 	bhndb_pci_write_core(sc, SB0_REG_ABS(SIBA_CFG0_INTVEC), sbintvec, 4);
1224caeff9a3SLandon J. Fuller 
1225caeff9a3SLandon J. Fuller 	BHNDB_PCI_UNLOCK(sc);
1226caeff9a3SLandon J. Fuller 
1227caeff9a3SLandon J. Fuller 	return (0);
1228caeff9a3SLandon J. Fuller }
1229caeff9a3SLandon J. Fuller 
1230caeff9a3SLandon J. Fuller /* BHNDB_ROUTE_INTERRUPTS() */
1231caeff9a3SLandon J. Fuller static int
bhndb_pci_route_interrupts(device_t dev,device_t child)1232caeff9a3SLandon J. Fuller bhndb_pci_route_interrupts(device_t dev, device_t child)
1233824b48efSLandon J. Fuller {
1234824b48efSLandon J. Fuller 	struct bhndb_pci_softc	*sc;
1235caeff9a3SLandon J. Fuller 	struct bhnd_core_info	 core;
1236caeff9a3SLandon J. Fuller 	uint32_t		 core_bit;
1237caeff9a3SLandon J. Fuller 	uint32_t		 intmask;
1238824b48efSLandon J. Fuller 
1239824b48efSLandon J. Fuller 	sc = device_get_softc(dev);
1240824b48efSLandon J. Fuller 
1241caeff9a3SLandon J. Fuller 	if (sc->pci_quirks & BHNDB_PCI_QUIRK_SIBA_INTVEC)
1242caeff9a3SLandon J. Fuller 		return (bhndb_pci_route_siba_interrupts(sc, child));
1243824b48efSLandon J. Fuller 
1244caeff9a3SLandon J. Fuller 	core = bhnd_get_core_info(child);
1245caeff9a3SLandon J. Fuller 	if (core.core_idx > BHNDB_PCI_SBIM_COREIDX_MAX) {
1246caeff9a3SLandon J. Fuller 		/* This should never be an issue in practice */
1247caeff9a3SLandon J. Fuller 		device_printf(dev, "cannot route interrupts to high core "
1248caeff9a3SLandon J. Fuller 		    "index %u\n", core.core_idx);
1249caeff9a3SLandon J. Fuller 		return (ENXIO);
1250caeff9a3SLandon J. Fuller 	}
1251824b48efSLandon J. Fuller 
1252caeff9a3SLandon J. Fuller 	BHNDB_PCI_LOCK(sc);
1253caeff9a3SLandon J. Fuller 
1254caeff9a3SLandon J. Fuller 	core_bit = (1<<core.core_idx) << BHNDB_PCI_SBIM_SHIFT;
1255caeff9a3SLandon J. Fuller 	intmask = pci_read_config(sc->parent, BHNDB_PCI_INT_MASK, 4);
1256caeff9a3SLandon J. Fuller 	intmask |= core_bit;
1257caeff9a3SLandon J. Fuller 	pci_write_config(sc->parent, BHNDB_PCI_INT_MASK, intmask, 4);
1258caeff9a3SLandon J. Fuller 
1259caeff9a3SLandon J. Fuller 	BHNDB_PCI_UNLOCK(sc);
1260caeff9a3SLandon J. Fuller 
1261caeff9a3SLandon J. Fuller 	return (0);
1262824b48efSLandon J. Fuller }
1263824b48efSLandon J. Fuller 
126489294a78SLandon J. Fuller /**
126584d6a5d4SLandon J. Fuller  * Using the generic PCI bridge hardware configuration, allocate, initialize
126684d6a5d4SLandon J. Fuller  * and return a new bhndb_pci probe state instance.
126789294a78SLandon J. Fuller  *
126884d6a5d4SLandon J. Fuller  * On success, the caller assumes ownership of the returned probe instance, and
126984d6a5d4SLandon J. Fuller  * is responsible for releasing this reference using bhndb_pci_probe_free().
127084d6a5d4SLandon J. Fuller  *
127184d6a5d4SLandon J. Fuller  * @param[out]	probe		On success, the newly allocated probe instance.
127284d6a5d4SLandon J. Fuller  * @param	dev		The bhndb_pci bridge device.
127384d6a5d4SLandon J. Fuller  * @param	hostb_devclass	The expected device class of the bridge core.
127484d6a5d4SLandon J. Fuller  *
127584d6a5d4SLandon J. Fuller  * @retval 0		success
127684d6a5d4SLandon J. Fuller  * @retval non-zero	if allocating the probe state fails, a regular
127784d6a5d4SLandon J. Fuller  * 			unix error code will be returned.
127884d6a5d4SLandon J. Fuller  *
127984d6a5d4SLandon J. Fuller  * @note This function requires exclusive ownership over allocating and
128084d6a5d4SLandon J. Fuller  * configuring host bridge resources, and should only be called prior to
128184d6a5d4SLandon J. Fuller  * completion of device attach and full configuration of the bridge.
128289294a78SLandon J. Fuller  */
128389294a78SLandon J. Fuller static int
bhndb_pci_probe_alloc(struct bhndb_pci_probe ** probe,device_t dev,bhnd_devclass_t hostb_devclass)128484d6a5d4SLandon J. Fuller bhndb_pci_probe_alloc(struct bhndb_pci_probe **probe, device_t dev,
128584d6a5d4SLandon J. Fuller     bhnd_devclass_t hostb_devclass)
128689294a78SLandon J. Fuller {
128784d6a5d4SLandon J. Fuller 	struct bhndb_pci_probe		*p;
128884d6a5d4SLandon J. Fuller 	struct bhnd_erom_io		*eio;
128984d6a5d4SLandon J. Fuller 	const struct bhndb_hwcfg	*hwcfg;
129084d6a5d4SLandon J. Fuller 	const struct bhnd_chipid	*hint;
129184d6a5d4SLandon J. Fuller 	device_t			 parent_dev;
129284d6a5d4SLandon J. Fuller 	int				 error;
129389294a78SLandon J. Fuller 
129484d6a5d4SLandon J. Fuller 	parent_dev = device_get_parent(dev);
129584d6a5d4SLandon J. Fuller 	eio = NULL;
129684d6a5d4SLandon J. Fuller 
129784d6a5d4SLandon J. Fuller 	p = malloc(sizeof(*p), M_BHND, M_ZERO|M_WAITOK);
129884d6a5d4SLandon J. Fuller 	p->dev = dev;
129984d6a5d4SLandon J. Fuller 	p->pci_dev = parent_dev;
130084d6a5d4SLandon J. Fuller 
130184d6a5d4SLandon J. Fuller 	/* Our register window mapping state must be initialized at this point,
130284d6a5d4SLandon J. Fuller 	 * as bhndb_pci_eio will begin making calls into
130384d6a5d4SLandon J. Fuller 	 * bhndb_pci_probe_(read|write|get_mapping) */
130484d6a5d4SLandon J. Fuller 	p->m_win = NULL;
130584d6a5d4SLandon J. Fuller 	p->m_res = NULL;
130684d6a5d4SLandon J. Fuller 	p->m_valid = false;
130784d6a5d4SLandon J. Fuller 
130884d6a5d4SLandon J. Fuller 	bhndb_pci_eio_init(&p->erom_io, p);
130984d6a5d4SLandon J. Fuller 	eio = &p->erom_io.eio;
131084d6a5d4SLandon J. Fuller 
131184d6a5d4SLandon J. Fuller 	/* Fetch our chipid hint (if any) and generic hardware configuration */
131284d6a5d4SLandon J. Fuller 	hwcfg = BHNDB_BUS_GET_GENERIC_HWCFG(parent_dev, dev);
131384d6a5d4SLandon J. Fuller 	hint = BHNDB_BUS_GET_CHIPID(parent_dev, dev);
131484d6a5d4SLandon J. Fuller 
131584d6a5d4SLandon J. Fuller 	/* Allocate our host resources */
131684d6a5d4SLandon J. Fuller 	error = bhndb_alloc_host_resources(&p->hr, dev, parent_dev, hwcfg);
131784d6a5d4SLandon J. Fuller 	if (error) {
131884d6a5d4SLandon J. Fuller 		p->hr = NULL;
131984d6a5d4SLandon J. Fuller 		goto failed;
132084d6a5d4SLandon J. Fuller 	}
132184d6a5d4SLandon J. Fuller 
132284d6a5d4SLandon J. Fuller 	/* Map the first bus core from our bridged bhnd(4) bus */
132384d6a5d4SLandon J. Fuller 	error = bhnd_erom_io_map(eio, BHND_DEFAULT_CHIPC_ADDR,
132484d6a5d4SLandon J. Fuller 	    BHND_DEFAULT_CORE_SIZE);
132584d6a5d4SLandon J. Fuller 	if (error)
132684d6a5d4SLandon J. Fuller 		goto failed;
132784d6a5d4SLandon J. Fuller 
132884d6a5d4SLandon J. Fuller 	/* Probe for a usable EROM class, and read the chip identifier */
132984d6a5d4SLandon J. Fuller 	p->erom_class = bhnd_erom_probe_driver_classes(
133084d6a5d4SLandon J. Fuller 	    device_get_devclass(dev), eio, hint, &p->cid);
133184d6a5d4SLandon J. Fuller 	if (p->erom_class == NULL) {
133284d6a5d4SLandon J. Fuller 		device_printf(dev, "device enumeration unsupported; no "
133384d6a5d4SLandon J. Fuller 		    "compatible driver found\n");
133484d6a5d4SLandon J. Fuller 
133584d6a5d4SLandon J. Fuller 		error = ENXIO;
133684d6a5d4SLandon J. Fuller 		goto failed;
133784d6a5d4SLandon J. Fuller 	}
133884d6a5d4SLandon J. Fuller 
133984d6a5d4SLandon J. Fuller 	/* Allocate EROM parser */
134084d6a5d4SLandon J. Fuller 	p->erom = bhnd_erom_alloc(p->erom_class, &p->cid, eio);
134184d6a5d4SLandon J. Fuller 	if (p->erom == NULL) {
134284d6a5d4SLandon J. Fuller 		device_printf(dev, "failed to allocate device enumeration "
134384d6a5d4SLandon J. Fuller 		    "table parser\n");
134484d6a5d4SLandon J. Fuller 		error = ENXIO;
134584d6a5d4SLandon J. Fuller 		goto failed;
134684d6a5d4SLandon J. Fuller 	}
134784d6a5d4SLandon J. Fuller 
134884d6a5d4SLandon J. Fuller 	/* The EROM I/O instance is now owned by our EROM parser */
134984d6a5d4SLandon J. Fuller 	eio = NULL;
135084d6a5d4SLandon J. Fuller 
135184d6a5d4SLandon J. Fuller 	/* Read the full core table */
135284d6a5d4SLandon J. Fuller 	error = bhnd_erom_get_core_table(p->erom, &p->cores, &p->ncores);
135384d6a5d4SLandon J. Fuller 	if (error) {
135484d6a5d4SLandon J. Fuller 		device_printf(p->dev, "error fetching core table: %d\n",
135584d6a5d4SLandon J. Fuller 		    error);
135684d6a5d4SLandon J. Fuller 
135784d6a5d4SLandon J. Fuller 		p->cores = NULL;
135884d6a5d4SLandon J. Fuller 		goto failed;
135984d6a5d4SLandon J. Fuller 	}
136084d6a5d4SLandon J. Fuller 
136184d6a5d4SLandon J. Fuller 	/* Identify the host bridge core */
136284d6a5d4SLandon J. Fuller 	error = bhndb_find_hostb_core(p->cores, p->ncores, hostb_devclass,
136384d6a5d4SLandon J. Fuller 	    &p->hostb_core);
136484d6a5d4SLandon J. Fuller 	if (error) {
136584d6a5d4SLandon J. Fuller 		device_printf(dev, "failed to identify the host bridge "
136684d6a5d4SLandon J. Fuller 		    "core: %d\n", error);
136784d6a5d4SLandon J. Fuller 
136884d6a5d4SLandon J. Fuller 		goto failed;
136984d6a5d4SLandon J. Fuller 	}
137084d6a5d4SLandon J. Fuller 
137184d6a5d4SLandon J. Fuller 	*probe = p;
137284d6a5d4SLandon J. Fuller 	return (0);
137384d6a5d4SLandon J. Fuller 
137484d6a5d4SLandon J. Fuller failed:
137584d6a5d4SLandon J. Fuller 	if (eio != NULL) {
137684d6a5d4SLandon J. Fuller 		KASSERT(p->erom == NULL, ("I/O instance will be freed by "
137784d6a5d4SLandon J. Fuller 		    "its owning parser"));
137884d6a5d4SLandon J. Fuller 
137984d6a5d4SLandon J. Fuller 		bhnd_erom_io_fini(eio);
138084d6a5d4SLandon J. Fuller 	}
138184d6a5d4SLandon J. Fuller 
138284d6a5d4SLandon J. Fuller 	if (p->erom != NULL) {
138384d6a5d4SLandon J. Fuller 		if (p->cores != NULL)
138484d6a5d4SLandon J. Fuller 			bhnd_erom_free_core_table(p->erom, p->cores);
138584d6a5d4SLandon J. Fuller 
138684d6a5d4SLandon J. Fuller 		bhnd_erom_free(p->erom);
138784d6a5d4SLandon J. Fuller 	} else {
138884d6a5d4SLandon J. Fuller 		KASSERT(p->cores == NULL, ("cannot free erom-owned core table "
138984d6a5d4SLandon J. Fuller 		    "without erom reference"));
139084d6a5d4SLandon J. Fuller 	}
139184d6a5d4SLandon J. Fuller 
139284d6a5d4SLandon J. Fuller 	if (p->hr != NULL)
139384d6a5d4SLandon J. Fuller 		bhndb_release_host_resources(p->hr);
139484d6a5d4SLandon J. Fuller 
139584d6a5d4SLandon J. Fuller 	free(p, M_BHND);
139684d6a5d4SLandon J. Fuller 
139784d6a5d4SLandon J. Fuller 	return (error);
139884d6a5d4SLandon J. Fuller }
139984d6a5d4SLandon J. Fuller 
140084d6a5d4SLandon J. Fuller /**
140184d6a5d4SLandon J. Fuller  * Free the given @p probe instance and any associated host bridge resources.
140284d6a5d4SLandon J. Fuller  */
140384d6a5d4SLandon J. Fuller static void
bhndb_pci_probe_free(struct bhndb_pci_probe * probe)140484d6a5d4SLandon J. Fuller bhndb_pci_probe_free(struct bhndb_pci_probe *probe)
140584d6a5d4SLandon J. Fuller {
140684d6a5d4SLandon J. Fuller 	bhnd_erom_free_core_table(probe->erom, probe->cores);
140784d6a5d4SLandon J. Fuller 	bhnd_erom_free(probe->erom);
140884d6a5d4SLandon J. Fuller 	bhndb_release_host_resources(probe->hr);
140984d6a5d4SLandon J. Fuller 	free(probe, M_BHND);
141084d6a5d4SLandon J. Fuller }
141184d6a5d4SLandon J. Fuller 
141284d6a5d4SLandon J. Fuller /**
141384d6a5d4SLandon J. Fuller  * Return a copy of probed core table from @p probe.
141484d6a5d4SLandon J. Fuller  *
141584d6a5d4SLandon J. Fuller  * @param	probe		The probe instance.
141684d6a5d4SLandon J. Fuller  * @param[out]	cores		On success, a copy of the probed core table. The
141784d6a5d4SLandon J. Fuller  *				caller is responsible for freeing this table
141884d6a5d4SLandon J. Fuller  *				bhndb_pci_probe_free_core_table().
141984d6a5d4SLandon J. Fuller  * @param[out]	ncores		On success, the number of cores found in
142084d6a5d4SLandon J. Fuller  *				@p cores.
142184d6a5d4SLandon J. Fuller  *
142284d6a5d4SLandon J. Fuller  * @retval 0		success
142384d6a5d4SLandon J. Fuller  * @retval non-zero	if enumerating the bridged bhnd(4) bus fails, a regular
142484d6a5d4SLandon J. Fuller  * 			unix error code will be returned.
142584d6a5d4SLandon J. Fuller  */
142684d6a5d4SLandon J. Fuller static int
bhndb_pci_probe_copy_core_table(struct bhndb_pci_probe * probe,struct bhnd_core_info ** cores,u_int * ncores)142784d6a5d4SLandon J. Fuller bhndb_pci_probe_copy_core_table(struct bhndb_pci_probe *probe,
142884d6a5d4SLandon J. Fuller     struct bhnd_core_info **cores, u_int *ncores)
142984d6a5d4SLandon J. Fuller {
143084d6a5d4SLandon J. Fuller 	size_t len = sizeof(**cores) * probe->ncores;
143184d6a5d4SLandon J. Fuller 
143284d6a5d4SLandon J. Fuller 	*cores = malloc(len, M_BHND, M_WAITOK);
143384d6a5d4SLandon J. Fuller 	memcpy(*cores, probe->cores, len);
143484d6a5d4SLandon J. Fuller 
143584d6a5d4SLandon J. Fuller 	*ncores = probe->ncores;
143689294a78SLandon J. Fuller 
143789294a78SLandon J. Fuller 	return (0);
143889294a78SLandon J. Fuller }
143989294a78SLandon J. Fuller 
144089294a78SLandon J. Fuller /**
144184d6a5d4SLandon J. Fuller  * Free a core table previously returned by bhndb_pci_probe_copy_core_table().
144289294a78SLandon J. Fuller  *
144384d6a5d4SLandon J. Fuller  * @param cores The core table to be freed.
144489294a78SLandon J. Fuller  */
144584d6a5d4SLandon J. Fuller static void
bhndb_pci_probe_free_core_table(struct bhnd_core_info * cores)144684d6a5d4SLandon J. Fuller bhndb_pci_probe_free_core_table(struct bhnd_core_info *cores)
144784d6a5d4SLandon J. Fuller {
144884d6a5d4SLandon J. Fuller 	free(cores, M_BHND);
144984d6a5d4SLandon J. Fuller }
145084d6a5d4SLandon J. Fuller 
145184d6a5d4SLandon J. Fuller /**
145284d6a5d4SLandon J. Fuller  * Return true if @p addr and @p size are mapped by the dynamic register window
145384d6a5d4SLandon J. Fuller  * backing @p probe.
145484d6a5d4SLandon J. Fuller  */
145584d6a5d4SLandon J. Fuller static bool
bhndb_pci_probe_has_mapping(struct bhndb_pci_probe * probe,bhnd_addr_t addr,bhnd_size_t size)145684d6a5d4SLandon J. Fuller bhndb_pci_probe_has_mapping(struct bhndb_pci_probe *probe, bhnd_addr_t addr,
145789294a78SLandon J. Fuller     bhnd_size_t size)
145889294a78SLandon J. Fuller {
145984d6a5d4SLandon J. Fuller 	if (!probe->m_valid)
146084d6a5d4SLandon J. Fuller 		return (false);
146184d6a5d4SLandon J. Fuller 
146284d6a5d4SLandon J. Fuller 	KASSERT(probe->m_win != NULL, ("missing register window"));
146384d6a5d4SLandon J. Fuller 	KASSERT(probe->m_res != NULL, ("missing regwin resource"));
146484d6a5d4SLandon J. Fuller 	KASSERT(probe->m_win->win_type == BHNDB_REGWIN_T_DYN,
146584d6a5d4SLandon J. Fuller 	    ("unexpected window type %d", probe->m_win->win_type));
146684d6a5d4SLandon J. Fuller 
146784d6a5d4SLandon J. Fuller 	if (addr < probe->m_target)
146884d6a5d4SLandon J. Fuller 		return (false);
146984d6a5d4SLandon J. Fuller 
147084d6a5d4SLandon J. Fuller 	if (addr >= probe->m_target + probe->m_win->win_size)
147184d6a5d4SLandon J. Fuller 		return (false);
147284d6a5d4SLandon J. Fuller 
147384d6a5d4SLandon J. Fuller 	if ((probe->m_target + probe->m_win->win_size) - addr < size)
147484d6a5d4SLandon J. Fuller 		return (false);
147584d6a5d4SLandon J. Fuller 
147684d6a5d4SLandon J. Fuller 	return (true);
147784d6a5d4SLandon J. Fuller }
147884d6a5d4SLandon J. Fuller 
147984d6a5d4SLandon J. Fuller /**
148084d6a5d4SLandon J. Fuller  * Attempt to adjust the dynamic register window backing @p probe to permit
148184d6a5d4SLandon J. Fuller  * accessing @p size bytes at @p addr.
148284d6a5d4SLandon J. Fuller  *
148384d6a5d4SLandon J. Fuller  * @param	probe		The bhndb_pci probe state to be modified.
148484d6a5d4SLandon J. Fuller  * @param	addr		The address at which @p size bytes will mapped.
148584d6a5d4SLandon J. Fuller  * @param	size		The number of bytes to be mapped.
148684d6a5d4SLandon J. Fuller  * @param[out]	res		On success, will be set to the host resource
148784d6a5d4SLandon J. Fuller  *				mapping @p size bytes at @p addr.
148884d6a5d4SLandon J. Fuller  * @param[out]	res_offset	On success, will be set to the offset of @addr
148984d6a5d4SLandon J. Fuller  *				within @p res.
149084d6a5d4SLandon J. Fuller  *
149184d6a5d4SLandon J. Fuller  * @retval 0		success
149284d6a5d4SLandon J. Fuller  * @retval non-zero	if an error occurs adjusting the backing dynamic
149384d6a5d4SLandon J. Fuller  *			register window.
149484d6a5d4SLandon J. Fuller  */
149584d6a5d4SLandon J. Fuller static int
bhndb_pci_probe_map(struct bhndb_pci_probe * probe,bhnd_addr_t addr,bhnd_size_t offset,bhnd_size_t size,struct resource ** res,bus_size_t * res_offset)149684d6a5d4SLandon J. Fuller bhndb_pci_probe_map(struct bhndb_pci_probe *probe, bhnd_addr_t addr,
149784d6a5d4SLandon J. Fuller     bhnd_size_t offset, bhnd_size_t size, struct resource **res,
149884d6a5d4SLandon J. Fuller     bus_size_t *res_offset)
149984d6a5d4SLandon J. Fuller {
150084d6a5d4SLandon J. Fuller 	const struct bhndb_regwin	*regwin, *regwin_table;
150184d6a5d4SLandon J. Fuller 	struct resource			*regwin_res;
150289294a78SLandon J. Fuller 	bhnd_addr_t			 target;
150389294a78SLandon J. Fuller 	int				 error;
150489294a78SLandon J. Fuller 
150584d6a5d4SLandon J. Fuller 	/* Determine the absolute address */
150684d6a5d4SLandon J. Fuller 	if (BHND_SIZE_MAX - offset < addr) {
150784d6a5d4SLandon J. Fuller 		device_printf(probe->dev, "invalid offset %#jx+%#jx\n", addr,
150884d6a5d4SLandon J. Fuller 		    offset);
150989294a78SLandon J. Fuller 		return (ENXIO);
151089294a78SLandon J. Fuller 	}
151189294a78SLandon J. Fuller 
151284d6a5d4SLandon J. Fuller 	addr += offset;
151384d6a5d4SLandon J. Fuller 
151484d6a5d4SLandon J. Fuller 	/* Can we use the existing mapping? */
151584d6a5d4SLandon J. Fuller 	if (bhndb_pci_probe_has_mapping(probe, addr, size)) {
151684d6a5d4SLandon J. Fuller 		*res = probe->m_res;
151784d6a5d4SLandon J. Fuller 		*res_offset = (addr - probe->m_target) +
151884d6a5d4SLandon J. Fuller 		    probe->m_win->win_offset;
151984d6a5d4SLandon J. Fuller 
152089294a78SLandon J. Fuller 		return (0);
152189294a78SLandon J. Fuller 	}
152289294a78SLandon J. Fuller 
152384d6a5d4SLandon J. Fuller 	/* Locate a useable dynamic register window */
152484d6a5d4SLandon J. Fuller 	regwin_table = probe->hr->cfg->register_windows;
152584d6a5d4SLandon J. Fuller 	regwin = bhndb_regwin_find_type(regwin_table,
152684d6a5d4SLandon J. Fuller 	    BHNDB_REGWIN_T_DYN, size);
152784d6a5d4SLandon J. Fuller 	if (regwin == NULL) {
152884d6a5d4SLandon J. Fuller 		device_printf(probe->dev, "unable to map %#jx+%#jx; no "
152984d6a5d4SLandon J. Fuller 		    "usable dynamic register window found\n", addr,
153084d6a5d4SLandon J. Fuller 		    size);
153184d6a5d4SLandon J. Fuller 		return (ENXIO);
153284d6a5d4SLandon J. Fuller 	}
153384d6a5d4SLandon J. Fuller 
153484d6a5d4SLandon J. Fuller 	/* Locate the host resource mapping our register window */
153584d6a5d4SLandon J. Fuller 	regwin_res = bhndb_host_resource_for_regwin(probe->hr, regwin);
153684d6a5d4SLandon J. Fuller 	if (regwin_res == NULL) {
153784d6a5d4SLandon J. Fuller 		device_printf(probe->dev, "unable to map %#jx+%#jx; no "
153884d6a5d4SLandon J. Fuller 		    "usable register resource found\n", addr, size);
153984d6a5d4SLandon J. Fuller 		return (ENXIO);
154084d6a5d4SLandon J. Fuller 	}
154184d6a5d4SLandon J. Fuller 
154289294a78SLandon J. Fuller 	/* Page-align the target address */
154384d6a5d4SLandon J. Fuller 	target = addr - (addr % regwin->win_size);
154489294a78SLandon J. Fuller 
154589294a78SLandon J. Fuller 	/* Configure the register window */
154684d6a5d4SLandon J. Fuller 	error = bhndb_pci_compat_setregwin(probe->dev, probe->pci_dev,
154784d6a5d4SLandon J. Fuller 	    regwin, target);
154889294a78SLandon J. Fuller 	if (error) {
154984d6a5d4SLandon J. Fuller 		device_printf(probe->dev, "failed to configure dynamic "
155084d6a5d4SLandon J. Fuller 		    "register window: %d\n", error);
155189294a78SLandon J. Fuller 		return (error);
155289294a78SLandon J. Fuller 	}
155389294a78SLandon J. Fuller 
155484d6a5d4SLandon J. Fuller 	/* Update our mapping state */
155584d6a5d4SLandon J. Fuller 	probe->m_win = regwin;
155684d6a5d4SLandon J. Fuller 	probe->m_res = regwin_res;
155784d6a5d4SLandon J. Fuller 	probe->m_addr = addr;
155884d6a5d4SLandon J. Fuller 	probe->m_size = size;
155984d6a5d4SLandon J. Fuller 	probe->m_target = target;
156084d6a5d4SLandon J. Fuller 	probe->m_valid = true;
156184d6a5d4SLandon J. Fuller 
156284d6a5d4SLandon J. Fuller 	*res = regwin_res;
156384d6a5d4SLandon J. Fuller 	*res_offset = (addr - target) + regwin->win_offset;
156484d6a5d4SLandon J. Fuller 
156589294a78SLandon J. Fuller 	return (0);
156689294a78SLandon J. Fuller }
156789294a78SLandon J. Fuller 
156884d6a5d4SLandon J. Fuller /**
156984d6a5d4SLandon J. Fuller  * Write a data item to the bridged address space at the given @p offset from
157084d6a5d4SLandon J. Fuller  * @p addr.
157184d6a5d4SLandon J. Fuller  *
157284d6a5d4SLandon J. Fuller  * A dynamic register window will be used to map @p addr.
157384d6a5d4SLandon J. Fuller  *
157484d6a5d4SLandon J. Fuller  * @param probe		The bhndb_pci probe state to be used to perform the
157584d6a5d4SLandon J. Fuller  *			write.
157684d6a5d4SLandon J. Fuller  * @param addr		The base address.
157784d6a5d4SLandon J. Fuller  * @param offset	The offset from @p addr at which @p value will be
157884d6a5d4SLandon J. Fuller  *			written.
157984d6a5d4SLandon J. Fuller  * @param value		The data item to be written.
158084d6a5d4SLandon J. Fuller  * @param width		The data item width (1, 2, or 4 bytes).
158184d6a5d4SLandon J. Fuller  */
158284d6a5d4SLandon J. Fuller static void
bhndb_pci_probe_write(struct bhndb_pci_probe * probe,bhnd_addr_t addr,bhnd_size_t offset,uint32_t value,u_int width)158384d6a5d4SLandon J. Fuller bhndb_pci_probe_write(struct bhndb_pci_probe *probe, bhnd_addr_t addr,
158484d6a5d4SLandon J. Fuller     bhnd_size_t offset, uint32_t value, u_int width)
158584d6a5d4SLandon J. Fuller {
158684d6a5d4SLandon J. Fuller 	struct resource	*r;
158784d6a5d4SLandon J. Fuller 	bus_size_t	 res_offset;
158884d6a5d4SLandon J. Fuller 	int		 error;
158984d6a5d4SLandon J. Fuller 
159084d6a5d4SLandon J. Fuller 	/* Map the target address */
159184d6a5d4SLandon J. Fuller 	error = bhndb_pci_probe_map(probe, addr, offset, width, &r,
159284d6a5d4SLandon J. Fuller 	    &res_offset);
159384d6a5d4SLandon J. Fuller 	if (error) {
159484d6a5d4SLandon J. Fuller 		device_printf(probe->dev, "error mapping %#jx+%#jx for "
159584d6a5d4SLandon J. Fuller 		    "writing: %d\n", addr, offset, error);
159684d6a5d4SLandon J. Fuller 		return;
159784d6a5d4SLandon J. Fuller 	}
159884d6a5d4SLandon J. Fuller 
159984d6a5d4SLandon J. Fuller 	/* Perform write */
160084d6a5d4SLandon J. Fuller 	switch (width) {
160184d6a5d4SLandon J. Fuller 	case 1:
160284d6a5d4SLandon J. Fuller 		return (bus_write_1(r, res_offset, value));
160384d6a5d4SLandon J. Fuller 	case 2:
160484d6a5d4SLandon J. Fuller 		return (bus_write_2(r, res_offset, value));
160584d6a5d4SLandon J. Fuller 	case 4:
160684d6a5d4SLandon J. Fuller 		return (bus_write_4(r, res_offset, value));
160784d6a5d4SLandon J. Fuller 	default:
160884d6a5d4SLandon J. Fuller 		panic("unsupported width: %u", width);
160984d6a5d4SLandon J. Fuller 	}
161084d6a5d4SLandon J. Fuller }
161184d6a5d4SLandon J. Fuller 
161284d6a5d4SLandon J. Fuller /**
161384d6a5d4SLandon J. Fuller  * Read a data item from the bridged address space at the given @p offset
161484d6a5d4SLandon J. Fuller  * from @p addr.
161584d6a5d4SLandon J. Fuller  *
161684d6a5d4SLandon J. Fuller  * A dynamic register window will be used to map @p addr.
161784d6a5d4SLandon J. Fuller  *
161884d6a5d4SLandon J. Fuller  * @param probe		The bhndb_pci probe state to be used to perform the
161984d6a5d4SLandon J. Fuller  *			read.
162084d6a5d4SLandon J. Fuller  * @param addr		The base address.
162184d6a5d4SLandon J. Fuller  * @param offset	The offset from @p addr at which to read a data item of
162284d6a5d4SLandon J. Fuller  *			@p width bytes.
162384d6a5d4SLandon J. Fuller  * @param width		Item width (1, 2, or 4 bytes).
162484d6a5d4SLandon J. Fuller  */
162584d6a5d4SLandon J. Fuller static uint32_t
bhndb_pci_probe_read(struct bhndb_pci_probe * probe,bhnd_addr_t addr,bhnd_size_t offset,u_int width)162684d6a5d4SLandon J. Fuller bhndb_pci_probe_read(struct bhndb_pci_probe *probe, bhnd_addr_t addr,
162784d6a5d4SLandon J. Fuller     bhnd_size_t offset, u_int width)
162884d6a5d4SLandon J. Fuller {
162984d6a5d4SLandon J. Fuller 	struct resource	*r;
163084d6a5d4SLandon J. Fuller 	bus_size_t	 res_offset;
163184d6a5d4SLandon J. Fuller 	int		 error;
163284d6a5d4SLandon J. Fuller 
163384d6a5d4SLandon J. Fuller 	/* Map the target address */
163484d6a5d4SLandon J. Fuller 	error = bhndb_pci_probe_map(probe, addr, offset, width, &r,
163584d6a5d4SLandon J. Fuller 	    &res_offset);
163684d6a5d4SLandon J. Fuller 	if (error) {
163784d6a5d4SLandon J. Fuller 		device_printf(probe->dev, "error mapping %#jx+%#jx for "
163884d6a5d4SLandon J. Fuller 		    "reading: %d\n", addr, offset, error);
163984d6a5d4SLandon J. Fuller 		return (UINT32_MAX);
164084d6a5d4SLandon J. Fuller 	}
164184d6a5d4SLandon J. Fuller 
164284d6a5d4SLandon J. Fuller 	/* Perform read */
164384d6a5d4SLandon J. Fuller 	switch (width) {
164484d6a5d4SLandon J. Fuller 	case 1:
164584d6a5d4SLandon J. Fuller 		return (bus_read_1(r, res_offset));
164684d6a5d4SLandon J. Fuller 	case 2:
164784d6a5d4SLandon J. Fuller 		return (bus_read_2(r, res_offset));
164884d6a5d4SLandon J. Fuller 	case 4:
164984d6a5d4SLandon J. Fuller 		return (bus_read_4(r, res_offset));
165084d6a5d4SLandon J. Fuller 	default:
165184d6a5d4SLandon J. Fuller 		panic("unsupported width: %u", width);
165284d6a5d4SLandon J. Fuller 	}
165384d6a5d4SLandon J. Fuller }
165484d6a5d4SLandon J. Fuller 
165584d6a5d4SLandon J. Fuller /**
165684d6a5d4SLandon J. Fuller  * Initialize a new bhndb PCI bridge EROM I/O instance. All I/O will be
165784d6a5d4SLandon J. Fuller  * performed using @p probe.
165884d6a5d4SLandon J. Fuller  *
165984d6a5d4SLandon J. Fuller  * @param pio		The instance to be initialized.
166084d6a5d4SLandon J. Fuller  * @param probe		The bhndb_pci probe state to be used to perform all
166184d6a5d4SLandon J. Fuller  *			I/O.
166284d6a5d4SLandon J. Fuller  */
166384d6a5d4SLandon J. Fuller static void
bhndb_pci_eio_init(struct bhndb_pci_eio * pio,struct bhndb_pci_probe * probe)166484d6a5d4SLandon J. Fuller bhndb_pci_eio_init(struct bhndb_pci_eio *pio, struct bhndb_pci_probe *probe)
166584d6a5d4SLandon J. Fuller {
166684d6a5d4SLandon J. Fuller 	memset(pio, 0, sizeof(*pio));
166784d6a5d4SLandon J. Fuller 
166884d6a5d4SLandon J. Fuller 	pio->eio.map = bhndb_pci_eio_map;
1669f3524ec8SLandon J. Fuller 	pio->eio.tell = bhndb_pci_eio_tell;
167084d6a5d4SLandon J. Fuller 	pio->eio.read = bhndb_pci_eio_read;
167184d6a5d4SLandon J. Fuller 	pio->eio.fini = NULL;
167284d6a5d4SLandon J. Fuller 
1673f3524ec8SLandon J. Fuller 	pio->mapped = false;
167484d6a5d4SLandon J. Fuller 	pio->addr = 0;
167584d6a5d4SLandon J. Fuller 	pio->size = 0;
167684d6a5d4SLandon J. Fuller 	pio->probe = probe;
167784d6a5d4SLandon J. Fuller }
167884d6a5d4SLandon J. Fuller 
167989294a78SLandon J. Fuller /* bhnd_erom_io_map() implementation */
168089294a78SLandon J. Fuller static int
bhndb_pci_eio_map(struct bhnd_erom_io * eio,bhnd_addr_t addr,bhnd_size_t size)168189294a78SLandon J. Fuller bhndb_pci_eio_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
168289294a78SLandon J. Fuller     bhnd_size_t size)
168389294a78SLandon J. Fuller {
168484d6a5d4SLandon J. Fuller 	struct bhndb_pci_eio *pio = (struct bhndb_pci_eio *)eio;
168589294a78SLandon J. Fuller 
168684d6a5d4SLandon J. Fuller 	if (BHND_ADDR_MAX - addr < size)
168784d6a5d4SLandon J. Fuller 		return (EINVAL); /* addr+size would overflow */
168889294a78SLandon J. Fuller 
168989294a78SLandon J. Fuller 	pio->addr = addr;
169089294a78SLandon J. Fuller 	pio->size = size;
1691f3524ec8SLandon J. Fuller 	pio->mapped = true;
1692f3524ec8SLandon J. Fuller 
1693f3524ec8SLandon J. Fuller 	return (0);
1694f3524ec8SLandon J. Fuller }
1695f3524ec8SLandon J. Fuller 
1696f3524ec8SLandon J. Fuller /* bhnd_erom_io_tell() implementation */
1697f3524ec8SLandon J. Fuller static int
bhndb_pci_eio_tell(struct bhnd_erom_io * eio,bhnd_addr_t * addr,bhnd_size_t * size)1698f3524ec8SLandon J. Fuller bhndb_pci_eio_tell(struct bhnd_erom_io *eio, bhnd_addr_t *addr,
1699f3524ec8SLandon J. Fuller     bhnd_size_t *size)
1700f3524ec8SLandon J. Fuller {
1701f3524ec8SLandon J. Fuller 	struct bhndb_pci_eio *pio = (struct bhndb_pci_eio *)eio;
1702f3524ec8SLandon J. Fuller 
1703f3524ec8SLandon J. Fuller 	if (!pio->mapped)
1704f3524ec8SLandon J. Fuller 		return (ENXIO);
1705f3524ec8SLandon J. Fuller 
1706f3524ec8SLandon J. Fuller 	*addr = pio->addr;
1707f3524ec8SLandon J. Fuller 	*size = pio->size;
170889294a78SLandon J. Fuller 
170989294a78SLandon J. Fuller 	return (0);
171089294a78SLandon J. Fuller }
171189294a78SLandon J. Fuller 
171289294a78SLandon J. Fuller /* bhnd_erom_io_read() implementation */
171389294a78SLandon J. Fuller static uint32_t
bhndb_pci_eio_read(struct bhnd_erom_io * eio,bhnd_size_t offset,u_int width)171489294a78SLandon J. Fuller bhndb_pci_eio_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
171589294a78SLandon J. Fuller {
171684d6a5d4SLandon J. Fuller 	struct bhndb_pci_eio *pio = (struct bhndb_pci_eio *)eio;
171789294a78SLandon J. Fuller 
1718f3524ec8SLandon J. Fuller 	/* Must have a valid mapping */
1719f3524ec8SLandon J. Fuller 	if (!pio->mapped)
1720f3524ec8SLandon J. Fuller 		panic("no active mapping");
1721f3524ec8SLandon J. Fuller 
172284d6a5d4SLandon J. Fuller 	/* The requested subrange must fall within the existing mapped range */
172384d6a5d4SLandon J. Fuller 	if (offset > pio->size ||
172484d6a5d4SLandon J. Fuller 	    width > pio->size ||
172584d6a5d4SLandon J. Fuller 	    pio->size - offset < width)
172684d6a5d4SLandon J. Fuller 	{
1727f3524ec8SLandon J. Fuller 		panic("invalid offset %#jx", offset);
172889294a78SLandon J. Fuller 	}
172989294a78SLandon J. Fuller 
173084d6a5d4SLandon J. Fuller 	return (bhndb_pci_probe_read(pio->probe, pio->addr, offset, width));
173189294a78SLandon J. Fuller }
173289294a78SLandon J. Fuller 
17334ad7e9b0SAdrian Chadd static device_method_t bhndb_pci_methods[] = {
17344ad7e9b0SAdrian Chadd 	/* Device interface */
17354ad7e9b0SAdrian Chadd 	DEVMETHOD(device_probe,				bhndb_pci_probe),
17364ad7e9b0SAdrian Chadd 	DEVMETHOD(device_attach,			bhndb_pci_attach),
17374ad7e9b0SAdrian Chadd 	DEVMETHOD(device_resume,			bhndb_pci_resume),
1738bb64eeccSAdrian Chadd 	DEVMETHOD(device_suspend,			bhndb_pci_suspend),
1739bb64eeccSAdrian Chadd 	DEVMETHOD(device_detach,			bhndb_pci_detach),
17404ad7e9b0SAdrian Chadd 
17414ad7e9b0SAdrian Chadd 	/* BHNDB interface */
17424ad7e9b0SAdrian Chadd 	DEVMETHOD(bhndb_set_window_addr,		bhndb_pci_set_window_addr),
1743d567592bSAdrian Chadd 	DEVMETHOD(bhndb_populate_board_info,		bhndb_pci_populate_board_info),
1744caeff9a3SLandon J. Fuller 	DEVMETHOD(bhndb_map_intr_isrc,			bhndb_pci_map_intr_isrc),
1745caeff9a3SLandon J. Fuller 	DEVMETHOD(bhndb_route_interrupts,		bhndb_pci_route_interrupts),
17464ad7e9b0SAdrian Chadd 
17474e96bf3aSLandon J. Fuller 	/* BHND PWRCTL hostb interface */
17484e96bf3aSLandon J. Fuller 	DEVMETHOD(bhnd_pwrctl_hostb_get_clksrc,		bhndb_pci_pwrctl_get_clksrc),
17494e96bf3aSLandon J. Fuller 	DEVMETHOD(bhnd_pwrctl_hostb_gate_clock,		bhndb_pci_pwrctl_gate_clock),
17504e96bf3aSLandon J. Fuller 	DEVMETHOD(bhnd_pwrctl_hostb_ungate_clock,	bhndb_pci_pwrctl_ungate_clock),
17514e96bf3aSLandon J. Fuller 
17524ad7e9b0SAdrian Chadd 	DEVMETHOD_END
17534ad7e9b0SAdrian Chadd };
17544ad7e9b0SAdrian Chadd 
17554ad7e9b0SAdrian Chadd DEFINE_CLASS_1(bhndb, bhndb_pci_driver, bhndb_pci_methods,
17564ad7e9b0SAdrian Chadd     sizeof(struct bhndb_pci_softc), bhndb_driver);
17574ad7e9b0SAdrian Chadd 
17584ad7e9b0SAdrian Chadd MODULE_VERSION(bhndb_pci, 1);
1759148ed571SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhnd_pci_hostb, 1, 1, 1);
17604ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb_pci, pci, 1, 1, 1);
17614ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhndb, 1, 1, 1);
1762148ed571SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhnd, 1, 1, 1);
1763