Blame view

drivers/mfd/twl6030-irq.c 13.5 KB
e8deb28ca   Balaji T K   mfd: Add support ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  /*
   * twl6030-irq.c - TWL6030 irq support
   *
   * Copyright (C) 2005-2009 Texas Instruments, Inc.
   *
   * Modifications to defer interrupt handling to a kernel thread:
   * Copyright (C) 2006 MontaVista Software, Inc.
   *
   * Based on tlv320aic23.c:
   * Copyright (c) by Kai Svahn <kai.svahn@nokia.com>
   *
   * Code cleanup and modifications to IRQ handler.
   * by syed khasim <x0khasim@ti.com>
   *
   * TWL6030 specific code and IRQ handling changes by
   * Jagadeesh Bhaskar Pakaravoor <j-pakaravoor@ti.com>
   * Balaji T K <balajitk@ti.com>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   */
5d4a357d8   Paul Gortmaker   mfd: Add export.h...
33
  #include <linux/export.h>
e8deb28ca   Balaji T K   mfd: Add support ...
34
35
36
37
  #include <linux/interrupt.h>
  #include <linux/irq.h>
  #include <linux/kthread.h>
  #include <linux/i2c/twl.h>
72f2e2c76   kishore kadiyala   mfd: Adding twl60...
38
  #include <linux/platform_device.h>
ab2b9260d   Todd Poynor   mfd: Fix twl6030 ...
39
  #include <linux/suspend.h>
78518ffa0   Benoit Cousson   mfd: Move twl-cor...
40
41
  #include <linux/of.h>
  #include <linux/irqdomain.h>
74d85e47a   Oleksandr Dmytryshyn   mfd: twl6030-irq:...
42
  #include <linux/of_device.h>
e8deb28ca   Balaji T K   mfd: Add support ...
43

b0b4a7c28   G, Manjunath Kondaiah   mfd: Fix twl-irq ...
44
  #include "twl-core.h"
e8deb28ca   Balaji T K   mfd: Add support ...
45
46
47
48
49
50
51
52
53
  /*
   * TWL6030 (unlike its predecessors, which had two level interrupt handling)
   * three interrupt registers INT_STS_A, INT_STS_B and INT_STS_C.
   * It exposes status bits saying who has raised an interrupt. There are
   * three mask registers that corresponds to these status registers, that
   * enables/disables these interrupts.
   *
   * We set up IRQs starting at a platform-specified base. An interrupt map table,
   * specifies mapping between interrupt number and the associated module.
e8deb28ca   Balaji T K   mfd: Add support ...
54
   */
78518ffa0   Benoit Cousson   mfd: Move twl-cor...
55
  #define TWL6030_NR_IRQS    20
e8deb28ca   Balaji T K   mfd: Add support ...
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
  
  static int twl6030_interrupt_mapping[24] = {
  	PWR_INTR_OFFSET,	/* Bit 0	PWRON			*/
  	PWR_INTR_OFFSET,	/* Bit 1	RPWRON			*/
  	PWR_INTR_OFFSET,	/* Bit 2	BAT_VLOW		*/
  	RTC_INTR_OFFSET,	/* Bit 3	RTC_ALARM		*/
  	RTC_INTR_OFFSET,	/* Bit 4	RTC_PERIOD		*/
  	HOTDIE_INTR_OFFSET,	/* Bit 5	HOT_DIE			*/
  	SMPSLDO_INTR_OFFSET,	/* Bit 6	VXXX_SHORT		*/
  	SMPSLDO_INTR_OFFSET,	/* Bit 7	VMMC_SHORT		*/
  
  	SMPSLDO_INTR_OFFSET,	/* Bit 8	VUSIM_SHORT		*/
  	BATDETECT_INTR_OFFSET,	/* Bit 9	BAT			*/
  	SIMDETECT_INTR_OFFSET,	/* Bit 10	SIM			*/
  	MMCDETECT_INTR_OFFSET,	/* Bit 11	MMC			*/
3103de8cd   Lee Jones   mfd: twl6030-irq:...
71
  	RSV_INTR_OFFSET,	/* Bit 12	Reserved		*/
e8deb28ca   Balaji T K   mfd: Add support ...
72
73
74
75
76
77
78
  	MADC_INTR_OFFSET,	/* Bit 13	GPADC_RT_EOC		*/
  	MADC_INTR_OFFSET,	/* Bit 14	GPADC_SW_EOC		*/
  	GASGAUGE_INTR_OFFSET,	/* Bit 15	CC_AUTOCAL		*/
  
  	USBOTG_INTR_OFFSET,	/* Bit 16	ID_WKUP			*/
  	USBOTG_INTR_OFFSET,	/* Bit 17	VBUS_WKUP		*/
  	USBOTG_INTR_OFFSET,	/* Bit 18	ID			*/
77b1d3fa8   Hema HK   mfd: TWL6030: USB...
79
  	USB_PRES_INTR_OFFSET,	/* Bit 19	VBUS			*/
e8deb28ca   Balaji T K   mfd: Add support ...
80
  	CHARGER_INTR_OFFSET,	/* Bit 20	CHRG_CTRL		*/
6523b148b   Graeme Gregory   mfd: Fix twl6030 ...
81
82
  	CHARGERFAULT_INTR_OFFSET,	/* Bit 21	EXT_CHRG	*/
  	CHARGERFAULT_INTR_OFFSET,	/* Bit 22	INT_CHRG	*/
e8deb28ca   Balaji T K   mfd: Add support ...
83
84
  	RSV_INTR_OFFSET,	/* Bit 23	Reserved		*/
  };
