From 4e26df805742cab5f7b38c80bdd7d0bc9d88416c Mon Sep 17 00:00:00 2001 From: kfx Date: Mon, 12 Mar 2012 18:19:25 +0800 Subject: [PATCH] rk30:add adc(saradc) support --- arch/arm/mach-rk30/devices.c | 29 +++- drivers/adc/Kconfig | 1 + drivers/adc/plat/Kconfig | 11 +- drivers/adc/plat/Makefile | 3 +- drivers/adc/plat/rk30_adc.c | 271 +++++++++++++++++++++++++++++++++++ drivers/adc/plat/rk30_adc.h | 29 ++++ 6 files changed, 340 insertions(+), 4 deletions(-) create mode 100755 drivers/adc/plat/rk30_adc.c create mode 100755 drivers/adc/plat/rk30_adc.h diff --git a/arch/arm/mach-rk30/devices.c b/arch/arm/mach-rk30/devices.c index 35c0b5c88beb..c5c5936f57e1 100755 --- a/arch/arm/mach-rk30/devices.c +++ b/arch/arm/mach-rk30/devices.c @@ -28,13 +28,35 @@ static u64 dma_dmamask = DMA_BIT_MASK(32); -#ifdef CONFIG_TSADC_RK30 -static struct resource rk30_tsadc_resource[] = { +#ifdef CONFIG_ADC_RK30 +static struct resource rk30_adc_resource[] = { { .start = IRQ_SARADC, .end = IRQ_SARADC, .flags = IORESOURCE_IRQ, }, + { + .start = RK30_SARADC_PHYS, + .end = RK30_SARADC_PHYS + RK30_SARADC_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device device_adc = { + .name = "rk30-adc", + .id = -1, + .num_resources = ARRAY_SIZE(rk30_adc_resource), + .resource = rk30_adc_resource, +}; +#endif + +#ifdef CONFIG_TSADC_RK30 +static struct resource rk30_tsadc_resource[] = { + { + .start = IRQ_TSADC, + .end = IRQ_TSADC, + .flags = IORESOURCE_IRQ, + }, { .start = RK30_TSADC_PHYS, .end = RK30_TSADC_PHYS + RK30_TSADC_SIZE - 1, @@ -994,6 +1016,9 @@ static int __init rk30_init_devices(void) #ifdef CONFIG_LCDC_RK30 platform_device_register(&device_lcdc); #endif +#ifdef CONFIG_ADC_RK30 + platform_device_register(&device_adc); +#endif #ifdef CONFIG_TSADC_RK30 platform_device_register(&device_tsadc); #endif diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig index d70501233335..6914a8f6c4a4 100644 --- a/drivers/adc/Kconfig +++ b/drivers/adc/Kconfig @@ -4,6 +4,7 @@ menuconfig ADC bool "ADC support" + depends on PLAT_RK default y if ADC diff --git a/drivers/adc/plat/Kconfig b/drivers/adc/plat/Kconfig index 8db94b957492..cbdd09c1d15f 100644 --- a/drivers/adc/plat/Kconfig +++ b/drivers/adc/plat/Kconfig @@ -13,16 +13,25 @@ config ADC_NULL config ADC_RK28 bool "RK28 adc interface" + depends on ARCH_RK2818 || ARCH_RK2808 || ARCH_RK2816 help This supports the use of the ADC interface on rk28 processors. config ADC_RK29 bool "RK29 adc interface" + depends on ARCH_RK29 help This supports the use of the ADC interface on rk29 processors. + +config ADC_RK30 + bool "RK30 adc interface" + depends on ARCH_RK30 + help + This supports the use of the ADC interface on rk30 processors. endchoice config TSADC_RK30 bool "RK30 tsadc interface" + depends on ARCH_RK30 help - This supports the use of the TSADC interface on rk30 processors. \ No newline at end of file + This supports the use of the TSADC interface on rk30 processors. diff --git a/drivers/adc/plat/Makefile b/drivers/adc/plat/Makefile index 64c5203317b2..9eb702dece14 100644 --- a/drivers/adc/plat/Makefile +++ b/drivers/adc/plat/Makefile @@ -3,4 +3,5 @@ # obj-$(CONFIG_ADC_RK28) += rk28_adc.o obj-$(CONFIG_ADC_RK29) += rk29_adc.o -obj-$(CONFIG_TSADC_RK30) += rk30_tsadc.o \ No newline at end of file +obj-$(CONFIG_ADC_RK29) += rk30_adc.o +obj-$(CONFIG_TSADC_RK30) += rk30_tsadc.o diff --git a/drivers/adc/plat/rk30_adc.c b/drivers/adc/plat/rk30_adc.c new file mode 100755 index 000000000000..422b36edf897 --- /dev/null +++ b/drivers/adc/plat/rk30_adc.c @@ -0,0 +1,271 @@ +/* drivers/adc/chips/rk30_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 + + +#include "rk30_adc.h" + +//#define ADC_TEST + +struct rk30_adc_device { + int irq; + void __iomem *regs; + struct clk * clk; + struct resource *ioarea; + struct adc_host *adc; +}; +static void rk30_adc_start(struct adc_host *adc) +{ + struct rk30_adc_device *dev = adc_priv(adc); + int chn = adc->cur->chn; + + writel(0, dev->regs + ADC_CTRL); + writel(ADC_CTRL_POWER_UP|ADC_CTRL_CH(chn), dev->regs + ADC_CTRL); + udelay(SAMPLE_RATE); + + writel(readl(dev->regs + ADC_CTRL)|ADC_CTRL_IRQ_ENABLE|ADC_CTRL_START, + dev->regs + ADC_CTRL); + return; +} +static void rk30_adc_stop(struct adc_host *adc) +{ + struct rk30_adc_device *dev = adc_priv(adc); + + writel(0, dev->regs + ADC_CTRL); +} +static int rk30_adc_read(struct adc_host *adc) +{ + struct rk30_adc_device *dev = adc_priv(adc); + + udelay(SAMPLE_RATE); + return readl(dev->regs + ADC_DATA) & ADC_DATA_MASK; +} +static irqreturn_t rk30_adc_irq(int irq, void *data) +{ + struct rk30_adc_device *dev = data; + adc_core_irq_handle(dev->adc); + return IRQ_HANDLED; +} +static const struct adc_ops rk30_adc_ops = { + .start = rk30_adc_start, + .stop = rk30_adc_stop, + .read = rk30_adc_read, +}; +#ifdef ADC_TEST +struct adc_test_data { + struct adc_client *client; + struct timer_list timer; + struct work_struct timer_work; +}; +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 void adc_timer(unsigned long data) +{ + //int sync_read = 0; + struct adc_test_data *test=(struct adc_test_data *)data; + + //sync_read = adc_sync_read(test->client); + //dev_info(test->client->adc->dev, "[chn%d] sync_read = %d\n", 0, sync_read); + schedule_work(&test->timer_work); + add_timer(&test->timer); +} +static void adc_timer_work(struct work_struct *work) +{ + int sync_read = 0; + struct adc_test_data *test = container_of(work, struct adc_test_data, + timer_work); + adc_async_read(test->client); + sync_read = adc_sync_read(test->client); + dev_info(test->client->adc->dev, "[chn%d] sync_read = %d\n", 0, sync_read); +} + +static int rk30_adc_test(void) +{ + struct adc_test_data *test = NULL; + + test = kzalloc(sizeof(struct adc_test_data), GFP_KERNEL); + + test->client = adc_register(0, callback, NULL); + INIT_WORK(&test->timer_work, adc_timer_work); + setup_timer(&test->timer, adc_timer, (unsigned long)test); + test->timer.expires = jiffies + 100; + add_timer(&test->timer); + + return 0; + +} +#endif + +static int rk30_adc_probe(struct platform_device *pdev) +{ + struct adc_host *adc = NULL; + struct rk30_adc_device *dev; + struct resource *res; + int ret; + + adc = adc_alloc_host(sizeof(struct rk30_adc_device), &pdev->dev); + if (!adc) + return -ENOMEM; + spin_lock_init(&adc->lock); + adc->dev = &pdev->dev; + adc->is_suspended = 0; + adc->ops = &rk30_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, rk30_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, "rk30 adc: driver initialized\n"); + 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 rk30_adc_remove(struct platform_device *pdev) +{ + struct rk30_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 rk30_adc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct rk30_adc_device *dev = platform_get_drvdata(pdev); + + dev->adc->is_suspended = 1; + return 0; +} + +static int rk30_adc_resume(struct platform_device *pdev) +{ + struct rk30_adc_device *dev = platform_get_drvdata(pdev); + + dev->adc->is_suspended = 0; + return 0; +} + +#else +#define rk30_adc_suspend NULL +#define rk30_adc_resume NULL +#endif + +static struct platform_driver rk30_adc_driver = { + .driver = { + .name = "rk30-adc", + .owner = THIS_MODULE, + }, + .probe = rk30_adc_probe, + .remove = __devexit_p(rk30_adc_remove), + .suspend = rk30_adc_suspend, + .resume = rk30_adc_resume, +}; + +static int __init rk30_adc_init(void) +{ + return platform_driver_register(&rk30_adc_driver); +} +subsys_initcall(rk30_adc_init); + +static void __exit rk30_adc_exit(void) +{ + platform_driver_unregister(&rk30_adc_driver); +} +module_exit(rk30_adc_exit); + +MODULE_DESCRIPTION("Driver for ADC"); +MODULE_AUTHOR("kfx, kfx@rock-chips.com"); +MODULE_LICENSE("GPL"); +static int __init adc_test_init(void) +{ +#ifdef ADC_TEST + rk30_adc_test(); +#endif + return 0; + +} +module_init(adc_test_init); diff --git a/drivers/adc/plat/rk30_adc.h b/drivers/adc/plat/rk30_adc.h new file mode 100755 index 000000000000..2fb92626b6db --- /dev/null +++ b/drivers/adc/plat/rk30_adc.h @@ -0,0 +1,29 @@ +/* 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_RK30_ADC_H +#define __ASM_RK30_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) (0x07 - ((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 +#define SAMPLE_RATE (20/ADC_CLK_RATE) //20 CLK + + +#endif /* __ASM_RK30_ADC_H */ -- 2.34.1