ADC driver for mini6410

emplab
Hi,
I need a sample code for adc driver based on S3C6410.
Please help me.

davef
Search <domdom> on this site

emplab
Thank you for your answer. Your answer is very nice and complete. ;)
I found two samples for adc drive on this site.
I could compile on of them and install it on linux on this board.
But I have fixed value on ADC. Also I need an incorruptible adc driver.
This driver is pulling.
Please see the following driver and help me for change it to incorruptible
driver. 
http://www.friendlyarm.net/forum/attachment/19220

davef
What does "incorruptible" mean?  And what does "pulling" mean?

I am not familiar with the mini6410, but I am aware that some of the ADC
channels are used for the touchscreen in the mini2440.

Also, I know very little about debugging drivers, sorry.

Perhaps, if we understand what your current problem is then someone else
can help.

emplab
After start conversion in ADC, a time is need for end of conversion. Ok?
We can wait and check a bit in one of the adc registers that inform me the
end of conversion. (This means pulling)
Or we can set an interrupt for end of conversion, in this way, after start
conversion, we could process other jobs. Any time that conversion finished
an interrupt will be generated and the Adc Interrupt Service Routine will
be called. (This means interrupt).

Now I need an ADC driver that work in 500khz sampling rate. So I need to
work in High speed. In the following driver, the driver is worked in
pulling mode and the ADC data register is fixed all times.
I want to one body check this codes and help me for developing an adc
driver for 500ksps.

emplab
http://www.friendlyarm.net/forum/attachment/19220

davef
Not "pulling", but "polling".  Polling means to wait in a "busy loop" until
the job is finished.

You have the correct understanding of the two processes and the correct
names for them.

Now, about the 500ksps issue.  I recall another thread recently
http://www.friendlyarm.net/forum/topic/5418
where the issue about how fast one can actually get a response when the
Linux OS is operating, even with interrupts.

It looks like there has been some recent progress on high resolution timers
and the RT patch for the kernel.  

I see from here:
http://git-public.pengutronix.de/?p=OSELAS.BSP-Pengutronix-Mini2440.git;...
that 3.10 is available for the Pengutronix BSP for the mini2440.

Maybe, you can become a tester!

sushant
#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/clk.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <mach/regs-clock.h>
#include <plat/regs-timer.h>
   
#include <plat/regs-adc.h>
#include <mach/regs-gpio.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>



#undef DEBUG
//#define DEBUG
#ifdef DEBUG
#define DPRINTK(x...) {printk(KERN_DEBUG "EmbedSky_adc: " x);}
#else
#define DPRINTK(x...) (void)(0)
#endif

#define DEVICE_NAME  "adc"

static void __iomem *base_addr;

typedef struct
{
  wait_queue_head_t wait;
  int channel;
  int prescale;
}ADC_DEV;

DECLARE_MUTEX(ADC_LOCK);
static int ADC_enable = 0;

static ADC_DEV adcdev;
static volatile int ev_adc = 0;
static int adc_data;

static struct clk  *adc_clock;

#define ADCCON    (*(volatile unsigned long *)(base_addr + S3C2410_ADCCON))
 //ADC control
#define ADCTSC    (*(volatile unsigned long *)(base_addr + S3C2410_ADCTSC))
 //ADC touch screen control
#define ADCDLY    (*(volatile unsigned long *)(base_addr + S3C2410_ADCDLY))
 //ADC start or Interval Delay
#define ADCDAT0    (*(volatile unsigned long *)(base_addr +
S3C2410_ADCDAT0))  //ADC conversion data 0
#define ADCDAT1    (*(volatile unsigned long *)(base_addr +
S3C2410_ADCDAT1))  //ADC conversion data 1
#define ADCUPDN    (*(volatile unsigned long *)(base_addr + 0x14))     
//Stylus Up/Down interrupt status

#define PRESCALE_DIS    (0 << 14)
#define PRESCALE_EN    (1 << 14)
#define PRSCVL(x)    ((x) << 6)
#define ADC_INPUT(x)    ((x) << 3)
#define ADC_START    (1 << 0)
#define ADC_ENDCVT    (1 << 15)

#define START_ADC_AIN(ch, prescale) \
  do{   ADCCON = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT((ch)) ; \
    ADCCON |= ADC_START; \
  }while(0)


static irqreturn_t adcdone_int_handler(int irq, void *dev_id)
{
  if (ADC_enable)
  {
    adc_data = ADCDAT0 & 0x3ff;

    ev_adc = 1;
    wake_up_interruptible(&adcdev.wait);
  }

  return IRQ_HANDLED;
}

