This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Linux/AM3358: ADC driver in SDK 6.0

Part Number: AM3358

Tool/software: Linux

Hi all,

I am using AM3358 and SDK6.0. I use the ADC as the common ADC and I will use the channels 0 2 3 ,and at one time use one and change the channel at any time .

Refered Steven-liu's Am3358_adc_driver.c, I changed something but the interrupt cannot run.

I add the device resources in the xx_board.c,just as below

 

static struct resource am335x_adc_resource[] = {

           {

              .start = 0x44e0d000,

              .end  = 0x44e0d000 + SZ_8K - 1,

              .flags = IORESOURCE_MEM,

           },

         {

               .start = AM33XX_IRQ_ADC_GEN,

              .end = AM33XX_IRQ_ADC_GEN,

                .flags = IORESOURCE_IRQ,

         },

};

the iomem is remap and the node is iopened successfulily .Please help me ,Thanks.

/*
 * TI Touch Screen driver
 *
*/


#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <linux/types.h>
#include <linux/moduleparam.h>




#define TSCADC_REG_IRQEOI		0x020
#define TSCADC_REG_RAWIRQSTATUS		0x024
#define TSCADC_REG_IRQSTATUS		0x028
#define TSCADC_REG_IRQENABLE		0x02C
#define TSCADC_REG_IRQCLR		0x030
#define TSCADC_REG_IRQWAKEUP		0x034
#define TSCADC_REG_CTRL			0x040
#define TSCADC_REG_ADCFSM		0x044
#define TSCADC_REG_CLKDIV		0x04C
#define TSCADC_REG_SE			0x054
#define TSCADC_REG_IDLECONFIG		0x058
#define TSCADC_REG_CHARGECONFIG		0x05C
#define TSCADC_REG_CHARGEDELAY		0x060
#define TSCADC_REG_STEPCONFIG(n)	(0x64 + ((n-1) * 8))
#define TSCADC_REG_STEPDELAY(n)		(0x68 + ((n-1) * 8))
#define TSCADC_REG_STEPCONFIG13		0x0C4
#define TSCADC_REG_STEPDELAY13		0x0C8
#define TSCADC_REG_STEPCONFIG14		0x0CC
#define TSCADC_REG_STEPDELAY14		0x0D0
#define TSCADC_REG_FIFO0CNT		0xE4
#define TSCADC_REG_FIFO0THR		0xE8
#define TSCADC_REG_FIFO1CNT		0xF0
#define TSCADC_REG_FIFO1THR		0xF4
#define TSCADC_REG_FIFO0		0x100
#define TSCADC_REG_FIFO1		0x200


/*	Register Bitfields	*/
#define TSCADC_IRQWKUP_ENB		BIT(0)
#define TSCADC_IRQWKUP_DISABLE		0x00
#define TSCADC_STPENB_STEPENB		0x7FFF
#define TSCADC_IRQENB_FIFO0THRES	BIT(2)

#define ADC_STPENB_STEPENB		0x1FE 
#define TSCADC_IRQENB_STEPEND	BIT(1) 
#define TSCADC_IRQENB_FIFO1THRES	BIT(5)
#define TSCADC_IRQENB_PENUP		BIT(9)
#define TSCADC_IRQENB_HW_PEN		BIT(0)
#define TSCADC_STEPCONFIG_MODE_HWSYNC	0x2
#define TSCADC_STEPCONFIG_2SAMPLES_AVG	(1 << 4)
#define TSCADC_STEPCONFIG_XPP		BIT(5)
#define TSCADC_STEPCONFIG_XNN		BIT(6)
#define TSCADC_STEPCONFIG_YPP		BIT(7)
#define TSCADC_STEPCONFIG_YNN		BIT(8)
#define TSCADC_STEPCONFIG_XNP		BIT(9)
#define TSCADC_STEPCONFIG_YPN		BIT(10)

#define TSCADC_STEPCONFIG_1_NEGATIVE_INP (0)		
#define TSCADC_STEPCONFIG_1_INP		(0) 
#define TSCADC_STEPCONFIG_2_INP		BIT(19) 
#define TSCADC_STEPCONFIG_3_INP		BIT(20) 
#define TSCADC_STEPCONFIG_4_INP		(BIT(19)|BIT(20)) 
#define TSCADC_STEPCONFIG_5_INP		BIT(21) 
#define TSCADC_STEPCONFIG_6_INP		(BIT(19)|BIT(21))  
#define TSCADC_STEPCONFIG_7_INP		(BIT(21)|BIT(20))  
#define TSCADC_STEPCONFIG_8_INP		(BIT(19)|BIT(20)|BIT(21))
#define TSCADC_STEPCONFIG_REFP		(BIT(12)|BIT(13))

