/arch/arm/plat-s5pc1xx/adc.c
C | 369 lines | 247 code | 79 blank | 43 comment | 20 complexity | 4419133c44ddfc237900eda0245f41e6 MD5 | raw file
- /* linux/arch/arm/plat-s5c1xx/adc.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, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Copyright (c) 2004 Arnaud Patard <arnaud.patard@rtp-net.org>
- * iPAQ H1940 touchscreen support
- *
- * ChangeLog
- *
- * 2004-09-05: Herbert Pƶtzl <herbert@13thfloor.at>
- * - added clock (de-)allocation code
- *
- * 2005-03-06: Arnaud Patard <arnaud.patard@rtp-net.org>
- * - h1940_ -> s3c24xx (this driver is now also used on the n30
- * machines :P)
- * - Debug messages are now enabled with the config option
- * TOUCHSCREEN_S3C_DEBUG
- * - Changed the way the value are read
- * - Input subsystem should now work
- * - Use ioremap and readl/writel
- *
- * 2005-03-23: Arnaud Patard <arnaud.patard@rtp-net.org>
- * - Make use of some undocumented features of the touchscreen
- * controller
- *
- */
- #include <linux/errno.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/input.h>
- #include <linux/init.h>
- #include <linux/serio.h>
- #include <linux/delay.h>
- #include <linux/platform_device.h>
- #include <linux/miscdevice.h>
- #include <linux/clk.h>
- #include <linux/mutex.h>
- #include <asm/io.h>
- #include <asm/irq.h>
- #include <mach/hardware.h>
- #include <asm/uaccess.h>
- #include <plat/regs-adc.h>
- #include <plat/adc.h>
- #include <mach/irqs.h>
- #define ADC_MINOR 131
- #define ADC_INPUT_PIN _IOW('S', 0x0c, unsigned long)
- #define ADC_WITH_TOUCHSCREEN
- static struct clk *adc_clock;
- static void __iomem *base_addr;
- static int adc_port = 0;
- struct s3c_adc_mach_info *plat_data;
- #ifdef ADC_WITH_TOUCHSCREEN
- static DEFINE_MUTEX(adc_mutex);
- static unsigned long data_for_ADCCON;
- static unsigned long data_for_ADCTSC;
- static void s3c_adc_save_SFR_on_ADC(void)
- {
- data_for_ADCCON = readl(base_addr + S3C_ADCCON);
- data_for_ADCTSC = readl(base_addr + S3C_ADCTSC);
- }
- static void s3c_adc_restore_SFR_on_ADC(void)
- {
- writel(data_for_ADCCON, base_addr + S3C_ADCCON);
- writel(data_for_ADCTSC, base_addr + S3C_ADCTSC);
- }
- #else
- static struct resource *adc_mem;
- #endif
- static int s3c_adc_open(struct inode *inode, struct file *file)
- {
- printk(KERN_INFO " s3c_adc_open() entered\n");
- return 0;
- }
- unsigned int s3c_adc_convert(void)
- {
- unsigned int adc_return = 0;
- unsigned long data0;
- unsigned long data1;
- writel(readl(base_addr + S3C_ADCCON) | S3C_ADCCON_SELMUX(adc_port), base_addr + S3C_ADCCON);
- udelay(10);
- writel(readl(base_addr + S3C_ADCCON) | S3C_ADCCON_ENABLE_START, base_addr + S3C_ADCCON);
- do {
- data0 = readl(base_addr + S3C_ADCCON);
- } while(!(data0 & S3C_ADCCON_ECFLG));
- data1 = readl(base_addr + S3C_ADCDAT0);
- if (plat_data->resolution == 12)
- adc_return = data1 & S3C_ADCDAT0_XPDATA_MASK_12BIT;
- else
- adc_return = data1 & S3C_ADCDAT0_XPDATA_MASK;
- return adc_return;
- }
- int s3c_adc_get(struct s3c_adc_request *req)
- {
- unsigned adc_channel = req->channel;
- int adc_value_ret = 0;
- adc_value_ret = s3c_adc_convert();
- req->callback(adc_channel, req->param, adc_value_ret);
- return 0;
- }
- EXPORT_SYMBOL(s3c_adc_get);
- static ssize_t
- s3c_adc_read(struct file *file, char __user * buffer,
- size_t size, loff_t * pos)
- {
- int adc_value = 0;
- printk(KERN_INFO " s3c_adc_read() entered\n");
- #ifdef ADC_WITH_TOUCHSCREEN
- mutex_lock(&adc_mutex);
- s3c_adc_save_SFR_on_ADC();
- #endif
- adc_value = s3c_adc_convert();
- #ifdef ADC_WITH_TOUCHSCREEN
- s3c_adc_restore_SFR_on_ADC();
- mutex_unlock(&adc_mutex);
- #endif
- printk(KERN_INFO " Converted Value: %03d\n", adc_value);
- if (copy_to_user(buffer, &adc_value, sizeof(unsigned int))) {
- return -EFAULT;
- }
- return sizeof(unsigned int);
- }
- static int s3c_adc_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
- {
- printk(KERN_INFO " s3c_adc_ioctl(cmd:: %d) entered\n", cmd);
- switch (cmd) {
- case ADC_INPUT_PIN:
- adc_port = (unsigned int) arg;
- if (adc_port >= 4)
- printk(" %d is already reserved for TouchScreen\n", adc_port);
- return 0;
- default:
- return -ENOIOCTLCMD;
- }
- }
- static struct file_operations s3c_adc_fops = {
- .owner = THIS_MODULE,
- .read = s3c_adc_read,
- .open = s3c_adc_open,
- .ioctl = s3c_adc_ioctl,
- };
- static struct miscdevice s3c_adc_miscdev = {
- .minor = ADC_MINOR,
- .name = "adc",
- .fops = &s3c_adc_fops,
- };
- static struct s3c_adc_mach_info *s3c_adc_get_platdata(struct device *dev)
- {
- if(dev->platform_data != NULL)
- {
- printk(KERN_INFO "ADC platform data read\n");
- return (struct s3c_adc_mach_info*) dev->platform_data;
- } else {
- printk(KERN_INFO "No ADC platform data \n");
- return 0;
- }
- }
- /*
- * The functions for inserting/removing us as a module.
- */
- static int __init s3c_adc_probe(struct platform_device *pdev)
- {
- struct resource *res;
- struct device *dev;
- int ret;
- int size;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dev = &pdev->dev;
- if(res == NULL){
- dev_err(dev,"no memory resource specified\n");
- return -ENOENT;
- }
- size = (res->end - res->start) + 1;
- #if !defined(ADC_WITH_TOUCHSCREEN)
- adc_mem = request_mem_region(res->start, size, pdev->name);
- if(adc_mem == NULL){
- dev_err(dev, "failed to get memory region\n");
- ret = -ENOENT;
- goto err_req;
- }
- #endif
- base_addr = ioremap(res->start, size);
- if(base_addr == NULL){
- dev_err(dev,"fail to ioremap() region\n");
- ret = -ENOENT;
- goto err_map;
- }
- adc_clock = clk_get(&pdev->dev, "adc");
- if(IS_ERR(adc_clock)){
- dev_err(dev,"failed to fine ADC clock source\n");
- ret = PTR_ERR(adc_clock);
- goto err_clk;
- }
- clk_enable(adc_clock);
- /* read platform data from device struct */
- plat_data = s3c_adc_get_platdata(&pdev->dev);
- if ((plat_data->presc & 0xff) > 0)
- writel(S3C_ADCCON_PRSCEN | S3C_ADCCON_PRSCVL(plat_data->presc & 0xff), base_addr + S3C_ADCCON);
- else
- writel(0, base_addr + S3C_ADCCON);
- /* Initialise registers */
- if ((plat_data->delay & 0xffff) > 0)
- writel(plat_data->delay & 0xffff, base_addr + S3C_ADCDLY);
- if (plat_data->resolution == 12)
- writel(readl(base_addr + S3C_ADCCON) | S3C_ADCCON_RESSEL_12BIT, base_addr + S3C_ADCCON);
- ret = misc_register(&s3c_adc_miscdev);
- if (ret) {
- printk (KERN_ERR "cannot register miscdev on minor=%d (%d)\n",
- ADC_MINOR, ret);
- goto err_clk;
- }
- printk(KERN_INFO "S5PC1XX ADC driver successfully probed\n");
- return 0;
- err_clk:
- clk_disable(adc_clock);
- clk_put(adc_clock);
- err_map:
- iounmap(base_addr);
- #if !defined(ADC_WITH_TOUCHSCREEN)
- err_req:
- release_resource(adc_mem);
- kfree(adc_mem);
- #endif
- return ret;
- }
- static int s3c_adc_remove(struct platform_device *dev)
- {
- printk(KERN_INFO "s3c_adc_remove() of ADC called !\n");
- return 0;
- }
- #ifdef CONFIG_PM
- static unsigned int adccon, adctsc, adcdly;
- static int s3c_adc_suspend(struct platform_device *dev, pm_message_t state)
- {
- adccon = readl(base_addr + S3C_ADCCON);
- adctsc = readl(base_addr + S3C_ADCTSC);
- adcdly = readl(base_addr + S3C_ADCDLY);
- clk_disable(adc_clock);
- return 0;
- }
- static int s3c_adc_resume(struct platform_device *pdev)
- {
- clk_enable(adc_clock);
- writel(adccon, base_addr + S3C_ADCCON);
- writel(adctsc, base_addr + S3C_ADCTSC);
- writel(adcdly, base_addr + S3C_ADCDLY);
- return 0;
- }
- #else
- #define s3c_adc_suspend NULL
- #define s3c_adc_resume NULL
- #endif
- static struct platform_driver s3c_adc_driver = {
- .probe = s3c_adc_probe,
- .remove = s3c_adc_remove,
- .suspend = s3c_adc_suspend,
- .resume = s3c_adc_resume,
- .driver = {
- .owner = THIS_MODULE,
- .name = "s3c-adc",
- },
- };
- static char banner[] __initdata = KERN_INFO "S5PC1XX ADC driver, (c) 2008 Samsung Electronics\n";
- int __init s3c_adc_init(void)
- {
- printk(banner);
- return platform_driver_register(&s3c_adc_driver);
- }
- void __exit s3c_adc_exit(void)
- {
- platform_driver_unregister(&s3c_adc_driver);
- }
- module_init(s3c_adc_init);
- module_exit(s3c_adc_exit);
- MODULE_AUTHOR("boyko.lee@samsung.com");
- MODULE_DESCRIPTION("S5PC1XX ADC driver");
- MODULE_LICENSE("GPL");