diff options
author | Ilkka Koskinen <ilkka.koskinen@nokia.com> | 2009-11-10 10:26:15 -0500 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2009-12-13 13:21:41 -0500 |
commit | 1920a61e208fac73d1a30a7cf4005701802fe69f (patch) | |
tree | 2282d5b842d46f5b48144d9950a89a7b78fb930a /drivers/mfd | |
parent | 6a6127462eb9096419fd4b3115ec5971d83a600f (diff) |
mfd: Initial support for twl5031
TWL5031 introduces two new interrupts in PIH. Moreover, BCI
has changed remarkably and, thus, it's disabled when TWL5031
is in use.
Signed-off-by: Ilkka Koskinen <ilkka.koskinen@nokia.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/mfd')
-rw-r--r-- | drivers/mfd/twl4030-core.c | 13 | ||||
-rw-r--r-- | drivers/mfd/twl4030-irq.c | 131 |
2 files changed, 139 insertions, 5 deletions
diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c index 90a38e43c31..334c86fcced 100644 --- a/drivers/mfd/twl4030-core.c +++ b/drivers/mfd/twl4030-core.c | |||
@@ -158,6 +158,10 @@ | |||
158 | #define TWL4030_BASEADD_PWMB 0x00F1 | 158 | #define TWL4030_BASEADD_PWMB 0x00F1 |
159 | #define TWL4030_BASEADD_KEYPAD 0x00D2 | 159 | #define TWL4030_BASEADD_KEYPAD 0x00D2 |
160 | 160 | ||
161 | #define TWL5031_BASEADD_ACCESSORY 0x0074 /* Replaces Main Charge */ | ||
162 | #define TWL5031_BASEADD_INTERRUPTS 0x00B9 /* Different than TWL4030's | ||
163 | one */ | ||
164 | |||
161 | /* subchip/slave 3 - POWER ID */ | 165 | /* subchip/slave 3 - POWER ID */ |
162 | #define TWL4030_BASEADD_BACKUP 0x0014 | 166 | #define TWL4030_BASEADD_BACKUP 0x0014 |
163 | #define TWL4030_BASEADD_INT 0x002E | 167 | #define TWL4030_BASEADD_INT 0x002E |
@@ -189,6 +193,7 @@ | |||
189 | /* chip-specific feature flags, for i2c_device_id.driver_data */ | 193 | /* chip-specific feature flags, for i2c_device_id.driver_data */ |
190 | #define TWL4030_VAUX2 BIT(0) /* pre-5030 voltage ranges */ | 194 | #define TWL4030_VAUX2 BIT(0) /* pre-5030 voltage ranges */ |
191 | #define TPS_SUBSET BIT(1) /* tps659[23]0 have fewer LDOs */ | 195 | #define TPS_SUBSET BIT(1) /* tps659[23]0 have fewer LDOs */ |
196 | #define TWL5031 BIT(2) /* twl5031 has different registers */ | ||
192 | 197 | ||
193 | /*----------------------------------------------------------------------*/ | 198 | /*----------------------------------------------------------------------*/ |
194 | 199 | ||
@@ -241,6 +246,8 @@ static struct twl4030mapping twl4030_map[TWL4030_MODULE_LAST + 1] = { | |||
241 | { 2, TWL4030_BASEADD_PWM1 }, | 246 | { 2, TWL4030_BASEADD_PWM1 }, |
242 | { 2, TWL4030_BASEADD_PWMA }, | 247 | { 2, TWL4030_BASEADD_PWMA }, |
243 | { 2, TWL4030_BASEADD_PWMB }, | 248 | { 2, TWL4030_BASEADD_PWMB }, |
249 | { 2, TWL5031_BASEADD_ACCESSORY }, | ||
250 | { 2, TWL5031_BASEADD_INTERRUPTS }, | ||
244 | 251 | ||
245 | { 3, TWL4030_BASEADD_BACKUP }, | 252 | { 3, TWL4030_BASEADD_BACKUP }, |
246 | { 3, TWL4030_BASEADD_INT }, | 253 | { 3, TWL4030_BASEADD_INT }, |
@@ -488,7 +495,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) | |||
488 | { | 495 | { |
489 | struct device *child; | 496 | struct device *child; |
490 | 497 | ||
491 | if (twl_has_bci() && pdata->bci && !(features & TPS_SUBSET)) { | 498 | if (twl_has_bci() && pdata->bci && |
499 | !(features & (TPS_SUBSET | TWL5031))) { | ||
492 | child = add_child(3, "twl4030_bci", | 500 | child = add_child(3, "twl4030_bci", |
493 | pdata->bci, sizeof(*pdata->bci), | 501 | pdata->bci, sizeof(*pdata->bci), |
494 | false, | 502 | false, |
@@ -760,6 +768,7 @@ static void clocks_init(struct device *dev, | |||
760 | 768 | ||
761 | int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end); | 769 | int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end); |
762 | int twl_exit_irq(void); | 770 | int twl_exit_irq(void); |
771 | int twl_init_chip_irq(const char *chip); | ||
763 | 772 | ||
764 | static int twl4030_remove(struct i2c_client *client) | 773 | static int twl4030_remove(struct i2c_client *client) |
765 | { | 774 | { |
@@ -835,6 +844,7 @@ twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id) | |||
835 | if (client->irq | 844 | if (client->irq |
836 | && pdata->irq_base | 845 | && pdata->irq_base |
837 | && pdata->irq_end > pdata->irq_base) { | 846 | && pdata->irq_end > pdata->irq_base) { |
847 | twl_init_chip_irq(id->name); | ||
838 | status = twl_init_irq(client->irq, pdata->irq_base, pdata->irq_end); | 848 | status = twl_init_irq(client->irq, pdata->irq_base, pdata->irq_end); |
839 | if (status < 0) | 849 | if (status < 0) |
840 | goto fail; | 850 | goto fail; |
@@ -850,6 +860,7 @@ fail: | |||
850 | static const struct i2c_device_id twl4030_ids[] = { | 860 | static const struct i2c_device_id twl4030_ids[] = { |
851 | { "twl4030", TWL4030_VAUX2 }, /* "Triton 2" */ | 861 | { "twl4030", TWL4030_VAUX2 }, /* "Triton 2" */ |
852 | { "twl5030", 0 }, /* T2 updated */ | 862 | { "twl5030", 0 }, /* T2 updated */ |
863 | { "twl5031", TWL5031 }, /* TWL5030 updated */ | ||
853 | { "tps65950", 0 }, /* catalog version of twl5030 */ | 864 | { "tps65950", 0 }, /* catalog version of twl5030 */ |
854 | { "tps65930", TPS_SUBSET }, /* fewer LDOs and DACs; no charger */ | 865 | { "tps65930", TPS_SUBSET }, /* fewer LDOs and DACs; no charger */ |
855 | { "tps65920", TPS_SUBSET }, /* fewer LDOs; no codec or charger */ | 866 | { "tps65920", TPS_SUBSET }, /* fewer LDOs; no codec or charger */ |
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index fb194fe244c..0b733028828 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c | |||
@@ -74,6 +74,8 @@ struct sih { | |||
74 | u8 edr_offset; | 74 | u8 edr_offset; |
75 | u8 bytes_edr; /* bytelen of EDR */ | 75 | u8 bytes_edr; /* bytelen of EDR */ |
76 | 76 | ||
77 | u8 irq_lines; /* number of supported irq lines */ | ||
78 | |||
77 | /* SIR ignored -- set interrupt, for testing only */ | 79 | /* SIR ignored -- set interrupt, for testing only */ |
78 | struct irq_data { | 80 | struct irq_data { |
79 | u8 isr_offset; | 81 | u8 isr_offset; |
@@ -82,6 +84,9 @@ struct sih { | |||
82 | /* + 2 bytes padding */ | 84 | /* + 2 bytes padding */ |
83 | }; | 85 | }; |
84 | 86 | ||
87 | static const struct sih *sih_modules; | ||
88 | static int nr_sih_modules; | ||
89 | |||
85 | #define SIH_INITIALIZER(modname, nbits) \ | 90 | #define SIH_INITIALIZER(modname, nbits) \ |
86 | .module = TWL4030_MODULE_ ## modname, \ | 91 | .module = TWL4030_MODULE_ ## modname, \ |
87 | .control_offset = TWL4030_ ## modname ## _SIH_CTRL, \ | 92 | .control_offset = TWL4030_ ## modname ## _SIH_CTRL, \ |
@@ -89,6 +94,7 @@ struct sih { | |||
89 | .bytes_ixr = DIV_ROUND_UP(nbits, 8), \ | 94 | .bytes_ixr = DIV_ROUND_UP(nbits, 8), \ |
90 | .edr_offset = TWL4030_ ## modname ## _EDR, \ | 95 | .edr_offset = TWL4030_ ## modname ## _EDR, \ |
91 | .bytes_edr = DIV_ROUND_UP((2*(nbits)), 8), \ | 96 | .bytes_edr = DIV_ROUND_UP((2*(nbits)), 8), \ |
97 | .irq_lines = 2, \ | ||
92 | .mask = { { \ | 98 | .mask = { { \ |
93 | .isr_offset = TWL4030_ ## modname ## _ISR1, \ | 99 | .isr_offset = TWL4030_ ## modname ## _ISR1, \ |
94 | .imr_offset = TWL4030_ ## modname ## _IMR1, \ | 100 | .imr_offset = TWL4030_ ## modname ## _IMR1, \ |
@@ -107,7 +113,8 @@ struct sih { | |||
107 | /* Order in this table matches order in PIH_ISR. That is, | 113 | /* Order in this table matches order in PIH_ISR. That is, |
108 | * BIT(n) in PIH_ISR is sih_modules[n]. | 114 | * BIT(n) in PIH_ISR is sih_modules[n]. |
109 | */ | 115 | */ |
110 | static const struct sih sih_modules[6] = { | 116 | /* sih_modules_twl4030 is used both in twl4030 and twl5030 */ |
117 | static const struct sih sih_modules_twl4030[6] = { | ||
111 | [0] = { | 118 | [0] = { |
112 | .name = "gpio", | 119 | .name = "gpio", |
113 | .module = TWL4030_MODULE_GPIO, | 120 | .module = TWL4030_MODULE_GPIO, |
@@ -118,6 +125,7 @@ static const struct sih sih_modules[6] = { | |||
118 | /* Note: *all* of these IRQs default to no-trigger */ | 125 | /* Note: *all* of these IRQs default to no-trigger */ |
119 | .edr_offset = REG_GPIO_EDR1, | 126 | .edr_offset = REG_GPIO_EDR1, |
120 | .bytes_edr = 5, | 127 | .bytes_edr = 5, |
128 | .irq_lines = 2, | ||
121 | .mask = { { | 129 | .mask = { { |
122 | .isr_offset = REG_GPIO_ISR1A, | 130 | .isr_offset = REG_GPIO_ISR1A, |
123 | .imr_offset = REG_GPIO_IMR1A, | 131 | .imr_offset = REG_GPIO_IMR1A, |
@@ -140,6 +148,7 @@ static const struct sih sih_modules[6] = { | |||
140 | .edr_offset = TWL4030_INTERRUPTS_BCIEDR1, | 148 | .edr_offset = TWL4030_INTERRUPTS_BCIEDR1, |
141 | /* Note: most of these IRQs default to no-trigger */ | 149 | /* Note: most of these IRQs default to no-trigger */ |
142 | .bytes_edr = 3, | 150 | .bytes_edr = 3, |
151 | .irq_lines = 2, | ||
143 | .mask = { { | 152 | .mask = { { |
144 | .isr_offset = TWL4030_INTERRUPTS_BCIISR1A, | 153 | .isr_offset = TWL4030_INTERRUPTS_BCIISR1A, |
145 | .imr_offset = TWL4030_INTERRUPTS_BCIIMR1A, | 154 | .imr_offset = TWL4030_INTERRUPTS_BCIIMR1A, |
@@ -164,6 +173,99 @@ static const struct sih sih_modules[6] = { | |||
164 | /* there are no SIH modules #6 or #7 ... */ | 173 | /* there are no SIH modules #6 or #7 ... */ |
165 | }; | 174 | }; |
166 | 175 | ||
176 | static const struct sih sih_modules_twl5031[8] = { | ||
177 | [0] = { | ||
178 | .name = "gpio", | ||
179 | .module = TWL4030_MODULE_GPIO, | ||
180 | .control_offset = REG_GPIO_SIH_CTRL, | ||
181 | .set_cor = true, | ||
182 | .bits = TWL4030_GPIO_MAX, | ||
183 | .bytes_ixr = 3, | ||
184 | /* Note: *all* of these IRQs default to no-trigger */ | ||
185 | .edr_offset = REG_GPIO_EDR1, | ||
186 | .bytes_edr = 5, | ||
187 | .irq_lines = 2, | ||
188 | .mask = { { | ||
189 | .isr_offset = REG_GPIO_ISR1A, | ||
190 | .imr_offset = REG_GPIO_IMR1A, | ||
191 | }, { | ||
192 | .isr_offset = REG_GPIO_ISR1B, | ||
193 | .imr_offset = REG_GPIO_IMR1B, | ||
194 | }, }, | ||
195 | }, | ||
196 | [1] = { | ||
197 | .name = "keypad", | ||
198 | .set_cor = true, | ||
199 | SIH_INITIALIZER(KEYPAD_KEYP, 4) | ||
200 | }, | ||
201 | [2] = { | ||
202 | .name = "bci", | ||
203 | .module = TWL5031_MODULE_INTERRUPTS, | ||
204 | .control_offset = TWL5031_INTERRUPTS_BCISIHCTRL, | ||
205 | .bits = 7, | ||
206 | .bytes_ixr = 1, | ||
207 | .edr_offset = TWL5031_INTERRUPTS_BCIEDR1, | ||
208 | /* Note: most of these IRQs default to no-trigger */ | ||
209 | .bytes_edr = 2, | ||
210 | .irq_lines = 2, | ||
211 | .mask = { { | ||
212 | .isr_offset = TWL5031_INTERRUPTS_BCIISR1, | ||
213 | .imr_offset = TWL5031_INTERRUPTS_BCIIMR1, | ||
214 | }, { | ||
215 | .isr_offset = TWL5031_INTERRUPTS_BCIISR2, | ||
216 | .imr_offset = TWL5031_INTERRUPTS_BCIIMR2, | ||
217 | }, }, | ||
218 | }, | ||
219 | [3] = { | ||
220 | .name = "madc", | ||
221 | SIH_INITIALIZER(MADC, 4) | ||
222 | }, | ||
223 | [4] = { | ||
224 | /* USB doesn't use the same SIH organization */ | ||
225 | .name = "usb", | ||
226 | }, | ||
227 | [5] = { | ||
228 | .name = "power", | ||
229 | .set_cor = true, | ||
230 | SIH_INITIALIZER(INT_PWR, 8) | ||
231 | }, | ||
232 | [6] = { | ||
233 | /* | ||
234 | * ACI doesn't use the same SIH organization. | ||
235 | * For example, it supports only one interrupt line | ||
236 | */ | ||
237 | .name = "aci", | ||
238 | .module = TWL5031_MODULE_ACCESSORY, | ||
239 | .bits = 9, | ||
240 | .bytes_ixr = 2, | ||
241 | .irq_lines = 1, | ||
242 | .mask = { { | ||
243 | .isr_offset = TWL5031_ACIIDR_LSB, | ||
244 | .imr_offset = TWL5031_ACIIMR_LSB, | ||
245 | }, }, | ||
246 | |||
247 | }, | ||
248 | [7] = { | ||
249 | /* Accessory */ | ||
250 | .name = "acc", | ||
251 | .module = TWL5031_MODULE_ACCESSORY, | ||
252 | .control_offset = TWL5031_ACCSIHCTRL, | ||
253 | .bits = 2, | ||
254 | .bytes_ixr = 1, | ||
255 | .edr_offset = TWL5031_ACCEDR1, | ||
256 | /* Note: most of these IRQs default to no-trigger */ | ||
257 | .bytes_edr = 1, | ||
258 | .irq_lines = 2, | ||
259 | .mask = { { | ||
260 | .isr_offset = TWL5031_ACCISR1, | ||
261 | .imr_offset = TWL5031_ACCIMR1, | ||
262 | }, { | ||
263 | .isr_offset = TWL5031_ACCISR2, | ||
264 | .imr_offset = TWL5031_ACCIMR2, | ||
265 | }, }, | ||
266 | }, | ||
267 | }; | ||
268 | |||
167 | #undef TWL4030_MODULE_KEYPAD_KEYP | 269 | #undef TWL4030_MODULE_KEYPAD_KEYP |
168 | #undef TWL4030_MODULE_INT_PWR | 270 | #undef TWL4030_MODULE_INT_PWR |
169 | #undef TWL4030_INT_PWR_EDR | 271 | #undef TWL4030_INT_PWR_EDR |
@@ -284,12 +386,16 @@ static int twl4030_init_sih_modules(unsigned line) | |||
284 | /* disable all interrupts on our line */ | 386 | /* disable all interrupts on our line */ |
285 | memset(buf, 0xff, sizeof buf); | 387 | memset(buf, 0xff, sizeof buf); |
286 | sih = sih_modules; | 388 | sih = sih_modules; |
287 | for (i = 0; i < ARRAY_SIZE(sih_modules); i++, sih++) { | 389 | for (i = 0; i < nr_sih_modules; i++, sih++) { |
288 | 390 | ||
289 | /* skip USB -- it's funky */ | 391 | /* skip USB -- it's funky */ |
290 | if (!sih->bytes_ixr) | 392 | if (!sih->bytes_ixr) |
291 | continue; | 393 | continue; |
292 | 394 | ||
395 | /* Not all the SIH modules support multiple interrupt lines */ | ||
396 | if (sih->irq_lines <= line) | ||
397 | continue; | ||
398 | |||
293 | status = twl4030_i2c_write(sih->module, buf, | 399 | status = twl4030_i2c_write(sih->module, buf, |
294 | sih->mask[line].imr_offset, sih->bytes_ixr); | 400 | sih->mask[line].imr_offset, sih->bytes_ixr); |
295 | if (status < 0) | 401 | if (status < 0) |
@@ -314,7 +420,7 @@ static int twl4030_init_sih_modules(unsigned line) | |||
314 | } | 420 | } |
315 | 421 | ||
316 | sih = sih_modules; | 422 | sih = sih_modules; |
317 | for (i = 0; i < ARRAY_SIZE(sih_modules); i++, sih++) { | 423 | for (i = 0; i < nr_sih_modules; i++, sih++) { |
318 | u8 rxbuf[4]; | 424 | u8 rxbuf[4]; |
319 | int j; | 425 | int j; |
320 | 426 | ||
@@ -322,6 +428,10 @@ static int twl4030_init_sih_modules(unsigned line) | |||
322 | if (!sih->bytes_ixr) | 428 | if (!sih->bytes_ixr) |
323 | continue; | 429 | continue; |
324 | 430 | ||
431 | /* Not all the SIH modules support multiple interrupt lines */ | ||
432 | if (sih->irq_lines <= line) | ||
433 | continue; | ||
434 | |||
325 | /* Clear pending interrupt status. Either the read was | 435 | /* Clear pending interrupt status. Either the read was |
326 | * enough, or we need to write those bits. Repeat, in | 436 | * enough, or we need to write those bits. Repeat, in |
327 | * case an IRQ is pending (PENDDIS=0) ... that's not | 437 | * case an IRQ is pending (PENDDIS=0) ... that's not |
@@ -611,7 +721,7 @@ int twl4030_sih_setup(int module) | |||
611 | 721 | ||
612 | /* only support modules with standard clear-on-read for now */ | 722 | /* only support modules with standard clear-on-read for now */ |
613 | for (sih_mod = 0, sih = sih_modules; | 723 | for (sih_mod = 0, sih = sih_modules; |
614 | sih_mod < ARRAY_SIZE(sih_modules); | 724 | sih_mod < nr_sih_modules; |
615 | sih_mod++, sih++) { | 725 | sih_mod++, sih++) { |
616 | if (sih->module == module && sih->set_cor) { | 726 | if (sih->module == module && sih->set_cor) { |
617 | if (!WARN((irq_base + sih->bits) > NR_IRQS, | 727 | if (!WARN((irq_base + sih->bits) > NR_IRQS, |
@@ -756,3 +866,16 @@ int twl_exit_irq(void) | |||
756 | } | 866 | } |
757 | return 0; | 867 | return 0; |
758 | } | 868 | } |
869 | |||
870 | int twl_init_chip_irq(const char *chip) | ||
871 | { | ||
872 | if (!strcmp(chip, "twl5031")) { | ||
873 | sih_modules = sih_modules_twl5031; | ||
874 | nr_sih_modules = ARRAY_SIZE(sih_modules_twl5031); | ||
875 | } else { | ||
876 | sih_modules = sih_modules_twl4030; | ||
877 | nr_sih_modules = ARRAY_SIZE(sih_modules_twl4030); | ||
878 | } | ||
879 | |||
880 | return 0; | ||
881 | } | ||