#define TSCADC_STEPCONFIG_RFM	((1 << 23)|(1 << 24)) 
#define TSCADC_STEPCONFIG_SINGLE_ENDED_OPER_MODE (0 <<25)
#define TSCADC_STEPCONFIG_MODE   (0)
#define TSCADC_STEPCONFIG_RFP		(1 << 12)
#define TSCADC_STEPCONFIG_INM		(1 << 18)
#define TSCADC_STEPCONFIG_INP_4		(1 << 19)
#define TSCADC_STEPCONFIG_INP		(1 << 20)
#define TSCADC_STEPCONFIG_INP_5		(1 << 21)
#define TSCADC_STEPCONFIG_FIFO1		(1 << 26)
#define TSCADC_STEPCONFIG_IDLE_INP	(1 << 22)
#define TSCADC_STEPCONFIG_OPENDLY	0x0
#define TSCADC_STEPCONFIG_SAMPLEDLY	0x0


#define TSCADC_STEPCONFIG_Z1		(3 << 19)

#define TSCADC_CNTRLREG_TSCSSENB	BIT(0)
#define TSCADC_CNTRLREG_STEPID		BIT(1)
#define TSCADC_CNTRLREG_STEPCONFIGWRT	BIT(2)
#define TSCADC_CNTRLREG_TSCENB		BIT(7)
#define TSCADC_CNTRLREG_4WIRE		(0x1 << 5)
#define TSCADC_CNTRLREG_5WIRE		(0x1 << 6)
#define TSCADC_CNTRLREG_8WIRE		(0x3 << 5)
#define TSCADC_ADCFSM_STEPID		0x10
#define TSCADC_ADCFSM_FSM		BIT(5)

#define ADC_CLK				3000000

#define MAX_12BIT                       ((1 << 12) - 1)

static volatile int ev_adc = 0;

static DECLARE_WAIT_QUEUE_HEAD(adc_waitq);
static int adc_data=0;


struct adc_st {
	struct cdev	adc;   
	int			irq;
	void __iomem		*adc_base;    
	
};

static int adc_major = 0;

struct adc_st			*adc_dev = NULL;

static int channel = 4;

module_param(channel, int, S_IRUGO);


static unsigned int adc_readl(struct adc_st *adc, unsigned int reg)
{
	return readl(adc->adc_base + reg);
}

static void adc_writel(struct adc_st *adc , unsigned int reg,
					unsigned int val)
{
	writel(val, adc->adc_base + reg);
}

static void start_adc(struct adc_st *dev)
{
   printk("ADC start!\n");
   int ctrl=0;
   ctrl |= TSCADC_CNTRLREG_STEPCONFIGWRT |			
			TSCADC_CNTRLREG_TSCSSENB;
   adc_writel(dev, TSCADC_REG_CTRL, ctrl); 
   
}

static void stop_adc(struct adc_st *dev)
{
   printk("ADC stop!\n");
   int ctrl=0;
   ctrl |= TSCADC_CNTRLREG_STEPCONFIGWRT;
   adc_writel(dev, TSCADC_REG_CTRL, ctrl); 
   
}



static void adc_step_config(struct adc_st *adc_dev)
{
    	unsigned int	stepconfigx = 0;
	unsigned int	delay;
	int i;

	/* Configure the Step registers */
    stepconfigx =  TSCADC_STEPCONFIG_REFP |  TSCADC_STEPCONFIG_RFM    
                    | TSCADC_STEPCONFIG_SINGLE_ENDED_OPER_MODE |TSCADC_STEPCONFIG_MODE;
	  
     switch (channel)
	{
	case 0:
		stepconfigx |= TSCADC_STEPCONFIG_1_INP;
		printk("ADC channel choose: 0 \n");
		break;
	case 1:
		stepconfigx |= TSCADC_STEPCONFIG_2_INP;
		printk("ADC channel choose: 1 \n");
		break;
	case 2:
		stepconfigx |= TSCADC_STEPCONFIG_3_INP;
		printk("ADC channel choose: 2 \n");
		break;
	case 3:
		stepconfigx |= TSCADC_STEPCONFIG_4_INP;
		printk("ADC channel choose: 3 \n");
		break;
	case 4:
		stepconfigx |= TSCADC_STEPCONFIG_5_INP;
		printk("ADC channel choose: 4 \n");
		break;
	case 5:
		stepconfigx |= TSCADC_STEPCONFIG_6_INP;
		printk("ADC channel choose: 5 \n");
		break;
	case 6:
		stepconfigx |= TSCADC_STEPCONFIG_7_INP;
		printk("ADC channel choose: 6 \n");
		break;
	case 7:
		stepconfigx |= TSCADC_STEPCONFIG_8_INP;
		printk("ADC channel choose: 7 \n");
		break;
	default:
		stepconfigx |= TSCADC_STEPCONFIG_5_INP;//default for channel 4
		printk("Input wrong!  Channel number should be [0..7] \n ADC channel choose: 4 \n");
		channel = 4;
		break;

	}

	for (i = 1; i < 9; i++) {
           adc_writel(adc_dev, TSCADC_REG_STEPCONFIG(i), stepconfigx);
           }
	delay = TSCADC_STEPCONFIG_SAMPLEDLY | TSCADC_STEPCONFIG_OPENDLY;
	for (i = 1; i < 9; i++) {    
		adc_writel(adc_dev, TSCADC_REG_STEPDELAY(i), delay);
		}
	

	adc_writel(adc_dev, TSCADC_REG_SE, ADC_STPENB_STEPENB); 
	
}

