Blame view

drivers/mfd/wm8994-irq.c 5.81 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
2
3
4
5
6
7
  /*
   * wm8994-irq.c  --  Interrupt controller support for Wolfson WM8994
   *
   * Copyright 2010 Wolfson Microelectronics PLC.
   *
   * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
8
9
10
11
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
7c8844481   Mark Brown   mfd: wm8994: Emul...
12
  #include <linux/gpio.h>
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
13
14
15
16
  #include <linux/i2c.h>
  #include <linux/irq.h>
  #include <linux/mfd/core.h>
  #include <linux/interrupt.h>
7c8844481   Mark Brown   mfd: wm8994: Emul...
17
  #include <linux/irqdomain.h>
8ab306918   Mark Brown   mfd: Convert wm89...
18
  #include <linux/regmap.h>
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
19
20
  
  #include <linux/mfd/wm8994/core.h>
b0ab907d3   Mark Brown   mfd: Support for ...
21
  #include <linux/mfd/wm8994/pdata.h>
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
22
23
24
  #include <linux/mfd/wm8994/registers.h>
  
  #include <linux/delay.h>
7ce7b26f8   Krzysztof Kozlowski   mfd: Constify reg...
25
  static const struct regmap_irq wm8994_irqs[] = {
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
26
  	[WM8994_IRQ_TEMP_SHUT] = {
8ab306918   Mark Brown   mfd: Convert wm89...
27
  		.reg_offset = 1,
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
28
29
30
  		.mask = WM8994_TEMP_SHUT_EINT,
  	},
  	[WM8994_IRQ_MIC1_DET] = {
8ab306918   Mark Brown   mfd: Convert wm89...
31
  		.reg_offset = 1,
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
32
33
34
  		.mask = WM8994_MIC1_DET_EINT,
  	},
  	[WM8994_IRQ_MIC1_SHRT] = {
8ab306918   Mark Brown   mfd: Convert wm89...
35
  		.reg_offset = 1,
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
36
37
38
  		.mask = WM8994_MIC1_SHRT_EINT,
  	},
  	[WM8994_IRQ_MIC2_DET] = {
8ab306918   Mark Brown   mfd: Convert wm89...
39
  		.reg_offset = 1,
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
40
41
42
  		.mask = WM8994_MIC2_DET_EINT,
  	},
  	[WM8994_IRQ_MIC2_SHRT] = {
8ab306918   Mark Brown   mfd: Convert wm89...
43
  		.reg_offset = 1,
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
44
45
46
  		.mask = WM8994_MIC2_SHRT_EINT,
  	},
  	[WM8994_IRQ_FLL1_LOCK] = {
8ab306918   Mark Brown   mfd: Convert wm89...
47
  		.reg_offset = 1,
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
48
49
50
  		.mask = WM8994_FLL1_LOCK_EINT,
  	},
  	[WM8994_IRQ_FLL2_LOCK] = {
8ab306918   Mark Brown   mfd: Convert wm89...
51
  		.reg_offset = 1,
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
52
53
54
  		.mask = WM8994_FLL2_LOCK_EINT,
  	},
  	[WM8994_IRQ_SRC1_LOCK] = {
8ab306918   Mark Brown   mfd: Convert wm89...
55
  		.reg_offset = 1,
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
56
57
58
  		.mask = WM8994_SRC1_LOCK_EINT,
  	},
  	[WM8994_IRQ_SRC2_LOCK] = {
8ab306918   Mark Brown   mfd: Convert wm89...
59
  		.reg_offset = 1,
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
60
61
62
  		.mask = WM8994_SRC2_LOCK_EINT,
  	},
  	[WM8994_IRQ_AIF1DRC1_SIG_DET] = {
8ab306918   Mark Brown   mfd: Convert wm89...
63
  		.reg_offset = 1,
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
64
65
66
  		.mask = WM8994_AIF1DRC1_SIG_DET,
  	},
  	[WM8994_IRQ_AIF1DRC2_SIG_DET] = {
8ab306918   Mark Brown   mfd: Convert wm89...
67
  		.reg_offset = 1,
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
68
69
70
  		.mask = WM8994_AIF1DRC2_SIG_DET_EINT,
  	},
  	[WM8994_IRQ_AIF2DRC_SIG_DET] = {
8ab306918   Mark Brown   mfd: Convert wm89...
71
  		.reg_offset = 1,
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
72
73
74
  		.mask = WM8994_AIF2DRC_SIG_DET_EINT,
  	},
  	[WM8994_IRQ_FIFOS_ERR] = {
8ab306918   Mark Brown   mfd: Convert wm89...
75
  		.reg_offset = 1,
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
76
77
78
  		.mask = WM8994_FIFOS_ERR_EINT,
  	},
  	[WM8994_IRQ_WSEQ_DONE] = {
8ab306918   Mark Brown   mfd: Convert wm89...
79
  		.reg_offset = 1,
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
80
81
82
  		.mask = WM8994_WSEQ_DONE_EINT,
  	},
  	[WM8994_IRQ_DCS_DONE] = {
8ab306918   Mark Brown   mfd: Convert wm89...
83
  		.reg_offset = 1,
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
84
85
86
  		.mask = WM8994_DCS_DONE_EINT,
  	},
  	[WM8994_IRQ_TEMP_WARN] = {
8ab306918   Mark Brown   mfd: Convert wm89...
87
  		.reg_offset = 1,
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
88
89
90
  		.mask = WM8994_TEMP_WARN_EINT,
  	},
  	[WM8994_IRQ_GPIO(1)] = {
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
91
92
93
  		.mask = WM8994_GP1_EINT,
  	},
  	[WM8994_IRQ_GPIO(2)] = {
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
94
95
96
  		.mask = WM8994_GP2_EINT,
  	},
  	[WM8994_IRQ_GPIO(3)] = {
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
97
98
99
  		.mask = WM8994_GP3_EINT,
  	},
  	[WM8994_IRQ_GPIO(4)] = {
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
100
101
102
  		.mask = WM8994_GP4_EINT,
  	},
  	[WM8994_IRQ_GPIO(5)] = {
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
103
104
105
  		.mask = WM8994_GP5_EINT,
  	},
  	[WM8994_IRQ_GPIO(6)] = {
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
106
107
108
  		.mask = WM8994_GP6_EINT,
  	},
  	[WM8994_IRQ_GPIO(7)] = {
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
109
110
111
  		.mask = WM8994_GP7_EINT,
  	},
  	[WM8994_IRQ_GPIO(8)] = {
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
112
113
114
  		.mask = WM8994_GP8_EINT,
  	},
  	[WM8994_IRQ_GPIO(9)] = {
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
115
116
117
  		.mask = WM8994_GP8_EINT,
  	},
  	[WM8994_IRQ_GPIO(10)] = {
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
118
119
120
  		.mask = WM8994_GP10_EINT,
  	},
  	[WM8994_IRQ_GPIO(11)] = {
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
121
122
123
  		.mask = WM8994_GP11_EINT,
  	},
  };
7ce7b26f8   Krzysztof Kozlowski   mfd: Constify reg...
124
  static const struct regmap_irq_chip wm8994_irq_chip = {
8ab306918   Mark Brown   mfd: Convert wm89...
125
126
127
  	.name = "wm8994",
  	.irqs = wm8994_irqs,
  	.num_irqs = ARRAY_SIZE(wm8994_irqs),
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
128

8ab306918   Mark Brown   mfd: Convert wm89...
129
130
131
132
  	.num_regs = 2,
  	.status_base = WM8994_INTERRUPT_STATUS_1,
  	.mask_base = WM8994_INTERRUPT_STATUS_1_MASK,
  	.ack_base = WM8994_INTERRUPT_STATUS_1,
7a9763798   Mark Brown   mfd: wm8994: Flag...
133
  	.runtime_pm = true,
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
134
  };
7c8844481   Mark Brown   mfd: wm8994: Emul...
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
  static void wm8994_edge_irq_enable(struct irq_data *data)
  {
  }
  
  static void wm8994_edge_irq_disable(struct irq_data *data)
  {
  }
  
  static struct irq_chip wm8994_edge_irq_chip = {
  	.name			= "wm8994_edge",
  	.irq_disable		= wm8994_edge_irq_disable,
  	.irq_enable		= wm8994_edge_irq_enable,
  };
  
  static irqreturn_t wm8994_edge_irq(int irq, void *data)
  {
  	struct wm8994 *wm8994 = data;
  
  	while (gpio_get_value_cansleep(wm8994->pdata.irq_gpio))
  		handle_nested_irq(irq_create_mapping(wm8994->edge_irq, 0));
  
  	return IRQ_HANDLED;
  }
  
  static int wm8994_edge_irq_map(struct irq_domain *h, unsigned int virq,
  			       irq_hw_number_t hw)
  {
  	struct wm8994 *wm8994 = h->host_data;
  
  	irq_set_chip_data(virq, wm8994);
  	irq_set_chip_and_handler(virq, &wm8994_edge_irq_chip, handle_edge_irq);
  	irq_set_nested_thread(virq, 1);
7c8844481   Mark Brown   mfd: wm8994: Emul...
167
  	irq_set_noprobe(virq);
7c8844481   Mark Brown   mfd: wm8994: Emul...
168
169
170
  
  	return 0;
  }
7ce7b26f8   Krzysztof Kozlowski   mfd: Constify reg...
171
  static const struct irq_domain_ops wm8994_edge_irq_ops = {
7c8844481   Mark Brown   mfd: wm8994: Emul...
172
173
174
  	.map	= wm8994_edge_irq_map,
  	.xlate	= irq_domain_xlate_twocell,
  };
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
175
176
  int wm8994_irq_init(struct wm8994 *wm8994)
  {
8ab306918   Mark Brown   mfd: Convert wm89...
177
  	int ret;
b0ab907d3   Mark Brown   mfd: Support for ...
178
  	unsigned long irqflags;
dd30acc88   Inha Song   mfd: wm8994: Fix ...
179
  	struct wm8994_pdata *pdata = &wm8994->pdata;
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
180
181
182
183
184
185
186
187
  
  	if (!wm8994->irq) {
  		dev_warn(wm8994->dev,
  			 "No interrupt specified, no interrupts
  ");
  		wm8994->irq_base = 0;
  		return 0;
  	}
b0ab907d3   Mark Brown   mfd: Support for ...
188
189
190
191
  	/* select user or default irq flags */
  	irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
  	if (pdata->irq_flags)
  		irqflags = pdata->irq_flags;
