12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2c9fbf7e0SMark Brown /*
3c9fbf7e0SMark Brown * wm8994-irq.c -- Interrupt controller support for Wolfson WM8994
4c9fbf7e0SMark Brown *
5c9fbf7e0SMark Brown * Copyright 2010 Wolfson Microelectronics PLC.
6c9fbf7e0SMark Brown *
7c9fbf7e0SMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
8c9fbf7e0SMark Brown */
9c9fbf7e0SMark Brown
10c9fbf7e0SMark Brown #include <linux/kernel.h>
11c9fbf7e0SMark Brown #include <linux/module.h>
127c884448SMark Brown #include <linux/gpio.h>
13c9fbf7e0SMark Brown #include <linux/i2c.h>
14c9fbf7e0SMark Brown #include <linux/irq.h>
15c9fbf7e0SMark Brown #include <linux/mfd/core.h>
16c9fbf7e0SMark Brown #include <linux/interrupt.h>
177c884448SMark Brown #include <linux/irqdomain.h>
188ab30691SMark Brown #include <linux/regmap.h>
19c9fbf7e0SMark Brown
20c9fbf7e0SMark Brown #include <linux/mfd/wm8994/core.h>
21b0ab907dSMark Brown #include <linux/mfd/wm8994/pdata.h>
22c9fbf7e0SMark Brown #include <linux/mfd/wm8994/registers.h>
23c9fbf7e0SMark Brown
24c9fbf7e0SMark Brown #include <linux/delay.h>
25c9fbf7e0SMark Brown
267ce7b26fSKrzysztof Kozlowski static const struct regmap_irq wm8994_irqs[] = {
27c9fbf7e0SMark Brown [WM8994_IRQ_TEMP_SHUT] = {
288ab30691SMark Brown .reg_offset = 1,
29c9fbf7e0SMark Brown .mask = WM8994_TEMP_SHUT_EINT,
30c9fbf7e0SMark Brown },
31c9fbf7e0SMark Brown [WM8994_IRQ_MIC1_DET] = {
328ab30691SMark Brown .reg_offset = 1,
33c9fbf7e0SMark Brown .mask = WM8994_MIC1_DET_EINT,
34c9fbf7e0SMark Brown },
35c9fbf7e0SMark Brown [WM8994_IRQ_MIC1_SHRT] = {
368ab30691SMark Brown .reg_offset = 1,
37c9fbf7e0SMark Brown .mask = WM8994_MIC1_SHRT_EINT,
38c9fbf7e0SMark Brown },
39c9fbf7e0SMark Brown [WM8994_IRQ_MIC2_DET] = {
408ab30691SMark Brown .reg_offset = 1,
41c9fbf7e0SMark Brown .mask = WM8994_MIC2_DET_EINT,
42c9fbf7e0SMark Brown },
43c9fbf7e0SMark Brown [WM8994_IRQ_MIC2_SHRT] = {
448ab30691SMark Brown .reg_offset = 1,
45c9fbf7e0SMark Brown .mask = WM8994_MIC2_SHRT_EINT,
46c9fbf7e0SMark Brown },
47c9fbf7e0SMark Brown [WM8994_IRQ_FLL1_LOCK] = {
488ab30691SMark Brown .reg_offset = 1,
49c9fbf7e0SMark Brown .mask = WM8994_FLL1_LOCK_EINT,
50c9fbf7e0SMark Brown },
51c9fbf7e0SMark Brown [WM8994_IRQ_FLL2_LOCK] = {
528ab30691SMark Brown .reg_offset = 1,
53c9fbf7e0SMark Brown .mask = WM8994_FLL2_LOCK_EINT,
54c9fbf7e0SMark Brown },
55c9fbf7e0SMark Brown [WM8994_IRQ_SRC1_LOCK] = {
568ab30691SMark Brown .reg_offset = 1,
57c9fbf7e0SMark Brown .mask = WM8994_SRC1_LOCK_EINT,
58c9fbf7e0SMark Brown },
59c9fbf7e0SMark Brown [WM8994_IRQ_SRC2_LOCK] = {
608ab30691SMark Brown .reg_offset = 1,
61c9fbf7e0SMark Brown .mask = WM8994_SRC2_LOCK_EINT,
62c9fbf7e0SMark Brown },
63c9fbf7e0SMark Brown [WM8994_IRQ_AIF1DRC1_SIG_DET] = {
648ab30691SMark Brown .reg_offset = 1,
65c9fbf7e0SMark Brown .mask = WM8994_AIF1DRC1_SIG_DET,
66c9fbf7e0SMark Brown },
67c9fbf7e0SMark Brown [WM8994_IRQ_AIF1DRC2_SIG_DET] = {
688ab30691SMark Brown .reg_offset = 1,
69c9fbf7e0SMark Brown .mask = WM8994_AIF1DRC2_SIG_DET_EINT,
70c9fbf7e0SMark Brown },
71c9fbf7e0SMark Brown [WM8994_IRQ_AIF2DRC_SIG_DET] = {
728ab30691SMark Brown .reg_offset = 1,
73c9fbf7e0SMark Brown .mask = WM8994_AIF2DRC_SIG_DET_EINT,
74c9fbf7e0SMark Brown },
75c9fbf7e0SMark Brown [WM8994_IRQ_FIFOS_ERR] = {
768ab30691SMark Brown .reg_offset = 1,
77c9fbf7e0SMark Brown .mask = WM8994_FIFOS_ERR_EINT,
78c9fbf7e0SMark Brown },
79c9fbf7e0SMark Brown [WM8994_IRQ_WSEQ_DONE] = {
808ab30691SMark Brown .reg_offset = 1,
81c9fbf7e0SMark Brown .mask = WM8994_WSEQ_DONE_EINT,
82c9fbf7e0SMark Brown },
83c9fbf7e0SMark Brown [WM8994_IRQ_DCS_DONE] = {
848ab30691SMark Brown .reg_offset = 1,
85c9fbf7e0SMark Brown .mask = WM8994_DCS_DONE_EINT,
86c9fbf7e0SMark Brown },
87c9fbf7e0SMark Brown [WM8994_IRQ_TEMP_WARN] = {
888ab30691SMark Brown .reg_offset = 1,
89c9fbf7e0SMark Brown .mask = WM8994_TEMP_WARN_EINT,
90c9fbf7e0SMark Brown },
91c9fbf7e0SMark Brown [WM8994_IRQ_GPIO(1)] = {
92c9fbf7e0SMark Brown .mask = WM8994_GP1_EINT,
93c9fbf7e0SMark Brown },
94c9fbf7e0SMark Brown [WM8994_IRQ_GPIO(2)] = {
95c9fbf7e0SMark Brown .mask = WM8994_GP2_EINT,
96c9fbf7e0SMark Brown },
97c9fbf7e0SMark Brown [WM8994_IRQ_GPIO(3)] = {
98c9fbf7e0SMark Brown .mask = WM8994_GP3_EINT,
99c9fbf7e0SMark Brown },
100c9fbf7e0SMark Brown [WM8994_IRQ_GPIO(4)] = {
101c9fbf7e0SMark Brown .mask = WM8994_GP4_EINT,
102c9fbf7e0SMark Brown },
103c9fbf7e0SMark Brown [WM8994_IRQ_GPIO(5)] = {
104c9fbf7e0SMark Brown .mask = WM8994_GP5_EINT,
105c9fbf7e0SMark Brown },
106c9fbf7e0SMark Brown [WM8994_IRQ_GPIO(6)] = {
107c9fbf7e0SMark Brown .mask = WM8994_GP6_EINT,
108c9fbf7e0SMark Brown },
109c9fbf7e0SMark Brown [WM8994_IRQ_GPIO(7)] = {
110c9fbf7e0SMark Brown .mask = WM8994_GP7_EINT,
111c9fbf7e0SMark Brown },
112c9fbf7e0SMark Brown [WM8994_IRQ_GPIO(8)] = {
113c9fbf7e0SMark Brown .mask = WM8994_GP8_EINT,
114c9fbf7e0SMark Brown },
115c9fbf7e0SMark Brown [WM8994_IRQ_GPIO(9)] = {
116c9fbf7e0SMark Brown .mask = WM8994_GP8_EINT,
117c9fbf7e0SMark Brown },
118c9fbf7e0SMark Brown [WM8994_IRQ_GPIO(10)] = {
119c9fbf7e0SMark Brown .mask = WM8994_GP10_EINT,
120c9fbf7e0SMark Brown },
121c9fbf7e0SMark Brown [WM8994_IRQ_GPIO(11)] = {
122c9fbf7e0SMark Brown .mask = WM8994_GP11_EINT,
123c9fbf7e0SMark Brown },
124c9fbf7e0SMark Brown };
125c9fbf7e0SMark Brown
1267ce7b26fSKrzysztof Kozlowski static const struct regmap_irq_chip wm8994_irq_chip = {
127c9fbf7e0SMark Brown .name = "wm8994",
1288ab30691SMark Brown .irqs = wm8994_irqs,
1298ab30691SMark Brown .num_irqs = ARRAY_SIZE(wm8994_irqs),
1308ab30691SMark Brown
1318ab30691SMark Brown .num_regs = 2,
1328ab30691SMark Brown .status_base = WM8994_INTERRUPT_STATUS_1,
1338ab30691SMark Brown .mask_base = WM8994_INTERRUPT_STATUS_1_MASK,
1348ab30691SMark Brown .ack_base = WM8994_INTERRUPT_STATUS_1,
1357a976379SMark Brown .runtime_pm = true,
136c9fbf7e0SMark Brown };
137c9fbf7e0SMark Brown
wm8994_edge_irq_enable(struct irq_data * data)1387c884448SMark Brown static void wm8994_edge_irq_enable(struct irq_data *data)
1397c884448SMark Brown {
1407c884448SMark Brown }
1417c884448SMark Brown
wm8994_edge_irq_disable(struct irq_data * data)1427c884448SMark Brown static void wm8994_edge_irq_disable(struct irq_data *data)
1437c884448SMark Brown {
1447c884448SMark Brown }
1457c884448SMark Brown
1467c884448SMark Brown static struct irq_chip wm8994_edge_irq_chip = {
1477c884448SMark Brown .name = "wm8994_edge",
1487c884448SMark Brown .irq_disable = wm8994_edge_irq_disable,
1497c884448SMark Brown .irq_enable = wm8994_edge_irq_enable,
1507c884448SMark Brown };
1517c884448SMark Brown
wm8994_edge_irq(int irq,void * data)1527c884448SMark Brown static irqreturn_t wm8994_edge_irq(int irq, void *data)
1537c884448SMark Brown {
1547c884448SMark Brown struct wm8994 *wm8994 = data;
1557c884448SMark Brown
1567c884448SMark Brown while (gpio_get_value_cansleep(wm8994->pdata.irq_gpio))
157*9ff80e2dSMarc Zyngier handle_nested_irq(irq_find_mapping(wm8994->edge_irq, 0));
1587c884448SMark Brown
1597c884448SMark Brown return IRQ_HANDLED;
1607c884448SMark Brown }
1617c884448SMark Brown
wm8994_edge_irq_map(struct irq_domain * h,unsigned int virq,irq_hw_number_t hw)1627c884448SMark Brown static int wm8994_edge_irq_map(struct irq_domain *h, unsigned int virq,
1637c884448SMark Brown irq_hw_number_t hw)
1647c884448SMark Brown {
1657c884448SMark Brown struct wm8994 *wm8994 = h->host_data;
1667c884448SMark Brown
1677c884448SMark Brown irq_set_chip_data(virq, wm8994);
1687c884448SMark Brown irq_set_chip_and_handler(virq, &wm8994_edge_irq_chip, handle_edge_irq);
1697c884448SMark Brown irq_set_nested_thread(virq, 1);
1707c884448SMark Brown irq_set_noprobe(virq);
1717c884448SMark Brown
1727c884448SMark Brown return 0;
1737c884448SMark Brown }
1747c884448SMark Brown
1757ce7b26fSKrzysztof Kozlowski static const struct irq_domain_ops wm8994_edge_irq_ops = {
1767c884448SMark Brown .map = wm8994_edge_irq_map,
1777c884448SMark Brown .xlate = irq_domain_xlate_twocell,
1787c884448SMark Brown };
1797c884448SMark Brown
wm8994_irq_init(struct wm8994 * wm8994)180c9fbf7e0SMark Brown int wm8994_irq_init(struct wm8994 *wm8994)
181c9fbf7e0SMark Brown {
1828ab30691SMark Brown int ret;
183b0ab907dSMark Brown unsigned long irqflags;
184dd30acc8SInha Song struct wm8994_pdata *pdata = &wm8994->pdata;
185c9fbf7e0SMark Brown
186c9fbf7e0SMark Brown if (!wm8994->irq) {
187c9fbf7e0SMark Brown dev_warn(wm8994->dev,
188c9fbf7e0SMark Brown "No interrupt specified, no interrupts\n");
189c9fbf7e0SMark Brown wm8994->irq_base = 0;
190c9fbf7e0SMark Brown return 0;
191c9fbf7e0SMark Brown }
192c9fbf7e0SMark Brown
193b0ab907dSMark Brown /* select user or default irq flags */
194b0ab907dSMark Brown irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
195b0ab907dSMark Brown if (pdata->irq_flags)
196b0ab907dSMark Brown irqflags = pdata->irq_flags;
197b0ab907dSMark Brown
1987c884448SMark Brown /* use a GPIO for edge triggered controllers */
1997c884448SMark Brown if (irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
2007c884448SMark Brown if (gpio_to_irq(pdata->irq_gpio) != wm8994->irq) {
2017c884448SMark Brown dev_warn(wm8994->dev, "IRQ %d is not GPIO %d (%d)\n",
2027c884448SMark Brown wm8994->irq, pdata->irq_gpio,
2037c884448SMark Brown gpio_to_irq(pdata->irq_gpio));
2047c884448SMark Brown wm8994->irq = gpio_to_irq(pdata->irq_gpio);
2057c884448SMark Brown }
2067c884448SMark Brown
2077c884448SMark Brown ret = devm_gpio_request_one(wm8994->dev, pdata->irq_gpio,
2087c884448SMark Brown GPIOF_IN, "WM8994 IRQ");
2097c884448SMark Brown
2107c884448SMark Brown if (ret != 0) {
2117c884448SMark Brown dev_err(wm8994->dev, "Failed to get IRQ GPIO: %d\n",
2127c884448SMark Brown ret);
2137c884448SMark Brown return ret;
2147c884448SMark Brown }
2157c884448SMark Brown
2167c884448SMark Brown wm8994->edge_irq = irq_domain_add_linear(NULL, 1,
2177c884448SMark Brown &wm8994_edge_irq_ops,
2187c884448SMark Brown wm8994);
2197c884448SMark Brown
2207c884448SMark Brown ret = regmap_add_irq_chip(wm8994->regmap,
2217c884448SMark Brown irq_create_mapping(wm8994->edge_irq,
2227c884448SMark Brown 0),
2237c884448SMark Brown IRQF_ONESHOT,
2247c884448SMark Brown wm8994->irq_base, &wm8994_irq_chip,
2257c884448SMark Brown &wm8994->irq_data);
2267c884448SMark Brown if (ret != 0) {
2277c884448SMark Brown dev_err(wm8994->dev, "Failed to get IRQ: %d\n",
2287c884448SMark Brown ret);
2297c884448SMark Brown return ret;
2307c884448SMark Brown }
2317c884448SMark Brown
2327c884448SMark Brown ret = request_threaded_irq(wm8994->irq,
2337c884448SMark Brown NULL, wm8994_edge_irq,
2347c884448SMark Brown irqflags,
2357c884448SMark Brown "WM8994 edge", wm8994);
2367c884448SMark Brown } else {
2378ab30691SMark Brown ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq,
238b0ab907dSMark Brown irqflags,
2398ab30691SMark Brown wm8994->irq_base, &wm8994_irq_chip,
2408ab30691SMark Brown &wm8994->irq_data);
2417c884448SMark Brown }
2427c884448SMark Brown
243c9fbf7e0SMark Brown if (ret != 0) {
2448ab30691SMark Brown dev_err(wm8994->dev, "Failed to register IRQ chip: %d\n", ret);
245c9fbf7e0SMark Brown return ret;
246c9fbf7e0SMark Brown }
247c9fbf7e0SMark Brown
248c9fbf7e0SMark Brown /* Enable top level interrupt if it was masked */
249c9fbf7e0SMark Brown wm8994_reg_write(wm8994, WM8994_INTERRUPT_CONTROL, 0);
250c9fbf7e0SMark Brown
251c9fbf7e0SMark Brown return 0;
252c9fbf7e0SMark Brown }
2537821d9b2SLee Jones EXPORT_SYMBOL(wm8994_irq_init);
254c9fbf7e0SMark Brown
wm8994_irq_exit(struct wm8994 * wm8994)255c9fbf7e0SMark Brown void wm8994_irq_exit(struct wm8994 *wm8994)
256c9fbf7e0SMark Brown {
2578ab30691SMark Brown regmap_del_irq_chip(wm8994->irq, wm8994->irq_data);
258c9fbf7e0SMark Brown }
2597821d9b2SLee Jones EXPORT_SYMBOL(wm8994_irq_exit);
260