static void adc_idle_config(struct adc_st *adc_config)
{
  /* Idle mode adc screen config */
	unsigned int	 idleconfig=0;	

	adc_writel(adc_config, TSCADC_REG_IDLECONFIG, idleconfig);
}

static int adc_open(struct inode *inode, struct file *file)
{
    int ret;
    printk("adc_open enter...\n");

    return 0;
}


static ssize_t adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
    printk("=========== Now ADC channel:%d working===========\n", channel);
    if(!ev_adc)
    {
        if(filp->f_flags & O_NONBLOCK)
        {
            return -EAGAIN;
        }
        else
        {
            start_adc(adc_dev);

            wait_event_interruptible(adc_waitq, ev_adc);
        }
    }

    ev_adc = 0;

    copy_to_user(buffer, (char *)&adc_data, sizeof(adc_data));

    adc_data=0;
    return sizeof(adc_data);
}

static int adc_release(struct inode *inode, struct file *filp)
{
    
    return 0;
}


static struct file_operations adc_fops = 
{
    .owner    = THIS_MODULE,
    .open     = adc_open,
    .read     = adc_read,    
    .release  = adc_release,
};


static int adc_setup_cdev(struct adc_st *dev, int index)
{
    int err, devno = MKDEV(adc_major, index);
    cdev_init(&dev->adc, &adc_fops);
    dev->adc.owner = THIS_MODULE;
    err = cdev_add(&dev->adc,  devno, 1);
    if(err)
    {
       printk("no memory resource defined.\n"); 
       return err;
    }
}

static irqreturn_t adc_interrupt(int irq, void *dev)
{
	

    unsigned int		status, irqclr = 0;
	int			i;
	int		fifo0count = 0;
	unsigned int		readx1 = 0;
    printk("adc_interrupt enter, ev_adc %d...\n", ev_adc);    
    if(!ev_adc)      
    {        
    	status = adc_readl(adc_dev, TSCADC_REG_IRQSTATUS);
         printk("adc_interrupt status %d...\n", status);
    	if (status & TSCADC_IRQENB_FIFO0THRES) {
    		fifo0count =adc_readl(adc_dev, TSCADC_REG_FIFO0CNT);
    		
            printk("ADC_Interrupt FIFO0_Count %d...\n", fifo0count);
    		for (i = 0; i < fifo0count; i++) {
    			readx1 = adc_readl(adc_dev, TSCADC_REG_FIFO0);
                printk("FIFO0_count %d : %d\n", i, readx1);
                adc_data+=readx1;
    			
    		}
            if(fifo0count)
                adc_data = adc_data/fifo0count;
            printk("ADC_DATA: %d \n", adc_data);
    		
    		irqclr |= TSCADC_IRQENB_FIFO0THRES;
    	}
        stop_adc(adc_dev);
        
        //clear TSCADC_IRQENB_FIFO0THRES irq
    	adc_writel(adc_dev, TSCADC_REG_IRQSTATUS, irqclr);


    	/* check pending interrupts */
    	adc_writel(adc_dev, TSCADC_REG_IRQEOI, 0x0);

    	adc_writel(adc_dev, TSCADC_REG_SE, ADC_STPENB_STEPENB);
    	

        ev_adc=1;
        
        //stop_adc();
        
        wake_up_interruptible(&adc_waitq);

        
    }
	return IRQ_HANDLED;
    
}

/*
* The functions for inserting/removing driver as a module.
*/

