1183840Sraj/*-
2183840Sraj * Copyright (c) 2006 Benno Rice.
3183840Sraj * Copyright (C) 2007-2008 MARVELL INTERNATIONAL LTD.
4183840Sraj * All rights reserved.
5183840Sraj *
6183840Sraj * Adapted and extended to Marvell SoCs by Semihalf.
7183840Sraj *
8183840Sraj * Redistribution and use in source and binary forms, with or without
9183840Sraj * modification, are permitted provided that the following conditions
10183840Sraj * are met:
11183840Sraj * 1. Redistributions of source code must retain the above copyright
12183840Sraj *    notice, this list of conditions and the following disclaimer.
13183840Sraj * 2. Redistributions in binary form must reproduce the above copyright
14183840Sraj *    notice, this list of conditions and the following disclaimer in the
15183840Sraj *    documentation and/or other materials provided with the distribution.
16183840Sraj *
17183840Sraj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18183840Sraj * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19183840Sraj * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20183840Sraj * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21183840Sraj * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22183840Sraj * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23183840Sraj * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24183840Sraj * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25183840Sraj * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26183840Sraj * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27183840Sraj *
28183840Sraj * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0_icu.c, rev 1
29183840Sraj */
30183840Sraj
31183840Sraj#include <sys/cdefs.h>
32183840Sraj__FBSDID("$FreeBSD$");
33183840Sraj
34183840Sraj#include <sys/param.h>
35183840Sraj#include <sys/systm.h>
36183840Sraj#include <sys/bus.h>
37183840Sraj#include <sys/kernel.h>
38218427Smarcel#include <sys/ktr.h>
39183840Sraj#include <sys/module.h>
40183840Sraj#include <sys/rman.h>
41183840Sraj#include <machine/bus.h>
42183840Sraj#include <machine/intr.h>
43183840Sraj
44209131Sraj#include <dev/ofw/ofw_bus.h>
45209131Sraj#include <dev/ofw/ofw_bus_subr.h>
46209131Sraj
47183840Sraj#include <arm/mv/mvreg.h>
48183840Sraj#include <arm/mv/mvvar.h>
49183840Sraj
50183840Srajstruct mv_ic_softc {
51183840Sraj	struct resource	*	ic_res[1];
52183840Sraj	bus_space_tag_t		ic_bst;
53183840Sraj	bus_space_handle_t	ic_bsh;
54183840Sraj	int			ic_high_regs;
55183840Sraj	int			ic_error_regs;
56183840Sraj};
57183840Sraj
58183840Srajstatic struct resource_spec mv_ic_spec[] = {
59183840Sraj	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
60183840Sraj	{ -1, 0 }
61183840Sraj};
62183840Sraj
63183840Srajstatic struct mv_ic_softc *mv_ic_sc = NULL;
64183840Sraj
65183840Srajstatic int	mv_ic_probe(device_t);
66183840Srajstatic int	mv_ic_attach(device_t);
67183840Sraj
68183840Srajuint32_t	mv_ic_get_cause(void);
69183840Srajuint32_t	mv_ic_get_mask(void);
70183840Srajvoid		mv_ic_set_mask(uint32_t);
71183840Srajuint32_t	mv_ic_get_cause_hi(void);
72183840Srajuint32_t	mv_ic_get_mask_hi(void);
73183840Srajvoid		mv_ic_set_mask_hi(uint32_t);
74183840Srajuint32_t	mv_ic_get_cause_error(void);
75183840Srajuint32_t	mv_ic_get_mask_error(void);
76183840Srajvoid		mv_ic_set_mask_error(uint32_t);
77183840Srajstatic void	arm_mask_irq_all(void);
78183840Sraj
79183840Srajstatic int
80183840Srajmv_ic_probe(device_t dev)
81183840Sraj{
82183840Sraj
83261410Sian	if (!ofw_bus_status_okay(dev))
84261410Sian		return (ENXIO);
85261410Sian
86209131Sraj	if (!ofw_bus_is_compatible(dev, "mrvl,pic"))
87209131Sraj		return (ENXIO);
88209131Sraj
89183840Sraj	device_set_desc(dev, "Marvell Integrated Interrupt Controller");
90183840Sraj	return (0);
91183840Sraj}
92183840Sraj
93183840Srajstatic int
94183840Srajmv_ic_attach(device_t dev)
95183840Sraj{
96183840Sraj	struct mv_ic_softc *sc;
97183840Sraj	uint32_t dev_id, rev_id;
98183840Sraj	int error;
99183840Sraj
100183840Sraj	sc = (struct mv_ic_softc *)device_get_softc(dev);
101183840Sraj
102183840Sraj	if (mv_ic_sc != NULL)
103183840Sraj		return (ENXIO);
104183840Sraj	mv_ic_sc = sc;
105183840Sraj
106183840Sraj	soc_id(&dev_id, &rev_id);
107183840Sraj
108183840Sraj	sc->ic_high_regs = 0;
109183840Sraj	sc->ic_error_regs = 0;
110183840Sraj
111238873Shrs	if (dev_id == MV_DEV_88F6281 ||
112238873Shrs	    dev_id == MV_DEV_88F6282 ||
113238873Shrs	    dev_id == MV_DEV_MV78100 ||
114191140Sraj	    dev_id == MV_DEV_MV78100_Z0)
115183840Sraj		sc->ic_high_regs = 1;
116183840Sraj
117191140Sraj	if (dev_id == MV_DEV_MV78100 || dev_id == MV_DEV_MV78100_Z0)
118183840Sraj		sc->ic_error_regs = 1;
119183840Sraj
120183840Sraj	error = bus_alloc_resources(dev, mv_ic_spec, sc->ic_res);
121183840Sraj	if (error) {
122183840Sraj		device_printf(dev, "could not allocate resources\n");
123183840Sraj		return (ENXIO);
124183840Sraj	}
125183840Sraj
126183840Sraj	sc->ic_bst = rman_get_bustag(sc->ic_res[0]);
127183840Sraj	sc->ic_bsh = rman_get_bushandle(sc->ic_res[0]);
128183840Sraj
129183840Sraj	/* Mask all interrupts */
130183840Sraj	arm_mask_irq_all();
131183840Sraj
132183840Sraj	return (0);
133183840Sraj}
134183840Sraj
135183840Srajstatic device_method_t mv_ic_methods[] = {
136183840Sraj	DEVMETHOD(device_probe,		mv_ic_probe),
137183840Sraj	DEVMETHOD(device_attach,	mv_ic_attach),
138183840Sraj	{ 0, 0 }
139183840Sraj};
140183840Sraj
141183840Srajstatic driver_t mv_ic_driver = {
142183840Sraj	"ic",
143183840Sraj	mv_ic_methods,
144183840Sraj	sizeof(struct mv_ic_softc),
145183840Sraj};
146183840Sraj
147183840Srajstatic devclass_t mv_ic_devclass;
148183840Sraj
149209131SrajDRIVER_MODULE(ic, simplebus, mv_ic_driver, mv_ic_devclass, 0, 0);
150183840Sraj
151183840Srajint
152218427Smarcelarm_get_next_irq(int last)
153183840Sraj{
154218427Smarcel	u_int filt, irq;
155218427Smarcel	int next;
156183840Sraj
157218427Smarcel	filt = ~((last >= 0) ? (2 << last) - 1 : 0);
158183840Sraj	irq = mv_ic_get_cause() & mv_ic_get_mask();
159218427Smarcel	if (irq & filt) {
160218427Smarcel		next = ffs(irq & filt) - 1;
161218427Smarcel		goto out;
162218427Smarcel	}
163183840Sraj	if (mv_ic_sc->ic_high_regs) {
164218427Smarcel		filt = ~((last >= 32) ? (2 << (last - 32)) - 1 : 0);
165183840Sraj		irq = mv_ic_get_cause_hi() & mv_ic_get_mask_hi();
166218427Smarcel		if (irq & filt) {
167218427Smarcel			next = ffs(irq & filt) + 31;
168218427Smarcel			goto out;
169218427Smarcel		}
170183840Sraj	}
171183840Sraj	if (mv_ic_sc->ic_error_regs) {
172218427Smarcel		filt = ~((last >= 64) ? (2 << (last - 64)) - 1 : 0);
173183840Sraj		irq = mv_ic_get_cause_error() & mv_ic_get_mask_error();
174218427Smarcel		if (irq & filt) {
175218427Smarcel			next = ffs(irq & filt) + 63;
176218427Smarcel			goto out;
177218427Smarcel		}
178183840Sraj	}
179218427Smarcel	next = -1;
180183840Sraj
181218427Smarcel out:
182218427Smarcel	CTR3(KTR_INTR, "%s: last=%d, next=%d", __func__, last, next);
183218427Smarcel	return (next);
184183840Sraj}
185183840Sraj
186183840Srajstatic void
187183840Srajarm_mask_irq_all(void)
188183840Sraj{
189183840Sraj
190183840Sraj	mv_ic_set_mask(0);
191183840Sraj
192183840Sraj	if (mv_ic_sc->ic_high_regs)
193183840Sraj		mv_ic_set_mask_hi(0);
194183840Sraj
195183840Sraj	if (mv_ic_sc->ic_error_regs)
196183840Sraj		mv_ic_set_mask_error(0);
197183840Sraj}
198183840Sraj
199183840Srajvoid
200183840Srajarm_mask_irq(uintptr_t nb)
201183840Sraj{
202183840Sraj	uint32_t	mr;
203183840Sraj
204183840Sraj	if (nb < 32) {
205183840Sraj		mr = mv_ic_get_mask();
206183840Sraj		mr &= ~(1 << nb);
207183840Sraj		mv_ic_set_mask(mr);
208183840Sraj
209183840Sraj	} else if ((nb < 64) && mv_ic_sc->ic_high_regs) {
210183840Sraj		mr = mv_ic_get_mask_hi();
211183840Sraj		mr &= ~(1 << (nb - 32));
212183840Sraj		mv_ic_set_mask_hi(mr);
213183840Sraj
214183840Sraj	} else if ((nb < 96) && mv_ic_sc->ic_error_regs) {
215183840Sraj		mr = mv_ic_get_mask_error();
216183840Sraj		mr &= ~(1 << (nb - 64));
217183840Sraj		mv_ic_set_mask_error(mr);
218183840Sraj	}
219183840Sraj}
220183840Sraj
221183840Srajvoid
222183840Srajarm_unmask_irq(uintptr_t nb)
223183840Sraj{
224183840Sraj	uint32_t	mr;
225183840Sraj
226183840Sraj	if (nb < 32) {
227183840Sraj		mr = mv_ic_get_mask();
228183840Sraj		mr |= (1 << nb);
229183840Sraj		mv_ic_set_mask(mr);
230183840Sraj
231183840Sraj	} else if ((nb < 64) && mv_ic_sc->ic_high_regs) {
232183840Sraj		mr = mv_ic_get_mask_hi();
233183840Sraj		mr |= (1 << (nb - 32));
234183840Sraj		mv_ic_set_mask_hi(mr);
235183840Sraj
236183840Sraj	} else if ((nb < 96) && mv_ic_sc->ic_error_regs) {
237183840Sraj		mr = mv_ic_get_mask_error();
238183840Sraj		mr |= (1 << (nb - 64));
239183840Sraj		mv_ic_set_mask_error(mr);
240183840Sraj	}
241183840Sraj}
242183840Sraj
243183840Srajvoid
244183840Srajmv_ic_set_mask(uint32_t val)
245183840Sraj{
246183840Sraj
247183840Sraj	bus_space_write_4(mv_ic_sc->ic_bst, mv_ic_sc->ic_bsh,
248183840Sraj	    IRQ_MASK, val);
249183840Sraj}
250183840Sraj
251183840Srajuint32_t
252183840Srajmv_ic_get_mask(void)
253183840Sraj{
254183840Sraj
255183840Sraj	return (bus_space_read_4(mv_ic_sc->ic_bst,
256183840Sraj	    mv_ic_sc->ic_bsh, IRQ_MASK));
257183840Sraj}
258183840Sraj
259183840Srajuint32_t
260183840Srajmv_ic_get_cause(void)
261183840Sraj{
262183840Sraj
263183840Sraj	return (bus_space_read_4(mv_ic_sc->ic_bst,
264183840Sraj	    mv_ic_sc->ic_bsh, IRQ_CAUSE));
265183840Sraj}
266183840Sraj
267183840Srajvoid
268183840Srajmv_ic_set_mask_hi(uint32_t val)
269183840Sraj{
270183840Sraj
271183840Sraj	bus_space_write_4(mv_ic_sc->ic_bst, mv_ic_sc->ic_bsh,
272183840Sraj	    IRQ_MASK_HI, val);
273183840Sraj}
274183840Sraj
275183840Srajuint32_t
276183840Srajmv_ic_get_mask_hi(void)
277183840Sraj{
278183840Sraj
279183840Sraj	return (bus_space_read_4(mv_ic_sc->ic_bst,
280183840Sraj	    mv_ic_sc->ic_bsh, IRQ_MASK_HI));
281183840Sraj}
282183840Sraj
283183840Srajuint32_t
284183840Srajmv_ic_get_cause_hi(void)
285183840Sraj{
286183840Sraj
287183840Sraj	return (bus_space_read_4(mv_ic_sc->ic_bst,
288183840Sraj	    mv_ic_sc->ic_bsh, IRQ_CAUSE_HI));
289183840Sraj}
290183840Sraj
291183840Srajvoid
292183840Srajmv_ic_set_mask_error(uint32_t val)
293183840Sraj{
294183840Sraj
295183840Sraj	bus_space_write_4(mv_ic_sc->ic_bst, mv_ic_sc->ic_bsh,
296183840Sraj	    IRQ_MASK_ERROR, val);
297183840Sraj}
298183840Sraj
299183840Srajuint32_t
300183840Srajmv_ic_get_mask_error(void)
301183840Sraj{
302183840Sraj
303183840Sraj	return (bus_space_read_4(mv_ic_sc->ic_bst,
304183840Sraj	    mv_ic_sc->ic_bsh, IRQ_MASK_ERROR));
305183840Sraj}
306183840Sraj
307183840Srajuint32_t
308183840Srajmv_ic_get_cause_error(void)
309183840Sraj{
310183840Sraj
311183840Sraj	return (bus_space_read_4(mv_ic_sc->ic_bst,
312183840Sraj	    mv_ic_sc->ic_bsh, IRQ_CAUSE_ERROR));
313183840Sraj}
314