vr4181giu.c revision 1.2
1/* $NetBSD: vr4181giu.c,v 1.2 2003/07/15 02:29:35 lukem Exp $ */
2
3/*-
4 * Copyright (c) 1999-2001
5 *         Shin Takemura and PocketBSD Project. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed by the PocketBSD project
18 *	and its contributors.
19 * 4. Neither the name of the project nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 */
36
37#include <sys/cdefs.h>
38__KERNEL_RCSID(0, "$NetBSD: vr4181giu.c,v 1.2 2003/07/15 02:29:35 lukem Exp $");
39
40#include <sys/param.h>
41#include <sys/device.h>
42#include <sys/malloc.h>
43#include <sys/queue.h>
44#include <sys/systm.h>
45
46#include <machine/bus.h>
47
48#include <hpcmips/vr/vripif.h>
49#include <hpcmips/vr/vr4181giureg.h>
50
51#define MAX_GIU4181INTR	16
52
53struct vr4181giu_intr_entry {
54	int	ih_port;
55	int	(*ih_fun)(void *);
56	void	*ih_arg;
57	TAILQ_ENTRY(vr4181giu_intr_entry) ih_link;
58};
59
60struct vr4181giu_softc {
61	struct device			sc_dev;
62	bus_space_tag_t			sc_iot;
63	bus_space_handle_t		sc_ioh;
64	vrip_chipset_tag_t		sc_vc;
65	void 				*sc_ih;
66	u_int32_t			sc_intr_mode[MAX_GIU4181INTR];
67	TAILQ_HEAD(, vr4181giu_intr_entry)
68					sc_intr_head[MAX_GIU4181INTR];
69	struct hpcio_chip		sc_iochip;
70	struct hpcio_attach_args	sc_haa;
71};
72
73static int vr4181giu_match(struct device *, struct cfdata *, void *);
74static void vr4181giu_attach(struct device *, struct device *, void *);
75
76static void vr4181giu_callback(struct device *self);
77static int vr4181giu_print(void *aux, const char *pnp);
78static int vr4181giu_port_read(hpcio_chip_t hc, int port);
79static void vr4181giu_port_write(hpcio_chip_t hc, int port, int onoff);
80static void vr4181giu_update(hpcio_chip_t hc);
81static void vr4181giu_dump(hpcio_chip_t hc);
82static hpcio_chip_t vr4181giu_getchip(void* scx, int chipid);
83static void *vr4181giu_intr_establish(hpcio_chip_t, int, int,
84				      int (*)(void *),void *);
85static void vr4181giu_intr_disestablish(hpcio_chip_t hc, void *arg);
86static void vr4181giu_intr_clear(hpcio_chip_t hc, void *arg);
87static void vr4181giu_register_iochip(hpcio_chip_t hc, hpcio_chip_t iochip);
88static int vr4181giu_intr(void *arg);
89
90
91
92static struct hpcio_chip vr4181giu_iochip = {
93	.hc_portread =		vr4181giu_port_read,
94	.hc_portwrite =		vr4181giu_port_write,
95	.hc_intr_establish =	vr4181giu_intr_establish,
96	.hc_intr_disestablish =	vr4181giu_intr_disestablish,
97	.hc_intr_clear =	vr4181giu_intr_clear,
98	.hc_register_iochip =	vr4181giu_register_iochip,
99	.hc_update =		vr4181giu_update,
100	.hc_dump =		vr4181giu_dump,
101};
102
103CFATTACH_DECL(vr4181giu, sizeof(struct vr4181giu_softc),
104	      vr4181giu_match, vr4181giu_attach, NULL, NULL);
105
106static int
107vr4181giu_match(struct device *parent, struct cfdata *match, void *aux)
108{
109	return (2); /* 1st attach group of vrip */
110}
111
112static void
113vr4181giu_attach(struct device *parent, struct device *self, void *aux)
114{
115	struct vr4181giu_softc	*sc = (struct vr4181giu_softc*) self;
116	struct vrip_attach_args	*va = aux;
117	int			i;
118
119	sc->sc_iot = va->va_iot;
120	sc->sc_vc = va->va_vc;
121
122	if (bus_space_map(sc->sc_iot, va->va_addr, va->va_size,
123			  0 /* no cache */, &sc->sc_ioh)) {
124		printf(": can't map i/o space\n");
125		return;
126	}
127
128	for (i = 0; i < MAX_GIU4181INTR; i++)
129		TAILQ_INIT(&sc->sc_intr_head[i]);
130
131	if (!(sc->sc_ih
132	      = vrip_intr_establish(va->va_vc, va->va_unit, 0,
133				    IPL_BIO, vr4181giu_intr, sc))) {
134		printf("%s: can't establish interrupt\n", sc->sc_dev.dv_xname);
135		return;
136	}
137
138	/*
139	 * fill hpcio_chip structure
140	 */
141	sc->sc_iochip = vr4181giu_iochip; /* structure copy */
142	sc->sc_iochip.hc_chipid = VRIP_IOCHIP_VR4181GIU;
143	sc->sc_iochip.hc_name = sc->sc_dev.dv_xname;
144	sc->sc_iochip.hc_sc = sc;
145	/* Register functions to upper interface */
146	vrip_register_gpio(va->va_vc, &sc->sc_iochip);
147
148	printf("\n");
149
150	/*
151	 *  hpcio I/F
152	 */
153	sc->sc_haa.haa_busname = HPCIO_BUSNAME;
154	sc->sc_haa.haa_sc = sc;
155	sc->sc_haa.haa_getchip = vr4181giu_getchip;
156	sc->sc_haa.haa_iot = sc->sc_iot;
157	while (config_found(self, &sc->sc_haa, vr4181giu_print)) ;
158
159	/*
160	 * GIU-ISA bridge
161	 */
162#if 1 /* XXX Sometimes mounting root device failed. Why? XXX*/
163	config_defer(self, vr4181giu_callback);
164#else
165	vr4181giu_callback(self);
166#endif
167}
168
169static void
170vr4181giu_callback(struct device *self)
171{
172	struct vr4181giu_softc		*sc = (void *) self;
173
174	sc->sc_haa.haa_busname = "vrisab";
175	config_found(self, &sc->sc_haa, vr4181giu_print);
176}
177
178static int
179vr4181giu_print(void *aux, const char *pnp)
180{
181	if (pnp)
182		return (QUIET);
183	return (UNCONF);
184}
185
186static int
187vr4181giu_port_read(hpcio_chip_t hc, int port)
188{
189	struct vr4181giu_softc	*sc = hc->hc_sc;
190	u_int16_t		r;
191
192	if (port < 0 || 32 <= port)
193		panic("vr4181giu_port_read: invalid gpio port");
194
195	if (port < 16) {
196		r = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
197				     VR4181GIU_PIOD_L_REG_W)
198			& 1 << port;
199	} else {
200		r = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
201				     VR4181GIU_PIOD_H_REG_W)
202			& 1 << (port - 16);
203	}
204	return r ? 1 : 0;
205}
206
207static void
208vr4181giu_port_write(hpcio_chip_t hc, int port, int onoff)
209{
210	struct vr4181giu_softc	*sc = hc->hc_sc;
211	u_int16_t		r;
212
213	if (port < 16) {
214		r = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
215				     VR4181GIU_PIOD_L_REG_W);
216		if (onoff) {
217			r |= 1 << port;
218		} else {
219			r &= ~(1 << port);
220		}
221		bus_space_write_2(sc->sc_iot, sc->sc_ioh,
222				  VR4181GIU_PIOD_L_REG_W, r);
223	} else {
224		r = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
225				     VR4181GIU_PIOD_H_REG_W);
226		if (onoff) {
227			r |= 1 << (port - 16);
228		} else {
229			r &= ~(1 << (port - 16));
230		}
231		bus_space_write_2(sc->sc_iot, sc->sc_ioh,
232				  VR4181GIU_PIOD_H_REG_W, r);
233	}
234}
235
236/*
237 * XXXXXXXXXXXXXXXXXXXXXXXX
238 */
239static void
240vr4181giu_update(hpcio_chip_t hc)
241{
242}
243
244static void
245vr4181giu_dump(hpcio_chip_t hc)
246{
247}
248
249static hpcio_chip_t
250vr4181giu_getchip(void* scx, int chipid)
251{
252	struct vr4181giu_softc	*sc = scx;
253
254	return (&sc->sc_iochip);
255}
256
257static void *
258vr4181giu_intr_establish(
259	hpcio_chip_t hc,
260	int port, /* GPIO pin # */
261	int mode, /* GIU trigger setting */
262	int (*ih_fun)(void *),
263	void *ih_arg)
264{
265	struct vr4181giu_softc		*sc = hc->hc_sc;
266	struct vr4181giu_intr_entry	*ih;
267	int				s;
268	u_int32_t 			mask;
269	u_int32_t 			raw_intr_type;
270	int				regmod;
271	int				reghl;
272	int				bitoff;
273	u_int16_t			r;
274
275	/*
276	 * trigger mode translation
277	 *
278	 * VR4181 only support for four type of interrupt trigger
279	 * listed below:
280	 *
281	 * 1. high level
282	 * 2. low level
283	 * 3. rising edge
284	 * 4. falling edge
285	 *
286	 * argument mode is a bitmap as following:
287	 *
288	 * 001 detection trigger       (1:edge/0:level  )
289	 * 010 signal hold/through     (1:hold/0:through)
290	 * 100 detection level         (1:high/0:low    )
291	 *
292	 * possible mode value is 000B to 111B.
293	 *
294	 * 000 HPCIO_INTR_LEVEL_LOW_THROUGH
295	 * 001 HPCIO_INTR_EDGE_THROUGH
296	 * 010 HPCIO_INTR_LEVEL_LOW_HOLD
297	 * 011 HPCIO_INTR_EDGE_HOLD
298	 * 100 HPCIO_INTR_LEVEL_HIGH_THROUGH
299	 * 101 falling edge and through?
300	 * 110 HPCIO_INTR_LEVEL_HIGH_HOLD
301	 * 111 falling edge and hold?
302	 */
303
304	static u_int32_t intr_mode_trans[8] = {
305		VR4181GIU_INTTYP_LOW_LEVEL,	/* 000 */
306		VR4181GIU_INTTYP_RISING_EDGE,	/* 001 */
307		VR4181GIU_INTTYP_LOW_LEVEL,	/* 010 */
308		VR4181GIU_INTTYP_RISING_EDGE,	/* 011 */
309		VR4181GIU_INTTYP_HIGH_LEVEL,	/* 100 */
310		VR4181GIU_INTTYP_FALLING_EDGE,	/* 101 */
311		VR4181GIU_INTTYP_HIGH_LEVEL,	/* 110 */
312		VR4181GIU_INTTYP_FALLING_EDGE,	/* 111 */
313	};
314
315	raw_intr_type = intr_mode_trans[mode];
316	if (raw_intr_type == VR4181GIU_INTTYP_INVALID)
317		panic("vr4181giu_intr_establish: invalid interrupt mode.");
318
319	if (port < 0 || MAX_GIU4181INTR <= port)
320		panic("vr4181giu_intr_establish: invalid interrupt line.");
321	if (!TAILQ_EMPTY(&sc->sc_intr_head[port])
322	    && raw_intr_type != sc->sc_intr_mode[port])
323		panic("vr4181giu_intr_establish: "
324		      "cannot use one line with two modes at a time.");
325	else
326		sc->sc_intr_mode[port] = raw_intr_type;
327	mask = (1 << port);
328
329	s = splhigh();
330
331	if ((ih = malloc(sizeof *ih, M_DEVBUF, M_NOWAIT)) == NULL)
332		panic("vr4181giu_intr_establish: memory exhausted.");
333
334	ih->ih_port = port;
335	ih->ih_fun = ih_fun;
336	ih->ih_arg = ih_arg;
337	TAILQ_INSERT_TAIL(&sc->sc_intr_head[port], ih, ih_link);
338
339	/*
340	 * setup GIU registers
341	 */
342
343	/* disable interrupt at first */
344	r = bus_space_read_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTEN_REG_W);
345	r &= ~mask;
346	bus_space_write_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTEN_REG_W, r);
347
348	/* mode */
349	regmod = port >> 3;
350	bitoff = (port & 0x7) << 1;
351	r = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
352			     VR4181GIU_MODE0_REG_W + regmod);
353	r &= ~(0x3 << bitoff);
354	r |= (VR4181GIU_MODE_IN  | VR4181GIU_MODE_GPIO) << bitoff;
355	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
356			  VR4181GIU_MODE0_REG_W + regmod, r);
357	/* interrupt type */
358	reghl = port < 8 ? 2 : 0;	/* high byte: 0x0, lowbyte: 0x2 */
359	r = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
360			     VR4181GIU_INTTYP_REG + reghl);
361	r &= ~(0x3 << bitoff);
362	r |= raw_intr_type << bitoff;
363	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
364			  VR4181GIU_INTTYP_REG + reghl, r);
365
366	/* clear status */
367	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
368			  VR4181GIU_INTSTAT_REG_W, mask);
369
370	/* unmask */
371	r = bus_space_read_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTMASK_REG_W);
372	r &= ~mask;
373	bus_space_write_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTMASK_REG_W, r);
374
375	/* enable */
376	r = bus_space_read_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTEN_REG_W);
377	r |= mask;
378	bus_space_write_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTEN_REG_W, r);
379
380	splx(s);
381
382	return ih;
383}
384
385static void
386vr4181giu_intr_disestablish(hpcio_chip_t hc, void *arg)
387{
388}
389
390static void
391vr4181giu_intr_clear(hpcio_chip_t hc, void *arg)
392{
393	struct vr4181giu_softc		*sc = hc->hc_sc;
394	struct vr4181giu_intr_entry	*ih = arg;
395
396	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
397			  VR4181GIU_INTSTAT_REG_W, 1 << ih->ih_port);
398}
399
400static void
401vr4181giu_register_iochip(hpcio_chip_t hc, hpcio_chip_t iochip)
402{
403	struct vr4181giu_softc	*sc = hc->hc_sc;
404
405	vrip_register_gpio(sc->sc_vc, iochip);
406}
407
408/*
409 * interrupt handler
410 */
411static int
412vr4181giu_intr(void *arg)
413{
414	struct vr4181giu_softc	*sc = arg;
415	int			i;
416	u_int16_t		r;
417
418	r = bus_space_read_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTSTAT_REG_W);
419	bus_space_write_2(sc->sc_iot, sc->sc_ioh, VR4181GIU_INTSTAT_REG_W, r);
420
421	for (i = 0; i < MAX_GIU4181INTR; i++) {
422		if (r & (1 << i)) {
423			struct vr4181giu_intr_entry *ih;
424			TAILQ_FOREACH(ih, &sc->sc_intr_head[i], ih_link) {
425				ih->ih_fun(ih->ih_arg);
426			}
427		}
428	}
429
430	return 0;
431}
432