[PATCH 3/9] fix-pcf50633-interrupt-work-enforce-wait-on-resume-completion.patch

Andy Green andy at openmoko.com
Fri Jun 13 23:16:21 CEST 2008


Improve pcf50633 interrupt service scheduling to enforce only servicing
when resume action is completed

Signed-off-by: Andy Green <andy at 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;





More information about the openmoko-kernel mailing list