1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2012-2017 Oleksandr Tymoshenko <gonzo@bluezbox.com>
5 * All rights reserved.
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
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: stable/11/sys/arm/arm/pl190.c 330897 2018-03-14 03:19:51Z eadler $");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/bus.h>
35#include <sys/kernel.h>
36#include <sys/ktr.h>
37#include <sys/module.h>
38#include <sys/proc.h>
39#include <sys/rman.h>
40#include <machine/bus.h>
41#include <machine/intr.h>
42
43#include <dev/ofw/openfirm.h>
44#include <dev/ofw/ofw_bus.h>
45#include <dev/ofw/ofw_bus_subr.h>
46
47#include "pic_if.h"
48
49#ifdef  DEBUG
50#define dprintf(fmt, args...) printf(fmt, ##args)
51#else
52#define dprintf(fmt, args...)
53#endif
54
55#define	VICIRQSTATUS	0x000
56#define	VICFIQSTATUS	0x004
57#define	VICRAWINTR	0x008
58#define	VICINTSELECT	0x00C
59#define	VICINTENABLE	0x010
60#define	VICINTENCLEAR	0x014
61#define	VICSOFTINT	0x018
62#define	VICSOFTINTCLEAR	0x01C
63#define	VICPROTECTION	0x020
64#define	VICPERIPHID	0xFE0
65#define	VICPRIMECELLID	0xFF0
66
67#define	VIC_NIRQS	32
68
69struct pl190_intc_irqsrc {
70	struct intr_irqsrc		isrc;
71	u_int				irq;
72};
73
74struct pl190_intc_softc {
75	device_t		dev;
76	struct mtx		mtx;
77	struct resource *	intc_res;
78	struct pl190_intc_irqsrc	isrcs[VIC_NIRQS];
79};
80
81#define	INTC_VIC_READ_4(sc, reg)		\
82    bus_read_4(sc->intc_res, (reg))
83#define	INTC_VIC_WRITE_4(sc, reg, val)		\
84    bus_write_4(sc->intc_res, (reg), (val))
85
86#define	VIC_LOCK(_sc) mtx_lock_spin(&(_sc)->mtx)
87#define	VIC_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->mtx)
88
89static inline void
90pl190_intc_irq_dispatch(struct pl190_intc_softc *sc, u_int irq,
91    struct trapframe *tf)
92{
93	struct pl190_intc_irqsrc *src;
94
95	src = &sc->isrcs[irq];
96	if (intr_isrc_dispatch(&src->isrc, tf) != 0)
97		device_printf(sc->dev, "Stray irq %u detected\n", irq);
98}
99
100static int
101pl190_intc_intr(void *arg)
102{
103	struct pl190_intc_softc *sc;
104	u_int cpu;
105	uint32_t num, pending;
106	struct trapframe *tf;
107
108	sc = arg;
109	cpu = PCPU_GET(cpuid);
110	tf = curthread->td_intr_frame;
111
112	VIC_LOCK(sc);
113	pending = INTC_VIC_READ_4(sc, VICIRQSTATUS);
114	VIC_UNLOCK(sc);
115	for (num = 0 ; num < VIC_NIRQS; num++) {
116		if (pending & (1 << num))
117			pl190_intc_irq_dispatch(sc, num, tf);
118	}
119
120	return (FILTER_HANDLED);
121}
122
123static void
124pl190_intc_disable_intr(device_t dev, struct intr_irqsrc *isrc)
125{
126	struct pl190_intc_softc *sc;
127	struct pl190_intc_irqsrc *src;
128
129	sc = device_get_softc(dev);
130	src = (struct pl190_intc_irqsrc *)isrc;
131
132	VIC_LOCK(sc);
133	INTC_VIC_WRITE_4(sc, VICINTENCLEAR, (1 << src->irq));
134	VIC_UNLOCK(sc);
135}
136
137static void
138pl190_intc_enable_intr(device_t dev, struct intr_irqsrc *isrc)
139{
140	struct pl190_intc_softc *sc;
141	struct pl190_intc_irqsrc *src;
142
143	sc = device_get_softc(dev);
144	src = (struct pl190_intc_irqsrc *)isrc;
145
146	VIC_LOCK(sc);
147	INTC_VIC_WRITE_4(sc, VICINTENABLE, (1 << src->irq));
148	VIC_UNLOCK(sc);
149}
150
151static int
152pl190_intc_map_intr(device_t dev, struct intr_map_data *data,
153    struct intr_irqsrc **isrcp)
154{
155	struct intr_map_data_fdt *daf;
156	struct pl190_intc_softc *sc;
157
158	if (data->type != INTR_MAP_DATA_FDT)
159		return (ENOTSUP);
160
161	daf = (struct intr_map_data_fdt *)data;
162	if (daf->ncells != 1 || daf->cells[0] >= VIC_NIRQS)
163		return (EINVAL);
164
165	sc = device_get_softc(dev);
166	*isrcp = &sc->isrcs[daf->cells[0]].isrc;
167	return (0);
168}
169
170static void
171pl190_intc_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
172{
173	pl190_intc_disable_intr(dev, isrc);
174}
175
176static void
177pl190_intc_post_ithread(device_t dev, struct intr_irqsrc *isrc)
178{
179	struct pl190_intc_irqsrc *src;
180
181	src = (struct pl190_intc_irqsrc *)isrc;
182	pl190_intc_enable_intr(dev, isrc);
183	arm_irq_memory_barrier(src->irq);
184}
185
186static void
187pl190_intc_post_filter(device_t dev, struct intr_irqsrc *isrc)
188{
189	struct pl190_intc_irqsrc *src;
190
191	src = (struct pl190_intc_irqsrc *)isrc;
192	arm_irq_memory_barrier(src->irq);
193}
194
195static int
196pl190_intc_setup_intr(device_t dev, struct intr_irqsrc *isrc,
197    struct resource *res, struct intr_map_data *data)
198{
199
200	return (0);
201}
202
203static int
204pl190_intc_probe(device_t dev)
205{
206
207	if (!ofw_bus_status_okay(dev))
208		return (ENXIO);
209
210	if (!ofw_bus_is_compatible(dev, "arm,versatile-vic"))
211		return (ENXIO);
212	device_set_desc(dev, "ARM PL190 VIC");
213	return (BUS_PROBE_DEFAULT);
214}
215
216static int
217pl190_intc_attach(device_t dev)
218{
219	struct		pl190_intc_softc *sc;
220	uint32_t	id;
221	int		i, rid;
222	struct		pl190_intc_irqsrc *isrcs;
223	struct intr_pic *pic;
224	int		error;
225	uint32_t	irq;
226	const char	*name;
227	phandle_t	xref;
228
229	sc = device_get_softc(dev);
230	sc->dev = dev;
231	mtx_init(&sc->mtx, device_get_nameunit(dev), "pl190",
232	    MTX_SPIN);
233
234	/* Request memory resources */
235	rid = 0;
236	sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
237	    RF_ACTIVE);
238	if (sc->intc_res == NULL) {
239		device_printf(dev, "Error: could not allocate memory resources\n");
240		return (ENXIO);
241	}
242
243	/*
244	 * All interrupts should use IRQ line
245	 */
246	INTC_VIC_WRITE_4(sc, VICINTSELECT, 0x00000000);
247	/* Disable all interrupts */
248	INTC_VIC_WRITE_4(sc, VICINTENCLEAR, 0xffffffff);
249
250	id = 0;
251	for (i = 3; i >= 0; i--) {
252		id = (id << 8) |
253		     (INTC_VIC_READ_4(sc, VICPERIPHID + i*4) & 0xff);
254	}
255
256	device_printf(dev, "Peripheral ID: %08x\n", id);
257
258	id = 0;
259	for (i = 3; i >= 0; i--) {
260		id = (id << 8) |
261		     (INTC_VIC_READ_4(sc, VICPRIMECELLID + i*4) & 0xff);
262	}
263
264	device_printf(dev, "PrimeCell ID: %08x\n", id);
265
266	/* PIC attachment */
267	isrcs = sc->isrcs;
268	name = device_get_nameunit(sc->dev);
269	for (irq = 0; irq < VIC_NIRQS; irq++) {
270		isrcs[irq].irq = irq;
271		error = intr_isrc_register(&isrcs[irq].isrc, sc->dev,
272		    0, "%s,%u", name, irq);
273		if (error != 0)
274			return (error);
275	}
276
277	xref = OF_xref_from_node(ofw_bus_get_node(sc->dev));
278	pic = intr_pic_register(sc->dev, xref);
279	if (pic == NULL)
280		return (ENXIO);
281
282	return (intr_pic_claim_root(sc->dev, xref, pl190_intc_intr, sc, 0));
283}
284
285static device_method_t pl190_intc_methods[] = {
286	DEVMETHOD(device_probe,		pl190_intc_probe),
287	DEVMETHOD(device_attach,	pl190_intc_attach),
288
289	DEVMETHOD(pic_disable_intr,	pl190_intc_disable_intr),
290	DEVMETHOD(pic_enable_intr,	pl190_intc_enable_intr),
291	DEVMETHOD(pic_map_intr,		pl190_intc_map_intr),
292	DEVMETHOD(pic_post_filter,	pl190_intc_post_filter),
293	DEVMETHOD(pic_post_ithread,	pl190_intc_post_ithread),
294	DEVMETHOD(pic_pre_ithread,	pl190_intc_pre_ithread),
295	DEVMETHOD(pic_setup_intr,	pl190_intc_setup_intr),
296
297	DEVMETHOD_END
298};
299
300static driver_t pl190_intc_driver = {
301	"intc",
302	pl190_intc_methods,
303	sizeof(struct pl190_intc_softc),
304};
305
306static devclass_t pl190_intc_devclass;
307
308EARLY_DRIVER_MODULE(intc, simplebus, pl190_intc_driver, pl190_intc_devclass,
309    0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
310