&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
#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[] = {
{
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
source "drivers/spi/Kconfig"
+source "drivers/adc/Kconfig"
+
source "drivers/fpga/Kconfig"
source "drivers/headset_observe/Kconfig"
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/
--- /dev/null
+#
+# ADC subsystem configuration
+#
+
+menuconfig ADC
+ bool "ADC support"
+ default y
+if ADC
+
+source drivers/adc/plat/Kconfig
+
+endif # ADC
--- /dev/null
+#
+# Makefile for the adc core.
+#
+
+obj-$(CONFIG_ADC) += core.o
+obj-y += plat/
+
--- /dev/null
+/* 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);
+
+
--- /dev/null
+#
+# 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
--- /dev/null
+#
+# Makefile for the adc hardware drivers.
+#
+obj-$(CONFIG_ADC_RK28) += rk28_adc.o
+obj-$(CONFIG_ADC_RK29) += rk29_adc.o
+
--- /dev/null
+/* 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
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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
--- /dev/null
+/* 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 */
}
#endif
}
-\r#endif
+#endif
}
return ret;
--- /dev/null
+/* 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