From 3cb0a576425bdd2cb9b8f48f9fe39c01dfc18bbf Mon Sep 17 00:00:00 2001 From: Andy Green 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 --- 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