The existing irqchip implementation does not fully support use cases where an irqchip driver has to be used as a module. In particular there is no support to remove an irqchip driver module. The use cases where an irqchip driver has to be loaded and then removed as a module are really relevant in fpga world. A user can decide to have a irqchip as part of a removable partial fpga region. In such cases not only the corresponding irqchip driver has to be loaded as a module, but must also be removed when the removable partial region is removed. The existing implementation updates the existing framework to achieve the above said goal. Signed-off-by: Anirudha Sarangi <anirudha.sarangi@xxxxxxxxxx> --- drivers/irqchip/irqchip.c | 38 +++++++++++++++++++++++++++++++++++--- include/linux/irq.h | 13 +++++++++++++ include/linux/of_irq.h | 1 + 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/drivers/irqchip/irqchip.c b/drivers/irqchip/irqchip.c index 3570f0a588c4..8687131e7268 100644 --- a/drivers/irqchip/irqchip.c +++ b/drivers/irqchip/irqchip.c @@ -15,6 +15,11 @@ #include <linux/irqchip.h> #include <linux/platform_device.h> +struct platform_irqchip_instance { + of_irq_init_cb_t irq_init_cb; + of_irq_remove_cb_t irq_remove_cb; +}; + /* * This special of_device_id is the sentinel at the end of the * of_device_id[] array of all irqchips. It is automatically placed at @@ -34,11 +39,22 @@ void __init irqchip_init(void) int platform_irqchip_probe(struct platform_device *pdev) { + struct platform_irqchip_instance *irqchip; + const struct irqc_init_remove_funps *irqchip_funps; struct device_node *np = pdev->dev.of_node; struct device_node *par_np = of_irq_find_parent(np); - of_irq_init_cb_t irq_init_cb = of_device_get_match_data(&pdev->dev); - if (!irq_init_cb) + irqchip = devm_kzalloc(&pdev->dev, sizeof(*irqchip), GFP_KERNEL); + if (!irqchip) + return -ENOMEM; + + platform_set_drvdata(pdev, irqchip); + + irqchip_funps = of_device_get_match_data(&pdev->dev); + irqchip->irq_init_cb = irqchip_funps->irqchip_initp; + irqchip->irq_remove_cb = irqchip_funps->irqchip_removep; + + if (!irqchip->irq_init_cb) return -EINVAL; if (par_np == np) @@ -55,6 +71,22 @@ int platform_irqchip_probe(struct platform_device *pdev) if (par_np && !irq_find_matching_host(par_np, DOMAIN_BUS_ANY)) return -EPROBE_DEFER; - return irq_init_cb(np, par_np); + return irqchip->irq_init_cb(np, par_np); } EXPORT_SYMBOL_GPL(platform_irqchip_probe); + +int platform_irqchip_remove(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *par_np = of_irq_find_parent(np); + struct platform_irqchip_instance *irqchip = platform_get_drvdata(pdev); + + if (!irqchip->irq_remove_cb) + return -EINVAL; + + if (par_np == np) + par_np = NULL; + + return irqchip->irq_remove_cb(np, par_np); +} +EXPORT_SYMBOL_GPL(platform_irqchip_remove); diff --git a/include/linux/irq.h b/include/linux/irq.h index 252fab8074de..d46d28f56bc0 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -1098,6 +1098,19 @@ struct irq_domain_chip_generic { struct irq_chip_generic *gc[]; }; +/** + * struct irqc_init_remove_funps - Stores function pointers for irqc init + * and remove APIs. Used when the irqchip driver is to be used as a module. + * @irqchip_initp: Function pointer for init/entry point of a irqchip driver. + * @irqchip_removep:Function pointer for irqchip driver remove function. + */ +struct irqc_init_remove_funps { + int (*irqchip_initp)(struct device_node *irqc, + struct device_node *parent); + int (*irqchip_removep)(struct device_node *irqc, + struct device_node *parent); +}; + /* Generic chip callback functions */ void irq_gc_noop(struct irq_data *d); void irq_gc_mask_disable_reg(struct irq_data *d); diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h index aaf219bd0354..4210ac64956f 100644 --- a/include/linux/of_irq.h +++ b/include/linux/of_irq.h @@ -10,6 +10,7 @@ #include <linux/of.h> typedef int (*of_irq_init_cb_t)(struct device_node *, struct device_node *); +typedef int (*of_irq_remove_cb_t)(struct device_node *, struct device_node *); /* * Workarounds only applied to 32bit powermac machines -- 2.17.1