1*de052479Scheloha /* $OpenBSD: openpic.c,v 1.90 2022/07/24 00:28:09 cheloha Exp $ */
2d9a5f17fSdrahn
3d9a5f17fSdrahn /*-
4d7469fedSdrahn * Copyright (c) 2008 Dale Rahn <drahn@openbsd.org>
5d9a5f17fSdrahn * Copyright (c) 1995 Per Fogelstrom
6d9a5f17fSdrahn * Copyright (c) 1993, 1994 Charles M. Hannum.
7d9a5f17fSdrahn * Copyright (c) 1990 The Regents of the University of California.
8d9a5f17fSdrahn * All rights reserved.
9d9a5f17fSdrahn *
10d9a5f17fSdrahn * This code is derived from software contributed to Berkeley by
11d9a5f17fSdrahn * William Jolitz and Don Ahn.
12d9a5f17fSdrahn *
13d9a5f17fSdrahn * Redistribution and use in source and binary forms, with or without
14d9a5f17fSdrahn * modification, are permitted provided that the following conditions
15d9a5f17fSdrahn * are met:
16d9a5f17fSdrahn * 1. Redistributions of source code must retain the above copyright
17d9a5f17fSdrahn * notice, this list of conditions and the following disclaimer.
18d9a5f17fSdrahn * 2. Redistributions in binary form must reproduce the above copyright
19d9a5f17fSdrahn * notice, this list of conditions and the following disclaimer in the
20d9a5f17fSdrahn * documentation and/or other materials provided with the distribution.
2129295d1cSmillert * 3. Neither the name of the University nor the names of its contributors
22d9a5f17fSdrahn * may be used to endorse or promote products derived from this software
23d9a5f17fSdrahn * without specific prior written permission.
24d9a5f17fSdrahn *
25d9a5f17fSdrahn * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26d9a5f17fSdrahn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27d9a5f17fSdrahn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28d9a5f17fSdrahn * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29d9a5f17fSdrahn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30d9a5f17fSdrahn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31d9a5f17fSdrahn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32d9a5f17fSdrahn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33d9a5f17fSdrahn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34d9a5f17fSdrahn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35d9a5f17fSdrahn * SUCH DAMAGE.
36d9a5f17fSdrahn *
37d9a5f17fSdrahn * @(#)isa.c 7.2 (Berkeley) 5/12/91
38d9a5f17fSdrahn */
39d9a5f17fSdrahn
40ba99a0edSmpi #include "hpb.h"
41ba99a0edSmpi
42d9a5f17fSdrahn #include <sys/param.h>
43d9a5f17fSdrahn #include <sys/device.h>
44d9a5f17fSdrahn #include <sys/systm.h>
45417aa565Smpi #include <sys/malloc.h>
46ba99a0edSmpi #include <sys/atomic.h>
4730c76742Smickey
48417aa565Smpi #include <uvm/uvm_extern.h>
49d9a5f17fSdrahn
50d9a5f17fSdrahn #include <machine/autoconf.h>
51d9a5f17fSdrahn #include <machine/intr.h>
52d9a5f17fSdrahn #include <machine/psl.h>
53d9a5f17fSdrahn #include <machine/pio.h>
54d9a5f17fSdrahn #include <dev/ofw/openfirm.h>
55d9a5f17fSdrahn
56bccc5538Smpi #include <macppc/dev/openpicreg.h>
57bccc5538Smpi
58cc150176Smpi #ifdef OPENPIC_DEBUG
59cc150176Smpi #define DPRINTF(x...) do { printf(x); } while(0)
60cc150176Smpi #else
61cc150176Smpi #define DPRINTF(x...)
62cc150176Smpi #endif
63cc150176Smpi
64d9a5f17fSdrahn #define ICU_LEN 128
65d7469fedSdrahn int openpic_numirq = ICU_LEN;
66d9a5f17fSdrahn #define LEGAL_IRQ(x) ((x >= 0) && (x < ICU_LEN))
67d9a5f17fSdrahn
68d7469fedSdrahn int openpic_pri_share[IPL_NUM];
69d9a5f17fSdrahn
70d7469fedSdrahn struct intrq openpic_handler[ICU_LEN];
71d7469fedSdrahn
72d9a5f17fSdrahn struct openpic_softc {
73d9a5f17fSdrahn struct device sc_dev;
74d9a5f17fSdrahn };
75d9a5f17fSdrahn
76cc150176Smpi vaddr_t openpic_base;
77cc150176Smpi int openpic_big_endian;
78cc150176Smpi struct evcount openpic_spurious;
79cc150176Smpi int openpic_spurious_irq = 255;
80cc150176Smpi
81c4071fd1Smillert int openpic_match(struct device *parent, void *cf, void *aux);
82c4071fd1Smillert void openpic_attach(struct device *, struct device *, void *);
83cc150176Smpi
84cc150176Smpi int openpic_splraise(int);
85cc150176Smpi int openpic_spllower(int);
86cc150176Smpi void openpic_splx(int);
87cc150176Smpi
88cc150176Smpi u_int openpic_read(int reg);
89cc150176Smpi void openpic_write(int reg, u_int val);
90ba99a0edSmpi
91ba99a0edSmpi void openpic_acknowledge_irq(int, int);
92ba99a0edSmpi void openpic_enable_irq(int, int, int);
93ba99a0edSmpi void openpic_disable_irq(int, int);
94ba99a0edSmpi
95cc150176Smpi void openpic_calc_mask(void);
96cc150176Smpi void openpic_set_priority(int, int);
97cc150176Smpi void *openpic_intr_establish(void *, int, int, int, int (*)(void *), void *,
98cc150176Smpi const char *);
99ba99a0edSmpi void openpic_intr_disestablish(void *, void *);
100c4071fd1Smillert void openpic_collect_preconf_intr(void);
101d7469fedSdrahn void openpic_ext_intr(void);
1027bf9d514Smpi int openpic_ext_intr_handler(struct intrhand *, int *);
103d9a5f17fSdrahn
104ba99a0edSmpi /* Generic IRQ management routines. */
105ba99a0edSmpi void openpic_gen_acknowledge_irq(int, int);
106ba99a0edSmpi void openpic_gen_enable_irq(int, int, int);
107ba99a0edSmpi void openpic_gen_disable_irq(int, int);
108ba99a0edSmpi
109ba99a0edSmpi #if NHPB > 0
110ba99a0edSmpi /* CPC945 IRQ management routines. */
111ba99a0edSmpi void openpic_cpc945_acknowledge_irq(int, int);
112ba99a0edSmpi void openpic_cpc945_enable_irq(int, int, int);
113ba99a0edSmpi void openpic_cpc945_disable_irq(int, int);
114ba99a0edSmpi #endif /* NHPB */
115ba99a0edSmpi
116ba99a0edSmpi struct openpic_ops {
117ba99a0edSmpi void (*acknowledge_irq)(int, int);
118ba99a0edSmpi void (*enable_irq)(int, int, int);
119ba99a0edSmpi void (*disable_irq)(int, int);
120ba99a0edSmpi } openpic_ops = {
121ba99a0edSmpi openpic_gen_acknowledge_irq,
122ba99a0edSmpi openpic_gen_enable_irq,
123ba99a0edSmpi openpic_gen_disable_irq
124ba99a0edSmpi };
125ba99a0edSmpi
126cc150176Smpi #ifdef MULTIPROCESSOR
127cc150176Smpi void openpic_ipi_ddb(void);
128cc150176Smpi
129cc150176Smpi /* IRQ vector used for inter-processor interrupts. */
130cc150176Smpi #define IPI_VECTOR_NOP 64
131cc150176Smpi #define IPI_VECTOR_DDB 65
132cc150176Smpi
133da646a34Smpi static struct evcount ipi_count;
134cc150176Smpi
135da646a34Smpi static int ipi_irq = IPI_VECTOR_NOP;
136cc150176Smpi
137cc150176Smpi intr_send_ipi_t openpic_send_ipi;
138cc150176Smpi #endif /* MULTIPROCESSOR */
139cc150176Smpi
140cc150176Smpi const struct cfattach openpic_ca = {
141cc150176Smpi sizeof(struct openpic_softc), openpic_match, openpic_attach
142d9a5f17fSdrahn };
143d9a5f17fSdrahn
144d9a5f17fSdrahn struct cfdriver openpic_cd = {
145d9a5f17fSdrahn NULL, "openpic", DV_DULL
146d9a5f17fSdrahn };
147d9a5f17fSdrahn
148d7469fedSdrahn u_int
openpic_read(int reg)149d7469fedSdrahn openpic_read(int reg)
150d7469fedSdrahn {
151d7469fedSdrahn char *addr = (void *)(openpic_base + reg);
152d7469fedSdrahn
153ba99a0edSmpi membar_sync();
154d7469fedSdrahn if (openpic_big_endian)
155d7469fedSdrahn return in32(addr);
156d7469fedSdrahn else
157d7469fedSdrahn return in32rb(addr);
158d7469fedSdrahn }
159d7469fedSdrahn
160d7469fedSdrahn void
openpic_write(int reg,u_int val)161d7469fedSdrahn openpic_write(int reg, u_int val)
162d7469fedSdrahn {
163d7469fedSdrahn char *addr = (void *)(openpic_base + reg);
164d7469fedSdrahn
165d7469fedSdrahn if (openpic_big_endian)
166d7469fedSdrahn out32(addr, val);
167d7469fedSdrahn else
168d7469fedSdrahn out32rb(addr, val);
169ba99a0edSmpi membar_sync();
170d7469fedSdrahn }
171d7469fedSdrahn
172d7469fedSdrahn static inline int
openpic_read_irq(int cpu)173d7469fedSdrahn openpic_read_irq(int cpu)
174d7469fedSdrahn {
175d7469fedSdrahn return openpic_read(OPENPIC_IACK(cpu)) & OPENPIC_VECTOR_MASK;
176d7469fedSdrahn }
177d7469fedSdrahn
178d7469fedSdrahn static inline void
openpic_eoi(int cpu)179d7469fedSdrahn openpic_eoi(int cpu)
180d7469fedSdrahn {
181d7469fedSdrahn openpic_write(OPENPIC_EOI(cpu), 0);
182d7469fedSdrahn }
183d7469fedSdrahn
184d9a5f17fSdrahn int
openpic_match(struct device * parent,void * cf,void * aux)185093da1aaSdrahn openpic_match(struct device *parent, void *cf, void *aux)
186d9a5f17fSdrahn {
187d9a5f17fSdrahn char type[40];
188eb32ac1fSdrahn int pirq;
189d9a5f17fSdrahn struct confargs *ca = aux;
190d9a5f17fSdrahn
191d9a5f17fSdrahn bzero (type, sizeof(type));
192d9a5f17fSdrahn
193eb32ac1fSdrahn if (OF_getprop(ca->ca_node, "interrupt-parent", &pirq, sizeof(pirq))
194eb32ac1fSdrahn == sizeof(pirq))
195eb32ac1fSdrahn return 0; /* XXX */
196eb32ac1fSdrahn
19784b94647Smiod if (strcmp(ca->ca_name, "interrupt-controller") != 0 &&
19884b94647Smiod strcmp(ca->ca_name, "mpic") != 0)
199d9a5f17fSdrahn return 0;
20084b94647Smiod
20184b94647Smiod OF_getprop(ca->ca_node, "device_type", type, sizeof(type));
20284b94647Smiod if (strcmp(type, "open-pic") != 0)
20384b94647Smiod return 0;
20484b94647Smiod
20584b94647Smiod if (ca->ca_nreg < 8)
20684b94647Smiod return 0;
20784b94647Smiod
20884b94647Smiod return 1;
209d9a5f17fSdrahn }
210d9a5f17fSdrahn
211d9a5f17fSdrahn void
openpic_attach(struct device * parent,struct device * self,void * aux)212093da1aaSdrahn openpic_attach(struct device *parent, struct device *self, void *aux)
213d9a5f17fSdrahn {
214d7469fedSdrahn struct cpu_info *ci = curcpu();
215d9a5f17fSdrahn struct confargs *ca = aux;
216af5ae5b8Smpi struct intrq *iq;
217af5ae5b8Smpi uint32_t reg = 0;
218af5ae5b8Smpi int i, irq;
219af5ae5b8Smpi u_int x;
2206a034491Sdrahn
2216a034491Sdrahn if (OF_getprop(ca->ca_node, "big-endian", ®, sizeof reg) == 0)
2226a034491Sdrahn openpic_big_endian = 1;
223d9a5f17fSdrahn
224d9a5f17fSdrahn openpic_base = (vaddr_t) mapiodev (ca->ca_baseaddr +
225eb32ac1fSdrahn ca->ca_reg[0], 0x40000);
226d9a5f17fSdrahn
227ba99a0edSmpi /* Reset the PIC */
228ba99a0edSmpi x = openpic_read(OPENPIC_CONFIG) | OPENPIC_CONFIG_RESET;
229ba99a0edSmpi openpic_write(OPENPIC_CONFIG, x);
230ba99a0edSmpi
231ba99a0edSmpi while (openpic_read(OPENPIC_CONFIG) & OPENPIC_CONFIG_RESET)
232ba99a0edSmpi delay(100);
233ba99a0edSmpi
23436fd90dcSjsg /* openpic may support more than 128 interrupts but driver doesn't */
235d7469fedSdrahn openpic_numirq = ((openpic_read(OPENPIC_FEATURE) >> 16) & 0x7f)+1;
236d7469fedSdrahn
237d7469fedSdrahn printf(": version 0x%x feature %x %s",
238d7469fedSdrahn openpic_read(OPENPIC_VENDOR_ID),
239d7469fedSdrahn openpic_read(OPENPIC_FEATURE),
240d7469fedSdrahn openpic_big_endian ? "BE" : "LE" );
241d9a5f17fSdrahn
242af5ae5b8Smpi openpic_set_priority(ci->ci_cpuid, 15);
243af5ae5b8Smpi
244af5ae5b8Smpi /* disable all interrupts */
245af5ae5b8Smpi for (irq = 0; irq < openpic_numirq; irq++)
246af5ae5b8Smpi openpic_write(OPENPIC_SRC_VECTOR(irq), OPENPIC_IMASK);
247af5ae5b8Smpi
248af5ae5b8Smpi for (i = 0; i < openpic_numirq; i++) {
249af5ae5b8Smpi iq = &openpic_handler[i];
250af5ae5b8Smpi TAILQ_INIT(&iq->iq_list);
251af5ae5b8Smpi }
252af5ae5b8Smpi
253af5ae5b8Smpi /* we don't need 8259 pass through mode */
254af5ae5b8Smpi x = openpic_read(OPENPIC_CONFIG);
255af5ae5b8Smpi x |= OPENPIC_CONFIG_8259_PASSTHRU_DISABLE;
256af5ae5b8Smpi openpic_write(OPENPIC_CONFIG, x);
257af5ae5b8Smpi
258af5ae5b8Smpi /* initialize all vectors to something sane */
259af5ae5b8Smpi for (irq = 0; irq < ICU_LEN; irq++) {
260af5ae5b8Smpi x = irq;
261af5ae5b8Smpi x |= OPENPIC_IMASK;
262af5ae5b8Smpi x |= OPENPIC_POLARITY_NEGATIVE;
263af5ae5b8Smpi x |= OPENPIC_SENSE_LEVEL;
264af5ae5b8Smpi x |= 8 << OPENPIC_PRIORITY_SHIFT;
265af5ae5b8Smpi openpic_write(OPENPIC_SRC_VECTOR(irq), x);
266af5ae5b8Smpi }
267af5ae5b8Smpi
268af5ae5b8Smpi /* send all interrupts to cpu 0 */
269af5ae5b8Smpi for (irq = 0; irq < openpic_numirq; irq++)
270af5ae5b8Smpi openpic_write(OPENPIC_IDEST(irq), 1 << 0);
271af5ae5b8Smpi
272d744f4d2Sjsg /* clear all pending interrupts */
273af5ae5b8Smpi for (irq = 0; irq < ICU_LEN; irq++) {
274af5ae5b8Smpi openpic_read_irq(ci->ci_cpuid);
275af5ae5b8Smpi openpic_eoi(ci->ci_cpuid);
276af5ae5b8Smpi }
277af5ae5b8Smpi
278af5ae5b8Smpi #ifdef MULTIPROCESSOR
279af5ae5b8Smpi /* Set up inter-processor interrupts. */
280af5ae5b8Smpi /* IPI0 - NOP */
281af5ae5b8Smpi x = IPI_VECTOR_NOP;
282af5ae5b8Smpi x |= 15 << OPENPIC_PRIORITY_SHIFT;
283af5ae5b8Smpi openpic_write(OPENPIC_IPI_VECTOR(0), x);
284af5ae5b8Smpi /* IPI1 - DDB */
285af5ae5b8Smpi x = IPI_VECTOR_DDB;
286af5ae5b8Smpi x |= 15 << OPENPIC_PRIORITY_SHIFT;
287af5ae5b8Smpi openpic_write(OPENPIC_IPI_VECTOR(1), x);
288af5ae5b8Smpi
289da646a34Smpi evcount_attach(&ipi_count, "ipi", &ipi_irq);
290af5ae5b8Smpi #endif
291af5ae5b8Smpi
292d744f4d2Sjsg /* clear all pending interrupts */
293af5ae5b8Smpi for (irq = 0; irq < ICU_LEN; irq++) {
294af5ae5b8Smpi openpic_read_irq(0);
295af5ae5b8Smpi openpic_eoi(0);
296af5ae5b8Smpi }
297af5ae5b8Smpi
298af5ae5b8Smpi #if 0
299af5ae5b8Smpi openpic_write(OPENPIC_SPURIOUS_VECTOR, 255);
300af5ae5b8Smpi #endif
301af5ae5b8Smpi
302ba99a0edSmpi #if NHPB > 0
303ba99a0edSmpi /* Only U4 systems have a big-endian MPIC. */
304ba99a0edSmpi if (openpic_big_endian) {
305ba99a0edSmpi openpic_ops.acknowledge_irq = openpic_cpc945_acknowledge_irq;
306ba99a0edSmpi openpic_ops.enable_irq = openpic_cpc945_enable_irq;
307ba99a0edSmpi openpic_ops.disable_irq = openpic_cpc945_disable_irq;
308ba99a0edSmpi }
309ba99a0edSmpi #endif
310ba99a0edSmpi
311af5ae5b8Smpi install_extint(openpic_ext_intr);
312af5ae5b8Smpi
313af5ae5b8Smpi openpic_set_priority(ci->ci_cpuid, 0);
314d9a5f17fSdrahn
315d9a5f17fSdrahn intr_establish_func = openpic_intr_establish;
316d9a5f17fSdrahn intr_disestablish_func = openpic_intr_disestablish;
3178d4cccd1Sdrahn #ifdef MULTIPROCESSOR
3188d4cccd1Sdrahn intr_send_ipi_func = openpic_send_ipi;
3198d4cccd1Sdrahn #endif
320d9a5f17fSdrahn
321d7469fedSdrahn ppc_smask_init();
322d7469fedSdrahn
323d9a5f17fSdrahn openpic_collect_preconf_intr();
324d7469fedSdrahn
325d7469fedSdrahn evcount_attach(&openpic_spurious, "spurious", &openpic_spurious_irq);
326d7469fedSdrahn
327d7469fedSdrahn ppc_intr_func.raise = openpic_splraise;
328d7469fedSdrahn ppc_intr_func.lower = openpic_spllower;
329d7469fedSdrahn ppc_intr_func.x = openpic_splx;
330d7469fedSdrahn
331d7469fedSdrahn openpic_set_priority(0, ci->ci_cpl);
332d9a5f17fSdrahn
333d9a5f17fSdrahn ppc_intr_enable(1);
334d9a5f17fSdrahn
335d9a5f17fSdrahn printf("\n");
336d9a5f17fSdrahn }
337d9a5f17fSdrahn
3381c471f42Smpi /* Must be called with interrupt disable. */
339d7469fedSdrahn static inline void
openpic_setipl(int newcpl)340d7469fedSdrahn openpic_setipl(int newcpl)
341d7469fedSdrahn {
342d7469fedSdrahn struct cpu_info *ci = curcpu();
3431c471f42Smpi
344d7469fedSdrahn ci->ci_cpl = newcpl;
345d7469fedSdrahn openpic_set_priority(ci->ci_cpuid, newcpl);
346d7469fedSdrahn }
347de98894eSjasper
348d7469fedSdrahn int
openpic_splraise(int newcpl)349d7469fedSdrahn openpic_splraise(int newcpl)
350d7469fedSdrahn {
351d7469fedSdrahn struct cpu_info *ci = curcpu();
352d7469fedSdrahn int ocpl = ci->ci_cpl;
3531c471f42Smpi int s;
3541c471f42Smpi
3551c471f42Smpi newcpl = openpic_pri_share[newcpl];
356d7469fedSdrahn if (ocpl > newcpl)
357d7469fedSdrahn newcpl = ocpl;
358d7469fedSdrahn
3591c471f42Smpi s = ppc_intr_disable();
360d7469fedSdrahn openpic_setipl(newcpl);
3611c471f42Smpi ppc_intr_enable(s);
362d7469fedSdrahn
363d7469fedSdrahn return ocpl;
364d7469fedSdrahn }
365d7469fedSdrahn
366d7469fedSdrahn int
openpic_spllower(int newcpl)367d7469fedSdrahn openpic_spllower(int newcpl)
368d7469fedSdrahn {
369d7469fedSdrahn struct cpu_info *ci = curcpu();
370d7469fedSdrahn int ocpl = ci->ci_cpl;
371d7469fedSdrahn
372d7469fedSdrahn openpic_splx(newcpl);
373d7469fedSdrahn
374d7469fedSdrahn return ocpl;
375d7469fedSdrahn }
376d7469fedSdrahn
377d7469fedSdrahn void
openpic_splx(int newcpl)378d7469fedSdrahn openpic_splx(int newcpl)
379d7469fedSdrahn {
3801c471f42Smpi struct cpu_info *ci = curcpu();
3811c471f42Smpi int intr, s;
3821c471f42Smpi
3831c471f42Smpi intr = ppc_intr_disable();
3841c471f42Smpi openpic_setipl(newcpl);
385*de052479Scheloha if (ci->ci_dec_deferred && newcpl < IPL_CLOCK) {
386*de052479Scheloha ppc_mtdec(0);
387*de052479Scheloha ppc_mtdec(UINT32_MAX); /* raise DEC exception */
388*de052479Scheloha }
3891c471f42Smpi if (newcpl < IPL_SOFTTTY && (ci->ci_ipending & ppc_smask[newcpl])) {
3901c471f42Smpi s = splsofttty();
3911c471f42Smpi dosoftint(newcpl);
3921c471f42Smpi openpic_setipl(s); /* no-overhead splx */
3931c471f42Smpi }
3941c471f42Smpi ppc_intr_enable(intr);
395d7469fedSdrahn }
396de98894eSjasper
397d9a5f17fSdrahn void
openpic_collect_preconf_intr(void)398af7e7ea9Sderaadt openpic_collect_preconf_intr(void)
399d9a5f17fSdrahn {
400d9a5f17fSdrahn int i;
401d9a5f17fSdrahn for (i = 0; i < ppc_configed_intr_cnt; i++) {
402cc150176Smpi DPRINTF("\n\t%s irq %d level %d fun %p arg %p",
403d9a5f17fSdrahn ppc_configed_intr[i].ih_what, ppc_configed_intr[i].ih_irq,
404d9a5f17fSdrahn ppc_configed_intr[i].ih_level, ppc_configed_intr[i].ih_fun,
405d9a5f17fSdrahn ppc_configed_intr[i].ih_arg);
406d9a5f17fSdrahn openpic_intr_establish(NULL, ppc_configed_intr[i].ih_irq,
407d9a5f17fSdrahn IST_LEVEL, ppc_configed_intr[i].ih_level,
408d9a5f17fSdrahn ppc_configed_intr[i].ih_fun, ppc_configed_intr[i].ih_arg,
409d9a5f17fSdrahn ppc_configed_intr[i].ih_what);
410d9a5f17fSdrahn }
411d9a5f17fSdrahn }
412d9a5f17fSdrahn
413d9a5f17fSdrahn /*
414d9a5f17fSdrahn * Register an interrupt handler.
415d9a5f17fSdrahn */
416d9a5f17fSdrahn void *
openpic_intr_establish(void * lcv,int irq,int type,int level,int (* ih_fun)(void *),void * ih_arg,const char * name)417093da1aaSdrahn openpic_intr_establish(void *lcv, int irq, int type, int level,
418c03b1b92Smk int (*ih_fun)(void *), void *ih_arg, const char *name)
419d9a5f17fSdrahn {
420d7469fedSdrahn struct intrhand *ih;
421d7469fedSdrahn struct intrq *iq;
422bb536b7dSmpi int s, flags;
423d9a5f17fSdrahn
424f0144ea9Smpi if (!LEGAL_IRQ(irq) || type == IST_NONE) {
425f0144ea9Smpi printf("%s: bogus irq %d or type %d", __func__, irq, type);
426f0144ea9Smpi return (NULL);
427f0144ea9Smpi }
428f0144ea9Smpi
429d9a5f17fSdrahn /* no point in sleeping unless someone can free memory. */
430d9a5f17fSdrahn ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
431d9a5f17fSdrahn if (ih == NULL)
432f0144ea9Smpi panic("%s: can't malloc handler info", __func__);
433d9a5f17fSdrahn
434bb536b7dSmpi iq = &openpic_handler[irq];
435d7469fedSdrahn switch (iq->iq_ist) {
436d9a5f17fSdrahn case IST_NONE:
437d7469fedSdrahn iq->iq_ist = type;
438d9a5f17fSdrahn break;
439d9a5f17fSdrahn case IST_EDGE:
4402edcc96aSderaadt intr_shared_edge = 1;
4412edcc96aSderaadt /* FALLTHROUGH */
442d9a5f17fSdrahn case IST_LEVEL:
443d7469fedSdrahn if (type == iq->iq_ist)
444d9a5f17fSdrahn break;
445d9a5f17fSdrahn case IST_PULSE:
446d9a5f17fSdrahn if (type != IST_NONE)
447d9a5f17fSdrahn panic("intr_establish: can't share %s with %s",
448d7469fedSdrahn ppc_intr_typename(iq->iq_ist),
449d7469fedSdrahn ppc_intr_typename(type));
450d9a5f17fSdrahn break;
451d9a5f17fSdrahn }
452d9a5f17fSdrahn
453bb536b7dSmpi flags = level & IPL_MPSAFE;
454bb536b7dSmpi level &= ~IPL_MPSAFE;
455bb536b7dSmpi
456bb536b7dSmpi KASSERT(level <= IPL_TTY || level >= IPL_CLOCK || flags & IPL_MPSAFE);
457bb536b7dSmpi
4585e058f67Sderaadt ih->ih_fun = ih_fun;
4595e058f67Sderaadt ih->ih_arg = ih_arg;
4605e058f67Sderaadt ih->ih_level = level;
461bb536b7dSmpi ih->ih_flags = flags;
4625e058f67Sderaadt ih->ih_irq = irq;
463d7469fedSdrahn
464d7469fedSdrahn evcount_attach(&ih->ih_count, name, &ih->ih_irq);
465d7469fedSdrahn
466d7469fedSdrahn /*
467d7469fedSdrahn * Append handler to end of list
468d7469fedSdrahn */
469d7469fedSdrahn s = ppc_intr_disable();
470d7469fedSdrahn
471d7469fedSdrahn TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list);
472d7469fedSdrahn openpic_calc_mask();
473d7469fedSdrahn
474d7469fedSdrahn ppc_intr_enable(s);
475d9a5f17fSdrahn
476d9a5f17fSdrahn return (ih);
477d9a5f17fSdrahn }
478d9a5f17fSdrahn
479d9a5f17fSdrahn /*
480d9a5f17fSdrahn * Deregister an interrupt handler.
481d9a5f17fSdrahn */
482d9a5f17fSdrahn void
openpic_intr_disestablish(void * lcp,void * arg)483093da1aaSdrahn openpic_intr_disestablish(void *lcp, void *arg)
484d9a5f17fSdrahn {
485d9a5f17fSdrahn struct intrhand *ih = arg;
486d9a5f17fSdrahn int irq = ih->ih_irq;
487fc5790baSjsg struct intrq *iq;
488d7469fedSdrahn int s;
489d9a5f17fSdrahn
490f0144ea9Smpi if (!LEGAL_IRQ(irq)) {
491f0144ea9Smpi printf("%s: bogus irq %d", __func__, irq);
492f0144ea9Smpi return;
493f0144ea9Smpi }
494fc5790baSjsg iq = &openpic_handler[irq];
495d9a5f17fSdrahn
496d9a5f17fSdrahn /*
497d9a5f17fSdrahn * Remove the handler from the chain.
498d9a5f17fSdrahn */
499d7469fedSdrahn s = ppc_intr_disable();
500d7469fedSdrahn
501d7469fedSdrahn TAILQ_REMOVE(&iq->iq_list, ih, ih_list);
502d7469fedSdrahn openpic_calc_mask();
503d7469fedSdrahn
504d7469fedSdrahn ppc_intr_enable(s);
505a4867f13Saaron
506a4867f13Saaron evcount_detach(&ih->ih_count);
50758d5cf8aSderaadt free(ih, M_DEVBUF, sizeof *ih);
508d9a5f17fSdrahn
509d7469fedSdrahn if (TAILQ_EMPTY(&iq->iq_list))
510d7469fedSdrahn iq->iq_ist = IST_NONE;
511d9a5f17fSdrahn }
512d9a5f17fSdrahn
513d9a5f17fSdrahn /*
514d9a5f17fSdrahn * Recalculate the interrupt masks from scratch.
515d9a5f17fSdrahn * We could code special registry and deregistry versions of this function that
516d9a5f17fSdrahn * would be faster, but the code would be nastier, and we don't expect this to
517d9a5f17fSdrahn * happen very much anyway.
518d9a5f17fSdrahn */
519548be99eSdrahn
520548be99eSdrahn void
openpic_calc_mask(void)521af7e7ea9Sderaadt openpic_calc_mask(void)
522d9a5f17fSdrahn {
523d7469fedSdrahn struct cpu_info *ci = curcpu();
524548be99eSdrahn int irq;
525548be99eSdrahn struct intrhand *ih;
526548be99eSdrahn int i;
527d3fed0b3Sdrahn
528d3fed0b3Sdrahn /* disable all openpic interrupts */
529d7469fedSdrahn openpic_set_priority(ci->ci_cpuid, 15);
530d9a5f17fSdrahn
531d7469fedSdrahn for (i = IPL_NONE; i < IPL_NUM; i++) {
532d7469fedSdrahn openpic_pri_share[i] = i;
533d9a5f17fSdrahn }
534d9a5f17fSdrahn
535d7469fedSdrahn for (irq = 0; irq < openpic_numirq; irq++) {
536d7469fedSdrahn int maxipl = IPL_NONE;
537d7469fedSdrahn int minipl = IPL_HIGH;
538d7469fedSdrahn struct intrq *iq = &openpic_handler[irq];
539548be99eSdrahn
540d7469fedSdrahn TAILQ_FOREACH(ih, &iq->iq_list, ih_list) {
541d7469fedSdrahn if (ih->ih_level > maxipl)
542d7469fedSdrahn maxipl = ih->ih_level;
543d7469fedSdrahn if (ih->ih_level < minipl)
544d7469fedSdrahn minipl = ih->ih_level;
545d3fed0b3Sdrahn }
546d3fed0b3Sdrahn
547d7469fedSdrahn if (maxipl == IPL_NONE) {
548d7469fedSdrahn minipl = IPL_NONE; /* Interrupt not enabled */
549548be99eSdrahn
550ba99a0edSmpi openpic_disable_irq(irq, iq->iq_ist);
551d7469fedSdrahn } else {
552d7469fedSdrahn for (i = minipl; i <= maxipl; i++) {
553d7469fedSdrahn openpic_pri_share[i] = maxipl;
554d9a5f17fSdrahn }
555ba99a0edSmpi openpic_enable_irq(irq, iq->iq_ist, maxipl);
556d7469fedSdrahn }
557d7469fedSdrahn
558d7469fedSdrahn iq->iq_ipl = maxipl;
559548be99eSdrahn }
560d3fed0b3Sdrahn
561d3fed0b3Sdrahn /* restore interrupts */
562d7469fedSdrahn openpic_set_priority(ci->ci_cpuid, ci->ci_cpl);
563747d31e5Sderaadt }
5645e058f67Sderaadt
565d9a5f17fSdrahn void
openpic_gen_acknowledge_irq(int irq,int cpuid)566ba99a0edSmpi openpic_gen_acknowledge_irq(int irq, int cpuid)
567ba99a0edSmpi {
568ba99a0edSmpi openpic_eoi(cpuid);
569ba99a0edSmpi }
570ba99a0edSmpi
571ba99a0edSmpi void
openpic_gen_enable_irq(int irq,int ist,int pri)572ba99a0edSmpi openpic_gen_enable_irq(int irq, int ist, int pri)
573d9a5f17fSdrahn {
574d9a5f17fSdrahn u_int x;
575d9a5f17fSdrahn
576d7469fedSdrahn x = irq;
577ba99a0edSmpi
578ba99a0edSmpi if (ist == IST_LEVEL)
579d9a5f17fSdrahn x |= OPENPIC_SENSE_LEVEL;
580093da1aaSdrahn else
581d9a5f17fSdrahn x |= OPENPIC_SENSE_EDGE;
582d7469fedSdrahn x |= OPENPIC_POLARITY_NEGATIVE;
583d7469fedSdrahn x |= pri << OPENPIC_PRIORITY_SHIFT;
5841f58a4a3Sdrahn openpic_write(OPENPIC_SRC_VECTOR(irq), x);
5851f58a4a3Sdrahn }
586d9a5f17fSdrahn
587d9a5f17fSdrahn void
openpic_gen_disable_irq(int irq,int ist)588ba99a0edSmpi openpic_gen_disable_irq(int irq, int ist)
589d9a5f17fSdrahn {
590d9a5f17fSdrahn u_int x;
591d9a5f17fSdrahn
592d9a5f17fSdrahn x = openpic_read(OPENPIC_SRC_VECTOR(irq));
593d9a5f17fSdrahn x |= OPENPIC_IMASK;
594d9a5f17fSdrahn openpic_write(OPENPIC_SRC_VECTOR(irq), x);
595d9a5f17fSdrahn }
596d9a5f17fSdrahn
597d9a5f17fSdrahn void
openpic_set_priority(int cpu,int pri)5985e058f67Sderaadt openpic_set_priority(int cpu, int pri)
599d9a5f17fSdrahn {
600d7469fedSdrahn openpic_write(OPENPIC_CPU_PRIORITY(cpu), pri);
601d9a5f17fSdrahn }
602d9a5f17fSdrahn
603d7469fedSdrahn int openpic_irqnest[PPC_MAXPROCS];
604d7469fedSdrahn int openpic_irqloop[PPC_MAXPROCS];
605ba99a0edSmpi
606d9a5f17fSdrahn void
openpic_ext_intr(void)607ba99a0edSmpi openpic_ext_intr(void)
608d9a5f17fSdrahn {
6099e61e18aSkettenis struct cpu_info *ci = curcpu();
610d7469fedSdrahn int irq, pcpl, ret;
611d7469fedSdrahn int maxipl = IPL_NONE;
612d9a5f17fSdrahn struct intrhand *ih;
613d7469fedSdrahn struct intrq *iq;
614d7469fedSdrahn int spurious;
615d9a5f17fSdrahn
6169e61e18aSkettenis pcpl = ci->ci_cpl;
617d9a5f17fSdrahn
618d7469fedSdrahn openpic_irqloop[ci->ci_cpuid] = 0;
619d7469fedSdrahn irq = openpic_read_irq(ci->ci_cpuid);
620d7469fedSdrahn openpic_irqnest[ci->ci_cpuid]++;
621d9a5f17fSdrahn
622d7469fedSdrahn while (irq != 255) {
623d7469fedSdrahn openpic_irqloop[ci->ci_cpuid]++;
624cc150176Smpi #ifdef OPENPIC_DEBUG
625d7469fedSdrahn if (openpic_irqloop[ci->ci_cpuid] > 20 ||
626d7469fedSdrahn openpic_irqnest[ci->ci_cpuid] > 3) {
627d7469fedSdrahn printf("irqloop %d irqnest %d\n",
628d7469fedSdrahn openpic_irqloop[ci->ci_cpuid],
629d7469fedSdrahn openpic_irqnest[ci->ci_cpuid]);
630d7469fedSdrahn }
6319aecfc33Sderaadt #endif
63231855013Sdrahn if (openpic_irqloop[ci->ci_cpuid] > 20) {
633cc150176Smpi DPRINTF("irqloop %d irqnest %d: returning\n",
63431855013Sdrahn openpic_irqloop[ci->ci_cpuid],
63531855013Sdrahn openpic_irqnest[ci->ci_cpuid]);
63631855013Sdrahn openpic_irqnest[ci->ci_cpuid]--;
63731855013Sdrahn return;
63831855013Sdrahn }
6391c49a0a6Skettenis #ifdef MULTIPROCESSOR
640da646a34Smpi if (irq == IPI_VECTOR_NOP || irq == IPI_VECTOR_DDB) {
641da646a34Smpi ipi_count.ec_count++;
6421c49a0a6Skettenis openpic_eoi(ci->ci_cpuid);
643da646a34Smpi if (irq == IPI_VECTOR_DDB)
6448d4cccd1Sdrahn openpic_ipi_ddb();
645d7469fedSdrahn irq = openpic_read_irq(ci->ci_cpuid);
6468d4cccd1Sdrahn continue;
6478d4cccd1Sdrahn }
6481c49a0a6Skettenis #endif
649d7469fedSdrahn iq = &openpic_handler[irq];
6501c49a0a6Skettenis
651bb536b7dSmpi #ifdef OPENPIC_DEBUG
652bb536b7dSmpi if (iq->iq_ipl <= pcpl)
653d7469fedSdrahn printf("invalid interrupt %d lvl %d at %d hw %d\n",
654bb536b7dSmpi irq, iq->iq_ipl, pcpl,
655d7469fedSdrahn openpic_read(OPENPIC_CPU_PRIORITY(ci->ci_cpuid)));
656bb536b7dSmpi #endif
657bb536b7dSmpi
658d7469fedSdrahn if (iq->iq_ipl > maxipl)
659d7469fedSdrahn maxipl = iq->iq_ipl;
6601c471f42Smpi openpic_splraise(iq->iq_ipl);
661ba99a0edSmpi openpic_acknowledge_irq(irq, ci->ci_cpuid);
662f8e13b6fSdrahn
663d7469fedSdrahn spurious = 1;
664d7469fedSdrahn TAILQ_FOREACH(ih, &iq->iq_list, ih_list) {
6651f58a4a3Sdrahn ppc_intr_enable(1);
6667bf9d514Smpi ret = openpic_ext_intr_handler(ih, &spurious);
6671f58a4a3Sdrahn (void)ppc_intr_disable();
6682edcc96aSderaadt if (intr_shared_edge == 00 && ret == 1)
6692edcc96aSderaadt break;
670d7469fedSdrahn }
671d7469fedSdrahn if (spurious) {
672d7469fedSdrahn openpic_spurious.ec_count++;
673cc150176Smpi DPRINTF("spurious intr %d\n", irq);
674d9a5f17fSdrahn }
675d9a5f17fSdrahn
676d9a5f17fSdrahn uvmexp.intrs++;
677d7469fedSdrahn openpic_setipl(pcpl);
678d7469fedSdrahn
679d7469fedSdrahn irq = openpic_read_irq(ci->ci_cpuid);
680d9a5f17fSdrahn }
681747d31e5Sderaadt
6821c471f42Smpi openpic_splx(pcpl); /* Process pendings. */
683d7469fedSdrahn openpic_irqnest[ci->ci_cpuid]--;
684d9a5f17fSdrahn }
685b3f09b5bSkettenis
686bb536b7dSmpi int
openpic_ext_intr_handler(struct intrhand * ih,int * spurious)6877bf9d514Smpi openpic_ext_intr_handler(struct intrhand *ih, int *spurious)
688bb536b7dSmpi {
689bb536b7dSmpi int ret;
690bb536b7dSmpi #ifdef MULTIPROCESSOR
691bb536b7dSmpi int need_lock;
692bb536b7dSmpi
693bb536b7dSmpi if (ih->ih_flags & IPL_MPSAFE)
694bb536b7dSmpi need_lock = 0;
695bb536b7dSmpi else
6967bf9d514Smpi need_lock = 1;
697bb536b7dSmpi
698bb536b7dSmpi if (need_lock)
699bb536b7dSmpi KERNEL_LOCK();
700bb536b7dSmpi #endif
701bb536b7dSmpi ret = (*ih->ih_fun)(ih->ih_arg);
702bb536b7dSmpi if (ret) {
703bb536b7dSmpi ih->ih_count.ec_count++;
704bb536b7dSmpi *spurious = 0;
705bb536b7dSmpi }
706bb536b7dSmpi
707bb536b7dSmpi #ifdef MULTIPROCESSOR
708bb536b7dSmpi if (need_lock)
709bb536b7dSmpi KERNEL_UNLOCK();
710bb536b7dSmpi #endif
711bb536b7dSmpi
712bb536b7dSmpi return (ret);
713bb536b7dSmpi }
714bb536b7dSmpi
715ba99a0edSmpi void
openpic_acknowledge_irq(int irq,int cpuid)716ba99a0edSmpi openpic_acknowledge_irq(int irq, int cpuid)
717ba99a0edSmpi {
718ba99a0edSmpi (openpic_ops.acknowledge_irq)(irq, cpuid);
719ba99a0edSmpi }
720ba99a0edSmpi
721ba99a0edSmpi void
openpic_enable_irq(int irq,int ist,int pri)722ba99a0edSmpi openpic_enable_irq(int irq, int ist, int pri)
723ba99a0edSmpi {
724ba99a0edSmpi (openpic_ops.enable_irq)(irq, ist, pri);
725ba99a0edSmpi }
726ba99a0edSmpi
727ba99a0edSmpi void
openpic_disable_irq(int irq,int ist)728ba99a0edSmpi openpic_disable_irq(int irq, int ist)
729ba99a0edSmpi {
730ba99a0edSmpi (openpic_ops.disable_irq)(irq, ist);
731ba99a0edSmpi }
732ba99a0edSmpi
733cc150176Smpi #ifdef MULTIPROCESSOR
7348d4cccd1Sdrahn void
openpic_send_ipi(struct cpu_info * ci,int id)735ba99a0edSmpi openpic_send_ipi(struct cpu_info *ci, int id)
736ba99a0edSmpi {
737ba99a0edSmpi switch (id) {
738ba99a0edSmpi case PPC_IPI_NOP:
739ba99a0edSmpi id = 0;
740ba99a0edSmpi break;
741ba99a0edSmpi case PPC_IPI_DDB:
742ba99a0edSmpi id = 1;
743ba99a0edSmpi break;
744ba99a0edSmpi default:
745ba99a0edSmpi panic("invalid ipi send to cpu %d %d", ci->ci_cpuid, id);
746ba99a0edSmpi }
747ba99a0edSmpi
748ba99a0edSmpi openpic_write(OPENPIC_IPI(curcpu()->ci_cpuid, id), 1 << ci->ci_cpuid);
749ba99a0edSmpi }
750ba99a0edSmpi
751ba99a0edSmpi void
openpic_ipi_ddb(void)752ba99a0edSmpi openpic_ipi_ddb(void)
7538d4cccd1Sdrahn {
7545dbffadbSderaadt #ifdef DDB
755e97088d6Smpi db_enter();
7565dbffadbSderaadt #endif
7578d4cccd1Sdrahn }
758cc150176Smpi #endif /* MULTIPROCESSOR */
759ba99a0edSmpi
760ba99a0edSmpi #if NHPB > 0
761ba99a0edSmpi extern int hpb_enable_irq(int, int);
762ba99a0edSmpi extern int hpb_disable_irq(int, int);
763ba99a0edSmpi extern void hpb_eoi(int);
764ba99a0edSmpi
765ba99a0edSmpi void
openpic_cpc945_acknowledge_irq(int irq,int cpuid)766ba99a0edSmpi openpic_cpc945_acknowledge_irq(int irq, int cpuid)
767ba99a0edSmpi {
768ba99a0edSmpi hpb_eoi(irq);
769ba99a0edSmpi openpic_gen_acknowledge_irq(irq, cpuid);
770ba99a0edSmpi }
771ba99a0edSmpi
772ba99a0edSmpi void
openpic_cpc945_enable_irq(int irq,int ist,int pri)773ba99a0edSmpi openpic_cpc945_enable_irq(int irq, int ist, int pri)
774ba99a0edSmpi {
775ba99a0edSmpi if (hpb_enable_irq(irq, ist)) {
776ba99a0edSmpi u_int x = irq;
777ba99a0edSmpi
778ba99a0edSmpi x |= OPENPIC_SENSE_EDGE;
779ba99a0edSmpi x |= OPENPIC_POLARITY_POSITIVE;
780ba99a0edSmpi x |= pri << OPENPIC_PRIORITY_SHIFT;
781ba99a0edSmpi openpic_write(OPENPIC_SRC_VECTOR(irq), x);
782ba99a0edSmpi
783ba99a0edSmpi hpb_eoi(irq);
784ba99a0edSmpi } else
785ba99a0edSmpi openpic_gen_enable_irq(irq, ist, pri);
786ba99a0edSmpi }
787ba99a0edSmpi
788ba99a0edSmpi void
openpic_cpc945_disable_irq(int irq,int ist)789ba99a0edSmpi openpic_cpc945_disable_irq(int irq, int ist)
790ba99a0edSmpi {
791ba99a0edSmpi hpb_disable_irq(irq, ist);
792ba99a0edSmpi openpic_gen_disable_irq(irq, ist);
793ba99a0edSmpi }
794ba99a0edSmpi #endif /* NHPB */
795ba99a0edSmpi
796