mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
pinctrl: mediatek: free EINT resources on unbind
mtk_eint_do_init() creates an IRQ domain, populates it with a mapping for
every EINT line and installs a chained handler on the parent interrupt,
but none of these are ever released. This was harmless while the drivers
were built-in, but now that they can be built as modules and
unbound/rmmod'd it leaves behind a dangling IRQ domain, interrupt mappings
whose chip data points at freed memory, and a chained handler that keeps
firing into that freed data.
The plain allocations in mtk_eint_do_init() already use the device-managed
devm_*() helpers, so tear the remaining resources down the same way:
register a devm action that detaches the chained handler, waits for any
in-flight handler to finish, disposes of the per-line mappings and removes
the IRQ domain. This mirrors the device-managed lifecycle adopted for the
GPIO chip and keeps the whole EINT setup self-cleaning on unbind.
Fixes: e46df235b4 ("pinctrl: mediatek: refactor EINT related code for all MediaTek pinctrl can fit")
Signed-off-by: Justin Yeh <justin.yeh@mediatek.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Linus Walleij <linusw@kernel.org>
This commit is contained in:
parent
9c650317ba
commit
88292b7103
|
|
@ -12,8 +12,10 @@
|
|||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
|
|
@ -509,6 +511,27 @@ int mtk_eint_find_irq(struct mtk_eint *eint, unsigned long eint_n)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(mtk_eint_find_irq);
|
||||
|
||||
static void mtk_eint_teardown(void *data)
|
||||
{
|
||||
struct mtk_eint *eint = data;
|
||||
unsigned int i, virq;
|
||||
|
||||
/* Detach the demux handler so it can no longer reference freed data. */
|
||||
irq_set_chained_handler_and_data(eint->irq, NULL, NULL);
|
||||
|
||||
/* Wait for any in-flight handler to finish before tearing down. */
|
||||
synchronize_irq(eint->irq);
|
||||
|
||||
/* Dispose of all child mappings before the domain is removed. */
|
||||
for (i = 0; i < eint->hw->ap_num; i++) {
|
||||
virq = irq_find_mapping(eint->domain, i);
|
||||
if (virq)
|
||||
irq_dispose_mapping(virq);
|
||||
}
|
||||
|
||||
irq_domain_remove(eint->domain);
|
||||
}
|
||||
|
||||
int mtk_eint_do_init(struct mtk_eint *eint, struct mtk_eint_pin *eint_pin)
|
||||
{
|
||||
unsigned int size, i, port, virq, inst = 0;
|
||||
|
|
@ -601,7 +624,7 @@ int mtk_eint_do_init(struct mtk_eint *eint, struct mtk_eint_pin *eint_pin)
|
|||
irq_set_chained_handler_and_data(eint->irq, mtk_eint_irq_handler,
|
||||
eint);
|
||||
|
||||
return 0;
|
||||
return devm_add_action_or_reset(eint->dev, mtk_eint_teardown, eint);
|
||||
|
||||
err_eint:
|
||||
for (i = 0; i < eint->nbase; i++) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user