首先感谢大家关注我的问题!
对于LINUX驱动编写来说,我是个新手,最近在编写AM3352 ADC驱动。公司产品用的是liunx 3.2内核:linux-3.2.0-psp04.06.00.08.sdk。
没有用到设备树。
通过参考TI Steven Liu 工程师写的驱动实例(http://www.deyisupport.com/question_answer/dsp_arm/sitara_arm/f/25/t/75146.aspx?pi2132219853=3)
进行改编成最简单的字符驱动模型,能够进行驱动的加载和卸载,驱动的主节点设成101,驱动名为drvADC,生成的驱动文件为drvADC.ko。驱动测试程序名为
drvADC_test.o编译好后,进行如下操作。
1、将drvADC.ko 和测试程序drvADC_test.o上传到核心板工作目录,并更改文件权限;
2、insmod drvADC.ko, 提示驱动安装成功, 检查在devices里面drvADC的主节点号的确为101;
3、mknod /dev/drvADC c 101 0, 检查在/dev目录下有drvADC;
4、在工作目录中运行测试程序./drvADC_test.o,输出信息如下:
[ 810.407348] Going to open ADC
st start…
[ 810.411773] map register memery
[ 810.416229] irq request success, err = 0
[ 810.420379] get clk success
[ 810.423278] get clock_rate success, clock_rate = 24000000
[ 810.428924] get clk_value success, clk_value = 8
[ 810.433715] Choose ADC channel 4
[ 810.437103] adc initialized!
[ 810.440582] ADC channel:4 working, reading adc data
[ 810.445678] Start adc channel 4
之后一直没有打印信息输出,分析对比程序,推断是没有触发中断,一直在等待中断。这会是什么原因呢?希望能得到大家的帮助。
下面是程序(附件也是一样的程序):
Eric Lia:
#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/irqs-33xx.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)#define ADC_MAJOR_NUMBER 101 #define ADC_MOUDLE_NAME "drvADC"#define AM335X_ADC_BASE_ADDR 0X44E0D000 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 am335x_adc_readl(struct adc_st *adc, unsigned int reg); static void am335x_adc_writel(struct adc_st *adc, unsigned int reg, unsigned int val); static void am335x_start_adc(struct adc_st *dev); static void am335x_stop_adc(struct adc_st *dev); static void am335x_adc_step_config(struct adc_st *adc_dev); static void am335x_adc_idle_config(struct adc_st *adc_config); static int am335x_adc_open(struct inode *inode, struct file *file); static ssize_t am335x_adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos); static int am335x_adc_release(struct inode *inode, struct file *filp); long am335x_adc_ioctl(struct file *file, unsigned int ioctl_num, unsigned long ioctl_param); static int am335x_adc_setup_cdev(struct adc_st *dev, int index);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 = am335x_adc_readl(adc_dev, TSCADC_REG_IRQSTATUS); printk("adc_interrupt status %d...\n", status); if (status & TSCADC_IRQENB_FIFO0THRES) { fifo0count = am335x_adc_readl(adc_dev, TSCADC_REG_FIFO0CNT); printk("ADC_Interrupt FIFO0_Count %d...\n", fifo0count); for (i = 0; i < fifo0count; i++) { readx1 = am335x_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; } am335x_stop_adc(adc_dev); am335x_adc_writel(adc_dev, TSCADC_REG_IRQSTATUS, irqclr); //clear TSCADC_IRQENB_FIFO0THRES irq am335x_adc_writel(adc_dev, TSCADC_REG_IRQEOI, 0x0); // check pending interrupts am335x_adc_writel(adc_dev, TSCADC_REG_SE, ADC_STPENB_STEPENB); ev_adc = 1; wake_up_interruptible(&adc_waitq); } return IRQ_HANDLED;}static unsigned int am335x_adc_readl(struct adc_st *adc, unsigned int reg) { return readl(adc->adc_base + reg); }static void am335x_adc_writel(struct adc_st *adc, unsigned int reg, unsigned int val) { writel(val, adc->adc_base + reg); }static void am335x_start_adc(struct adc_st *dev) { int ctrl = 0; printk("Start adc channel %d\n", channel); //TSC_ADC_SS module enable bit. //After programming all the steps and configuration registers, write a //1 to this bit to turn on TSC_ADC_SS. //Writing a 0 will disable the module (after the current conversion) ctrl |= TSCADC_CNTRLREG_STEPCONFIGWRT | TSCADC_CNTRLREG_TSCSSENB; am335x_adc_writel(dev, TSCADC_REG_CTRL, ctrl); }static void am335x_stop_adc(struct adc_st *dev) { int ctrl = 0; printk("Stop adc channel %d\n", channel); //TSC_ADC_SS module enable bit. //After programming all the steps and configuration registers, write a //1 to this bit to turn on TSC_ADC_SS. //Writing a 0 will disable the module (after the current conversion) ctrl |= TSCADC_CNTRLREG_STEPCONFIGWRT; am335x_adc_writel(dev, TSCADC_REG_CTRL, ctrl);}static void am335x_adc_step_config(struct adc_st *adc_dev) { unsigned int step_config = 0; unsigned int delay = 0; int i; /* Configure the Step registers */ step_config = TSCADC_STEPCONFIG_REFP | TSCADC_STEPCONFIG_RFM | TSCADC_STEPCONFIG_SINGLE_ENDED_OPER_MODE | TSCADC_STEPCONFIG_MODE; switch (channel) { case 0: step_config |= TSCADC_STEPCONFIG_1_INP; break; case 1: step_config |= TSCADC_STEPCONFIG_2_INP; break; case 2: step_config |= TSCADC_STEPCONFIG_3_INP; break; case 3: step_config |= TSCADC_STEPCONFIG_4_INP; break; case 4: step_config |= TSCADC_STEPCONFIG_5_INP; break; case 5: step_config |= TSCADC_STEPCONFIG_6_INP; break; case 6: step_config |= TSCADC_STEPCONFIG_7_INP; break; case 7: step_config |= TSCADC_STEPCONFIG_8_INP; break; default: channel = 4; step_config |= TSCADC_STEPCONFIG_5_INP; //default for channel 4 printk("Input wrong! Channel number should be [0..7]\n"); break; } printk("Choose ADC channel %d\n", channel); for (i = 1; i < 9; i++) { am335x_adc_writel(adc_dev, TSCADC_REG_STEPCONFIG(i), step_config); } delay = TSCADC_STEPCONFIG_SAMPLEDLY | TSCADC_STEPCONFIG_OPENDLY; for (i = 1; i < 9; i++) { am335x_adc_writel(adc_dev, TSCADC_REG_STEPDELAY(i), delay); } am335x_adc_writel(adc_dev, TSCADC_REG_SE, ADC_STPENB_STEPENB); // steven:this affect which step configs can be used!!! Important!!! }static void am335x_adc_idle_config(struct adc_st *adc_config) { /* Idle mode adc screen config */ unsigned int idle_config = 0; am335x_adc_writel(adc_config, TSCADC_REG_IDLECONFIG, idle_config); }static int am335x_adc_open(struct inode *inode, struct file *file) { int err; int clk_value; int clock_rate, ctrl; // struct resource *res; struct clk *clk; dev_t devno; // int ret; printk("Going to open ADC\n"); /* Allocate memory for device */ adc_dev = kzalloc(sizeof(struct adc_st), GFP_KERNEL); if (!adc_dev) { printk("failed to allocate memory.\n"); return -ENOMEM; } adc_dev->adc_base = ioremap(AM335X_ADC_BASE_ADDR, 0x1000); if (!adc_dev->adc_base) { printk("failed to map register memery.\n"); err = -ENOMEM; goto err_release_mem; } printk("map register memery\n"); //AM33XX_IRQ_ADC_GEN err = request_irq(16, adc_interrupt, IRQF_DISABLED, ADC_MOUDLE_NAME, adc_dev); if (err) { printk("Request adc irq failed\n"); return err; } printk("irq request success, err = %x\n", err); clk = clk_get(NULL, "adc_tsc_fck"); if (IS_ERR(clk)) { printk("failed to get TSC fck\n"); err = PTR_ERR(clk); goto err_free_irq; } printk("get clk success\n"); clock_rate = clk_get_rate(clk); printk("get clock_rate success, clock_rate = %d\n", clock_rate); clk_value = clock_rate / ADC_CLK; if (clk_value < 7) { printk("clock input less than min clock requirement\n"); err = -EINVAL; goto err_fail; } printk("get clk_value success, clk_value = %d\n", clk_value); /* TSCADC_CLKDIV needs to be configured to the value minus 1 */ clk_value = clk_value - 1; am335x_adc_writel(adc_dev, TSCADC_REG_CLKDIV, clk_value); /* Set the control register bits */ ctrl = TSCADC_CNTRLREG_STEPCONFIGWRT | TSCADC_CNTRLREG_TSCENB; am335x_adc_writel(adc_dev, TSCADC_REG_CTRL, ctrl); /* Set register bits for Idel Config Mode */ am335x_adc_idle_config(adc_dev); am335x_adc_step_config(adc_dev); /* IRQ Enable */ am335x_adc_writel(adc_dev, TSCADC_REG_IRQENABLE, TSCADC_IRQENB_FIFO0THRES); am335x_adc_writel(adc_dev, TSCADC_REG_FIFO0THR, 7); //FIFO THRESHOLD SET 0-7// device_init_wakeup(&pdev->dev, true); printk("adc initialized!\n");err_release_mem: iounmap(adc_dev->adc_base); err_free_irq: free_irq(16, adc_dev); err_fail: return 0; }//standard read function static ssize_t am335x_adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos) { printk("ADC channel:%d working, reading adc data\n", channel); if (!ev_adc) { if (filp->f_flags & O_NONBLOCK) { return -EAGAIN; } else { am335x_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 am335x_adc_close (struct inode *inode, struct file *file) { printk("close adc \n\r"); free_irq(16, adc_dev); iounmap(adc_dev->adc_base); kfifo_free(ptrFifoMsgTx); kfifo_free(ptrFifoMsgRx); MOD_DEC_USE_COUNT; return 0; }static int am335x_adc_release(struct inode *inode, struct file *filp) { return 0; }long am335x_adc_ioctl(struct file *file, unsigned int ioctl_num, unsigned long ioctl_param) { return 0; }static struct file_operations am335x_adc_fops = { .owner = THIS_MODULE, .open = am335x_adc_open, .close = am335x_adc_close, .read = am335x_adc_read, .release = am335x_adc_release, .unlocked_ioctl = am335x_adc_ioctl, };static int am335x_adc_setup_cdev(struct adc_st *dev, int index) { int err, devno = MKDEV(adc_major, index); cdev_init(&dev->adc, &am335x_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 int __init am335x_adc_init(void) { int ret; /* Fill in date and time into the module info. */ if ((ret = register_chrdev(ADC_MAJOR_NUMBER, ADC_MOUDLE_NAME, &am335x_adc_fops)) < 0) { printk("am335x adc dirver register failed!\r\n"); return ret; } printk("am335x adc dirver registed!\r\n"); return 0; }module_init(am335x_adc_init);static void __exit am335x_adc_exit(void) { unregister_chrdev(ADC_MAJOR_NUMBER, ADC_MOUDLE_NAME); printk("am335x adc dirver unregister\r\n"); }module_exit(am335x_adc_exit);MODULE_DESCRIPTION("TOK ADC controller driver"); MODULE_AUTHOR("TOK"); MODULE_LICENSE("GPL");