7c8844481   Mark Brown   mfd: wm8994: Emul...
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
  	/* use a GPIO for edge triggered controllers */
  	if (irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
  		if (gpio_to_irq(pdata->irq_gpio) != wm8994->irq) {
  			dev_warn(wm8994->dev, "IRQ %d is not GPIO %d (%d)
  ",
  				 wm8994->irq, pdata->irq_gpio,
  				 gpio_to_irq(pdata->irq_gpio));
  			wm8994->irq = gpio_to_irq(pdata->irq_gpio);
  		}
  
  		ret = devm_gpio_request_one(wm8994->dev, pdata->irq_gpio,
  					    GPIOF_IN, "WM8994 IRQ");
  
  		if (ret != 0) {
  			dev_err(wm8994->dev, "Failed to get IRQ GPIO: %d
  ",
  				ret);
  			return ret;
  		}
  
  		wm8994->edge_irq = irq_domain_add_linear(NULL, 1,
  							 &wm8994_edge_irq_ops,
  							 wm8994);
  
  		ret = regmap_add_irq_chip(wm8994->regmap,
  					  irq_create_mapping(wm8994->edge_irq,
  							     0),
  					  IRQF_ONESHOT,
  					  wm8994->irq_base, &wm8994_irq_chip,
  					  &wm8994->irq_data);
  		if (ret != 0) {
  			dev_err(wm8994->dev, "Failed to get IRQ: %d
  ",
  				ret);
  			return ret;
  		}
  
  		ret = request_threaded_irq(wm8994->irq,
  					   NULL, wm8994_edge_irq,
  					   irqflags,
  					   "WM8994 edge", wm8994);
  	} else {
  		ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq,
  					  irqflags,
  					  wm8994->irq_base, &wm8994_irq_chip,
  					  &wm8994->irq_data);
  	}
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
239
  	if (ret != 0) {
8ab306918   Mark Brown   mfd: Convert wm89...
240
241
  		dev_err(wm8994->dev, "Failed to register IRQ chip: %d
  ", ret);
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
242
243
244
245
246
247
248
249
  		return ret;
  	}
  
  	/* Enable top level interrupt if it was masked */
  	wm8994_reg_write(wm8994, WM8994_INTERRUPT_CONTROL, 0);
  
  	return 0;
  }
7821d9b24   Lee Jones   mfd: wm8994: Expo...
250
  EXPORT_SYMBOL(wm8994_irq_init);
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
251
252
253
  
  void wm8994_irq_exit(struct wm8994 *wm8994)
  {
8ab306918   Mark Brown   mfd: Convert wm89...
254
  	regmap_del_irq_chip(wm8994->irq, wm8994->irq_data);
c9fbf7e07   Mark Brown   mfd: Add WM8994 i...
255
  }
7821d9b24   Lee Jones   mfd: wm8994: Expo...
256
  EXPORT_SYMBOL(wm8994_irq_exit);