4 * Copyright (c) 2002, 2003, 2005 Genetec corp. All rights reserved.
5 * Written by Hiroyuki Bessho for Genetec corp.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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. The name of Genetec corp. may not be used to endorse
16 * or promote products derived from this software without specific prior
19 * THIS SOFTWARE IS PROVIDED BY GENETEC CORP. ``AS IS'' AND
20 * 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 GENETEC CORP.
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.
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/device.h>
36 #include <sys/kernel.h>
37 #include <sys/reboot.h>
39 #include <machine/cpu.h>
40 #include <machine/bus.h>
41 #include <machine/intr.h>
42 #include <arm/cpufunc.h>
44 #include <arm/mainbus/mainbus.h>
45 #include <arm/xscale/pxa2x0cpu.h>
46 #include <arm/xscale/pxa2x0reg.h>
47 #include <arm/xscale/pxa2x0var.h>
48 #include <arm/xscale/pxa2x0_gpio.h>
49 #include <arm/sa11x0/sa11x0_var.h>
50 #include <evbarm/g42xxeb/g42xxeb_reg.h>
51 #include <evbarm/g42xxeb/g42xxeb_var.h>
56 static int obio_match(struct device
*, struct cfdata
*, void *);
57 static void obio_attach(struct device
*, struct device
*, void *);
58 static int obio_search(struct device
*, struct cfdata
*,
60 static int obio_print(void *, const char *);
62 /* attach structures */
63 CFATTACH_DECL(obio
, sizeof(struct obio_softc
), obio_match
, obio_attach
,
67 obio_spurious(void *arg
)
71 printf("Spurious interrupt %d on On-board peripheral", irqno
);
77 * interrupt handler for GPIO0 (on-board peripherals)
79 * On G4250ebx, 10 interrupts are ORed through on-board logic,
80 * and routed to GPIO0 of PXA250 processor.
86 struct obio_softc
*sc
= (struct obio_softc
*)arg
;
89 #define get_pending(sc) \
90 (bus_space_read_2( sc->sc_iot, sc->sc_obioreg_ioh, G42XXEB_INTSTS1) \
91 & ~(sc->sc_intr_pending|sc->sc_intr_mask))
94 printf("obio_intr: pend=%x, mask=%x, pend=%x, mask=%x\n",
95 bus_space_read_2(sc
->sc_iot
, sc
->sc_obioreg_ioh
, G42XXEB_INTSTS1
),
96 bus_space_read_2(sc
->sc_iot
, sc
->sc_obioreg_ioh
, G42XXEB_INTMASK
),
101 for (pending
= get_pending(sc
);
102 (irqno
= find_first_bit(pending
)) >= 0;
103 pending
= get_pending(sc
)) {
105 /* reset pending bit */
106 bus_space_write_2(sc
->sc_iot
, sc
->sc_obioreg_ioh
,
107 G42XXEB_INTSTS1
, ~(1<<irqno
));
110 if (sc
->sc_handler
[irqno
].level
> saved_spl_level
) {
111 int spl_save
= _splraise(sc
->sc_handler
[irqno
].level
);
112 (* sc
->sc_handler
[irqno
].func
)(
113 sc
->sc_handler
[irqno
].arg
);
119 int psw
= disable_interrupts(I32_bit
); /* XXX */
121 /* mask this interrupt until software
122 interrupt is handled. */
123 sc
->sc_intr_pending
|= (1U<<irqno
);
124 obio_update_intrmask(sc
);
126 restore_interrupts(psw
);
131 panic("obio_intr: stayed too long");
136 /* handle it later */
137 softint_schedule(sc
->sc_si
);
140 /* GPIO interrupt is edge triggered. make a pulse
141 to let Cotulla notice when other interrupts are
143 bus_space_write_2(sc
->sc_iot
, sc
->sc_obioreg_ioh
,
144 G42XXEB_INTMASK
, 0xffff);
145 obio_update_intrmask(sc
);
151 obio_softint(void *arg
)
153 struct obio_softc
*sc
= (struct obio_softc
*)arg
;
155 int spl_save
= curcpl();
158 psw
= disable_interrupts(I32_bit
);
159 while ((irqno
= find_first_bit(sc
->sc_intr_pending
)) >= 0) {
160 sc
->sc_intr_pending
&= ~(1U<<irqno
);
162 restore_interrupts(psw
);
164 _splraise(sc
->sc_handler
[irqno
].level
);
165 (* sc
->sc_handler
[irqno
].func
)(
166 sc
->sc_handler
[irqno
].arg
);
169 psw
= disable_interrupts(I32_bit
);
172 /* assert(sc->sc_intr_pending==0) */
174 bus_space_write_2(sc
->sc_iot
, sc
->sc_obioreg_ioh
,
175 G42XXEB_INTMASK
, 0xffff);
176 obio_update_intrmask(sc
);
178 restore_interrupts(psw
);
182 * int obio_print(void *aux, const char *name)
183 * print configuration info for children
187 obio_print(void *aux
, const char *name
)
189 struct obio_attach_args
*oba
= (struct obio_attach_args
*)aux
;
191 if (oba
->oba_addr
!= OBIOCF_ADDR_DEFAULT
)
192 printf(" addr 0x%lx", oba
->oba_addr
);
193 if (oba
->oba_intr
> 0)
194 printf(" intr %d", oba
->oba_intr
);
199 obio_match(struct device
*parent
, struct cfdata
*match
, void *aux
)
205 obio_attach(struct device
*parent
, struct device
*self
, void *aux
)
207 struct obio_softc
*sc
= (struct obio_softc
*)self
;
208 struct sa11x0_attach_args
*sa
= (struct sa11x0_attach_args
*)aux
;
209 bus_space_tag_t iot
= sa
->sa_iot
;
213 /* tweak memory access timing for CS3.
214 the value set by redboot is too slow */
215 if (bus_space_map(iot
, PXA2X0_MEMCTL_BASE
, PXA2X0_MEMCTL_SIZE
, 0,
218 bus_space_write_4(iot
, sc
->sc_memctl_ioh
, MEMCTL_MSC1
,
219 (0xffff & bus_space_read_4(iot
, sc
->sc_memctl_ioh
, MEMCTL_MSC1
))
222 /* Map on-board FPGA registers */
224 if (bus_space_map(iot
, G42XXEB_PLDREG_BASE
, G42XXEB_PLDREG_SIZE
,
225 0, &(sc
->sc_obioreg_ioh
)))
229 * Mask all interrupts.
230 * They are later unmasked at each device's attach routine.
232 sc
->sc_intr_mask
= 0xffff;
233 bus_space_write_2(iot
, sc
->sc_obioreg_ioh
, G42XXEB_INTMASK
,
237 sc
->sc_intr
= 8; /* GPIO0 */
239 sc
->sc_intr_pending
= 0;
241 for (i
=0; i
< G42XXEB_N_INTS
; ++i
) {
242 sc
->sc_handler
[i
].func
= obio_spurious
;
243 sc
->sc_handler
[i
].arg
= (void *)i
;
246 obio_peripheral_reset(sc
, 1, 0);
249 * establish interrupt handler.
250 * level is very high to allow high priority sub-interrupts.
252 sc
->sc_ipl
= IPL_AUDIO
;
253 sc
->sc_ih
= pxa2x0_gpio_intr_establish(0, IST_EDGE_FALLING
, sc
->sc_ipl
,
255 sc
->sc_si
= softint_establish(SOFTINT_NET
, obio_softint
, sc
);
257 reg
= bus_space_read_2(iot
, sc
->sc_obioreg_ioh
, G42XXEB_PLDVER
);
258 aprint_normal(": board %d version %x\n", reg
>>8, reg
& 0xff);
261 * Attach each devices
263 config_search_ia(obio_search
, self
, "obio", NULL
);
267 printf( "%s: can't map FPGA registers\n", self
->dv_xname
);
271 obio_search(struct device
*parent
, struct cfdata
*cf
,
272 const int *ldesc
, void *aux
)
274 struct obio_softc
*sc
= (struct obio_softc
*)parent
;
275 struct obio_attach_args oba
;
278 oba
.oba_iot
= sc
->sc_iot
;
279 oba
.oba_addr
= cf
->cf_loc
[OBIOCF_ADDR
];
280 oba
.oba_intr
= cf
->cf_loc
[OBIOCF_INTR
];
282 if (config_match(parent
, cf
, &oba
) > 0)
283 config_attach(parent
, cf
, &oba
, obio_print
);
289 obio_intr_establish(struct obio_softc
*sc
, int irq
, int ipl
,
290 int type
, int (*func
)(void *), void *arg
)
295 static const uint8_t ist_code
[] = {
297 G42XXEB_INT_EDGE_FALLING
, /* pulse */
298 G42XXEB_INT_EDGE_FALLING
, /* IST_EDGE */
299 G42XXEB_INT_LEVEL_LOW
, /* IST_LEVEL */
300 G42XXEB_INT_LEVEL_HIGH
, /* IST_LEVEL_HIGH */
301 G42XXEB_INT_EDGE_RISING
, /* IST_EDGE_RISING */
302 G42XXEB_INT_EDGE_BOTH
, /* IST_EDGE_BOTH */
305 if (irq
< 0 || G42XXEB_N_INTS
<= irq
)
306 panic("Bad irq no. for obio (%d)", irq
);
308 if (type
< 0 || IST_EDGE_BOTH
< type
)
309 panic("Bad interrupt type for obio (%d)", type
);
311 regidx
= G42XXEB_INTCNTL
;
314 regidx
= G42XXEB_INTCNTH
;
318 save
= disable_interrupts(I32_bit
);
320 sc
->sc_handler
[irq
].func
= func
;
321 sc
->sc_handler
[irq
].arg
= arg
;
322 sc
->sc_handler
[irq
].level
= ipl
;
324 /* set interrupt type */
325 reg
= bus_space_read_2(sc
->sc_iot
, sc
->sc_obioreg_ioh
, regidx
);
326 bus_space_write_2(sc
->sc_iot
, sc
->sc_obioreg_ioh
, regidx
,
327 (reg
& ~(7<<sft
)) | (ist_code
[type
] << sft
));
330 printf("INTCTL=%x,%x\n",
331 bus_space_read_2(sc
->sc_iot
, sc
->sc_obioreg_ioh
, G42XXEB_INTCNTL
),
332 bus_space_read_2(sc
->sc_iot
, sc
->sc_obioreg_ioh
, G42XXEB_INTCNTH
));
335 sc
->sc_intr_mask
&= ~(1U << irq
);
336 obio_update_intrmask(sc
);
338 restore_interrupts(save
);
341 if (ipl
> sc
->sc_ipl
) {
342 pxa2x0_update_intr_masks(sc
->sc_intr
, ipl
);
347 return &sc
->sc_handler
[irq
];
351 obio_intr_disestablish(struct obio_softc
*sc
, int irq
, int (* func
)(void *))
356 save
= disable_interrupts(I32_bit
);
358 if (sc
->sc_handler
[irq
].func
!= func
)
361 sc
->sc_handler
[irq
].func
= obio_spurious
;
362 sc
->sc_handler
[irq
].level
= IPL_NONE
;
364 sc
->sc_intr_pending
&= ~(1U << irq
);
365 sc
->sc_intr_mask
|= (1U << irq
);
366 obio_update_intrmask(sc
);
369 restore_interrupts(save
);
372 aprint_error("%s: bad intr_disestablish\n",
373 sc
->sc_dev
.dv_xname
);
377 obio_intr_mask(struct obio_softc
*sc
, struct obio_handler
*ih
)
382 irqno
= ih
- sc
->sc_handler
;
384 if (ih
== NULL
|| ih
->func
==NULL
|| irqno
< 0 ||
385 irqno
>= G42XXEB_N_INTS
)
386 panic("Bad arg for obio_intr_mask");
389 save
= disable_interrupts(I32_bit
);
390 sc
->sc_intr_mask
|= 1U<<irqno
;
391 obio_update_intrmask(sc
);
392 restore_interrupts(save
);
396 obio_intr_unmask(struct obio_softc
*sc
, struct obio_handler
*ih
)
401 irqno
= ih
- sc
->sc_handler
;
403 if (ih
== NULL
|| ih
->func
==NULL
|| irqno
< 0 ||
404 irqno
>= G42XXEB_N_INTS
)
405 panic("Bad arg for obio_intr_unmask");
408 save
= disable_interrupts(I32_bit
);
409 sc
->sc_intr_mask
&= ~(1U<<irqno
);
410 obio_update_intrmask(sc
);
411 restore_interrupts(save
);
415 obio_peripheral_reset(struct obio_softc
*bsc
, int no
, int onoff
)
419 reg
= bus_space_read_2(bsc
->sc_iot
, bsc
->sc_obioreg_ioh
,
421 bus_space_write_2(bsc
->sc_iot
, bsc
->sc_obioreg_ioh
, G42XXEB_RST
,
422 onoff
? (reg
& ~RST_EXT(no
)) : (reg
| RST_EXT(no
)));