aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/s3c24xx/patches-2.6.24/1185-fix-pcf50633-interrupt-work-enforce-wait-on-resume-c.patch
blob: be03e135efcc80e2d2eafd229c4f7d5ff5800a7d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
From ac949375c7ef5882e8d3ea4f4a165b2fce943f21 Mon Sep 17 00:00:00 2001
From: Andy Green <andy@openmoko.com>
Date: Wed, 2 Jul 2008 22:39:56 +0100
Subject: [PATCH] fix-pcf50633-interrupt-work-enforce-wait-on-resume-completion.patch

Improve pcf50633 interrupt service scheduling to enforce only servicing
when resume action is completed

Signed-off-by: Andy Green <andy@openmoko.com>
---
 drivers/i2c/chips/pcf50633.c |   67 ++++++++++++++++++++++++++++++------------
 1 files changed, 48 insertions(+), 19 deletions(-)

diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c
index 4d92c84..33e2eaf 100644
--- a/drivers/i2c/chips/pcf50633.c
+++ b/drivers/i2c/chips/pcf50633.c
@@ -656,6 +656,26 @@ static void pcf50633_work(struct work_struct *work)
 
 	mutex_lock(&pcf->working_lock);
 	pcf->working = 1;
+
+	dev_info(&pcf->client.dev, "pcf50633_work called with suspended = %d\n",
+				   pcf->have_been_suspended);
+
+	/*
+	 * 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
+	 * choose to modify as part of the service action have not been
+	 * reloaded with their pre-suspend states yet.  Therefore we will
+	 * defer our service if we are called like that until our resume has
+	 * completed.
+	 */
+
+	if (pcf->have_been_suspended && (pcf->have_been_suspended < 2)) {
+		dev_info(&pcf->client.dev, "rescheduling,  suspended = %d\n",
+					   pcf->have_been_suspended);
+		goto reschedule;
+	}
+
 	/*
 	 * datasheet says we have to read the five IRQ
 	 * status regs in one transaction
@@ -663,30 +683,25 @@ static void pcf50633_work(struct work_struct *work)
 	ret = i2c_smbus_read_i2c_block_data(&pcf->client, PCF50633_REG_INT1, 5,
 					    pcfirq);
 	if (ret != 5) {
-		DEBUGP("Oh crap PMU IRQ register read failed -- "
-		       "retrying later %d\n", ret);
+		dev_info(&pcf->client.dev,
+			 "Oh crap PMU IRQ register read failed -- "
+		         "retrying later %d\n", ret);
 		/*
-		 * this situation can happen during resume, just defer
-		 * handling the interrupt until enough I2C is up we can
-		 * actually talk to the PMU.  We can't just ignore this
-		 * because we are on a falling edge interrupt and our
-		 * PMU interrupt source does not clear until we read these
-		 * interrupt source registers.
+		 * 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.
 		 */
-		if (!schedule_work(&pcf->work) && !pcf->working)
-			dev_dbg(&pcf->client.dev, "work item may be lost\n");
-
-		/* we don't put the device here, hold it for next time */
-		mutex_unlock(&pcf->working_lock);
-		/* don't spew, delaying whatever else is happening */
-		msleep(1);
-		return;
+		goto reschedule;
 	}
 
 	/* hey did we just resume? */
 
 	if (pcf->have_been_suspended) {
+		/* resume is officially over now then */
 		pcf->have_been_suspended = 0;
+
 		/*
 		 * grab a copy of resume interrupt reasons
 		 * from pcf50633 POV
@@ -1045,6 +1060,19 @@ static void pcf50633_work(struct work_struct *work)
 	input_sync(pcf->input_dev);
 	put_device(&pcf->client.dev);
 	mutex_unlock(&pcf->working_lock);
+
+	return;
+
+reschedule:
+	/* don't spew, delaying whatever else is happening */
+	msleep(100);
+
+	dev_info(&pcf->client.dev, "rescheduling interrupt service\n");
+	if (!schedule_work(&pcf->work))
+		dev_err(&pcf->client.dev, "int service reschedule failed\n");
+
+	/* we don't put the device here, hold it for next time */
+	mutex_unlock(&pcf->working_lock);
 }
 
 static irqreturn_t pcf50633_irq(int irq, void *_pcf)
@@ -1052,10 +1080,11 @@ static irqreturn_t pcf50633_irq(int irq, void *_pcf)
 	struct pcf50633_data *pcf = _pcf;
 
 	DEBUGP("entering(irq=%u, pcf=%p): scheduling work\n", irq, _pcf);
+	dev_info(&pcf->client.dev, "pcf50633_irq scheduling work\n");
 
 	get_device(&pcf->client.dev);
 	if (!schedule_work(&pcf->work) && !pcf->working)
-		dev_dbg(&pcf->client.dev, "work item may be lost\n");
+		dev_err(&pcf->client.dev, "work item may be lost\n");
 
 	return IRQ_HANDLED;
 }
@@ -2244,9 +2273,9 @@ static int pcf50633_suspend(struct device *dev, pm_message_t state)
 int pcf50633_ready(struct pcf50633_data *pcf)
 {
 	if (!pcf)
-		return -EBUSY;
+		return -EACCES;
 
-	if (pcf->have_been_suspended)
+	if (pcf->have_been_suspended && (pcf->have_been_suspended < 3))
 		return -EBUSY;
 
 	return 0;
-- 
1.5.6.5