ts-7000
[Top] [All Lists]

Re: [ts-7000] Re: ADC in interrupt mode?

To:
Subject: Re: [ts-7000] Re: ADC in interrupt mode?
From: "Breton M. Saunders" <>
Date: Wed, 15 Apr 2009 20:21:35 +0100
Hi Charlie,

  I checked the TIN bit - it is set.  This bit is also known as ADCEN in 
the EP9302 manual.  In the kernel 2.6.27 I have has this bit defined 
incorrectly as 0x00010000 (and this may well be incorrect in mainline).  
I am using 0x00020000 as described in the ep93xx manual, using 
SYSCON_DEVCFG_TIN.

  When I change the clock rate I don't seem to notice any difference in 
the interrupt rate.  I think something screwy is going on, as the 
interrupt rate looks like pretty much bang on the external oscillator 
rate divided by 1000.  I also noticed that if I turn off the ADC 
interrupt bit (RINTEN) that I observe no interrupts; hence I must be 
triggering off of the correct interrupt line.

  I am counting interrupts by looking at the count value displayed in 
/proc/interrupts.

  I've attached the source code here, which may give some further 
insights.  Also, of note, I worked out from the ep9312 manual the switch 
settings to measure ground and vcc to measure the full-scale range of 
the ADC; since the ep93xx are all the same die with different bondings.

  Hopefully there is something obviously wrong in this code!

    Cheers,

       -Brett






charliem_1216 wrote:
> Hi Brett --
>
> --- In  "Breton M. Saunders" <> 
> wrote:
>   
>> Hi,
>>  
>>  Has anyone successfully run the EP93xx ADC in interrupt mode?
>>     
> No, I haven't tried.
>
>   
>>   I am finding that the ADC (or rather touch screen interrupt) is stuck 
>> on - and fires at a rate of ~14.47 KHz, irrespective of whether the SDR 
>> (Synchronous data ready bit) is set or not; meaning that partially 
>> completed conversions are reported.
>>
>>   This is what my setup looks like:
>>
>> reg EP93XX_TSSETUP: 00000000
>> reg EP93XX_TSXYMAXMIN: 00000000
>> reg EP93XX_ADC_RESULT: 00002a68
>> reg EP93XX_TSDISCHARGE: 00000000
>> reg EP93XX_TSXSAMPLE: 00000000
>> reg EP93XX_TSYSAMPLE: 00000000
>> reg EP93XX_ADC_SWITCH: 00000608
>> reg EP93XX_TSDETECT: 00000000
>> reg EP93XX_ADC_SWLOCK: 00000000
>> reg EP93XX_ADC_ADCINTEN: 00000800
>>
>>   Anyone have any ideas?
>>     
>
> What is TIN (bit17 in DeviceCfg, 0x8093_0080)?  It's supposed to be 1 to turn 
> off the TS controller (Touchscreen INactive), so the ADC can be used 
> directly.  I know the 9302 isn't supposed to have the TS controller, but it 
> doesn't hurt to check.
>
> If you change the ADC clock divider (KeyTchClkDiv), does the interrupt rate 
> change from 14.47 kHz?
>
> regards, ........ Charlie
>
>   
>>     -Brett
>>
>>     
>
>
>
>
> ------------------------------------
>
> Yahoo! Groups Links
>
>
>



------------------------------------

Yahoo! Groups Links

<*> To visit your group on the web, go to:
    http://groups.yahoo.com/group/ts-7000/

<*> Your email settings:
    Individual Email | Traditional

<*> To change settings online go to:
    http://groups.yahoo.com/group/ts-7000/join
    (Yahoo! ID required)

<*> To change settings via email:
     
    

<*> To unsubscribe from this group, send an email to:
    

<*> Your use of Yahoo! Groups is subject to:
    http://docs.yahoo.com/info/terms/

