#include <linux/idr.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
+#include <linux/irqdesc.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/lockdep.h>
 }
 EXPORT_SYMBOL_GPL(gpiochip_line_is_valid);
 
+static void gpiod_free_irqs(struct gpio_desc *desc)
+{
+       int irq = gpiod_to_irq(desc);
+       struct irq_desc *irqd = irq_to_desc(irq);
+       void *cookie;
+
+       for (;;) {
+               /*
+                * Make sure the action doesn't go away while we're
+                * dereferencing it. Retrieve and store the cookie value.
+                * If the irq is freed after we release the lock, that's
+                * alright - the underlying maple tree lookup will return NULL
+                * and nothing will happen in free_irq().
+                */
+               scoped_guard(mutex, &irqd->request_mutex) {
+                       if (!irq_desc_has_action(irqd))
+                               return;
+
+                       cookie = irqd->action->dev_id;
+               }
+
+               free_irq(irq, cookie);
+       }
+}
+
+/*
+ * The chip is going away but there may be users who had requested interrupts
+ * on its GPIO lines who have no idea about its removal and have no way of
+ * being notified about it. We need to free any interrupts still in use here or
+ * we'll leak memory and resources (like procfs files).
+ */
+static void gpiochip_free_remaining_irqs(struct gpio_chip *gc)
+{
+       struct gpio_desc *desc;
+
+       for_each_gpio_desc_with_flag(gc, desc, FLAG_USED_AS_IRQ)
+               gpiod_free_irqs(desc);
+}
+
 static void gpiodev_release(struct device *dev)
 {
        struct gpio_device *gdev = to_gpio_device(dev);
        /* FIXME: should the legacy sysfs handling be moved to gpio_device? */
        gpiochip_sysfs_unregister(gdev);
        gpiochip_free_hogs(gc);
+       gpiochip_free_remaining_irqs(gc);
 
        scoped_guard(mutex, &gpio_devices_lock)
                list_del_rcu(&gdev->list);