From: kfx Date: Fri, 12 Nov 2010 08:34:40 +0000 (+0800) Subject: add adc driver(path: drivers/adc) X-Git-Tag: firefly_0821_release~11026 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=e94c69a05c594355072b323df8a255086d6fc78b;p=firefly-linux-kernel-4.4.55.git add adc driver(path: drivers/adc) --- diff --git a/arch/arm/mach-rk29/board-rk29sdk.c b/arch/arm/mach-rk29/board-rk29sdk.c index c17cd8163c5f..cb6a04796045 100755 --- a/arch/arm/mach-rk29/board-rk29sdk.c +++ b/arch/arm/mach-rk29/board-rk29sdk.c @@ -561,7 +561,9 @@ static struct platform_device *devices[] __initdata = { &rk29xx_device_spi0m, &rk29xx_device_spi1m, #endif - +#ifdef CONFIG_ADC_RK29 + &rk29_device_adc, +#endif #ifdef CONFIG_I2C0_RK29 &rk29_device_i2c0, #endif diff --git a/arch/arm/mach-rk29/devices.c b/arch/arm/mach-rk29/devices.c index ee664a305f84..2ca57c781a3e 100755 --- a/arch/arm/mach-rk29/devices.c +++ b/arch/arm/mach-rk29/devices.c @@ -21,6 +21,29 @@ #include #include #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[] = { { diff --git a/arch/arm/mach-rk29/devices.h b/arch/arm/mach-rk29/devices.h index 351c695f0ea3..8e476673f714 100755 --- a/arch/arm/mach-rk29/devices.h +++ b/arch/arm/mach-rk29/devices.h @@ -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 diff --git a/drivers/Kconfig b/drivers/Kconfig index ea153397d1fc..e973e28b8a83 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -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" diff --git a/drivers/Makefile b/drivers/Makefile index fe5282739cba..73020396902a 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -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 index 000000000000..d70501233335 --- /dev/null +++ b/drivers/adc/Kconfig @@ -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 index 000000000000..3352b9167fcd --- /dev/null +++ b/drivers/adc/Makefile @@ -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 index 000000000000..97704207d596 --- /dev/null +++ b/drivers/adc/core.c @@ -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 +#include +#include +#include +#include +#include + + +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 index 000000000000..d2b7cf2dc320 --- /dev/null +++ b/drivers/adc/plat/Kconfig @@ -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 index 000000000000..78558b6d2562 --- /dev/null +++ b/drivers/adc/plat/Makefile @@ -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 index 000000000000..e17f01179e17 --- /dev/null +++ b/drivers/adc/plat/rk28_adc.c @@ -0,0 +1,236 @@ +/* drivers/adc/chips/rk28_adc.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "rk28_adc.h" + +//#define ADC_TEST + +struct rk28_adc_device { + int irq; + void __iomem *regs; + struct clk * clk; + struct resource *ioarea; + struct adc_host *adc; +}; +static void rk28_adc_start(struct adc_host *adc) +{ + struct rk28_adc_device *dev = adc_priv(adc); + int chn = adc->cur->chn; + + writel(ADC_CTRL_IRQ_ENABLE|ADC_CTRL_POWER_UP|ADC_CTRL_START|ADC_CTRL_CH(chn), + dev->regs + ADC_CTRL); +} +static void rk28_adc_stop(struct adc_host *adc) +{ + struct rk28_adc_device *dev = adc_priv(adc); + + writel(ADC_CTRL_IRQ_STATUS, dev->regs + ADC_CTRL); +} +static int rk28_adc_read(struct adc_host *adc) +{ + struct rk28_adc_device *dev = adc_priv(adc); + + udelay(SAMPLE_RATE); + return readl(dev->regs + ADC_DATA) & ADC_DATA_MASK; +} +static irqreturn_t rk28_adc_irq(int irq, void *data) +{ + struct rk28_adc_device *dev = data; + adc_core_irq_handle(dev->adc); + return IRQ_HANDLED; +} +static const struct adc_ops rk28_adc_ops = { + .start = rk28_adc_start, + .stop = rk28_adc_stop, + .read = rk28_adc_read, +}; +#ifdef ADC_TEST +static void callback(struct adc_client *client, void *param, int result) +{ + dev_info(client->adc->dev, "[chn%d] async_read = %d\n", client->chn, result); + return; +} +static int rk28_adc_test(void) +{ + int sync_read = 0; + struct adc_client *client = adc_register(1, callback, NULL); + + while(1) + { + adc_async_read(client); + udelay(20); + sync_read = adc_sync_read(client); + dev_info(client->adc->dev, "[chn%d] sync_read = %d\n", client->chn, sync_read); + udelay(20); + } + adc_unregister(client); + return 0; +} +#endif + +static int rk28_adc_probe(struct platform_device *pdev) +{ + struct adc_host *adc = NULL; + struct rk28_adc_device *dev; + struct resource *res; + int ret; + + adc = adc_alloc_host(sizeof(struct rk28_adc_device), &pdev->dev); + if (!adc) + return -ENOMEM; + mutex_init(&adc->queue_mutex); + adc->dev = &pdev->dev; + adc->is_suspended = 0; + adc->ops = &rk28_adc_ops; + dev = adc_priv(adc); + dev->adc = adc; + dev->irq = platform_get_irq(pdev, 0); + if (dev->irq <= 0) { + dev_err(&pdev->dev, "failed to get adc irq\n"); + ret = -ENOENT; + goto err_alloc; + } + + ret = request_irq(dev->irq, rk28_adc_irq, 0, pdev->name, dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to attach adc irq\n"); + goto err_alloc; + } + dev->clk = clk_get(&pdev->dev, "saradc"); + if (IS_ERR(dev->clk)) { + dev_err(&pdev->dev, "failed to get adc clock\n"); + ret = PTR_ERR(dev->clk); + goto err_irq; + } + + ret = clk_set_rate(dev->clk, ADC_CLK_RATE * 1000 * 1000); + if(ret < 0) { + dev_err(&pdev->dev, "failed to set adc clk\n"); + goto err_clk; + } + clk_enable(dev->clk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "cannot find IO resource\n"); + ret = -ENOENT; + goto err_clk; + } + dev->ioarea = request_mem_region(res->start, (res->end - res->start) + 1, + pdev->name); + if(dev->ioarea == NULL) { + dev_err(&pdev->dev, "cannot request IO\n"); + ret = -ENXIO; + goto err_clk; + } + dev->regs = ioremap(res->start, (res->end - res->start) + 1); + if (!dev->regs) { + dev_err(&pdev->dev, "cannot map IO\n"); + ret = -ENXIO; + goto err_ioarea; + } + platform_set_drvdata(pdev, dev); + dev_info(&pdev->dev, "rk28 adc: driver initialized\n"); +#ifdef ADC_TEST + rk28_adc_test(); +#endif + return 0; +// err_iomap: +// iounmap(dev->regs); + + err_ioarea: + release_resource(dev->ioarea); + kfree(dev->ioarea); + clk_disable(dev->clk); + + err_clk: + clk_put(dev->clk); + + err_irq: + free_irq(dev->irq, dev); + + err_alloc: + adc_free_host(dev->adc); + return ret; +} + +static int rk28_adc_remove(struct platform_device *pdev) +{ + struct rk28_adc_device *dev = platform_get_drvdata(pdev); + + iounmap(dev->regs); + release_resource(dev->ioarea); + kfree(dev->ioarea); + free_irq(dev->irq, dev); + clk_disable(dev->clk); + clk_put(dev->clk); + adc_free_host(dev->adc); + + return 0; +} + +#ifdef CONFIG_PM +static int rk28_adc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct rk28_adc_device *dev = platform_get_drvdata(pdev); + + dev->adc->is_suspended = 1; + return 0; +} + +static int rk28_adc_resume(struct platform_device *pdev) +{ + struct rk28_adc_device *dev = platform_get_drvdata(pdev); + + dev->adc->is_suspended = 0; + return 0; +} + +#else +#define rk28_adc_suspend NULL +#define rk28_adc_resume NULL +#endif + +static struct platform_driver rk28_adc_driver = { + .driver = { + .name = "rk28-adc", + .owner = THIS_MODULE, + }, + .probe = rk28_adc_probe, + .remove = __devexit_p(rk28_adc_remove), + .suspend = rk28_adc_suspend, + .resume = rk28_adc_resume, +}; + +static int __init rk28_adc_init(void) +{ + return platform_driver_register(&rk28_adc_driver); +} +subsys_initcall(rk28_adc_init); + +static void __exit rk28_adc_exit(void) +{ + platform_driver_unregister(&rk28_adc_driver); +} +module_exit(rk28_adc_exit); + +MODULE_DESCRIPTION("Driver for ADC"); +MODULE_AUTHOR("kfx, kfx@rock-chips.com"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/adc/plat/rk28_adc.h b/drivers/adc/plat/rk28_adc.h new file mode 100755 index 000000000000..ccd8daf062b9 --- /dev/null +++ b/drivers/adc/plat/rk28_adc.h @@ -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 index 000000000000..e95a35592bb4 --- /dev/null +++ b/drivers/adc/plat/rk29_adc.c @@ -0,0 +1,236 @@ +/* drivers/adc/chips/rk29_adc.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "rk29_adc.h" + +//#define ADC_TEST + +struct rk29_adc_device { + int irq; + void __iomem *regs; + struct clk * clk; + struct resource *ioarea; + struct adc_host *adc; +}; +static void rk29_adc_start(struct adc_host *adc) +{ + struct rk29_adc_device *dev = adc_priv(adc); + int chn = adc->cur->chn; + + writel(ADC_CTRL_IRQ_ENABLE|ADC_CTRL_POWER_UP|ADC_CTRL_START|ADC_CTRL_CH(chn), + dev->regs + ADC_CTRL); +} +static void rk29_adc_stop(struct adc_host *adc) +{ + struct rk29_adc_device *dev = adc_priv(adc); + + writel(ADC_CTRL_IRQ_STATUS, dev->regs + ADC_CTRL); +} +static int rk29_adc_read(struct adc_host *adc) +{ + struct rk29_adc_device *dev = adc_priv(adc); + + udelay(SAMPLE_RATE); + return readl(dev->regs + ADC_DATA) & ADC_DATA_MASK; +} +static irqreturn_t rk29_adc_irq(int irq, void *data) +{ + struct rk29_adc_device *dev = data; + adc_core_irq_handle(dev->adc); + return IRQ_HANDLED; +} +static const struct adc_ops rk29_adc_ops = { + .start = rk29_adc_start, + .stop = rk29_adc_stop, + .read = rk29_adc_read, +}; +#ifdef ADC_TEST +static void callback(struct adc_client *client, void *param, int result) +{ + dev_info(client->adc->dev, "[chn%d] async_read = %d\n", client->chn, result); + return; +} +static int rk29_adc_test(void) +{ + int sync_read = 0; + struct adc_client *client = adc_register(1, callback, NULL); + + while(1) + { + adc_async_read(client); + udelay(20); + sync_read = adc_sync_read(client); + dev_info(client->adc->dev, "[chn%d] sync_read = %d\n", client->chn, sync_read); + udelay(20); + } + adc_unregister(client); + return 0; +} +#endif + +static int rk29_adc_probe(struct platform_device *pdev) +{ + struct adc_host *adc = NULL; + struct rk29_adc_device *dev; + struct resource *res; + int ret; + + adc = adc_alloc_host(sizeof(struct rk29_adc_device), &pdev->dev); + if (!adc) + return -ENOMEM; + mutex_init(&adc->queue_mutex); + adc->dev = &pdev->dev; + adc->is_suspended = 0; + adc->ops = &rk29_adc_ops; + dev = adc_priv(adc); + dev->adc = adc; + dev->irq = platform_get_irq(pdev, 0); + if (dev->irq <= 0) { + dev_err(&pdev->dev, "failed to get adc irq\n"); + ret = -ENOENT; + goto err_alloc; + } + + ret = request_irq(dev->irq, rk29_adc_irq, 0, pdev->name, dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to attach adc irq\n"); + goto err_alloc; + } + dev->clk = clk_get(&pdev->dev, "saradc"); + if (IS_ERR(dev->clk)) { + dev_err(&pdev->dev, "failed to get adc clock\n"); + ret = PTR_ERR(dev->clk); + goto err_irq; + } + + ret = clk_set_rate(dev->clk, ADC_CLK_RATE * 1000 * 1000); + if(ret < 0) { + dev_err(&pdev->dev, "failed to set adc clk\n"); + goto err_clk; + } + clk_enable(dev->clk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "cannot find IO resource\n"); + ret = -ENOENT; + goto err_clk; + } + dev->ioarea = request_mem_region(res->start, (res->end - res->start) + 1, + pdev->name); + if(dev->ioarea == NULL) { + dev_err(&pdev->dev, "cannot request IO\n"); + ret = -ENXIO; + goto err_clk; + } + dev->regs = ioremap(res->start, (res->end - res->start) + 1); + if (!dev->regs) { + dev_err(&pdev->dev, "cannot map IO\n"); + ret = -ENXIO; + goto err_ioarea; + } + platform_set_drvdata(pdev, dev); + dev_info(&pdev->dev, "rk29 adc: driver initialized\n"); +#ifdef ADC_TEST + rk29_adc_test(); +#endif + return 0; +// err_iomap: +// iounmap(dev->regs); + + err_ioarea: + release_resource(dev->ioarea); + kfree(dev->ioarea); + clk_disable(dev->clk); + + err_clk: + clk_put(dev->clk); + + err_irq: + free_irq(dev->irq, dev); + + err_alloc: + adc_free_host(dev->adc); + return ret; +} + +static int rk29_adc_remove(struct platform_device *pdev) +{ + struct rk29_adc_device *dev = platform_get_drvdata(pdev); + + iounmap(dev->regs); + release_resource(dev->ioarea); + kfree(dev->ioarea); + free_irq(dev->irq, dev); + clk_disable(dev->clk); + clk_put(dev->clk); + adc_free_host(dev->adc); + + return 0; +} + +#ifdef CONFIG_PM +static int rk29_adc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct rk29_adc_device *dev = platform_get_drvdata(pdev); + + dev->adc->is_suspended = 1; + return 0; +} + +static int rk29_adc_resume(struct platform_device *pdev) +{ + struct rk29_adc_device *dev = platform_get_drvdata(pdev); + + dev->adc->is_suspended = 0; + return 0; +} + +#else +#define rk29_adc_suspend NULL +#define rk29_adc_resume NULL +#endif + +static struct platform_driver rk29_adc_driver = { + .driver = { + .name = "rk29-adc", + .owner = THIS_MODULE, + }, + .probe = rk29_adc_probe, + .remove = __devexit_p(rk29_adc_remove), + .suspend = rk29_adc_suspend, + .resume = rk29_adc_resume, +}; + +static int __init rk29_adc_init(void) +{ + return platform_driver_register(&rk29_adc_driver); +} +subsys_initcall(rk29_adc_init); + +static void __exit rk29_adc_exit(void) +{ + platform_driver_unregister(&rk29_adc_driver); +} +module_exit(rk29_adc_exit); + +MODULE_DESCRIPTION("Driver for ADC"); +MODULE_AUTHOR("kfx, kfx@rock-chips.com"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/adc/plat/rk29_adc.h b/drivers/adc/plat/rk29_adc.h new file mode 100755 index 000000000000..c595427d7f8c --- /dev/null +++ b/drivers/adc/plat/rk29_adc.h @@ -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 */ diff --git a/drivers/i2c/busses/i2c-rk29.c b/drivers/i2c/busses/i2c-rk29.c index b5b82271f81e..b1916ec06c91 100755 --- a/drivers/i2c/busses/i2c-rk29.c +++ b/drivers/i2c/busses/i2c-rk29.c @@ -478,7 +478,7 @@ exit: } #endif } - #endif +#endif } return ret; diff --git a/include/linux/adc.h b/include/linux/adc.h new file mode 100755 index 000000000000..47df46e5862f --- /dev/null +++ b/include/linux/adc.h @@ -0,0 +1,73 @@ +/* include/linux/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_ADC_CORE_H +#define __ASM_ADC_CORE_H + +#define MAX_ADC_CHN 4 +#define MAX_ADC_FIFO_DEPTH 8 + + +struct adc_client { + int chn; + int time; + int result; + void (*callback)(struct adc_client *, void *, int); + void *callback_param; + + struct adc_host *adc; +}; + +struct adc_request { + int result; + int chn; + void (*callback)(struct adc_client *, void *, int); + void *callback_param; + struct adc_client *client; + /* Used in case of sync requests */ + struct completion completion; +}; +struct adc_host; +struct adc_ops { + void (*start)(struct adc_host *); + void (*stop)(struct adc_host *); + int (*read)(struct adc_host *); +}; + + +struct adc_host { + struct device *dev; + int is_suspended; + struct adc_request *queue[MAX_ADC_FIFO_DEPTH]; + int queue_head; + int queue_tail; + struct mutex queue_mutex; + struct adc_client *cur; + const struct adc_ops *ops; + unsigned long private[0]; +}; +static inline void *adc_priv(struct adc_host *adc) +{ + return (void *)adc->private; +} + +extern struct adc_host *adc_alloc_host(int extra, struct device *dev); +extern void adc_free_host(struct adc_host *adc); +extern void adc_core_irq_handle(struct adc_host *adc); + + +extern struct adc_client *adc_register(int chn, + void (*callback)(struct adc_client *, void *, int), + void *callback_param); +extern void adc_unregister(struct adc_client *client); + +extern int adc_sync_read(struct adc_client *client); +extern int adc_async_read(struct adc_client *client); + +#endif +