--- /dev/null
+/*
+ * Copyright (C) 2012 ROCKCHIP, Inc.
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#define TSADC_DATA 0x00
+#define TSADC_DATA_MASK 0xfff
+
+#define TSADC_STAS 0x04
+#define TSADC_STAS_BUSY (1 << 0)
+#define TSADC_STAS_BUSY_MASK (1 << 0)
+
+#define TSADC_CTRL 0x08
+#define TSADC_CTRL_CH(ch) ((ch) << 0)
+#define TSADC_CTRL_POWER_UP (1 << 3)
+#define TSADC_CTRL_START (1 << 4)
+#define TSADC_CTRL_IRQ_ENABLE (1 << 5)
+#define TSADC_CTRL_IRQ_STATUS (1 << 6)
+
+#define TSADC_DLY_PU_SOC 0x0C
+
+#define TSADC_CLK_RATE 50000 /* 50KHz */
+
+struct tsadc_table
+{
+ int code;
+ int temp;
+};
+
+static const struct tsadc_table table[] =
+{
+ {TSADC_DATA_MASK, -40},
+
+ {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},
+
+ {0, 125},
+};
+
+struct rk30_tsadc_device {
+ void __iomem *regs;
+ struct clk *clk;
+ struct clk *pclk;
+ struct resource *ioarea;
+};
+
+static struct rk30_tsadc_device *g_dev;
+
+static u32 tsadc_readl(u32 offset)
+{
+ return readl_relaxed(g_dev->regs + offset);
+}
+
+static void tsadc_writel(u32 val, u32 offset)
+{
+ writel_relaxed(val, g_dev->regs + offset);
+}
+
+static void rk30_tsadc_get(unsigned int chn, int *temp, int *code)
+{
+ *temp = 0;
+ *code = 0;
+
+ if (!g_dev || chn > 1)
+ return;
+
+ clk_enable(g_dev->pclk);
+ clk_enable(g_dev->clk);
+
+ msleep(10);
+ tsadc_writel(0, TSADC_CTRL);
+ tsadc_writel(TSADC_CTRL_POWER_UP | TSADC_CTRL_CH(chn), TSADC_CTRL);
+ msleep(10);
+ if ((tsadc_readl(TSADC_STAS) & TSADC_STAS_BUSY_MASK) != TSADC_STAS_BUSY) {
+ int i;
+ *code = tsadc_readl(TSADC_DATA) & TSADC_DATA_MASK;
+ for (i = 0; i < ARRAY_SIZE(table) - 1; i++) {
+ if ((*code) <= table[i].code && (*code) > table[i + 1].code) {
+ *temp = table[i].temp + (table[i + 1].temp - table[i].temp) * (table[i].code - (*code)) / (table[i].code - table[i + 1].code);
+ }
+ }
+ }
+ tsadc_writel(0, TSADC_CTRL);
+ msleep(10);
+
+ clk_disable(g_dev->clk);
+ clk_disable(g_dev->pclk);
+}
+
+int rk30_tsadc_get_temp(unsigned int chn)
+{
+ int temp, code;
+
+ rk30_tsadc_get(chn, &temp, &code);
+ return temp;
+}
+EXPORT_SYMBOL(rk30_tsadc_get_temp);
+
+static int rk30_tsadc_get_temp0(char *buffer, struct kernel_param *kp)
+{
+ int temp, code;
+ rk30_tsadc_get(0, &temp, &code);
+ return sprintf(buffer, "temp: %d code: %d", temp, code);
+}
+module_param_call(temp0, NULL, rk30_tsadc_get_temp0, NULL, S_IRUGO);
+
+static int rk30_tsadc_get_temp1(char *buffer, struct kernel_param *kp)
+{
+ int temp, code;
+ rk30_tsadc_get(1, &temp, &code);
+ return sprintf(buffer, "temp: %d code: %d", temp, code);
+}
+module_param_call(temp1, NULL, rk30_tsadc_get_temp1, NULL, S_IRUGO);
+
+static int __init rk30_tsadc_probe(struct platform_device *pdev)
+{
+ struct rk30_tsadc_device *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ struct resource *res;
+ int ret;
+
+ if (!dev) {
+ dev_err(&pdev->dev, "failed to alloc mem\n");
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "cannot find IO resource\n");
+ ret = -ENOENT;
+ goto err1;
+ }
+
+ dev->ioarea = request_mem_region(res->start, (res->end - res->start) + 1, pdev->name);
+ if (!dev->ioarea) {
+ dev_err(&pdev->dev, "cannot request IO\n");
+ ret = -ENXIO;
+ goto err1;
+ }
+
+ dev->regs = ioremap(res->start, (res->end - res->start) + 1);
+ if (!dev->regs) {
+ dev_err(&pdev->dev, "cannot map IO\n");
+ ret = -ENXIO;
+ goto err2;
+ }
+
+ dev->clk = clk_get(NULL, "tsadc");
+ if (IS_ERR(dev->clk)) {
+ dev_err(&pdev->dev, "failed to get clk\n");
+ ret = PTR_ERR(dev->clk);
+ goto err3;
+ }
+
+ ret = clk_set_rate(dev->clk, TSADC_CLK_RATE);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to set clk\n");
+ goto err4;
+ }
+
+ dev->pclk = clk_get(NULL, "pclk_tsadc");
+ if (IS_ERR(dev->pclk)) {
+ dev_err(&pdev->dev, "failed to get pclk\n");
+ ret = PTR_ERR(dev->clk);
+ goto err4;
+ }
+
+ platform_set_drvdata(pdev, dev);
+ g_dev = dev;
+
+ dev_info(&pdev->dev, "initialized\n");
+
+ return 0;
+
+err4:
+ clk_put(dev->clk);
+err3:
+ iounmap(dev->regs);
+err2:
+ release_resource(dev->ioarea);
+err1:
+ kfree(dev);
+ return ret;
+}
+
+static struct platform_driver rk30_tsadc_driver = {
+ .driver = {
+ .name = "rk30-tsadc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init rk30_tsadc_init(void)
+{
+ return platform_driver_probe(&rk30_tsadc_driver, rk30_tsadc_probe);
+}
+rootfs_initcall(rk30_tsadc_init);
+
+MODULE_DESCRIPTION("Driver for TSADC");
+MODULE_AUTHOR("lw, lw@rock-chips.com");
+MODULE_LICENSE("GPL");
+++ /dev/null
-/* 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(>emp->client[chn]);\r
- sync_read = adc_sync_read(>emp->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