static	int __devinit adc_probe(struct platform_device *pdev)
{
	int				err;
	int				clk_value;
	int				clock_rate, irqenable, ctrl;
	struct resource			*res;
	struct clk			*clk;
    	dev_t devno;
    	int ret;
      
     
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res) {
		dev_err(&pdev->dev, "no memory resource defined.\n");
		return -EINVAL;
	}

	/* Allocate memory for device */
	adc_dev = kzalloc(sizeof(struct adc_st), GFP_KERNEL);
	if (!adc_dev) {
		dev_err(&pdev->dev, "failed to allocate memory.\n");
		return -ENOMEM;
	}

     ret = alloc_chrdev_region(&devno, // pointer to where the device number to be stored
        0, // first minor number requested
        1, // number of devices
        "adc"); // device name

    if(ret < 0)
    {
        err  = ret;
        goto err_free_mem;
    }   
    adc_major = MAJOR(devno);
    printk("adc_probe adc_major %d\n", adc_major);
    ret = adc_setup_cdev(adc_dev, 0);
    if(ret)
    {
        err = ret;
        goto err_alloc_chrdev;
    }    

	adc_dev->irq = platform_get_irq(pdev, 0);
	if (adc_dev->irq < 0) {
		dev_err(&pdev->dev, "no irq ID is specified.\n");
		err =  -ENODEV;
        goto err_add_cdev;
	}

	res =  request_mem_region(res->start, resource_size(res), pdev->name);
	if (!res) {
		dev_err(&pdev->dev, "failed to reserve registers.\n");
		err = -EBUSY;
		goto err_add_cdev;
	}

	adc_dev->adc_base = ioremap(res->start, resource_size(res));
	if (!adc_dev->adc_base) {
		dev_err(&pdev->dev, "failed to map registers.\n");
		err = -ENOMEM;
		goto err_release_mem;
	}

	err = request_irq(adc_dev->irq, adc_interrupt, IRQF_DISABLED,
				pdev->dev.driver->name, adc_dev);
	if (err) {
		dev_err(&pdev->dev, "failed to allocate irq.\n");
		goto err_unmap_regs;
	}

	pm_runtime_enable(&pdev->dev);
	pm_runtime_get_sync(&pdev->dev);

	clk = clk_get(&pdev->dev, "adc_tsc_fck");
	if (IS_ERR(clk)) {
		dev_err(&pdev->dev, "failed to get TSC fck\n");
		err = PTR_ERR(clk);
		goto err_free_irq;
	}
	clock_rate = clk_get_rate(clk);
	clk_value = clock_rate / ADC_CLK;
	if (clk_value < 7) {
		dev_err(&pdev->dev, "clock input less than min clock requirement\n");
		err = -EINVAL;
		goto err_fail;
	}
	/* TSCADC_CLKDIV needs to be configured to the value minus 1 */
	clk_value = clk_value - 1;
	adc_writel(adc_dev, TSCADC_REG_CLKDIV, clk_value);


	/* Set the control register bits */
	ctrl = TSCADC_CNTRLREG_STEPCONFIGWRT |  TSCADC_CNTRLREG_TSCENB ;
	adc_writel(adc_dev, TSCADC_REG_CTRL, ctrl);    

	/* Set register bits for Idel Config Mode */
	adc_idle_config(adc_dev);

    	adc_step_config(adc_dev);

    
	/* IRQ Enable */
    	irqenable = TSCADC_IRQENB_FIFO0THRES;   
    	adc_writel(adc_dev,TSCADC_REG_IRQENABLE, irqenable);
	adc_writel(adc_dev, TSCADC_REG_FIFO0THR, 7);//FIFO THRESHOLD SET 0-7


	device_init_wakeup(&pdev->dev, true);
    printk("adc initialized!\n");
	return 0;

err_fail:
	pm_runtime_disable(&pdev->dev);
err_free_irq:
	free_irq(adc_dev->irq, adc_dev);
err_unmap_regs:
	iounmap(adc_dev->adc_base);
err_release_mem:
	release_mem_region(res->start, resource_size(res));	
err_add_cdev:
    cdev_del(&adc_dev->adc); 
err_alloc_chrdev:
    unregister_chrdev_region(MKDEV(adc_major, 0), 1);
err_free_mem:
	kfree(adc_dev);
	return err;
}

static int __devexit adc_remove(struct platform_device *pdev)
{
	
	struct resource		*res;

    cdev_del(&adc_dev->adc); 
    unregister_chrdev_region(MKDEV(adc_major, 0), 1);
    
    free_irq(adc_dev->irq, adc_dev);
	

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	iounmap(adc_dev->adc_base);
	release_mem_region(res->start, resource_size(res));

	pm_runtime_disable(&pdev->dev);

	kfree(adc_dev);

	device_init_wakeup(&pdev->dev, 0);
	platform_set_drvdata(pdev, NULL);
	return 0;
}


static struct platform_driver ti_adc_driver = {
	.probe	  = adc_probe,
	.remove	 = __devexit_p(adc_remove),
	.driver	 = {
		.name   = "tsc",
		.owner  = THIS_MODULE,
	},	
};

static int __init ti_adc_init(void)
{
	return platform_driver_register(&ti_adc_driver);

}
module_init(ti_adc_init);

static void __exit ti_adc_exit(void)
{
	platform_driver_unregister(&ti_adc_driver);
}
module_exit(ti_adc_exit);

MODULE_DESCRIPTION("TI ADC controller driver");
MODULE_AUTHOR("Ti");
MODULE_LICENSE("GPL");