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