diff --git a/drivers/staging/gasket/gasket_interrupt.c b/drivers/staging/gasket/gasket_interrupt.c index 27fde991edc6..3079b59b122b 100644 --- a/drivers/staging/gasket/gasket_interrupt.c +++ b/drivers/staging/gasket/gasket_interrupt.c @@ -70,32 +70,259 @@ struct gasket_interrupt_data { int irq; }; -/* Function definitions. */ -static ssize_t interrupt_sysfs_show( - struct device *device, struct device_attribute *attr, char *buf); - -static irqreturn_t gasket_msix_interrupt_handler(int irq, void *dev_id); - /* Structures to display interrupt counts in sysfs. */ enum interrupt_sysfs_attribute_type { ATTR_INTERRUPT_COUNTS, }; +/* Set up device registers for interrupt handling. */ +static void gasket_interrupt_setup(struct gasket_dev *gasket_dev) +{ + int i; + int pack_shift; + ulong mask; + ulong value; + struct gasket_interrupt_data *interrupt_data = + gasket_dev->interrupt_data; + + if (!interrupt_data) { + dev_dbg(gasket_dev->dev, "Interrupt data is not initialized\n"); + return; + } + + dev_dbg(gasket_dev->dev, "Running interrupt setup\n"); + + if (interrupt_data->type == PLATFORM_WIRE || + interrupt_data->type == PCI_MSI) { + /* Nothing needs to be done for platform or PCI devices. */ + return; + } + + if (interrupt_data->type != PCI_MSIX) { + dev_dbg(gasket_dev->dev, + "Cannot handle unsupported interrupt type %d\n", + interrupt_data->type); + return; + } + + /* Setup the MSIX table. */ + + for (i = 0; i < interrupt_data->num_interrupts; i++) { + /* + * If the interrupt is not packed, we can write the index into + * the register directly. If not, we need to deal with a read- + * modify-write and shift based on the packing index. + */ + dev_dbg(gasket_dev->dev, + "Setting up interrupt index %d with index 0x%llx and " + "packing %d\n", + interrupt_data->interrupts[i].index, + interrupt_data->interrupts[i].reg, + interrupt_data->interrupts[i].packing); + if (interrupt_data->interrupts[i].packing == UNPACKED) { + value = interrupt_data->interrupts[i].index; + } else { + switch (interrupt_data->interrupts[i].packing) { + case PACK_0: + pack_shift = 0; + break; + case PACK_1: + pack_shift = interrupt_data->pack_width; + break; + case PACK_2: + pack_shift = 2 * interrupt_data->pack_width; + break; + case PACK_3: + pack_shift = 3 * interrupt_data->pack_width; + break; + default: + dev_dbg(gasket_dev->dev, + "Found interrupt description with " + "unknown enum %d\n", + interrupt_data->interrupts[i].packing); + return; + } + + mask = ~(0xFFFF << pack_shift); + value = gasket_dev_read_64( + gasket_dev, + interrupt_data->interrupt_bar_index, + interrupt_data->interrupts[i].reg) & + mask; + value |= interrupt_data->interrupts[i].index + << pack_shift; + } + gasket_dev_write_64(gasket_dev, value, + interrupt_data->interrupt_bar_index, + interrupt_data->interrupts[i].reg); + } +} + +static irqreturn_t gasket_msix_interrupt_handler(int irq, void *dev_id) +{ + struct eventfd_ctx *ctx; + struct gasket_interrupt_data *interrupt_data = dev_id; + int interrupt = -1; + int i; + + /* If this linear lookup is a problem, we can maintain a map/hash. */ + for (i = 0; i < interrupt_data->num_interrupts; i++) { + if (interrupt_data->msix_entries[i].vector == irq) { + interrupt = interrupt_data->msix_entries[i].entry; + break; + } + } + if (interrupt == -1) { + pr_err("Received unknown irq %d\n", irq); + return IRQ_HANDLED; + } + trace_gasket_interrupt_event(interrupt_data->name, interrupt); + + ctx = interrupt_data->eventfd_ctxs[interrupt]; + if (ctx) + eventfd_signal(ctx, 1); + + ++(interrupt_data->interrupt_counts[interrupt]); + + return IRQ_HANDLED; +} + +static int gasket_interrupt_msix_init( + struct gasket_interrupt_data *interrupt_data) +{ + int ret = 1; + int i; + + for (i = 0; i < interrupt_data->num_interrupts; i++) { + interrupt_data->msix_entries[i].entry = i; + interrupt_data->msix_entries[i].vector = 0; + interrupt_data->eventfd_ctxs[i] = NULL; + } + + /* Retry MSIX_RETRY_COUNT times if not enough IRQs are available. */ + for (i = 0; i < MSIX_RETRY_COUNT && ret > 0; i++) + ret = pci_enable_msix_exact(interrupt_data->pci_dev, + interrupt_data->msix_entries, + interrupt_data->num_interrupts); + + if (ret) + return ret > 0 ? -EBUSY : ret; + interrupt_data->msix_configured = 1; + + for (i = 0; i < interrupt_data->num_interrupts; i++) { + ret = request_irq( + interrupt_data->msix_entries[i].vector, + gasket_msix_interrupt_handler, 0, interrupt_data->name, + interrupt_data); + + if (ret) { + dev_err(&interrupt_data->pci_dev->dev, + "Cannot get IRQ for interrupt %d, vector %d; " + "%d\n", + i, interrupt_data->msix_entries[i].vector, ret); + return ret; + } + + interrupt_data->num_configured++; + } + + return 0; +} + +/* + * On QCM DragonBoard, we exit gasket_interrupt_msix_init() and kernel interrupt + * setup code with MSIX vectors masked. This is wrong because nothing else in + * the driver will normally touch the MSIX vectors. + * + * As a temporary hack, force unmasking there. + * + * TODO: Figure out why QCM kernel doesn't unmask the MSIX vectors, after + * gasket_interrupt_msix_init(), and remove this code. + */ +static void force_msix_interrupt_unmasking(struct gasket_dev *gasket_dev) +{ + int i; +#define MSIX_VECTOR_SIZE 16 +#define MSIX_MASK_BIT_OFFSET 12 +#define APEX_BAR2_REG_KERNEL_HIB_MSIX_TABLE 0x46800 + for (i = 0; i < gasket_dev->interrupt_data->num_configured; i++) { + /* Check if the MSIX vector is unmasked */ + ulong location = APEX_BAR2_REG_KERNEL_HIB_MSIX_TABLE + + MSIX_MASK_BIT_OFFSET + i * MSIX_VECTOR_SIZE; + u32 mask = + gasket_dev_read_32( + gasket_dev, + gasket_dev->interrupt_data->interrupt_bar_index, + location); + if (!(mask & 1)) + continue; + /* Unmask the msix vector (clear 32 bits) */ + gasket_dev_write_32( + gasket_dev, 0, + gasket_dev->interrupt_data->interrupt_bar_index, + location); + } +#undef MSIX_VECTOR_SIZE +#undef MSIX_MASK_BIT_OFFSET +#undef APEX_BAR2_REG_KERNEL_HIB_MSIX_TABLE +} + +static ssize_t interrupt_sysfs_show( + struct device *device, struct device_attribute *attr, char *buf) +{ + int i, ret; + ssize_t written = 0, total_written = 0; + struct gasket_interrupt_data *interrupt_data; + struct gasket_dev *gasket_dev; + struct gasket_sysfs_attribute *gasket_attr; + enum interrupt_sysfs_attribute_type sysfs_type; + + gasket_dev = gasket_sysfs_get_device_data(device); + if (!gasket_dev) { + dev_dbg(device, "No sysfs mapping found for device\n"); + return 0; + } + + gasket_attr = gasket_sysfs_get_attr(device, attr); + if (!gasket_attr) { + dev_dbg(device, "No sysfs attr data found for device\n"); + gasket_sysfs_put_device_data(device, gasket_dev); + return 0; + } + + sysfs_type = (enum interrupt_sysfs_attribute_type) + gasket_attr->data.attr_type; + interrupt_data = gasket_dev->interrupt_data; + switch (sysfs_type) { + case ATTR_INTERRUPT_COUNTS: + for (i = 0; i < interrupt_data->num_interrupts; ++i) { + written = + scnprintf(buf, PAGE_SIZE - total_written, + "0x%02x: %ld\n", i, + interrupt_data->interrupt_counts[i]); + total_written += written; + buf += written; + } + ret = total_written; + break; + default: + dev_dbg(gasket_dev->dev, "Unknown attribute: %s\n", + attr->attr.name); + ret = 0; + break; + } + + gasket_sysfs_put_attr(device, gasket_attr); + gasket_sysfs_put_device_data(device, gasket_dev); + return ret; +} + static struct gasket_sysfs_attribute interrupt_sysfs_attrs[] = { GASKET_SYSFS_RO( interrupt_counts, interrupt_sysfs_show, ATTR_INTERRUPT_COUNTS), GASKET_END_OF_ATTR_ARRAY, }; -static void gasket_interrupt_setup(struct gasket_dev *gasket_dev); - -/* MSIX init and cleanup. */ -static int gasket_interrupt_msix_init( - struct gasket_interrupt_data *interrupt_data); -static void gasket_interrupt_msix_cleanup( - struct gasket_interrupt_data *interrupt_data); -static void force_msix_interrupt_unmasking(struct gasket_dev *gasket_dev); - int gasket_interrupt_init( struct gasket_dev *gasket_dev, const char *name, int type, const struct gasket_interrupt_desc *interrupts, @@ -181,48 +408,6 @@ int gasket_interrupt_init( return 0; } -static int gasket_interrupt_msix_init( - struct gasket_interrupt_data *interrupt_data) -{ - int ret = 1; - int i; - - for (i = 0; i < interrupt_data->num_interrupts; i++) { - interrupt_data->msix_entries[i].entry = i; - interrupt_data->msix_entries[i].vector = 0; - interrupt_data->eventfd_ctxs[i] = NULL; - } - - /* Retry MSIX_RETRY_COUNT times if not enough IRQs are available. */ - for (i = 0; i < MSIX_RETRY_COUNT && ret > 0; i++) - ret = pci_enable_msix_exact(interrupt_data->pci_dev, - interrupt_data->msix_entries, - interrupt_data->num_interrupts); - - if (ret) - return ret > 0 ? -EBUSY : ret; - interrupt_data->msix_configured = 1; - - for (i = 0; i < interrupt_data->num_interrupts; i++) { - ret = request_irq( - interrupt_data->msix_entries[i].vector, - gasket_msix_interrupt_handler, 0, interrupt_data->name, - interrupt_data); - - if (ret) { - dev_err(&interrupt_data->pci_dev->dev, - "Cannot get IRQ for interrupt %d, vector %d; " - "%d\n", - i, interrupt_data->msix_entries[i].vector, ret); - return ret; - } - - interrupt_data->num_configured++; - } - - return 0; -} - static void gasket_interrupt_msix_cleanup( struct gasket_interrupt_data *interrupt_data) { @@ -238,44 +423,6 @@ static void gasket_interrupt_msix_cleanup( interrupt_data->msix_configured = 0; } -/* - * On QCM DragonBoard, we exit gasket_interrupt_msix_init() and kernel interrupt - * setup code with MSIX vectors masked. This is wrong because nothing else in - * the driver will normally touch the MSIX vectors. - * - * As a temporary hack, force unmasking there. - * - * TODO: Figure out why QCM kernel doesn't unmask the MSIX vectors, after - * gasket_interrupt_msix_init(), and remove this code. - */ -static void force_msix_interrupt_unmasking(struct gasket_dev *gasket_dev) -{ - int i; -#define MSIX_VECTOR_SIZE 16 -#define MSIX_MASK_BIT_OFFSET 12 -#define APEX_BAR2_REG_KERNEL_HIB_MSIX_TABLE 0x46800 - for (i = 0; i < gasket_dev->interrupt_data->num_configured; i++) { - /* Check if the MSIX vector is unmasked */ - ulong location = APEX_BAR2_REG_KERNEL_HIB_MSIX_TABLE + - MSIX_MASK_BIT_OFFSET + i * MSIX_VECTOR_SIZE; - u32 mask = - gasket_dev_read_32( - gasket_dev, - gasket_dev->interrupt_data->interrupt_bar_index, - location); - if (!(mask & 1)) - continue; - /* Unmask the msix vector (clear 32 bits) */ - gasket_dev_write_32( - gasket_dev, 0, - gasket_dev->interrupt_data->interrupt_bar_index, - location); - } -#undef MSIX_VECTOR_SIZE -#undef MSIX_MASK_BIT_OFFSET -#undef APEX_BAR2_REG_KERNEL_HIB_MSIX_TABLE -} - int gasket_interrupt_reinit(struct gasket_dev *gasket_dev) { int ret; @@ -327,89 +474,6 @@ int gasket_interrupt_reset_counts(struct gasket_dev *gasket_dev) return 0; } -/* Set up device registers for interrupt handling. */ -static void gasket_interrupt_setup(struct gasket_dev *gasket_dev) -{ - int i; - int pack_shift; - ulong mask; - ulong value; - struct gasket_interrupt_data *interrupt_data = - gasket_dev->interrupt_data; - - if (!interrupt_data) { - dev_dbg(gasket_dev->dev, "Interrupt data is not initialized\n"); - return; - } - - dev_dbg(gasket_dev->dev, "Running interrupt setup\n"); - - if (interrupt_data->type == PLATFORM_WIRE || - interrupt_data->type == PCI_MSI) { - /* Nothing needs to be done for platform or PCI devices. */ - return; - } - - if (interrupt_data->type != PCI_MSIX) { - dev_dbg(gasket_dev->dev, - "Cannot handle unsupported interrupt type %d\n", - interrupt_data->type); - return; - } - - /* Setup the MSIX table. */ - - for (i = 0; i < interrupt_data->num_interrupts; i++) { - /* - * If the interrupt is not packed, we can write the index into - * the register directly. If not, we need to deal with a read- - * modify-write and shift based on the packing index. - */ - dev_dbg(gasket_dev->dev, - "Setting up interrupt index %d with index 0x%llx and " - "packing %d\n", - interrupt_data->interrupts[i].index, - interrupt_data->interrupts[i].reg, - interrupt_data->interrupts[i].packing); - if (interrupt_data->interrupts[i].packing == UNPACKED) { - value = interrupt_data->interrupts[i].index; - } else { - switch (interrupt_data->interrupts[i].packing) { - case PACK_0: - pack_shift = 0; - break; - case PACK_1: - pack_shift = interrupt_data->pack_width; - break; - case PACK_2: - pack_shift = 2 * interrupt_data->pack_width; - break; - case PACK_3: - pack_shift = 3 * interrupt_data->pack_width; - break; - default: - dev_dbg(gasket_dev->dev, - "Found interrupt description with " - "unknown enum %d\n", - interrupt_data->interrupts[i].packing); - return; - } - - mask = ~(0xFFFF << pack_shift); - value = gasket_dev_read_64( - gasket_dev, - interrupt_data->interrupt_bar_index, - interrupt_data->interrupts[i].reg) & - mask; - value |= interrupt_data->interrupts[i].index - << pack_shift; - } - gasket_dev_write_64(gasket_dev, value, - interrupt_data->interrupt_bar_index, - interrupt_data->interrupts[i].reg); - } -} - /* See gasket_interrupt.h for description. */ void gasket_interrupt_cleanup(struct gasket_dev *gasket_dev) { @@ -489,82 +553,3 @@ int gasket_interrupt_clear_eventfd( interrupt_data->eventfd_ctxs[interrupt] = NULL; return 0; } - -static ssize_t interrupt_sysfs_show( - struct device *device, struct device_attribute *attr, char *buf) -{ - int i, ret; - ssize_t written = 0, total_written = 0; - struct gasket_interrupt_data *interrupt_data; - struct gasket_dev *gasket_dev; - struct gasket_sysfs_attribute *gasket_attr; - enum interrupt_sysfs_attribute_type sysfs_type; - - gasket_dev = gasket_sysfs_get_device_data(device); - if (!gasket_dev) { - dev_dbg(device, "No sysfs mapping found for device\n"); - return 0; - } - - gasket_attr = gasket_sysfs_get_attr(device, attr); - if (!gasket_attr) { - dev_dbg(device, "No sysfs attr data found for device\n"); - gasket_sysfs_put_device_data(device, gasket_dev); - return 0; - } - - sysfs_type = (enum interrupt_sysfs_attribute_type) - gasket_attr->data.attr_type; - interrupt_data = gasket_dev->interrupt_data; - switch (sysfs_type) { - case ATTR_INTERRUPT_COUNTS: - for (i = 0; i < interrupt_data->num_interrupts; ++i) { - written = - scnprintf(buf, PAGE_SIZE - total_written, - "0x%02x: %ld\n", i, - interrupt_data->interrupt_counts[i]); - total_written += written; - buf += written; - } - ret = total_written; - break; - default: - dev_dbg(gasket_dev->dev, "Unknown attribute: %s\n", - attr->attr.name); - ret = 0; - break; - } - - gasket_sysfs_put_attr(device, gasket_attr); - gasket_sysfs_put_device_data(device, gasket_dev); - return ret; -} - -static irqreturn_t gasket_msix_interrupt_handler(int irq, void *dev_id) -{ - struct eventfd_ctx *ctx; - struct gasket_interrupt_data *interrupt_data = dev_id; - int interrupt = -1; - int i; - - /* If this linear lookup is a problem, we can maintain a map/hash. */ - for (i = 0; i < interrupt_data->num_interrupts; i++) { - if (interrupt_data->msix_entries[i].vector == irq) { - interrupt = interrupt_data->msix_entries[i].entry; - break; - } - } - if (interrupt == -1) { - pr_err("Received unknown irq %d\n", irq); - return IRQ_HANDLED; - } - trace_gasket_interrupt_event(interrupt_data->name, interrupt); - - ctx = interrupt_data->eventfd_ctxs[interrupt]; - if (ctx) - eventfd_signal(ctx, 1); - - ++(interrupt_data->interrupt_counts[interrupt]); - - return IRQ_HANDLED; -}