Skip to content

Commit

Permalink
irqchip: xilinx: Add support to remove Xilinx INTC irqchip driver module
Browse files Browse the repository at this point in the history
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.

In the proposed implementation irqchip framework is updated to add a new
function platform_irqchip_remove through irqchip.h MODULE_DEVICE_TABLE
implementation.

The platform_irqchip_remove calls the corresponding interrupt controller
driver's remove routine. It is the responsibility of the interrupt
controller remove routine to ensure that all irq descriptors attached to
its irqdomain are freed up.
Changes in this patch is made through a new irqchip kconfig option with the
name IRQCHIP_XILINX_INTC_MODULE_SUPPORT_EXPERIMENTAL which is disabled
by default.

Signed-off-by: Anirudha Sarangi <anirudha.sarangi@xilinx.com>
State: pending
  • Loading branch information
Anirudha Sarangi authored and Michal Simek committed May 28, 2021
1 parent 566aba2 commit 88c9066
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 1 deletion.
65 changes: 65 additions & 0 deletions drivers/irqchip/irqchip.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@
#include <linux/irqchip.h>
#include <linux/platform_device.h>

#ifdef CONFIG_IRQCHIP_XILINX_INTC_MODULE_SUPPORT_EXPERIMENTAL
struct platform_irqchip_instance {
of_irq_init_cb_t irq_init_cb;
of_irq_remove_cb_t irq_remove_cb;
struct device_node *parent_node;
};
#endif

/*
* 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
Expand All @@ -32,6 +40,7 @@ void __init irqchip_init(void)
acpi_probe_device_table(irqchip);
}

#ifndef CONFIG_IRQCHIP_XILINX_INTC_MODULE_SUPPORT_EXPERIMENTAL
int platform_irqchip_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
Expand All @@ -58,3 +67,59 @@ int platform_irqchip_probe(struct platform_device *pdev)
return irq_init_cb(np, par_np);
}
EXPORT_SYMBOL_GPL(platform_irqchip_probe);
#else
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);

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;
irqchip->parent_node = par_np;

if (!irqchip->irq_init_cb)
return -EINVAL;

if (par_np == np)
par_np = NULL;

/*
* If there's a parent interrupt controller and none of the parent irq
* domains have been registered, that means the parent interrupt
* controller has not been initialized yet. it's not time for this
* interrupt controller to initialize. So, defer probe of this
* interrupt controller. The actual initialization callback of this
* interrupt controller can check for specific domains as necessary.
*/
if (par_np && !irq_find_matching_host(par_np, DOMAIN_BUS_ANY))
return -EPROBE_DEFER;

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 platform_irqchip_instance *irqchip = platform_get_drvdata(pdev);
struct device_node *par_np = irqchip->parent_node;

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);
#endif
15 changes: 15 additions & 0 deletions include/linux/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,21 @@ struct irq_domain_chip_generic {
struct irq_chip_generic *gc[];
};

#ifdef CONFIG_IRQCHIP_XILINX_INTC_MODULE_SUPPORT_EXPERIMENTAL
/**
* 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);
};
#endif

/* Generic chip callback functions */
void irq_gc_noop(struct irq_data *d);
void irq_gc_mask_disable_reg(struct irq_data *d);
Expand Down
21 changes: 21 additions & 0 deletions include/linux/irqchip.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,16 @@
#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)

extern int platform_irqchip_probe(struct platform_device *pdev);
#ifdef CONFIG_IRQCHIP_XILINX_INTC_MODULE_SUPPORT_EXPERIMENTAL
extern int platform_irqchip_remove(struct platform_device *pdev);
#endif

#define IRQCHIP_PLATFORM_DRIVER_BEGIN(drv_name) \
static const struct of_device_id drv_name##_irqchip_match_table[] = {

#define IRQCHIP_MATCH(compat, fn) { .compatible = compat, .data = fn },

#ifndef CONFIG_IRQCHIP_XILINX_INTC_MODULE_SUPPORT_EXPERIMENTAL
#define IRQCHIP_PLATFORM_DRIVER_END(drv_name) \
{}, \
}; \
Expand All @@ -49,6 +53,23 @@ static struct platform_driver drv_name##_driver = { \
}, \
}; \
builtin_platform_driver(drv_name##_driver)
#else
#define IRQCHIP_PLATFORM_DRIVER_END(drv_name) \
{}, \
}; \
MODULE_DEVICE_TABLE(of, drv_name##_irqchip_match_table); \
static struct platform_driver drv_name##_driver = { \
.probe = platform_irqchip_probe, \
.remove = platform_irqchip_remove, \
.driver = { \
.name = #drv_name, \
.owner = THIS_MODULE, \
.of_match_table = drv_name##_irqchip_match_table, \
.suppress_bind_attrs = true, \
}, \
}; \
builtin_platform_driver(drv_name##_driver)
#endif

/*
* This macro must be used by the different irqchip drivers to declare
Expand Down
4 changes: 3 additions & 1 deletion include/linux/of_irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
#include <linux/of.h>

typedef int (*of_irq_init_cb_t)(struct device_node *, struct device_node *);

#ifdef CONFIG_IRQCHIP_XILINX_INTC_MODULE_SUPPORT_EXPERIMENTAL
typedef int (*of_irq_remove_cb_t)(struct device_node *, struct device_node *);
#endif
/*
* Workarounds only applied to 32bit powermac machines
*/
Expand Down

0 comments on commit 88c9066

Please sign in to comment.