xref: /freebsd/sys/dev/bhnd/bhndb/bhndb_pci.c (revision 148ed571)
1 /*-
2  * Copyright (c) 2015-2016 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  * PCI-specific implementation for the BHNDB bridge driver.
35  *
36  * Provides support for bridging from a PCI parent bus to a BHND-compatible
37  * bus (e.g. bcma or siba) via a Broadcom PCI core configured in end-point
38  * mode.
39  *
40  * This driver handles all host-level PCI interactions with a PCI/PCIe bridge
41  * core operating in endpoint mode. On the bridged bhnd bus, the PCI core
42  * device will be managed by a bhnd_pci_hostb driver.
43  */
44 
45 #include <sys/param.h>
46 #include <sys/kernel.h>
47 #include <sys/bus.h>
48 #include <sys/limits.h>
49 #include <sys/malloc.h>
50 #include <sys/module.h>
51 #include <sys/systm.h>
52 
53 #include <dev/pci/pcireg.h>
54 #include <dev/pci/pcivar.h>
55 
56 #include <dev/bhnd/bhnd.h>
57 
58 #include <dev/bhnd/cores/pci/bhnd_pcireg.h>
59 
60 #include "bhndb_pcireg.h"
61 #include "bhndb_pcivar.h"
62 #include "bhndb_private.h"
63 
64 static int	bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc);
65 static int	bhndb_disable_pci_clocks(struct bhndb_pci_softc *sc);
66 
67 static int	bhndb_pci_compat_setregwin(struct bhndb_pci_softc *,
68 		    const struct bhndb_regwin *, bhnd_addr_t);
69 static int	bhndb_pci_fast_setregwin(struct bhndb_pci_softc *,
70 		    const struct bhndb_regwin *, bhnd_addr_t);
71 
72 static void	bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc);
73 
74 /**
75  * Default bhndb_pci implementation of device_probe().
76  *
77  * Verifies that the parent is a PCI/PCIe device.
78  */
79 static int
80 bhndb_pci_probe(device_t dev)
81 {
82 	device_t	parent;
83 	devclass_t	parent_bus;
84 	devclass_t	pci;
85 
86 	/* Our parent must be a PCI/PCIe device. */
87 	pci = devclass_find("pci");
88 	parent = device_get_parent(dev);
89 	parent_bus = device_get_devclass(device_get_parent(parent));
90 
91 	if (parent_bus != pci)
92 		return (ENXIO);
93 
94 	device_set_desc(dev, "PCI-BHND bridge");
95 
96 	return (BUS_PROBE_DEFAULT);
97 }
98 
99 static int
100 bhndb_pci_attach(device_t dev)
101 {
102 	struct bhndb_pci_softc	*sc;
103 	int			 error, reg;
104 
105 	sc = device_get_softc(dev);
106 	sc->dev = dev;
107 
108 	/* Enable PCI bus mastering */
109 	pci_enable_busmaster(device_get_parent(dev));
110 
111 	/* Determine our bridge device class */
112 	sc->pci_devclass = BHND_DEVCLASS_PCI;
113 	if (pci_find_cap(device_get_parent(dev), PCIY_EXPRESS, &reg) == 0)
114 		sc->pci_devclass = BHND_DEVCLASS_PCIE;
115 
116 	/* Enable clocks (if supported by this hardware) */
117 	if ((error = bhndb_enable_pci_clocks(sc)))
118 		return (error);
119 
120 	/* Use siba(4)-compatible regwin handling until we know
121 	 * what kind of bus is attached */
122 	sc->set_regwin = bhndb_pci_compat_setregwin;
123 
124 	/* Perform full bridge attach. This should call back into our
125 	 * bhndb_pci_init_full_config() implementation once the bridged
126 	 * bhnd(4) bus has been enumerated, but before any devices have been
127 	 * probed or attached. */
128 	if ((error = bhndb_attach(dev, sc->pci_devclass)))
129 		return (error);
130 
131 	/* If supported, switch to the faster regwin handling */
132 	if (sc->bhndb.chipid.chip_type != BHND_CHIPTYPE_SIBA) {
133 		atomic_store_rel_ptr((volatile void *) &sc->set_regwin,
134 		    (uintptr_t) &bhndb_pci_fast_setregwin);
135 	}
136 
137 	return (0);
138 }
139 
140 static int
141 bhndb_pci_init_full_config(device_t dev, device_t child,
142     const struct bhndb_hw_priority *hw_prio_table)
143 {
144 	struct bhndb_pci_softc	*sc;
145 	int			 error;
146 
147 	sc = device_get_softc(dev);
148 
149 	/* Let our parent perform standard initialization first */
150 	if ((error = bhndb_generic_init_full_config(dev, child, hw_prio_table)))
151 		return (error);
152 
153 	/* Fix-up power on defaults for SROM-less devices. */
154 	bhndb_init_sromless_pci_config(sc);
155 
156 	return (0);
157 }
158 
159 /*
160  * On devices without a SROM, the PCI(e) cores will be initialized with
161  * their Power-on-Reset defaults; this can leave two of the BAR0 PCI windows
162  * mapped to the wrong core.
163  *
164  * This function updates the SROM shadow to point the BAR0 windows at the
165  * current PCI core.
166  *
167  * Applies to all PCI/PCIe revisions.
168  */
169 static void
170 bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc)
171 {
172 	struct bhndb_resources		*bres;
173 	const struct bhndb_hwcfg	*cfg;
174 	const struct bhndb_regwin	*win;
175 	struct resource			*core_regs;
176 	bus_size_t			 srom_offset;
177 	u_int				 pci_cidx, sprom_cidx;
178 	uint16_t			 val;
179 
180 	bres = sc->bhndb.bus_res;
181 	cfg = bres->cfg;
182 
183 	if (bhnd_get_vendor(sc->bhndb.hostb_dev) != BHND_MFGID_BCM)
184 		return;
185 
186 	switch (bhnd_get_device(sc->bhndb.hostb_dev)) {
187 	case BHND_COREID_PCI:
188 		srom_offset = BHND_PCI_SRSH_PI_OFFSET;
189 		break;
190 	case BHND_COREID_PCIE:
191 		srom_offset = BHND_PCIE_SRSH_PI_OFFSET;
192 		break;
193 	default:
194 		device_printf(sc->dev, "unsupported PCI host bridge device\n");
195 		return;
196 	}
197 
198 	/* Locate the static register window mapping the PCI core */
199 	win = bhndb_regwin_find_core(cfg->register_windows, sc->pci_devclass,
200 	    0, BHND_PORT_DEVICE, 0, 0);
201 	if (win == NULL) {
202 		device_printf(sc->dev, "missing PCI core register window\n");
203 		return;
204 	}
205 
206 	/* Fetch the resource containing the register window */
207 	core_regs = bhndb_find_regwin_resource(bres, win);
208 	if (core_regs == NULL) {
209 		device_printf(sc->dev, "missing PCI core register resource\n");
210 		return;
211 	}
212 
213 	/* Fetch the SPROM's configured core index */
214 	val = bus_read_2(core_regs, win->win_offset + srom_offset);
215 	sprom_cidx = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT;
216 
217 	/* If it doesn't match host bridge's core index, update the index
218 	 * value */
219 	pci_cidx = bhnd_get_core_index(sc->bhndb.hostb_dev);
220 	if (sprom_cidx != pci_cidx) {
221 		val &= ~BHND_PCI_SRSH_PI_MASK;
222 		val |= (pci_cidx << BHND_PCI_SRSH_PI_SHIFT);
223 		bus_write_2(core_regs,
224 		    win->win_offset + srom_offset, val);
225 	}
226 }
227 
228 static int
229 bhndb_pci_resume(device_t dev)
230 {
231 	struct bhndb_pci_softc	*sc;
232 	int			 error;
233 
234 	sc = device_get_softc(dev);
235 
236 	/* Enable clocks (if supported by this hardware) */
237 	if ((error = bhndb_enable_pci_clocks(sc)))
238 		return (error);
239 
240 	/* Perform resume */
241 	return (bhndb_generic_resume(dev));
242 }
243 
244 static int
245 bhndb_pci_suspend(device_t dev)
246 {
247 	struct bhndb_pci_softc	*sc;
248 	int			 error;
249 
250 	sc = device_get_softc(dev);
251 
252 	/* Disable clocks (if supported by this hardware) */
253 	if ((error = bhndb_disable_pci_clocks(sc)))
254 		return (error);
255 
256 	/* Perform suspend */
257 	return (bhndb_generic_suspend(dev));
258 }
259 
260 static int
261 bhndb_pci_detach(device_t dev)
262 {
263 	struct bhndb_pci_softc	*sc;
264 	int			 error;
265 
266 	sc = device_get_softc(dev);
267 
268 	/* Disable clocks (if supported by this hardware) */
269 	if ((error = bhndb_disable_pci_clocks(sc)))
270 		return (error);
271 
272 	/* Perform detach */
273 	if ((error = bhndb_generic_detach(dev)))
274 		return (error);
275 
276 	/* Disable PCI bus mastering */
277 	pci_disable_busmaster(device_get_parent(dev));
278 
279 	return (0);
280 }
281 
282 static int
283 bhndb_pci_set_window_addr(device_t dev, const struct bhndb_regwin *rw,
284     bhnd_addr_t addr)
285 {
286 	struct bhndb_pci_softc *sc = device_get_softc(dev);
287 	return (sc->set_regwin(sc, rw, addr));
288 }
289 
290 /**
291  * A siba(4) and bcma(4)-compatible bhndb_set_window_addr implementation.
292  *
293  * On siba(4) devices, it's possible that writing a PCI window register may
294  * not succeed; it's necessary to immediately read the configuration register
295  * and retry if not set to the desired value.
296  *
297  * This is not necessary on bcma(4) devices, but other than the overhead of
298  * validating the register, there's no harm in performing the verification.
299  */
300 static int
301 bhndb_pci_compat_setregwin(struct bhndb_pci_softc *sc,
302     const struct bhndb_regwin *rw, bhnd_addr_t addr)
303 {
304 	device_t	parent;
305 	int		error;
306 
307 	parent = sc->bhndb.parent_dev;
308 
309 	if (rw->win_type != BHNDB_REGWIN_T_DYN)
310 		return (ENODEV);
311 
312 	for (u_int i = 0; i < BHNDB_PCI_BARCTRL_WRITE_RETRY; i++) {
313 		if ((error = bhndb_pci_fast_setregwin(sc, rw, addr)))
314 			return (error);
315 
316 		if (pci_read_config(parent, rw->dyn.cfg_offset, 4) == addr)
317 			return (0);
318 
319 		DELAY(10);
320 	}
321 
322 	/* Unable to set window */
323 	return (ENODEV);
324 }
325 
326 /**
327  * A bcma(4)-only bhndb_set_window_addr implementation.
328  */
329 static int
330 bhndb_pci_fast_setregwin(struct bhndb_pci_softc *sc,
331     const struct bhndb_regwin *rw, bhnd_addr_t addr)
332 {
333 	device_t parent = sc->bhndb.parent_dev;
334 
335 	/* The PCI bridge core only supports 32-bit addressing, regardless
336 	 * of the bus' support for 64-bit addressing */
337 	if (addr > UINT32_MAX)
338 		return (ERANGE);
339 
340 	switch (rw->win_type) {
341 	case BHNDB_REGWIN_T_DYN:
342 		/* Addresses must be page aligned */
343 		if (addr % rw->win_size != 0)
344 			return (EINVAL);
345 
346 		pci_write_config(parent, rw->dyn.cfg_offset, addr, 4);
347 		break;
348 	default:
349 		return (ENODEV);
350 	}
351 
352 	return (0);
353 }
354 
355 /**
356  * Enable externally managed clocks, if required.
357  *
358  * Some PCI chipsets (BCM4306, possibly others) chips do not support
359  * the idle low-power clock. Clocking must be bootstrapped at
360  * attach/resume by directly adjusting GPIO registers exposed in the
361  * PCI config space, and correspondingly, explicitly shutdown at
362  * detach/suspend.
363  *
364  * @param sc Bridge driver state.
365  */
366 static int
367 bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc)
368 {
369 	device_t		pci_parent;
370 	uint32_t		gpio_in, gpio_out, gpio_en;
371 	uint32_t		gpio_flags;
372 	uint16_t		pci_status;
373 
374 	/* Only supported and required on PCI devices */
375 	if (sc->pci_devclass != BHND_DEVCLASS_PCI)
376 		return (0);
377 
378 	pci_parent = device_get_parent(sc->dev);
379 
380 	/* Read state of XTAL pin */
381 	gpio_in = pci_read_config(pci_parent, BHNDB_PCI_GPIO_IN, 4);
382 	if (gpio_in & BHNDB_PCI_GPIO_XTAL_ON)
383 		return (0); /* already enabled */
384 
385 	/* Fetch current config */
386 	gpio_out = pci_read_config(pci_parent, BHNDB_PCI_GPIO_OUT, 4);
387 	gpio_en = pci_read_config(pci_parent, BHNDB_PCI_GPIO_OUTEN, 4);
388 
389 	/* Set PLL_OFF/XTAL_ON pins to HIGH and enable both pins */
390 	gpio_flags = (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON);
391 	gpio_out |= gpio_flags;
392 	gpio_en |= gpio_flags;
393 
394 	pci_write_config(pci_parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
395 	pci_write_config(pci_parent, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4);
396 	DELAY(1000);
397 
398 	/* Reset PLL_OFF */
399 	gpio_out &= ~BHNDB_PCI_GPIO_PLL_OFF;
400 	pci_write_config(pci_parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
401 	DELAY(5000);
402 
403 	/* Clear any PCI 'sent target-abort' flag. */
404 	pci_status = pci_read_config(pci_parent, PCIR_STATUS, 2);
405 	pci_status &= ~PCIM_STATUS_STABORT;
406 	pci_write_config(pci_parent, PCIR_STATUS, pci_status, 2);
407 
408 	return (0);
409 }
410 
411 /**
412  * Disable externally managed clocks, if required.
413  *
414  * @param sc Bridge driver state.
415  */
416 static int
417 bhndb_disable_pci_clocks(struct bhndb_pci_softc *sc)
418 {
419 	device_t	parent_dev;
420 	uint32_t	gpio_out, gpio_en;
421 
422 	/* Only supported and required on PCI devices */
423 	if (sc->pci_devclass != BHND_DEVCLASS_PCI)
424 		return (0);
425 
426 	parent_dev = device_get_parent(sc->dev);
427 
428 	// TODO: Check board flags for BFL2_XTALBUFOUTEN?
429 	// TODO: Check PCI core revision?
430 	// TODO: Switch to 'slow' clock?
431 
432 	/* Fetch current config */
433 	gpio_out = pci_read_config(parent_dev, BHNDB_PCI_GPIO_OUT, 4);
434 	gpio_en = pci_read_config(parent_dev, BHNDB_PCI_GPIO_OUTEN, 4);
435 
436 	/* Set PLL_OFF to HIGH, XTAL_ON to LOW. */
437 	gpio_out &= ~BHNDB_PCI_GPIO_XTAL_ON;
438 	gpio_out |= BHNDB_PCI_GPIO_PLL_OFF;
439 	pci_write_config(parent_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
440 
441 	/* Enable both output pins */
442 	gpio_en |= (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON);
443 	pci_write_config(parent_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4);
444 
445 	return (0);
446 }
447 
448 static device_method_t bhndb_pci_methods[] = {
449 	/* Device interface */
450 	DEVMETHOD(device_probe,			bhndb_pci_probe),
451 	DEVMETHOD(device_attach,		bhndb_pci_attach),
452 	DEVMETHOD(device_resume,		bhndb_pci_resume),
453 	DEVMETHOD(device_suspend,		bhndb_pci_suspend),
454 	DEVMETHOD(device_detach,		bhndb_pci_detach),
455 
456 	/* BHNDB interface */
457 	DEVMETHOD(bhndb_init_full_config,	bhndb_pci_init_full_config),
458 	DEVMETHOD(bhndb_set_window_addr,	bhndb_pci_set_window_addr),
459 
460 	DEVMETHOD_END
461 };
462 
463 DEFINE_CLASS_1(bhndb, bhndb_pci_driver, bhndb_pci_methods,
464     sizeof(struct bhndb_pci_softc), bhndb_driver);
465 
466 MODULE_VERSION(bhndb_pci, 1);
467 MODULE_DEPEND(bhndb_pci, bhnd_pci_hostb, 1, 1, 1);
468 MODULE_DEPEND(bhndb_pci, pci, 1, 1, 1);
469 MODULE_DEPEND(bhndb_pci, bhndb, 1, 1, 1);
470 MODULE_DEPEND(bhndb_pci, bhnd, 1, 1, 1);
471