bhnd_pci_hostb.c revision 298479
1/*-
2 * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer,
10 *    without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 *    redistribution must be conditioned upon including a substantially
14 *    similar Disclaimer requirement for further binary redistribution.
15 *
16 * NO WARRANTY
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c 298479 2016-04-22 16:26:53Z adrian $");
32
33/*
34 * Broadcom BHND PCI/PCIe-Gen1 PCI-Host Bridge.
35 *
36 * This driver handles all interactions with PCI bridge cores operating in
37 * endpoint mode.
38 *
39 * Host-level PCI operations are handled at the bhndb bridge level by the
40 * bhndb_pci driver.
41 */
42
43#include <sys/param.h>
44#include <sys/kernel.h>
45
46#include <sys/malloc.h>
47
48#include <sys/bus.h>
49#include <sys/module.h>
50
51#include <sys/systm.h>
52
53#include <machine/bus.h>
54#include <sys/rman.h>
55#include <machine/resource.h>
56
57#include <dev/bhnd/bhnd.h>
58
59#include "bhnd_pcireg.h"
60#include "bhnd_pci_hostbvar.h"
61
62#define	BHND_PCI_ASSERT_QUIRK(_sc, _name)	\
63    KASSERT((_sc)->quirks & (_name), ("quirk " __STRING(_name) " not set"))
64
65#define	BHND_PCI_DEV(_core, _quirks)				\
66	BHND_DEVICE(_core, "", _quirks, BHND_DF_HOSTB)
67
68static const struct bhnd_device_quirk bhnd_pci_quirks[];
69static const struct bhnd_device_quirk bhnd_pcie_quirks[];
70
71static int	bhnd_pci_wars_early_once(struct bhnd_pcihb_softc *sc);
72static int	bhnd_pci_wars_hwup(struct bhnd_pcihb_softc *sc);
73static int	bhnd_pci_wars_hwdown(struct bhnd_pcihb_softc *sc);
74
75/*
76 * device/quirk tables
77 */
78static const struct bhnd_device bhnd_pci_devs[] = {
79	BHND_PCI_DEV(PCI,	bhnd_pci_quirks),
80	BHND_PCI_DEV(PCIE,	bhnd_pcie_quirks),
81	BHND_DEVICE_END
82};
83
84static const struct bhnd_device_quirk bhnd_pci_quirks[] = {
85	{ BHND_HWREV_ANY,	BHND_PCI_QUIRK_SBTOPCI2_PREF_BURST },
86	{ BHND_HWREV_GTE(11),	BHND_PCI_QUIRK_SBTOPCI2_READMULTI |
87				BHND_PCI_QUIRK_CLKRUN_DSBL },
88	BHND_DEVICE_QUIRK_END
89};
90
91static const struct bhnd_device_quirk bhnd_pcie_quirks[] = {
92	{ BHND_HWREV_EQ		(0),	BHND_PCIE_QUIRK_SDR9_L0s_HANG },
93	{ BHND_HWREV_RANGE	(0, 1),	BHND_PCIE_QUIRK_UR_STATUS_FIX },
94	{ BHND_HWREV_EQ		(1),	BHND_PCIE_QUIRK_PCIPM_REQEN },
95
96	{ BHND_HWREV_RANGE	(3, 5),	BHND_PCIE_QUIRK_ASPM_OVR |
97					BHND_PCIE_QUIRK_SDR9_POLARITY |
98					BHND_PCIE_QUIRK_SDR9_NO_FREQRETRY },
99
100	{ BHND_HWREV_LTE	(6),	BHND_PCIE_QUIRK_L1_IDLE_THRESH },
101	{ BHND_HWREV_GTE	(6),	BHND_PCIE_QUIRK_SPROM_L23_PCI_RESET },
102	{ BHND_HWREV_EQ		(7),	BHND_PCIE_QUIRK_SERDES_NOPLLDOWN },
103	{ BHND_HWREV_GTE	(8),	BHND_PCIE_QUIRK_L1_TIMER_PERF },
104	{ BHND_HWREV_GTE	(10),	BHND_PCIE_QUIRK_SD_C22_EXTADDR },
105	BHND_DEVICE_QUIRK_END
106};
107
108// Quirk handling TODO
109// WARs for the following are not yet implemented:
110// - BHND_PCIE_QUIRK_ASPM_OVR
111// - BHND_PCIE_QUIRK_SERDES_NOPLLDOWN
112// Quirks (and WARs) for the following are not yet defined:
113// - Power savings via MDIO BLK1/PWR_MGMT3 on PCIe hwrev 15-20, 21-22
114// - WOWL PME enable/disable
115// - 4360 PCIe SerDes Tx amplitude/deemphasis (vendor Apple, boards
116//   BCM94360X51P2, BCM94360X51A).
117// - PCI latency timer (boards CB2_4321_BOARD, CB2_4321_AG_BOARD)
118// - Max SerDes TX drive strength (vendor Apple, pcie >= rev10,
119//   board BCM94322X9)
120// - 700mV SerDes TX drive strength (chipid BCM4331, boards BCM94331X19,
121//   BCM94331X28, BCM94331X29B, BCM94331X19C)
122
123#define	BHND_PCI_SOFTC(_sc)	(&((_sc)->common))
124
125#define	BHND_PCI_READ_2(_sc, _reg)		\
126	bhnd_bus_read_2(BHND_PCI_SOFTC(_sc)->mem_res, (_reg))
127
128#define	BHND_PCI_READ_4(_sc, _reg)		\
129	bhnd_bus_read_4(BHND_PCI_SOFTC(_sc)->mem_res, (_reg))
130
131#define	BHND_PCI_WRITE_2(_sc, _reg, _val)	\
132	bhnd_bus_write_2(BHND_PCI_SOFTC(_sc)->mem_res, (_reg), (_val))
133
134#define	BHND_PCI_WRITE_4(_sc, _reg, _val)	\
135	bhnd_bus_write_4(BHND_PCI_SOFTC(_sc)->mem_res, (_reg), (_val))
136
137#define	BHND_PCI_PROTO_READ_4(_sc, _reg)	\
138	bhnd_pcie_read_proto_reg(BHND_PCI_SOFTC(_sc), (_reg))
139
140#define	BHND_PCI_PROTO_WRITE_4(_sc, _reg, _val)	\
141	bhnd_pcie_write_proto_reg(BHND_PCI_SOFTC(_sc), (_reg), (_val))
142
143#define	BHND_PCI_MDIO_READ(_sc, _phy, _reg)	\
144	bhnd_pcie_mdio_read(BHND_PCI_SOFTC(_sc), (_phy), (_reg))
145
146#define	BHND_PCI_MDIO_WRITE(_sc, _phy, _reg, _val)		\
147	bhnd_pcie_mdio_write(BHND_PCI_SOFTC(_sc), (_phy), (_reg), (_val))
148
149#define	BPCI_REG_SET(_regv, _attr, _val)	\
150	BHND_PCI_REG_SET((_regv), BHND_ ## _attr, (_val))
151
152#define	BPCI_REG_GET(_regv, _attr)	\
153	BHND_PCI_REG_GET((_regv), BHND_ ## _attr)
154
155#define	BPCI_CMN_REG_SET(_regv, _attr, _val)			\
156	BHND_PCI_CMN_REG_SET(BHND_PCI_SOFTC(_sc)->regfmt, (_regv),	\
157	    BHND_ ## _attr, (_val))
158
159#define	BPCI_CMN_REG_GET(_regv, _attr)				\
160	BHND_PCI_CMN_REG_GET(BHND_PCI_SOFTC(_sc)->regfmt, (_regv),	\
161	    BHND_ ## _attr)
162
163static int
164bhnd_pci_hostb_attach(device_t dev)
165{
166	struct bhnd_pcihb_softc	*sc;
167	int			 error;
168
169	sc = device_get_softc(dev);
170	sc->quirks = bhnd_device_quirks(dev, bhnd_pci_devs,
171	    sizeof(bhnd_pci_devs[0]));
172
173	if ((error = bhnd_pci_generic_attach(dev)))
174		return (error);
175
176	/* Apply early single-shot work-arounds */
177	if ((error = bhnd_pci_wars_early_once(sc))) {
178		bhnd_pci_generic_detach(dev);
179		return (error);
180	}
181
182	/* Apply attach/resume work-arounds */
183	if ((error = bhnd_pci_wars_hwup(sc))) {
184		bhnd_pci_generic_detach(dev);
185		return (error);
186	}
187
188
189	return (0);
190}
191
192static int
193bhnd_pci_hostb_detach(device_t dev)
194{
195	struct bhnd_pcihb_softc *sc;
196	int			 error;
197
198	sc = device_get_softc(dev);
199
200	/* Apply suspend/detach work-arounds */
201	if ((error = bhnd_pci_wars_hwdown(sc)))
202		return (error);
203
204	return (bhnd_pci_generic_detach(dev));
205}
206
207static int
208bhnd_pci_hostb_suspend(device_t dev)
209{
210	struct bhnd_pcihb_softc *sc;
211	int			 error;
212
213	sc = device_get_softc(dev);
214
215	/* Apply suspend/detach work-arounds */
216	if ((error = bhnd_pci_wars_hwdown(sc)))
217		return (error);
218
219	return (bhnd_pci_generic_suspend(dev));
220}
221
222static int
223bhnd_pci_hostb_resume(device_t dev)
224{
225	struct bhnd_pcihb_softc	*sc;
226	int			 error;
227
228	sc = device_get_softc(dev);
229
230	if ((error = bhnd_pci_generic_resume(dev)))
231		return (error);
232
233	/* Apply attach/resume work-arounds */
234	if ((error = bhnd_pci_wars_hwup(sc))) {
235		bhnd_pci_generic_detach(dev);
236		return (error);
237	}
238
239	return (0);
240}
241
242/**
243 * Apply any hardware work-arounds that must be executed exactly once, early in
244 * the attach process.
245 *
246 * This must be called after core enumeration and discovery of all applicable
247 * quirks, but prior to probe/attach of any cores, parsing of
248 * SPROM, etc.
249 */
250static int
251bhnd_pci_wars_early_once(struct bhnd_pcihb_softc *sc)
252{
253	/* Determine correct polarity by observing the attach-time PCIe PHY
254	 * link status. This is used later to reset/force the SerDes
255	 * polarity */
256	if (sc->quirks & BHND_PCIE_QUIRK_SDR9_POLARITY) {
257		uint32_t st;
258		bool inv;
259
260
261		st = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_PLP_STATUSREG);
262		inv = ((st & BHND_PCIE_PLP_POLARITY_INV) != 0);
263		sc->sdr9_quirk_polarity.inv = inv;
264	}
265
266	return (0);
267}
268
269/**
270 * Apply any hardware workarounds that are required upon attach or resume
271 * of the bridge device.
272 */
273static int
274bhnd_pci_wars_hwup(struct bhnd_pcihb_softc *sc)
275{
276	/* Note that the order here matters; these work-arounds
277	 * should not be re-ordered without careful review of their
278	 * interdependencies */
279
280	/* Enable PCI prefetch/burst/readmulti flags */
281	if (sc->quirks & BHND_PCI_QUIRK_SBTOPCI2_PREF_BURST ||
282	    sc->quirks & BHND_PCI_QUIRK_SBTOPCI2_READMULTI)
283	{
284		uint32_t sbp2;
285		sbp2 = BHND_PCI_READ_4(sc, BHND_PCI_SBTOPCI2);
286
287		if (sc->quirks & BHND_PCI_QUIRK_SBTOPCI2_PREF_BURST)
288			sbp2 |= (BHND_PCI_SBTOPCI_PREF|BHND_PCI_SBTOPCI_BURST);
289
290		if (sc->quirks & BHND_PCI_QUIRK_SBTOPCI2_READMULTI)
291			sbp2 |= BHND_PCI_SBTOPCI_RC_READMULTI;
292
293		BHND_PCI_WRITE_4(sc, BHND_PCI_SBTOPCI2, sbp2);
294	}
295
296	/* Disable PCI CLKRUN# */
297	if (sc->quirks & BHND_PCI_QUIRK_CLKRUN_DSBL) {
298		uint32_t ctl;
299
300		ctl = BHND_PCI_READ_4(sc, BHND_PCI_CLKRUN_CTL);
301		ctl |= BHND_PCI_CLKRUN_DSBL;
302		BHND_PCI_WRITE_4(sc, BHND_PCI_CLKRUN_CTL, ctl);
303	}
304
305	/* Enable TLP unmatched address handling work-around */
306	if (sc->quirks & BHND_PCIE_QUIRK_UR_STATUS_FIX) {
307		uint32_t wrs;
308		wrs = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_TLP_WORKAROUNDSREG);
309		wrs |= BHND_PCIE_TLP_WORKAROUND_URBIT;
310		BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_TLP_WORKAROUNDSREG, wrs);
311	}
312
313	/* Adjust SerDes CDR tuning to ensure that CDR is stable before sending
314	 * data during L0s to L0 exit transitions. */
315	if (sc->quirks & BHND_PCIE_QUIRK_SDR9_L0s_HANG) {
316		uint16_t sdv;
317
318		/* Set RX track/acquire timers to 2.064us/40.96us */
319		sdv = BPCI_REG_SET(0, PCIE_SDR9_RX_TIMER1_LKTRK, (2064/16));
320		sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_TIMER1_LKACQ,
321		    (40960/1024));
322		BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_TXRX,
323		    BHND_PCIE_SDR9_RX_TIMER1, sdv);
324
325		/* Apply CDR frequency workaround */
326		sdv = BHND_PCIE_SDR9_RX_CDR_FREQ_OVR_EN;
327		sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDR_FREQ_OVR, 0x0);
328		BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_TXRX,
329		    BHND_PCIE_SDR9_RX_CDR, sdv);
330
331		/* Apply CDR BW tunings */
332		sdv = 0;
333		sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDRBW_INTGTRK, 0x2);
334		sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDRBW_INTGACQ, 0x4);
335		sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDRBW_PROPTRK, 0x6);
336		sdv = BPCI_REG_SET(sdv, PCIE_SDR9_RX_CDRBW_PROPACQ, 0x6);
337		BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_TXRX,
338		    BHND_PCIE_SDR9_RX_CDRBW, sdv);
339	}
340
341	/* Force correct SerDes polarity */
342	if (sc->quirks & BHND_PCIE_QUIRK_SDR9_POLARITY) {
343		uint16_t	rxctl;
344
345		rxctl = BHND_PCI_MDIO_READ(sc, BHND_PCIE_PHY_SDR9_TXRX,
346		    BHND_PCIE_SDR9_RX_CTRL);
347
348		rxctl |= BHND_PCIE_SDR9_RX_CTRL_FORCE;
349		if (sc->sdr9_quirk_polarity.inv)
350			rxctl |= BHND_PCIE_SDR9_RX_CTRL_POLARITY_INV;
351		else
352			rxctl &= ~BHND_PCIE_SDR9_RX_CTRL_POLARITY_INV;
353
354		BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_TXRX,
355		    BHND_PCIE_SDR9_RX_CTRL, rxctl);
356	}
357
358	/* Disable startup retry on PLL frequency detection failure */
359	if (sc->quirks & BHND_PCIE_QUIRK_SDR9_NO_FREQRETRY) {
360		uint16_t	pctl;
361
362		pctl = BHND_PCI_MDIO_READ(sc, BHND_PCIE_PHY_SDR9_PLL,
363		    BHND_PCIE_SDR9_PLL_CTRL);
364
365		pctl &= ~BHND_PCIE_SDR9_PLL_CTRL_FREQDET_EN;
366		BHND_PCI_MDIO_WRITE(sc, BHND_PCIE_PHY_SDR9_PLL,
367		    BHND_PCIE_SDR9_PLL_CTRL, pctl);
368	}
369
370	/* Explicitly enable PCI-PM */
371	if (sc->quirks & BHND_PCIE_QUIRK_PCIPM_REQEN) {
372		uint32_t lcreg;
373		lcreg = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_DLLP_LCREG);
374		lcreg |= BHND_PCIE_DLLP_LCREG_PCIPM_EN;
375		BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_DLLP_LCREG, lcreg);
376	}
377
378	/* Adjust L1 timer to fix slow L1->L0 transitions */
379	if (sc->quirks & BHND_PCIE_QUIRK_L1_IDLE_THRESH) {
380		uint32_t pmt;
381		pmt = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_DLLP_PMTHRESHREG);
382		pmt = BPCI_REG_SET(pmt, PCIE_L1THRESHOLDTIME,
383		    BHND_PCIE_L1THRESHOLD_WARVAL);
384		BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt);
385	}
386
387	/* Extend L1 timer for better performance.
388	 * TODO: We could enable/disable this on demand for better power
389	 * savings if we tie this to HT clock request handling */
390	if (sc->quirks & BHND_PCIE_QUIRK_L1_TIMER_PERF) {
391		uint32_t pmt;
392		pmt = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_DLLP_PMTHRESHREG);
393		pmt |= BHND_PCIE_ASPMTIMER_EXTEND;
394		BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt);
395	}
396
397	/* Enable L23READY_EXIT_NOPRST if not already set in SPROM. */
398	if (sc->quirks & BHND_PCIE_QUIRK_SPROM_L23_PCI_RESET) {
399		bus_size_t	reg;
400		uint16_t	cfg;
401
402		/* Fetch the misc cfg flags from SPROM */
403		reg = BHND_PCIE_SPROM_SHADOW + BHND_PCIE_SRSH_PCIE_MISC_CONFIG;
404		cfg = BHND_PCI_READ_2(sc, reg);
405
406		/* Write EXIT_NOPRST flag if not already set in SPROM */
407		if (!(cfg & BHND_PCIE_SRSH_L23READY_EXIT_NOPRST)) {
408			cfg |= BHND_PCIE_SRSH_L23READY_EXIT_NOPRST;
409			BHND_PCI_WRITE_2(sc, reg, cfg);
410		}
411	}
412
413	return (0);
414}
415
416/**
417 * Apply any hardware workarounds that are required upon detach or suspend
418 * of the bridge device.
419 */
420static int
421bhnd_pci_wars_hwdown(struct bhnd_pcihb_softc *sc)
422{
423	/* Reduce L1 timer for better power savings.
424	 * TODO: We could enable/disable this on demand for better power
425	 * savings if we tie this to HT clock request handling */
426	if (sc->quirks & BHND_PCIE_QUIRK_L1_TIMER_PERF) {
427		uint32_t pmt;
428		pmt = BHND_PCI_PROTO_READ_4(sc, BHND_PCIE_DLLP_PMTHRESHREG);
429		pmt &= ~BHND_PCIE_ASPMTIMER_EXTEND;
430		BHND_PCI_PROTO_WRITE_4(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt);
431	}
432
433	return (0);
434}
435
436static device_method_t bhnd_pci_hostb_methods[] = {
437	/* Device interface */
438	DEVMETHOD(device_attach,		bhnd_pci_hostb_attach),
439	DEVMETHOD(device_detach,		bhnd_pci_hostb_detach),
440	DEVMETHOD(device_suspend,		bhnd_pci_hostb_suspend),
441	DEVMETHOD(device_resume,		bhnd_pci_hostb_resume),
442
443	DEVMETHOD_END
444};
445
446DEFINE_CLASS_1(bhnd_pci_hostb, bhnd_pci_hostb_driver, bhnd_pci_hostb_methods,
447    sizeof(struct bhnd_pcihb_softc), bhnd_pci_driver);
448
449DRIVER_MODULE(bhnd_hostb, bhnd, bhnd_pci_hostb_driver, bhnd_hostb_devclass, 0, 0);
450
451MODULE_VERSION(bhnd_pci_hostb, 1);
452MODULE_DEPEND(bhnd_pci_hostb, bhnd_pci, 1, 1, 1);
453