diff options
| -rw-r--r-- | arch/arm/plat-omap/include/plat/irqs.h | 16 | ||||
| -rw-r--r-- | drivers/mfd/Kconfig | 4 | ||||
| -rw-r--r-- | drivers/mfd/Makefile | 2 | ||||
| -rw-r--r-- | drivers/mfd/twl-core.c | 120 | ||||
| -rw-r--r-- | drivers/mfd/twl4030-irq.c | 6 | ||||
| -rw-r--r-- | drivers/mfd/twl6030-irq.c | 299 | ||||
| -rw-r--r-- | include/linux/i2c/twl.h | 64 |
7 files changed, 490 insertions, 21 deletions
diff --git a/arch/arm/plat-omap/include/plat/irqs.h b/arch/arm/plat-omap/include/plat/irqs.h index ce5dd2d1dc21..97d6c50c3dcb 100644 --- a/arch/arm/plat-omap/include/plat/irqs.h +++ b/arch/arm/plat-omap/include/plat/irqs.h | |||
| @@ -472,8 +472,22 @@ | |||
| 472 | #endif | 472 | #endif |
| 473 | #define TWL4030_GPIO_IRQ_END (TWL4030_GPIO_IRQ_BASE + TWL4030_GPIO_NR_IRQS) | 473 | #define TWL4030_GPIO_IRQ_END (TWL4030_GPIO_IRQ_BASE + TWL4030_GPIO_NR_IRQS) |
| 474 | 474 | ||
| 475 | #define TWL6030_IRQ_BASE (OMAP_FPGA_IRQ_END) | ||
| 476 | #ifdef CONFIG_TWL4030_CORE | ||
| 477 | #define TWL6030_BASE_NR_IRQS 20 | ||
| 478 | #else | ||
| 479 | #define TWL6030_BASE_NR_IRQS 0 | ||
| 480 | #endif | ||
| 481 | #define TWL6030_IRQ_END (TWL6030_IRQ_BASE + TWL6030_BASE_NR_IRQS) | ||
| 482 | |||
| 475 | /* Total number of interrupts depends on the enabled blocks above */ | 483 | /* Total number of interrupts depends on the enabled blocks above */ |
| 476 | #define NR_IRQS TWL4030_GPIO_IRQ_END | 484 | #if (TWL4030_GPIO_IRQ_END > TWL6030_IRQ_END) |
| 485 | #define TWL_IRQ_END TWL4030_GPIO_IRQ_END | ||
| 486 | #else | ||
| 487 | #define TWL_IRQ_END TWL6030_IRQ_END | ||
| 488 | #endif | ||
| 489 | |||
| 490 | #define NR_IRQS TWL_IRQ_END | ||
| 477 | 491 | ||
| 478 | #define OMAP_IRQ_BIT(irq) (1 << ((irq) % 32)) | 492 | #define OMAP_IRQ_BIT(irq) (1 << ((irq) % 32)) |
| 479 | 493 | ||
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 | ||
| 105 | config TWL4030_CORE | 105 | config 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 | |||
| 26 | obj-$(CONFIG_TPS65010) += tps65010.o | 26 | obj-$(CONFIG_TPS65010) += tps65010.o |
| 27 | obj-$(CONFIG_MENELAUS) += menelaus.o | 27 | obj-$(CONFIG_MENELAUS) += menelaus.o |
| 28 | 28 | ||
| 29 | obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o | 29 | obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o |
| 30 | obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o | 30 | obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o |
| 31 | obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o | 31 | obj-$(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? */ |
| 209 | static bool inuse; | 234 | static bool inuse; |
| 210 | 235 | ||
| 211 | /* Structure for each TWL4030 Slave */ | 236 | static unsigned int twl_id; |
| 237 | unsigned int twl_rev(void) | ||
| 238 | { | ||
| 239 | return twl_id; | ||
| 240 | } | ||
| 241 | EXPORT_SYMBOL(twl_rev); | ||
| 242 | |||
| 243 | /* Structure for each TWL4030/TWL6030 Slave */ | ||
| 212 | struct twl_client { | 244 | struct 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 | }; |
| 263 | struct twl_mapping *twl_map; | ||
| 231 | 264 | ||
| 232 | static struct twl_mapping twl4030_map[TWL4030_MODULE_LAST + 1] = { | 265 | static 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 | ||
| 301 | static 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 | |||
| 516 | add_children(struct twl4030_platform_data *pdata, unsigned long features) | 585 | add_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 | ||
| 815 | int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end); | 886 | int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end); |
| 816 | int twl_exit_irq(void); | 887 | int twl4030_exit_irq(void); |
| 817 | int twl_init_chip_irq(const char *chip); | 888 | int twl4030_init_chip_irq(const char *chip); |
| 889 | int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end); | ||
| 890 | int twl6030_exit_irq(void); | ||
| 818 | 891 | ||
| 819 | static int twl_remove(struct i2c_client *client) | 892 | static 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 | }; |
| 915 | MODULE_DEVICE_TABLE(i2c, twl_ids); | 1007 | MODULE_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 | ||
| 781 | int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) | 781 | int 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 | ||
| 861 | int twl_exit_irq(void) | 861 | int 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 | ||
| 871 | int twl_init_chip_irq(const char *chip) | 871 | int 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 | |||
| 52 | static 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 | |||
| 82 | static unsigned twl6030_irq_base; | ||
| 83 | |||
| 84 | static struct completion irq_event; | ||
| 85 | |||
| 86 | /* | ||
| 87 | * This thread processes interrupts reported by the Primary Interrupt Handler. | ||
| 88 | */ | ||
| 89 | static 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 | */ | ||
| 173 | static 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 | |||
| 182 | static 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 | |||
| 197 | static unsigned twl6030_irq_next; | ||
| 198 | |||
| 199 | /*----------------------------------------------------------------------*/ | ||
| 200 | int 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 | } | ||
| 211 | EXPORT_SYMBOL(twl6030_interrupt_unmask); | ||
| 212 | |||
| 213 | int 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 | } | ||
| 224 | EXPORT_SYMBOL(twl6030_interrupt_mask); | ||
| 225 | |||
| 226 | int 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; | ||
| 281 | fail_irq: | ||
| 282 | free_irq(irq_num, &irq_event); | ||
| 283 | |||
| 284 | fail_kthread: | ||
| 285 | for (i = irq_base; i < irq_end; i++) | ||
| 286 | set_irq_chip_and_handler(i, NULL, NULL); | ||
| 287 | return status; | ||
| 288 | } | ||
| 289 | |||
| 290 | int 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 | |||
diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 0f812f5aa723..8e7405d9c624 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h | |||
| @@ -89,6 +89,67 @@ | |||
| 89 | #define BCI_PRES_INTR_OFFSET 9 | 89 | #define BCI_PRES_INTR_OFFSET 9 |
| 90 | #define USB_PRES_INTR_OFFSET 10 | 90 | #define USB_PRES_INTR_OFFSET 10 |
| 91 | #define RTC_INTR_OFFSET 11 | 91 | #define RTC_INTR_OFFSET 11 |
| 92 | |||
| 93 | /* | ||
| 94 | * Offset from TWL6030_IRQ_BASE / pdata->irq_base | ||
| 95 | */ | ||
| 96 | #define PWR_INTR_OFFSET 0 | ||
| 97 | #define HOTDIE_INTR_OFFSET 12 | ||
| 98 | #define SMPSLDO_INTR_OFFSET 13 | ||
| 99 | #define BATDETECT_INTR_OFFSET 14 | ||
| 100 | #define SIMDETECT_INTR_OFFSET 15 | ||
| 101 | #define MMCDETECT_INTR_OFFSET 16 | ||
| 102 | #define GASGAUGE_INTR_OFFSET 17 | ||
| 103 | #define USBOTG_INTR_OFFSET 4 | ||
| 104 | #define CHARGER_INTR_OFFSET 2 | ||
| 105 | #define RSV_INTR_OFFSET 0 | ||
| 106 | |||
| 107 | /* INT register offsets */ | ||
| 108 | #define REG_INT_STS_A 0x00 | ||
| 109 | #define REG_INT_STS_B 0x01 | ||
| 110 | #define REG_INT_STS_C 0x02 | ||
| 111 | |||
| 112 | #define REG_INT_MSK_LINE_A 0x03 | ||
| 113 | #define REG_INT_MSK_LINE_B 0x04 | ||
| 114 | #define REG_INT_MSK_LINE_C 0x05 | ||
| 115 | |||
| 116 | #define REG_INT_MSK_STS_A 0x06 | ||
| 117 | #define REG_INT_MSK_STS_B 0x07 | ||
| 118 | #define REG_INT_MSK_STS_C 0x08 | ||
| 119 | |||
| 120 | /* MASK INT REG GROUP A */ | ||
| 121 | #define TWL6030_PWR_INT_MASK 0x07 | ||
| 122 | #define TWL6030_RTC_INT_MASK 0x18 | ||
| 123 | #define TWL6030_HOTDIE_INT_MASK 0x20 | ||
| 124 | #define TWL6030_SMPSLDOA_INT_MASK 0xC0 | ||
| 125 | |||
| 126 | /* MASK INT REG GROUP B */ | ||
| 127 | #define TWL6030_SMPSLDOB_INT_MASK 0x01 | ||
| 128 | #define TWL6030_BATDETECT_INT_MASK 0x02 | ||
| 129 | #define TWL6030_SIMDETECT_INT_MASK 0x04 | ||
| 130 | #define TWL6030_MMCDETECT_INT_MASK 0x08 | ||
| 131 | #define TWL6030_GPADC_INT_MASK 0x60 | ||
| 132 | #define TWL6030_GASGAUGE_INT_MASK 0x80 | ||
| 133 | |||
| 134 | /* MASK INT REG GROUP C */ | ||
| 135 | #define TWL6030_USBOTG_INT_MASK 0x0F | ||
| 136 | #define TWL6030_CHARGER_CTRL_INT_MASK 0x10 | ||
| 137 | #define TWL6030_CHARGER_FAULT_INT_MASK 0x60 | ||
| 138 | |||
| 139 | |||
| 140 | #define TWL4030_CLASS_ID 0x4030 | ||
| 141 | #define TWL6030_CLASS_ID 0x6030 | ||
| 142 | unsigned int twl_rev(void); | ||
| 143 | #define GET_TWL_REV (twl_rev()) | ||
| 144 | #define TWL_CLASS_IS(class, id) \ | ||
| 145 | static inline int twl_class_is_ ##class(void) \ | ||
| 146 | { \ | ||
| 147 | return ((id) == (GET_TWL_REV)) ? 1 : 0; \ | ||
| 148 | } | ||
| 149 | |||
| 150 | TWL_CLASS_IS(4030, TWL4030_CLASS_ID) | ||
| 151 | TWL_CLASS_IS(6030, TWL6030_CLASS_ID) | ||
| 152 | |||
| 92 | /* | 153 | /* |
| 93 | * Read and write single 8-bit registers | 154 | * Read and write single 8-bit registers |
| 94 | */ | 155 | */ |
| @@ -104,6 +165,9 @@ int twl_i2c_read_u8(u8 mod_no, u8 *val, u8 reg); | |||
| 104 | int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); | 165 | int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); |
| 105 | int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); | 166 | int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); |
| 106 | 167 | ||
| 168 | int twl6030_interrupt_unmask(u8 bit_mask, u8 offset); | ||
| 169 | int twl6030_interrupt_mask(u8 bit_mask, u8 offset); | ||
| 170 | |||
| 107 | /*----------------------------------------------------------------------*/ | 171 | /*----------------------------------------------------------------------*/ |
| 108 | 172 | ||
| 109 | /* | 173 | /* |