74d85e47a   Oleksandr Dmytryshyn   mfd: twl6030-irq:...
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  
  static int twl6032_interrupt_mapping[24] = {
  	PWR_INTR_OFFSET,	/* Bit 0	PWRON			*/
  	PWR_INTR_OFFSET,	/* Bit 1	RPWRON			*/
  	PWR_INTR_OFFSET,	/* Bit 2	SYS_VLOW		*/
  	RTC_INTR_OFFSET,	/* Bit 3	RTC_ALARM		*/
  	RTC_INTR_OFFSET,	/* Bit 4	RTC_PERIOD		*/
  	HOTDIE_INTR_OFFSET,	/* Bit 5	HOT_DIE			*/
  	SMPSLDO_INTR_OFFSET,	/* Bit 6	VXXX_SHORT		*/
  	PWR_INTR_OFFSET,	/* Bit 7	SPDURATION		*/
  
  	PWR_INTR_OFFSET,	/* Bit 8	WATCHDOG		*/
  	BATDETECT_INTR_OFFSET,	/* Bit 9	BAT			*/
  	SIMDETECT_INTR_OFFSET,	/* Bit 10	SIM			*/
  	MMCDETECT_INTR_OFFSET,	/* Bit 11	MMC			*/
  	MADC_INTR_OFFSET,	/* Bit 12	GPADC_RT_EOC		*/
  	MADC_INTR_OFFSET,	/* Bit 13	GPADC_SW_EOC		*/
  	GASGAUGE_INTR_OFFSET,	/* Bit 14	CC_EOC			*/
  	GASGAUGE_INTR_OFFSET,	/* Bit 15	CC_AUTOCAL		*/
  
  	USBOTG_INTR_OFFSET,	/* Bit 16	ID_WKUP			*/
  	USBOTG_INTR_OFFSET,	/* Bit 17	VBUS_WKUP		*/
  	USBOTG_INTR_OFFSET,	/* Bit 18	ID			*/
  	USB_PRES_INTR_OFFSET,	/* Bit 19	VBUS			*/
  	CHARGER_INTR_OFFSET,	/* Bit 20	CHRG_CTRL		*/
  	CHARGERFAULT_INTR_OFFSET,	/* Bit 21	EXT_CHRG	*/
  	CHARGERFAULT_INTR_OFFSET,	/* Bit 22	INT_CHRG	*/
  	RSV_INTR_OFFSET,	/* Bit 23	Reserved		*/
  };
e8deb28ca   Balaji T K   mfd: Add support ...
114
  /*----------------------------------------------------------------------*/
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
115
116
117
118
119
120
121
122
  struct twl6030_irq {
  	unsigned int		irq_base;
  	int			twl_irq;
  	bool			irq_wake_enabled;
  	atomic_t		wakeirqs;
  	struct notifier_block	pm_nb;
  	struct irq_chip		irq_chip;
  	struct irq_domain	*irq_domain;
74d85e47a   Oleksandr Dmytryshyn   mfd: twl6030-irq:...
123
  	const int		*irq_mapping_tbl;
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
124
  };
