Hello All,
I'm using the processor imx28 with ltib linux, and I am with difficult to use the ADC of the processor.
In the Freescale document i.MX28_Linux_BSP_RM.pdf in chapter 18, they talk about and ADC Touch Screen Drivers, however, the driver imx_adc_ts.c is not build in linux build. Im try to setect this driver but this is a MSX family dependence.
Can someone help me make the acquisition of ADC IMX28?
Thank you.
Solved! Go to Solution.
Hi Raul,
Here, the steps to add Touch screen driver to your rootfs.
$ cd (path of the ltib)
$ ./ltib -m config
Select Package list --->
Select TSLIB
Exit
Exit
Yes
$ ./ltib
$./ltib -p boot_stream.spec -f
Thank you,
RAM
At the end I wrote a minimal kernel module (char device) installed in /dev/my-mxs-lradc (install this via insmod - I used template module in LTIB).
In this module I read LRADC registers when I open this virtual file; when I read this file (for exaple with cat command) ai put all LRADC values in an "INI like" format:
LRAD_chx=val
LRAD_chy=val
This is my first Linux driver...it isn't a good programming example ad I don't know if it is all ok...but it runs for me.
Source (I am not able to uplad file in this post...):
#include <linux/module.h> /* Needed by all modules */
#include <linux/init.h> /* Needed for the macros */
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/seq_file.h>
#include <linux/cdev.h>
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_*_user */
#include <linux/platform_device.h>
#include <mach/hardware.h>
#include <mach/lradc.h>
#include <mach/device.h>
#include <mach/regs-lradc.h>
#include <linux/imx_adc.h>
#include <linux/err.h>
#include <linux/delay.h>
/* major number of device */
static int gMajor;
static struct class *my_lradc_class;
//extern int hw_lradc_use_channel(int);
extern u32 hw_lradc_vddio(void);
static char myBuff[100] = {0};
//static u32 myhw_lradc_vddio(void);
//static struct ACQ_nxc_adc adcConversion = {0};
#define GAIN_CORRECTION 1012 /* 1.012 */
#define REGS_LRADC_BASE IO_ADDRESS(LRADC_PHYS_ADDR)
/*
mknod /dev/nome_che_vuoi c major minor (nota: minor = 0)
*/
struct my_mxs_lradc_dev {
struct cdev cdev; /* Char device structure */
};
struct my_mxs_lradc_dev *lradc_device = NULL;
static int my_mxs_lradc_major = 0;
static int my_mxs_lradc_minor = 0;
static uint16_t MeasureInternalDieTemperature(void);
static void hw_load_adc(int *vCANBus,int *vACQBus);
static void say_hello(void)
{
printk("\nmy-imx28-lradc installed\n");
}
static void say_goodbye(void)
{
printk("\nmy-imx28-lradc removed\n");
}
static void hw_load_adc(int *vCANBus,int *vACQBus)
{
unsigned int ctrl4_backup,ctrl2_backup;
/* backup dei registri */
ctrl4_backup = __raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL4);
ctrl2_backup = __raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL2);
/* imposto i virtual channel come di default */
__raw_writel(BM_LRADC_CTRL4_LRADC6SELECT | BM_LRADC_CTRL4_LRADC1SELECT,
REGS_LRADC_BASE + HW_LRADC_CTRL4_CLR);
__raw_writel(BF_LRADC_CTRL4_LRADC6SELECT(LRADC_CH6),
REGS_LRADC_BASE + HW_LRADC_CTRL4_SET);
__raw_writel(BF_LRADC_CTRL4_LRADC1SELECT(LRADC_CH1),
REGS_LRADC_BASE + HW_LRADC_CTRL4_SET);
/* Clear the Soft Reset and Clock Gate for normal operation
* Ovvero: avvio il convertitore LRADC*/
__raw_writel(BM_LRADC_CTRL0_SFTRST | BM_LRADC_CTRL0_CLKGATE,
REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
/*
* Clear the divide by two for channel 6 since
* it has a HW divide-by-two built in.
* Ovvero: divido per due tutti i canali (pazienza la risoluzione) ranne il BATT perchè divide già per 4
*/
__raw_writel( (BF_LRADC_CTRL2_DIVIDE_BY_TWO(1 << ACQ_NXC_ADC_CANBUS)) |
(BF_LRADC_CTRL2_DIVIDE_BY_TWO(1 << ACQ_NXC_ADC_ACQBUS)),
REGS_LRADC_BASE + HW_LRADC_CTRL2_SET);
/* Clear the accumulator & NUM_SAMPLES per tutti i canali che voglio acquisire */
__raw_writel(0xFFFFFFFF,REGS_LRADC_BASE + HW_LRADC_CHn_CLR(ACQ_NXC_ADC_CANBUS));
__raw_writel(0xFFFFFFFF,REGS_LRADC_BASE + HW_LRADC_CHn_CLR(ACQ_NXC_ADC_ACQBUS));
/* Clear the interrupt flag */
__raw_writel((BM_LRADC_CTRL1_LRADC0_IRQ<<ACQ_NXC_ADC_CANBUS) |
(BM_LRADC_CTRL1_LRADC0_IRQ<<ACQ_NXC_ADC_ACQBUS),
REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
/*
* Get VddIO; this is the max scale value for the button resistor
* ladder.
*/
__raw_writel(BF_LRADC_CTRL0_SCHEDULE(1 << ACQ_NXC_ADC_CANBUS) |
BF_LRADC_CTRL0_SCHEDULE(1 << ACQ_NXC_ADC_ACQBUS),
REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);
/* wait for completion */
while ((__raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL1)
& ((BM_LRADC_CTRL1_LRADC0_IRQ<<ACQ_NXC_ADC_CANBUS) |
(BM_LRADC_CTRL1_LRADC0_IRQ<<ACQ_NXC_ADC_ACQBUS))
) != (
(BM_LRADC_CTRL1_LRADC0_IRQ<<ACQ_NXC_ADC_CANBUS) |
(BM_LRADC_CTRL1_LRADC0_IRQ<<ACQ_NXC_ADC_ACQBUS)
)
)
cpu_relax();
/* Clear the interrupt flag */
__raw_writel((BM_LRADC_CTRL1_LRADC0_IRQ<<ACQ_NXC_ADC_CANBUS) |
(BM_LRADC_CTRL1_LRADC0_IRQ<<ACQ_NXC_ADC_ACQBUS),
REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
/* read ch value. */
*vCANBus = __raw_readl(REGS_LRADC_BASE + HW_LRADC_CHn(ACQ_NXC_ADC_CANBUS)) & BM_LRADC_CHn_VALUE;
*vACQBus = __raw_readl(REGS_LRADC_BASE + HW_LRADC_CHn(ACQ_NXC_ADC_ACQBUS)) & BM_LRADC_CHn_VALUE;
__raw_writel(ctrl4_backup,REGS_LRADC_BASE + HW_LRADC_CTRL4);
__raw_writel(ctrl2_backup,REGS_LRADC_BASE + HW_LRADC_CTRL2);
}
ssize_t my_mxs_lradc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
int maxLen = strlen(myBuff);
if(*f_pos + count > maxLen)
count = maxLen - *f_pos;
if (copy_to_user(buf, &myBuff[*f_pos], count)) {
goto errore;
}
*f_pos += count;
return count;
errore:
return -EFAULT;
}
/*
* Use the the lradc channel
* get the die temperature from on-chip sensor.
*/
uint16_t MeasureInternalDieTemperature(void)
{
uint32_t ch8Value, ch9Value, lradc_irq_mask, channel;
uint16_t v;
channel = 0; /* LRADC 0 */
lradc_irq_mask = 1 << channel;
/* power up internal tep sensor block */
__raw_writel(BM_LRADC_CTRL2_TEMPSENSE_PWD,
REGS_LRADC_BASE + HW_LRADC_CTRL2_CLR);
/* mux to the lradc 8th temp channel */
__raw_writel((0xF << (4 * channel)),
REGS_LRADC_BASE + HW_LRADC_CTRL4_CLR);
__raw_writel((8 << (4 * channel)),
REGS_LRADC_BASE + HW_LRADC_CTRL4_SET);
/* Clear the interrupt flag */
__raw_writel(lradc_irq_mask,
REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
__raw_writel(BF_LRADC_CTRL0_SCHEDULE(1 << channel),
REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);
/* Wait for conversion complete*/
while (!(__raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL1)
& lradc_irq_mask))
cpu_relax();
/* Clear the interrupt flag again */
__raw_writel(lradc_irq_mask,
REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
/* read temperature value and clr lradc */
ch8Value = __raw_readl(REGS_LRADC_BASE +
HW_LRADC_CHn(channel)) & BM_LRADC_CHn_VALUE;
__raw_writel(BM_LRADC_CHn_VALUE,
REGS_LRADC_BASE + HW_LRADC_CHn_CLR(channel));
/* mux to the lradc 9th temp channel */
__raw_writel((0xF << (4 * channel)),
REGS_LRADC_BASE + HW_LRADC_CTRL4_CLR);
__raw_writel((9 << (4 * channel)),
REGS_LRADC_BASE + HW_LRADC_CTRL4_SET);
/* Clear the interrupt flag */
__raw_writel(lradc_irq_mask,
REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
__raw_writel(BF_LRADC_CTRL0_SCHEDULE(1 << channel),
REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);
/* Wait for conversion complete */
while (!(__raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL1)
& lradc_irq_mask))
cpu_relax();
/* Clear the interrupt flag */
__raw_writel(lradc_irq_mask,
REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
/* read temperature value */
ch9Value = __raw_readl(
REGS_LRADC_BASE + HW_LRADC_CHn(channel))
& BM_LRADC_CHn_VALUE;
__raw_writel(BM_LRADC_CHn_VALUE,
REGS_LRADC_BASE + HW_LRADC_CHn_CLR(channel));
/* power down temp sensor block */
__raw_writel(BM_LRADC_CTRL2_TEMPSENSE_PWD,
REGS_LRADC_BASE + HW_LRADC_CTRL2_SET);
v = (uint16_t)((ch9Value-ch8Value)*GAIN_CORRECTION/4000);
//printk(KERN_NOTICE "T: %d\n",v);
return v;
}
/*
* Open and close
*/
int my_mxs_lradc_open(struct inode *inode, struct file *filp)
{
struct my_mxs_lradc_dev *dev; /* device information */
int internalTemp,vddio;
int vCANBus,vACQBus;
dev = container_of(inode->i_cdev, struct my_mxs_lradc_dev, cdev);
filp->private_data = dev; /* for other methods */
internalTemp = MeasureInternalDieTemperature();
vddio = hw_lradc_vddio();
hw_load_adc(&vCANBus,&vACQBus);
sprintf(myBuff, "[NXC ADC]\nCANBus=%d\nACQBus=%d\nVDDIO=%d\nDIE=%d\n", vCANBus, vACQBus,vddio,internalTemp);
return 0; /* success */
}
int my_mxs_lradc_release(struct inode *inode, struct file *filp)
{
return 0;
}
struct file_operations my_mxs_lradc_fops = {
.owner = THIS_MODULE,
.llseek = NULL,
.read = my_mxs_lradc_read,
.write = NULL,
.ioctl = NULL,
.open = my_mxs_lradc_open,
.release = my_mxs_lradc_release,
};
/*
* Set up the char_dev structure for this device.
*/
static int my_mxs_lradc_setup_cdev(struct my_mxs_lradc_dev *dev)
{
int err, devno = MKDEV(my_mxs_lradc_major, my_mxs_lradc_minor);
printk(KERN_NOTICE "Dev: %d ", devno);
cdev_init(&dev->cdev, &my_mxs_lradc_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &my_mxs_lradc_fops;
err = cdev_add(&dev->cdev, devno, 1);
/* Fail gracefully if need be */
if (err) {
printk(KERN_NOTICE "Error %d in setup cdev", err);
return err;
}
return 0;
}
void cleanup_module(void)
{
unregister_chrdev(gMajor, "my-mxs-lradc");
device_destroy(my_lradc_class, MKDEV(gMajor, 0));
class_destroy(my_lradc_class);
say_goodbye();
}
static int __devinit my_mxs_lradc_probe(struct platform_device *pdev) {
unsigned int x_plus;
printk(KERN_WARNING "Devinit");
//x_plus = __raw_readl(LRADC_PHYS_ADDR + HW_LRADC_CHn(0)) & BM_LRADC_CHn_VALUE;
printk(KERN_WARNING "x_plus = %d",x_plus);
return 0;
}
static int __devexit mxs_pwm_led_remove(struct platform_device *pdev) {
printk(KERN_WARNING "Devexit");
return 0;
}
static struct platform_driver my_mxs_lradc_driver = {
.probe = my_mxs_lradc_probe,
.remove = __devexit_p(mxs_pwm_led_remove),
.suspend = NULL,
.resume = NULL,
.driver = {
.name = "mxs-pippone",
},
};
int init_module(void)
{
struct device *temp_class;
int M;
/* register a character device */
M = register_chrdev(0, "my-mxs-lradc", &my_mxs_lradc_fops);
if (M < 0) {
printk("\nmy-mxs-lradc: can't get major number\n");
return M;
}
gMajor = M;
my_lradc_class = class_create(THIS_MODULE, "my-mxs-lradc");
if (IS_ERR(my_lradc_class)) {
printk(KERN_ERR "Error creating my-mxs-lradc module class.\n");
unregister_chrdev(gMajor, "my-mxs-lradc");
return PTR_ERR(my_lradc_class);
}
temp_class = device_create(my_lradc_class, NULL,
MKDEV(gMajor, 0), NULL, "my-mxs-lradc");
if (IS_ERR(temp_class)) {
printk(KERN_ERR "Error creating my-mxs-lradc class device.\n");
class_destroy(my_lradc_class);
unregister_chrdev(gMajor, "my-mxs-lradc");
return -1;
}
return 0;
}
MODULE_LICENSE("GPL"); /* Get rid of the taint message */
/* actually this example is public domain */
MODULE_AUTHOR("Emanuele Coli"); /* Who wrote this */
MODULE_DESCRIPTION("Gestione LRADC per iMX28"); /* Helpful info */
Thanks that's most helpful.
Regards,
Dave
Hi Raul,
Here, the steps to add Touch screen driver to your rootfs.
$ cd (path of the ltib)
$ ./ltib -m config
Select Package list --->
Select TSLIB
Exit
Exit
Yes
$ ./ltib
$./ltib -p boot_stream.spec -f
Thank you,
RAM
I also use iMX28 and I have to read LRADC from my programs. I've correctly installed TSLIB (I use touch screen) but I don't know how can read a generic LRADC input in user space via ioctl.
In test unit (imx_adc_test.c) there is an example where is showed the use of ioct with /dev/imx_adc (open("/dev/imx_adc", O_RDONLY);) but unfortunatley I don't have /dev/imx_adc link. How can I enable this file system object?
Thanks
Regards,
Dave