diff options
Diffstat (limited to 'meta/recipes-kernel/linux/linux-omap2-git/beagleboard/TWL4030-05.patch')
-rw-r--r-- | meta/recipes-kernel/linux/linux-omap2-git/beagleboard/TWL4030-05.patch | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/meta/recipes-kernel/linux/linux-omap2-git/beagleboard/TWL4030-05.patch b/meta/recipes-kernel/linux/linux-omap2-git/beagleboard/TWL4030-05.patch new file mode 100644 index 0000000000..02a72ed9df --- /dev/null +++ b/meta/recipes-kernel/linux/linux-omap2-git/beagleboard/TWL4030-05.patch | |||
@@ -0,0 +1,303 @@ | |||
1 | TWL4030: use *_SIH_CTRL.COR bit to determine whether to read or write ISR to clear | ||
2 | |||
3 | From: Paul Walmsley <paul@pwsan.com> | ||
4 | |||
5 | TWL4030 interrupt status register bits can be cleared in one of two ways: | ||
6 | either by reading from the register, or by writing a 1 to the | ||
7 | appropriate bit(s) in the register. This behavior can be altered at any | ||
8 | time by the <twlmodule>_SIH_CTRL.COR register bit ("clear-on-read"). | ||
9 | |||
10 | The TWL4030 TRM is deeply confused as to whether COR=1 means that the | ||
11 | registers are cleared on reads, or cleared on writes. Peter De | ||
12 | Schrijver <peter.de-schrijver> confirms that COR=1 means that the registers | ||
13 | are cleared on read. | ||
14 | |||
15 | So, for each TWL4030 SIH, check the value of the *_SIH_CTRL.COR bit, and if | ||
16 | it is 1, use reads to clear the ISRs; if it is 0, use writes. | ||
17 | |||
18 | Also, use WARN_ON() to warn if the read/write failed, and don't skip | ||
19 | the rest of the initialization on failure either. | ||
20 | |||
21 | Thanks to Peter for his help with this patch. | ||
22 | |||
23 | |||
24 | Signed-off-by: Paul Walmsley <paul@pwsan.com> | ||
25 | --- | ||
26 | |||
27 | drivers/i2c/chips/twl4030-core.c | 183 ++++++++++++++++++++++---------------- | ||
28 | 1 files changed, 106 insertions(+), 77 deletions(-) | ||
29 | |||
30 | diff --git a/drivers/i2c/chips/twl4030-core.c b/drivers/i2c/chips/twl4030-core.c | ||
31 | index 9d93524..eae0634 100644 | ||
32 | --- a/drivers/i2c/chips/twl4030-core.c | ||
33 | +++ b/drivers/i2c/chips/twl4030-core.c | ||
34 | @@ -133,6 +133,16 @@ | ||
35 | /* on I2C-1 for 2430SDP */ | ||
36 | #define CONFIG_I2C_TWL4030_ID 1 | ||
37 | |||
38 | +/* SIH_CTRL registers */ | ||
39 | +#define TWL4030_INT_PWR_SIH_CTRL 0x07 | ||
40 | +#define TWL4030_INTERRUPTS_BCISIHCTRL 0x0d | ||
41 | +#define TWL4030_MADC_MADC_SIH_CTRL 0x67 | ||
42 | +#define TWL4030_KEYPAD_KEYP_SIH_CTRL 0x17 | ||
43 | +#define TWL4030_GPIO_GPIO_SIH_CTRL 0x2d | ||
44 | + | ||
45 | +#define TWL4030_SIH_CTRL_COR_MASK (1 << 2) | ||
46 | + | ||
47 | + | ||
48 | /* Helper functions */ | ||
49 | static int | ||
50 | twl4030_detect_client(struct i2c_adapter *adapter, unsigned char sid); | ||
51 | @@ -712,13 +722,61 @@ static int power_companion_init(void) | ||
52 | return e; | ||
53 | } | ||
54 | |||
55 | +/** | ||
56 | + * twl4030_i2c_clear_isr - clear TWL4030 SIH ISR regs via read + write | ||
57 | + * @mod_no: TWL4030 module number | ||
58 | + * @reg: register index to clear | ||
59 | + * @cor: value of the <module>_SIH_CTRL.COR bit (1 or 0) | ||
60 | + * | ||
61 | + * Either reads (cor == 1) or writes (cor == 0) to a TWL4030 interrupt | ||
62 | + * status register to ensure that any prior interrupts are cleared. | ||
63 | + * Returns the status from the I2C read operation. | ||
64 | + */ | ||
65 | +static int twl4030_i2c_clear_isr(u8 mod_no, u8 reg, u8 cor) | ||
66 | +{ | ||
67 | + u8 tmp; | ||
68 | + | ||
69 | + return (cor) ? twl4030_i2c_read_u8(mod_no, &tmp, reg) : | ||
70 | + twl4030_i2c_write_u8(mod_no, 0xff, reg); | ||
71 | +} | ||
72 | + | ||
73 | +/** | ||
74 | + * twl4030_read_cor_bit - are TWL module ISRs cleared by reads or writes? | ||
75 | + * @mod_no: TWL4030 module number | ||
76 | + * @reg: register index to clear | ||
77 | + * | ||
78 | + * Returns 1 if the TWL4030 SIH interrupt status registers (ISRs) for | ||
79 | + * the specified TWL module are cleared by reads, or 0 if cleared by | ||
80 | + * writes. | ||
81 | + */ | ||
82 | +static int twl4030_read_cor_bit(u8 mod_no, u8 reg) | ||
83 | +{ | ||
84 | + u8 tmp = 0; | ||
85 | + | ||
86 | + WARN_ON(twl4030_i2c_read_u8(mod_no, &tmp, reg) < 0); | ||
87 | + | ||
88 | + tmp &= TWL4030_SIH_CTRL_COR_MASK; | ||
89 | + tmp >>= __ffs(TWL4030_SIH_CTRL_COR_MASK); | ||
90 | + | ||
91 | + return tmp; | ||
92 | +} | ||
93 | + | ||
94 | static void twl_init_irq(void) | ||
95 | { | ||
96 | int i = 0; | ||
97 | int res = 0; | ||
98 | + int cor; | ||
99 | char *msg = "Unable to register interrupt subsystem"; | ||
100 | unsigned int irq_num; | ||
101 | |||
102 | + /* | ||
103 | + * For each TWL4030 module with ISR/IMR registers, mask all | ||
104 | + * interrupts and then clear any existing interrupt status bits, | ||
105 | + * since we initially do not have any TWL4030 module interrupt | ||
106 | + * handlers present. | ||
107 | + */ | ||
108 | + | ||
109 | + | ||
110 | /* PWR_IMR1 */ | ||
111 | res = twl4030_i2c_write_u8(TWL4030_MODULE_INT, 0xFF, 0x1); | ||
112 | if (res < 0) { | ||
113 | @@ -734,20 +792,18 @@ static void twl_init_irq(void) | ||
114 | } | ||
115 | |||
116 | /* Clear off any other pending interrupts on power */ | ||
117 | + | ||
118 | + /* Are PWR interrupt status bits cleared by reads or writes? */ | ||
119 | + cor = twl4030_read_cor_bit(TWL4030_MODULE_INT, | ||
120 | + TWL4030_INT_PWR_SIH_CTRL); | ||
121 | + WARN_ON(cor < 0); | ||
122 | + | ||
123 | /* PWR_ISR1 */ | ||
124 | - res = twl4030_i2c_write_u8(TWL4030_MODULE_INT, 0xFF, 0x00); | ||
125 | - if (res < 0) { | ||
126 | - pr_err("%s[%d][%d]\n", msg, res, __LINE__); | ||
127 | - return; | ||
128 | - } | ||
129 | + WARN_ON(twl4030_i2c_clear_isr(TWL4030_MODULE_INT, 0x00, cor) < 0); | ||
130 | |||
131 | /* PWR_ISR2 */ | ||
132 | - res = twl4030_i2c_write_u8(TWL4030_MODULE_INT, 0xFF, 0x02); | ||
133 | - if (res < 0) { | ||
134 | - pr_err("%s[%d][%d]\n", msg, res, __LINE__); | ||
135 | - return; | ||
136 | - } | ||
137 | - /* POWER HACK (END) */ | ||
138 | + WARN_ON(twl4030_i2c_clear_isr(TWL4030_MODULE_INT, 0x02, cor) < 0); | ||
139 | + | ||
140 | /* Slave address 0x4A */ | ||
141 | |||
142 | /* BCIIMR1A */ | ||
143 | @@ -778,33 +834,22 @@ static void twl_init_irq(void) | ||
144 | return; | ||
145 | } | ||
146 | |||
147 | + /* Are BCI interrupt status bits cleared by reads or writes? */ | ||
148 | + cor = twl4030_read_cor_bit(TWL4030_MODULE_INTERRUPTS, | ||
149 | + TWL4030_INTERRUPTS_BCISIHCTRL); | ||
150 | + WARN_ON(cor < 0); | ||
151 | + | ||
152 | /* BCIISR1A */ | ||
153 | - res = twl4030_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xFF, 0x0); | ||
154 | - if (res < 0) { | ||
155 | - pr_err("%s[%d][%d]\n", msg, res, __LINE__); | ||
156 | - return; | ||
157 | - } | ||
158 | + WARN_ON(twl4030_i2c_clear_isr(TWL4030_MODULE_INTERRUPTS, 0x0, cor) < 0); | ||
159 | |||
160 | /* BCIISR2A */ | ||
161 | - res = twl4030_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xFF, 0x1); | ||
162 | - if (res < 0) { | ||
163 | - pr_err("%s[%d][%d]\n", msg, res, __LINE__); | ||
164 | - return; | ||
165 | - } | ||
166 | + WARN_ON(twl4030_i2c_clear_isr(TWL4030_MODULE_INTERRUPTS, 0x1, cor) < 0); | ||
167 | |||
168 | /* BCIISR1B */ | ||
169 | - res = twl4030_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xFF, 0x4); | ||
170 | - if (res < 0) { | ||
171 | - pr_err("%s[%d][%d]\n", msg, res, __LINE__); | ||
172 | - return; | ||
173 | - } | ||
174 | + WARN_ON(twl4030_i2c_clear_isr(TWL4030_MODULE_INTERRUPTS, 0x4, cor) < 0); | ||
175 | |||
176 | /* BCIISR2B */ | ||
177 | - res = twl4030_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xFF, 0x5); | ||
178 | - if (res < 0) { | ||
179 | - pr_err("%s[%d][%d]\n", msg, res, __LINE__); | ||
180 | - return; | ||
181 | - } | ||
182 | + WARN_ON(twl4030_i2c_clear_isr(TWL4030_MODULE_INTERRUPTS, 0x5, cor) < 0); | ||
183 | |||
184 | /* MAD C */ | ||
185 | /* MADC_IMR1 */ | ||
186 | @@ -821,19 +866,16 @@ static void twl_init_irq(void) | ||
187 | return; | ||
188 | } | ||
189 | |||
190 | + /* Are MADC interrupt status bits cleared by reads or writes? */ | ||
191 | + cor = twl4030_read_cor_bit(TWL4030_MODULE_MADC, | ||
192 | + TWL4030_MADC_MADC_SIH_CTRL); | ||
193 | + WARN_ON(cor < 0); | ||
194 | + | ||
195 | /* MADC_ISR1 */ | ||
196 | - res = twl4030_i2c_write_u8(TWL4030_MODULE_MADC, 0xFF, 0x61); | ||
197 | - if (res < 0) { | ||
198 | - pr_err("%s[%d][%d]\n", msg, res, __LINE__); | ||
199 | - return; | ||
200 | - } | ||
201 | + WARN_ON(twl4030_i2c_clear_isr(TWL4030_MODULE_MADC, 0x61, cor) < 0); | ||
202 | |||
203 | /* MADC_ISR2 */ | ||
204 | - res = twl4030_i2c_write_u8(TWL4030_MODULE_MADC, 0xFF, 0x63); | ||
205 | - if (res < 0) { | ||
206 | - pr_err("%s[%d][%d]\n", msg, res, __LINE__); | ||
207 | - return; | ||
208 | - } | ||
209 | + WARN_ON(twl4030_i2c_clear_isr(TWL4030_MODULE_MADC, 0x63, cor) < 0); | ||
210 | |||
211 | /* key Pad */ | ||
212 | /* KEYPAD - IMR1 */ | ||
213 | @@ -842,12 +884,15 @@ static void twl_init_irq(void) | ||
214 | pr_err("%s[%d][%d]\n", msg, res, __LINE__); | ||
215 | return; | ||
216 | } | ||
217 | - { | ||
218 | - u8 clear; | ||
219 | - /* Clear ISR */ | ||
220 | - twl4030_i2c_read_u8(TWL4030_MODULE_KEYPAD, &clear, 0x11); | ||
221 | - twl4030_i2c_read_u8(TWL4030_MODULE_KEYPAD, &clear, 0x11); | ||
222 | - } | ||
223 | + | ||
224 | + /* Are keypad interrupt status bits cleared by reads or writes? */ | ||
225 | + cor = twl4030_read_cor_bit(TWL4030_MODULE_KEYPAD, | ||
226 | + TWL4030_KEYPAD_KEYP_SIH_CTRL); | ||
227 | + WARN_ON(cor < 0); | ||
228 | + | ||
229 | + /* KEYPAD - ISR1 */ | ||
230 | + /* XXX does this still need to be done twice for some reason? */ | ||
231 | + WARN_ON(twl4030_i2c_clear_isr(TWL4030_MODULE_KEYPAD, 0x11, cor) < 0); | ||
232 | |||
233 | /* KEYPAD - IMR2 */ | ||
234 | res = twl4030_i2c_write_u8(TWL4030_MODULE_KEYPAD, 0xFF, (0x14)); | ||
235 | @@ -856,6 +901,9 @@ static void twl_init_irq(void) | ||
236 | return; | ||
237 | } | ||
238 | |||
239 | + /* KEYPAD - ISR2 */ | ||
240 | + WARN_ON(twl4030_i2c_clear_isr(TWL4030_MODULE_KEYPAD, 0x13, cor) < 0); | ||
241 | + | ||
242 | /* Slave address 0x49 */ | ||
243 | /* GPIO_IMR1A */ | ||
244 | res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xFF, (0x1C)); | ||
245 | @@ -899,47 +947,28 @@ static void twl_init_irq(void) | ||
246 | return; | ||
247 | } | ||
248 | |||
249 | + /* Are GPIO interrupt status bits cleared by reads or writes? */ | ||
250 | + cor = twl4030_read_cor_bit(TWL4030_MODULE_GPIO, | ||
251 | + TWL4030_GPIO_GPIO_SIH_CTRL); | ||
252 | + WARN_ON(cor < 0); | ||
253 | + | ||
254 | /* GPIO_ISR1A */ | ||
255 | - res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xff, 0x19); | ||
256 | - if (res < 0) { | ||
257 | - pr_err("%s[%d][%d]\n", msg, res, __LINE__); | ||
258 | - return; | ||
259 | - } | ||
260 | + WARN_ON(twl4030_i2c_clear_isr(TWL4030_MODULE_GPIO, 0x19, cor) < 0); | ||
261 | |||
262 | /* GPIO_ISR2A */ | ||
263 | - res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xff, 0x1a); | ||
264 | - if (res < 0) { | ||
265 | - pr_err("%s[%d][%d]\n", msg, res, __LINE__); | ||
266 | - return; | ||
267 | - } | ||
268 | + WARN_ON(twl4030_i2c_clear_isr(TWL4030_MODULE_GPIO, 0x1a, cor) < 0); | ||
269 | |||
270 | /* GPIO_ISR3A */ | ||
271 | - res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xff, 0x1b); | ||
272 | - if (res < 0) { | ||
273 | - pr_err("%s[%d][%d]\n", msg, res, __LINE__); | ||
274 | - return; | ||
275 | - } | ||
276 | + WARN_ON(twl4030_i2c_clear_isr(TWL4030_MODULE_GPIO, 0x1b, cor) < 0); | ||
277 | |||
278 | /* GPIO_ISR1B */ | ||
279 | - res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xff, 0x1f); | ||
280 | - if (res < 0) { | ||
281 | - pr_err("%s[%d][%d]\n", msg, res, __LINE__); | ||
282 | - return; | ||
283 | - } | ||
284 | + WARN_ON(twl4030_i2c_clear_isr(TWL4030_MODULE_GPIO, 0x1f, cor) < 0); | ||
285 | |||
286 | /* GPIO_ISR2B */ | ||
287 | - res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xff, 0x20); | ||
288 | - if (res < 0) { | ||
289 | - pr_err("%s[%d][%d]\n", msg, res, __LINE__); | ||
290 | - return; | ||
291 | - } | ||
292 | + WARN_ON(twl4030_i2c_clear_isr(TWL4030_MODULE_GPIO, 0x20, cor) < 0); | ||
293 | |||
294 | /* GPIO_ISR3B */ | ||
295 | - res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xff, 0x21); | ||
296 | - if (res < 0) { | ||
297 | - pr_err("%s[%d][%d]\n", msg, res, __LINE__); | ||
298 | - return; | ||
299 | - } | ||
300 | + WARN_ON(twl4030_i2c_clear_isr(TWL4030_MODULE_GPIO, 0x21, cor) < 0); | ||
301 | |||
302 | /* install an irq handler for each of the PIH modules */ | ||
303 | for (i = TWL4030_IRQ_BASE; i < TWL4030_IRQ_END; i++) { | ||