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
|