xref: /freebsd/sys/dev/xilinx/xlnx_pcib.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
1dee4c1d2SRuslan Bukin /*-
2dee4c1d2SRuslan Bukin  * SPDX-License-Identifier: BSD-2-Clause
3dee4c1d2SRuslan Bukin  *
4dee4c1d2SRuslan Bukin  * Copyright (c) 2020 Ruslan Bukin <br@bsdpad.com>
5dee4c1d2SRuslan Bukin  *
6dee4c1d2SRuslan Bukin  * This software was developed by SRI International and the University of
7dee4c1d2SRuslan Bukin  * Cambridge Computer Laboratory (Department of Computer Science and
8dee4c1d2SRuslan Bukin  * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
9dee4c1d2SRuslan Bukin  * DARPA SSITH research programme.
10dee4c1d2SRuslan Bukin  *
11dee4c1d2SRuslan Bukin  * Redistribution and use in source and binary forms, with or without
12dee4c1d2SRuslan Bukin  * modification, are permitted provided that the following conditions
13dee4c1d2SRuslan Bukin  * are met:
14dee4c1d2SRuslan Bukin  * 1. Redistributions of source code must retain the above copyright
15dee4c1d2SRuslan Bukin  *    notice, this list of conditions and the following disclaimer.
16dee4c1d2SRuslan Bukin  * 2. Redistributions in binary form must reproduce the above copyright
17dee4c1d2SRuslan Bukin  *    notice, this list of conditions and the following disclaimer in the
18dee4c1d2SRuslan Bukin  *    documentation and/or other materials provided with the distribution.
19dee4c1d2SRuslan Bukin  *
20dee4c1d2SRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21dee4c1d2SRuslan Bukin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22dee4c1d2SRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23dee4c1d2SRuslan Bukin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24dee4c1d2SRuslan Bukin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25dee4c1d2SRuslan Bukin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26dee4c1d2SRuslan Bukin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27dee4c1d2SRuslan Bukin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28dee4c1d2SRuslan Bukin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29dee4c1d2SRuslan Bukin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30dee4c1d2SRuslan Bukin  * SUCH DAMAGE.
31dee4c1d2SRuslan Bukin  */
32dee4c1d2SRuslan Bukin 
33dee4c1d2SRuslan Bukin #include "opt_platform.h"
34dee4c1d2SRuslan Bukin 
35dee4c1d2SRuslan Bukin #include <sys/param.h>
36dee4c1d2SRuslan Bukin #include <sys/systm.h>
37dee4c1d2SRuslan Bukin #include <sys/malloc.h>
38dee4c1d2SRuslan Bukin #include <sys/types.h>
39dee4c1d2SRuslan Bukin #include <sys/sysctl.h>
40dee4c1d2SRuslan Bukin #include <sys/kernel.h>
41dee4c1d2SRuslan Bukin #include <sys/rman.h>
42dee4c1d2SRuslan Bukin #include <sys/module.h>
43dee4c1d2SRuslan Bukin #include <sys/bus.h>
44dee4c1d2SRuslan Bukin #include <sys/endian.h>
45dee4c1d2SRuslan Bukin #include <sys/cpuset.h>
46dee4c1d2SRuslan Bukin #include <sys/mutex.h>
47dee4c1d2SRuslan Bukin #include <sys/proc.h>
48dee4c1d2SRuslan Bukin 
49dee4c1d2SRuslan Bukin #include <machine/intr.h>
50dee4c1d2SRuslan Bukin #include <machine/bus.h>
51dee4c1d2SRuslan Bukin 
52dee4c1d2SRuslan Bukin #include <vm/vm.h>
53dee4c1d2SRuslan Bukin #include <vm/vm_extern.h>
54dee4c1d2SRuslan Bukin #include <vm/vm_kern.h>
55dee4c1d2SRuslan Bukin #include <vm/pmap.h>
56dee4c1d2SRuslan Bukin 
57dee4c1d2SRuslan Bukin #include <dev/ofw/openfirm.h>
58dee4c1d2SRuslan Bukin #include <dev/ofw/ofw_bus.h>
59dee4c1d2SRuslan Bukin #include <dev/ofw/ofw_bus_subr.h>
60dee4c1d2SRuslan Bukin 
61dee4c1d2SRuslan Bukin #include <dev/pci/pcireg.h>
62dee4c1d2SRuslan Bukin #include <dev/pci/pcivar.h>
63dee4c1d2SRuslan Bukin #include <dev/pci/pci_host_generic.h>
64dee4c1d2SRuslan Bukin #include <dev/pci/pci_host_generic_fdt.h>
65dee4c1d2SRuslan Bukin #include <dev/pci/pcib_private.h>
66dee4c1d2SRuslan Bukin 
67dee4c1d2SRuslan Bukin #include "xlnx_pcib.h"
68dee4c1d2SRuslan Bukin 
69dee4c1d2SRuslan Bukin #include "ofw_bus_if.h"
70dee4c1d2SRuslan Bukin #include "msi_if.h"
71dee4c1d2SRuslan Bukin #include "pcib_if.h"
72dee4c1d2SRuslan Bukin #include "pic_if.h"
73dee4c1d2SRuslan Bukin 
74dee4c1d2SRuslan Bukin #define	XLNX_PCIB_MAX_MSI	64
75dee4c1d2SRuslan Bukin 
76dee4c1d2SRuslan Bukin static int xlnx_pcib_fdt_attach(device_t);
77dee4c1d2SRuslan Bukin static int xlnx_pcib_fdt_probe(device_t);
78dee4c1d2SRuslan Bukin static int xlnx_pcib_fdt_get_id(device_t, device_t, enum pci_id_type,
79dee4c1d2SRuslan Bukin     uintptr_t *);
80dee4c1d2SRuslan Bukin static void xlnx_pcib_msi_mask(device_t dev, struct intr_irqsrc *isrc,
81dee4c1d2SRuslan Bukin     bool mask);
82dee4c1d2SRuslan Bukin 
83dee4c1d2SRuslan Bukin struct xlnx_pcib_softc {
84dee4c1d2SRuslan Bukin 	struct generic_pcie_fdt_softc	fdt_sc;
85dee4c1d2SRuslan Bukin 	struct resource			*res[4];
86dee4c1d2SRuslan Bukin 	struct mtx			mtx;
87dee4c1d2SRuslan Bukin 	vm_offset_t			msi_page;
88dee4c1d2SRuslan Bukin 	struct xlnx_pcib_irqsrc		*isrcs;
89dee4c1d2SRuslan Bukin 	device_t			dev;
90dee4c1d2SRuslan Bukin 	void				*intr_cookie[3];
91dee4c1d2SRuslan Bukin };
92dee4c1d2SRuslan Bukin 
93dee4c1d2SRuslan Bukin static struct resource_spec xlnx_pcib_spec[] = {
94dee4c1d2SRuslan Bukin 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
95dee4c1d2SRuslan Bukin 	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
96dee4c1d2SRuslan Bukin 	{ SYS_RES_IRQ,		1,	RF_ACTIVE },
97dee4c1d2SRuslan Bukin 	{ SYS_RES_IRQ,		2,	RF_ACTIVE },
98dee4c1d2SRuslan Bukin 	{ -1, 0 }
99dee4c1d2SRuslan Bukin };
100dee4c1d2SRuslan Bukin 
101dee4c1d2SRuslan Bukin struct xlnx_pcib_irqsrc {
102dee4c1d2SRuslan Bukin 	struct intr_irqsrc	isrc;
103dee4c1d2SRuslan Bukin 	u_int			irq;
104dee4c1d2SRuslan Bukin #define	XLNX_IRQ_FLAG_USED	(1 << 0)
105dee4c1d2SRuslan Bukin 	u_int			flags;
106dee4c1d2SRuslan Bukin };
107dee4c1d2SRuslan Bukin 
108dee4c1d2SRuslan Bukin static void
xlnx_pcib_clear_err_interrupts(struct generic_pcie_core_softc * sc)109dee4c1d2SRuslan Bukin xlnx_pcib_clear_err_interrupts(struct generic_pcie_core_softc *sc)
110dee4c1d2SRuslan Bukin {
111dee4c1d2SRuslan Bukin 	uint32_t reg;
112dee4c1d2SRuslan Bukin 
113dee4c1d2SRuslan Bukin 	reg = bus_read_4(sc->res, XLNX_PCIE_RPERRFRR);
114dee4c1d2SRuslan Bukin 
115dee4c1d2SRuslan Bukin 	if (reg & RPERRFRR_VALID) {
116dee4c1d2SRuslan Bukin 		device_printf(sc->dev, "Requested ID: %x\n",
117dee4c1d2SRuslan Bukin 		    reg & RPERRFRR_REQ_ID_M);
118dee4c1d2SRuslan Bukin 		bus_write_4(sc->res, XLNX_PCIE_RPERRFRR, ~0U);
119dee4c1d2SRuslan Bukin 	}
120dee4c1d2SRuslan Bukin }
121dee4c1d2SRuslan Bukin 
122dee4c1d2SRuslan Bukin static int
xlnx_pcib_intr(void * arg)123dee4c1d2SRuslan Bukin xlnx_pcib_intr(void *arg)
124dee4c1d2SRuslan Bukin {
125dee4c1d2SRuslan Bukin 	struct generic_pcie_fdt_softc *fdt_sc;
126dee4c1d2SRuslan Bukin 	struct generic_pcie_core_softc *sc;
127dee4c1d2SRuslan Bukin 	struct xlnx_pcib_softc *xlnx_sc;
128dee4c1d2SRuslan Bukin 	uint32_t val, mask, status;
129dee4c1d2SRuslan Bukin 
130dee4c1d2SRuslan Bukin 	xlnx_sc = arg;
131dee4c1d2SRuslan Bukin 	fdt_sc = &xlnx_sc->fdt_sc;
132dee4c1d2SRuslan Bukin 	sc = &fdt_sc->base;
133dee4c1d2SRuslan Bukin 
134dee4c1d2SRuslan Bukin 	val = bus_read_4(sc->res, XLNX_PCIE_IDR);
135dee4c1d2SRuslan Bukin 	mask = bus_read_4(sc->res, XLNX_PCIE_IMR);
136dee4c1d2SRuslan Bukin 
137dee4c1d2SRuslan Bukin 	status = val & mask;
138dee4c1d2SRuslan Bukin 	if (!status)
139dee4c1d2SRuslan Bukin 		return (FILTER_HANDLED);
140dee4c1d2SRuslan Bukin 
141dee4c1d2SRuslan Bukin 	if (status & IMR_LINK_DOWN)
142dee4c1d2SRuslan Bukin 		device_printf(sc->dev, "Link down");
143dee4c1d2SRuslan Bukin 
144dee4c1d2SRuslan Bukin 	if (status & IMR_HOT_RESET)
145dee4c1d2SRuslan Bukin 		device_printf(sc->dev, "Hot reset");
146dee4c1d2SRuslan Bukin 
147dee4c1d2SRuslan Bukin 	if (status & IMR_CORRECTABLE)
148dee4c1d2SRuslan Bukin 		xlnx_pcib_clear_err_interrupts(sc);
149dee4c1d2SRuslan Bukin 
150dee4c1d2SRuslan Bukin 	if (status & IMR_FATAL)
151dee4c1d2SRuslan Bukin 		xlnx_pcib_clear_err_interrupts(sc);
152dee4c1d2SRuslan Bukin 
153dee4c1d2SRuslan Bukin 	if (status & IMR_NON_FATAL)
154dee4c1d2SRuslan Bukin 		xlnx_pcib_clear_err_interrupts(sc);
155dee4c1d2SRuslan Bukin 
156dee4c1d2SRuslan Bukin 	if (status & IMR_MSI) {
157dee4c1d2SRuslan Bukin 		device_printf(sc->dev, "MSI interrupt");
158dee4c1d2SRuslan Bukin 
159dee4c1d2SRuslan Bukin 		/* FIFO mode MSI not implemented. */
160dee4c1d2SRuslan Bukin 	}
161dee4c1d2SRuslan Bukin 
162dee4c1d2SRuslan Bukin 	if (status & IMR_INTX) {
163dee4c1d2SRuslan Bukin 		device_printf(sc->dev, "INTx received");
164dee4c1d2SRuslan Bukin 
165dee4c1d2SRuslan Bukin 		/* Not implemented. */
166dee4c1d2SRuslan Bukin 	}
167dee4c1d2SRuslan Bukin 
168dee4c1d2SRuslan Bukin 	if (status & IMR_SLAVE_UNSUPP_REQ)
169dee4c1d2SRuslan Bukin 		device_printf(sc->dev, "Slave unsupported request");
170dee4c1d2SRuslan Bukin 
171dee4c1d2SRuslan Bukin 	if (status & IMR_SLAVE_UNEXP_COMPL)
172dee4c1d2SRuslan Bukin 		device_printf(sc->dev, "Slave unexpected completion");
173dee4c1d2SRuslan Bukin 
174dee4c1d2SRuslan Bukin 	if (status & IMR_SLAVE_COMPL_TIMOUT)
175dee4c1d2SRuslan Bukin 		device_printf(sc->dev, "Slave completion timeout");
176dee4c1d2SRuslan Bukin 
177dee4c1d2SRuslan Bukin 	if (status & IMR_SLAVE_ERROR_POISON)
178dee4c1d2SRuslan Bukin 		device_printf(sc->dev, "Slave error poison");
179dee4c1d2SRuslan Bukin 
180dee4c1d2SRuslan Bukin 	if (status & IMR_SLAVE_COMPL_ABORT)
181dee4c1d2SRuslan Bukin 		device_printf(sc->dev, "Slave completion abort");
182dee4c1d2SRuslan Bukin 
183dee4c1d2SRuslan Bukin 	if (status & IMR_SLAVE_ILLEG_BURST)
184dee4c1d2SRuslan Bukin 		device_printf(sc->dev, "Slave illegal burst");
185dee4c1d2SRuslan Bukin 
186dee4c1d2SRuslan Bukin 	if (status & IMR_MASTER_DECERR)
187dee4c1d2SRuslan Bukin 		device_printf(sc->dev, "Master decode error");
188dee4c1d2SRuslan Bukin 
189dee4c1d2SRuslan Bukin 	if (status & IMR_MASTER_SLVERR)
190dee4c1d2SRuslan Bukin 		device_printf(sc->dev, "Master slave error");
191dee4c1d2SRuslan Bukin 
192dee4c1d2SRuslan Bukin 	bus_write_4(sc->res, XLNX_PCIE_IDR, val);
193dee4c1d2SRuslan Bukin 
194dee4c1d2SRuslan Bukin 	return (FILTER_HANDLED);
195dee4c1d2SRuslan Bukin }
196dee4c1d2SRuslan Bukin 
197dee4c1d2SRuslan Bukin static void
xlnx_pcib_handle_msi_intr(void * arg,int msireg)198dee4c1d2SRuslan Bukin xlnx_pcib_handle_msi_intr(void *arg, int msireg)
199dee4c1d2SRuslan Bukin {
200dee4c1d2SRuslan Bukin 	struct generic_pcie_fdt_softc *fdt_sc;
201dee4c1d2SRuslan Bukin 	struct generic_pcie_core_softc *sc;
202dee4c1d2SRuslan Bukin 	struct xlnx_pcib_softc *xlnx_sc;
203dee4c1d2SRuslan Bukin 	struct xlnx_pcib_irqsrc *xi;
204dee4c1d2SRuslan Bukin 	struct trapframe *tf;
205dee4c1d2SRuslan Bukin 	int irq;
206dee4c1d2SRuslan Bukin 	int reg;
207dee4c1d2SRuslan Bukin 	int i;
208dee4c1d2SRuslan Bukin 
209dee4c1d2SRuslan Bukin 	xlnx_sc = arg;
210dee4c1d2SRuslan Bukin 	fdt_sc = &xlnx_sc->fdt_sc;
211dee4c1d2SRuslan Bukin 	sc = &fdt_sc->base;
212dee4c1d2SRuslan Bukin 	tf = curthread->td_intr_frame;
213dee4c1d2SRuslan Bukin 
214dee4c1d2SRuslan Bukin 	do {
215dee4c1d2SRuslan Bukin 		reg = bus_read_4(sc->res, msireg);
216dee4c1d2SRuslan Bukin 
217dee4c1d2SRuslan Bukin 		for (i = 0; i < sizeof(uint32_t) * 8; i++) {
218dee4c1d2SRuslan Bukin 			if (reg & (1 << i)) {
219dee4c1d2SRuslan Bukin 				bus_write_4(sc->res, msireg, (1 << i));
220dee4c1d2SRuslan Bukin 
221dee4c1d2SRuslan Bukin 				irq = i;
222dee4c1d2SRuslan Bukin 				if (msireg == XLNX_PCIE_RPMSIID2)
223dee4c1d2SRuslan Bukin 					irq += 32;
224dee4c1d2SRuslan Bukin 
225dee4c1d2SRuslan Bukin 				xi = &xlnx_sc->isrcs[irq];
226dee4c1d2SRuslan Bukin 				if (intr_isrc_dispatch(&xi->isrc, tf) != 0) {
227dee4c1d2SRuslan Bukin 					/* Disable stray. */
228dee4c1d2SRuslan Bukin 					xlnx_pcib_msi_mask(sc->dev,
229dee4c1d2SRuslan Bukin 					    &xi->isrc, 1);
230dee4c1d2SRuslan Bukin 					device_printf(sc->dev,
231dee4c1d2SRuslan Bukin 					    "Stray irq %u disabled\n", irq);
232dee4c1d2SRuslan Bukin 				}
233dee4c1d2SRuslan Bukin 			}
234dee4c1d2SRuslan Bukin 		}
235dee4c1d2SRuslan Bukin 	} while (reg != 0);
236dee4c1d2SRuslan Bukin }
237dee4c1d2SRuslan Bukin 
238dee4c1d2SRuslan Bukin static int
xlnx_pcib_msi0_intr(void * arg)239dee4c1d2SRuslan Bukin xlnx_pcib_msi0_intr(void *arg)
240dee4c1d2SRuslan Bukin {
241dee4c1d2SRuslan Bukin 
242dee4c1d2SRuslan Bukin 	xlnx_pcib_handle_msi_intr(arg, XLNX_PCIE_RPMSIID1);
243dee4c1d2SRuslan Bukin 
244dee4c1d2SRuslan Bukin 	return (FILTER_HANDLED);
245dee4c1d2SRuslan Bukin }
246dee4c1d2SRuslan Bukin 
247dee4c1d2SRuslan Bukin static int
xlnx_pcib_msi1_intr(void * arg)248dee4c1d2SRuslan Bukin xlnx_pcib_msi1_intr(void *arg)
249dee4c1d2SRuslan Bukin {
250dee4c1d2SRuslan Bukin 
251dee4c1d2SRuslan Bukin 	xlnx_pcib_handle_msi_intr(arg, XLNX_PCIE_RPMSIID2);
252dee4c1d2SRuslan Bukin 
253dee4c1d2SRuslan Bukin 	return (FILTER_HANDLED);
254dee4c1d2SRuslan Bukin }
255dee4c1d2SRuslan Bukin 
256dee4c1d2SRuslan Bukin static int
xlnx_pcib_register_msi(struct xlnx_pcib_softc * sc)257dee4c1d2SRuslan Bukin xlnx_pcib_register_msi(struct xlnx_pcib_softc *sc)
258dee4c1d2SRuslan Bukin {
259dee4c1d2SRuslan Bukin 	const char *name;
260dee4c1d2SRuslan Bukin 	int error;
261dee4c1d2SRuslan Bukin 	int irq;
262dee4c1d2SRuslan Bukin 
263dee4c1d2SRuslan Bukin 	sc->isrcs = malloc(sizeof(*sc->isrcs) * XLNX_PCIB_MAX_MSI, M_DEVBUF,
264dee4c1d2SRuslan Bukin 	    M_WAITOK | M_ZERO);
265dee4c1d2SRuslan Bukin 
266dee4c1d2SRuslan Bukin 	name = device_get_nameunit(sc->dev);
267dee4c1d2SRuslan Bukin 
268dee4c1d2SRuslan Bukin 	for (irq = 0; irq < XLNX_PCIB_MAX_MSI; irq++) {
269dee4c1d2SRuslan Bukin 		sc->isrcs[irq].irq = irq;
270dee4c1d2SRuslan Bukin 		error = intr_isrc_register(&sc->isrcs[irq].isrc,
271dee4c1d2SRuslan Bukin 		    sc->dev, 0, "%s,%u", name, irq);
272dee4c1d2SRuslan Bukin 		if (error != 0)
273dee4c1d2SRuslan Bukin 			return (error); /* XXX deregister ISRCs */
274dee4c1d2SRuslan Bukin 	}
275dee4c1d2SRuslan Bukin 
276dee4c1d2SRuslan Bukin 	if (intr_msi_register(sc->dev,
277dee4c1d2SRuslan Bukin 	    OF_xref_from_node(ofw_bus_get_node(sc->dev))) != 0)
278dee4c1d2SRuslan Bukin 		return (ENXIO);
279dee4c1d2SRuslan Bukin 
280dee4c1d2SRuslan Bukin 	return (0);
281dee4c1d2SRuslan Bukin }
282dee4c1d2SRuslan Bukin 
283dee4c1d2SRuslan Bukin static void
xlnx_pcib_init(struct xlnx_pcib_softc * sc)284dee4c1d2SRuslan Bukin xlnx_pcib_init(struct xlnx_pcib_softc *sc)
285dee4c1d2SRuslan Bukin {
286dee4c1d2SRuslan Bukin 	bus_addr_t addr;
287dee4c1d2SRuslan Bukin 	int reg;
288dee4c1d2SRuslan Bukin 
289dee4c1d2SRuslan Bukin 	/* Disable interrupts. */
290dee4c1d2SRuslan Bukin 	bus_write_4(sc->res[0], XLNX_PCIE_IMR, 0);
291dee4c1d2SRuslan Bukin 
292dee4c1d2SRuslan Bukin 	/* Clear pending interrupts.*/
293dee4c1d2SRuslan Bukin 	reg = bus_read_4(sc->res[0], XLNX_PCIE_IDR);
294dee4c1d2SRuslan Bukin 	bus_write_4(sc->res[0], XLNX_PCIE_IDR, reg);
295dee4c1d2SRuslan Bukin 
296dee4c1d2SRuslan Bukin 	/* Setup an MSI page. */
297dee4c1d2SRuslan Bukin 	sc->msi_page = kmem_alloc_contig(PAGE_SIZE, M_WAITOK, 0,
298dee4c1d2SRuslan Bukin 	    BUS_SPACE_MAXADDR, PAGE_SIZE, 0, VM_MEMATTR_DEFAULT);
299dee4c1d2SRuslan Bukin 	addr = vtophys(sc->msi_page);
300dee4c1d2SRuslan Bukin 	bus_write_4(sc->res[0], XLNX_PCIE_RPMSIBR1, (addr >> 32));
301dee4c1d2SRuslan Bukin 	bus_write_4(sc->res[0], XLNX_PCIE_RPMSIBR2, (addr >>  0));
302dee4c1d2SRuslan Bukin 
303dee4c1d2SRuslan Bukin 	/* Enable the bridge. */
304dee4c1d2SRuslan Bukin 	reg = bus_read_4(sc->res[0], XLNX_PCIE_RPSCR);
305dee4c1d2SRuslan Bukin 	reg |= RPSCR_BE;
306dee4c1d2SRuslan Bukin 	bus_write_4(sc->res[0], XLNX_PCIE_RPSCR, reg);
307dee4c1d2SRuslan Bukin 
308dee4c1d2SRuslan Bukin 	/* Enable interrupts. */
309dee4c1d2SRuslan Bukin 	reg = IMR_LINK_DOWN
310dee4c1d2SRuslan Bukin 		| IMR_HOT_RESET
311dee4c1d2SRuslan Bukin 		| IMR_CFG_COMPL_STATUS_M
312dee4c1d2SRuslan Bukin 		| IMR_CFG_TIMEOUT
313dee4c1d2SRuslan Bukin 		| IMR_CORRECTABLE
314dee4c1d2SRuslan Bukin 		| IMR_NON_FATAL
315dee4c1d2SRuslan Bukin 		| IMR_FATAL
316dee4c1d2SRuslan Bukin 		| IMR_INTX
317dee4c1d2SRuslan Bukin 		| IMR_MSI
318dee4c1d2SRuslan Bukin 		| IMR_SLAVE_UNSUPP_REQ
319dee4c1d2SRuslan Bukin 		| IMR_SLAVE_UNEXP_COMPL
320dee4c1d2SRuslan Bukin 		| IMR_SLAVE_COMPL_TIMOUT
321dee4c1d2SRuslan Bukin 		| IMR_SLAVE_ERROR_POISON
322dee4c1d2SRuslan Bukin 		| IMR_SLAVE_COMPL_ABORT
323dee4c1d2SRuslan Bukin 		| IMR_SLAVE_ILLEG_BURST
324dee4c1d2SRuslan Bukin 		| IMR_MASTER_DECERR
325dee4c1d2SRuslan Bukin 		| IMR_MASTER_SLVERR;
326dee4c1d2SRuslan Bukin 	bus_write_4(sc->res[0], XLNX_PCIE_IMR, reg);
327dee4c1d2SRuslan Bukin }
328dee4c1d2SRuslan Bukin 
329dee4c1d2SRuslan Bukin static int
xlnx_pcib_fdt_probe(device_t dev)330dee4c1d2SRuslan Bukin xlnx_pcib_fdt_probe(device_t dev)
331dee4c1d2SRuslan Bukin {
332dee4c1d2SRuslan Bukin 
333dee4c1d2SRuslan Bukin 	if (!ofw_bus_status_okay(dev))
334dee4c1d2SRuslan Bukin 		return (ENXIO);
335dee4c1d2SRuslan Bukin 
336dee4c1d2SRuslan Bukin 	if (ofw_bus_is_compatible(dev, "xlnx,xdma-host-3.00")) {
337dee4c1d2SRuslan Bukin 		device_set_desc(dev, "Xilinx XDMA PCIe Controller");
338dee4c1d2SRuslan Bukin 		return (BUS_PROBE_DEFAULT);
339dee4c1d2SRuslan Bukin 	}
340dee4c1d2SRuslan Bukin 
341dee4c1d2SRuslan Bukin 	return (ENXIO);
342dee4c1d2SRuslan Bukin }
343dee4c1d2SRuslan Bukin 
344dee4c1d2SRuslan Bukin static int
xlnx_pcib_fdt_attach(device_t dev)345dee4c1d2SRuslan Bukin xlnx_pcib_fdt_attach(device_t dev)
346dee4c1d2SRuslan Bukin {
347dee4c1d2SRuslan Bukin 	struct xlnx_pcib_softc *sc;
348dee4c1d2SRuslan Bukin 	int error;
349dee4c1d2SRuslan Bukin 
350dee4c1d2SRuslan Bukin 	sc = device_get_softc(dev);
351dee4c1d2SRuslan Bukin 	sc->dev = dev;
352dee4c1d2SRuslan Bukin 
353dee4c1d2SRuslan Bukin 	mtx_init(&sc->mtx, "msi_mtx", NULL, MTX_DEF);
354dee4c1d2SRuslan Bukin 
355dee4c1d2SRuslan Bukin 	if (bus_alloc_resources(dev, xlnx_pcib_spec, sc->res)) {
356dee4c1d2SRuslan Bukin 		device_printf(dev, "could not allocate resources\n");
357dee4c1d2SRuslan Bukin 		return (ENXIO);
358dee4c1d2SRuslan Bukin 	}
359dee4c1d2SRuslan Bukin 
360dee4c1d2SRuslan Bukin 	/* Setup MISC interrupt handler. */
361dee4c1d2SRuslan Bukin 	error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,
362dee4c1d2SRuslan Bukin 	    xlnx_pcib_intr, NULL, sc, &sc->intr_cookie[0]);
363dee4c1d2SRuslan Bukin 	if (error != 0) {
364dee4c1d2SRuslan Bukin 		device_printf(dev, "could not setup interrupt handler.\n");
365dee4c1d2SRuslan Bukin 		return (ENXIO);
366dee4c1d2SRuslan Bukin 	}
367dee4c1d2SRuslan Bukin 
368dee4c1d2SRuslan Bukin 	/* Setup MSI0 interrupt handler. */
369dee4c1d2SRuslan Bukin 	error = bus_setup_intr(dev, sc->res[2], INTR_TYPE_MISC | INTR_MPSAFE,
370dee4c1d2SRuslan Bukin 	    xlnx_pcib_msi0_intr, NULL, sc, &sc->intr_cookie[1]);
371dee4c1d2SRuslan Bukin 	if (error != 0) {
372dee4c1d2SRuslan Bukin 		device_printf(dev, "could not setup interrupt handler.\n");
373dee4c1d2SRuslan Bukin 		return (ENXIO);
374dee4c1d2SRuslan Bukin 	}
375dee4c1d2SRuslan Bukin 
376dee4c1d2SRuslan Bukin 	/* Setup MSI1 interrupt handler. */
377dee4c1d2SRuslan Bukin 	error = bus_setup_intr(dev, sc->res[3], INTR_TYPE_MISC | INTR_MPSAFE,
378dee4c1d2SRuslan Bukin 	    xlnx_pcib_msi1_intr, NULL, sc, &sc->intr_cookie[2]);
379dee4c1d2SRuslan Bukin 	if (error != 0) {
380dee4c1d2SRuslan Bukin 		device_printf(dev, "could not setup interrupt handler.\n");
381dee4c1d2SRuslan Bukin 		return (ENXIO);
382dee4c1d2SRuslan Bukin 	}
383dee4c1d2SRuslan Bukin 
384dee4c1d2SRuslan Bukin 	xlnx_pcib_init(sc);
385dee4c1d2SRuslan Bukin 
386dee4c1d2SRuslan Bukin 	/*
387dee4c1d2SRuslan Bukin 	 * Allow the core driver to map registers.
388dee4c1d2SRuslan Bukin 	 * We will be accessing the device memory using core_softc.
389dee4c1d2SRuslan Bukin 	 */
390dee4c1d2SRuslan Bukin 	bus_release_resources(dev, xlnx_pcib_spec, sc->res);
391dee4c1d2SRuslan Bukin 
392dee4c1d2SRuslan Bukin 	error = xlnx_pcib_register_msi(sc);
393dee4c1d2SRuslan Bukin 	if (error)
394dee4c1d2SRuslan Bukin 		return (error);
395dee4c1d2SRuslan Bukin 
396*ad52fba1SAndrew Turner 	return (pci_host_generic_fdt_attach(dev));
397dee4c1d2SRuslan Bukin }
398dee4c1d2SRuslan Bukin 
399dee4c1d2SRuslan Bukin static int
xlnx_pcib_fdt_get_id(device_t pci,device_t child,enum pci_id_type type,uintptr_t * id)400dee4c1d2SRuslan Bukin xlnx_pcib_fdt_get_id(device_t pci, device_t child, enum pci_id_type type,
401dee4c1d2SRuslan Bukin     uintptr_t *id)
402dee4c1d2SRuslan Bukin {
403dee4c1d2SRuslan Bukin 	phandle_t node;
404dee4c1d2SRuslan Bukin 	int bsf;
405dee4c1d2SRuslan Bukin 
406dee4c1d2SRuslan Bukin 	if (type != PCI_ID_MSI)
407dee4c1d2SRuslan Bukin 		return (pcib_get_id(pci, child, type, id));
408dee4c1d2SRuslan Bukin 
409dee4c1d2SRuslan Bukin 	node = ofw_bus_get_node(pci);
410dee4c1d2SRuslan Bukin 	if (OF_hasprop(node, "msi-map"))
411dee4c1d2SRuslan Bukin 		return (generic_pcie_get_id(pci, child, type, id));
412dee4c1d2SRuslan Bukin 
413dee4c1d2SRuslan Bukin 	bsf = pci_get_rid(child);
414dee4c1d2SRuslan Bukin 	*id = (pci_get_domain(child) << PCI_RID_DOMAIN_SHIFT) | bsf;
415dee4c1d2SRuslan Bukin 
416dee4c1d2SRuslan Bukin 	return (0);
417dee4c1d2SRuslan Bukin }
418dee4c1d2SRuslan Bukin 
419dee4c1d2SRuslan Bukin static int
xlnx_pcib_req_valid(struct generic_pcie_core_softc * sc,u_int bus,u_int slot,u_int func,u_int reg)420dee4c1d2SRuslan Bukin xlnx_pcib_req_valid(struct generic_pcie_core_softc *sc,
421dee4c1d2SRuslan Bukin     u_int bus, u_int slot, u_int func, u_int reg)
422dee4c1d2SRuslan Bukin {
423dee4c1d2SRuslan Bukin 	bus_space_handle_t h;
424dee4c1d2SRuslan Bukin 	bus_space_tag_t t;
425dee4c1d2SRuslan Bukin 	uint32_t val;
426dee4c1d2SRuslan Bukin 
427dee4c1d2SRuslan Bukin 	t = sc->bst;
428dee4c1d2SRuslan Bukin 	h = sc->bsh;
429dee4c1d2SRuslan Bukin 
430dee4c1d2SRuslan Bukin 	if ((bus < sc->bus_start) || (bus > sc->bus_end))
431dee4c1d2SRuslan Bukin 		return (0);
432dee4c1d2SRuslan Bukin 	if ((slot > PCI_SLOTMAX) || (func > PCI_FUNCMAX) ||
433dee4c1d2SRuslan Bukin 	    (reg > PCIE_REGMAX))
434dee4c1d2SRuslan Bukin 		return (0);
435dee4c1d2SRuslan Bukin 
436dee4c1d2SRuslan Bukin 	if (bus == 0 && slot > 0)
437dee4c1d2SRuslan Bukin 		return (0);
438dee4c1d2SRuslan Bukin 
439dee4c1d2SRuslan Bukin 	val = bus_space_read_4(t, h, XLNX_PCIE_PHYSCR);
440dee4c1d2SRuslan Bukin 	if ((val & PHYSCR_LINK_UP) == 0) {
441dee4c1d2SRuslan Bukin 		/* Link is down */
442dee4c1d2SRuslan Bukin 		return (0);
443dee4c1d2SRuslan Bukin 	}
444dee4c1d2SRuslan Bukin 
445dee4c1d2SRuslan Bukin 	/* Valid */
446dee4c1d2SRuslan Bukin 
447dee4c1d2SRuslan Bukin 	return (1);
448dee4c1d2SRuslan Bukin }
449dee4c1d2SRuslan Bukin 
450dee4c1d2SRuslan Bukin static uint32_t
xlnx_pcib_read_config(device_t dev,u_int bus,u_int slot,u_int func,u_int reg,int bytes)451dee4c1d2SRuslan Bukin xlnx_pcib_read_config(device_t dev, u_int bus, u_int slot,
452dee4c1d2SRuslan Bukin     u_int func, u_int reg, int bytes)
453dee4c1d2SRuslan Bukin {
454dee4c1d2SRuslan Bukin 	struct generic_pcie_fdt_softc *fdt_sc;
455dee4c1d2SRuslan Bukin 	struct xlnx_pcib_softc *xlnx_sc;
456dee4c1d2SRuslan Bukin 	struct generic_pcie_core_softc *sc;
457dee4c1d2SRuslan Bukin 	bus_space_handle_t h;
458dee4c1d2SRuslan Bukin 	bus_space_tag_t t;
459dee4c1d2SRuslan Bukin 	uint64_t offset;
460dee4c1d2SRuslan Bukin 	uint32_t data;
461dee4c1d2SRuslan Bukin 
462dee4c1d2SRuslan Bukin 	xlnx_sc = device_get_softc(dev);
463dee4c1d2SRuslan Bukin 	fdt_sc = &xlnx_sc->fdt_sc;
464dee4c1d2SRuslan Bukin 	sc = &fdt_sc->base;
465dee4c1d2SRuslan Bukin 
466dee4c1d2SRuslan Bukin 	if (!xlnx_pcib_req_valid(sc, bus, slot, func, reg))
467dee4c1d2SRuslan Bukin 		return (~0U);
468dee4c1d2SRuslan Bukin 
469dee4c1d2SRuslan Bukin 	offset = PCIE_ADDR_OFFSET(bus - sc->bus_start, slot, func, reg);
470dee4c1d2SRuslan Bukin 	t = sc->bst;
471dee4c1d2SRuslan Bukin 	h = sc->bsh;
472dee4c1d2SRuslan Bukin 
473dee4c1d2SRuslan Bukin 	data = bus_space_read_4(t, h, offset & ~3);
474dee4c1d2SRuslan Bukin 
475dee4c1d2SRuslan Bukin 	switch (bytes) {
476dee4c1d2SRuslan Bukin 	case 1:
477dee4c1d2SRuslan Bukin 		data >>= (offset & 3) * 8;
478dee4c1d2SRuslan Bukin 		data &= 0xff;
479dee4c1d2SRuslan Bukin 		break;
480dee4c1d2SRuslan Bukin 	case 2:
481dee4c1d2SRuslan Bukin 		data >>= (offset & 3) * 8;
482dee4c1d2SRuslan Bukin 		data = le16toh(data);
483dee4c1d2SRuslan Bukin 		break;
484dee4c1d2SRuslan Bukin 	case 4:
485dee4c1d2SRuslan Bukin 		data = le32toh(data);
486dee4c1d2SRuslan Bukin 		break;
487dee4c1d2SRuslan Bukin 	default:
488dee4c1d2SRuslan Bukin 		return (~0U);
489dee4c1d2SRuslan Bukin 	}
490dee4c1d2SRuslan Bukin 
491dee4c1d2SRuslan Bukin 	return (data);
492dee4c1d2SRuslan Bukin }
493dee4c1d2SRuslan Bukin 
494dee4c1d2SRuslan Bukin static void
xlnx_pcib_write_config(device_t dev,u_int bus,u_int slot,u_int func,u_int reg,uint32_t val,int bytes)495dee4c1d2SRuslan Bukin xlnx_pcib_write_config(device_t dev, u_int bus, u_int slot,
496dee4c1d2SRuslan Bukin     u_int func, u_int reg, uint32_t val, int bytes)
497dee4c1d2SRuslan Bukin {
498dee4c1d2SRuslan Bukin 	struct generic_pcie_fdt_softc *fdt_sc;
499dee4c1d2SRuslan Bukin 	struct xlnx_pcib_softc *xlnx_sc;
500dee4c1d2SRuslan Bukin 	struct generic_pcie_core_softc *sc;
501dee4c1d2SRuslan Bukin 	bus_space_handle_t h;
502dee4c1d2SRuslan Bukin 	bus_space_tag_t t;
503dee4c1d2SRuslan Bukin 	uint64_t offset;
504dee4c1d2SRuslan Bukin 	uint32_t data;
505dee4c1d2SRuslan Bukin 
506dee4c1d2SRuslan Bukin 	xlnx_sc = device_get_softc(dev);
507dee4c1d2SRuslan Bukin 	fdt_sc = &xlnx_sc->fdt_sc;
508dee4c1d2SRuslan Bukin 	sc = &fdt_sc->base;
509dee4c1d2SRuslan Bukin 
510dee4c1d2SRuslan Bukin 	if (!xlnx_pcib_req_valid(sc, bus, slot, func, reg))
511dee4c1d2SRuslan Bukin 		return;
512dee4c1d2SRuslan Bukin 
513dee4c1d2SRuslan Bukin 	offset = PCIE_ADDR_OFFSET(bus - sc->bus_start, slot, func, reg);
514dee4c1d2SRuslan Bukin 
515dee4c1d2SRuslan Bukin 	t = sc->bst;
516dee4c1d2SRuslan Bukin 	h = sc->bsh;
517dee4c1d2SRuslan Bukin 
518dee4c1d2SRuslan Bukin 	/*
519dee4c1d2SRuslan Bukin 	 * 32-bit access used due to a bug in the Xilinx bridge that
520dee4c1d2SRuslan Bukin 	 * requires to write primary and secondary buses in one blast.
521dee4c1d2SRuslan Bukin 	 *
522dee4c1d2SRuslan Bukin 	 * TODO: This is probably wrong on big-endian.
523dee4c1d2SRuslan Bukin 	 */
524dee4c1d2SRuslan Bukin 	switch (bytes) {
525dee4c1d2SRuslan Bukin 	case 1:
526dee4c1d2SRuslan Bukin 		data = bus_space_read_4(t, h, offset & ~3);
527dee4c1d2SRuslan Bukin 		data &= ~(0xff << ((offset & 3) * 8));
528dee4c1d2SRuslan Bukin 		data |= (val & 0xff) << ((offset & 3) * 8);
529dee4c1d2SRuslan Bukin 		bus_space_write_4(t, h, offset & ~3, htole32(data));
530dee4c1d2SRuslan Bukin 		break;
531dee4c1d2SRuslan Bukin 	case 2:
532dee4c1d2SRuslan Bukin 		data = bus_space_read_4(t, h, offset & ~3);
533dee4c1d2SRuslan Bukin 		data &= ~(0xffff << ((offset & 3) * 8));
534dee4c1d2SRuslan Bukin 		data |= (val & 0xffff) << ((offset & 3) * 8);
535dee4c1d2SRuslan Bukin 		bus_space_write_4(t, h, offset & ~3, htole32(data));
536dee4c1d2SRuslan Bukin 		break;
537dee4c1d2SRuslan Bukin 	case 4:
538dee4c1d2SRuslan Bukin 		bus_space_write_4(t, h, offset, htole32(val));
539dee4c1d2SRuslan Bukin 		break;
540dee4c1d2SRuslan Bukin 	default:
541dee4c1d2SRuslan Bukin 		return;
542dee4c1d2SRuslan Bukin 	}
543dee4c1d2SRuslan Bukin }
544dee4c1d2SRuslan Bukin 
545dee4c1d2SRuslan Bukin static int
xlnx_pcib_alloc_msi(device_t pci,device_t child,int count,int maxcount,int * irqs)546dee4c1d2SRuslan Bukin xlnx_pcib_alloc_msi(device_t pci, device_t child, int count, int maxcount,
547dee4c1d2SRuslan Bukin     int *irqs)
548dee4c1d2SRuslan Bukin {
549dee4c1d2SRuslan Bukin 	phandle_t msi_parent;
550dee4c1d2SRuslan Bukin 
551dee4c1d2SRuslan Bukin 	ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent,
552dee4c1d2SRuslan Bukin 	    NULL);
553dee4c1d2SRuslan Bukin 	msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
554dee4c1d2SRuslan Bukin 	return (intr_alloc_msi(pci, child, msi_parent, count, maxcount,
555dee4c1d2SRuslan Bukin 	    irqs));
556dee4c1d2SRuslan Bukin }
557dee4c1d2SRuslan Bukin 
558dee4c1d2SRuslan Bukin static int
xlnx_pcib_release_msi(device_t pci,device_t child,int count,int * irqs)559dee4c1d2SRuslan Bukin xlnx_pcib_release_msi(device_t pci, device_t child, int count, int *irqs)
560dee4c1d2SRuslan Bukin {
561dee4c1d2SRuslan Bukin 	phandle_t msi_parent;
562dee4c1d2SRuslan Bukin 
563dee4c1d2SRuslan Bukin 	ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent,
564dee4c1d2SRuslan Bukin 	    NULL);
565dee4c1d2SRuslan Bukin 	msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
566dee4c1d2SRuslan Bukin 	return (intr_release_msi(pci, child, msi_parent, count, irqs));
567dee4c1d2SRuslan Bukin }
568dee4c1d2SRuslan Bukin 
569dee4c1d2SRuslan Bukin static int
xlnx_pcib_map_msi(device_t pci,device_t child,int irq,uint64_t * addr,uint32_t * data)570dee4c1d2SRuslan Bukin xlnx_pcib_map_msi(device_t pci, device_t child, int irq, uint64_t *addr,
571dee4c1d2SRuslan Bukin     uint32_t *data)
572dee4c1d2SRuslan Bukin {
573dee4c1d2SRuslan Bukin 	phandle_t msi_parent;
574dee4c1d2SRuslan Bukin 
575dee4c1d2SRuslan Bukin 	ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent,
576dee4c1d2SRuslan Bukin 	    NULL);
577dee4c1d2SRuslan Bukin 	msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
578dee4c1d2SRuslan Bukin 	return (intr_map_msi(pci, child, msi_parent, irq, addr, data));
579dee4c1d2SRuslan Bukin }
580dee4c1d2SRuslan Bukin 
581dee4c1d2SRuslan Bukin static int
xlnx_pcib_msi_alloc_msi(device_t dev,device_t child,int count,int maxcount,device_t * pic,struct intr_irqsrc ** srcs)582dee4c1d2SRuslan Bukin xlnx_pcib_msi_alloc_msi(device_t dev, device_t child, int count, int maxcount,
583dee4c1d2SRuslan Bukin     device_t *pic, struct intr_irqsrc **srcs)
584dee4c1d2SRuslan Bukin {
585dee4c1d2SRuslan Bukin 	struct xlnx_pcib_softc *sc;
586dee4c1d2SRuslan Bukin 	int irq, end_irq, i;
587dee4c1d2SRuslan Bukin 	bool found;
588dee4c1d2SRuslan Bukin 
589dee4c1d2SRuslan Bukin 	sc = device_get_softc(dev);
590dee4c1d2SRuslan Bukin 
591dee4c1d2SRuslan Bukin 	mtx_lock(&sc->mtx);
592dee4c1d2SRuslan Bukin 
593dee4c1d2SRuslan Bukin 	found = false;
594dee4c1d2SRuslan Bukin 
595dee4c1d2SRuslan Bukin 	for (irq = 0; (irq + count - 1) < XLNX_PCIB_MAX_MSI; irq++) {
596dee4c1d2SRuslan Bukin 		/* Assume the range is valid. */
597dee4c1d2SRuslan Bukin 		found = true;
598dee4c1d2SRuslan Bukin 
599dee4c1d2SRuslan Bukin 		/* Check this range is valid. */
600dee4c1d2SRuslan Bukin 		for (end_irq = irq; end_irq < irq + count; end_irq++) {
601dee4c1d2SRuslan Bukin 			if (sc->isrcs[end_irq].flags & XLNX_IRQ_FLAG_USED) {
602dee4c1d2SRuslan Bukin 				/* This is already used. */
603dee4c1d2SRuslan Bukin 				found = false;
604dee4c1d2SRuslan Bukin 				break;
605dee4c1d2SRuslan Bukin 			}
606dee4c1d2SRuslan Bukin 		}
607dee4c1d2SRuslan Bukin 
608dee4c1d2SRuslan Bukin 		if (found)
609dee4c1d2SRuslan Bukin 			break;
610dee4c1d2SRuslan Bukin 	}
611dee4c1d2SRuslan Bukin 
612dee4c1d2SRuslan Bukin 	if (!found || irq == (XLNX_PCIB_MAX_MSI - 1)) {
613dee4c1d2SRuslan Bukin 		/* Not enough interrupts were found. */
614dee4c1d2SRuslan Bukin 		mtx_unlock(&sc->mtx);
615dee4c1d2SRuslan Bukin 		return (ENXIO);
616dee4c1d2SRuslan Bukin 	}
617dee4c1d2SRuslan Bukin 
618dee4c1d2SRuslan Bukin 	/* Mark the interrupt as used. */
619dee4c1d2SRuslan Bukin 	for (i = 0; i < count; i++)
620dee4c1d2SRuslan Bukin 		sc->isrcs[irq + i].flags |= XLNX_IRQ_FLAG_USED;
621dee4c1d2SRuslan Bukin 
622dee4c1d2SRuslan Bukin 	mtx_unlock(&sc->mtx);
623dee4c1d2SRuslan Bukin 
624dee4c1d2SRuslan Bukin 	for (i = 0; i < count; i++)
625dee4c1d2SRuslan Bukin 		srcs[i] = (struct intr_irqsrc *)&sc->isrcs[irq + i];
626dee4c1d2SRuslan Bukin 
627dee4c1d2SRuslan Bukin 	*pic = device_get_parent(dev);
628dee4c1d2SRuslan Bukin 
629dee4c1d2SRuslan Bukin 	return (0);
630dee4c1d2SRuslan Bukin }
631dee4c1d2SRuslan Bukin 
632dee4c1d2SRuslan Bukin static int
xlnx_pcib_msi_release_msi(device_t dev,device_t child,int count,struct intr_irqsrc ** isrc)633dee4c1d2SRuslan Bukin xlnx_pcib_msi_release_msi(device_t dev, device_t child, int count,
634dee4c1d2SRuslan Bukin     struct intr_irqsrc **isrc)
635dee4c1d2SRuslan Bukin {
636dee4c1d2SRuslan Bukin 	struct xlnx_pcib_softc *sc;
637dee4c1d2SRuslan Bukin 	struct xlnx_pcib_irqsrc *xi;
638dee4c1d2SRuslan Bukin 	int i;
639dee4c1d2SRuslan Bukin 
640dee4c1d2SRuslan Bukin 	sc = device_get_softc(dev);
641dee4c1d2SRuslan Bukin 	mtx_lock(&sc->mtx);
642dee4c1d2SRuslan Bukin 	for (i = 0; i < count; i++) {
643dee4c1d2SRuslan Bukin 		xi = (struct xlnx_pcib_irqsrc *)isrc[i];
644dee4c1d2SRuslan Bukin 
645dee4c1d2SRuslan Bukin 		KASSERT(xi->flags & XLNX_IRQ_FLAG_USED,
646dee4c1d2SRuslan Bukin 		    ("%s: Releasing an unused MSI interrupt", __func__));
647dee4c1d2SRuslan Bukin 
648dee4c1d2SRuslan Bukin 		xi->flags &= ~XLNX_IRQ_FLAG_USED;
649dee4c1d2SRuslan Bukin 	}
650dee4c1d2SRuslan Bukin 
651dee4c1d2SRuslan Bukin 	mtx_unlock(&sc->mtx);
652dee4c1d2SRuslan Bukin 	return (0);
653dee4c1d2SRuslan Bukin }
654dee4c1d2SRuslan Bukin 
655dee4c1d2SRuslan Bukin static int
xlnx_pcib_msi_map_msi(device_t dev,device_t child,struct intr_irqsrc * isrc,uint64_t * addr,uint32_t * data)656dee4c1d2SRuslan Bukin xlnx_pcib_msi_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
657dee4c1d2SRuslan Bukin     uint64_t *addr, uint32_t *data)
658dee4c1d2SRuslan Bukin {
659dee4c1d2SRuslan Bukin 	struct xlnx_pcib_softc *sc;
660dee4c1d2SRuslan Bukin 	struct xlnx_pcib_irqsrc *xi;
661dee4c1d2SRuslan Bukin 
662dee4c1d2SRuslan Bukin 	sc = device_get_softc(dev);
663dee4c1d2SRuslan Bukin 	xi = (struct xlnx_pcib_irqsrc *)isrc;
664dee4c1d2SRuslan Bukin 
665dee4c1d2SRuslan Bukin 	*addr = vtophys(sc->msi_page);
666dee4c1d2SRuslan Bukin 	*data = xi->irq;
667dee4c1d2SRuslan Bukin 
668dee4c1d2SRuslan Bukin 	return (0);
669dee4c1d2SRuslan Bukin }
670dee4c1d2SRuslan Bukin 
671dee4c1d2SRuslan Bukin static void
xlnx_pcib_msi_mask(device_t dev,struct intr_irqsrc * isrc,bool mask)672dee4c1d2SRuslan Bukin xlnx_pcib_msi_mask(device_t dev, struct intr_irqsrc *isrc, bool mask)
673dee4c1d2SRuslan Bukin {
674dee4c1d2SRuslan Bukin 	struct generic_pcie_fdt_softc *fdt_sc;
675dee4c1d2SRuslan Bukin 	struct generic_pcie_core_softc *sc;
676dee4c1d2SRuslan Bukin 	struct xlnx_pcib_softc *xlnx_sc;
677dee4c1d2SRuslan Bukin 	struct xlnx_pcib_irqsrc *xi;
678dee4c1d2SRuslan Bukin 	uint32_t msireg, irq;
679dee4c1d2SRuslan Bukin 	uint32_t reg;
680dee4c1d2SRuslan Bukin 
681dee4c1d2SRuslan Bukin 	xlnx_sc = device_get_softc(dev);
682dee4c1d2SRuslan Bukin 	fdt_sc = &xlnx_sc->fdt_sc;
683dee4c1d2SRuslan Bukin 	sc = &fdt_sc->base;
684dee4c1d2SRuslan Bukin 
685dee4c1d2SRuslan Bukin 	xi = (struct xlnx_pcib_irqsrc *)isrc;
686dee4c1d2SRuslan Bukin 
687dee4c1d2SRuslan Bukin 	irq = xi->irq;
688dee4c1d2SRuslan Bukin 	if (irq < 32)
689dee4c1d2SRuslan Bukin 		msireg = XLNX_PCIE_RPMSIID1_MASK;
690dee4c1d2SRuslan Bukin 	else
691dee4c1d2SRuslan Bukin 		msireg = XLNX_PCIE_RPMSIID2_MASK;
692dee4c1d2SRuslan Bukin 
693dee4c1d2SRuslan Bukin 	reg = bus_read_4(sc->res, msireg);
694dee4c1d2SRuslan Bukin 	if (mask)
695dee4c1d2SRuslan Bukin 		reg &= ~(1 << irq);
696dee4c1d2SRuslan Bukin 	else
697dee4c1d2SRuslan Bukin 		reg |= (1 << irq);
698dee4c1d2SRuslan Bukin 	bus_write_4(sc->res, msireg, reg);
699dee4c1d2SRuslan Bukin }
700dee4c1d2SRuslan Bukin 
701dee4c1d2SRuslan Bukin static void
xlnx_pcib_msi_disable_intr(device_t dev,struct intr_irqsrc * isrc)702dee4c1d2SRuslan Bukin xlnx_pcib_msi_disable_intr(device_t dev, struct intr_irqsrc *isrc)
703dee4c1d2SRuslan Bukin {
704dee4c1d2SRuslan Bukin 
705dee4c1d2SRuslan Bukin 	xlnx_pcib_msi_mask(dev, isrc, true);
706dee4c1d2SRuslan Bukin }
707dee4c1d2SRuslan Bukin 
708dee4c1d2SRuslan Bukin static void
xlnx_pcib_msi_enable_intr(device_t dev,struct intr_irqsrc * isrc)709dee4c1d2SRuslan Bukin xlnx_pcib_msi_enable_intr(device_t dev, struct intr_irqsrc *isrc)
710dee4c1d2SRuslan Bukin {
711dee4c1d2SRuslan Bukin 
712dee4c1d2SRuslan Bukin 	xlnx_pcib_msi_mask(dev, isrc, false);
713dee4c1d2SRuslan Bukin }
714dee4c1d2SRuslan Bukin 
715dee4c1d2SRuslan Bukin static void
xlnx_pcib_msi_post_filter(device_t dev,struct intr_irqsrc * isrc)716dee4c1d2SRuslan Bukin xlnx_pcib_msi_post_filter(device_t dev, struct intr_irqsrc *isrc)
717dee4c1d2SRuslan Bukin {
718dee4c1d2SRuslan Bukin 
719dee4c1d2SRuslan Bukin }
720dee4c1d2SRuslan Bukin 
721dee4c1d2SRuslan Bukin static void
xlnx_pcib_msi_post_ithread(device_t dev,struct intr_irqsrc * isrc)722dee4c1d2SRuslan Bukin xlnx_pcib_msi_post_ithread(device_t dev, struct intr_irqsrc *isrc)
723dee4c1d2SRuslan Bukin {
724dee4c1d2SRuslan Bukin 
725dee4c1d2SRuslan Bukin 	xlnx_pcib_msi_mask(dev, isrc, false);
726dee4c1d2SRuslan Bukin }
727dee4c1d2SRuslan Bukin 
728dee4c1d2SRuslan Bukin static void
xlnx_pcib_msi_pre_ithread(device_t dev,struct intr_irqsrc * isrc)729dee4c1d2SRuslan Bukin xlnx_pcib_msi_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
730dee4c1d2SRuslan Bukin {
731dee4c1d2SRuslan Bukin 
732dee4c1d2SRuslan Bukin 	xlnx_pcib_msi_mask(dev, isrc, true);
733dee4c1d2SRuslan Bukin }
734dee4c1d2SRuslan Bukin 
735dee4c1d2SRuslan Bukin static int
xlnx_pcib_msi_setup_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)736dee4c1d2SRuslan Bukin xlnx_pcib_msi_setup_intr(device_t dev, struct intr_irqsrc *isrc,
737dee4c1d2SRuslan Bukin     struct resource *res, struct intr_map_data *data)
738dee4c1d2SRuslan Bukin {
739dee4c1d2SRuslan Bukin 
740dee4c1d2SRuslan Bukin 	return (0);
741dee4c1d2SRuslan Bukin }
742dee4c1d2SRuslan Bukin 
743dee4c1d2SRuslan Bukin static int
xlnx_pcib_msi_teardown_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)744dee4c1d2SRuslan Bukin xlnx_pcib_msi_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
745dee4c1d2SRuslan Bukin     struct resource *res, struct intr_map_data *data)
746dee4c1d2SRuslan Bukin {
747dee4c1d2SRuslan Bukin 
748dee4c1d2SRuslan Bukin 	return (0);
749dee4c1d2SRuslan Bukin }
750dee4c1d2SRuslan Bukin 
751dee4c1d2SRuslan Bukin static device_method_t xlnx_pcib_fdt_methods[] = {
752dee4c1d2SRuslan Bukin 	/* Device interface */
753dee4c1d2SRuslan Bukin 	DEVMETHOD(device_probe,		xlnx_pcib_fdt_probe),
754dee4c1d2SRuslan Bukin 	DEVMETHOD(device_attach,	xlnx_pcib_fdt_attach),
755dee4c1d2SRuslan Bukin 
756dee4c1d2SRuslan Bukin 	/* pcib interface */
757dee4c1d2SRuslan Bukin 	DEVMETHOD(pcib_get_id,		xlnx_pcib_fdt_get_id),
758dee4c1d2SRuslan Bukin 	DEVMETHOD(pcib_read_config,	xlnx_pcib_read_config),
759dee4c1d2SRuslan Bukin 	DEVMETHOD(pcib_write_config,	xlnx_pcib_write_config),
760dee4c1d2SRuslan Bukin 	DEVMETHOD(pcib_alloc_msi,	xlnx_pcib_alloc_msi),
761dee4c1d2SRuslan Bukin 	DEVMETHOD(pcib_release_msi,	xlnx_pcib_release_msi),
762dee4c1d2SRuslan Bukin 	DEVMETHOD(pcib_map_msi,		xlnx_pcib_map_msi),
763dee4c1d2SRuslan Bukin 
764dee4c1d2SRuslan Bukin 	/* MSI interface */
765dee4c1d2SRuslan Bukin 	DEVMETHOD(msi_alloc_msi,		xlnx_pcib_msi_alloc_msi),
766dee4c1d2SRuslan Bukin 	DEVMETHOD(msi_release_msi,		xlnx_pcib_msi_release_msi),
767dee4c1d2SRuslan Bukin 	DEVMETHOD(msi_map_msi,			xlnx_pcib_msi_map_msi),
768dee4c1d2SRuslan Bukin 
769dee4c1d2SRuslan Bukin 	/* Interrupt controller interface */
770dee4c1d2SRuslan Bukin 	DEVMETHOD(pic_disable_intr,		xlnx_pcib_msi_disable_intr),
771dee4c1d2SRuslan Bukin 	DEVMETHOD(pic_enable_intr,		xlnx_pcib_msi_enable_intr),
772dee4c1d2SRuslan Bukin 	DEVMETHOD(pic_setup_intr,		xlnx_pcib_msi_setup_intr),
773dee4c1d2SRuslan Bukin 	DEVMETHOD(pic_teardown_intr,		xlnx_pcib_msi_teardown_intr),
774dee4c1d2SRuslan Bukin 	DEVMETHOD(pic_post_filter,		xlnx_pcib_msi_post_filter),
775dee4c1d2SRuslan Bukin 	DEVMETHOD(pic_post_ithread,		xlnx_pcib_msi_post_ithread),
776dee4c1d2SRuslan Bukin 	DEVMETHOD(pic_pre_ithread,		xlnx_pcib_msi_pre_ithread),
777dee4c1d2SRuslan Bukin 
778dee4c1d2SRuslan Bukin 	/* End */
779dee4c1d2SRuslan Bukin 	DEVMETHOD_END
780dee4c1d2SRuslan Bukin };
781dee4c1d2SRuslan Bukin 
782dee4c1d2SRuslan Bukin DEFINE_CLASS_1(pcib, xlnx_pcib_fdt_driver, xlnx_pcib_fdt_methods,
783dee4c1d2SRuslan Bukin     sizeof(struct xlnx_pcib_softc), generic_pcie_fdt_driver);
784dee4c1d2SRuslan Bukin 
78590b8b224SJohn Baldwin DRIVER_MODULE(xlnx_pcib, simplebus, xlnx_pcib_fdt_driver, 0, 0);
78690b8b224SJohn Baldwin DRIVER_MODULE(xlnx_pcib, ofwbus, xlnx_pcib_fdt_driver, 0, 0);
787