1*aed05100Sjmcneill /* $Id: imx23_icoll.c,v 1.4 2022/06/25 12:41:56 jmcneill Exp $ */
2eba5cacbSjkunz
3eba5cacbSjkunz /*
4eba5cacbSjkunz * Copyright (c) 2012 The NetBSD Foundation, Inc.
5eba5cacbSjkunz * All rights reserved.
6eba5cacbSjkunz *
7eba5cacbSjkunz * This code is derived from software contributed to The NetBSD Foundation
8eba5cacbSjkunz * by Petri Laakso.
9eba5cacbSjkunz *
10eba5cacbSjkunz * Redistribution and use in source and binary forms, with or without
11eba5cacbSjkunz * modification, are permitted provided that the following conditions
12eba5cacbSjkunz * are met:
13eba5cacbSjkunz * 1. Redistributions of source code must retain the above copyright
14eba5cacbSjkunz * notice, this list of conditions and the following disclaimer.
15eba5cacbSjkunz * 2. Redistributions in binary form must reproduce the above copyright
16eba5cacbSjkunz * notice, this list of conditions and the following disclaimer in the
17eba5cacbSjkunz * documentation and/or other materials provided with the distribution.
18eba5cacbSjkunz *
19eba5cacbSjkunz * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20eba5cacbSjkunz * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21eba5cacbSjkunz * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22eba5cacbSjkunz * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23eba5cacbSjkunz * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24eba5cacbSjkunz * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25eba5cacbSjkunz * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26eba5cacbSjkunz * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27eba5cacbSjkunz * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28eba5cacbSjkunz * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29eba5cacbSjkunz * POSSIBILITY OF SUCH DAMAGE.
30eba5cacbSjkunz */
31eba5cacbSjkunz
32eba5cacbSjkunz #include <sys/param.h>
33eba5cacbSjkunz #include <sys/bus.h>
34eba5cacbSjkunz #include <sys/cpu.h>
35eba5cacbSjkunz #include <sys/device.h>
36eba5cacbSjkunz #include <sys/errno.h>
37eba5cacbSjkunz #include <sys/systm.h>
38eba5cacbSjkunz
3917d96df1Smartin #include <arm/cpufunc.h>
4017d96df1Smartin
41eba5cacbSjkunz #define _INTR_PRIVATE
42eba5cacbSjkunz #include <arm/pic/picvar.h>
43eba5cacbSjkunz
44eba5cacbSjkunz #include <arm/imx/imx23_icollreg.h>
45eba5cacbSjkunz #include <arm/imx/imx23var.h>
46eba5cacbSjkunz
47eba5cacbSjkunz #define ICOLL_SOFT_RST_LOOP 455 /* At least 1 us ... */
48eba5cacbSjkunz #define ICOLL_READ(sc, reg) \
49eba5cacbSjkunz bus_space_read_4(sc->sc_iot, sc->sc_hdl, (reg))
50eba5cacbSjkunz #define ICOLL_WRITE(sc, reg, val) \
51eba5cacbSjkunz bus_space_write_4(sc->sc_iot, sc->sc_hdl, (reg), (val))
52eba5cacbSjkunz
53eba5cacbSjkunz #define ICOLL_IRQ_REG_SIZE 0x10
54eba5cacbSjkunz #define ICOLL_CLR_IRQ(sc, irq) \
55eba5cacbSjkunz ICOLL_WRITE(sc, HW_ICOLL_INTERRUPT0_CLR + \
56eba5cacbSjkunz (irq) * ICOLL_IRQ_REG_SIZE, \
57eba5cacbSjkunz HW_ICOLL_INTERRUPT_ENABLE)
58eba5cacbSjkunz #define ICOLL_SET_IRQ(sc, irq) \
59eba5cacbSjkunz ICOLL_WRITE(sc, HW_ICOLL_INTERRUPT0_SET + \
60eba5cacbSjkunz (irq) * ICOLL_IRQ_REG_SIZE, \
61eba5cacbSjkunz HW_ICOLL_INTERRUPT_ENABLE)
62eba5cacbSjkunz #define ICOLL_GET_PRIO(sc, irq) \
63eba5cacbSjkunz __SHIFTOUT(ICOLL_READ(sc, HW_ICOLL_INTERRUPT0 + \
64eba5cacbSjkunz (irq) * ICOLL_IRQ_REG_SIZE), \
65eba5cacbSjkunz HW_ICOLL_INTERRUPT_PRIORITY)
66eba5cacbSjkunz
67eba5cacbSjkunz #define PICTOSOFTC(pic) \
68eba5cacbSjkunz ((struct icoll_softc *)((char *)(pic) - \
69eba5cacbSjkunz offsetof(struct icoll_softc, sc_pic)))
70eba5cacbSjkunz
71eba5cacbSjkunz /*
72eba5cacbSjkunz * pic callbacks.
73eba5cacbSjkunz */
74eba5cacbSjkunz static void icoll_unblock_irqs(struct pic_softc *, size_t, uint32_t);
75eba5cacbSjkunz static void icoll_block_irqs(struct pic_softc *, size_t, uint32_t);
76eba5cacbSjkunz static int icoll_find_pending_irqs(struct pic_softc *);
77eba5cacbSjkunz static void icoll_establish_irq(struct pic_softc *, struct intrsource *);
78eba5cacbSjkunz static void icoll_source_name(struct pic_softc *, int, char *, size_t);
79eba5cacbSjkunz static void icoll_set_priority(struct pic_softc *, int);
80eba5cacbSjkunz
81eba5cacbSjkunz /*
82eba5cacbSjkunz * autoconf(9) callbacks.
83eba5cacbSjkunz */
84eba5cacbSjkunz static int icoll_match(device_t, cfdata_t, void *);
85eba5cacbSjkunz static void icoll_attach(device_t, device_t, void *);
86eba5cacbSjkunz static int icoll_activate(device_t, enum devact);
87eba5cacbSjkunz
88eba5cacbSjkunz /*
89eba5cacbSjkunz * ARM interrupt handler.
90eba5cacbSjkunz */
91eba5cacbSjkunz void imx23_intr_dispatch(struct clockframe *);
92eba5cacbSjkunz
93eba5cacbSjkunz const static struct pic_ops icoll_pic_ops = {
94eba5cacbSjkunz .pic_unblock_irqs = icoll_unblock_irqs,
95eba5cacbSjkunz .pic_block_irqs = icoll_block_irqs,
96eba5cacbSjkunz .pic_find_pending_irqs = icoll_find_pending_irqs,
97eba5cacbSjkunz .pic_establish_irq = icoll_establish_irq,
98eba5cacbSjkunz .pic_source_name = icoll_source_name,
99eba5cacbSjkunz .pic_set_priority = icoll_set_priority
100eba5cacbSjkunz };
101eba5cacbSjkunz
102eba5cacbSjkunz struct icoll_softc {
103eba5cacbSjkunz device_t sc_dev;
104eba5cacbSjkunz struct pic_softc sc_pic;
105eba5cacbSjkunz bus_space_tag_t sc_iot;
106eba5cacbSjkunz bus_space_handle_t sc_hdl;
107eba5cacbSjkunz };
108eba5cacbSjkunz
109eba5cacbSjkunz /* For IRQ handler. */
110eba5cacbSjkunz static struct icoll_softc *icoll_sc;
111eba5cacbSjkunz
112eba5cacbSjkunz /*
113eba5cacbSjkunz * Private to driver.
114eba5cacbSjkunz */
115eba5cacbSjkunz static void icoll_reset(struct icoll_softc *);
116eba5cacbSjkunz
117eba5cacbSjkunz CFATTACH_DECL3_NEW(icoll,
118eba5cacbSjkunz sizeof(struct icoll_softc),
119eba5cacbSjkunz icoll_match,
120eba5cacbSjkunz icoll_attach,
121eba5cacbSjkunz NULL,
122eba5cacbSjkunz icoll_activate,
123eba5cacbSjkunz NULL,
124eba5cacbSjkunz NULL,
125eba5cacbSjkunz 0);
126eba5cacbSjkunz
127eba5cacbSjkunz /*
128eba5cacbSjkunz * ARM interrupt handler.
129eba5cacbSjkunz */
130eba5cacbSjkunz void
imx23_intr_dispatch(struct clockframe * frame)131eba5cacbSjkunz imx23_intr_dispatch(struct clockframe *frame)
132eba5cacbSjkunz {
133eba5cacbSjkunz struct cpu_info * const ci = curcpu();
134eba5cacbSjkunz struct pic_softc *pic_sc;
135eba5cacbSjkunz int saved_spl;
136eba5cacbSjkunz uint8_t irq;
137eba5cacbSjkunz uint8_t prio;
138eba5cacbSjkunz
139eba5cacbSjkunz pic_sc = &icoll_sc->sc_pic;
140eba5cacbSjkunz
141eba5cacbSjkunz ci->ci_data.cpu_nintr++;
142eba5cacbSjkunz
143eba5cacbSjkunz /* Save current spl. */
144eba5cacbSjkunz saved_spl = curcpl();
145eba5cacbSjkunz
146eba5cacbSjkunz /* IRQ to be handled. */
147eba5cacbSjkunz irq = __SHIFTOUT(ICOLL_READ(icoll_sc, HW_ICOLL_STAT),
148eba5cacbSjkunz HW_ICOLL_STAT_VECTOR_NUMBER);
149eba5cacbSjkunz
150eba5cacbSjkunz /* Save IRQ's priority. Acknowledge it later. */
151eba5cacbSjkunz prio = ICOLL_GET_PRIO(icoll_sc, irq);
152eba5cacbSjkunz
153eba5cacbSjkunz /*
154eba5cacbSjkunz * Notify ICOLL to deassert IRQ before re-enabling the IRQ's.
155eba5cacbSjkunz * This is done by writing anything to HW_ICOLL_VECTOR.
156eba5cacbSjkunz */
157eba5cacbSjkunz ICOLL_WRITE(icoll_sc, HW_ICOLL_VECTOR,
158eba5cacbSjkunz __SHIFTIN(0x3fffffff, HW_ICOLL_VECTOR_IRQVECTOR));
159eba5cacbSjkunz
160eba5cacbSjkunz /* Bogus IRQ. */
161eba5cacbSjkunz if (irq == 0x7f) {
162eba5cacbSjkunz cpsie(I32_bit);
163eba5cacbSjkunz ICOLL_WRITE(icoll_sc, HW_ICOLL_LEVELACK, (1<<prio));
164eba5cacbSjkunz cpsid(I32_bit);
165eba5cacbSjkunz return;
166eba5cacbSjkunz }
167eba5cacbSjkunz
168eba5cacbSjkunz /* Raise the spl to the level of the IRQ. */
169eba5cacbSjkunz if (pic_sc->pic_sources[irq]->is_ipl > ci->ci_cpl)
170eba5cacbSjkunz saved_spl = _splraise(pic_sc->pic_sources[irq]->is_ipl);
171eba5cacbSjkunz
172eba5cacbSjkunz /* Call the handler registered for the IRQ. */
173eba5cacbSjkunz cpsie(I32_bit);
174eba5cacbSjkunz pic_dispatch(pic_sc->pic_sources[irq], frame);
175eba5cacbSjkunz
176eba5cacbSjkunz /*
177eba5cacbSjkunz * Acknowledge the IRQ by writing its priority to HW_ICOLL_LEVELACK.
178eba5cacbSjkunz * Interrupts should be enabled.
179eba5cacbSjkunz */
180eba5cacbSjkunz ICOLL_WRITE(icoll_sc, HW_ICOLL_LEVELACK, (1<<prio));
181eba5cacbSjkunz cpsid(I32_bit);
182eba5cacbSjkunz
183eba5cacbSjkunz /* Restore the saved spl. */
184eba5cacbSjkunz splx(saved_spl);
185eba5cacbSjkunz
186eba5cacbSjkunz return;
187eba5cacbSjkunz }
188eba5cacbSjkunz
189eba5cacbSjkunz /*
190eba5cacbSjkunz * pic callbacks.
191eba5cacbSjkunz */
192eba5cacbSjkunz static void
icoll_unblock_irqs(struct pic_softc * pic,size_t irq_base,uint32_t irq_mask)193eba5cacbSjkunz icoll_unblock_irqs(struct pic_softc *pic, size_t irq_base, uint32_t irq_mask)
194eba5cacbSjkunz {
195eba5cacbSjkunz struct icoll_softc *sc = PICTOSOFTC(pic);
196eba5cacbSjkunz uint8_t b;
197eba5cacbSjkunz
198eba5cacbSjkunz for (;;) {
199eba5cacbSjkunz b = ffs(irq_mask);
200eba5cacbSjkunz if (b == 0) break;
201eba5cacbSjkunz b--; /* Zero based index. */
202eba5cacbSjkunz ICOLL_SET_IRQ(sc, irq_base + b);
203eba5cacbSjkunz irq_mask &= ~(1<<b);
204eba5cacbSjkunz }
205eba5cacbSjkunz
206eba5cacbSjkunz return;
207eba5cacbSjkunz }
208eba5cacbSjkunz
209eba5cacbSjkunz static void
icoll_block_irqs(struct pic_softc * pic,size_t irq_base,uint32_t irq_mask)210eba5cacbSjkunz icoll_block_irqs(struct pic_softc *pic, size_t irq_base, uint32_t irq_mask)
211eba5cacbSjkunz {
212eba5cacbSjkunz struct icoll_softc *sc = PICTOSOFTC(pic);
213eba5cacbSjkunz uint8_t b;
214eba5cacbSjkunz
215eba5cacbSjkunz for (;;) {
216eba5cacbSjkunz b = ffs(irq_mask);
217eba5cacbSjkunz if (b == 0) break;
218eba5cacbSjkunz b--; /* Zero based index. */
219eba5cacbSjkunz ICOLL_CLR_IRQ(sc, irq_base + b);
220eba5cacbSjkunz irq_mask &= ~(1<<b);
221eba5cacbSjkunz }
222eba5cacbSjkunz
223eba5cacbSjkunz return;
224eba5cacbSjkunz }
225eba5cacbSjkunz
226eba5cacbSjkunz static int
icoll_find_pending_irqs(struct pic_softc * pic)227eba5cacbSjkunz icoll_find_pending_irqs(struct pic_softc *pic)
228eba5cacbSjkunz {
229eba5cacbSjkunz return 0; /* ICOLL HW doesn't provide list of pending interrupts. */
230eba5cacbSjkunz }
231eba5cacbSjkunz
232eba5cacbSjkunz static void
icoll_establish_irq(struct pic_softc * pic,struct intrsource * is)233eba5cacbSjkunz icoll_establish_irq(struct pic_softc *pic, struct intrsource *is)
234eba5cacbSjkunz {
235eba5cacbSjkunz return; /* Nothing to establish. */
236eba5cacbSjkunz }
237eba5cacbSjkunz
238eba5cacbSjkunz static void
icoll_source_name(struct pic_softc * pic,int irq,char * is_source,size_t size)239eba5cacbSjkunz icoll_source_name(struct pic_softc *pic, int irq, char *is_source, size_t size)
240eba5cacbSjkunz {
241eba5cacbSjkunz snprintf(is_source, size, "irq %d", irq);
242eba5cacbSjkunz }
243eba5cacbSjkunz
244eba5cacbSjkunz /*
245eba5cacbSjkunz * Set new interrupt priority level by enabling or disabling IRQ's.
246eba5cacbSjkunz */
247eba5cacbSjkunz static void
icoll_set_priority(struct pic_softc * pic,int newipl)248eba5cacbSjkunz icoll_set_priority(struct pic_softc *pic, int newipl)
249eba5cacbSjkunz {
250eba5cacbSjkunz struct icoll_softc *sc = PICTOSOFTC(pic);
251eba5cacbSjkunz struct intrsource *is;
252eba5cacbSjkunz int i;
253eba5cacbSjkunz
254*aed05100Sjmcneill register_t psw = DISABLE_INTERRUPT_SAVE();
255*aed05100Sjmcneill
256eba5cacbSjkunz for (i = 0; i < pic->pic_maxsources; i++) {
257eba5cacbSjkunz is = pic->pic_sources[i];
258eba5cacbSjkunz if (is == NULL)
259eba5cacbSjkunz continue;
260eba5cacbSjkunz if (is->is_ipl > newipl)
261eba5cacbSjkunz ICOLL_SET_IRQ(sc, pic->pic_irqbase + is->is_irq);
262eba5cacbSjkunz else
263eba5cacbSjkunz ICOLL_CLR_IRQ(sc, pic->pic_irqbase + is->is_irq);
264eba5cacbSjkunz }
265*aed05100Sjmcneill
266*aed05100Sjmcneill curcpu()->ci_cpl = newipl;
267*aed05100Sjmcneill
268*aed05100Sjmcneill if ((psw & I32_bit) == 0) {
269*aed05100Sjmcneill ENABLE_INTERRUPT();
270*aed05100Sjmcneill }
271eba5cacbSjkunz }
272eba5cacbSjkunz
273eba5cacbSjkunz /*
274eba5cacbSjkunz * autoconf(9) callbacks.
275eba5cacbSjkunz */
276eba5cacbSjkunz static int
icoll_match(device_t parent,cfdata_t match,void * aux)277eba5cacbSjkunz icoll_match(device_t parent, cfdata_t match, void *aux)
278eba5cacbSjkunz {
279eba5cacbSjkunz struct apb_attach_args *aa = aux;
280eba5cacbSjkunz
281eba5cacbSjkunz if ((aa->aa_addr == HW_ICOLL_BASE) && (aa->aa_size == HW_ICOLL_SIZE))
282eba5cacbSjkunz return 1;
283eba5cacbSjkunz
284eba5cacbSjkunz return 0;
285eba5cacbSjkunz }
286eba5cacbSjkunz
287eba5cacbSjkunz static void
icoll_attach(device_t parent,device_t self,void * aux)288eba5cacbSjkunz icoll_attach(device_t parent, device_t self, void *aux)
289eba5cacbSjkunz {
290eba5cacbSjkunz static int icoll_attached = 0;
291eba5cacbSjkunz struct icoll_softc *sc = device_private(self);
292eba5cacbSjkunz struct apb_attach_args *aa = aux;
293eba5cacbSjkunz
294eba5cacbSjkunz if (icoll_attached)
295eba5cacbSjkunz return;
296eba5cacbSjkunz
297eba5cacbSjkunz icoll_sc = sc;
298eba5cacbSjkunz
299eba5cacbSjkunz sc->sc_dev = self;
300eba5cacbSjkunz sc->sc_iot = aa->aa_iot;
301eba5cacbSjkunz
302eba5cacbSjkunz sc->sc_pic.pic_maxsources = IRQ_LAST + 1;
303eba5cacbSjkunz sc->sc_pic.pic_ops = &icoll_pic_ops;
304eba5cacbSjkunz strlcpy(sc->sc_pic.pic_name, device_xname(self),
305eba5cacbSjkunz sizeof(sc->sc_pic.pic_name));
306eba5cacbSjkunz
307eba5cacbSjkunz if (bus_space_map(sc->sc_iot,
308eba5cacbSjkunz aa->aa_addr, aa->aa_size, 0, &(sc->sc_hdl))) {
309eba5cacbSjkunz aprint_error_dev(sc->sc_dev, "unable to map bus space\n");
310eba5cacbSjkunz return;
311eba5cacbSjkunz }
312eba5cacbSjkunz
313eba5cacbSjkunz icoll_reset(sc);
314eba5cacbSjkunz pic_add(&sc->sc_pic, 0);
315eba5cacbSjkunz aprint_normal("\n");
316eba5cacbSjkunz icoll_attached = 1;
317eba5cacbSjkunz
318eba5cacbSjkunz return;
319eba5cacbSjkunz }
320eba5cacbSjkunz
321eba5cacbSjkunz static int
icoll_activate(device_t self,enum devact act)322eba5cacbSjkunz icoll_activate(device_t self, enum devact act)
323eba5cacbSjkunz {
324eba5cacbSjkunz return EOPNOTSUPP;
325eba5cacbSjkunz }
326eba5cacbSjkunz
327eba5cacbSjkunz /*
328eba5cacbSjkunz * Reset the ICOLL block.
329eba5cacbSjkunz *
3308a7d722dSjkunz * Inspired by i.MX23 RM "39.3.10 Correct Way to Soft Reset a Block"
331eba5cacbSjkunz */
332eba5cacbSjkunz static void
icoll_reset(struct icoll_softc * sc)333eba5cacbSjkunz icoll_reset(struct icoll_softc *sc)
334eba5cacbSjkunz {
335eba5cacbSjkunz unsigned int loop;
336eba5cacbSjkunz
337eba5cacbSjkunz /*
338eba5cacbSjkunz * Prepare for soft-reset by making sure that SFTRST is not currently
339eba5cacbSjkunz * asserted. Also clear CLKGATE so we can wait for its assertion below.
340eba5cacbSjkunz */
341eba5cacbSjkunz ICOLL_WRITE(sc, HW_ICOLL_CTRL_CLR, HW_ICOLL_CTRL_SFTRST);
342eba5cacbSjkunz
343eba5cacbSjkunz /* Wait at least a microsecond for SFTRST to deassert. */
344eba5cacbSjkunz loop = 0;
345eba5cacbSjkunz while ((ICOLL_READ(sc, HW_ICOLL_CTRL) & HW_ICOLL_CTRL_SFTRST) ||
346eba5cacbSjkunz (loop < ICOLL_SOFT_RST_LOOP)) {
347eba5cacbSjkunz loop++;
348eba5cacbSjkunz }
349eba5cacbSjkunz
350eba5cacbSjkunz /* Clear CLKGATE so we can wait for its assertion below. */
351eba5cacbSjkunz ICOLL_WRITE(sc, HW_ICOLL_CTRL_CLR, HW_ICOLL_CTRL_CLKGATE);
352eba5cacbSjkunz
353eba5cacbSjkunz /* Soft-reset the block. */
354eba5cacbSjkunz ICOLL_WRITE(sc, HW_ICOLL_CTRL_SET, HW_ICOLL_CTRL_SFTRST);
355eba5cacbSjkunz
356eba5cacbSjkunz /* Wait until clock is in the gated state. */
357eba5cacbSjkunz while (!(ICOLL_READ(sc, HW_ICOLL_CTRL) & HW_ICOLL_CTRL_CLKGATE));
358eba5cacbSjkunz
359eba5cacbSjkunz /* Bring block out of reset. */
360eba5cacbSjkunz ICOLL_WRITE(sc, HW_ICOLL_CTRL_CLR, HW_ICOLL_CTRL_SFTRST);
361eba5cacbSjkunz
362eba5cacbSjkunz loop = 0;
363eba5cacbSjkunz while ((ICOLL_READ(sc, HW_ICOLL_CTRL) & HW_ICOLL_CTRL_SFTRST) ||
364eba5cacbSjkunz (loop < ICOLL_SOFT_RST_LOOP)) {
365eba5cacbSjkunz loop++;
366eba5cacbSjkunz }
367eba5cacbSjkunz
368eba5cacbSjkunz ICOLL_WRITE(sc, HW_ICOLL_CTRL_CLR, HW_ICOLL_CTRL_CLKGATE);
369eba5cacbSjkunz
370eba5cacbSjkunz /* Wait until clock is in the NON-gated state. */
371eba5cacbSjkunz while (ICOLL_READ(sc, HW_ICOLL_CTRL) & HW_ICOLL_CTRL_CLKGATE);
372eba5cacbSjkunz
373eba5cacbSjkunz return;
374eba5cacbSjkunz }
375