1248557Sray/*-
2250357Sray * Copyright (c) 2012, 2013 The FreeBSD Foundation
3248557Sray * All rights reserved.
4248557Sray *
5248557Sray * This software was developed by Oleksandr Rybalko under sponsorship
6248557Sray * from the FreeBSD Foundation.
7248557Sray *
8248557Sray * Redistribution and use in source and binary forms, with or without
9248557Sray * modification, are permitted provided that the following conditions
10248557Sray * are met:
11248557Sray * 1.	Redistributions of source code must retain the above copyright
12248557Sray *	notice, this list of conditions and the following disclaimer.
13248557Sray * 2.	Redistributions in binary form must reproduce the above copyright
14248557Sray *	notice, this list of conditions and the following disclaimer in the
15248557Sray *	documentation and/or other materials provided with the distribution.
16248557Sray *
17248557Sray * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18248557Sray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19248557Sray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20248557Sray * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21248557Sray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22248557Sray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23248557Sray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24248557Sray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25248557Sray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26248557Sray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27248557Sray * SUCH DAMAGE.
28248557Sray */
29248557Sray
30248557Sray#include <sys/cdefs.h>
31248557Sray__FBSDID("$FreeBSD: stable/11/sys/arm/freescale/imx/tzic.c 323403 2017-09-10 23:41:23Z ian $");
32248557Sray
33248557Sray#include <sys/param.h>
34248557Sray#include <sys/systm.h>
35248557Sray#include <sys/bus.h>
36248557Sray#include <sys/kernel.h>
37248557Sray#include <sys/module.h>
38248557Sray#include <sys/rman.h>
39248557Sray#include <sys/proc.h>
40248557Sray#include <sys/lock.h>
41248557Sray#include <sys/mutex.h>
42323403Sian
43248557Sray#include <machine/bus.h>
44248557Sray#include <machine/intr.h>
45248557Sray
46248557Sray#include <dev/fdt/fdt_common.h>
47248557Sray#include <dev/ofw/openfirm.h>
48248557Sray#include <dev/ofw/ofw_bus.h>
49248557Sray#include <dev/ofw/ofw_bus_subr.h>
50248557Sray
51248557Sray#include <arm/freescale/imx/imx51_tzicreg.h>
52248557Sray
53323403Sian#include "pic_if.h"
54323403Sian
55323403Sian#define	TZIC_NIRQS	128
56323403Sian
57323403Sianstruct tzic_irqsrc {
58323403Sian	struct intr_irqsrc	isrc;
59323403Sian	u_int			irq;
60248557Sray};
61248557Sray
62323403Sianstruct tzic_softc {
63323403Sian	device_t	   dev;
64323403Sian	struct resource    *tzicregs;
65323403Sian	struct tzic_irqsrc isrcs[TZIC_NIRQS];
66248557Sray};
67248557Sray
68323403Sianstatic struct tzic_softc *tzic_sc;
69248557Sray
70323403Sianstatic inline uint32_t
71323403Siantzic_read_4(struct tzic_softc *sc, int reg)
72323403Sian{
73248557Sray
74323403Sian	return (bus_read_4(sc->tzicregs, reg));
75323403Sian}
76248557Sray
77323403Sianstatic inline void
78323403Siantzic_write_4(struct tzic_softc *sc, int reg, uint32_t val)
79323403Sian{
80323403Sian
81323403Sian    bus_write_4(sc->tzicregs, reg, val);
82323403Sian}
83323403Sian
84323403Sianstatic inline void
85323403Siantzic_irq_eoi(struct tzic_softc *sc)
86323403Sian{
87323403Sian
88323403Sian	tzic_write_4(sc, TZIC_PRIOMASK, 0xff);
89323403Sian}
90323403Sian
91323403Sianstatic inline void
92323403Siantzic_irq_mask(struct tzic_softc *sc, u_int irq)
93323403Sian{
94323403Sian
95323403Sian	tzic_write_4(sc, TZIC_ENCLEAR(irq >> 5), (1u << (irq & 0x1f)));
96323403Sian}
97323403Sian
98323403Sianstatic inline void
99323403Siantzic_irq_unmask(struct tzic_softc *sc, u_int irq)
100323403Sian{
101323403Sian
102323403Sian	tzic_write_4(sc, TZIC_ENSET(irq >> 5), (1u << (irq & 0x1f)));
103323403Sian}
104323403Sian
105248557Sraystatic int
106323403Siantzic_intr(void *arg)
107323403Sian{
108323403Sian	struct tzic_softc *sc = arg;
109323403Sian	int b, i, irq;
110323403Sian	uint32_t pending;
111323403Sian
112323403Sian	/* Get active interrupt */
113323403Sian	for (i = 0; i < TZIC_NIRQS / 32; ++i) {
114323403Sian		pending = tzic_read_4(sc, TZIC_PND(i));
115323403Sian		if ((b = 31 - __builtin_clz(pending)) < 0)
116323403Sian			continue;
117323403Sian		irq = i * 32 + b;
118323403Sian		tzic_write_4(sc, TZIC_PRIOMASK, 0);
119323403Sian		if (intr_isrc_dispatch(&sc->isrcs[irq].isrc,
120323403Sian		    curthread->td_intr_frame) != 0) {
121323403Sian			tzic_irq_mask(sc, irq);
122323403Sian			tzic_irq_eoi(sc);
123323403Sian			arm_irq_memory_barrier(irq);
124323403Sian			if (bootverbose) {
125323403Sian				device_printf(sc->dev,
126323403Sian				    "Stray irq %u disabled\n", irq);
127323403Sian			}
128323403Sian		}
129323403Sian		return (FILTER_HANDLED);
130323403Sian	}
131323403Sian
132323403Sian	if (bootverbose)
133323403Sian		device_printf(sc->dev, "Spurious interrupt detected\n");
134323403Sian
135323403Sian	return (FILTER_HANDLED);
136323403Sian}
137323403Sian
138323403Sianstatic void
139323403Siantzic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
140323403Sian{
141323403Sian	u_int irq = ((struct tzic_irqsrc *)isrc)->irq;
142323403Sian	struct tzic_softc *sc = device_get_softc(dev);
143323403Sian
144323403Sian	arm_irq_memory_barrier(irq);
145323403Sian	tzic_irq_unmask(sc, irq);
146323403Sian}
147323403Sian
148323403Sianstatic void
149323403Siantzic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
150323403Sian{
151323403Sian	u_int irq = ((struct tzic_irqsrc *)isrc)->irq;
152323403Sian	struct tzic_softc *sc = device_get_softc(dev);
153323403Sian
154323403Sian	tzic_irq_mask(sc, irq);
155323403Sian}
156323403Sian
157323403Sianstatic int
158323403Siantzic_map_intr(device_t dev, struct intr_map_data *data,
159323403Sian    struct intr_irqsrc **isrcp)
160323403Sian{
161323403Sian	struct intr_map_data_fdt *daf;
162323403Sian	struct tzic_softc *sc;
163323403Sian
164323403Sian	if (data->type != INTR_MAP_DATA_FDT)
165323403Sian		return (ENOTSUP);
166323403Sian
167323403Sian	daf = (struct intr_map_data_fdt *)data;
168323403Sian	if (daf->ncells != 1 || daf->cells[0] >= TZIC_NIRQS)
169323403Sian		return (EINVAL);
170323403Sian
171323403Sian	sc = device_get_softc(dev);
172323403Sian	*isrcp = &sc->isrcs[daf->cells[0]].isrc;
173323403Sian
174323403Sian	return (0);
175323403Sian}
176323403Sian
177323403Sianstatic void
178323403Siantzic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
179323403Sian{
180323403Sian	struct tzic_softc *sc = device_get_softc(dev);
181323403Sian
182323403Sian	tzic_irq_mask(sc, ((struct tzic_irqsrc *)isrc)->irq);
183323403Sian	tzic_irq_eoi(sc);
184323403Sian}
185323403Sian
186323403Sianstatic void
187323403Siantzic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
188323403Sian{
189323403Sian
190323403Sian	tzic_enable_intr(dev, isrc);
191323403Sian}
192323403Sian
193323403Sianstatic void
194323403Siantzic_post_filter(device_t dev, struct intr_irqsrc *isrc)
195323403Sian{
196323403Sian
197323403Sian	tzic_irq_eoi(device_get_softc(dev));
198323403Sian}
199323403Sian
200323403Sianstatic int
201323403Siantzic_pic_attach(struct tzic_softc *sc)
202323403Sian{
203323403Sian	struct intr_pic *pic;
204323403Sian	const char *name;
205323403Sian	intptr_t xref;
206323403Sian	int error;
207323403Sian	u_int irq;
208323403Sian
209323403Sian	name = device_get_nameunit(sc->dev);
210323403Sian	for (irq = 0; irq < TZIC_NIRQS; irq++) {
211323403Sian		sc->isrcs[irq].irq = irq;
212323403Sian		error = intr_isrc_register(&sc->isrcs[irq].isrc,
213323403Sian		    sc->dev, 0, "%s,%u", name, irq);
214323403Sian		if (error != 0)
215323403Sian			return (error);
216323403Sian	}
217323403Sian
218323403Sian	xref = OF_xref_from_node(ofw_bus_get_node(sc->dev));
219323403Sian	pic = intr_pic_register(sc->dev, xref);
220323403Sian	if (pic == NULL)
221323403Sian		return (ENXIO);
222323403Sian
223323403Sian	return (intr_pic_claim_root(sc->dev, xref, tzic_intr, sc, 0));
224323403Sian}
225323403Sian
226323403Sianstatic int
227248557Sraytzic_probe(device_t dev)
228248557Sray{
229261410Sian
230261410Sian	if (!ofw_bus_status_okay(dev))
231261410Sian		return (ENXIO);
232261410Sian
233248557Sray	if (ofw_bus_is_compatible(dev, "fsl,tzic")) {
234248557Sray		device_set_desc(dev, "TrustZone Interrupt Controller");
235248557Sray		return (BUS_PROBE_DEFAULT);
236248557Sray	}
237248557Sray	return (ENXIO);
238248557Sray}
239248557Sray
240248557Sraystatic int
241248557Sraytzic_attach(device_t dev)
242248557Sray{
243323403Sian	struct tzic_softc *sc = device_get_softc(dev);
244323403Sian	int i;
245248557Sray
246248557Sray	if (tzic_sc)
247248557Sray		return (ENXIO);
248323403Sian	tzic_sc = sc;
249323403Sian	sc->dev = dev;
250248557Sray
251323403Sian	i = 0;
252323403Sian	sc->tzicregs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &i,
253323403Sian	    RF_ACTIVE);
254323403Sian	if (sc->tzicregs == NULL) {
255248557Sray		device_printf(dev, "could not allocate resources\n");
256248557Sray		return (ENXIO);
257248557Sray	}
258248557Sray
259248557Sray	/* route all interrupts to IRQ.  secure interrupts are for FIQ */
260248557Sray	for (i = 0; i < 4; i++)
261323403Sian		tzic_write_4(sc, TZIC_INTSEC(i), 0xffffffff);
262248557Sray
263248557Sray	/* disable all interrupts */
264248557Sray	for (i = 0; i < 4; i++)
265323403Sian		tzic_write_4(sc, TZIC_ENCLEAR(i), 0xffffffff);
266248557Sray
267323403Sian	/* Set all interrupts to priority 0 (max). */
268323403Sian	for (i = 0; i < 128 / 4; ++i)
269323403Sian		tzic_write_4(sc, TZIC_PRIORITY(i), 0);
270323403Sian
271323403Sian	/*
272323403Sian	 * Set priority mask to lowest (unmasked) prio, set synchronizer to
273323403Sian	 * low-latency mode (as opposed to low-power), enable the controller.
274323403Sian	 */
275323403Sian	tzic_write_4(sc, TZIC_PRIOMASK, 0xff);
276323403Sian	tzic_write_4(sc, TZIC_SYNCCTRL, 0);
277323403Sian	tzic_write_4(sc, TZIC_INTCNTL, INTCNTL_NSEN_MASK|INTCNTL_NSEN|INTCNTL_EN);
278323403Sian
279323403Sian	/* Register as a root pic. */
280323403Sian	if (tzic_pic_attach(sc) != 0) {
281323403Sian		device_printf(dev, "could not attach PIC\n");
282323403Sian		return (ENXIO);
283323403Sian	}
284323403Sian
285248557Sray	return (0);
286248557Sray}
287248557Sray
288248557Sraystatic device_method_t tzic_methods[] = {
289248557Sray	DEVMETHOD(device_probe,		tzic_probe),
290248557Sray	DEVMETHOD(device_attach,	tzic_attach),
291323403Sian
292323403Sian	DEVMETHOD(pic_disable_intr,	tzic_disable_intr),
293323403Sian	DEVMETHOD(pic_enable_intr,	tzic_enable_intr),
294323403Sian	DEVMETHOD(pic_map_intr,		tzic_map_intr),
295323403Sian	DEVMETHOD(pic_post_filter,	tzic_post_filter),
296323403Sian	DEVMETHOD(pic_post_ithread,	tzic_post_ithread),
297323403Sian	DEVMETHOD(pic_pre_ithread,	tzic_pre_ithread),
298323403Sian
299323403Sian	DEVMETHOD_END
300248557Sray};
301248557Sray
302248557Sraystatic driver_t tzic_driver = {
303248557Sray	"tzic",
304248557Sray	tzic_methods,
305248557Sray	sizeof(struct tzic_softc),
306248557Sray};
307248557Sray
308248557Sraystatic devclass_t tzic_devclass;
309248557Sray
310261513SnwhitehornEARLY_DRIVER_MODULE(tzic, ofwbus, tzic_driver, tzic_devclass, 0, 0,
311248557Sray    BUS_PASS_INTERRUPT);
312