xref: /freebsd/sys/arm64/cavium/thunder_pcie_fdt.c (revision 1f4bcc459a76b7aa664f3fd557684cd0ba6da352)
1 /*
2  * Copyright (C) 2016 Cavium Inc.
3  * All rights reserved.
4  *
5  * Developed by Semihalf.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 #include "opt_platform.h"
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/malloc.h>
36 #include <sys/types.h>
37 #include <sys/sysctl.h>
38 #include <sys/kernel.h>
39 #include <sys/rman.h>
40 #include <sys/module.h>
41 #include <sys/bus.h>
42 #include <sys/endian.h>
43 #include <sys/cpuset.h>
44 
45 #include <dev/ofw/openfirm.h>
46 #include <dev/ofw/ofw_bus.h>
47 #include <dev/ofw/ofw_bus_subr.h>
48 
49 #include "thunder_pcie_common.h"
50 
51 #define	OFW_CELL_TO_UINT64(cell)	\
52     (((uint64_t)(*(cell)) << 32) | (uint64_t)(*((cell) + 1)))
53 
54 #define	SPACE_CODE_SHIFT	24
55 #define	SPACE_CODE_MASK		0x3
56 #define	SPACE_CODE_IO_SPACE	0x1
57 #define	PROPS_CELL_SIZE		1
58 #define	PCI_ADDR_CELL_SIZE	2
59 
60 static int thunder_pcie_fdt_probe(device_t);
61 static int thunder_pcie_fdt_attach(device_t);
62 
63 static struct resource * thunder_pcie_ofw_bus_alloc_res(device_t, device_t,
64     int, int *, rman_res_t, rman_res_t, rman_res_t, u_int);
65 static int thunder_pcie_ofw_bus_rel_res(device_t, device_t, int, int,
66     struct resource *);
67 
68 static const struct ofw_bus_devinfo *thunder_pcie_ofw_get_devinfo(device_t,
69     device_t);
70 
71 static device_method_t thunder_pcie_fdt_methods[] = {
72 	/* Device interface */
73 	DEVMETHOD(device_probe,		thunder_pcie_fdt_probe),
74 	DEVMETHOD(device_attach,	thunder_pcie_fdt_attach),
75 
76 	/* Bus interface */
77 	DEVMETHOD(bus_alloc_resource,		thunder_pcie_ofw_bus_alloc_res),
78 	DEVMETHOD(bus_release_resource,		thunder_pcie_ofw_bus_rel_res),
79 	DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
80 
81 	/* ofw_bus interface */
82 	DEVMETHOD(ofw_bus_get_devinfo,	thunder_pcie_ofw_get_devinfo),
83 	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
84 	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
85 	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
86 	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
87 	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
88 
89 	/* End */
90 	DEVMETHOD_END
91 };
92 
93 DEFINE_CLASS_1(pcib, thunder_pcie_fdt_driver, thunder_pcie_fdt_methods,
94     sizeof(struct thunder_pcie_softc), thunder_pcie_driver);
95 
96 static devclass_t thunder_pcie_fdt_devclass;
97 
98 DRIVER_MODULE(thunder_pcib, simplebus, thunder_pcie_fdt_driver,
99     thunder_pcie_fdt_devclass, 0, 0);
100 DRIVER_MODULE(thunder_pcib, ofwbus, thunder_pcie_fdt_driver,
101     thunder_pcie_fdt_devclass, 0, 0);
102 
103 static int thunder_pcie_fdt_ranges(device_t);
104 static int thunder_pcie_ofw_bus_attach(device_t);
105 
106 static int
107 thunder_pcie_fdt_probe(device_t dev)
108 {
109 
110 	if (!ofw_bus_status_okay(dev))
111 		return (ENXIO);
112 
113 	if (ofw_bus_is_compatible(dev, "cavium,thunder-pcie") ||
114 	    ofw_bus_is_compatible(dev, "cavium,pci-host-thunder-ecam")) {
115 		device_set_desc(dev, "Cavium Integrated PCI/PCI-E Controller");
116 		return (BUS_PROBE_DEFAULT);
117 	}
118 
119 	return (ENXIO);
120 }
121 
122 static int
123 thunder_pcie_fdt_attach(device_t dev)
124 {
125 	int err;
126 
127 	/* Retrieve 'ranges' property from FDT */
128 	if (thunder_pcie_fdt_ranges(dev) != 0)
129 		return (ENXIO);
130 
131 	err = thunder_pcie_ofw_bus_attach(dev);
132 	if (err != 0)
133 		return (err);
134 
135 	return (thunder_pcie_attach(dev));
136 }
137 
138 static __inline void
139 get_addr_size_cells(phandle_t node, pcell_t *addr_cells, pcell_t *size_cells)
140 {
141 
142 	*addr_cells = 2;
143 	/* Find address cells if present */
144 	OF_getencprop(node, "#address-cells", addr_cells, sizeof(*addr_cells));
145 
146 	*size_cells = 2;
147 	/* Find size cells if present */
148 	OF_getencprop(node, "#size-cells", size_cells, sizeof(*size_cells));
149 }
150 
151 static int
152 thunder_pcie_fdt_ranges(device_t dev)
153 {
154 	struct thunder_pcie_softc *sc;
155 	phandle_t node;
156 	pcell_t pci_addr_cells, parent_addr_cells, size_cells;
157 	pcell_t attributes;
158 	pcell_t *ranges_buf, *cell_ptr;
159 	int cells_count, tuples_count;
160 	int tuple;
161 	int rv;
162 
163 	sc = device_get_softc(dev);
164 	node = ofw_bus_get_node(dev);
165 
166 	get_addr_size_cells(node, &pci_addr_cells, &size_cells);
167 
168 	/* Find parent address cells if present */
169 	if (OF_getencprop(OF_parent(node), "#address-cells",
170 	    &parent_addr_cells, sizeof(parent_addr_cells)) < sizeof(parent_addr_cells))
171 		parent_addr_cells = 2;
172 
173 	/* Check if FDT format matches driver requirements */
174 	if ((parent_addr_cells != 2) || (pci_addr_cells != 3) ||
175 	    (size_cells != 2)) {
176 		device_printf(dev,
177 		    "Unexpected number of address or size cells in FDT "
178 		    " %d:%d:%d\n",
179 		    parent_addr_cells, pci_addr_cells, size_cells);
180 		return (ENXIO);
181 	}
182 
183 	cells_count = OF_getencprop_alloc(node, "ranges",
184 	    sizeof(pcell_t), (void **)&ranges_buf);
185 	if (cells_count == -1) {
186 		device_printf(dev, "Error parsing FDT 'ranges' property\n");
187 		return (ENXIO);
188 	}
189 
190 	tuples_count = cells_count /
191 	    (pci_addr_cells + parent_addr_cells + size_cells);
192 	if (tuples_count > RANGES_TUPLES_MAX) {
193 		device_printf(dev,
194 		    "Unexpected number of 'ranges' tuples in FDT\n");
195 		rv = ENXIO;
196 		goto out;
197 	}
198 
199 	cell_ptr = ranges_buf;
200 
201 	for (tuple = 0; tuple < tuples_count; tuple++) {
202 		/*
203 		 * TUPLE FORMAT:
204 		 *  attributes  - 32-bit attributes field
205 		 *  PCI address - bus address combined of two cells in
206 		 *                a following format:
207 		 *                <ADDR MSB> <ADDR LSB>
208 		 *  PA address  - physical address combined of two cells in
209 		 *                a following format:
210 		 *                <ADDR MSB> <ADDR LSB>
211 		 *  size        - range size combined of two cells in
212 		 *                a following format:
213 		 *                <ADDR MSB> <ADDR LSB>
214 		 */
215 		attributes = *cell_ptr;
216 		attributes = (attributes >> SPACE_CODE_SHIFT) & SPACE_CODE_MASK;
217 		if (attributes == SPACE_CODE_IO_SPACE) {
218 			/* Internal PCIe does not support IO space, ignore. */
219 			sc->ranges[tuple].phys_base = 0;
220 			sc->ranges[tuple].size = 0;
221 			cell_ptr +=
222 			    (pci_addr_cells + parent_addr_cells + size_cells);
223 			continue;
224 		}
225 		cell_ptr += PROPS_CELL_SIZE;
226 		sc->ranges[tuple].pci_base = OFW_CELL_TO_UINT64(cell_ptr);
227 		cell_ptr += PCI_ADDR_CELL_SIZE;
228 		sc->ranges[tuple].phys_base = OFW_CELL_TO_UINT64(cell_ptr);
229 		cell_ptr += parent_addr_cells;
230 		sc->ranges[tuple].size = OFW_CELL_TO_UINT64(cell_ptr);
231 		cell_ptr += size_cells;
232 
233 		if (bootverbose) {
234 			device_printf(dev,
235 			    "\tPCI addr: 0x%jx, CPU addr: 0x%jx, Size: 0x%jx\n",
236 			    sc->ranges[tuple].pci_base,
237 			    sc->ranges[tuple].phys_base,
238 			    sc->ranges[tuple].size);
239 		}
240 
241 	}
242 	for (; tuple < RANGES_TUPLES_MAX; tuple++) {
243 		/* zero-fill remaining tuples to mark empty elements in array */
244 		sc->ranges[tuple].phys_base = 0;
245 		sc->ranges[tuple].size = 0;
246 	}
247 
248 	rv = 0;
249 out:
250 	free(ranges_buf, M_OFWPROP);
251 	return (rv);
252 }
253 
254 /* OFW bus interface */
255 struct thunder_pcie_ofw_devinfo {
256 	struct ofw_bus_devinfo	di_dinfo;
257 	struct resource_list	di_rl;
258 };
259 
260 static const struct ofw_bus_devinfo *
261 thunder_pcie_ofw_get_devinfo(device_t bus __unused, device_t child)
262 {
263 	struct thunder_pcie_ofw_devinfo *di;
264 
265 	di = device_get_ivars(child);
266 	return (&di->di_dinfo);
267 }
268 
269 static struct resource *
270 thunder_pcie_ofw_bus_alloc_res(device_t bus, device_t child, int type, int *rid,
271     rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
272 {
273 	struct thunder_pcie_softc *sc;
274 	struct thunder_pcie_ofw_devinfo *di;
275 	struct resource_list_entry *rle;
276 	int i;
277 
278 	/* For PCIe devices that do not have FDT nodes, use PCIB method */
279 	if ((int)ofw_bus_get_node(child) <= 0) {
280 		return (thunder_pcie_alloc_resource(bus, child, type, rid,
281 		    start, end, count, flags));
282 	}
283 
284 	sc = device_get_softc(bus);
285 
286 	if ((start == 0UL) && (end == ~0UL)) {
287 		if ((di = device_get_ivars(child)) == NULL)
288 			return (NULL);
289 		if (type == SYS_RES_IOPORT)
290 		    type = SYS_RES_MEMORY;
291 
292 		/* Find defaults for this rid */
293 		rle = resource_list_find(&di->di_rl, type, *rid);
294 		if (rle == NULL)
295 			return (NULL);
296 
297 		start = rle->start;
298 		end = rle->end;
299 		count = rle->count;
300 	}
301 
302 	if (type == SYS_RES_MEMORY) {
303 		/* Remap through ranges property */
304 		for (i = 0; i < RANGES_TUPLES_MAX; i++) {
305 			if (start >= sc->ranges[i].phys_base && end <
306 			    sc->ranges[i].pci_base + sc->ranges[i].size) {
307 				start -= sc->ranges[i].phys_base;
308 				start += sc->ranges[i].pci_base;
309 				end -= sc->ranges[i].phys_base;
310 				end += sc->ranges[i].pci_base;
311 				break;
312 			}
313 		}
314 
315 		if (i == RANGES_TUPLES_MAX) {
316 			device_printf(bus, "Could not map resource "
317 			    "%#lx-%#lx\n", start, end);
318 			return (NULL);
319 		}
320 	}
321 
322 	return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
323 	    count, flags));
324 }
325 
326 static int
327 thunder_pcie_ofw_bus_rel_res(device_t bus, device_t child, int type, int rid,
328     struct resource *res)
329 {
330 
331 	/* For PCIe devices that do not have FDT nodes, use PCIB method */
332 	if ((int)ofw_bus_get_node(child) <= 0) {
333 		return (thunder_pcie_release_resource(bus,
334 		    child, type, rid, res));
335 	}
336 
337 	return (bus_generic_release_resource(bus, child, type, rid, res));
338 }
339 
340 /* Helper functions */
341 
342 static int
343 thunder_pcie_ofw_bus_attach(device_t dev)
344 {
345 	struct thunder_pcie_ofw_devinfo *di;
346 	device_t child;
347 	phandle_t parent, node;
348 	pcell_t addr_cells, size_cells;
349 
350 	parent = ofw_bus_get_node(dev);
351 	if (parent > 0) {
352 		get_addr_size_cells(parent, &addr_cells, &size_cells);
353 		/* Iterate through all bus subordinates */
354 		for (node = OF_child(parent); node > 0; node = OF_peer(node)) {
355 			/* Allocate and populate devinfo. */
356 			di = malloc(sizeof(*di), M_THUNDER_PCIE, M_WAITOK | M_ZERO);
357 			if (ofw_bus_gen_setup_devinfo(&di->di_dinfo, node) != 0) {
358 				free(di, M_THUNDER_PCIE);
359 				continue;
360 			}
361 
362 			/* Initialize and populate resource list. */
363 			resource_list_init(&di->di_rl);
364 			ofw_bus_reg_to_rl(dev, node, addr_cells, size_cells,
365 			    &di->di_rl);
366 			ofw_bus_intr_to_rl(dev, node, &di->di_rl, NULL);
367 
368 			/* Add newbus device for this FDT node */
369 			child = device_add_child(dev, NULL, -1);
370 			if (child == NULL) {
371 				resource_list_free(&di->di_rl);
372 				ofw_bus_gen_destroy_devinfo(&di->di_dinfo);
373 				free(di, M_THUNDER_PCIE);
374 				continue;
375 			}
376 
377 			device_set_ivars(child, di);
378 		}
379 	}
380 
381 	return (0);
382 }
383