[PATCH 04/10] fix-pcf50606-atomic-i2c-int-read.patch
warmcat
andy at openmoko.com
Mon Feb 11 17:34:40 CET 2008
From: Andy Green <andy at openmoko.com>
Datasheet says reading the interrupt source regs (and thereby
clearing them) must be done in a single i2c read transaction
Signed-off-by: Andy Green <andy at openmoko.com>
---
drivers/i2c/chips/pcf50606.c | 60 ++++++++++++++++++++++++------------------
1 files changed, 34 insertions(+), 26 deletions(-)
diff --git a/drivers/i2c/chips/pcf50606.c b/drivers/i2c/chips/pcf50606.c
index def2d35..970a523 100644
--- a/drivers/i2c/chips/pcf50606.c
+++ b/drivers/i2c/chips/pcf50606.c
@@ -563,25 +563,33 @@ static void pcf50606_work(struct work_struct *work)
{
struct pcf50606_data *pcf =
container_of(work, struct pcf50606_data, work);
- u_int8_t int1, int2, int3;
+ u_int8_t pcfirq[3];
+ int ret;
mutex_lock(&pcf->working_lock);
pcf->working = 1;
-
- int1 = __reg_read(pcf, PCF50606_REG_INT1);
- int2 = __reg_read(pcf, PCF50606_REG_INT2);
- int3 = __reg_read(pcf, PCF50606_REG_INT3);
+ /*
+ * p35 pcf50606 datasheet rev 2.2:
+ * ''The system controller shall read all interrupt registers in
+ * one I2C read action''
+ * because if you don't INT# gets stuck asserted forever after a
+ * while
+ */
+ ret = i2c_smbus_read_i2c_block_data(&pcf->client, PCF50606_REG_INT1, 3,
+ pcfirq);
+ if (ret != 3)
+ DEBUGPC("Oh crap PMU IRQ register read failed %d\n", ret);
dev_dbg(&pcf->client.dev, "INT1=0x%02x INT2=0x%02x INT3=0x%02x:",
- int1, int2, int3);
+ pcfirq[0], pcfirq[1], pcfirq[2]);
- if (int1 & PCF50606_INT1_ONKEYF) {
+ if (pcfirq[0] & PCF50606_INT1_ONKEYF) {
/* ONKEY falling edge (start of button press) */
DEBUGPC("ONKEYF ");
pcf->flags |= PCF50606_F_PWR_PRESSED;
input_report_key(pcf->input_dev, KEY_POWER, 1);
}
- if (int1 & PCF50606_INT1_ONKEY1S) {
+ if (pcfirq[0] & PCF50606_INT1_ONKEY1S) {
/* ONKEY pressed for more than 1 second */
pcf->onkey_seconds = 0;
DEBUGPC("ONKEY1S ");
@@ -592,7 +600,7 @@ static void pcf50606_work(struct work_struct *work)
/* enable SECOND interrupt (hz tick) */
reg_clear_bits(pcf, PCF50606_REG_INT1M, PCF50606_INT1_SECOND);
}
- if (int1 & PCF50606_INT1_ONKEYR) {
+ if (pcfirq[0] & PCF50606_INT1_ONKEYR) {
/* ONKEY rising edge (end of button press) */
DEBUGPC("ONKEYR ");
pcf->flags &= ~PCF50606_F_PWR_PRESSED;
@@ -605,15 +613,15 @@ static void pcf50606_work(struct work_struct *work)
PCF50606_INT1_SECOND,
PCF50606_INT1_SECOND);
}
- if (int1 & PCF50606_INT1_EXTONR) {
+ if (pcfirq[0] & PCF50606_INT1_EXTONR) {
DEBUGPC("EXTONR ");
input_report_key(pcf->input_dev, KEY_POWER2, 1);
}
- if (int1 & PCF50606_INT1_EXTONF) {
+ if (pcfirq[0] & PCF50606_INT1_EXTONF) {
DEBUGPC("EXTONF ");
input_report_key(pcf->input_dev, KEY_POWER2, 0);
}
- if (int1 & PCF50606_INT1_SECOND) {
+ if (pcfirq[0] & PCF50606_INT1_SECOND) {
DEBUGPC("SECOND ");
if (pcf->flags & PCF50606_F_RTC_SECOND)
rtc_update_irq(pcf->rtc, 1,
@@ -635,14 +643,14 @@ static void pcf50606_work(struct work_struct *work)
}
}
}
- if (int1 & PCF50606_INT1_ALARM) {
+ if (pcfirq[0] & PCF50606_INT1_ALARM) {
DEBUGPC("ALARM ");
if (pcf->pdata->used_features & PCF50606_FEAT_RTC)
rtc_update_irq(pcf->rtc, 1,
RTC_AF | RTC_IRQF);
}
- if (int2 & PCF50606_INT2_CHGINS) {
+ if (pcfirq[1] & PCF50606_INT2_CHGINS) {
/* Charger inserted */
DEBUGPC("CHGINS ");
input_report_key(pcf->input_dev, KEY_BATTERY, 1);
@@ -653,7 +661,7 @@ static void pcf50606_work(struct work_struct *work)
PCF50606_FEAT_MBC, PMU_EVT_INSERT);
/* FIXME: how to signal this to userspace */
}
- if (int2 & PCF50606_INT2_CHGRM) {
+ if (pcfirq[1] & PCF50606_INT2_CHGRM) {
/* Charger removed */
DEBUGPC("CHGRM ");
input_report_key(pcf->input_dev, KEY_BATTERY, 0);
@@ -664,57 +672,57 @@ static void pcf50606_work(struct work_struct *work)
PCF50606_FEAT_MBC, PMU_EVT_INSERT);
/* FIXME: how signal this to userspace */
}
- if (int2 & PCF50606_INT2_CHGFOK) {
+ if (pcfirq[1] & PCF50606_INT2_CHGFOK) {
/* Battery ready for fast charging */
DEBUGPC("CHGFOK ");
pcf->flags |= PCF50606_F_CHG_FOK;
/* FIXME: how to signal this to userspace */
}
- if (int2 & PCF50606_INT2_CHGERR) {
+ if (pcfirq[1] & PCF50606_INT2_CHGERR) {
/* Error in charge mode */
DEBUGPC("CHGERR ");
pcf->flags |= PCF50606_F_CHG_ERR;
pcf->flags &= ~(PCF50606_F_CHG_FOK|PCF50606_F_CHG_READY);
/* FIXME: how to signal this to userspace */
}
- if (int2 & PCF50606_INT2_CHGFRDY) {
+ if (pcfirq[1] & PCF50606_INT2_CHGFRDY) {
/* Fast charge completed */
DEBUGPC("CHGFRDY ");
pcf->flags |= PCF50606_F_CHG_READY;
pcf->flags &= ~PCF50606_F_CHG_FOK;
/* FIXME: how to signal this to userspace */
}
- if (int2 & PCF50606_INT2_CHGPROT) {
+ if (pcfirq[1] & PCF50606_INT2_CHGPROT) {
/* Charging protection interrupt */
DEBUGPC("CHGPROT ");
pcf->flags &= ~(PCF50606_F_CHG_FOK|PCF50606_F_CHG_READY);
/* FIXME: signal this to userspace */
}
- if (int2 & PCF50606_INT2_CHGWD10S) {
+ if (pcfirq[1] & PCF50606_INT2_CHGWD10S) {
/* Charger watchdog will expire in 10 seconds */
DEBUGPC("CHGWD10S ");
reg_set_bit_mask(pcf, PCF50606_REG_OOCC1,
PCF50606_OOCC1_WDTRST,
PCF50606_OOCC1_WDTRST);
}
- if (int2 & PCF50606_INT2_CHGWDEXP) {
+ if (pcfirq[1] & PCF50606_INT2_CHGWDEXP) {
/* Charger watchdog expires */
DEBUGPC("CHGWDEXP ");
/* FIXME: how to signal this to userspace */
}
- if (int3 & PCF50606_INT3_ADCRDY) {
+ if (pcfirq[2] & PCF50606_INT3_ADCRDY) {
/* ADC result ready */
DEBUGPC("ADCRDY ");
}
- if (int3 & PCF50606_INT3_ACDINS) {
+ if (pcfirq[2] & PCF50606_INT3_ACDINS) {
/* Accessory insertion detected */
DEBUGPC("ACDINS ");
if (pcf->pdata->cb)
pcf->pdata->cb(&pcf->client.dev,
PCF50606_FEAT_ACD, PMU_EVT_INSERT);
}
- if (int3 & PCF50606_INT3_ACDREM) {
+ if (pcfirq[2] & PCF50606_INT3_ACDREM) {
/* Accessory removal detected */
DEBUGPC("ACDREM ");
if (pcf->pdata->cb)
@@ -722,7 +730,7 @@ static void pcf50606_work(struct work_struct *work)
PCF50606_FEAT_ACD, PMU_EVT_REMOVE);
}
/* FIXME: TSCPRES */
- if (int3 & PCF50606_INT3_LOWBAT) {
+ if (pcfirq[2] & PCF50606_INT3_LOWBAT) {
/* Really low battery voltage, we have 8 seconds left */
DEBUGPC("LOWBAT ");
apm_queue_event(APM_LOW_BATTERY);
@@ -733,7 +741,7 @@ static void pcf50606_work(struct work_struct *work)
PCF50606_OOCC1_TOTRST,
PCF50606_OOCC1_TOTRST);
}
- if (int3 & PCF50606_INT3_HIGHTMP) {
+ if (pcfirq[2] & PCF50606_INT3_HIGHTMP) {
/* High temperature */
DEBUGPC("HIGHTMP ");
apm_queue_event(APM_CRITICAL_SUSPEND);
More information about the openmoko-kernel
mailing list