1/*	$NetBSD: plumicu.c,v 1.10 2005/12/24 23:24:00 perry Exp $ */
2
3/*-
4 * Copyright (c) 1999, 2000 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by UCHIYAMA Yasushi.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: plumicu.c,v 1.10 2005/12/24 23:24:00 perry Exp $");
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/device.h>
38
39#include <machine/bus.h>
40#include <machine/intr.h>
41
42#include <hpcmips/tx/tx39var.h>
43#include <hpcmips/dev/plumvar.h>
44#include <hpcmips/dev/plumicuvar.h>
45#include <hpcmips/dev/plumicureg.h>
46
47#ifdef PLUMICUDEBUG
48#define	DPRINTF(arg) printf arg
49#else
50#define	DPRINTF(arg)
51#endif
52
53int plumicu_match(struct device *, struct cfdata *, void *);
54void plumicu_attach(struct device *, struct device *, void *);
55int plumicu_intr(void *);
56
57static inline void plum_di(plum_chipset_tag_t);
58static inline void plum_ei(plum_chipset_tag_t);
59
60const struct plum_intr_ctrl {
61	plumreg_t	ic_ackpat1;
62	plumreg_t	ic_ackpat2;	int	ic_ackreg2;
63	plumreg_t	ic_ienpat;	int	ic_ienreg;
64	plumreg_t	ic_senpat;	int	ic_senreg;
65} pi_ctrl[PLUM_INTR_MAX] = {
66	[PLUM_INT_C1IO]	= {PLUM_INT_INTSTA_PCCINT,
67			   PLUM_INT_PCCINTS_C1IO,	PLUM_INT_PCCINTS_REG,
68			   PLUM_INT_PCCIEN_IENC1IO,	PLUM_INT_PCCIEN_REG,
69			   PLUM_INT_PCCIEN_SENC1IO,	PLUM_INT_PCCIEN_REG
70	},
71	[PLUM_INT_C1RI] = {PLUM_INT_INTSTA_PCCINT,
72			   PLUM_INT_PCCINTS_C1RI,	PLUM_INT_PCCINTS_REG,
73			   PLUM_INT_PCCIEN_IENC1RI,	PLUM_INT_PCCIEN_REG,
74			   PLUM_INT_PCCIEN_SENC1RI,	PLUM_INT_PCCIEN_REG
75	},
76	[PLUM_INT_C1SC] = {PLUM_INT_INTSTA_C1SCINT,	0, 0, 0, 0, 0, 0},
77	[PLUM_INT_C2IO] = {PLUM_INT_INTSTA_PCCINT,
78			   PLUM_INT_PCCINTS_C2IO,	PLUM_INT_PCCINTS_REG,
79			   PLUM_INT_PCCIEN_IENC2IO,	PLUM_INT_PCCIEN_REG,
80			   PLUM_INT_PCCIEN_SENC2IO,	PLUM_INT_PCCIEN_REG
81	},
82	[PLUM_INT_C2RI] = {PLUM_INT_INTSTA_PCCINT,
83			   PLUM_INT_PCCINTS_C2RI,	PLUM_INT_PCCINTS_REG,
84			   PLUM_INT_PCCIEN_IENC2RI,	PLUM_INT_PCCIEN_REG,
85			   PLUM_INT_PCCIEN_SENC2RI,	PLUM_INT_PCCIEN_REG
86	},
87	[PLUM_INT_C2SC] = {PLUM_INT_INTSTA_C2SCINT,	0, 0, 0, 0, 0, 0},
88	[PLUM_INT_DISP] = {PLUM_INT_INTSTA_DISPINT,	0, 0, 0, 0, 0, 0},
89	[PLUM_INT_USB]  = {PLUM_INT_INTSTA_USBINT,
90			   0, 0,
91			   PLUM_INT_USBINTEN_IEN,	PLUM_INT_USBINTEN_REG,
92			   0, 0
93	},
94	[PLUM_INT_USBWAKE] = {PLUM_INT_INTSTA_USBWAKE,
95			   0, 0,
96			   PLUM_INT_USBINTEN_WIEN,	PLUM_INT_USBINTEN_REG,
97			   0, 0
98	},
99	[PLUM_INT_SM]	= {PLUM_INT_INTSTA_SMINT,
100			   0, 0,
101			   PLUM_INT_SMIEN,		PLUM_INT_SMIEN_REG,
102			   0, 0
103	},
104	[PLUM_INT_EXT5IO0] = {PLUM_INT_INTSTA_EXTINT,
105			   PLUM_INT_EXTINTS_IO5INT0,	PLUM_INT_EXTINTS_REG,
106			   PLUM_INT_EXTIEN_IENIO5INT0,	PLUM_INT_EXTIEN_REG,
107			   PLUM_INT_EXTIEN_SENIO5INT0,	PLUM_INT_EXTIEN_REG,
108	},
109	[PLUM_INT_EXT5IO1] = {PLUM_INT_INTSTA_EXTINT,
110			   PLUM_INT_EXTINTS_IO5INT1,	PLUM_INT_EXTINTS_REG,
111			   PLUM_INT_EXTIEN_IENIO5INT1,	PLUM_INT_EXTIEN_REG,
112			   PLUM_INT_EXTIEN_SENIO5INT1,	PLUM_INT_EXTIEN_REG,
113	},
114	[PLUM_INT_EXT5IO2] = {PLUM_INT_INTSTA_EXTINT,
115			   PLUM_INT_EXTINTS_IO5INT2,	PLUM_INT_EXTINTS_REG,
116			   PLUM_INT_EXTIEN_IENIO5INT2,	PLUM_INT_EXTIEN_REG,
117			   PLUM_INT_EXTIEN_SENIO5INT2,	PLUM_INT_EXTIEN_REG,
118	},
119	[PLUM_INT_EXT5IO3] = {PLUM_INT_INTSTA_EXTINT,
120			   PLUM_INT_EXTINTS_IO5INT3,	PLUM_INT_EXTINTS_REG,
121			   PLUM_INT_EXTIEN_IENIO5INT0,	PLUM_INT_EXTIEN_REG,
122			   PLUM_INT_EXTIEN_SENIO5INT0,	PLUM_INT_EXTIEN_REG,
123	},
124	[PLUM_INT_EXT3IO0] = {PLUM_INT_INTSTA_EXTINT,
125			   PLUM_INT_EXTINTS_IO3INT0,	PLUM_INT_EXTINTS_REG,
126			   PLUM_INT_EXTIEN_IENIO3INT0,	PLUM_INT_EXTIEN_REG,
127			   PLUM_INT_EXTIEN_SENIO3INT0,	PLUM_INT_EXTIEN_REG,
128	},
129	[PLUM_INT_EXT3IO1] = {PLUM_INT_INTSTA_EXTINT,
130			   PLUM_INT_EXTINTS_IO3INT1,	PLUM_INT_EXTINTS_REG,
131			   PLUM_INT_EXTIEN_IENIO3INT1,	PLUM_INT_EXTIEN_REG,
132			   PLUM_INT_EXTIEN_SENIO3INT1,	PLUM_INT_EXTIEN_REG,
133	}
134};
135
136struct plum_intr_entry {
137	int pi_enabled;
138	int pi_line;
139	int (*pi_fun)(void *);
140	void *pi_arg;
141	const struct plum_intr_ctrl *pi_ctrl;
142};
143
144struct plumicu_softc {
145	struct	device		sc_dev;
146	plum_chipset_tag_t	sc_pc;
147	bus_space_tag_t		sc_regt;
148	bus_space_handle_t	sc_regh;
149	void			*sc_ih;
150	int			sc_enable_count;
151	struct plum_intr_entry  sc_intr[PLUM_INTR_MAX];
152};
153
154CFATTACH_DECL(plumicu, sizeof(struct plumicu_softc),
155    plumicu_match, plumicu_attach, NULL, NULL);
156
157#ifdef PLUMICUDEBUG
158void plumicu_dump(struct plumicu_softc *);
159#endif
160
161int
162plumicu_match(struct device *parent, struct cfdata *cf, void *aux)
163{
164
165	return (2); /* 1st attach group */
166}
167
168void
169plumicu_attach(struct device *parent, struct device *self, void *aux)
170{
171	struct plum_attach_args *pa = aux;
172	struct plumicu_softc *sc = (void*)self;
173	const struct plum_intr_ctrl *pic;
174	bus_space_tag_t regt;
175	bus_space_handle_t regh;
176	plumreg_t reg;
177	int i;
178
179	sc->sc_pc	= pa->pa_pc;
180	sc->sc_regt	= pa->pa_regt;
181
182	/* map plum2 interrupt controller register space */
183	if (bus_space_map(sc->sc_regt, PLUM_INT_REGBASE,
184			  PLUM_INT_REGSIZE, 0, &sc->sc_regh)) {
185		printf(":interrupt register map failed\n");
186		return;
187	}
188#ifdef PLUMICUDEBUG
189	plumicu_dump(sc);
190#endif
191	/* disable all interrupt */
192	regt = sc->sc_regt;
193	regh = sc->sc_regh;
194	for (i = 0; i < PLUM_INTR_MAX; i++) {
195		pic = &pi_ctrl[i];
196		if (pic->ic_ienreg) {
197			reg = plum_conf_read(regt, regh, pic->ic_ienreg);
198			reg &= ~pic->ic_ienpat;
199			plum_conf_write(regt, regh, pic->ic_ienreg, reg);
200		}
201		if (pic->ic_senreg) {
202			reg = plum_conf_read(regt, regh, pic->ic_senreg);
203			reg &= ~pic->ic_senpat;
204			plum_conf_write(regt, regh, pic->ic_senreg, reg);
205		}
206	}
207
208	/* register handle to plum_chipset_tag */
209	plum_conf_register_intr(sc->sc_pc, (void*)sc);
210
211	/* disable interrupt redirect to TX39 core */
212	plum_di(sc->sc_pc);
213
214	if (!(sc->sc_ih = tx_intr_establish(sc->sc_pc->pc_tc, pa->pa_irq,
215					    IST_EDGE, IPL_BIO,
216					    plumicu_intr, sc))) {
217		printf(": can't establish interrupt\n");
218	}
219	printf("\n");
220}
221
222inline void
223plum_di(plum_chipset_tag_t pc)
224{
225	struct plumicu_softc *sc = pc->pc_intrt;
226
227	plum_conf_write(sc->sc_regt, sc->sc_regh, PLUM_INT_INTIEN_REG, 0);
228}
229
230inline void
231plum_ei(plum_chipset_tag_t pc)
232{
233	struct plumicu_softc *sc = pc->pc_intrt;
234
235	plum_conf_write(sc->sc_regt, sc->sc_regh, PLUM_INT_INTIEN_REG,
236			PLUM_INT_INTIEN);
237}
238
239void*
240plum_intr_establish(plum_chipset_tag_t pc, int line, int mode, int level,
241    int (*ih_fun)(void *), void *ih_arg)
242{
243	struct plumicu_softc *sc = pc->pc_intrt;
244	bus_space_tag_t regt = sc->sc_regt;
245	bus_space_handle_t regh = sc->sc_regh;
246	plumreg_t reg;
247	struct plum_intr_entry *pi;
248
249	if (!LEGAL_PRUM_INTR(line)) {
250		panic("plum_intr_establish: bogus interrupt line");
251	}
252
253	pi = &sc->sc_intr[line];
254	pi->pi_line = line;
255	pi->pi_fun  = ih_fun;
256	pi->pi_arg  = ih_arg;
257	pi->pi_ctrl = &pi_ctrl[line];
258
259	/* Enable interrupt */
260
261	/* status enable */
262	if (pi->pi_ctrl->ic_senreg) {
263		reg = plum_conf_read(regt, regh, pi->pi_ctrl->ic_senreg);
264		reg |= pi->pi_ctrl->ic_senpat;
265		plum_conf_write(regt, regh, pi->pi_ctrl->ic_senreg, reg);
266	}
267	/* interrupt enable */
268	if (pi->pi_ctrl->ic_ienreg) {
269		reg = plum_conf_read(regt, regh, pi->pi_ctrl->ic_ienreg);
270		reg |= pi->pi_ctrl->ic_ienpat;
271		plum_conf_write(regt, regh, pi->pi_ctrl->ic_ienreg, reg);
272	}
273
274	/* Enable redirect to TX39 core */
275	DPRINTF(("plum_intr_establish: %d (count=%d)\n", line,
276		 sc->sc_enable_count));
277
278	if (sc->sc_enable_count++ == 0)
279		plum_ei(pc);
280
281	pi->pi_enabled = 1;
282
283	return (ih_fun);
284}
285
286void
287plum_intr_disestablish(plum_chipset_tag_t pc, void *arg)
288{
289	struct plumicu_softc *sc = pc->pc_intrt;
290	bus_space_tag_t regt = sc->sc_regt;
291	bus_space_handle_t regh = sc->sc_regh;
292	plumreg_t reg;
293	struct plum_intr_entry *pi;
294	int i;
295
296	sc = pc->pc_intrt;
297
298	for (i = 0; i < PLUM_INTR_MAX; i++) {
299		pi = &sc->sc_intr[i];
300		if (pi->pi_fun != arg)
301			continue;
302		DPRINTF(("plum_intr_disestablish: %d (count=%d)\n",
303			 pi->pi_line, sc->sc_enable_count - 1));
304		goto found;
305	}
306	panic("plum_intr_disestablish: can't find entry.");
307	/* NOTREACHED */
308 found:
309	pi->pi_enabled = 0;
310	/* Disable interrupt */
311	if (pi->pi_ctrl->ic_ienreg) {
312		reg = plum_conf_read(regt, regh, pi->pi_ctrl->ic_ienreg);
313		reg &= ~(pi->pi_ctrl->ic_ienpat);
314		plum_conf_write(regt, regh, pi->pi_ctrl->ic_ienreg, reg);
315	}
316	if (pi->pi_ctrl->ic_senreg) {
317		reg = plum_conf_read(regt, regh, pi->pi_ctrl->ic_senreg);
318		reg &= ~(pi->pi_ctrl->ic_senpat);
319		plum_conf_write(regt, regh, pi->pi_ctrl->ic_senreg, reg);
320	}
321
322	/* Disable/Enable interrupt redirect to TX39 core */
323	if (--sc->sc_enable_count == 0)
324		plum_di(pc);
325}
326
327int
328plumicu_intr(void *arg)
329{
330	struct plumicu_softc *sc = arg;
331	bus_space_tag_t regt = sc->sc_regt;
332	bus_space_handle_t regh = sc->sc_regh;
333	plumreg_t reg1, reg2, reg_ext, reg_pccard;
334	int i;
335
336	plum_di(sc->sc_pc);
337	/* read level 1 status */
338	reg1 = plum_conf_read(regt, regh, PLUM_INT_INTSTA_REG);
339
340	/* read level 2 status and acknowledge */
341	reg_ext = plum_conf_read(regt, regh, PLUM_INT_EXTINTS_REG);
342	plum_conf_write(regt, regh, PLUM_INT_EXTINTS_REG, reg_ext);
343
344	reg_pccard = plum_conf_read(regt, regh, PLUM_INT_PCCINTS_REG);
345	plum_conf_write(regt, regh, PLUM_INT_PCCINTS_REG, reg_pccard);
346
347	for (i = 0; i < PLUM_INTR_MAX; i++) {
348		register struct plum_intr_entry *pi;
349		register const struct plum_intr_ctrl *pic = &pi_ctrl[i];
350
351		if (!(pic->ic_ackpat1 & reg1))
352			continue;
353
354		pi = &sc->sc_intr[i];
355		if (!pi->pi_enabled)
356			continue;
357
358		if (pic->ic_ackreg2 == 0) {
359			(*pi->pi_fun)(pi->pi_arg);
360			continue;
361		}
362
363		reg2 = pic->ic_ackreg2 == PLUM_INT_PCCINTS_REG
364			? reg_pccard : reg_ext;
365
366		if (pic->ic_ackpat2 & reg2)
367			(*pi->pi_fun)(pi->pi_arg);
368	}
369	plum_ei(sc->sc_pc);
370
371	return (0);
372}
373
374#ifdef PLUMICUDEBUG
375void
376plumicu_dump(struct plumicu_softc *sc)
377{
378	bus_space_tag_t regt = sc->sc_regt;
379	bus_space_handle_t regh = sc->sc_regh;
380	plumreg_t reg;
381
382	printf("status:");
383	reg = plum_conf_read(regt, regh, PLUM_INT_INTSTA_REG);
384	dbg_bit_print(reg);
385	printf("ExtIO\n");
386	printf("status:");
387	reg = plum_conf_read(regt, regh, PLUM_INT_EXTINTS_REG);
388	dbg_bit_print(reg);
389	printf("enable:");
390	reg = plum_conf_read(regt, regh, PLUM_INT_EXTIEN_REG);
391 	dbg_bit_print(reg);
392
393}
394#endif /* PLUMICUDEBUG */
395