xref: /freebsd/sys/dev/bhnd/bhndb/bhndb_pci.c (revision 079171874c9bf263b69e3af10784ad2bcd1fe699)
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(
73 			    struct bhndb_pci_softc *sc);
74 
75 static bus_addr_t	bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc);
76 static bus_size_t	bhndb_pci_sprom_size(struct bhndb_pci_softc *sc);
77 
78 /**
79  * Default bhndb_pci implementation of device_probe().
80  *
81  * Verifies that the parent is a PCI/PCIe device.
82  */
83 static int
84 bhndb_pci_probe(device_t dev)
85 {
86 	device_t	parent;
87 	devclass_t	parent_bus;
88 	devclass_t	pci;
89 
90 	/* Our parent must be a PCI/PCIe device. */
91 	pci = devclass_find("pci");
92 	parent = device_get_parent(dev);
93 	parent_bus = device_get_devclass(device_get_parent(parent));
94 
95 	if (parent_bus != pci)
96 		return (ENXIO);
97 
98 	device_set_desc(dev, "PCI-BHND bridge");
99 
100 	return (BUS_PROBE_DEFAULT);
101 }
102 
103 static int
104 bhndb_pci_attach(device_t dev)
105 {
106 	struct bhndb_pci_softc	*sc;
107 	int			 error, reg;
108 
109 	sc = device_get_softc(dev);
110 	sc->dev = dev;
111 	sc->parent = device_get_parent(dev);
112 
113 	/* Enable PCI bus mastering */
114 	pci_enable_busmaster(sc->parent);
115 
116 	/* Determine our bridge device class */
117 	sc->pci_devclass = BHND_DEVCLASS_PCI;
118 	if (pci_find_cap(sc->parent, PCIY_EXPRESS, &reg) == 0)
119 		sc->pci_devclass = BHND_DEVCLASS_PCIE;
120 
121 	/* Enable clocks (if supported by this hardware) */
122 	if ((error = bhndb_enable_pci_clocks(sc)))
123 		return (error);
124 
125 	/* Use siba(4)-compatible regwin handling until we know
126 	 * what kind of bus is attached */
127 	sc->set_regwin = bhndb_pci_compat_setregwin;
128 
129 	/* Perform full bridge attach. This should call back into our
130 	 * bhndb_pci_init_full_config() implementation once the bridged
131 	 * bhnd(4) bus has been enumerated, but before any devices have been
132 	 * probed or attached. */
133 	if ((error = bhndb_attach(dev, sc->pci_devclass)))
134 		return (error);
135 
136 	/* If supported, switch to the faster regwin handling */
137 	if (sc->bhndb.chipid.chip_type != BHND_CHIPTYPE_SIBA) {
138 		atomic_store_rel_ptr((volatile void *) &sc->set_regwin,
139 		    (uintptr_t) &bhndb_pci_fast_setregwin);
140 	}
141 
142 	return (0);
143 }
144 
145 static int
146 bhndb_pci_init_full_config(device_t dev, device_t child,
147     const struct bhndb_hw_priority *hw_prio_table)
148 {
149 	struct bhndb_pci_softc	*sc;
150 	device_t		 nv_dev;
151 	bus_size_t		 nv_sz;
152 	int			 error;
153 
154 	sc = device_get_softc(dev);
155 
156 	/* Let our parent perform standard initialization first */
157 	if ((error = bhndb_generic_init_full_config(dev, child, hw_prio_table)))
158 		return (error);
159 
160 	/* Fix-up power on defaults for SROM-less devices. */
161 	bhndb_init_sromless_pci_config(sc);
162 
163 	/* If SPROM is mapped directly into BAR0, add NVRAM device. */
164 	nv_sz = bhndb_pci_sprom_size(sc);
165 	if (nv_sz > 0) {
166 		struct bhndb_devinfo	*dinfo;
167 		const char		*dname;
168 
169 		if (bootverbose) {
170 			device_printf(dev, "found SPROM (%u bytes)\n",
171 			    (unsigned int) nv_sz);
172 		}
173 
174 		/* Add sprom device */
175 		dname = "bhnd_nvram";
176 		if ((nv_dev = BUS_ADD_CHILD(dev, 0, dname, -1)) == NULL) {
177 			device_printf(dev, "failed to add sprom device\n");
178 			return (ENXIO);
179 		}
180 
181 		/* Initialize device address space and resource covering the
182 		 * BAR0 SPROM shadow. */
183 		dinfo = device_get_ivars(nv_dev);
184 		dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE;
185 		error = bus_set_resource(nv_dev, SYS_RES_MEMORY, 0,
186 		    bhndb_pci_sprom_addr(sc), nv_sz);
187 
188 		if (error) {
189 			device_printf(dev,
190 			    "failed to register sprom resources\n");
191 			return (error);
192 		}
193 
194 		/* Attach the device */
195 		if ((error = device_probe_and_attach(nv_dev))) {
196 			device_printf(dev, "sprom attach failed\n");
197 			return (error);
198 		}
199 	}
200 
201 	return (0);
202 }
203 
204 static const struct bhndb_regwin *
205 bhndb_pci_sprom_regwin(struct bhndb_pci_softc *sc)
206 {
207 	struct bhndb_resources		*bres;
208 	const struct bhndb_hwcfg	*cfg;
209 	const struct bhndb_regwin	*sprom_win;
210 
211 	bres = sc->bhndb.bus_res;
212 	cfg = bres->cfg;
213 
214 	sprom_win = bhndb_regwin_find_type(cfg->register_windows,
215 	    BHNDB_REGWIN_T_SPROM, BHNDB_PCI_V0_BAR0_SPROM_SIZE);
216 
217 	return (sprom_win);
218 }
219 
220 static bus_addr_t
221 bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc)
222 {
223 	const struct bhndb_regwin	*sprom_win;
224 	struct resource			*r;
225 
226 	/* Fetch the SPROM register window */
227 	sprom_win = bhndb_pci_sprom_regwin(sc);
228 	KASSERT(sprom_win != NULL, ("requested sprom address on PCI_V2+"));
229 
230 	/* Fetch the associated resource */
231 	r = bhndb_find_regwin_resource(sc->bhndb.bus_res, sprom_win);
232 	KASSERT(r != NULL, ("missing resource for sprom window\n"));
233 
234 	return (rman_get_start(r) + sprom_win->win_offset);
235 }
236 
237 static bus_size_t
238 bhndb_pci_sprom_size(struct bhndb_pci_softc *sc)
239 {
240 	const struct bhndb_regwin	*sprom_win;
241 	uint32_t			 sctl;
242 	bus_size_t			 sprom_sz;
243 
244 	sprom_win = bhndb_pci_sprom_regwin(sc);
245 
246 	/* PCI_V2 and later devices map SPROM/OTP via ChipCommon */
247 	if (sprom_win == NULL)
248 		return (0);
249 
250 	/* Determine SPROM size */
251 	sctl = pci_read_config(sc->parent, BHNDB_PCI_SPROM_CONTROL, 4);
252 	if (sctl & BHNDB_PCI_SPROM_BLANK)
253 		return (0);
254 
255 	switch (sctl & BHNDB_PCI_SPROM_SZ_MASK) {
256 	case BHNDB_PCI_SPROM_SZ_1KB:
257 		sprom_sz = (1 * 1024);
258 		break;
259 
260 	case BHNDB_PCI_SPROM_SZ_4KB:
261 		sprom_sz = (4 * 1024);
262 		break;
263 
264 	case BHNDB_PCI_SPROM_SZ_16KB:
265 		sprom_sz = (16 * 1024);
266 		break;
267 
268 	case BHNDB_PCI_SPROM_SZ_RESERVED:
269 	default:
270 		device_printf(sc->dev, "invalid PCI sprom size 0x%x\n", sctl);
271 		return (0);
272 	}
273 
274 	if (sprom_sz > sprom_win->win_size) {
275 		device_printf(sc->dev,
276 		    "PCI sprom size (0x%x) overruns defined register window\n",
277 		    sctl);
278 		return (0);
279 	}
280 
281 	return (sprom_sz);
282 }
283 
284 /*
285  * On devices without a SROM, the PCI(e) cores will be initialized with
286  * their Power-on-Reset defaults; this can leave two of the BAR0 PCI windows
287  * mapped to the wrong core.
288  *
289  * This function updates the SROM shadow to point the BAR0 windows at the
290  * current PCI core.
291  *
292  * Applies to all PCI/PCIe revisions.
293  */
294 static void
295 bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc)
296 {
297 	struct bhndb_resources		*bres;
298 	const struct bhndb_hwcfg	*cfg;
299 	const struct bhndb_regwin	*win;
300 	struct resource			*core_regs;
301 	bus_size_t			 srom_offset;
302 	u_int				 pci_cidx, sprom_cidx;
303 	uint16_t			 val;
304 
305 	bres = sc->bhndb.bus_res;
306 	cfg = bres->cfg;
307 
308 	if (bhnd_get_vendor(sc->bhndb.hostb_dev) != BHND_MFGID_BCM)
309 		return;
310 
311 	switch (bhnd_get_device(sc->bhndb.hostb_dev)) {
312 	case BHND_COREID_PCI:
313 		srom_offset = BHND_PCI_SRSH_PI_OFFSET;
314 		break;
315 	case BHND_COREID_PCIE:
316 		srom_offset = BHND_PCIE_SRSH_PI_OFFSET;
317 		break;
318 	default:
319 		device_printf(sc->dev, "unsupported PCI host bridge device\n");
320 		return;
321 	}
322 
323 	/* Locate the static register window mapping the PCI core */
324 	win = bhndb_regwin_find_core(cfg->register_windows, sc->pci_devclass,
325 	    0, BHND_PORT_DEVICE, 0, 0);
326 	if (win == NULL) {
327 		device_printf(sc->dev, "missing PCI core register window\n");
328 		return;
329 	}
330 
331 	/* Fetch the resource containing the register window */
332 	core_regs = bhndb_find_regwin_resource(bres, win);
333 	if (core_regs == NULL) {
334 		device_printf(sc->dev, "missing PCI core register resource\n");
335 		return;
336 	}
337 
338 	/* Fetch the SPROM's configured core index */
339 	val = bus_read_2(core_regs, win->win_offset + srom_offset);
340 	sprom_cidx = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT;
341 
342 	/* If it doesn't match host bridge's core index, update the index
343 	 * value */
344 	pci_cidx = bhnd_get_core_index(sc->bhndb.hostb_dev);
345 	if (sprom_cidx != pci_cidx) {
346 		val &= ~BHND_PCI_SRSH_PI_MASK;
347 		val |= (pci_cidx << BHND_PCI_SRSH_PI_SHIFT);
348 		bus_write_2(core_regs,
349 		    win->win_offset + srom_offset, val);
350 	}
351 }
352 
353 static int
354 bhndb_pci_resume(device_t dev)
355 {
356 	struct bhndb_pci_softc	*sc;
357 	int			 error;
358 
359 	sc = device_get_softc(dev);
360 
361 	/* Enable clocks (if supported by this hardware) */
362 	if ((error = bhndb_enable_pci_clocks(sc)))
363 		return (error);
364 
365 	/* Perform resume */
366 	return (bhndb_generic_resume(dev));
367 }
368 
369 static int
370 bhndb_pci_suspend(device_t dev)
371 {
372 	struct bhndb_pci_softc	*sc;
373 	int			 error;
374 
375 	sc = device_get_softc(dev);
376 
377 	/* Disable clocks (if supported by this hardware) */
378 	if ((error = bhndb_disable_pci_clocks(sc)))
379 		return (error);
380 
381 	/* Perform suspend */
382 	return (bhndb_generic_suspend(dev));
383 }
384 
385 static int
386 bhndb_pci_detach(device_t dev)
387 {
388 	struct bhndb_pci_softc	*sc;
389 	int			 error;
390 
391 	sc = device_get_softc(dev);
392 
393 	/* Disable clocks (if supported by this hardware) */
394 	if ((error = bhndb_disable_pci_clocks(sc)))
395 		return (error);
396 
397 	/* Perform detach */
398 	if ((error = bhndb_generic_detach(dev)))
399 		return (error);
400 
401 	/* Disable PCI bus mastering */
402 	pci_disable_busmaster(sc->parent);
403 
404 	return (0);
405 }
406 
407 static int
408 bhndb_pci_set_window_addr(device_t dev, const struct bhndb_regwin *rw,
409     bhnd_addr_t addr)
410 {
411 	struct bhndb_pci_softc *sc = device_get_softc(dev);
412 	return (sc->set_regwin(sc, rw, addr));
413 }
414 
415 /**
416  * A siba(4) and bcma(4)-compatible bhndb_set_window_addr implementation.
417  *
418  * On siba(4) devices, it's possible that writing a PCI window register may
419  * not succeed; it's necessary to immediately read the configuration register
420  * and retry if not set to the desired value.
421  *
422  * This is not necessary on bcma(4) devices, but other than the overhead of
423  * validating the register, there's no harm in performing the verification.
424  */
425 static int
426 bhndb_pci_compat_setregwin(struct bhndb_pci_softc *sc,
427     const struct bhndb_regwin *rw, bhnd_addr_t addr)
428 {
429 	int		error;
430 	int		reg;
431 
432 	if (rw->win_type != BHNDB_REGWIN_T_DYN)
433 		return (ENODEV);
434 
435 	reg = rw->d.dyn.cfg_offset;
436 	for (u_int i = 0; i < BHNDB_PCI_BARCTRL_WRITE_RETRY; i++) {
437 		if ((error = bhndb_pci_fast_setregwin(sc, rw, addr)))
438 			return (error);
439 
440 		if (pci_read_config(sc->parent, reg, 4) == addr)
441 			return (0);
442 
443 		DELAY(10);
444 	}
445 
446 	/* Unable to set window */
447 	return (ENODEV);
448 }
449 
450 /**
451  * A bcma(4)-only bhndb_set_window_addr implementation.
452  */
453 static int
454 bhndb_pci_fast_setregwin(struct bhndb_pci_softc *sc,
455     const struct bhndb_regwin *rw, bhnd_addr_t addr)
456 {
457 	/* The PCI bridge core only supports 32-bit addressing, regardless
458 	 * of the bus' support for 64-bit addressing */
459 	if (addr > UINT32_MAX)
460 		return (ERANGE);
461 
462 	switch (rw->win_type) {
463 	case BHNDB_REGWIN_T_DYN:
464 		/* Addresses must be page aligned */
465 		if (addr % rw->win_size != 0)
466 			return (EINVAL);
467 
468 		pci_write_config(sc->parent, rw->d.dyn.cfg_offset, addr, 4);
469 		break;
470 	default:
471 		return (ENODEV);
472 	}
473 
474 	return (0);
475 }
476 
477 /**
478  * Enable externally managed clocks, if required.
479  *
480  * Some PCI chipsets (BCM4306, possibly others) chips do not support
481  * the idle low-power clock. Clocking must be bootstrapped at
482  * attach/resume by directly adjusting GPIO registers exposed in the
483  * PCI config space, and correspondingly, explicitly shutdown at
484  * detach/suspend.
485  *
486  * @param sc Bridge driver state.
487  */
488 static int
489 bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc)
490 {
491 	uint32_t		gpio_in, gpio_out, gpio_en;
492 	uint32_t		gpio_flags;
493 	uint16_t		pci_status;
494 
495 	/* Only supported and required on PCI devices */
496 	if (sc->pci_devclass != BHND_DEVCLASS_PCI)
497 		return (0);
498 
499 	/* Read state of XTAL pin */
500 	gpio_in = pci_read_config(sc->parent, BHNDB_PCI_GPIO_IN, 4);
501 	if (gpio_in & BHNDB_PCI_GPIO_XTAL_ON)
502 		return (0); /* already enabled */
503 
504 	/* Fetch current config */
505 	gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4);
506 	gpio_en = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, 4);
507 
508 	/* Set PLL_OFF/XTAL_ON pins to HIGH and enable both pins */
509 	gpio_flags = (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON);
510 	gpio_out |= gpio_flags;
511 	gpio_en |= gpio_flags;
512 
513 	pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
514 	pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4);
515 	DELAY(1000);
516 
517 	/* Reset PLL_OFF */
518 	gpio_out &= ~BHNDB_PCI_GPIO_PLL_OFF;
519 	pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
520 	DELAY(5000);
521 
522 	/* Clear any PCI 'sent target-abort' flag. */
523 	pci_status = pci_read_config(sc->parent, PCIR_STATUS, 2);
524 	pci_status &= ~PCIM_STATUS_STABORT;
525 	pci_write_config(sc->parent, PCIR_STATUS, pci_status, 2);
526 
527 	return (0);
528 }
529 
530 /**
531  * Disable externally managed clocks, if required.
532  *
533  * @param sc Bridge driver state.
534  */
535 static int
536 bhndb_disable_pci_clocks(struct bhndb_pci_softc *sc)
537 {
538 	uint32_t	gpio_out, gpio_en;
539 
540 	/* Only supported and required on PCI devices */
541 	if (sc->pci_devclass != BHND_DEVCLASS_PCI)
542 		return (0);
543 
544 	// TODO: Check board flags for BFL2_XTALBUFOUTEN?
545 	// TODO: Check PCI core revision?
546 	// TODO: Switch to 'slow' clock?
547 
548 	/* Fetch current config */
549 	gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4);
550 	gpio_en = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, 4);
551 
552 	/* Set PLL_OFF to HIGH, XTAL_ON to LOW. */
553 	gpio_out &= ~BHNDB_PCI_GPIO_XTAL_ON;
554 	gpio_out |= BHNDB_PCI_GPIO_PLL_OFF;
555 	pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4);
556 
557 	/* Enable both output pins */
558 	gpio_en |= (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON);
559 	pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4);
560 
561 	return (0);
562 }
563 
564 static device_method_t bhndb_pci_methods[] = {
565 	/* Device interface */
566 	DEVMETHOD(device_probe,			bhndb_pci_probe),
567 	DEVMETHOD(device_attach,		bhndb_pci_attach),
568 	DEVMETHOD(device_resume,		bhndb_pci_resume),
569 	DEVMETHOD(device_suspend,		bhndb_pci_suspend),
570 	DEVMETHOD(device_detach,		bhndb_pci_detach),
571 
572 	/* BHNDB interface */
573 	DEVMETHOD(bhndb_init_full_config,	bhndb_pci_init_full_config),
574 	DEVMETHOD(bhndb_set_window_addr,	bhndb_pci_set_window_addr),
575 
576 	DEVMETHOD_END
577 };
578 
579 DEFINE_CLASS_1(bhndb, bhndb_pci_driver, bhndb_pci_methods,
580     sizeof(struct bhndb_pci_softc), bhndb_driver);
581 
582 MODULE_VERSION(bhndb_pci, 1);
583 MODULE_DEPEND(bhndb_pci, bhnd_pci_hostb, 1, 1, 1);
584 MODULE_DEPEND(bhndb_pci, pci, 1, 1, 1);
585 MODULE_DEPEND(bhndb_pci, bhndb, 1, 1, 1);
586 MODULE_DEPEND(bhndb_pci, bhnd, 1, 1, 1);
587