Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/subr_intr.c
Show All 25 Lines | |||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
/* | /* | ||||
* New-style Interrupt Framework | * New-style Interrupt Framework | ||||
* | * | ||||
* TODO: - to support IPI (PPI) enabling on other CPUs if already started | * TODO: - add support for disconnected PICs. | ||||
* - to complete things for removable PICs | * - to support IPI (PPI) enabling on other CPUs if already started. | ||||
* - to complete things for removable PICs. | |||||
*/ | */ | ||||
#include "opt_ddb.h" | #include "opt_ddb.h" | ||||
#include "opt_hwpmc_hooks.h" | #include "opt_hwpmc_hooks.h" | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
▲ Show 20 Lines • Show All 93 Lines • ▼ Show 20 Lines | |||||
/* Data for MI statistics reporting. */ | /* Data for MI statistics reporting. */ | ||||
u_long intrcnt[INTRCNT_COUNT]; | u_long intrcnt[INTRCNT_COUNT]; | ||||
char intrnames[INTRCNT_COUNT * INTRNAME_LEN]; | char intrnames[INTRCNT_COUNT * INTRNAME_LEN]; | ||||
size_t sintrcnt = sizeof(intrcnt); | size_t sintrcnt = sizeof(intrcnt); | ||||
size_t sintrnames = sizeof(intrnames); | size_t sintrnames = sizeof(intrnames); | ||||
static u_int intrcnt_index; | static u_int intrcnt_index; | ||||
static struct intr_irqsrc *intr_map_get_isrc(u_int res_id); | |||||
static void intr_map_set_isrc(u_int res_id, struct intr_irqsrc *isrc); | |||||
static void intr_map_copy_map_data(u_int res_id, device_t *dev, intptr_t *xref, | |||||
struct intr_map_data **data); | |||||
/* | /* | ||||
* Interrupt framework initialization routine. | * Interrupt framework initialization routine. | ||||
*/ | */ | ||||
static void | static void | ||||
intr_irq_init(void *dummy __unused) | intr_irq_init(void *dummy __unused) | ||||
{ | { | ||||
SLIST_INIT(&pic_list); | SLIST_INIT(&pic_list); | ||||
▲ Show 20 Lines • Show All 256 Lines • ▼ Show 20 Lines | if (irq_sources[isrc->isrc_irq] != isrc) | ||||
return (EINVAL); | return (EINVAL); | ||||
irq_sources[isrc->isrc_irq] = NULL; | irq_sources[isrc->isrc_irq] = NULL; | ||||
isrc->isrc_irq = INTR_IRQ_INVALID; /* just to be safe */ | isrc->isrc_irq = INTR_IRQ_INVALID; /* just to be safe */ | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Lookup interrupt source by interrupt number (resource handle). | |||||
*/ | |||||
static inline struct intr_irqsrc * | |||||
isrc_lookup(u_int irq) | |||||
{ | |||||
if (irq < nitems(irq_sources)) | |||||
return (irq_sources[irq]); | |||||
return (NULL); | |||||
} | |||||
/* | |||||
* Initialize interrupt source and register it into global interrupt table. | * Initialize interrupt source and register it into global interrupt table. | ||||
*/ | */ | ||||
int | int | ||||
intr_isrc_register(struct intr_irqsrc *isrc, device_t dev, u_int flags, | intr_isrc_register(struct intr_irqsrc *isrc, device_t dev, u_int flags, | ||||
const char *fmt, ...) | const char *fmt, ...) | ||||
{ | { | ||||
int error; | int error; | ||||
va_list ap; | va_list ap; | ||||
▲ Show 20 Lines • Show All 457 Lines • ▼ Show 20 Lines | #ifdef INVARIANTS | ||||
} | } | ||||
#endif | #endif | ||||
SLIST_INSERT_HEAD(&parent_pic->pic_children, newchild, pc_next); | SLIST_INSERT_HEAD(&parent_pic->pic_children, newchild, pc_next); | ||||
mtx_unlock_spin(&parent_pic->pic_child_lock); | mtx_unlock_spin(&parent_pic->pic_child_lock); | ||||
return (pic); | return (pic); | ||||
} | } | ||||
int | static int | ||||
intr_map_irq(device_t dev, intptr_t xref, struct intr_map_data *data, | intr_resolve_irq(device_t dev, intptr_t xref, struct intr_map_data *data, | ||||
u_int *irqp) | struct intr_irqsrc **isrc) | ||||
{ | { | ||||
int error; | |||||
struct intr_irqsrc *isrc; | |||||
struct intr_pic *pic; | struct intr_pic *pic; | ||||
struct intr_map_data_msi *msi; | |||||
if (data == NULL) | if (data == NULL) | ||||
return (EINVAL); | return (EINVAL); | ||||
pic = pic_lookup(dev, xref); | pic = pic_lookup(dev, xref); | ||||
if (pic == NULL) | if (pic == NULL) | ||||
return (ESRCH); | return (ESRCH); | ||||
switch (data->type) { | |||||
case INTR_MAP_DATA_MSI: | |||||
KASSERT((pic->pic_flags & FLAG_MSI) != 0, | |||||
("%s: Found a non-MSI controller: %s", __func__, | |||||
device_get_name(pic->pic_dev))); | |||||
msi = (struct intr_map_data_msi *)data; | |||||
*isrc = msi->isrc; | |||||
return (0); | |||||
default: | |||||
KASSERT((pic->pic_flags & FLAG_PIC) != 0, | KASSERT((pic->pic_flags & FLAG_PIC) != 0, | ||||
("%s: Found a non-PIC controller: %s", __func__, | ("%s: Found a non-PIC controller: %s", __func__, | ||||
device_get_name(pic->pic_dev))); | device_get_name(pic->pic_dev))); | ||||
return (PIC_MAP_INTR(pic->pic_dev, data, isrc)); | |||||
error = PIC_MAP_INTR(pic->pic_dev, data, &isrc); | |||||
if (error == 0) | |||||
*irqp = isrc->isrc_irq; | |||||
return (error); | |||||
} | } | ||||
} | |||||
int | int | ||||
intr_alloc_irq(device_t dev, struct resource *res) | intr_activate_irq(device_t dev, struct resource *res) | ||||
{ | { | ||||
device_t map_dev; | |||||
intptr_t map_xref; | |||||
struct intr_map_data *data; | struct intr_map_data *data; | ||||
struct intr_irqsrc *isrc; | struct intr_irqsrc *isrc; | ||||
u_int res_id; | |||||
int error; | |||||
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__)); | ||||
isrc = isrc_lookup(rman_get_start(res)); | res_id = (u_int)rman_get_start(res); | ||||
if (isrc == NULL) | if (intr_map_get_isrc(res_id) != NULL) | ||||
return (EINVAL); | panic("Attempt to double activation of resource id: %u\n", | ||||
res_id); | |||||
data = rman_get_virtual(res); | intr_map_copy_map_data(res_id, &map_dev, &map_xref, &data); | ||||
return (PIC_ALLOC_INTR(isrc->isrc_dev, isrc, res, data)); | error = intr_resolve_irq(map_dev, map_xref, data, &isrc); | ||||
if (error != 0) { | |||||
free(data, M_INTRNG); | |||||
/* XXX TODO DISCONECTED PICs */ | |||||
/* if (error == EINVAL) return(0); */ | |||||
return (error); | |||||
} | } | ||||
intr_map_set_isrc(res_id, isrc); | |||||
rman_set_virtual(res, data); | |||||
return (PIC_ACTIVATE_INTR(isrc->isrc_dev, isrc, res, data)); | |||||
} | |||||
int | int | ||||
intr_release_irq(device_t dev, struct resource *res) | intr_deactivate_irq(device_t dev, struct resource *res) | ||||
{ | { | ||||
struct intr_map_data *data; | struct intr_map_data *data; | ||||
struct intr_irqsrc *isrc; | struct intr_irqsrc *isrc; | ||||
u_int res_id; | |||||
int error; | |||||
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__)); | ||||
isrc = isrc_lookup(rman_get_start(res)); | res_id = (u_int)rman_get_start(res); | ||||
isrc = intr_map_get_isrc(res_id); | |||||
if (isrc == NULL) | if (isrc == NULL) | ||||
return (EINVAL); | panic("Attempt to deactivate non-active resource id: %u\n", | ||||
res_id); | |||||
data = rman_get_virtual(res); | data = rman_get_virtual(res); | ||||
return (PIC_RELEASE_INTR(isrc->isrc_dev, isrc, res, data)); | error = PIC_DEACTIVATE_INTR(isrc->isrc_dev, isrc, res, data); | ||||
intr_map_set_isrc(res_id, NULL); | |||||
rman_set_virtual(res, NULL); | |||||
free(data, M_INTRNG); | |||||
return (error); | |||||
} | } | ||||
int | int | ||||
intr_setup_irq(device_t dev, struct resource *res, driver_filter_t filt, | intr_setup_irq(device_t dev, struct resource *res, driver_filter_t filt, | ||||
driver_intr_t hand, void *arg, int flags, void **cookiep) | driver_intr_t hand, void *arg, int flags, void **cookiep) | ||||
{ | { | ||||
int error; | int error; | ||||
struct intr_map_data *data; | struct intr_map_data *data; | ||||
struct intr_irqsrc *isrc; | struct intr_irqsrc *isrc; | ||||
const char *name; | const char *name; | ||||
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__)); | ||||
isrc = isrc_lookup(rman_get_start(res)); | res_id = (u_int)rman_get_start(res); | ||||
if (isrc == NULL) | isrc = intr_map_get_isrc(res_id); | ||||
if (isrc == NULL) { | |||||
/* 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 | #ifdef INTR_SOLO | ||||
/* | /* | ||||
* Standard handling is done through MI interrupt framework. However, | * Standard handling is done through MI interrupt framework. However, | ||||
* some interrupts could request solely own special handling. This | * some interrupts could request solely own special handling. This | ||||
Show All 38 Lines | |||||
} | } | ||||
int | int | ||||
intr_teardown_irq(device_t dev, struct resource *res, void *cookie) | intr_teardown_irq(device_t dev, struct resource *res, void *cookie) | ||||
{ | { | ||||
int error; | int error; | ||||
struct intr_map_data *data; | struct intr_map_data *data; | ||||
struct intr_irqsrc *isrc; | struct intr_irqsrc *isrc; | ||||
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__)); | ||||
isrc = isrc_lookup(rman_get_start(res)); | res_id = (u_int)rman_get_start(res); | ||||
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 | #ifdef INTR_SOLO | ||||
if (isrc->isrc_filter != NULL) { | if (isrc->isrc_filter != NULL) { | ||||
if (isrc != cookie) | if (isrc != cookie) | ||||
Show All 27 Lines | |||||
} | } | ||||
int | int | ||||
intr_describe_irq(device_t dev, struct resource *res, void *cookie, | intr_describe_irq(device_t dev, struct resource *res, void *cookie, | ||||
const char *descr) | const char *descr) | ||||
{ | { | ||||
int error; | int error; | ||||
struct intr_irqsrc *isrc; | struct intr_irqsrc *isrc; | ||||
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__)); | ||||
isrc = isrc_lookup(rman_get_start(res)); | res_id = (u_int)rman_get_start(res); | ||||
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 | #ifdef INTR_SOLO | ||||
if (isrc->isrc_filter != NULL) { | if (isrc->isrc_filter != NULL) { | ||||
if (isrc != cookie) | if (isrc != cookie) | ||||
return (EINVAL); | return (EINVAL); | ||||
mtx_lock(&isrc_table_lock); | mtx_lock(&isrc_table_lock); | ||||
Show All 11 Lines | #endif | ||||
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; | |||||
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__)); | ||||
isrc = isrc_lookup(rman_get_start(res)); | res_id = (u_int)rman_get_start(res); | ||||
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 | #ifdef INTR_SOLO | ||||
if (isrc->isrc_filter != NULL) | if (isrc->isrc_filter != NULL) | ||||
return (intr_isrc_assign_cpu(isrc, cpu)); | return (intr_isrc_assign_cpu(isrc, cpu)); | ||||
#endif | #endif | ||||
return (intr_event_bind(isrc->isrc_event, cpu)); | return (intr_event_bind(isrc->isrc_event, cpu)); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | |||||
intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask) | intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask) | ||||
{ | { | ||||
return (PCPU_GET(cpuid)); | return (PCPU_GET(cpuid)); | ||||
} | } | ||||
#endif | #endif | ||||
/* | /* | ||||
* Allocate memory for new intr_map_data structure. | |||||
* Initialize common fields. | |||||
*/ | |||||
struct intr_map_data * | |||||
intr_alloc_map_data(enum intr_map_data_type type, size_t len, int flags) | |||||
{ | |||||
struct intr_map_data *data; | |||||
data = malloc(len, M_INTRNG, flags); | |||||
data->type = type; | |||||
data->len = len; | |||||
return (data); | |||||
} | |||||
void intr_free_intr_map_data(struct intr_map_data *data) | |||||
{ | |||||
free(data, M_INTRNG); | |||||
} | |||||
/* | |||||
* Register a MSI/MSI-X interrupt controller | * Register a MSI/MSI-X interrupt controller | ||||
*/ | */ | ||||
int | int | ||||
intr_msi_register(device_t dev, intptr_t xref) | intr_msi_register(device_t dev, intptr_t xref) | ||||
{ | { | ||||
struct intr_pic *pic; | struct intr_pic *pic; | ||||
if (dev == NULL) | if (dev == NULL) | ||||
Show All 11 Lines | |||||
int | int | ||||
intr_alloc_msi(device_t pci, device_t child, intptr_t xref, int count, | intr_alloc_msi(device_t pci, device_t child, intptr_t xref, int count, | ||||
int maxcount, int *irqs) | int maxcount, int *irqs) | ||||
{ | { | ||||
struct intr_irqsrc **isrc; | struct intr_irqsrc **isrc; | ||||
struct intr_pic *pic; | struct intr_pic *pic; | ||||
device_t pdev; | device_t pdev; | ||||
struct intr_map_data_msi *msi; | |||||
int err, i; | int err, i; | ||||
pic = pic_lookup(NULL, xref); | pic = pic_lookup(NULL, xref); | ||||
if (pic == NULL) | if (pic == NULL) | ||||
return (ESRCH); | return (ESRCH); | ||||
KASSERT((pic->pic_flags & FLAG_MSI) != 0, | KASSERT((pic->pic_flags & FLAG_MSI) != 0, | ||||
("%s: Found a non-MSI controller: %s", __func__, | ("%s: Found a non-MSI controller: %s", __func__, | ||||
device_get_name(pic->pic_dev))); | device_get_name(pic->pic_dev))); | ||||
isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK); | isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK); | ||||
err = MSI_ALLOC_MSI(pic->pic_dev, child, count, maxcount, &pdev, isrc); | err = MSI_ALLOC_MSI(pic->pic_dev, child, count, maxcount, &pdev, isrc); | ||||
if (err == 0) { | if (err != 0) { | ||||
for (i = 0; i < count; i++) { | free(isrc, M_INTRNG); | ||||
irqs[i] = isrc[i]->isrc_irq; | return (err); | ||||
} | } | ||||
} | |||||
for (i = 0; i < count; i++) { | |||||
msi = (struct intr_map_data_msi *)intr_alloc_map_data( | |||||
INTR_MAP_DATA_MSI, sizeof(*msi), M_WAITOK | M_ZERO); | |||||
msi-> isrc = isrc[i]; | |||||
irqs[i] = intr_map_irq(pic->pic_dev, xref, | |||||
(struct intr_map_data *)msi); | |||||
} | |||||
free(isrc, M_INTRNG); | free(isrc, M_INTRNG); | ||||
return (err); | return (err); | ||||
} | } | ||||
int | int | ||||
intr_release_msi(device_t pci, device_t child, intptr_t xref, int count, | intr_release_msi(device_t pci, device_t child, intptr_t xref, int count, | ||||
int *irqs) | int *irqs) | ||||
{ | { | ||||
struct intr_irqsrc **isrc; | struct intr_irqsrc **isrc; | ||||
struct intr_pic *pic; | struct intr_pic *pic; | ||||
int i, err; | int i, err; | ||||
pic = pic_lookup(NULL, xref); | pic = pic_lookup(NULL, xref); | ||||
if (pic == NULL) | if (pic == NULL) | ||||
return (ESRCH); | return (ESRCH); | ||||
KASSERT((pic->pic_flags & FLAG_MSI) != 0, | KASSERT((pic->pic_flags & FLAG_MSI) != 0, | ||||
("%s: Found a non-MSI controller: %s", __func__, | ("%s: Found a non-MSI controller: %s", __func__, | ||||
device_get_name(pic->pic_dev))); | device_get_name(pic->pic_dev))); | ||||
isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK); | isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK); | ||||
for (i = 0; i < count; i++) | |||||
isrc[i] = intr_map_get_isrc(irqs[i]); | |||||
err = MSI_RELEASE_MSI(pic->pic_dev, child, count, isrc); | |||||
for (i = 0; i < count; i++) { | for (i = 0; i < count; i++) { | ||||
isrc[i] = isrc_lookup(irqs[i]); | if (isrc[i] != NULL) | ||||
if (isrc == NULL) { | intr_unmap_irq(irqs[i]); | ||||
free(isrc, M_INTRNG); | |||||
return (EINVAL); | |||||
} | } | ||||
} | |||||
err = MSI_RELEASE_MSI(pic->pic_dev, child, count, isrc); | |||||
free(isrc, M_INTRNG); | free(isrc, M_INTRNG); | ||||
return (err); | return (err); | ||||
} | } | ||||
int | int | ||||
intr_alloc_msix(device_t pci, device_t child, intptr_t xref, int *irq) | intr_alloc_msix(device_t pci, device_t child, intptr_t xref, int *irq) | ||||
{ | { | ||||
struct intr_irqsrc *isrc; | struct intr_irqsrc *isrc; | ||||
struct intr_pic *pic; | struct intr_pic *pic; | ||||
device_t pdev; | device_t pdev; | ||||
struct intr_map_data_msi *msi; | |||||
int err; | int err; | ||||
pic = pic_lookup(NULL, xref); | pic = pic_lookup(NULL, xref); | ||||
if (pic == NULL) | if (pic == NULL) | ||||
return (ESRCH); | return (ESRCH); | ||||
KASSERT((pic->pic_flags & FLAG_MSI) != 0, | KASSERT((pic->pic_flags & FLAG_MSI) != 0, | ||||
("%s: Found a non-MSI controller: %s", __func__, | ("%s: Found a non-MSI controller: %s", __func__, | ||||
device_get_name(pic->pic_dev))); | device_get_name(pic->pic_dev))); | ||||
err = MSI_ALLOC_MSIX(pic->pic_dev, child, &pdev, &isrc); | err = MSI_ALLOC_MSIX(pic->pic_dev, child, &pdev, &isrc); | ||||
if (err != 0) | if (err != 0) | ||||
return (err); | return (err); | ||||
*irq = isrc->isrc_irq; | msi = (struct intr_map_data_msi *)intr_alloc_map_data( | ||||
INTR_MAP_DATA_MSI, sizeof(*msi), M_WAITOK | M_ZERO); | |||||
msi->isrc = isrc; | |||||
*irq = intr_map_irq(pic->pic_dev, xref, (struct intr_map_data *)msi); | |||||
return (0); | return (0); | ||||
} | } | ||||
int | int | ||||
intr_release_msix(device_t pci, device_t child, intptr_t xref, int irq) | intr_release_msix(device_t pci, device_t child, intptr_t xref, int irq) | ||||
{ | { | ||||
struct intr_irqsrc *isrc; | struct intr_irqsrc *isrc; | ||||
struct intr_pic *pic; | struct intr_pic *pic; | ||||
int err; | int err; | ||||
pic = pic_lookup(NULL, xref); | pic = pic_lookup(NULL, xref); | ||||
if (pic == NULL) | if (pic == NULL) | ||||
return (ESRCH); | return (ESRCH); | ||||
KASSERT((pic->pic_flags & FLAG_MSI) != 0, | KASSERT((pic->pic_flags & FLAG_MSI) != 0, | ||||
("%s: Found a non-MSI controller: %s", __func__, | ("%s: Found a non-MSI controller: %s", __func__, | ||||
device_get_name(pic->pic_dev))); | device_get_name(pic->pic_dev))); | ||||
isrc = isrc_lookup(irq); | isrc = intr_map_get_isrc(irq); | ||||
if (isrc == NULL) | if (isrc == NULL) { | ||||
intr_unmap_irq(irq); | |||||
return (EINVAL); | return (EINVAL); | ||||
} | |||||
err = MSI_RELEASE_MSIX(pic->pic_dev, child, isrc); | err = MSI_RELEASE_MSIX(pic->pic_dev, child, isrc); | ||||
intr_unmap_irq(irq); | |||||
return (err); | return (err); | ||||
} | } | ||||
int | int | ||||
intr_map_msi(device_t pci, device_t child, intptr_t xref, int irq, | intr_map_msi(device_t pci, device_t child, intptr_t xref, int irq, | ||||
uint64_t *addr, uint32_t *data) | uint64_t *addr, uint32_t *data) | ||||
{ | { | ||||
struct intr_irqsrc *isrc; | struct intr_irqsrc *isrc; | ||||
struct intr_pic *pic; | struct intr_pic *pic; | ||||
int err; | int err; | ||||
pic = pic_lookup(NULL, xref); | pic = pic_lookup(NULL, xref); | ||||
if (pic == NULL) | if (pic == NULL) | ||||
return (ESRCH); | return (ESRCH); | ||||
KASSERT((pic->pic_flags & FLAG_MSI) != 0, | KASSERT((pic->pic_flags & FLAG_MSI) != 0, | ||||
("%s: Found a non-MSI controller: %s", __func__, | ("%s: Found a non-MSI controller: %s", __func__, | ||||
device_get_name(pic->pic_dev))); | device_get_name(pic->pic_dev))); | ||||
isrc = isrc_lookup(irq); | isrc = intr_map_get_isrc(irq); | ||||
if (isrc == NULL) | if (isrc == NULL) | ||||
return (EINVAL); | return (EINVAL); | ||||
err = MSI_MAP_MSI(pic->pic_dev, child, isrc, addr, data); | err = MSI_MAP_MSI(pic->pic_dev, child, isrc, addr, data); | ||||
return (err); | return (err); | ||||
} | } | ||||
Show All 38 Lines | for (irqsum = 0, i = 0; i < NIRQ; i++) { | ||||
db_printf("irq%-3u <%s>: cpu %02lx%s cnt %lu\n", i, | db_printf("irq%-3u <%s>: cpu %02lx%s cnt %lu\n", i, | ||||
isrc->isrc_name, isrc->isrc_cpu.__bits[0], | isrc->isrc_name, isrc->isrc_cpu.__bits[0], | ||||
isrc->isrc_flags & INTR_ISRCF_BOUND ? " (bound)" : "", num); | isrc->isrc_flags & INTR_ISRCF_BOUND ? " (bound)" : "", num); | ||||
irqsum += num; | irqsum += num; | ||||
} | } | ||||
db_printf("irq total %u\n", irqsum); | db_printf("irq total %u\n", irqsum); | ||||
} | } | ||||
#endif | #endif | ||||
/* | |||||
* Interrupt mapping table functions. | |||||
* | |||||
* Please, keep this part separately, it can be transformed to | |||||
* extension of standard resources. | |||||
*/ | |||||
struct intr_map_entry | |||||
{ | |||||
device_t dev; | |||||
intptr_t xref; | |||||
struct intr_map_data *map_data; | |||||
struct intr_irqsrc *isrc; | |||||
/* XXX TODO DISCONECTED PICs */ | |||||
/*int flags */ | |||||
}; | |||||
/* XXX Convert irq_map[] to dynamicaly expandable one. */ | |||||
static struct intr_map_entry *irq_map[2 * NIRQ]; | |||||
static int irq_map_count = nitems(irq_map); | |||||
static int irq_map_first_free_idx; | |||||
static struct mtx irq_map_lock; | |||||
static struct intr_irqsrc * | |||||
intr_map_get_isrc(u_int res_id) | |||||
{ | |||||
struct intr_irqsrc *isrc; | |||||
mtx_lock(&irq_map_lock); | |||||
if ((res_id >= irq_map_count) || (irq_map[res_id] == NULL)) { | |||||
mtx_unlock(&irq_map_lock); | |||||
return (NULL); | |||||
} | |||||
isrc = irq_map[res_id]->isrc; | |||||
mtx_unlock(&irq_map_lock); | |||||
return (isrc); | |||||
} | |||||
static void | |||||
intr_map_set_isrc(u_int res_id, struct intr_irqsrc *isrc) | |||||
{ | |||||
mtx_lock(&irq_map_lock); | |||||
if ((res_id >= irq_map_count) || (irq_map[res_id] == NULL)) { | |||||
mtx_unlock(&irq_map_lock); | |||||
return; | |||||
} | |||||
irq_map[res_id]->isrc = isrc; | |||||
mtx_unlock(&irq_map_lock); | |||||
} | |||||
/* | |||||
* Get a copy of intr_map_entry data | |||||
*/ | |||||
static void | |||||
intr_map_copy_map_data(u_int res_id, device_t *map_dev, intptr_t *map_xref, | |||||
struct intr_map_data **data) | |||||
{ | |||||
size_t len; | |||||
len = 0; | |||||
mtx_lock(&irq_map_lock); | |||||
if (res_id >= irq_map_count || irq_map[res_id] == NULL) | |||||
panic("Attempt to copy invalid resource id: %u\n", res_id); | |||||
if (irq_map[res_id]->map_data != NULL) | |||||
len = irq_map[res_id]->map_data->len; | |||||
mtx_unlock(&irq_map_lock); | |||||
if (len == 0) | |||||
*data = NULL; | |||||
else | |||||
*data = malloc(len, M_INTRNG, M_WAITOK | M_ZERO); | |||||
mtx_lock(&irq_map_lock); | |||||
if (irq_map[res_id] == NULL) | |||||
panic("Attempt to copy invalid resource id: %u\n", res_id); | |||||
if (len != 0) { | |||||
if (len != irq_map[res_id]->map_data->len) | |||||
panic("Resource id: %u has changed.\n", res_id); | |||||
memcpy(*data, irq_map[res_id]->map_data, len); | |||||
} | |||||
*map_dev = irq_map[res_id]->dev; | |||||
*map_xref = irq_map[res_id]->xref; | |||||
mtx_unlock(&irq_map_lock); | |||||
} | |||||
/* | |||||
* Allocate and fill new entry in irq_map table. | |||||
*/ | |||||
u_int | |||||
intr_map_irq(device_t dev, intptr_t xref, struct intr_map_data *data) | |||||
{ | |||||
u_int i; | |||||
struct intr_map_entry *entry; | |||||
/* Prepare new entry first. */ | |||||
entry = malloc(sizeof(*entry), M_INTRNG, M_WAITOK | M_ZERO); | |||||
entry->dev = dev; | |||||
entry->xref = xref; | |||||
entry->map_data = data; | |||||
entry->isrc = NULL; | |||||
mtx_lock(&irq_map_lock); | |||||
for (i = irq_map_first_free_idx; i < irq_map_count; i++) { | |||||
if (irq_map[i] == NULL) { | |||||
irq_map[i] = entry; | |||||
irq_map_first_free_idx = i + 1; | |||||
mtx_unlock(&irq_map_lock); | |||||
return (i); | |||||
} | |||||
} | |||||
mtx_unlock(&irq_map_lock); | |||||
/* XXX Expand irq_map table */ | |||||
panic("IRQ mapping table is full."); | |||||
} | |||||
/* | |||||
* Remove and free mapping entry. | |||||
*/ | |||||
void | |||||
intr_unmap_irq(u_int res_id) | |||||
{ | |||||
struct intr_map_entry *entry; | |||||
mtx_lock(&irq_map_lock); | |||||
if ((res_id >= irq_map_count) || (irq_map[res_id] == NULL)) | |||||
panic("Attempt to unmap invalid resource id: %u\n", res_id); | |||||
entry = irq_map[res_id]; | |||||
irq_map[res_id] = NULL; | |||||
irq_map_first_free_idx = res_id; | |||||
mtx_unlock(&irq_map_lock); | |||||
intr_free_intr_map_data(entry->map_data); | |||||
free(entry, M_INTRNG); | |||||
} | |||||
/* | |||||
* Clone mapping entry. | |||||
*/ | |||||
u_int | |||||
intr_map_clone_irq(u_int old_res_id) | |||||
{ | |||||
device_t map_dev; | |||||
intptr_t map_xref; | |||||
struct intr_map_data *data; | |||||
intr_map_copy_map_data(old_res_id, &map_dev, &map_xref, &data); | |||||
return (intr_map_irq(map_dev, map_xref, data)); | |||||
} | |||||
static void | |||||
intr_map_init(void *dummy __unused) | |||||
{ | |||||
mtx_init(&irq_map_lock, "intr map table", NULL, MTX_DEF); | |||||
} | |||||
SYSINIT(intr_map_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_map_init, NULL); |