File: [local] / sys / arch / arm / xscale / i80321_intr.c (download)
Revision 1.1.1.1 (vendor branch), Tue Mar 4 16:05:21 2008 UTC (16 years, 4 months ago) by nbrk
Branch: OPENBSD_4_2_BASE, MAIN
CVS Tags: jornada-partial-support-wip, HEAD Changes since 1.1: +0 -0 lines
Import of OpenBSD 4.2 release kernel tree with initial code to support
Jornada 720/728, StrongARM 1110-based handheld PC.
At this point kernel roots on NFS and boots into vfs_mountroot() and traps.
What is supported:
- glass console, Jornada framebuffer (jfb) works in 16bpp direct color mode
(needs some palette tweaks for non black/white/blue colors, i think)
- saic, SA11x0 interrupt controller (needs cleanup)
- sacom, SA11x0 UART (supported only as boot console for now)
- SA11x0 GPIO controller fully supported (but can't handle multiple interrupt
handlers on one gpio pin)
- sassp, SSP port on SA11x0 that attaches spibus
- Jornada microcontroller (jmcu) to control kbd, battery, etc throught
the SPI bus (wskbd attaches on jmcu, but not tested)
- tod functions seem work
- initial code for SA-1111 (chip companion) : this is TODO
Next important steps, i think:
- gpio and intc on sa1111
- pcmcia support for sa11x0 (and sa1111 help logic)
- REAL root on nfs when we have PCMCIA support (we may use any of supported pccard NICs)
- root on wd0! (using already supported PCMCIA-ATA)
|
/* $OpenBSD: i80321_intr.c,v 1.11 2007/05/19 15:47:16 miod Exp $ */
/*
* Copyright (c) 2006 Dale Rahn <drahn@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/evcount.h>
#include <uvm/uvm.h> /* uvmexp */
#include <machine/intr.h>
#include <arm/cpufunc.h>
#include <arm/xscale/i80321reg.h>
#include <arm/xscale/i80321var.h>
/*
* autoconf glue
*/
int i80321intc_match(struct device *, void *, void *);
void i80321intc_attach(struct device *, struct device *, void *);
/* internal functions */
static void i80321intc_write_intctl(uint32_t mask);
void i80321intc_write_steer(uint32_t mask);
uint32_t i80321intc_read_intsrc(void);
void i80321intc_calc_mask(void);
void i80321intc_init(void);
void i80321intc_intr_init(void);
static void i80321intc_setipl(int new);
void i80321intc_do_pending(void);
uint32_t i80321intc_imask[NIPL];
uint32_t i80321intc_smask[NIPL];
#define SI_TO_IRQBIT(x) (1 << (x))
__volatile int current_ipl_level;
__volatile int softint_pending;
struct cfattach i80321intc_ca = {
sizeof(struct device), i80321intc_match, i80321intc_attach
};
struct cfdriver i80321intc_cd = {
NULL, "i80321intc", DV_DULL
};
int i80321intc_attached = 0;
int
i80321intc_match(struct device *parent, void *v, void *aux)
{
if (i80321intc_attached == 0)
return 1;
i80321intc_attached = 1;
return 0;
}
void
i80321intc_attach(struct device *parent, struct device *self, void *args)
{
i80321intc_init();
}
static inline void
i80321intc_write_intctl(uint32_t mask)
{
__asm__ volatile ("mcr p6, 0, %0, c0, c0, 0" : : "r" (mask));
}
void
i80321intc_write_steer(uint32_t mask)
{
__asm__ volatile ("mcr p6, 0, %0, c4, c0, 0" : : "r" (mask));
}
uint32_t
i80321intc_read_intsrc(void)
{
uint32_t mask;
__asm__ volatile ("mrc p6, 0, %0, c8, c0, 0" : "=r" (mask));
return mask;
}
static inline void
i80321intc_setipl(int new)
{
int psw;
psw = disable_interrupts(I32_bit);
current_ipl_level = new;
i80321intc_write_intctl(i80321intc_imask[new]);
restore_interrupts(psw);
}
struct intrq i80321_handler[NIRQ];
/*
* Recompute the irq mask bits.
* Must be called with interrupts disabled.
*/
void
i80321intc_calc_mask(void)
{
int irq;
struct intrhand *ih;
int i;
for (irq = 0; irq < NIRQ; irq++) {
int i;
int max = IPL_NONE;
int min = IPL_HIGH;
TAILQ_FOREACH(ih, &i80321_handler[irq].iq_list, ih_list) {
if (ih->ih_ipl > max)
max = ih->ih_ipl;
if (ih->ih_ipl < min)
min = ih->ih_ipl;
}
i80321_handler[irq].iq_irq = max;
if (max == IPL_NONE)
min = IPL_NONE; /* interrupt not enabled */
#if 0
printf("irq %d: min %x max %x\n", irq, min, max);
#endif
/* Enable interrupts at lower levels */
for (i = 0; i < min; i++)
i80321intc_imask[i] |= (1 << irq);
/* Disable interrupts at upper levels */
for (;i <= IPL_HIGH; i++)
i80321intc_imask[i] &= ~(1 << irq);
}
/* initialize soft interrupt mask */
for (i = IPL_NONE; i <= IPL_HIGH; i++) {
i80321intc_smask[i] = 0;
if (i < IPL_SOFT)
i80321intc_smask[i] |= SI_TO_IRQBIT(SI_SOFT);
if (i < IPL_SOFTCLOCK)
i80321intc_smask[i] |= SI_TO_IRQBIT(SI_SOFTCLOCK);
if (i < IPL_SOFTNET)
i80321intc_smask[i] |= SI_TO_IRQBIT(SI_SOFTNET);
if (i < IPL_SOFTSERIAL)
i80321intc_smask[i] |= SI_TO_IRQBIT(SI_SOFTSERIAL);
#if 0
printf("mask[%d]: %x %x\n", i, i80321intc_smask[i],
i80321intc_imask[i]);
#endif
}
i80321intc_setipl(current_ipl_level);
}
void
i80321intc_do_pending(void)
{
static int processing = 0;
int oldirqstate, spl_save;
oldirqstate = disable_interrupts(I32_bit);
spl_save = current_ipl_level;
if (processing == 1) {
restore_interrupts(oldirqstate);
return;
}
#define DO_SOFTINT(si, ipl) \
if ((softint_pending & i80321intc_smask[current_ipl_level]) & \
SI_TO_IRQBIT(si)) { \
softint_pending &= ~SI_TO_IRQBIT(si); \
if (current_ipl_level < ipl) \
i80321intc_setipl(ipl); \
restore_interrupts(oldirqstate); \
softintr_dispatch(si); \
oldirqstate = disable_interrupts(I32_bit); \
i80321intc_setipl(spl_save); \
}
do {
DO_SOFTINT(SI_SOFTSERIAL, IPL_SOFTSERIAL);
DO_SOFTINT(SI_SOFTNET, IPL_SOFTNET);
DO_SOFTINT(SI_SOFTCLOCK, IPL_SOFTCLOCK);
DO_SOFTINT(SI_SOFT, IPL_SOFT);
} while (softint_pending & i80321intc_smask[current_ipl_level]);
processing = 0;
restore_interrupts(oldirqstate);
}
void
splx(int new)
{
i80321intc_setipl(new);
if (softint_pending & i80321intc_smask[current_ipl_level])
i80321intc_do_pending();
}
int
_spllower(int new)
{
int old = current_ipl_level;
splx(new);
return (old);
}
int
_splraise(int new)
{
int old;
old = current_ipl_level;
/*
* setipl must always be called because there is a race window
* where the variable is updated before the mask is set
* an interrupt occurs in that window without the mask always
* being set, the hardware might not get updated on the next
* splraise completely messing up spl protection.
*/
if (old > new)
new = old;
i80321intc_setipl(new);
return (old);
}
void
_setsoftintr(int si)
{
int oldirqstate;
oldirqstate = disable_interrupts(I32_bit);
softint_pending |= SI_TO_IRQBIT(si);
restore_interrupts(oldirqstate);
/* Process unmasked pending soft interrupts. */
if (softint_pending & i80321intc_smask[current_ipl_level])
i80321intc_do_pending();
}
/*
* i80321_icu_init:
*
* Initialize the i80321 ICU. Called early in bootstrap
* to make sure the ICU is in a pristine state.
*/
void
i80321intc_intr_init(void)
{
i80321intc_write_intctl(0);
i80321intc_write_steer(0);
}
/*
* i80321_intr_init:
*
* Initialize the rest of the interrupt subsystem, making it
* ready to handle interrupts from devices.
*/
void
i80321intc_init(void)
{
struct intrq *iq;
int i;
for (i = 0; i < NIRQ; i++) {
iq = &i80321_handler[i];
TAILQ_INIT(&iq->iq_list);
}
i80321intc_calc_mask();
/* Enable IRQs (don't yet use FIQs). */
enable_interrupts(I32_bit);
}
void *
i80321_intr_establish(int irq, int ipl, int (*func)(void *), void *arg,
char *name)
{
struct intrq *iq;
struct intrhand *ih;
uint32_t oldirqstate;
if (irq < 0 || irq > NIRQ)
panic("i80321_intr_establish: IRQ %d out of range", irq);
ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
if (ih == NULL)
return (NULL);
ih->ih_func = func;
ih->ih_arg = arg;
ih->ih_ipl = ipl;
ih->ih_name = name;
ih->ih_irq = irq;
iq = &i80321_handler[irq];
if (name != NULL)
evcount_attach(&ih->ih_count, name, (void *)&ih->ih_irq,
&evcount_intr);
/* All IOP321 interrupts are level-triggered. */
iq->iq_ist = IST_LEVEL;
oldirqstate = disable_interrupts(I32_bit);
TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list);
i80321intc_calc_mask();
restore_interrupts(oldirqstate);
return (ih);
}
void
i80321_intr_disestablish(void *cookie)
{
struct intrhand *ih = cookie;
struct intrq *iq = &i80321_handler[ih->ih_irq];
int oldirqstate;
oldirqstate = disable_interrupts(I32_bit);
TAILQ_REMOVE(&iq->iq_list, ih, ih_list);
if (ih->ih_name != NULL)
evcount_detach(&ih->ih_count);
i80321intc_calc_mask();
restore_interrupts(oldirqstate);
}
void
i80321_irq_handler(void *arg)
{
struct clockframe *frame = arg;
uint32_t hwpend;
int irq;
int saved_spl_level;
struct intrhand *ih;
saved_spl_level = current_ipl_level;
/* get pending IRQs */
hwpend = i80321intc_read_intsrc();
while ((irq = find_first_bit(hwpend)) >= 0) {
/* XXX: Should we handle IRQs in priority order? */
/* raise spl to stop interrupts of lower priorities */
if (saved_spl_level < i80321_handler[irq].iq_irq)
i80321intc_setipl(i80321_handler[irq].iq_irq);
/* Enable interrupt */
enable_interrupts(I32_bit);
TAILQ_FOREACH(ih, &i80321_handler[irq].iq_list, ih_list) {
if ((ih->ih_func)( ih->ih_arg == 0
? frame : ih->ih_arg))
ih->ih_count.ec_count++;
}
/* Disable interrupt */
disable_interrupts(I32_bit);
hwpend &= ~(1<<irq);
}
uvmexp.intrs++;
/* restore spl to that was when this interrupt happen */
i80321intc_setipl(saved_spl_level);
if(softint_pending & i80321intc_smask[current_ipl_level])
i80321intc_do_pending();
}
#ifdef DIAGNOSTIC
void
i80321_splassert_check(int wantipl, const char *func)
{
int oldipl = current_ipl_level;
if (oldipl < wantipl) {
splassert_fail(wantipl, oldipl, func);
/*
* If the splassert_ctl is set to not panic, raise the ipl
* in a feeble attempt to reduce damage.
*/
i80321intc_setipl(wantipl);
}
}
#endif