--- /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