File: [local] / sys / arch / macppc / dev / openpic.c (download)
Revision 1.1.1.1 (vendor branch), Tue Mar 4 16:07:27 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: openpic.c,v 1.40 2007/05/29 18:10:43 miod Exp $ */
/*-
* Copyright (c) 1995 Per Fogelstrom
* Copyright (c) 1993, 1994 Charles M. Hannum.
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* William Jolitz and Don Ahn.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)isa.c 7.2 (Berkeley) 5/12/91
*/
#include <sys/param.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/systm.h>
#include <uvm/uvm.h>
#include <ddb/db_var.h>
#include <machine/atomic.h>
#include <machine/autoconf.h>
#include <machine/intr.h>
#include <machine/psl.h>
#include <machine/pio.h>
#include <machine/powerpc.h>
#include <macppc/dev/openpicreg.h>
#include <dev/ofw/openfirm.h>
#define ICU_LEN 128
#define LEGAL_IRQ(x) ((x >= 0) && (x < ICU_LEN))
int o_intrtype[ICU_LEN], o_intrmask[ICU_LEN], o_intrlevel[ICU_LEN];
struct intrhand *o_intrhand[ICU_LEN] = { 0 };
int o_hwirq[ICU_LEN], o_virq[ICU_LEN];
unsigned int imen_o = 0xffffffff;
int o_virq_max;
static int fakeintr(void *);
static char *intr_typename(int type);
static void intr_calculatemasks(void);
static __inline int cntlzw(int x);
static int mapirq(int irq);
int openpic_prog_button(void *arg);
void openpic_enable_irq_mask(int irq_mask);
#define HWIRQ_MAX 27
#define HWIRQ_MASK 0x0fffffff
static __inline u_int openpic_read(int);
static __inline void openpic_write(int, u_int);
void openpic_set_enable_irq(int, int);
void openpic_enable_irq(int);
void openpic_disable_irq(int);
void openpic_init(void);
void openpic_set_priority(int, int);
static __inline int openpic_read_irq(int);
static __inline void openpic_eoi(int);
struct openpic_softc {
struct device sc_dev;
};
int openpic_match(struct device *parent, void *cf, void *aux);
void openpic_attach(struct device *, struct device *, void *);
void openpic_do_pending_int(void);
void openpic_collect_preconf_intr(void);
void ext_intr_openpic(void);
struct cfattach openpic_ca = {
sizeof(struct openpic_softc),
openpic_match,
openpic_attach
};
struct cfdriver openpic_cd = {
NULL, "openpic", DV_DULL
};
int
openpic_match(struct device *parent, void *cf, void *aux)
{
char type[40];
int pirq;
struct confargs *ca = aux;
bzero (type, sizeof(type));
if (OF_getprop(ca->ca_node, "interrupt-parent", &pirq, sizeof(pirq))
== sizeof(pirq))
return 0; /* XXX */
if (strcmp(ca->ca_name, "interrupt-controller") != 0 &&
strcmp(ca->ca_name, "mpic") != 0)
return 0;
OF_getprop(ca->ca_node, "device_type", type, sizeof(type));
if (strcmp(type, "open-pic") != 0)
return 0;
if (ca->ca_nreg < 8)
return 0;
return 1;
}
typedef void (void_f) (void);
extern void_f *pending_int_f;
vaddr_t openpic_base;
void * openpic_intr_establish( void * lcv, int irq, int type, int level,
int (*ih_fun)(void *), void *ih_arg, char *name);
void openpic_intr_disestablish( void *lcp, void *arg);
void openpic_collect_preconf_intr(void);
int openpic_big_endian;
void
openpic_attach(struct device *parent, struct device *self, void *aux)
{
struct confargs *ca = aux;
extern intr_establish_t *intr_establish_func;
extern intr_disestablish_t *intr_disestablish_func;
extern intr_establish_t *mac_intr_establish_func;
extern intr_disestablish_t *mac_intr_disestablish_func;
u_int32_t reg;
reg = 0;
if (OF_getprop(ca->ca_node, "big-endian", ®, sizeof reg) == 0)
openpic_big_endian = 1;
openpic_base = (vaddr_t) mapiodev (ca->ca_baseaddr +
ca->ca_reg[0], 0x40000);
printf(": version 0x%x %s endian", openpic_read(OPENPIC_VENDOR_ID),
openpic_big_endian ? "big" : "little" );
openpic_init();
pending_int_f = openpic_do_pending_int;
intr_establish_func = openpic_intr_establish;
intr_disestablish_func = openpic_intr_disestablish;
mac_intr_establish_func = openpic_intr_establish;
mac_intr_disestablish_func = openpic_intr_disestablish;
install_extint(ext_intr_openpic);
#if 1
openpic_collect_preconf_intr();
#endif
#if 1
mac_intr_establish(parent, 0x37, IST_LEVEL,
IPL_HIGH, openpic_prog_button, (void *)0x37, "progbutton");
#endif
ppc_intr_enable(1);
printf("\n");
}
void
openpic_collect_preconf_intr()
{
int i;
for (i = 0; i < ppc_configed_intr_cnt; i++) {
#ifdef DEBUG
printf("\n\t%s irq %d level %d fun %x arg %x",
ppc_configed_intr[i].ih_what, ppc_configed_intr[i].ih_irq,
ppc_configed_intr[i].ih_level, ppc_configed_intr[i].ih_fun,
ppc_configed_intr[i].ih_arg);
#endif
openpic_intr_establish(NULL, ppc_configed_intr[i].ih_irq,
IST_LEVEL, ppc_configed_intr[i].ih_level,
ppc_configed_intr[i].ih_fun, ppc_configed_intr[i].ih_arg,
ppc_configed_intr[i].ih_what);
}
}
static int
fakeintr(void *arg)
{
return 0;
}
/*
* Register an interrupt handler.
*/
void *
openpic_intr_establish(void *lcv, int irq, int type, int level,
int (*ih_fun)(void *), void *ih_arg, char *name)
{
struct intrhand **p, *q, *ih;
static struct intrhand fakehand;
fakehand.ih_next = NULL;
fakehand.ih_fun = fakeintr;
#if 0
printf("mac_intr_establish, hI %d L %d ", irq, type);
#endif
irq = mapirq(irq);
#if 0
printf("vI %d ", irq);
#endif
/* no point in sleeping unless someone can free memory. */
ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
if (ih == NULL)
panic("intr_establish: can't malloc handler info");
if (!LEGAL_IRQ(irq) || type == IST_NONE)
panic("intr_establish: bogus irq or type");
switch (o_intrtype[irq]) {
case IST_NONE:
o_intrtype[irq] = type;
break;
case IST_EDGE:
case IST_LEVEL:
if (type == o_intrtype[irq])
break;
case IST_PULSE:
if (type != IST_NONE)
panic("intr_establish: can't share %s with %s",
intr_typename(o_intrtype[irq]),
intr_typename(type));
break;
}
/*
* Figure out where to put the handler.
* This is O(N^2), but we want to preserve the order, and N is
* generally small.
*/
for (p = &o_intrhand[irq]; (q = *p) != NULL; p = &q->ih_next)
;
/*
* Actually install a fake handler momentarily, since we might be doing
* this with interrupts enabled and DON'T WANt the real routine called
* until masking is set up.
*/
fakehand.ih_level = level;
*p = &fakehand;
intr_calculatemasks();
/*
* Poke the real handler in now.
*/
ih->ih_fun = ih_fun;
ih->ih_arg = ih_arg;
ih->ih_next = NULL;
ih->ih_level = level;
ih->ih_irq = irq;
evcount_attach(&ih->ih_count, name, (void *)&o_hwirq[irq],
&evcount_intr);
*p = ih;
return (ih);
}
/*
* Deregister an interrupt handler.
*/
void
openpic_intr_disestablish(void *lcp, void *arg)
{
struct intrhand *ih = arg;
int irq = ih->ih_irq;
struct intrhand **p, *q;
if (!LEGAL_IRQ(irq))
panic("intr_disestablish: bogus irq");
/*
* Remove the handler from the chain.
* This is O(n^2), too.
*/
for (p = &o_intrhand[irq]; (q = *p) != NULL && q != ih; p = &q->ih_next)
;
if (q)
*p = q->ih_next;
else
panic("intr_disestablish: handler not registered");
evcount_detach(&ih->ih_count);
free((void *)ih, M_DEVBUF);
intr_calculatemasks();
if (o_intrhand[irq] == NULL)
o_intrtype[irq] = IST_NONE;
}
static char *
intr_typename(int type)
{
switch (type) {
case IST_NONE:
return ("none");
case IST_PULSE:
return ("pulsed");
case IST_EDGE:
return ("edge-triggered");
case IST_LEVEL:
return ("level-triggered");
default:
panic("intr_typename: invalid type %d", type);
#if 1 /* XXX */
return ("unknown");
#endif
}
}
/*
* Recalculate the interrupt masks from scratch.
* We could code special registry and deregistry versions of this function that
* would be faster, but the code would be nastier, and we don't expect this to
* happen very much anyway.
*/
static void
intr_calculatemasks()
{
int irq, level;
struct intrhand *q;
/* First, figure out which levels each IRQ uses. */
for (irq = 0; irq < ICU_LEN; irq++) {
register int levels = 0;
for (q = o_intrhand[irq]; q; q = q->ih_next)
levels |= 1 << q->ih_level;
o_intrlevel[irq] = levels;
}
/* Then figure out which IRQs use each level. */
for (level = IPL_NONE; level < IPL_NUM; level++) {
register int irqs = 0;
for (irq = 0; irq < ICU_LEN; irq++)
if (o_intrlevel[irq] & (1 << level))
irqs |= 1 << irq;
imask[level] = irqs | SINT_MASK;
}
/*
* There are tty, network and disk drivers that use free() at interrupt
* time, so vm > (tty | net | bio).
*
* Enforce a hierarchy that gives slow devices a better chance at not
* dropping data.
*/
imask[IPL_NET] |= imask[IPL_BIO];
imask[IPL_TTY] |= imask[IPL_NET];
imask[IPL_VM] |= imask[IPL_TTY];
imask[IPL_CLOCK] |= imask[IPL_VM] | SPL_CLOCK;
/*
* These are pseudo-levels.
*/
imask[IPL_NONE] = 0x00000000;
imask[IPL_HIGH] = 0xffffffff;
/* And eventually calculate the complete masks. */
for (irq = 0; irq < ICU_LEN; irq++) {
register int irqs = 1 << irq;
for (q = o_intrhand[irq]; q; q = q->ih_next)
irqs |= imask[q->ih_level];
o_intrmask[irq] = irqs | SINT_MASK;
}
/* Lastly, determine which IRQs are actually in use. */
{
register int irqs = 0;
for (irq = 0; irq < ICU_LEN; irq++) {
if (o_intrhand[irq]) {
irqs |= 1 << irq;
openpic_enable_irq(o_hwirq[irq]);
} else {
openpic_disable_irq(o_hwirq[irq]);
}
}
imen_o = ~irqs;
}
}
/*
* Map 64 irqs into 32 (bits).
*/
static int
mapirq(int irq)
{
int v;
/* irq in table already? */
if (o_virq[irq] != 0)
return o_virq[irq];
if (irq < 0 || irq >= ICU_LEN)
panic("invalid irq %d", irq);
o_virq_max++;
v = o_virq_max;
if (v > HWIRQ_MAX)
panic("virq overflow");
o_hwirq[v] = irq;
o_virq[irq] = v;
#if 0
printf("\nmapirq %x to %x\n", irq, v);
#endif
return v;
}
/*
* Count leading zeros.
*/
static __inline int
cntlzw(int x)
{
int a;
__asm __volatile ("cntlzw %0,%1" : "=r"(a) : "r"(x));
return a;
}
void
openpic_do_pending_int()
{
struct cpu_info *ci = curcpu();
struct intrhand *ih;
int irq;
int pcpl;
int hwpend;
int s;
if (ci->ci_iactive)
return;
ci->ci_iactive = 1;
pcpl = splhigh(); /* Turn off all */
s = ppc_intr_disable();
hwpend = ci->ci_ipending & ~pcpl; /* Do now unmasked pendings */
imen_o &= ~hwpend;
openpic_enable_irq_mask(~imen_o);
hwpend &= HWIRQ_MASK;
while (hwpend) {
irq = 31 - cntlzw(hwpend);
hwpend &= ~(1L << irq);
ih = o_intrhand[irq];
while(ih) {
ppc_intr_enable(1);
if ((*ih->ih_fun)(ih->ih_arg))
ih->ih_count.ec_count++;
(void)ppc_intr_disable();
ih = ih->ih_next;
}
}
/*out32rb(INT_ENABLE_REG, ~imen_o);*/
do {
if((ci->ci_ipending & SINT_CLOCK) & ~pcpl) {
ci->ci_ipending &= ~SINT_CLOCK;
softclock();
}
if((ci->ci_ipending & SINT_NET) & ~pcpl) {
extern int netisr;
int pisr;
ci->ci_ipending &= ~SINT_NET;
while ((pisr = netisr) != 0) {
atomic_clearbits_int(&netisr, pisr);
softnet(pisr);
}
}
if((ci->ci_ipending & SINT_TTY) & ~pcpl) {
ci->ci_ipending &= ~SINT_TTY;
softtty();
}
} while ((ci->ci_ipending & SINT_MASK) & ~pcpl);
ci->ci_ipending &= pcpl;
ci->ci_cpl = pcpl; /* Don't use splx... we are here already! */
ppc_intr_enable(s);
ci->ci_iactive = 0;
}
u_int
openpic_read(int reg)
{
char *addr = (void *)(openpic_base + reg);
if (openpic_big_endian)
return in32(addr);
else
return in32rb(addr);
}
void
openpic_write(int reg, u_int val)
{
char *addr = (void *)(openpic_base + reg);
if (openpic_big_endian)
out32(addr, val);
else
out32rb(addr, val);
}
void
openpic_enable_irq_mask(int irq_mask)
{
int irq;
for ( irq = 0; irq <= o_virq_max; irq++) {
if (irq_mask & (1 << irq))
openpic_enable_irq(o_hwirq[irq]);
else
openpic_disable_irq(o_hwirq[irq]);
}
}
void
openpic_set_enable_irq(int irq, int type)
{
u_int x;
x = openpic_read(OPENPIC_SRC_VECTOR(irq));
x &= ~(OPENPIC_IMASK|OPENPIC_SENSE_LEVEL|OPENPIC_SENSE_EDGE);
if (type == IST_LEVEL)
x |= OPENPIC_SENSE_LEVEL;
else
x |= OPENPIC_SENSE_EDGE;
openpic_write(OPENPIC_SRC_VECTOR(irq), x);
}
void
openpic_enable_irq(int irq)
{
u_int x;
x = openpic_read(OPENPIC_SRC_VECTOR(irq));
x &= ~(OPENPIC_IMASK|OPENPIC_SENSE_LEVEL|OPENPIC_SENSE_EDGE);
if (o_intrtype[o_virq[irq]] == IST_LEVEL)
x |= OPENPIC_SENSE_LEVEL;
else
x |= OPENPIC_SENSE_EDGE;
openpic_write(OPENPIC_SRC_VECTOR(irq), x);
}
void
openpic_disable_irq(int irq)
{
u_int x;
x = openpic_read(OPENPIC_SRC_VECTOR(irq));
x |= OPENPIC_IMASK;
openpic_write(OPENPIC_SRC_VECTOR(irq), x);
}
void
openpic_set_priority(int cpu, int pri)
{
u_int x;
x = openpic_read(OPENPIC_CPU_PRIORITY(cpu));
x &= ~OPENPIC_CPU_PRIORITY_MASK;
x |= pri;
openpic_write(OPENPIC_CPU_PRIORITY(cpu), x);
}
int
openpic_read_irq(int cpu)
{
return openpic_read(OPENPIC_IACK(cpu)) & OPENPIC_VECTOR_MASK;
}
void
openpic_eoi(int cpu)
{
openpic_write(OPENPIC_EOI(cpu), 0);
openpic_read(OPENPIC_EOI(cpu));
}
void
ext_intr_openpic()
{
struct cpu_info *ci = curcpu();
int irq, realirq;
int r_imen;
int pcpl, ocpl;
struct intrhand *ih;
pcpl = ci->ci_cpl;
realirq = openpic_read_irq(0);
while (realirq != 255) {
irq = o_virq[realirq];
/* XXX check range */
r_imen = 1 << irq;
if ((pcpl & r_imen) != 0) {
/* Masked! Mark this as pending. */
ci->ci_ipending |= r_imen;
openpic_disable_irq(realirq);
openpic_eoi(0);
} else {
openpic_disable_irq(realirq);
openpic_eoi(0);
ocpl = splraise(o_intrmask[irq]);
ih = o_intrhand[irq];
while (ih) {
ppc_intr_enable(1);
if ((*ih->ih_fun)(ih->ih_arg))
ih->ih_count.ec_count++;
(void)ppc_intr_disable();
ih = ih->ih_next;
}
uvmexp.intrs++;
__asm__ volatile("":::"memory"); /* don't reorder.... */
ci->ci_cpl = ocpl;
__asm__ volatile("":::"memory"); /* don't reorder.... */
openpic_enable_irq(realirq);
}
realirq = openpic_read_irq(0);
}
ppc_intr_enable(1);
splx(pcpl); /* Process pendings. */
}
void
openpic_init()
{
int irq;
u_int x;
/* disable all interrupts */
for (irq = 0; irq < 255; irq++)
openpic_write(OPENPIC_SRC_VECTOR(irq), OPENPIC_IMASK);
openpic_set_priority(0, 15);
/* we don't need 8259 pass through mode */
x = openpic_read(OPENPIC_CONFIG);
x |= OPENPIC_CONFIG_8259_PASSTHRU_DISABLE;
openpic_write(OPENPIC_CONFIG, x);
/* send all interrupts to cpu 0 */
for (irq = 0; irq < ICU_LEN; irq++)
openpic_write(OPENPIC_IDEST(irq), 1 << 0);
for (irq = 0; irq < ICU_LEN; irq++) {
x = irq;
x |= OPENPIC_IMASK;
x |= OPENPIC_POLARITY_POSITIVE;
x |= OPENPIC_SENSE_LEVEL;
x |= 8 << OPENPIC_PRIORITY_SHIFT;
openpic_write(OPENPIC_SRC_VECTOR(irq), x);
}
/* XXX set spurious intr vector */
openpic_set_priority(0, 0);
/* clear all pending interrunts */
for (irq = 0; irq < ICU_LEN; irq++) {
openpic_read_irq(0);
openpic_eoi(0);
}
for (irq = 0; irq < ICU_LEN; irq++)
openpic_disable_irq(irq);
install_extint(ext_intr_openpic);
}
/*
* programmer_button function to fix args to Debugger.
* deal with any enables/disables, if necessary.
*/
int
openpic_prog_button (void *arg)
{
#ifdef DDB
if (db_console)
Debugger();
#else
printf("programmer button pressed, debugger not available\n");
#endif
return 1;
}