e8deb28ca   Balaji T K   mfd: Add support ...
125

0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
126
  static struct twl6030_irq *twl6030_irq;
ab2b9260d   Todd Poynor   mfd: Fix twl6030 ...
127
128
129
130
131
  
  static int twl6030_irq_pm_notifier(struct notifier_block *notifier,
  				   unsigned long pm_event, void *unused)
  {
  	int chained_wakeups;
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
132
133
  	struct twl6030_irq *pdata = container_of(notifier, struct twl6030_irq,
  						  pm_nb);
ab2b9260d   Todd Poynor   mfd: Fix twl6030 ...
134
135
136
  
  	switch (pm_event) {
  	case PM_SUSPEND_PREPARE:
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
137
  		chained_wakeups = atomic_read(&pdata->wakeirqs);
ab2b9260d   Todd Poynor   mfd: Fix twl6030 ...
138

0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
139
140
  		if (chained_wakeups && !pdata->irq_wake_enabled) {
  			if (enable_irq_wake(pdata->twl_irq))
ab2b9260d   Todd Poynor   mfd: Fix twl6030 ...
141
142
143
  				pr_err("twl6030 IRQ wake enable failed
  ");
  			else
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
144
145
146
147
  				pdata->irq_wake_enabled = true;
  		} else if (!chained_wakeups && pdata->irq_wake_enabled) {
  			disable_irq_wake(pdata->twl_irq);
  			pdata->irq_wake_enabled = false;
ab2b9260d   Todd Poynor   mfd: Fix twl6030 ...
148
  		}
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
149
  		disable_irq(pdata->twl_irq);
ab2b9260d   Todd Poynor   mfd: Fix twl6030 ...
150
  		break;
782baa20b   Todd Poynor   mfd: Disable twl6...
151
152
  
  	case PM_POST_SUSPEND:
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
153
  		enable_irq(pdata->twl_irq);
782baa20b   Todd Poynor   mfd: Disable twl6...
154
  		break;
ab2b9260d   Todd Poynor   mfd: Fix twl6030 ...
155
156
157
158
159
160
  	default:
  		break;
  	}
  
  	return NOTIFY_DONE;
  }
e8deb28ca   Balaji T K   mfd: Add support ...
161
  /*
87343e534   Naga Venkata Srikanth V   mfd: twl6030-irq:...
162
163
164
165
166
167
  * Threaded irq handler for the twl6030 interrupt.
  * We query the interrupt controller in the twl6030 to determine
  * which module is generating the interrupt request and call
  * handle_nested_irq for that module.
  */
  static irqreturn_t twl6030_irq_thread(int irq, void *data)
e8deb28ca   Balaji T K   mfd: Add support ...
168
  {
87343e534   Naga Venkata Srikanth V   mfd: twl6030-irq:...
169
170
  	int i, ret;
  	union {
e8deb28ca   Balaji T K   mfd: Add support ...
171
  		u8 bytes[4];
754fa7bc9   Danke Xie   mfd: twl6030: Fix...
172
  		__le32 int_sts;
87343e534   Naga Venkata Srikanth V   mfd: twl6030-irq:...
173
  	} sts;
754fa7bc9   Danke Xie   mfd: twl6030: Fix...
174
  	u32 int_sts; /* sts.int_sts converted to CPU endianness */
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
175
  	struct twl6030_irq *pdata = data;
e8deb28ca   Balaji T K   mfd: Add support ...
176

87343e534   Naga Venkata Srikanth V   mfd: twl6030-irq:...
177
178
179
180
181
182
183
  	/* read INT_STS_A, B and C in one shot using a burst read */
  	ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes, REG_INT_STS_A, 3);
  	if (ret) {
  		pr_warn("twl6030_irq: I2C error %d reading PIH ISR
  ", ret);
  		return IRQ_HANDLED;
  	}
e8deb28ca   Balaji T K   mfd: Add support ...
184

87343e534   Naga Venkata Srikanth V   mfd: twl6030-irq:...
185
  	sts.bytes[3] = 0; /* Only 24 bits are valid*/
e8deb28ca   Balaji T K   mfd: Add support ...
186

87343e534   Naga Venkata Srikanth V   mfd: twl6030-irq:...
187
188
189
190
191
192
  	/*
  	 * Since VBUS status bit is not reliable for VBUS disconnect
  	 * use CHARGER VBUS detection status bit instead.
  	 */
  	if (sts.bytes[2] & 0x10)
  		sts.bytes[2] |= 0x08;
77b1d3fa8   Hema HK   mfd: TWL6030: USB...
193

754fa7bc9   Danke Xie   mfd: twl6030: Fix...
194
195
196
  	int_sts = le32_to_cpu(sts.int_sts);
  	for (i = 0; int_sts; int_sts >>= 1, i++)
  		if (int_sts & 0x1) {
b32408f64   Grygorii Strashko   mfd: twl6030-irq:...
197
  			int module_irq =
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
198
  				irq_find_mapping(pdata->irq_domain,
74d85e47a   Oleksandr Dmytryshyn   mfd: twl6030-irq:...
199
  						 pdata->irq_mapping_tbl[i]);
b32408f64   Grygorii Strashko   mfd: twl6030-irq:...
200
201
202
203
204
205
  			if (module_irq)
  				handle_nested_irq(module_irq);
  			else
  				pr_err("twl6030_irq: Unmapped PIH ISR %u detected
  ",
  				       i);
87343e534   Naga Venkata Srikanth V   mfd: twl6030-irq:...
206
207
208
  			pr_debug("twl6030_irq: PIH ISR %u, virq%u
  ",
  				 i, module_irq);
e8deb28ca   Balaji T K   mfd: Add support ...
209
  		}
3f8349e6e   Nishanth Menon   mfd: Clear twl603...
210

87343e534   Naga Venkata Srikanth V   mfd: twl6030-irq:...
211
212
213
214
215
216
217
218
219
220
221
222
223
  	/*
  	 * NOTE:
  	 * Simulation confirms that documentation is wrong w.r.t the
  	 * interrupt status clear operation. A single *byte* write to
  	 * any one of STS_A to STS_C register results in all three
  	 * STS registers being reset. Since it does not matter which
  	 * value is written, all three registers are cleared on a
  	 * single byte write, so we just use 0x0 to clear.
  	 */
  	ret = twl_i2c_write_u8(TWL_MODULE_PIH, 0x00, REG_INT_STS_A);
  	if (ret)
  		pr_warn("twl6030_irq: I2C error in clearing PIH ISR
  ");
e8deb28ca   Balaji T K   mfd: Add support ...
224

e8deb28ca   Balaji T K   mfd: Add support ...
225
226
227
228
  	return IRQ_HANDLED;
  }
  
  /*----------------------------------------------------------------------*/
b8b8d7932   Nishanth Menon   mfd: Make twl6030...
229
  static int twl6030_irq_set_wake(struct irq_data *d, unsigned int on)
49dcd070d   Santosh Shilimkar   mfd: Set twl6030 ...
230
  {
1e84aa445   Jiang Liu   mfd: Use irq_desc...
231
  	struct twl6030_irq *pdata = irq_data_get_irq_chip_data(d);
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
232

ab2b9260d   Todd Poynor   mfd: Fix twl6030 ...
233
  	if (on)
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
234
  		atomic_inc(&pdata->wakeirqs);
ab2b9260d   Todd Poynor   mfd: Fix twl6030 ...
235
  	else
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
236
  		atomic_dec(&pdata->wakeirqs);
49dcd070d   Santosh Shilimkar   mfd: Set twl6030 ...
237

ab2b9260d   Todd Poynor   mfd: Fix twl6030 ...
238
  	return 0;
49dcd070d   Santosh Shilimkar   mfd: Set twl6030 ...
239
  }
e8deb28ca   Balaji T K   mfd: Add support ...
240
241
242
243
  int twl6030_interrupt_unmask(u8 bit_mask, u8 offset)
  {
  	int ret;
  	u8 unmask_value;
3103de8cd   Lee Jones   mfd: twl6030-irq:...
244

e8deb28ca   Balaji T K   mfd: Add support ...
245
246
247
248
249
250
251
252
253
254
255
256
257
  	ret = twl_i2c_read_u8(TWL_MODULE_PIH, &unmask_value,
  			REG_INT_STS_A + offset);
  	unmask_value &= (~(bit_mask));
  	ret |= twl_i2c_write_u8(TWL_MODULE_PIH, unmask_value,
  			REG_INT_STS_A + offset); /* unmask INT_MSK_A/B/C */
  	return ret;
  }
  EXPORT_SYMBOL(twl6030_interrupt_unmask);
  
  int twl6030_interrupt_mask(u8 bit_mask, u8 offset)
  {
  	int ret;
  	u8 mask_value;
3103de8cd   Lee Jones   mfd: twl6030-irq:...
258

e8deb28ca   Balaji T K   mfd: Add support ...
259
260
261
262
263
264
265
266
  	ret = twl_i2c_read_u8(TWL_MODULE_PIH, &mask_value,
  			REG_INT_STS_A + offset);
  	mask_value |= (bit_mask);
  	ret |= twl_i2c_write_u8(TWL_MODULE_PIH, mask_value,
  			REG_INT_STS_A + offset); /* mask INT_MSK_A/B/C */
  	return ret;
  }
  EXPORT_SYMBOL(twl6030_interrupt_mask);
72f2e2c76   kishore kadiyala   mfd: Adding twl60...
267
268
269
270
271
272
273
274
275
276
277
  int twl6030_mmc_card_detect_config(void)
  {
  	int ret;
  	u8 reg_val = 0;
  
  	/* Unmasking the Card detect Interrupt line for MMC1 from Phoenix */
  	twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK,
  						REG_INT_MSK_LINE_B);
  	twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK,
  						REG_INT_MSK_STS_B);
  	/*
25985edce   Lucas De Marchi   Fix common misspe...
278
  	 * Initially Configuring MMC_CTRL for receiving interrupts &
72f2e2c76   kishore kadiyala   mfd: Adding twl60...
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
  	 * Card status on TWL6030 for MMC1
  	 */
  	ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &reg_val, TWL6030_MMCCTRL);
  	if (ret < 0) {
  		pr_err("twl6030: Failed to read MMCCTRL, error %d
  ", ret);
  		return ret;
  	}
  	reg_val &= ~VMMC_AUTO_OFF;
  	reg_val |= SW_FC;
  	ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val, TWL6030_MMCCTRL);
  	if (ret < 0) {
  		pr_err("twl6030: Failed to write MMCCTRL, error %d
  ", ret);
  		return ret;
  	}
  
  	/* Configuring PullUp-PullDown register */
  	ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &reg_val,
  						TWL6030_CFG_INPUT_PUPD3);
  	if (ret < 0) {
  		pr_err("twl6030: Failed to read CFG_INPUT_PUPD3, error %d
  ",
  									ret);
  		return ret;
  	}
  	reg_val &= ~(MMC_PU | MMC_PD);
  	ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val,
  						TWL6030_CFG_INPUT_PUPD3);
  	if (ret < 0) {
  		pr_err("twl6030: Failed to write CFG_INPUT_PUPD3, error %d
  ",
  									ret);
  		return ret;
  	}
