xref: /openbsd/sys/arch/armv7/marvell/mvmpic.c (revision 73471bf0)
1 /* $OpenBSD: mvmpic.c,v 1.5 2021/10/24 17:52:27 mpi Exp $ */
2 /*
3  * Copyright (c) 2007,2009,2011 Dale Rahn <drahn@openbsd.org>
4  * Copyright (c) 2015 Patrick Wildt <patrick@blueri.se>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/malloc.h>
22 #include <sys/device.h>
23 #include <sys/evcount.h>
24 
25 #include <arm/cpufunc.h>
26 #include <machine/bus.h>
27 #include <machine/fdt.h>
28 
29 #include <dev/ofw/openfirm.h>
30 #include <dev/ofw/fdt.h>
31 
32 #define	MPIC_CTRL		0x000 /* control register */
33 #define	 MPIC_CTRL_PRIO_EN		0x1
34 #define	MPIC_SOFTINT		0x004 /* software triggered interrupt register */
35 #define	MPIC_INTERR		0x020 /* SOC main interrupt error cause register */
36 #define	MPIC_ISE		0x030 /* interrupt set enable */
37 #define	MPIC_ICE		0x034 /* interrupt clear enable */
38 #define	MPIC_ISCR(x)		(0x100 + (4 * x)) /* interrupt x source control register */
39 #define	 MPIC_ISCR_PRIO_SHIFT		24
40 #define	 MPIC_ISCR_INTEN		(1 << 28)
41 
42 #define	MPIC_DOORBELL_CAUSE	0x008
43 #define	MPIC_CTP		0x040 /* current task priority */
44 #define	 MPIC_CTP_SHIFT			28
45 #define	MPIC_IACK		0x044 /* interrupt acknowledge */
46 #define	MPIC_ISM		0x048 /* set mask */
47 #define	MPIC_ICM		0x04c /* clear mask */
48 
49 struct mpic_softc {
50 	struct device		 sc_dev;
51 	bus_space_tag_t		 sc_iot;
52 	bus_space_handle_t	 sc_m_ioh, sc_c_ioh;
53 	int			 sc_node;
54 
55 	struct intrhand		**sc_handlers;
56 	int			 sc_ipl;
57 	int			 sc_nintr;
58 	struct evcount		 sc_spur;
59 	struct interrupt_controller sc_intc;
60 	void 			*sc_ih;
61 };
62 
63 struct intrhand {
64 	int (*ih_func)(void *);		/* handler */
65 	void *ih_arg;			/* arg for handler */
66 	int ih_ipl;			/* IPL_* */
67 	int ih_irq;			/* IRQ number */
68 	struct evcount	ih_count;
69 	char *ih_name;
70 	void *ih_sc;
71 };
72 
73 int		 mpic_match(struct device *, void *, void *);
74 void		 mpic_attach(struct device *, struct device *, void *);
75 void		 mpic_calc_mask(struct mpic_softc *);
76 void		*mpic_intr_establish(void *, int *, int, struct cpu_info *,
77 		    int (*)(void *), void *, char *);
78 void		 mpic_intr_disestablish(void *);
79 int		 mpic_intr(void *);
80 void		 mpic_set_priority(struct mpic_softc *, int, int);
81 void		 mpic_intr_enable(struct mpic_softc *, int);
82 void		 mpic_intr_disable(struct mpic_softc *, int);
83 
84 const struct cfattach	mvmpic_ca = {
85 	sizeof (struct mpic_softc), mpic_match, mpic_attach
86 };
87 
88 struct cfdriver mvmpic_cd = {
89 	NULL, "mvmpic", DV_DULL
90 };
91 
92 int
93 mpic_match(struct device *parent, void *cfdata, void *aux)
94 {
95 	struct fdt_attach_args *faa = aux;
96 
97 	return OF_is_compatible(faa->fa_node, "marvell,mpic");
98 }
99 
100 void
101 mpic_attach(struct device *parent, struct device *self, void *args)
102 {
103 	struct mpic_softc *sc = (struct mpic_softc *)self;
104 	struct fdt_attach_args *faa = args;
105 	int i;
106 
107 	sc->sc_node = faa->fa_node;
108 	sc->sc_iot = faa->fa_iot;
109 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
110 	    faa->fa_reg[0].size, 0, &sc->sc_m_ioh))
111 		panic("%s: main bus_space_map failed!", __func__);
112 
113 	if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
114 	    faa->fa_reg[1].size, 0, &sc->sc_c_ioh))
115 		panic("%s: cpu bus_space_map failed!", __func__);
116 
117 	evcount_attach(&sc->sc_spur, "irq1023/spur", NULL);
118 
119 	sc->sc_nintr = (bus_space_read_4(sc->sc_iot, sc->sc_m_ioh,
120 	    MPIC_CTRL) >> 2) & 0x3ff;
121 	printf(" nirq %d\n", sc->sc_nintr);
122 
123 	/* Disable all interrupts */
124 	for (i = 0; i < sc->sc_nintr; i++) {
125 		bus_space_write_4(sc->sc_iot, sc->sc_m_ioh, MPIC_ICE, i);
126 		bus_space_write_4(sc->sc_iot, sc->sc_c_ioh, MPIC_ISM, i);
127 	}
128 
129 	/* Clear pending IPIs */
130 	bus_space_write_4(sc->sc_iot, sc->sc_c_ioh, MPIC_DOORBELL_CAUSE, 0);
131 
132 	/* Enable hardware priorization selection */
133 	bus_space_write_4(sc->sc_iot, sc->sc_m_ioh, MPIC_CTRL,
134 	    MPIC_CTRL_PRIO_EN);
135 
136 	/* Always allow everything. */
137 	bus_space_write_4(sc->sc_iot, sc->sc_c_ioh, MPIC_CTP,
138 	    (bus_space_read_4(sc->sc_iot, sc->sc_c_ioh, MPIC_CTP) &
139 	    ~(0xf << MPIC_CTP_SHIFT)) | (IPL_NONE << MPIC_CTP_SHIFT));
140 
141 	sc->sc_handlers = mallocarray(sc->sc_nintr,
142 	    sizeof(*sc->sc_handlers), M_DEVBUF, M_ZERO | M_NOWAIT);
143 
144 	sc->sc_ipl = IPL_NONE;
145 	mpic_calc_mask(sc);
146 
147 	sc->sc_intc.ic_node = faa->fa_node;
148 	sc->sc_intc.ic_cookie = sc;
149 	sc->sc_intc.ic_establish = mpic_intr_establish;
150 	arm_intr_register_fdt(&sc->sc_intc);
151 }
152 
153 void
154 mpic_set_priority(struct mpic_softc *sc, int irq, int pri)
155 {
156 	bus_space_write_4(sc->sc_iot, sc->sc_m_ioh, MPIC_ISCR(irq),
157 	    (bus_space_read_4(sc->sc_iot, sc->sc_m_ioh, MPIC_ISCR(irq)) &
158 	    ~(0xf << MPIC_ISCR_PRIO_SHIFT)) | (pri << MPIC_ISCR_PRIO_SHIFT));
159 }
160 
161 void
162 mpic_intr_enable(struct mpic_softc *sc, int irq)
163 {
164 	bus_space_write_4(sc->sc_iot, sc->sc_m_ioh, MPIC_ISE, irq);
165 	bus_space_write_4(sc->sc_iot, sc->sc_c_ioh, MPIC_ICM, irq);
166 }
167 
168 void
169 mpic_intr_disable(struct mpic_softc *sc, int irq)
170 {
171 	bus_space_write_4(sc->sc_iot, sc->sc_m_ioh, MPIC_ICE, irq);
172 	bus_space_write_4(sc->sc_iot, sc->sc_c_ioh, MPIC_ISM, irq);
173 }
174 
175 void
176 mpic_calc_mask(struct mpic_softc *sc)
177 {
178 	struct intrhand		*ih;
179 	int			 irq;
180 	int			 max = IPL_NONE;
181 	int			 min = IPL_HIGH;
182 
183 	for (irq = 0; irq < sc->sc_nintr; irq++) {
184 		ih = sc->sc_handlers[irq];
185 		if (ih == NULL)
186 			continue;
187 
188 		if (ih->ih_ipl > max)
189 			max = ih->ih_ipl;
190 
191 		if (ih->ih_ipl < min)
192 			min = ih->ih_ipl;
193 	}
194 
195 	if (max == IPL_NONE)
196 		min = IPL_NONE;
197 
198 	if (sc->sc_ipl != min) {
199 		sc->sc_ipl = min;
200 
201 		if (sc->sc_ih != NULL)
202 			arm_intr_disestablish_fdt(sc->sc_ih);
203 
204 		if (sc->sc_ipl != IPL_NONE)
205 			sc->sc_ih = arm_intr_establish_fdt(sc->sc_node,
206 			    sc->sc_ipl, mpic_intr, sc, sc->sc_dev.dv_xname);
207 	}
208 }
209 
210 int
211 mpic_intr(void *cookie)
212 {
213 	struct mpic_softc	*sc = cookie;
214 	struct intrhand		*ih;
215 	int			 irq, s;
216 
217 	irq = bus_space_read_4(sc->sc_iot, sc->sc_c_ioh, MPIC_IACK) & 0x3ff;
218 
219 	if (irq == 1023) {
220 		sc->sc_spur.ec_count++;
221 		return 1;
222 	}
223 
224 	if (irq >= sc->sc_nintr)
225 		return 1;
226 
227 	if ((ih = sc->sc_handlers[irq]) != NULL) {
228 		s = splraise(ih->ih_ipl);
229 		if (ih->ih_func(ih->ih_arg))
230 			ih->ih_count.ec_count++;
231 		splx(s);
232 	}
233 
234 	return 1;
235 }
236 
237 void *
238 mpic_intr_establish(void *cookie, int *cells, int level,
239     struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
240 {
241 	struct mpic_softc	*sc = cookie;
242 	struct intrhand		*ih;
243 	int			 psw;
244 	int			 irqno = cells[0];
245 
246 	if (irqno < 0 || irqno >= sc->sc_nintr)
247 		panic("%s: bogus irqnumber %d: %s", __func__,
248 		    irqno, name);
249 
250 	if (sc->sc_handlers[irqno] != NULL)
251 		panic("%s: irq %d already registered" , __func__,
252 		    irqno);
253 
254 	if (ci != NULL && !CPU_IS_PRIMARY(ci))
255 		return NULL;
256 
257 	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
258 	ih->ih_func = func;
259 	ih->ih_arg = arg;
260 	ih->ih_ipl = level;
261 	ih->ih_irq = irqno;
262 	ih->ih_name = name;
263 	ih->ih_sc = sc;
264 
265 	psw = disable_interrupts(PSR_I);
266 
267 	sc->sc_handlers[irqno] = ih;
268 
269 	if (name != NULL)
270 		evcount_attach(&ih->ih_count, name, &ih->ih_irq);
271 
272 #ifdef DEBUG_INTC
273 	printf("%s: irq %d level %d [%s]\n", __func__, irqno, level, name);
274 #endif
275 
276 	mpic_calc_mask(sc);
277 	mpic_set_priority(sc, irqno, level);
278 	mpic_intr_enable(sc, irqno);
279 
280 	restore_interrupts(psw);
281 	return (ih);
282 }
283 
284 void
285 mpic_intr_disestablish(void *cookie)
286 {
287 	struct intrhand		*ih = cookie;
288 	struct mpic_softc	*sc = ih->ih_sc;
289 	int			 psw;
290 
291 	psw = disable_interrupts(PSR_I);
292 
293 #ifdef DEBUG_INTC
294 	printf("%s: irq %d ipl %d [%s]\n", __func__, ih->ih_irq, ih->ih_ipl,
295 	    ih->ih_name);
296 #endif
297 
298 	mpic_intr_disable(sc, ih->ih_irq);
299 
300 	sc->sc_handlers[ih->ih_irq] = NULL;
301 	if (ih->ih_name != NULL)
302 		evcount_detach(&ih->ih_count);
303 	free(ih, M_DEVBUF, sizeof(*ih));
304 
305 	mpic_calc_mask(sc);
306 
307 	restore_interrupts(psw);
308 }
309