rk30:add tsadc support
authorlw <lw@rock-chips.com>
Sat, 25 Feb 2012 08:19:49 +0000 (16:19 +0800)
committerlw <lw@rock-chips.com>
Sat, 25 Feb 2012 08:19:49 +0000 (16:19 +0800)
arch/arm/mach-rk30/devices.c
drivers/adc/plat/Kconfig
drivers/adc/plat/Makefile
drivers/adc/plat/rk30_tsadc.c [new file with mode: 0755]
drivers/adc/plat/rk30_tsadc.h [new file with mode: 0755]

index 708eaaeb434806d4c4211e34a0bd88047ec90e32..48431859e9b6d4b35e7370062136e93a89ba3603 100755 (executable)
 #include <mach/iomux.h>
 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);
index d2b7cf2dc320b7de85d920a2c597126e2e57096e..8db94b95749274b2b9b8cfecdbeb25bc813dac5e 100644 (file)
@@ -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
index 78558b6d2562606e208a23a72b0840de6f2f33fc..64c5203317b2405994ed70a781d87043c71952dc 100644 (file)
@@ -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 (executable)
index 0000000..8d9a971
--- /dev/null
@@ -0,0 +1,442 @@
+/* drivers/adc/chips/rk30_tadc.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
+#include <linux/slab.h>\r
+\r
+\r
+#include "rk30_tsadc.h"\r
+\r
+//#define ADC_TEST\r
+#define ADC_POLL       1       //if no tsadc intterupt\r
+\r
+struct rk30_tsadc_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 rk30_tsadc_start(struct adc_host *adc)\r
+{\r
+       struct rk30_tsadc_device *dev  = adc_priv(adc); \r
+       int chn = adc->cur->chn;\r
+       \r
+       writel(0, dev->regs + ADC_CTRL);\r
+       writel(ADC_CTRL_POWER_UP|ADC_CTRL_CH(chn), dev->regs + ADC_CTRL);\r
+       udelay(SAMPLE_RATE);\r
+\r
+       writel(readl(dev->regs + ADC_CTRL)|ADC_CTRL_IRQ_ENABLE|ADC_CTRL_START, \r
+               dev->regs + ADC_CTRL);\r
+       return;\r
+}\r
+\r
+static void rk30_tsadc_start_poll(struct adc_host *adc)\r
+{\r
+       struct rk30_tsadc_device *dev  = adc_priv(adc); \r
+       int chn = adc->cur->chn;\r
+       \r
+       writel(0, dev->regs + ADC_CTRL);\r
+       writel(ADC_CTRL_POWER_UP|ADC_CTRL_CH(chn), dev->regs + ADC_CTRL);\r
+       udelay(SAMPLE_RATE);\r
+\r
+       writel(readl(dev->regs + ADC_CTRL)|ADC_CTRL_START, \r
+               dev->regs + ADC_CTRL);\r
+       return;\r
+}\r
+\r
+static void rk30_tsadc_stop(struct adc_host *adc)\r
+{\r
+       struct rk30_tsadc_device *dev  = adc_priv(adc);\r
+       \r
+       writel(0, dev->regs + ADC_CTRL);\r
+}\r
+static int rk30_tsadc_read(struct adc_host *adc)\r
+{\r
+       struct rk30_tsadc_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 rk30_tsadc_irq(int irq, void *data)\r
+{\r
+       struct rk30_tsadc_device *dev = data;\r
+       adc_core_irq_handle(dev->adc);\r
+       return IRQ_HANDLED;\r
+}\r
+static const struct adc_ops rk30_tsadc_ops = {\r
+       .start          = rk30_tsadc_start,\r
+       .stop           = rk30_tsadc_stop,\r
+       .read           = rk30_tsadc_read,\r
+};\r
+\r
+\r
+#ifdef ADC_TEST\r
+struct adc_test_data {\r
+       struct adc_client client[2];\r
+       struct timer_list timer;\r
+       struct work_struct      timer_work;\r
+};\r
+static void callback_test(struct adc_client *client, void *param, int result)\r
+{\r
+       int i = 0;\r
+       for(i=0;i<2;i++)\r
+       dev_info(client[i].adc->dev, "[chn=%d] async_read = %d\n", client[i].chn, result);\r
+       return;\r
+}\r
+static void adc_timer(unsigned long data)\r
+{\r
+       //int sync_read = 0;\r
+        struct adc_test_data *test=(struct adc_test_data *)data;\r
+       \r
+       //sync_read = adc_sync_read(test->client);\r
+       //dev_info(test->client->adc->dev, "[chn%d] sync_read = %d\n", 0, sync_read);\r
+       schedule_work(&test->timer_work);\r
+       add_timer(&test->timer);\r
+}\r
+\r
+static void adc_timer_work(struct work_struct *work)\r
+{      \r
+       struct adc_test_data *test = container_of(work, struct adc_test_data,\r
+                                               timer_work);\r
+       int i = 0;\r
+#if ADC_POLL\r
+       int ret = 0, count = 0;\r
+       struct adc_host *adc = test->client[i].adc;\r
+       struct rk30_tsadc_device *dev  = adc_priv(adc);\r
+       adc->cur = &test->client[i];\r
+       rk30_tsadc_start(adc);\r
+       while(1)\r
+       {       \r
+               udelay(SAMPLE_RATE);\r
+               ret = readl(dev->regs + ADC_STAS);\r
+               if(!(ret & ADC_STAS_BUSY))\r
+               {       \r
+                       rk30_tsadc_stop(adc);\r
+                       break;\r
+               }\r
+               if(count++ > 10)\r
+               {\r
+                       rk30_tsadc_stop(adc);\r
+                       printk("%s:timeout\n",__func__);\r
+                       break;\r
+               }\r
+       }\r
+       \r
+       sync_read = readl(dev->regs + ADC_DATA);\r
+       dev_info(test->client[i].adc->dev, "[chn=%d] sync_read = %d\n", i, sync_read);\r
+#else  \r
+       int sync_read = 0;\r
+       for(i=0;i<2;i++)\r
+       {       \r
+               adc_async_read(&test->client[i]);       \r
+               sync_read = adc_sync_read(&test->client[i]);            \r
+               dev_info(test->client[i].adc->dev, "[chn=%d] sync_read = %d\n", i, sync_read);\r
+       }\r
+#endif\r
+}\r
+\r
+static int rk30_tsadc_test(void)\r
+{\r
+       struct adc_test_data *test = NULL;\r
+       int i = 0;\r
+       test = kzalloc(sizeof(struct adc_test_data), GFP_KERNEL);\r
+       for(i=0;i<2;i++)\r
+       test->client[i] = *adc_register(i, callback_test, NULL);\r
+\r
+       INIT_WORK(&test->timer_work, adc_timer_work);\r
+       setup_timer(&test->timer, adc_timer, (unsigned long)test);\r
+       test->timer.expires  = jiffies + 200;\r
+       add_timer(&test->timer);\r
+       \r
+       return 0;\r
+}\r
+#endif\r
+\r
+#if 1\r
+struct temp_sample_data {\r
+       struct adc_client client[2];\r
+       struct timer_list timer;\r
+       struct work_struct      timer_work;\r
+};\r
+static struct temp_sample_data *gtemp;\r
+static void callback(struct adc_client *client, void *param, int result)\r
+{\r
+       int i = 0;\r
+       for(i=0; i<2; i++)\r
+       dev_info(client[i].adc->dev, "[chn=%d] async_read = %d\n", client[i].chn, result);\r
+       return;\r
+}\r
+\r
+static int rk30_temp_sample_init(void)\r
+{\r
+       struct temp_sample_data *temp = NULL;   \r
+       int i = 0;\r
+       temp = kzalloc(sizeof(struct temp_sample_data), GFP_KERNEL);\r
+       if (!temp){\r
+               printk("%s:no memory for adc request\n",__func__);\r
+               return -ENOMEM;
+       }\r
+       \r
+       for(i=0; i<2; i++)\r
+       temp->client[i] = *adc_register(i, callback, NULL);\r
+       gtemp = temp;\r
+       return 0;\r
+\r
+}\r
+\r
+\r
+int rk30_temp_sample(int chn, int *result)\r
+{\r
+       int sync_read = 0;\r
+       int i = 0, num = 0;\r
+#if ADC_POLL\r
+       int ret = 0, count = 0; \r
+       struct temp_sample_data *temp = gtemp;  \r
+       struct adc_host *adc;\r
+       struct rk30_tsadc_device *dev;\r
+       chn &= 0x01;    //0 or 1\r
+       adc = temp->client[chn].adc;\r
+       dev = adc_priv(adc);\r
+       adc->cur = &temp->client[chn];\r
+       rk30_tsadc_start_poll(adc);\r
+       while(1)\r
+       {       \r
+               udelay(SAMPLE_RATE);\r
+               ret = readl(dev->regs + ADC_STAS);\r
+               if(!(ret & ADC_STAS_BUSY))\r
+               {       \r
+                       rk30_tsadc_stop(adc);\r
+                       break;\r
+               }\r
+               if(count++ > 20)\r
+               {\r
+                       rk30_tsadc_stop(adc);\r
+                       printk("%s:timeout\n",__func__);\r
+                       break;\r
+               }\r
+       }\r
+       \r
+       sync_read = readl(dev->regs + ADC_DATA);\r
+       //dev_info(temp->client[chn].adc->dev, "[chn=%d] sync_read = %d\n", chn, sync_read);\r
+#else\r
+       adc_async_read(&gtemp->client[chn]);\r
+       sync_read = adc_sync_read(&gtemp->client[chn]);\r
+       dev_info(gtemp->client[chn].adc->dev, "[chn=%d] sync_read = %d\n", chn, sync_read);\r
+#endif\r
+       //get temperature according to ADC value\r
+       num = sizeof(table_code_to_temp)/sizeof(struct tsadc_table);    \r
+       for(i=0; i<num-1;i++)\r
+       {\r
+               if((sync_read >= table_code_to_temp[i+1].code) && (sync_read < table_code_to_temp[i].code))\r
+               {\r
+                       *result = table_code_to_temp[i+1].temp;\r
+                       return 0;\r
+               }\r
+       }\r
+\r
+       if(sync_read <= table_code_to_temp[num-1].code)\r
+       {\r
+               *result = table_code_to_temp[num-1].temp;               \r
+               printk("%s:temperature is out of table\n",__func__);\r
+               return -1;\r
+       }\r
+       else if(sync_read >= table_code_to_temp[0].code)\r
+       {\r
+               *result = table_code_to_temp[0].temp;\r
+               printk("%s:temperature is out of table\n",__func__);\r
+               return -1;\r
+       }\r
+\r
+       return -1;\r
+               \r
+}\r
+\r
+EXPORT_SYMBOL(rk30_temp_sample);\r
+\r
+#endif\r
+\r
+static int rk30_tsadc_probe(struct platform_device *pdev)\r
+{\r
+       struct adc_host *adc = NULL;\r
+       struct rk30_tsadc_device *dev;\r
+       struct resource *res;\r
+       int ret;\r
+\r
+       adc = adc_alloc_host(sizeof(struct rk30_tsadc_device), &pdev->dev);\r
+       if (!adc)\r
+               return -ENOMEM;\r
+       spin_lock_init(&adc->lock);\r
+       adc->dev = &pdev->dev;\r
+       adc->is_suspended = 0;\r
+       adc->ops = &rk30_tsadc_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, rk30_tsadc_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
+\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, "rk30 adc: driver initialized\n");\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 rk30_tsadc_remove(struct platform_device *pdev)\r
+{\r
+       struct rk30_tsadc_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 rk30_tsadc_suspend(struct platform_device *pdev, pm_message_t state)\r
+{\r
+       struct rk30_tsadc_device *dev = platform_get_drvdata(pdev);\r
+\r
+       dev->adc->is_suspended = 1;\r
+       return 0;\r
+}\r
+\r
+static int rk30_tsadc_resume(struct platform_device *pdev)\r
+{\r
+       struct rk30_tsadc_device *dev = platform_get_drvdata(pdev);\r
+\r
+       dev->adc->is_suspended = 0;\r
+       return 0;\r
+}\r
+\r
+#else\r
+#define rk30_tsadc_suspend NULL\r
+#define rk30_tsadc_resume NULL\r
+#endif\r
+\r
+static struct platform_driver rk30_tsadc_driver = {\r
+       .driver         = {\r
+               .name   = "rk30-tsadc",\r
+               .owner  = THIS_MODULE,\r
+       },\r
+       .probe          = rk30_tsadc_probe,\r
+       .remove         = __devexit_p(rk30_tsadc_remove),\r
+       .suspend        = rk30_tsadc_suspend,\r
+       .resume         = rk30_tsadc_resume,\r
+};\r
+\r
+static int __init rk30_tsadc_init(void)\r
+{\r
+       return platform_driver_register(&rk30_tsadc_driver);\r
+}\r
+subsys_initcall(rk30_tsadc_init);\r
+\r
+static void __exit rk30_tsadc_exit(void)\r
+{\r
+       platform_driver_unregister(&rk30_tsadc_driver);\r
+}\r
+module_exit(rk30_tsadc_exit);\r
+\r
+MODULE_DESCRIPTION("Driver for TSADC");\r
+MODULE_AUTHOR("lw, lw@rock-chips.com");\r
+MODULE_LICENSE("GPL");\r
+\r
+static int __init rk30_temp_init(void)\r
+{\r
+       int ret = 0;\r
+       ret = rk30_temp_sample_init();\r
+#ifdef ADC_TEST        \r
+       rk30_tsadc_test();\r
+#endif \r
+       printk("%s:initialized\n",__func__);\r
+       return ret;\r
+}\r
+\r
+static void __exit rk30_temp_exit(void)\r
+{\r
+       int i = 0;\r
+       struct temp_sample_data *temp = gtemp;\r
+       for(i=0; i<2; i++)\r
+       adc_unregister(&temp->client[i]);\r
+       kfree(temp);\r
+}\r
+\r
+module_init(rk30_temp_init);\r
+module_exit(rk30_temp_exit);\r
+\r
diff --git a/drivers/adc/plat/rk30_tsadc.h b/drivers/adc/plat/rk30_tsadc.h
new file mode 100755 (executable)
index 0000000..eb52e51
--- /dev/null
@@ -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 */