Blame view

drivers/mfd/tps65910-irq.c 5.62 KB
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
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
33
34
35
36
37
38
39
40
41
42
43
  /*
   * tps65910-irq.c  --  TI TPS6591x
   *
   * Copyright 2010 Texas Instruments Inc.
   *
   * Author: Graeme Gregory <gg@slimlogic.co.uk>
   * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
   *
   *  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.
   *
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/bug.h>
  #include <linux/device.h>
  #include <linux/interrupt.h>
  #include <linux/irq.h>
  #include <linux/gpio.h>
  #include <linux/mfd/tps65910.h>
  
  static inline int irq_to_tps65910_irq(struct tps65910 *tps65910,
  							int irq)
  {
  	return (irq - tps65910->irq_base);
  }
  
  /*
   * This is a threaded IRQ handler so can access I2C/SPI.  Since all
   * interrupts are clear on read the IRQ line will be reasserted and
   * the physical IRQ will be handled again if another interrupt is
   * asserted while we run - in the normal course of events this is a
   * rare occurrence so we save I2C/SPI reads.  We're also assuming that
   * it's rare to get lots of interrupts firing simultaneously so try to
   * minimise I/O.
   */
  static irqreturn_t tps65910_irq(int irq, void *irq_data)
  {
  	struct tps65910 *tps65910 = irq_data;
a2974732c   Jorge Eduardo Candelaria   TPS65911: Add new...
44
45
  	u32 irq_sts;
  	u32 irq_mask;
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
46
47
48
49
50
51
52
  	u8 reg;
  	int i;
  
  	tps65910->read(tps65910, TPS65910_INT_STS, 1, &reg);
  	irq_sts = reg;
  	tps65910->read(tps65910, TPS65910_INT_STS2, 1, &reg);
  	irq_sts |= reg << 8;
a2974732c   Jorge Eduardo Candelaria   TPS65911: Add new...
53
54
55
56
57
  	switch (tps65910_chip_id(tps65910)) {
  	case TPS65911:
  		tps65910->read(tps65910, TPS65910_INT_STS3, 1, &reg);
  		irq_sts |= reg << 16;
  	}
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
58
59
60
61
62
  
  	tps65910->read(tps65910, TPS65910_INT_MSK, 1, &reg);
  	irq_mask = reg;
  	tps65910->read(tps65910, TPS65910_INT_MSK2, 1, &reg);
  	irq_mask |= reg << 8;
a2974732c   Jorge Eduardo Candelaria   TPS65911: Add new...
63
64
65
66
67
  	switch (tps65910_chip_id(tps65910)) {
  	case TPS65911:
  		tps65910->read(tps65910, TPS65910_INT_MSK3, 1, &reg);
  		irq_mask |= reg << 16;
  	}
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
68
69
70
71
72
  
  	irq_sts &= ~irq_mask;
  
  	if (!irq_sts)
  		return IRQ_NONE;
a2974732c   Jorge Eduardo Candelaria   TPS65911: Add new...
73
  	for (i = 0; i < tps65910->irq_num; i++) {
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
74
75
76
77
78
79
80
81
82
  
  		if (!(irq_sts & (1 << i)))
  			continue;
  
  		handle_nested_irq(tps65910->irq_base + i);
  	}
  
  	/* Write the STS register back to clear IRQs we handled */
  	reg = irq_sts & 0xFF;
a2974732c   Jorge Eduardo Candelaria   TPS65911: Add new...
83
  	irq_sts >>= 8;
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
84
  	tps65910->write(tps65910, TPS65910_INT_STS, 1, &reg);
a2974732c   Jorge Eduardo Candelaria   TPS65911: Add new...
85
  	reg = irq_sts & 0xFF;
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
86
  	tps65910->write(tps65910, TPS65910_INT_STS2, 1, &reg);
a2974732c   Jorge Eduardo Candelaria   TPS65911: Add new...
87
88
89
90
91
  	switch (tps65910_chip_id(tps65910)) {
  	case TPS65911:
  		reg = irq_sts >> 8;
  		tps65910->write(tps65910, TPS65910_INT_STS3, 1, &reg);
  	}
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
92
93
94
95
96
97
98
99
100
101
102
103
104
105
  
  	return IRQ_HANDLED;
  }
  
  static void tps65910_irq_lock(struct irq_data *data)
  {
  	struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
  
  	mutex_lock(&tps65910->irq_lock);
  }
  
  static void tps65910_irq_sync_unlock(struct irq_data *data)
  {
  	struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
a2974732c   Jorge Eduardo Candelaria   TPS65911: Add new...
106
  	u32 reg_mask;
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
107
108
109
110
111
112
  	u8 reg;
  
  	tps65910->read(tps65910, TPS65910_INT_MSK, 1, &reg);
  	reg_mask = reg;
  	tps65910->read(tps65910, TPS65910_INT_MSK2, 1, &reg);
  	reg_mask |= reg << 8;
a2974732c   Jorge Eduardo Candelaria   TPS65911: Add new...
113
114
115
116
117
  	switch (tps65910_chip_id(tps65910)) {
  	case TPS65911:
  		tps65910->read(tps65910, TPS65910_INT_MSK3, 1, &reg);
  		reg_mask |= reg << 16;
  	}
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
118
119
120
121
  
  	if (tps65910->irq_mask != reg_mask) {
  		reg = tps65910->irq_mask & 0xFF;
  		tps65910->write(tps65910, TPS65910_INT_MSK, 1, &reg);
a2974732c   Jorge Eduardo Candelaria   TPS65911: Add new...
122
  		reg = tps65910->irq_mask >> 8 & 0xFF;
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
123
  		tps65910->write(tps65910, TPS65910_INT_MSK2, 1, &reg);
a2974732c   Jorge Eduardo Candelaria   TPS65911: Add new...
124
125
126
127
128
  		switch (tps65910_chip_id(tps65910)) {
  		case TPS65911:
  			reg = tps65910->irq_mask >> 16;
  			tps65910->write(tps65910, TPS65910_INT_MSK3, 1, &reg);
  		}
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
129
130
131
132
133
134
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
  	}
  	mutex_unlock(&tps65910->irq_lock);
  }
  
  static void tps65910_irq_enable(struct irq_data *data)
  {
  	struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
  
  	tps65910->irq_mask &= ~( 1 << irq_to_tps65910_irq(tps65910, data->irq));
  }
  
  static void tps65910_irq_disable(struct irq_data *data)
  {
  	struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
  
  	tps65910->irq_mask |= ( 1 << irq_to_tps65910_irq(tps65910, data->irq));
  }
  
  static struct irq_chip tps65910_irq_chip = {
  	.name = "tps65910",
  	.irq_bus_lock = tps65910_irq_lock,
  	.irq_bus_sync_unlock = tps65910_irq_sync_unlock,
  	.irq_disable = tps65910_irq_disable,
  	.irq_enable = tps65910_irq_enable,
  };
  
  int tps65910_irq_init(struct tps65910 *tps65910, int irq,
  		    struct tps65910_platform_data *pdata)
  {
  	int ret, cur_irq;
  	int flags = IRQF_ONESHOT;
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
160
161
162
163
164
165
166
167
168
169
170
171
  
  	if (!irq) {
  		dev_warn(tps65910->dev, "No interrupt support, no core IRQ
  ");
  		return -EINVAL;
  	}
  
  	if (!pdata || !pdata->irq_base) {
  		dev_warn(tps65910->dev, "No interrupt support, no IRQ base
  ");
  		return -EINVAL;
  	}
a2974732c   Jorge Eduardo Candelaria   TPS65911: Add new...
172
  	tps65910->irq_mask = 0xFFFFFF;
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
173
174
175
176
  
  	mutex_init(&tps65910->irq_lock);
  	tps65910->chip_irq = irq;
  	tps65910->irq_base = pdata->irq_base;
a2974732c   Jorge Eduardo Candelaria   TPS65911: Add new...
177
178
179
  	switch (tps65910_chip_id(tps65910)) {
  	case TPS65910:
  		tps65910->irq_num = TPS65910_NUM_IRQ;
fa948761e   Johan Hovold   mfd: Fix initiali...
180
  		break;
a2974732c   Jorge Eduardo Candelaria   TPS65911: Add new...
181
182
  	case TPS65911:
  		tps65910->irq_num = TPS65911_NUM_IRQ;
fa948761e   Johan Hovold   mfd: Fix initiali...
183
  		break;
a2974732c   Jorge Eduardo Candelaria   TPS65911: Add new...
184
  	}
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
185
186
  	/* Register with genirq */
  	for (cur_irq = tps65910->irq_base;
a2974732c   Jorge Eduardo Candelaria   TPS65911: Add new...
187
  	     cur_irq < tps65910->irq_num + tps65910->irq_base;
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
  	     cur_irq++) {
  		irq_set_chip_data(cur_irq, tps65910);
  		irq_set_chip_and_handler(cur_irq, &tps65910_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
  	}
  
  	ret = request_threaded_irq(irq, NULL, tps65910_irq, flags,
  				   "tps65910", tps65910);
a2974732c   Jorge Eduardo Candelaria   TPS65911: Add new...
205
206
  
  	irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
207
208
209
210
211
212
213
214
215
  	if (ret != 0)
  		dev_err(tps65910->dev, "Failed to request IRQ: %d
  ", ret);
  
  	return ret;
  }
  
  int tps65910_irq_exit(struct tps65910 *tps65910)
  {
1e351a95b   Afzal Mohammed   mfd: Make TPS6591...
216
217
  	if (tps65910->chip_irq)
  		free_irq(tps65910->chip_irq, tps65910);
e3471bdc2   Graeme Gregory   TPS65910: IRQ: Ad...
218
219
  	return 0;
  }