From 6c231a43f08f470eb862fd1604efb0b0e3bca0fe Mon Sep 17 00:00:00 2001 From: lw Date: Sat, 25 Feb 2012 16:19:49 +0800 Subject: [PATCH] rk30:add tsadc support --- arch/arm/mach-rk30/devices.c | 27 +++ drivers/adc/plat/Kconfig | 12 + drivers/adc/plat/Makefile | 2 +- drivers/adc/plat/rk30_tsadc.c | 442 ++++++++++++++++++++++++++++++++++ drivers/adc/plat/rk30_tsadc.h | 80 ++++++ 5 files changed, 562 insertions(+), 1 deletion(-) create mode 100755 drivers/adc/plat/rk30_tsadc.c create mode 100755 drivers/adc/plat/rk30_tsadc.h diff --git a/arch/arm/mach-rk30/devices.c b/arch/arm/mach-rk30/devices.c index 708eaaeb4348..48431859e9b6 100755 --- a/arch/arm/mach-rk30/devices.c +++ b/arch/arm/mach-rk30/devices.c @@ -26,6 +26,29 @@ #include static u64 dma_dmamask = DMA_BIT_MASK(32); +#ifdef CONFIG_TSADC_RK30 +static struct resource rk30_tsadc_resource[] = { + { + .start = IRQ_SARADC, + .end = IRQ_SARADC, + .flags = IORESOURCE_IRQ, + }, + { + .start = RK30_TSADC_PHYS, + .end = RK30_TSADC_PHYS + RK30_TSADC_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device device_tsadc = { + .name = "rk30-tsadc", + .id = -1, + .num_resources = ARRAY_SIZE(rk30_tsadc_resource), + .resource = rk30_tsadc_resource, +}; +#endif + + static struct resource resource_dmac1[] = { [0] = { .start = RK30_DMACS1_PHYS, @@ -799,6 +822,10 @@ static int __init rk30_init_devices(void) #ifdef CONFIG_LCDC_RK30 platform_device_register(&device_lcdc); #endif +#ifdef CONFIG_TSADC_RK30 + platform_device_register(&device_tsadc); +#endif + return 0; } arch_initcall(rk30_init_devices); diff --git a/drivers/adc/plat/Kconfig b/drivers/adc/plat/Kconfig index d2b7cf2dc320..8db94b957492 100644 --- a/drivers/adc/plat/Kconfig +++ b/drivers/adc/plat/Kconfig @@ -4,6 +4,13 @@ choice prompt "ADC hardware drivers" + +config ADC_NULL + bool "NULL" + default y + help + null + config ADC_RK28 bool "RK28 adc interface" help @@ -14,3 +21,8 @@ config ADC_RK29 help This supports the use of the ADC interface on rk29 processors. endchoice + +config TSADC_RK30 + bool "RK30 tsadc interface" + help + This supports the use of the TSADC interface on rk30 processors. \ No newline at end of file diff --git a/drivers/adc/plat/Makefile b/drivers/adc/plat/Makefile index 78558b6d2562..64c5203317b2 100644 --- a/drivers/adc/plat/Makefile +++ b/drivers/adc/plat/Makefile @@ -3,4 +3,4 @@ # 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 diff --git a/drivers/adc/plat/rk30_tsadc.c b/drivers/adc/plat/rk30_tsadc.c new file mode 100755 index 000000000000..8d9a971d0bb9 --- /dev/null +++ b/drivers/adc/plat/rk30_tsadc.c @@ -0,0 +1,442 @@ +/* drivers/adc/chips/rk30_tadc.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_tsadc.h" + +//#define ADC_TEST +#define ADC_POLL 1 //if no tsadc intterupt + +struct rk30_tsadc_device { + int irq; + void __iomem *regs; + struct clk * clk; + struct resource *ioarea; + struct adc_host *adc; +}; +static void rk30_tsadc_start(struct adc_host *adc) +{ + struct rk30_tsadc_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_tsadc_start_poll(struct adc_host *adc) +{ + struct rk30_tsadc_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_START, + dev->regs + ADC_CTRL); + return; +} + +static void rk30_tsadc_stop(struct adc_host *adc) +{ + struct rk30_tsadc_device *dev = adc_priv(adc); + + writel(0, dev->regs + ADC_CTRL); +} +static int rk30_tsadc_read(struct adc_host *adc) +{ + struct rk30_tsadc_device *dev = adc_priv(adc); + + udelay(SAMPLE_RATE); + return readl(dev->regs + ADC_DATA) & ADC_DATA_MASK; +} +static irqreturn_t rk30_tsadc_irq(int irq, void *data) +{ + struct rk30_tsadc_device *dev = data; + adc_core_irq_handle(dev->adc); + return IRQ_HANDLED; +} +static const struct adc_ops rk30_tsadc_ops = { + .start = rk30_tsadc_start, + .stop = rk30_tsadc_stop, + .read = rk30_tsadc_read, +}; + + +#ifdef ADC_TEST +struct adc_test_data { + struct adc_client client[2]; + struct timer_list timer; + struct work_struct timer_work; +}; +static void callback_test(struct adc_client *client, void *param, int result) +{ + int i = 0; + for(i=0;i<2;i++) + dev_info(client[i].adc->dev, "[chn=%d] async_read = %d\n", client[i].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) +{ + struct adc_test_data *test = container_of(work, struct adc_test_data, + timer_work); + int i = 0; +#if ADC_POLL + int ret = 0, count = 0; + struct adc_host *adc = test->client[i].adc; + struct rk30_tsadc_device *dev = adc_priv(adc); + adc->cur = &test->client[i]; + rk30_tsadc_start(adc); + while(1) + { + udelay(SAMPLE_RATE); + ret = readl(dev->regs + ADC_STAS); + if(!(ret & ADC_STAS_BUSY)) + { + rk30_tsadc_stop(adc); + break; + } + if(count++ > 10) + { + rk30_tsadc_stop(adc); + printk("%s:timeout\n",__func__); + break; + } + } + + sync_read = readl(dev->regs + ADC_DATA); + dev_info(test->client[i].adc->dev, "[chn=%d] sync_read = %d\n", i, sync_read); +#else + int sync_read = 0; + for(i=0;i<2;i++) + { + adc_async_read(&test->client[i]); + sync_read = adc_sync_read(&test->client[i]); + dev_info(test->client[i].adc->dev, "[chn=%d] sync_read = %d\n", i, sync_read); + } +#endif +} + +static int rk30_tsadc_test(void) +{ + struct adc_test_data *test = NULL; + int i = 0; + test = kzalloc(sizeof(struct adc_test_data), GFP_KERNEL); + for(i=0;i<2;i++) + test->client[i] = *adc_register(i, callback_test, NULL); + + INIT_WORK(&test->timer_work, adc_timer_work); + setup_timer(&test->timer, adc_timer, (unsigned long)test); + test->timer.expires = jiffies + 200; + add_timer(&test->timer); + + return 0; +} +#endif + +#if 1 +struct temp_sample_data { + struct adc_client client[2]; + struct timer_list timer; + struct work_struct timer_work; +}; +static struct temp_sample_data *gtemp; +static void callback(struct adc_client *client, void *param, int result) +{ + int i = 0; + for(i=0; i<2; i++) + dev_info(client[i].adc->dev, "[chn=%d] async_read = %d\n", client[i].chn, result); + return; +} + +static int rk30_temp_sample_init(void) +{ + struct temp_sample_data *temp = NULL; + int i = 0; + temp = kzalloc(sizeof(struct temp_sample_data), GFP_KERNEL); + if (!temp){ + printk("%s:no memory for adc request\n",__func__); + return -ENOMEM; + } + + for(i=0; i<2; i++) + temp->client[i] = *adc_register(i, callback, NULL); + gtemp = temp; + return 0; + +} + + +int rk30_temp_sample(int chn, int *result) +{ + int sync_read = 0; + int i = 0, num = 0; +#if ADC_POLL + int ret = 0, count = 0; + struct temp_sample_data *temp = gtemp; + struct adc_host *adc; + struct rk30_tsadc_device *dev; + chn &= 0x01; //0 or 1 + adc = temp->client[chn].adc; + dev = adc_priv(adc); + adc->cur = &temp->client[chn]; + rk30_tsadc_start_poll(adc); + while(1) + { + udelay(SAMPLE_RATE); + ret = readl(dev->regs + ADC_STAS); + if(!(ret & ADC_STAS_BUSY)) + { + rk30_tsadc_stop(adc); + break; + } + if(count++ > 20) + { + rk30_tsadc_stop(adc); + printk("%s:timeout\n",__func__); + break; + } + } + + sync_read = readl(dev->regs + ADC_DATA); + //dev_info(temp->client[chn].adc->dev, "[chn=%d] sync_read = %d\n", chn, sync_read); +#else + adc_async_read(>emp->client[chn]); + sync_read = adc_sync_read(>emp->client[chn]); + dev_info(gtemp->client[chn].adc->dev, "[chn=%d] sync_read = %d\n", chn, sync_read); +#endif + //get temperature according to ADC value + num = sizeof(table_code_to_temp)/sizeof(struct tsadc_table); + for(i=0; i= table_code_to_temp[i+1].code) && (sync_read < table_code_to_temp[i].code)) + { + *result = table_code_to_temp[i+1].temp; + return 0; + } + } + + if(sync_read <= table_code_to_temp[num-1].code) + { + *result = table_code_to_temp[num-1].temp; + printk("%s:temperature is out of table\n",__func__); + return -1; + } + else if(sync_read >= table_code_to_temp[0].code) + { + *result = table_code_to_temp[0].temp; + printk("%s:temperature is out of table\n",__func__); + return -1; + } + + return -1; + +} + +EXPORT_SYMBOL(rk30_temp_sample); + +#endif + +static int rk30_tsadc_probe(struct platform_device *pdev) +{ + struct adc_host *adc = NULL; + struct rk30_tsadc_device *dev; + struct resource *res; + int ret; + + adc = adc_alloc_host(sizeof(struct rk30_tsadc_device), &pdev->dev); + if (!adc) + return -ENOMEM; + spin_lock_init(&adc->lock); + adc->dev = &pdev->dev; + adc->is_suspended = 0; + adc->ops = &rk30_tsadc_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_tsadc_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_tsadc_remove(struct platform_device *pdev) +{ + struct rk30_tsadc_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_tsadc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct rk30_tsadc_device *dev = platform_get_drvdata(pdev); + + dev->adc->is_suspended = 1; + return 0; +} + +static int rk30_tsadc_resume(struct platform_device *pdev) +{ + struct rk30_tsadc_device *dev = platform_get_drvdata(pdev); + + dev->adc->is_suspended = 0; + return 0; +} + +#else +#define rk30_tsadc_suspend NULL +#define rk30_tsadc_resume NULL +#endif + +static struct platform_driver rk30_tsadc_driver = { + .driver = { + .name = "rk30-tsadc", + .owner = THIS_MODULE, + }, + .probe = rk30_tsadc_probe, + .remove = __devexit_p(rk30_tsadc_remove), + .suspend = rk30_tsadc_suspend, + .resume = rk30_tsadc_resume, +}; + +static int __init rk30_tsadc_init(void) +{ + return platform_driver_register(&rk30_tsadc_driver); +} +subsys_initcall(rk30_tsadc_init); + +static void __exit rk30_tsadc_exit(void) +{ + platform_driver_unregister(&rk30_tsadc_driver); +} +module_exit(rk30_tsadc_exit); + +MODULE_DESCRIPTION("Driver for TSADC"); +MODULE_AUTHOR("lw, lw@rock-chips.com"); +MODULE_LICENSE("GPL"); + +static int __init rk30_temp_init(void) +{ + int ret = 0; + ret = rk30_temp_sample_init(); +#ifdef ADC_TEST + rk30_tsadc_test(); +#endif + printk("%s:initialized\n",__func__); + return ret; +} + +static void __exit rk30_temp_exit(void) +{ + int i = 0; + struct temp_sample_data *temp = gtemp; + for(i=0; i<2; i++) + adc_unregister(&temp->client[i]); + kfree(temp); +} + +module_init(rk30_temp_init); +module_exit(rk30_temp_exit); + diff --git a/drivers/adc/plat/rk30_tsadc.h b/drivers/adc/plat/rk30_tsadc.h new file mode 100755 index 000000000000..eb52e51a0235 --- /dev/null +++ b/drivers/adc/plat/rk30_tsadc.h @@ -0,0 +1,80 @@ +/* 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_DLY_PU_SOC 0x0C + +#define ADC_CLK_RATE 1 //1M +#define SAMPLE_RATE (20/ADC_CLK_RATE) //20 CLK + +struct tsadc_table +{ + int code; + int temp; +}; + +static struct tsadc_table table_code_to_temp[] = +{ + {3800, -40}, + {3792, -35}, + {3783, -30}, + {3774, -25}, + {3765, -20}, + {3756, -15}, + {3747, -10}, + {3737, -5}, + {3728, 0}, + {3718, 5}, + + {3708, 10}, + {3698, 15}, + {3688, 20}, + {3678, 25}, + {3667, 30}, + {3656, 35}, + {3645, 40}, + {3634, 45}, + {3623, 50}, + {3611, 55}, + + {3600, 60}, + {3588, 65}, + {3575, 70}, + {3563, 75}, + {3550, 80}, + {3537, 85}, + {3524, 90}, + {3510, 95}, + {3496, 100}, + {3482, 105}, + + {3467, 110}, + {3452, 115}, + {3437, 120}, + {3421, 125}, + +}; + + + +#endif /* __ASM_RK30_ADC_H */ -- 2.34.1