summaryrefslogtreecommitdiff
path: root/target/linux/s3c24xx/patches/0164-fix-pcf50633-interrupt-work-enforce-wait-on-resume-c.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/s3c24xx/patches/0164-fix-pcf50633-interrupt-work-enforce-wait-on-resume-c.patch')
-rwxr-xr-xtarget/linux/s3c24xx/patches/0164-fix-pcf50633-interrupt-work-enforce-wait-on-resume-c.patch134
1 files changed, 134 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/patches/0164-fix-pcf50633-interrupt-work-enforce-wait-on-resume-c.patch b/target/linux/s3c24xx/patches/0164-fix-pcf50633-interrupt-work-enforce-wait-on-resume-c.patch
new file mode 100755
index 0000000000..6226c69db9
--- /dev/null
+++ b/target/linux/s3c24xx/patches/0164-fix-pcf50633-interrupt-work-enforce-wait-on-resume-c.patch
@@ -0,0 +1,134 @@
+From 3cb0a576425bdd2cb9b8f48f9fe39c01dfc18bbf Mon Sep 17 00:00:00 2001
+From: Andy Green <andy@openmoko.com>
+Date: Fri, 25 Jul 2008 23:06:12 +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 82ee2f0..5c9d209 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;
+--
+1.5.6.3
+