135 lines
4.5 KiB
Diff
135 lines
4.5 KiB
Diff
|
From ac949375c7ef5882e8d3ea4f4a165b2fce943f21 Mon Sep 17 00:00:00 2001
|
||
|
From: Andy Green <andy@openmoko.com>
|
||
|
Date: Wed, 2 Jul 2008 22:39:56 +0100
|
||
|
Subject: [PATCH] fix-pcf50633-interrupt-work-enforce-wait-on-resume-completion.patch
|
||
|
|
||
|
Improve pcf50633 interrupt service scheduling to enforce only servicing
|
||
|
when resume action is completed
|
||
|
|
||
|
Signed-off-by: Andy Green <andy@openmoko.com>
|
||
|
---
|
||
|
drivers/i2c/chips/pcf50633.c | 67 ++++++++++++++++++++++++++++++------------
|
||
|
1 files changed, 48 insertions(+), 19 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c
|
||
|
index 4d92c84..33e2eaf 100644
|
||
|
--- a/drivers/i2c/chips/pcf50633.c
|
||
|
+++ b/drivers/i2c/chips/pcf50633.c
|
||
|
@@ -656,6 +656,26 @@ static void pcf50633_work(struct work_struct *work)
|
||
|
|
||
|
mutex_lock(&pcf->working_lock);
|
||
|
pcf->working = 1;
|
||
|
+
|
||
|
+ dev_info(&pcf->client.dev, "pcf50633_work called with suspended = %d\n",
|
||
|
+ pcf->have_been_suspended);
|
||
|
+
|
||
|
+ /*
|
||
|
+ * If we are inside suspend -> resume completion time we don't attempt
|
||
|
+ * service until we have fully resumed. Although we could talk to the
|
||
|
+ * device as soon as I2C is up, the regs in the device which we might
|
||
|
+ * choose to modify as part of the service action have not been
|
||
|
+ * reloaded with their pre-suspend states yet. Therefore we will
|
||
|
+ * defer our service if we are called like that until our resume has
|
||
|
+ * completed.
|
||
|
+ */
|
||
|
+
|
||
|
+ if (pcf->have_been_suspended && (pcf->have_been_suspended < 2)) {
|
||
|
+ dev_info(&pcf->client.dev, "rescheduling, suspended = %d\n",
|
||
|
+ pcf->have_been_suspended);
|
||
|
+ goto reschedule;
|
||
|
+ }
|
||
|
+
|
||
|
/*
|
||
|
* datasheet says we have to read the five IRQ
|
||
|
* status regs in one transaction
|
||
|
@@ -663,30 +683,25 @@ static void pcf50633_work(struct work_struct *work)
|
||
|
ret = i2c_smbus_read_i2c_block_data(&pcf->client, PCF50633_REG_INT1, 5,
|
||
|
pcfirq);
|
||
|
if (ret != 5) {
|
||
|
- DEBUGP("Oh crap PMU IRQ register read failed -- "
|
||
|
- "retrying later %d\n", ret);
|
||
|
+ dev_info(&pcf->client.dev,
|
||
|
+ "Oh crap PMU IRQ register read failed -- "
|
||
|
+ "retrying later %d\n", ret);
|
||
|
/*
|
||
|
- * this situation can happen during resume, just defer
|
||
|
- * handling the interrupt until enough I2C is up we can
|
||
|
- * actually talk to the PMU. We can't just ignore this
|
||
|
- * because we are on a falling edge interrupt and our
|
||
|
- * PMU interrupt source does not clear until we read these
|
||
|
- * interrupt source registers.
|
||
|
+ * it shouldn't fail, we no longer attempt to use I2C while
|
||
|
+ * it can be suspended. But we don't have much option but to
|
||
|
+ * retry if if it ever did fail, because if we don't service
|
||
|
+ * the interrupt to clear it, we will never see another PMU
|
||
|
+ * interrupt edge.
|
||
|
*/
|
||
|
- if (!schedule_work(&pcf->work) && !pcf->working)
|
||
|
- dev_dbg(&pcf->client.dev, "work item may be lost\n");
|
||
|
-
|
||
|
- /* we don't put the device here, hold it for next time */
|
||
|
- mutex_unlock(&pcf->working_lock);
|
||
|
- /* don't spew, delaying whatever else is happening */
|
||
|
- msleep(1);
|
||
|
- return;
|
||
|
+ goto reschedule;
|
||
|
}
|
||
|
|
||
|
/* hey did we just resume? */
|
||
|
|
||
|
if (pcf->have_been_suspended) {
|
||
|
+ /* resume is officially over now then */
|
||
|
pcf->have_been_suspended = 0;
|
||
|
+
|
||
|
/*
|
||
|
* grab a copy of resume interrupt reasons
|
||
|
* from pcf50633 POV
|
||
|
@@ -1045,6 +1060,19 @@ static void pcf50633_work(struct work_struct *work)
|
||
|
input_sync(pcf->input_dev);
|
||
|
put_device(&pcf->client.dev);
|
||
|
mutex_unlock(&pcf->working_lock);
|
||
|
+
|
||
|
+ return;
|
||
|
+
|
||
|
+reschedule:
|
||
|
+ /* don't spew, delaying whatever else is happening */
|
||
|
+ msleep(100);
|
||
|
+
|
||
|
+ dev_info(&pcf->client.dev, "rescheduling interrupt service\n");
|
||
|
+ if (!schedule_work(&pcf->work))
|
||
|
+ dev_err(&pcf->client.dev, "int service reschedule failed\n");
|
||
|
+
|
||
|
+ /* we don't put the device here, hold it for next time */
|
||
|
+ mutex_unlock(&pcf->working_lock);
|
||
|
}
|
||
|
|
||
|
static irqreturn_t pcf50633_irq(int irq, void *_pcf)
|
||
|
@@ -1052,10 +1080,11 @@ static irqreturn_t pcf50633_irq(int irq, void *_pcf)
|
||
|
struct pcf50633_data *pcf = _pcf;
|
||
|
|
||
|
DEBUGP("entering(irq=%u, pcf=%p): scheduling work\n", irq, _pcf);
|
||
|
+ dev_info(&pcf->client.dev, "pcf50633_irq scheduling work\n");
|
||
|
|
||
|
get_device(&pcf->client.dev);
|
||
|
if (!schedule_work(&pcf->work) && !pcf->working)
|
||
|
- dev_dbg(&pcf->client.dev, "work item may be lost\n");
|
||
|
+ dev_err(&pcf->client.dev, "work item may be lost\n");
|
||
|
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
@@ -2244,9 +2273,9 @@ static int pcf50633_suspend(struct device *dev, pm_message_t state)
|
||
|
int pcf50633_ready(struct pcf50633_data *pcf)
|
||
|
{
|
||
|
if (!pcf)
|
||
|
- return -EBUSY;
|
||
|
+ return -EACCES;
|
||
|
|
||
|
- if (pcf->have_been_suspended)
|
||
|
+ if (pcf->have_been_suspended && (pcf->have_been_suspended < 3))
|
||
|
return -EBUSY;
|
||
|
|
||
|
return 0;
|
||
|
--
|
||
|
1.5.6.5
|
||
|
|