interrupt framework
Svatopluk Kraus
onwahe at gmail.com
Thu Jan 15 13:51:19 UTC 2015
Hi community,
I and Michal Meloun have done some work on ARM interrupt framework and
this is the result.
We've started with intrng project with Ian's WIP changes, have looked
at Andrew's ARM64 git repository, and this is how we think an
interrupt framework should look like. We've implemented it with
removable interrupt controllers in mind (PCI world). It's not finished
from this point of view, however some functions are more complex
because of it.
It's tested on pandaboard and only GIC is implemented now. There is no
problem to implement it to other controllers. We are open to questions
and can finish our work considering any comments. Whoever is waiting
for ARM interrupt framework as we were, you are welcome to test it.
Whoever is welcome. The patches are done against FreeBSD-11-current
revision 277210. There are two new files.
ARM_INTRNG option must be added to board configuration file for new framework.
There are still some things not implemented and some things which
should be discussed like PPI support. For example, how to enable PPI
interrupt on other CPUs when they are already running?
We keep in mind that an interrupt framework should be helpfull but
general enough to not dictate interrupt controlles too much. Thus we
try to keep some things as much separated as possible. Each interrupt
is represented by an interrupt source (ISRC) in the framework. An ISRC
is described by an interrupt number which is much more an unique
resource handle - totally independent on internal representation of
interrupts in any interrupt controller.
An interrupt is described by cells in FDT world. The cells can be
decoded only by associated interrupt controller and as such, they are
transparent for interrupt framework. The framework provides
arm_fdt_map_irq() function which maps this transparent cells to an
interrupt number. It creates an ISRC, saves cells on it, and once when
associated interrupt controller is registered, it provides the ISRC
with cells into the controller.
It's a controller responsibility to save an ISRC associated with
cells. An ISRC is transparent for any controller. However, an
controller can set/get its data to/from an ISRC. Further, an
controller should set a name to an ISRC according to internal
representation of associated interrupt.
An controller interrupt dispatch function can call framework only if
it has associated ISRC to received interrupt.
For legacy reason, there is arm_namespace_map_irq() function. An
interrupt is described by namespace type and a number from the
namespace. It's intented for use with no FDT drivers. Now, it's used
for mapping an IPI on a controller.
We think that it's better to call chained controllers (with filter
only) without MI interrupt framework overhead, so we implemented
shortcut. It could be utilized by INTR_SOLO flag during
bus_setup_intr().
Only an interrupt controller can really know its position in interrupt
controller's tree. So root controller must claim itself as a root. In
FDT world, according to ePAPR approved version 1.1 from 08 April 2011,
page 30:
"The root of the interrupt tree is determined when traversal of the
interrupt tree reaches an interrupt controller node without an
interrupts property and thus no explicit interrupt parent."
Thus there are no need for any non-standard things in DTS files.
Svata
-------------- next part --------------
Index: sys/arm/arm/gic.c
===================================================================
--- sys/arm/arm/gic.c (revision 277210)
+++ sys/arm/arm/gic.c (working copy)
@@ -34,6 +34,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_platform.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
@@ -40,6 +42,7 @@
#include <sys/kernel.h>
#include <sys/ktr.h>
#include <sys/module.h>
+#include <sys/malloc.h>
#include <sys/rman.h>
#include <sys/pcpu.h>
#include <sys/proc.h>
@@ -55,6 +58,11 @@
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
+#ifdef ARM_INTRNG
+#include <sys/sched.h>
+#include "pic_if.h"
+#endif
+
/* We are using GICv2 register naming */
/* Distributor Registers */
@@ -83,8 +91,8 @@
#define GICC_ABPR 0x001C /* v1 ICCABPR */
#define GICC_IIDR 0x00FC /* v1 ICCIIDR*/
-#define GIC_FIRST_IPI 0 /* Irqs 0-15 are SGIs/IPIs. */
-#define GIC_LAST_IPI 15
+#define GIC_FIRST_SGI 0 /* Irqs 0-15 are SGIs/IPIs. */
+#define GIC_LAST_SGI 15
#define GIC_FIRST_PPI 16 /* Irqs 16-31 are private (per */
#define GIC_LAST_PPI 31 /* core) peripheral interrupts. */
#define GIC_FIRST_SPI 32 /* Irqs 32+ are shared peripherals. */
@@ -98,8 +106,23 @@
#define GICD_ICFGR_TRIG_EDGE (1 << 1)
#define GICD_ICFGR_TRIG_MASK 0x2
+#ifdef ARM_INTRNG
+#define GIC_INTRNAME_LEN 32
+
+struct arm_gic_irq {
+ struct arm_irqsrc * gi_isrc;
+ char gi_name[GIC_INTRNAME_LEN];
+};
+
+static int arm_gic_intr(void *, struct trapframe *);
+#endif
+
struct arm_gic_softc {
device_t gic_dev;
+#ifdef ARM_INTRNG
+ void * gic_intrhand;
+ struct arm_gic_irq * gic_irqs;
+#endif
struct resource * gic_res[3];
bus_space_tag_t gic_c_bst;
bus_space_tag_t gic_d_bst;
@@ -113,10 +136,13 @@
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 */
+#ifdef ARM_INTRNG
+ { SYS_RES_IRQ, 0, RF_ACTIVE | RF_OPTIONAL }, /* Parent interrupt */
+#endif
{ -1, 0 }
};
-static struct arm_gic_softc *arm_gic_sc = NULL;
+static struct arm_gic_softc *gic_sc = NULL;
#define gic_c_read_4(_sc, _reg) \
bus_space_read_4((_sc)->gic_c_bst, (_sc)->gic_c_bsh, (_reg))
@@ -127,9 +153,11 @@
#define gic_d_write_4(_sc, _reg, _val) \
bus_space_write_4((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg), (_val))
+#ifndef ARM_INTRNG
static int gic_config_irq(int irq, enum intr_trigger trig,
enum intr_polarity pol);
static void gic_post_filter(void *);
+#endif
static struct ofw_compat_data compat_data[] = {
{"arm,gic", true}, /* Non-standard, used in FreeBSD dts. */
@@ -155,10 +183,58 @@
return (BUS_PROBE_DEFAULT);
}
+#ifdef ARM_INTRNG
+static inline void
+gic_irq_unmask(struct arm_gic_softc *sc, u_int irq)
+{
+
+ gic_d_write_4(sc, GICD_ISENABLER(irq >> 5), (1UL << (irq & 0x1F)));
+}
+
+static inline void
+gic_irq_mask(struct arm_gic_softc *sc, u_int irq)
+{
+
+ gic_d_write_4(sc, GICD_ICENABLER(irq >> 5), (1UL << (irq & 0x1F)));
+}
+#endif
+
+#ifdef SMP
+#ifdef ARM_INTRNG
static void
arm_gic_init_secondary(device_t dev)
{
struct arm_gic_softc *sc = device_get_softc(dev);
+ u_int irq;
+
+ for (irq = 0; irq < sc->nirqs; irq += 4)
+ gic_d_write_4(sc, GICD_IPRIORITYR(irq >> 2), 0);
+
+ /* Set all the interrupts to be in Group 0 (secure) */
+ for (irq = 0; irq < sc->nirqs; irq += 32) {
+ gic_d_write_4(sc, GICD_IGROUPR(irq >> 5), 0);
+ }
+
+ /* Enable CPU interface */
+ gic_c_write_4(sc, GICC_CTLR, 1);
+
+ /* Set priority mask register. */
+ gic_c_write_4(sc, GICC_PMR, 0xff);
+
+ /* Enable interrupt distribution */
+ gic_d_write_4(sc, GICD_CTLR, 0x01);
+
+ /* Unmask attached PPI interrupts. */
+ for (irq = GIC_FIRST_PPI; irq <= GIC_LAST_PPI; irq++) {
+ if (sc->gic_irqs[irq].gi_isrc != NULL)
+ gic_irq_unmask(sc, irq);
+ }
+}
+#else
+static void
+arm_gic_init_secondary(device_t dev)
+{
+ struct arm_gic_softc *sc = device_get_softc(dev);
int i;
for (i = 0; i < sc->nirqs; i += 4)
@@ -185,7 +261,10 @@
gic_d_write_4(sc, GICD_ISENABLER(29 >> 5), (1UL << (29 & 0x1F)));
gic_d_write_4(sc, GICD_ISENABLER(30 >> 5), (1UL << (30 & 0x1F)));
}
+#endif /* ARM_INTRNG */
+#endif /* SMP */
+#ifndef ARM_INTRNG
int
gic_decode_fdt(uint32_t iparent, uint32_t *intr, int *interrupt,
int *trig, int *pol)
@@ -230,6 +309,7 @@
}
return (0);
}
+#endif
static int
arm_gic_attach(device_t dev)
@@ -238,7 +318,7 @@
int i;
uint32_t icciidr;
- if (arm_gic_sc)
+ if (gic_sc)
return (ENXIO);
sc = device_get_softc(dev);
@@ -249,7 +329,7 @@
}
sc->gic_dev = dev;
- arm_gic_sc = sc;
+ gic_sc = sc;
/* Initialize mutex */
mtx_init(&sc->mutex, "GIC lock", "", MTX_SPIN);
@@ -269,9 +349,14 @@
sc->nirqs = gic_d_read_4(sc, GICD_TYPER);
sc->nirqs = 32 * ((sc->nirqs & 0x1f) + 1);
+#ifdef ARM_INTRNG
+ sc->gic_irqs = malloc(sc->nirqs * sizeof (*sc->gic_irqs), M_DEVBUF,
+ M_WAITOK | M_ZERO);
+#else
/* Set up function pointers */
arm_post_filter = gic_post_filter;
arm_config_irq = gic_config_irq;
+#endif
icciidr = gic_c_read_4(sc, GICC_IIDR);
device_printf(dev,"pn 0x%x, arch 0x%x, rev 0x%x, implementer 0x%x irqs %u\n",
@@ -307,11 +392,429 @@
/* Enable interrupt distribution */
gic_d_write_4(sc, GICD_CTLR, 0x01);
+#ifndef ARM_INTRNG
+ return (0);
+#else
+ /*
+ * Now, when everything is initialized, it's right time to
+ * register interrupt controller to interrupt framefork.
+ */
+ if (arm_register_pic(dev) != 0) {
+ device_printf(dev, "could not register IC\n");
+ goto cleanup;
+ }
+ if (sc->gic_res[2] == NULL) {
+ if (arm_irq_set_root(dev, arm_gic_intr, sc,
+ GIC_LAST_SGI - GIC_FIRST_SGI + 1) != 0) {
+ device_printf(dev, "could not set PIC as a root\n");
+ arm_unregister_pic(dev);
+ goto cleanup;
+ }
+ } else {
+ if (bus_setup_intr(dev, sc->gic_res[2], INTR_TYPE_MISC |
+ INTR_SOLO, (void*)arm_gic_intr, NULL, sc,
+ &sc->gic_intrhand)) {
+ device_printf(dev, "could not setup irq handler\n");
+ arm_unregister_pic(dev);
+ goto cleanup;
+ }
+ }
+
return (0);
+
+cleanup:
+ /*
+ * XXX - not implemented arm_gic_detach() should be called !
+ */
+ if (sc->gic_irqs != NULL)
+ free(sc->gic_irqs, M_DEVBUF);
+ bus_release_resources(dev, arm_gic_spec, sc->gic_res);
+ return(ENXIO);
+#endif
}
+#ifdef ARM_INTRNG
static int
+arm_gic_intr(void *arg, struct trapframe *tf)
+{
+ struct arm_gic_softc *sc = (struct arm_gic_softc *)arg;
+ struct arm_gic_irq *gi;
+ uint32_t irq_active_reg, irq;
+
+ irq_active_reg = gic_c_read_4(sc, GICC_IAR);
+ irq = irq_active_reg & 0x3FF;
+
+ /*
+ * 1. EOI must be done here as the other bits (i.e. CPU number), not
+ * just the IRQ number are required, and we do not have a method
+ * how to pass this information to dispatch function.
+ * 2. EOI must be done on same CPU, where interrupt has fired. Thus
+ * we must ensure that interrupted thread does not migrate to
+ * another CPU.
+ * 3. EOI cannot be delayed by any preemption, which could happen on
+ * critical_exit() used in MI intr code, when interrupt thread is
+ * scheduled. See next point.
+ * 4. IPI_RENDEZVOUS assumes that no preemption is permitted during
+ * an action and any use of critical_exit() could break this
+ * assumption. See comments within smp_rendezvous_action().
+ */
+
+ if (irq >= sc->nirqs) {
+ device_printf(sc->gic_dev, "Spurious interrupt detected\n");
+ gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
+ return (0);
+ }
+
+ gi = &sc->gic_irqs[irq];
+ if (gi->gi_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);
+ return (0);
+ }
+
+ /*
+ * 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.
+ */
+ if (irq <= GIC_LAST_SGI) {
+#ifdef SMP
+ /* Call EOI for all IPI before dispatch. */
+ gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
+ arm_dispatch_sgi(gi->gi_isrc, tf);
+ return (FILTER_HANDLED);
+#else
+ printf("SGI %u on UP system detected\n", irq - GIC_FIRST_SGI);
+ gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
+ return (0);
+#endif
+ }
+
+ critical_enter();
+ if (irq <= GIC_LAST_PPI)
+ arm_dispatch_ppi(gi->gi_isrc, tf);
+ else
+ arm_dispatch_spi(gi->gi_isrc, tf);
+ gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
+ critical_exit();
+ arm_irq_memory_barrier(irq);
+ return (FILTER_HANDLED);
+}
+
+static int
+gic_namespace_to_irq(struct arm_gic_softc *sc, u_int type, u_int num,
+ u_int *irqp)
+{
+ u_int irq;
+
+ if (type == ISRCT_SGI)
+ irq = GIC_FIRST_SGI + num;
+ else if (type == ISRCT_PPI)
+ irq = GIC_FIRST_PPI + num;
+ else if (type == ISRCT_SPI)
+ irq = GIC_FIRST_SPI + num;
+ else
+ return (EINVAL);
+
+ if (irq >= sc->nirqs)
+ return (EINVAL);
+
+ *irqp = irq;
+ return (0);
+}
+
+static int
+gic_irq_to_namespace(struct arm_gic_softc *sc, u_int irq, u_int *typep,
+u_int *nump)
+{
+
+ if (irq <= GIC_LAST_SGI) {
+ *typep = ISRCT_SGI;
+ *nump = irq - GIC_FIRST_SGI;
+ } else if (irq <= GIC_LAST_PPI) {
+ *typep = ISRCT_PPI;
+ *nump = irq - GIC_FIRST_PPI;
+ } else if (irq < sc->nirqs) {
+ *typep = ISRCT_SPI;
+ *nump = irq - GIC_FIRST_SPI;
+ } else
+ return (EINVAL);
+
+ return (0);
+}
+
+static int
+gic_attach_isrc(struct arm_gic_softc *sc, struct arm_irqsrc *isrc, u_int irq)
+{
+ struct arm_gic_irq *gi = &sc->gic_irqs[irq];
+
+ if (gi->gi_isrc == isrc)
+ return (0);
+
+ if (gi->gi_isrc != NULL)
+ return (EEXIST);
+
+ if (irq <= GIC_LAST_SGI)
+ snprintf(gi->gi_name, GIC_INTRNAME_LEN, "%s(sgi%u)",
+ device_get_nameunit(sc->gic_dev), irq - GIC_FIRST_SGI);
+ else if (irq <= GIC_LAST_PPI)
+ snprintf(gi->gi_name, GIC_INTRNAME_LEN, "%s(ppi%u)",
+ device_get_nameunit(sc->gic_dev), irq - GIC_FIRST_PPI);
+ else
+ snprintf(gi->gi_name, GIC_INTRNAME_LEN, "%s(spi%u)",
+ device_get_nameunit(sc->gic_dev), irq - GIC_FIRST_SPI);
+
+ arm_irq_set_data(isrc, (void *)irq);
+ arm_irq_set_name(isrc, gi->gi_name);
+
+ gi->gi_isrc = isrc;
+ return (0);
+}
+
+static int
+gic_detach_isrc(struct arm_gic_softc *sc, struct arm_irqsrc *isrc, u_int irq)
+{
+ struct arm_gic_irq *gi = &sc->gic_irqs[irq];
+
+ if (irq >= sc->nirqs || gi->gi_isrc != isrc)
+ return (EINVAL);
+
+ arm_irq_set_data(isrc, NULL);
+ arm_irq_set_name(isrc, NULL);
+
+ gi->gi_isrc = NULL;
+ return (0);
+}
+
+static int
+arm_gic_map(device_t dev, struct arm_irqsrc *isrc, u_int type, u_int num)
+{
+ struct arm_gic_softc *sc = device_get_softc(dev);
+ u_int irq;
+ int error;
+
+ error = gic_namespace_to_irq(sc, type, num, &irq);
+ if (error != 0)
+ return (error);
+
+ return (gic_attach_isrc(sc, isrc, irq));
+}
+
+static void
+gic_config(struct arm_gic_softc *sc, u_int irq, enum intr_trigger trig,
+ enum intr_polarity pol)
+{
+ uint32_t reg;
+ uint32_t mask;
+
+ if (irq < GIC_FIRST_SPI)
+ return;
+
+ mtx_lock_spin(&sc->mutex);
+
+ reg = gic_d_read_4(sc, GICD_ICFGR(irq >> 4));
+ mask = (reg >> 2*(irq % 16)) & 0x3;
+
+ if (pol == INTR_POLARITY_LOW) {
+ mask &= ~GICD_ICFGR_POL_MASK;
+ mask |= GICD_ICFGR_POL_LOW;
+ } else if (pol == INTR_POLARITY_HIGH) {
+ mask &= ~GICD_ICFGR_POL_MASK;
+ mask |= GICD_ICFGR_POL_HIGH;
+ }
+
+ if (trig == INTR_TRIGGER_LEVEL) {
+ mask &= ~GICD_ICFGR_TRIG_MASK;
+ mask |= GICD_ICFGR_TRIG_LVL;
+ } else if (trig == INTR_TRIGGER_EDGE) {
+ mask &= ~GICD_ICFGR_TRIG_MASK;
+ mask |= GICD_ICFGR_TRIG_EDGE;
+ }
+
+ /* Set mask */
+ reg = reg & ~(0x3 << 2*(irq % 16));
+ reg = reg | (mask << 2*(irq % 16));
+ gic_d_write_4(sc, GICD_ICFGR(irq >> 4), reg);
+
+ mtx_unlock_spin(&sc->mutex);
+}
+
+#ifdef FDT
+static int
+arm_gic_map_fdt(device_t dev, struct arm_irqsrc *isrc, pcell_t *cells,
+ u_int ncells)
+{
+ struct arm_gic_softc *sc = device_get_softc(dev);
+ u_int irq, type, num, tripol;
+ enum intr_trigger trig;
+ enum intr_polarity pol;
+ int error;
+
+ if (ncells == 1) {
+ irq = cells[0];
+
+ error = gic_irq_to_namespace(sc, irq, &type, &num);
+ if (error != 0)
+ return (error);
+
+ pol = INTR_POLARITY_CONFORM;
+ trig = INTR_TRIGGER_CONFORM;
+ } else {
+
+ type = cells[0] == 0 ? ISRCT_SPI : ISRCT_PPI;
+ num = cells[1];
+
+ error = gic_namespace_to_irq(sc, type, num, &irq);
+ if (error != 0)
+ return (error);
+
+ /*
+ * 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 = cells[2];
+ if (tripol & 0x0a) {
+ printf("unsupported trigger/polarity configuration "
+ "0x%2x\n", tripol & 0x0f);
+ return (ENOTSUP);
+ }
+ pol = INTR_POLARITY_CONFORM;
+ if (tripol & 0x01)
+ trig = INTR_TRIGGER_EDGE;
+ else
+ trig = INTR_TRIGGER_LEVEL;
+ }
+
+ error = gic_attach_isrc(sc, isrc, irq);
+ if (error != 0)
+ return (error);
+
+ arm_irq_set_namespace(isrc, type, num);
+
+ gic_config(sc, irq, trig, pol);
+ return (0);
+}
+#endif
+
+static int
+arm_gic_unmap(device_t dev, struct arm_irqsrc *isrc)
+{
+ struct arm_gic_softc *sc = device_get_softc(dev);
+ u_int irq;
+
+ irq = (u_int)arm_irq_get_data(isrc);
+ return (gic_detach_isrc(sc, isrc, irq));
+}
+
+static int
+arm_gic_bind(device_t dev, struct arm_irqsrc *isrc, cpuset_t cpumask)
+{
+
+ return (EOPNOTSUPP); /* for now */
+}
+
+static int
+arm_gic_config(device_t dev, struct arm_irqsrc *isrc, enum intr_trigger trig,
+ enum intr_polarity pol)
+{
+ struct arm_gic_softc *sc = device_get_softc(dev);
+ u_int irq;
+
+ /* XXX move these checks to framework */
+ if ((trig != INTR_TRIGGER_EDGE) && (trig != INTR_TRIGGER_LEVEL) &&
+ (trig != INTR_TRIGGER_CONFORM))
+ return (EINVAL);
+ if ((pol != INTR_POLARITY_HIGH) && (pol != INTR_POLARITY_LOW) &&
+ (pol != INTR_POLARITY_CONFORM))
+ return (EINVAL);
+
+ irq = (u_int)arm_irq_get_data(isrc);
+ if (irq >= sc->nirqs || sc->gic_irqs[irq].gi_isrc != isrc)
+ return (EINVAL);
+
+ gic_config(sc, irq, trig, pol);
+ return (0);
+}
+
+static void
+arm_gic_enable(device_t dev, struct arm_irqsrc *isrc)
+{
+ struct arm_gic_softc *sc = device_get_softc(dev);
+ u_int irq;
+
+ irq = (u_int)arm_irq_get_data(isrc);
+ if (irq >= sc->nirqs || sc->gic_irqs[irq].gi_isrc != isrc)
+ return;
+
+ if (irq <= GIC_LAST_SGI)
+ return; /* v2 IMPLEMENTATION DEFINED */
+
+ arm_irq_memory_barrier(irq);
+ gic_irq_unmask(sc, irq);
+}
+
+static void
+arm_gic_disable(device_t dev, struct arm_irqsrc *isrc)
+{
+ struct arm_gic_softc *sc = device_get_softc(dev);
+ u_int irq;
+
+ irq = (u_int)arm_irq_get_data(isrc);
+ if (irq >= sc->nirqs || sc->gic_irqs[irq].gi_isrc != isrc)
+ return;
+
+ if (irq <= GIC_LAST_SGI)
+ return; /* v2 IMPLEMENTATION DEFINED */
+
+ gic_irq_mask(sc, irq);
+}
+
+static void
+arm_gic_pre_ithread(device_t dev, struct arm_irqsrc *isrc)
+{
+
+ arm_gic_disable(dev, isrc);
+}
+
+static void
+arm_gic_post_ithread(device_t dev, struct arm_irqsrc *isrc)
+{
+
+ arm_gic_enable(dev, isrc);
+}
+
+static void
+arm_gic_post_filter(device_t dev, struct arm_irqsrc *isrc)
+{
+
+ /* EOI must be done in controller's interrupt routine. */
+}
+
+#ifdef SMP
+static void
+arm_gic_ipi_send(device_t dev, struct arm_irqsrc *isrc, cpuset_t cpus)
+{
+ struct arm_gic_softc *sc = device_get_softc(dev);
+ uint32_t irq, val = 0, i;
+
+ irq = (u_int)arm_irq_get_data(isrc);
+ if (irq >= sc->nirqs || sc->gic_irqs[irq].gi_isrc != isrc)
+ return;
+
+ 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);
+}
+#endif
+#else
+static int
arm_gic_next_irq(struct arm_gic_softc *sc, int last_irq)
{
uint32_t active_irq;
@@ -323,7 +826,7 @@
* bits (ie CPU number), not just the IRQ number, and we do not
* have this information later.
*/
- if ((active_irq & 0x3ff) <= GIC_LAST_IPI)
+ if ((active_irq & 0x3ff) <= GIC_LAST_SGI)
gic_c_write_4(sc, GICC_EOIR, active_irq);
active_irq &= 0x3FF;
@@ -396,7 +899,7 @@
struct arm_gic_softc *sc = device_get_softc(dev);
gic_d_write_4(sc, GICD_ICENABLER(irq >> 5), (1UL << (irq & 0x1F)));
- gic_c_write_4(sc, GICC_EOIR, irq);
+ gic_c_write_4(sc, GICC_EOIR, irq); /* XXX - not allowed */
}
static void
@@ -404,7 +907,7 @@
{
struct arm_gic_softc *sc = device_get_softc(dev);
- if (irq > GIC_LAST_IPI)
+ if (irq > GIC_LAST_SGI)
arm_irq_memory_barrier(irq);
gic_d_write_4(sc, GICD_ISENABLER(irq >> 5), (1UL << (irq & 0x1F)));
@@ -451,10 +954,10 @@
static void
gic_post_filter(void *arg)
{
- struct arm_gic_softc *sc = arm_gic_sc;
+ struct arm_gic_softc *sc = gic_sc;
uintptr_t irq = (uintptr_t) arg;
- if (irq > GIC_LAST_IPI)
+ if (irq > GIC_LAST_SGI)
arm_irq_memory_barrier(irq);
gic_c_write_4(sc, GICC_EOIR, irq);
}
@@ -463,7 +966,7 @@
gic_config_irq(int irq, enum intr_trigger trig, enum intr_polarity pol)
{
- return (arm_gic_config(arm_gic_sc->gic_dev, irq, trig, pol));
+ return (arm_gic_config(gic_sc->gic_dev, irq, trig, pol));
}
void
@@ -470,7 +973,7 @@
arm_mask_irq(uintptr_t nb)
{
- arm_gic_mask(arm_gic_sc->gic_dev, nb);
+ arm_gic_mask(gic_sc->gic_dev, nb);
}
void
@@ -477,7 +980,7 @@
arm_unmask_irq(uintptr_t nb)
{
- arm_gic_unmask(arm_gic_sc->gic_dev, nb);
+ arm_gic_unmask(gic_sc->gic_dev, nb);
}
int
@@ -484,7 +987,7 @@
arm_get_next_irq(int last_irq)
{
- return (arm_gic_next_irq(arm_gic_sc, last_irq));
+ return (arm_gic_next_irq(gic_sc, last_irq));
}
void
@@ -491,7 +994,7 @@
arm_init_secondary_ic(void)
{
- arm_gic_init_secondary(arm_gic_sc->gic_dev);
+ arm_gic_init_secondary(gic_sc->gic_dev);
}
#ifdef SMP
@@ -499,7 +1002,7 @@
pic_ipi_send(cpuset_t cpus, u_int ipi)
{
- arm_gic_ipi_send(arm_gic_sc->gic_dev, cpus, ipi);
+ arm_gic_ipi_send(gic_sc->gic_dev, cpus, ipi);
}
int
@@ -506,7 +1009,7 @@
pic_ipi_read(int i)
{
- return (arm_gic_ipi_read(arm_gic_sc->gic_dev, i));
+ return (arm_gic_ipi_read(gic_sc->gic_dev, i));
}
void
@@ -513,14 +1016,34 @@
pic_ipi_clear(int ipi)
{
- arm_gic_ipi_clear(arm_gic_sc->gic_dev, ipi);
+ arm_gic_ipi_clear(gic_sc->gic_dev, ipi);
}
#endif
+#endif /* ARM_INTRNG */
static device_method_t arm_gic_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, arm_gic_probe),
DEVMETHOD(device_attach, arm_gic_attach),
+#ifdef ARM_INTRNG
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_map, arm_gic_map),
+#ifdef FDT
+ DEVMETHOD(pic_map_fdt, arm_gic_map_fdt),
+#endif
+ DEVMETHOD(pic_unmap, arm_gic_unmap),
+ DEVMETHOD(pic_bind, arm_gic_bind),
+ DEVMETHOD(pic_config, arm_gic_config),
+ DEVMETHOD(pic_disable, arm_gic_disable),
+ DEVMETHOD(pic_enable, arm_gic_enable),
+ DEVMETHOD(pic_pre_ithread, arm_gic_pre_ithread),
+ DEVMETHOD(pic_post_ithread, arm_gic_post_ithread),
+ DEVMETHOD(pic_post_filter, arm_gic_post_filter),
+#ifdef SMP
+ DEVMETHOD(pic_init_secondary, arm_gic_init_secondary),
+ DEVMETHOD(pic_ipi_send, arm_gic_ipi_send),
+#endif
+#endif
{ 0, 0 }
};
Index: sys/arm/arm/mp_machdep.c
===================================================================
--- sys/arm/arm/mp_machdep.c (revision 277210)
+++ sys/arm/arm/mp_machdep.c (working copy)
@@ -73,7 +73,9 @@
/* Set to 1 once we're ready to let the APs out of the pen. */
volatile int aps_ready = 0;
+#ifndef ARM_INTRNG
static int ipi_handler(void *arg);
+#endif
void set_stackptrs(int cpu);
/* Temporary variables for init_secondary() */
@@ -149,7 +151,6 @@
{
struct pcpu *pc;
uint32_t loop_counter;
- int start = 0, end = 0;
cpu_setup(NULL);
setttb(pmap_pa);
@@ -199,18 +200,6 @@
mtx_unlock_spin(&ap_boot_mtx);
- /* Enable ipi */
-#ifdef IPI_IRQ_START
- start = IPI_IRQ_START;
-#ifdef IPI_IRQ_END
- end = IPI_IRQ_END;
-#else
- end = IPI_IRQ_START;
-#endif
-#endif
-
- for (int i = start; i <= end; i++)
- arm_unmask_irq(i);
enable_interrupts(PSR_I);
loop_counter = 0;
@@ -233,6 +222,108 @@
/* NOTREACHED */
}
+#ifdef ARM_INTRNG
+static void
+ipi_rendezvous(void *dummy __unused)
+{
+
+ CTR0(KTR_SMP, "IPI_RENDEZVOUS");
+ smp_rendezvous_action();
+}
+
+static void
+ipi_ast(void *dummy __unused)
+{
+
+ CTR0(KTR_SMP, "IPI_AST");
+}
+
+static void
+ipi_stop(void *dummy __unused)
+{
+ u_int cpu;
+
+ /*
+ * IPI_STOP_HARD is mapped to IPI_STOP.
+ */
+ CTR0(KTR_SMP, "IPI_STOP or IPI_STOP_HARD");
+
+ cpu = PCPU_GET(cpuid);
+ savectx(&stoppcbs[cpu]);
+
+ /*
+ * CPUs are stopped when entering the debugger and at
+ * system shutdown, both events which can precede a
+ * panic dump. For the dump to be correct, all caches
+ * must be flushed and invalidated, but on ARM there's
+ * no way to broadcast a wbinv_all to other cores.
+ * Instead, we have each core do the local wbinv_all as
+ * part of stopping the core. The core requesting the
+ * stop will do the l2 cache flush after all other cores
+ * have done their l1 flushes and stopped.
+ */
+ cpu_idcache_wbinv_all();
+
+ /* Indicate we are stopped */
+ CPU_SET_ATOMIC(cpu, &stopped_cpus);
+
+ /* Wait for restart */
+ while (!CPU_ISSET(cpu, &started_cpus))
+ cpu_spinwait();
+
+ CPU_CLR_ATOMIC(cpu, &started_cpus);
+ CPU_CLR_ATOMIC(cpu, &stopped_cpus);
+ CTR0(KTR_SMP, "IPI_STOP (restart)");
+}
+
+static void
+ipi_preempt(void *arg)
+{
+ struct trapframe *oldframe;
+ struct thread *td;
+
+ critical_enter();
+ td = curthread;
+ td->td_intr_nesting_level++;
+ oldframe = td->td_intr_frame;
+ td->td_intr_frame = (struct trapframe *)arg;
+
+ CTR1(KTR_SMP, "%s: IPI_PREEMPT", __func__);
+ sched_preempt(td);
+
+ td->td_intr_frame = oldframe;
+ td->td_intr_nesting_level--;
+ critical_exit();
+}
+
+static void
+ipi_hardclock(void *arg)
+{
+ struct trapframe *oldframe;
+ struct thread *td;
+
+ critical_enter();
+ td = curthread;
+ td->td_intr_nesting_level++;
+ oldframe = td->td_intr_frame;
+ td->td_intr_frame = (struct trapframe *)arg;
+
+ CTR1(KTR_SMP, "%s: IPI_HARDCLOCK", __func__);
+ hardclockintr();
+
+ td->td_intr_frame = oldframe;
+ td->td_intr_nesting_level--;
+ critical_exit();
+}
+
+static void
+ipi_tlb(void *dummy __unused)
+{
+
+ CTR1(KTR_SMP, "%s: IPI_TLB", __func__);
+ cpufuncs.cf_tlb_flushID();
+}
+#else
static int
ipi_handler(void *arg)
{
@@ -308,15 +399,28 @@
return (FILTER_HANDLED);
}
+#endif
static void
release_aps(void *dummy __unused)
{
uint32_t loop_counter;
+#ifndef ARM_INTRNG
int start = 0, end = 0;
+#endif
if (mp_ncpus == 1)
return;
+
+#ifdef ARM_INTRNG
+ arm_setup_ipihandler(IPI_RENDEZVOUS, NULL, ipi_rendezvous, NULL, 0);
+ arm_setup_ipihandler(IPI_AST, NULL, ipi_ast, NULL, 0);
+ arm_setup_ipihandler(IPI_STOP, NULL, ipi_stop, NULL, 0);
+ arm_setup_ipihandler(IPI_PREEMPT, NULL, ipi_preempt, NULL, 0);
+ arm_setup_ipihandler(IPI_HARDCLOCK, NULL, ipi_hardclock, NULL, 0);
+ arm_setup_ipihandler(IPI_TLB, NULL, ipi_tlb, NULL, 0);
+
+#else
#ifdef IPI_IRQ_START
start = IPI_IRQ_START;
#ifdef IPI_IRQ_END
@@ -341,6 +445,7 @@
/* Enable ipi */
arm_unmask_irq(i);
}
+#endif
atomic_store_rel_int(&aps_ready, 1);
printf("Release APs\n");
Index: sys/arm/arm/nexus.c
===================================================================
--- sys/arm/arm/nexus.c (revision 277210)
+++ sys/arm/arm/nexus.c (working copy)
@@ -251,9 +251,12 @@
{
int ret = ENODEV;
+#ifdef ARM_INTRNG
+ ret = arm_intrng_config_irq(irq, trig, pol);
+#else
if (arm_config_irq)
ret = (*arm_config_irq)(irq, trig, pol);
-
+#endif
return (ret);
}
@@ -267,9 +270,14 @@
flags |= INTR_EXCL;
for (irq = rman_get_start(res); irq <= rman_get_end(res); irq++) {
+#if defined(ARM_INTRNG)
+ arm_setup_irqhandler(child, filt, intr, arg, irq, flags,
+ cookiep);
+#else
arm_setup_irqhandler(device_get_nameunit(child),
filt, intr, arg, irq, flags, cookiep);
arm_unmask_irq(irq);
+#endif
}
return (0);
}
@@ -278,7 +286,11 @@
nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
{
+#if defined(ARM_INTRNG)
+ return (arm_remove_irqhandler(child, rman_get_start(r), ih));
+#else
return (arm_remove_irqhandler(rman_get_start(r), ih));
+#endif
}
Index: sys/arm/include/fdt.h
===================================================================
--- sys/arm/include/fdt.h (revision 277210)
+++ sys/arm/include/fdt.h (working copy)
@@ -32,6 +32,7 @@
#ifndef _MACHINE_FDT_H_
#define _MACHINE_FDT_H_
+#include "opt_platform.h"
#include <dev/ofw/openfirm.h>
#include <vm/vm.h>
@@ -40,12 +41,25 @@
#include <machine/bus.h>
#include <machine/intr.h>
+#ifdef ARM_INTRNG
+
/* Max interrupt number */
+#define FDT_INTR_MAX (0xffff)
+
+/* Map phandle/intpin pair to global IRQ number */
+#define FDT_MAP_IRQ(node, pin) (arm_fdt_map_irq(node, pin))
+#define FDT_DESCRIBE_IRQ(irq) (arm_describe_irq(irq))
+
+#else
+
+/* Max interrupt number */
#define FDT_INTR_MAX NIRQ
/* Map phandle/intpin pair to global IRQ number */
#define FDT_MAP_IRQ(node, pin) (pin)
+#endif /* ARM_INTRNG */
+
/*
* Bus space tag. XXX endianess info needs to be derived from the blob.
*/
Index: sys/arm/include/intr.h
===================================================================
--- sys/arm/include/intr.h (revision 277210)
+++ sys/arm/include/intr.h (working copy)
@@ -43,6 +43,68 @@
#include <dev/ofw/openfirm.h>
#endif
+#ifdef ARM_INTRNG
+
+#ifndef NIRQ
+#define NIRQ 1024 /* XXX - It should be an option. */
+#endif
+
+#define INTR_SOLO INTR_MD1
+
+struct arm_irqsrc;
+typedef int arm_irq_filter_t(void *arg, struct trapframe *tf);
+
+#define ISRCT_NONE 0
+#define ISRCT_SPI 1
+#define ISRCT_PPI 2
+#define ISRCT_SGI 3
+
+#define ISRCF_NAMESPACE 0x0001
+#define ISRCF_FDT 0x0002
+#define ISRCF_MAPPED 0x0004
+
+void arm_dispatch_spi(struct arm_irqsrc *isrc, struct trapframe *tf);
+#define arm_dispatch_ppi arm_dispatch_spi
+
+u_int arm_namespace_map_irq(device_t dev, uint8_t type, uint16_t num);
+
+void* arm_irq_get_data(struct arm_irqsrc *isrc);
+void arm_irq_set_data(struct arm_irqsrc *isrc, void *data);
+void arm_irq_set_name(struct arm_irqsrc *isrc, const char *name);
+void arm_irq_set_namespace(struct arm_irqsrc *isrc, uint8_t type, uint16_t num);
+
+int arm_irq_set_root(device_t dev, arm_irq_filter_t *filter, void *arg,
+ u_int ipicount);
+
+int arm_register_pic(device_t dev);
+int arm_unregister_pic(device_t dev);
+
+int arm_setup_irqhandler(device_t dev, driver_filter_t, driver_intr_t, void *,
+ u_int, int, void **);
+int arm_remove_irqhandler(device_t dev, u_int, void *);
+int arm_intrng_config_irq(u_int, enum intr_trigger, enum intr_polarity);
+
+const char *arm_describe_irq(u_int irq);
+
+#ifdef FDT
+u_int arm_fdt_map_irq(phandle_t, pcell_t *, u_int);
+#endif
+
+#ifdef SMP
+typedef void arm_ipi_filter_t(void *);
+
+void arm_dispatch_sgi(struct arm_irqsrc *isrc, struct trapframe *tf);
+
+#define ASIF_NOALLOC 0x0001
+
+int arm_setup_ipihandler(u_int ipi, char *name, arm_ipi_filter_t *filter,
+ void *arg, u_int flags);
+
+void arm_init_secondary_pic(void);
+#endif
+
+#else /* ARM_INTRNG */
+
/* XXX move to std.* files? */
#ifdef CPU_XSCALE_81342
#define NIRQ 128
@@ -71,7 +133,6 @@
#define NIRQ 32
#endif
-
int arm_get_next_irq(int);
void arm_mask_irq(uintptr_t);
void arm_unmask_irq(uintptr_t);
@@ -83,8 +144,6 @@
extern int (*arm_config_irq)(int irq, enum intr_trigger trig,
enum intr_polarity pol);
-void arm_irq_memory_barrier(uintptr_t);
-
void arm_init_secondary_ic(void);
int gic_decode_fdt(uint32_t iparentnode, uint32_t *intrcells, int *interrupt,
int *trig, int *pol);
@@ -93,4 +152,8 @@
int arm_fdt_map_irq(phandle_t, pcell_t *, int);
#endif
+#endif /* ARM_INTRNG */
+
+void arm_irq_memory_barrier(uintptr_t);
+
#endif /* _MACHINE_INTR_H */
Index: sys/arm/include/smp.h
===================================================================
--- sys/arm/include/smp.h (revision 277210)
+++ sys/arm/include/smp.h (working copy)
@@ -6,6 +6,18 @@
#include <sys/_cpuset.h>
#include <machine/pcb.h>
+#ifdef ARM_INTRNG
+enum {
+ IPI_AST,
+ IPI_PREEMPT,
+ IPI_RENDEZVOUS,
+ IPI_STOP,
+ IPI_STOP_HARD = IPI_STOP, /* These are synonyms on arm. */
+ IPI_HARDCLOCK,
+ IPI_TLB,
+ ARM_IPI_COUNT
+};
+#else
#define IPI_AST 0
#define IPI_PREEMPT 2
#define IPI_RENDEZVOUS 3
@@ -13,6 +25,7 @@
#define IPI_STOP_HARD 4
#define IPI_HARDCLOCK 6
#define IPI_TLB 7
+#endif
void init_secondary(int cpu);
void mpentry(void);
@@ -23,8 +36,10 @@
/* PIC interface */
void pic_ipi_send(cpuset_t cpus, u_int ipi);
+#ifndef ARM_INTRNG
void pic_ipi_clear(int ipi);
int pic_ipi_read(int arg);
+#endif
/* Platform interface */
void platform_mp_setmaxid(void);
Index: sys/arm/ti/omap4/omap4_mp.c
===================================================================
--- sys/arm/ti/omap4/omap4_mp.c (revision 277210)
+++ sys/arm/ti/omap4/omap4_mp.c (working copy)
@@ -41,7 +41,12 @@
void
platform_mp_init_secondary(void)
{
+
+#ifdef ARM_INTRNG
+ arm_init_secondary_pic();
+#else
arm_init_secondary_ic();
+#endif
}
void
Index: sys/conf/files.arm
===================================================================
--- sys/conf/files.arm (revision 277210)
+++ sys/conf/files.arm (working copy)
@@ -29,7 +29,8 @@
arm/arm/identcpu.c standard
arm/arm/in_cksum.c optional inet | inet6
arm/arm/in_cksum_arm.S optional inet | inet6
-arm/arm/intr.c standard
+arm/arm/intr.c optional !arm_intrng
+arm/arm/intrng.c optional arm_intrng
arm/arm/locore.S standard no-obj
arm/arm/machdep.c standard
arm/arm/mem.c optional mem
@@ -37,6 +38,7 @@
arm/arm/mp_machdep.c optional smp
arm/arm/nexus.c standard
arm/arm/physmem.c standard
+arm/arm/pic_if.m optional arm_intrng
arm/arm/pl190.c optional pl190
arm/arm/pl310.c optional pl310
arm/arm/platform.c optional platform
Index: sys/conf/options.arm
===================================================================
--- sys/conf/options.arm (revision 277210)
+++ sys/conf/options.arm (working copy)
@@ -2,6 +2,7 @@
ARM9_CACHE_WRITE_THROUGH opt_global.h
ARMV6 opt_global.h
ARM_CACHE_LOCK_ENABLE opt_global.h
+ARM_INTRNG opt_global.h
ARM_KERN_DIRECTMAP opt_vm.h
ARM_L2_PIPT opt_global.h
ARM_MANY_BOARD opt_global.h
Index: sys/dev/fdt/simplebus.c
===================================================================
--- sys/dev/fdt/simplebus.c (revision 277210)
+++ sys/dev/fdt/simplebus.c (working copy)
@@ -38,6 +38,8 @@
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
+#include <machine/fdt.h>
+
struct simplebus_range {
uint64_t bus;
uint64_t host;
@@ -68,6 +70,9 @@
int *, u_long, u_long, u_long, u_int);
static void simplebus_probe_nomatch(device_t bus, device_t child);
static int simplebus_print_child(device_t bus, device_t child);
+#ifdef ARM_INTRNG
+static int simplebus_print_irqs(struct resource_list *rl);
+#endif
/*
* ofw_bus interface
@@ -344,7 +349,11 @@
rv = 0;
rv += resource_list_print_type(&di->rl, "mem", SYS_RES_MEMORY, "%#lx");
+#ifdef ARM_INTRNG
+ rv += simplebus_print_irqs(&di->rl);
+#else
rv += resource_list_print_type(&di->rl, "irq", SYS_RES_IRQ, "%ld");
+#endif
return (rv);
}
@@ -383,3 +392,26 @@
rv += bus_print_child_footer(bus, child);
return (rv);
}
+
+#ifdef ARM_INTRNG
+static int
+simplebus_print_irqs(struct resource_list *rl)
+{
+ struct resource_list_entry *rle;
+ int printed, retval;
+
+ printed = 0;
+ retval = 0;
+
+ STAILQ_FOREACH(rle, rl, link) {
+ if (rle->type != SYS_RES_IRQ)
+ continue;
+
+ retval += printf("%s", printed ? "," : " irq ");
+ retval += printf("%s", FDT_DESCRIBE_IRQ(rle->start));
+ printed++;
+ }
+
+ return (retval);
+}
+#endif
-------------- next part --------------
A non-text attachment was scrubbed...
Name: pic_if.m
Type: application/octet-stream
Size: 2871 bytes
Desc: not available
URL: <http://lists.freebsd.org/pipermail/freebsd-arm/attachments/20150115/def0d31f/attachment.obj>
More information about the freebsd-arm
mailing list