xref: /openbmc/linux/drivers/mfd/wm8994-irq.c (revision 9ff80e2d)
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