Generalize IPI support for ARM intrng and use it for interrupt
controller IPI provider. New struct intr_ipi is defined which keeps all info about an IPI: its name, counter, send and dispatch methods. Generic intr_ipi_setup(), intr_ipi_send() and intr_ipi_dispatch() functions are implemented. An IPI provider must implement two functions: (1) an intr_ipi_send_t function which is able to send an IPI, (2) a setup function which initializes itself for an IPI and calls intr_ipi_setup() with appropriate arguments. Differential Revision: https://reviews.freebsd.org/D5700
This commit is contained in:
parent
6227e59635
commit
61c8fde5d6
@ -121,6 +121,11 @@ __FBSDID("$FreeBSD$");
|
||||
static u_int gic_irq_cpu;
|
||||
static int arm_gic_intr(void *);
|
||||
static int arm_gic_bind(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]
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct arm_gic_softc {
|
||||
@ -562,7 +567,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, tf);
|
||||
intr_ipi_dispatch(ISRC_IPI(isrc), tf);
|
||||
goto next_irq;
|
||||
#else
|
||||
device_printf(sc->gic_dev, "SGI %u on UP system detected\n",
|
||||
@ -918,6 +923,20 @@ arm_gic_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus)
|
||||
|
||||
gic_d_write_4(sc, GICD_SGIR(0), val | irq);
|
||||
}
|
||||
|
||||
static int
|
||||
arm_gic_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc *isrc)
|
||||
{
|
||||
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;
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
static int
|
||||
@ -1146,6 +1165,7 @@ static device_method_t arm_gic_methods[] = {
|
||||
DEVMETHOD(pic_bind, arm_gic_bind),
|
||||
DEVMETHOD(pic_init_secondary, arm_gic_init_secondary),
|
||||
DEVMETHOD(pic_ipi_send, arm_gic_ipi_send),
|
||||
DEVMETHOD(pic_ipi_setup, arm_gic_ipi_setup),
|
||||
#endif
|
||||
#endif
|
||||
{ 0, 0 }
|
||||
|
@ -64,8 +64,19 @@ __FBSDID("$FreeBSD$");
|
||||
#include "pic_if.h"
|
||||
|
||||
#ifdef SMP
|
||||
static struct intr_irqsrc ipi_sources[INTR_IPI_COUNT];
|
||||
static u_int ipi_next_num;
|
||||
#define INTR_IPI_NAMELEN (MAXCOMLEN + 1)
|
||||
|
||||
struct intr_ipi {
|
||||
intr_ipi_handler_t * ii_handler;
|
||||
void * ii_handler_arg;
|
||||
intr_ipi_send_t * ii_send;
|
||||
void * ii_send_arg;
|
||||
char ii_name[INTR_IPI_NAMELEN];
|
||||
u_long * ii_count;
|
||||
};
|
||||
|
||||
static struct intr_ipi ipi_sources[INTR_IPI_COUNT];
|
||||
u_int ipi_next_num;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -134,10 +145,7 @@ arm_irq_memory_barrier(uintptr_t irq)
|
||||
|
||||
#ifdef ARM_INTRNG
|
||||
#ifdef SMP
|
||||
/*
|
||||
* Lookup IPI source.
|
||||
*/
|
||||
static struct intr_irqsrc *
|
||||
static inline struct intr_ipi *
|
||||
intr_ipi_lookup(u_int ipi)
|
||||
{
|
||||
|
||||
@ -147,112 +155,97 @@ intr_ipi_lookup(u_int ipi)
|
||||
return (&ipi_sources[ipi]);
|
||||
}
|
||||
|
||||
/*
|
||||
* interrupt controller dispatch function for IPIs. It should
|
||||
* be called straight from the interrupt controller, when associated
|
||||
* interrupt source is learned. Or from anybody who has an interrupt
|
||||
* source mapped.
|
||||
*/
|
||||
void
|
||||
intr_ipi_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf)
|
||||
intr_ipi_dispatch(u_int ipi, struct trapframe *tf)
|
||||
{
|
||||
void *arg;
|
||||
struct intr_ipi *ii;
|
||||
|
||||
KASSERT(isrc != NULL, ("%s: no source", __func__));
|
||||
ii = intr_ipi_lookup(ipi);
|
||||
if (ii->ii_count == NULL)
|
||||
panic("%s: not setup IPI %u", __func__, ipi);
|
||||
|
||||
intr_ipi_increment_count(isrc->isrc_count, PCPU_GET(cpuid));
|
||||
intr_ipi_increment_count(ii->ii_count, PCPU_GET(cpuid));
|
||||
|
||||
/*
|
||||
* Supply ipi filter with trapframe argument
|
||||
* if none is registered.
|
||||
*/
|
||||
arg = isrc->isrc_arg != NULL ? isrc->isrc_arg : tf;
|
||||
isrc->isrc_ipifilter(arg);
|
||||
arg = ii->ii_handler_arg != NULL ? ii->ii_handler_arg : tf;
|
||||
ii->ii_handler(arg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Map IPI into interrupt controller.
|
||||
*
|
||||
* Not SMP coherent.
|
||||
*/
|
||||
static int
|
||||
ipi_map(struct intr_irqsrc *isrc, u_int ipi)
|
||||
void
|
||||
intr_ipi_send(cpuset_t cpus, u_int ipi)
|
||||
{
|
||||
boolean_t is_percpu;
|
||||
int error;
|
||||
struct intr_ipi *ii;
|
||||
|
||||
if (ipi >= INTR_IPI_COUNT)
|
||||
panic("%s: no such IPI %u", __func__, ipi);
|
||||
ii = intr_ipi_lookup(ipi);
|
||||
if (ii->ii_count == NULL)
|
||||
panic("%s: not setup IPI %u", __func__, ipi);
|
||||
|
||||
KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__));
|
||||
|
||||
isrc->isrc_type = INTR_ISRCT_NAMESPACE;
|
||||
isrc->isrc_nspc_type = INTR_IRQ_NSPC_IPI;
|
||||
isrc->isrc_nspc_num = ipi_next_num;
|
||||
|
||||
error = PIC_REGISTER(intr_irq_root_dev, isrc, &is_percpu);
|
||||
if (error == 0) {
|
||||
isrc->isrc_dev = intr_irq_root_dev;
|
||||
ipi_next_num++;
|
||||
}
|
||||
return (error);
|
||||
ii->ii_send(ii->ii_send_arg, cpus);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup IPI handler to interrupt source.
|
||||
*
|
||||
* Note that there could be more ways how to send and receive IPIs
|
||||
* on a platform like fast interrupts for example. In that case,
|
||||
* one can call this function with ASIF_NOALLOC flag set and then
|
||||
* call intr_ipi_dispatch() when appropriate.
|
||||
*
|
||||
* Not SMP coherent.
|
||||
*/
|
||||
int
|
||||
intr_ipi_set_handler(u_int ipi, const char *name, intr_ipi_filter_t *filter,
|
||||
void *arg, u_int flags)
|
||||
void
|
||||
intr_ipi_setup(u_int ipi, const char *name, intr_ipi_handler_t *hand,
|
||||
void *h_arg, intr_ipi_send_t *send, void *s_arg)
|
||||
{
|
||||
struct intr_irqsrc *isrc;
|
||||
int error;
|
||||
struct intr_ipi *ii;
|
||||
|
||||
if (filter == NULL)
|
||||
return(EINVAL);
|
||||
ii = intr_ipi_lookup(ipi);
|
||||
|
||||
isrc = intr_ipi_lookup(ipi);
|
||||
if (isrc->isrc_ipifilter != NULL)
|
||||
return (EEXIST);
|
||||
KASSERT(hand != NULL, ("%s: ipi %u no handler", __func__, ipi));
|
||||
KASSERT(send != NULL, ("%s: ipi %u no sender", __func__, ipi));
|
||||
KASSERT(ii->ii_count == NULL, ("%s: ipi %u reused", __func__, ipi));
|
||||
|
||||
if ((flags & AISHF_NOALLOC) == 0) {
|
||||
error = ipi_map(isrc, ipi);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
}
|
||||
|
||||
isrc->isrc_ipifilter = filter;
|
||||
isrc->isrc_arg = arg;
|
||||
isrc->isrc_handlers = 1;
|
||||
isrc->isrc_count = intr_ipi_setup_counters(name);
|
||||
isrc->isrc_index = 0; /* it should not be used in IPI case */
|
||||
|
||||
if (isrc->isrc_dev != NULL) {
|
||||
PIC_ENABLE_INTR(isrc->isrc_dev, isrc);
|
||||
PIC_ENABLE_SOURCE(isrc->isrc_dev, isrc);
|
||||
}
|
||||
return (0);
|
||||
ii->ii_handler = hand;
|
||||
ii->ii_handler_arg = h_arg;
|
||||
ii->ii_send = send;
|
||||
ii->ii_send_arg = s_arg;
|
||||
strlcpy(ii->ii_name, name, INTR_IPI_NAMELEN);
|
||||
ii->ii_count = intr_ipi_setup_counters(name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send IPI thru interrupt controller.
|
||||
*/
|
||||
void
|
||||
pic_ipi_send(cpuset_t cpus, u_int ipi)
|
||||
static void
|
||||
pic_ipi_send(void *arg, cpuset_t cpus)
|
||||
{
|
||||
struct intr_irqsrc *isrc;
|
||||
|
||||
isrc = intr_ipi_lookup(ipi);
|
||||
|
||||
KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__));
|
||||
PIC_IPI_SEND(intr_irq_root_dev, isrc, cpus);
|
||||
PIC_IPI_SEND(intr_irq_root_dev, arg, cpus);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup IPI handler on interrupt controller.
|
||||
*
|
||||
* Not SMP coherent.
|
||||
*/
|
||||
int
|
||||
intr_pic_ipi_setup(u_int ipi, const char *name, intr_ipi_handler_t *hand,
|
||||
void *arg)
|
||||
{
|
||||
int error;
|
||||
struct intr_irqsrc *isrc;
|
||||
|
||||
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);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
ipi_next_num++;
|
||||
|
||||
isrc->isrc_dev = intr_irq_root_dev;
|
||||
isrc->isrc_handlers = 1;
|
||||
intr_ipi_setup(ipi, name, hand, arg, pic_ipi_send, isrc);
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
@ -429,12 +429,11 @@ release_aps(void *dummy __unused)
|
||||
return;
|
||||
|
||||
#ifdef ARM_INTRNG
|
||||
intr_ipi_set_handler(IPI_RENDEZVOUS, "rendezvous", ipi_rendezvous, NULL, 0);
|
||||
intr_ipi_set_handler(IPI_AST, "ast", ipi_ast, NULL, 0);
|
||||
intr_ipi_set_handler(IPI_STOP, "stop", ipi_stop, NULL, 0);
|
||||
intr_ipi_set_handler(IPI_PREEMPT, "preempt", ipi_preempt, NULL, 0);
|
||||
intr_ipi_set_handler(IPI_HARDCLOCK, "hardclock", ipi_hardclock, NULL, 0);
|
||||
|
||||
intr_pic_ipi_setup(IPI_RENDEZVOUS, "rendezvous", ipi_rendezvous, NULL);
|
||||
intr_pic_ipi_setup(IPI_AST, "ast", ipi_ast, NULL);
|
||||
intr_pic_ipi_setup(IPI_STOP, "stop", ipi_stop, NULL);
|
||||
intr_pic_ipi_setup(IPI_PREEMPT, "preempt", ipi_preempt, NULL);
|
||||
intr_pic_ipi_setup(IPI_HARDCLOCK, "hardclock", ipi_hardclock, NULL);
|
||||
#else
|
||||
#ifdef IPI_IRQ_START
|
||||
start = IPI_IRQ_START;
|
||||
@ -502,7 +501,11 @@ ipi_all_but_self(u_int ipi)
|
||||
other_cpus = all_cpus;
|
||||
CPU_CLR(PCPU_GET(cpuid), &other_cpus);
|
||||
CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi);
|
||||
#ifdef ARM_INTRNG
|
||||
intr_ipi_send(other_cpus, ipi);
|
||||
#else
|
||||
pic_ipi_send(other_cpus, ipi);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -514,7 +517,11 @@ ipi_cpu(int cpu, u_int ipi)
|
||||
CPU_SET(cpu, &cpus);
|
||||
|
||||
CTR3(KTR_SMP, "%s: cpu: %d, ipi: %x", __func__, cpu, ipi);
|
||||
#ifdef ARM_INTRNG
|
||||
intr_ipi_send(cpus, ipi);
|
||||
#else
|
||||
pic_ipi_send(cpus, ipi);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -522,6 +529,9 @@ ipi_selected(cpuset_t cpus, u_int ipi)
|
||||
{
|
||||
|
||||
CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi);
|
||||
#ifdef ARM_INTRNG
|
||||
intr_ipi_send(cpus, ipi);
|
||||
#else
|
||||
pic_ipi_send(cpus, ipi);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -52,14 +52,17 @@
|
||||
#include <sys/intr.h>
|
||||
|
||||
#ifdef SMP
|
||||
void intr_ipi_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf);
|
||||
typedef void intr_ipi_send_t(void *, cpuset_t);
|
||||
typedef void intr_ipi_handler_t(void *);
|
||||
|
||||
#define AISHF_NOALLOC 0x0001
|
||||
void intr_ipi_dispatch(u_int, struct trapframe *);
|
||||
void intr_ipi_send(cpuset_t, u_int);
|
||||
|
||||
int intr_ipi_set_handler(u_int ipi, const char *name, intr_ipi_filter_t *filter,
|
||||
void *arg, u_int flags);
|
||||
void intr_ipi_setup(u_int, const char *, intr_ipi_handler_t *, void *,
|
||||
intr_ipi_send_t *, void *);
|
||||
|
||||
int intr_pic_ipi_setup(u_int, const char *, intr_ipi_handler_t *, void *);
|
||||
#endif
|
||||
|
||||
#else /* ARM_INTRNG */
|
||||
|
||||
/* XXX move to std.* files? */
|
||||
|
@ -37,8 +37,8 @@ void ipi_cpu(int cpu, u_int ipi);
|
||||
void ipi_selected(cpuset_t cpus, u_int ipi);
|
||||
|
||||
/* PIC interface */
|
||||
void pic_ipi_send(cpuset_t cpus, u_int ipi);
|
||||
#ifndef ARM_INTRNG
|
||||
void pic_ipi_send(cpuset_t cpus, u_int ipi);
|
||||
void pic_ipi_clear(int ipi);
|
||||
int pic_ipi_read(int arg);
|
||||
#endif
|
||||
|
@ -60,6 +60,13 @@ CODE {
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
dflt_pic_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc *isrc)
|
||||
{
|
||||
|
||||
return (EOPNOTSUPP);
|
||||
}
|
||||
};
|
||||
|
||||
METHOD int register {
|
||||
@ -122,3 +129,9 @@ METHOD void ipi_send {
|
||||
struct intr_irqsrc *isrc;
|
||||
cpuset_t cpus;
|
||||
} DEFAULT null_pic_ipi_send;
|
||||
|
||||
METHOD int ipi_setup {
|
||||
device_t dev;
|
||||
u_int ipi;
|
||||
struct intr_irqsrc *isrc;
|
||||
} DEFAULT dflt_pic_ipi_setup;
|
||||
|
@ -311,8 +311,8 @@ intr_irq_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf)
|
||||
/*
|
||||
* Allocate interrupt source.
|
||||
*/
|
||||
static struct intr_irqsrc *
|
||||
isrc_alloc(u_int type, u_int extsize)
|
||||
struct intr_irqsrc *
|
||||
intr_isrc_alloc(u_int type, u_int extsize)
|
||||
{
|
||||
struct intr_irqsrc *isrc;
|
||||
|
||||
@ -329,8 +329,8 @@ isrc_alloc(u_int type, u_int extsize)
|
||||
/*
|
||||
* Free interrupt source.
|
||||
*/
|
||||
static void
|
||||
isrc_free(struct intr_irqsrc *isrc)
|
||||
void
|
||||
intr_isrc_free(struct intr_irqsrc *isrc)
|
||||
{
|
||||
|
||||
free(isrc, M_INTRNG);
|
||||
@ -462,20 +462,20 @@ intr_namespace_map_irq(device_t dev, uint16_t type, uint16_t num)
|
||||
struct intr_irqsrc *isrc, *new_isrc;
|
||||
int error;
|
||||
|
||||
new_isrc = isrc_alloc(INTR_ISRCT_NAMESPACE, 0);
|
||||
new_isrc = intr_isrc_alloc(INTR_ISRCT_NAMESPACE, 0);
|
||||
|
||||
mtx_lock(&isrc_table_lock);
|
||||
isrc = isrc_namespace_lookup(dev, type, num);
|
||||
if (isrc != NULL) {
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
isrc_free(new_isrc);
|
||||
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);
|
||||
isrc_free(new_isrc);
|
||||
intr_isrc_free(new_isrc);
|
||||
return (IRQ_INVALID); /* no space left */
|
||||
}
|
||||
|
||||
@ -526,20 +526,20 @@ intr_fdt_map_irq(phandle_t node, pcell_t *cells, u_int ncells)
|
||||
xref = (intptr_t)node; /* It's so simple for now. */
|
||||
|
||||
cellsize = ncells * sizeof(*cells);
|
||||
new_isrc = isrc_alloc(INTR_ISRCT_FDT, cellsize);
|
||||
new_isrc = intr_isrc_alloc(INTR_ISRCT_FDT, cellsize);
|
||||
|
||||
mtx_lock(&isrc_table_lock);
|
||||
isrc = isrc_fdt_lookup(xref, cells, ncells);
|
||||
if (isrc != NULL) {
|
||||
mtx_unlock(&isrc_table_lock);
|
||||
isrc_free(new_isrc);
|
||||
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);
|
||||
isrc_free(new_isrc);
|
||||
intr_isrc_free(new_isrc);
|
||||
return (IRQ_INVALID); /* no space left */
|
||||
}
|
||||
|
||||
|
@ -50,8 +50,6 @@ typedef int intr_irq_filter_t(void *arg);
|
||||
|
||||
#define INTR_ISRC_NAMELEN (MAXCOMLEN + 1)
|
||||
|
||||
typedef void intr_ipi_filter_t(void *arg);
|
||||
|
||||
enum intr_isrc_type {
|
||||
INTR_ISRCT_NAMESPACE,
|
||||
INTR_ISRCT_FDT
|
||||
@ -81,15 +79,17 @@ struct intr_irqsrc {
|
||||
struct intr_event * isrc_event;
|
||||
#ifdef INTR_SOLO
|
||||
intr_irq_filter_t * isrc_filter;
|
||||
#endif
|
||||
intr_ipi_filter_t * isrc_ipifilter;
|
||||
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);
|
||||
|
||||
void intr_irq_set_name(struct intr_irqsrc *isrc, const char *fmt, ...)
|
||||
__printflike(2, 3);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user