bdd61bc66   Benoit Cousson   mfd: Return twl60...
314

0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
315
316
  	return irq_find_mapping(twl6030_irq->irq_domain,
  				 MMCDETECT_INTR_OFFSET);
72f2e2c76   kishore kadiyala   mfd: Adding twl60...
317
318
319
320
321
322
323
324
325
326
327
328
329
  }
  EXPORT_SYMBOL(twl6030_mmc_card_detect_config);
  
  int twl6030_mmc_card_detect(struct device *dev, int slot)
  {
  	int ret = -EIO;
  	u8 read_reg = 0;
  	struct platform_device *pdev = to_platform_device(dev);
  
  	if (pdev->id) {
  		/* TWL6030 provide's Card detect support for
  		 * only MMC1 controller.
  		 */
25985edce   Lucas De Marchi   Fix common misspe...
330
331
  		pr_err("Unknown MMC controller %d in %s
  ", pdev->id, __func__);
72f2e2c76   kishore kadiyala   mfd: Adding twl60...
332
333
334
335
336
337
338
339
340
341
342
343
344
  		return ret;
  	}
  	/*
  	 * BIT0 of MMC_CTRL on TWL6030 provides card status for MMC1
  	 * 0 - Card not present ,1 - Card present
  	 */
  	ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &read_reg,
  						TWL6030_MMCCTRL);
  	if (ret >= 0)
  		ret = read_reg & STS_MMC;
  	return ret;
  }
  EXPORT_SYMBOL(twl6030_mmc_card_detect);
