aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd
diff options
context:
space:
mode:
authorBalaji T K <balajitk@ti.com>2009-12-13 18:25:31 -0500
committerSamuel Ortiz <sameo@linux.intel.com>2009-12-13 18:25:31 -0500
commite8deb28ca8e221de0239eafb3c3d431d8854278e (patch)
tree2dffdb207366aa0a8f229775515fc207c16f8574 /drivers/mfd
parentc4aa6f314328142974c78377cd9476f8ec6f0eba (diff)
mfd: Add support for twl6030 irq framework
This patch adds support for phoenix interrupt framework. New iInterrupt status register A, B, C are introduced in Phoenix and are cleared on write. Due to the differences in interrupt handling with respect to TWL4030, twl6030-irq.c is created for TWL6030 PMIC Signed-off-by: Rajendra Nayak <rnayak@ti.com> Signed-off-by: Balaji T K <balajitk@ti.com> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Reviewed-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig4
-rw-r--r--drivers/mfd/Makefile2
-rw-r--r--drivers/mfd/twl-core.c120
-rw-r--r--drivers/mfd/twl4030-irq.c6
-rw-r--r--drivers/mfd/twl6030-irq.c299
5 files changed, 411 insertions, 20 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index b23343cdc196..87829789243e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -103,10 +103,10 @@ config MENELAUS
103 cell phones and PDAs. 103 cell phones and PDAs.
104 104
105config TWL4030_CORE 105config TWL4030_CORE
106 bool "Texas Instruments TWL4030/TPS659x0 Support" 106 bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support"
107 depends on I2C=y && GENERIC_HARDIRQS 107 depends on I2C=y && GENERIC_HARDIRQS
108 help 108 help
109 Say yes here if you have TWL4030 family chip on your board. 109 Say yes here if you have TWL4030 / TWL6030 family chip on your board.
110 This core driver provides register access and IRQ handling 110 This core driver provides register access and IRQ handling
111 facilities, and registers devices for the various functions 111 facilities, and registers devices for the various functions
112 so that function-specific drivers can bind to them. 112 so that function-specific drivers can bind to them.
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f4d14b7589bf..ca2f2c4ff05e 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -26,7 +26,7 @@ obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o
26obj-$(CONFIG_TPS65010) += tps65010.o 26obj-$(CONFIG_TPS65010) += tps65010.o
27obj-$(CONFIG_MENELAUS) += menelaus.o 27obj-$(CONFIG_MENELAUS) += menelaus.o
28 28
29obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o 29obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
30obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o 30obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
31obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o 31obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o
32 32
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 79946fe800af..c48a6138c575 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -181,6 +181,30 @@
181/* Triton Core internal information (END) */ 181/* Triton Core internal information (END) */
182 182
183 183
184/* subchip/slave 0 0x48 - POWER */
185#define TWL6030_BASEADD_RTC 0x0000
186#define TWL6030_BASEADD_MEM 0x0017
187#define TWL6030_BASEADD_PM_MASTER 0x001F
188#define TWL6030_BASEADD_PM_SLAVE_MISC 0x0030 /* PM_RECEIVER */
189#define TWL6030_BASEADD_PM_MISC 0x00E2
190#define TWL6030_BASEADD_PM_PUPD 0x00F0
191
192/* subchip/slave 1 0x49 - FEATURE */
193#define TWL6030_BASEADD_USB 0x0000
194#define TWL6030_BASEADD_GPADC_CTRL 0x002E
195#define TWL6030_BASEADD_AUX 0x0090
196#define TWL6030_BASEADD_PWM 0x00BA
197#define TWL6030_BASEADD_GASGAUGE 0x00C0
198#define TWL6030_BASEADD_PIH 0x00D0
199#define TWL6030_BASEADD_CHARGER 0x00E0
200
201/* subchip/slave 2 0x4A - DFT */
202#define TWL6030_BASEADD_DIEID 0x00C0
203
204/* subchip/slave 3 0x4B - AUDIO */
205#define TWL6030_BASEADD_AUDIO 0x0000
206#define TWL6030_BASEADD_RSV 0x0000
207
184/* Few power values */ 208/* Few power values */
185#define R_CFG_BOOT 0x05 209#define R_CFG_BOOT 0x05
186#define R_PROTECT_KEY 0x0E 210#define R_PROTECT_KEY 0x0E
@@ -202,13 +226,21 @@
202#define TWL4030_VAUX2 BIT(0) /* pre-5030 voltage ranges */ 226#define TWL4030_VAUX2 BIT(0) /* pre-5030 voltage ranges */
203#define TPS_SUBSET BIT(1) /* tps659[23]0 have fewer LDOs */ 227#define TPS_SUBSET BIT(1) /* tps659[23]0 have fewer LDOs */
204#define TWL5031 BIT(2) /* twl5031 has different registers */ 228#define TWL5031 BIT(2) /* twl5031 has different registers */
229#define TWL6030_CLASS BIT(3) /* TWL6030 class */
205 230
206/*----------------------------------------------------------------------*/ 231/*----------------------------------------------------------------------*/
207 232
208/* is driver active, bound to a chip? */ 233/* is driver active, bound to a chip? */
209static bool inuse; 234static bool inuse;
210 235
211/* Structure for each TWL4030 Slave */ 236static unsigned int twl_id;
237unsigned int twl_rev(void)
238{
239 return twl_id;
240}
241EXPORT_SYMBOL(twl_rev);
242
243/* Structure for each TWL4030/TWL6030 Slave */
212struct twl_client { 244struct twl_client {
213 struct i2c_client *client; 245 struct i2c_client *client;
214 u8 address; 246 u8 address;
@@ -228,11 +260,12 @@ struct twl_mapping {
228 unsigned char sid; /* Slave ID */ 260 unsigned char sid; /* Slave ID */
229 unsigned char base; /* base address */ 261 unsigned char base; /* base address */
230}; 262};
263struct twl_mapping *twl_map;
231 264
232static struct twl_mapping twl4030_map[TWL4030_MODULE_LAST + 1] = { 265static struct twl_mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
233 /* 266 /*
234 * NOTE: don't change this table without updating the 267 * NOTE: don't change this table without updating the
235 * <linux/i2c/twl4030.h> defines for TWL4030_MODULE_* 268 * <linux/i2c/twl.h> defines for TWL4030_MODULE_*
236 * so they continue to match the order in this table. 269 * so they continue to match the order in this table.
237 */ 270 */
238 271
@@ -265,6 +298,40 @@ static struct twl_mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
265 { 3, TWL4030_BASEADD_SECURED_REG }, 298 { 3, TWL4030_BASEADD_SECURED_REG },
266}; 299};
267 300
301static struct twl_mapping twl6030_map[] = {
302 /*
303 * NOTE: don't change this table without updating the
304 * <linux/i2c/twl.h> defines for TWL4030_MODULE_*
305 * so they continue to match the order in this table.
306 */
307 { SUB_CHIP_ID1, TWL6030_BASEADD_USB },
308 { SUB_CHIP_ID3, TWL6030_BASEADD_AUDIO },
309 { SUB_CHIP_ID2, TWL6030_BASEADD_DIEID },
310 { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
311 { SUB_CHIP_ID1, TWL6030_BASEADD_PIH },
312
313 { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
314 { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
315 { SUB_CHIP_ID1, TWL6030_BASEADD_GPADC_CTRL },
316 { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
317 { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
318
319 { SUB_CHIP_ID1, TWL6030_BASEADD_CHARGER },
320 { SUB_CHIP_ID1, TWL6030_BASEADD_GASGAUGE },
321 { SUB_CHIP_ID1, TWL6030_BASEADD_PWM },
322 { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
323 { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
324
325 { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
326 { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
327 { SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
328 { SUB_CHIP_ID0, TWL6030_BASEADD_PM_MASTER },
329 { SUB_CHIP_ID0, TWL6030_BASEADD_PM_SLAVE_MISC },
330
331 { SUB_CHIP_ID0, TWL6030_BASEADD_RTC },
332 { SUB_CHIP_ID0, TWL6030_BASEADD_MEM },
333};
334
268/*----------------------------------------------------------------------*/ 335/*----------------------------------------------------------------------*/
269 336
270/* Exported Functions */ 337/* Exported Functions */
@@ -292,7 +359,7 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
292 pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); 359 pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
293 return -EPERM; 360 return -EPERM;
294 } 361 }
295 sid = twl4030_map[mod_no].sid; 362 sid = twl_map[mod_no].sid;
296 twl = &twl_modules[sid]; 363 twl = &twl_modules[sid];
297 364
298 if (unlikely(!inuse)) { 365 if (unlikely(!inuse)) {
@@ -310,7 +377,7 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
310 msg->flags = 0; 377 msg->flags = 0;
311 msg->buf = value; 378 msg->buf = value;
312 /* over write the first byte of buffer with the register address */ 379 /* over write the first byte of buffer with the register address */
313 *value = twl4030_map[mod_no].base + reg; 380 *value = twl_map[mod_no].base + reg;
314 ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 1); 381 ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 1);
315 mutex_unlock(&twl->xfer_lock); 382 mutex_unlock(&twl->xfer_lock);
316 383
@@ -349,7 +416,7 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
349 pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); 416 pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
350 return -EPERM; 417 return -EPERM;
351 } 418 }
352 sid = twl4030_map[mod_no].sid; 419 sid = twl_map[mod_no].sid;
353 twl = &twl_modules[sid]; 420 twl = &twl_modules[sid];
354 421
355 if (unlikely(!inuse)) { 422 if (unlikely(!inuse)) {
@@ -362,7 +429,7 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
362 msg->addr = twl->address; 429 msg->addr = twl->address;
363 msg->len = 1; 430 msg->len = 1;
364 msg->flags = 0; /* Read the register value */ 431 msg->flags = 0; /* Read the register value */
365 val = twl4030_map[mod_no].base + reg; 432 val = twl_map[mod_no].base + reg;
366 msg->buf = &val; 433 msg->buf = &val;
367 /* [MSG2] fill the data rx buffer */ 434 /* [MSG2] fill the data rx buffer */
368 msg = &twl->xfer_msg[1]; 435 msg = &twl->xfer_msg[1];
@@ -486,6 +553,7 @@ add_regulator_linked(int num, struct regulator_init_data *pdata,
486 struct regulator_consumer_supply *consumers, 553 struct regulator_consumer_supply *consumers,
487 unsigned num_consumers) 554 unsigned num_consumers)
488{ 555{
556 unsigned sub_chip_id;
489 /* regulator framework demands init_data ... */ 557 /* regulator framework demands init_data ... */
490 if (!pdata) 558 if (!pdata)
491 return NULL; 559 return NULL;
@@ -496,7 +564,8 @@ add_regulator_linked(int num, struct regulator_init_data *pdata,
496 } 564 }
497 565
498 /* NOTE: we currently ignore regulator IRQs, e.g. for short circuits */ 566 /* NOTE: we currently ignore regulator IRQs, e.g. for short circuits */
499 return add_numbered_child(3, "twl_reg", num, 567 sub_chip_id = twl_map[TWL_MODULE_PM_MASTER].sid;
568 return add_numbered_child(sub_chip_id, "twl_reg", num,
500 pdata, sizeof(*pdata), false, 0, 0); 569 pdata, sizeof(*pdata), false, 0, 0);
501} 570}
502 571
@@ -516,6 +585,7 @@ static int
516add_children(struct twl4030_platform_data *pdata, unsigned long features) 585add_children(struct twl4030_platform_data *pdata, unsigned long features)
517{ 586{
518 struct device *child; 587 struct device *child;
588 unsigned sub_chip_id;
519 589
520 if (twl_has_bci() && pdata->bci && 590 if (twl_has_bci() && pdata->bci &&
521 !(features & (TPS_SUBSET | TWL5031))) { 591 !(features & (TPS_SUBSET | TWL5031))) {
@@ -561,7 +631,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
561 * Eventually, Linux might become more aware of such 631 * Eventually, Linux might become more aware of such
562 * HW security concerns, and "least privilege". 632 * HW security concerns, and "least privilege".
563 */ 633 */
564 child = add_child(3, "twl_rtc", 634 sub_chip_id = twl_map[TWL_MODULE_RTC].sid;
635 child = add_child(sub_chip_id, "twl_rtc",
565 NULL, 0, 636 NULL, 0,
566 true, pdata->irq_base + RTC_INTR_OFFSET, 0); 637 true, pdata->irq_base + RTC_INTR_OFFSET, 0);
567 if (IS_ERR(child)) 638 if (IS_ERR(child))
@@ -812,16 +883,22 @@ static void clocks_init(struct device *dev,
812 883
813/*----------------------------------------------------------------------*/ 884/*----------------------------------------------------------------------*/
814 885
815int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end); 886int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
816int twl_exit_irq(void); 887int twl4030_exit_irq(void);
817int twl_init_chip_irq(const char *chip); 888int twl4030_init_chip_irq(const char *chip);
889int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
890int twl6030_exit_irq(void);
818 891
819static int twl_remove(struct i2c_client *client) 892static int twl_remove(struct i2c_client *client)
820{ 893{
821 unsigned i; 894 unsigned i;
822 int status; 895 int status;
823 896
824 status = twl_exit_irq(); 897 if (twl_class_is_4030())
898 status = twl4030_exit_irq();
899 else
900 status = twl6030_exit_irq();
901
825 if (status < 0) 902 if (status < 0)
826 return status; 903 return status;
827 904
@@ -878,6 +955,13 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
878 mutex_init(&twl->xfer_lock); 955 mutex_init(&twl->xfer_lock);
879 } 956 }
880 inuse = true; 957 inuse = true;
958 if ((id->driver_data) & TWL6030_CLASS) {
959 twl_id = TWL6030_CLASS_ID;
960 twl_map = &twl6030_map[0];
961 } else {
962 twl_id = TWL4030_CLASS_ID;
963 twl_map = &twl4030_map[0];
964 }
881 965
882 /* setup clock framework */ 966 /* setup clock framework */
883 clocks_init(&client->dev, pdata->clock); 967 clocks_init(&client->dev, pdata->clock);
@@ -890,8 +974,15 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
890 if (client->irq 974 if (client->irq
891 && pdata->irq_base 975 && pdata->irq_base
892 && pdata->irq_end > pdata->irq_base) { 976 && pdata->irq_end > pdata->irq_base) {
893 twl_init_chip_irq(id->name); 977 if (twl_class_is_4030()) {
894 status = twl_init_irq(client->irq, pdata->irq_base, pdata->irq_end); 978 twl4030_init_chip_irq(id->name);
979 status = twl4030_init_irq(client->irq, pdata->irq_base,
980 pdata->irq_end);
981 } else {
982 status = twl6030_init_irq(client->irq, pdata->irq_base,
983 pdata->irq_end);
984 }
985
895 if (status < 0) 986 if (status < 0)
896 goto fail; 987 goto fail;
897 } 988 }
@@ -910,6 +1001,7 @@ static const struct i2c_device_id twl_ids[] = {
910 { "tps65950", 0 }, /* catalog version of twl5030 */ 1001 { "tps65950", 0 }, /* catalog version of twl5030 */
911 { "tps65930", TPS_SUBSET }, /* fewer LDOs and DACs; no charger */ 1002 { "tps65930", TPS_SUBSET }, /* fewer LDOs and DACs; no charger */
912 { "tps65920", TPS_SUBSET }, /* fewer LDOs; no codec or charger */ 1003 { "tps65920", TPS_SUBSET }, /* fewer LDOs; no codec or charger */
1004 { "twl6030", TWL6030_CLASS }, /* "Phoenix power chip" */
913 { /* end of list */ }, 1005 { /* end of list */ },
914}; 1006};
915MODULE_DEVICE_TABLE(i2c, twl_ids); 1007MODULE_DEVICE_TABLE(i2c, twl_ids);
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c
index 5a62cf916987..20d29bafc9f5 100644
--- a/drivers/mfd/twl4030-irq.c
+++ b/drivers/mfd/twl4030-irq.c
@@ -778,7 +778,7 @@ int twl4030_sih_setup(int module)
778/* FIXME pass in which interrupt line we'll use ... */ 778/* FIXME pass in which interrupt line we'll use ... */
779#define twl_irq_line 0 779#define twl_irq_line 0
780 780
781int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) 781int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
782{ 782{
783 static struct irq_chip twl4030_irq_chip; 783 static struct irq_chip twl4030_irq_chip;
784 784
@@ -858,7 +858,7 @@ fail:
858 return status; 858 return status;
859} 859}
860 860
861int twl_exit_irq(void) 861int twl4030_exit_irq(void)
862{ 862{
863 /* FIXME undo twl_init_irq() */ 863 /* FIXME undo twl_init_irq() */
864 if (twl4030_irq_base) { 864 if (twl4030_irq_base) {
@@ -868,7 +868,7 @@ int twl_exit_irq(void)
868 return 0; 868 return 0;
869} 869}
870 870
871int twl_init_chip_irq(const char *chip) 871int twl4030_init_chip_irq(const char *chip)
872{ 872{
873 if (!strcmp(chip, "twl5031")) { 873 if (!strcmp(chip, "twl5031")) {
874 sih_modules = sih_modules_twl5031; 874 sih_modules = sih_modules_twl5031;
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c
new file mode 100644
index 000000000000..10bf228ad626
--- /dev/null
+++ b/drivers/mfd/twl6030-irq.c
@@ -0,0 +1,299 @@
1/*
2 * twl6030-irq.c - TWL6030 irq support
3 *
4 * Copyright (C) 2005-2009 Texas Instruments, Inc.
5 *
6 * Modifications to defer interrupt handling to a kernel thread:
7 * Copyright (C) 2006 MontaVista Software, Inc.
8 *
9 * Based on tlv320aic23.c:
10 * Copyright (c) by Kai Svahn <kai.svahn@nokia.com>
11 *
12 * Code cleanup and modifications to IRQ handler.
13 * by syed khasim <x0khasim@ti.com>
14 *
15 * TWL6030 specific code and IRQ handling changes by
16 * Jagadeesh Bhaskar Pakaravoor <j-pakaravoor@ti.com>
17 * Balaji T K <balajitk@ti.com>
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 */
33
34#include <linux/init.h>
35#include <linux/interrupt.h>
36#include <linux/irq.h>
37#include <linux/kthread.h>
38#include <linux/i2c/twl.h>
39
40/*
41 * TWL6030 (unlike its predecessors, which had two level interrupt handling)
42 * three interrupt registers INT_STS_A, INT_STS_B and INT_STS_C.
43 * It exposes status bits saying who has raised an interrupt. There are
44 * three mask registers that corresponds to these status registers, that
45 * enables/disables these interrupts.
46 *
47 * We set up IRQs starting at a platform-specified base. An interrupt map table,
48 * specifies mapping between interrupt number and the associated module.
49 *
50 */
51
52static int twl6030_interrupt_mapping[24] = {
53 PWR_INTR_OFFSET, /* Bit 0 PWRON */
54 PWR_INTR_OFFSET, /* Bit 1 RPWRON */
55 PWR_INTR_OFFSET, /* Bit 2 BAT_VLOW */
56 RTC_INTR_OFFSET, /* Bit 3 RTC_ALARM */
57 RTC_INTR_OFFSET, /* Bit 4 RTC_PERIOD */
58 HOTDIE_INTR_OFFSET, /* Bit 5 HOT_DIE */
59 SMPSLDO_INTR_OFFSET, /* Bit 6 VXXX_SHORT */
60 SMPSLDO_INTR_OFFSET, /* Bit 7 VMMC_SHORT */
61
62 SMPSLDO_INTR_OFFSET, /* Bit 8 VUSIM_SHORT */
63 BATDETECT_INTR_OFFSET, /* Bit 9 BAT */
64 SIMDETECT_INTR_OFFSET, /* Bit 10 SIM */
65 MMCDETECT_INTR_OFFSET, /* Bit 11 MMC */
66 RSV_INTR_OFFSET, /* Bit 12 Reserved */
67 MADC_INTR_OFFSET, /* Bit 13 GPADC_RT_EOC */
68 MADC_INTR_OFFSET, /* Bit 14 GPADC_SW_EOC */
69 GASGAUGE_INTR_OFFSET, /* Bit 15 CC_AUTOCAL */
70
71 USBOTG_INTR_OFFSET, /* Bit 16 ID_WKUP */
72 USBOTG_INTR_OFFSET, /* Bit 17 VBUS_WKUP */
73 USBOTG_INTR_OFFSET, /* Bit 18 ID */
74 USBOTG_INTR_OFFSET, /* Bit 19 VBUS */
75 CHARGER_INTR_OFFSET, /* Bit 20 CHRG_CTRL */
76 CHARGER_INTR_OFFSET, /* Bit 21 EXT_CHRG */
77 CHARGER_INTR_OFFSET, /* Bit 22 INT_CHRG */
78 RSV_INTR_OFFSET, /* Bit 23 Reserved */
79};
80/*----------------------------------------------------------------------*/
81
82static unsigned twl6030_irq_base;
83
84static struct completion irq_event;
85
86/*
87 * This thread processes interrupts reported by the Primary Interrupt Handler.
88 */
89static int twl6030_irq_thread(void *data)
90{
91 long irq = (long)data;
92 static unsigned i2c_errors;
93 static const unsigned max_i2c_errors = 100;
94 int ret;
95
96 current->flags |= PF_NOFREEZE;
97
98 while (!kthread_should_stop()) {
99 int i;
100 union {
101 u8 bytes[4];
102 u32 int_sts;
103 } sts;
104
105 /* Wait for IRQ, then read PIH irq status (also blocking) */
106 wait_for_completion_interruptible(&irq_event);
107
108 /* read INT_STS_A, B and C in one shot using a burst read */
109 ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes,
110 REG_INT_STS_A, 3);
111 if (ret) {
112 pr_warning("twl6030: I2C error %d reading PIH ISR\n",
113 ret);
114 if (++i2c_errors >= max_i2c_errors) {
115 printk(KERN_ERR "Maximum I2C error count"
116 " exceeded. Terminating %s.\n",
117 __func__);
118 break;
119 }
120 complete(&irq_event);
121 continue;
122 }
123
124
125
126 sts.bytes[3] = 0; /* Only 24 bits are valid*/
127
128 for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) {
129 local_irq_disable();
130 if (sts.int_sts & 0x1) {
131 int module_irq = twl6030_irq_base +
132 twl6030_interrupt_mapping[i];
133 struct irq_desc *d = irq_to_desc(module_irq);
134
135 if (!d) {
136 pr_err("twl6030: Invalid SIH IRQ: %d\n",
137 module_irq);
138 return -EINVAL;
139 }
140
141 /* These can't be masked ... always warn
142 * if we get any surprises.
143 */
144 if (d->status & IRQ_DISABLED)
145 note_interrupt(module_irq, d,
146 IRQ_NONE);
147 else
148 d->handle_irq(module_irq, d);
149
150 }
151 local_irq_enable();
152 }
153 ret = twl_i2c_write(TWL_MODULE_PIH, sts.bytes,
154 REG_INT_STS_A, 3); /* clear INT_STS_A */
155 if (ret)
156 pr_warning("twl6030: I2C error in clearing PIH ISR\n");
157
158 enable_irq(irq);
159 }
160
161 return 0;
162}
163
164/*
165 * handle_twl6030_int() is the desc->handle method for the twl6030 interrupt.
166 * This is a chained interrupt, so there is no desc->action method for it.
167 * Now we need to query the interrupt controller in the twl6030 to determine
168 * which module is generating the interrupt request. However, we can't do i2c
169 * transactions in interrupt context, so we must defer that work to a kernel
170 * thread. All we do here is acknowledge and mask the interrupt and wakeup
171 * the kernel thread.
172 */
173static irqreturn_t handle_twl6030_pih(int irq, void *devid)
174{
175 disable_irq_nosync(irq);
176 complete(devid);
177 return IRQ_HANDLED;
178}
179
180/*----------------------------------------------------------------------*/
181
182static inline void activate_irq(int irq)
183{
184#ifdef CONFIG_ARM
185 /* ARM requires an extra step to clear IRQ_NOREQUEST, which it
186 * sets on behalf of every irq_chip. Also sets IRQ_NOPROBE.
187 */
188 set_irq_flags(irq, IRQF_VALID);
189#else
190 /* same effect on other architectures */
191 set_irq_noprobe(irq);
192#endif
193}
194
195/*----------------------------------------------------------------------*/
196
197static unsigned twl6030_irq_next;
198
199/*----------------------------------------------------------------------*/
200int twl6030_interrupt_unmask(u8 bit_mask, u8 offset)
201{
202 int ret;
203 u8 unmask_value;
204 ret = twl_i2c_read_u8(TWL_MODULE_PIH, &unmask_value,
205 REG_INT_STS_A + offset);
206 unmask_value &= (~(bit_mask));
207 ret |= twl_i2c_write_u8(TWL_MODULE_PIH, unmask_value,
208 REG_INT_STS_A + offset); /* unmask INT_MSK_A/B/C */
209 return ret;
210}
211EXPORT_SYMBOL(twl6030_interrupt_unmask);
212
213int twl6030_interrupt_mask(u8 bit_mask, u8 offset)
214{
215 int ret;
216 u8 mask_value;
217 ret = twl_i2c_read_u8(TWL_MODULE_PIH, &mask_value,
218 REG_INT_STS_A + offset);
219 mask_value |= (bit_mask);
220 ret |= twl_i2c_write_u8(TWL_MODULE_PIH, mask_value,
221 REG_INT_STS_A + offset); /* mask INT_MSK_A/B/C */
222 return ret;
223}
224EXPORT_SYMBOL(twl6030_interrupt_mask);
225
226int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
227{
228
229 int status = 0;
230 int i;
231 struct task_struct *task;
232 int ret;
233 u8 mask[4];
234
235 static struct irq_chip twl6030_irq_chip;
236 mask[1] = 0xFF;
237 mask[2] = 0xFF;
238 mask[3] = 0xFF;
239 ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0],
240 REG_INT_MSK_LINE_A, 3); /* MASK ALL INT LINES */
241 ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0],
242 REG_INT_MSK_STS_A, 3); /* MASK ALL INT STS */
243 ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0],
244 REG_INT_STS_A, 3); /* clear INT_STS_A,B,C */
245
246 twl6030_irq_base = irq_base;
247
248 /* install an irq handler for each of the modules;
249 * clone dummy irq_chip since PIH can't *do* anything
250 */
251 twl6030_irq_chip = dummy_irq_chip;
252 twl6030_irq_chip.name = "twl6030";
253 twl6030_irq_chip.set_type = NULL;
254
255 for (i = irq_base; i < irq_end; i++) {
256 set_irq_chip_and_handler(i, &twl6030_irq_chip,
257 handle_simple_irq);
258 activate_irq(i);
259 }
260
261 twl6030_irq_next = i;
262 pr_info("twl6030: %s (irq %d) chaining IRQs %d..%d\n", "PIH",
263 irq_num, irq_base, twl6030_irq_next - 1);
264
265 /* install an irq handler to demultiplex the TWL6030 interrupt */
266 init_completion(&irq_event);
267 task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");
268 if (IS_ERR(task)) {
269 pr_err("twl6030: could not create irq %d thread!\n", irq_num);
270 status = PTR_ERR(task);
271 goto fail_kthread;
272 }
273
274 status = request_irq(irq_num, handle_twl6030_pih, IRQF_DISABLED,
275 "TWL6030-PIH", &irq_event);
276 if (status < 0) {
277 pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status);
278 goto fail_irq;
279 }
280 return status;
281fail_irq:
282 free_irq(irq_num, &irq_event);
283
284fail_kthread:
285 for (i = irq_base; i < irq_end; i++)
286 set_irq_chip_and_handler(i, NULL, NULL);
287 return status;
288}
289
290int twl6030_exit_irq(void)
291{
292
293 if (twl6030_irq_base) {
294 pr_err("twl6030: can't yet clean up IRQs?\n");
295 return -ENOSYS;
296 }
297 return 0;
298}
299