1263936Sbr/*-
2263936Sbr * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
3263936Sbr * All rights reserved.
4263936Sbr *
5263936Sbr * Redistribution and use in source and binary forms, with or without
6263936Sbr * modification, are permitted provided that the following conditions
7263936Sbr * are met:
8263936Sbr * 1. Redistributions of source code must retain the above copyright
9263936Sbr *    notice, this list of conditions and the following disclaimer.
10263936Sbr * 2. Redistributions in binary form must reproduce the above copyright
11263936Sbr *    notice, this list of conditions and the following disclaimer in the
12263936Sbr *    documentation and/or other materials provided with the distribution.
13263936Sbr *
14263936Sbr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15263936Sbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16263936Sbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17263936Sbr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18263936Sbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19263936Sbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20263936Sbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21263936Sbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22263936Sbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23263936Sbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24263936Sbr * SUCH DAMAGE.
25263936Sbr */
26263936Sbr
27263936Sbr/*
28263936Sbr * Samsung Exynos 5 Interrupt Combiner
29263936Sbr * Chapter 7, Exynos 5 Dual User's Manual Public Rev 1.00
30263936Sbr */
31263936Sbr
32263936Sbr#include <sys/cdefs.h>
33263936Sbr__FBSDID("$FreeBSD: releng/10.2/sys/arm/samsung/exynos/exynos5_combiner.c 266341 2014-05-17 19:37:04Z ian $");
34263936Sbr
35263936Sbr#include <sys/param.h>
36263936Sbr#include <sys/systm.h>
37263936Sbr#include <sys/bus.h>
38263936Sbr#include <sys/kernel.h>
39263936Sbr#include <sys/module.h>
40263936Sbr#include <sys/malloc.h>
41263936Sbr#include <sys/rman.h>
42263936Sbr#include <sys/timeet.h>
43263936Sbr#include <sys/timetc.h>
44263936Sbr#include <sys/watchdog.h>
45263936Sbr
46263936Sbr#include <dev/fdt/fdt_common.h>
47263936Sbr#include <dev/ofw/openfirm.h>
48263936Sbr#include <dev/ofw/ofw_bus.h>
49263936Sbr#include <dev/ofw/ofw_bus_subr.h>
50263936Sbr
51263936Sbr#include <machine/bus.h>
52263936Sbr#include <machine/fdt.h>
53263936Sbr#include <machine/cpu.h>
54263936Sbr#include <machine/intr.h>
55263936Sbr
56263936Sbr#include <arm/samsung/exynos/exynos5_common.h>
57263936Sbr#include <arm/samsung/exynos/exynos5_combiner.h>
58263936Sbr
59263936Sbr#define NGRP		32
60263936Sbr#define ITABLE_LEN	24
61263936Sbr
62263936Sbr#define	IESR(n)	(0x10 * n + 0x0)	/* Interrupt enable set */
63263936Sbr#define	IECR(n)	(0x10 * n + 0x4)	/* Interrupt enable clear */
64263936Sbr#define	ISTR(n)	(0x10 * n + 0x8)	/* Interrupt status */
65263936Sbr#define	IMSR(n)	(0x10 * n + 0xC)	/* Interrupt masked status */
66263936Sbr#define	CIPSR	0x100			/* Combined interrupt pending */
67263936Sbr
68263936Sbrstruct combiner_softc {
69263936Sbr	struct resource		*res[1 + NGRP];
70263936Sbr	bus_space_tag_t		bst;
71263936Sbr	bus_space_handle_t	bsh;
72263936Sbr	void			*ih[NGRP];
73263936Sbr	device_t		dev;
74263936Sbr};
75263936Sbr
76263936Sbrstruct combiner_softc *combiner_sc;
77263936Sbr
78263936Sbrstatic struct resource_spec combiner_spec[] = {
79263936Sbr	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
80263936Sbr	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
81263936Sbr	{ SYS_RES_IRQ,		1,	RF_ACTIVE },
82263936Sbr	{ SYS_RES_IRQ,		2,	RF_ACTIVE },
83263936Sbr	{ SYS_RES_IRQ,		3,	RF_ACTIVE },
84263936Sbr	{ SYS_RES_IRQ,		4,	RF_ACTIVE },
85263936Sbr	{ SYS_RES_IRQ,		5,	RF_ACTIVE },
86263936Sbr	{ SYS_RES_IRQ,		6,	RF_ACTIVE },
87263936Sbr	{ SYS_RES_IRQ,		7,	RF_ACTIVE },
88263936Sbr	{ SYS_RES_IRQ,		8,	RF_ACTIVE },
89263936Sbr	{ SYS_RES_IRQ,		9,	RF_ACTIVE },
90263936Sbr	{ SYS_RES_IRQ,		10,	RF_ACTIVE },
91263936Sbr	{ SYS_RES_IRQ,		11,	RF_ACTIVE },
92263936Sbr	{ SYS_RES_IRQ,		12,	RF_ACTIVE },
93263936Sbr	{ SYS_RES_IRQ,		13,	RF_ACTIVE },
94263936Sbr	{ SYS_RES_IRQ,		14,	RF_ACTIVE },
95263936Sbr	{ SYS_RES_IRQ,		15,	RF_ACTIVE },
96263936Sbr	{ SYS_RES_IRQ,		16,	RF_ACTIVE },
97263936Sbr	{ SYS_RES_IRQ,		17,	RF_ACTIVE },
98263936Sbr	{ SYS_RES_IRQ,		18,	RF_ACTIVE },
99263936Sbr	{ SYS_RES_IRQ,		19,	RF_ACTIVE },
100263936Sbr	{ SYS_RES_IRQ,		20,	RF_ACTIVE },
101263936Sbr	{ SYS_RES_IRQ,		21,	RF_ACTIVE },
102263936Sbr	{ SYS_RES_IRQ,		22,	RF_ACTIVE },
103263936Sbr	{ SYS_RES_IRQ,		23,	RF_ACTIVE },
104263936Sbr	{ SYS_RES_IRQ,		24,	RF_ACTIVE },
105263936Sbr	{ SYS_RES_IRQ,		25,	RF_ACTIVE },
106263936Sbr	{ SYS_RES_IRQ,		26,	RF_ACTIVE },
107263936Sbr	{ SYS_RES_IRQ,		27,	RF_ACTIVE },
108263936Sbr	{ SYS_RES_IRQ,		28,	RF_ACTIVE },
109263936Sbr	{ SYS_RES_IRQ,		29,	RF_ACTIVE },
110263936Sbr	{ SYS_RES_IRQ,		30,	RF_ACTIVE },
111263936Sbr	{ SYS_RES_IRQ,		31,	RF_ACTIVE },
112263936Sbr	{ -1, 0 }
113263936Sbr};
114263936Sbr
115263936Sbrstruct combiner_entry {
116263936Sbr	int combiner_id;
117263936Sbr	int bit;
118263936Sbr	char *source_name;
119263936Sbr};
120263936Sbr
121263936Sbrstatic struct combiner_entry interrupt_table[ITABLE_LEN] = {
122263936Sbr	{ 63, 1, "EINT[15]" },
123263936Sbr	{ 63, 0, "EINT[14]" },
124263936Sbr	{ 62, 1, "EINT[13]" },
125263936Sbr	{ 62, 0, "EINT[12]" },
126263936Sbr	{ 61, 1, "EINT[11]" },
127263936Sbr	{ 61, 0, "EINT[10]" },
128263936Sbr	{ 60, 1, "EINT[9]" },
129263936Sbr	{ 60, 0, "EINT[8]" },
130263936Sbr	{ 59, 1, "EINT[7]" },
131263936Sbr	{ 59, 0, "EINT[6]" },
132263936Sbr	{ 58, 1, "EINT[5]" },
133263936Sbr	{ 58, 0, "EINT[4]" },
134263936Sbr	{ 57, 3, "MCT_G3" },
135263936Sbr	{ 57, 2, "MCT_G2" },
136263936Sbr	{ 57, 1, "EINT[3]" },
137263936Sbr	{ 57, 0, "EINT[2]" },
138263936Sbr	{ 56, 6, "SYSMMU_G2D[1]" },
139263936Sbr	{ 56, 5, "SYSMMU_G2D[0]" },
140263936Sbr	{ 56, 2, "SYSMMU_FIMC_LITE1[1]" },
141263936Sbr	{ 56, 1, "SYSMMU_FIMC_LITE1[0]" },
142263936Sbr	{ 56, 0, "EINT[1]" },
143263936Sbr	{ 55, 4, "MCT_G1" },
144263936Sbr	{ 55, 3, "MCT_G0" },
145263936Sbr	{ 55, 0, "EINT[0]" },
146263936Sbr
147263936Sbr	/* TODO: add groups 54-32 */
148263936Sbr};
149263936Sbr
150263936Sbrstruct combined_intr {
151263936Sbr	uint32_t	enabled;
152263936Sbr	void		(*ih) (void *);
153263936Sbr	void		*ih_user;
154263936Sbr};
155263936Sbr
156263936Sbrstatic struct combined_intr intr_map[32][8];
157263936Sbr
158263936Sbrstatic void
159263936Sbrcombiner_intr(void *arg)
160263936Sbr{
161263936Sbr	struct combiner_softc *sc;
162263936Sbr	void (*ih) (void *);
163263936Sbr	void *ih_user;
164263936Sbr	int enabled;
165263936Sbr	int intrs;
166263936Sbr	int shift;
167263936Sbr	int cirq;
168263936Sbr	int grp;
169263936Sbr	int i,n;
170263936Sbr
171263936Sbr	sc = arg;
172263936Sbr
173263936Sbr	intrs = READ4(sc, CIPSR);
174263936Sbr	for (grp = 0; grp < 32; grp++) {
175263936Sbr		if (intrs & (1 << grp)) {
176263936Sbr			n = (grp / 4);
177263936Sbr			shift = (grp % 4) * 8;
178263936Sbr
179263936Sbr			cirq = READ4(sc, ISTR(n));
180263936Sbr			for (i = 0; i < 8; i++) {
181263936Sbr				if (cirq & (1 << (i + shift))) {
182263936Sbr					ih = intr_map[grp][i].ih;
183263936Sbr					ih_user = intr_map[grp][i].ih_user;
184263936Sbr					enabled = intr_map[grp][i].enabled;
185263936Sbr					if (enabled && (ih != NULL)) {
186263936Sbr						ih(ih_user);
187263936Sbr					}
188263936Sbr				}
189263936Sbr			}
190263936Sbr		}
191263936Sbr	}
192263936Sbr}
193263936Sbr
194263936Sbrvoid
195263936Sbrcombiner_setup_intr(char *source_name, void (*ih)(void *), void *ih_user)
196263936Sbr{
197263936Sbr	struct combiner_entry *entry;
198263936Sbr	struct combined_intr *cirq;
199263936Sbr	struct combiner_softc *sc;
200263936Sbr	int shift;
201263936Sbr	int reg;
202263936Sbr	int grp;
203263936Sbr	int n;
204263936Sbr	int i;
205263936Sbr
206263936Sbr	sc = combiner_sc;
207263936Sbr
208263936Sbr	if (sc == NULL) {
209263936Sbr		device_printf(sc->dev, "Error: combiner is not attached\n");
210263936Sbr		return;
211263936Sbr	}
212263936Sbr
213263936Sbr	entry = NULL;
214263936Sbr
215263936Sbr	for (i = 0; i < ITABLE_LEN; i++) {
216263936Sbr		if (strcmp(interrupt_table[i].source_name, source_name) == 0) {
217263936Sbr			entry = &interrupt_table[i];
218263936Sbr		}
219263936Sbr	}
220263936Sbr
221263936Sbr	if (entry == NULL) {
222263936Sbr		device_printf(sc->dev, "Can't find interrupt name %s\n",
223263936Sbr		    source_name);
224263936Sbr		return;
225263936Sbr	}
226263936Sbr
227263936Sbr#if 0
228263936Sbr	device_printf(sc->dev, "Setting up interrupt %s\n", source_name);
229263936Sbr#endif
230263936Sbr
231263936Sbr	grp = entry->combiner_id - 32;
232263936Sbr
233263936Sbr	cirq = &intr_map[grp][entry->bit];
234263936Sbr	cirq->enabled = 1;
235263936Sbr	cirq->ih = ih;
236263936Sbr	cirq->ih_user = ih_user;
237263936Sbr
238263936Sbr	n = grp / 4;
239263936Sbr	shift = (grp % 4) * 8 + entry->bit;
240263936Sbr
241263936Sbr	reg = (1 << shift);
242263936Sbr	WRITE4(sc, IESR(n), reg);
243263936Sbr}
244263936Sbr
245263936Sbrstatic int
246263936Sbrcombiner_probe(device_t dev)
247263936Sbr{
248263936Sbr
249263936Sbr	if (!ofw_bus_is_compatible(dev, "exynos,combiner"))
250263936Sbr		return (ENXIO);
251263936Sbr
252263936Sbr	device_set_desc(dev, "Samsung Exynos 5 Interrupt Combiner");
253263936Sbr	return (BUS_PROBE_DEFAULT);
254263936Sbr}
255263936Sbr
256263936Sbrstatic int
257263936Sbrcombiner_attach(device_t dev)
258263936Sbr{
259263936Sbr	struct combiner_softc *sc;
260263936Sbr	int err;
261263936Sbr	int i;
262263936Sbr
263263936Sbr	sc = device_get_softc(dev);
264263936Sbr	sc->dev = dev;
265263936Sbr
266263936Sbr	if (bus_alloc_resources(dev, combiner_spec, sc->res)) {
267263936Sbr		device_printf(dev, "could not allocate resources\n");
268263936Sbr		return (ENXIO);
269263936Sbr	}
270263936Sbr
271263936Sbr	/* Memory interface */
272263936Sbr	sc->bst = rman_get_bustag(sc->res[0]);
273263936Sbr	sc->bsh = rman_get_bushandle(sc->res[0]);
274263936Sbr
275263936Sbr	combiner_sc = sc;
276263936Sbr
277263936Sbr        /* Setup interrupt handler */
278263936Sbr	for (i = 0; i < NGRP; i++) {
279263936Sbr		err = bus_setup_intr(dev, sc->res[1+i], INTR_TYPE_BIO | \
280263936Sbr		    INTR_MPSAFE, NULL, combiner_intr, sc, &sc->ih[i]);
281263936Sbr		if (err) {
282263936Sbr			device_printf(dev, "Unable to alloc int resource.\n");
283263936Sbr			return (ENXIO);
284263936Sbr		}
285263936Sbr	}
286263936Sbr
287263936Sbr	return (0);
288263936Sbr}
289263936Sbr
290263936Sbrstatic device_method_t combiner_methods[] = {
291263936Sbr	DEVMETHOD(device_probe,		combiner_probe),
292263936Sbr	DEVMETHOD(device_attach,	combiner_attach),
293263936Sbr	{ 0, 0 }
294263936Sbr};
295263936Sbr
296263936Sbrstatic driver_t combiner_driver = {
297263936Sbr	"combiner",
298263936Sbr	combiner_methods,
299263936Sbr	sizeof(struct combiner_softc),
300263936Sbr};
301263936Sbr
302263936Sbrstatic devclass_t combiner_devclass;
303263936Sbr
304263936SbrDRIVER_MODULE(combiner, simplebus, combiner_driver, combiner_devclass, 0, 0);
305