add adc driver(path: drivers/adc)
authorkfx <kfx@rock-chips.com>
Fri, 12 Nov 2010 08:34:40 +0000 (16:34 +0800)
committerkfx <kfx@rock-chips.com>
Fri, 12 Nov 2010 08:34:40 +0000 (16:34 +0800)
16 files changed:
arch/arm/mach-rk29/board-rk29sdk.c
arch/arm/mach-rk29/devices.c
arch/arm/mach-rk29/devices.h
drivers/Kconfig
drivers/Makefile
drivers/adc/Kconfig [new file with mode: 0644]
drivers/adc/Makefile [new file with mode: 0644]
drivers/adc/core.c [new file with mode: 0755]
drivers/adc/plat/Kconfig [new file with mode: 0644]
drivers/adc/plat/Makefile [new file with mode: 0644]
drivers/adc/plat/rk28_adc.c [new file with mode: 0755]
drivers/adc/plat/rk28_adc.h [new file with mode: 0755]
drivers/adc/plat/rk29_adc.c [new file with mode: 0755]
drivers/adc/plat/rk29_adc.h [new file with mode: 0755]
drivers/i2c/busses/i2c-rk29.c
include/linux/adc.h [new file with mode: 0755]

index c17cd8163c5fce6214a1484f06dbf751e6e77d84..cb6a04796045b934ef427186462f99251d85d0eb 100755 (executable)
@@ -561,7 +561,9 @@ static struct platform_device *devices[] __initdata = {
     &rk29xx_device_spi0m,\r
     &rk29xx_device_spi1m,\r
 #endif\r
-\r
+#ifdef CONFIG_ADC_RK29\r
+       &rk29_device_adc,\r
+#endif\r
 #ifdef CONFIG_I2C0_RK29\r
        &rk29_device_i2c0,\r
 #endif\r
index ee664a305f84c2319efd380295be01f1e914d715..2ca57c781a3e66e93bea01f9c53584137026dbee 100755 (executable)
 #include <mach/rk29_iomap.h>
 #include <mach/rk29-dma-pl330.h> 
 #include "devices.h"
+#ifdef CONFIG_ADC_RK29
+static struct resource rk29_adc_resource[] = {
+       {
+               .start = IRQ_SARADC,
+               .end   = IRQ_SARADC,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .start = RK29_ADC_PHYS,
+               .end   = RK29_ADC_PHYS + RK29_ADC_SIZE - 1,
+               .flags = IORESOURCE_MEM,
+       },
+
+};
+
+struct platform_device rk29_device_adc = {
+       .name             = "rk29-adc",
+       .id               = -1,
+       .num_resources    = ARRAY_SIZE(rk29_adc_resource),
+       .resource         = rk29_adc_resource,
+};
+
+#endif
 #ifdef CONFIG_I2C_RK29
 static struct resource resources_i2c0[] = {
        {
index 351c695f0ea3b219519d74a164b869c675f91e02..8e476673f71429e7e0223757c295430f1f5696de 100755 (executable)
@@ -39,5 +39,6 @@ extern struct rk29_sdmmc_platform_data default_sdmmc0_data;
 extern struct rk29_sdmmc_platform_data default_sdmmc1_data;
 extern struct platform_device rk29_device_sdmmc0;
 extern struct platform_device rk29_device_sdmmc1;
+extern struct platform_device rk29_device_adc;
 
 #endif
index ea153397d1fca338c48bb4dde7a9d948b9e3551d..e973e28b8a83b62291bc72bd4741827cf39536c4 100644 (file)
@@ -52,6 +52,8 @@ source "drivers/i2c/Kconfig"
 
 source "drivers/spi/Kconfig"
 
+source "drivers/adc/Kconfig"
+
 source "drivers/fpga/Kconfig"
 
 source "drivers/headset_observe/Kconfig"
index fe5282739cba64c09a42e54e16eb6a93e9ea747c..73020396902a45d482254db9c102bcf160958b31 100644 (file)
@@ -74,6 +74,7 @@ obj-$(CONFIG_INPUT)           += input/
 obj-$(CONFIG_I2O)              += message/
 obj-$(CONFIG_RTC_LIB)          += rtc/
 obj-y                          += i2c/ media/
+obj-y                          += adc/
 obj-$(CONFIG_PPS)              += pps/
 obj-$(CONFIG_W1)               += w1/
 obj-$(CONFIG_POWER_SUPPLY)     += power/
diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig
new file mode 100644 (file)
index 0000000..d705012
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# ADC subsystem configuration
+#
+
+menuconfig ADC
+       bool "ADC support"
+       default y
+if ADC
+
+source drivers/adc/plat/Kconfig
+
+endif # ADC
diff --git a/drivers/adc/Makefile b/drivers/adc/Makefile
new file mode 100644 (file)
index 0000000..3352b91
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for the adc core.
+#
+
+obj-$(CONFIG_ADC)              += core.o
+obj-y                                  += plat/
+
diff --git a/drivers/adc/core.c b/drivers/adc/core.c
new file mode 100755 (executable)
index 0000000..9770420
--- /dev/null
@@ -0,0 +1,211 @@
+/* drivers/adc/core.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+*/
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/adc.h>
+
+
+static struct adc_host *g_adc = NULL;
+
+struct adc_host *adc_alloc_host(int extra, struct device *dev)
+{
+       struct adc_host *adc;
+       
+       adc = kzalloc(sizeof(struct adc_host) + extra, GFP_KERNEL);
+       if (!adc)
+               return NULL;
+       adc->dev = dev;
+       g_adc = adc;
+       return adc;
+}
+EXPORT_SYMBOL(adc_alloc_host);
+void adc_free_host(struct adc_host *adc)
+{
+       kfree(adc);
+       adc = NULL;
+       return;
+}
+EXPORT_SYMBOL(adc_free_host);
+
+
+struct adc_client *adc_register(int chn,
+                               void (*callback)(struct adc_client *, void *, int), 
+                               void *callback_param)
+{
+       struct adc_client *client;
+       if(!g_adc)
+       {
+               printk(KERN_ERR "adc host has not initialized\n");
+               return NULL;
+       }
+       if(chn >= MAX_ADC_CHN)
+       {
+               dev_err(g_adc->dev, "channel[%d] is greater than the maximum[%d]\n", chn, MAX_ADC_CHN);
+               return NULL;
+       }
+       client = kzalloc(sizeof(struct adc_client), GFP_KERNEL);
+       if(!client)
+       {
+               dev_err(g_adc->dev, "no memory for adc client\n");
+               return NULL;
+       }
+       client->callback = callback;
+       client->callback_param = callback_param;
+       client->chn = chn;
+
+       client->adc = g_adc;
+
+       return client;
+}
+EXPORT_SYMBOL(adc_register);
+
+void adc_unregister(struct adc_client *client)
+{
+       kfree(client);
+       client = NULL;
+       return;
+}
+EXPORT_SYMBOL(adc_unregister);
+
+
+static void trigger_next_adc_job_if_any(struct adc_host *adc)
+{
+       int head = adc->queue_head;
+
+       if (!adc->queue[head])
+               return;
+       adc->cur = adc->queue[head]->client;
+       adc->ops->start(adc);
+       return;
+}
+
+static int
+adc_enqueue_request(struct adc_host *adc, struct adc_request *req)
+{
+       int head, tail;
+
+       mutex_lock(&adc->queue_mutex);
+
+       head = adc->queue_head;
+       tail = adc->queue_tail;
+
+       if (adc->queue[tail]) {
+               mutex_unlock(&adc->queue_mutex);
+               dev_err(adc->dev, "ADC queue is full, dropping request\n");
+               return -EBUSY;
+       }
+
+       adc->queue[tail] = req;
+       if (head == tail)
+               trigger_next_adc_job_if_any(adc);
+       adc->queue_tail = (tail + 1) & (MAX_ADC_FIFO_DEPTH - 1);
+
+       mutex_unlock(&adc->queue_mutex);
+
+       return 0;
+}
+
+static void
+adc_sync_read_callback(struct adc_client *client, void *param, int result)
+{
+       struct adc_request *req = param;
+
+       client->result = result;
+       complete(&req->completion);
+}
+
+int adc_sync_read(struct adc_client *client)
+{
+       struct adc_request *req = NULL;
+       int err;
+
+       if(client == NULL) {
+               printk(KERN_ERR "client point is NULL");
+               return -EINVAL;
+       }
+       if(client->adc->is_suspended == 1) {
+               dev_dbg(client->adc->dev, "system enter sleep\n");
+               return -1;
+       }
+       req = kzalloc(sizeof(*req), GFP_KERNEL);
+       if (!req){
+               dev_err(client->adc->dev, "no memory for adc request\n");
+               return -ENOMEM;
+       }
+       req->chn = client->chn;
+       req->callback =  adc_sync_read_callback;
+       req->callback_param = req;
+       req->client = client;
+
+       init_completion(&req->completion);
+       err = adc_enqueue_request(client->adc, req);
+       if (err)
+       {
+               dev_err(client->adc->dev, "fail to enqueue request\n");
+               kfree(req);
+               return err;
+       }
+       wait_for_completion(&req->completion);
+       return client->result;
+}
+EXPORT_SYMBOL(adc_sync_read);
+
+int adc_async_read(struct adc_client *client)
+{
+       struct adc_request *req = NULL;
+       
+       if(client == NULL) {
+               printk(KERN_ERR "client point is NULL");
+               return -EINVAL;
+       }
+       if(client->adc->is_suspended == 1) {
+               dev_dbg(client->adc->dev, "system enter sleep\n");
+               return -1;
+       }
+       req = kzalloc(sizeof(*req), GFP_KERNEL);
+       if (!req) {
+               dev_err(client->adc->dev, "no memory for adc request\n");
+               return -ENOMEM;
+       }
+       req->chn = client->chn;
+       req->callback = client->callback;
+       req->callback_param = client->callback_param;
+       req->client = client;
+
+       return adc_enqueue_request(client->adc, req);
+}
+EXPORT_SYMBOL(adc_async_read);
+
+void adc_core_irq_handle(struct adc_host *adc)
+{
+       struct adc_request *req;
+       int head, res;
+
+       head = adc->queue_head;
+
+       req = adc->queue[head];
+       if (WARN_ON(!req)) {
+               dev_err(adc->dev, "adc irq: ADC queue empty!\n");
+               return;
+       }
+       adc->queue[head] = NULL;
+       adc->queue_head = (head + 1) & (MAX_ADC_FIFO_DEPTH - 1);
+       
+       res = adc->ops->read(adc);
+       adc->ops->stop(adc);
+       trigger_next_adc_job_if_any(adc);
+
+       req->callback(adc->cur, req->callback_param, res);
+       kfree(req);
+}
+EXPORT_SYMBOL(adc_core_irq_handle);
+
+
diff --git a/drivers/adc/plat/Kconfig b/drivers/adc/plat/Kconfig
new file mode 100644 (file)
index 0000000..d2b7cf2
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# Adc hardware configuration
+#
+
+choice
+       prompt "ADC hardware drivers"
+config ADC_RK28
+       bool "RK28 adc interface"
+       help
+               This supports the use of the ADC interface on rk28 processors.
+
+config ADC_RK29
+       bool "RK29 adc interface"
+       help
+               This supports the use of the ADC interface on rk29 processors.
+endchoice
diff --git a/drivers/adc/plat/Makefile b/drivers/adc/plat/Makefile
new file mode 100644 (file)
index 0000000..78558b6
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Makefile for the adc hardware drivers.
+#
+obj-$(CONFIG_ADC_RK28) += rk28_adc.o
+obj-$(CONFIG_ADC_RK29) += rk29_adc.o
+
diff --git a/drivers/adc/plat/rk28_adc.c b/drivers/adc/plat/rk28_adc.c
new file mode 100755 (executable)
index 0000000..e17f011
--- /dev/null
@@ -0,0 +1,236 @@
+/* drivers/adc/chips/rk28_adc.c\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License.\r
+*/\r
+#include <linux/kernel.h>\r
+#include <linux/module.h>\r
+#include <linux/init.h>\r
+#include <linux/device.h>\r
+#include <linux/platform_device.h>\r
+#include <linux/err.h>\r
+#include <linux/clk.h>\r
+#include <linux/interrupt.h>\r
+#include <linux/io.h>\r
+#include <linux/adc.h>\r
+#include <linux/delay.h>\r
+\r
+\r
+#include "rk28_adc.h"\r
+\r
+//#define ADC_TEST\r
+\r
+struct rk28_adc_device {\r
+       int                                     irq;\r
+       void __iomem            *regs;\r
+       struct clk *            clk;\r
+       struct resource         *ioarea;\r
+       struct adc_host         *adc;\r
+};\r
+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
+       \r
+       writel(ADC_CTRL_IRQ_ENABLE|ADC_CTRL_POWER_UP|ADC_CTRL_START|ADC_CTRL_CH(chn),\r
+               dev->regs + ADC_CTRL);\r
+}\r
+static void rk28_adc_stop(struct adc_host *adc)\r
+{\r
+       struct rk28_adc_device *dev  = adc_priv(adc);\r
+       \r
+       writel(ADC_CTRL_IRQ_STATUS, dev->regs + ADC_CTRL);\r
+}\r
+static int rk28_adc_read(struct adc_host *adc)\r
+{\r
+       struct rk28_adc_device *dev  = adc_priv(adc);\r
+\r
+       udelay(SAMPLE_RATE);\r
+       return readl(dev->regs + ADC_DATA) & ADC_DATA_MASK;\r
+}\r
+static irqreturn_t rk28_adc_irq(int irq, void *data)\r
+{\r
+       struct rk28_adc_device *dev = data;\r
+       adc_core_irq_handle(dev->adc);\r
+       return IRQ_HANDLED;\r
+}\r
+static const struct adc_ops rk28_adc_ops = {\r
+       .start          = rk28_adc_start,\r
+       .stop           = rk28_adc_stop,\r
+       .read           = rk28_adc_read,\r
+};\r
+#ifdef ADC_TEST\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
+       return;\r
+}\r
+static int rk28_adc_test(void)\r
+{\r
+       int sync_read = 0;\r
+       struct adc_client *client = adc_register(1, callback, NULL);\r
+\r
+       while(1)\r
+       {\r
+               adc_async_read(client);\r
+               udelay(20);\r
+               sync_read = adc_sync_read(client);\r
+               dev_info(client->adc->dev, "[chn%d] sync_read = %d\n", client->chn, sync_read);\r
+               udelay(20);\r
+       }\r
+       adc_unregister(client);\r
+       return 0;\r
+}\r
+#endif\r
+\r
+static int rk28_adc_probe(struct platform_device *pdev)\r
+{\r
+       struct adc_host *adc = NULL;\r
+       struct rk28_adc_device *dev;\r
+       struct resource *res;\r
+       int ret;\r
+\r
+       adc = adc_alloc_host(sizeof(struct rk28_adc_device), &pdev->dev);\r
+       if (!adc)\r
+               return -ENOMEM;\r
+       mutex_init(&adc->queue_mutex);\r
+       adc->dev = &pdev->dev;\r
+       adc->is_suspended = 0;\r
+       adc->ops = &rk28_adc_ops;\r
+       dev = adc_priv(adc);\r
+       dev->adc = adc;\r
+       dev->irq = platform_get_irq(pdev, 0);\r
+       if (dev->irq <= 0) {\r
+               dev_err(&pdev->dev, "failed to get adc irq\n");\r
+               ret = -ENOENT;\r
+               goto err_alloc;\r
+       }\r
+\r
+       ret = request_irq(dev->irq, rk28_adc_irq, 0, pdev->name, dev);\r
+       if (ret < 0) {\r
+               dev_err(&pdev->dev, "failed to attach adc irq\n");\r
+               goto err_alloc;\r
+       }\r
+       dev->clk = clk_get(&pdev->dev, "saradc");\r
+       if (IS_ERR(dev->clk)) {\r
+               dev_err(&pdev->dev, "failed to get adc clock\n");\r
+               ret = PTR_ERR(dev->clk);\r
+               goto err_irq;\r
+       }\r
+\r
+       ret = clk_set_rate(dev->clk, ADC_CLK_RATE * 1000 * 1000);\r
+       if(ret < 0) {\r
+               dev_err(&pdev->dev, "failed to set adc clk\n");\r
+               goto err_clk;\r
+       }\r
+       clk_enable(dev->clk);\r
+\r
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);\r
+       if (!res) {\r
+               dev_err(&pdev->dev, "cannot find IO resource\n");\r
+               ret = -ENOENT;\r
+               goto err_clk;\r
+       }\r
+       dev->ioarea = request_mem_region(res->start, (res->end - res->start) + 1, \r
+                                                                       pdev->name);\r
+       if(dev->ioarea == NULL) {\r
+               dev_err(&pdev->dev, "cannot request IO\n");\r
+               ret = -ENXIO;\r
+               goto err_clk;\r
+       }\r
+       dev->regs = ioremap(res->start, (res->end - res->start) + 1);\r
+       if (!dev->regs) {\r
+               dev_err(&pdev->dev, "cannot map IO\n");\r
+               ret = -ENXIO;\r
+               goto err_ioarea;\r
+       }\r
+       platform_set_drvdata(pdev, dev);\r
+       dev_info(&pdev->dev, "rk28 adc: driver initialized\n");\r
+#ifdef ADC_TEST        \r
+       rk28_adc_test();\r
+#endif\r
+       return 0;\r
+// err_iomap:\r
+//     iounmap(dev->regs);\r
+\r
+ err_ioarea:\r
+       release_resource(dev->ioarea);\r
+       kfree(dev->ioarea);\r
+       clk_disable(dev->clk);\r
+\r
+ err_clk:\r
+       clk_put(dev->clk);\r
+\r
+ err_irq:\r
+       free_irq(dev->irq, dev);\r
+\r
+ err_alloc:\r
+       adc_free_host(dev->adc);\r
+       return ret;\r
+}\r
+\r
+static int rk28_adc_remove(struct platform_device *pdev)\r
+{\r
+       struct rk28_adc_device *dev = platform_get_drvdata(pdev);\r
+\r
+       iounmap(dev->regs);\r
+       release_resource(dev->ioarea);\r
+       kfree(dev->ioarea);\r
+       free_irq(dev->irq, dev);\r
+       clk_disable(dev->clk);\r
+       clk_put(dev->clk);\r
+       adc_free_host(dev->adc);\r
+\r
+       return 0;\r
+}\r
+\r
+#ifdef CONFIG_PM\r
+static int rk28_adc_suspend(struct platform_device *pdev, pm_message_t state)\r
+{\r
+       struct rk28_adc_device *dev = platform_get_drvdata(pdev);\r
+\r
+       dev->adc->is_suspended = 1;\r
+       return 0;\r
+}\r
+\r
+static int rk28_adc_resume(struct platform_device *pdev)\r
+{\r
+       struct rk28_adc_device *dev = platform_get_drvdata(pdev);\r
+\r
+       dev->adc->is_suspended = 0;\r
+       return 0;\r
+}\r
+\r
+#else\r
+#define rk28_adc_suspend NULL\r
+#define rk28_adc_resume NULL\r
+#endif\r
+\r
+static struct platform_driver rk28_adc_driver = {\r
+       .driver         = {\r
+               .name   = "rk28-adc",\r
+               .owner  = THIS_MODULE,\r
+       },\r
+       .probe          = rk28_adc_probe,\r
+       .remove         = __devexit_p(rk28_adc_remove),\r
+       .suspend        = rk28_adc_suspend,\r
+       .resume         = rk28_adc_resume,\r
+};\r
+\r
+static int __init rk28_adc_init(void)\r
+{\r
+       return platform_driver_register(&rk28_adc_driver);\r
+}\r
+subsys_initcall(rk28_adc_init);\r
+\r
+static void __exit rk28_adc_exit(void)\r
+{\r
+       platform_driver_unregister(&rk28_adc_driver);\r
+}\r
+module_exit(rk28_adc_exit);\r
+\r
+MODULE_DESCRIPTION("Driver for ADC");\r
+MODULE_AUTHOR("kfx, kfx@rock-chips.com");\r
+MODULE_LICENSE("GPL");\r
+\r
diff --git a/drivers/adc/plat/rk28_adc.h b/drivers/adc/plat/rk28_adc.h
new file mode 100755 (executable)
index 0000000..ccd8daf
--- /dev/null
@@ -0,0 +1,32 @@
+/* drivers/adc/chips/rk28_adc.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+*/
+
+#ifndef __ASM_RK29_ADC_H
+#define __ASM_RK29_ADC_H
+
+#define ADC_DATA                       0x00
+#define ADC_DATA_MASK          0x3ff
+
+#define ADC_STAS                       0x04
+#define ADC_STAS_BUSY          (1<<0)
+
+#define ADC_CTRL                       0x08
+#define ADC_CTRL_CH(ch)                ((ch)<<0)
+#define ADC_CTRL_POWER_UP      (1<<3)
+#define ADC_CTRL_START         (1<<4)
+#define ADC_CTRL_IRQ_ENABLE    (1<<5)
+#define ADC_CTRL_IRQ_STATUS    (1<<6)
+
+#define ADC_CLK_RATE           1  //1M
+/* maximum conversion rate of 100KSPS with 1MHZ ADC converter clock.
+ * SET: real conversion rate is half of maximum conversion rate
+ */
+#define SAMPLE_RATE                    ((1000/100) * 2 /(ADC_CLK_RATE))
+
+
+#endif /* __ASM_RK29_ADC_H */
diff --git a/drivers/adc/plat/rk29_adc.c b/drivers/adc/plat/rk29_adc.c
new file mode 100755 (executable)
index 0000000..e95a355
--- /dev/null
@@ -0,0 +1,236 @@
+/* drivers/adc/chips/rk29_adc.c\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License.\r
+*/\r
+#include <linux/kernel.h>\r
+#include <linux/module.h>\r
+#include <linux/init.h>\r
+#include <linux/device.h>\r
+#include <linux/platform_device.h>\r
+#include <linux/err.h>\r
+#include <linux/clk.h>\r
+#include <linux/interrupt.h>\r
+#include <linux/io.h>\r
+#include <linux/adc.h>\r
+#include <linux/delay.h>\r
+\r
+\r
+#include "rk29_adc.h"\r
+\r
+//#define ADC_TEST\r
+\r
+struct rk29_adc_device {\r
+       int                                     irq;\r
+       void __iomem            *regs;\r
+       struct clk *            clk;\r
+       struct resource         *ioarea;\r
+       struct adc_host         *adc;\r
+};\r
+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
+       \r
+       writel(ADC_CTRL_IRQ_ENABLE|ADC_CTRL_POWER_UP|ADC_CTRL_START|ADC_CTRL_CH(chn),\r
+               dev->regs + ADC_CTRL);\r
+}\r
+static void rk29_adc_stop(struct adc_host *adc)\r
+{\r
+       struct rk29_adc_device *dev  = adc_priv(adc);\r
+       \r
+       writel(ADC_CTRL_IRQ_STATUS, dev->regs + ADC_CTRL);\r
+}\r
+static int rk29_adc_read(struct adc_host *adc)\r
+{\r
+       struct rk29_adc_device *dev  = adc_priv(adc);\r
+\r
+       udelay(SAMPLE_RATE);\r
+       return readl(dev->regs + ADC_DATA) & ADC_DATA_MASK;\r
+}\r
+static irqreturn_t rk29_adc_irq(int irq, void *data)\r
+{\r
+       struct rk29_adc_device *dev = data;\r
+       adc_core_irq_handle(dev->adc);\r
+       return IRQ_HANDLED;\r
+}\r
+static const struct adc_ops rk29_adc_ops = {\r
+       .start          = rk29_adc_start,\r
+       .stop           = rk29_adc_stop,\r
+       .read           = rk29_adc_read,\r
+};\r
+#ifdef ADC_TEST\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
+       return;\r
+}\r
+static int rk29_adc_test(void)\r
+{\r
+       int sync_read = 0;\r
+       struct adc_client *client = adc_register(1, callback, NULL);\r
+\r
+       while(1)\r
+       {\r
+               adc_async_read(client);\r
+               udelay(20);\r
+               sync_read = adc_sync_read(client);\r
+               dev_info(client->adc->dev, "[chn%d] sync_read = %d\n", client->chn, sync_read);\r
+               udelay(20);\r
+       }\r
+       adc_unregister(client);\r
+       return 0;\r
+}\r
+#endif\r
+\r
+static int rk29_adc_probe(struct platform_device *pdev)\r
+{\r
+       struct adc_host *adc = NULL;\r
+       struct rk29_adc_device *dev;\r
+       struct resource *res;\r
+       int ret;\r
+\r
+       adc = adc_alloc_host(sizeof(struct rk29_adc_device), &pdev->dev);\r
+       if (!adc)\r
+               return -ENOMEM;\r
+       mutex_init(&adc->queue_mutex);\r
+       adc->dev = &pdev->dev;\r
+       adc->is_suspended = 0;\r
+       adc->ops = &rk29_adc_ops;\r
+       dev = adc_priv(adc);\r
+       dev->adc = adc;\r
+       dev->irq = platform_get_irq(pdev, 0);\r
+       if (dev->irq <= 0) {\r
+               dev_err(&pdev->dev, "failed to get adc irq\n");\r
+               ret = -ENOENT;\r
+               goto err_alloc;\r
+       }\r
+\r
+       ret = request_irq(dev->irq, rk29_adc_irq, 0, pdev->name, dev);\r
+       if (ret < 0) {\r
+               dev_err(&pdev->dev, "failed to attach adc irq\n");\r
+               goto err_alloc;\r
+       }\r
+       dev->clk = clk_get(&pdev->dev, "saradc");\r
+       if (IS_ERR(dev->clk)) {\r
+               dev_err(&pdev->dev, "failed to get adc clock\n");\r
+               ret = PTR_ERR(dev->clk);\r
+               goto err_irq;\r
+       }\r
+\r
+       ret = clk_set_rate(dev->clk, ADC_CLK_RATE * 1000 * 1000);\r
+       if(ret < 0) {\r
+               dev_err(&pdev->dev, "failed to set adc clk\n");\r
+               goto err_clk;\r
+       }\r
+       clk_enable(dev->clk);\r
+\r
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);\r
+       if (!res) {\r
+               dev_err(&pdev->dev, "cannot find IO resource\n");\r
+               ret = -ENOENT;\r
+               goto err_clk;\r
+       }\r
+       dev->ioarea = request_mem_region(res->start, (res->end - res->start) + 1, \r
+                                                                       pdev->name);\r
+       if(dev->ioarea == NULL) {\r
+               dev_err(&pdev->dev, "cannot request IO\n");\r
+               ret = -ENXIO;\r
+               goto err_clk;\r
+       }\r
+       dev->regs = ioremap(res->start, (res->end - res->start) + 1);\r
+       if (!dev->regs) {\r
+               dev_err(&pdev->dev, "cannot map IO\n");\r
+               ret = -ENXIO;\r
+               goto err_ioarea;\r
+       }\r
+       platform_set_drvdata(pdev, dev);\r
+       dev_info(&pdev->dev, "rk29 adc: driver initialized\n");\r
+#ifdef ADC_TEST        \r
+       rk29_adc_test();\r
+#endif\r
+       return 0;\r
+// err_iomap:\r
+//     iounmap(dev->regs);\r
+\r
+ err_ioarea:\r
+       release_resource(dev->ioarea);\r
+       kfree(dev->ioarea);\r
+       clk_disable(dev->clk);\r
+\r
+ err_clk:\r
+       clk_put(dev->clk);\r
+\r
+ err_irq:\r
+       free_irq(dev->irq, dev);\r
+\r
+ err_alloc:\r
+       adc_free_host(dev->adc);\r
+       return ret;\r
+}\r
+\r
+static int rk29_adc_remove(struct platform_device *pdev)\r
+{\r
+       struct rk29_adc_device *dev = platform_get_drvdata(pdev);\r
+\r
+       iounmap(dev->regs);\r
+       release_resource(dev->ioarea);\r
+       kfree(dev->ioarea);\r
+       free_irq(dev->irq, dev);\r
+       clk_disable(dev->clk);\r
+       clk_put(dev->clk);\r
+       adc_free_host(dev->adc);\r
+\r
+       return 0;\r
+}\r
+\r
+#ifdef CONFIG_PM\r
+static int rk29_adc_suspend(struct platform_device *pdev, pm_message_t state)\r
+{\r
+       struct rk29_adc_device *dev = platform_get_drvdata(pdev);\r
+\r
+       dev->adc->is_suspended = 1;\r
+       return 0;\r
+}\r
+\r
+static int rk29_adc_resume(struct platform_device *pdev)\r
+{\r
+       struct rk29_adc_device *dev = platform_get_drvdata(pdev);\r
+\r
+       dev->adc->is_suspended = 0;\r
+       return 0;\r
+}\r
+\r
+#else\r
+#define rk29_adc_suspend NULL\r
+#define rk29_adc_resume NULL\r
+#endif\r
+\r
+static struct platform_driver rk29_adc_driver = {\r
+       .driver         = {\r
+               .name   = "rk29-adc",\r
+               .owner  = THIS_MODULE,\r
+       },\r
+       .probe          = rk29_adc_probe,\r
+       .remove         = __devexit_p(rk29_adc_remove),\r
+       .suspend        = rk29_adc_suspend,\r
+       .resume         = rk29_adc_resume,\r
+};\r
+\r
+static int __init rk29_adc_init(void)\r
+{\r
+       return platform_driver_register(&rk29_adc_driver);\r
+}\r
+subsys_initcall(rk29_adc_init);\r
+\r
+static void __exit rk29_adc_exit(void)\r
+{\r
+       platform_driver_unregister(&rk29_adc_driver);\r
+}\r
+module_exit(rk29_adc_exit);\r
+\r
+MODULE_DESCRIPTION("Driver for ADC");\r
+MODULE_AUTHOR("kfx, kfx@rock-chips.com");\r
+MODULE_LICENSE("GPL");\r
+\r
diff --git a/drivers/adc/plat/rk29_adc.h b/drivers/adc/plat/rk29_adc.h
new file mode 100755 (executable)
index 0000000..c595427
--- /dev/null
@@ -0,0 +1,32 @@
+/* drivers/adc/chips/rk29_adc.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+*/
+
+#ifndef __ASM_RK29_ADC_H
+#define __ASM_RK29_ADC_H
+
+#define ADC_DATA                       0x00
+#define ADC_DATA_MASK          0x3ff
+
+#define ADC_STAS                       0x04
+#define ADC_STAS_BUSY          (1<<0)
+
+#define ADC_CTRL                       0x08
+#define ADC_CTRL_CH(ch)                ((ch)<<0)
+#define ADC_CTRL_POWER_UP      (1<<3)
+#define ADC_CTRL_START         (1<<4)
+#define ADC_CTRL_IRQ_ENABLE    (1<<5)
+#define ADC_CTRL_IRQ_STATUS    (1<<6)
+
+#define ADC_CLK_RATE           1  //1M
+/* maximum conversion rate of 100KSPS with 1MHZ ADC converter clock.
+ * SET: real conversion rate is half of maximum conversion rate
+ */
+#define SAMPLE_RATE                    ((1000/100) * 2 /(ADC_CLK_RATE))
+
+
+#endif /* __ASM_RK29_ADC_H */
index b5b82271f81ed6e4b63fec1c7c8234cbced952e5..b1916ec06c91f6a890e6543aa5875eac0467c709 100755 (executable)
@@ -478,7 +478,7 @@ exit:
                        }
                        #endif  
                }
