aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/s3c24xx/patches/0176-fix-pcf50633-suspend-state-as-enum.patch.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/s3c24xx/patches/0176-fix-pcf50633-suspend-state-as-enum.patch.patch')
-rwxr-xr-xtarget/linux/s3c24xx/patches/0176-fix-pcf50633-suspend-state-as-enum.patch.patch197
1 files changed, 197 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/patches/0176-fix-pcf50633-suspend-state-as-enum.patch.patch b/target/linux/s3c24xx/patches/0176-fix-pcf50633-suspend-state-as-enum.patch.patch
new file mode 100755
index 0000000000..243710a4d3
--- /dev/null
+++ b/target/linux/s3c24xx/patches/0176-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
+