diff --git a/include/linux/irq.h b/include/linux/irq.h index a217e1029c1d..58264b236cbf 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -1117,6 +1117,27 @@ struct irq_domain_chip_generic { struct irq_chip_generic *gc[]; }; +/** + * struct irq_domain_chip_generic_info - Generic chip information structure + * @name: Name of the generic interrupt chip + * @handler: Interrupt handler used by the generic interrupt chip + * @irqs_per_chip: Number of interrupts each chip handles (max 32) + * @num_ct: Number of irq_chip_type instances associated with each + * chip + * @irq_flags_to_clear: IRQ_* bits to clear in the mapping function + * @irq_flags_to_set: IRQ_* bits to set in the mapping function + * @gc_flags: Generic chip specific setup flags + */ +struct irq_domain_chip_generic_info { + const char *name; + irq_flow_handler_t handler; + unsigned int irqs_per_chip; + unsigned int num_ct; + unsigned int irq_flags_to_clear; + unsigned int irq_flags_to_set; + enum irq_gc_flags gc_flags; +}; + /* Generic chip callback functions */ void irq_gc_noop(struct irq_data *d); void irq_gc_mask_disable_reg(struct irq_data *d); @@ -1153,6 +1174,10 @@ int devm_irq_setup_generic_chip(struct device *dev, struct irq_chip_generic *gc, struct irq_chip_generic *irq_get_domain_generic_chip(struct irq_domain *d, unsigned int hw_irq); +int irq_domain_alloc_generic_chips(struct irq_domain *d, + const struct irq_domain_chip_generic_info *info); +void irq_domain_remove_generic_chips(struct irq_domain *d); + int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip, int num_ct, const char *name, irq_flow_handler_t handler, diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c index d39a40bc542b..d9696f5dcc38 100644 --- a/kernel/irq/generic-chip.c +++ b/kernel/irq/generic-chip.c @@ -275,6 +275,92 @@ irq_gc_init_mask_cache(struct irq_chip_generic *gc, enum irq_gc_flags flags) } } +/** + * irq_domain_alloc_generic_chips - Allocate generic chips for an irq domain + * @d: irq domain for which to allocate chips + * @info: Generic chip information + * + * Return: 0 on success, negative error code on failure + */ +int irq_domain_alloc_generic_chips(struct irq_domain *d, + const struct irq_domain_chip_generic_info *info) +{ + struct irq_domain_chip_generic *dgc; + struct irq_chip_generic *gc; + unsigned long flags; + int numchips, i; + size_t dgc_sz; + size_t gc_sz; + size_t sz; + void *tmp; + + if (d->gc) + return -EBUSY; + + numchips = DIV_ROUND_UP(d->revmap_size, info->irqs_per_chip); + if (!numchips) + return -EINVAL; + + /* Allocate a pointer, generic chip and chiptypes for each chip */ + gc_sz = struct_size(gc, chip_types, info->num_ct); + dgc_sz = struct_size(dgc, gc, numchips); + sz = dgc_sz + numchips * gc_sz; + + tmp = dgc = kzalloc(sz, GFP_KERNEL); + if (!dgc) + return -ENOMEM; + dgc->irqs_per_chip = info->irqs_per_chip; + dgc->num_chips = numchips; + dgc->irq_flags_to_set = info->irq_flags_to_set; + dgc->irq_flags_to_clear = info->irq_flags_to_clear; + dgc->gc_flags = info->gc_flags; + d->gc = dgc; + + /* Calc pointer to the first generic chip */ + tmp += dgc_sz; + for (i = 0; i < numchips; i++) { + /* Store the pointer to the generic chip */ + dgc->gc[i] = gc = tmp; + irq_init_generic_chip(gc, info->name, info->num_ct, + i * dgc->irqs_per_chip, NULL, + info->handler); + + gc->domain = d; + if (dgc->gc_flags & IRQ_GC_BE_IO) { + gc->reg_readl = &irq_readl_be; + gc->reg_writel = &irq_writel_be; + } + + raw_spin_lock_irqsave(&gc_lock, flags); + list_add_tail(&gc->list, &gc_list); + raw_spin_unlock_irqrestore(&gc_lock, flags); + /* Calc pointer to the next generic chip */ + tmp += gc_sz; + } + return 0; +} +EXPORT_SYMBOL_GPL(irq_domain_alloc_generic_chips); + +/** + * irq_domain_remove_generic_chips - Remove generic chips from an irq domain + * @d: irq domain for which generic chips are to be removed + */ +void irq_domain_remove_generic_chips(struct irq_domain *d) +{ + struct irq_domain_chip_generic *dgc = d->gc; + unsigned int i; + + if (!dgc) + return; + + for (i = 0; i < dgc->num_chips; i++) + irq_remove_generic_chip(dgc->gc[i], ~0U, 0, 0); + + d->gc = NULL; + kfree(dgc); +} +EXPORT_SYMBOL_GPL(irq_domain_remove_generic_chips); + /** * __irq_alloc_domain_generic_chips - Allocate generic chips for an irq domain * @d: irq domain for which to allocate chips @@ -292,58 +378,17 @@ int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip, unsigned int clr, unsigned int set, enum irq_gc_flags gcflags) { - struct irq_domain_chip_generic *dgc; - struct irq_chip_generic *gc; - unsigned long flags; - int numchips, i; - size_t dgc_sz; - size_t gc_sz; - size_t sz; - void *tmp; + struct irq_domain_chip_generic_info info = { + .irqs_per_chip = irqs_per_chip, + .num_ct = num_ct, + .name = name, + .handler = handler, + .irq_flags_to_clear = clr, + .irq_flags_to_set = set, + .gc_flags = gcflags, + }; - if (d->gc) - return -EBUSY; - - numchips = DIV_ROUND_UP(d->revmap_size, irqs_per_chip); - if (!numchips) - return -EINVAL; - - /* Allocate a pointer, generic chip and chiptypes for each chip */ - gc_sz = struct_size(gc, chip_types, num_ct); - dgc_sz = struct_size(dgc, gc, numchips); - sz = dgc_sz + numchips * gc_sz; - - tmp = dgc = kzalloc(sz, GFP_KERNEL); - if (!dgc) - return -ENOMEM; - dgc->irqs_per_chip = irqs_per_chip; - dgc->num_chips = numchips; - dgc->irq_flags_to_set = set; - dgc->irq_flags_to_clear = clr; - dgc->gc_flags = gcflags; - d->gc = dgc; - - /* Calc pointer to the first generic chip */ - tmp += dgc_sz; - for (i = 0; i < numchips; i++) { - /* Store the pointer to the generic chip */ - dgc->gc[i] = gc = tmp; - irq_init_generic_chip(gc, name, num_ct, i * irqs_per_chip, - NULL, handler); - - gc->domain = d; - if (gcflags & IRQ_GC_BE_IO) { - gc->reg_readl = &irq_readl_be; - gc->reg_writel = &irq_writel_be; - } - - raw_spin_lock_irqsave(&gc_lock, flags); - list_add_tail(&gc->list, &gc_list); - raw_spin_unlock_irqrestore(&gc_lock, flags); - /* Calc pointer to the next generic chip */ - tmp += gc_sz; - } - return 0; + return irq_domain_alloc_generic_chips(d, &info); } EXPORT_SYMBOL_GPL(__irq_alloc_domain_generic_chips);