diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/mfd/tps65910-irq.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/mfd/tps65910-irq.c')
-rw-r--r-- | drivers/mfd/tps65910-irq.c | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/drivers/mfd/tps65910-irq.c b/drivers/mfd/tps65910-irq.c new file mode 100644 index 00000000000..c9ed5c00a62 --- /dev/null +++ b/drivers/mfd/tps65910-irq.c | |||
@@ -0,0 +1,232 @@ | |||
1 | /* | ||
2 | * tps65910-irq.c -- TI TPS6591x | ||
3 | * | ||
4 | * Copyright 2010 Texas Instruments Inc. | ||
5 | * | ||
6 | * Author: Graeme Gregory <gg@slimlogic.co.uk> | ||
7 | * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk> | ||
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; | ||
44 | u32 irq_sts; | ||
45 | u32 irq_mask; | ||
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; | ||
53 | switch (tps65910_chip_id(tps65910)) { | ||
54 | case TPS65911: | ||
55 | tps65910->read(tps65910, TPS65910_INT_STS3, 1, ®); | ||
56 | irq_sts |= reg << 16; | ||
57 | } | ||
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; | ||
63 | switch (tps65910_chip_id(tps65910)) { | ||
64 | case TPS65911: | ||
65 | tps65910->read(tps65910, TPS65910_INT_MSK3, 1, ®); | ||
66 | irq_mask |= reg << 16; | ||
67 | } | ||
68 | |||
69 | irq_sts &= ~irq_mask; | ||
70 | |||
71 | if (!irq_sts) | ||
72 | return IRQ_NONE; | ||
73 | |||
74 | for (i = 0; i < tps65910->irq_num; i++) { | ||
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; | ||
84 | irq_sts >>= 8; | ||
85 | tps65910->write(tps65910, TPS65910_INT_STS, 1, ®); | ||
86 | reg = irq_sts & 0xFF; | ||
87 | tps65910->write(tps65910, TPS65910_INT_STS2, 1, ®); | ||
88 | switch (tps65910_chip_id(tps65910)) { | ||
89 | case TPS65911: | ||
90 | reg = irq_sts >> 8; | ||
91 | tps65910->write(tps65910, TPS65910_INT_STS3, 1, ®); | ||
92 | } | ||
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); | ||
107 | u32 reg_mask; | ||
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; | ||
114 | switch (tps65910_chip_id(tps65910)) { | ||
115 | case TPS65911: | ||
116 | tps65910->read(tps65910, TPS65910_INT_MSK3, 1, ®); | ||
117 | reg_mask |= reg << 16; | ||
118 | } | ||
119 | |||
120 | if (tps65910->irq_mask != reg_mask) { | ||
121 | reg = tps65910->irq_mask & 0xFF; | ||
122 | tps65910->write(tps65910, TPS65910_INT_MSK, 1, ®); | ||
123 | reg = tps65910->irq_mask >> 8 & 0xFF; | ||
124 | tps65910->write(tps65910, TPS65910_INT_MSK2, 1, ®); | ||
125 | switch (tps65910_chip_id(tps65910)) { | ||
126 | case TPS65911: | ||
127 | reg = tps65910->irq_mask >> 16; | ||
128 | tps65910->write(tps65910, TPS65910_INT_MSK3, 1, ®); | ||
129 | } | ||
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 | |||
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 | |||
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, | ||
164 | .irq_set_wake = tps65910_irq_set_wake, | ||
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; | ||
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 | |||
183 | tps65910->irq_mask = 0xFFFFFF; | ||
184 | |||
185 | mutex_init(&tps65910->irq_lock); | ||
186 | tps65910->chip_irq = irq; | ||
187 | tps65910->irq_base = pdata->irq_base; | ||
188 | |||
189 | switch (tps65910_chip_id(tps65910)) { | ||
190 | case TPS65910: | ||
191 | tps65910->irq_num = TPS65910_NUM_IRQ; | ||
192 | break; | ||
193 | case TPS65911: | ||
194 | tps65910->irq_num = TPS65911_NUM_IRQ; | ||
195 | break; | ||
196 | } | ||
197 | |||
198 | /* Register with genirq */ | ||
199 | for (cur_irq = tps65910->irq_base; | ||
200 | cur_irq < tps65910->irq_num + tps65910->irq_base; | ||
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); | ||
218 | |||
219 | irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); | ||
220 | |||
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 | { | ||
229 | if (tps65910->chip_irq) | ||
230 | free_irq(tps65910->chip_irq, tps65910); | ||
231 | return 0; | ||
232 | } | ||