xref: /freebsd/sys/dev/bhnd/bhndb/bhndb_pci.c (revision 824b48eff36156b1d9adbb7372b26f2b853fb310)
14ad7e9b0SAdrian Chadd /*-
2bb64eeccSAdrian Chadd  * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
34ad7e9b0SAdrian Chadd  * All rights reserved.
44ad7e9b0SAdrian Chadd  *
54ad7e9b0SAdrian Chadd  * Redistribution and use in source and binary forms, with or without
64ad7e9b0SAdrian Chadd  * modification, are permitted provided that the following conditions
74ad7e9b0SAdrian Chadd  * are met:
84ad7e9b0SAdrian Chadd  * 1. Redistributions of source code must retain the above copyright
94ad7e9b0SAdrian Chadd  *    notice, this list of conditions and the following disclaimer,
104ad7e9b0SAdrian Chadd  *    without modification.
114ad7e9b0SAdrian Chadd  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
124ad7e9b0SAdrian Chadd  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
134ad7e9b0SAdrian Chadd  *    redistribution must be conditioned upon including a substantially
144ad7e9b0SAdrian Chadd  *    similar Disclaimer requirement for further binary redistribution.
154ad7e9b0SAdrian Chadd  *
164ad7e9b0SAdrian Chadd  * NO WARRANTY
174ad7e9b0SAdrian Chadd  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
184ad7e9b0SAdrian Chadd  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
194ad7e9b0SAdrian Chadd  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
204ad7e9b0SAdrian Chadd  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
214ad7e9b0SAdrian Chadd  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
224ad7e9b0SAdrian Chadd  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
234ad7e9b0SAdrian Chadd  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
244ad7e9b0SAdrian Chadd  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
254ad7e9b0SAdrian Chadd  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
264ad7e9b0SAdrian Chadd  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
274ad7e9b0SAdrian Chadd  * THE POSSIBILITY OF SUCH DAMAGES.
284ad7e9b0SAdrian Chadd  */
294ad7e9b0SAdrian Chadd 
304ad7e9b0SAdrian Chadd #include <sys/cdefs.h>
314ad7e9b0SAdrian Chadd __FBSDID("$FreeBSD$");
324ad7e9b0SAdrian Chadd 
334ad7e9b0SAdrian Chadd /*
344ad7e9b0SAdrian Chadd  * PCI-specific implementation for the BHNDB bridge driver.
354ad7e9b0SAdrian Chadd  *
364ad7e9b0SAdrian Chadd  * Provides support for bridging from a PCI parent bus to a BHND-compatible
374ad7e9b0SAdrian Chadd  * bus (e.g. bcma or siba) via a Broadcom PCI core configured in end-point
384ad7e9b0SAdrian Chadd  * mode.
394ad7e9b0SAdrian Chadd  *
408ef24a0dSAdrian Chadd  * This driver handles all initial generic host-level PCI interactions with a
418ef24a0dSAdrian Chadd  * PCI/PCIe bridge core operating in endpoint mode. Once the bridged bhnd(4)
428ef24a0dSAdrian Chadd  * bus has been enumerated, this driver works in tandem with a core-specific
438ef24a0dSAdrian Chadd  * bhnd_pci_hostb driver to manage the PCI core.
444ad7e9b0SAdrian Chadd  */
454ad7e9b0SAdrian Chadd 
464ad7e9b0SAdrian Chadd #include <sys/param.h>
474ad7e9b0SAdrian Chadd #include <sys/kernel.h>
484ad7e9b0SAdrian Chadd #include <sys/bus.h>
494ad7e9b0SAdrian Chadd #include <sys/limits.h>
504ad7e9b0SAdrian Chadd #include <sys/malloc.h>
514ad7e9b0SAdrian Chadd #include <sys/module.h>
524ad7e9b0SAdrian Chadd #include <sys/systm.h>
534ad7e9b0SAdrian Chadd 
544ad7e9b0SAdrian Chadd #include <dev/pci/pcireg.h>
554ad7e9b0SAdrian Chadd #include <dev/pci/pcivar.h>
564ad7e9b0SAdrian Chadd 
574ad7e9b0SAdrian Chadd #include <dev/bhnd/bhnd.h>
584ad7e9b0SAdrian Chadd 
594ad7e9b0SAdrian Chadd #include <dev/bhnd/cores/pci/bhnd_pcireg.h>
604ad7e9b0SAdrian Chadd 
614ad7e9b0SAdrian Chadd #include "bhndb_pcireg.h"
624ad7e9b0SAdrian Chadd #include "bhndb_pcivar.h"
634ad7e9b0SAdrian Chadd #include "bhndb_private.h"
644ad7e9b0SAdrian Chadd 
65*824b48efSLandon J. Fuller static int		bhndb_pci_init_msi(struct bhndb_pci_softc *sc);
66111d7cb2SLandon J. Fuller static int		bhndb_pci_add_children(struct bhndb_pci_softc *sc);
67111d7cb2SLandon J. Fuller 
684ad7e9b0SAdrian Chadd static int		bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc);
694ad7e9b0SAdrian Chadd static int		bhndb_disable_pci_clocks(struct bhndb_pci_softc *sc);
704ad7e9b0SAdrian Chadd 
714ad7e9b0SAdrian Chadd static int		bhndb_pci_compat_setregwin(struct bhndb_pci_softc *,
724ad7e9b0SAdrian Chadd 			    const struct bhndb_regwin *, bhnd_addr_t);
734ad7e9b0SAdrian Chadd static int		bhndb_pci_fast_setregwin(struct bhndb_pci_softc *,
744ad7e9b0SAdrian Chadd 			    const struct bhndb_regwin *, bhnd_addr_t);
754ad7e9b0SAdrian Chadd 
76e83ce340SAdrian Chadd static void		bhndb_init_sromless_pci_config(
77e83ce340SAdrian Chadd 			    struct bhndb_pci_softc *sc);
78e83ce340SAdrian Chadd 
79e83ce340SAdrian Chadd static bus_addr_t	bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc);
80f9cf87a0SAdrian Chadd static bus_size_t	bhndb_pci_sprom_size(struct bhndb_pci_softc *sc);
814ad7e9b0SAdrian Chadd 
82*824b48efSLandon J. Fuller #define	BHNDB_PCI_MSI_COUNT	1
83*824b48efSLandon J. Fuller 
844ad7e9b0SAdrian Chadd /**
854ad7e9b0SAdrian Chadd  * Default bhndb_pci implementation of device_probe().
864ad7e9b0SAdrian Chadd  *
874ad7e9b0SAdrian Chadd  * Verifies that the parent is a PCI/PCIe device.
884ad7e9b0SAdrian Chadd  */
894ad7e9b0SAdrian Chadd static int
904ad7e9b0SAdrian Chadd bhndb_pci_probe(device_t dev)
914ad7e9b0SAdrian Chadd {
924ad7e9b0SAdrian Chadd 	device_t	parent;
934ad7e9b0SAdrian Chadd 	devclass_t	parent_bus;
944ad7e9b0SAdrian Chadd 	devclass_t	pci;
954ad7e9b0SAdrian Chadd 
964ad7e9b0SAdrian Chadd 	/* Our parent must be a PCI/PCIe device. */
974ad7e9b0SAdrian Chadd 	pci = devclass_find("pci");
984ad7e9b0SAdrian Chadd 	parent = device_get_parent(dev);
994ad7e9b0SAdrian Chadd 	parent_bus = device_get_devclass(device_get_parent(parent));
1004ad7e9b0SAdrian Chadd 
1014ad7e9b0SAdrian Chadd 	if (parent_bus != pci)
1024ad7e9b0SAdrian Chadd 		return (ENXIO);
1034ad7e9b0SAdrian Chadd 
1044ad7e9b0SAdrian Chadd 	device_set_desc(dev, "PCI-BHND bridge");
1054ad7e9b0SAdrian Chadd 
1064ad7e9b0SAdrian Chadd 	return (BUS_PROBE_DEFAULT);
1074ad7e9b0SAdrian Chadd }
1084ad7e9b0SAdrian Chadd 
109*824b48efSLandon J. Fuller /* Configure MSI interrupts */
110*824b48efSLandon J. Fuller static int
111*824b48efSLandon J. Fuller bhndb_pci_init_msi(struct bhndb_pci_softc *sc)
112*824b48efSLandon J. Fuller {
113*824b48efSLandon J. Fuller 	int error;
114*824b48efSLandon J. Fuller 
115*824b48efSLandon J. Fuller 	/* Is MSI available? */
116*824b48efSLandon J. Fuller 	if (pci_msi_count(sc->parent) < BHNDB_PCI_MSI_COUNT)
117*824b48efSLandon J. Fuller 		return (ENXIO);
118*824b48efSLandon J. Fuller 
119*824b48efSLandon J. Fuller 	/* Allocate expected message count */
120*824b48efSLandon J. Fuller 	sc->intr.msi_count = BHNDB_PCI_MSI_COUNT;
121*824b48efSLandon J. Fuller 	if ((error = pci_alloc_msi(sc->parent, &sc->intr.msi_count))) {
122*824b48efSLandon J. Fuller 		device_printf(sc->dev, "failed to allocate MSI interrupts: "
123*824b48efSLandon J. Fuller 		    "%d\n", error);
124*824b48efSLandon J. Fuller 		return (error);
125*824b48efSLandon J. Fuller 	}
126*824b48efSLandon J. Fuller 
127*824b48efSLandon J. Fuller 	if (sc->intr.msi_count < BHNDB_PCI_MSI_COUNT)
128*824b48efSLandon J. Fuller 		return (ENXIO);
129*824b48efSLandon J. Fuller 
130*824b48efSLandon J. Fuller 	/* MSI uses resource IDs starting at 1 */
131*824b48efSLandon J. Fuller 	sc->intr.intr_rid = 1;
132*824b48efSLandon J. Fuller 
133*824b48efSLandon J. Fuller 	return (0);
134*824b48efSLandon J. Fuller }
135*824b48efSLandon J. Fuller 
1364ad7e9b0SAdrian Chadd static int
1374ad7e9b0SAdrian Chadd bhndb_pci_attach(device_t dev)
1384ad7e9b0SAdrian Chadd {
1394ad7e9b0SAdrian Chadd 	struct bhndb_pci_softc	*sc;
1404ad7e9b0SAdrian Chadd 	int			 error, reg;
1414ad7e9b0SAdrian Chadd 
1424ad7e9b0SAdrian Chadd 	sc = device_get_softc(dev);
1434ad7e9b0SAdrian Chadd 	sc->dev = dev;
144e83ce340SAdrian Chadd 	sc->parent = device_get_parent(dev);
1454ad7e9b0SAdrian Chadd 	sc->set_regwin = bhndb_pci_compat_setregwin;
1464ad7e9b0SAdrian Chadd 
147*824b48efSLandon J. Fuller 	/* Enable PCI bus mastering */
148*824b48efSLandon J. Fuller 	pci_enable_busmaster(sc->parent);
149*824b48efSLandon J. Fuller 
150*824b48efSLandon J. Fuller 	/* Set up interrupt handling */
151*824b48efSLandon J. Fuller 	if (bhndb_pci_init_msi(sc) == 0) {
152*824b48efSLandon J. Fuller 		device_printf(dev, "Using MSI interrupts on %s\n",
153*824b48efSLandon J. Fuller 		    device_get_nameunit(sc->parent));
154*824b48efSLandon J. Fuller 	} else {
155*824b48efSLandon J. Fuller 		device_printf(dev, "Using INTx interrupts on %s\n",
156*824b48efSLandon J. Fuller 		    device_get_nameunit(sc->parent));
157*824b48efSLandon J. Fuller 		sc->intr.intr_rid = 0;
158*824b48efSLandon J. Fuller 	}
159*824b48efSLandon J. Fuller 
160*824b48efSLandon J. Fuller 	/* Determine our bridge device class */
161*824b48efSLandon J. Fuller 	sc->pci_devclass = BHND_DEVCLASS_PCI;
162111d7cb2SLandon J. Fuller 	if (pci_find_cap(sc->parent, PCIY_EXPRESS, &reg) == 0)
163111d7cb2SLandon J. Fuller 		sc->pci_devclass = BHND_DEVCLASS_PCIE;
164111d7cb2SLandon J. Fuller 	else
165111d7cb2SLandon J. Fuller 		sc->pci_devclass = BHND_DEVCLASS_PCI;
1664ad7e9b0SAdrian Chadd 
167111d7cb2SLandon J. Fuller 	/* Enable clocks (if required by this hardware) */
168111d7cb2SLandon J. Fuller 	if ((error = bhndb_enable_pci_clocks(sc)))
169111d7cb2SLandon J. Fuller 		goto cleanup;
170111d7cb2SLandon J. Fuller 
171111d7cb2SLandon J. Fuller 	/* Perform bridge attach, fully initializing the bridge
172111d7cb2SLandon J. Fuller 	 * configuration. */
173111d7cb2SLandon J. Fuller 	if ((error = bhndb_attach(dev, sc->pci_devclass)))
174111d7cb2SLandon J. Fuller 		goto cleanup;
175111d7cb2SLandon J. Fuller 
176111d7cb2SLandon J. Fuller 	/* If supported, switch to faster regwin handling */
1774ad7e9b0SAdrian Chadd 	if (sc->bhndb.chipid.chip_type != BHND_CHIPTYPE_SIBA) {
1784ad7e9b0SAdrian Chadd 		atomic_store_rel_ptr((volatile void *) &sc->set_regwin,
1794ad7e9b0SAdrian Chadd 		    (uintptr_t) &bhndb_pci_fast_setregwin);
1804ad7e9b0SAdrian Chadd 	}
1814ad7e9b0SAdrian Chadd 
182111d7cb2SLandon J. Fuller 	/* Enable PCI bus mastering */
183111d7cb2SLandon J. Fuller 	pci_enable_busmaster(sc->parent);
1844ad7e9b0SAdrian Chadd 
185bb64eeccSAdrian Chadd 	/* Fix-up power on defaults for SROM-less devices. */
1864ad7e9b0SAdrian Chadd 	bhndb_init_sromless_pci_config(sc);
1874ad7e9b0SAdrian Chadd 
188111d7cb2SLandon J. Fuller 	/* Add any additional child devices */
189111d7cb2SLandon J. Fuller 	if ((error = bhndb_pci_add_children(sc)))
190111d7cb2SLandon J. Fuller 		goto cleanup;
191111d7cb2SLandon J. Fuller 
192111d7cb2SLandon J. Fuller 	/* Probe and attach our children */
193111d7cb2SLandon J. Fuller 	if ((error = bus_generic_attach(dev)))
194111d7cb2SLandon J. Fuller 		goto cleanup;
195111d7cb2SLandon J. Fuller 
196111d7cb2SLandon J. Fuller 	return (0);
197111d7cb2SLandon J. Fuller 
198111d7cb2SLandon J. Fuller cleanup:
199111d7cb2SLandon J. Fuller 	device_delete_children(dev);
200111d7cb2SLandon J. Fuller 	bhndb_disable_pci_clocks(sc);
201*824b48efSLandon J. Fuller 	if (sc->intr.msi_count > 0)
202*824b48efSLandon J. Fuller 		pci_release_msi(dev);
203*824b48efSLandon J. Fuller 
204111d7cb2SLandon J. Fuller 	pci_disable_busmaster(sc->parent);
205111d7cb2SLandon J. Fuller 
206111d7cb2SLandon J. Fuller 	return (error);
207111d7cb2SLandon J. Fuller }
208111d7cb2SLandon J. Fuller 
209111d7cb2SLandon J. Fuller static int
210111d7cb2SLandon J. Fuller bhndb_pci_detach(device_t dev)
211111d7cb2SLandon J. Fuller {
212111d7cb2SLandon J. Fuller 	struct bhndb_pci_softc	*sc;
213111d7cb2SLandon J. Fuller 	int			 error;
214111d7cb2SLandon J. Fuller 
215111d7cb2SLandon J. Fuller 	sc = device_get_softc(dev);
216111d7cb2SLandon J. Fuller 
217111d7cb2SLandon J. Fuller 	/* Attempt to detach our children */
218111d7cb2SLandon J. Fuller 	if ((error = bus_generic_detach(dev)))
219111d7cb2SLandon J. Fuller 		return (error);
220111d7cb2SLandon J. Fuller 
221111d7cb2SLandon J. Fuller 	/* Perform generic bridge detach */
222111d7cb2SLandon J. Fuller 	if ((error = bhndb_generic_detach(dev)))
223111d7cb2SLandon J. Fuller 		return (error);
224111d7cb2SLandon J. Fuller 
225111d7cb2SLandon J. Fuller 	/* Disable clocks (if required by this hardware) */
226111d7cb2SLandon J. Fuller 	if ((error = bhndb_disable_pci_clocks(sc)))
227111d7cb2SLandon J. Fuller 		return (error);
228111d7cb2SLandon J. Fuller 
229*824b48efSLandon J. Fuller 	/* Release MSI interrupts */
230*824b48efSLandon J. Fuller 	if (sc->intr.msi_count > 0)
231*824b48efSLandon J. Fuller 		pci_release_msi(dev);
232*824b48efSLandon J. Fuller 
233111d7cb2SLandon J. Fuller 	/* Disable PCI bus mastering */
234111d7cb2SLandon J. Fuller 	pci_disable_busmaster(sc->parent);
235111d7cb2SLandon J. Fuller 
236111d7cb2SLandon J. Fuller 	return (0);
237111d7cb2SLandon J. Fuller }
238111d7cb2SLandon J. Fuller 
239111d7cb2SLandon J. Fuller static int
240111d7cb2SLandon J. Fuller bhndb_pci_add_children(struct bhndb_pci_softc *sc)
241111d7cb2SLandon J. Fuller {
242111d7cb2SLandon J. Fuller 	bus_size_t		 nv_sz;
243111d7cb2SLandon J. Fuller 	int			 error;
244111d7cb2SLandon J. Fuller 
245111d7cb2SLandon J. Fuller 	/**
246111d7cb2SLandon J. Fuller 	 * If SPROM is mapped directly into BAR0, add child NVRAM
247111d7cb2SLandon J. Fuller 	 * device.
248111d7cb2SLandon J. Fuller 	 */
249e83ce340SAdrian Chadd 	nv_sz = bhndb_pci_sprom_size(sc);
250e83ce340SAdrian Chadd 	if (nv_sz > 0) {
251e83ce340SAdrian Chadd 		struct bhndb_devinfo	*dinfo;
252111d7cb2SLandon J. Fuller 		device_t		 child;
253e83ce340SAdrian Chadd 
254e83ce340SAdrian Chadd 		if (bootverbose) {
255111d7cb2SLandon J. Fuller 			device_printf(sc->dev, "found SPROM (%ju bytes)\n",
256111d7cb2SLandon J. Fuller 			    (uintmax_t)nv_sz);
257e83ce340SAdrian Chadd 		}
258e83ce340SAdrian Chadd 
259111d7cb2SLandon J. Fuller 		/* Add sprom device, ordered early enough to be available
260111d7cb2SLandon J. Fuller 		 * before the bridged bhnd(4) bus is attached. */
261111d7cb2SLandon J. Fuller 		child = BUS_ADD_CHILD(sc->dev,
262111d7cb2SLandon J. Fuller 		    BHND_PROBE_ROOT + BHND_PROBE_ORDER_EARLY, "bhnd_nvram", -1);
263111d7cb2SLandon J. Fuller 		if (child == NULL) {
264111d7cb2SLandon J. Fuller 			device_printf(sc->dev, "failed to add sprom device\n");
265e83ce340SAdrian Chadd 			return (ENXIO);
266e83ce340SAdrian Chadd 		}
267e83ce340SAdrian Chadd 
268e83ce340SAdrian Chadd 		/* Initialize device address space and resource covering the
269e83ce340SAdrian Chadd 		 * BAR0 SPROM shadow. */
270111d7cb2SLandon J. Fuller 		dinfo = device_get_ivars(child);
271e83ce340SAdrian Chadd 		dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE;
272111d7cb2SLandon J. Fuller 
273111d7cb2SLandon J. Fuller 		error = bus_set_resource(child, SYS_RES_MEMORY, 0,
274e83ce340SAdrian Chadd 		    bhndb_pci_sprom_addr(sc), nv_sz);
275e83ce340SAdrian Chadd 		if (error) {
276111d7cb2SLandon J. Fuller 			device_printf(sc->dev,
277e83ce340SAdrian Chadd 			    "failed to register sprom resources\n");
278e83ce340SAdrian Chadd 			return (error);
279e83ce340SAdrian Chadd 		}
280e83ce340SAdrian Chadd 	}
281e83ce340SAdrian Chadd 
2824ad7e9b0SAdrian Chadd 	return (0);
2834ad7e9b0SAdrian Chadd }
2844ad7e9b0SAdrian Chadd 
285e83ce340SAdrian Chadd static const struct bhndb_regwin *
286e83ce340SAdrian Chadd bhndb_pci_sprom_regwin(struct bhndb_pci_softc *sc)
287e83ce340SAdrian Chadd {
288e83ce340SAdrian Chadd 	struct bhndb_resources		*bres;
289e83ce340SAdrian Chadd 	const struct bhndb_hwcfg	*cfg;
290e83ce340SAdrian Chadd 	const struct bhndb_regwin	*sprom_win;
291e83ce340SAdrian Chadd 
292e83ce340SAdrian Chadd 	bres = sc->bhndb.bus_res;
293e83ce340SAdrian Chadd 	cfg = bres->cfg;
294e83ce340SAdrian Chadd 
295e83ce340SAdrian Chadd 	sprom_win = bhndb_regwin_find_type(cfg->register_windows,
296e83ce340SAdrian Chadd 	    BHNDB_REGWIN_T_SPROM, BHNDB_PCI_V0_BAR0_SPROM_SIZE);
297e83ce340SAdrian Chadd 
298e83ce340SAdrian Chadd 	return (sprom_win);
299e83ce340SAdrian Chadd }
300e83ce340SAdrian Chadd 
301e83ce340SAdrian Chadd static bus_addr_t
302e83ce340SAdrian Chadd bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc)
303e83ce340SAdrian Chadd {
304e83ce340SAdrian Chadd 	const struct bhndb_regwin	*sprom_win;
305e83ce340SAdrian Chadd 	struct resource			*r;
306e83ce340SAdrian Chadd 
307e83ce340SAdrian Chadd 	/* Fetch the SPROM register window */
308e83ce340SAdrian Chadd 	sprom_win = bhndb_pci_sprom_regwin(sc);
309e83ce340SAdrian Chadd 	KASSERT(sprom_win != NULL, ("requested sprom address on PCI_V2+"));
310e83ce340SAdrian Chadd 
311e83ce340SAdrian Chadd 	/* Fetch the associated resource */
312e83ce340SAdrian Chadd 	r = bhndb_find_regwin_resource(sc->bhndb.bus_res, sprom_win);
313e83ce340SAdrian Chadd 	KASSERT(r != NULL, ("missing resource for sprom window\n"));
314e83ce340SAdrian Chadd 
315e83ce340SAdrian Chadd 	return (rman_get_start(r) + sprom_win->win_offset);
316e83ce340SAdrian Chadd }
317e83ce340SAdrian Chadd 
318e83ce340SAdrian Chadd static bus_size_t
319e83ce340SAdrian Chadd bhndb_pci_sprom_size(struct bhndb_pci_softc *sc)
320e83ce340SAdrian Chadd {
321e83ce340SAdrian Chadd 	const struct bhndb_regwin	*sprom_win;
322e83ce340SAdrian Chadd 	uint32_t			 sctl;
323e83ce340SAdrian Chadd 	bus_size_t			 sprom_sz;
324e83ce340SAdrian Chadd 
325e83ce340SAdrian Chadd 	sprom_win = bhndb_pci_sprom_regwin(sc);
326e83ce340SAdrian Chadd 
327e83ce340SAdrian Chadd 	/* PCI_V2 and later devices map SPROM/OTP via ChipCommon */
328e83ce340SAdrian Chadd 	if (sprom_win == NULL)
329e83ce340SAdrian Chadd 		return (0);
330e83ce340SAdrian Chadd 
331e83ce340SAdrian Chadd 	/* Determine SPROM size */
332e83ce340SAdrian Chadd 	sctl = pci_read_config(sc->parent, BHNDB_PCI_SPROM_CONTROL, 4);
333e83ce340SAdrian Chadd 	if (sctl & BHNDB_PCI_SPROM_BLANK)
334e83ce340SAdrian Chadd 		return (0);
335e83ce340SAdrian Chadd 
336e83ce340SAdrian Chadd 	switch (sctl & BHNDB_PCI_SPROM_SZ_MASK) {
337e83ce340SAdrian Chadd 	case BHNDB_PCI_SPROM_SZ_1KB:
338e83ce340SAdrian Chadd 		sprom_sz = (1 * 1024);
339e83ce340SAdrian Chadd 		break;
340e83ce340SAdrian Chadd 
341e83ce340SAdrian Chadd 	case BHNDB_PCI_SPROM_SZ_4KB:
342e83ce340SAdrian Chadd 		sprom_sz = (4 * 1024);
343e83ce340SAdrian Chadd 		break;
344e83ce340SAdrian Chadd 
345e83ce340SAdrian Chadd 	case BHNDB_PCI_SPROM_SZ_16KB:
346e83ce340SAdrian Chadd 		sprom_sz = (16 * 1024);
347e83ce340SAdrian Chadd 		break;
348e83ce340SAdrian Chadd 
349e83ce340SAdrian Chadd 	case BHNDB_PCI_SPROM_SZ_RESERVED:
350e83ce340SAdrian Chadd 	default:
351e83ce340SAdrian Chadd 		device_printf(sc->dev, "invalid PCI sprom size 0x%x\n", sctl);
352e83ce340SAdrian Chadd 		return (0);
353e83ce340SAdrian Chadd 	}
354e83ce340SAdrian Chadd 
355e83ce340SAdrian Chadd 	if (sprom_sz > sprom_win->win_size) {
356e83ce340SAdrian Chadd 		device_printf(sc->dev,
357e83ce340SAdrian Chadd 		    "PCI sprom size (0x%x) overruns defined register window\n",
358e83ce340SAdrian Chadd 		    sctl);
359e83ce340SAdrian Chadd 		return (0);
360e83ce340SAdrian Chadd 	}
361e83ce340SAdrian Chadd 
362e83ce340SAdrian Chadd 	return (sprom_sz);
363e83ce340SAdrian Chadd }
364e83ce340SAdrian Chadd 
3654ad7e9b0SAdrian Chadd /*
3664ad7e9b0SAdrian Chadd  * On devices without a SROM, the PCI(e) cores will be initialized with
367bb64eeccSAdrian Chadd  * their Power-on-Reset defaults; this can leave two of the BAR0 PCI windows
368bb64eeccSAdrian Chadd  * mapped to the wrong core.
3694ad7e9b0SAdrian Chadd  *
370bb64eeccSAdrian Chadd  * This function updates the SROM shadow to point the BAR0 windows at the
3714ad7e9b0SAdrian Chadd  * current PCI core.
3724ad7e9b0SAdrian Chadd  *
373bb64eeccSAdrian Chadd  * Applies to all PCI/PCIe revisions.
3744ad7e9b0SAdrian Chadd  */
3754ad7e9b0SAdrian Chadd static void
3764ad7e9b0SAdrian Chadd bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc)
3774ad7e9b0SAdrian Chadd {
378bb64eeccSAdrian Chadd 	struct bhndb_resources		*bres;
379bb64eeccSAdrian Chadd 	const struct bhndb_hwcfg	*cfg;
380bb64eeccSAdrian Chadd 	const struct bhndb_regwin	*win;
381111d7cb2SLandon J. Fuller 	struct bhnd_core_info		 hostb_core;
382bb64eeccSAdrian Chadd 	struct resource			*core_regs;
383bb64eeccSAdrian Chadd 	bus_size_t			 srom_offset;
384bb64eeccSAdrian Chadd 	u_int				 pci_cidx, sprom_cidx;
3854ad7e9b0SAdrian Chadd 	uint16_t			 val;
386111d7cb2SLandon J. Fuller 	int				 error;
3874ad7e9b0SAdrian Chadd 
388bb64eeccSAdrian Chadd 	bres = sc->bhndb.bus_res;
389bb64eeccSAdrian Chadd 	cfg = bres->cfg;
390bb64eeccSAdrian Chadd 
391111d7cb2SLandon J. Fuller 	/* Find our hostb core */
392111d7cb2SLandon J. Fuller 	error = BHNDB_GET_HOSTB_CORE(sc->dev, sc->bhndb.bus_dev, &hostb_core);
393111d7cb2SLandon J. Fuller 	if (error) {
394111d7cb2SLandon J. Fuller 		device_printf(sc->dev, "no host bridge device found\n");
395111d7cb2SLandon J. Fuller 		return;
396111d7cb2SLandon J. Fuller 	}
397111d7cb2SLandon J. Fuller 
398111d7cb2SLandon J. Fuller 	if (hostb_core.vendor != BHND_MFGID_BCM)
399bb64eeccSAdrian Chadd 		return;
400bb64eeccSAdrian Chadd 
401111d7cb2SLandon J. Fuller 	switch (hostb_core.device) {
402bb64eeccSAdrian Chadd 	case BHND_COREID_PCI:
403bb64eeccSAdrian Chadd 		srom_offset = BHND_PCI_SRSH_PI_OFFSET;
404bb64eeccSAdrian Chadd 		break;
405bb64eeccSAdrian Chadd 	case BHND_COREID_PCIE:
406bb64eeccSAdrian Chadd 		srom_offset = BHND_PCIE_SRSH_PI_OFFSET;
407bb64eeccSAdrian Chadd 		break;
408bb64eeccSAdrian Chadd 	default:
409bb64eeccSAdrian Chadd 		device_printf(sc->dev, "unsupported PCI host bridge device\n");
410bb64eeccSAdrian Chadd 		return;
411bb64eeccSAdrian Chadd 	}
412bb64eeccSAdrian Chadd 
413bb64eeccSAdrian Chadd 	/* Locate the static register window mapping the PCI core */
414bb64eeccSAdrian Chadd 	win = bhndb_regwin_find_core(cfg->register_windows, sc->pci_devclass,
415bb64eeccSAdrian Chadd 	    0, BHND_PORT_DEVICE, 0, 0);
416bb64eeccSAdrian Chadd 	if (win == NULL) {
417bb64eeccSAdrian Chadd 		device_printf(sc->dev, "missing PCI core register window\n");
418bb64eeccSAdrian Chadd 		return;
419bb64eeccSAdrian Chadd 	}
420bb64eeccSAdrian Chadd 
421bb64eeccSAdrian Chadd 	/* Fetch the resource containing the register window */
422bb64eeccSAdrian Chadd 	core_regs = bhndb_find_regwin_resource(bres, win);
423bb64eeccSAdrian Chadd 	if (core_regs == NULL) {
424bb64eeccSAdrian Chadd 		device_printf(sc->dev, "missing PCI core register resource\n");
425bb64eeccSAdrian Chadd 		return;
426bb64eeccSAdrian Chadd 	}
427bb64eeccSAdrian Chadd 
4284ad7e9b0SAdrian Chadd 	/* Fetch the SPROM's configured core index */
429bb64eeccSAdrian Chadd 	val = bus_read_2(core_regs, win->win_offset + srom_offset);
430bb64eeccSAdrian Chadd 	sprom_cidx = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT;
4314ad7e9b0SAdrian Chadd 
4324ad7e9b0SAdrian Chadd 	/* If it doesn't match host bridge's core index, update the index
4334ad7e9b0SAdrian Chadd 	 * value */
434111d7cb2SLandon J. Fuller 	pci_cidx = hostb_core.core_idx;
435bb64eeccSAdrian Chadd 	if (sprom_cidx != pci_cidx) {
436bb64eeccSAdrian Chadd 		val &= ~BHND_PCI_SRSH_PI_MASK;
437bb64eeccSAdrian Chadd 		val |= (pci_cidx << BHND_PCI_SRSH_PI_SHIFT);
438bb64eeccSAdrian Chadd 		bus_write_2(core_regs,
439bb64eeccSAdrian Chadd 		    win->win_offset + srom_offset, val);
4404ad7e9b0SAdrian Chadd 	}
4414ad7e9b0SAdrian Chadd }
4424ad7e9b0SAdrian Chadd 
4434ad7e9b0SAdrian Chadd static int
4444ad7e9b0SAdrian Chadd bhndb_pci_resume(device_t dev)
4454ad7e9b0SAdrian Chadd {
4464ad7e9b0SAdrian Chadd 	struct bhndb_pci_softc	*sc;
4474ad7e9b0SAdrian Chadd 	int			 error;
4484ad7e9b0SAdrian Chadd 
4494ad7e9b0SAdrian Chadd 	sc = device_get_softc(dev);
4504ad7e9b0SAdrian Chadd 
451bb64eeccSAdrian Chadd 	/* Enable clocks (if supported by this hardware) */
452bb64eeccSAdrian Chadd 	if ((error = bhndb_enable_pci_clocks(sc)))
4534ad7e9b0SAdrian Chadd 		return (error);
4544ad7e9b0SAdrian Chadd 
455bb64eeccSAdrian Chadd 	/* Perform resume */
456bb64eeccSAdrian Chadd 	return (bhndb_generic_resume(dev));
457bb64eeccSAdrian Chadd }
458bb64eeccSAdrian Chadd 
459bb64eeccSAdrian Chadd static int
460bb64eeccSAdrian Chadd bhndb_pci_suspend(device_t dev)
461bb64eeccSAdrian Chadd {
462bb64eeccSAdrian Chadd 	struct bhndb_pci_softc	*sc;
463bb64eeccSAdrian Chadd 	int			 error;
464bb64eeccSAdrian Chadd 
465bb64eeccSAdrian Chadd 	sc = device_get_softc(dev);
466bb64eeccSAdrian Chadd 
467bb64eeccSAdrian Chadd 	/* Disable clocks (if supported by this hardware) */
468bb64eeccSAdrian Chadd 	if ((error = bhndb_disable_pci_clocks(sc)))
4694ad7e9b0SAdrian Chadd 		return (error);
4704ad7e9b0SAdrian Chadd 
471bb64eeccSAdrian Chadd 	/* Perform suspend */
472bb64eeccSAdrian Chadd 	return (bhndb_generic_suspend(dev));
473bb64eeccSAdrian Chadd }
474bb64eeccSAdrian Chadd 
475bb64eeccSAdrian Chadd static int
4764ad7e9b0SAdrian Chadd bhndb_pci_set_window_addr(device_t dev, const struct bhndb_regwin *rw,
4774ad7e9b0SAdrian Chadd     bhnd_addr_t addr)
4784ad7e9b0SAdrian Chadd {
4794ad7e9b0SAdrian Chadd 	struct bhndb_pci_softc *sc = device_get_softc(dev);
4804ad7e9b0SAdrian Chadd 	return (sc->set_regwin(sc, rw, addr));
4814ad7e9b0SAdrian Chadd }
4824ad7e9b0SAdrian Chadd 
4834ad7e9b0SAdrian Chadd /**
4844ad7e9b0SAdrian Chadd  * A siba(4) and bcma(4)-compatible bhndb_set_window_addr implementation.
4854ad7e9b0SAdrian Chadd  *
4864ad7e9b0SAdrian Chadd  * On siba(4) devices, it's possible that writing a PCI window register may
4874ad7e9b0SAdrian Chadd  * not succeed; it's necessary to immediately read the configuration register
4884ad7e9b0SAdrian Chadd  * and retry if not set to the desired value.
4894ad7e9b0SAdrian Chadd  *
4904ad7e9b0SAdrian Chadd  * This is not necessary on bcma(4) devices, but other than the overhead of
4914ad7e9b0SAdrian Chadd  * validating the register, there's no harm in performing the verification.
4924ad7e9b0SAdrian Chadd  */
4934ad7e9b0SAdrian Chadd static int
4944ad7e9b0SAdrian Chadd bhndb_pci_compat_setregwin(struct bhndb_pci_softc *sc,
4954ad7e9b0SAdrian Chadd     const struct bhndb_regwin *rw, bhnd_addr_t addr)
4964ad7e9b0SAdrian Chadd {
4974ad7e9b0SAdrian Chadd 	int		error;
498e83ce340SAdrian Chadd 	int		reg;
4994ad7e9b0SAdrian Chadd 
5004ad7e9b0SAdrian Chadd 	if (rw->win_type != BHNDB_REGWIN_T_DYN)
5014ad7e9b0SAdrian Chadd 		return (ENODEV);
5024ad7e9b0SAdrian Chadd 
503e83ce340SAdrian Chadd 	reg = rw->d.dyn.cfg_offset;
5044ad7e9b0SAdrian Chadd 	for (u_int i = 0; i < BHNDB_PCI_BARCTRL_WRITE_RETRY; i++) {
5054ad7e9b0SAdrian Chadd 		if ((error = bhndb_pci_fast_setregwin(sc, rw, addr)))
5064ad7e9b0SAdrian Chadd 			return (error);
5074ad7e9b0SAdrian Chadd 
508e83ce340SAdrian Chadd 		if (pci_read_config(sc->parent, reg, 4) == addr)
5094ad7e9b0SAdrian Chadd 			return (0);
5104ad7e9b0SAdrian Chadd 
5114ad7e9b0SAdrian Chadd 		DELAY(10);
5124ad7e9b0SAdrian Chadd 	}
5134ad7e9b0SAdrian Chadd 
5144ad7e9b0SAdrian Chadd 	/* Unable to set window */
5154ad7e9b0SAdrian Chadd 	return (ENODEV);
5164ad7e9b0SAdrian Chadd }
5174ad7e9b0SAdrian Chadd 
5184ad7e9b0SAdrian Chadd /**
5194ad7e9b0SAdrian Chadd  * A bcma(4)-only bhndb_set_window_addr implementation.
5204ad7e9b0SAdrian Chadd  */
5214ad7e9b0SAdrian Chadd static int
5224ad7e9b0SAdrian Chadd bhndb_pci_fast_setregwin(struct bhndb_pci_softc *sc,
5234ad7e9b0SAdrian Chadd     const struct bhndb_regwin *rw, bhnd_addr_t addr)
5244ad7e9b0SAdrian Chadd {
5254ad7e9b0SAdrian Chadd 	/* The PCI bridge core only supports 32-bit addressing, regardless
5264ad7e9b0SAdrian Chadd 	 * of the bus' support for 64-bit addressing */
5274ad7e9b0SAdrian Chadd 	if (addr > UINT32_MAX)
5284ad7e9b0SAdrian Chadd 		return (ERANGE);
5294ad7e9b0SAdrian Chadd 
5304ad7e9b0SAdrian Chadd 	switch (rw->win_type) {
5314ad7e9b0SAdrian Chadd 	case BHNDB_REGWIN_T_DYN:
5324ad7e9b0SAdrian Chadd 		/* Addresses must be page aligned */
5334ad7e9b0SAdrian Chadd 		if (addr % rw->win_size != 0)
5344ad7e9b0SAdrian Chadd 			return (EINVAL);
5354ad7e9b0SAdrian Chadd 
536e83ce340SAdrian Chadd 		pci_write_config(sc->parent, rw->d.dyn.cfg_offset, addr, 4);
5374ad7e9b0SAdrian Chadd 		break;
5384ad7e9b0SAdrian Chadd 	default:
5394ad7e9b0SAdrian Chadd 		return (ENODEV);
5404ad7e9b0SAdrian Chadd 	}
5414ad7e9b0SAdrian Chadd 
5424ad7e9b0SAdrian Chadd 	return (0);
5434ad7e9b0SAdrian Chadd }
5444ad7e9b0SAdrian Chadd 
545d567592bSAdrian Chadd static int
546d567592bSAdrian Chadd bhndb_pci_populate_board_info(device_t dev, device_t child,
547d567592bSAdrian Chadd     struct bhnd_board_info *info)
548d567592bSAdrian Chadd {
549d567592bSAdrian Chadd 	struct bhndb_pci_softc	*sc;
550d567592bSAdrian Chadd 
551d567592bSAdrian Chadd 	sc = device_get_softc(dev);
552d567592bSAdrian Chadd 
5538ef24a0dSAdrian Chadd 	/*
5548ef24a0dSAdrian Chadd 	 * On a subset of Apple BCM4360 modules, always prefer the
5558ef24a0dSAdrian Chadd 	 * PCI subdevice to the SPROM-supplied boardtype.
5568ef24a0dSAdrian Chadd 	 *
5578ef24a0dSAdrian Chadd 	 * TODO:
5588ef24a0dSAdrian Chadd 	 *
5598ef24a0dSAdrian Chadd 	 * Broadcom's own drivers implement this override, and then later use
5608ef24a0dSAdrian Chadd 	 * the remapped BCM4360 board type to determine the required
5618ef24a0dSAdrian Chadd 	 * board-specific workarounds.
5628ef24a0dSAdrian Chadd 	 *
5638ef24a0dSAdrian Chadd 	 * Without access to this hardware, it's unclear why this mapping
5648ef24a0dSAdrian Chadd 	 * is done, and we must do the same. If we can survey the hardware
5658ef24a0dSAdrian Chadd 	 * in question, it may be possible to replace this behavior with
5668ef24a0dSAdrian Chadd 	 * explicit references to the SPROM-supplied boardtype(s) in our
5678ef24a0dSAdrian Chadd 	 * quirk definitions.
5688ef24a0dSAdrian Chadd 	 */
5698ef24a0dSAdrian Chadd 	if (pci_get_subvendor(sc->parent) == PCI_VENDOR_APPLE) {
5708ef24a0dSAdrian Chadd 		switch (info->board_type) {
5718ef24a0dSAdrian Chadd 		case BHND_BOARD_BCM94360X29C:
5728ef24a0dSAdrian Chadd 		case BHND_BOARD_BCM94360X29CP2:
5738ef24a0dSAdrian Chadd 		case BHND_BOARD_BCM94360X51:
5748ef24a0dSAdrian Chadd 		case BHND_BOARD_BCM94360X51P2:
5758ef24a0dSAdrian Chadd 			info->board_type = 0;	/* allow override below */
5768ef24a0dSAdrian Chadd 			break;
5778ef24a0dSAdrian Chadd 		default:
5788ef24a0dSAdrian Chadd 			break;
5798ef24a0dSAdrian Chadd 		}
5808ef24a0dSAdrian Chadd 	}
5818ef24a0dSAdrian Chadd 
582d567592bSAdrian Chadd 	/* If NVRAM did not supply vendor/type info, provide the PCI
583d567592bSAdrian Chadd 	 * subvendor/subdevice values. */
584d567592bSAdrian Chadd 	if (info->board_vendor == 0)
585d567592bSAdrian Chadd 		info->board_vendor = pci_get_subvendor(sc->parent);
586d567592bSAdrian Chadd 
587d567592bSAdrian Chadd 	if (info->board_type == 0)
588d567592bSAdrian Chadd 		info->board_type = pci_get_subdevice(sc->parent);
589d567592bSAdrian Chadd 
590d567592bSAdrian Chadd 	return (0);
591d567592bSAdrian Chadd }
592d567592bSAdrian Chadd 
5934ad7e9b0SAdrian Chadd /**
594bb64eeccSAdrian Chadd  * Enable externally managed clocks, if required.
5954ad7e9b0SAdrian Chadd  *
596bb64eeccSAdrian Chadd  * Some PCI chipsets (BCM4306, possibly others) chips do not support
597bb64eeccSAdrian Chadd  * the idle low-power clock. Clocking must be bootstrapped at
598bb64eeccSAdrian Chadd  * attach/resume by directly adjusting GPIO registers exposed in the
599bb64eeccSAdrian Chadd  * PCI config space, and correspondingly, explicitly shutdown at
600bb64eeccSAdrian Chadd  * detach/suspend.
6014ad7e9b0SAdrian Chadd  *
6024ad7e9b0SAdrian Chadd  * @param sc Bridge driver state.
6034ad7e9b0SAdrian Chadd  */
6044ad7e9b0SAdrian Chadd static int
6054ad7e9b0SAdrian Chadd bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc)
6064ad7e9b0SAdrian Chadd {
6074ad7e9b0SAdrian Chadd 	uint32_t		gpio_in, gpio_out, gpio_en;
6084ad7e9b0SAdrian Chadd 	uint32_t		gpio_flags;
6094ad7e9b0SAdrian Chadd 	uint16_t		pci_status;
6104ad7e9b0SAdrian Chadd 
611bb64eeccSAdrian Chadd 	/* Only supported and required on PCI devices */
612bb64eeccSAdrian Chadd 	if (sc->pci_devclass != BHND_DEVCLASS_PCI)
613bb64eeccSAdrian Chadd 		return (0);
6144ad7e9b0SAdrian Chadd 
6154ad7e9b0SAdrian Chadd 	/* Read state of XTAL pin */
616e83ce340SAdrian Chadd 	gpio_in = pci_read_config(sc->parent, BHNDB_PCI_GPIO_IN, 4);
6174ad7e9b0SAdrian Chadd 	if (gpio_in & BHNDB_PCI_GPIO_XTAL_ON)
6184ad7e9b0SAdrian Chadd 		return (0); /* already enabled */
6194ad7e9b0SAdrian Chadd 
6204ad7e9b0SAdrian Chadd 	/* Fetch current config */
621e83ce340SAdrian Chadd 	gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4);
622e83ce340SAdrian Chadd 	gpio_en = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, 4);
6234ad7e9b0SAdrian Chadd 
6244ad7e9b0SAdrian Chadd 	/* Set PLL_OFF/XTAL_ON pins to HIGH and enable both pins */
6254ad7e9b0SAdrian Chadd 	gpio_flags = (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON);
6264ad7e9b0SAdrian Chadd 	gpio_out |= gpio_flags;
6274ad7e9b0SAdrian Chadd 	gpio_en |= gpio_flags;
6284ad7e9b0SAdrian Chadd 
629e83ce340SAdrian Chadd 	pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
630e83ce340SAdrian Chadd 	pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4);
6314ad7e9b0SAdrian Chadd 	DELAY(1000);
6324ad7e9b0SAdrian Chadd 
6334ad7e9b0SAdrian Chadd 	/* Reset PLL_OFF */
6344ad7e9b0SAdrian Chadd 	gpio_out &= ~BHNDB_PCI_GPIO_PLL_OFF;
635e83ce340SAdrian Chadd 	pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
6364ad7e9b0SAdrian Chadd 	DELAY(5000);
6374ad7e9b0SAdrian Chadd 
6384ad7e9b0SAdrian Chadd 	/* Clear any PCI 'sent target-abort' flag. */
639e83ce340SAdrian Chadd 	pci_status = pci_read_config(sc->parent, PCIR_STATUS, 2);
6404ad7e9b0SAdrian Chadd 	pci_status &= ~PCIM_STATUS_STABORT;
641e83ce340SAdrian Chadd 	pci_write_config(sc->parent, PCIR_STATUS, pci_status, 2);
6424ad7e9b0SAdrian Chadd 
6434ad7e9b0SAdrian Chadd 	return (0);
6444ad7e9b0SAdrian Chadd }
6454ad7e9b0SAdrian Chadd 
6464ad7e9b0SAdrian Chadd /**
647bb64eeccSAdrian Chadd  * Disable externally managed clocks, if required.
6484ad7e9b0SAdrian Chadd  *
6494ad7e9b0SAdrian Chadd  * @param sc Bridge driver state.
6504ad7e9b0SAdrian Chadd  */
6514ad7e9b0SAdrian Chadd static int
6524ad7e9b0SAdrian Chadd bhndb_disable_pci_clocks(struct bhndb_pci_softc *sc)
6534ad7e9b0SAdrian Chadd {
6544ad7e9b0SAdrian Chadd 	uint32_t	gpio_out, gpio_en;
6554ad7e9b0SAdrian Chadd 
656bb64eeccSAdrian Chadd 	/* Only supported and required on PCI devices */
657bb64eeccSAdrian Chadd 	if (sc->pci_devclass != BHND_DEVCLASS_PCI)
658bb64eeccSAdrian Chadd 		return (0);
6594ad7e9b0SAdrian Chadd 
6604ad7e9b0SAdrian Chadd 	/* Fetch current config */
661e83ce340SAdrian Chadd 	gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4);
662e83ce340SAdrian Chadd 	gpio_en = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, 4);
6634ad7e9b0SAdrian Chadd 
6644ad7e9b0SAdrian Chadd 	/* Set PLL_OFF to HIGH, XTAL_ON to LOW. */
6654ad7e9b0SAdrian Chadd 	gpio_out &= ~BHNDB_PCI_GPIO_XTAL_ON;
6664ad7e9b0SAdrian Chadd 	gpio_out |= BHNDB_PCI_GPIO_PLL_OFF;
667e83ce340SAdrian Chadd 	pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
6684ad7e9b0SAdrian Chadd 
6694ad7e9b0SAdrian Chadd 	/* Enable both output pins */
6704ad7e9b0SAdrian Chadd 	gpio_en |= (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON);
671e83ce340SAdrian Chadd 	pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4);
6724ad7e9b0SAdrian Chadd 
6734ad7e9b0SAdrian Chadd 	return (0);
6744ad7e9b0SAdrian Chadd }
6754ad7e9b0SAdrian Chadd 
676f90f4b65SLandon J. Fuller static bhnd_clksrc
677f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_get_clksrc(device_t dev, device_t child,
678f90f4b65SLandon J. Fuller 	bhnd_clock clock)
679f90f4b65SLandon J. Fuller {
680f90f4b65SLandon J. Fuller 	struct bhndb_pci_softc	*sc;
681f90f4b65SLandon J. Fuller 	uint32_t		 gpio_out;
682f90f4b65SLandon J. Fuller 
683f90f4b65SLandon J. Fuller 	sc = device_get_softc(dev);
684f90f4b65SLandon J. Fuller 
685f90f4b65SLandon J. Fuller 	/* Only supported on PCI devices */
686f90f4b65SLandon J. Fuller 	if (sc->pci_devclass != BHND_DEVCLASS_PCI)
687f90f4b65SLandon J. Fuller 		return (ENODEV);
688f90f4b65SLandon J. Fuller 
689f90f4b65SLandon J. Fuller 	/* Only ILP is supported */
690f90f4b65SLandon J. Fuller 	if (clock != BHND_CLOCK_ILP)
691f90f4b65SLandon J. Fuller 		return (ENXIO);
692f90f4b65SLandon J. Fuller 
693f90f4b65SLandon J. Fuller 	gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4);
694f90f4b65SLandon J. Fuller 	if (gpio_out & BHNDB_PCI_GPIO_SCS)
695f90f4b65SLandon J. Fuller 		return (BHND_CLKSRC_PCI);
696f90f4b65SLandon J. Fuller 	else
697f90f4b65SLandon J. Fuller 		return (BHND_CLKSRC_XTAL);
698f90f4b65SLandon J. Fuller }
699f90f4b65SLandon J. Fuller 
700f90f4b65SLandon J. Fuller static int
701f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_gate_clock(device_t dev, device_t child,
702f90f4b65SLandon J. Fuller 	bhnd_clock clock)
703f90f4b65SLandon J. Fuller {
704f90f4b65SLandon J. Fuller 	struct bhndb_pci_softc *sc = device_get_softc(dev);
705f90f4b65SLandon J. Fuller 
706f90f4b65SLandon J. Fuller 	/* Only supported on PCI devices */
707f90f4b65SLandon J. Fuller 	if (sc->pci_devclass != BHND_DEVCLASS_PCI)
708f90f4b65SLandon J. Fuller 		return (ENODEV);
709f90f4b65SLandon J. Fuller 
710f90f4b65SLandon J. Fuller 	/* Only HT is supported */
711f90f4b65SLandon J. Fuller 	if (clock != BHND_CLOCK_HT)
712f90f4b65SLandon J. Fuller 		return (ENXIO);
713f90f4b65SLandon J. Fuller 
714f90f4b65SLandon J. Fuller 	return (bhndb_disable_pci_clocks(sc));
715f90f4b65SLandon J. Fuller }
716f90f4b65SLandon J. Fuller 
717f90f4b65SLandon J. Fuller static int
718f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_ungate_clock(device_t dev, device_t child,
719f90f4b65SLandon J. Fuller 	bhnd_clock clock)
720f90f4b65SLandon J. Fuller {
721f90f4b65SLandon J. Fuller 	struct bhndb_pci_softc *sc = device_get_softc(dev);
722f90f4b65SLandon J. Fuller 
723f90f4b65SLandon J. Fuller 	/* Only supported on PCI devices */
724f90f4b65SLandon J. Fuller 	if (sc->pci_devclass != BHND_DEVCLASS_PCI)
725f90f4b65SLandon J. Fuller 		return (ENODEV);
726f90f4b65SLandon J. Fuller 
727f90f4b65SLandon J. Fuller 	/* Only HT is supported */
728f90f4b65SLandon J. Fuller 	if (clock != BHND_CLOCK_HT)
729f90f4b65SLandon J. Fuller 		return (ENXIO);
730f90f4b65SLandon J. Fuller 
731f90f4b65SLandon J. Fuller 	return (bhndb_enable_pci_clocks(sc));
732f90f4b65SLandon J. Fuller }
733f90f4b65SLandon J. Fuller 
734*824b48efSLandon J. Fuller static int
735*824b48efSLandon J. Fuller bhndb_pci_assign_intr(device_t dev, device_t child, int rid)
736*824b48efSLandon J. Fuller {
737*824b48efSLandon J. Fuller 	struct bhndb_pci_softc	*sc;
738*824b48efSLandon J. Fuller 	rman_res_t		 start, count;
739*824b48efSLandon J. Fuller 	int			 error;
740*824b48efSLandon J. Fuller 
741*824b48efSLandon J. Fuller 	sc = device_get_softc(dev);
742*824b48efSLandon J. Fuller 
743*824b48efSLandon J. Fuller 	/* Is the rid valid? */
744*824b48efSLandon J. Fuller 	if (rid >= bhnd_get_intr_count(child))
745*824b48efSLandon J. Fuller 		return (EINVAL);
746*824b48efSLandon J. Fuller 
747*824b48efSLandon J. Fuller 	/* Fetch our common PCI interrupt's start/count. */
748*824b48efSLandon J. Fuller 	error = bus_get_resource(sc->parent, SYS_RES_IRQ, sc->intr.intr_rid,
749*824b48efSLandon J. Fuller 	    &start, &count);
750*824b48efSLandon J. Fuller 	if (error)
751*824b48efSLandon J. Fuller 		return (error);
752*824b48efSLandon J. Fuller 
753*824b48efSLandon J. Fuller 	/* Add to child's resource list */
754*824b48efSLandon J. Fuller         return (bus_set_resource(child, SYS_RES_IRQ, rid, start, count));
755*824b48efSLandon J. Fuller }
756*824b48efSLandon J. Fuller 
7574ad7e9b0SAdrian Chadd static device_method_t bhndb_pci_methods[] = {
7584ad7e9b0SAdrian Chadd 	/* Device interface */
7594ad7e9b0SAdrian Chadd 	DEVMETHOD(device_probe,			bhndb_pci_probe),
7604ad7e9b0SAdrian Chadd 	DEVMETHOD(device_attach,		bhndb_pci_attach),
7614ad7e9b0SAdrian Chadd 	DEVMETHOD(device_resume,		bhndb_pci_resume),
762bb64eeccSAdrian Chadd 	DEVMETHOD(device_suspend,		bhndb_pci_suspend),
763bb64eeccSAdrian Chadd 	DEVMETHOD(device_detach,		bhndb_pci_detach),
7644ad7e9b0SAdrian Chadd 
765f90f4b65SLandon J. Fuller 	/* BHND interface */
766*824b48efSLandon J. Fuller 	DEVMETHOD(bhnd_bus_assign_intr,		bhndb_pci_assign_intr),
767*824b48efSLandon J. Fuller 
768f90f4b65SLandon J. Fuller 	DEVMETHOD(bhnd_bus_pwrctl_get_clksrc,	bhndb_pci_pwrctl_get_clksrc),
769f90f4b65SLandon J. Fuller 	DEVMETHOD(bhnd_bus_pwrctl_gate_clock,	bhndb_pci_pwrctl_gate_clock),
770f90f4b65SLandon J. Fuller 	DEVMETHOD(bhnd_bus_pwrctl_ungate_clock,	bhndb_pci_pwrctl_ungate_clock),
771f90f4b65SLandon J. Fuller 
7724ad7e9b0SAdrian Chadd 	/* BHNDB interface */
7734ad7e9b0SAdrian Chadd 	DEVMETHOD(bhndb_set_window_addr,	bhndb_pci_set_window_addr),
774d567592bSAdrian Chadd 	DEVMETHOD(bhndb_populate_board_info,	bhndb_pci_populate_board_info),
7754ad7e9b0SAdrian Chadd 
7764ad7e9b0SAdrian Chadd 	DEVMETHOD_END
7774ad7e9b0SAdrian Chadd };
7784ad7e9b0SAdrian Chadd 
7794ad7e9b0SAdrian Chadd DEFINE_CLASS_1(bhndb, bhndb_pci_driver, bhndb_pci_methods,
7804ad7e9b0SAdrian Chadd     sizeof(struct bhndb_pci_softc), bhndb_driver);
7814ad7e9b0SAdrian Chadd 
7824ad7e9b0SAdrian Chadd MODULE_VERSION(bhndb_pci, 1);
783148ed571SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhnd_pci_hostb, 1, 1, 1);
7844ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb_pci, pci, 1, 1, 1);
7854ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhndb, 1, 1, 1);
786148ed571SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhnd, 1, 1, 1);
787