Commit d49a0f3f14a763242b71244019d7881ee06e0658
Committed by
Samuel Ortiz
1 parent
36e52873c6
Exists in
master
and in
38 other branches
tps65912: irq: add interrupt controller
This module controls the interrupt handling for the tps65912. The interrupt sources can be the following: - GPIO - PWRON signal - PWRHOLD signal - Temperature detection Signed-off-by: Margarita Olaya Cabrera <magi@slimlogic.co.uk> Acked-by: Samuel Ortiz <sameo@linux.intel.com> Acked-by: Liam Girdwood <lrg@ti.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Showing 4 changed files with 243 additions and 1 deletions Side-by-side Diff
drivers/mfd/Makefile
... | ... | @@ -36,7 +36,7 @@ |
36 | 36 | obj-$(CONFIG_TPS6105X) += tps6105x.o |
37 | 37 | obj-$(CONFIG_TPS65010) += tps65010.o |
38 | 38 | obj-$(CONFIG_TPS6507X) += tps6507x.o |
39 | -tps65912-objs := tps65912-core.o | |
39 | +tps65912-objs := tps65912-core.o tps65912-irq.o | |
40 | 40 | obj-$(CONFIG_MFD_TPS65912) += tps65912.o |
41 | 41 | obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o |
42 | 42 | obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o |
drivers/mfd/tps65912-core.c
... | ... | @@ -124,8 +124,16 @@ |
124 | 124 | int tps65912_device_init(struct tps65912 *tps65912) |
125 | 125 | { |
126 | 126 | struct tps65912_board *pmic_plat_data = tps65912->dev->platform_data; |
127 | + struct tps65912_platform_data *init_data; | |
127 | 128 | int ret, dcdc_avs, value; |
128 | 129 | |
130 | + init_data = kzalloc(sizeof(struct tps65912_platform_data), GFP_KERNEL); | |
131 | + if (init_data == NULL) | |
132 | + return -ENOMEM; | |
133 | + | |
134 | + init_data->irq = pmic_plat_data->irq; | |
135 | + init_data->irq_base = pmic_plat_data->irq; | |
136 | + | |
129 | 137 | mutex_init(&tps65912->io_mutex); |
130 | 138 | dev_set_drvdata(tps65912->dev, tps65912); |
131 | 139 | |
132 | 140 | |
... | ... | @@ -145,9 +153,14 @@ |
145 | 153 | if (ret < 0) |
146 | 154 | goto err; |
147 | 155 | |
156 | + ret = tps65912_irq_init(tps65912, init_data->irq, init_data); | |
157 | + if (ret < 0) | |
158 | + goto err; | |
159 | + | |
148 | 160 | return ret; |
149 | 161 | |
150 | 162 | err: |
163 | + kfree(init_data); | |
151 | 164 | mfd_remove_devices(tps65912->dev); |
152 | 165 | kfree(tps65912); |
153 | 166 | return ret; |
drivers/mfd/tps65912-irq.c
1 | +/* | |
2 | + * tps65912-irq.c -- TI TPS6591x | |
3 | + * | |
4 | + * Copyright 2011 Texas Instruments Inc. | |
5 | + * | |
6 | + * Author: Margarita Olaya <magi@slimlogic.co.uk> | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify it | |
9 | + * under the terms of the GNU General Public License as published by the | |
10 | + * Free Software Foundation; either version 2 of the License, or (at your | |
11 | + * option) any later version. | |
12 | + * | |
13 | + * This driver is based on wm8350 implementation. | |
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/tps65912.h> | |
25 | + | |
26 | +static inline int irq_to_tps65912_irq(struct tps65912 *tps65912, | |
27 | + int irq) | |
28 | +{ | |
29 | + return irq - tps65912->irq_base; | |
30 | +} | |
31 | + | |
32 | +/* | |
33 | + * This is a threaded IRQ handler so can access I2C/SPI. Since the | |
34 | + * IRQ handler explicitly clears the IRQ it handles the IRQ line | |
35 | + * will be reasserted and the physical IRQ will be handled again if | |
36 | + * another interrupt is asserted while we run - in the normal course | |
37 | + * of events this is a rare occurrence so we save I2C/SPI reads. We're | |
38 | + * also assuming that it's rare to get lots of interrupts firing | |
39 | + * simultaneously so try to minimise I/O. | |
40 | + */ | |
41 | +static irqreturn_t tps65912_irq(int irq, void *irq_data) | |
42 | +{ | |
43 | + struct tps65912 *tps65912 = irq_data; | |
44 | + u32 irq_sts; | |
45 | + u32 irq_mask; | |
46 | + u8 reg; | |
47 | + int i; | |
48 | + | |
49 | + | |
50 | + tps65912->read(tps65912, TPS65912_INT_STS, 1, ®); | |
51 | + irq_sts = reg; | |
52 | + tps65912->read(tps65912, TPS65912_INT_STS2, 1, ®); | |
53 | + irq_sts |= reg << 8; | |
54 | + tps65912->read(tps65912, TPS65912_INT_STS3, 1, ®); | |
55 | + irq_sts |= reg << 16; | |
56 | + tps65912->read(tps65912, TPS65912_INT_STS4, 1, ®); | |
57 | + irq_sts |= reg << 24; | |
58 | + | |
59 | + tps65912->read(tps65912, TPS65912_INT_MSK, 1, ®); | |
60 | + irq_mask = reg; | |
61 | + tps65912->read(tps65912, TPS65912_INT_MSK2, 1, ®); | |
62 | + irq_mask |= reg << 8; | |
63 | + tps65912->read(tps65912, TPS65912_INT_MSK3, 1, ®); | |
64 | + irq_mask |= reg << 16; | |
65 | + tps65912->read(tps65912, TPS65912_INT_MSK4, 1, ®); | |
66 | + irq_mask |= reg << 24; | |
67 | + | |
68 | + irq_sts &= ~irq_mask; | |
69 | + if (!irq_sts) | |
70 | + return IRQ_NONE; | |
71 | + | |
72 | + for (i = 0; i < tps65912->irq_num; i++) { | |
73 | + if (!(irq_sts & (1 << i))) | |
74 | + continue; | |
75 | + | |
76 | + handle_nested_irq(tps65912->irq_base + i); | |
77 | + } | |
78 | + | |
79 | + /* Write the STS register back to clear IRQs we handled */ | |
80 | + reg = irq_sts & 0xFF; | |
81 | + irq_sts >>= 8; | |
82 | + if (reg) | |
83 | + tps65912->write(tps65912, TPS65912_INT_STS, 1, ®); | |
84 | + reg = irq_sts & 0xFF; | |
85 | + irq_sts >>= 8; | |
86 | + if (reg) | |
87 | + tps65912->write(tps65912, TPS65912_INT_STS2, 1, ®); | |
88 | + reg = irq_sts & 0xFF; | |
89 | + irq_sts >>= 8; | |
90 | + if (reg) | |
91 | + tps65912->write(tps65912, TPS65912_INT_STS3, 1, ®); | |
92 | + reg = irq_sts & 0xFF; | |
93 | + if (reg) | |
94 | + tps65912->write(tps65912, TPS65912_INT_STS4, 1, ®); | |
95 | + | |
96 | + return IRQ_HANDLED; | |
97 | +} | |
98 | + | |
99 | +static void tps65912_irq_lock(struct irq_data *data) | |
100 | +{ | |
101 | + struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data); | |
102 | + | |
103 | + mutex_lock(&tps65912->irq_lock); | |
104 | +} | |
105 | + | |
106 | +static void tps65912_irq_sync_unlock(struct irq_data *data) | |
107 | +{ | |
108 | + struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data); | |
109 | + u32 reg_mask; | |
110 | + u8 reg; | |
111 | + | |
112 | + tps65912->read(tps65912, TPS65912_INT_MSK, 1, ®); | |
113 | + reg_mask = reg; | |
114 | + tps65912->read(tps65912, TPS65912_INT_MSK2, 1, ®); | |
115 | + reg_mask |= reg << 8; | |
116 | + tps65912->read(tps65912, TPS65912_INT_MSK3, 1, ®); | |
117 | + reg_mask |= reg << 16; | |
118 | + tps65912->read(tps65912, TPS65912_INT_MSK4, 1, ®); | |
119 | + reg_mask |= reg << 24; | |
120 | + | |
121 | + if (tps65912->irq_mask != reg_mask) { | |
122 | + reg = tps65912->irq_mask & 0xFF; | |
123 | + tps65912->write(tps65912, TPS65912_INT_MSK, 1, ®); | |
124 | + reg = tps65912->irq_mask >> 8 & 0xFF; | |
125 | + tps65912->write(tps65912, TPS65912_INT_MSK2, 1, ®); | |
126 | + reg = tps65912->irq_mask >> 16 & 0xFF; | |
127 | + tps65912->write(tps65912, TPS65912_INT_MSK3, 1, ®); | |
128 | + reg = tps65912->irq_mask >> 24 & 0xFF; | |
129 | + tps65912->write(tps65912, TPS65912_INT_MSK4, 1, ®); | |
130 | + } | |
131 | + | |
132 | + mutex_unlock(&tps65912->irq_lock); | |
133 | +} | |
134 | + | |
135 | +static void tps65912_irq_enable(struct irq_data *data) | |
136 | +{ | |
137 | + struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data); | |
138 | + | |
139 | + tps65912->irq_mask &= ~(1 << irq_to_tps65912_irq(tps65912, data->irq)); | |
140 | +} | |
141 | + | |
142 | +static void tps65912_irq_disable(struct irq_data *data) | |
143 | +{ | |
144 | + struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data); | |
145 | + | |
146 | + tps65912->irq_mask |= (1 << irq_to_tps65912_irq(tps65912, data->irq)); | |
147 | +} | |
148 | + | |
149 | +static struct irq_chip tps65912_irq_chip = { | |
150 | + .name = "tps65912", | |
151 | + .irq_bus_lock = tps65912_irq_lock, | |
152 | + .irq_bus_sync_unlock = tps65912_irq_sync_unlock, | |
153 | + .irq_disable = tps65912_irq_disable, | |
154 | + .irq_enable = tps65912_irq_enable, | |
155 | +}; | |
156 | + | |
157 | +int tps65912_irq_init(struct tps65912 *tps65912, int irq, | |
158 | + struct tps65912_platform_data *pdata) | |
159 | +{ | |
160 | + int ret, cur_irq; | |
161 | + int flags = IRQF_ONESHOT; | |
162 | + u8 reg; | |
163 | + | |
164 | + if (!irq) { | |
165 | + dev_warn(tps65912->dev, "No interrupt support, no core IRQ\n"); | |
166 | + return 0; | |
167 | + } | |
168 | + | |
169 | + if (!pdata || !pdata->irq_base) { | |
170 | + dev_warn(tps65912->dev, "No interrupt support, no IRQ base\n"); | |
171 | + return 0; | |
172 | + } | |
173 | + | |
174 | + /* Clear unattended interrupts */ | |
175 | + tps65912->read(tps65912, TPS65912_INT_STS, 1, ®); | |
176 | + tps65912->write(tps65912, TPS65912_INT_STS, 1, ®); | |
177 | + tps65912->read(tps65912, TPS65912_INT_STS2, 1, ®); | |
178 | + tps65912->write(tps65912, TPS65912_INT_STS2, 1, ®); | |
179 | + tps65912->read(tps65912, TPS65912_INT_STS3, 1, ®); | |
180 | + tps65912->write(tps65912, TPS65912_INT_STS3, 1, ®); | |
181 | + tps65912->read(tps65912, TPS65912_INT_STS4, 1, ®); | |
182 | + tps65912->write(tps65912, TPS65912_INT_STS4, 1, ®); | |
183 | + | |
184 | + /* Mask top level interrupts */ | |
185 | + tps65912->irq_mask = 0xFFFFFFFF; | |
186 | + | |
187 | + mutex_init(&tps65912->irq_lock); | |
188 | + tps65912->chip_irq = irq; | |
189 | + tps65912->irq_base = pdata->irq_base; | |
190 | + | |
191 | + tps65912->irq_num = TPS65912_NUM_IRQ; | |
192 | + | |
193 | + /* Register with genirq */ | |
194 | + for (cur_irq = tps65912->irq_base; | |
195 | + cur_irq < tps65912->irq_num + tps65912->irq_base; | |
196 | + cur_irq++) { | |
197 | + irq_set_chip_data(cur_irq, tps65912); | |
198 | + irq_set_chip_and_handler(cur_irq, &tps65912_irq_chip, | |
199 | + handle_edge_irq); | |
200 | + irq_set_nested_thread(cur_irq, 1); | |
201 | + /* ARM needs us to explicitly flag the IRQ as valid | |
202 | + * and will set them noprobe when we do so. */ | |
203 | +#ifdef CONFIG_ARM | |
204 | + set_irq_flags(cur_irq, IRQF_VALID); | |
205 | +#else | |
206 | + irq_set_noprobe(cur_irq); | |
207 | +#endif | |
208 | + } | |
209 | + | |
210 | + ret = request_threaded_irq(irq, NULL, tps65912_irq, flags, | |
211 | + "tps65912", tps65912); | |
212 | + | |
213 | + irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); | |
214 | + if (ret != 0) | |
215 | + dev_err(tps65912->dev, "Failed to request IRQ: %d\n", ret); | |
216 | + | |
217 | + return ret; | |
218 | +} | |
219 | + | |
220 | +int tps65912_irq_exit(struct tps65912 *tps65912) | |
221 | +{ | |
222 | + free_irq(tps65912->chip_irq, tps65912); | |
223 | + return 0; | |
224 | +} |
include/linux/mfd/tps65912.h
... | ... | @@ -273,6 +273,8 @@ |
273 | 273 | int is_dcdc2_avs; |
274 | 274 | int is_dcdc3_avs; |
275 | 275 | int is_dcdc4_avs; |
276 | + int irq; | |
277 | + int irq_base; | |
276 | 278 | struct regulator_init_data *tps65912_pmic_init_data; |
277 | 279 | }; |
278 | 280 | |
... | ... | @@ -306,6 +308,7 @@ |
306 | 308 | }; |
307 | 309 | |
308 | 310 | struct tps65912_platform_data { |
311 | + int irq; | |
309 | 312 | int irq_base; |
310 | 313 | }; |
311 | 314 | |
... | ... | @@ -317,6 +320,8 @@ |
317 | 320 | int tps65912_reg_write(struct tps65912 *tps65912, u8 reg, u8 val); |
318 | 321 | int tps65912_device_init(struct tps65912 *tps65912); |
319 | 322 | void tps65912_device_exit(struct tps65912 *tps65912); |
323 | +int tps65912_irq_init(struct tps65912 *tps65912, int irq, | |
324 | + struct tps65912_platform_data *pdata); | |
320 | 325 | |
321 | 326 | #endif /* __LINUX_MFD_TPS65912_H */ |