Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/subr_intr.c
Show First 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | |||||
MALLOC_DECLARE(M_INTRNG); | MALLOC_DECLARE(M_INTRNG); | ||||
MALLOC_DEFINE(M_INTRNG, "intr", "intr interrupt handling"); | MALLOC_DEFINE(M_INTRNG, "intr", "intr interrupt handling"); | ||||
/* Main interrupt handler called from assembler -> 'hidden' for C code. */ | /* Main interrupt handler called from assembler -> 'hidden' for C code. */ | ||||
void intr_irq_handler(struct trapframe *tf); | void intr_irq_handler(struct trapframe *tf); | ||||
/* Root interrupt controller stuff. */ | /* Root interrupt controller stuff. */ | ||||
device_t intr_irq_root_dev; | device_t intr_irq_root_dev; | ||||
static intr_irq_filter_t *irq_root_filter; | static driver_filter_t *irq_root_filter; | ||||
static void *irq_root_arg; | static void *irq_root_arg; | ||||
static u_int irq_root_ipicount; | static u_int irq_root_ipicount; | ||||
struct intr_pic_child { | struct intr_pic_child { | ||||
SLIST_ENTRY(intr_pic_child) pc_next; | SLIST_ENTRY(intr_pic_child) pc_next; | ||||
struct intr_pic *pc_pic; | struct intr_pic *pc_pic; | ||||
intr_child_irq_filter_t *pc_filter; | intr_child_irq_filter_t *pc_filter; | ||||
void *pc_filter_arg; | void *pc_filter_arg; | ||||
▲ Show 20 Lines • Show All 282 Lines • ▼ Show 20 Lines | |||||
u_long | u_long | ||||
intr_isrc_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf) | intr_isrc_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf) | ||||
{ | { | ||||
KASSERT(isrc != NULL, ("%s: no source", __func__)); | KASSERT(isrc != NULL, ("%s: no source", __func__)); | ||||
isrc_increment_count(isrc); | isrc_increment_count(isrc); | ||||
#ifdef INTR_SOLO | |||||
if (isrc->isrc_filter != NULL) { | |||||
int error; | |||||
error = isrc->isrc_filter(isrc->isrc_arg, tf); | |||||
PIC_POST_FILTER(isrc->isrc_dev, isrc); | |||||
if (error == FILTER_HANDLED) | |||||
return (0); | |||||
} else | |||||
#endif | |||||
if (intr_event_handle(isrc->isrc_event, tf) == 0) | if (intr_event_handle(isrc->isrc_event, tf) == 0) | ||||
return (0); | return (0); | ||||
return (isrc_increment_straycount(isrc)); | return (isrc_increment_straycount(isrc)); | ||||
} | } | ||||
/* | /* | ||||
* Alloc unique interrupt number (resource handle) for interrupt source. | * Alloc unique interrupt number (resource handle) for interrupt source. | ||||
▲ Show 20 Lines • Show All 151 Lines • ▼ Show 20 Lines | intr_isrc_init_on_cpu(struct intr_irqsrc *isrc, u_int cpu) | ||||
if (isrc->isrc_flags & INTR_ISRCF_BOUND) | if (isrc->isrc_flags & INTR_ISRCF_BOUND) | ||||
return (CPU_ISSET(cpu, &isrc->isrc_cpu)); | return (CPU_ISSET(cpu, &isrc->isrc_cpu)); | ||||
CPU_SET(cpu, &isrc->isrc_cpu); | CPU_SET(cpu, &isrc->isrc_cpu); | ||||
return (true); | return (true); | ||||
} | } | ||||
#endif | #endif | ||||
#ifdef INTR_SOLO | |||||
/* | /* | ||||
* Setup filter into interrupt source. | |||||
*/ | |||||
static int | |||||
iscr_setup_filter(struct intr_irqsrc *isrc, const char *name, | |||||
intr_irq_filter_t *filter, void *arg, void **cookiep) | |||||
{ | |||||
if (filter == NULL) | |||||
return (EINVAL); | |||||
mtx_lock(&isrc_table_lock); | |||||
/* | |||||
* Make sure that we do not mix the two ways | |||||
* how we handle interrupt sources. | |||||
*/ | |||||
if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) { | |||||
mtx_unlock(&isrc_table_lock); | |||||
return (EBUSY); | |||||
} | |||||
isrc->isrc_filter = filter; | |||||
isrc->isrc_arg = arg; | |||||
isrc_update_name(isrc, name); | |||||
mtx_unlock(&isrc_table_lock); | |||||
*cookiep = isrc; | |||||
return (0); | |||||
} | |||||
#endif | |||||
/* | |||||
* Interrupt source pre_ithread method for MI interrupt framework. | * Interrupt source pre_ithread method for MI interrupt framework. | ||||
*/ | */ | ||||
static void | static void | ||||
intr_isrc_pre_ithread(void *arg) | intr_isrc_pre_ithread(void *arg) | ||||
{ | { | ||||
struct intr_irqsrc *isrc = arg; | struct intr_irqsrc *isrc = arg; | ||||
PIC_PRE_ITHREAD(isrc->isrc_dev, isrc); | PIC_PRE_ITHREAD(isrc->isrc_dev, isrc); | ||||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | isrc_event_create(struct intr_irqsrc *isrc) | ||||
error = intr_event_create(&ie, isrc, 0, intr_isrc_pre_ithread, | error = intr_event_create(&ie, isrc, 0, intr_isrc_pre_ithread, | ||||
intr_isrc_post_ithread, intr_isrc_post_filter, | intr_isrc_post_ithread, intr_isrc_post_filter, | ||||
intr_isrc_assign_cpu, "%s:", isrc->isrc_name); | intr_isrc_assign_cpu, "%s:", isrc->isrc_name); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
mtx_lock(&isrc_table_lock); | mtx_lock(&isrc_table_lock); | ||||
/* | |||||
* Make sure that we do not mix the two ways | |||||
* how we handle interrupt sources. Let contested event wins. | |||||
*/ | |||||
#ifdef INTR_SOLO | |||||
if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) { | |||||
#else | |||||
if (isrc->isrc_event != NULL) { | if (isrc->isrc_event != NULL) { | ||||
#endif | /* We entered a race and lost. An event was added. */ | ||||
mtx_unlock(&isrc_table_lock); | mtx_unlock(&isrc_table_lock); | ||||
intr_event_destroy(ie); | return (intr_event_destroy(ie)); | ||||
return (isrc->isrc_event != NULL ? EBUSY : 0); | |||||
} | } | ||||
/* We may or may not have entered a race and won. */ | |||||
isrc->isrc_event = ie; | isrc->isrc_event = ie; | ||||
mtx_unlock(&isrc_table_lock); | mtx_unlock(&isrc_table_lock); | ||||
return (0); | return (0); | ||||
} | } | ||||
#ifdef notyet | #ifdef notyet | ||||
/* | /* | ||||
* Destroy interrupt event for interrupt source. | * Destroy interrupt event for interrupt source. | ||||
▲ Show 20 Lines • Show All 168 Lines • ▼ Show 20 Lines | |||||
* | * | ||||
* In FDT case, according to ePAPR approved version 1.1 from 08 April 2011, | * In FDT case, according to ePAPR approved version 1.1 from 08 April 2011, | ||||
* page 30: | * page 30: | ||||
* "The root of the interrupt tree is determined when traversal | * "The root of the interrupt tree is determined when traversal | ||||
* of the interrupt tree reaches an interrupt controller node without | * of the interrupt tree reaches an interrupt controller node without | ||||
* an interrupts property and thus no explicit interrupt parent." | * an interrupts property and thus no explicit interrupt parent." | ||||
*/ | */ | ||||
int | int | ||||
intr_pic_claim_root(device_t dev, intptr_t xref, intr_irq_filter_t *filter, | intr_pic_claim_root(device_t dev, intptr_t xref, driver_filter_t *filter, | ||||
void *arg, u_int ipicount) | void *arg, u_int ipicount) | ||||
{ | { | ||||
struct intr_pic *pic; | struct intr_pic *pic; | ||||
pic = pic_lookup(dev, xref, FLAG_PIC); | pic = pic_lookup(dev, xref, FLAG_PIC); | ||||
if (pic == NULL) { | if (pic == NULL) { | ||||
device_printf(dev, "not registered\n"); | device_printf(dev, "not registered\n"); | ||||
return (EINVAL); | return (EINVAL); | ||||
▲ Show 20 Lines • Show All 186 Lines • ▼ Show 20 Lines | intr_setup_irq(device_t dev, struct resource *res, driver_filter_t filt, | ||||
if (isrc == NULL) { | if (isrc == NULL) { | ||||
/* XXX TODO DISCONECTED PICs */ | /* XXX TODO DISCONECTED PICs */ | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
data = rman_get_virtual(res); | data = rman_get_virtual(res); | ||||
name = device_get_nameunit(dev); | name = device_get_nameunit(dev); | ||||
#ifdef INTR_SOLO | error = isrc_add_handler(isrc, name, filt, hand, arg, flags, cookiep); | ||||
/* | |||||
* Standard handling is done through MI interrupt framework. However, | |||||
* some interrupts could request solely own special handling. This | |||||
* non standard handling can be used for interrupt controllers without | |||||
* handler (filter only), so in case that interrupt controllers are | |||||
* chained, MI interrupt framework is called only in leaf controller. | |||||
* | |||||
* Note that root interrupt controller routine is served as well, | |||||
* however in intr_irq_handler(), i.e. main system dispatch routine. | |||||
*/ | |||||
if (flags & INTR_SOLO && hand != NULL) { | |||||
debugf("irq %u cannot solo on %s\n", irq, name); | |||||
return (EINVAL); | |||||
} | |||||
if (flags & INTR_SOLO) { | |||||
error = iscr_setup_filter(isrc, name, (intr_irq_filter_t *)filt, | |||||
arg, cookiep); | |||||
debugf("irq %u setup filter error %d on %s\n", isrc->isrc_irq, error, | |||||
name); | |||||
} else | |||||
#endif | |||||
{ | |||||
error = isrc_add_handler(isrc, name, filt, hand, arg, flags, | |||||
cookiep); | |||||
debugf("irq %u add handler error %d on %s\n", isrc->isrc_irq, error, name); | debugf("irq %u add handler error %d on %s\n", isrc->isrc_irq, error, name); | ||||
} | |||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
mtx_lock(&isrc_table_lock); | mtx_lock(&isrc_table_lock); | ||||
error = PIC_SETUP_INTR(isrc->isrc_dev, isrc, res, data); | error = PIC_SETUP_INTR(isrc->isrc_dev, isrc, res, data); | ||||
if (error == 0) { | if (error == 0) { | ||||
isrc->isrc_handlers++; | isrc->isrc_handlers++; | ||||
if (isrc->isrc_handlers == 1) | if (isrc->isrc_handlers == 1) | ||||
Show All 18 Lines | intr_teardown_irq(device_t dev, struct resource *res, void *cookie) | ||||
res_id = (u_int)rman_get_start(res); | res_id = (u_int)rman_get_start(res); | ||||
isrc = intr_map_get_isrc(res_id); | isrc = intr_map_get_isrc(res_id); | ||||
if (isrc == NULL || isrc->isrc_handlers == 0) | if (isrc == NULL || isrc->isrc_handlers == 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
data = rman_get_virtual(res); | data = rman_get_virtual(res); | ||||
#ifdef INTR_SOLO | |||||
if (isrc->isrc_filter != NULL) { | |||||
if (isrc != cookie) | |||||
return (EINVAL); | |||||
mtx_lock(&isrc_table_lock); | |||||
isrc->isrc_filter = NULL; | |||||
isrc->isrc_arg = NULL; | |||||
isrc->isrc_handlers = 0; | |||||
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); | |||||
} | |||||
#endif | |||||
if (isrc != intr_handler_source(cookie)) | if (isrc != intr_handler_source(cookie)) | ||||
return (EINVAL); | return (EINVAL); | ||||
error = intr_event_remove_handler(cookie); | error = intr_event_remove_handler(cookie); | ||||
if (error == 0) { | if (error == 0) { | ||||
mtx_lock(&isrc_table_lock); | mtx_lock(&isrc_table_lock); | ||||
isrc->isrc_handlers--; | isrc->isrc_handlers--; | ||||
if (isrc->isrc_handlers == 0) | if (isrc->isrc_handlers == 0) | ||||
Show All 15 Lines | intr_describe_irq(device_t dev, struct resource *res, void *cookie, | ||||
KASSERT(rman_get_start(res) == rman_get_end(res), | KASSERT(rman_get_start(res) == rman_get_end(res), | ||||
("%s: more interrupts in resource", __func__)); | ("%s: more interrupts in resource", __func__)); | ||||
res_id = (u_int)rman_get_start(res); | res_id = (u_int)rman_get_start(res); | ||||
isrc = intr_map_get_isrc(res_id); | isrc = intr_map_get_isrc(res_id); | ||||
if (isrc == NULL || isrc->isrc_handlers == 0) | if (isrc == NULL || isrc->isrc_handlers == 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
#ifdef INTR_SOLO | |||||
if (isrc->isrc_filter != NULL) { | |||||
if (isrc != cookie) | |||||
return (EINVAL); | |||||
mtx_lock(&isrc_table_lock); | |||||
isrc_update_name(isrc, descr); | |||||
mtx_unlock(&isrc_table_lock); | |||||
return (0); | |||||
} | |||||
#endif | |||||
error = intr_event_describe_handler(isrc->isrc_event, cookie, descr); | error = intr_event_describe_handler(isrc->isrc_event, cookie, descr); | ||||
if (error == 0) { | if (error == 0) { | ||||
mtx_lock(&isrc_table_lock); | mtx_lock(&isrc_table_lock); | ||||
intrcnt_updatename(isrc); | intrcnt_updatename(isrc); | ||||
mtx_unlock(&isrc_table_lock); | mtx_unlock(&isrc_table_lock); | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
#ifdef SMP | #ifdef SMP | ||||
int | int | ||||
intr_bind_irq(device_t dev, struct resource *res, int cpu) | intr_bind_irq(device_t dev, struct resource *res, int cpu) | ||||
{ | { | ||||
struct intr_irqsrc *isrc; | struct intr_irqsrc *isrc; | ||||
u_int res_id; | u_int res_id; | ||||
KASSERT(rman_get_start(res) == rman_get_end(res), | KASSERT(rman_get_start(res) == rman_get_end(res), | ||||
("%s: more interrupts in resource", __func__)); | ("%s: more interrupts in resource", __func__)); | ||||
res_id = (u_int)rman_get_start(res); | res_id = (u_int)rman_get_start(res); | ||||
isrc = intr_map_get_isrc(res_id); | isrc = intr_map_get_isrc(res_id); | ||||
if (isrc == NULL || isrc->isrc_handlers == 0) | if (isrc == NULL || isrc->isrc_handlers == 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
#ifdef INTR_SOLO | |||||
if (isrc->isrc_filter != NULL) | |||||
return (intr_isrc_assign_cpu(isrc, cpu)); | |||||
#endif | |||||
return (intr_event_bind(isrc->isrc_event, cpu)); | return (intr_event_bind(isrc->isrc_event, cpu)); | ||||
} | } | ||||
/* | /* | ||||
* Return the CPU that the next interrupt source should use. | * Return the CPU that the next interrupt source should use. | ||||
* For now just returns the next CPU according to round-robin. | * For now just returns the next CPU according to round-robin. | ||||
*/ | */ | ||||
u_int | u_int | ||||
▲ Show 20 Lines • Show All 535 Lines • Show Last 20 Lines |