1/*-
2 * Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com>
3 * Copyright (c) 2014,2016 The FreeBSD Foundation
4 * All rights reserved.
5 *
6 * This software was developed by Andrew Turner under
7 * the sponsorship of the FreeBSD Foundation.
8 *
9 * This software was developed by Semihalf under
10 * the sponsorship of the FreeBSD Foundation.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34/* Generic ECAM PCIe driver FDT attachment */
35
36#include <sys/cdefs.h>
37__FBSDID("$FreeBSD$");
38
39#include "opt_platform.h"
40
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/bus.h>
44#include <sys/kernel.h>
45#include <sys/malloc.h>
46#include <sys/module.h>
47#include <sys/rman.h>
48
49#if defined(INTRNG)
50#include <machine/intr.h>
51#endif
52
53#include <dev/ofw/openfirm.h>
54#include <dev/ofw/ofw_bus.h>
55#include <dev/ofw/ofw_bus_subr.h>
56#include <dev/ofw/ofw_pci.h>
57
58#include <dev/pci/pcivar.h>
59#include <dev/pci/pcireg.h>
60#include <dev/pci/pcib_private.h>
61#include <dev/pci/pci_host_generic.h>
62#include <dev/pci/pci_host_generic_fdt.h>
63
64#include <machine/intr.h>
65
66#include "pcib_if.h"
67
68#define	SPACE_CODE_SHIFT	24
69#define	SPACE_CODE_MASK		0x3
70#define	SPACE_CODE_IO_SPACE	0x1
71#define	PROPS_CELL_SIZE		1
72#define	PCI_ADDR_CELL_SIZE	2
73
74/* OFW bus interface */
75struct generic_pcie_ofw_devinfo {
76	struct ofw_bus_devinfo	di_dinfo;
77	struct resource_list	di_rl;
78};
79
80/* Forward prototypes */
81
82static int generic_pcie_fdt_probe(device_t dev);
83static int parse_pci_mem_ranges(device_t, struct generic_pcie_core_softc *);
84static int generic_pcie_fdt_release_resource(device_t dev, device_t child,
85    int type, int rid, struct resource *res);
86static int generic_pcie_ofw_bus_attach(device_t);
87static const struct ofw_bus_devinfo *generic_pcie_ofw_get_devinfo(device_t,
88    device_t);
89
90static __inline void
91get_addr_size_cells(phandle_t node, pcell_t *addr_cells, pcell_t *size_cells)
92{
93
94	*addr_cells = 2;
95	/* Find address cells if present */
96	OF_getencprop(node, "#address-cells", addr_cells, sizeof(*addr_cells));
97
98	*size_cells = 2;
99	/* Find size cells if present */
100	OF_getencprop(node, "#size-cells", size_cells, sizeof(*size_cells));
101}
102
103static int
104generic_pcie_fdt_probe(device_t dev)
105{
106
107	if (!ofw_bus_status_okay(dev))
108		return (ENXIO);
109
110	if (ofw_bus_is_compatible(dev, "pci-host-ecam-generic")) {
111		device_set_desc(dev, "Generic PCI host controller");
112		return (BUS_PROBE_GENERIC);
113	}
114	if (ofw_bus_is_compatible(dev, "arm,gem5_pcie")) {
115		device_set_desc(dev, "GEM5 PCIe host controller");
116		return (BUS_PROBE_DEFAULT);
117	}
118
119	return (ENXIO);
120}
121
122int
123pci_host_generic_setup_fdt(device_t dev)
124{
125	struct generic_pcie_fdt_softc *sc;
126	phandle_t node;
127	int error;
128
129	sc = device_get_softc(dev);
130
131	/* Retrieve 'ranges' property from FDT */
132	if (bootverbose)
133		device_printf(dev, "parsing FDT for ECAM%d:\n", sc->base.ecam);
134	if (parse_pci_mem_ranges(dev, &sc->base))
135		return (ENXIO);
136
137	/* Attach OFW bus */
138	if (generic_pcie_ofw_bus_attach(dev) != 0)
139		return (ENXIO);
140
141	node = ofw_bus_get_node(dev);
142	if (sc->base.coherent == 0) {
143		sc->base.coherent = OF_hasprop(node, "dma-coherent");
144	}
145	if (bootverbose)
146		device_printf(dev, "Bus is%s cache-coherent\n",
147		    sc->base.coherent ? "" : " not");
148
149	/* TODO parse FDT bus ranges */
150	sc->base.bus_start = 0;
151	sc->base.bus_end = 0xFF;
152
153	error = pci_host_generic_core_attach(dev);
154	if (error != 0)
155		return (error);
156
157	ofw_bus_setup_iinfo(node, &sc->pci_iinfo, sizeof(cell_t));
158
159	return (0);
160}
161
162int
163pci_host_generic_attach(device_t dev)
164{
165	struct generic_pcie_fdt_softc *sc;
166	int error;
167
168	sc = device_get_softc(dev);
169
170	error = pci_host_generic_setup_fdt(dev);
171	if (error != 0)
172		return (error);
173
174	device_add_child(dev, "pci", -1);
175	return (bus_generic_attach(dev));
176}
177
178static int
179parse_pci_mem_ranges(device_t dev, struct generic_pcie_core_softc *sc)
180{
181	pcell_t pci_addr_cells, parent_addr_cells;
182	pcell_t attributes, size_cells;
183	cell_t *base_ranges;
184	int nbase_ranges;
185	phandle_t node;
186	int i, j, k;
187	int tuple;
188
189	node = ofw_bus_get_node(dev);
190
191	OF_getencprop(node, "#address-cells", &pci_addr_cells,
192					sizeof(pci_addr_cells));
193	OF_getencprop(node, "#size-cells", &size_cells,
194					sizeof(size_cells));
195	OF_getencprop(OF_parent(node), "#address-cells", &parent_addr_cells,
196					sizeof(parent_addr_cells));
197
198	if (parent_addr_cells > 2 || pci_addr_cells != 3 || size_cells > 2) {
199		device_printf(dev,
200		    "Unexpected number of address or size cells in FDT\n");
201		return (ENXIO);
202	}
203
204	nbase_ranges = OF_getproplen(node, "ranges");
205	sc->nranges = nbase_ranges / sizeof(cell_t) /
206	    (parent_addr_cells + pci_addr_cells + size_cells);
207	base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
208	OF_getencprop(node, "ranges", base_ranges, nbase_ranges);
209
210	for (i = 0, j = 0; i < sc->nranges; i++) {
211		attributes = (base_ranges[j++] >> SPACE_CODE_SHIFT) & \
212							SPACE_CODE_MASK;
213		if (attributes == SPACE_CODE_IO_SPACE) {
214			sc->ranges[i].flags |= FLAG_TYPE_IO;
215		} else {
216			sc->ranges[i].flags |= FLAG_TYPE_MEM;
217		}
218
219		sc->ranges[i].pci_base = 0;
220		for (k = 0; k < (pci_addr_cells - 1); k++) {
221			sc->ranges[i].pci_base <<= 32;
222			sc->ranges[i].pci_base |= base_ranges[j++];
223		}
224		sc->ranges[i].phys_base = 0;
225		for (k = 0; k < parent_addr_cells; k++) {
226			sc->ranges[i].phys_base <<= 32;
227			sc->ranges[i].phys_base |= base_ranges[j++];
228		}
229		sc->ranges[i].size = 0;
230		for (k = 0; k < size_cells; k++) {
231			sc->ranges[i].size <<= 32;
232			sc->ranges[i].size |= base_ranges[j++];
233		}
234	}
235
236	for (; i < MAX_RANGES_TUPLES; i++) {
237		/* zero-fill remaining tuples to mark empty elements in array */
238		sc->ranges[i].pci_base = 0;
239		sc->ranges[i].phys_base = 0;
240		sc->ranges[i].size = 0;
241	}
242
243	if (bootverbose) {
244		for (tuple = 0; tuple < MAX_RANGES_TUPLES; tuple++) {
245			device_printf(dev,
246			    "\tPCI addr: 0x%jx, CPU addr: 0x%jx, Size: 0x%jx\n",
247			    sc->ranges[tuple].pci_base,
248			    sc->ranges[tuple].phys_base,
249			    sc->ranges[tuple].size);
250		}
251	}
252
253	free(base_ranges, M_DEVBUF);
254	return (0);
255}
256
257static int
258generic_pcie_fdt_route_interrupt(device_t bus, device_t dev, int pin)
259{
260	struct generic_pcie_fdt_softc *sc;
261	struct ofw_pci_register reg;
262	uint32_t pintr, mintr[4];
263	phandle_t iparent;
264	int intrcells;
265
266	sc = device_get_softc(bus);
267	pintr = pin;
268
269	bzero(&reg, sizeof(reg));
270	reg.phys_hi = (pci_get_bus(dev) << OFW_PCI_PHYS_HI_BUSSHIFT) |
271	    (pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) |
272	    (pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT);
273
274	intrcells = ofw_bus_lookup_imap(ofw_bus_get_node(dev),
275	    &sc->pci_iinfo, &reg, sizeof(reg), &pintr, sizeof(pintr),
276	    mintr, sizeof(mintr), &iparent);
277	if (intrcells) {
278		pintr = ofw_bus_map_intr(dev, iparent, intrcells, mintr);
279		return (pintr);
280	}
281
282	device_printf(bus, "could not route pin %d for device %d.%d\n",
283	    pin, pci_get_slot(dev), pci_get_function(dev));
284	return (PCI_INVALID_IRQ);
285}
286
287static int
288generic_pcie_fdt_release_resource(device_t dev, device_t child, int type,
289    int rid, struct resource *res)
290{
291
292#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
293	if (type == PCI_RES_BUS) {
294		return (pci_host_generic_core_release_resource(dev, child, type,
295		    rid, res));
296	}
297#endif
298
299	/* For PCIe devices that do not have FDT nodes, use PCIB method */
300	if ((int)ofw_bus_get_node(child) <= 0) {
301		return (pci_host_generic_core_release_resource(dev, child, type,
302		    rid, res));
303	}
304
305	/* For other devices use OFW method */
306	return (bus_generic_release_resource(dev, child, type, rid, res));
307}
308
309struct resource *
310pci_host_generic_alloc_resource(device_t dev, device_t child, int type,
311    int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
312{
313	struct generic_pcie_fdt_softc *sc;
314	struct generic_pcie_ofw_devinfo *di;
315	struct resource_list_entry *rle;
316	int i;
317
318#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
319	if (type == PCI_RES_BUS) {
320		return (pci_host_generic_core_alloc_resource(dev, child, type, rid,
321		    start, end, count, flags));
322	}
323#endif
324
325	/* For PCIe devices that do not have FDT nodes, use PCIB method */
326	if ((int)ofw_bus_get_node(child) <= 0)
327		return (pci_host_generic_core_alloc_resource(dev, child, type,
328		    rid, start, end, count, flags));
329
330	/* For other devices use OFW method */
331	sc = device_get_softc(dev);
332
333	if (RMAN_IS_DEFAULT_RANGE(start, end)) {
334		if ((di = device_get_ivars(child)) == NULL)
335			return (NULL);
336		if (type == SYS_RES_IOPORT)
337		    type = SYS_RES_MEMORY;
338
339		/* Find defaults for this rid */
340		rle = resource_list_find(&di->di_rl, type, *rid);
341		if (rle == NULL)
342			return (NULL);
343
344		start = rle->start;
345		end = rle->end;
346		count = rle->count;
347	}
348
349	if (type == SYS_RES_MEMORY) {
350		/* Remap through ranges property */
351		for (i = 0; i < MAX_RANGES_TUPLES; i++) {
352			if (start >= sc->base.ranges[i].phys_base &&
353			    end < (sc->base.ranges[i].pci_base +
354			    sc->base.ranges[i].size)) {
355				start -= sc->base.ranges[i].phys_base;
356				start += sc->base.ranges[i].pci_base;
357				end -= sc->base.ranges[i].phys_base;
358				end += sc->base.ranges[i].pci_base;
359				break;
360			}
361		}
362
363		if (i == MAX_RANGES_TUPLES) {
364			device_printf(dev, "Could not map resource "
365			    "%#jx-%#jx\n", start, end);
366			return (NULL);
367		}
368	}
369
370	return (bus_generic_alloc_resource(dev, child, type, rid, start,
371	    end, count, flags));
372}
373
374static int
375generic_pcie_fdt_alloc_msi(device_t pci, device_t child, int count,
376    int maxcount, int *irqs)
377{
378#if defined(INTRNG)
379	phandle_t msi_parent;
380	int err;
381
382	err = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child),
383	    &msi_parent, NULL);
384	if (err != 0)
385		return (err);
386	return (intr_alloc_msi(pci, child, msi_parent, count, maxcount,
387	    irqs));
388#else
389	return (ENXIO);
390#endif
391}
392
393static int
394generic_pcie_fdt_release_msi(device_t pci, device_t child, int count, int *irqs)
395{
396#if defined(INTRNG)
397	phandle_t msi_parent;
398	int err;
399
400	err = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child),
401	    &msi_parent, NULL);
402	if (err != 0)
403		return (err);
404	return (intr_release_msi(pci, child, msi_parent, count, irqs));
405#else
406	return (ENXIO);
407#endif
408}
409
410static int
411generic_pcie_fdt_map_msi(device_t pci, device_t child, int irq, uint64_t *addr,
412    uint32_t *data)
413{
414#if defined(INTRNG)
415	phandle_t msi_parent;
416	int err;
417
418	err = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child),
419	    &msi_parent, NULL);
420	if (err != 0)
421		return (err);
422	return (intr_map_msi(pci, child, msi_parent, irq, addr, data));
423#else
424	return (ENXIO);
425#endif
426}
427
428static int
429generic_pcie_fdt_alloc_msix(device_t pci, device_t child, int *irq)
430{
431#if defined(INTRNG)
432	phandle_t msi_parent;
433	int err;
434
435	err = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child),
436	    &msi_parent, NULL);
437	if (err != 0)
438		return (err);
439	return (intr_alloc_msix(pci, child, msi_parent, irq));
440#else
441	return (ENXIO);
442#endif
443}
444
445static int
446generic_pcie_fdt_release_msix(device_t pci, device_t child, int irq)
447{
448#if defined(INTRNG)
449	phandle_t msi_parent;
450	int err;
451
452	err = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child),
453	    &msi_parent, NULL);
454	if (err != 0)
455		return (err);
456	return (intr_release_msix(pci, child, msi_parent, irq));
457#else
458	return (ENXIO);
459#endif
460}
461
462int
463generic_pcie_get_id(device_t pci, device_t child, enum pci_id_type type,
464    uintptr_t *id)
465{
466	phandle_t node;
467	int err;
468	uint32_t rid;
469	uint16_t pci_rid;
470
471	if (type != PCI_ID_MSI)
472		return (pcib_get_id(pci, child, type, id));
473
474	node = ofw_bus_get_node(pci);
475	pci_rid = pci_get_rid(child);
476
477	err = ofw_bus_msimap(node, pci_rid, NULL, &rid);
478	if (err != 0)
479		return (err);
480	*id = rid;
481
482	return (0);
483}
484
485static const struct ofw_bus_devinfo *
486generic_pcie_ofw_get_devinfo(device_t bus __unused, device_t child)
487{
488	struct generic_pcie_ofw_devinfo *di;
489
490	di = device_get_ivars(child);
491	return (&di->di_dinfo);
492}
493
494/* Helper functions */
495
496static int
497generic_pcie_ofw_bus_attach(device_t dev)
498{
499	struct generic_pcie_ofw_devinfo *di;
500	device_t child;
501	phandle_t parent, node;
502	pcell_t addr_cells, size_cells;
503
504	parent = ofw_bus_get_node(dev);
505	if (parent > 0) {
506		get_addr_size_cells(parent, &addr_cells, &size_cells);
507		/* Iterate through all bus subordinates */
508		for (node = OF_child(parent); node > 0; node = OF_peer(node)) {
509			/* Allocate and populate devinfo. */
510			di = malloc(sizeof(*di), M_DEVBUF, M_WAITOK | M_ZERO);
511			if (ofw_bus_gen_setup_devinfo(&di->di_dinfo, node) != 0) {
512				free(di, M_DEVBUF);
513				continue;
514			}
515
516			/* Initialize and populate resource list. */
517			resource_list_init(&di->di_rl);
518			ofw_bus_reg_to_rl(dev, node, addr_cells, size_cells,
519			    &di->di_rl);
520			ofw_bus_intr_to_rl(dev, node, &di->di_rl, NULL);
521
522			/* Add newbus device for this FDT node */
523			child = device_add_child(dev, NULL, -1);
524			if (child == NULL) {
525				resource_list_free(&di->di_rl);
526				ofw_bus_gen_destroy_devinfo(&di->di_dinfo);
527				free(di, M_DEVBUF);
528				continue;
529			}
530
531			device_set_ivars(child, di);
532		}
533	}
534
535	return (0);
536}
537
538static device_method_t generic_pcie_fdt_methods[] = {
539	DEVMETHOD(device_probe,		generic_pcie_fdt_probe),
540	DEVMETHOD(device_attach,	pci_host_generic_attach),
541	DEVMETHOD(bus_alloc_resource,	pci_host_generic_alloc_resource),
542	DEVMETHOD(bus_release_resource,	generic_pcie_fdt_release_resource),
543
544	/* pcib interface */
545	DEVMETHOD(pcib_route_interrupt,	generic_pcie_fdt_route_interrupt),
546	DEVMETHOD(pcib_alloc_msi,	generic_pcie_fdt_alloc_msi),
547	DEVMETHOD(pcib_release_msi,	generic_pcie_fdt_release_msi),
548	DEVMETHOD(pcib_alloc_msix,	generic_pcie_fdt_alloc_msix),
549	DEVMETHOD(pcib_release_msix,	generic_pcie_fdt_release_msix),
550	DEVMETHOD(pcib_map_msi,		generic_pcie_fdt_map_msi),
551	DEVMETHOD(pcib_get_id,		generic_pcie_get_id),
552	DEVMETHOD(pcib_request_feature,	pcib_request_feature_allow),
553
554	/* ofw_bus interface */
555	DEVMETHOD(ofw_bus_get_devinfo,	generic_pcie_ofw_get_devinfo),
556	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
557	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
558	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
559	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
560	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
561
562	DEVMETHOD_END
563};
564
565DEFINE_CLASS_1(pcib, generic_pcie_fdt_driver, generic_pcie_fdt_methods,
566    sizeof(struct generic_pcie_fdt_softc), generic_pcie_core_driver);
567
568static devclass_t generic_pcie_fdt_devclass;
569
570DRIVER_MODULE(pcib, simplebus, generic_pcie_fdt_driver,
571    generic_pcie_fdt_devclass, 0, 0);
572DRIVER_MODULE(pcib, ofwbus, generic_pcie_fdt_driver, generic_pcie_fdt_devclass,
573    0, 0);
574