|
@@ -18,6 +18,7 @@
|
|
|
#include <linux/irq.h>
|
|
|
#include <linux/mfd/core.h>
|
|
|
#include <linux/interrupt.h>
|
|
|
+#include <linux/irqdomain.h>
|
|
|
|
|
|
#include <linux/mfd/wm831x/core.h>
|
|
|
#include <linux/mfd/wm831x/pdata.h>
|
|
@@ -328,7 +329,7 @@ static inline int irq_data_to_status_reg(struct wm831x_irq_data *irq_data)
|
|
|
static inline struct wm831x_irq_data *irq_to_wm831x_irq(struct wm831x *wm831x,
|
|
|
int irq)
|
|
|
{
|
|
|
- return &wm831x_irqs[irq - wm831x->irq_base];
|
|
|
+ return &wm831x_irqs[irq];
|
|
|
}
|
|
|
|
|
|
static void wm831x_irq_lock(struct irq_data *data)
|
|
@@ -374,7 +375,7 @@ static void wm831x_irq_enable(struct irq_data *data)
|
|
|
{
|
|
|
struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
|
|
|
struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x,
|
|
|
- data->irq);
|
|
|
+ data->hwirq);
|
|
|
|
|
|
wm831x->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
|
|
|
}
|
|
@@ -383,7 +384,7 @@ static void wm831x_irq_disable(struct irq_data *data)
|
|
|
{
|
|
|
struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
|
|
|
struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x,
|
|
|
- data->irq);
|
|
|
+ data->hwirq);
|
|
|
|
|
|
wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
|
|
|
}
|
|
@@ -393,7 +394,7 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
|
|
|
struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
|
|
|
int irq;
|
|
|
|
|
|
- irq = data->irq - wm831x->irq_base;
|
|
|
+ irq = data->hwirq;
|
|
|
|
|
|
if (irq < WM831X_IRQ_GPIO_1 || irq > WM831X_IRQ_GPIO_11) {
|
|
|
/* Ignore internal-only IRQs */
|
|
@@ -469,9 +470,11 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
|
|
|
* descriptors.
|
|
|
*/
|
|
|
if (primary & WM831X_TCHPD_INT)
|
|
|
- handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHPD);
|
|
|
+ handle_nested_irq(irq_find_mapping(wm831x->irq_domain,
|
|
|
+ WM831X_IRQ_TCHPD));
|
|
|
if (primary & WM831X_TCHDATA_INT)
|
|
|
- handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHDATA);
|
|
|
+ handle_nested_irq(irq_find_mapping(wm831x->irq_domain,
|
|
|
+ WM831X_IRQ_TCHDATA));
|
|
|
primary &= ~(WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT);
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(wm831x_irqs); i++) {
|
|
@@ -507,7 +510,8 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
|
|
|
}
|
|
|
|
|
|
if (*status & wm831x_irqs[i].mask)
|
|
|
- handle_nested_irq(wm831x->irq_base + i);
|
|
|
+ handle_nested_irq(irq_find_mapping(wm831x->irq_domain,
|
|
|
+ i));
|
|
|
|
|
|
/* Simulate an edge triggered IRQ by polling the input
|
|
|
* status. This is sucky but improves interoperability.
|
|
@@ -516,7 +520,8 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
|
|
|
wm831x->gpio_level[i - WM831X_IRQ_GPIO_1]) {
|
|
|
ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL);
|
|
|
while (ret & 1 << (i - WM831X_IRQ_GPIO_1)) {
|
|
|
- handle_nested_irq(wm831x->irq_base + i);
|
|
|
+ handle_nested_irq(irq_find_mapping(wm831x->irq_domain,
|
|
|
+ i));
|
|
|
ret = wm831x_reg_read(wm831x,
|
|
|
WM831X_GPIO_LEVEL);
|
|
|
}
|
|
@@ -527,10 +532,34 @@ out:
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
|
|
|
|
+static int wm831x_irq_map(struct irq_domain *h, unsigned int virq,
|
|
|
+ irq_hw_number_t hw)
|
|
|
+{
|
|
|
+ irq_set_chip_data(virq, h->host_data);
|
|
|
+ irq_set_chip_and_handler(virq, &wm831x_irq_chip, handle_edge_irq);
|
|
|
+ irq_set_nested_thread(virq, 1);
|
|
|
+
|
|
|
+ /* ARM needs us to explicitly flag the IRQ as valid
|
|
|
+ * and will set them noprobe when we do so. */
|
|
|
+#ifdef CONFIG_ARM
|
|
|
+ set_irq_flags(virq, IRQF_VALID);
|
|
|
+#else
|
|
|
+ irq_set_noprobe(virq);
|
|
|
+#endif
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static struct irq_domain_ops wm831x_irq_domain_ops = {
|
|
|
+ .map = wm831x_irq_map,
|
|
|
+ .xlate = irq_domain_xlate_twocell,
|
|
|
+};
|
|
|
+
|
|
|
int wm831x_irq_init(struct wm831x *wm831x, int irq)
|
|
|
{
|
|
|
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
|
|
|
- int i, cur_irq, ret;
|
|
|
+ struct irq_domain *domain;
|
|
|
+ int i, ret, irq_base;
|
|
|
|
|
|
mutex_init(&wm831x->irq_lock);
|
|
|
|
|
@@ -543,18 +572,33 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
|
|
|
}
|
|
|
|
|
|
/* Try to dynamically allocate IRQs if no base is specified */
|
|
|
- if (!pdata || !pdata->irq_base)
|
|
|
- wm831x->irq_base = -1;
|
|
|
+ if (pdata && pdata->irq_base) {
|
|
|
+ irq_base = irq_alloc_descs(pdata->irq_base, 0,
|
|
|
+ WM831X_NUM_IRQS, 0);
|
|
|
+ if (irq_base < 0) {
|
|
|
+ dev_warn(wm831x->dev, "Failed to allocate IRQs: %d\n",
|
|
|
+ irq_base);
|
|
|
+ irq_base = 0;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ irq_base = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (irq_base)
|
|
|
+ domain = irq_domain_add_legacy(wm831x->dev->of_node,
|
|
|
+ ARRAY_SIZE(wm831x_irqs),
|
|
|
+ irq_base, 0,
|
|
|
+ &wm831x_irq_domain_ops,
|
|
|
+ wm831x);
|
|
|
else
|
|
|
- wm831x->irq_base = pdata->irq_base;
|
|
|
+ domain = irq_domain_add_linear(wm831x->dev->of_node,
|
|
|
+ ARRAY_SIZE(wm831x_irqs),
|
|
|
+ &wm831x_irq_domain_ops,
|
|
|
+ wm831x);
|
|
|
|
|
|
- wm831x->irq_base = irq_alloc_descs(wm831x->irq_base, 0,
|
|
|
- WM831X_NUM_IRQS, 0);
|
|
|
- if (wm831x->irq_base < 0) {
|
|
|
- dev_warn(wm831x->dev, "Failed to allocate IRQs: %d\n",
|
|
|
- wm831x->irq_base);
|
|
|
- wm831x->irq_base = 0;
|
|
|
- return 0;
|
|
|
+ if (!domain) {
|
|
|
+ dev_warn(wm831x->dev, "Failed to allocate IRQ domain\n");
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
if (pdata && pdata->irq_cmos)
|
|
@@ -566,24 +610,7 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
|
|
|
WM831X_IRQ_OD, i);
|
|
|
|
|
|
wm831x->irq = irq;
|
|
|
-
|
|
|
- /* Register them with genirq */
|
|
|
- for (cur_irq = wm831x->irq_base;
|
|
|
- cur_irq < ARRAY_SIZE(wm831x_irqs) + wm831x->irq_base;
|
|
|
- cur_irq++) {
|
|
|
- irq_set_chip_data(cur_irq, wm831x);
|
|
|
- irq_set_chip_and_handler(cur_irq, &wm831x_irq_chip,
|
|
|
- handle_edge_irq);
|
|
|
- irq_set_nested_thread(cur_irq, 1);
|
|
|
-
|
|
|
- /* ARM needs us to explicitly flag the IRQ as valid
|
|
|
- * and will set them noprobe when we do so. */
|
|
|
-#ifdef CONFIG_ARM
|
|
|
- set_irq_flags(cur_irq, IRQF_VALID);
|
|
|
-#else
|
|
|
- irq_set_noprobe(cur_irq);
|
|
|
-#endif
|
|
|
- }
|
|
|
+ wm831x->irq_domain = domain;
|
|
|
|
|
|
if (irq) {
|
|
|
/* Try to flag /IRQ as a wake source; there are a number of
|