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$");
32 
33 /*
34  * Broadcom BHND PCIe-Gen2 PCI-Host Bridge.
35  *
36  * This driver handles all interactions with PCIe-G2 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 // TODO
44 //
45 // A full survey of known quirks/work-arounds has not been completed.
46 //
47 // Work-arounds for the following are not yet implemented:
48 // - BHND_PCIE2_QUIRK_SERDES_TXDRV_DEEMPH
49 //   4360 PCIe SerDes Tx amplitude/deemphasis (vendor Apple, boards
50 //   BCM94360X51P2, BCM94360X51A)
51 
52 #include <sys/param.h>
53 #include <sys/kernel.h>
54 
55 #include <sys/malloc.h>
56 
57 #include <sys/bus.h>
58 #include <sys/module.h>
59 
60 #include <sys/systm.h>
61 
62 #include <machine/bus.h>
63 #include <sys/rman.h>
64 #include <machine/resource.h>
65 
66 #include <dev/bhnd/bhnd.h>
67 
68 #include <dev/pci/pcireg.h>
69 #include <dev/pci/pcivar.h>
70 
71 #include "bhnd_pcie2_reg.h"
72 #include "bhnd_pcie2_hostbvar.h"
73 
74 static const struct bhnd_device_quirk bhnd_pcie2_quirks[];
75 
76 
77 static int	bhnd_pcie2_wars_early_once(struct bhnd_pcie2hb_softc *sc);
78 static int	bhnd_pcie2_wars_hwup(struct bhnd_pcie2hb_softc *sc);
79 static int	bhnd_pcie2_wars_hwdown(struct bhnd_pcie2hb_softc *sc);
80 
81 /*
82  * device/quirk tables
83  */
84 
85 #define	BHND_PCI_DEV(_core, _quirks)		\
86 	BHND_DEVICE(BCM, _core, NULL, _quirks, BHND_DF_HOSTB)
87 
88 static const struct bhnd_device bhnd_pcie2_devs[] = {
89 	BHND_PCI_DEV(PCIE2,	bhnd_pcie2_quirks),
90 	BHND_DEVICE_END
91 };
92 
93 static const struct bhnd_device_quirk bhnd_pcie2_quirks[] = {
94 	/* Apple BCM4360 boards that require adjusting TX amplitude and
95 	 * differential output de-emphasis of the PCIe SerDes */
96 	{{ BHND_MATCH_BOARD(PCI_VENDOR_APPLE, BCM94360X51P2), },
97 		BHND_PCIE2_QUIRK_SERDES_TXDRV_DEEMPH },
98 	{{ BHND_MATCH_BOARD(PCI_VENDOR_APPLE, BCM94360X51A), },
99 		BHND_PCIE2_QUIRK_SERDES_TXDRV_DEEMPH },
100 
101 	BHND_DEVICE_QUIRK_END
102 };
103 
104 static int
105 bhnd_pcie2_hostb_attach(device_t dev)
106 {
107 	struct bhnd_pcie2hb_softc	*sc;
108 	int				 error;
109 
110 	sc = device_get_softc(dev);
111 	sc->dev = dev;
112 	sc->quirks = bhnd_device_quirks(dev, bhnd_pcie2_devs,
113 	    sizeof(bhnd_pcie2_devs[0]));
114 
115 	/* Find the host PCI bridge device */
116 	sc->pci_dev = bhnd_find_bridge_root(dev, devclass_find("pci"));
117 	if (sc->pci_dev == NULL) {
118 		device_printf(dev, "parent pci bridge device not found\n");
119 		return (ENXIO);
120 	}
121 
122 	/* Common setup */
123 	if ((error = bhnd_pcie2_generic_attach(dev)))
124 		return (error);
125 
126 
127 	/* Apply early single-shot work-arounds */
128 	if ((error = bhnd_pcie2_wars_early_once(sc)))
129 		goto failed;
130 
131 
132 	/* Apply attach/resume work-arounds */
133 	if ((error = bhnd_pcie2_wars_hwup(sc)))
134 		goto failed;
135 
136 
137 	return (0);
138 
139 failed:
140 	bhnd_pcie2_generic_detach(dev);
141 	return (error);
142 }
143 
144 static int
145 bhnd_pcie2_hostb_detach(device_t dev)
146 {
147 	struct bhnd_pcie2hb_softc	*sc;
148 	int				 error;
149 
150 	sc = device_get_softc(dev);
151 
152 	/* Apply suspend/detach work-arounds */
153 	if ((error = bhnd_pcie2_wars_hwdown(sc)))
154 		return (error);
155 
156 	return (bhnd_pcie2_generic_detach(dev));
157 }
158 
159 static int
160 bhnd_pcie2_hostb_suspend(device_t dev)
161 {
162 	struct bhnd_pcie2hb_softc	*sc;
163 	int				 error;
164 
165 	sc = device_get_softc(dev);
166 
167 	/* Apply suspend/detach work-arounds */
168 	if ((error = bhnd_pcie2_wars_hwdown(sc)))
169 		return (error);
170 
171 	return (bhnd_pcie2_generic_suspend(dev));
172 }
173 
174 static int
175 bhnd_pcie2_hostb_resume(device_t dev)
176 {
177 	struct bhnd_pcie2hb_softc	*sc;
178 	int				 error;
179 
180 	sc = device_get_softc(dev);
181 
182 	if ((error = bhnd_pcie2_generic_resume(dev)))
183 		return (error);
184 
185 	/* Apply attach/resume work-arounds */
186 	if ((error = bhnd_pcie2_wars_hwup(sc))) {
187 		bhnd_pcie2_generic_detach(dev);
188 		return (error);
189 	}
190 
191 	return (0);
192 }
193 
194 /**
195  * Apply any hardware work-arounds that must be executed exactly once, early in
196  * the attach process.
197  *
198  * This must be called after core enumeration and discovery of all applicable
199  * quirks, but prior to probe/attach of any cores, parsing of
200  * SPROM, etc.
201  */
202 static int
203 bhnd_pcie2_wars_early_once(struct bhnd_pcie2hb_softc *sc)
204 {
205 	// TODO
206 	return (ENXIO);
207 }
208 
209 /**
210  * Apply any hardware workarounds that are required upon attach or resume
211  * of the bridge device.
212  */
213 static int
214 bhnd_pcie2_wars_hwup(struct bhnd_pcie2hb_softc *sc)
215 {
216 	// TODO
217 	return (ENXIO);
218 }
219 
220 /**
221  * Apply any hardware workarounds that are required upon detach or suspend
222  * of the bridge device.
223  */
224 static int
225 bhnd_pcie2_wars_hwdown(struct bhnd_pcie2hb_softc *sc)
226 {
227 	// TODO
228 	return (ENXIO);
229 }
230 
231 static device_method_t bhnd_pcie2_hostb_methods[] = {
232 	/* Device interface */
233 	DEVMETHOD(device_attach,		bhnd_pcie2_hostb_attach),
234 	DEVMETHOD(device_detach,		bhnd_pcie2_hostb_detach),
235 	DEVMETHOD(device_suspend,		bhnd_pcie2_hostb_suspend),
236 	DEVMETHOD(device_resume,		bhnd_pcie2_hostb_resume),
237 
238 	DEVMETHOD_END
239 };
240 
241 DEFINE_CLASS_1(bhnd_hostb, bhnd_pcie2_hostb_driver,
242     bhnd_pcie2_hostb_methods, sizeof(struct bhnd_pcie2hb_softc),
243     bhnd_pcie2_driver);
244 
245 DRIVER_MODULE(bhnd_pcie2_hostb, bhnd, bhnd_pcie2_hostb_driver, bhnd_hostb_devclass, 0, 0);
246 
247 MODULE_VERSION(bhnd_pcie2_hostb, 1);
248 MODULE_DEPEND(bhnd_pcie2_hostb, bhnd, 1, 1, 1);
249 MODULE_DEPEND(bhnd_pcie2_hostb, bhnd_pcie2, 1, 1, 1);
250