static ssize_t tq2440_adc_read(struct file *filp, char *buffer, size_t
count, loff_t *ppos)
{
  char str[20];
  int value;
  size_t len;
  if (down_trylock(&ADC_LOCK) == 0)
  {
    ADC_enable = 1;
    START_ADC_AIN(adcdev.channel, adcdev.prescale);
    wait_event_interruptible(adcdev.wait, ev_adc);

    ev_adc = 0;

    DPRINTK("AIN[%d] = 0x%04x, %d\n", adcdev.channel, adc_data, ((ADCCON &
0x80) ? 1:0));

    value = adc_data;
    sprintf(str,"%5d", adc_data);
    copy_to_user(buffer, (char *)&adc_data, sizeof(adc_data));

    ADC_enable = 0;
    up(&ADC_LOCK);
  }
  else
  {
    value = -1;
  }

  len = sprintf(str, "%d\n", value);
  if (count >= len)
  {
    int r = copy_to_user(buffer, str, len);
    return r ? r : len;
  }
  else
  {
    return -EINVAL;
  }
}

static int tq2440_adc_open(struct inode *inode, struct file *filp)
{
  init_waitqueue_head(&(adcdev.wait));

  adcdev.channel=2;  
  adcdev.prescale=0xff;

  DPRINTK( "ADC opened\n");
  return 0;
}

static int tq2440_adc_release(struct inode *inode, struct file *filp)
{
  DPRINTK( "ADC closed\n");
  return 0;
}


static struct file_operations dev_fops = {
  owner:  THIS_MODULE,
  open:  tq2440_adc_open,
  read:  tq2440_adc_read,  
  release:  tq2440_adc_release,
};

static struct miscdevice misc = {
  .minor = MISC_DYNAMIC_MINOR,
  .name = DEVICE_NAME,
  .fops = &dev_fops,
};

static int __init dev_init(void)
{
  int ret;

  base_addr=ioremap(S3C2410_PA_ADC,0x20);
  if (base_addr == NULL)
  {
    printk(KERN_ERR "failed to remap register block\n");
    return -ENOMEM;
  }

  adc_clock = clk_get(NULL, "adc");
  if (!adc_clock)
  {
    printk(KERN_ERR "failed to get adc clock source\n");
    return -ENOENT;
  }
  clk_enable(adc_clock);
  
  ADCTSC = 0;

  ret = request_irq(IRQ_ADC, adcdone_int_handler, IRQF_SHARED, DEVICE_NAME,
&adcdev);
  if (ret)
  {
    iounmap(base_addr);
    return ret;
  }

  ret = misc_register(&misc);

  printk (DEVICE_NAME" initialized\n");
  return ret;
}

static void __exit dev_exit(void)
{
  free_irq(IRQ_ADC, &adcdev);
  iounmap(base_addr);

  if (adc_clock)
  {
    clk_disable(adc_clock);
    clk_put(adc_clock);
    adc_clock = NULL;
  }

  misc_deregister(&misc);
}

EXPORT_SYMBOL(ADC_LOCK);
module_init(dev_init);
module_exit(dev_exit);

MODULE_LICENSE("GPL");

Not inserted the  this driver in Mini2440 ..
Error shows : Module is already inserted 
i have check the lsmod no module available on the same name

sushant
###
# Error Generated to inserted this module
##

[    ]# insmod my_adc.ko
base_address of __ioremap =c487a000<4>------------[ cut here ]------------
WARNING: at fs/sysfs/dir.c:487 sysfs_add_one+0x88/0xb0()
sysfs: cannot create duplicate filename '/devices/virtual/misc/adc'
Modules linked in: EmbedSky_adc(+) ov9650 rt73usb rt2x00usb rt2x00lib
mac80211 i
nput_polldev
Backtrace:
[<c0039fd4>] (dump_backtrace+0x0/0x10c) from [<c02b14b8>]
(dump_stack+0x18/0x1c)

 r7:c3af7ce8 r6:c00d7ef4 r5:c034834c r4:000001e7
[<c02b14a0>] (dump_stack+0x0/0x1c) from [<c0047f50>]
(warn_slowpath_common+0x4c/
0x80)
[<c0047f04>] (warn_slowpath_common+0x0/0x80) from [<c0047fd0>]
(warn_slowpath_fm
t+0x30/0x38)
 r7:c3a36000 r6:c3a5b420 r5:ffffffef r4:c3af7d0c
[<c0047fa0>] (warn_slowpath_fmt+0x0/0x38) from [<c00d7ef4>]
(sysfs_add_one+0x88/
0xb0)
 r3:c3a36000 r2:c034835c
 r4:c3a36000
[<c00d7e6c>] (sysfs_add_one+0x0/0xb0) from [<c00d8544>]
(create_dir+0x58/0xb0)
 r7:c388bdb0 r6:c3af1248 r5:c3a5b420 r4:c3af7d30
[<c00d84ec>] (create_dir+0x0/0xb0) from [<c00d85d4>]
(sysfs_create_dir+0x38/0x64
)
 r8:c387edc0 r7:00000000 r6:c387edc0 r5:c3af1248 r4:c3af1248
[<c00d859c>] (sysfs_create_dir+0x0/0x64) from [<c013c08c>]
(kobject_add_internal
+0xac/0x1d8)
 r4:c3af1248
[<c013bfe0>] (kobject_add_internal+0x0/0x1d8) from [<c013c2b0>]
(kobject_add_var
g+0x34/0x50)
 r9:00000000 r8:00000000 r7:00000000 r6:c387edc0 r5:00000000
