On Thu, Oct 1, 2009 at 4:44 AM, Vasily Khoruzhick <[email protected]> wrote:
> В сообщении от 1 октября 2009 11:10:40 автор Radek Polak написал:
>> Hi,
>> it seems that the last commits in andy-tracking broke touchscreen. After
>> few suspend/resume cycles the touchscreen stops working.
>>
>> It was confirmed by Jim Morris and me on QtMoko v12 distribution, but
>> probably everybody will be affected.
>>
>> Can i do something more about this? I can try to provide logs if needed.
>>
>> Regards
>>
>> Radek
>>
>
> I'm pretty sure it's due bug in adc driver.
> ts driver request conversion from adc driver, in this moment machine goes into
> suspend, after resume ts driver still waits for response from adc driver, but
> adc driver will never respond, as there's client active, but hardware was not
> configured to perform conversion.

Vasily, this is indeed the case.

> I've fixed this bug in my patches for rx1950, so, Nelson, you can check it out
> and push fix into your kernel branch.

I sent the attached patch to andy-tracking. It's not far from
upstream. Will you send it?

Radek: I hope this fixes the bug. Please let us know if it doesn't. I
reproduced the bug and in my tests the patch fixed it.

Thanks,
Nelson.-
From cf82bb6c889758f0e575f95e38f9dedecaa072d0 Mon Sep 17 00:00:00 2001
From: Vasily Khoruzhick <[email protected]>
Date: Thu, 1 Oct 2009 20:58:18 -0500
Subject: [PATCH] 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 <[email protected]>
Signed-off-by: Vasily Khoruzhick <[email protected]>
Signed-off-by: Nelson Castillo <[email protected]>

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;
 }
 

Reply via email to