From dd7ff306b150c36950fa0f9dd718a1745a984d38 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Wed, 2 Jul 2008 22:41:38 +0100 Subject: [PATCH] fix-pcf50633-suspend-state-as-enum.patch 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 --- 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", -- 1.5.6.5