b32408f64   Grygorii Strashko   mfd: twl6030-irq:...
345
346
347
  static int twl6030_irq_map(struct irq_domain *d, unsigned int virq,
  			      irq_hw_number_t hwirq)
  {
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
348
349
350
351
  	struct twl6030_irq *pdata = d->host_data;
  
  	irq_set_chip_data(virq, pdata);
  	irq_set_chip_and_handler(virq,  &pdata->irq_chip, handle_simple_irq);
b32408f64   Grygorii Strashko   mfd: twl6030-irq:...
352
  	irq_set_nested_thread(virq, true);
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
353
  	irq_set_parent(virq, pdata->twl_irq);
b32408f64   Grygorii Strashko   mfd: twl6030-irq:...
354
  	irq_set_noprobe(virq);
b32408f64   Grygorii Strashko   mfd: twl6030-irq:...
355
356
357
358
359
360
  
  	return 0;
  }
  
  static void twl6030_irq_unmap(struct irq_domain *d, unsigned int virq)
  {
b32408f64   Grygorii Strashko   mfd: twl6030-irq:...
361
362
363
  	irq_set_chip_and_handler(virq, NULL, NULL);
  	irq_set_chip_data(virq, NULL);
  }
7ce7b26f8   Krzysztof Kozlowski   mfd: Constify reg...
364
  static const struct irq_domain_ops twl6030_irq_domain_ops = {
b32408f64   Grygorii Strashko   mfd: twl6030-irq:...
365
366
367
368
  	.map	= twl6030_irq_map,
  	.unmap	= twl6030_irq_unmap,
  	.xlate	= irq_domain_xlate_onetwocell,
  };
