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