PageRenderTime 52ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/arch/arm/plat-s5pc1xx/adc.c

https://github.com/CyanogenMod/samsung-kernel-galaxys
C | 369 lines | 247 code | 79 blank | 43 comment | 20 complexity | 4419133c44ddfc237900eda0245f41e6 MD5 | raw file
  1. /* linux/arch/arm/plat-s5c1xx/adc.c
  2. *
  3. * This program is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. *
  17. * Copyright (c) 2004 Arnaud Patard <arnaud.patard@rtp-net.org>
  18. * iPAQ H1940 touchscreen support
  19. *
  20. * ChangeLog
  21. *
  22. * 2004-09-05: Herbert Pƶtzl <herbert@13thfloor.at>
  23. * - added clock (de-)allocation code
  24. *
  25. * 2005-03-06: Arnaud Patard <arnaud.patard@rtp-net.org>
  26. * - h1940_ -> s3c24xx (this driver is now also used on the n30
  27. * machines :P)
  28. * - Debug messages are now enabled with the config option
  29. * TOUCHSCREEN_S3C_DEBUG
  30. * - Changed the way the value are read
  31. * - Input subsystem should now work
  32. * - Use ioremap and readl/writel
  33. *
  34. * 2005-03-23: Arnaud Patard <arnaud.patard@rtp-net.org>
  35. * - Make use of some undocumented features of the touchscreen
  36. * controller
  37. *
  38. */
  39. #include <linux/errno.h>
  40. #include <linux/kernel.h>
  41. #include <linux/module.h>
  42. #include <linux/slab.h>
  43. #include <linux/input.h>
  44. #include <linux/init.h>
  45. #include <linux/serio.h>
  46. #include <linux/delay.h>
  47. #include <linux/platform_device.h>
  48. #include <linux/miscdevice.h>
  49. #include <linux/clk.h>
  50. #include <linux/mutex.h>
  51. #include <asm/io.h>
  52. #include <asm/irq.h>
  53. #include <mach/hardware.h>
  54. #include <asm/uaccess.h>
  55. #include <plat/regs-adc.h>
  56. #include <plat/adc.h>
  57. #include <mach/irqs.h>
  58. #define ADC_MINOR 131
  59. #define ADC_INPUT_PIN _IOW('S', 0x0c, unsigned long)
  60. #define ADC_WITH_TOUCHSCREEN
  61. static struct clk *adc_clock;
  62. static void __iomem *base_addr;
  63. static int adc_port = 0;
  64. struct s3c_adc_mach_info *plat_data;
  65. #ifdef ADC_WITH_TOUCHSCREEN
  66. static DEFINE_MUTEX(adc_mutex);
  67. static unsigned long data_for_ADCCON;
  68. static unsigned long data_for_ADCTSC;
  69. static void s3c_adc_save_SFR_on_ADC(void)
  70. {
  71. data_for_ADCCON = readl(base_addr + S3C_ADCCON);
  72. data_for_ADCTSC = readl(base_addr + S3C_ADCTSC);
  73. }
  74. static void s3c_adc_restore_SFR_on_ADC(void)
  75. {
  76. writel(data_for_ADCCON, base_addr + S3C_ADCCON);
  77. writel(data_for_ADCTSC, base_addr + S3C_ADCTSC);
  78. }
  79. #else
  80. static struct resource *adc_mem;
  81. #endif
  82. static int s3c_adc_open(struct inode *inode, struct file *file)
  83. {
  84. printk(KERN_INFO " s3c_adc_open() entered\n");
  85. return 0;
  86. }
  87. unsigned int s3c_adc_convert(void)
  88. {
  89. unsigned int adc_return = 0;
  90. unsigned long data0;
  91. unsigned long data1;
  92. writel(readl(base_addr + S3C_ADCCON) | S3C_ADCCON_SELMUX(adc_port), base_addr + S3C_ADCCON);
  93. udelay(10);
  94. writel(readl(base_addr + S3C_ADCCON) | S3C_ADCCON_ENABLE_START, base_addr + S3C_ADCCON);
  95. do {
  96. data0 = readl(base_addr + S3C_ADCCON);
  97. } while(!(data0 & S3C_ADCCON_ECFLG));
  98. data1 = readl(base_addr + S3C_ADCDAT0);
  99. if (plat_data->resolution == 12)
  100. adc_return = data1 & S3C_ADCDAT0_XPDATA_MASK_12BIT;
  101. else
  102. adc_return = data1 & S3C_ADCDAT0_XPDATA_MASK;
  103. return adc_return;
  104. }
  105. int s3c_adc_get(struct s3c_adc_request *req)
  106. {
  107. unsigned adc_channel = req->channel;
  108. int adc_value_ret = 0;
  109. adc_value_ret = s3c_adc_convert();
  110. req->callback(adc_channel, req->param, adc_value_ret);
  111. return 0;
  112. }
  113. EXPORT_SYMBOL(s3c_adc_get);
  114. static ssize_t
  115. s3c_adc_read(struct file *file, char __user * buffer,
  116. size_t size, loff_t * pos)
  117. {
  118. int adc_value = 0;
  119. printk(KERN_INFO " s3c_adc_read() entered\n");
  120. #ifdef ADC_WITH_TOUCHSCREEN
  121. mutex_lock(&adc_mutex);
  122. s3c_adc_save_SFR_on_ADC();
  123. #endif
  124. adc_value = s3c_adc_convert();
  125. #ifdef ADC_WITH_TOUCHSCREEN
  126. s3c_adc_restore_SFR_on_ADC();
  127. mutex_unlock(&adc_mutex);
  128. #endif
  129. printk(KERN_INFO " Converted Value: %03d\n", adc_value);
  130. if (copy_to_user(buffer, &adc_value, sizeof(unsigned int))) {
  131. return -EFAULT;
  132. }
  133. return sizeof(unsigned int);
  134. }
  135. static int s3c_adc_ioctl(struct inode *inode, struct file *file,
  136. unsigned int cmd, unsigned long arg)
  137. {
  138. printk(KERN_INFO " s3c_adc_ioctl(cmd:: %d) entered\n", cmd);
  139. switch (cmd) {
  140. case ADC_INPUT_PIN:
  141. adc_port = (unsigned int) arg;
  142. if (adc_port >= 4)
  143. printk(" %d is already reserved for TouchScreen\n", adc_port);
  144. return 0;
  145. default:
  146. return -ENOIOCTLCMD;
  147. }
  148. }
  149. static struct file_operations s3c_adc_fops = {
  150. .owner = THIS_MODULE,
  151. .read = s3c_adc_read,
  152. .open = s3c_adc_open,
  153. .ioctl = s3c_adc_ioctl,
  154. };
  155. static struct miscdevice s3c_adc_miscdev = {
  156. .minor = ADC_MINOR,
  157. .name = "adc",
  158. .fops = &s3c_adc_fops,
  159. };
  160. static struct s3c_adc_mach_info *s3c_adc_get_platdata(struct device *dev)
  161. {
  162. if(dev->platform_data != NULL)
  163. {
  164. printk(KERN_INFO "ADC platform data read\n");
  165. return (struct s3c_adc_mach_info*) dev->platform_data;
  166. } else {
  167. printk(KERN_INFO "No ADC platform data \n");
  168. return 0;
  169. }
  170. }
  171. /*
  172. * The functions for inserting/removing us as a module.
  173. */
  174. static int __init s3c_adc_probe(struct platform_device *pdev)
  175. {
  176. struct resource *res;
  177. struct device *dev;
  178. int ret;
  179. int size;
  180. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  181. dev = &pdev->dev;
  182. if(res == NULL){
  183. dev_err(dev,"no memory resource specified\n");
  184. return -ENOENT;
  185. }
  186. size = (res->end - res->start) + 1;
  187. #if !defined(ADC_WITH_TOUCHSCREEN)
  188. adc_mem = request_mem_region(res->start, size, pdev->name);
  189. if(adc_mem == NULL){
  190. dev_err(dev, "failed to get memory region\n");
  191. ret = -ENOENT;
  192. goto err_req;
  193. }
  194. #endif
  195. base_addr = ioremap(res->start, size);
  196. if(base_addr == NULL){
  197. dev_err(dev,"fail to ioremap() region\n");
  198. ret = -ENOENT;
  199. goto err_map;
  200. }
  201. adc_clock = clk_get(&pdev->dev, "adc");
  202. if(IS_ERR(adc_clock)){
  203. dev_err(dev,"failed to fine ADC clock source\n");
  204. ret = PTR_ERR(adc_clock);
  205. goto err_clk;
  206. }
  207. clk_enable(adc_clock);
  208. /* read platform data from device struct */
  209. plat_data = s3c_adc_get_platdata(&pdev->dev);
  210. if ((plat_data->presc & 0xff) > 0)
  211. writel(S3C_ADCCON_PRSCEN | S3C_ADCCON_PRSCVL(plat_data->presc & 0xff), base_addr + S3C_ADCCON);
  212. else
  213. writel(0, base_addr + S3C_ADCCON);
  214. /* Initialise registers */
  215. if ((plat_data->delay & 0xffff) > 0)
  216. writel(plat_data->delay & 0xffff, base_addr + S3C_ADCDLY);
  217. if (plat_data->resolution == 12)
  218. writel(readl(base_addr + S3C_ADCCON) | S3C_ADCCON_RESSEL_12BIT, base_addr + S3C_ADCCON);
  219. ret = misc_register(&s3c_adc_miscdev);
  220. if (ret) {
  221. printk (KERN_ERR "cannot register miscdev on minor=%d (%d)\n",
  222. ADC_MINOR, ret);
  223. goto err_clk;
  224. }
  225. printk(KERN_INFO "S5PC1XX ADC driver successfully probed\n");
  226. return 0;
  227. err_clk:
  228. clk_disable(adc_clock);
  229. clk_put(adc_clock);
  230. err_map:
  231. iounmap(base_addr);
  232. #if !defined(ADC_WITH_TOUCHSCREEN)
  233. err_req:
  234. release_resource(adc_mem);
  235. kfree(adc_mem);
  236. #endif
  237. return ret;
  238. }
  239. static int s3c_adc_remove(struct platform_device *dev)
  240. {
  241. printk(KERN_INFO "s3c_adc_remove() of ADC called !\n");
  242. return 0;
  243. }
  244. #ifdef CONFIG_PM
  245. static unsigned int adccon, adctsc, adcdly;
  246. static int s3c_adc_suspend(struct platform_device *dev, pm_message_t state)
  247. {
  248. adccon = readl(base_addr + S3C_ADCCON);
  249. adctsc = readl(base_addr + S3C_ADCTSC);
  250. adcdly = readl(base_addr + S3C_ADCDLY);
  251. clk_disable(adc_clock);
  252. return 0;
  253. }
  254. static int s3c_adc_resume(struct platform_device *pdev)
  255. {
  256. clk_enable(adc_clock);
  257. writel(adccon, base_addr + S3C_ADCCON);
  258. writel(adctsc, base_addr + S3C_ADCTSC);
  259. writel(adcdly, base_addr + S3C_ADCDLY);
  260. return 0;
  261. }
  262. #else
  263. #define s3c_adc_suspend NULL
  264. #define s3c_adc_resume NULL
  265. #endif
  266. static struct platform_driver s3c_adc_driver = {
  267. .probe = s3c_adc_probe,
  268. .remove = s3c_adc_remove,
  269. .suspend = s3c_adc_suspend,
  270. .resume = s3c_adc_resume,
  271. .driver = {
  272. .owner = THIS_MODULE,
  273. .name = "s3c-adc",
  274. },
  275. };
  276. static char banner[] __initdata = KERN_INFO "S5PC1XX ADC driver, (c) 2008 Samsung Electronics\n";
  277. int __init s3c_adc_init(void)
  278. {
  279. printk(banner);
  280. return platform_driver_register(&s3c_adc_driver);
  281. }
  282. void __exit s3c_adc_exit(void)
  283. {
  284. platform_driver_unregister(&s3c_adc_driver);
  285. }
  286. module_init(s3c_adc_init);
  287. module_exit(s3c_adc_exit);
  288. MODULE_AUTHOR("boyko.lee@samsung.com");
  289. MODULE_DESCRIPTION("S5PC1XX ADC driver");
  290. MODULE_LICENSE("GPL");