update adc drivers: fix: 'adc get value timeout' and 'adc get zero, but voltage is...
authorkfx <kfx@rock-chips.com>
Tue, 19 Jun 2012 02:07:08 +0000 (10:07 +0800)
committerkfx <kfx@rock-chips.com>
Tue, 19 Jun 2012 02:07:08 +0000 (10:07 +0800)
drivers/adc/adc_priv.h
drivers/adc/core.c
drivers/adc/plat/rk28_adc.c
drivers/adc/plat/rk29_adc.c
drivers/adc/plat/rk30_adc.c
include/linux/adc.h

index 6ac01d5ff53805bfdf1b9f12ede2e72e8fb181b3..078bf75dd1b481e6c0860cf958e39f563e0c430f 100755 (executable)
@@ -19,6 +19,7 @@
 #include <linux/err.h>\r
 #include <linux/io.h>\r
 #include <linux/clk.h>\r
+#include <linux/workqueue.h>\r
 #include <linux/interrupt.h>\r
 #include <mach/board.h>\r
 \r
 #define adc_writel                 writel_relaxed\r
 #define adc_readl                  readl_relaxed\r
 \r
+#if 0\r
+#define adc_dbg(dev, format, arg...)           \\r
+       dev_printk(KERN_INFO , dev , format , ## arg)\r
+#else\r
+#define adc_dbg(dev, format, arg...)\r
+#endif\r
+\r
 enum read_type{\r
-        ADC_SYNC_READ,\r
+        ADC_SYNC_READ = 0,\r
         ADC_ASYNC_READ,\r
 };\r
 \r
@@ -40,17 +48,19 @@ struct adc_ops {
        void (*start)(struct adc_host *);\r
        void (*stop)(struct adc_host *);\r
        int (*read)(struct adc_host *);\r
+       void (*dump)(struct adc_host *);\r
 };\r
 struct adc_host {\r
         struct list_head entry;\r
-        struct list_head request_head;\r
+        struct list_head req_head;\r
         unsigned int is_suspended;\r
         enum host_chn_mask mask;\r
         struct device *dev;\r
-        struct adc_client *cur;\r
+        unsigned int chn;\r
         spinlock_t lock;\r
         unsigned int client_count;\r
        const struct adc_ops *ops;\r
+        struct work_struct work;\r
         unsigned long priv[0];\r
 };\r
 \r
index 011dcb0a19d1a108b28c8689ffe4e9b45f3d2640..f31dc08899f4eda3a64fc48d4381bf4b9e9d35f0 100755 (executable)
@@ -9,6 +9,7 @@
 
 struct list_head adc_host_head;
 
+static void adc_host_work(struct work_struct *work);
 struct adc_host *adc_alloc_host(struct device *dev, int extra, enum host_chn_mask mask)
 {
        struct adc_host *adc;
@@ -18,8 +19,10 @@ struct adc_host *adc_alloc_host(struct device *dev, int extra, enum host_chn_mas
                return NULL;
         adc->mask = mask;
        adc->dev = dev;
+        adc->chn = -1;
         spin_lock_init(&adc->lock);
-        INIT_LIST_HEAD(&adc->request_head);
+        INIT_LIST_HEAD(&adc->req_head);
+        INIT_WORK(&adc->work, adc_host_work);
 
         list_add_tail(&adc->entry, &adc_host_head);
 
@@ -77,36 +80,38 @@ static inline void trigger_next_adc_job_if_any(struct adc_host *adc)
 {
         struct adc_request *req = NULL;
 
-        if(list_empty(&adc->request_head))
+        if(adc->chn != -1)
                 return;
+        req = list_first_entry(&adc->req_head, struct adc_request, entry);
+        if(req){
+                adc->chn = req->client->chn;
+               adc->ops->start(adc);
+        }
 
-        req = list_first_entry(&adc->request_head, struct adc_request, entry);
-
-        if(req == NULL)
-                return;
-        list_del_init(&req->entry);
-       adc->cur = req->client;
-       kfree(req);
-       adc->ops->start(adc);
        return;
 }
+static void adc_host_work(struct work_struct *work)
+{
+        unsigned long flags;
+       struct adc_host *adc =
+               container_of(work, struct adc_host, work);
+
+       spin_lock_irqsave(&adc->lock, flags);
+        trigger_next_adc_job_if_any(adc);
+       spin_unlock_irqrestore(&adc->lock, flags);
+}
 static int adc_request_add(struct adc_host *adc, struct adc_client *client)
 {
         struct adc_request *req = NULL;
 
-        list_for_each_entry(req, &adc->request_head, entry) {
-                if(req->client->index == client->index)
-                        return 0;
-        }
         req = kzalloc(sizeof(struct adc_request), GFP_ATOMIC);
 
         if(!req)
                 return -ENOMEM;
+        INIT_LIST_HEAD(&req->entry);
         req->client = client;
-        list_add_tail(&req->entry, &adc->request_head);
-
+        list_add_tail(&req->entry, &adc->req_head);
         trigger_next_adc_job_if_any(adc);
-
         return 0;
 }
 static void
@@ -114,18 +119,41 @@ adc_sync_read_callback(struct adc_client *client, void *param, int result)
 {
         client->result = result;
 }
+static void adc_finished(struct adc_host *adc, int result)
+{
+        struct adc_request *req = NULL, *n = NULL;
+
+        adc_dbg(adc->dev, "chn[%d] read value: %d\n", adc->chn, result);
+        adc->ops->stop(adc);
+        list_for_each_entry_safe(req, n, &adc->req_head, entry) {
+                if(req->client->chn == adc->chn){
+                        if(req->client->flags & (1<<ADC_ASYNC_READ)){
+                                req->client->callback(req->client, req->client->callback_param, result);
+                        }
+                        if(req->client->flags & (1<<ADC_SYNC_READ)){
+                                adc_sync_read_callback(req->client, NULL, result);
+                                req->client->is_finished = 1;
+                                wake_up(&req->client->wait);
+                        }
+                        req->client->result = result;
+                        req->client->flags = 0;
+                        list_del_init(&req->entry);
+                        kfree(req);
+                }
+        }
+        adc->chn = -1;
+}
 void adc_core_irq_handle(struct adc_host *adc)
 {
-        int result = adc->ops->read(adc);
+        int result = 0;
 
        spin_lock(&adc->lock);
-        adc->ops->stop(adc);
-        adc->cur->callback(adc->cur, adc->cur->callback_param, result);
-        adc_sync_read_callback(adc->cur, NULL, result);
-        adc->cur->is_finished = 1;
-        wake_up(&adc->cur->wait);
+        result = adc->ops->read(adc);
 
-        trigger_next_adc_job_if_any(adc);
+        adc_finished(adc, result);
+
+        if(!list_empty(&adc->req_head))
+                schedule_work(&adc->work);
        spin_unlock(&adc->lock);
 }
 
@@ -141,29 +169,45 @@ int adc_host_read(struct adc_client *client, enum read_type type)
        }
         adc = client->adc;
        if(adc->is_suspended == 1) {
-               dev_dbg(adc->dev, "system enter sleep\n");
+               dev_err(adc->dev, "adc is in suspend state\n");
                return -EIO;
        }
 
        spin_lock_irqsave(&adc->lock, flags);
-        ret = adc_request_add(adc, client);
-        if(ret < 0){
-                spin_unlock_irqrestore(&adc->lock, flags);
-                dev_err(adc->dev, "No memory for req\n");
-                return ret;
+        if(client->flags & (1<<type)){
+               spin_unlock_irqrestore(&adc->lock, flags);
+                adc_dbg(adc->dev, "req is exist: %s, client->index: %d\n", 
+                                (type == ADC_ASYNC_READ)?"async_read":"sync_read", client->index);
+                return -EEXIST;
+        }else if(client->flags != 0){
+                client->flags |= 1<<type;
+        }else{
+                client->flags = 1<<type;
+                ret = adc_request_add(adc, client);
+                if(ret < 0){
+                        spin_unlock_irqrestore(&adc->lock, flags);
+                        dev_err(adc->dev, "fail to add request\n");
+                        return ret;
+                }
+        }
+        if(type == ADC_ASYNC_READ){
+               spin_unlock_irqrestore(&adc->lock, flags);
+                return 0;
         }
         client->is_finished = 0;
        spin_unlock_irqrestore(&adc->lock, flags);
 
-        if(type == ADC_ASYNC_READ)
-                return 0;
-
         tmo = wait_event_timeout(client->wait, ( client->is_finished == 1 ), msecs_to_jiffies(ADC_READ_TMO));
-        if(tmo <= 0) {
-                adc->ops->stop(adc);
-                dev_dbg(adc->dev, "get adc value timeout\n");
+       spin_lock_irqsave(&adc->lock, flags);
+        if(unlikely((tmo <= 0) && (client->is_finished == 0))) {
+                if(adc->ops->dump)
+                        adc->ops->dump(adc);
+                dev_err(adc->dev, "get adc value timeout.................................\n");
+                adc_finished(adc, -1);
+               spin_unlock_irqrestore(&adc->lock, flags);
                 return -ETIMEDOUT;
         } 
+       spin_unlock_irqrestore(&adc->lock, flags);
 
         return client->result;
 }
index e5ac7a35c1a3b3ed3780e81e446ac300a69a29c3..859d56fdb2a341427e06c43a44519a2937690ac2 100755 (executable)
@@ -21,7 +21,7 @@ struct rk28_adc_device {
 static void rk28_adc_start(struct adc_host *adc)\r
 {\r
        struct rk28_adc_device *dev  = adc_priv(adc);\r
-       int chn = adc->cur->chn;\r
+       int chn = adc->chn;\r
        \r
        writel(ADC_CTRL_IRQ_ENABLE|ADC_CTRL_POWER_UP|ADC_CTRL_START|ADC_CTRL_CH(chn),\r
                dev->regs + ADC_CTRL);\r
index db45d1b8d6b4d3f753b61e312d3a88c641a9b535..bf511ca8b4252079610bdbee3b829c853038cdfc 100755 (executable)
@@ -21,7 +21,7 @@ struct rk29_adc_device {
 static void rk29_adc_start(struct adc_host *adc)\r
 {\r
        struct rk29_adc_device *dev  = adc_priv(adc);\r
-       int chn = adc->cur->chn;\r
+       int chn = adc->chn;\r
 \r
        writel(0, dev->regs + ADC_CTRL);\r
        writel(ADC_CTRL_POWER_UP|ADC_CTRL_CH(chn), dev->regs + ADC_CTRL);\r
index 90a49d8de0af4d17f2e8eb8242b9eab0d035354e..fbfb26a940b537323271876fa42a5cb41f1357f7 100755 (executable)
@@ -19,12 +19,22 @@ struct rk30_adc_device {
        struct resource         *ioarea;\r
        struct adc_host         *adc;\r
 };\r
+static void rk30_adc_dump(struct adc_host *adc)\r
+{\r
+       struct rk30_adc_device *dev  = adc_priv(adc);\r
+\r
+        dev_info(adc->dev, "[0x00-0x0c]: 0x%08x 0x%08x 0x%08x 0x%08x\n",\r
+                        adc_readl(dev->regs + 0x00),\r
+                        adc_readl(dev->regs + 0x04),\r
+                        adc_readl(dev->regs + 0x08),\r
+                        adc_readl(dev->regs + 0x0c));\r
+}\r
 static void rk30_adc_start(struct adc_host *adc)\r
 {\r
        struct rk30_adc_device *dev  = adc_priv(adc);\r
-       int chn = adc->cur->chn;\r
+       int chn = adc->chn;\r
 \r
-       adc_writel(0, dev->regs + ADC_CTRL);\r
+       //adc_writel(0, dev->regs + ADC_CTRL);\r
         adc_writel(0x08, dev->regs + ADC_DELAY_PU_SOC);\r
        adc_writel(ADC_CTRL_POWER_UP|ADC_CTRL_CH(chn)|ADC_CTRL_IRQ_ENABLE, dev->regs + ADC_CTRL);\r
 \r
@@ -53,8 +63,11 @@ static const struct adc_ops rk30_adc_ops = {
        .start          = rk30_adc_start,\r
        .stop           = rk30_adc_stop,\r
        .read           = rk30_adc_read,\r
+       .dump           = rk30_adc_dump,\r
 };\r
 #ifdef ADC_TEST\r
+#define CHN_NR  3\r
+struct workqueue_struct *adc_wq;\r
 struct adc_test_data {\r
        struct adc_client *client;\r
        struct timer_list timer;\r
@@ -62,14 +75,17 @@ struct adc_test_data {
 };\r
 static void callback(struct adc_client *client, void *param, int result)\r
 {\r
-       dev_info(client->adc->dev, "[chn%d] async_read = %d\n", client->chn, result);\r
+        if(result < 70)\r
+               dev_info(client->adc->dev, "[chn%d] async_read = %d\n", client->chn, result);\r
+        else\r
+               dev_dbg(client->adc->dev, "[chn%d] async_read = %d\n", client->chn, result);\r
        return;\r
 }\r
 static void adc_timer(unsigned long data)\r
 {\r
         struct adc_test_data *test=(struct adc_test_data *)data;\r
        \r
-       schedule_work(&test->timer_work);\r
+       queue_work(adc_wq, &test->timer_work);\r
        add_timer(&test->timer);\r
 }\r
 static void adc_timer_work(struct work_struct *work)\r
@@ -79,20 +95,26 @@ static void adc_timer_work(struct work_struct *work)
                                                timer_work);\r
        adc_async_read(test->client);\r
        sync_read = adc_sync_read(test->client);\r
-       dev_info(test->client->adc->dev, "[chn%d] sync_read = %d\n", test->client->chn, sync_read);\r
+        if(sync_read < 70)\r
+               dev_info(test->client->adc->dev, "[chn%d] sync_read = %d\n", test->client->chn, sync_read);\r
+        else\r
+               dev_dbg(test->client->adc->dev, "[chn%d] sync_read = %d\n", test->client->chn, sync_read);\r
 }\r
 \r
 static int rk30_adc_test(void)\r
 {\r
-       struct adc_test_data *test = NULL;\r
-\r
-       test = kzalloc(sizeof(struct adc_test_data), GFP_KERNEL);\r
-       \r
-       test->client = adc_register(1, callback, NULL);\r
-       INIT_WORK(&test->timer_work, adc_timer_work);\r
-       setup_timer(&test->timer, adc_timer, (unsigned long)test);\r
-       test->timer.expires  = jiffies + 1;\r
-       add_timer(&test->timer);\r
+        int i;\r
+       struct adc_test_data *test[CHN_NR];\r
+\r
+        adc_wq = create_singlethread_workqueue("adc_test");\r
+       for(i = 0; i < CHN_NR; i++){\r
+               test[i] = kzalloc(sizeof(struct adc_test_data), GFP_KERNEL);\r
+               test[i]->client = adc_register(i, callback, NULL);\r
+               INIT_WORK(&test[i]->timer_work, adc_timer_work);\r
+               setup_timer(&test[i]->timer, adc_timer, (unsigned long)test[i]);\r
+               test[i]->timer.expires  = jiffies + 1;\r
+               add_timer(&test[i]->timer);\r
+        }\r
        \r
        return 0;\r
 \r
index 1b61b47106ab5f50353ff9b91d4bd4ed0ba6213a..90e44913dae7ca04e09a233cfdf82b4331529d89 100755 (executable)
@@ -27,6 +27,7 @@ struct adc_client {
         unsigned int index;\r
         unsigned int chn;\r
         unsigned int is_finished;\r
+        unsigned int flags;\r
         int result;\r
        struct adc_host *adc;\r
         struct list_head list;\r