1139825Simp/*-
286231Stmm * Copyright (c) 1999, 2000 Matthew R. Green
3117119Stmm * Copyright (c) 2001 - 2003 by Thomas Moestl <tmm@FreeBSD.org>
4287726Smarius * Copyright (c) 2005 - 2015 by Marius Strobl <marius@FreeBSD.org>
586231Stmm * All rights reserved.
686231Stmm *
786231Stmm * Redistribution and use in source and binary forms, with or without
886231Stmm * modification, are permitted provided that the following conditions
986231Stmm * are met:
1086231Stmm * 1. Redistributions of source code must retain the above copyright
1186231Stmm *    notice, this list of conditions and the following disclaimer.
1286231Stmm * 2. Redistributions in binary form must reproduce the above copyright
1386231Stmm *    notice, this list of conditions and the following disclaimer in the
1486231Stmm *    documentation and/or other materials provided with the distribution.
1586231Stmm * 3. The name of the author may not be used to endorse or promote products
1686231Stmm *    derived from this software without specific prior written permission.
1786231Stmm *
1886231Stmm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1986231Stmm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2086231Stmm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2186231Stmm * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2286231Stmm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2386231Stmm * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2486231Stmm * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2586231Stmm * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2686231Stmm * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2786231Stmm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2886231Stmm * SUCH DAMAGE.
2986231Stmm *
3086231Stmm *	from: NetBSD: psycho.c,v 1.35 2001/09/10 16:17:06 eeh Exp
3186231Stmm */
3286231Stmm
33153057Smarius#include <sys/cdefs.h>
34153057Smarius__FBSDID("$FreeBSD: releng/10.3/sys/sparc64/pci/ofw_pci.c 292789 2015-12-27 19:37:47Z marius $");
35153057Smarius
3686231Stmm#include "opt_ofw_pci.h"
3786231Stmm
3886231Stmm#include <sys/param.h>
3986231Stmm#include <sys/systm.h>
4086231Stmm#include <sys/bus.h>
41287726Smarius#include <sys/kernel.h>
42287726Smarius#include <sys/rman.h>
4386231Stmm
44287726Smarius#include <dev/ofw/ofw_bus.h>
45287726Smarius#include <dev/ofw/ofw_pci.h>
4686231Stmm#include <dev/ofw/openfirm.h>
4786231Stmm
48287726Smarius#include <dev/pci/pcireg.h>
49287726Smarius#include <dev/pci/pcivar.h>
50287726Smarius
51287726Smarius#include <machine/asi.h>
52115417Stmm#include <machine/bus.h>
53287726Smarius#include <machine/bus_private.h>
54287726Smarius#include <machine/cpufunc.h>
55287726Smarius#include <machine/fsr.h>
56287726Smarius#include <machine/resource.h>
5786231Stmm
58117119Stmm#include <sparc64/pci/ofw_pci.h>
59117119Stmm
60292789Smarius/* XXX */
61292789Smariusextern struct bus_space_tag nexus_bustag;
62292789Smarius
63287726Smariusint
64287726Smariusofw_pci_attach_common(device_t dev, bus_dma_tag_t dmat, u_long iosize,
65287726Smarius    u_long memsize)
66287726Smarius{
67287726Smarius	struct ofw_pci_softc *sc;
68287726Smarius	struct ofw_pci_ranges *range;
69287726Smarius	phandle_t node;
70287726Smarius	uint32_t prop_array[2];
71287726Smarius	u_int i, j, nrange;
7286231Stmm
73287726Smarius	sc = device_get_softc(dev);
74287726Smarius	node = ofw_bus_get_node(dev);
75287726Smarius	sc->sc_node = node;
76287726Smarius	sc->sc_pci_dmat = dmat;
77117119Stmm
78287726Smarius	/* Initialize memory and I/O rmans. */
79287726Smarius	sc->sc_pci_io_rman.rm_type = RMAN_ARRAY;
80287726Smarius	sc->sc_pci_io_rman.rm_descr = "PCI I/O Ports";
81287726Smarius	if (rman_init(&sc->sc_pci_io_rman) != 0 ||
82287726Smarius	    rman_manage_region(&sc->sc_pci_io_rman, 0, iosize) != 0) {
83287726Smarius		device_printf(dev, "failed to set up I/O rman\n");
84287726Smarius		return (ENXIO);
85287726Smarius	}
86287726Smarius	sc->sc_pci_mem_rman.rm_type = RMAN_ARRAY;
87287726Smarius	sc->sc_pci_mem_rman.rm_descr = "PCI Memory";
88287726Smarius	if (rman_init(&sc->sc_pci_mem_rman) != 0 ||
89287726Smarius	    rman_manage_region(&sc->sc_pci_mem_rman, 0, memsize) != 0) {
90287726Smarius		device_printf(dev, "failed to set up memory rman\n");
91287726Smarius		return (ENXIO);
92287726Smarius	}
9398148Stmm
94287726Smarius	/*
95287726Smarius	 * Find the addresses of the various bus spaces.  The physical
96287726Smarius	 * start addresses of the ranges are the configuration, I/O and
97287726Smarius	 * memory handles.  There should not be multiple ones of one kind.
98287726Smarius	 */
99287726Smarius	nrange = OF_getprop_alloc(node, "ranges", sizeof(*range),
100287726Smarius	    (void **)&range);
101287726Smarius	for (i = 0; i < nrange; i++) {
102287726Smarius		j = OFW_PCI_RANGE_CS(&range[i]);
103287726Smarius		if (sc->sc_pci_bh[j] != 0) {
104287726Smarius			device_printf(dev, "duplicate range for space %d\n",
105287726Smarius			    j);
106287726Smarius			free(range, M_OFWPROP);
107287726Smarius			return (EINVAL);
10898148Stmm		}
109287726Smarius		sc->sc_pci_bh[j] = OFW_PCI_RANGE_PHYS(&range[i]);
11098148Stmm	}
111287726Smarius	free(range, M_OFWPROP);
112287726Smarius
113287726Smarius	/*
114287726Smarius	 * Make sure that the expected ranges are actually present.
115287726Smarius	 * The OFW_PCI_CS_MEM64 one is not currently used.
116287726Smarius	 */
117287726Smarius	if (sc->sc_pci_bh[OFW_PCI_CS_CONFIG] == 0) {
118287726Smarius		device_printf(dev, "missing CONFIG range\n");
119287726Smarius		return (ENXIO);
120287726Smarius	}
121287726Smarius	if (sc->sc_pci_bh[OFW_PCI_CS_IO] == 0) {
122287726Smarius		device_printf(dev, "missing IO range\n");
123287726Smarius		return (ENXIO);
124287726Smarius	}
125287726Smarius	if (sc->sc_pci_bh[OFW_PCI_CS_MEM32] == 0) {
126287726Smarius		device_printf(dev, "missing MEM32 range\n");
127287726Smarius		return (ENXIO);
128287726Smarius	}
129287726Smarius
130287726Smarius	/* Allocate our tags. */
131292789Smarius	sc->sc_pci_iot = sparc64_alloc_bus_tag(NULL, &nexus_bustag,
132292789Smarius	    PCI_IO_BUS_SPACE, NULL);
133287726Smarius	if (sc->sc_pci_iot == NULL) {
134287726Smarius		device_printf(dev, "could not allocate PCI I/O tag\n");
135287726Smarius		return (ENXIO);
136287726Smarius	}
137292789Smarius	sc->sc_pci_cfgt = sparc64_alloc_bus_tag(NULL, &nexus_bustag,
138292789Smarius	    PCI_CONFIG_BUS_SPACE, NULL);
139287726Smarius	if (sc->sc_pci_cfgt == NULL) {
140287726Smarius		device_printf(dev,
141287726Smarius		    "could not allocate PCI configuration space tag\n");
142287726Smarius		return (ENXIO);
143287726Smarius	}
144287726Smarius
145287726Smarius	/*
146287726Smarius	 * Get the bus range from the firmware.
147287726Smarius	 */
148287726Smarius	i = OF_getprop(node, "bus-range", (void *)prop_array,
149287726Smarius	    sizeof(prop_array));
150287726Smarius	if (i == -1) {
151287726Smarius		device_printf(dev, "could not get bus-range\n");
152287726Smarius		return (ENXIO);
153287726Smarius	}
154287726Smarius	if (i != sizeof(prop_array)) {
155287726Smarius		device_printf(dev, "broken bus-range (%d)", i);
156287726Smarius		return (EINVAL);
157287726Smarius	}
158287726Smarius	sc->sc_pci_secbus = prop_array[0];
159287726Smarius	sc->sc_pci_subbus = prop_array[1];
160287726Smarius	if (bootverbose != 0)
161287726Smarius		device_printf(dev, "bus range %u to %u; PCI bus %d\n",
162287726Smarius		    sc->sc_pci_secbus, sc->sc_pci_subbus, sc->sc_pci_secbus);
163287726Smarius
164287726Smarius	ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(ofw_pci_intr_t));
165287726Smarius
166287726Smarius	return (0);
16798148Stmm}
168287726Smarius
169287726Smariusuint32_t
170287726Smariusofw_pci_read_config_common(device_t dev, u_int regmax, u_long offset,
171287726Smarius    u_int bus, u_int slot, u_int func, u_int reg, int width)
172287726Smarius{
173287726Smarius	struct ofw_pci_softc *sc;
174287726Smarius	bus_space_handle_t bh;
175287726Smarius	uint32_t r, wrd;
176287726Smarius	int i;
177287726Smarius	uint16_t shrt;
178287726Smarius	uint8_t byte;
179287726Smarius
180287726Smarius	sc = device_get_softc(dev);
181287726Smarius	if (bus < sc->sc_pci_secbus || bus > sc->sc_pci_subbus ||
182287726Smarius	    slot > PCI_SLOTMAX || func > PCI_FUNCMAX || reg > regmax)
183287726Smarius		return (-1);
184287726Smarius
185287726Smarius	bh = sc->sc_pci_bh[OFW_PCI_CS_CONFIG];
186287726Smarius	switch (width) {
187287726Smarius	case 1:
188287726Smarius		i = bus_space_peek_1(sc->sc_pci_cfgt, bh, offset, &byte);
189287726Smarius		r = byte;
190287726Smarius		break;
191287726Smarius	case 2:
192287726Smarius		i = bus_space_peek_2(sc->sc_pci_cfgt, bh, offset, &shrt);
193287726Smarius		r = shrt;
194287726Smarius		break;
195287726Smarius	case 4:
196287726Smarius		i = bus_space_peek_4(sc->sc_pci_cfgt, bh, offset, &wrd);
197287726Smarius		r = wrd;
198287726Smarius		break;
199287726Smarius	default:
200287726Smarius		panic("%s: bad width %d", __func__, width);
201287726Smarius		/* NOTREACHED */
202287726Smarius	}
203287726Smarius
204287726Smarius	if (i) {
205287726Smarius#ifdef OFW_PCI_DEBUG
206287726Smarius		printf("%s: read data error reading: %d.%d.%d: 0x%x\n",
207287726Smarius		    __func__, bus, slot, func, reg);
208287726Smarius#endif
209287726Smarius		r = -1;
210287726Smarius	}
211287726Smarius	return (r);
212287726Smarius}
213287726Smarius
214287726Smariusvoid
215287726Smariusofw_pci_write_config_common(device_t dev, u_int regmax, u_long offset,
216287726Smarius    u_int bus, u_int slot, u_int func, u_int reg, uint32_t val, int width)
217287726Smarius{
218287726Smarius	struct ofw_pci_softc *sc;
219287726Smarius	bus_space_handle_t bh;
220287726Smarius
221287726Smarius	sc = device_get_softc(dev);
222287726Smarius	if (bus < sc->sc_pci_secbus || bus > sc->sc_pci_subbus ||
223287726Smarius	    slot > PCI_SLOTMAX || func > PCI_FUNCMAX || reg > regmax)
224287726Smarius		return;
225287726Smarius
226287726Smarius	bh = sc->sc_pci_bh[OFW_PCI_CS_CONFIG];
227287726Smarius	switch (width) {
228287726Smarius	case 1:
229287726Smarius		bus_space_write_1(sc->sc_pci_cfgt, bh, offset, val);
230287726Smarius		break;
231287726Smarius	case 2:
232287726Smarius		bus_space_write_2(sc->sc_pci_cfgt, bh, offset, val);
233287726Smarius		break;
234287726Smarius	case 4:
235287726Smarius		bus_space_write_4(sc->sc_pci_cfgt, bh, offset, val);
236287726Smarius		break;
237287726Smarius	default:
238287726Smarius		panic("%s: bad width %d", __func__, width);
239287726Smarius		/* NOTREACHED */
240287726Smarius	}
241287726Smarius}
242287726Smarius
243287726Smariusofw_pci_intr_t
244287726Smariusofw_pci_route_interrupt_common(device_t bridge, device_t dev, int pin)
245287726Smarius{
246287726Smarius	struct ofw_pci_softc *sc;
247287726Smarius	struct ofw_pci_register reg;
248287726Smarius	ofw_pci_intr_t pintr, mintr;
249287726Smarius
250287726Smarius	sc = device_get_softc(bridge);
251287726Smarius	pintr = pin;
252287726Smarius	if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo,
253287726Smarius	    &reg, sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr),
254287726Smarius	    NULL) != 0)
255287726Smarius		return (mintr);
256287726Smarius	return (PCI_INVALID_IRQ);
257287726Smarius}
258287726Smarius
259287726Smariusvoid
260287726Smariusofw_pci_dmamap_sync_stst_order_common(void)
261287726Smarius{
262287726Smarius	static u_char buf[VIS_BLOCKSIZE] __aligned(VIS_BLOCKSIZE);
263287726Smarius	register_t reg, s;
264287726Smarius
265287726Smarius	s = intr_disable();
266287726Smarius	reg = rd(fprs);
267287726Smarius	wr(fprs, reg | FPRS_FEF, 0);
268287726Smarius	__asm __volatile("stda %%f0, [%0] %1"
269287726Smarius	    : : "r" (buf), "n" (ASI_BLK_COMMIT_S));
270287726Smarius	membar(Sync);
271287726Smarius	wr(fprs, reg, 0);
272287726Smarius	intr_restore(s);
273287726Smarius}
274287726Smarius
275287726Smariusint
276287726Smariusofw_pci_read_ivar(device_t dev, device_t child __unused, int which,
277287726Smarius    uintptr_t *result)
278287726Smarius{
279287726Smarius	struct ofw_pci_softc *sc;
280287726Smarius
281287726Smarius	switch (which) {
282287726Smarius	case PCIB_IVAR_DOMAIN:
283287726Smarius		*result = device_get_unit(dev);
284287726Smarius		return (0);
285287726Smarius	case PCIB_IVAR_BUS:
286287726Smarius		sc = device_get_softc(dev);
287287726Smarius		*result = sc->sc_pci_secbus;
288287726Smarius		return (0);
289287726Smarius	}
290287726Smarius	return (ENOENT);
291287726Smarius}
292287726Smarius
293287726Smariusstruct resource *
294287726Smariusofw_pci_alloc_resource(device_t bus, device_t child, int type, int *rid,
295287726Smarius    u_long start, u_long end, u_long count, u_int flags)
296287726Smarius{
297287726Smarius	struct ofw_pci_softc *sc;
298287726Smarius	struct resource *rv;
299287726Smarius	struct rman *rm;
300287726Smarius
301287726Smarius	sc = device_get_softc(bus);
302287726Smarius	switch (type) {
303287726Smarius	case SYS_RES_IRQ:
304287726Smarius		/*
305287726Smarius		 * XXX: Don't accept blank ranges for now, only single
306287726Smarius		 * interrupts.  The other case should not happen with
307287726Smarius		 * the MI PCI code ...
308287726Smarius		 * XXX: This may return a resource that is out of the
309287726Smarius		 * range that was specified.  Is this correct ...?
310287726Smarius		 */
311287726Smarius		if (start != end)
312287726Smarius			panic("%s: XXX: interrupt range", __func__);
313287726Smarius		return (bus_generic_alloc_resource(bus, child, type, rid,
314287726Smarius		    start, end, count, flags));
315287726Smarius	case SYS_RES_MEMORY:
316287726Smarius		rm = &sc->sc_pci_mem_rman;
317287726Smarius		break;
318287726Smarius	case SYS_RES_IOPORT:
319287726Smarius		rm = &sc->sc_pci_io_rman;
320287726Smarius		break;
321287726Smarius	default:
322287726Smarius		return (NULL);
323287726Smarius	}
324287726Smarius
325287726Smarius	rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE,
326287726Smarius	    child);
327287726Smarius	if (rv == NULL)
328287726Smarius		return (NULL);
329287726Smarius	rman_set_rid(rv, *rid);
330287726Smarius
331287726Smarius	if ((flags & RF_ACTIVE) != 0 && bus_activate_resource(child, type,
332287726Smarius	    *rid, rv) != 0) {
333287726Smarius		rman_release_resource(rv);
334287726Smarius		return (NULL);
335287726Smarius	}
336287726Smarius	return (rv);
337287726Smarius}
338287726Smarius
339287726Smariusint
340287726Smariusofw_pci_activate_resource(device_t bus, device_t child, int type, int rid,
341287726Smarius    struct resource *r)
342287726Smarius{
343287726Smarius	struct ofw_pci_softc *sc;
344287726Smarius	struct bus_space_tag *tag;
345287726Smarius
346287726Smarius	sc = device_get_softc(bus);
347287726Smarius	switch (type) {
348287726Smarius	case SYS_RES_IRQ:
349287726Smarius		return (bus_generic_activate_resource(bus, child, type, rid,
350287726Smarius		    r));
351287726Smarius	case SYS_RES_MEMORY:
352292789Smarius		tag = sparc64_alloc_bus_tag(r, &nexus_bustag,
353292789Smarius		    PCI_MEMORY_BUS_SPACE, NULL);
354287726Smarius		if (tag == NULL)
355287726Smarius			return (ENOMEM);
356287726Smarius		rman_set_bustag(r, tag);
357287726Smarius		rman_set_bushandle(r, sc->sc_pci_bh[OFW_PCI_CS_MEM32] +
358287726Smarius		    rman_get_start(r));
359287726Smarius		break;
360287726Smarius	case SYS_RES_IOPORT:
361287726Smarius		rman_set_bustag(r, sc->sc_pci_iot);
362287726Smarius		rman_set_bushandle(r, sc->sc_pci_bh[OFW_PCI_CS_IO] +
363287726Smarius		    rman_get_start(r));
364287726Smarius		break;
365287726Smarius	}
366287726Smarius	return (rman_activate_resource(r));
367287726Smarius}
368287726Smarius
369287726Smariusint
370287726Smariusofw_pci_adjust_resource(device_t bus, device_t child, int type,
371287726Smarius    struct resource *r, u_long start, u_long end)
372287726Smarius{
373287726Smarius	struct ofw_pci_softc *sc;
374287726Smarius	struct rman *rm;
375287726Smarius
376287726Smarius	sc = device_get_softc(bus);
377287726Smarius	switch (type) {
378287726Smarius	case SYS_RES_IRQ:
379287726Smarius		return (bus_generic_adjust_resource(bus, child, type, r,
380287726Smarius		    start, end));
381287726Smarius	case SYS_RES_MEMORY:
382287726Smarius		rm = &sc->sc_pci_mem_rman;
383287726Smarius		break;
384287726Smarius	case SYS_RES_IOPORT:
385287726Smarius		rm = &sc->sc_pci_io_rman;
386287726Smarius		break;
387287726Smarius	default:
388287726Smarius		return (EINVAL);
389287726Smarius	}
390287726Smarius	if (rman_is_region_manager(r, rm) == 0)
391287726Smarius		return (EINVAL);
392287726Smarius	return (rman_adjust_resource(r, start, end));
393287726Smarius}
394287726Smarius
395287726Smariusbus_dma_tag_t
396287726Smariusofw_pci_get_dma_tag(device_t bus, device_t child __unused)
397287726Smarius{
398287726Smarius	struct ofw_pci_softc *sc;
399287726Smarius
400287726Smarius	sc = device_get_softc(bus);
401287726Smarius	return (sc->sc_pci_dmat);
402287726Smarius}
403287726Smarius
404287726Smariusphandle_t
405287726Smariusofw_pci_get_node(device_t bus, device_t child __unused)
406287726Smarius{
407287726Smarius	struct ofw_pci_softc *sc;
408287726Smarius
409287726Smarius	sc = device_get_softc(bus);
410287726Smarius	/* We only have one child, the PCI bus, which needs our own node. */
411287726Smarius	return (sc->sc_node);
412287726Smarius}
413