summaryrefslogtreecommitdiffstats
path: root/sys/arm/mv/mpic.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arm/mv/mpic.c')
-rw-r--r--sys/arm/mv/mpic.c346
1 files changed, 293 insertions, 53 deletions
diff --git a/sys/arm/mv/mpic.c b/sys/arm/mv/mpic.c
index ebd1477..e81820c 100644
--- a/sys/arm/mv/mpic.c
+++ b/sys/arm/mv/mpic.c
@@ -33,14 +33,20 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_platform.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/cpuset.h>
#include <sys/ktr.h>
+#include <sys/kdb.h>
#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
#include <sys/rman.h>
+#include <sys/proc.h>
#include <machine/bus.h>
#include <machine/intr.h>
@@ -48,11 +54,16 @@ __FBSDID("$FreeBSD$");
#include <machine/smp.h>
#include <arm/mv/mvvar.h>
+#include <arm/mv/mvreg.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/fdt/fdt_common.h>
+#ifdef ARM_INTRNG
+#include "pic_if.h"
+#endif
+
#ifdef DEBUG
#define debugf(fmt, args...) do { printf("%s(): ", __func__); \
printf(fmt,##args); } while (0)
@@ -60,46 +71,63 @@ __FBSDID("$FreeBSD$");
#define debugf(fmt, args...)
#endif
-#define MPIC_INT_ERR 4
-#define MPIC_INT_MSI 96
+#define MPIC_INT_ERR 4
+#define MPIC_INT_MSI 96
-#define IRQ_MASK 0x3ff
+#define IRQ_MASK 0x3ff
-#define MPIC_CTRL 0x0
-#define MPIC_SOFT_INT 0x4
-#define MPIC_SOFT_INT_DRBL1 (1 << 5)
-#define MPIC_ERR_CAUSE 0x20
-#define MPIC_ISE 0x30
-#define MPIC_ICE 0x34
+#define MPIC_CTRL 0x0
+#define MPIC_SOFT_INT 0x4
+#define MPIC_SOFT_INT_DRBL1 (1 << 5)
+#define MPIC_ERR_CAUSE 0x20
+#define MPIC_ISE 0x30
+#define MPIC_ICE 0x34
+#define MPIC_INT_CTL(irq) (0x100 + (irq)*4)
+#define MPIC_INT_IRQ_FIQ_MASK(cpuid) (0x101 << (cpuid))
+#define MPIC_CTRL_NIRQS(ctrl) (((ctrl) >> 2) & 0x3ff)
-#define MPIC_IN_DRBL 0x78
-#define MPIC_IN_DRBL_MASK 0x7c
-#define MPIC_CTP 0xb0
-#define MPIC_CTP 0xb0
-#define MPIC_IIACK 0xb4
-#define MPIC_ISM 0xb8
-#define MPIC_ICM 0xbc
-#define MPIC_ERR_MASK 0xec0
+#define MPIC_IN_DRBL 0x08
+#define MPIC_IN_DRBL_MASK 0x0c
+#define MPIC_PPI_CAUSE 0x10
+#define MPIC_CTP 0x40
+#define MPIC_IIACK 0x44
+#define MPIC_ISM 0x48
+#define MPIC_ICM 0x4c
+#define MPIC_ERR_MASK 0xe50
+
+#define MPIC_PPI 32
struct mv_mpic_softc {
device_t sc_dev;
- struct resource * mpic_res[3];
+ struct resource * mpic_res[4];
bus_space_tag_t mpic_bst;
bus_space_handle_t mpic_bsh;
bus_space_tag_t cpu_bst;
bus_space_handle_t cpu_bsh;
bus_space_tag_t drbl_bst;
bus_space_handle_t drbl_bsh;
+ struct mtx mtx;
+
+ struct intr_irqsrc ** mpic_isrcs;
+ int nirqs;
+ void * intr_hand;
};
static struct resource_spec mv_mpic_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ SYS_RES_MEMORY, 1, RF_ACTIVE },
- { SYS_RES_MEMORY, 2, RF_ACTIVE },
+ { SYS_RES_MEMORY, 2, RF_ACTIVE | RF_OPTIONAL },
+ { SYS_RES_IRQ, 0, RF_ACTIVE | RF_OPTIONAL },
{ -1, 0 }
};
+static struct ofw_compat_data compat_data[] = {
+ {"mrvl,mpic", true},
+ {"marvell,mpic", true},
+ {NULL, false}
+};
+
static struct mv_mpic_softc *mv_mpic_sc = NULL;
void mpic_send_ipi(int cpus, u_int ipi);
@@ -109,9 +137,21 @@ static int mv_mpic_attach(device_t);
uint32_t mv_mpic_get_cause(void);
uint32_t mv_mpic_get_cause_err(void);
uint32_t mv_mpic_get_msi(void);
+static void mpic_unmask_irq(uintptr_t nb);
+static void mpic_mask_irq(uintptr_t nb);
+static void mpic_mask_irq_err(uintptr_t nb);
+static void mpic_unmask_irq_err(uintptr_t nb);
+static int mpic_intr(void *arg);
+static void mpic_unmask_msi(void);
+#ifndef ARM_INTRNG
static void arm_mask_irq_err(uintptr_t);
static void arm_unmask_irq_err(uintptr_t);
-static void arm_unmask_msi(void);
+#endif
+
+#define MPIC_WRITE(softc, reg, val) \
+ bus_space_write_4((softc)->mpic_bst, (softc)->mpic_bsh, (reg), (val))
+#define MPIC_READ(softc, reg) \
+ bus_space_read_4((softc)->mpic_bst, (softc)->mpic_bsh, (reg))
#define MPIC_CPU_WRITE(softc, reg, val) \
bus_space_write_4((softc)->cpu_bst, (softc)->cpu_bsh, (reg), (val))
@@ -130,7 +170,7 @@ mv_mpic_probe(device_t dev)
if (!ofw_bus_status_okay(dev))
return (ENXIO);
- if (!ofw_bus_is_compatible(dev, "mrvl,mpic"))
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
return (ENXIO);
device_set_desc(dev, "Marvell Integrated Interrupt Controller");
@@ -142,6 +182,7 @@ mv_mpic_attach(device_t dev)
{
struct mv_mpic_softc *sc;
int error;
+ uint32_t val;
sc = (struct mv_mpic_softc *)device_get_softc(dev);
@@ -151,11 +192,20 @@ mv_mpic_attach(device_t dev)
sc->sc_dev = dev;
+ mtx_init(&sc->mtx, "MPIC lock", NULL, MTX_SPIN);
+
error = bus_alloc_resources(dev, mv_mpic_spec, sc->mpic_res);
if (error) {
device_printf(dev, "could not allocate resources\n");
return (ENXIO);
}
+#ifdef ARM_INTRNG
+ if (sc->mpic_res[3] == NULL)
+ device_printf(dev, "No interrupt to use.\n");
+ else
+ bus_setup_intr(dev, sc->mpic_res[3], INTR_TYPE_CLK,
+ mpic_intr, NULL, sc, &sc->intr_hand);
+#endif
sc->mpic_bst = rman_get_bustag(sc->mpic_res[0]);
sc->mpic_bsh = rman_get_bushandle(sc->mpic_res[0]);
@@ -163,21 +213,182 @@ mv_mpic_attach(device_t dev)
sc->cpu_bst = rman_get_bustag(sc->mpic_res[1]);
sc->cpu_bsh = rman_get_bushandle(sc->mpic_res[1]);
- sc->drbl_bst = rman_get_bustag(sc->mpic_res[2]);
- sc->drbl_bsh = rman_get_bushandle(sc->mpic_res[2]);
+ if (sc->mpic_res[2] != NULL) {
+ /* This is required only if MSIs are used. */
+ sc->drbl_bst = rman_get_bustag(sc->mpic_res[2]);
+ sc->drbl_bsh = rman_get_bushandle(sc->mpic_res[2]);
+ }
bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh,
MPIC_CTRL, 1);
MPIC_CPU_WRITE(mv_mpic_sc, MPIC_CTP, 0);
- arm_unmask_msi();
+ val = MPIC_READ(mv_mpic_sc, MPIC_CTRL);
+ 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 (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);
+ return (ENXIO);
+ }
+#endif
+
+ mpic_unmask_msi();
+
+ return (0);
+}
+
+#ifdef ARM_INTRNG
+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;
+
+ for (cause = MPIC_CPU_READ(sc, MPIC_PPI_CAUSE); cause > 0;
+ cause >>= 1, irq++) {
+ if (cause & 1) {
+ 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);
+ mpic_mask_irq(irq);
+ continue;
+ }
+ 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)
+{
+ u_int irq;
+
+ irq = isrc->isrc_data;
+ mpic_mask_irq(irq);
+}
+
+static void
+mpic_enable_source(device_t dev, struct intr_irqsrc *isrc)
+{
+ u_int irq;
+
+ irq = isrc->isrc_data;
+ mpic_unmask_irq(irq);
+}
+static void
+mpic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ mpic_disable_source(dev, isrc);
+}
+
+static void
+mpic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ mpic_enable_source(dev, isrc);
+}
+#endif
static device_method_t mv_mpic_methods[] = {
DEVMETHOD(device_probe, mv_mpic_probe),
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_post_ithread, mpic_post_ithread),
+ DEVMETHOD(pic_pre_ithread, mpic_pre_ithread),
+#endif
{ 0, 0 }
};
@@ -189,8 +400,10 @@ static driver_t mv_mpic_driver = {
static devclass_t mv_mpic_devclass;
-DRIVER_MODULE(mpic, simplebus, mv_mpic_driver, mv_mpic_devclass, 0, 0);
+EARLY_DRIVER_MODULE(mpic, simplebus, mv_mpic_driver, mv_mpic_devclass, 0, 0,
+ BUS_PASS_INTERRUPT);
+#ifndef ARM_INTRNG
int
arm_get_next_irq(int last)
{
@@ -219,49 +432,42 @@ void
arm_mask_irq(uintptr_t nb)
{
- MPIC_CPU_WRITE(mv_mpic_sc, MPIC_CTP, 1);
-
- if (nb < ERR_IRQ) {
- bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh,
- MPIC_ICE, nb);
- MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ISM, nb);
- } else if (nb < MSI_IRQ)
- arm_mask_irq_err(nb);
+ mpic_mask_irq(nb);
}
static void
arm_mask_irq_err(uintptr_t nb)
{
- uint32_t mask;
- uint8_t bit_off;
- bit_off = nb - ERR_IRQ;
- mask = MPIC_CPU_READ(mv_mpic_sc, MPIC_ERR_MASK);
- mask &= ~(1 << bit_off);
- MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ERR_MASK, mask);
+ mpic_mask_irq_err(nb);
}
void
arm_unmask_irq(uintptr_t nb)
{
- MPIC_CPU_WRITE(mv_mpic_sc, MPIC_CTP, 0);
-
- if (nb < ERR_IRQ) {
- bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh,
- MPIC_ISE, nb);
- MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ICM, nb);
- } else if (nb < MSI_IRQ)
- arm_unmask_irq_err(nb);
-
- if (nb == 0)
- MPIC_CPU_WRITE(mv_mpic_sc, MPIC_IN_DRBL_MASK, 0xffffffff);
+ mpic_unmask_irq(nb);
}
void
arm_unmask_irq_err(uintptr_t nb)
{
+
+ mpic_unmask_irq_err(nb);
+}
+#endif
+
+static void
+mpic_unmask_msi(void)
+{
+
+ mpic_unmask_irq(MPIC_INT_MSI);
+}
+
+static void
+mpic_unmask_irq_err(uintptr_t nb)
+{
uint32_t mask;
uint8_t bit_off;
@@ -276,10 +482,42 @@ arm_unmask_irq_err(uintptr_t nb)
}
static void
-arm_unmask_msi(void)
+mpic_mask_irq_err(uintptr_t nb)
+{
+ uint32_t mask;
+ uint8_t bit_off;
+
+ bit_off = nb - ERR_IRQ;
+ mask = MPIC_CPU_READ(mv_mpic_sc, MPIC_ERR_MASK);
+ mask &= ~(1 << bit_off);
+ MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ERR_MASK, mask);
+}
+
+static void
+mpic_unmask_irq(uintptr_t nb)
+{
+
+ if (nb < ERR_IRQ) {
+ bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh,
+ MPIC_ISE, nb);
+ MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ICM, nb);
+ } else if (nb < MSI_IRQ)
+ mpic_unmask_irq_err(nb);
+
+ if (nb == 0)
+ MPIC_CPU_WRITE(mv_mpic_sc, MPIC_IN_DRBL_MASK, 0xffffffff);
+}
+
+static void
+mpic_mask_irq(uintptr_t nb)
{
- arm_unmask_irq(MPIC_INT_MSI);
+ if (nb < ERR_IRQ) {
+ bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh,
+ MPIC_ICE, nb);
+ MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ISM, nb);
+ } else if (nb < MSI_IRQ)
+ mpic_mask_irq_err(nb);
}
uint32_t
@@ -313,6 +551,7 @@ mv_mpic_get_msi(void)
uint32_t cause;
uint8_t bit_off;
+ KASSERT(mv_mpic_sc->drbl_bst != NULL, ("No doorbell in mv_mpic_get_msi"));
cause = MPIC_DRBL_READ(mv_mpic_sc, 0);
if (cause)
@@ -359,7 +598,8 @@ mv_msi_data(int irq, uint64_t *addr, uint32_t *data)
return (0);
}
-#if defined(SMP)
+
+#if defined(SMP) && defined(SOC_MV_ARMADAXP)
void
intr_pic_init_secondary(void)
{
OpenPOWER on IntegriCloud