diff options
Diffstat (limited to 'target/linux/s3c24xx/patches-2.6.26/1176-fix-pcf50633-suspend-state-as-enum.patch.patch')
-rwxr-xr-x | target/linux/s3c24xx/patches-2.6.26/1176-fix-pcf50633-suspend-state-as-enum.patch.patch | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/patches-2.6.26/1176-fix-pcf50633-suspend-state-as-enum.patch.patch b/target/linux/s3c24xx/patches-2.6.26/1176-fix-pcf50633-suspend-state-as-enum.patch.patch new file mode 100755 index 0000000..243710a --- /dev/null +++ b/target/linux/s3c24xx/patches-2.6.26/1176-fix-pcf50633-suspend-state-as-enum.patch.patch @@ -0,0 +1,197 @@ +From 591a5d7e8da059de812d315b865fd5c0fa89071e Mon Sep 17 00:00:00 2001 +From: Andy Green <andy@openmoko.com> +Date: Fri, 25 Jul 2008 23:06:15 +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 <andy@openmoko.com> +--- + 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 37dfca6..87a3003 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 +@@ -1154,8 +1180,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)) +@@ -2315,8 +2342,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 +@@ -2380,7 +2408,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); + +@@ -2393,7 +2421,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; +@@ -2428,10 +2457,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 */ + +@@ -2464,7 +2493,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); + +@@ -2498,6 +2527,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) + { +@@ -2519,6 +2563,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.3 + |