ofw_pci.c revision 290989
1/*-
2 * Copyright (c) 2011 Nathan Whitehorn
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 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/powerpc/ofw/ofw_pci.c 290989 2015-11-17 16:07:43Z nwhitehorn $");
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/module.h>
32#include <sys/bus.h>
33#include <sys/conf.h>
34#include <sys/kernel.h>
35
36#include <dev/ofw/openfirm.h>
37#include <dev/ofw/ofw_pci.h>
38#include <dev/ofw/ofw_bus.h>
39#include <dev/ofw/ofw_bus_subr.h>
40
41#include <dev/pci/pcivar.h>
42#include <dev/pci/pcireg.h>
43
44#include <machine/bus.h>
45#include <machine/intr_machdep.h>
46#include <machine/md_var.h>
47#include <machine/pio.h>
48#include <machine/resource.h>
49
50#include <sys/rman.h>
51
52#include <vm/vm.h>
53#include <vm/pmap.h>
54
55#include <powerpc/ofw/ofw_pci.h>
56
57#include "pcib_if.h"
58
59/*
60 * Bus interface.
61 */
62static int		ofw_pci_read_ivar(device_t, device_t, int,
63			    uintptr_t *);
64static struct		resource * ofw_pci_alloc_resource(device_t bus,
65			    device_t child, int type, int *rid, u_long start,
66			    u_long end, u_long count, u_int flags);
67static int		ofw_pci_release_resource(device_t bus, device_t child,
68    			    int type, int rid, struct resource *res);
69static int		ofw_pci_activate_resource(device_t bus, device_t child,
70			    int type, int rid, struct resource *res);
71static int		ofw_pci_deactivate_resource(device_t bus,
72    			    device_t child, int type, int rid,
73    			    struct resource *res);
74static int		ofw_pci_adjust_resource(device_t bus, device_t child,
75			    int type, struct resource *res, u_long start,
76			    u_long end);
77
78/*
79 * pcib interface.
80 */
81static int		ofw_pci_maxslots(device_t);
82static int		ofw_pci_route_interrupt(device_t, device_t, int);
83
84/*
85 * ofw_bus interface
86 */
87static phandle_t ofw_pci_get_node(device_t bus, device_t dev);
88
89/*
90 * local methods
91 */
92
93static int ofw_pci_nranges(phandle_t node);
94static int ofw_pci_fill_ranges(phandle_t node, struct ofw_pci_range *ranges);
95
96/*
97 * Driver methods.
98 */
99static device_method_t	ofw_pci_methods[] = {
100	/* Device interface */
101	DEVMETHOD(device_attach,	ofw_pci_attach),
102
103	/* Bus interface */
104	DEVMETHOD(bus_print_child,	bus_generic_print_child),
105	DEVMETHOD(bus_read_ivar,	ofw_pci_read_ivar),
106	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
107	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
108	DEVMETHOD(bus_alloc_resource,	ofw_pci_alloc_resource),
109	DEVMETHOD(bus_release_resource,	ofw_pci_release_resource),
110	DEVMETHOD(bus_activate_resource,	ofw_pci_activate_resource),
111	DEVMETHOD(bus_deactivate_resource,	ofw_pci_deactivate_resource),
112	DEVMETHOD(bus_adjust_resource,	ofw_pci_adjust_resource),
113
114	/* pcib interface */
115	DEVMETHOD(pcib_maxslots,	ofw_pci_maxslots),
116	DEVMETHOD(pcib_route_interrupt,	ofw_pci_route_interrupt),
117
118	/* ofw_bus interface */
119	DEVMETHOD(ofw_bus_get_node,     ofw_pci_get_node),
120
121	DEVMETHOD_END
122};
123
124DEFINE_CLASS_0(ofw_pci, ofw_pci_driver, ofw_pci_methods, 0);
125
126int
127ofw_pci_init(device_t dev)
128{
129	struct		ofw_pci_softc *sc;
130	phandle_t	node;
131	u_int32_t	busrange[2];
132	struct		ofw_pci_range *rp;
133	int		error;
134
135	node = ofw_bus_get_node(dev);
136	sc = device_get_softc(dev);
137	sc->sc_initialized = 1;
138
139	if (OF_getencprop(node, "reg", (pcell_t *)&sc->sc_pcir,
140	    sizeof(sc->sc_pcir)) == -1)
141		return (ENXIO);
142
143	if (OF_getencprop(node, "bus-range", busrange, sizeof(busrange)) != 8)
144		busrange[0] = 0;
145
146	sc->sc_dev = dev;
147	sc->sc_node = node;
148	sc->sc_bus = busrange[0];
149
150	if (sc->sc_quirks & OFW_PCI_QUIRK_RANGES_ON_CHILDREN) {
151		phandle_t c;
152		int n, i;
153
154		sc->sc_nrange = 0;
155		for (c = OF_child(node); c != 0; c = OF_peer(c)) {
156			n = ofw_pci_nranges(c);
157			if (n > 0)
158				sc->sc_nrange += n;
159		}
160		if (sc->sc_nrange == 0)
161			return (ENXIO);
162		sc->sc_range = malloc(sc->sc_nrange * sizeof(sc->sc_range[0]),
163		    M_DEVBUF, M_WAITOK);
164		i = 0;
165		for (c = OF_child(node); c != 0; c = OF_peer(c)) {
166			n = ofw_pci_fill_ranges(c, &sc->sc_range[i]);
167			if (n > 0)
168				i += n;
169		}
170		KASSERT(i == sc->sc_nrange, ("range count mismatch"));
171	} else {
172		sc->sc_nrange = ofw_pci_nranges(node);
173		if (sc->sc_nrange <= 0) {
174			device_printf(dev, "could not get ranges\n");
175			return (ENXIO);
176		}
177		sc->sc_range = malloc(sc->sc_nrange * sizeof(sc->sc_range[0]),
178		    M_DEVBUF, M_WAITOK);
179		ofw_pci_fill_ranges(node, sc->sc_range);
180	}
181
182	sc->sc_io_rman.rm_type = RMAN_ARRAY;
183	sc->sc_io_rman.rm_descr = "PCI I/O Ports";
184	error = rman_init(&sc->sc_io_rman);
185	if (error) {
186		device_printf(dev, "rman_init() failed. error = %d\n", error);
187		return (error);
188	}
189
190	sc->sc_mem_rman.rm_type = RMAN_ARRAY;
191	sc->sc_mem_rman.rm_descr = "PCI Memory";
192	error = rman_init(&sc->sc_mem_rman);
193	if (error) {
194		device_printf(dev, "rman_init() failed. error = %d\n", error);
195		return (error);
196	}
197
198	for (rp = sc->sc_range; rp < sc->sc_range + sc->sc_nrange &&
199	       rp->pci_hi != 0; rp++) {
200		error = 0;
201
202		switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) {
203		case OFW_PCI_PHYS_HI_SPACE_CONFIG:
204			break;
205		case OFW_PCI_PHYS_HI_SPACE_IO:
206			error = rman_manage_region(&sc->sc_io_rman, rp->pci,
207			    rp->pci + rp->size - 1);
208			break;
209		case OFW_PCI_PHYS_HI_SPACE_MEM32:
210		case OFW_PCI_PHYS_HI_SPACE_MEM64:
211			error = rman_manage_region(&sc->sc_mem_rman, rp->pci,
212			    rp->pci + rp->size - 1);
213			break;
214		}
215
216		if (error) {
217			device_printf(dev,
218			    "rman_manage_region(%x, %#jx, %#jx) failed. "
219			    "error = %d\n", rp->pci_hi &
220			    OFW_PCI_PHYS_HI_SPACEMASK, rp->pci,
221			    rp->pci + rp->size - 1, error);
222			return (error);
223		}
224	}
225
226	ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(cell_t));
227
228	return (error);
229}
230
231int
232ofw_pci_attach(device_t dev)
233{
234	struct ofw_pci_softc *sc;
235	int error;
236
237	sc = device_get_softc(dev);
238	if (!sc->sc_initialized) {
239		error = ofw_pci_init(dev);
240		if (error)
241			return (error);
242	}
243
244	device_add_child(dev, "pci", -1);
245	return (bus_generic_attach(dev));
246}
247
248static int
249ofw_pci_maxslots(device_t dev)
250{
251
252	return (PCI_SLOTMAX);
253}
254
255static int
256ofw_pci_route_interrupt(device_t bus, device_t dev, int pin)
257{
258	struct ofw_pci_softc *sc;
259	struct ofw_pci_register reg;
260	uint32_t pintr, mintr[2];
261	int intrcells;
262	phandle_t iparent;
263
264	sc = device_get_softc(bus);
265	pintr = pin;
266
267	/* Fabricate imap information in case this isn't an OFW device */
268	bzero(&reg, sizeof(reg));
269	reg.phys_hi = (pci_get_bus(dev) << OFW_PCI_PHYS_HI_BUSSHIFT) |
270	    (pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) |
271	    (pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT);
272
273	intrcells = ofw_bus_lookup_imap(ofw_bus_get_node(dev),
274	    &sc->sc_pci_iinfo, &reg, sizeof(reg), &pintr, sizeof(pintr),
275	    mintr, sizeof(mintr), &iparent);
276	if (intrcells) {
277		pintr = ofw_bus_map_intr(dev, iparent, intrcells, mintr);
278		return (pintr);
279	}
280
281	/* Maybe it's a real interrupt, not an intpin */
282	if (pin > 4)
283		return (pin);
284
285	device_printf(bus, "could not route pin %d for device %d.%d\n",
286	    pin, pci_get_slot(dev), pci_get_function(dev));
287	return (PCI_INVALID_IRQ);
288}
289
290static int
291ofw_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
292{
293	struct	ofw_pci_softc *sc;
294
295	sc = device_get_softc(dev);
296
297	switch (which) {
298	case PCIB_IVAR_DOMAIN:
299		*result = device_get_unit(dev);
300		return (0);
301	case PCIB_IVAR_BUS:
302		*result = sc->sc_bus;
303		return (0);
304	}
305
306	return (ENOENT);
307}
308
309static struct resource *
310ofw_pci_alloc_resource(device_t bus, device_t child, int type, int *rid,
311    u_long start, u_long end, u_long count, u_int flags)
312{
313	struct			ofw_pci_softc *sc;
314	struct			resource *rv;
315	struct			rman *rm;
316	int			needactivate;
317
318	needactivate = flags & RF_ACTIVE;
319	flags &= ~RF_ACTIVE;
320
321	sc = device_get_softc(bus);
322
323	switch (type) {
324	case SYS_RES_MEMORY:
325		rm = &sc->sc_mem_rman;
326		break;
327
328	case SYS_RES_IOPORT:
329		rm = &sc->sc_io_rman;
330		break;
331
332	case SYS_RES_IRQ:
333		return (bus_alloc_resource(bus, type, rid, start, end, count,
334		    flags));
335
336	default:
337		device_printf(bus, "unknown resource request from %s\n",
338		    device_get_nameunit(child));
339		return (NULL);
340	}
341
342	rv = rman_reserve_resource(rm, start, end, count, flags, child);
343	if (rv == NULL) {
344		device_printf(bus, "failed to reserve resource for %s\n",
345		    device_get_nameunit(child));
346		return (NULL);
347	}
348
349	rman_set_rid(rv, *rid);
350
351	if (needactivate) {
352		if (bus_activate_resource(child, type, *rid, rv) != 0) {
353			device_printf(bus,
354			    "failed to activate resource for %s\n",
355			    device_get_nameunit(child));
356			rman_release_resource(rv);
357			return (NULL);
358		}
359	}
360
361	return (rv);
362}
363
364static int
365ofw_pci_release_resource(device_t bus, device_t child, int type, int rid,
366    struct resource *res)
367{
368	if (rman_get_flags(res) & RF_ACTIVE) {
369		int error = bus_deactivate_resource(child, type, rid, res);
370		if (error)
371			return error;
372	}
373
374	return (rman_release_resource(res));
375}
376
377static int
378ofw_pci_activate_resource(device_t bus, device_t child, int type, int rid,
379    struct resource *res)
380{
381	struct ofw_pci_softc *sc;
382	void	*p;
383
384	sc = device_get_softc(bus);
385
386	if (type == SYS_RES_IRQ) {
387		return (bus_activate_resource(bus, type, rid, res));
388	}
389	if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) {
390		struct ofw_pci_range *rp;
391		vm_offset_t start;
392		int space;
393
394		start = (vm_offset_t)rman_get_start(res);
395
396		/*
397		 * Map this through the ranges list
398		 */
399		for (rp = sc->sc_range; rp < sc->sc_range + sc->sc_nrange &&
400		       rp->pci_hi != 0; rp++) {
401			if (start < rp->pci || start >= rp->pci + rp->size)
402				continue;
403
404			switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) {
405			case OFW_PCI_PHYS_HI_SPACE_IO:
406				space = SYS_RES_IOPORT;
407				break;
408			case OFW_PCI_PHYS_HI_SPACE_MEM32:
409			case OFW_PCI_PHYS_HI_SPACE_MEM64:
410				space = SYS_RES_MEMORY;
411				break;
412			default:
413				space = -1;
414			}
415
416			if (type == space) {
417				start += (rp->host - rp->pci);
418				break;
419			}
420		}
421
422		if (bootverbose)
423			printf("ofw_pci mapdev: start %zx, len %ld\n", start,
424			    rman_get_size(res));
425
426		p = pmap_mapdev(start, (vm_size_t)rman_get_size(res));
427		if (p == NULL)
428			return (ENOMEM);
429
430		rman_set_virtual(res, p);
431		rman_set_bustag(res, &bs_le_tag);
432		rman_set_bushandle(res, (u_long)p);
433	}
434
435	return (rman_activate_resource(res));
436}
437
438static int
439ofw_pci_deactivate_resource(device_t bus, device_t child, int type, int rid,
440    struct resource *res)
441{
442	/*
443	 * If this is a memory resource, unmap it.
444	 */
445	if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) {
446		u_int32_t psize;
447
448		psize = rman_get_size(res);
449		pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize);
450	}
451
452	return (rman_deactivate_resource(res));
453}
454
455static int
456ofw_pci_adjust_resource(device_t bus, device_t child, int type,
457    struct resource *res, u_long start, u_long end)
458{
459	struct rman *rm = NULL;
460	struct ofw_pci_softc *sc = device_get_softc(bus);
461
462	KASSERT(!(rman_get_flags(res) & RF_ACTIVE),
463	    ("active resources cannot be adjusted"));
464	if (rman_get_flags(res) & RF_ACTIVE)
465		return (EINVAL);
466
467	switch (type) {
468	case SYS_RES_MEMORY:
469		rm = &sc->sc_mem_rman;
470		break;
471	case SYS_RES_IOPORT:
472		rm = &sc->sc_io_rman;
473		break;
474	default:
475		return (ENXIO);
476	}
477
478	if (!rman_is_region_manager(res, rm))
479		return (EINVAL);
480
481	return (rman_adjust_resource(res, start, end));
482}
483
484
485static phandle_t
486ofw_pci_get_node(device_t bus, device_t dev)
487{
488	struct ofw_pci_softc *sc;
489
490	sc = device_get_softc(bus);
491	/* We only have one child, the PCI bus, which needs our own node. */
492
493	return (sc->sc_node);
494}
495
496static int
497ofw_pci_nranges(phandle_t node)
498{
499	int host_address_cells = 1, pci_address_cells = 3, size_cells = 2;
500	ssize_t nbase_ranges;
501
502	OF_getencprop(OF_parent(node), "#address-cells", &host_address_cells,
503	    sizeof(host_address_cells));
504	OF_getencprop(node, "#address-cells", &pci_address_cells,
505	    sizeof(pci_address_cells));
506	OF_getencprop(node, "#size-cells", &size_cells, sizeof(size_cells));
507
508	nbase_ranges = OF_getproplen(node, "ranges");
509	if (nbase_ranges <= 0)
510		return (-1);
511
512	return (nbase_ranges / sizeof(cell_t) /
513	    (pci_address_cells + host_address_cells + size_cells));
514}
515
516static int
517ofw_pci_fill_ranges(phandle_t node, struct ofw_pci_range *ranges)
518{
519	int host_address_cells = 1, pci_address_cells = 3, size_cells = 2;
520	cell_t *base_ranges;
521	ssize_t nbase_ranges;
522	int nranges;
523	int i, j, k;
524
525	OF_getencprop(OF_parent(node), "#address-cells", &host_address_cells,
526	    sizeof(host_address_cells));
527	OF_getencprop(node, "#address-cells", &pci_address_cells,
528	    sizeof(pci_address_cells));
529	OF_getencprop(node, "#size-cells", &size_cells, sizeof(size_cells));
530
531	nbase_ranges = OF_getproplen(node, "ranges");
532	if (nbase_ranges <= 0)
533		return (-1);
534	nranges = nbase_ranges / sizeof(cell_t) /
535	    (pci_address_cells + host_address_cells + size_cells);
536
537	base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
538	OF_getencprop(node, "ranges", base_ranges, nbase_ranges);
539
540	for (i = 0, j = 0; i < nranges; i++) {
541		ranges[i].pci_hi = base_ranges[j++];
542		ranges[i].pci = 0;
543		for (k = 0; k < pci_address_cells - 1; k++) {
544			ranges[i].pci <<= 32;
545			ranges[i].pci |= base_ranges[j++];
546		}
547		ranges[i].host = 0;
548		for (k = 0; k < host_address_cells; k++) {
549			ranges[i].host <<= 32;
550			ranges[i].host |= base_ranges[j++];
551		}
552		ranges[i].size = 0;
553		for (k = 0; k < size_cells; k++) {
554			ranges[i].size <<= 32;
555			ranges[i].size |= base_ranges[j++];
556		}
557	}
558
559	free(base_ranges, M_DEVBUF);
560	return (nranges);
561}
562
563