diff options
Diffstat (limited to 'target/linux/s3c24xx/patches-2.6.26/1164-fix-pcf50633-interrupt-work-enforce-wait-on-resume-c.patch')
-rwxr-xr-x | target/linux/s3c24xx/patches-2.6.26/1164-fix-pcf50633-interrupt-work-enforce-wait-on-resume-c.patch | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/patches-2.6.26/1164-fix-pcf50633-interrupt-work-enforce-wait-on-resume-c.patch b/target/linux/s3c24xx/patches-2.6.26/1164-fix-pcf50633-interrupt-work-enforce-wait-on-resume-c.patch new file mode 100755 index 0000000000..6226c69db9 --- /dev/null +++ b/target/linux/s3c24xx/patches-2.6.26/1164-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 + |