xref: /freebsd/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c (revision 685dc743)
14ad7e9b0SAdrian Chadd /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
36e778a7eSPedro F. Giffuni  *
48e35bf83SLandon J. Fuller  * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
58e35bf83SLandon J. Fuller  * Copyright (c) 2017 The FreeBSD Foundation
64ad7e9b0SAdrian Chadd  * All rights reserved.
74ad7e9b0SAdrian Chadd  *
88e35bf83SLandon J. Fuller  * Portions of this software were developed by Landon Fuller
98e35bf83SLandon J. Fuller  * under sponsorship from the FreeBSD Foundation.
108e35bf83SLandon J. Fuller  *
114ad7e9b0SAdrian Chadd  * Redistribution and use in source and binary forms, with or without
124ad7e9b0SAdrian Chadd  * modification, are permitted provided that the following conditions
134ad7e9b0SAdrian Chadd  * are met:
144ad7e9b0SAdrian Chadd  * 1. Redistributions of source code must retain the above copyright
154ad7e9b0SAdrian Chadd  *    notice, this list of conditions and the following disclaimer,
164ad7e9b0SAdrian Chadd  *    without modification.
174ad7e9b0SAdrian Chadd  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
184ad7e9b0SAdrian Chadd  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
194ad7e9b0SAdrian Chadd  *    redistribution must be conditioned upon including a substantially
204ad7e9b0SAdrian Chadd  *    similar Disclaimer requirement for further binary redistribution.
214ad7e9b0SAdrian Chadd  *
224ad7e9b0SAdrian Chadd  * NO WARRANTY
234ad7e9b0SAdrian Chadd  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
244ad7e9b0SAdrian Chadd  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
254ad7e9b0SAdrian Chadd  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
264ad7e9b0SAdrian Chadd  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
274ad7e9b0SAdrian Chadd  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
284ad7e9b0SAdrian Chadd  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
294ad7e9b0SAdrian Chadd  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
304ad7e9b0SAdrian Chadd  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
314ad7e9b0SAdrian Chadd  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
324ad7e9b0SAdrian Chadd  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
334ad7e9b0SAdrian Chadd  * THE POSSIBILITY OF SUCH DAMAGES.
344ad7e9b0SAdrian Chadd  */
354ad7e9b0SAdrian Chadd 
364ad7e9b0SAdrian Chadd #include <sys/cdefs.h>
374ad7e9b0SAdrian Chadd /*
38bb64eeccSAdrian Chadd  * Broadcom BHND PCI/PCIe-Gen1 PCI-Host Bridge.
394ad7e9b0SAdrian Chadd  *
40bb64eeccSAdrian Chadd  * This driver handles all interactions with PCI bridge cores operating in
41bb64eeccSAdrian Chadd  * endpoint mode.
42bb64eeccSAdrian Chadd  *
43bb64eeccSAdrian Chadd  * Host-level PCI operations are handled at the bhndb bridge level by the
44bb64eeccSAdrian Chadd  * bhndb_pci driver.
454ad7e9b0SAdrian Chadd  */
464ad7e9b0SAdrian Chadd 
474ad7e9b0SAdrian Chadd #include <sys/param.h>
484ad7e9b0SAdrian Chadd #include <sys/kernel.h>
49bb64eeccSAdrian Chadd 
50bb64eeccSAdrian Chadd #include <sys/malloc.h>
51bb64eeccSAdrian Chadd 
524ad7e9b0SAdrian Chadd #include <sys/bus.h>
534ad7e9b0SAdrian Chadd #include <sys/module.h>
54bb64eeccSAdrian Chadd 
554ad7e9b0SAdrian Chadd #include <sys/systm.h>
564ad7e9b0SAdrian Chadd 
574ad7e9b0SAdrian Chadd #include <machine/bus.h>
584ad7e9b0SAdrian Chadd #include <sys/rman.h>
594ad7e9b0SAdrian Chadd #include <machine/resource.h>
604ad7e9b0SAdrian Chadd 
614ad7e9b0SAdrian Chadd #include <dev/bhnd/bhnd.h>
624ad7e9b0SAdrian Chadd 
638ef24a0dSAdrian Chadd #include <dev/pci/pcireg.h>
648ef24a0dSAdrian Chadd #include <dev/pci/pcivar.h>
658ef24a0dSAdrian Chadd 
668ef24a0dSAdrian Chadd #include <dev/bhnd/cores/chipc/chipc.h>
678ef24a0dSAdrian Chadd #include <dev/bhnd/cores/chipc/chipcreg.h>
688ef24a0dSAdrian Chadd 
69bb64eeccSAdrian Chadd #include "bhnd_pcireg.h"
70bb64eeccSAdrian Chadd #include "bhnd_pci_hostbvar.h"
71bb64eeccSAdrian Chadd 
72bb64eeccSAdrian Chadd static const struct bhnd_device_quirk bhnd_pci_quirks[];
73bb64eeccSAdrian Chadd static const struct bhnd_device_quirk bhnd_pcie_quirks[];
74bb64eeccSAdrian Chadd 
758ef24a0dSAdrian Chadd /* Device driver work-around variations */
768ef24a0dSAdrian Chadd typedef enum {
778ef24a0dSAdrian Chadd 	BHND_PCI_WAR_ATTACH,	/**< apply attach workarounds */
788ef24a0dSAdrian Chadd 	BHND_PCI_WAR_RESUME,	/**< apply resume workarounds */
798ef24a0dSAdrian Chadd 	BHND_PCI_WAR_SUSPEND,	/**< apply suspend workarounds */
808ef24a0dSAdrian Chadd 	BHND_PCI_WAR_DETACH	/**< apply detach workarounds */
818ef24a0dSAdrian Chadd } bhnd_pci_war_state;
828ef24a0dSAdrian Chadd 
83bb64eeccSAdrian Chadd static int	bhnd_pci_wars_early_once(struct bhnd_pcihb_softc *sc);
848ef24a0dSAdrian Chadd static int	bhnd_pci_wars_hwup(struct bhnd_pcihb_softc *sc,
858ef24a0dSAdrian Chadd 		    bhnd_pci_war_state state);
868ef24a0dSAdrian Chadd static int	bhnd_pci_wars_hwdown(struct bhnd_pcihb_softc *sc,
878ef24a0dSAdrian Chadd 		    bhnd_pci_war_state state);
88bb64eeccSAdrian Chadd 
89bb64eeccSAdrian Chadd /*
90bb64eeccSAdrian Chadd  * device/quirk tables
91bb64eeccSAdrian Chadd  */
928ef24a0dSAdrian Chadd 
935ad9ac03SAdrian Chadd #define	BHND_PCI_DEV(_core, _quirks)		\
94b0b9c854SLandon J. Fuller 	BHND_DEVICE(BCM, _core, NULL, _quirks, BHND_DF_HOSTB)
958ef24a0dSAdrian Chadd 
96bb64eeccSAdrian Chadd static const struct bhnd_device bhnd_pci_devs[] = {
975ad9ac03SAdrian Chadd 	BHND_PCI_DEV(PCI,	bhnd_pci_quirks),
985ad9ac03SAdrian Chadd 	BHND_PCI_DEV(PCIE,	bhnd_pcie_quirks),
99bb64eeccSAdrian Chadd 	BHND_DEVICE_END
1004ad7e9b0SAdrian Chadd };
1014ad7e9b0SAdrian Chadd 
102bb64eeccSAdrian Chadd static const struct bhnd_device_quirk bhnd_pci_quirks[] = {
1035ad9ac03SAdrian Chadd 	/* core revision quirks */
1045ad9ac03SAdrian Chadd 	BHND_CORE_QUIRK	(HWREV_ANY,	BHND_PCI_QUIRK_SBTOPCI2_PREF_BURST),
1055ad9ac03SAdrian Chadd 	BHND_CORE_QUIRK	(HWREV_GTE(11),	BHND_PCI_QUIRK_SBTOPCI2_READMULTI |
1065ad9ac03SAdrian Chadd 					BHND_PCI_QUIRK_CLKRUN_DSBL),
1074ad7e9b0SAdrian Chadd 
1088ef24a0dSAdrian Chadd 	/* BCM4321CB2 boards that require 960ns latency timer override */
1095ad9ac03SAdrian Chadd 	BHND_BOARD_QUIRK(BCM4321CB2,	BHND_PCI_QUIRK_960NS_LATTIM_OVR),
1105ad9ac03SAdrian Chadd 	BHND_BOARD_QUIRK(BCM4321CB2_AG,	BHND_PCI_QUIRK_960NS_LATTIM_OVR),
1118ef24a0dSAdrian Chadd 
1125ad9ac03SAdrian Chadd 	BHND_DEVICE_QUIRK_END
1138ef24a0dSAdrian Chadd };
1148ef24a0dSAdrian Chadd 
115bb64eeccSAdrian Chadd static const struct bhnd_device_quirk bhnd_pcie_quirks[] = {
1165ad9ac03SAdrian Chadd 	/* core revision quirks */
1175ad9ac03SAdrian Chadd 	BHND_CORE_QUIRK	(HWREV_EQ (0),	BHND_PCIE_QUIRK_SDR9_L0s_HANG),
1185ad9ac03SAdrian Chadd 	BHND_CORE_QUIRK	(HWREV_RANGE(0,1),
1195ad9ac03SAdrian Chadd 	    BHND_PCIE_QUIRK_UR_STATUS_FIX),
1204ad7e9b0SAdrian Chadd 
1215ad9ac03SAdrian Chadd 	BHND_CORE_QUIRK	(HWREV_EQ (1),	BHND_PCIE_QUIRK_PCIPM_REQEN),
122bb64eeccSAdrian Chadd 
1235ad9ac03SAdrian Chadd 	BHND_CORE_QUIRK	(HWREV_RANGE(3,5),
1245ad9ac03SAdrian Chadd 	    BHND_PCIE_QUIRK_ASPM_OVR | BHND_PCIE_QUIRK_SDR9_POLARITY |
1255ad9ac03SAdrian Chadd 	    BHND_PCIE_QUIRK_SDR9_NO_FREQRETRY),
1268ef24a0dSAdrian Chadd 
1275ad9ac03SAdrian Chadd 	BHND_CORE_QUIRK	(HWREV_LTE(6),	BHND_PCIE_QUIRK_L1_IDLE_THRESH),
1285ad9ac03SAdrian Chadd 	BHND_CORE_QUIRK	(HWREV_GTE(6),	BHND_PCIE_QUIRK_SPROM_L23_PCI_RESET),
1295ad9ac03SAdrian Chadd 	BHND_CORE_QUIRK	(HWREV_EQ (7),	BHND_PCIE_QUIRK_SERDES_NOPLLDOWN),
1305ad9ac03SAdrian Chadd 	BHND_CORE_QUIRK	(HWREV_GTE(8),	BHND_PCIE_QUIRK_L1_TIMER_PERF),
1315ad9ac03SAdrian Chadd 
1325ad9ac03SAdrian Chadd 	BHND_CORE_QUIRK	(HWREV_LTE(17),	BHND_PCIE_QUIRK_MAX_MRRS_128),
1335ad9ac03SAdrian Chadd 
1345ad9ac03SAdrian Chadd 	/* Apple boards on which BHND_BFL2_PCIEWAR_OVR should be assumed
1355ad9ac03SAdrian Chadd 	 * to be set. */
1365ad9ac03SAdrian Chadd 	{{ BHND_MATCH_BOARD_VENDOR	(PCI_VENDOR_APPLE),
1375ad9ac03SAdrian Chadd 	   BHND_MATCH_BOARD_REV		(HWREV_LTE(0x71)),
1385ad9ac03SAdrian Chadd 	   BHND_MATCH_SROMREV		(EQ(4)) },
1395ad9ac03SAdrian Chadd 		BHND_PCIE_QUIRK_BFL2_PCIEWAR_EN },
1405ad9ac03SAdrian Chadd 
1415ad9ac03SAdrian Chadd 	/* Apple BCM4322 boards that require 700mV SerDes TX drive strength. */
142caeff9a3SLandon J. Fuller 	{{ BHND_MATCH_CHIP_ID(BCM4322),
1435ad9ac03SAdrian Chadd 	   BHND_MATCH_BOARD(PCI_VENDOR_APPLE, BCM94322X9), },
1445ad9ac03SAdrian Chadd 		BHND_PCIE_QUIRK_SERDES_TXDRV_700MV },
1455ad9ac03SAdrian Chadd 
1465ad9ac03SAdrian Chadd 	/* Apple BCM4331 board-specific quirks */
1475ad9ac03SAdrian Chadd #define	BHND_A4331_QUIRK(_board, ...)	\
148caeff9a3SLandon J. Fuller 	{{ BHND_MATCH_CHIP_ID(BCM4331),		\
1495ad9ac03SAdrian Chadd 	    BHND_MATCH_BOARD(PCI_VENDOR_APPLE, _board) }, __VA_ARGS__ }
1505ad9ac03SAdrian Chadd 
1515ad9ac03SAdrian Chadd 	BHND_A4331_QUIRK(BCM94331X19,	BHND_PCIE_QUIRK_SERDES_TXDRV_MAX |
1525ad9ac03SAdrian Chadd 					BHND_PCIE_QUIRK_DEFAULT_MRRS_512),
1535ad9ac03SAdrian Chadd 
1545ad9ac03SAdrian Chadd 	BHND_A4331_QUIRK(BCM94331X28,	BHND_PCIE_QUIRK_SERDES_TXDRV_MAX |
1555ad9ac03SAdrian Chadd 					BHND_PCIE_QUIRK_DEFAULT_MRRS_512),
1565ad9ac03SAdrian Chadd 
1575ad9ac03SAdrian Chadd 	BHND_A4331_QUIRK(BCM94331X28B,	BHND_PCIE_QUIRK_DEFAULT_MRRS_512),
1585ad9ac03SAdrian Chadd 
1595ad9ac03SAdrian Chadd 	BHND_A4331_QUIRK(BCM94331X29B,	BHND_PCIE_QUIRK_SERDES_TXDRV_MAX |
1605ad9ac03SAdrian Chadd 					BHND_PCIE_QUIRK_DEFAULT_MRRS_512),
1615ad9ac03SAdrian Chadd 
1625ad9ac03SAdrian Chadd 	BHND_A4331_QUIRK(BCM94331X19C,	BHND_PCIE_QUIRK_SERDES_TXDRV_MAX |
1635ad9ac03SAdrian Chadd 					BHND_PCIE_QUIRK_DEFAULT_MRRS_512),
1645ad9ac03SAdrian Chadd 
1655ad9ac03SAdrian Chadd 	BHND_A4331_QUIRK(BCM94331X29D,	BHND_PCIE_QUIRK_DEFAULT_MRRS_512),
1665ad9ac03SAdrian Chadd 
1675ad9ac03SAdrian Chadd 	BHND_A4331_QUIRK(BCM94331X33,	BHND_PCIE_QUIRK_DEFAULT_MRRS_512),
1685ad9ac03SAdrian Chadd 
1695ad9ac03SAdrian Chadd #undef BHND_A4331_QUIRK
1708ef24a0dSAdrian Chadd 
171bb64eeccSAdrian Chadd 	BHND_DEVICE_QUIRK_END
172bb64eeccSAdrian Chadd };
173bb64eeccSAdrian Chadd 
174bb64eeccSAdrian Chadd #define	BHND_PCI_SOFTC(_sc)	(&((_sc)->common))
175bb64eeccSAdrian Chadd 
176bb64eeccSAdrian Chadd #define	BHND_PCI_READ_2(_sc, _reg)		\
177bb64eeccSAdrian Chadd 	bhnd_bus_read_2(BHND_PCI_SOFTC(_sc)->mem_res, (_reg))
178bb64eeccSAdrian Chadd 
179bb64eeccSAdrian Chadd #define	BHND_PCI_READ_4(_sc, _reg)		\
180bb64eeccSAdrian Chadd 	bhnd_bus_read_4(BHND_PCI_SOFTC(_sc)->mem_res, (_reg))
181bb64eeccSAdrian Chadd 
182bb64eeccSAdrian Chadd #define	BHND_PCI_WRITE_2(_sc, _reg, _val)	\
183bb64eeccSAdrian Chadd 	bhnd_bus_write_2(BHND_PCI_SOFTC(_sc)->mem_res, (_reg), (_val))
184bb64eeccSAdrian Chadd 
185bb64eeccSAdrian Chadd #define	BHND_PCI_WRITE_4(_sc, _reg, _val)	\
186bb64eeccSAdrian Chadd 	bhnd_bus_write_4(BHND_PCI_SOFTC(_sc)->mem_res, (_reg), (_val))
187bb64eeccSAdrian Chadd 
188bb64eeccSAdrian Chadd #define	BHND_PCI_PROTO_READ_4(_sc, _reg)	\
189bb64eeccSAdrian Chadd 	bhnd_pcie_read_proto_reg(BHND_PCI_SOFTC(_sc), (_reg))
190bb64eeccSAdrian Chadd 
191bb64eeccSAdrian Chadd #define	BHND_PCI_PROTO_WRITE_4(_sc, _reg, _val)	\
192bb64eeccSAdrian Chadd 	bhnd_pcie_write_proto_reg(BHND_PCI_SOFTC(_sc), (_reg), (_val))
193bb64eeccSAdrian Chadd 
194bb64eeccSAdrian Chadd #define	BHND_PCI_MDIO_READ(_sc, _phy, _reg)	\
195bb64eeccSAdrian Chadd 	bhnd_pcie_mdio_read(BHND_PCI_SOFTC(_sc), (_phy), (_reg))
196bb64eeccSAdrian Chadd 
197bb64eeccSAdrian Chadd #define	BHND_PCI_MDIO_WRITE(_sc, _phy, _reg, _val)		\
198bb64eeccSAdrian Chadd 	bhnd_pcie_mdio_write(BHND_PCI_SOFTC(_sc), (_phy), (_reg), (_val))
199bb64eeccSAdrian Chadd 
2008ef24a0dSAdrian Chadd #define	BHND_PCI_MDIO_READ_EXT(_sc, _phy, _devaddr, _reg)		\
2018ef24a0dSAdrian Chadd 	bhnd_pcie_mdio_read_ext(BHND_PCI_SOFTC(_sc), (_phy), (_devaddr), (_reg))
2028ef24a0dSAdrian Chadd 
2038ef24a0dSAdrian Chadd #define	BHND_PCI_MDIO_WRITE_EXT(_sc, _phy, _devaddr, _reg, _val)	\
2048ef24a0dSAdrian Chadd 	bhnd_pcie_mdio_write_ext(BHND_PCI_SOFTC(_sc), (_phy),		\
2058ef24a0dSAdrian Chadd 	    (_devaddr), (_reg), (_val))
2068ef24a0dSAdrian Chadd 
207bb64eeccSAdrian Chadd #define	BPCI_REG_SET(_regv, _attr, _val)	\
208bb64eeccSAdrian Chadd 	BHND_PCI_REG_SET((_regv), BHND_ ## _attr, (_val))
209bb64eeccSAdrian Chadd 
210bb64eeccSAdrian Chadd #define	BPCI_REG_GET(_regv, _attr)	\
211bb64eeccSAdrian Chadd 	BHND_PCI_REG_GET((_regv), BHND_ ## _attr)
212bb64eeccSAdrian Chadd 
213bb64eeccSAdrian Chadd #define	BPCI_CMN_REG_SET(_regv, _attr, _val)			\
214bb64eeccSAdrian Chadd 	BHND_PCI_CMN_REG_SET(BHND_PCI_SOFTC(_sc)->regfmt, (_regv),	\
215bb64eeccSAdrian Chadd 	    BHND_ ## _attr, (_val))
216bb64eeccSAdrian Chadd 
217bb64eeccSAdrian Chadd #define	BPCI_CMN_REG_GET(_regv, _attr)				\
218bb64eeccSAdrian Chadd 	BHND_PCI_CMN_REG_GET(BHND_PCI_SOFTC(_sc)->regfmt, (_regv),	\
219bb64eeccSAdrian Chadd 	    BHND_ ## _attr)
2204ad7e9b0SAdrian Chadd 
2214ad7e9b0SAdrian Chadd static int
bhnd_pci_hostb_attach(device_t dev)2224ad7e9b0SAdrian Chadd bhnd_pci_hostb_attach(device_t dev)
2234ad7e9b0SAdrian Chadd {
224bb64eeccSAdrian Chadd 	struct bhnd_pcihb_softc	*sc;
225bb64eeccSAdrian Chadd 	int			 error;
226bb64eeccSAdrian Chadd 
227bb64eeccSAdrian Chadd 	sc = device_get_softc(dev);
2288ef24a0dSAdrian Chadd 	sc->dev = dev;
229bb64eeccSAdrian Chadd 	sc->quirks = bhnd_device_quirks(dev, bhnd_pci_devs,
230bb64eeccSAdrian Chadd 	    sizeof(bhnd_pci_devs[0]));
231bb64eeccSAdrian Chadd 
2328ef24a0dSAdrian Chadd 	/* Find the host PCI bridge device */
2338ef24a0dSAdrian Chadd 	sc->pci_dev = bhnd_find_bridge_root(dev, devclass_find("pci"));
2348ef24a0dSAdrian Chadd 	if (sc->pci_dev == NULL) {
2358ef24a0dSAdrian Chadd 		device_printf(dev, "parent pci bridge device not found\n");
2368ef24a0dSAdrian Chadd 		return (ENXIO);
2378ef24a0dSAdrian Chadd 	}
2388ef24a0dSAdrian Chadd 
2398ef24a0dSAdrian Chadd 	/* Common setup */
240bb64eeccSAdrian Chadd 	if ((error = bhnd_pci_generic_attach(dev)))
241bb64eeccSAdrian Chadd 		return (error);
242bb64eeccSAdrian Chadd 
243bb64eeccSAdrian Chadd 	/* Apply early single-shot work-arounds */
2448ef24a0dSAdrian Chadd 	if ((error = bhnd_pci_wars_early_once(sc)))
2458ef24a0dSAdrian Chadd 		goto failed;
246bb64eeccSAdrian Chadd 
247bb64eeccSAdrian Chadd 	/* Apply attach/resume work-arounds */
2488ef24a0dSAdrian Chadd 	if ((error = bhnd_pci_wars_hwup(sc, BHND_PCI_WAR_ATTACH)))
2498ef24a0dSAdrian Chadd 		goto failed;
250bb64eeccSAdrian Chadd 
2514ad7e9b0SAdrian Chadd 	return (0);
2528ef24a0dSAdrian Chadd 
2538ef24a0dSAdrian Chadd failed:
2548ef24a0dSAdrian Chadd 	bhnd_pci_generic_detach(dev);
2558ef24a0dSAdrian Chadd 	return (error);
2564ad7e9b0SAdrian Chadd }
2574ad7e9b0SAdrian Chadd 
2584ad7e9b0SAdrian Chadd static int
bhnd_pci_hostb_detach(device_t dev)2594ad7e9b0SAdrian Chadd bhnd_pci_hostb_detach(device_t dev)
2604ad7e9b0SAdrian Chadd {
261bb64eeccSAdrian Chadd 	struct bhnd_pcihb_softc *sc;
262bb64eeccSAdrian Chadd 	int			 error;
263bb64eeccSAdrian Chadd 
264bb64eeccSAdrian Chadd 	sc = device_get_softc(dev);
265bb64eeccSAdrian Chadd 
266bb64eeccSAdrian Chadd 	/* Apply suspend/detach work-arounds */
2678ef24a0dSAdrian Chadd 	if ((error = bhnd_pci_wars_hwdown(sc, BHND_PCI_WAR_DETACH)))
268bb64eeccSAdrian Chadd 		return (error);
269bb64eeccSAdrian Chadd 
270bb64eeccSAdrian Chadd 	return (bhnd_pci_generic_detach(dev));
2714ad7e9b0SAdrian Chadd }
2724ad7e9b0SAdrian Chadd 
2734ad7e9b0SAdrian Chadd static int
bhnd_pci_hostb_suspend(device_t dev)2744ad7e9b0SAdrian Chadd bhnd_pci_hostb_suspend(device_t dev)
2754ad7e9b0SAdrian Chadd {
276bb64eeccSAdrian Chadd 	struct bhnd_pcihb_softc *sc;
277bb64eeccSAdrian Chadd 	int			 error;
278bb64eeccSAdrian Chadd 
279bb64eeccSAdrian Chadd 	sc = device_get_softc(dev);
280bb64eeccSAdrian Chadd 
281bb64eeccSAdrian Chadd 	/* Apply suspend/detach work-arounds */
2828ef24a0dSAdrian Chadd 	if ((error = bhnd_pci_wars_hwdown(sc, BHND_PCI_WAR_SUSPEND)))
283bb64eeccSAdrian Chadd 		return (error);
284bb64eeccSAdrian Chadd 
285bb64eeccSAdrian Chadd 	return (bhnd_pci_generic_suspend(dev));
2864ad7e9b0SAdrian Chadd }
2874ad7e9b0SAdrian Chadd 
2884ad7e9b0SAdrian Chadd static int
bhnd_pci_hostb_resume(device_t dev)2894ad7e9b0SAdrian Chadd bhnd_pci_hostb_resume(device_t dev)
2904ad7e9b0SAdrian Chadd {
291bb64eeccSAdrian Chadd 	struct bhnd_pcihb_softc	*sc;
292bb64eeccSAdrian Chadd 	int			 error;
293bb64eeccSAdrian Chadd 
294bb64eeccSAdrian Chadd 	sc = device_get_softc(dev);
295bb64eeccSAdrian Chadd 
296bb64eeccSAdrian Chadd 	if ((error = bhnd_pci_generic_resume(dev)))
297bb64eeccSAdrian Chadd 		return (error);
298bb64eeccSAdrian Chadd 
299bb64eeccSAdrian Chadd 	/* Apply attach/resume work-arounds */
3008ef24a0dSAdrian Chadd 	if ((error = bhnd_pci_wars_hwup(sc, BHND_PCI_WAR_RESUME))) {
301bb64eeccSAdrian Chadd 		bhnd_pci_generic_detach(dev);
302bb64eeccSAdrian Chadd 		return (error);
303bb64eeccSAdrian Chadd 	}
304bb64eeccSAdrian Chadd 
305bb64eeccSAdrian Chadd 	return (0);
306bb64eeccSAdrian Chadd }
307bb64eeccSAdrian Chadd 
308bb64eeccSAdrian Chadd /**
309bb64eeccSAdrian Chadd  * Apply any hardware work-arounds that must be executed exactly once, early in
310bb64eeccSAdrian Chadd  * the attach process.
311bb64eeccSAdrian Chadd  *
312bb64eeccSAdrian Chadd  * This must be called after core enumeration and discovery of all applicable
313bb64eeccSAdrian Chadd  * quirks, but prior to probe/attach of any cores, parsing of
314bb64eeccSAdrian Chadd  * SPROM, etc.
315bb64eeccSAdrian Chadd  */
316bb64eeccSAdrian Chadd static int
bhnd_pci_wars_early_once(struct bhnd_pcihb_softc * sc)317bb64eeccSAdrian Chadd bhnd_pci_wars_early_once(struct bhnd_pcihb_softc *sc)
318bb64eeccSAdrian Chadd {
3198ef24a0dSAdrian Chadd 	int error;
3208ef24a0dSAdrian Chadd 
3218ef24a0dSAdrian Chadd 	/* Set PCI latency timer */
3228ef24a0dSAdrian Chadd 	if (sc->quirks & BHND_PCI_QUIRK_960NS_LATTIM_OVR) {
3238ef24a0dSAdrian Chadd 		pci_write_config(sc->pci_dev, PCIR_LATTIMER, 0x20 /* 960ns */,
3248ef24a0dSAdrian Chadd 		    1);
3258ef24a0dSAdrian Chadd 	}
3268ef24a0dSAdrian Chadd 
3278ef24a0dSAdrian Chadd 	/* Determine whether ASPM/CLKREQ should be forced on, or forced off. */
3288ef24a0dSAdrian Chadd 	if (sc->quirks & BHND_PCIE_QUIRK_ASPM_OVR) {
3298ef24a0dSAdrian Chadd 		struct bhnd_board_info	board;
3308ef24a0dSAdrian Chadd 		bool			aspm_en;
3318ef24a0dSAdrian Chadd 
3328ef24a0dSAdrian Chadd 		/* Fetch board info */
3338ef24a0dSAdrian Chadd 		if ((error = bhnd_read_board_info(sc->dev, &board)))
3348ef24a0dSAdrian Chadd 			return (error);
3358ef24a0dSAdrian Chadd 
3368ef24a0dSAdrian Chadd 		/* Check board flags */
3378ef24a0dSAdrian Chadd 		aspm_en = true;
3388ef24a0dSAdrian Chadd 		if (board.board_flags2 & BHND_BFL2_PCIEWAR_OVR)
3398ef24a0dSAdrian Chadd 			aspm_en = false;
3408ef24a0dSAdrian Chadd 
3418ef24a0dSAdrian Chadd 		/* Early Apple devices did not (but should have) set
3428ef24a0dSAdrian Chadd 		 * BHND_BFL2_PCIEWAR_OVR in SPROM. */
3438ef24a0dSAdrian Chadd 		if (sc->quirks & BHND_PCIE_QUIRK_BFL2_PCIEWAR_EN)
3448ef24a0dSAdrian Chadd 			aspm_en = false;
3458ef24a0dSAdrian Chadd 
3468ef24a0dSAdrian Chadd 		sc->aspm_quirk_override.aspm_en = aspm_en;
3478ef24a0dSAdrian Chadd 	}
3488ef24a0dSAdrian Chadd 
349bb64eeccSAdrian Chadd 	/* Determine correct polarity by observing the attach-time PCIe PHY
350bb64eeccSAdrian Chadd 	 * link status. This is used later to reset/force the SerDes
351bb64eeccSAdrian Chadd 	 * polarity */
352bb64eeccSAdrian Chadd 	if (sc->quirks & BHND_PCIE_QUIRK_SDR9_POLARITY) {
353bb64eeccSAdrian Chadd 		uint32_t st;
354bb64eeccSAdrian Chadd 		bool inv;
355bb64eeccSAdrian Chadd 
356bb64eeccSAdrian Chadd 		st = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_PLP_STATUSREG);
357bb64eeccSAdrian Chadd 		inv = ((st & BHND_PCIE_PLP_POLARITY_INV) != 0);
358bb64eeccSAdrian Chadd 		sc->sdr9_quirk_polarity.inv = inv;
359bb64eeccSAdrian Chadd 	}
360bb64eeccSAdrian Chadd 
3618ef24a0dSAdrian Chadd 	/* Override maximum read request size */
3628ef24a0dSAdrian Chadd 	if (bhnd_get_class(sc->dev) == BHND_DEVCLASS_PCIE) {
3638ef24a0dSAdrian Chadd 		int	msize;
3648ef24a0dSAdrian Chadd 
3658ef24a0dSAdrian Chadd 		msize = 128; /* compatible with all PCIe-G1 core revisions */
3668ef24a0dSAdrian Chadd 		if (sc->quirks & BHND_PCIE_QUIRK_DEFAULT_MRRS_512)
3678ef24a0dSAdrian Chadd 			msize = 512;
3688ef24a0dSAdrian Chadd 
3698ef24a0dSAdrian Chadd 		if (pci_set_max_read_req(sc->pci_dev, msize) == 0)
3708ef24a0dSAdrian Chadd 			panic("set mrrs on non-PCIe device");
3718ef24a0dSAdrian Chadd 	}
3728ef24a0dSAdrian Chadd 
373bb64eeccSAdrian Chadd 	return (0);
374bb64eeccSAdrian Chadd }
375bb64eeccSAdrian Chadd 
376bb64eeccSAdrian Chadd /**
377bb64eeccSAdrian Chadd  * Apply any hardware workarounds that are required upon attach or resume
378bb64eeccSAdrian Chadd  * of the bridge device.
379bb64eeccSAdrian Chadd  */
380bb64eeccSAdrian Chadd static int
bhnd_pci_wars_hwup(struct bhnd_pcihb_softc * sc,bhnd_pci_war_state state)3818ef24a0dSAdrian Chadd bhnd_pci_wars_hwup(struct bhnd_pcihb_softc *sc, bhnd_pci_war_state state)
382bb64eeccSAdrian Chadd {
383bb64eeccSAdrian Chadd 	/* Note that the order here matters; these work-arounds
384bb64eeccSAdrian Chadd 	 * should not be re-ordered without careful review of their
385bb64eeccSAdrian Chadd 	 * interdependencies */
386bb64eeccSAdrian Chadd 
387bb64eeccSAdrian Chadd 	/* Enable PCI prefetch/burst/readmulti flags */
388bb64eeccSAdrian Chadd 	if (sc->quirks & BHND_PCI_QUIRK_SBTOPCI2_PREF_BURST ||
389bb64eeccSAdrian Chadd 	    sc->quirks & BHND_PCI_QUIRK_SBTOPCI2_READMULTI)
390bb64eeccSAdrian Chadd 	{
391bb64eeccSAdrian Chadd 		uint32_t sbp2;
392bb64eeccSAdrian Chadd 		sbp2 = BHND_PCI_READ_4(sc, BHND_PCI_SBTOPCI2);
393bb64eeccSAdrian Chadd 
394bb64eeccSAdrian Chadd 		if (sc->quirks & BHND_PCI_QUIRK_SBTOPCI2_PREF_BURST)
395bb64eeccSAdrian Chadd 			sbp2 |= (BHND_PCI_SBTOPCI_PREF|BHND_PCI_SBTOPCI_BURST);
396bb64eeccSAdrian Chadd 
397bb64eeccSAdrian Chadd 		if (sc->quirks & BHND_PCI_QUIRK_SBTOPCI2_READMULTI)
398bb64eeccSAdrian Chadd 			sbp2 |= BHND_PCI_SBTOPCI_RC_READMULTI;
399bb64eeccSAdrian Chadd 
400bb64eeccSAdrian Chadd 		BHND_PCI_WRITE_4(sc, BHND_PCI_SBTOPCI2, sbp2);
401bb64eeccSAdrian Chadd 	}
402bb64eeccSAdrian Chadd 
403bb64eeccSAdrian Chadd 	/* Disable PCI CLKRUN# */
404bb64eeccSAdrian Chadd 	if (sc->quirks & BHND_PCI_QUIRK_CLKRUN_DSBL) {
405bb64eeccSAdrian Chadd 		uint32_t ctl;
406bb64eeccSAdrian Chadd 
407bb64eeccSAdrian Chadd 		ctl = BHND_PCI_READ_4(sc, BHND_PCI_CLKRUN_CTL);
408bb64eeccSAdrian Chadd 		ctl |= BHND_PCI_CLKRUN_DSBL;
409bb64eeccSAdrian Chadd 		BHND_PCI_WRITE_4(sc, BHND_PCI_CLKRUN_CTL, ctl);
410bb64eeccSAdrian Chadd 	}
411bb64eeccSAdrian Chadd 
412bb64eeccSAdrian Chadd 	/* Enable TLP unmatched address handling work-around */
413bb64eeccSAdrian Chadd 	if (sc->quirks & BHND_PCIE_QUIRK_UR_STATUS_FIX) {
414bb64eeccSAdrian Chadd 		uint32_t wrs;
415bb64eeccSAdrian Chadd 		wrs = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_TLP_WORKAROUNDSREG);
416bb64eeccSAdrian Chadd 		wrs |= BHND_PCIE_TLP_WORKAROUND_URBIT;
417bb64eeccSAdrian Chadd 		BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_TLP_WORKAROUNDSREG, wrs);
418bb64eeccSAdrian Chadd 	}
419bb64eeccSAdrian Chadd 
420bb64eeccSAdrian Chadd 	/* Adjust SerDes CDR tuning to ensure that CDR is stable before sending
421bb64eeccSAdrian Chadd 	 * data during L0s to L0 exit transitions. */
422bb64eeccSAdrian Chadd 	if (sc->quirks & BHND_PCIE_QUIRK_SDR9_L0s_HANG) {
423bb64eeccSAdrian Chadd 		uint16_t sdv;
424bb64eeccSAdrian Chadd 
425bb64eeccSAdrian Chadd 		/* Set RX track/acquire timers to 2.064us/40.96us */
426bb64eeccSAdrian Chadd 		sdv = BPCI_REG_SET(0, PCIE_SDR9_RX_TIMER1_LKTRK, (2064/16));
427bb64eeccSAdrian Chadd 		sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_TIMER1_LKACQ,
428bb64eeccSAdrian Chadd 		    (40960/1024));
429bb64eeccSAdrian Chadd 		BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_TXRX,
430bb64eeccSAdrian Chadd 		    BHND_PCIE_SDR9_RX_TIMER1, sdv);
431bb64eeccSAdrian Chadd 
432bb64eeccSAdrian Chadd 		/* Apply CDR frequency workaround */
433bb64eeccSAdrian Chadd 		sdv = BHND_PCIE_SDR9_RX_CDR_FREQ_OVR_EN;
434bb64eeccSAdrian Chadd 		sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDR_FREQ_OVR, 0x0);
435bb64eeccSAdrian Chadd 		BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_TXRX,
436bb64eeccSAdrian Chadd 		    BHND_PCIE_SDR9_RX_CDR, sdv);
437bb64eeccSAdrian Chadd 
438bb64eeccSAdrian Chadd 		/* Apply CDR BW tunings */
439bb64eeccSAdrian Chadd 		sdv = 0;
440bb64eeccSAdrian Chadd 		sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDRBW_INTGTRK, 0x2);
441bb64eeccSAdrian Chadd 		sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDRBW_INTGACQ, 0x4);
442bb64eeccSAdrian Chadd 		sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDRBW_PROPTRK, 0x6);
443bb64eeccSAdrian Chadd 		sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDRBW_PROPACQ, 0x6);
444bb64eeccSAdrian Chadd 		BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_TXRX,
445bb64eeccSAdrian Chadd 		    BHND_PCIE_SDR9_RX_CDRBW, sdv);
446bb64eeccSAdrian Chadd 	}
447bb64eeccSAdrian Chadd 
448bb64eeccSAdrian Chadd 	/* Force correct SerDes polarity */
449bb64eeccSAdrian Chadd 	if (sc->quirks & BHND_PCIE_QUIRK_SDR9_POLARITY) {
450bb64eeccSAdrian Chadd 		uint16_t	rxctl;
451bb64eeccSAdrian Chadd 
452bb64eeccSAdrian Chadd 		rxctl = BHND_PCI_MDIO_READ(sc, BHND_PCIE_PHY_SDR9_TXRX,
453bb64eeccSAdrian Chadd 		    BHND_PCIE_SDR9_RX_CTRL);
454bb64eeccSAdrian Chadd 
455bb64eeccSAdrian Chadd 		rxctl |= BHND_PCIE_SDR9_RX_CTRL_FORCE;
456bb64eeccSAdrian Chadd 		if (sc->sdr9_quirk_polarity.inv)
457bb64eeccSAdrian Chadd 			rxctl |= BHND_PCIE_SDR9_RX_CTRL_POLARITY_INV;
458bb64eeccSAdrian Chadd 		else
459bb64eeccSAdrian Chadd 			rxctl &= ~BHND_PCIE_SDR9_RX_CTRL_POLARITY_INV;
460bb64eeccSAdrian Chadd 
461bb64eeccSAdrian Chadd 		BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_TXRX,
462bb64eeccSAdrian Chadd 		    BHND_PCIE_SDR9_RX_CTRL, rxctl);
463bb64eeccSAdrian Chadd 	}
464bb64eeccSAdrian Chadd 
465bb64eeccSAdrian Chadd 	/* Disable startup retry on PLL frequency detection failure */
466bb64eeccSAdrian Chadd 	if (sc->quirks & BHND_PCIE_QUIRK_SDR9_NO_FREQRETRY) {
467bb64eeccSAdrian Chadd 		uint16_t	pctl;
468bb64eeccSAdrian Chadd 
469bb64eeccSAdrian Chadd 		pctl = BHND_PCI_MDIO_READ(sc, BHND_PCIE_PHY_SDR9_PLL,
470bb64eeccSAdrian Chadd 		    BHND_PCIE_SDR9_PLL_CTRL);
471bb64eeccSAdrian Chadd 
472bb64eeccSAdrian Chadd 		pctl &= ~BHND_PCIE_SDR9_PLL_CTRL_FREQDET_EN;
473bb64eeccSAdrian Chadd 		BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_PLL,
474bb64eeccSAdrian Chadd 		    BHND_PCIE_SDR9_PLL_CTRL, pctl);
475bb64eeccSAdrian Chadd 	}
476bb64eeccSAdrian Chadd 
477bb64eeccSAdrian Chadd 	/* Explicitly enable PCI-PM */
478bb64eeccSAdrian Chadd 	if (sc->quirks & BHND_PCIE_QUIRK_PCIPM_REQEN) {
479bb64eeccSAdrian Chadd 		uint32_t lcreg;
480bb64eeccSAdrian Chadd 		lcreg = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_DLLP_LCREG);
481bb64eeccSAdrian Chadd 		lcreg |= BHND_PCIE_DLLP_LCREG_PCIPM_EN;
482bb64eeccSAdrian Chadd 		BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_DLLP_LCREG, lcreg);
483bb64eeccSAdrian Chadd 	}
484bb64eeccSAdrian Chadd 
485bb64eeccSAdrian Chadd 	/* Adjust L1 timer to fix slow L1->L0 transitions */
486bb64eeccSAdrian Chadd 	if (sc->quirks & BHND_PCIE_QUIRK_L1_IDLE_THRESH) {
487bb64eeccSAdrian Chadd 		uint32_t pmt;
488bb64eeccSAdrian Chadd 		pmt = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_DLLP_PMTHRESHREG);
489bb64eeccSAdrian Chadd 		pmt = BPCI_REG_SET(pmt, PCIE_L1THRESHOLDTIME,
490bb64eeccSAdrian Chadd 		    BHND_PCIE_L1THRESHOLD_WARVAL);
491bb64eeccSAdrian Chadd 		BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt);
492bb64eeccSAdrian Chadd 	}
493bb64eeccSAdrian Chadd 
494bb64eeccSAdrian Chadd 	/* Extend L1 timer for better performance.
495bb64eeccSAdrian Chadd 	 * TODO: We could enable/disable this on demand for better power
496bb64eeccSAdrian Chadd 	 * savings if we tie this to HT clock request handling */
497bb64eeccSAdrian Chadd 	if (sc->quirks & BHND_PCIE_QUIRK_L1_TIMER_PERF) {
498bb64eeccSAdrian Chadd 		uint32_t pmt;
499bb64eeccSAdrian Chadd 		pmt = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_DLLP_PMTHRESHREG);
500bb64eeccSAdrian Chadd 		pmt |= BHND_PCIE_ASPMTIMER_EXTEND;
501bb64eeccSAdrian Chadd 		BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt);
502bb64eeccSAdrian Chadd 	}
503bb64eeccSAdrian Chadd 
5048ef24a0dSAdrian Chadd 	/* Override ASPM/ECPM settings in SPROM shadow and PCIER_LINK_CTL */
5058ef24a0dSAdrian Chadd 	if (sc->quirks & BHND_PCIE_QUIRK_ASPM_OVR) {
5068ef24a0dSAdrian Chadd 		bus_size_t	reg;
5078ef24a0dSAdrian Chadd 		uint16_t	cfg;
5088ef24a0dSAdrian Chadd 
5098ef24a0dSAdrian Chadd 		/* Set ASPM L1/L0s flags in SPROM shadow */
5108ef24a0dSAdrian Chadd 		reg = BHND_PCIE_SPROM_SHADOW + BHND_PCIE_SRSH_ASPM_OFFSET;
5118ef24a0dSAdrian Chadd 		cfg = BHND_PCI_READ_2(sc, reg);
5128ef24a0dSAdrian Chadd 
5138ef24a0dSAdrian Chadd 		if (sc->aspm_quirk_override.aspm_en)
5148ef24a0dSAdrian Chadd 			cfg |= BHND_PCIE_SRSH_ASPM_ENB;
5158ef24a0dSAdrian Chadd 		else
5168ef24a0dSAdrian Chadd 			cfg &= ~BHND_PCIE_SRSH_ASPM_ENB;
5178ef24a0dSAdrian Chadd 
5188ef24a0dSAdrian Chadd 		BHND_PCI_WRITE_2(sc, reg, cfg);
5198ef24a0dSAdrian Chadd 
5208ef24a0dSAdrian Chadd 		/* Set ASPM/ECPM (CLKREQ) flags in PCIe link control register */
5218ef24a0dSAdrian Chadd 		cfg = pcie_read_config(sc->pci_dev, PCIER_LINK_CTL, 2);
5228ef24a0dSAdrian Chadd 
5238ef24a0dSAdrian Chadd 		if (sc->aspm_quirk_override.aspm_en)
5248ef24a0dSAdrian Chadd 			cfg |= PCIEM_LINK_CTL_ASPMC;
5258ef24a0dSAdrian Chadd 		else
5268ef24a0dSAdrian Chadd 			cfg &= ~PCIEM_LINK_CTL_ASPMC;
5278ef24a0dSAdrian Chadd 
5288ef24a0dSAdrian Chadd 		cfg &= ~PCIEM_LINK_CTL_ECPM;		/* CLKREQ# */
5298ef24a0dSAdrian Chadd 
5308ef24a0dSAdrian Chadd 		pcie_write_config(sc->pci_dev, PCIER_LINK_CTL, cfg, 2);
5318ef24a0dSAdrian Chadd 
5328ef24a0dSAdrian Chadd 		/* Set CLKREQ (ECPM) flags in SPROM shadow */
5338ef24a0dSAdrian Chadd 		reg = BHND_PCIE_SPROM_SHADOW + BHND_PCIE_SRSH_CLKREQ_OFFSET_R5;
5348ef24a0dSAdrian Chadd 		cfg = BHND_PCI_READ_2(sc, reg);
5358ef24a0dSAdrian Chadd 
5368ef24a0dSAdrian Chadd 		if (sc->aspm_quirk_override.aspm_en)
5378ef24a0dSAdrian Chadd 			cfg |= BHND_PCIE_SRSH_CLKREQ_ENB;
5388ef24a0dSAdrian Chadd 		else
5398ef24a0dSAdrian Chadd 			cfg &= ~BHND_PCIE_SRSH_CLKREQ_ENB;
5408ef24a0dSAdrian Chadd 
5418ef24a0dSAdrian Chadd 		BHND_PCI_WRITE_2(sc, reg, cfg);
5428ef24a0dSAdrian Chadd 	}
5438ef24a0dSAdrian Chadd 
544bb64eeccSAdrian Chadd 	/* Enable L23READY_EXIT_NOPRST if not already set in SPROM. */
545bb64eeccSAdrian Chadd 	if (sc->quirks & BHND_PCIE_QUIRK_SPROM_L23_PCI_RESET) {
546bb64eeccSAdrian Chadd 		bus_size_t	reg;
547bb64eeccSAdrian Chadd 		uint16_t	cfg;
548bb64eeccSAdrian Chadd 
549bb64eeccSAdrian Chadd 		/* Fetch the misc cfg flags from SPROM */
550bb64eeccSAdrian Chadd 		reg = BHND_PCIE_SPROM_SHADOW + BHND_PCIE_SRSH_PCIE_MISC_CONFIG;
551bb64eeccSAdrian Chadd 		cfg = BHND_PCI_READ_2(sc, reg);
552bb64eeccSAdrian Chadd 
553bb64eeccSAdrian Chadd 		/* Write EXIT_NOPRST flag if not already set in SPROM */
554bb64eeccSAdrian Chadd 		if (!(cfg & BHND_PCIE_SRSH_L23READY_EXIT_NOPRST)) {
555bb64eeccSAdrian Chadd 			cfg |= BHND_PCIE_SRSH_L23READY_EXIT_NOPRST;
556bb64eeccSAdrian Chadd 			BHND_PCI_WRITE_2(sc, reg, cfg);
557bb64eeccSAdrian Chadd 		}
558bb64eeccSAdrian Chadd 	}
559bb64eeccSAdrian Chadd 
5608ef24a0dSAdrian Chadd 	/* Disable SerDes PLL down */
5618ef24a0dSAdrian Chadd 	if (sc->quirks & BHND_PCIE_QUIRK_SERDES_NOPLLDOWN) {
5628ef24a0dSAdrian Chadd 		device_t	bhnd, chipc;
5638ef24a0dSAdrian Chadd 		bus_size_t	reg;
5648ef24a0dSAdrian Chadd 
5658ef24a0dSAdrian Chadd 		bhnd = device_get_parent(sc->dev);
5668e35bf83SLandon J. Fuller 		chipc = bhnd_bus_find_child(bhnd, BHND_DEVCLASS_CC, 0);
5678ef24a0dSAdrian Chadd 		KASSERT(chipc != NULL, ("missing chipcommon device"));
5688ef24a0dSAdrian Chadd 
5698ef24a0dSAdrian Chadd 		/* Write SerDes PLL disable flag to the ChipCommon core */
5708ef24a0dSAdrian Chadd 		BHND_CHIPC_WRITE_CHIPCTRL(chipc, CHIPCTRL_4321_PLL_DOWN,
5718ef24a0dSAdrian Chadd 		    CHIPCTRL_4321_PLL_DOWN);
5728ef24a0dSAdrian Chadd 
5738ef24a0dSAdrian Chadd 		/* Clear SPROM shadow backdoor register */
5748ef24a0dSAdrian Chadd 		reg = BHND_PCIE_SPROM_SHADOW + BHND_PCIE_SRSH_BD_OFFSET;
5758ef24a0dSAdrian Chadd 		BHND_PCI_WRITE_2(sc, reg, 0);
5768ef24a0dSAdrian Chadd 	}
5778ef24a0dSAdrian Chadd 
5788ef24a0dSAdrian Chadd 	/* Adjust TX drive strength and pre-emphasis coefficient */
5798ef24a0dSAdrian Chadd 	if (sc->quirks & BHND_PCIE_QUIRK_SERDES_TXDRV_ADJUST) {
5808ef24a0dSAdrian Chadd 		uint16_t txdrv;
5818ef24a0dSAdrian Chadd 
5828ef24a0dSAdrian Chadd 		/* Fetch current TX driver parameters */
5838ef24a0dSAdrian Chadd 		txdrv = BHND_PCI_MDIO_READ_EXT(sc, BHND_PCIE_PHYADDR_SD,
5848ef24a0dSAdrian Chadd 		    BHND_PCIE_SD_REGS_TX0, BHND_PCIE_SD_TX_DRIVER);
5858ef24a0dSAdrian Chadd 
5868ef24a0dSAdrian Chadd 		/* Set 700mV drive strength */
5878ef24a0dSAdrian Chadd 		if (sc->quirks & BHND_PCIE_QUIRK_SERDES_TXDRV_700MV) {
5888ef24a0dSAdrian Chadd 			txdrv = BPCI_REG_SET(txdrv, PCIE_SD_TX_DRIVER_P2_COEFF,
5898ef24a0dSAdrian Chadd 			    BHND_PCIE_APPLE_TX_P2_COEFF_700MV);
5908ef24a0dSAdrian Chadd 
5918ef24a0dSAdrian Chadd 			txdrv = BPCI_REG_SET(txdrv, PCIE_SD_TX_DRIVER_IDRIVER,
5928ef24a0dSAdrian Chadd 			    BHND_PCIE_APPLE_TX_IDRIVER_700MV);
5938ef24a0dSAdrian Chadd 		}
5948ef24a0dSAdrian Chadd 
5958ef24a0dSAdrian Chadd 		/* ... or, set max drive strength */
5968ef24a0dSAdrian Chadd 		if (sc->quirks & BHND_PCIE_QUIRK_SERDES_TXDRV_MAX) {
5978ef24a0dSAdrian Chadd 			txdrv = BPCI_REG_SET(txdrv, PCIE_SD_TX_DRIVER_P2_COEFF,
5988ef24a0dSAdrian Chadd 			    BHND_PCIE_APPLE_TX_P2_COEFF_MAX);
5998ef24a0dSAdrian Chadd 
6008ef24a0dSAdrian Chadd 			txdrv = BPCI_REG_SET(txdrv, PCIE_SD_TX_DRIVER_IDRIVER,
6018ef24a0dSAdrian Chadd 			    BHND_PCIE_APPLE_TX_IDRIVER_MAX);
6028ef24a0dSAdrian Chadd 		}
6038ef24a0dSAdrian Chadd 
6048ef24a0dSAdrian Chadd 		BHND_PCI_MDIO_WRITE_EXT(sc, BHND_PCIE_PHYADDR_SD,
6058ef24a0dSAdrian Chadd 		    BHND_PCIE_SD_REGS_TX0, BHND_PCIE_SD_TX_DRIVER, txdrv);
6068ef24a0dSAdrian Chadd 	}
6078ef24a0dSAdrian Chadd 
608bb64eeccSAdrian Chadd 	return (0);
609bb64eeccSAdrian Chadd }
610bb64eeccSAdrian Chadd 
611bb64eeccSAdrian Chadd /**
612bb64eeccSAdrian Chadd  * Apply any hardware workarounds that are required upon detach or suspend
613bb64eeccSAdrian Chadd  * of the bridge device.
614bb64eeccSAdrian Chadd  */
615bb64eeccSAdrian Chadd static int
bhnd_pci_wars_hwdown(struct bhnd_pcihb_softc * sc,bhnd_pci_war_state state)6168ef24a0dSAdrian Chadd bhnd_pci_wars_hwdown(struct bhnd_pcihb_softc *sc, bhnd_pci_war_state state)
617bb64eeccSAdrian Chadd {
618bb64eeccSAdrian Chadd 	/* Reduce L1 timer for better power savings.
619bb64eeccSAdrian Chadd 	 * TODO: We could enable/disable this on demand for better power
620bb64eeccSAdrian Chadd 	 * savings if we tie this to HT clock request handling */
621bb64eeccSAdrian Chadd 	if (sc->quirks & BHND_PCIE_QUIRK_L1_TIMER_PERF) {
622bb64eeccSAdrian Chadd 		uint32_t pmt;
623bb64eeccSAdrian Chadd 		pmt = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_DLLP_PMTHRESHREG);
624bb64eeccSAdrian Chadd 		pmt &= ~BHND_PCIE_ASPMTIMER_EXTEND;
625bb64eeccSAdrian Chadd 		BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt);
626bb64eeccSAdrian Chadd 	}
627bb64eeccSAdrian Chadd 
6288ef24a0dSAdrian Chadd 	/* Enable CLKREQ (ECPM). If suspending, also disable ASPM L1 entry */
6298ef24a0dSAdrian Chadd 	if (sc->quirks & BHND_PCIE_QUIRK_ASPM_OVR) {
6308ef24a0dSAdrian Chadd 		uint16_t	lcreg;
6318ef24a0dSAdrian Chadd 
6328ef24a0dSAdrian Chadd 		lcreg = pcie_read_config(sc->pci_dev, PCIER_LINK_CTL, 2);
6338ef24a0dSAdrian Chadd 
6348ef24a0dSAdrian Chadd 		lcreg |= PCIEM_LINK_CTL_ECPM;	/* CLKREQ# */
6358ef24a0dSAdrian Chadd 		if (state == BHND_PCI_WAR_SUSPEND)
6368ef24a0dSAdrian Chadd 			lcreg &= ~PCIEM_LINK_CTL_ASPMC_L1;
6378ef24a0dSAdrian Chadd 
6388ef24a0dSAdrian Chadd 		pcie_write_config(sc->pci_dev, PCIER_LINK_CTL, lcreg, 2);
6398ef24a0dSAdrian Chadd 	}
6408ef24a0dSAdrian Chadd 
6414ad7e9b0SAdrian Chadd 	return (0);
6424ad7e9b0SAdrian Chadd }
6434ad7e9b0SAdrian Chadd 
6444ad7e9b0SAdrian Chadd static device_method_t bhnd_pci_hostb_methods[] = {
6454ad7e9b0SAdrian Chadd 	/* Device interface */
6464ad7e9b0SAdrian Chadd 	DEVMETHOD(device_attach,		bhnd_pci_hostb_attach),
6474ad7e9b0SAdrian Chadd 	DEVMETHOD(device_detach,		bhnd_pci_hostb_detach),
6484ad7e9b0SAdrian Chadd 	DEVMETHOD(device_suspend,		bhnd_pci_hostb_suspend),
6494ad7e9b0SAdrian Chadd 	DEVMETHOD(device_resume,		bhnd_pci_hostb_resume),
6504ad7e9b0SAdrian Chadd 
6514ad7e9b0SAdrian Chadd 	DEVMETHOD_END
6524ad7e9b0SAdrian Chadd };
6534ad7e9b0SAdrian Chadd 
6548ef24a0dSAdrian Chadd DEFINE_CLASS_1(bhnd_hostb, bhnd_pci_hostb_driver, bhnd_pci_hostb_methods,
655bb64eeccSAdrian Chadd     sizeof(struct bhnd_pcihb_softc), bhnd_pci_driver);
656162c26adSJohn Baldwin DRIVER_MODULE(bhnd_pci_hostb, bhnd, bhnd_pci_hostb_driver, 0, 0);
6574ad7e9b0SAdrian Chadd 
6584ad7e9b0SAdrian Chadd MODULE_VERSION(bhnd_pci_hostb, 1);
659eaddb807SAdrian Chadd MODULE_DEPEND(bhnd_pci_hostb, bhnd, 1, 1, 1);
6604ad7e9b0SAdrian Chadd MODULE_DEPEND(bhnd_pci_hostb, bhnd_pci, 1, 1, 1);
661