aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanke Xie <d.xie@sta.samsung.com>2011-09-08 13:37:44 -0700
committerTodd Poynor <toddpoynor@google.com>2011-09-12 13:43:11 -0700
commit987583925df180b95e9191f8cd89e90986267202 (patch)
tree383e884346f0d158468c51dd9079db91d9ca650c
parent91b1f0f81833b6cbec2de6bcd3d2247cc140265a (diff)
downloadkernel_samsung_espresso10-987583925df180b95e9191f8cd89e90986267202.zip
kernel_samsung_espresso10-987583925df180b95e9191f8cd89e90986267202.tar.gz
kernel_samsung_espresso10-987583925df180b95e9191f8cd89e90986267202.tar.bz2
MFD: TWL 6030: Add VLOW interrupt handler to shutdown device
This change adds an interrupt handler to shutdown device when battery voltage drops below TWL 6030 threshold VBATMIN_HI. Change-Id: I6556fd213ab0fafa9ae78c37c1a4672d1440c304 Signed-off-by: Danke Xie <d.xie@sta.samsung.com>
-rw-r--r--drivers/mfd/twl-core.c5
-rw-r--r--drivers/mfd/twl6030-irq.c90
-rw-r--r--include/linux/i2c/twl.h13
3 files changed, 106 insertions, 2 deletions
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 194a674..ed11f61 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -126,6 +126,7 @@
/* Last - for index max*/
#define TWL4030_MODULE_LAST TWL4030_MODULE_SECURED_REG
+#define TWL6030_MODULE_LAST TWL6030_MODULE_SLAVE_RES
#define TWL_NUM_SLAVES 4
@@ -141,7 +142,7 @@
#define SUB_CHIP_ID2 2
#define SUB_CHIP_ID3 3
-#define TWL_MODULE_LAST TWL4030_MODULE_LAST
+#define TWL_MODULE_LAST TWL6030_MODULE_LAST
/* Base Address defns for twl4030_map[] */
@@ -187,6 +188,7 @@
#define TWL6030_BASEADD_MEM 0x0017
#define TWL6030_BASEADD_PM_MASTER 0x001F
#define TWL6030_BASEADD_PM_SLAVE_MISC 0x0030 /* PM_RECEIVER */
+#define TWL6030_BASEADD_PM_SLAVE_RES 0x00AD
#define TWL6030_BASEADD_PM_MISC 0x00E2
#define TWL6030_BASEADD_PM_PUPD 0x00F0
@@ -333,6 +335,7 @@ static struct twl_mapping twl6030_map[] = {
{ SUB_CHIP_ID0, TWL6030_BASEADD_RTC },
{ SUB_CHIP_ID0, TWL6030_BASEADD_MEM },
{ SUB_CHIP_ID1, TWL6025_BASEADD_CHARGER },
+ { SUB_CHIP_ID0, TWL6030_BASEADD_PM_SLAVE_RES },
};
/*----------------------------------------------------------------------*/
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c
index 7999d3f..67fb7ff 100644
--- a/drivers/mfd/twl6030-irq.c
+++ b/drivers/mfd/twl6030-irq.c
@@ -38,6 +38,7 @@
#include <linux/i2c/twl.h>
#include <linux/platform_device.h>
#include <linux/suspend.h>
+#include <linux/reboot.h>
#include "twl-core.h"
@@ -56,7 +57,7 @@
static int twl6030_interrupt_mapping[24] = {
PWR_INTR_OFFSET, /* Bit 0 PWRON */
PWR_INTR_OFFSET, /* Bit 1 RPWRON */
- PWR_INTR_OFFSET, /* Bit 2 BAT_VLOW */
+ TWL_VLOW_INTR_OFFSET, /* Bit 2 BAT_VLOW */
RTC_INTR_OFFSET, /* Bit 3 RTC_ALARM */
RTC_INTR_OFFSET, /* Bit 4 RTC_PERIOD */
HOTDIE_INTR_OFFSET, /* Bit 5 HOT_DIE */
@@ -216,6 +217,17 @@ static irqreturn_t handle_twl6030_pih(int irq, void *devid)
return IRQ_HANDLED;
}
+/*
+ * handle_twl6030_vlow() is a threaded BAT_VLOW interrupt handler. BAT_VLOW
+ * is a secondary interrupt generated in twl6030_irq_thread().
+ */
+static irqreturn_t handle_twl6030_vlow(int irq, void *unused)
+{
+ pr_info("handle_twl6030_vlow: kernel_power_off()\n");
+ kernel_power_off();
+ return IRQ_HANDLED;
+}
+
/*----------------------------------------------------------------------*/
static inline void activate_irq(int irq)
@@ -344,6 +356,70 @@ int twl6030_mmc_card_detect(struct device *dev, int slot)
}
EXPORT_SYMBOL(twl6030_mmc_card_detect);
+int twl6030_vlow_init(int vlow_irq)
+{
+ int status;
+ u8 val;
+
+ status = twl_i2c_read_u8(TWL_MODULE_PM_SLAVE_RES, &val,
+ REG_VBATMIN_HI_CFG_STATE);
+ if (status < 0) {
+ pr_err("twl6030: I2C err reading REG_VBATMIN_HI_CFG_STATE: %d\n",
+ status);
+ return status;
+ }
+
+ status = twl_i2c_write_u8(TWL_MODULE_PM_SLAVE_RES,
+ val | VBATMIN_VLOW_EN, REG_VBATMIN_HI_CFG_STATE);
+ if (status < 0) {
+ pr_err("twl6030: I2C err writing REG_VBATMIN_HI_CFG_STATE: %d\n",
+ status);
+ return status;
+ }
+
+ status = twl_i2c_read_u8(TWL_MODULE_PIH, &val, REG_INT_MSK_LINE_A);
+ if (status < 0) {
+ pr_err("twl6030: I2C err reading REG_INT_MSK_LINE_A: %d\n",
+ status);
+ return status;
+ }
+
+ status = twl_i2c_write_u8(TWL_MODULE_PIH, val & ~VLOW_INT_MASK,
+ REG_INT_MSK_LINE_A);
+ if (status < 0) {
+ pr_err("twl6030: I2C err writing REG_INT_MSK_LINE_A: %d\n",
+ status);
+ return status;
+ }
+
+ status = twl_i2c_read_u8(TWL_MODULE_PIH, &val, REG_INT_MSK_STS_A);
+ if (status < 0) {
+ pr_err("twl6030: I2C err reading REG_INT_MSK_STS_A: %d\n",
+ status);
+ return status;
+ }
+
+ status = twl_i2c_write_u8(TWL_MODULE_PIH, val & ~VLOW_INT_MASK,
+ REG_INT_MSK_STS_A);
+ if (status < 0) {
+ pr_err("twl6030: I2C err writing REG_INT_MSK_STS_A: %d\n",
+ status);
+ return status;
+ }
+
+ /* install an irq handler for vlow */
+ status = request_threaded_irq(vlow_irq, NULL, handle_twl6030_vlow,
+ IRQF_ONESHOT,
+ "TWL6030-VLOW", handle_twl6030_vlow);
+ if (status < 0) {
+ pr_err("twl6030: could not claim vlow irq %d: %d\n", vlow_irq,
+ status);
+ return status;
+ }
+
+ return 0;
+}
+
int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
{
@@ -403,7 +479,16 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
twl_irq = irq_num;
register_pm_notifier(&twl6030_irq_pm_notifier_block);
+
+ status = twl6030_vlow_init(twl6030_irq_base + TWL_VLOW_INTR_OFFSET);
+ if (status < 0)
+ goto fail_vlow;
+
return status;
+
+fail_vlow:
+ free_irq(irq_num, &irq_event);
+
fail_irq:
free_irq(irq_num, &irq_event);
@@ -426,6 +511,9 @@ int twl6030_exit_irq(void)
return -ENOSYS;
}
+ free_irq(twl6030_irq_base + TWL_VLOW_INTR_OFFSET,
+ handle_twl6030_vlow);
+
free_irq(twl_irq, &irq_event);
for (i = twl6030_irq_base; i < twl6030_irq_end; i++)
diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h
index a9bfb2a..a163c7d 100644
--- a/include/linux/i2c/twl.h
+++ b/include/linux/i2c/twl.h
@@ -71,6 +71,7 @@
#define TWL4030_MODULE_PM_RECEIVER 0x15
#define TWL4030_MODULE_RTC 0x16
#define TWL4030_MODULE_SECURED_REG 0x17
+#define TWL6030_MODULE_SLAVE_RES 0x19
#define TWL_MODULE_USB TWL4030_MODULE_USB
#define TWL_MODULE_AUDIO_VOICE TWL4030_MODULE_AUDIO_VOICE
@@ -81,6 +82,7 @@
#define TWL_MODULE_PM_RECEIVER TWL4030_MODULE_PM_RECEIVER
#define TWL_MODULE_RTC TWL4030_MODULE_RTC
#define TWL_MODULE_PWM TWL4030_MODULE_PWM0
+#define TWL_MODULE_PM_SLAVE_RES TWL6030_MODULE_SLAVE_RES
#define TWL6030_MODULE_ID0 0x0D
#define TWL6030_MODULE_ID1 0x0E
@@ -100,6 +102,7 @@
* Offset from TWL6030_IRQ_BASE / pdata->irq_base
*/
#define PWR_INTR_OFFSET 0
+#define TWL_VLOW_INTR_OFFSET 6
#define HOTDIE_INTR_OFFSET 12
#define SMPSLDO_INTR_OFFSET 13
#define BATDETECT_INTR_OFFSET 14
@@ -151,6 +154,8 @@
#define MMC_PU (0x1 << 3)
#define MMC_PD (0x1 << 2)
+#define VLOW_INT_MASK (0x1 << 2)
+
#define TWL_SIL_TYPE(rev) ((rev) & 0x00FFFFFF)
#define TWL_SIL_REV(rev) ((rev) >> 24)
#define TWL_SIL_5030 0x09002F
@@ -450,6 +455,14 @@ static inline int twl6030_mmc_card_detect(struct device *dev, int slot)
#define TWL4030_PM_MASTER_GLOBAL_TST 0xb6
+/*
+ * PM Slave resource module register offsets (use TWL6030_MODULE_SLAVE_RES)
+ */
+
+#define REG_VBATMIN_HI_CFG_STATE 0x1D
+
+#define VBATMIN_VLOW_EN 0x21
+
/*----------------------------------------------------------------------*/
/* Power bus message definitions */