/*
 * arch/arm/mach-ep93xx/ep93xx-adc.c
 *
 * Copyright (C) 2009 Mynah-Software Ltd
 *
 * 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.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/spinlock.h>

#include <linux/mm.h>

#include <linux/fs.h>
#include <linux/cdev.h>

#include <linux/interrupt.h>

#include <asm/uaccess.h>
#include <asm/io.h>

#include <mach/ep93xx-regs.h>
#include <mach/regs_syscon.h>
#include <linux/delay.h>



#include <linux/platform_device.h>

#include <linux/hwmon-sysfs.h>

#include <mach/hardware.h>

#define DR(x) { printk(KERN_INFO "reg %s: %08x\n", #x, __raw_readl(x)); } 


// signed or non-signed mode
#define NSIGND 0x200
// enable interrupts
#define RINTEN 0x800

#define SW0  0x00000001
#define SW1  0x00000002
#define SW2  0x00000004
#define SW3  0x00000008
#define SW4  0x00000010
#define SW5  0x00000020
#define SW6  0x00000040
#define SW7  0x00000080
#define SW8  0x00000100
#define SW9  0x00000200
#define SW10 0x00000400
#define SW11 0x00000800
#define SW12 0x00001000
#define SW13 0x00002000
#define SW14 0x00004000
#define SW15 0x00008000
#define SW16 0x00010000
#define SW17 0x00020000
#define SW18 0x00040000
#define SW19 0x00080000
#define SW20 0x00100000
#define SW21 0x00200000
#define SW22 0x00400000
#define SW23 0x00800000
#define SW24 0x01000000
#define SW25 0x02000000
#define SW26 0x04000000
#define SW27 0x08000000
#define SW28 0x10000000
#define SW29 0x20000000
#define SW30 0x40000000


/* marked as volatile as it will be modified by interrupt */
volatile unsigned int adc_value;

/* Used to serialize access to driver code*/
static struct semaphore ep93xx_adc_lock;

/* Reflects the current status of the multiplexor */
static int ep93xx_adc_channel;


/* Invalid count is a count of samples that will be marked invalid during a 
channel switch 
 * operation. The intention here is to block any read operations until at least 
two samples 
 * have been completed - this corresponds roughly to the 2ms required at the 
925Hz sample rate
 */
static unsigned int invalid_count=0;


static int wait_flag = 1;

wait_queue_head_t user_wait;

unsigned int firstPrint = 2;

/* Interrupt function - fires 925 times per second minus the interrrupt latency 
overhead, and updates
 * adc_value at that rate.
 *
 * Note: Strapping this onto FIQ will likely improve performance dramatically 
as jitter will be
 *       reduced.
 *
 */
static irqreturn_t adc_irq(int irq, void* dev_id) { 
  unsigned int value;

  // this will clear the interrupt and start another conversion
  value =__raw_readl(EP93XX_ADC_RESULT) & 0xffff;

  adc_value = value;

  // stop the clock!

  //__raw_writel(SYSCON_DEVCFG_ADCPD | 
__raw_readl(EP93XX_SYSCON_DEVICE_CONFIG), EP93XX_SYSCON_DEVICE_CONFIG);


  if (firstPrint && ((value & 0x80000000) == 0)) {
    firstPrint -= 1;
    printk("Dodgey interrupt\n");
    printk("    Inteen is: %08x\n", __raw_readl(EP93XX_ADC_ADCINTEN));
  }


  if (invalid_count) { 
    invalid_count -= 1;
  }

  // finally, if the user is waiting, wake him/her up
  if ((invalid_count == 0) && (wait_flag == 0)) { 
    wait_flag = 1;
    wake_up_interruptible(&user_wait);
  }

  return IRQ_HANDLED;
}



/* 
 * Check that you understand what these values mean before writing them.  
 * It may be possible to damage the ADC multiplexor by writing invalid
 * values into this table.
 *
 * Check the EP9312 or 9315 manual to understand how these are derived.
 *
 */
#define ADC_CHANNEL_0 0x0608
#define ADC_CHANNEL_1 0x0680
#define ADC_CHANNEL_2 0x0640
#define ADC_CHANNEL_3 0x0620
#define ADC_CHANNEL_4 0x0610
#define ADC_INPUT_GND (SW11 | SW0 | SW9 | SW10)
#define ADC_INPUT_VCC (SW23 | SW2 | SW9 | SW10)

