summaryrefslogtreecommitdiff
path: root/recipes/linux/linux-openmoko-shr-drm-devel/0001-Fix-s3c-adc-suspend.patch
blob: 6627e698f0ed14c9b7311f26ecdb1305c18fdcc6 (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
From e22e97d2266d100f501f1e22275595eb68dd3e6f Mon Sep 17 00:00:00 2001
From: Vasily Khoruzhick <anarsoul@gmail.com>
Date: Thu, 1 Oct 2009 20:58:18 -0500
Subject: [PATCH 1/7] Fix s3c-adc suspend

Fix for a bug that shows when the s3c2410 TS driver requests
a conversion from the s3c-adc driver and the machine goes into suspend.
In this case the touchscreen stops working.

Note: Nelson edited the original patch with a few small changes.

Reported-by: Radek Polak <psonek2@seznam.cz>
Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
Signed-off-by: Nelson Castillo <arhuaco@freaks-unidos.net>
---
 arch/arm/plat-s3c24xx/adc.c |   42 +++++++++++++++++++++++++++++++++++++-----
 1 files changed, 37 insertions(+), 5 deletions(-)

diff --git a/arch/arm/plat-s3c24xx/adc.c b/arch/arm/plat-s3c24xx/adc.c
index 9056bcc..4ce45c5 100644
--- a/arch/arm/plat-s3c24xx/adc.c
+++ b/arch/arm/plat-s3c24xx/adc.c
@@ -43,6 +43,7 @@ struct s3c_adc_client {
 	unsigned int		 nr_samples;
 	unsigned char		 is_ts;
 	unsigned char		 channel;
+	unsigned selected;
 
 	void	(*select_cb)(unsigned selected);
 	void	(*convert_cb)(unsigned val1, unsigned val2,
@@ -68,6 +69,7 @@ static struct adc_device *adc_dev;
 static LIST_HEAD(adc_pending);
 
 #define adc_dbg(_adc, msg...) dev_dbg(&(_adc)->pdev->dev, msg)
+#define adc_info(_adc, msg...) dev_info(&(_adc)->pdev->dev, msg)
 
 #define AUTOPST	(S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | \
 		 S3C2410_ADCTSC_XP_SEN | S3C2410_ADCTSC_AUTO_PST | \
@@ -91,7 +93,10 @@ static inline void s3c_adc_select(struct adc_device *adc,
 {
 	unsigned con = readl(adc->regs + S3C2410_ADCCON);
 
-	client->select_cb(1);
+	if (!client->selected) {
+		client->selected = 1;
+		client->select_cb(1);
+	}
 
 	con &= ~S3C2410_ADCCON_MUXMASK;
 	con &= ~S3C2410_ADCCON_STDBM;
@@ -115,12 +120,9 @@ void s3c_adc_try(struct adc_device *adc)
 {
 	struct s3c_adc_client *next = adc->ts_pend;
 
-	if (!next && !list_empty(&adc_pending)) {
+	if (!next && !list_empty(&adc_pending))
 		next = list_first_entry(&adc_pending,
 					struct s3c_adc_client, pend);
-		list_del(&next->pend);
-	} else
-		adc->ts_pend = NULL;
 
 	if (next) {
 		adc_dbg(adc, "new client is %p\n", next);
@@ -229,9 +231,16 @@ static irqreturn_t s3c_adc_irq(int irq, void *pw)
 		/* fire another conversion for this */
 
 		client->select_cb(1);
+		client->selected = 1;
 		s3c_adc_convert(adc);
 	} else {
 		local_irq_save(flags);
+		client->selected = 0;
+		if (!adc->cur->is_ts)
+			list_del(&adc->cur->pend);
+		else
+			adc->ts_pend = NULL;
+
 		(client->select_cb)(0);
 		adc->cur = NULL;
 
@@ -341,20 +350,43 @@ static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state)
 	writel(con, adc->regs + S3C2410_ADCCON);
 
 	clk_disable(adc->clk);
+	disable_irq(IRQ_ADC);
+
+	if (!list_empty(&adc_pending) || adc->ts_pend)
+		adc_info(adc, "%s:We still have clients pending\n", __func__);
 
 	return 0;
 }
 
+static struct work_struct resume_work;
+
+static void adc_resume_work(struct work_struct *work)
+{
+	struct adc_device *adc = platform_get_drvdata(adc_dev->pdev);
+
+	adc_info(adc, "%s:We still have clients pending\n", __func__);
+	s3c_adc_try(adc_dev);
+}
+
 static int s3c_adc_resume(struct platform_device *pdev)
 {
 	struct adc_device *adc = platform_get_drvdata(pdev);
 
+	enable_irq(IRQ_ADC);
 	clk_enable(adc->clk);
 
 	writel(adc->prescale | S3C2410_ADCCON_PRSCEN,
 	       adc->regs + S3C2410_ADCCON);
 	writel(adc->delay, adc->regs + S3C2410_ADCDLY);
 
+	/* Schedule task if there are clients pending. */
+	if (!list_empty(&adc_pending) || adc_dev->ts_pend) {
+		INIT_WORK(&resume_work, adc_resume_work);
+		if (!schedule_work(&resume_work))
+			dev_err(&pdev->dev,
+				"Failed to schedule adc_resume work!\n");
+	}
+
 	return 0;
 }
 
-- 
1.6.5.2