LRADC with IMX28

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

LRADC with IMX28

Jump to solution
4,301 Views
munoz0raul
Contributor II

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.

Labels (1)
1 Solution
1,392 Views
Ramtry
Contributor III

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

View solution in original post

0 Kudos
5 Replies
1,392 Views
emanuelec
Contributor III

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   */




1,392 Views
davee
Contributor III

Thanks that's most helpful.

Regards,

Dave

0 Kudos
1,393 Views
Ramtry
Contributor III

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

0 Kudos
1,392 Views
emanuelec
Contributor III

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

0 Kudos
1,392 Views
davee
Contributor III
Emanuele,
Did you find a solution to your problem?


Regards,

Dave

0 Kudos