[BACK]Return to intr.c CVS log [TXT][DIR] Up to [local] / sys / arch / hppa / hppa

File: [local] / sys / arch / hppa / hppa / intr.c (download)

Revision 1.1, Tue Mar 4 16:05:46 2008 UTC (16 years, 2 months ago) by nbrk
Branch point for: MAIN

Initial revision

/*	$OpenBSD: intr.c,v 1.23 2007/05/27 16:36:07 kettenis Exp $	*/

/*
 * Copyright (c) 2002-2004 Michael Shalayeff
 * All rights reserved.
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR OR HIS RELATIVES 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 MIND, 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.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/evcount.h>
#include <sys/malloc.h>

#include <net/netisr.h>

#include <uvm/uvm_extern.h>	/* for uvmexp */

#include <machine/autoconf.h>
#include <machine/frame.h>
#include <machine/reg.h>

void softnet(void);
void softtty(void);

struct hppa_iv {
	char pri;
	char irq;
	char flags;
#define	HPPA_IV_CALL	0x01
#define	HPPA_IV_SOFT	0x02
	char pad;
	int pad2;
	int (*handler)(void *);
	void *arg;
	u_int bit;
	struct hppa_iv *share;
	struct hppa_iv *next;
	struct evcount *cnt;
} __packed;

register_t kpsw = PSL_Q | PSL_P | PSL_C | PSL_D;
volatile int cpu_inintr, cpl = IPL_NESTED;
u_long cpu_mask;
struct hppa_iv intr_store[8*2*CPU_NINTS] __attribute__ ((aligned(32))),
    *intr_more = intr_store, *intr_list;
struct hppa_iv intr_table[CPU_NINTS] __attribute__ ((aligned(32))) = {
	{ IPL_SOFTCLOCK, 0, HPPA_IV_SOFT, 0, 0, (int (*)(void *))&softclock },
	{ IPL_SOFTNET  , 0, HPPA_IV_SOFT, 0, 0, (int (*)(void *))&softnet },
	{ 0 }, { 0 },
	{ IPL_SOFTTTY  , 0, HPPA_IV_SOFT, 0, 0, (int (*)(void *))&softtty }
};
volatile u_long ipending, imask[NIPL] = {
	0,
	1 << (IPL_SOFTCLOCK - 1),
	1 << (IPL_SOFTNET - 1),
	0, 0,
	1 << (IPL_SOFTTTY - 1)
};

#ifdef DIAGNOSTIC
void
splassert_check(int wantipl, const char *func)
{
	if (cpl < wantipl) {
		splassert_fail(wantipl, cpl, func);
	}
}
#endif

void
softnet(void)
{
	int ni;

	/* use atomic "load & clear" */
	__asm __volatile(
	    "ldcws	0(%2), %0": "=&r" (ni), "+m" (netisr): "r" (&netisr));
#define DONETISR(m,c) if (ni & (1 << (m))) c()
#include <net/netisr_dispatch.h>
}

void
softtty(void)
{

}

void
cpu_intr_init(void)
{
	u_long mask = cpu_mask | SOFTINT_MASK;
	struct hppa_iv *iv;
	int level, bit;

	/* map the shared ints */
	while (intr_list) {
		iv = intr_list;
		intr_list = iv->next;
		bit = ffs(imask[(int)iv->pri]);
		if (!bit--) {
			bit = ffs(~mask);
			if (!bit--)
				panic("cpu_intr_init: out of bits");

			iv->next = NULL;
			iv->bit = 1 << bit;
			intr_table[bit] = *iv;
			mask |= (1 << bit);
			imask[(int)iv->pri] |= (1 << bit);
		} else {
			iv->bit = 1 << bit;
			iv->next = intr_table[bit].next;
			intr_table[bit].next = iv;
		}
	}

	for (level = 0; level < NIPL - 1; level++)
		imask[level + 1] |= imask[level];

	printf("biomask 0x%lx netmask 0x%lx ttymask 0x%lx\n",
	    imask[IPL_BIO], imask[IPL_NET], imask[IPL_TTY]);

	/* XXX the whacky trick is to prevent hardclock from happenning */
	mfctl(CR_ITMR, mask);
	mtctl(mask - 1, CR_ITMR);

	mtctl(cpu_mask, CR_EIEM);
	/* ack the unwanted interrupts */
	mfctl(CR_EIRR, mask);
	mtctl(mask & (1 << 31), CR_EIRR);

	/* in spl*() we trust, clock is started in initclocks() */
	kpsw |= PSL_I;
	ssm(PSL_I, mask);
}