-\r#endif                        
+#endif                 
        }
        return ret;
 
diff --git a/include/linux/adc.h b/include/linux/adc.h
new file mode 100755 (executable)
index 0000000..47df46e
--- /dev/null
@@ -0,0 +1,73 @@
+/* include/linux/adc.h\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2 as\r
+ * published by the Free Software Foundation.\r
+ *\r
+*/\r
+\r
+#ifndef __ASM_ADC_CORE_H\r
+#define __ASM_ADC_CORE_H\r
+\r
+#define MAX_ADC_CHN 4\r
+#define MAX_ADC_FIFO_DEPTH 8\r
+\r
+\r
+struct adc_client {\r
+       int chn;\r
+       int time;\r
+       int result;\r
+       void (*callback)(struct adc_client *, void *, int);\r
+       void *callback_param;\r
+\r
+       struct adc_host *adc;\r
+};\r
+\r
+struct adc_request {\r
+       int result;\r
+       int chn;\r
+       void (*callback)(struct adc_client *, void *, int);\r
+       void *callback_param;\r
+       struct adc_client *client;\r
+       /* Used in case of sync requests */\r
+       struct completion completion;\r
+};\r
+struct adc_host;\r
+struct adc_ops {\r
+       void (*start)(struct adc_host *);\r
+       void (*stop)(struct adc_host *);\r
+       int (*read)(struct adc_host *);\r
+};\r
+       \r
+       \r
+struct adc_host {\r
+       struct device *dev;\r
+       int is_suspended;\r
+       struct adc_request *queue[MAX_ADC_FIFO_DEPTH];\r
+       int queue_head;\r
+       int queue_tail;\r
+       struct mutex queue_mutex;\r
+       struct adc_client *cur;\r
+       const struct adc_ops *ops;\r
+       unsigned long           private[0];\r
+};\r
+static inline void *adc_priv(struct adc_host *adc)\r
+{\r
+       return (void *)adc->private;\r
+}\r
+       \r
+extern struct adc_host *adc_alloc_host(int extra, struct device *dev);\r
+extern void adc_free_host(struct adc_host *adc);\r
+extern void adc_core_irq_handle(struct adc_host *adc);\r
+\r
+\r
+extern struct adc_client *adc_register(int chn,\r
+                               void (*callback)(struct adc_client *, void *, int), \r
+                               void *callback_param);\r
+extern void adc_unregister(struct adc_client *client);\r
+\r
+extern int adc_sync_read(struct adc_client *client);\r
+extern int adc_async_read(struct adc_client *client);\r
+\r
+#endif\r
+\r