Fix s3c-adc suspend
authorVasily Khoruzhick <anarsoul@gmail.com>
Fri, 2 Oct 2009 01:58:18 +0000 (20:58 -0500)
committerNelson Castillo <arhuaco@freaks-unidos.net>
Fri, 2 Oct 2009 02:02:02 +0000 (21:02 -0500)
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

index 9056bcc..4ce45c5 100644 (file)
@@ -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;
 }