r4:c3af1248
[<c013c27c>] (kobject_add_varg+0x0/0x50) from [<c013c34c>]
(kobject_add+0x3c/0x6
4)
 r7:00000000 r6:00a00096 r5:c3af1240 r4:c3af1240
[<c013c310>] (kobject_add+0x0/0x64) from [<c0176d7c>]
(device_add+0xc4/0x540)
 r3:00000013 r2:00000000
[<c0176cb8>] (device_add+0x0/0x540) from [<c0177214>]
(device_register+0x1c/0x20
)
[<c01771f8>] (device_register+0x0/0x20) from [<c01772ac>]
(device_create_vargs+0
x94/0xb0)
 r5:c384ed40 r4:c3af1240
[<c0177218>] (device_create_vargs+0x0/0xb0) from [<c01772f0>]
(device_create+0x2
8/0x30)
 r9:bf051000 r8:c03a7ce8 r7:bf04ebbc r6:bf04ebb0 r5:c03a7cf4
r4:c3af7ea4
[<c01772c8>] (device_create+0x0/0x30) from [<c0164c8c>]
(misc_register+0x9c/0x15
4)
 r4:bf04e3c0
[<c0164bf0>] (misc_register+0x0/0x154) from [<bf0510c0>]
(dev_init+0xc0/0xf0 [Em
bedSky_adc])
 r8:c0036008 r7:c3af6000 r6:00000000 r5:bf04ed4c r4:00000000
[<bf051000>] (dev_init+0x0/0xf0 [EmbedSky_adc]) from [<c0035284>]
(do_one_initca
ll+0x3c/0x1bc)
 r5:001dffa8 r4:0000162f
[<c0035248>] (do_one_initcall+0x0/0x1bc) from [<c006c290>]
(sys_init_module+0x94
/0x1a4)
[<c006c1fc>] (sys_init_module+0x0/0x1a4) from [<c0035e60>]
(ret_fast_syscall+0x0
/0x2c)
 r7:00000080 r6:be921f66 r5:00000001 r4:0000162f
---[ end trace af68097608269693 ]---
kobject_add_internal failed for adc with -EEXIST, don't try to register
things w
ith the same name in the same directory.
Backtrace:
[<c0039fd4>] (dump_backtrace+0x0/0x10c) from [<c02b14b8>]
(dump_stack+0x18/0x1c)

 r7:00000000 r6:ffffffef r5:c3af1248 r4:c3af1248
[<c02b14a0>] (dump_stack+0x0/0x1c) from [<c013c0f4>]
(kobject_add_internal+0x114
/0x1d8)
[<c013bfe0>] (kobject_add_internal+0x0/0x1d8) from [<c013c2b0>]
(kobject_add_var
g+0x34/0x50)
 r9:00000000 r8:00000000 r7:00000000 r6:c387edc0 r5:00000000
r4:c3af1248
[<c013c27c>] (kobject_add_varg+0x0/0x50) from [<c013c34c>]
(kobject_add+0x3c/0x6
4)
 r7:00000000 r6:00a00096 r5:c3af1240 r4:c3af1240
[<c013c310>] (kobject_add+0x0/0x64) from [<c0176d7c>]
(device_add+0xc4/0x540)
 r3:00000013 r2:00000000
[<c0176cb8>] (device_add+0x0/0x540) from [<c0177214>]
(device_register+0x1c/0x20
)
[<c01771f8>] (device_register+0x0/0x20) from [<c01772ac>]
(device_create_vargs+0
x94/0xb0)
 r5:c384ed40 r4:c3af1240
[<c0177218>] (device_create_vargs+0x0/0xb0) from [<c01772f0>]
(device_create+0x2
8/0x30)
 r9:bf051000 r8:c03a7ce8 r7:bf04ebbc r6:bf04ebb0 r5:c03a7cf4
r4:c3af7ea4
[<c01772c8>] (device_create+0x0/0x30) from [<c0164c8c>]
(misc_register+0x9c/0x15
4)
 r4:bf04e3c0
[<c0164bf0>] (misc_register+0x0/0x154) from [<bf0510c0>]
(dev_init+0xc0/0xf0 [Em
bedSky_adc])
 r8:c0036008 r7:c3af6000 r6:00000000 r5:bf04ed4c r4:00000000
[<bf051000>] (dev_init+0x0/0xf0 [EmbedSky_adc]) from [<c0035284>]
(do_one_initca
ll+0x3c/0x1bc)
 r5:001dffa8 r4:0000162f
[<c0035248>] (do_one_initcall+0x0/0x1bc) from [<c006c290>]
(sys_init_module+0x94
/0x1a4)
[<c006c1fc>] (sys_init_module+0x0/0x1a4) from [<c0035e60>]
(ret_fast_syscall+0x0
/0x2c)
 r7:00000080 r6:be921f66 r5:00000001 r4:0000162f
adc initialized
insmod: cannot insert 'ADC_INFO.ko': File exists

Sona
I will be using windows ce on mini 6410. Can I use ain1-7 in any way
without the adc driver?