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