summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWang Xiaojun <b41435@freescale.com>2012-12-10 18:05:18 +0800
committerEd Nash <enash@enash-desktop.(none)>2012-12-12 14:46:32 -0500
commitb358081d3db11e4f4582bea7658b93a7eef539d3 (patch)
tree9d45952dfa8b73b0b63c6fd568dc1cdd39167259
parent3664f4e8ca3d18e87bc695f4b6838be61382dc17 (diff)
ENGR00181365-2: ADC: Add driver support for ADC Vybrid
Add driver support for ADC Vybrid. Signed-off-by: Wang Xiaojun <b41435@freescale.com>
-rw-r--r--drivers/misc/Kconfig10
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/mvf_adc.c771
-rw-r--r--include/linux/mvf_adc.h204
4 files changed, 986 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index b8bc353fa6aa..8ffd002f0649 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -490,6 +490,16 @@ config PCH_PHUB
To compile this driver as a module, choose M here: the module will
be called pch_phub.
+config MVF_ADC
+ tristate "Vybrid board adc Driver"
+ depends on ARCH_MVF
+ help
+ If you say yes here you get support for the Vybrid board
+ analog-to-digital converter.
+ This driver is for Vybrid board.
+ To compile this driver as a module, choose M here: the module will
+ be called mvf_adc.
+
config MXS_PERFMON
tristate "i.MX Performance Monitor"
depends on ARCH_MX50 || ARCH_MX6
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 88a160b2eb1d..1531d0f5b594 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -47,3 +47,4 @@ obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o
obj-y += lis3lv02d/
obj-y += carma/
obj-$(CONFIG_MXS_PERFMON) += mxs-perfmon.o
+obj-$(CONFIG_MVF_ADC) += mvf_adc.o
diff --git a/drivers/misc/mvf_adc.c b/drivers/misc/mvf_adc.c
new file mode 100644
index 000000000000..63e0f95804f9
--- /dev/null
+++ b/drivers/misc/mvf_adc.c
@@ -0,0 +1,771 @@
+/* Copyright 2012 Freescale Semiconductor, Inc.
+ *
+ * Freescale Faraday Quad ADC driver
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioctl.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/mvf_adc.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+
+#define DRIVER_NAME "mvf-adc"
+#define DRV_VERSION "1.0"
+
+/*
+ * wait_event_interruptible(wait_queue_head_t suspendq, int suspend_flag)
+ */
+
+struct adc_client {
+ struct platform_device *pdev;
+ struct list_head list;
+ wait_queue_head_t *wait;
+
+ unsigned int result;
+ unsigned int channel;
+};
+
+static LIST_HEAD(client_list);/* Initialization client list head */
+static DECLARE_COMPLETION(adc_tsi);
+
+struct adc_device {
+ struct platform_device *pdev;
+ struct platform_device *owner;
+ struct clk *clk;
+ struct adc_client *cur;
+ void __iomem *regs;
+ spinlock_t lock;
+
+ int irq;
+};
+
+struct data {
+ unsigned int res_value;
+ bool flag;
+};
+
+struct data data_array[7];
+
+static struct adc_device *adc_dev;
+
+#define adc_dbg(_adc, msg...) dev_dbg(&(_adc)->pdev->dev, msg)
+
+static int res_proc(void);
+struct adc_client *adc_register(struct platform_device *pdev,
+ unsigned char channel);
+
+/* select channel and enable conversion */
+static inline void adc_convert(struct adc_device *adc,
+ struct adc_client *client)
+{
+
+ unsigned con = readl(adc->regs + ADC_HC0);
+ con = ADCHC0(client->channel);
+ con |= IRQ_EN;
+ writel(con, adc->regs + ADC_HC0);
+}
+
+/* find next conversion client */
+static void adc_try(struct adc_device *adc)
+{
+ struct adc_client *next = adc->cur;
+ if (!list_empty(&client_list)) {
+ next = list_entry(client_list.next,
+ struct adc_client, list);
+ list_del(client_list.next);
+
+ adc->cur = next;
+ adc_convert(adc, adc->cur);
+ }
+}
+
+/* channel and sample */
+int adc_start(struct adc_client *client,
+ unsigned int channel, unsigned int nr_samples)
+{
+ struct adc_device *adc = adc_dev;
+ unsigned long flags;
+
+ if (!adc) {
+ printk(KERN_ERR "%s: failed to find adc\n", __func__);
+ return -EINVAL;
+ }
+
+
+ spin_lock_irqsave(&adc->lock, flags);
+
+ client->channel = channel;
+
+ if (!adc->cur)
+ adc_try(adc_dev);
+
+ spin_unlock_irqrestore(&adc->lock, flags);
+
+ return 0;
+}
+
+int adc_initiate(struct adc_device *adc_dev)
+{
+ unsigned long reg, tmp, pin;
+ struct adc_device *adc = adc_dev;
+
+
+ /* PCTL: pin control 5 for Sliding rheostat */
+ pin = readl(adc->regs+ADC_PCTL);
+ pin &= ~CLEPCTL23;
+ pin &= SETPCTL5;
+ writel(pin, adc->regs+ADC_PCTL);
+
+ /* CFG: Feature set */
+ reg = readl(adc->regs+ADC_CFG);
+ reg &= ~CLECFG16;
+ reg |= SETCFG;
+ writel(reg, adc->regs+ADC_CFG);
+
+ /* GC: software trigger */
+ tmp = readl(adc->regs+ADC_GC);
+ tmp = 0;
+ writel(tmp, adc->regs+ADC_GC);
+
+ return 0;
+}
+
+static int adc_set(struct adc_device *adc_dev, struct adc_feature *adc_fea)
+{
+ struct adc_device *adc = adc_dev;
+ int con, res;
+ con = readl(adc->regs+ADC_CFG);
+ res = readl(adc->regs+ADC_GC);
+
+ /* clock select and clock devide */
+ switch (adc_fea->clk_sel) {
+ case ADCIOC_BUSCLK_SET:
+ /* clear 1-0,6-5 */
+ con &= ~CLEAR_CLK_BIT;
+ switch (adc_fea->clk_div_num) {
+ case 1:
+ break;
+ case 2:
+ con |= CLK_DIV2;
+ break;
+ case 4:
+ con |= CLK_DIV4;
+ break;
+ case 8:
+ con |= CLK_DIV8;
+ break;
+ case 16:
+ con |= BUSCLK2_SEL | CLK_DIV8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ writel(con, adc->regs+ADC_CFG);
+
+ break;
+
+ case ADCIOC_ALTCLK_SET:
+ /* clear 1-0,6-5 */
+ con &= ~CLEAR_CLK_BIT;
+
+ con |= ALTCLK_SEL;
+ switch (adc_fea->clk_div_num) {
+ case 1:
+ break;
+ case 2:
+ con |= CLK_DIV2;
+ break;
+ case 4:
+ con |= CLK_DIV4;
+ break;
+ case 8:
+ con |= CLK_DIV8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ writel(con, adc->regs+ADC_CFG);
+
+ break;
+
+ case ADCIOC_ADACK_SET:
+ /* clear 1-0,6-5 */
+ con &= ~CLEAR_CLK_BIT;
+
+ con |= ADACK_SEL;
+ switch (adc_fea->clk_div_num) {
+ case 1:
+ break;
+ case 2:
+ con |= CLK_DIV2;
+ break;
+ case 4:
+ con |= CLK_DIV4;
+ break;
+ case 8:
+ con |= CLK_DIV8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ writel(con, adc->regs+ADC_CFG);
+ break;
+
+ default:
+ pr_debug("adc_ioctl: unsupported ioctl command 0x%x\n",
+ adc_fea->clk_sel);
+ return -EINVAL;
+ }
+
+ /* resolution mode,clear 3-2 */
+ con &= ~CLEAR_MODE_BIT;
+ switch (adc_fea->res_mode) {
+ case 8:
+ break;
+ case 10:
+ con |= BIT10;
+ break;
+ case 12:
+ con |= BIT12;
+ break;
+ default:
+ pr_debug("adc_ioctl: unsupported ioctl resolution mode 0x%x\n",
+ adc_fea->res_mode);
+ return -EINVAL;
+ }
+ writel(con, adc->regs+ADC_CFG);
+
+ /* Defines the sample time duration */
+ /* clear 4, 9-8 */
+ con &= ~CLEAR_LSTC_BIT;
+ switch (adc_fea->sam_time) {
+ case 2:
+ break;
+ case 4:
+ con |= ADSTS_SHORT;
+ break;
+ case 6:
+ con |= ADSTS_NORMAL;
+ break;
+ case 8:
+ con |= ADSTS_LONG;
+ break;
+ case 12:
+ con |= ADLSMP_LONG;
+ break;
+ case 16:
+ con |= ADLSMP_LONG | ADSTS_SHORT;
+ break;
+ case 20:
+ con |= ADLSMP_LONG | ADSTS_NORMAL;
+ break;
+ case 24:
+ con |= ADLSMP_LONG | ADSTS_LONG;
+ break;
+ default:
+ pr_debug("adc_ioctl: unsupported ioctl sample"
+ "time duration 0x%x\n",
+ adc_fea->sam_time);
+ return -EINVAL;
+ }
+ writel(con, adc->regs+ADC_CFG);
+
+ /* low power configuration */
+ /* */
+ switch (adc_fea->lp_con) {
+ case ADCIOC_LPOFF_SET:
+ con &= ~CLEAR_ADLPC_BIT;
+ writel(con, adc->regs+ADC_CFG);
+ break;
+
+ case ADCIOC_LPON_SET:
+ con &= ~CLEAR_ADLPC_BIT;
+ con |= ADLPC_EN;
+ writel(con, adc->regs+ADC_CFG);
+ break;
+ default:
+ return -EINVAL;
+
+ }
+ /* high speed operation */
+ switch (adc_fea->hs_oper) {
+ case ADCIOC_HSON_SET:
+ con &= ~CLEAR_ADHSC_BIT;
+ con |= ADHSC_EN;
+ writel(con, adc->regs+ADC_CFG);
+ break;
+
+ case ADCIOC_HSOFF_SET:
+ con &= ~CLEAR_ADHSC_BIT;
+ writel(con, adc->regs+ADC_CFG);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* voltage reference*/
+ switch (adc_fea->vol_ref) {
+ case ADCIOC_VR_VREF_SET:
+ con &= ~CLEAR_REFSEL_BIT;
+ writel(con, adc->regs+ADC_CFG);
+ break;
+
+ case ADCIOC_VR_VALT_SET:
+ con &= ~CLEAR_REFSEL_BIT;
+ con |= REFSEL_VALT;
+ writel(con, adc->regs+ADC_CFG);
+ break;
+
+ case ADCIOC_VR_VBG_SET:
+ con &= ~CLEAR_REFSEL_BIT;
+ con |= REFSEL_VBG;
+ writel(con, adc->regs+ADC_CFG);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* trigger select */
+ switch (adc_fea->tri_sel) {
+ case ADCIOC_SOFTTS_SET:
+ con &= ~CLEAR_ADTRG_BIT;
+ writel(con, adc->regs+ADC_CFG);
+ break;
+
+ case ADCIOC_HARDTS_SET:
+ con &= ~CLEAR_ADTRG_BIT;
+ con |= ADTRG_HARD;
+ writel(con, adc->regs+ADC_CFG);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* hardware average select */
+ switch (adc_fea->ha_sel) {
+ case ADCIOC_HA_DIS:
+ res &= ~CLEAR_AVGE_BIT;
+ writel(con, adc->regs+ADC_GC);
+ break;
+
+ case ADCIOC_HA_SET:
+ con &= ~CLEAR_AVGS_BIT;
+ switch (adc_fea->ha_sam) {
+ case 4:
+ break;
+ case 8:
+ con |= AVGS_8;
+ break;
+ case 16:
+ con |= AVGS_16;
+ break;
+ case 32:
+ con |= AVGS_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+ res &= ~CLEAR_AVGE_BIT;
+ res |= AVGEN;
+ writel(con, adc->regs+ADC_CFG);
+ writel(res, adc->regs+ADC_GC);
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* data overwrite enable */
+ switch (adc_fea->do_ena) {
+ case ADCIOC_DOEON_SET:
+ con &= ~CLEAR_OVWREN_BIT;
+ writel(con, adc->regs+ADC_CFG);
+ break;
+
+ case ADCIOC_DOEOFF_SET:
+ con |= OVWREN;
+ writel(con, adc->regs+ADC_CFG);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Asynchronous clock output enable */
+ switch (adc_fea->ac_ena) {
+ case ADCIOC_ADACKENON_SET:
+ res &= ~CLEAR_ADACKEN_BIT;
+ writel(res, adc->regs+ADC_GC);
+ break;
+
+ case ADCIOC_ADACKENOFF_SET:
+ res &= ~CLEAR_ADACKEN_BIT;
+ res |= ADACKEN;
+ writel(res, adc->regs+ADC_GC);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* dma enable */
+ switch (adc_fea->dma_ena) {
+ case ADCIDC_DMAON_SET:
+ res &= ~CLEAR_DMAEN_BIT;
+ writel(res, adc->regs+ADC_GC);
+ break;
+
+ case ADCIDC_DMAOFF_SET:
+ res &= ~CLEAR_DMAEN_BIT;
+ res |= DMAEN;
+ writel(res, adc->regs+ADC_GC);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* continue function enable */
+ switch (adc_fea->cc_ena) {
+ case ADCIOC_CCEOFF_SET:
+ res &= ~CLEAR_ADCO_BIT;
+ writel(res, adc->regs+ADC_GC);
+ break;
+
+ case ADCIOC_CCEON_SET:
+ res &= ~CLEAR_ADCO_BIT;
+ res |= ADCON;
+ writel(res, adc->regs+ADC_GC);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* compare function enable */
+ switch (adc_fea->compare_func_ena) {
+ case ADCIOC_ACFEON_SET:
+ res &= ~CLEAR_ACFE_BIT;
+ res |= ACFE;
+ writel(res, adc->regs+ADC_GC);
+ break;
+
+ case ADCIOC_ACFEOFF_SET:
+ res &= ~CLEAR_ACFE_BIT;
+ writel(res, adc->regs+ADC_GC);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* greater than enable */
+ switch (adc_fea->greater_ena) {
+ case ADCIOC_ACFGTON_SET:
+ res &= ~CLEAR_ACFGT_BIT;
+ res |= ACFGT;
+ writel(res, adc->regs+ADC_GC);
+ break;
+
+ case ADCIOC_ACFGTOFF_SET:
+ res &= ~CLEAR_ACFGT_BIT;
+ writel(res, adc->regs+ADC_GC);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* range enable */
+ switch (adc_fea->range_ena) {
+ case ADCIOC_ACRENON_SET:
+ res &= ~CLEAR_ACREN_BIT;
+ res |= ACREN;
+ writel(res, adc->regs+ADC_GC);
+ break;
+
+ case ADCIOC_ACRENOFF_SET:
+ res &= ~CLEAR_ACREN_BIT;
+ writel(res, adc->regs+ADC_GC);
+ break;
+
+ default: return -ENOTTY;
+ }
+ return 0;
+}
+
+static long adc_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct adc_feature feature;
+ int channel;
+
+ if (_IOC_TYPE(cmd) != 'p')
+ return -ENOTTY;
+
+ if (copy_from_user(&feature, (struct adc_feature *)argp,
+ sizeof(feature))) {
+ return -EFAULT;
+ }
+
+ switch (cmd) {
+ case ADC_INIT:
+ adc_initiate(adc_dev);
+ break;
+
+ case ADC_CONFIGURATION:
+
+ adc_set(adc_dev, &feature);
+ break;
+
+ case ADC_REG_CLIENT:
+ channel = feature.channel;
+ adc_register(adc_dev->pdev, channel);
+ break;
+
+ case ADC_CONVERT:
+ INIT_COMPLETION(adc_tsi);
+ adc_try(adc_dev);
+ wait_for_completion_interruptible(&adc_tsi);
+ if (data_array[feature.channel].flag) {
+ feature.result0 = data_array[feature.channel].res_value;
+ data_array[feature.channel].flag = 0;
+ }
+ if (copy_to_user((struct adc_feature *)argp, &feature,
+ sizeof(feature)))
+ return -EFAULT;
+
+ kfree(adc_dev->cur);
+ break;
+
+ default:
+ pr_debug("adc_ioctl: unsupported ioctl command 0x%x\n", cmd);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* client register */
+struct adc_client *adc_register(struct platform_device *pdev,
+ unsigned char channel)
+{
+ struct adc_client *client = kzalloc(sizeof(struct adc_client),
+ GFP_KERNEL);
+
+ WARN_ON(!pdev);
+
+ if (!pdev)
+ return ERR_PTR(-EINVAL);
+
+
+ if (!client) {
+ dev_err(&pdev->dev, "no memory for adc client\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ client->pdev = pdev;
+ client->channel = channel;
+ list_add(&client->list, &client_list);
+
+ return client;
+}
+
+
+/*result process */
+static int res_proc(void)
+{
+ int con, res;
+ struct adc_device *adc = adc_dev;
+ con = readl(adc->regs + ADC_CFG);
+
+ if ((con & (1 << 2)) == 0) {
+ if ((con & (1 << 3)) == 1)
+ res = (0xFFF & readl(adc->regs + ADC_R0));
+ else
+ res = (0xFF & readl(adc->regs + ADC_R0));
+ } else
+ res = (0x3FF & readl(adc->regs + ADC_R0));
+
+ return readl(adc->regs + ADC_R0);
+ return res;
+}
+
+static irqreturn_t adc_irq(int irq, void *pw)
+{
+ int coco;
+ struct adc_device *adc = pw;
+ struct adc_client *client = adc->cur;
+
+ if (!client) {
+ dev_warn(&adc->pdev->dev, "%s: no adc pending\n", __func__);
+ goto exit;
+ }
+
+ coco = readl(adc->regs + ADC_HS);
+ if (coco & 1) {
+ data_array[client->channel].res_value = res_proc();
+ data_array[client->channel].flag = 1;
+ complete(&adc_tsi);
+ }
+
+exit:
+ return IRQ_NONE;
+}
+
+static const struct file_operations adc_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = adc_ioctl,
+ .open = NULL,
+ .read = NULL,
+};
+
+/* probe */
+static int __devinit adc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct adc_device *adc;
+ struct resource *regs;
+ struct cdev *adc_cdev;
+ static struct class *adc_class;
+ int ret;
+ dev_t id;
+
+
+ adc = kzalloc(sizeof(struct adc_device), GFP_KERNEL);
+ if (adc == NULL) {
+ dev_err(dev, "failed to allocate adc_device\n");
+ return -ENOMEM;
+ }
+ adc->pdev = pdev;
+
+ /* Initialize spin_lock */
+ spin_lock_init(&adc->lock);
+
+ adc->irq = platform_get_irq(pdev, 0);
+ if (adc->irq <= 0) {
+ dev_err(dev, "failed to get adc irq\n");
+ ret = -EINVAL;
+ goto err_alloc;
+ }
+
+ ret = request_irq(adc->irq, adc_irq, 0, dev_name(dev), adc);
+ if (ret < 0) {
+ dev_err(dev, "failed to attach adc irq\n");
+ goto err_alloc;
+ }
+
+ adc->clk = clk_get(dev, NULL);
+ if (IS_ERR(adc->clk)) {
+ dev_err(dev, "failed to get adc clock\n");
+ ret = PTR_ERR(adc->clk);
+ goto err_irq;
+ }
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs) {
+ dev_err(dev, "failed to find registers\n");
+ ret = -ENXIO;
+ goto err_clk;
+ }
+
+ adc->regs = ioremap(regs->start, resource_size(regs));
+ if (!adc->regs) {
+ dev_err(dev, "failed to map registers\n");
+ ret = -ENXIO;
+ goto err_clk;
+ }
+
+ /* Obtain device numbers and register char device */
+ ret = alloc_chrdev_region(&id, 0, 1, "mvf-adc");
+ if (ret < 0)
+ return ret;
+
+ adc_cdev = cdev_alloc();
+ adc_cdev->ops = &adc_fops;
+ adc_cdev->owner = THIS_MODULE;
+ ret = cdev_add(adc_cdev, id, 1);
+ if (ret < 0)
+ return ret;
+
+ adc_class = class_create(THIS_MODULE, "mvf-adc.0");
+ if (IS_ERR(adc_class))
+ return -1;
+
+ device_create(adc_class, NULL, id, NULL, "mvf-adc.0");
+ /* clk enable */
+ clk_enable(adc->clk);
+ /* Associated structures */
+ platform_set_drvdata(pdev, adc);
+
+ adc_dev = adc;
+
+ dev_info(dev, "attached adc driver\n");
+
+ return 0;
+
+err_clk:
+ clk_put(adc->clk);
+
+err_irq:
+ free_irq(adc->irq, adc);
+
+err_alloc:
+ kfree(adc);
+
+ return ret;
+}
+
+static int __devexit adc_remove(struct platform_device *pdev)
+{
+ struct adc_device *adc = platform_get_drvdata(pdev);
+
+ iounmap(adc->regs);
+ free_irq(adc->irq, adc);
+ clk_disable(adc->clk);
+ clk_put(adc->clk);
+ kfree(adc);
+
+ return 0;
+}
+
+static struct platform_driver adc_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = adc_probe,
+ .remove = __devexit_p(adc_remove),
+};
+
+static int __init adc_init(void)
+{
+ int ret;
+ ret = platform_driver_register(&adc_driver);
+ if (ret)
+ printk(KERN_ERR "%s: failed to add adc driver\n", __func__);
+
+ return ret;
+}
+
+static void __exit adc_exit(void)
+{
+ platform_driver_unregister(&adc_driver);
+}
+module_init(adc_init);
+module_exit(adc_exit);
+
+MODULE_AUTHOR("Xiaojun Wang");
+MODULE_DESCRIPTION("Vybrid ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRV_VERSION);
diff --git a/include/linux/mvf_adc.h b/include/linux/mvf_adc.h
new file mode 100644
index 000000000000..26562531afe0
--- /dev/null
+++ b/include/linux/mvf_adc.h
@@ -0,0 +1,204 @@
+/* Copyright 2012 Freescale Semiconductor, Inc.
+ *
+ * Freescale Faraday Quad ADC driver
+ *
+ * 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.
+*/
+
+#ifndef MVF_ADC_H
+#define MVF_ADC_H
+
+#define ADC_CFG 0x14 /* Configuration Register */
+
+/* Input Clock Select 1-0, Divide ratio 6-5 */
+#define CLEAR_CLK_BIT 0x63
+#define BUSCLK_SEL 0x00
+#define BUSCLK2_SEL 0x01
+#define ALTCLK_SEL 0x02
+#define ADACK_SEL 0x03
+#define CLK_DIV2 0x20
+#define CLK_DIV4 0x40
+#define CLK_DIV8 0x60
+/* Conversion RES Mode Selection 3-2 */
+#define CLEAR_MODE_BIT 0xC
+#define BIT8 0x00
+#define BIT10 0x01
+#define BIT12 0x10
+/* Low-Power Configuration 7 */
+#define CLEAR_ADLPC_BIT 0x80
+#define ADLPC_EN 0x80
+/* Long/Short Sample Time Configuration 4,9-8 */
+#define CLEAR_LSTC_BIT 0x310
+#define ADLSMP_LONG 0x10
+#define ADSTS_SHORT 0x100
+#define ADSTS_NORMAL 0x200
+#define ADSTS_LONG 0x300
+/* High speed operation 10 */
+#define CLEAR_ADHSC_BIT 0x400
+#define ADHSC_EN 0x400
+/* Voltage Reference Selection 12-11 */
+#define CLEAR_REFSEL_BIT 0x1800
+#define REFSEL_VALT 0x100
+#define REFSEL_VBG 0x1000
+/* Conversion Trigger Select 13 */
+#define CLEAR_ADTRG_BIT 0x2000
+#define ADTRG_HARD 0x2000
+/* Hardware Average select 15-14 */
+#define CLEAR_AVGS_BIT 0xC000
+#define AVGS_8 0x4000
+#define AVGS_16 0x8000
+#define AVGS_32 0xC000
+/* Data Overwrite Enable 16 */
+#define CLEAR_OVWREN_BIT 0x10000
+#define OVWREN 0x10000
+
+#define ADC_GC 0x18 /* General control register */
+
+/* Asynchronous clock output 0 */
+#define CLEAR_ADACKEN_BIT 0x1
+#define ADACKEN 0x1
+/* DMA Enable 1 */
+#define CLEAR_DMAEN_BIT 0x2
+#define DMAEN 0x2
+/* Compare Function Rang Enable 2*/
+#define CLEAR_ACREN_BIT 0x4
+#define ACREN 0x4
+/* Compare Function Greater Than Enable 3 */
+#define CLEAR_ACFGT_BIT 0x8
+#define ACFGT 0x8
+/* Enable compare function 4 */
+#define CLEAR_ACFE_BIT 0x10
+#define ACFE 0x10
+/* Hardware Average Enable 5 */
+#define CLEAR_AVGE_BIT 0x20
+#define AVGEN 0x20
+/* Continue Conversion Enable 6 */
+#define CLEAR_ADCO_BIT 0x40
+#define ADCON 0x40
+
+
+#define ADC_HC0 0x00/* Control register for hardware triggers 0 */
+#define ADC_HC1 0x04/* Control register for hardware triggers 1 */
+
+#define IRQ_EN 0x80
+#define ADCHC0(x) ((x)&0xF)
+#define AIEN1 0x00000080
+#define COCOA 0x00000000
+
+#define ADC_HS 0x08/* Status register for HW triggers */
+
+#define ADC_R0 0x0c/* Data result register for HW triggers */
+#define ADC_R1 0x10/* Data result register for HW triggers */
+
+#define ADC_GS 0x1c/* General status register (ADC0_GS) */
+#define ADC_CV 0x20/* Compare value register (ADC0_CV) */
+/* Offset correction value register (ADC0_OFS) */
+#define ADC_OFS 0x24
+#define ADC_CAL 0x28/* Calibration value register (ADC0_CAL) */
+#define ADC_PCTL 0x30/* Pin control register (ADC0_PCTL) */
+
+/* init */
+#define CLECFG16 0x1ffff
+#define SETCFG 0x10098
+
+#define CLEHC0 0xff
+#define SETHC0 0x85
+
+#define CLEPCTL23 0xffffff
+#define SETPCTL5 0x20
+
+#define ADC_INIT _IO('p', 0xb1)
+#define ADC_CONVERT _IO('p', 0xb2)
+#define ADC_CONFIGURATION _IOWR('p', 0xb3, struct adc_feature)
+#define ADC_REG_CLIENT _IOWR('p', 0xb4, struct adc_feature)
+
+enum clk_sel {
+ ADCIOC_BUSCLK_SET,
+ ADCIOC_ALTCLK_SET,
+ ADCIOC_ADACK_SET,
+};
+
+
+#define ADCIOC_LPOFF_SET 0
+#define ADCIOC_LPON_SET 1
+
+#define ADCIOC_HSON_SET 1
+#define ADCIOC_HSOFF_SET 0
+
+enum vol_ref {
+ ADCIOC_VR_VREF_SET,
+ ADCIOC_VR_VALT_SET,
+ ADCIOC_VR_VBG_SET,
+};
+
+#define ADCIOC_SOFTTS_SET 0
+#define ADCIOC_HARDTS_SET 1
+
+#define ADCIOC_HA_DIS 0
+#define ADCIOC_HA_SET 1
+
+#define ADCIOC_DOEON_SET 1
+#define ADCIOC_DOEOFF_SET 0
+
+#define ADCIOC_ADACKENON_SET 1
+#define ADCIOC_ADACKENOFF_SET 0
+
+#define ADCIDC_DMAON_SET 1
+#define ADCIDC_DMAOFF_SET 0
+
+#define ADCIOC_CCEOFF_SET 0
+#define ADCIOC_CCEON_SET 1
+
+#define ADCIOC_ACFEON_SET 1
+#define ADCIOC_ACFEOFF_SET 0
+
+#define ADCIOC_ACFGTON_SET 1
+#define ADCIOC_ACFGTOFF_SET 0
+
+#define ADCIOC_ACRENON_SET 1
+#define ADCIOC_ACRENOFF_SET 0
+
+enum adc_channel {
+ ADC0,
+ ADC1,
+ ADC2,
+ ADC3,
+ ADC4,
+ ADC5,
+ ADC6,
+ ADC7,
+ ADC8,
+ ADC9,
+ ADC10,
+ ADC11,
+ ADC12,
+ ADC13,
+ ADC14,
+ ADC15,
+};
+
+struct adc_feature {
+ enum adc_channel channel;
+ enum clk_sel clk_sel;/* clock select */
+ int clk_div_num;/* clock divide number*/
+ int res_mode;/* resolution sample */
+ int sam_time;/* sample time */
+ int lp_con;/* low power configuration */
+ int hs_oper;/* high speed operation */
+ enum vol_ref vol_ref;/* voltage reference */
+ int tri_sel;/* trigger select */
+ int ha_sel;/* hardware average select */
+ int ha_sam;/* hardware average sample */
+ int do_ena;/* data overwrite enable */
+ int ac_ena;/* Asynchronous clock output enable */
+ int dma_ena;/* DMA enable */
+ int cc_ena;/* continue converdion enable */
+ int compare_func_ena;/* enable compare function */
+ int range_ena;/* range enable */
+ int greater_ena;/* greater than enable */
+ unsigned int result0, result1;
+};
+
+#endif