/* Note - 6 closes switch 9 and switch 10 - connecting ref+ to vdd and ref- to 
ground */
/* to measure max range, we need to measure with ADC setup s.t. input is 
grounded, */
/* and also input is at vdd */



static spinlock_t slock = SPIN_LOCK_UNLOCKED;


int ep93xx_adc_set_channel(int channel) { 
  unsigned long flags;

  /* Acquire lock */
  if ( down_interruptible(&ep93xx_adc_lock) < 0 )
    return -EINTR;
  
  if ( channel != ep93xx_adc_channel ) {
    ep93xx_adc_channel = channel;
    spin_lock_irqsave(&slock, flags);
    __raw_writel(0xaa, EP93XX_ADC_SWLOCK);
    __raw_writel(channel, EP93XX_ADC_SWITCH);
    spin_unlock_irqrestore(&slock, flags);

    // invalidate the next two samples
    invalid_count = 2;
  }


  up(&ep93xx_adc_lock);

  return 0;
}



int ep93xx_adc_read(void) { 
  int value;

  /* Acquire lock */
  if ( down_interruptible(&ep93xx_adc_lock) < 0 )
    return -EINTR;

  // set the wait flag - indicating we want waking on sample completion
  wait_flag = 0;

  wait_event_interruptible(user_wait, wait_flag);

  // copy the volatile
  value = adc_value;
  up(&ep93xx_adc_lock);

  return value;
}


static void __devexit adc_release(struct device* pd) { 

}

static struct platform_device adc_device = {
  .dev = {
    .release = __devexit_p(adc_release),
  },
  .name = "ep93xx-adc",
  .id = -1,
};


static ssize_t adc_show(struct device* dev,
                        struct device_attribute* attr,
                        char* buf) {

  struct sensor_device_attribute* psa = to_sensor_dev_attr(attr);
  
  int channel = psa->index;
  
  ep93xx_adc_set_channel(channel);
  return sprintf(buf, "%d\n", ep93xx_adc_read());

}

#define ADC_ENTRY_RO(name, cmd_idx) \
  static SENSOR_DEVICE_ATTR(name, S_IRUGO, adc_show, NULL, cmd_idx);


ADC_ENTRY_RO(adc0, ADC_CHANNEL_0);
ADC_ENTRY_RO(adc1, ADC_CHANNEL_1);
ADC_ENTRY_RO(adc2, ADC_CHANNEL_2);
ADC_ENTRY_RO(adc3, ADC_CHANNEL_3);
ADC_ENTRY_RO(adc4, ADC_CHANNEL_4);
ADC_ENTRY_RO(adcgnd, ADC_INPUT_GND);
ADC_ENTRY_RO(adcvdd, ADC_INPUT_VCC);

static struct attribute* adc_attributes[] = {
  &sensor_dev_attr_adc0.dev_attr.attr,
  &sensor_dev_attr_adc1.dev_attr.attr,
  &sensor_dev_attr_adc2.dev_attr.attr,
  &sensor_dev_attr_adc3.dev_attr.attr,
  &sensor_dev_attr_adc4.dev_attr.attr,
  &sensor_dev_attr_adcgnd.dev_attr.attr,
  &sensor_dev_attr_adcvdd.dev_attr.attr,
  NULL
};

static struct attribute_group adc_defattr_group = {
  .attrs = adc_attributes,
};



