aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorAndy Green <andy@openmoko.com>2008-11-19 17:09:47 +0000
committerAndy Green <agreen@pads.home.warmcat.com>2008-11-19 17:09:47 +0000
commit0c161ef0fc3da083e12d681b488c0f7e4ed21e25 (patch)
treeb4e9976d1c27bc01a87064c4f4f1a9fc913b71c4 /drivers
parentdaa1e772623f91a59364094f845f51a1fe9f236f (diff)
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/i2c/chips/pcf50633.c67
1 files changed, 48 insertions, 19 deletions
diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c
index 82ee2f0bf16..5c9d209fe80 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
@@ -1044,6 +1059,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)
@@ -1051,10 +1079,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;
}
@@ -2234,9 +2263,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;