1/*	$NetBSD: pic_uic.c,v 1.9 2021/03/05 05:35:50 rin Exp $	*/
2
3/*
4 * Copyright 2002 Wasabi Systems, Inc.
5 * All rights reserved.
6 *
7 * Written by Eduardo Horvath and Simon Burge for Wasabi Systems, Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *      This product includes software developed for the NetBSD Project by
20 *      Wasabi Systems, Inc.
21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22 *    or promote products derived from this software without specific prior
23 *    written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#include <sys/cdefs.h>
39__KERNEL_RCSID(0, "$NetBSD: pic_uic.c,v 1.9 2021/03/05 05:35:50 rin Exp $");
40
41#ifdef _KERNEL_OPT
42#include "opt_ppcarch.h"
43#include "opt_uic.h"
44#endif
45
46#include <sys/param.h>
47#include <sys/kernel.h>
48#include <sys/evcnt.h>
49#include <sys/cpu.h>
50
51#include <machine/intr.h>
52#include <machine/psl.h>
53
54#include <powerpc/spr.h>
55#include <powerpc/ibm4xx/spr.h>
56#include <powerpc/ibm4xx/cpu.h>
57
58#include <powerpc/pic/picvar.h>
59
60/*
61 * Number of interrupts (hard + soft), irq number legality test,
62 * mapping of irq number to mask and a way to pick irq number
63 * off a mask of active intrs.
64 */
65#define	IRQ_TO_MASK(irq) 	(0x80000000UL >> ((irq) & 0x1f))
66#define	IRQ_OF_MASK(mask) 	__builtin_clz(mask)
67
68static void	uic_enable_irq(struct pic_ops *, int, int);
69static void	uic_disable_irq(struct pic_ops *, int);
70static int	uic_get_irq(struct pic_ops *, int);
71static void	uic_ack_irq(struct pic_ops *, int);
72static void	uic_establish_irq(struct pic_ops *, int, int, int);
73
74struct uic {
75	uint32_t uic_intr_enable;	/* cached intr enable mask */
76#ifdef PPC_IBM403
77	/*
78	 * Not clearly documented in reference manual, but DCR_EXISR
79	 * register is not updated immediately after some bits are
80	 * cleared by mtdcr, no matter whether sync (= eieio) and/or
81	 * isync are issued.
82	 *
83	 * Therefore, we have to manage our own status mask in the
84	 * interrupt handler; see uic_{ack,get}_irq() for more details.
85	 * This is what we did in obsoleted powerpc/ibm4xx/intr.c.
86	 */
87	uint32_t uic_intr_status;
88#endif
89	uint32_t (*uic_mf_intr_status)(void);
90	uint32_t (*uic_mf_intr_enable)(void);
91	void (*uic_mt_intr_enable)(uint32_t);
92	void (*uic_mt_intr_ack)(uint32_t);
93};
94
95/*
96 * Platform specific code may override any of the above.
97 */
98#ifdef PPC_IBM403
99
100#include <powerpc/ibm4xx/dcr403cgx.h>
101
102static uint32_t
103uic403_mfdcr_intr_status(void)
104{
105	return mfdcr(DCR_EXISR);
106}
107
108static uint32_t
109uic403_mfdcr_intr_enable(void)
110{
111	return mfdcr(DCR_EXIER);
112}
113
114static void
115uic403_mtdcr_intr_ack(uint32_t v)
116{
117	mtdcr(DCR_EXISR, v);
118}
119
120static void
121uic403_mtdcr_intr_enable(uint32_t v)
122{
123	mtdcr(DCR_EXIER, v);
124}
125
126struct uic uic403 = {
127	.uic_intr_enable =	0,
128	.uic_mf_intr_status =	uic403_mfdcr_intr_status,
129	.uic_mf_intr_enable =	uic403_mfdcr_intr_enable,
130	.uic_mt_intr_enable =	uic403_mtdcr_intr_enable,
131	.uic_mt_intr_ack =	uic403_mtdcr_intr_ack,
132};
133
134struct pic_ops pic_uic403 = {
135	.pic_cookie = &uic403,
136	.pic_numintrs = 32,
137	.pic_enable_irq = uic_enable_irq,
138	.pic_reenable_irq = uic_enable_irq,
139	.pic_disable_irq = uic_disable_irq,
140	.pic_establish_irq = uic_establish_irq,
141	.pic_get_irq = uic_get_irq,
142	.pic_ack_irq = uic_ack_irq,
143	.pic_finish_setup = NULL,
144	.pic_name = "uic0"
145};
146
147#else /* Generic 405/440/460 Universal Interrupt Controller */
148
149#include <powerpc/ibm4xx/dcr4xx.h>
150
151#include "opt_uic.h"
152
153/* 405EP/405GP/405GPr/Virtex-4 */
154
155static uint32_t
156uic0_mfdcr_intr_status(void)
157{
158	return mfdcr(DCR_UIC0_BASE + DCR_UIC_MSR);
159}
160
161static uint32_t
162uic0_mfdcr_intr_enable(void)
163{
164	return mfdcr(DCR_UIC0_BASE + DCR_UIC_ER);
165}
166
167static void
168uic0_mtdcr_intr_ack(uint32_t v)
169{
170	mtdcr(DCR_UIC0_BASE + DCR_UIC_SR, v);
171}
172
173static void
174uic0_mtdcr_intr_enable(uint32_t v)
175{
176	mtdcr(DCR_UIC0_BASE + DCR_UIC_ER, v);
177}
178
179struct uic uic0 = {
180	.uic_intr_enable =	0,
181	.uic_mf_intr_status =	uic0_mfdcr_intr_status,
182	.uic_mf_intr_enable =	uic0_mfdcr_intr_enable,
183	.uic_mt_intr_enable =	uic0_mtdcr_intr_enable,
184	.uic_mt_intr_ack =	uic0_mtdcr_intr_ack,
185};
186
187struct pic_ops pic_uic0 = {
188	.pic_cookie = &uic0,
189	.pic_numintrs = 32,
190	.pic_enable_irq = uic_enable_irq,
191	.pic_reenable_irq = uic_enable_irq,
192	.pic_disable_irq = uic_disable_irq,
193	.pic_establish_irq = uic_establish_irq,
194	.pic_get_irq = uic_get_irq,
195	.pic_ack_irq = uic_ack_irq,
196	.pic_finish_setup = NULL,
197	.pic_name = "uic0"
198};
199
200#ifdef MULTIUIC
201
202/* 440EP/440GP/440SP/405EX/440SPe/440GX */
203
204static uint32_t
205uic1_mfdcr_intr_status(void)
206{
207	return mfdcr(DCR_UIC1_BASE + DCR_UIC_MSR);
208}
209
210static uint32_t
211uic1_mfdcr_intr_enable(void)
212{
213	return mfdcr(DCR_UIC1_BASE + DCR_UIC_ER);
214}
215
216static void
217uic1_mtdcr_intr_ack(uint32_t v)
218{
219	mtdcr(DCR_UIC1_BASE + DCR_UIC_SR, v);
220}
221
222static void
223uic1_mtdcr_intr_enable(uint32_t v)
224{
225	mtdcr(DCR_UIC1_BASE + DCR_UIC_ER, v);
226}
227
228extern struct pic_ops pic_uic1;
229
230static void
231uic1_finish_setup(struct pic_ops *pic)
232{
233	intr_establish_xname(30, IST_LEVEL, IPL_HIGH, pic_handle_intr,
234	    &pic_uic1, "uic1");
235}
236
237struct uic uic1 = {
238	.uic_intr_enable =	0,
239	.uic_mf_intr_status =	uic1_mfdcr_intr_status,
240	.uic_mf_intr_enable =	uic1_mfdcr_intr_enable,
241	.uic_mt_intr_enable =	uic1_mtdcr_intr_enable,
242	.uic_mt_intr_ack =	uic1_mtdcr_intr_ack,
243};
244
245struct pic_ops pic_uic1 = {
246	.pic_cookie = &uic1,
247	.pic_numintrs = 32,
248	.pic_enable_irq = uic_enable_irq,
249	.pic_reenable_irq = uic_enable_irq,
250	.pic_disable_irq = uic_disable_irq,
251	.pic_establish_irq = uic_establish_irq,
252	.pic_get_irq = uic_get_irq,
253	.pic_ack_irq = uic_ack_irq,
254	.pic_finish_setup = uic1_finish_setup,
255	.pic_name = "uic1"
256};
257
258/* 440EP/440GP/440SP/405EX/440SPe */
259
260static uint32_t
261uic2_mfdcr_intr_status(void)
262{
263	return mfdcr(DCR_UIC2_BASE + DCR_UIC_MSR);
264}
265
266static uint32_t
267uic2_mfdcr_intr_enable(void)
268{
269	return mfdcr(DCR_UIC2_BASE + DCR_UIC_ER);
270}
271
272static void
273uic2_mtdcr_intr_ack(uint32_t v)
274{
275	mtdcr(DCR_UIC2_BASE + DCR_UIC_SR, v);
276}
277
278static void
279uic2_mtdcr_intr_enable(uint32_t v)
280{
281	mtdcr(DCR_UIC2_BASE + DCR_UIC_ER, v);
282}
283
284extern struct pic_ops pic_uic2;
285
286static void
287uic2_finish_setup(struct pic_ops *pic)
288{
289	intr_establish_xname(28, IST_LEVEL, IPL_HIGH, pic_handle_intr,
290	    &pic_uic2, "uic2");
291}
292
293static struct uic uic2 = {
294	.uic_intr_enable =	0,
295	.uic_mf_intr_status =	uic2_mfdcr_intr_status,
296	.uic_mf_intr_enable =	uic2_mfdcr_intr_enable,
297	.uic_mt_intr_enable =	uic2_mtdcr_intr_enable,
298	.uic_mt_intr_ack =	uic2_mtdcr_intr_ack,
299};
300
301struct pic_ops pic_uic2 = {
302	.pic_cookie = &uic2,
303	.pic_numintrs = 32,
304	.pic_enable_irq = uic_enable_irq,
305	.pic_reenable_irq = uic_enable_irq,
306	.pic_disable_irq = uic_disable_irq,
307	.pic_establish_irq = uic_establish_irq,
308	.pic_get_irq = uic_get_irq,
309	.pic_ack_irq = uic_ack_irq,
310	.pic_finish_setup = uic2_finish_setup,
311	.pic_name = "uic2"
312};
313
314#endif /* MULTIUIC */
315#endif /* !PPC_IBM403 */
316
317/*
318 * Set up interrupt mapping array.
319 */
320void
321intr_init(void)
322{
323#ifdef PPC_IBM403
324	struct pic_ops * const pic = &pic_uic403;
325#else
326	struct pic_ops * const pic = &pic_uic0;
327#endif
328	struct uic * const uic = pic->pic_cookie;
329
330	uic->uic_mt_intr_enable(0x00000000); 	/* mask all */
331	uic->uic_mt_intr_ack(0xffffffff);	/* acknowledge all */
332
333	pic_add(pic);
334}
335
336static void
337uic_disable_irq(struct pic_ops *pic, int irq)
338{
339	struct uic * const uic = pic->pic_cookie;
340	const uint32_t irqmask = IRQ_TO_MASK(irq);
341	if ((uic->uic_intr_enable & irqmask) == 0)
342		return;
343	uic->uic_intr_enable ^= irqmask;
344	(*uic->uic_mt_intr_enable)(uic->uic_intr_enable);
345#ifdef IRQ_DEBUG
346	printf("%s: %s: irq=%d, mask=%08x\n", __func__,
347	    pic->pic_name, irq, irqmask);
348#endif
349}
350
351static void
352uic_enable_irq(struct pic_ops *pic, int irq, int type)
353{
354	struct uic * const uic = pic->pic_cookie;
355	const uint32_t irqmask = IRQ_TO_MASK(irq);
356	if ((uic->uic_intr_enable & irqmask) != 0)
357		return;
358	uic->uic_intr_enable ^= irqmask;
359	(*uic->uic_mt_intr_enable)(uic->uic_intr_enable);
360#ifdef IRQ_DEBUG
361	printf("%s: %s: irq=%d, mask=%08x\n", __func__,
362	    pic->pic_name, irq, irqmask);
363#endif
364}
365
366static void
367uic_ack_irq(struct pic_ops *pic, int irq)
368{
369	struct uic * const uic = pic->pic_cookie;
370	const uint32_t irqmask = IRQ_TO_MASK(irq);
371
372#ifdef PPC_IBM403
373	uic->uic_intr_status &= ~irqmask;
374#endif
375
376	(*uic->uic_mt_intr_ack)(irqmask);
377}
378
379static int
380uic_get_irq(struct pic_ops *pic, int req)
381{
382	struct uic * const uic = pic->pic_cookie;
383
384#ifdef PPC_IBM403
385	if (req == PIC_GET_IRQ)
386		uic->uic_intr_status = (*uic->uic_mf_intr_status)();
387	const uint32_t irqmask = uic->uic_intr_status;
388#else
389	const uint32_t irqmask = (*uic->uic_mf_intr_status)();
390#endif
391
392	if (irqmask == 0)
393		return 255;
394	return IRQ_OF_MASK(irqmask);
395}
396
397/*
398 * Register an interrupt handler.
399 */
400static void
401uic_establish_irq(struct pic_ops *pic, int irq, int type, int ipl)
402{
403}
404