[PATCH 01/21] irq: add devres version of OF IRQ mapping routines

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




From: Nikita Yushchenko <nyushchenko@xxxxxxxxxxxxx>

Many drivers use devres to manage their resources, and at the same time
use irq_of_parse_and_map() / irq_dispose_mapping(). This creates problem
on driver unload paths and on error paths:
- it is invalid to call irq_dispose_mapping() while IRQ handler is still
  installed,
- devres moves removal of IRQ handler out of driver,
- without explicit devres support for IRQ mapping, irq_dispose_mapping()
  stays in driver and thus gets called while IRQ handler is still
  installed.

This patch adds devm_irq_create_of_mapping() and devm_irq_of_parse_and_map()
routines to be used by drivers for correct release of resources.

Signed-off-by: Nikita Yushchenko <nyushchenko@xxxxxxxxxxxxx>
---
 drivers/of/irq.c          |   24 +++++++++++++++++++++++
 include/linux/irqdomain.h |    3 +++
 include/linux/of_irq.h    |   12 ++++++++++++
 kernel/irq/irqdomain.c    |   47 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 86 insertions(+)

diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 5aeb894..30b5010 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -46,6 +46,30 @@ unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
 EXPORT_SYMBOL_GPL(irq_of_parse_and_map);
 
 /**
+ * devm_irq_of_parse_and_map - Parse and map an interrupt into linux virq space
+ * @dev: Device interrupt will be used for
+ * @dn: Device node of the device whose interrupt is to be mapped
+ * @index: Index of the interrupt to map
+ *
+ * This function does the same as irq_of_parse_and_map(), but ensures that
+ * irq_dispose_mapping() will be called automatically at driver detatch.
+ *
+ * If IRQ mapping created by this function needs to be removed manually,
+ * devm_irq_dispose_mapping() must be called instead of irq_dispose_mapping().
+ */
+int devm_irq_of_parse_and_map(struct device *dev, struct device_node *dn,
+		int index)
+{
+	struct of_phandle_args oirq;
+
+	if (of_irq_parse_one(dn, index, &oirq))
+		return 0;
+
+	return devm_irq_create_of_mapping(dev, &oirq);
+}
+EXPORT_SYMBOL_GPL(devm_irq_of_parse_and_map);
+
+/**
  * of_irq_find_parent - Given a device node, find its interrupt parent node
  * @child: pointer to device node
  *
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index c983ed1..44e6261 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -176,6 +176,7 @@ extern void irq_domain_associate_many(struct irq_domain *domain,
 extern unsigned int irq_create_mapping(struct irq_domain *host,
 				       irq_hw_number_t hwirq);
 extern void irq_dispose_mapping(unsigned int virq);
+extern void devm_irq_dispose_mapping(struct device *dev, unsigned int virq);
 
 /**
  * irq_linear_revmap() - Find a linux irq from a hw irq number.
@@ -220,6 +221,8 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr,
 
 #else /* CONFIG_IRQ_DOMAIN */
 static inline void irq_dispose_mapping(unsigned int virq) { }
+static inline void devm_irq_dispose_mapping(struct device *dev,
+		unsigned int virq) { }
 #endif /* !CONFIG_IRQ_DOMAIN */
 
 #endif /* _LINUX_IRQDOMAIN_H */
diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h
index 6404253..4ac7138 100644
--- a/include/linux/of_irq.h
+++ b/include/linux/of_irq.h
@@ -35,6 +35,8 @@ extern int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
 extern int of_irq_parse_one(struct device_node *device, int index,
 			  struct of_phandle_args *out_irq);
 extern unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data);
+extern int devm_irq_create_of_mapping(struct device *dev,
+		struct of_phandle_args *irq_data);
 extern int of_irq_to_resource(struct device_node *dev, int index,
 			      struct resource *r);
 extern int of_irq_to_resource_table(struct device_node *dev,
@@ -63,6 +65,9 @@ static inline int of_irq_get(struct device_node *dev, int index)
  * so declare it here regardless of the CONFIG_OF_IRQ setting.
  */
 extern unsigned int irq_of_parse_and_map(struct device_node *node, int index);
+extern int devm_irq_of_parse_and_map(struct device *dev,
+				     struct device_node *node,
+				     int index);
 extern struct device_node *of_irq_find_parent(struct device_node *child);
 
 #else /* !CONFIG_OF */
@@ -72,6 +77,13 @@ static inline unsigned int irq_of_parse_and_map(struct device_node *dev,
 	return 0;
 }
 
+static inline int devm_irq_of_parse_and_map(struct device *dev,
+					    struct device_node *node,
+					    int index)
+{
+	return 0;
+}
+
 static inline void *of_irq_find_parent(struct device_node *child)
 {
 	return NULL;
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index f140337..c8705de 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -16,6 +16,7 @@
 #include <linux/slab.h>
 #include <linux/smp.h>
 #include <linux/fs.h>
+#include <linux/device.h>
 
 static LIST_HEAD(irq_domain_list);
 static DEFINE_MUTEX(irq_domain_mutex);
@@ -502,6 +503,34 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
 }
 EXPORT_SYMBOL_GPL(irq_create_of_mapping);
 
+static void devm_release_irq_mapping(void *p)
+{
+	unsigned int virq = (unsigned int)((unsigned long)p);
+
+	if (virq)
+		irq_dispose_mapping(virq);
+}
+
+int devm_irq_create_of_mapping(struct device *dev,
+		struct of_phandle_args *irq_data)
+{
+	unsigned int virq;
+	int ret;
+
+	virq = irq_create_of_mapping(irq_data);
+	if (virq) {
+		ret = devm_add_action(dev, devm_release_irq_mapping,
+				(void *)((unsigned long)virq));
+		if (ret) {
+			irq_dispose_mapping(virq);
+			return ret;
+		}
+	}
+
+	return virq;
+}
+EXPORT_SYMBOL_GPL(devm_irq_create_of_mapping);
+
 /**
  * irq_dispose_mapping() - Unmap an interrupt
  * @virq: linux irq number of the interrupt to unmap
@@ -524,6 +553,24 @@ void irq_dispose_mapping(unsigned int virq)
 EXPORT_SYMBOL_GPL(irq_dispose_mapping);
 
 /**
+ * devm_irq_dispose_mapping() - Unmap an interrupt
+ * @dev: device irq was used for
+ * @virq: linux irq number of the interrupt to unmap
+ *
+ * This should be used instead of irq_dispose_mapping() if mapping was created
+ * with devm_irq_create_of_mapping()
+ */
+void devm_irq_dispose_mapping(struct device *dev, unsigned int virq)
+{
+	if (virq) {
+		devm_remove_action(dev, devm_release_irq_mapping,
+				(void *)((unsigned long)virq));
+		irq_dispose_mapping(virq);
+	}
+}
+EXPORT_SYMBOL_GPL(devm_irq_dispose_mapping);
+
+/**
  * irq_find_mapping() - Find a linux irq from an hw irq number.
  * @domain: domain owning this hardware interrupt
  * @hwirq: hardware irq number in that domain space
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]
  Powered by Linux