int
cpu_intr_findirq(void)
{
	int irq;

	for (irq = 0; irq < CPU_NINTS; irq++)
		if (intr_table[irq].handler == NULL)
			return irq;

	return -1;
}

void *
cpu_intr_map(void *v, int pri, int irq, int (*handler)(void *), void *arg,
    const char *name)
{
	struct hppa_iv *iv, *pv = v, *ivb = pv->next;
	struct evcount *cnt;

	if (irq < 0 || irq >= CPU_NINTS)
		return (NULL);

	MALLOC(cnt, struct evcount *, sizeof *cnt, M_DEVBUF, M_NOWAIT);
	if (!cnt)
		return (NULL);

	iv = &ivb[irq];
	if (iv->handler) {
		if (!pv->share) {
			FREE(cnt, M_DEVBUF);
			return (NULL);
		} else {
			iv = pv->share;
			pv->share = iv->share;
			iv->share = ivb[irq].share;
			ivb[irq].share = iv;
		}
	}

	evcount_attach(cnt, name, NULL, &evcount_intr);
	iv->pri = pri;
	iv->irq = irq;
	iv->flags = 0;
	iv->handler = handler;
	iv->arg = arg;
	iv->cnt = cnt;
	iv->next = intr_list;
	intr_list = iv;

	return (iv);
}

void *
cpu_intr_establish(int pri, int irq, int (*handler)(void *), void *arg,
    const char *name)
{
	struct hppa_iv *iv, *ev;
	struct evcount *cnt;

	if (irq < 0 || irq >= CPU_NINTS || intr_table[irq].handler)
		return (NULL);

	MALLOC(cnt, struct evcount *, sizeof *cnt, M_DEVBUF, M_NOWAIT);
	if (!cnt)
		return (NULL);

	cpu_mask |= (1 << irq);
	imask[pri] |= (1 << irq);

	iv = &intr_table[irq];
	iv->pri = pri;
	iv->irq = irq;
	iv->bit = 1 << irq;
	iv->flags = 0;
	iv->handler = handler;
	iv->arg = arg;
	iv->cnt = cnt;
	iv->next = NULL;
	iv->share = NULL;

	if (pri == IPL_NESTED) {
		iv->flags = HPPA_IV_CALL;
		iv->next = intr_more;
		intr_more += 2 * CPU_NINTS;
		for (ev = iv->next + CPU_NINTS; ev < intr_more; ev++)
			ev->share = iv->share, iv->share = ev;
		FREE(cnt, M_DEVBUF);
		iv->cnt = NULL;
	} else
		evcount_attach(cnt, name, NULL, &evcount_intr);

	return (iv);
}

int
fls(u_int mask)
{
	int bit;

	bit = 32;
	if (!(mask & 0xffff0000)) {
		bit -= 16;
		mask <<= 16;
	}

	if (!(mask & 0xff000000)) {
		bit -= 8;
		mask <<= 8;
	}

	if (!(mask & 0xf0000000)) {
		bit -= 4;
		mask <<= 4;
	}

	if (!(mask & 0xc0000000)) {
		bit -= 2;
		mask <<= 2;
	}

	if (!(mask & 0x80000000)) {
		bit -= 1;
		mask <<= 1;
	}

	return mask? bit : 0;
}

void
cpu_intr(void *v)
{
	struct trapframe *frame = v;
	u_long mask;
	int s = cpl;

	if (cpu_inintr++)
		frame->tf_flags |= TFF_INTR;

	while ((mask = ipending & ~imask[s])) {
		int r, bit = fls(mask) - 1;
		struct hppa_iv *iv = &intr_table[bit];

		ipending &= ~(1L << bit);
		if (iv->flags & HPPA_IV_CALL)
			continue;

		uvmexp.intrs++;
		if (iv->flags & HPPA_IV_SOFT)
			uvmexp.softs++;

		cpl = iv->pri;
		mtctl(frame->tf_eiem, CR_EIEM);
		for (r = iv->flags & HPPA_IV_SOFT;
		    iv && iv->handler; iv = iv->next)
			/* no arg means pass the frame */
			if ((iv->handler)(iv->arg? iv->arg : v) == 1) {
				if (iv->cnt)
					iv->cnt->ec_count++;
				r |= 1;
			}
#if 0	/* XXX this does not work, lasi gives us double ints */
		if (!r) {
			cpl = 0;
			printf("stray interrupt %d\n", bit);
		}
#endif
		mtctl(0, CR_EIEM);
	}
	cpu_inintr--;
	cpl = s;
}