74d85e47a   Oleksandr Dmytryshyn   mfd: twl6030-irq:...
369
370
371
372
373
  static const struct of_device_id twl6030_of_match[] = {
  	{.compatible = "ti,twl6030", &twl6030_interrupt_mapping},
  	{.compatible = "ti,twl6032", &twl6032_interrupt_mapping},
  	{ },
  };
78518ffa0   Benoit Cousson   mfd: Move twl-cor...
374
  int twl6030_init_irq(struct device *dev, int irq_num)
e8deb28ca   Balaji T K   mfd: Add support ...
375
  {
78518ffa0   Benoit Cousson   mfd: Move twl-cor...
376
  	struct			device_node *node = dev->of_node;
b32408f64   Grygorii Strashko   mfd: twl6030-irq:...
377
  	int			nr_irqs;
a820e5686   Grygorii Strashko   mfd: twl6030-irq:...
378
  	int			status;
14591d888   Peter Ujfalusi   mfd/rtc/gpio: twl...
379
  	u8			mask[3];
74d85e47a   Oleksandr Dmytryshyn   mfd: twl6030-irq:...
380
381
382
383
384
385
386
387
  	const struct of_device_id *of_id;
  
  	of_id = of_match_device(twl6030_of_match, dev);
  	if (!of_id || !of_id->data) {
  		dev_err(dev, "Unknown TWL device model
  ");
  		return -EINVAL;
  	}
78518ffa0   Benoit Cousson   mfd: Move twl-cor...
388
389
  
  	nr_irqs = TWL6030_NR_IRQS;
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
390
391
392
393
394
395
  	twl6030_irq = devm_kzalloc(dev, sizeof(*twl6030_irq), GFP_KERNEL);
  	if (!twl6030_irq) {
  		dev_err(dev, "twl6030_irq: Memory allocation failed
  ");
  		return -ENOMEM;
  	}
14591d888   Peter Ujfalusi   mfd/rtc/gpio: twl...
396
  	mask[0] = 0xFF;
e8deb28ca   Balaji T K   mfd: Add support ...
397
398
  	mask[1] = 0xFF;
  	mask[2] = 0xFF;
ec1a07b34   Benoit Cousson   mfd: Replace twl-...
399
400
  
  	/* mask all int lines */
a820e5686   Grygorii Strashko   mfd: twl6030-irq:...
401
  	status = twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_LINE_A, 3);
ec1a07b34   Benoit Cousson   mfd: Replace twl-...
402
  	/* mask all int sts */
a820e5686   Grygorii Strashko   mfd: twl6030-irq:...
403
  	status |= twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_STS_A, 3);
ec1a07b34   Benoit Cousson   mfd: Replace twl-...
404
  	/* clear INT_STS_A,B,C */
a820e5686   Grygorii Strashko   mfd: twl6030-irq:...
405
406
407
408
409
410
411
  	status |= twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_STS_A, 3);
  
  	if (status < 0) {
  		dev_err(dev, "I2C err writing TWL_MODULE_PIH: %d
  ", status);
  		return status;
  	}
e8deb28ca   Balaji T K   mfd: Add support ...
412

ec1a07b34   Benoit Cousson   mfd: Replace twl-...
413
414
  	/*
  	 * install an irq handler for each of the modules;
e8deb28ca   Balaji T K   mfd: Add support ...
415
416
  	 * clone dummy irq_chip since PIH can't *do* anything
  	 */
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
417
418
419
420
421
422
423
  	twl6030_irq->irq_chip = dummy_irq_chip;
  	twl6030_irq->irq_chip.name = "twl6030";
  	twl6030_irq->irq_chip.irq_set_type = NULL;
  	twl6030_irq->irq_chip.irq_set_wake = twl6030_irq_set_wake;
  
  	twl6030_irq->pm_nb.notifier_call = twl6030_irq_pm_notifier;
  	atomic_set(&twl6030_irq->wakeirqs, 0);
74d85e47a   Oleksandr Dmytryshyn   mfd: twl6030-irq:...
424
  	twl6030_irq->irq_mapping_tbl = of_id->data;
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
425
426
427
428
429
  
  	twl6030_irq->irq_domain =
  		irq_domain_add_linear(node, nr_irqs,
  				      &twl6030_irq_domain_ops, twl6030_irq);
  	if (!twl6030_irq->irq_domain) {
b32408f64   Grygorii Strashko   mfd: twl6030-irq:...
430
431
432
  		dev_err(dev, "Can't add irq_domain
  ");
  		return -ENOMEM;
e8deb28ca   Balaji T K   mfd: Add support ...
433
  	}
b32408f64   Grygorii Strashko   mfd: twl6030-irq:...
434
435
  	dev_info(dev, "PIH (irq %d) nested IRQs
  ", irq_num);
e8deb28ca   Balaji T K   mfd: Add support ...
436
437
  
  	/* install an irq handler to demultiplex the TWL6030 interrupt */
87343e534   Naga Venkata Srikanth V   mfd: twl6030-irq:...
438
  	status = request_threaded_irq(irq_num, NULL, twl6030_irq_thread,
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
439
  				      IRQF_ONESHOT, "TWL6030-PIH", twl6030_irq);
e8deb28ca   Balaji T K   mfd: Add support ...
440
  	if (status < 0) {
ec1a07b34   Benoit Cousson   mfd: Replace twl-...
441
442
  		dev_err(dev, "could not claim irq %d: %d
  ", irq_num, status);
e8deb28ca   Balaji T K   mfd: Add support ...
443
444
  		goto fail_irq;
  	}
862de70c1   Axel Lin   mfd: Make sure to...
445

0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
446
447
  	twl6030_irq->twl_irq = irq_num;
  	register_pm_notifier(&twl6030_irq->pm_nb);
b32408f64   Grygorii Strashko   mfd: twl6030-irq:...
448
  	return 0;
e8deb28ca   Balaji T K   mfd: Add support ...
449

862de70c1   Axel Lin   mfd: Make sure to...
450
  fail_irq:
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
451
  	irq_domain_remove(twl6030_irq->irq_domain);
e8deb28ca   Balaji T K   mfd: Add support ...
452
453
454
455
456
  	return status;
  }
  
  int twl6030_exit_irq(void)
  {
0aa8c6853   Grygorii Strashko   mfd: twl6030-irq:...
457
458
459
  	if (twl6030_irq && twl6030_irq->twl_irq) {
  		unregister_pm_notifier(&twl6030_irq->pm_nb);
  		free_irq(twl6030_irq->twl_irq, NULL);
b32408f64   Grygorii Strashko   mfd: twl6030-irq:...
460
461
462
463
464
465
466
467
468
  		/*
  		 * TODO: IRQ domain and allocated nested IRQ descriptors
  		 * should be freed somehow here. Now It can't be done, because
  		 * child devices will not be deleted during removing of
  		 * TWL Core driver and they will still contain allocated
  		 * virt IRQs in their Resources tables.
  		 * The same prevents us from using devm_request_threaded_irq()
  		 * in this module.
  		 */
e8deb28ca   Balaji T K   mfd: Add support ...
469
470
471
  	}
  	return 0;
  }