617 lines
16 KiB
C
617 lines
16 KiB
C
/* $NetBSD: sysfpga.c,v 1.18 2003/07/15 01:37:39 lukem Exp $ */
|
|
|
|
/*
|
|
* Copyright 2002 Wasabi Systems, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Written by Steve C. Woodford for Wasabi Systems, Inc.
|
|
*
|
|
* 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. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed for the NetBSD Project by
|
|
* Wasabi Systems, Inc.
|
|
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
|
|
* or promote products derived from this software without specific prior
|
|
* written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
|
|
* 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.
|
|
*/
|
|
|
|
/* Cayman's System FPGA Chip */
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: sysfpga.c,v 1.18 2003/07/15 01:37:39 lukem Exp $");
|
|
|
|
#include "sh5pci.h"
|
|
#include "superio.h"
|
|
#include "sm_sysfpga.h"
|
|
#include "opt_ddb.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/callout.h>
|
|
#include <sys/device.h>
|
|
#include <sys/kernel.h>
|
|
|
|
#include <machine/cpu.h>
|
|
#include <machine/bus.h>
|
|
#include <machine/intr.h>
|
|
|
|
#include <sh5/dev/femivar.h>
|
|
#include <sh5/dev/intcreg.h>
|
|
#include <evbsh5/dev/sysfpgareg.h>
|
|
#include <evbsh5/dev/sysfpgavar.h>
|
|
|
|
struct sysfpga_ihandler {
|
|
int (*ih_func)(void *);
|
|
void *ih_arg;
|
|
int ih_level;
|
|
int ih_group;
|
|
int ih_inum;
|
|
};
|
|
|
|
struct sysfpga_softc {
|
|
struct device sc_dev;
|
|
bus_space_tag_t sc_bust;
|
|
bus_space_handle_t sc_bush;
|
|
struct callout sc_ledco;
|
|
u_int8_t sc_intmr[SYSFPGA_NGROUPS];
|
|
void *sc_ih[SYSFPGA_NGROUPS];
|
|
#if (NSUPERIO > 0) || defined(SM_SYSFPGA)
|
|
struct sysfpga_ihandler sc_ih_irl1[SYSFPGA_IRL1_NINTR];
|
|
#endif
|
|
#if NSH5PCI > 0
|
|
struct sysfpga_ihandler sc_ih_irl2[SYSFPGA_IRL2_NINTR];
|
|
struct sysfpga_ihandler sc_ih_irl3[SYSFPGA_IRL3_NINTR];
|
|
#endif
|
|
};
|
|
|
|
static int sysfpgamatch(struct device *, struct cfdata *, void *);
|
|
static void sysfpgaattach(struct device *, struct device *, void *);
|
|
static int sysfpgaprint(void *, const char *);
|
|
|
|
CFATTACH_DECL(sysfpga, sizeof(struct sysfpga_softc),
|
|
sysfpgamatch, sysfpgaattach, NULL, NULL);
|
|
extern struct cfdriver sysfpga_cd;
|
|
|
|
/*
|
|
* Devices which hang off the System FPGA
|
|
*/
|
|
struct sysfpga_device {
|
|
const char *sd_name;
|
|
bus_addr_t sd_offset;
|
|
};
|
|
|
|
static struct sysfpga_device sysfpga_devices[] = {
|
|
{"superio", SYSFPGA_OFFSET_SUPERIO},
|
|
{"sm", SYSFPGA_OFFSET_LAN},
|
|
{"alphaled", SYSFPGA_OFFSET_ALPHALED},
|
|
{NULL, 0}
|
|
};
|
|
|
|
|
|
#define sysfpga_reg_read(s,r) \
|
|
bus_space_read_4((s)->sc_bust, (s)->sc_bush, (r))
|
|
#define sysfpga_reg_write(s,r,v) \
|
|
bus_space_write_4((s)->sc_bust, (s)->sc_bush, (r), (v))
|
|
|
|
/*
|
|
* Flash the Discrete LED twice per second, with 10% duty-cycle for ON
|
|
*/
|
|
#define TWINKLE_PERIOD (hz / 2)
|
|
#define TWINKLE_DUTY 10
|
|
static void sysfpga_twinkle_led(void *);
|
|
|
|
#if NSUPERIO > 0
|
|
static int sysfpga_intr_handler_irl1(void *);
|
|
#endif
|
|
#if NSH5PCI > 0
|
|
static int sysfpga_intr_handler_irl2(void *);
|
|
static int sysfpga_intr_handler_irl3(void *);
|
|
#endif
|
|
|
|
static int sysfpga_intr_dispatch(const struct sysfpga_ihandler *, int, int);
|
|
|
|
|
|
static const char *sysfpga_cpuclksel[] = {
|
|
"400/200/100MHz", "400/200/66MHz", "400/200/50MHz", "<invalid>"
|
|
};
|
|
|
|
#if NSUPERIO > 0
|
|
static const char *sysfpga_irl1_intr_names[SYSFPGA_IRL1_NINTR] = {
|
|
"dcd0", "lan", "keyboard", "uart2", "uart1", "lpt", "mouse", "ide"
|
|
};
|
|
static struct evcnt sysfpga_irl1_intr_events[SYSFPGA_IRL1_NINTR];
|
|
#endif
|
|
#if NSH5PCI > 0
|
|
static struct evcnt sysfpga_irl2_intr_events;
|
|
static struct evcnt sysfpga_irl3_intr_events;
|
|
#endif
|
|
|
|
static struct sysfpga_softc *sysfpga_sc;
|
|
|
|
|
|
/*ARGSUSED*/
|
|
static int
|
|
sysfpgamatch(struct device *parent, struct cfdata *cf, void *args)
|
|
{
|
|
|
|
if (sysfpga_sc)
|
|
return (0);
|
|
|
|
return (1);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static void
|
|
sysfpgaattach(struct device *parent, struct device *self, void *args)
|
|
{
|
|
struct sysfpga_softc *sc = (struct sysfpga_softc *)self;
|
|
struct femi_attach_args *fa = args;
|
|
struct sysfpga_attach_args sa;
|
|
u_int32_t reg;
|
|
int i;
|
|
#if (NSUPERIO > 0) || defined(SM_SYSFPGA) || (NSH5PCI > 0)
|
|
struct evcnt *ev;
|
|
static const char sysfpga_intr[] = "sysfpga intr";
|
|
#endif
|
|
|
|
sysfpga_sc = sc;
|
|
|
|
sc->sc_bust = fa->fa_bust;
|
|
|
|
bus_space_map(sc->sc_bust, fa->fa_offset + SYSFPGA_OFFSET_REGS,
|
|
SYSFPGA_REG_SZ, 0, &sc->sc_bush);
|
|
|
|
reg = sysfpga_reg_read(sc, SYSFPGA_REG_DATE);
|
|
printf(
|
|
": Cayman System FPGA, Revision: %d - %02x/%02x/%02x (yy/mm/dd)\n",
|
|
SYSFPGA_DATE_REV(reg), SYSFPGA_DATE_YEAR(reg),
|
|
SYSFPGA_DATE_MONTH(reg), SYSFPGA_DATE_DATE(reg));
|
|
|
|
reg = sysfpga_reg_read(sc, SYSFPGA_REG_BDMR);
|
|
printf("%s: CPUCLKSEL: %s, CPU Clock Mode: %d\n", sc->sc_dev.dv_xname,
|
|
sysfpga_cpuclksel[SYSFPGA_BDMR_CPUCLKSEL(reg)],
|
|
SYSFPGA_CPUMR_CLKMODE(sysfpga_reg_read(sc, SYSFPGA_REG_CPUMR)));
|
|
|
|
#if (NSUPERIO > 0) || defined(SM_SYSFPGA)
|
|
memset(sc->sc_ih_irl1, 0, sizeof(sc->sc_ih_irl1));
|
|
#endif
|
|
#if NSH5PCI > 0
|
|
memset(sc->sc_ih_irl1, 0, sizeof(sc->sc_ih_irl2));
|
|
memset(sc->sc_ih_irl1, 0, sizeof(sc->sc_ih_irl3));
|
|
#endif
|
|
|
|
for (i = 0; i < SYSFPGA_NGROUPS; i++) {
|
|
sysfpga_reg_write(sc, SYSFPGA_REG_INTMR(i), 0);
|
|
sc->sc_intmr[i] = 0;
|
|
}
|
|
|
|
#if (NSUPERIO > 0) || defined(SM_SYSFPGA)
|
|
/*
|
|
* Hook interrupts for IRL1 devices
|
|
*/
|
|
sc->sc_ih[SYSFPGA_IGROUP_IRL1] =
|
|
sh5_intr_establish(INTC_INTEVT_IRL1, IST_LEVEL, IPL_SUPERIO,
|
|
sysfpga_intr_handler_irl1, sc);
|
|
|
|
if (sc->sc_ih[SYSFPGA_IGROUP_IRL1] == NULL)
|
|
panic("sysfpga: failed to register irl1 isr");
|
|
|
|
ev = sh5_intr_evcnt(sc->sc_ih[SYSFPGA_IGROUP_IRL1]);
|
|
for (i = 0; i < SYSFPGA_IRL1_NINTR; i++) {
|
|
evcnt_attach_dynamic(&sysfpga_irl1_intr_events[i],
|
|
EVCNT_TYPE_INTR, ev,
|
|
(i >= SYSFPGA_IRL1_INUM_KBD) ? "isa intr" : sysfpga_intr,
|
|
sysfpga_irl1_intr_names[i]);
|
|
}
|
|
#endif
|
|
|
|
#if NSH5PCI > 0
|
|
/*
|
|
* Hook interrupts from the PCI1 and PCI2 pins
|
|
*/
|
|
sc->sc_ih[SYSFPGA_IGROUP_IRL2] =
|
|
sh5_intr_establish(INTC_INTEVT_IRL2, IST_LEVEL, IPL_SH5PCI,
|
|
sysfpga_intr_handler_irl2, sc);
|
|
|
|
sc->sc_ih[SYSFPGA_IGROUP_IRL3] =
|
|
sh5_intr_establish(INTC_INTEVT_IRL3, IST_LEVEL, IPL_SH5PCI,
|
|
sysfpga_intr_handler_irl3, sc);
|
|
|
|
if (sc->sc_ih[SYSFPGA_IGROUP_IRL2] == NULL ||
|
|
sc->sc_ih[SYSFPGA_IGROUP_IRL3] == NULL)
|
|
panic("sysfpga: failed to register pci isr");
|
|
|
|
ev = sh5_intr_evcnt(sc->sc_ih[SYSFPGA_IGROUP_IRL2]);
|
|
evcnt_attach_dynamic(&sysfpga_irl2_intr_events,
|
|
EVCNT_TYPE_INTR, ev, sysfpga_intr, "pci1");
|
|
|
|
ev = sh5_intr_evcnt(sc->sc_ih[SYSFPGA_IGROUP_IRL3]);
|
|
evcnt_attach_dynamic(&sysfpga_irl3_intr_events,
|
|
EVCNT_TYPE_INTR, ev, sysfpga_intr, "pci2");
|
|
#endif
|
|
|
|
#ifdef DDB
|
|
sysfpga_reg_write(sc, SYSFPGA_REG_NMIMR, 1);
|
|
#endif
|
|
|
|
/*
|
|
* Arrange to twinkle the "Discrete LED" periodically
|
|
* as a crude "heartbeat" indication.
|
|
*/
|
|
callout_init(&sc->sc_ledco);
|
|
sysfpga_twinkle_led(sc);
|
|
|
|
/*
|
|
* Attach configured children
|
|
*/
|
|
sa._sa_base = fa->fa_offset;
|
|
for (i = 0; sysfpga_devices[i].sd_name != NULL; i++) {
|
|
sa.sa_name = sysfpga_devices[i].sd_name;
|
|
sa.sa_bust = fa->fa_bust;
|
|
sa.sa_dmat = fa->fa_dmat;
|
|
sa.sa_offset = sysfpga_devices[i].sd_offset + sa._sa_base;
|
|
|
|
(void) config_found(self, &sa, sysfpgaprint);
|
|
}
|
|
}
|
|
|
|
static int
|
|
sysfpgaprint(void *arg, const char *cp)
|
|
{
|
|
struct sysfpga_attach_args *sa = arg;
|
|
|
|
if (cp)
|
|
aprint_normal("%s at %s", sa->sa_name, cp);
|
|
|
|
aprint_normal(" offset 0x%x", sa->sa_offset - sa->_sa_base);
|
|
|
|
return (UNCONF);
|
|
}
|
|
|
|
static void
|
|
sysfpga_twinkle_led(void *arg)
|
|
{
|
|
struct sysfpga_softc *sc = arg;
|
|
u_int32_t ledcr;
|
|
int next;
|
|
|
|
/*
|
|
* Flip the state of the Cayman's discrete LED
|
|
*/
|
|
ledcr = sysfpga_reg_read(sc, SYSFPGA_REG_LEDCR);
|
|
ledcr ^= SYSFPGA_LEDCR_SLED_MASK;
|
|
sysfpga_reg_write(sc, SYSFPGA_REG_LEDCR, ledcr);
|
|
ledcr &= SYSFPGA_LEDCR_SLED_MASK;
|
|
|
|
next = (ledcr == SYSFPGA_LEDCR_SLED_ON) ?
|
|
TWINKLE_PERIOD / (100 / TWINKLE_DUTY) :
|
|
TWINKLE_PERIOD - (TWINKLE_PERIOD / (100 / TWINKLE_DUTY));
|
|
|
|
callout_reset(&sc->sc_ledco, next, sysfpga_twinkle_led, sc);
|
|
}
|
|
|
|
#if (NSUPERIO > 0) || defined(SM_SYSFPGA)
|
|
static int
|
|
sysfpga_intr_handler_irl1(void *arg)
|
|
{
|
|
struct sysfpga_softc *sc = arg;
|
|
struct sysfpga_ihandler *ih;
|
|
struct evcnt *events = sysfpga_irl1_intr_events;
|
|
u_int8_t intsr, intmr;
|
|
int sr_reg, h = 0;
|
|
|
|
ih = sc->sc_ih_irl1;
|
|
sr_reg = SYSFPGA_REG_INTSR(SYSFPGA_IGROUP_IRL1);
|
|
intmr = sc->sc_intmr[SYSFPGA_IGROUP_IRL1];
|
|
|
|
for (intsr = sysfpga_reg_read(sc, sr_reg);
|
|
(intsr &= intmr) != 0;
|
|
intsr = sysfpga_reg_read(sc, sr_reg)) {
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL1_INUM_UART1)) {
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SUPERIO,
|
|
SYSFPGA_IRL1_INUM_UART1);
|
|
events[SYSFPGA_IRL1_INUM_UART1].ev_count++;
|
|
}
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL1_INUM_UART2)) {
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SUPERIO,
|
|
SYSFPGA_IRL1_INUM_UART2);
|
|
events[SYSFPGA_IRL1_INUM_UART2].ev_count++;
|
|
}
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL1_INUM_LAN)) {
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SUPERIO,
|
|
SYSFPGA_IRL1_INUM_LAN);
|
|
events[SYSFPGA_IRL1_INUM_LAN].ev_count++;
|
|
}
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL1_INUM_MOUSE)) {
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SUPERIO,
|
|
SYSFPGA_IRL1_INUM_MOUSE);
|
|
events[SYSFPGA_IRL1_INUM_MOUSE].ev_count++;
|
|
}
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL1_INUM_KBD)) {
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SUPERIO,
|
|
SYSFPGA_IRL1_INUM_KBD);
|
|
events[SYSFPGA_IRL1_INUM_KBD].ev_count++;
|
|
}
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL1_INUM_IDE)) {
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SUPERIO,
|
|
SYSFPGA_IRL1_INUM_IDE);
|
|
events[SYSFPGA_IRL1_INUM_IDE].ev_count++;
|
|
}
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL1_INUM_LPT)) {
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SUPERIO,
|
|
SYSFPGA_IRL1_INUM_LPT);
|
|
events[SYSFPGA_IRL1_INUM_LPT].ev_count++;
|
|
}
|
|
|
|
if (h == 0)
|
|
panic("sysfpga: unclaimed IRL1 interrupt: 0x%02x",
|
|
intsr);
|
|
}
|
|
|
|
return (h);
|
|
}
|
|
#endif
|
|
|
|
#if NSH5PCI > 0
|
|
static int
|
|
sysfpga_intr_handler_irl2(void *arg)
|
|
{
|
|
struct sysfpga_softc *sc = arg;
|
|
struct sysfpga_ihandler *ih;
|
|
u_int8_t intsr, intmr;
|
|
int sr_reg, h = 0;
|
|
|
|
ih = sc->sc_ih_irl2;
|
|
sr_reg = SYSFPGA_REG_INTSR(SYSFPGA_IGROUP_IRL2);
|
|
intmr = sc->sc_intmr[SYSFPGA_IGROUP_IRL2];
|
|
|
|
for (intsr = sysfpga_reg_read(sc, sr_reg);
|
|
(intsr &= intmr) != 0;
|
|
intsr = sysfpga_reg_read(sc, sr_reg)) {
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL2_INTA))
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SH5PCI,
|
|
SYSFPGA_IRL2_INTA);
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL2_INTB))
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SH5PCI,
|
|
SYSFPGA_IRL2_INTB);
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL2_INTC))
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SH5PCI,
|
|
SYSFPGA_IRL2_INTC);
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL2_INTD))
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SH5PCI,
|
|
SYSFPGA_IRL2_INTD);
|
|
|
|
if (h == 0)
|
|
panic("sysfpga: unclaimed IRL2 interrupt: 0x%02x",
|
|
intsr);
|
|
|
|
sysfpga_irl2_intr_events.ev_count++;
|
|
}
|
|
|
|
return (h);
|
|
}
|
|
|
|
static int
|
|
sysfpga_intr_handler_irl3(void *arg)
|
|
{
|
|
struct sysfpga_softc *sc = arg;
|
|
struct sysfpga_ihandler *ih;
|
|
u_int8_t intsr, intmr;
|
|
int sr_reg, h = 0;
|
|
|
|
ih = sc->sc_ih_irl3;
|
|
sr_reg = SYSFPGA_REG_INTSR(SYSFPGA_IGROUP_IRL3);
|
|
intmr = sc->sc_intmr[SYSFPGA_IGROUP_IRL3];
|
|
|
|
for (intsr = sysfpga_reg_read(sc, sr_reg);
|
|
(intsr &= intmr) != 0;
|
|
intsr = sysfpga_reg_read(sc, sr_reg)) {
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL3_INTA))
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SH5PCI,
|
|
SYSFPGA_IRL3_INTA);
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL3_INTB))
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SH5PCI,
|
|
SYSFPGA_IRL3_INTB);
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL3_INTC))
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SH5PCI,
|
|
SYSFPGA_IRL3_INTC);
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL3_INTD))
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SH5PCI,
|
|
SYSFPGA_IRL3_INTD);
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL3_FAL))
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SH5PCI,
|
|
SYSFPGA_IRL3_FAL);
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL3_DEG))
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SH5PCI,
|
|
SYSFPGA_IRL3_DEG);
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL3_INTP))
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SH5PCI,
|
|
SYSFPGA_IRL3_INTP);
|
|
|
|
if (intsr & (1 << SYSFPGA_IRL3_INTS))
|
|
h |= sysfpga_intr_dispatch(ih, IPL_SH5PCI,
|
|
SYSFPGA_IRL3_INTS);
|
|
|
|
if (h == 0)
|
|
panic("sysfpga: unclaimed IRL3 interrupt: 0x%02x",
|
|
intsr);
|
|
|
|
sysfpga_irl3_intr_events.ev_count++;
|
|
}
|
|
|
|
return (h);
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
sysfpga_intr_dispatch(const struct sysfpga_ihandler *ih, int level, int hnum)
|
|
{
|
|
int h, s;
|
|
|
|
ih += hnum;
|
|
|
|
#ifdef DEBUG
|
|
if (ih->ih_func == NULL)
|
|
panic("sysfpga_intr_dispatch: NULL handler for isr %d", hnum);
|
|
#endif
|
|
|
|
/*
|
|
* This splraise() is fine since sysfpga's interrupt handler
|
|
* runs at a lower ipl than anything the child drivers could request.
|
|
*/
|
|
s = (ih->ih_level > level) ? splraise(ih->ih_level) : -1;
|
|
|
|
h = (*ih->ih_func)(ih->ih_arg);
|
|
|
|
if (s >= 0)
|
|
splx(s);
|
|
|
|
return (h);
|
|
}
|
|
|
|
struct evcnt *
|
|
sysfpga_intr_evcnt(int group, int inum)
|
|
{
|
|
struct evcnt *ev = NULL;
|
|
|
|
KDASSERT(group < SYSFPGA_NGROUPS);
|
|
KDASSERT(sysfpga_sc->sc_ih[group] != NULL);
|
|
|
|
switch (group) {
|
|
case SYSFPGA_IGROUP_IRL1:
|
|
KDASSERT(inum >= 0 && inum < SYSFPGA_IRL1_NINTR);
|
|
ev = &sysfpga_irl1_intr_events[inum];
|
|
break;
|
|
|
|
case SYSFPGA_IGROUP_IRL2:
|
|
ev = &sysfpga_irl2_intr_events;
|
|
break;
|
|
|
|
case SYSFPGA_IGROUP_IRL3:
|
|
ev = &sysfpga_irl3_intr_events;
|
|
break;
|
|
}
|
|
|
|
return (ev);
|
|
}
|
|
|
|
void *
|
|
sysfpga_intr_establish(int group, int level, int inum,
|
|
int (*func)(void *), void *arg)
|
|
{
|
|
struct sysfpga_softc *sc = sysfpga_sc;
|
|
struct sysfpga_ihandler *ih;
|
|
int s;
|
|
|
|
switch (group) {
|
|
#if (NSUPERIO > 0) || defined(SM_SYSFPGA)
|
|
case SYSFPGA_IGROUP_IRL1:
|
|
KDASSERT(inum < SYSFPGA_IRL1_NINTR);
|
|
KDASSERT(level >= IPL_SUPERIO);
|
|
ih = sc->sc_ih_irl1;
|
|
break;
|
|
#endif
|
|
#if NSH5PCI > 0
|
|
case SYSFPGA_IGROUP_IRL2:
|
|
KDASSERT(inum < SYSFPGA_IRL2_NINTR);
|
|
KDASSERT(level >= IPL_SH5PCI);
|
|
ih = sc->sc_ih_irl2;
|
|
break;
|
|
|
|
case SYSFPGA_IGROUP_IRL3:
|
|
KDASSERT(inum < SYSFPGA_IRL3_NINTR);
|
|
KDASSERT(level >= IPL_SH5PCI);
|
|
ih = sc->sc_ih_irl3;
|
|
break;
|
|
#endif
|
|
default:
|
|
return (NULL);
|
|
}
|
|
|
|
ih += inum;
|
|
|
|
KDASSERT(ih->ih_func == NULL);
|
|
|
|
ih->ih_level = level;
|
|
ih->ih_group = group;
|
|
ih->ih_inum = inum;
|
|
ih->ih_arg = arg;
|
|
ih->ih_func = func;
|
|
|
|
s = splhigh();
|
|
sc->sc_intmr[group] |= 1 << inum;
|
|
sysfpga_reg_write(sc, SYSFPGA_REG_INTMR(group), sc->sc_intmr[group]);
|
|
splx(s);
|
|
|
|
return ((void *)ih);
|
|
}
|
|
|
|
void
|
|
sysfpga_intr_disestablish(void *cookie)
|
|
{
|
|
struct sysfpga_softc *sc = sysfpga_sc;
|
|
struct sysfpga_ihandler *ih = cookie;
|
|
int s;
|
|
|
|
s = splhigh();
|
|
sc->sc_intmr[ih->ih_group] &= ~(1 << ih->ih_inum);
|
|
sysfpga_reg_write(sc, SYSFPGA_REG_INTMR(ih->ih_group),
|
|
sc->sc_intmr[ih->ih_group]);
|
|
splx(s);
|
|
|
|
ih->ih_func = NULL;
|
|
}
|
|
|
|
void
|
|
sysfpga_nmi_clear(void)
|
|
{
|
|
|
|
sysfpga_reg_write(sysfpga_sc, SYSFPGA_REG_NMISR, 0);
|
|
}
|
|
|
|
void
|
|
sysfpga_sreset(void)
|
|
{
|
|
|
|
sysfpga_reg_write(sysfpga_sc, SYSFPGA_REG_SOFT_RESET,
|
|
SYSFPGA_SOFT_RESET_ASSERT);
|
|
}
|