aboutsummaryrefslogtreecommitdiff
path: root/drivers/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/chips/pcf50633.c89
1 files changed, 63 insertions, 26 deletions
diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c
index 87a3003be4b..33c4ef4fd88 100644
--- a/drivers/i2c/chips/pcf50633.c
+++ b/drivers/i2c/chips/pcf50633.c
@@ -632,6 +632,11 @@ static void pcf50633_work_usbcurlim(struct work_struct *work)
mutex_lock(&pcf->working_lock_usb_curlimit);
+ /* just can't cope with it if we are suspending, don't reschedule */
+ if ((pcf->suspend_state == PCF50633_SS_STARTING_SUSPEND) ||
+ (pcf->suspend_state == PCF50633_SS_COMPLETED_SUSPEND))
+ goto bail;
+
dev_info(&pcf->client.dev, "pcf50633_work_usbcurlim\n");
if (!pcf->probe_completed)
@@ -663,7 +668,8 @@ bail:
reschedule:
dev_info(&pcf->client.dev, "pcf50633_work_usbcurlim rescheduling\n");
if (!schedule_work(&pcf->work_usb_curlimit))
- dev_err(&pcf->client.dev, "work item may be lost\n");
+ dev_err(&pcf->client.dev, "curlim reschedule work "
+ "already queued\n");
mutex_unlock(&pcf->working_lock_usb_curlimit);
/* don't spew, delaying whatever else is happening */
@@ -700,7 +706,7 @@ int pcf50633_notify_usb_current_limit_change(struct pcf50633_data *pcf,
pcf->pending_curlimit = ma;
if (!schedule_work(&pcf->work_usb_curlimit))
- dev_err(&pcf->client.dev, "work item may be lost\n");
+ dev_err(&pcf->client.dev, "curlim work item already queued\n");
return 0;
}
@@ -727,6 +733,9 @@ static void pcf50633_work_nobat(struct work_struct *work)
while (1) {
msleep(1000);
+ if (pcf->suspend_state != PCF50633_SS_RUNNING)
+ continue;
+
/* there's a battery in there now? */
if (reg_read(pcf, PCF50633_REG_MBCS3) & 0x40) {
@@ -761,6 +770,10 @@ static void pcf50633_work(struct work_struct *work)
mutex_lock(&pcf->working_lock);
pcf->working = 1;
+ /* sanity */
+ if (!&pcf->client.dev)
+ goto bail;
+
/*
* if we are presently suspending, we are not in a position to deal
* with pcf50633 interrupts at all.
@@ -784,42 +797,55 @@ static void pcf50633_work(struct work_struct *work)
* reloaded with their pre-suspend states yet. Therefore we will
* defer our service if we are called like that until our resume has
* completed.
+ *
+ * This shouldn't happen any more because we disable servicing this
+ * interrupt in suspend and don't re-enable it until resume is
+ * completed.
*/
- if (pcf->have_been_suspended && (pcf->have_been_suspended < 3))
+ if (pcf->suspend_state &&
+ (pcf->suspend_state != PCF50633_SS_COMPLETED_RESUME))
+ goto reschedule;
+
+ /* this is the case early in resume! Sanity check! */
+ if (i2c_get_clientdata(&pcf->client) == NULL)
goto reschedule;
/*
- * datasheet says we have to read the five IRQ
- * status regs in one transaction
- */
- ret = i2c_smbus_read_i2c_block_data(&pcf->client, PCF50633_REG_INT1, 5,
- pcfirq);
- if (ret != 5) {
+ * datasheet says we have to read the five IRQ
+ * status regs in one transaction
+ */
+ ret = i2c_smbus_read_i2c_block_data(&pcf->client,
+ PCF50633_REG_INT1,
+ sizeof(pcfirq),
+ pcfirq);
+ if (ret != sizeof(pcfirq)) {
dev_info(&pcf->client.dev,
"Oh crap PMU IRQ register read failed -- "
"retrying later %d\n", ret);
/*
- * 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.
+ * 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.
*/
goto reschedule;
}
- /* hey did we just resume? */
-
- if (pcf->have_been_suspended) {
- /* resume is officially over now then */
- pcf->have_been_suspended = 0;
+ /* hey did we just resume? (because we don't get here unless we are
+ * running normally or the first call after resumption)
+ */
+ if (pcf->suspend_state != PCF50633_SS_RUNNING) {
/*
- * grab a copy of resume interrupt reasons
- * from pcf50633 POV
- */
+ * grab a copy of resume interrupt reasons
+ * from pcf50633 POV
+ */
memcpy(pcf->pcfirq_resume, pcfirq, sizeof(pcf->pcfirq_resume));
+
+ /* pcf50633 resume is really really over now then */
+ pcf->suspend_state = PCF50633_SS_RUNNING;
}
if (!pcf->coldplug_done) {
@@ -1168,6 +1194,7 @@ static void pcf50633_work(struct work_struct *work)
DEBUGPC("\n");
+bail:
pcf->working = 0;
input_sync(pcf->input_dev);
put_device(&pcf->client.dev);
@@ -1201,7 +1228,7 @@ static irqreturn_t pcf50633_irq(int irq, void *_pcf)
get_device(&pcf->client.dev);
if (!schedule_work(&pcf->work) && !pcf->working)
- dev_err(&pcf->client.dev, "work item may be lost\n");
+ dev_err(&pcf->client.dev, "pcf irq work already queued\n");
return IRQ_HANDLED;
}
@@ -2303,7 +2330,7 @@ int pcf50633_report_resumers(struct pcf50633_data *pcf, char *buf)
char *end = buf;
int n;
- for (n = 0; n < 40; n++)
+ for (n = 0; n < ARRAY_SIZE(int_names); n++)
if (int_names[n]) {
if (pcf->pcfirq_resume[n >> 3] & (1 >> (n & 7)))
end += sprintf(end, " * %s\n", int_names[n]);
@@ -2352,6 +2379,15 @@ static int pcf50633_suspend(struct device *dev, pm_message_t state)
mutex_lock(&pcf->lock);
+ pcf->suspend_state = PCF50633_SS_STARTING_SUSPEND;
+
+ /* we are not going to service any further interrupts until we
+ * resume. If the IRQ workqueue is still pending in the background,
+ * it will bail when it sees we set suspend state above
+ */
+
+ disable_irq(pcf->irq);
+
/* Save all registers that don't "survive" standby state */
pcf->standby_regs.ooctim2 = __reg_read(pcf, PCF50633_REG_OOCTIM2);
@@ -2495,6 +2531,8 @@ static int pcf50633_resume(struct device *dev)
pcf->suspend_state = PCF50633_SS_COMPLETED_RESUME;
+ enable_irq(pcf->irq);
+
mutex_unlock(&pcf->lock);
/* gratuitous call to PCF work function, in the case that the PCF
@@ -2504,8 +2542,7 @@ static int pcf50633_resume(struct device *dev)
*/
get_device(&pcf->client.dev);
- if (!schedule_work(&pcf->work) && !pcf->working)
- dev_err(&pcf->client.dev, "resume work item may be lost\n");
+ pcf50633_work(&pcf->work);
callback_all_resume_dependencies(&pcf->resume_dependency);