From b358081d3db11e4f4582bea7658b93a7eef539d3 Mon Sep 17 00:00:00 2001 From: Wang Xiaojun Date: Mon, 10 Dec 2012 18:05:18 +0800 Subject: ENGR00181365-2: ADC: Add driver support for ADC Vybrid Add driver support for ADC Vybrid. Signed-off-by: Wang Xiaojun --- drivers/misc/Kconfig | 10 + drivers/misc/Makefile | 1 + drivers/misc/mvf_adc.c | 771 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mvf_adc.h | 204 +++++++++++++ 4 files changed, 986 insertions(+) create mode 100644 drivers/misc/mvf_adc.c create mode 100644 include/linux/mvf_adc.h 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 -- cgit v1.2.3