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");
|