static int __init ep93xx_adc_init(void) {
    unsigned long flags;
    int err;

    ep93xx_adc_channel = ADC_CHANNEL_0;

    printk(KERN_INFO "ep93xx-adc driver version 0.2\n");
    
    // enable the clocks to the ADC
    spin_lock_irqsave(&slock, flags);
    __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
    __raw_writel(~SYSCON_DEVCFG_ADCPD & 
__raw_readl(EP93XX_SYSCON_DEVICE_CONFIG), EP93XX_SYSCON_DEVICE_CONFIG);


    __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
    __raw_writel(0x80010000, EP93XX_SYSCON_ADC_CLKDIV); // ADC clock enable, 
using ext oscillator div 16 - p95 EP9301 User Guide

    __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
    __raw_writel(~SYSCON_DEVCFG_ADCEN & 
__raw_readl(EP93XX_SYSCON_DEVICE_CONFIG), EP93XX_SYSCON_DEVICE_CONFIG);

    // and set the TIN bit to disable the touch screen controller
    __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
    __raw_writel(SYSCON_DEVCFG_TIN | __raw_readl(EP93XX_SYSCON_DEVICE_CONFIG), 
EP93XX_SYSCON_DEVICE_CONFIG);

    __raw_writel(0xaa, EP93XX_ADC_SWLOCK);
    __raw_writel(ADC_CHANNEL_0, EP93XX_ADC_SWITCH);

    spin_unlock_irqrestore(&slock, flags);


    printk("Device cfg: %08x\n", __raw_readl(EP93XX_SYSCON_DEVICE_CONFIG));


    err = request_irq(IRQ_EP93XX_TOUCH, 
                      adc_irq, 
                      0, 
                      "ep93xx-adc",
                      &adc_device);
    if (err) { 
      printk(KERN_ERR "Failed to allocate ep93xx-adc interrupt\n");
      return -ENODEV;
    }


    init_waitqueue_head(&user_wait);

    /* Finally - set ADC to run in non signed mode, with interrupt support. */
    __raw_writel(0, EP93XX_ADC_ADCINTEN);  //  | RINTEN NSIGND 



    printk("Before starting, TSSETUP is : %08x\n", __raw_readl(EP93XX_TSSETUP));
    printk("    Inteen is: %08x\n", __raw_readl(EP93XX_ADC_ADCINTEN));



    sema_init(&ep93xx_adc_lock, 1);

    if ( platform_device_register(&adc_device) != 0) { 
      printk(KERN_INFO "ADC platform device failed to register\n");
    }

    
    err = sysfs_create_group(&adc_device.dev.kobj, 
                             &adc_defattr_group);

    if (err) { 
      printk(KERN_INFO "Failed to create ADC sysfs entries\n");
    }
    
    // kick start the ADC interrupt driven process
    //__raw_readl(EP93XX_ADC_RESULT);

    DR(EP93XX_TSSETUP);
    DR(EP93XX_TSXYMAXMIN);
    DR(EP93XX_ADC_RESULT);
    DR(EP93XX_TSDISCHARGE);
    DR(EP93XX_TSXSAMPLE);
    DR(EP93XX_TSYSAMPLE);
    DR(EP93XX_ADC_SWITCH);
    DR(EP93XX_TSDETECT);
    DR(EP93XX_ADC_SWLOCK);
    DR(EP93XX_ADC_ADCINTEN);


    return 0;
}

static void __exit ep93xx_adc_exit(void)
{
  unsigned long flags;
  // disable the clocks to the ADC
  spin_lock_irqsave(&slock, flags);
  __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
  __raw_writel(SYSCON_DEVCFG_ADCPD | __raw_readl(EP93XX_SYSCON_DEVICE_CONFIG), 
EP93XX_SYSCON_DEVICE_CONFIG);
  spin_unlock_irqrestore(&slock, flags);
  
  /* Disable interrupts */
  __raw_writel(0, EP93XX_ADC_ADCINTEN);




  printk(KERN_INFO "Removing ep93xx-adc driver\n");


  // free the irq
  free_irq(IRQ_EP93XX_TOUCH, &adc_device);


  // remove sysfs entries

  sysfs_remove_group(&adc_device.dev.kobj,
                     &adc_defattr_group);

  // unregister device
  platform_device_unregister(&adc_device);

}

module_init(ep93xx_adc_init);
module_exit(ep93xx_adc_exit);

MODULE_AUTHOR("Breton M. Saunders <>");
MODULE_DESCRIPTION("ep93xx-adc driver");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.2");
<Prev in Thread] Current Thread [Next in Thread>
Admin

Disclaimer: Neither Andrew Taylor nor the University of NSW School of Computer and Engineering take any responsibility for the contents of this archive. It is purely a compilation of material sent by many people to the birding-aus mailing list. It has not been checked for accuracy nor its content verified in any way. If you wish to get material removed from the archive or have other queries about the archive e-mail Andrew Taylor at this address: andrewt@cse.unsw.EDU.AU