]>
Commit | Line | Data |
---|---|---|
e3471bdc GG |
1 | /* |
2 | * tps65910-irq.c -- TI TPS6591x | |
3 | * | |
4 | * Copyright 2010 Texas Instruments Inc. | |
5 | * | |
6 | * Author: Graeme Gregory <[email protected]> | |
7 | * Author: Jorge Eduardo Candelaria <[email protected]> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify it | |
10 | * under the terms of the GNU General Public License as published by the | |
11 | * Free Software Foundation; either version 2 of the License, or (at your | |
12 | * option) any later version. | |
13 | * | |
14 | */ | |
15 | ||
16 | #include <linux/kernel.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/init.h> | |
19 | #include <linux/bug.h> | |
20 | #include <linux/device.h> | |
21 | #include <linux/interrupt.h> | |
22 | #include <linux/irq.h> | |
23 | #include <linux/gpio.h> | |
24 | #include <linux/mfd/tps65910.h> | |
25 | ||
26 | static inline int irq_to_tps65910_irq(struct tps65910 *tps65910, | |
27 | int irq) | |
28 | { | |
29 | return (irq - tps65910->irq_base); | |
30 | } | |
31 | ||
32 | /* | |
33 | * This is a threaded IRQ handler so can access I2C/SPI. Since all | |
34 | * interrupts are clear on read the IRQ line will be reasserted and | |
35 | * the physical IRQ will be handled again if another interrupt is | |
36 | * asserted while we run - in the normal course of events this is a | |
37 | * rare occurrence so we save I2C/SPI reads. We're also assuming that | |
38 | * it's rare to get lots of interrupts firing simultaneously so try to | |
39 | * minimise I/O. | |
40 | */ | |
41 | static irqreturn_t tps65910_irq(int irq, void *irq_data) | |
42 | { | |
43 | struct tps65910 *tps65910 = irq_data; | |
a2974732 JEC |
44 | u32 irq_sts; |
45 | u32 irq_mask; | |
e3471bdc GG |
46 | u8 reg; |
47 | int i; | |
48 | ||
49 | tps65910->read(tps65910, TPS65910_INT_STS, 1, ®); | |
50 | irq_sts = reg; | |
51 | tps65910->read(tps65910, TPS65910_INT_STS2, 1, ®); | |
52 | irq_sts |= reg << 8; | |
a2974732 JEC |
53 | switch (tps65910_chip_id(tps65910)) { |
54 | case TPS65911: | |
55 | tps65910->read(tps65910, TPS65910_INT_STS3, 1, ®); | |
56 | irq_sts |= reg << 16; | |
57 | } | |
e3471bdc GG |
58 | |
59 | tps65910->read(tps65910, TPS65910_INT_MSK, 1, ®); | |
60 | irq_mask = reg; | |
61 | tps65910->read(tps65910, TPS65910_INT_MSK2, 1, ®); | |
62 | irq_mask |= reg << 8; | |
a2974732 JEC |
63 | switch (tps65910_chip_id(tps65910)) { |
64 | case TPS65911: | |
65 | tps65910->read(tps65910, TPS65910_INT_MSK3, 1, ®); | |
66 | irq_mask |= reg << 16; | |
67 | } | |
e3471bdc GG |
68 | |
69 | irq_sts &= ~irq_mask; | |
70 | ||
71 | if (!irq_sts) | |
72 | return IRQ_NONE; | |
73 | ||
a2974732 | 74 | for (i = 0; i < tps65910->irq_num; i++) { |
e3471bdc GG |
75 | |
76 | if (!(irq_sts & (1 << i))) | |
77 | continue; | |
78 | ||
79 | handle_nested_irq(tps65910->irq_base + i); | |
80 | } | |
81 | ||
82 | /* Write the STS register back to clear IRQs we handled */ | |
83 | reg = irq_sts & 0xFF; | |
a2974732 | 84 | irq_sts >>= 8; |
e3471bdc | 85 | tps65910->write(tps65910, TPS65910_INT_STS, 1, ®); |
a2974732 | 86 | reg = irq_sts & 0xFF; |
e3471bdc | 87 | tps65910->write(tps65910, TPS65910_INT_STS2, 1, ®); |
a2974732 JEC |
88 | switch (tps65910_chip_id(tps65910)) { |
89 | case TPS65911: | |
90 | reg = irq_sts >> 8; | |
91 | tps65910->write(tps65910, TPS65910_INT_STS3, 1, ®); | |
92 | } | |
e3471bdc GG |
93 | |
94 | return IRQ_HANDLED; | |
95 | } | |
96 | ||
97 | static void tps65910_irq_lock(struct irq_data *data) | |
98 | { | |
99 | struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); | |
100 | ||
101 | mutex_lock(&tps65910->irq_lock); | |
102 | } | |
103 | ||
104 | static void tps65910_irq_sync_unlock(struct irq_data *data) | |
105 | { | |
106 | struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); | |
a2974732 | 107 | u32 reg_mask; |
e3471bdc GG |
108 | u8 reg; |
109 | ||
110 | tps65910->read(tps65910, TPS65910_INT_MSK, 1, ®); | |
111 | reg_mask = reg; | |
112 | tps65910->read(tps65910, TPS65910_INT_MSK2, 1, ®); | |
113 | reg_mask |= reg << 8; | |
a2974732 JEC |
114 | switch (tps65910_chip_id(tps65910)) { |
115 | case TPS65911: | |
116 | tps65910->read(tps65910, TPS65910_INT_MSK3, 1, ®); | |
117 | reg_mask |= reg << 16; | |
118 | } | |
e3471bdc GG |
119 | |
120 | if (tps65910->irq_mask != reg_mask) { | |
121 | reg = tps65910->irq_mask & 0xFF; | |
122 | tps65910->write(tps65910, TPS65910_INT_MSK, 1, ®); | |
a2974732 | 123 | reg = tps65910->irq_mask >> 8 & 0xFF; |
e3471bdc | 124 | tps65910->write(tps65910, TPS65910_INT_MSK2, 1, ®); |
a2974732 JEC |
125 | switch (tps65910_chip_id(tps65910)) { |
126 | case TPS65911: | |
127 | reg = tps65910->irq_mask >> 16; | |
128 | tps65910->write(tps65910, TPS65910_INT_MSK3, 1, ®); | |
129 | } | |
e3471bdc GG |
130 | } |
131 | mutex_unlock(&tps65910->irq_lock); | |
132 | } | |
133 | ||
134 | static void tps65910_irq_enable(struct irq_data *data) | |
135 | { | |
136 | struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); | |
137 | ||
138 | tps65910->irq_mask &= ~( 1 << irq_to_tps65910_irq(tps65910, data->irq)); | |
139 | } | |
140 | ||
141 | static void tps65910_irq_disable(struct irq_data *data) | |
142 | { | |
143 | struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); | |
144 | ||
145 | tps65910->irq_mask |= ( 1 << irq_to_tps65910_irq(tps65910, data->irq)); | |
146 | } | |
147 | ||
0dc299a3 LD |
148 | #ifdef CONFIG_PM_SLEEP |
149 | static int tps65910_irq_set_wake(struct irq_data *data, unsigned int enable) | |
150 | { | |
151 | struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); | |
152 | return irq_set_irq_wake(tps65910->chip_irq, enable); | |
153 | } | |
154 | #else | |
155 | #define tps65910_irq_set_wake NULL | |
156 | #endif | |
157 | ||
e3471bdc GG |
158 | static struct irq_chip tps65910_irq_chip = { |
159 | .name = "tps65910", | |
160 | .irq_bus_lock = tps65910_irq_lock, | |
161 | .irq_bus_sync_unlock = tps65910_irq_sync_unlock, | |
162 | .irq_disable = tps65910_irq_disable, | |
163 | .irq_enable = tps65910_irq_enable, | |
0dc299a3 | 164 | .irq_set_wake = tps65910_irq_set_wake, |
e3471bdc GG |
165 | }; |
166 | ||
167 | int tps65910_irq_init(struct tps65910 *tps65910, int irq, | |
168 | struct tps65910_platform_data *pdata) | |
169 | { | |
170 | int ret, cur_irq; | |
171 | int flags = IRQF_ONESHOT; | |
e3471bdc GG |
172 | |
173 | if (!irq) { | |
174 | dev_warn(tps65910->dev, "No interrupt support, no core IRQ\n"); | |
175 | return -EINVAL; | |
176 | } | |
177 | ||
178 | if (!pdata || !pdata->irq_base) { | |
179 | dev_warn(tps65910->dev, "No interrupt support, no IRQ base\n"); | |
180 | return -EINVAL; | |
181 | } | |
182 | ||
a2974732 | 183 | tps65910->irq_mask = 0xFFFFFF; |
e3471bdc GG |
184 | |
185 | mutex_init(&tps65910->irq_lock); | |
186 | tps65910->chip_irq = irq; | |
187 | tps65910->irq_base = pdata->irq_base; | |
188 | ||
a2974732 JEC |
189 | switch (tps65910_chip_id(tps65910)) { |
190 | case TPS65910: | |
191 | tps65910->irq_num = TPS65910_NUM_IRQ; | |
fa948761 | 192 | break; |
a2974732 JEC |
193 | case TPS65911: |
194 | tps65910->irq_num = TPS65911_NUM_IRQ; | |
fa948761 | 195 | break; |
a2974732 JEC |
196 | } |
197 | ||
e3471bdc GG |
198 | /* Register with genirq */ |
199 | for (cur_irq = tps65910->irq_base; | |
a2974732 | 200 | cur_irq < tps65910->irq_num + tps65910->irq_base; |
e3471bdc GG |
201 | cur_irq++) { |
202 | irq_set_chip_data(cur_irq, tps65910); | |
203 | irq_set_chip_and_handler(cur_irq, &tps65910_irq_chip, | |
204 | handle_edge_irq); | |
205 | irq_set_nested_thread(cur_irq, 1); | |
206 | ||
207 | /* ARM needs us to explicitly flag the IRQ as valid | |
208 | * and will set them noprobe when we do so. */ | |
209 | #ifdef CONFIG_ARM | |
210 | set_irq_flags(cur_irq, IRQF_VALID); | |
211 | #else | |
212 | irq_set_noprobe(cur_irq); | |
213 | #endif | |
214 | } | |
215 | ||
216 | ret = request_threaded_irq(irq, NULL, tps65910_irq, flags, | |
217 | "tps65910", tps65910); | |
a2974732 JEC |
218 | |
219 | irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); | |
220 | ||
e3471bdc GG |
221 | if (ret != 0) |
222 | dev_err(tps65910->dev, "Failed to request IRQ: %d\n", ret); | |
223 | ||
224 | return ret; | |
225 | } | |
226 | ||
227 | int tps65910_irq_exit(struct tps65910 *tps65910) | |
228 | { | |
1e351a95 AM |
229 | if (tps65910->chip_irq) |
230 | free_irq(tps65910->chip_irq, tps65910); | |
e3471bdc GG |
231 | return 0; |
232 | } |