mirror of
https://git.FreeBSD.org/src.git
synced 2025-02-02 17:08:56 +00:00
Remove FDT specific parts from INTRNG. Change its interface to make it
universal. (1) New struct intr_map_data is defined as a container for arbitrary description of an interrupt used by a device. Typically, an interrupt number and configuration relevant to an interrupt controller is encoded in such description. However, any additional information may be encoded too like a set of cpus on which an interrupt should be enabled or vendor specific data needed for setup of an interrupt in controller. The struct intr_map_data itself is meant to be opaque for INTRNG. (2) An intr_map_irq() function is created which takes an interrupt controller identification and struct intr_map_data as arguments and returns global interrupt number which identifies an interrupt. (3) A set of functions to be used by bus drivers is created as well as a corresponding set of methods for interrupt controller drivers. These sets take both struct resource and struct intr_map_data as one of the arguments. There is a goal to keep struct intr_map_data in struct resource, however, this way a final solution is not limited to that. (4) Other small changes are done to reflect new situation. This is only first step aiming to create stable interface for interrupt controller drivers. Thus, some temporary solution is taken. Interrupt descriptions for devices are stored in INTRNG and two specific mapping function are created to be temporary used by bus drivers. That's why the struct intr_map_data is not opaque for INTRNG now. This temporary solution will be replaced by final one in next step. Differential Revision: https://reviews.freebsd.org/D5730
This commit is contained in:
parent
4d1dd74a50
commit
bff6be3e9b
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=297539
@ -36,8 +36,6 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "opt_platform.h"
|
||||
|
||||
#include "opt_platform.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
@ -118,13 +116,20 @@ __FBSDID("$FreeBSD$");
|
||||
#endif
|
||||
|
||||
#ifdef ARM_INTRNG
|
||||
struct gic_irqsrc {
|
||||
struct intr_irqsrc gi_isrc;
|
||||
uint32_t gi_irq;
|
||||
enum intr_polarity gi_pol;
|
||||
enum intr_trigger gi_trig;
|
||||
};
|
||||
|
||||
static u_int gic_irq_cpu;
|
||||
static int arm_gic_intr(void *);
|
||||
static int arm_gic_bind(device_t dev, struct intr_irqsrc *isrc);
|
||||
static int arm_gic_bind_intr(device_t dev, struct intr_irqsrc *isrc);
|
||||
|
||||
#ifdef SMP
|
||||
u_int sgi_to_ipi[GIC_LAST_SGI - GIC_FIRST_SGI + 1];
|
||||
#define ISRC_IPI(isrc) sgi_to_ipi[isrc->isrc_data - GIC_FIRST_SGI]
|
||||
u_int sgi_first_unused = GIC_FIRST_SGI;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -132,7 +137,7 @@ struct arm_gic_softc {
|
||||
device_t gic_dev;
|
||||
#ifdef ARM_INTRNG
|
||||
void * gic_intrhand;
|
||||
struct intr_irqsrc ** gic_irqs;
|
||||
struct gic_irqsrc * gic_irqs;
|
||||
#endif
|
||||
struct resource * gic_res[3];
|
||||
bus_space_tag_t gic_c_bst;
|
||||
@ -147,6 +152,10 @@ struct arm_gic_softc {
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef ARM_INTRNG
|
||||
#define GIC_INTR_ISRC(sc, irq) (&sc->gic_irqs[irq].gi_isrc)
|
||||
#endif
|
||||
|
||||
static struct resource_spec arm_gic_spec[] = {
|
||||
{ SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Distributor registers */
|
||||
{ SYS_RES_MEMORY, 1, RF_ACTIVE }, /* CPU Interrupt Intf. registers */
|
||||
@ -243,7 +252,7 @@ arm_gic_init_secondary(device_t dev)
|
||||
|
||||
/* Unmask attached SGI interrupts. */
|
||||
for (irq = GIC_FIRST_SGI; irq <= GIC_LAST_SGI; irq++) {
|
||||
isrc = sc->gic_irqs[irq];
|
||||
isrc = GIC_INTR_ISRC(sc, irq);
|
||||
if (isrc != NULL && isrc->isrc_handlers != 0) {
|
||||
CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu);
|
||||
gic_irq_unmask(sc, irq);
|
||||
@ -252,7 +261,7 @@ arm_gic_init_secondary(device_t dev)
|
||||
|
||||
/* Unmask attached PPI interrupts. */
|
||||
for (irq = GIC_FIRST_PPI; irq <= GIC_LAST_PPI; irq++) {
|
||||
isrc = sc->gic_irqs[irq];
|
||||
isrc = GIC_INTR_ISRC(sc, irq);
|
||||
if (isrc == NULL || isrc->isrc_handlers == 0)
|
||||
continue;
|
||||
if (isrc->isrc_flags & INTR_ISRCF_BOUND) {
|
||||
@ -369,6 +378,46 @@ gic_xref(device_t dev)
|
||||
return (0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
arm_gic_register_isrcs(struct arm_gic_softc *sc, uint32_t num)
|
||||
{
|
||||
int error;
|
||||
uint32_t irq;
|
||||
struct gic_irqsrc *irqs;
|
||||
struct intr_irqsrc *isrc;
|
||||
const char *name;
|
||||
|
||||
irqs = malloc(num * sizeof(struct gic_irqsrc), M_DEVBUF,
|
||||
M_WAITOK | M_ZERO);
|
||||
|
||||
name = device_get_nameunit(sc->gic_dev);
|
||||
for (irq = 0; irq < num; irq++) {
|
||||
irqs[irq].gi_irq = irq;
|
||||
irqs[irq].gi_pol = INTR_POLARITY_CONFORM;
|
||||
irqs[irq].gi_trig = INTR_TRIGGER_CONFORM;
|
||||
|
||||
isrc = &irqs[irq].gi_isrc;
|
||||
if (irq <= GIC_LAST_SGI) {
|
||||
error = intr_isrc_register(isrc, sc->gic_dev,
|
||||
INTR_ISRCF_IPI, "%s,i%u", name, irq - GIC_FIRST_SGI);
|
||||
} else if (irq <= GIC_LAST_PPI) {
|
||||
error = intr_isrc_register(isrc, sc->gic_dev,
|
||||
INTR_ISRCF_PPI, "%s,p%u", name, irq - GIC_FIRST_PPI);
|
||||
} else {
|
||||
error = intr_isrc_register(isrc, sc->gic_dev, 0,
|
||||
"%s,s%u", name, irq - GIC_FIRST_SPI);
|
||||
}
|
||||
if (error != 0) {
|
||||
/* XXX call intr_isrc_deregister() */
|
||||
free(irqs, M_DEVBUF);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
sc->gic_irqs = irqs;
|
||||
sc->nirqs = num;
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
@ -376,7 +425,7 @@ arm_gic_attach(device_t dev)
|
||||
{
|
||||
struct arm_gic_softc *sc;
|
||||
int i;
|
||||
uint32_t icciidr, mask;
|
||||
uint32_t icciidr, mask, nirqs;
|
||||
#ifdef ARM_INTRNG
|
||||
phandle_t pxref;
|
||||
intptr_t xref = gic_xref(dev);
|
||||
@ -410,13 +459,17 @@ arm_gic_attach(device_t dev)
|
||||
gic_d_write_4(sc, GICD_CTLR, 0x00);
|
||||
|
||||
/* Get the number of interrupts */
|
||||
sc->nirqs = gic_d_read_4(sc, GICD_TYPER);
|
||||
sc->nirqs = 32 * ((sc->nirqs & 0x1f) + 1);
|
||||
nirqs = gic_d_read_4(sc, GICD_TYPER);
|
||||
nirqs = 32 * ((nirqs & 0x1f) + 1);
|
||||
|
||||
#ifdef ARM_INTRNG
|
||||
sc->gic_irqs = malloc(sc->nirqs * sizeof (*sc->gic_irqs), M_DEVBUF,
|
||||
M_WAITOK | M_ZERO);
|
||||
if (arm_gic_register_isrcs(sc, nirqs)) {
|
||||
device_printf(dev, "could not register irqs\n");
|
||||
goto cleanup;
|
||||
}
|
||||
#else
|
||||
sc->nirqs = nirqs;
|
||||
|
||||
/* Set up function pointers */
|
||||
arm_post_filter = gic_post_filter;
|
||||
arm_config_irq = gic_config_irq;
|
||||
@ -496,20 +549,20 @@ arm_gic_attach(device_t dev)
|
||||
if (intr_pic_claim_root(dev, xref, arm_gic_intr, sc,
|
||||
GIC_LAST_SGI - GIC_FIRST_SGI + 1) != 0) {
|
||||
device_printf(dev, "could not set PIC as a root\n");
|
||||
intr_pic_unregister(dev, xref);
|
||||
intr_pic_deregister(dev, xref);
|
||||
goto cleanup;
|
||||
}
|
||||
} else {
|
||||
if (sc->gic_res[2] == NULL) {
|
||||
device_printf(dev,
|
||||
"not root PIC must have defined interrupt\n");
|
||||
intr_pic_unregister(dev, xref);
|
||||
intr_pic_deregister(dev, xref);
|
||||
goto cleanup;
|
||||
}
|
||||
if (bus_setup_intr(dev, sc->gic_res[2], INTR_TYPE_CLK,
|
||||
arm_gic_intr, NULL, sc, &sc->gic_intrhand)) {
|
||||
device_printf(dev, "could not setup irq handler\n");
|
||||
intr_pic_unregister(dev, xref);
|
||||
intr_pic_deregister(dev, xref);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
@ -533,7 +586,7 @@ static int
|
||||
arm_gic_intr(void *arg)
|
||||
{
|
||||
struct arm_gic_softc *sc = arg;
|
||||
struct intr_irqsrc *isrc;
|
||||
struct gic_irqsrc *gi;
|
||||
uint32_t irq_active_reg, irq;
|
||||
struct trapframe *tf;
|
||||
|
||||
@ -569,14 +622,7 @@ arm_gic_intr(void *arg)
|
||||
|
||||
tf = curthread->td_intr_frame;
|
||||
dispatch_irq:
|
||||
isrc = sc->gic_irqs[irq];
|
||||
if (isrc == NULL) {
|
||||
device_printf(sc->gic_dev, "Stray interrupt %u detected\n", irq);
|
||||
gic_irq_mask(sc, irq);
|
||||
gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
|
||||
goto next_irq;
|
||||
}
|
||||
|
||||
gi = sc->gic_irqs + irq;
|
||||
/*
|
||||
* Note that GIC_FIRST_SGI is zero and is not used in 'if' statement
|
||||
* as compiler complains that comparing u_int >= 0 is always true.
|
||||
@ -585,7 +631,7 @@ dispatch_irq:
|
||||
#ifdef SMP
|
||||
/* Call EOI for all IPI before dispatch. */
|
||||
gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
|
||||
intr_ipi_dispatch(ISRC_IPI(isrc), tf);
|
||||
intr_ipi_dispatch(sgi_to_ipi[gi->gi_irq], tf);
|
||||
goto next_irq;
|
||||
#else
|
||||
device_printf(sc->gic_dev, "SGI %u on UP system detected\n",
|
||||
@ -598,10 +644,15 @@ dispatch_irq:
|
||||
#ifdef GIC_DEBUG_SPURIOUS
|
||||
sc->last_irq[PCPU_GET(cpuid)] = irq;
|
||||
#endif
|
||||
if (isrc->isrc_trig == INTR_TRIGGER_EDGE)
|
||||
if (gi->gi_trig == INTR_TRIGGER_EDGE)
|
||||
gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
|
||||
|
||||
intr_irq_dispatch(isrc, tf);
|
||||
if (intr_isrc_dispatch(&gi->gi_isrc, tf) != 0) {
|
||||
gic_irq_mask(sc, irq);
|
||||
if (gi->gi_trig != INTR_TRIGGER_EDGE)
|
||||
gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
|
||||
device_printf(sc->gic_dev, "Stray irq %u disabled\n", irq);
|
||||
}
|
||||
|
||||
next_irq:
|
||||
arm_irq_memory_barrier(irq);
|
||||
@ -613,52 +664,6 @@ next_irq:
|
||||
return (FILTER_HANDLED);
|
||||
}
|
||||
|
||||
static int
|
||||
gic_attach_isrc(struct arm_gic_softc *sc, struct intr_irqsrc *isrc, u_int irq)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
/*
|
||||
* 1. The link between ISRC and controller must be set atomically.
|
||||
* 2. Just do things only once in rare case when consumers
|
||||
* of shared interrupt came here at the same moment.
|
||||
*/
|
||||
mtx_lock_spin(&sc->mutex);
|
||||
if (sc->gic_irqs[irq] != NULL) {
|
||||
mtx_unlock_spin(&sc->mutex);
|
||||
return (sc->gic_irqs[irq] == isrc ? 0 : EEXIST);
|
||||
}
|
||||
sc->gic_irqs[irq] = isrc;
|
||||
isrc->isrc_data = irq;
|
||||
mtx_unlock_spin(&sc->mutex);
|
||||
|
||||
name = device_get_nameunit(sc->gic_dev);
|
||||
if (irq <= GIC_LAST_SGI)
|
||||
intr_irq_set_name(isrc, "%s,i%u", name, irq - GIC_FIRST_SGI);
|
||||
else if (irq <= GIC_LAST_PPI)
|
||||
intr_irq_set_name(isrc, "%s,p%u", name, irq - GIC_FIRST_PPI);
|
||||
else
|
||||
intr_irq_set_name(isrc, "%s,s%u", name, irq - GIC_FIRST_SPI);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
gic_detach_isrc(struct arm_gic_softc *sc, struct intr_irqsrc *isrc, u_int irq)
|
||||
{
|
||||
|
||||
mtx_lock_spin(&sc->mutex);
|
||||
if (sc->gic_irqs[irq] != isrc) {
|
||||
mtx_unlock_spin(&sc->mutex);
|
||||
return (sc->gic_irqs[irq] == NULL ? 0 : EINVAL);
|
||||
}
|
||||
sc->gic_irqs[irq] = NULL;
|
||||
isrc->isrc_data = 0;
|
||||
mtx_unlock_spin(&sc->mutex);
|
||||
|
||||
intr_irq_set_name(isrc, "");
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
gic_config(struct arm_gic_softc *sc, u_int irq, enum intr_trigger trig,
|
||||
enum intr_polarity pol)
|
||||
@ -716,127 +721,163 @@ gic_bind(struct arm_gic_softc *sc, u_int irq, cpuset_t *cpus)
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef FDT
|
||||
static int
|
||||
gic_irq_from_nspc(struct arm_gic_softc *sc, u_int type, u_int num, u_int *irqp)
|
||||
gic_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp,
|
||||
enum intr_polarity *polp, enum intr_trigger *trigp)
|
||||
{
|
||||
|
||||
switch (type) {
|
||||
case INTR_IRQ_NSPC_PLAIN:
|
||||
*irqp = num;
|
||||
return (*irqp < sc->nirqs ? 0 : EINVAL);
|
||||
if (ncells == 1) {
|
||||
*irqp = cells[0];
|
||||
*polp = INTR_POLARITY_CONFORM;
|
||||
*trigp = INTR_TRIGGER_CONFORM;
|
||||
return (0);
|
||||
}
|
||||
if (ncells == 3) {
|
||||
u_int irq, tripol;
|
||||
|
||||
case INTR_IRQ_NSPC_IRQ:
|
||||
*irqp = num + GIC_FIRST_PPI;
|
||||
return (*irqp < sc->nirqs ? 0 : EINVAL);
|
||||
/*
|
||||
* The 1st cell is the interrupt type:
|
||||
* 0 = SPI
|
||||
* 1 = PPI
|
||||
* The 2nd cell contains the interrupt number:
|
||||
* [0 - 987] for SPI
|
||||
* [0 - 15] for PPI
|
||||
* The 3rd cell is the flags, encoded as follows:
|
||||
* bits[3:0] trigger type and level flags
|
||||
* 1 = low-to-high edge triggered
|
||||
* 2 = high-to-low edge triggered
|
||||
* 4 = active high level-sensitive
|
||||
* 8 = active low level-sensitive
|
||||
* bits[15:8] PPI interrupt cpu mask
|
||||
* Each bit corresponds to each of the 8 possible cpus
|
||||
* attached to the GIC. A bit set to '1' indicated
|
||||
* the interrupt is wired to that CPU.
|
||||
*/
|
||||
switch (cells[0]) {
|
||||
case 0:
|
||||
irq = GIC_FIRST_SPI + cells[1];
|
||||
/* SPI irq is checked later. */
|
||||
break;
|
||||
case 1:
|
||||
irq = GIC_FIRST_PPI + cells[1];
|
||||
if (irq > GIC_LAST_PPI) {
|
||||
device_printf(dev, "unsupported PPI interrupt "
|
||||
"number %u\n", cells[1]);
|
||||
return (EINVAL);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
device_printf(dev, "unsupported interrupt type "
|
||||
"configuration %u\n", cells[0]);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
case INTR_IRQ_NSPC_IPI:
|
||||
*irqp = num + GIC_FIRST_SGI;
|
||||
return (*irqp < GIC_LAST_SGI ? 0 : EINVAL);
|
||||
tripol = cells[2] & 0xff;
|
||||
if (tripol & 0xf0 || (tripol & 0x0a && cells[0] == 0))
|
||||
device_printf(dev, "unsupported trigger/polarity "
|
||||
"configuration 0x%02x\n", tripol);
|
||||
|
||||
*irqp = irq;
|
||||
*polp = INTR_POLARITY_CONFORM;
|
||||
*trigp = tripol & 0x03 ? INTR_TRIGGER_EDGE : INTR_TRIGGER_LEVEL;
|
||||
return (0);
|
||||
}
|
||||
return (EINVAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
gic_map_intr(device_t dev, struct intr_map_data *data, u_int *irqp,
|
||||
enum intr_polarity *polp, enum intr_trigger *trigp)
|
||||
{
|
||||
u_int irq;
|
||||
enum intr_polarity pol;
|
||||
enum intr_trigger trig;
|
||||
struct arm_gic_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
switch (data->type) {
|
||||
#ifdef FDT
|
||||
case INTR_MAP_DATA_FDT:
|
||||
if (gic_map_fdt(dev, data->fdt.ncells, data->fdt.cells, &irq,
|
||||
&pol, &trig) != 0)
|
||||
return (EINVAL);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
gic_map_nspc(struct arm_gic_softc *sc, struct intr_irqsrc *isrc, u_int *irqp)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = gic_irq_from_nspc(sc, isrc->isrc_nspc_type, isrc->isrc_nspc_num,
|
||||
irqp);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
return (gic_attach_isrc(sc, isrc, *irqp));
|
||||
}
|
||||
|
||||
#ifdef FDT
|
||||
static int
|
||||
gic_map_fdt(struct arm_gic_softc *sc, struct intr_irqsrc *isrc, u_int *irqp)
|
||||
{
|
||||
u_int irq, tripol;
|
||||
enum intr_trigger trig;
|
||||
enum intr_polarity pol;
|
||||
int error;
|
||||
|
||||
if (isrc->isrc_ncells == 1) {
|
||||
irq = isrc->isrc_cells[0];
|
||||
pol = INTR_POLARITY_CONFORM;
|
||||
trig = INTR_TRIGGER_CONFORM;
|
||||
} else if (isrc->isrc_ncells == 3) {
|
||||
if (isrc->isrc_cells[0] == 0)
|
||||
irq = isrc->isrc_cells[1] + GIC_FIRST_SPI;
|
||||
else
|
||||
irq = isrc->isrc_cells[1] + GIC_FIRST_PPI;
|
||||
|
||||
/*
|
||||
* In intr[2], bits[3:0] are trigger type and level flags.
|
||||
* 1 = low-to-high edge triggered
|
||||
* 2 = high-to-low edge triggered
|
||||
* 4 = active high level-sensitive
|
||||
* 8 = active low level-sensitive
|
||||
* The hardware only supports active-high-level or rising-edge.
|
||||
*/
|
||||
tripol = isrc->isrc_cells[2];
|
||||
if (tripol & 0x0a && irq >= GIC_FIRST_SPI) {
|
||||
device_printf(sc->gic_dev,
|
||||
"unsupported trigger/polarity configuration "
|
||||
"0x%02x\n", tripol & 0x0f);
|
||||
}
|
||||
pol = INTR_POLARITY_CONFORM;
|
||||
if (tripol & 0x03)
|
||||
trig = INTR_TRIGGER_EDGE;
|
||||
else
|
||||
trig = INTR_TRIGGER_LEVEL;
|
||||
} else
|
||||
return (EINVAL);
|
||||
|
||||
if (irq >= sc->nirqs)
|
||||
return (EINVAL);
|
||||
|
||||
error = gic_attach_isrc(sc, isrc, irq);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
isrc->isrc_nspc_type = INTR_IRQ_NSPC_PLAIN;
|
||||
isrc->isrc_nspc_num = irq;
|
||||
isrc->isrc_trig = trig;
|
||||
isrc->isrc_pol = pol;
|
||||
|
||||
*irqp = irq;
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
arm_gic_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu)
|
||||
{
|
||||
struct arm_gic_softc *sc = device_get_softc(dev);
|
||||
u_int irq;
|
||||
int error;
|
||||
|
||||
if (isrc->isrc_type == INTR_ISRCT_NAMESPACE)
|
||||
error = gic_map_nspc(sc, isrc, &irq);
|
||||
#ifdef FDT
|
||||
else if (isrc->isrc_type == INTR_ISRCT_FDT)
|
||||
error = gic_map_fdt(sc, isrc, &irq);
|
||||
#endif
|
||||
else
|
||||
if (pol != INTR_POLARITY_CONFORM && pol != INTR_POLARITY_LOW &&
|
||||
pol != INTR_POLARITY_HIGH)
|
||||
return (EINVAL);
|
||||
if (trig != INTR_TRIGGER_CONFORM && trig != INTR_TRIGGER_EDGE &&
|
||||
trig != INTR_TRIGGER_LEVEL)
|
||||
return (EINVAL);
|
||||
|
||||
if (error == 0)
|
||||
*is_percpu = irq < GIC_FIRST_SPI ? TRUE : FALSE;
|
||||
*irqp = irq;
|
||||
if (polp != NULL)
|
||||
*polp = pol;
|
||||
if (trigp != NULL)
|
||||
*trigp = trig;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
arm_gic_map_intr(device_t dev, struct intr_map_data *data,
|
||||
struct intr_irqsrc **isrcp)
|
||||
{
|
||||
int error;
|
||||
u_int irq;
|
||||
struct arm_gic_softc *sc;
|
||||
|
||||
error = gic_map_intr(dev, data, &irq, NULL, NULL);
|
||||
if (error == 0) {
|
||||
sc = device_get_softc(dev);
|
||||
*isrcp = GIC_INTR_ISRC(sc, irq);
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
static void
|
||||
arm_gic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
static int
|
||||
arm_gic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
struct resource *res, struct intr_map_data *data)
|
||||
{
|
||||
struct arm_gic_softc *sc = device_get_softc(dev);
|
||||
u_int irq = isrc->isrc_data;
|
||||
struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
|
||||
u_int irq;
|
||||
enum intr_trigger trig;
|
||||
enum intr_polarity pol;
|
||||
|
||||
if (isrc->isrc_trig == INTR_TRIGGER_CONFORM)
|
||||
isrc->isrc_trig = INTR_TRIGGER_LEVEL;
|
||||
if (data == NULL)
|
||||
return (ENOTSUP);
|
||||
|
||||
/* Get config for resource. */
|
||||
if (gic_map_intr(dev, data, &irq, &pol, &trig))
|
||||
return (EINVAL);
|
||||
|
||||
if (gi->gi_irq != irq)
|
||||
return (EINVAL);
|
||||
|
||||
/* Compare config if this is not first setup. */
|
||||
if (isrc->isrc_handlers != 0) {
|
||||
if ((pol != INTR_POLARITY_CONFORM && pol != gi->gi_pol) ||
|
||||
(trig != INTR_TRIGGER_CONFORM && trig != gi->gi_trig))
|
||||
return (EINVAL);
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (pol == INTR_POLARITY_CONFORM)
|
||||
pol = INTR_POLARITY_LOW; /* just pick some */
|
||||
if (trig == INTR_TRIGGER_CONFORM)
|
||||
trig = INTR_TRIGGER_EDGE; /* just pick some */
|
||||
|
||||
gi->gi_pol = pol;
|
||||
gi->gi_trig = trig;
|
||||
|
||||
/*
|
||||
* XXX - In case that per CPU interrupt is going to be enabled in time
|
||||
@ -845,48 +886,54 @@ arm_gic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
* pic_enable_source() and pic_disable_source() should act on
|
||||
* per CPU basis only. Thus, it should be solved here somehow.
|
||||
*/
|
||||
if (isrc->isrc_flags & INTR_ISRCF_PERCPU)
|
||||
if (isrc->isrc_flags & INTR_ISRCF_PPI)
|
||||
CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu);
|
||||
|
||||
gic_config(sc, irq, isrc->isrc_trig, isrc->isrc_pol);
|
||||
arm_gic_bind(dev, isrc);
|
||||
}
|
||||
|
||||
static void
|
||||
arm_gic_enable_source(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct arm_gic_softc *sc = device_get_softc(dev);
|
||||
u_int irq = isrc->isrc_data;
|
||||
|
||||
arm_irq_memory_barrier(irq);
|
||||
gic_irq_unmask(sc, irq);
|
||||
}
|
||||
|
||||
static void
|
||||
arm_gic_disable_source(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct arm_gic_softc *sc = device_get_softc(dev);
|
||||
u_int irq = isrc->isrc_data;
|
||||
|
||||
gic_irq_mask(sc, irq);
|
||||
gic_config(sc, gi->gi_irq, trig, pol);
|
||||
arm_gic_bind_intr(dev, isrc);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
arm_gic_unregister(device_t dev, struct intr_irqsrc *isrc)
|
||||
arm_gic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
struct resource *res, struct intr_map_data *data)
|
||||
{
|
||||
struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
|
||||
|
||||
if (isrc->isrc_handlers == 0) {
|
||||
gi->gi_pol = INTR_POLARITY_CONFORM;
|
||||
gi->gi_trig = INTR_TRIGGER_CONFORM;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
arm_gic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct arm_gic_softc *sc = device_get_softc(dev);
|
||||
u_int irq = isrc->isrc_data;
|
||||
struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
|
||||
|
||||
return (gic_detach_isrc(sc, isrc, irq));
|
||||
arm_irq_memory_barrier(gi->gi_irq);
|
||||
gic_irq_unmask(sc, gi->gi_irq);
|
||||
}
|
||||
|
||||
static void
|
||||
arm_gic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct arm_gic_softc *sc = device_get_softc(dev);
|
||||
struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
|
||||
|
||||
gic_irq_mask(sc, gi->gi_irq);
|
||||
}
|
||||
|
||||
static void
|
||||
arm_gic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct arm_gic_softc *sc = device_get_softc(dev);
|
||||
struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
|
||||
|
||||
arm_gic_disable_source(dev, isrc);
|
||||
gic_c_write_4(sc, GICC_EOIR, isrc->isrc_data);
|
||||
arm_gic_disable_intr(dev, isrc);
|
||||
gic_c_write_4(sc, GICC_EOIR, gi->gi_irq);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -894,65 +941,65 @@ arm_gic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
|
||||
arm_irq_memory_barrier(0);
|
||||
arm_gic_enable_source(dev, isrc);
|
||||
arm_gic_enable_intr(dev, isrc);
|
||||
}
|
||||
|
||||
static void
|
||||
arm_gic_post_filter(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct arm_gic_softc *sc = device_get_softc(dev);
|
||||
struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
|
||||
|
||||
/* EOI for edge-triggered done earlier. */
|
||||
if (isrc->isrc_trig == INTR_TRIGGER_EDGE)
|
||||
if (gi->gi_trig == INTR_TRIGGER_EDGE)
|
||||
return;
|
||||
|
||||
arm_irq_memory_barrier(0);
|
||||
gic_c_write_4(sc, GICC_EOIR, isrc->isrc_data);
|
||||
gic_c_write_4(sc, GICC_EOIR, gi->gi_irq);
|
||||
}
|
||||
|
||||
static int
|
||||
arm_gic_bind(device_t dev, struct intr_irqsrc *isrc)
|
||||
arm_gic_bind_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct arm_gic_softc *sc = device_get_softc(dev);
|
||||
uint32_t irq = isrc->isrc_data;
|
||||
struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
|
||||
|
||||
if (irq < GIC_FIRST_SPI)
|
||||
if (gi->gi_irq < GIC_FIRST_SPI)
|
||||
return (EINVAL);
|
||||
|
||||
if (CPU_EMPTY(&isrc->isrc_cpu)) {
|
||||
gic_irq_cpu = intr_irq_next_cpu(gic_irq_cpu, &all_cpus);
|
||||
CPU_SETOF(gic_irq_cpu, &isrc->isrc_cpu);
|
||||
}
|
||||
return (gic_bind(sc, irq, &isrc->isrc_cpu));
|
||||
return (gic_bind(sc, gi->gi_irq, &isrc->isrc_cpu));
|
||||
}
|
||||
|
||||
#ifdef SMP
|
||||
static void
|
||||
arm_gic_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus)
|
||||
arm_gic_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus,
|
||||
u_int ipi)
|
||||
{
|
||||
struct arm_gic_softc *sc = device_get_softc(dev);
|
||||
uint32_t irq, val = 0, i;
|
||||
|
||||
irq = isrc->isrc_data;
|
||||
struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
|
||||
uint32_t val = 0, i;
|
||||
|
||||
for (i = 0; i < MAXCPU; i++)
|
||||
if (CPU_ISSET(i, &cpus))
|
||||
val |= 1 << (16 + i);
|
||||
|
||||
gic_d_write_4(sc, GICD_SGIR(0), val | irq);
|
||||
gic_d_write_4(sc, GICD_SGIR(0), val | gi->gi_irq);
|
||||
}
|
||||
|
||||
static int
|
||||
arm_gic_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc *isrc)
|
||||
arm_gic_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc **isrcp)
|
||||
{
|
||||
struct arm_gic_softc *sc = device_get_softc(dev);
|
||||
u_int irq;
|
||||
int error;
|
||||
|
||||
error = gic_map_nspc(sc, isrc, &irq);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
sgi_to_ipi[irq - GIC_FIRST_SGI] = ipi;
|
||||
if (sgi_first_unused > GIC_LAST_SGI)
|
||||
return (ENOSPC);
|
||||
|
||||
*isrcp = GIC_INTR_ISRC(sc, sgi_first_unused);
|
||||
sgi_to_ipi[sgi_first_unused++] = ipi;
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
@ -1171,16 +1218,16 @@ static device_method_t arm_gic_methods[] = {
|
||||
DEVMETHOD(device_attach, arm_gic_attach),
|
||||
#ifdef ARM_INTRNG
|
||||
/* Interrupt controller interface */
|
||||
DEVMETHOD(pic_disable_source, arm_gic_disable_source),
|
||||
DEVMETHOD(pic_disable_intr, arm_gic_disable_intr),
|
||||
DEVMETHOD(pic_enable_intr, arm_gic_enable_intr),
|
||||
DEVMETHOD(pic_enable_source, arm_gic_enable_source),
|
||||
DEVMETHOD(pic_map_intr, arm_gic_map_intr),
|
||||
DEVMETHOD(pic_setup_intr, arm_gic_setup_intr),
|
||||
DEVMETHOD(pic_teardown_intr, arm_gic_teardown_intr),
|
||||
DEVMETHOD(pic_post_filter, arm_gic_post_filter),
|
||||
DEVMETHOD(pic_post_ithread, arm_gic_post_ithread),
|
||||
DEVMETHOD(pic_pre_ithread, arm_gic_pre_ithread),
|
||||
DEVMETHOD(pic_register, arm_gic_register),
|
||||
DEVMETHOD(pic_unregister, arm_gic_unregister),
|
||||
#ifdef SMP
|
||||
DEVMETHOD(pic_bind, arm_gic_bind),
|
||||
DEVMETHOD(pic_bind_intr, arm_gic_bind_intr),
|
||||
DEVMETHOD(pic_init_secondary, arm_gic_init_secondary),
|
||||
DEVMETHOD(pic_ipi_send, arm_gic_ipi_send),
|
||||
DEVMETHOD(pic_ipi_setup, arm_gic_ipi_setup),
|
||||
|
@ -1,8 +1,6 @@
|
||||
/* $NetBSD: intr.c,v 1.12 2003/07/15 00:24:41 lukem Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2004 Olivier Houchard.
|
||||
* Copyright (c) 1994-1998 Mark Brinicombe.
|
||||
* Copyright (c) 2015-2016 Svatopluk Kraus
|
||||
* Copyright (c) 2015-2016 Michal Meloun
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -13,27 +11,18 @@
|
||||
* 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 by Mark Brinicombe
|
||||
* for the NetBSD Project.
|
||||
* 4. The name of the company nor the name of the author may be used to
|
||||
* endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* 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 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)
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* Soft interrupt and other generic interrupt functions.
|
||||
*/
|
||||
|
||||
#include "opt_platform.h"
|
||||
@ -76,7 +65,6 @@ struct intr_ipi {
|
||||
};
|
||||
|
||||
static struct intr_ipi ipi_sources[INTR_IPI_COUNT];
|
||||
u_int ipi_next_num;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -184,7 +172,7 @@ intr_ipi_send(cpuset_t cpus, u_int ipi)
|
||||
if (ii->ii_count == NULL)
|
||||
panic("%s: not setup IPI %u", __func__, ipi);
|
||||
|
||||
ii->ii_send(ii->ii_send_arg, cpus);
|
||||
ii->ii_send(ii->ii_send_arg, cpus, ipi);
|
||||
}
|
||||
|
||||
void
|
||||
@ -211,11 +199,11 @@ intr_ipi_setup(u_int ipi, const char *name, intr_ipi_handler_t *hand,
|
||||
* Send IPI thru interrupt controller.
|
||||
*/
|
||||
static void
|
||||
pic_ipi_send(void *arg, cpuset_t cpus)
|
||||
pic_ipi_send(void *arg, cpuset_t cpus, u_int ipi)
|
||||
{
|
||||
|
||||
KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__));
|
||||
PIC_IPI_SEND(intr_irq_root_dev, arg, cpus);
|
||||
PIC_IPI_SEND(intr_irq_root_dev, arg, cpus, ipi);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -232,18 +220,11 @@ intr_pic_ipi_setup(u_int ipi, const char *name, intr_ipi_handler_t *hand,
|
||||
|
||||
KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__));
|
||||
|
||||
isrc = intr_isrc_alloc(INTR_ISRCT_NAMESPACE, 0);
|
||||
isrc->isrc_nspc_type = INTR_IRQ_NSPC_IPI;
|
||||
isrc->isrc_nspc_num = ipi_next_num;
|
||||
|
||||
error = PIC_IPI_SETUP(intr_irq_root_dev, ipi, isrc);
|
||||
error = PIC_IPI_SETUP(intr_irq_root_dev, ipi, &isrc);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
ipi_next_num++;
|
||||
|
||||
isrc->isrc_dev = intr_irq_root_dev;
|
||||
isrc->isrc_handlers = 1;
|
||||
isrc->isrc_handlers++;
|
||||
intr_ipi_setup(ipi, name, hand, arg, pic_ipi_send, isrc);
|
||||
return (0);
|
||||
}
|
||||
|
@ -281,7 +281,8 @@ nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
|
||||
int ret = ENODEV;
|
||||
|
||||
#ifdef ARM_INTRNG
|
||||
ret = intr_irq_config(irq, trig, pol);
|
||||
device_printf(dev, "bus_config_intr is obsolete and not supported!\n");
|
||||
ret = EOPNOTSUPP;
|
||||
#else
|
||||
if (arm_config_irq)
|
||||
ret = (*arm_config_irq)(irq, trig, pol);
|
||||
@ -293,22 +294,23 @@ static int
|
||||
nexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
|
||||
driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep)
|
||||
{
|
||||
#ifndef ARM_INTRNG
|
||||
int irq;
|
||||
#endif
|
||||
|
||||
if ((rman_get_flags(res) & RF_SHAREABLE) == 0)
|
||||
flags |= INTR_EXCL;
|
||||
|
||||
for (irq = rman_get_start(res); irq <= rman_get_end(res); irq++) {
|
||||
#ifdef ARM_INTRNG
|
||||
intr_irq_add_handler(child, filt, intr, arg, irq, flags,
|
||||
cookiep);
|
||||
return(intr_setup_irq(child, res, filt, intr, arg, flags, cookiep));
|
||||
#else
|
||||
for (irq = rman_get_start(res); irq <= rman_get_end(res); irq++) {
|
||||
arm_setup_irqhandler(device_get_nameunit(child),
|
||||
filt, intr, arg, irq, flags, cookiep);
|
||||
arm_unmask_irq(irq);
|
||||
#endif
|
||||
}
|
||||
return (0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
@ -316,7 +318,7 @@ nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
|
||||
{
|
||||
|
||||
#ifdef ARM_INTRNG
|
||||
return (intr_irq_remove_handler(child, rman_get_start(r), ih));
|
||||
return (intr_teardown_irq(child, r, ih));
|
||||
#else
|
||||
return (arm_remove_irqhandler(rman_get_start(r), ih));
|
||||
#endif
|
||||
@ -328,7 +330,7 @@ nexus_describe_intr(device_t dev, device_t child, struct resource *irq,
|
||||
void *cookie, const char *descr)
|
||||
{
|
||||
|
||||
return (intr_irq_describe(rman_get_start(irq), cookie, descr));
|
||||
return (intr_describe_irq(child, irq, cookie, descr));
|
||||
}
|
||||
|
||||
#ifdef SMP
|
||||
@ -336,7 +338,7 @@ static int
|
||||
nexus_bind_intr(device_t dev, device_t child, struct resource *irq, int cpu)
|
||||
{
|
||||
|
||||
return (intr_irq_bind(rman_get_start(irq), cpu));
|
||||
return (intr_bind_irq(child, irq, cpu));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
@ -91,6 +91,15 @@ __FBSDID("$FreeBSD$");
|
||||
#define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)
|
||||
#define NGPIO 32
|
||||
|
||||
#ifdef ARM_INTRNG
|
||||
struct gpio_irqsrc {
|
||||
struct intr_irqsrc gi_isrc;
|
||||
u_int gi_irq;
|
||||
enum intr_polarity gi_pol;
|
||||
enum intr_trigger gi_trig;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct imx51_gpio_softc {
|
||||
device_t dev;
|
||||
device_t sc_busdev;
|
||||
@ -101,7 +110,9 @@ struct imx51_gpio_softc {
|
||||
bus_space_handle_t sc_ioh;
|
||||
int gpio_npins;
|
||||
struct gpio_pin gpio_pins[NGPIO];
|
||||
struct intr_irqsrc *gpio_pic_irqsrc[NGPIO];
|
||||
#ifdef ARM_INTRNG
|
||||
struct gpio_irqsrc gpio_pic_irqsrc[NGPIO];
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct ofw_compat_data compat_data[] = {
|
||||
@ -145,8 +156,30 @@ static int imx51_gpio_pin_get(device_t, uint32_t, unsigned int *);
|
||||
static int imx51_gpio_pin_toggle(device_t, uint32_t pin);
|
||||
|
||||
#ifdef ARM_INTRNG
|
||||
static int
|
||||
gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
struct resource *res, struct intr_map_data *data)
|
||||
{
|
||||
struct imx51_gpio_softc *sc;
|
||||
struct gpio_irqsrc *gi;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
if (isrc->isrc_handlers == 0) {
|
||||
gi = (struct gpio_irqsrc *)isrc;
|
||||
gi->gi_pol = INTR_POLARITY_CONFORM;
|
||||
gi->gi_trig = INTR_TRIGGER_CONFORM;
|
||||
|
||||
// XXX Not sure this is necessary
|
||||
mtx_lock_spin(&sc->sc_mtx);
|
||||
CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << gi->gi_irq));
|
||||
WRITE4(sc, IMX_GPIO_ISR_REG, (1U << gi->gi_irq));
|
||||
mtx_unlock_spin(&sc->sc_mtx);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* this is teardown_intr
|
||||
* this is mask_intr
|
||||
*/
|
||||
static void
|
||||
gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
@ -155,55 +188,143 @@ gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
u_int irq;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
irq = isrc->isrc_data;
|
||||
irq = ((struct gpio_irqsrc *)isrc)->gi_irq;
|
||||
|
||||
// XXX Not sure this is necessary
|
||||
mtx_lock_spin(&sc->sc_mtx);
|
||||
CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << irq));
|
||||
WRITE4(sc, IMX_GPIO_ISR_REG, (1U << irq));
|
||||
mtx_unlock_spin(&sc->sc_mtx);
|
||||
}
|
||||
|
||||
/*
|
||||
* this is mask_intr
|
||||
*/
|
||||
static void
|
||||
gpio_pic_disable_source(device_t dev, struct intr_irqsrc *isrc)
|
||||
static int
|
||||
gpio_pic_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp,
|
||||
enum intr_polarity *polp, enum intr_trigger *trigp)
|
||||
{
|
||||
struct imx51_gpio_softc *sc;
|
||||
u_int irq, tripol;
|
||||
enum intr_polarity pol;
|
||||
enum intr_trigger trig;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
mtx_lock_spin(&sc->sc_mtx);
|
||||
CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << isrc->isrc_data));
|
||||
mtx_unlock_spin(&sc->sc_mtx);
|
||||
/*
|
||||
* From devicetree/bindings/gpio/fsl-imx-gpio.txt:
|
||||
* #interrupt-cells: 2. The first cell is the GPIO number. The second
|
||||
* cell bits[3:0] is used to specify trigger type and level flags:
|
||||
* 1 = low-to-high edge triggered.
|
||||
* 2 = high-to-low edge triggered.
|
||||
* 4 = active high level-sensitive.
|
||||
* 8 = active low level-sensitive.
|
||||
* We can do any single one of these modes, but nothing in combo.
|
||||
*/
|
||||
|
||||
if (ncells != 2) {
|
||||
device_printf(sc->dev, "Invalid #interrupt-cells");
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
irq = cells[0];
|
||||
tripol = cells[1];
|
||||
if (irq >= sc->gpio_npins) {
|
||||
device_printf(sc->dev, "Invalid interrupt number %d", irq);
|
||||
return (EINVAL);
|
||||
}
|
||||
switch (tripol) {
|
||||
case 1:
|
||||
trig = INTR_TRIGGER_EDGE;
|
||||
pol = INTR_POLARITY_HIGH;
|
||||
break;
|
||||
case 2:
|
||||
trig = INTR_TRIGGER_EDGE;
|
||||
pol = INTR_POLARITY_LOW;
|
||||
break;
|
||||
case 4:
|
||||
trig = INTR_TRIGGER_LEVEL;
|
||||
pol = INTR_POLARITY_HIGH;
|
||||
break;
|
||||
case 8:
|
||||
trig = INTR_TRIGGER_LEVEL;
|
||||
pol = INTR_POLARITY_LOW;
|
||||
break;
|
||||
default:
|
||||
device_printf(sc->dev, "unsupported trigger/polarity 0x%2x\n",
|
||||
tripol);
|
||||
return (ENOTSUP);
|
||||
}
|
||||
*irqp = irq;
|
||||
if (polp != NULL)
|
||||
*polp = pol;
|
||||
if (trigp != NULL)
|
||||
*trigp = trig;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* this is setup_intr
|
||||
*/
|
||||
static void
|
||||
gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
static int
|
||||
gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
|
||||
struct intr_irqsrc **isrcp)
|
||||
{
|
||||
int error;
|
||||
u_int irq;
|
||||
struct imx51_gpio_softc *sc;
|
||||
|
||||
if (data->type != INTR_MAP_DATA_FDT)
|
||||
return (ENOTSUP);
|
||||
|
||||
error = gpio_pic_map_fdt(dev, data->fdt.ncells, data->fdt.cells, &irq,
|
||||
NULL, NULL);
|
||||
if (error == 0) {
|
||||
sc = device_get_softc(dev);
|
||||
*isrcp = &sc->gpio_pic_irqsrc[irq].gi_isrc;
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
struct resource *res, struct intr_map_data *data)
|
||||
{
|
||||
struct imx51_gpio_softc *sc;
|
||||
int icfg;
|
||||
struct gpio_irqsrc *gi;
|
||||
int error, icfg;
|
||||
u_int irq, reg, shift, wrk;
|
||||
enum intr_trigger trig;
|
||||
enum intr_polarity pol;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
gi = (struct gpio_irqsrc *)isrc;
|
||||
|
||||
if (isrc->isrc_trig == INTR_TRIGGER_LEVEL) {
|
||||
if (isrc->isrc_pol == INTR_POLARITY_LOW)
|
||||
/* Get config for interrupt. */
|
||||
if (data == NULL || data->type != INTR_MAP_DATA_FDT)
|
||||
return (ENOTSUP);
|
||||
error = gpio_pic_map_fdt(dev, data->fdt.ncells, data->fdt.cells, &irq,
|
||||
&pol, &trig);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
if (gi->gi_irq != irq)
|
||||
return (EINVAL);
|
||||
|
||||
/* Compare config if this is not first setup. */
|
||||
if (isrc->isrc_handlers != 0) {
|
||||
if (pol != gi->gi_pol || trig != gi->gi_trig)
|
||||
return (EINVAL);
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
|
||||
gi->gi_pol = pol;
|
||||
gi->gi_trig = trig;
|
||||
|
||||
if (trig == INTR_TRIGGER_LEVEL) {
|
||||
if (pol == INTR_POLARITY_LOW)
|
||||
icfg = GPIO_ICR_COND_LOW;
|
||||
else
|
||||
icfg = GPIO_ICR_COND_HIGH;
|
||||
} else {
|
||||
if (isrc->isrc_pol == INTR_POLARITY_HIGH)
|
||||
if (pol == INTR_POLARITY_HIGH)
|
||||
icfg = GPIO_ICR_COND_FALL;
|
||||
else
|
||||
icfg = GPIO_ICR_COND_RISE;
|
||||
}
|
||||
|
||||
irq = isrc->isrc_data;
|
||||
if (irq < 16) {
|
||||
reg = IMX_GPIO_ICR1_REG;
|
||||
shift = 2 * irq;
|
||||
@ -220,20 +341,23 @@ gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
wrk |= icfg << shift;
|
||||
WRITE4(sc, reg, wrk);
|
||||
mtx_unlock_spin(&sc->sc_mtx);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* this is unmask_intr
|
||||
*/
|
||||
static void
|
||||
gpio_pic_enable_source(device_t dev, struct intr_irqsrc *isrc)
|
||||
gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct imx51_gpio_softc *sc;
|
||||
u_int irq;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
irq = ((struct gpio_irqsrc *)isrc)->gi_irq;
|
||||
|
||||
mtx_lock_spin(&sc->sc_mtx);
|
||||
SET4(sc, IMX_GPIO_IMR_REG, (1U << isrc->isrc_data));
|
||||
SET4(sc, IMX_GPIO_IMR_REG, (1U << irq));
|
||||
mtx_unlock_spin(&sc->sc_mtx);
|
||||
}
|
||||
|
||||
@ -241,12 +365,14 @@ static void
|
||||
gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct imx51_gpio_softc *sc;
|
||||
u_int irq;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
irq = ((struct gpio_irqsrc *)isrc)->gi_irq;
|
||||
|
||||
arm_irq_memory_barrier(0);
|
||||
/* EOI. W1C reg so no r-m-w, no locking needed. */
|
||||
WRITE4(sc, IMX_GPIO_ISR_REG, (1U << isrc->isrc_data));
|
||||
WRITE4(sc, IMX_GPIO_ISR_REG, (1U << irq));
|
||||
}
|
||||
|
||||
static void
|
||||
@ -254,119 +380,21 @@ gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
|
||||
arm_irq_memory_barrier(0);
|
||||
gpio_pic_enable_source(dev, isrc);
|
||||
gpio_pic_enable_intr(dev, isrc);
|
||||
}
|
||||
|
||||
static void
|
||||
gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
|
||||
gpio_pic_disable_source(dev, isrc);
|
||||
}
|
||||
|
||||
/*
|
||||
* intrng calls this to make a new isrc known to us.
|
||||
*/
|
||||
static int
|
||||
gpio_pic_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu)
|
||||
{
|
||||
struct imx51_gpio_softc *sc;
|
||||
u_int irq, tripol;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
/*
|
||||
* From devicetree/bindings/gpio/fsl-imx-gpio.txt:
|
||||
* #interrupt-cells: 2. The first cell is the GPIO number. The second
|
||||
* cell bits[3:0] is used to specify trigger type and level flags:
|
||||
* 1 = low-to-high edge triggered.
|
||||
* 2 = high-to-low edge triggered.
|
||||
* 4 = active high level-sensitive.
|
||||
* 8 = active low level-sensitive.
|
||||
* We can do any single one of these modes, but nothing in combo.
|
||||
*/
|
||||
|
||||
if (isrc->isrc_ncells != 2) {
|
||||
device_printf(sc->dev, "Invalid #interrupt-cells");
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
irq = isrc->isrc_cells[0];
|
||||
tripol = isrc->isrc_cells[1];
|
||||
if (irq >= sc->gpio_npins) {
|
||||
device_printf(sc->dev, "Invalid interrupt number %d", irq);
|
||||
return (EINVAL);
|
||||
}
|
||||
switch (tripol)
|
||||
{
|
||||
case 1:
|
||||
isrc->isrc_trig = INTR_TRIGGER_EDGE;
|
||||
isrc->isrc_pol = INTR_POLARITY_HIGH;
|
||||
break;
|
||||
case 2:
|
||||
isrc->isrc_trig = INTR_TRIGGER_EDGE;
|
||||
isrc->isrc_pol = INTR_POLARITY_LOW;
|
||||
break;
|
||||
case 4:
|
||||
isrc->isrc_trig = INTR_TRIGGER_LEVEL;
|
||||
isrc->isrc_pol = INTR_POLARITY_HIGH;
|
||||
break;
|
||||
case 8:
|
||||
isrc->isrc_trig = INTR_TRIGGER_LEVEL;
|
||||
isrc->isrc_pol = INTR_POLARITY_LOW;
|
||||
break;
|
||||
default:
|
||||
device_printf(sc->dev, "unsupported trigger/polarity 0x%2x\n",
|
||||
tripol);
|
||||
return (ENOTSUP);
|
||||
}
|
||||
isrc->isrc_nspc_type = INTR_IRQ_NSPC_PLAIN;
|
||||
isrc->isrc_nspc_num = irq;
|
||||
|
||||
/*
|
||||
* 1. The link between ISRC and controller must be set atomically.
|
||||
* 2. Just do things only once in rare case when consumers
|
||||
* of shared interrupt came here at the same moment.
|
||||
*/
|
||||
mtx_lock_spin(&sc->sc_mtx);
|
||||
if (sc->gpio_pic_irqsrc[irq] != NULL) {
|
||||
mtx_unlock_spin(&sc->sc_mtx);
|
||||
return (sc->gpio_pic_irqsrc[irq] == isrc ? 0 : EEXIST);
|
||||
}
|
||||
sc->gpio_pic_irqsrc[irq] = isrc;
|
||||
isrc->isrc_data = irq;
|
||||
mtx_unlock_spin(&sc->sc_mtx);
|
||||
|
||||
intr_irq_set_name(isrc, "%s,%u", device_get_nameunit(sc->dev), irq);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
gpio_pic_unregister(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct imx51_gpio_softc *sc;
|
||||
u_int irq;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
mtx_lock_spin(&sc->sc_mtx);
|
||||
irq = isrc->isrc_data;
|
||||
if (sc->gpio_pic_irqsrc[irq] != isrc) {
|
||||
mtx_unlock_spin(&sc->sc_mtx);
|
||||
return (sc->gpio_pic_irqsrc[irq] == NULL ? 0 : EINVAL);
|
||||
}
|
||||
sc->gpio_pic_irqsrc[irq] = NULL;
|
||||
isrc->isrc_data = 0;
|
||||
mtx_unlock_spin(&sc->sc_mtx);
|
||||
|
||||
intr_irq_set_name(isrc, "");
|
||||
return (0);
|
||||
gpio_pic_disable_intr(dev, isrc);
|
||||
}
|
||||
|
||||
static int
|
||||
gpio_pic_filter(void *arg)
|
||||
{
|
||||
struct imx51_gpio_softc *sc;
|
||||
struct intr_irqsrc *isrc;
|
||||
uint32_t i, interrupts;
|
||||
|
||||
sc = arg;
|
||||
@ -377,14 +405,43 @@ gpio_pic_filter(void *arg)
|
||||
for (i = 0; interrupts != 0; i++, interrupts >>= 1) {
|
||||
if ((interrupts & 0x1) == 0)
|
||||
continue;
|
||||
if (sc->gpio_pic_irqsrc[i])
|
||||
intr_irq_dispatch(sc->gpio_pic_irqsrc[i], curthread->td_intr_frame);
|
||||
else
|
||||
device_printf(sc->dev, "spurious interrupt %d\n", i);
|
||||
isrc = &sc->gpio_pic_irqsrc[i].gi_isrc;
|
||||
if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) {
|
||||
gpio_pic_disable_intr(sc->dev, isrc);
|
||||
gpio_pic_post_filter(sc->dev, isrc);
|
||||
device_printf(sc->dev, "Stray irq %u disabled\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
return (FILTER_HANDLED);
|
||||
}
|
||||
|
||||
/*
|
||||
* register our isrcs into intrng to make it known about them.
|
||||
*/
|
||||
static int
|
||||
gpio_pic_register_isrcs(struct imx51_gpio_softc *sc)
|
||||
{
|
||||
int error;
|
||||
uint32_t irq;
|
||||
const char *name;
|
||||
|
||||
name = device_get_nameunit(sc->dev);
|
||||
for (irq = 0; irq < NGPIO; irq++) {
|
||||
sc->gpio_pic_irqsrc[irq].gi_irq = irq;
|
||||
sc->gpio_pic_irqsrc[irq].gi_pol = INTR_POLARITY_CONFORM;
|
||||
sc->gpio_pic_irqsrc[irq].gi_trig = INTR_TRIGGER_CONFORM;
|
||||
|
||||
error = intr_isrc_register(&sc->gpio_pic_irqsrc[irq].gi_isrc,
|
||||
sc->dev, 0, "%s,%u", name, irq);
|
||||
if (error != 0) {
|
||||
/* XXX call intr_isrc_deregister() */
|
||||
device_printf(sc->dev, "%s failed", __func__);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -656,6 +713,7 @@ imx51_gpio_attach(device_t dev)
|
||||
}
|
||||
|
||||
#ifdef ARM_INTRNG
|
||||
gpio_pic_register_isrcs(sc);
|
||||
intr_pic_register(dev, OF_xref_from_node(ofw_bus_get_node(dev)));
|
||||
#endif
|
||||
sc->sc_busdev = gpiobus_attach_bus(dev);
|
||||
@ -695,14 +753,13 @@ static device_method_t imx51_gpio_methods[] = {
|
||||
#ifdef ARM_INTRNG
|
||||
/* Interrupt controller interface */
|
||||
DEVMETHOD(pic_disable_intr, gpio_pic_disable_intr),
|
||||
DEVMETHOD(pic_disable_source, gpio_pic_disable_source),
|
||||
DEVMETHOD(pic_enable_intr, gpio_pic_enable_intr),
|
||||
DEVMETHOD(pic_enable_source, gpio_pic_enable_source),
|
||||
DEVMETHOD(pic_map_intr, gpio_pic_map_intr),
|
||||
DEVMETHOD(pic_setup_intr, gpio_pic_setup_intr),
|
||||
DEVMETHOD(pic_teardown_intr, gpio_pic_teardown_intr),
|
||||
DEVMETHOD(pic_post_filter, gpio_pic_post_filter),
|
||||
DEVMETHOD(pic_post_ithread, gpio_pic_post_ithread),
|
||||
DEVMETHOD(pic_pre_ithread, gpio_pic_pre_ithread),
|
||||
DEVMETHOD(pic_register, gpio_pic_register),
|
||||
DEVMETHOD(pic_unregister, gpio_pic_unregister),
|
||||
#endif
|
||||
|
||||
/* GPIO protocol */
|
||||
|
@ -52,7 +52,7 @@
|
||||
#include <sys/intr.h>
|
||||
|
||||
#ifdef SMP
|
||||
typedef void intr_ipi_send_t(void *, cpuset_t);
|
||||
typedef void intr_ipi_send_t(void *, cpuset_t, u_int);
|
||||
typedef void intr_ipi_handler_t(void *);
|
||||
|
||||
void intr_ipi_dispatch(u_int, struct trapframe *);
|
||||
|
@ -98,6 +98,13 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#define MPIC_PPI 32
|
||||
|
||||
#ifdef ARM_INTRNG
|
||||
struct mv_mpic_irqsrc {
|
||||
struct intr_irqsrc mmi_isrc;
|
||||
u_int mmi_irq;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct mv_mpic_softc {
|
||||
device_t sc_dev;
|
||||
struct resource * mpic_res[4];
|
||||
@ -108,8 +115,9 @@ struct mv_mpic_softc {
|
||||
bus_space_tag_t drbl_bst;
|
||||
bus_space_handle_t drbl_bsh;
|
||||
struct mtx mtx;
|
||||
|
||||
struct intr_irqsrc ** mpic_isrcs;
|
||||
#ifdef ARM_INTRNG
|
||||
struct mv_mpic_irqsrc * mpic_isrcs;
|
||||
#endif
|
||||
int nirqs;
|
||||
void * intr_hand;
|
||||
};
|
||||
@ -177,6 +185,40 @@ mv_mpic_probe(device_t dev)
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef ARM_INTRNG
|
||||
static int
|
||||
mv_mpic_register_isrcs(struct mv_mpic_softc *sc)
|
||||
{
|
||||
int error;
|
||||
uint32_t irq;
|
||||
struct intr_irqsrc *isrc;
|
||||
const char *name;
|
||||
|
||||
sc->mpic_isrcs = malloc(sc->nirqs * sizeof (*sc->mpic_isrcs), M_DEVBUF,
|
||||
M_WAITOK | M_ZERO);
|
||||
|
||||
name = device_get_nameunit(sc->sc_dev);
|
||||
for (irq = 0; irq < sc->nirqs; irq++) {
|
||||
sc->mpic_isrcs[irq].mmi_irq = irq;
|
||||
|
||||
isrc = &sc->mpic_isrcs[irq].mmi_isrc;
|
||||
if (irq < MPIC_PPI) {
|
||||
error = intr_isrc_register(isrc, sc->sc_dev,
|
||||
INTR_ISRCF_PPI, "%s", name);
|
||||
} else {
|
||||
error = intr_isrc_register(isrc, sc->sc_dev, 0, "%s",
|
||||
name);
|
||||
}
|
||||
if (error != 0) {
|
||||
/* XXX call intr_isrc_deregister() */
|
||||
device_printf(sc->sc_dev, "%s failed", __func__);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
mv_mpic_attach(device_t dev)
|
||||
{
|
||||
@ -227,9 +269,11 @@ mv_mpic_attach(device_t dev)
|
||||
sc->nirqs = MPIC_CTRL_NIRQS(val);
|
||||
|
||||
#ifdef ARM_INTRNG
|
||||
sc->mpic_isrcs = malloc(sc->nirqs * sizeof (*sc->mpic_isrcs), M_DEVBUF,
|
||||
M_WAITOK | M_ZERO);
|
||||
|
||||
if (mv_mpic_register_isrcs(sc) != 0) {
|
||||
device_printf(dev, "could not register PIC ISRCs\n");
|
||||
bus_release_resources(dev, mv_mpic_spec, sc->mpic_res);
|
||||
return (ENXIO);
|
||||
}
|
||||
if (intr_pic_register(dev, OF_xref_from_device(dev)) != 0) {
|
||||
device_printf(dev, "could not register PIC\n");
|
||||
bus_release_resources(dev, mv_mpic_spec, sc->mpic_res);
|
||||
@ -247,14 +291,11 @@ static int
|
||||
mpic_intr(void *arg)
|
||||
{
|
||||
struct mv_mpic_softc *sc;
|
||||
struct trapframe *tf;
|
||||
struct intr_irqsrc *isrc;
|
||||
uint32_t cause, irqsrc;
|
||||
unsigned int irq;
|
||||
u_int cpuid;
|
||||
|
||||
sc = arg;
|
||||
tf = curthread->td_intr_frame;
|
||||
cpuid = PCPU_GET(cpuid);
|
||||
irq = 0;
|
||||
|
||||
@ -264,117 +305,64 @@ mpic_intr(void *arg)
|
||||
irqsrc = MPIC_READ(sc, MPIC_INT_CTL(irq));
|
||||
if ((irqsrc & MPIC_INT_IRQ_FIQ_MASK(cpuid)) == 0)
|
||||
continue;
|
||||
isrc = sc->mpic_isrcs[irq];
|
||||
if (isrc == NULL) {
|
||||
device_printf(sc->sc_dev, "Stray interrupt %u detected\n", irq);
|
||||
if (intr_isrc_dispatch(&sc->mpic_isrcs[irq].mmi_isrc,
|
||||
curthread->td_intr_frame) != 0) {
|
||||
mpic_mask_irq(irq);
|
||||
continue;
|
||||
device_printf(sc->sc_dev, "Stray irq %u "
|
||||
"disabled\n", irq);
|
||||
}
|
||||
intr_irq_dispatch(isrc, tf);
|
||||
}
|
||||
}
|
||||
|
||||
return (FILTER_HANDLED);
|
||||
}
|
||||
|
||||
static int
|
||||
mpic_attach_isrc(struct mv_mpic_softc *sc, struct intr_irqsrc *isrc, u_int irq)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
mtx_lock_spin(&sc->mtx);
|
||||
if (sc->mpic_isrcs[irq] != NULL) {
|
||||
mtx_unlock_spin(&sc->mtx);
|
||||
return (sc->mpic_isrcs[irq] == isrc ? 0 : EEXIST);
|
||||
}
|
||||
sc->mpic_isrcs[irq] = isrc;
|
||||
isrc->isrc_data = irq;
|
||||
mtx_unlock_spin(&sc->mtx);
|
||||
|
||||
name = device_get_nameunit(sc->sc_dev);
|
||||
intr_irq_set_name(isrc, "%s", name);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef FDT
|
||||
static int
|
||||
mpic_map_fdt(struct mv_mpic_softc *sc, struct intr_irqsrc *isrc, u_int *irqp)
|
||||
{
|
||||
u_int irq;
|
||||
int error;
|
||||
|
||||
if (isrc->isrc_ncells != 1)
|
||||
return (EINVAL);
|
||||
|
||||
irq = isrc->isrc_cells[0];
|
||||
|
||||
error = mpic_attach_isrc(sc, isrc, irq);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
isrc->isrc_nspc_num = irq;
|
||||
isrc->isrc_trig = INTR_TRIGGER_CONFORM;
|
||||
isrc->isrc_pol = INTR_POLARITY_CONFORM;
|
||||
isrc->isrc_nspc_type = INTR_IRQ_NSPC_PLAIN;
|
||||
|
||||
*irqp = irq;
|
||||
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
mpic_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu)
|
||||
{
|
||||
struct mv_mpic_softc *sc;
|
||||
int error;
|
||||
u_int irq = 0;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
#ifdef FDT
|
||||
if (isrc->isrc_type == INTR_ISRCT_FDT)
|
||||
error = mpic_map_fdt(sc, isrc, &irq);
|
||||
else
|
||||
#endif
|
||||
error = EINVAL;
|
||||
|
||||
if (error == 0)
|
||||
*is_percpu = irq < MPIC_PPI;
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static void
|
||||
mpic_disable_source(device_t dev, struct intr_irqsrc *isrc)
|
||||
mpic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
u_int irq;
|
||||
|
||||
irq = isrc->isrc_data;
|
||||
irq = ((struct mv_mpic_irqsrc *)isrc)->mmi_irq;
|
||||
mpic_mask_irq(irq);
|
||||
}
|
||||
|
||||
static void
|
||||
mpic_enable_source(device_t dev, struct intr_irqsrc *isrc)
|
||||
mpic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
u_int irq;
|
||||
|
||||
irq = isrc->isrc_data;
|
||||
irq = ((struct mv_mpic_irqsrc *)isrc)->mmi_irq;
|
||||
mpic_unmask_irq(irq);
|
||||
}
|
||||
|
||||
static int
|
||||
mpic_map_intr(device_t dev, struct intr_map_data *data,
|
||||
struct intr_irqsrc **isrcp)
|
||||
{
|
||||
struct mv_mpic_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
if (data->type != INTR_MAP_DATA_FDT || data->fdt.ncells !=1 ||
|
||||
data->fdt.cells[0] >= sc->nirqs)
|
||||
return (EINVAL);
|
||||
|
||||
*isrcp = &sc->mpic_isrcs[data->fdt.cells[0]].mmi_isrc;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
mpic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
|
||||
mpic_disable_source(dev, isrc);
|
||||
mpic_disable_intr(dev, isrc);
|
||||
}
|
||||
|
||||
static void
|
||||
mpic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
|
||||
mpic_enable_source(dev, isrc);
|
||||
mpic_enable_intr(dev, isrc);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -383,9 +371,9 @@ static device_method_t mv_mpic_methods[] = {
|
||||
DEVMETHOD(device_attach, mv_mpic_attach),
|
||||
|
||||
#ifdef ARM_INTRNG
|
||||
DEVMETHOD(pic_register, mpic_register),
|
||||
DEVMETHOD(pic_disable_source, mpic_disable_source),
|
||||
DEVMETHOD(pic_enable_source, mpic_enable_source),
|
||||
DEVMETHOD(pic_disable_intr, mpic_disable_intr),
|
||||
DEVMETHOD(pic_enable_intr, mpic_enable_intr),
|
||||
DEVMETHOD(pic_map_intr, mpic_map_intr),
|
||||
DEVMETHOD(pic_post_ithread, mpic_post_ithread),
|
||||
DEVMETHOD(pic_pre_ithread, mpic_pre_ithread),
|
||||
#endif
|
||||
|
@ -88,35 +88,20 @@ struct tegra_lic_sc {
|
||||
};
|
||||
|
||||
static int
|
||||
tegra_lic_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu)
|
||||
tegra_lic_alloc_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
struct resource *res, struct intr_map_data *data)
|
||||
{
|
||||
struct tegra_lic_sc *sc = device_get_softc(dev);
|
||||
|
||||
return (PIC_REGISTER(sc->parent, isrc, is_percpu));
|
||||
}
|
||||
|
||||
static int
|
||||
tegra_lic_unregister(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct tegra_lic_sc *sc = device_get_softc(dev);
|
||||
|
||||
return (PIC_UNREGISTER(sc->parent, isrc));
|
||||
return (PIC_ALLOC_INTR(sc->parent, isrc, res, data));
|
||||
}
|
||||
|
||||
static void
|
||||
tegra_lic_enable_source(device_t dev, struct intr_irqsrc *isrc)
|
||||
tegra_lic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct tegra_lic_sc *sc = device_get_softc(dev);
|
||||
|
||||
PIC_ENABLE_SOURCE(sc->parent, isrc);
|
||||
}
|
||||
|
||||
static void
|
||||
tegra_lic_disable_source(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct tegra_lic_sc *sc = device_get_softc(dev);
|
||||
|
||||
PIC_DISABLE_SOURCE(sc->parent, isrc);
|
||||
PIC_DISABLE_INTR(sc->parent, isrc);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -127,6 +112,42 @@ tegra_lic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
PIC_ENABLE_INTR(sc->parent, isrc);
|
||||
}
|
||||
|
||||
static int
|
||||
tegra_lic_map_intr(device_t dev, struct intr_map_data *data,
|
||||
struct intr_irqsrc **isrcp)
|
||||
{
|
||||
struct tegra_lic_sc *sc = device_get_softc(dev);
|
||||
|
||||
return (PIC_MAP_INTR(sc->parent, data, isrcp));
|
||||
}
|
||||
|
||||
static int
|
||||
tegra_lic_release_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
struct resource *res, struct intr_map_data *data)
|
||||
{
|
||||
struct tegra_lic_sc *sc = device_get_softc(dev);
|
||||
|
||||
return (PIC_RELEASE_INTR(sc->parent, isrc, res, data));
|
||||
}
|
||||
|
||||
static int
|
||||
tegra_lic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
struct resource *res, struct intr_map_data *data)
|
||||
{
|
||||
struct tegra_lic_sc *sc = device_get_softc(dev);
|
||||
|
||||
return (PIC_SETUP_INTR(sc->parent, isrc, res, data));
|
||||
}
|
||||
|
||||
static int
|
||||
tegra_lic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
struct resource *res, struct intr_map_data *data)
|
||||
{
|
||||
struct tegra_lic_sc *sc = device_get_softc(dev);
|
||||
|
||||
return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data));
|
||||
}
|
||||
|
||||
static void
|
||||
tegra_lic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
@ -154,11 +175,11 @@ tegra_lic_post_filter(device_t dev, struct intr_irqsrc *isrc)
|
||||
|
||||
#ifdef SMP
|
||||
static int
|
||||
tegra_lic_bind(device_t dev, struct intr_irqsrc *isrc)
|
||||
tegra_lic_bind_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct tegra_lic_sc *sc = device_get_softc(dev);
|
||||
|
||||
return (PIC_BIND(sc->parent, isrc));
|
||||
return (PIC_BIND_INTR(sc->parent, isrc));
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -245,16 +266,18 @@ static device_method_t tegra_lic_methods[] = {
|
||||
DEVMETHOD(device_detach, tegra_lic_detach),
|
||||
|
||||
/* Interrupt controller interface */
|
||||
DEVMETHOD(pic_register, tegra_lic_register),
|
||||
DEVMETHOD(pic_unregister, tegra_lic_unregister),
|
||||
DEVMETHOD(pic_enable_source, tegra_lic_enable_source),
|
||||
DEVMETHOD(pic_disable_source, tegra_lic_disable_source),
|
||||
DEVMETHOD(pic_alloc_intr, tegra_lic_alloc_intr),
|
||||
DEVMETHOD(pic_disable_intr, tegra_lic_disable_intr),
|
||||
DEVMETHOD(pic_enable_intr, tegra_lic_enable_intr),
|
||||
DEVMETHOD(pic_map_intr, tegra_lic_map_intr),
|
||||
DEVMETHOD(pic_release_intr, tegra_lic_release_intr),
|
||||
DEVMETHOD(pic_setup_intr, tegra_lic_setup_intr),
|
||||
DEVMETHOD(pic_teardown_intr, tegra_lic_teardown_intr),
|
||||
DEVMETHOD(pic_pre_ithread, tegra_lic_pre_ithread),
|
||||
DEVMETHOD(pic_post_ithread, tegra_lic_post_ithread),
|
||||
DEVMETHOD(pic_post_filter, tegra_lic_post_filter),
|
||||
#ifdef SMP
|
||||
DEVMETHOD(pic_bind, tegra_lic_bind),
|
||||
DEVMETHOD(pic_bind_intr, tegra_lic_bind_intr),
|
||||
#endif
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
@ -57,36 +57,20 @@ struct omap4_wugen_sc {
|
||||
};
|
||||
|
||||
static int
|
||||
omap4_wugen_register(device_t dev, struct intr_irqsrc *isrc,
|
||||
boolean_t *is_percpu)
|
||||
omap4_wugen_alloc_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
struct resource *res, struct intr_map_data *data)
|
||||
{
|
||||
struct omap4_wugen_sc *sc = device_get_softc(dev);
|
||||
|
||||
return (PIC_REGISTER(sc->sc_parent, isrc, is_percpu));
|
||||
}
|
||||
|
||||
static int
|
||||
omap4_wugen_unregister(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct omap4_wugen_sc *sc = device_get_softc(dev);
|
||||
|
||||
return (PIC_UNREGISTER(sc->sc_parent, isrc));
|
||||
return (PIC_ALLOC_INTR(sc->sc_parent, isrc, res, data));
|
||||
}
|
||||
|
||||
static void
|
||||
omap4_wugen_enable_source(device_t dev, struct intr_irqsrc *isrc)
|
||||
omap4_wugen_disable_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct omap4_wugen_sc *sc = device_get_softc(dev);
|
||||
|
||||
PIC_ENABLE_SOURCE(sc->sc_parent, isrc);
|
||||
}
|
||||
|
||||
static void
|
||||
omap4_wugen_disable_source(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct omap4_wugen_sc *sc = device_get_softc(dev);
|
||||
|
||||
PIC_DISABLE_SOURCE(sc->sc_parent, isrc);
|
||||
PIC_DISABLE_INTR(sc->sc_parent, isrc);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -97,6 +81,42 @@ omap4_wugen_enable_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
PIC_ENABLE_INTR(sc->sc_parent, isrc);
|
||||
}
|
||||
|
||||
static int
|
||||
omap4_wugen_map_intr(device_t dev, struct intr_map_data *data,
|
||||
struct intr_irqsrc **isrcp)
|
||||
{
|
||||
struct omap4_wugen_sc *sc = device_get_softc(dev);
|
||||
|
||||
return (PIC_MAP_INTR(sc->sc_parent, data, isrcp));
|
||||
}
|
||||
|
||||
static int
|
||||
omap4_wugen_release_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
struct resource *res, struct intr_map_data *data)
|
||||
{
|
||||
struct omap4_wugen_sc *sc = device_get_softc(dev);
|
||||
|
||||
return (PIC_RELEASE_INTR(sc->sc_parent, isrc, res, data));
|
||||
}
|
||||
|
||||
static int
|
||||
omap4_wugen_setup_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
struct resource *res, struct intr_map_data *data)
|
||||
{
|
||||
struct omap4_wugen_sc *sc = device_get_softc(dev);
|
||||
|
||||
return (PIC_SETUP_INTR(sc->sc_parent, isrc, res, data));
|
||||
}
|
||||
|
||||
static int
|
||||
omap4_wugen_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
struct resource *res, struct intr_map_data *data)
|
||||
{
|
||||
struct omap4_wugen_sc *sc = device_get_softc(dev);
|
||||
|
||||
return (PIC_TEARDOWN_INTR(sc->sc_parent, isrc, res, data));
|
||||
}
|
||||
|
||||
static void
|
||||
omap4_wugen_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
@ -124,11 +144,11 @@ omap4_wugen_post_filter(device_t dev, struct intr_irqsrc *isrc)
|
||||
|
||||
#ifdef SMP
|
||||
static int
|
||||
omap4_wugen_bind(device_t dev, struct intr_irqsrc *isrc)
|
||||
omap4_wugen_bind_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct omap4_wugen_sc *sc = device_get_softc(dev);
|
||||
|
||||
return (PIC_BIND(sc->sc_parent, isrc));
|
||||
return (PIC_BIND_INTR(sc->sc_parent, isrc));
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -207,16 +227,18 @@ static device_method_t omap4_wugen_methods[] = {
|
||||
DEVMETHOD(device_detach, omap4_wugen_detach),
|
||||
|
||||
/* Interrupt controller interface */
|
||||
DEVMETHOD(pic_register, omap4_wugen_register),
|
||||
DEVMETHOD(pic_unregister, omap4_wugen_unregister),
|
||||
DEVMETHOD(pic_enable_source, omap4_wugen_enable_source),
|
||||
DEVMETHOD(pic_disable_source, omap4_wugen_disable_source),
|
||||
DEVMETHOD(pic_alloc_intr, omap4_wugen_alloc_intr),
|
||||
DEVMETHOD(pic_disable_intr, omap4_wugen_disable_intr),
|
||||
DEVMETHOD(pic_enable_intr, omap4_wugen_enable_intr),
|
||||
DEVMETHOD(pic_map_intr, omap4_wugen_map_intr),
|
||||
DEVMETHOD(pic_release_intr, omap4_wugen_release_intr),
|
||||
DEVMETHOD(pic_setup_intr, omap4_wugen_setup_intr),
|
||||
DEVMETHOD(pic_teardown_intr, omap4_wugen_teardown_intr),
|
||||
DEVMETHOD(pic_pre_ithread, omap4_wugen_pre_ithread),
|
||||
DEVMETHOD(pic_post_ithread, omap4_wugen_post_ithread),
|
||||
DEVMETHOD(pic_post_filter, omap4_wugen_post_filter),
|
||||
#ifdef SMP
|
||||
DEVMETHOD(pic_bind, omap4_wugen_bind),
|
||||
DEVMETHOD(pic_bind_intr, omap4_wugen_bind_intr),
|
||||
#endif
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
@ -1,7 +1,6 @@
|
||||
#-
|
||||
# Copyright (c) 2012 Jakub Wojciech Klama <jceel@FreeBSD.org>
|
||||
# Copyright (c) 2015 Svatopluk Kraus
|
||||
# Copyright (c) 2015 Michal Meloun
|
||||
# Copyright (c) 2015-2016 Svatopluk Kraus
|
||||
# Copyright (c) 2015-2016 Michal Meloun
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -30,35 +29,59 @@
|
||||
|
||||
#include <sys/bus.h>
|
||||
#include <sys/cpuset.h>
|
||||
#include <machine/frame.h>
|
||||
#include <machine/intr.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/intr.h>
|
||||
|
||||
INTERFACE pic;
|
||||
|
||||
CODE {
|
||||
static int null_pic_bind(device_t dev, struct intr_irqsrc *isrc)
|
||||
static int
|
||||
dflt_pic_bind_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
{
|
||||
|
||||
return (EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static void null_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
static int
|
||||
null_pic_alloc_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
struct resource *res, struct intr_map_data *data)
|
||||
{
|
||||
return;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void null_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
|
||||
static int
|
||||
null_pic_release_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
struct resource *res, struct intr_map_data *data)
|
||||
{
|
||||
return;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void null_pic_init_secondary(device_t dev)
|
||||
static int
|
||||
null_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
struct resource *res, struct intr_map_data *data)
|
||||
{
|
||||
return;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void null_pic_ipi_send(device_t dev, cpuset_t cpus, u_int ipi)
|
||||
static int
|
||||
null_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
struct resource *res, struct intr_map_data *data)
|
||||
{
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
null_pic_init_secondary(device_t dev)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
null_pic_ipi_send(device_t dev, cpuset_t cpus, u_int ipi)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -69,38 +92,56 @@ CODE {
|
||||
}
|
||||
};
|
||||
|
||||
METHOD int register {
|
||||
METHOD int alloc_intr {
|
||||
device_t dev;
|
||||
struct intr_irqsrc *isrc;
|
||||
boolean_t *is_percpu;
|
||||
};
|
||||
struct resource *res;
|
||||
struct intr_map_data *data;
|
||||
} DEFAULT null_pic_alloc_intr;
|
||||
|
||||
METHOD int unregister {
|
||||
METHOD int bind_intr {
|
||||
device_t dev;
|
||||
struct intr_irqsrc *isrc;
|
||||
};
|
||||
} DEFAULT dflt_pic_bind_intr;
|
||||
|
||||
METHOD void disable_intr {
|
||||
device_t dev;
|
||||
struct intr_irqsrc *isrc;
|
||||
} DEFAULT null_pic_disable_intr;
|
||||
|
||||
METHOD void disable_source {
|
||||
device_t dev;
|
||||
struct intr_irqsrc *isrc;
|
||||
};
|
||||
|
||||
METHOD void enable_source {
|
||||
device_t dev;
|
||||
struct intr_irqsrc *isrc;
|
||||
};
|
||||
|
||||
METHOD void enable_intr {
|
||||
device_t dev;
|
||||
struct intr_irqsrc *isrc;
|
||||
} DEFAULT null_pic_enable_intr;
|
||||
};
|
||||
|
||||
METHOD void pre_ithread {
|
||||
METHOD int map_intr {
|
||||
device_t dev;
|
||||
struct intr_map_data *data;
|
||||
struct intr_irqsrc **isrcp;
|
||||
};
|
||||
|
||||
METHOD int release_intr {
|
||||
device_t dev;
|
||||
struct intr_irqsrc *isrc;
|
||||
struct resource *res;
|
||||
struct intr_map_data *data;
|
||||
} DEFAULT null_pic_release_intr;
|
||||
|
||||
METHOD int setup_intr {
|
||||
device_t dev;
|
||||
struct intr_irqsrc *isrc;
|
||||
struct resource *res;
|
||||
struct intr_map_data *data;
|
||||
} DEFAULT null_pic_setup_intr;
|
||||
|
||||
METHOD int teardown_intr {
|
||||
device_t dev;
|
||||
struct intr_irqsrc *isrc;
|
||||
struct resource *res;
|
||||
struct intr_map_data *data;
|
||||
} DEFAULT null_pic_teardown_intr;
|
||||
|
||||
METHOD void post_filter {
|
||||
device_t dev;
|
||||
struct intr_irqsrc *isrc;
|
||||
};
|
||||
@ -110,16 +151,11 @@ METHOD void post_ithread {
|
||||
struct intr_irqsrc *isrc;
|
||||
};
|
||||
|
||||
METHOD void post_filter {
|
||||
METHOD void pre_ithread {
|
||||
device_t dev;
|
||||
struct intr_irqsrc *isrc;
|
||||
};
|
||||
|
||||
METHOD int bind {
|
||||
device_t dev;
|
||||
struct intr_irqsrc *isrc;
|
||||
} DEFAULT null_pic_bind;
|
||||
|
||||
METHOD void init_secondary {
|
||||
device_t dev;
|
||||
} DEFAULT null_pic_init_secondary;
|
||||
@ -128,10 +164,11 @@ METHOD void ipi_send {
|
||||
device_t dev;
|
||||
struct intr_irqsrc *isrc;
|
||||
cpuset_t cpus;
|
||||
u_int ipi;
|
||||
} DEFAULT null_pic_ipi_send;
|
||||
|
||||
METHOD int ipi_setup {
|
||||
device_t dev;
|
||||
u_int ipi;
|
||||
struct intr_irqsrc *isrc;
|
||||
struct intr_irqsrc **isrcp;
|
||||
} DEFAULT dflt_pic_ipi_setup;
|
||||
|
@ -1,7 +1,6 @@
|
||||
/*-
|
||||
* Copyright (c) 2012-2014 Jakub Wojciech Klama <jceel@FreeBSD.org>.
|
||||
* Copyright (c) 2015 Svatopluk Kraus
|
||||
* Copyright (c) 2015 Michal Meloun
|
||||
* Copyright (c) 2015-2016 Svatopluk Kraus
|
||||
* Copyright (c) 2015-2016 Michal Meloun
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -24,8 +23,6 @@
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
@ -38,6 +35,7 @@ __FBSDID("$FreeBSD$");
|
||||
* - to complete things for removable PICs
|
||||
*/
|
||||
|
||||
#include "opt_acpi.h"
|
||||
#include "opt_ddb.h"
|
||||
#include "opt_platform.h"
|
||||
|
||||
@ -112,6 +110,35 @@ u_int irq_next_free;
|
||||
|
||||
#define IRQ_INVALID nitems(irq_sources)
|
||||
|
||||
/*
|
||||
* XXX - All stuff around struct intr_dev_data is considered as temporary
|
||||
* until better place for storing struct intr_map_data will be find.
|
||||
*
|
||||
* For now, there are two global interrupt numbers spaces:
|
||||
* <0, NIRQ) ... interrupts without config data
|
||||
* managed in irq_sources[]
|
||||
* IRQ_DDATA_BASE + <0, 2 * NIRQ) ... interrupts with config data
|
||||
* managed in intr_ddata_tab[]
|
||||
*
|
||||
* Read intr_ddata_lookup() to see how these spaces are worked with.
|
||||
* Note that each interrupt number from second space duplicates some number
|
||||
* from first space at this moment. An interrupt number from first space can
|
||||
* be duplicated even multiple times in second space.
|
||||
*/
|
||||
struct intr_dev_data {
|
||||
device_t idd_dev;
|
||||
intptr_t idd_xref;
|
||||
u_int idd_irq;
|
||||
struct intr_map_data idd_data;
|
||||
struct intr_irqsrc * idd_isrc;
|
||||
};
|
||||
|
||||
static struct intr_dev_data *intr_ddata_tab[2 * NIRQ];
|
||||
static u_int intr_ddata_first_unused;
|
||||
|
||||
#define IRQ_DDATA_BASE 10000
|
||||
CTASSERT(IRQ_DDATA_BASE > IRQ_INVALID);
|
||||
|
||||
#ifdef SMP
|
||||
static boolean_t irq_assign_cpu = FALSE;
|
||||
#endif
|
||||
@ -173,12 +200,10 @@ static inline void
|
||||
isrc_increment_count(struct intr_irqsrc *isrc)
|
||||
{
|
||||
|
||||
/*
|
||||
* XXX - It should be atomic for PPI interrupts. It was proven that
|
||||
* the lost is measurable easily for timer PPI interrupts.
|
||||
*/
|
||||
isrc->isrc_count[0]++;
|
||||
/*atomic_add_long(&isrc->isrc_count[0], 1);*/
|
||||
if (isrc->isrc_flags & INTR_ISRCF_PPI)
|
||||
atomic_add_long(&isrc->isrc_count[0], 1);
|
||||
else
|
||||
isrc->isrc_count[0]++;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -233,6 +258,16 @@ isrc_setup_counters(struct intr_irqsrc *isrc)
|
||||
isrc_update_name(isrc, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Virtualization for interrupt source interrupt counters release.
|
||||
*/
|
||||
static void
|
||||
isrc_release_counters(struct intr_irqsrc *isrc)
|
||||
{
|
||||
|
||||
panic("%s: not implemented", __func__);
|
||||
}
|
||||
|
||||
#ifdef SMP
|
||||
/*
|
||||
* Virtualization for interrupt source IPI counters setup.
|
||||
@ -279,8 +314,8 @@ intr_irq_handler(struct trapframe *tf)
|
||||
* be called straight from the interrupt controller, when associated interrupt
|
||||
* source is learned.
|
||||
*/
|
||||
void
|
||||
intr_irq_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf)
|
||||
int
|
||||
intr_isrc_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf)
|
||||
{
|
||||
|
||||
KASSERT(isrc != NULL, ("%s: no source", __func__));
|
||||
@ -293,57 +328,16 @@ intr_irq_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf)
|
||||
error = isrc->isrc_filter(isrc->isrc_arg, tf);
|
||||
PIC_POST_FILTER(isrc->isrc_dev, isrc);
|
||||
if (error == FILTER_HANDLED)
|
||||
return;
|
||||
} else
|
||||
return (0);
|
||||
} else
|
||||
#endif
|
||||
if (isrc->isrc_event != NULL) {
|
||||
if (intr_event_handle(isrc->isrc_event, tf) == 0)
|
||||
return;
|
||||
return (0);
|
||||
}
|
||||
|
||||
isrc_increment_straycount(isrc);
|
||||
PIC_DISABLE_SOURCE(isrc->isrc_dev, isrc);
|
||||
|
||||
device_printf(isrc->isrc_dev, "stray irq <%s> disabled",
|
||||
isrc->isrc_name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate interrupt source.
|
||||
*/
|
||||
struct intr_irqsrc *
|
||||
intr_isrc_alloc(u_int type, u_int extsize)
|
||||
{
|
||||
struct intr_irqsrc *isrc;
|
||||
|
||||
isrc = malloc(sizeof(*isrc) + extsize, M_INTRNG, M_WAITOK | M_ZERO);
|
||||
isrc->isrc_irq = IRQ_INVALID; /* just to be safe */
|
||||
isrc->isrc_type = type;
|
||||
isrc->isrc_nspc_type = INTR_IRQ_NSPC_NONE;
|
||||
isrc->isrc_trig = INTR_TRIGGER_CONFORM;
|
||||
isrc->isrc_pol = INTR_POLARITY_CONFORM;
|
||||
CPU_ZERO(&isrc->isrc_cpu);
|
||||
return (isrc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Free interrupt source.
|
||||
*/
|
||||
void
|
||||
intr_isrc_free(struct intr_irqsrc *isrc)
|
||||
{
|
||||
|
||||
free(isrc, M_INTRNG);
|
||||
}
|
||||
|
||||
void
|
||||
intr_irq_set_name(struct intr_irqsrc *isrc, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(isrc->isrc_name, INTR_ISRC_NAMELEN, fmt, ap);
|
||||
va_end(ap);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -356,8 +350,8 @@ intr_irq_set_name(struct intr_irqsrc *isrc, const char *fmt, ...)
|
||||
* immediately. However, if only one free handle left which is reused
|
||||
* constantly...
|
||||
*/
|
||||
static int
|
||||
isrc_alloc_irq_locked(struct intr_irqsrc *isrc)
|
||||
static inline int
|
||||
isrc_alloc_irq(struct intr_irqsrc *isrc)
|
||||
{
|
||||
u_int maxirqs, irq;
|
||||
|
||||
@ -383,46 +377,35 @@ found:
|
||||
isrc->isrc_irq = irq;
|
||||
irq_sources[irq] = isrc;
|
||||
|
||||
intr_irq_set_name(isrc, "irq%u", irq);
|
||||
isrc_setup_counters(isrc);
|
||||
|
||||
irq_next_free = irq + 1;
|
||||
if (irq_next_free >= maxirqs)
|
||||
irq_next_free = 0;
|
||||
return (0);
|
||||
}
|
||||
#ifdef notyet
|
||||
|
||||
/*
|
||||
* Free unique interrupt number (resource handle) from interrupt source.
|
||||
*/
|
||||
static int
|
||||
static inline int
|
||||
isrc_free_irq(struct intr_irqsrc *isrc)
|
||||
{
|
||||
u_int maxirqs;
|
||||
|
||||
mtx_assert(&isrc_table_lock, MA_NOTOWNED);
|
||||
mtx_assert(&isrc_table_lock, MA_OWNED);
|
||||
|
||||
maxirqs = nitems(irq_sources);
|
||||
if (isrc->isrc_irq >= maxirqs)
|
||||
if (isrc->isrc_irq >= nitems(irq_sources))
|
||||
return (EINVAL);
|
||||
|
||||
mtx_lock(&isrc_table_lock);
|
||||
if (irq_sources[isrc->isrc_irq] != isrc) {
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
if (irq_sources[isrc->isrc_irq] != isrc)
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
irq_sources[isrc->isrc_irq] = NULL;
|
||||
isrc->isrc_irq = IRQ_INVALID; /* just to be safe */
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Lookup interrupt source by interrupt number (resource handle).
|
||||
*/
|
||||
static struct intr_irqsrc *
|
||||
static inline struct intr_irqsrc *
|
||||
isrc_lookup(u_int irq)
|
||||
{
|
||||
|
||||
@ -432,84 +415,135 @@ isrc_lookup(u_int irq)
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup interrupt source by namespace description.
|
||||
* Initialize interrupt source and register it into global interrupt table.
|
||||
*/
|
||||
static struct intr_irqsrc *
|
||||
isrc_namespace_lookup(device_t dev, uint16_t type, uint16_t num)
|
||||
int
|
||||
intr_isrc_register(struct intr_irqsrc *isrc, device_t dev, u_int flags,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
u_int irq;
|
||||
struct intr_irqsrc *isrc;
|
||||
int error;
|
||||
va_list ap;
|
||||
|
||||
mtx_assert(&isrc_table_lock, MA_OWNED);
|
||||
bzero(isrc, sizeof(struct intr_irqsrc));
|
||||
isrc->isrc_dev = dev;
|
||||
isrc->isrc_irq = IRQ_INVALID; /* just to be safe */
|
||||
isrc->isrc_flags = flags;
|
||||
|
||||
for (irq = 0; irq < nitems(irq_sources); irq++) {
|
||||
isrc = irq_sources[irq];
|
||||
if (isrc != NULL && isrc->isrc_dev == dev &&
|
||||
isrc->isrc_nspc_type == type && isrc->isrc_nspc_num == num)
|
||||
return (isrc);
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(isrc->isrc_name, INTR_ISRC_NAMELEN, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
mtx_lock(&isrc_table_lock);
|
||||
error = isrc_alloc_irq(isrc);
|
||||
if (error != 0) {
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
return (error);
|
||||
}
|
||||
return (NULL);
|
||||
/*
|
||||
* Setup interrupt counters, but not for IPI sources. Those are setup
|
||||
* later and only for used ones (up to INTR_IPI_COUNT) to not exhaust
|
||||
* our counter pool.
|
||||
*/
|
||||
if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0)
|
||||
isrc_setup_counters(isrc);
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Map interrupt source according to namespace into framework. If such mapping
|
||||
* Deregister interrupt source from global interrupt table.
|
||||
*/
|
||||
int
|
||||
intr_isrc_deregister(struct intr_irqsrc *isrc)
|
||||
{
|
||||
int error;
|
||||
|
||||
mtx_lock(&isrc_table_lock);
|
||||
if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0)
|
||||
isrc_release_counters(isrc);
|
||||
error = isrc_free_irq(isrc);
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static struct intr_dev_data *
|
||||
intr_ddata_alloc(u_int extsize)
|
||||
{
|
||||
struct intr_dev_data *ddata;
|
||||
|
||||
ddata = malloc(sizeof(*ddata) + extsize, M_INTRNG, M_WAITOK | M_ZERO);
|
||||
|
||||
mtx_lock(&isrc_table_lock);
|
||||
if (intr_ddata_first_unused >= nitems(intr_ddata_tab)) {
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
free(ddata, M_INTRNG);
|
||||
return (NULL);
|
||||
}
|
||||
intr_ddata_tab[intr_ddata_first_unused] = ddata;
|
||||
ddata->idd_irq = IRQ_DDATA_BASE + intr_ddata_first_unused++;
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
return (ddata);
|
||||
}
|
||||
|
||||
static struct intr_irqsrc *
|
||||
intr_ddata_lookup(u_int irq, struct intr_map_data **datap)
|
||||
{
|
||||
int error;
|
||||
struct intr_irqsrc *isrc;
|
||||
struct intr_dev_data *ddata;
|
||||
|
||||
isrc = isrc_lookup(irq);
|
||||
if (isrc != NULL) {
|
||||
if (datap != NULL)
|
||||
*datap = NULL;
|
||||
return (isrc);
|
||||
}
|
||||
|
||||
if (irq < IRQ_DDATA_BASE)
|
||||
return (NULL);
|
||||
|
||||
irq -= IRQ_DDATA_BASE;
|
||||
if (irq >= nitems(intr_ddata_tab))
|
||||
return (NULL);
|
||||
|
||||
ddata = intr_ddata_tab[irq];
|
||||
if (ddata->idd_isrc == NULL) {
|
||||
error = intr_map_irq(ddata->idd_dev, ddata->idd_xref,
|
||||
&ddata->idd_data, &irq);
|
||||
if (error != 0)
|
||||
return (NULL);
|
||||
ddata->idd_isrc = isrc_lookup(irq);
|
||||
}
|
||||
if (datap != NULL)
|
||||
*datap = &ddata->idd_data;
|
||||
return (ddata->idd_isrc);
|
||||
}
|
||||
|
||||
#ifdef DEV_ACPI
|
||||
/*
|
||||
* Map interrupt source according to ACPI info into framework. If such mapping
|
||||
* does not exist, create it. Return unique interrupt number (resource handle)
|
||||
* associated with mapped interrupt source.
|
||||
*/
|
||||
u_int
|
||||
intr_namespace_map_irq(device_t dev, uint16_t type, uint16_t num)
|
||||
intr_acpi_map_irq(device_t dev, u_int irq, enum intr_polarity pol,
|
||||
enum intr_trigger trig)
|
||||
{
|
||||
struct intr_irqsrc *isrc, *new_isrc;
|
||||
int error;
|
||||
struct intr_dev_data *ddata;
|
||||
|
||||
new_isrc = intr_isrc_alloc(INTR_ISRCT_NAMESPACE, 0);
|
||||
ddata = intr_ddata_alloc(0);
|
||||
if (ddata == NULL)
|
||||
return (0xFFFFFFFF); /* no space left */
|
||||
|
||||
mtx_lock(&isrc_table_lock);
|
||||
isrc = isrc_namespace_lookup(dev, type, num);
|
||||
if (isrc != NULL) {
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
intr_isrc_free(new_isrc);
|
||||
return (isrc->isrc_irq); /* already mapped */
|
||||
}
|
||||
|
||||
error = isrc_alloc_irq_locked(new_isrc);
|
||||
if (error != 0) {
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
intr_isrc_free(new_isrc);
|
||||
return (IRQ_INVALID); /* no space left */
|
||||
}
|
||||
|
||||
new_isrc->isrc_dev = dev;
|
||||
new_isrc->isrc_nspc_type = type;
|
||||
new_isrc->isrc_nspc_num = num;
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
|
||||
return (new_isrc->isrc_irq);
|
||||
ddata->idd_dev = dev;
|
||||
ddata->idd_data.type = INTR_MAP_DATA_ACPI;
|
||||
ddata->idd_data.acpi.irq = irq;
|
||||
ddata->idd_data.acpi.pol = pol;
|
||||
ddata->idd_data.acpi.trig = trig;
|
||||
return (ddata->idd_irq);
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef FDT
|
||||
/*
|
||||
* Lookup interrupt source by FDT description.
|
||||
*/
|
||||
static struct intr_irqsrc *
|
||||
isrc_fdt_lookup(intptr_t xref, pcell_t *cells, u_int ncells)
|
||||
{
|
||||
u_int irq, cellsize;
|
||||
struct intr_irqsrc *isrc;
|
||||
|
||||
mtx_assert(&isrc_table_lock, MA_OWNED);
|
||||
|
||||
cellsize = ncells * sizeof(*cells);
|
||||
for (irq = 0; irq < nitems(irq_sources); irq++) {
|
||||
isrc = irq_sources[irq];
|
||||
if (isrc != NULL && isrc->isrc_type == INTR_ISRCT_FDT &&
|
||||
isrc->isrc_xref == xref && isrc->isrc_ncells == ncells &&
|
||||
memcmp(isrc->isrc_cells, cells, cellsize) == 0)
|
||||
return (isrc);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Map interrupt source according to FDT data into framework. If such mapping
|
||||
* does not exist, create it. Return unique interrupt number (resource handle)
|
||||
@ -518,73 +552,23 @@ isrc_fdt_lookup(intptr_t xref, pcell_t *cells, u_int ncells)
|
||||
u_int
|
||||
intr_fdt_map_irq(phandle_t node, pcell_t *cells, u_int ncells)
|
||||
{
|
||||
struct intr_irqsrc *isrc, *new_isrc;
|
||||
struct intr_dev_data *ddata;
|
||||
u_int cellsize;
|
||||
intptr_t xref;
|
||||
int error;
|
||||
|
||||
xref = (intptr_t)node; /* It's so simple for now. */
|
||||
|
||||
cellsize = ncells * sizeof(*cells);
|
||||
new_isrc = intr_isrc_alloc(INTR_ISRCT_FDT, cellsize);
|
||||
ddata = intr_ddata_alloc(cellsize);
|
||||
if (ddata == NULL)
|
||||
return (0xFFFFFFFF); /* no space left */
|
||||
|
||||
mtx_lock(&isrc_table_lock);
|
||||
isrc = isrc_fdt_lookup(xref, cells, ncells);
|
||||
if (isrc != NULL) {
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
intr_isrc_free(new_isrc);
|
||||
return (isrc->isrc_irq); /* already mapped */
|
||||
}
|
||||
|
||||
error = isrc_alloc_irq_locked(new_isrc);
|
||||
if (error != 0) {
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
intr_isrc_free(new_isrc);
|
||||
return (IRQ_INVALID); /* no space left */
|
||||
}
|
||||
|
||||
new_isrc->isrc_xref = xref;
|
||||
new_isrc->isrc_ncells = ncells;
|
||||
memcpy(new_isrc->isrc_cells, cells, cellsize);
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
|
||||
return (new_isrc->isrc_irq);
|
||||
ddata->idd_xref = (intptr_t)node;
|
||||
ddata->idd_data.type = INTR_MAP_DATA_FDT;
|
||||
ddata->idd_data.fdt.ncells = ncells;
|
||||
ddata->idd_data.fdt.cells = (pcell_t *)(ddata + 1);
|
||||
memcpy(ddata->idd_data.fdt.cells, cells, cellsize);
|
||||
return (ddata->idd_irq);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Register interrupt source into interrupt controller.
|
||||
*/
|
||||
static int
|
||||
isrc_register(struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct intr_pic *pic;
|
||||
boolean_t is_percpu;
|
||||
int error;
|
||||
|
||||
if (isrc->isrc_flags & INTR_ISRCF_REGISTERED)
|
||||
return (0);
|
||||
|
||||
if (isrc->isrc_dev == NULL) {
|
||||
pic = pic_lookup(NULL, isrc->isrc_xref);
|
||||
if (pic == NULL || pic->pic_dev == NULL)
|
||||
return (ESRCH);
|
||||
isrc->isrc_dev = pic->pic_dev;
|
||||
}
|
||||
|
||||
error = PIC_REGISTER(isrc->isrc_dev, isrc, &is_percpu);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
mtx_lock(&isrc_table_lock);
|
||||
isrc->isrc_flags |= INTR_ISRCF_REGISTERED;
|
||||
if (is_percpu)
|
||||
isrc->isrc_flags |= INTR_ISRCF_PERCPU;
|
||||
isrc_update_name(isrc, NULL);
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef INTR_SOLO
|
||||
/*
|
||||
* Setup filter into interrupt source.
|
||||
@ -678,7 +662,7 @@ intr_isrc_assign_cpu(void *arg, int cpu)
|
||||
* informed if the call is successfull.
|
||||
*/
|
||||
if (irq_assign_cpu) {
|
||||
error = PIC_BIND(isrc->isrc_dev, isrc);
|
||||
error = PIC_BIND_INTR(isrc->isrc_dev, isrc);
|
||||
if (error) {
|
||||
CPU_ZERO(&isrc->isrc_cpu);
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
@ -774,7 +758,7 @@ isrc_add_handler(struct intr_irqsrc *isrc, const char *name,
|
||||
/*
|
||||
* Lookup interrupt controller locked.
|
||||
*/
|
||||
static struct intr_pic *
|
||||
static inline struct intr_pic *
|
||||
pic_lookup_locked(device_t dev, intptr_t xref)
|
||||
{
|
||||
struct intr_pic *pic;
|
||||
@ -801,7 +785,6 @@ pic_lookup(device_t dev, intptr_t xref)
|
||||
mtx_lock(&pic_list_lock);
|
||||
pic = pic_lookup_locked(dev, xref);
|
||||
mtx_unlock(&pic_list_lock);
|
||||
|
||||
return (pic);
|
||||
}
|
||||
|
||||
@ -871,7 +854,7 @@ intr_pic_register(device_t dev, intptr_t xref)
|
||||
* Unregister interrupt controller.
|
||||
*/
|
||||
int
|
||||
intr_pic_unregister(device_t dev, intptr_t xref)
|
||||
intr_pic_deregister(device_t dev, intptr_t xref)
|
||||
{
|
||||
|
||||
panic("%s: not implemented", __func__);
|
||||
@ -923,12 +906,73 @@ intr_pic_claim_root(device_t dev, intptr_t xref, intr_irq_filter_t *filter,
|
||||
}
|
||||
|
||||
int
|
||||
intr_irq_add_handler(device_t dev, driver_filter_t filt, driver_intr_t hand,
|
||||
void *arg, u_int irq, int flags, void **cookiep)
|
||||
intr_map_irq(device_t dev, intptr_t xref, struct intr_map_data *data,
|
||||
u_int *irqp)
|
||||
{
|
||||
const char *name;
|
||||
struct intr_irqsrc *isrc;
|
||||
int error;
|
||||
struct intr_irqsrc *isrc;
|
||||
struct intr_pic *pic;
|
||||
|
||||
if (data == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
pic = pic_lookup(dev, xref);
|
||||
if (pic == NULL || pic->pic_dev == NULL)
|
||||
return (ESRCH);
|
||||
|
||||
error = PIC_MAP_INTR(pic->pic_dev, data, &isrc);
|
||||
if (error == 0)
|
||||
*irqp = isrc->isrc_irq;
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
intr_alloc_irq(device_t dev, struct resource *res)
|
||||
{
|
||||
struct intr_map_data *data;
|
||||
struct intr_irqsrc *isrc;
|
||||
|
||||
KASSERT(rman_get_start(res) == rman_get_end(res),
|
||||
("%s: more interrupts in resource", __func__));
|
||||
|
||||
isrc = intr_ddata_lookup(rman_get_start(res), &data);
|
||||
if (isrc == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
return (PIC_ALLOC_INTR(isrc->isrc_dev, isrc, res, data));
|
||||
}
|
||||
|
||||
int
|
||||
intr_release_irq(device_t dev, struct resource *res)
|
||||
{
|
||||
struct intr_map_data *data;
|
||||
struct intr_irqsrc *isrc;
|
||||
|
||||
KASSERT(rman_get_start(res) == rman_get_end(res),
|
||||
("%s: more interrupts in resource", __func__));
|
||||
|
||||
isrc = intr_ddata_lookup(rman_get_start(res), &data);
|
||||
if (isrc == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
return (PIC_RELEASE_INTR(isrc->isrc_dev, isrc, res, data));
|
||||
}
|
||||
|
||||
int
|
||||
intr_setup_irq(device_t dev, struct resource *res, driver_filter_t filt,
|
||||
driver_intr_t hand, void *arg, int flags, void **cookiep)
|
||||
{
|
||||
int error;
|
||||
struct intr_map_data *data;
|
||||
struct intr_irqsrc *isrc;
|
||||
const char *name;
|
||||
|
||||
KASSERT(rman_get_start(res) == rman_get_end(res),
|
||||
("%s: more interrupts in resource", __func__));
|
||||
|
||||
isrc = intr_ddata_lookup(rman_get_start(res), &data);
|
||||
if (isrc == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
name = device_get_nameunit(dev);
|
||||
|
||||
@ -947,21 +991,7 @@ intr_irq_add_handler(device_t dev, driver_filter_t filt, driver_intr_t hand,
|
||||
debugf("irq %u cannot solo on %s\n", irq, name);
|
||||
return (EINVAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
isrc = isrc_lookup(irq);
|
||||
if (isrc == NULL) {
|
||||
debugf("irq %u without source on %s\n", irq, name);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
error = isrc_register(isrc);
|
||||
if (error != 0) {
|
||||
debugf("irq %u map error %d on %s\n", irq, error, name);
|
||||
return (error);
|
||||
}
|
||||
|
||||
#ifdef INTR_SOLO
|
||||
if (flags & INTR_SOLO) {
|
||||
error = iscr_setup_filter(isrc, name, (intr_irq_filter_t *)filt,
|
||||
arg, cookiep);
|
||||
@ -978,24 +1008,32 @@ intr_irq_add_handler(device_t dev, driver_filter_t filt, driver_intr_t hand,
|
||||
return (error);
|
||||
|
||||
mtx_lock(&isrc_table_lock);
|
||||
isrc->isrc_handlers++;
|
||||
if (isrc->isrc_handlers == 1) {
|
||||
PIC_ENABLE_INTR(isrc->isrc_dev, isrc);
|
||||
PIC_ENABLE_SOURCE(isrc->isrc_dev, isrc);
|
||||
error = PIC_SETUP_INTR(isrc->isrc_dev, isrc, res, data);
|
||||
if (error == 0) {
|
||||
isrc->isrc_handlers++;
|
||||
if (isrc->isrc_handlers == 1)
|
||||
PIC_ENABLE_INTR(isrc->isrc_dev, isrc);
|
||||
}
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
return (0);
|
||||
if (error != 0)
|
||||
intr_event_remove_handler(*cookiep);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
intr_irq_remove_handler(device_t dev, u_int irq, void *cookie)
|
||||
intr_teardown_irq(device_t dev, struct resource *res, void *cookie)
|
||||
{
|
||||
struct intr_irqsrc *isrc;
|
||||
int error;
|
||||
struct intr_map_data *data;
|
||||
struct intr_irqsrc *isrc;
|
||||
|
||||
isrc = isrc_lookup(irq);
|
||||
KASSERT(rman_get_start(res) == rman_get_end(res),
|
||||
("%s: more interrupts in resource", __func__));
|
||||
|
||||
isrc = intr_ddata_lookup(rman_get_start(res), &data);
|
||||
if (isrc == NULL || isrc->isrc_handlers == 0)
|
||||
return (EINVAL);
|
||||
|
||||
#ifdef INTR_SOLO
|
||||
if (isrc->isrc_filter != NULL) {
|
||||
if (isrc != cookie)
|
||||
@ -1005,8 +1043,8 @@ intr_irq_remove_handler(device_t dev, u_int irq, void *cookie)
|
||||
isrc->isrc_filter = NULL;
|
||||
isrc->isrc_arg = NULL;
|
||||
isrc->isrc_handlers = 0;
|
||||
PIC_DISABLE_SOURCE(isrc->isrc_dev, isrc);
|
||||
PIC_DISABLE_INTR(isrc->isrc_dev, isrc);
|
||||
PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data);
|
||||
isrc_update_name(isrc, NULL);
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
return (0);
|
||||
@ -1019,10 +1057,9 @@ intr_irq_remove_handler(device_t dev, u_int irq, void *cookie)
|
||||
if (error == 0) {
|
||||
mtx_lock(&isrc_table_lock);
|
||||
isrc->isrc_handlers--;
|
||||
if (isrc->isrc_handlers == 0) {
|
||||
PIC_DISABLE_SOURCE(isrc->isrc_dev, isrc);
|
||||
if (isrc->isrc_handlers == 0)
|
||||
PIC_DISABLE_INTR(isrc->isrc_dev, isrc);
|
||||
}
|
||||
PIC_TEARDOWN_INTR(isrc->isrc_dev, isrc, res, data);
|
||||
intrcnt_updatename(isrc);
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
}
|
||||
@ -1030,36 +1067,16 @@ intr_irq_remove_handler(device_t dev, u_int irq, void *cookie)
|
||||
}
|
||||
|
||||
int
|
||||
intr_irq_config(u_int irq, enum intr_trigger trig, enum intr_polarity pol)
|
||||
intr_describe_irq(device_t dev, struct resource *res, void *cookie,
|
||||
const char *descr)
|
||||
{
|
||||
struct intr_irqsrc *isrc;
|
||||
|
||||
isrc = isrc_lookup(irq);
|
||||
if (isrc == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
if (isrc->isrc_handlers != 0)
|
||||
return (EBUSY); /* interrrupt is enabled (active) */
|
||||
|
||||
/*
|
||||
* Once an interrupt is enabled, we do not change its configuration.
|
||||
* A controller PIC_ENABLE_INTR() method is called when an interrupt
|
||||
* is going to be enabled. In this method, a controller should setup
|
||||
* the interrupt according to saved configuration parameters.
|
||||
*/
|
||||
isrc->isrc_trig = trig;
|
||||
isrc->isrc_pol = pol;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
intr_irq_describe(u_int irq, void *cookie, const char *descr)
|
||||
{
|
||||
struct intr_irqsrc *isrc;
|
||||
int error;
|
||||
struct intr_irqsrc *isrc;
|
||||
|
||||
isrc = isrc_lookup(irq);
|
||||
KASSERT(rman_get_start(res) == rman_get_end(res),
|
||||
("%s: more interrupts in resource", __func__));
|
||||
|
||||
isrc = intr_ddata_lookup(rman_get_start(res), NULL);
|
||||
if (isrc == NULL || isrc->isrc_handlers == 0)
|
||||
return (EINVAL);
|
||||
#ifdef INTR_SOLO
|
||||
@ -1084,11 +1101,14 @@ intr_irq_describe(u_int irq, void *cookie, const char *descr)
|
||||
|
||||
#ifdef SMP
|
||||
int
|
||||
intr_irq_bind(u_int irq, int cpu)
|
||||
intr_bind_irq(device_t dev, struct resource *res, int cpu)
|
||||
{
|
||||
struct intr_irqsrc *isrc;
|
||||
|
||||
isrc = isrc_lookup(irq);
|
||||
KASSERT(rman_get_start(res) == rman_get_end(res),
|
||||
("%s: more interrupts in resource", __func__));
|
||||
|
||||
isrc = intr_ddata_lookup(rman_get_start(res), NULL);
|
||||
if (isrc == NULL || isrc->isrc_handlers == 0)
|
||||
return (EINVAL);
|
||||
#ifdef INTR_SOLO
|
||||
@ -1135,7 +1155,7 @@ intr_irq_shuffle(void *arg __unused)
|
||||
for (i = 0; i < NIRQ; i++) {
|
||||
isrc = irq_sources[i];
|
||||
if (isrc == NULL || isrc->isrc_handlers == 0 ||
|
||||
isrc->isrc_flags & INTR_ISRCF_PERCPU)
|
||||
isrc->isrc_flags & INTR_ISRCF_PPI)
|
||||
continue;
|
||||
|
||||
if (isrc->isrc_event != NULL &&
|
||||
@ -1151,7 +1171,7 @@ intr_irq_shuffle(void *arg __unused)
|
||||
* for bound ISRC. The best thing we can do is to clear
|
||||
* isrc_cpu so inconsistency with ie_cpu will be detectable.
|
||||
*/
|
||||
if (PIC_BIND(isrc->isrc_dev, isrc) != 0)
|
||||
if (PIC_BIND_INTR(isrc->isrc_dev, isrc) != 0)
|
||||
CPU_ZERO(&isrc->isrc_cpu);
|
||||
}
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
@ -1196,6 +1216,7 @@ intr_pic_init_secondary(void)
|
||||
DB_SHOW_COMMAND(irqs, db_show_irqs)
|
||||
{
|
||||
u_int i, irqsum;
|
||||
u_long num;
|
||||
struct intr_irqsrc *isrc;
|
||||
|
||||
for (irqsum = 0, i = 0; i < NIRQ; i++) {
|
||||
@ -1203,11 +1224,11 @@ DB_SHOW_COMMAND(irqs, db_show_irqs)
|
||||
if (isrc == NULL)
|
||||
continue;
|
||||
|
||||
num = isrc->isrc_count != NULL ? isrc->isrc_count[0] : 0;
|
||||
db_printf("irq%-3u <%s>: cpu %02lx%s cnt %lu\n", i,
|
||||
isrc->isrc_name, isrc->isrc_cpu.__bits[0],
|
||||
isrc->isrc_flags & INTR_ISRCF_BOUND ? " (bound)" : "",
|
||||
isrc->isrc_count[0]);
|
||||
irqsum += isrc->isrc_count[0];
|
||||
isrc->isrc_flags & INTR_ISRCF_BOUND ? " (bound)" : "", num);
|
||||
irqsum += num;
|
||||
}
|
||||
db_printf("irq total %u\n", irqsum);
|
||||
}
|
||||
|
131
sys/sys/intr.h
131
sys/sys/intr.h
@ -1,7 +1,6 @@
|
||||
/* $NetBSD: intr.h,v 1.7 2003/06/16 20:01:00 thorpej Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1997 Mark Brinicombe.
|
||||
* Copyright (c) 2015-2016 Svatopluk Kraus
|
||||
* Copyright (c) 2015-2016 Michal Meloun
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -12,28 +11,20 @@
|
||||
* 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 by Mark Brinicombe
|
||||
* for the NetBSD Project.
|
||||
* 4. The name of the company nor the name of the author may be used to
|
||||
* endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* 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 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)
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SYS_INTR_H_
|
||||
@ -41,6 +32,37 @@
|
||||
|
||||
#include <sys/systm.h>
|
||||
|
||||
enum intr_map_data_type {
|
||||
INTR_MAP_DATA_ACPI,
|
||||
INTR_MAP_DATA_FDT,
|
||||
};
|
||||
|
||||
#ifdef DEV_ACPI
|
||||
struct intr_map_data_acpi {
|
||||
u_int irq;
|
||||
enum intr_polarity pol;
|
||||
enum intr_trigger trig;
|
||||
};
|
||||
#endif
|
||||
#ifdef FDT
|
||||
struct intr_map_data_fdt {
|
||||
u_int ncells;
|
||||
pcell_t *cells;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct intr_map_data {
|
||||
enum intr_map_data_type type;
|
||||
union {
|
||||
#ifdef DEV_ACPI
|
||||
struct intr_map_data_acpi acpi;
|
||||
#endif
|
||||
#ifdef FDT
|
||||
struct intr_map_data_fdt fdt;
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
#ifdef notyet
|
||||
#define INTR_SOLO INTR_MD1
|
||||
typedef int intr_irq_filter_t(void *arg, struct trapframe *tf);
|
||||
@ -50,28 +72,16 @@ typedef int intr_irq_filter_t(void *arg);
|
||||
|
||||
#define INTR_ISRC_NAMELEN (MAXCOMLEN + 1)
|
||||
|
||||
enum intr_isrc_type {
|
||||
INTR_ISRCT_NAMESPACE,
|
||||
INTR_ISRCT_FDT
|
||||
};
|
||||
|
||||
#define INTR_ISRCF_REGISTERED 0x01 /* registered in a controller */
|
||||
#define INTR_ISRCF_PERCPU 0x02 /* per CPU interrupt */
|
||||
#define INTR_ISRCF_IPI 0x01 /* IPI interrupt */
|
||||
#define INTR_ISRCF_PPI 0x02 /* PPI interrupt */
|
||||
#define INTR_ISRCF_BOUND 0x04 /* bound to a CPU */
|
||||
|
||||
/* Interrupt source definition. */
|
||||
struct intr_irqsrc {
|
||||
device_t isrc_dev; /* where isrc is mapped */
|
||||
intptr_t isrc_xref; /* device reference key */
|
||||
uintptr_t isrc_data; /* device data for isrc */
|
||||
u_int isrc_irq; /* unique identificator */
|
||||
enum intr_isrc_type isrc_type; /* how is isrc decribed */
|
||||
u_int isrc_flags;
|
||||
char isrc_name[INTR_ISRC_NAMELEN];
|
||||
uint16_t isrc_nspc_type;
|
||||
uint16_t isrc_nspc_num;
|
||||
enum intr_trigger isrc_trig;
|
||||
enum intr_polarity isrc_pol;
|
||||
cpuset_t isrc_cpu; /* on which CPUs is enabled */
|
||||
u_int isrc_index;
|
||||
u_long * isrc_count;
|
||||
@ -81,47 +91,44 @@ struct intr_irqsrc {
|
||||
intr_irq_filter_t * isrc_filter;
|
||||
void * isrc_arg;
|
||||
#endif
|
||||
#ifdef FDT
|
||||
u_int isrc_ncells;
|
||||
pcell_t isrc_cells[]; /* leave it last */
|
||||
#endif
|
||||
};
|
||||
|
||||
struct intr_irqsrc *intr_isrc_alloc(u_int type, u_int extsize);
|
||||
void intr_isrc_free(struct intr_irqsrc *isrc);
|
||||
/* Intr interface for PIC. */
|
||||
int intr_isrc_deregister(struct intr_irqsrc *);
|
||||
int intr_isrc_register(struct intr_irqsrc *, device_t, u_int, const char *, ...)
|
||||
__printflike(4, 5);
|
||||
|
||||
void intr_irq_set_name(struct intr_irqsrc *isrc, const char *fmt, ...)
|
||||
__printflike(2, 3);
|
||||
int intr_isrc_dispatch(struct intr_irqsrc *, struct trapframe *);
|
||||
u_int intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask);
|
||||
|
||||
void intr_irq_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf);
|
||||
int intr_pic_register(device_t, intptr_t);
|
||||
int intr_pic_deregister(device_t, intptr_t);
|
||||
int intr_pic_claim_root(device_t, intptr_t, intr_irq_filter_t *, void *, u_int);
|
||||
|
||||
#define INTR_IRQ_NSPC_NONE 0
|
||||
#define INTR_IRQ_NSPC_PLAIN 1
|
||||
#define INTR_IRQ_NSPC_IRQ 2
|
||||
#define INTR_IRQ_NSPC_IPI 3
|
||||
extern device_t intr_irq_root_dev;
|
||||
|
||||
u_int intr_namespace_map_irq(device_t dev, uint16_t type, uint16_t num);
|
||||
/* Intr interface for BUS. */
|
||||
int intr_map_irq(device_t, intptr_t, struct intr_map_data *, u_int *);
|
||||
|
||||
int intr_alloc_irq(device_t, struct resource *);
|
||||
int intr_release_irq(device_t, struct resource *);
|
||||
|
||||
int intr_setup_irq(device_t, struct resource *, driver_filter_t, driver_intr_t,
|
||||
void *, int, void **);
|
||||
int intr_teardown_irq(device_t, struct resource *, void *);
|
||||
|
||||
int intr_describe_irq(device_t, struct resource *, void *, const char *);
|
||||
|
||||
#ifdef DEV_ACPI
|
||||
u_int intr_acpi_map_irq(device_t, u_int, enum intr_polarity,
|
||||
enum intr_trigger);
|
||||
#endif
|
||||
#ifdef FDT
|
||||
u_int intr_fdt_map_irq(phandle_t, pcell_t *, u_int);
|
||||
#endif
|
||||
|
||||
extern device_t intr_irq_root_dev;
|
||||
|
||||
int intr_pic_register(device_t dev, intptr_t xref);
|
||||
int intr_pic_unregister(device_t dev, intptr_t xref);
|
||||
int intr_pic_claim_root(device_t dev, intptr_t xref, intr_irq_filter_t *filter,
|
||||
void *arg, u_int ipicount);
|
||||
|
||||
int intr_irq_add_handler(device_t dev, driver_filter_t, driver_intr_t, void *,
|
||||
u_int, int, void **);
|
||||
int intr_irq_remove_handler(device_t dev, u_int, void *);
|
||||
int intr_irq_config(u_int, enum intr_trigger, enum intr_polarity);
|
||||
int intr_irq_describe(u_int, void *, const char *);
|
||||
|
||||
u_int intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask);
|
||||
|
||||
#ifdef SMP
|
||||
int intr_irq_bind(u_int, int);
|
||||
int intr_bind_irq(device_t, struct resource *, int);
|
||||
|
||||
void intr_pic_init_secondary(void);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user