Use an enum to define pcf50633 suspend / resume state.
Add PCF50633_SS_RESUMING_BUT_NOT_US_YET to be the state
early in resume: add platform driver resume function just
to set this state so we can differentiate between early
resume and late suspend.

Signed-off-by: Andy Green <[EMAIL PROTECTED]>
---

 drivers/i2c/chips/pcf50633.c |   73 ++++++++++++++++++++++++++++++++++--------
 1 files changed, 59 insertions(+), 14 deletions(-)

diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c
index 2e5981d..c014384 100644
--- a/drivers/i2c/chips/pcf50633.c
+++ b/drivers/i2c/chips/pcf50633.c
@@ -108,6 +108,16 @@ enum charger_type {
 
 #define MAX_ADC_FIFO_DEPTH 8
 
+enum pcf50633_suspend_states {
+       PCF50633_SS_RUNNING,
+       PCF50633_SS_STARTING_SUSPEND,
+       PCF50633_SS_COMPLETED_SUSPEND,
+       PCF50633_SS_RESUMING_BUT_NOT_US_YET,
+       PCF50633_SS_STARTING_RESUME,
+       PCF50633_SS_COMPLETED_RESUME,
+};
+
+
 struct pcf50633_data {
        struct i2c_client client;
        struct pcf50633_platform_data *pdata;
@@ -122,9 +132,9 @@ struct pcf50633_data {
        int allow_close;
        int onkey_seconds;
        int irq;
-       int have_been_suspended;
+       enum pcf50633_suspend_states suspend_state;
        int usb_removal_count;
-       unsigned char pcfirq_resume[5];
+       u8 pcfirq_resume[5];
        int probe_completed;
 
        /* if he pulls battery while charging, we notice that and correctly
@@ -191,7 +201,7 @@ static struct platform_device *pcf50633_pdev;
 
 static int __reg_write(struct pcf50633_data *pcf, u_int8_t reg, u_int8_t val)
 {
-       if (pcf->have_been_suspended == 1) {
+       if (pcf->suspend_state == PCF50633_SS_COMPLETED_SUSPEND) {
                dev_err(&pcf->client.dev, "__reg_write while suspended\n");
                dump_stack();
        }
@@ -213,7 +223,7 @@ static int32_t __reg_read(struct pcf50633_data *pcf, 
u_int8_t reg)
 {
        int32_t ret;
 
-       if (pcf->have_been_suspended == 1) {
+       if (pcf->suspend_state == PCF50633_SS_COMPLETED_SUSPEND) {
                dev_err(&pcf->client.dev, "__reg_read while suspended\n");
                dump_stack();
        }
@@ -630,7 +640,8 @@ static void pcf50633_work_usbcurlim(struct work_struct 
*work)
        /* we got a notification from USB stack before we completed resume...
         * that can only make trouble, reschedule for a retry
         */
-       if (pcf->have_been_suspended && (pcf->have_been_suspended < 3))
+       if (pcf->suspend_state &&
+                    (pcf->suspend_state < PCF50633_SS_COMPLETED_RESUME))
                goto reschedule;
 
        /*
@@ -751,6 +762,21 @@ static void pcf50633_work(struct work_struct *work)
        pcf->working = 1;
 
        /*
+        * if we are presently suspending, we are not in a position to deal
+        * with pcf50633 interrupts at all.
+        *
+        * Because we didn't clear the int pending registers, there will be
+        * no edge / interrupt waiting for us when we wake.  But it is OK
+        * because at the end of our resume, we call this workqueue function
+        * gratuitously, clearing the pending register and re-enabling
+        * servicing this interrupt.
+        */
+
+       if ((pcf->suspend_state == PCF50633_SS_STARTING_SUSPEND) ||
+           (pcf->suspend_state == PCF50633_SS_COMPLETED_SUSPEND))
+               goto bail;
+
+       /*
         * 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
@@ -1155,8 +1181,9 @@ reschedule:
        /* EXCEPTION: if we are in the middle of suspending, we don't have
         * time to hang around since we may be turned off core 1V3 already
         */
-       if (pcf->have_been_suspended != 1) {
-               msleep(50);
+       if ((pcf->suspend_state != PCF50633_SS_STARTING_SUSPEND) &&
+           (pcf->suspend_state != PCF50633_SS_COMPLETED_SUSPEND)) {
+               msleep(10);
                dev_info(&pcf->client.dev, "rescheduling interrupt service\n");
        }
        if (!schedule_work(&pcf->work))
@@ -2322,8 +2349,9 @@ static int pcf50633_suspend(struct device *dev, 
pm_message_t state)
 
        /* we suspend once (!) as late as possible in the suspend sequencing */
 
-       if ((state.event != PM_EVENT_SUSPEND) || (pcf->have_been_suspended))
-               return 0;
+       if ((state.event != PM_EVENT_SUSPEND) ||
+           (pcf->suspend_state != PCF50633_SS_RUNNING))
+               return -EBUSY;
 
        /* The general idea is to power down all unused power supplies,
         * and then mask all PCF50633 interrupt sources but EXTONR, ONKEYF
@@ -2387,7 +2415,7 @@ static int pcf50633_suspend(struct device *dev, 
pm_message_t state)
        if (ret)
                dev_err(dev, "Failed to set wake masks :-( %d\n", ret);
 
-       pcf->have_been_suspended = 1;
+       pcf->suspend_state = PCF50633_SS_COMPLETED_SUSPEND;
 
        mutex_unlock(&pcf->lock);
 
@@ -2400,7 +2428,8 @@ int pcf50633_ready(struct pcf50633_data *pcf)
        if (!pcf)
                return -EACCES;
 
-       if (pcf->have_been_suspended && (pcf->have_been_suspended < 3))
+       if ((pcf->suspend_state != PCF50633_SS_RUNNING) &&
+           (pcf->suspend_state < PCF50633_SS_COMPLETED_RESUME))
                return -EBUSY;
 
        return 0;
@@ -2435,10 +2464,10 @@ static int pcf50633_resume(struct device *dev)
        u8 res[5];
 
        dev_info(dev, "pcf50633_resume suspended on entry = %d\n",
-                                                     pcf->have_been_suspended);
+                                                (int)pcf->suspend_state);
        mutex_lock(&pcf->lock);
 
-       pcf->have_been_suspended = 2; /* resuming */
+       pcf->suspend_state = PCF50633_SS_STARTING_RESUME;
 
        /* these guys get reset while pcf50633 is suspend state, refresh */
 
@@ -2471,7 +2500,7 @@ static int pcf50633_resume(struct device *dev)
        if (ret)
                dev_err(dev, "Failed to set int masks :-( %d\n", ret);
 
-       pcf->have_been_suspended = 3; /* resume completed */
+       pcf->suspend_state = PCF50633_SS_COMPLETED_RESUME;
 
        mutex_unlock(&pcf->lock);
 
@@ -2505,6 +2534,21 @@ static struct i2c_driver pcf50633_driver = {
        .detach_client  = pcf50633_detach_client,
 };
 
+/* we have this purely to capture an early indication that we are coming out
+ * of suspend, before our device resume got called; async interrupt service is
+ * interested in this
+ */
+
+static int pcf50633_plat_resume(struct platform_device *pdev)
+{
+       /* i2c_get_clientdata(to_i2c_client(&pdev->dev)) returns NULL at this
+        * early resume time so we have to use pcf50633_global
+        */
+       pcf50633_global->suspend_state = PCF50633_SS_RESUMING_BUT_NOT_US_YET;
+
+       return 0;
+}
+
 /* platform driver, since i2c devices don't have platform_data */
 static int __init pcf50633_plat_probe(struct platform_device *pdev)
 {
@@ -2526,6 +2570,7 @@ static int pcf50633_plat_remove(struct platform_device 
*pdev)
 static struct platform_driver pcf50633_plat_driver = {
        .probe  = pcf50633_plat_probe,
        .remove = pcf50633_plat_remove,
+       .resume_early = pcf50633_plat_resume,
        .driver = {
                .owner  = THIS_MODULE,
                .name   = "pcf50633",


Reply via email to