imx6中的epit+pwm固定pwm脉冲输出个数

摘要:

  imx6 提供了一个 GPT 和两个 EPIT,共三个定时器中断,但是 GPT 已经被用作为系统的时钟中断,所以本文使用 EPIT 来实现 PWM 脉冲个数的输出,先确定 PWM 的周期,然后根据周期计算出对应脉冲所占用的时间,当时间达到设定时,产生定时中断,关闭 PWM 的输出。PWM 输出有多种方式,本文直接调用内核空间 PWM 中的API进行操作,将 PWM 中的部分参数放在设备树中。

编写EPIT驱动存在的问题

	GPC中断号 《设备树中》
	a. 手册中对应的硬件中断号 - 32 =  interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>;
	b. cat /proc/interrupts 第一个数为映射出的中断号   倒数第二个为硬件 GIC中断号 = 设备数
	c. cd  /proc/device-tree 根节点下的所有属性和子节点
	d. 定义全局指针时,一定要分配空间
	e. 内核变量改变,应用空间调用时, ==》》 需上锁
	f. 卸载驱动时,出现错误原因:定义一个reg 结构体指针时,如果需要进行地址ioremap映射,不应再kmalloc分配空间。

EPIT 驱动设置步骤

  1. 先添加设备树文件,最好添加在最后调用的设备树文件
    需指定父中断:interrupt-parent = <&gpc> 或者直接放入 imx6qdl.dtsi 中;
  epit1: epit@020d0000 { /* EPIT1 */
                compatible = "fsl,imx6q-epit1"; 
                reg = <0x020d0000 0x4000>;
                interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>;
  };


  epit2: epit@020d4000 { /* EPIT2 */
                compatible = "fsl,imx6q-epit2"; 
                reg = <0x020d4000 0x4000>;
                interrupts = <0 57 IRQ_TYPE_LEVEL_HIGH>;
  };
  1. 在 include/dt-bindings/clock/imx6qdl-clock.h 添加两个宏
	  #define IMX6QDL_CLK_EPIT1                        ***
 	  #define IMX6QDL_CLK_EPIT2                        ***
 	  //数字大小,定义在原来宏后面,防止 CLK[] 数组越界, 宏大小 < IMX6QDL_CLK_END;
  1. 添加时钟,【/sys/kernel/debug/clk/】开发板可用时钟目录
    在driver/clk/imx/clk-imx6q.c
      -> 在imx6q_clocks_init函数中添加:
  clk[IMX6QDL_CLK_EPIT1]        = imx_clk_gate2("epit1(/sys/kernel/debug/clk/名字)",        "ipg_per(/sys/kernel/debug/clk/名字/clk-rate)",    base + 0x6c(epit1 控制地址), 12 (epit1 clocks (epit1_clk_enable));
  clk[IMX6QDL_CLK_EPIT2]        = imx_clk_gate2("epit2",        "ipg_per",            base + 0x6c, 14);clk_register_clkdev(clk[IMX6QDL_CLK_EPIT1], "per(匹配的字符)", "imx-epit.1(匹配的字符)");
  clk_register_clkdev(clk[IMX6QDL_CLK_EPIT2], "per", "imx-epit.2");
  这里一定不能漏了,否则在驱动中clk_get_sys会失败的。clk_get_sys("imx-epit.2", "per");


最后,给出一些与时钟有关的API函数:

struct clk *__clk_lookup(const char *name) 通过时钟名找到时钟
static inline int clk_enable(struct clk *clk) 使能时钟,不会睡眠
static inline void clk_disable(struct clk *clk) 禁止时钟,不会睡眠
static inline int clk_prepare_enable(struct clk *clk) 使能时钟,可能会睡眠
static inline void clk_disable_unprepare(struct clk *clk) 禁止时钟,可能会睡眠
static inline unsigned long clk_get_rate(struct clk *clk) 获取时钟频率
static inline int clk_set_rate(struct clk *clk, unsigned long rate) 设置时钟频率
static inline long clk_round_rate(struct clk *clk, unsigned long rate) 获取最接近的时钟频率
static inline int clk_set_parent(struct clk *clk, struct clk *parent) 设置时钟的父时钟
static inline struct clk *clk_get_parent(struct clk *clk) 获取时钟的父时钟

驱动源文件

#include "pwm_epit.h"

pwm_dev *pwmdev;
epit_reg *reg;


/* Clear interrupt flag */
static void clearinter(epit_reg *reg)
{
	writel(0x1, &(reg->EPITSR));
}

/* After request_irq enable epit by epit EPITCR register */
static void epit_enable(epit_reg *reg, int isOn)
{
	u32 val;

	val = readl(&(reg->EPITCR));
	if(isOn) {
		val |= EPITCR_EN;
	} else {
		val &= ~(EPITCR_EN);
	}
	
	writel(val, &(reg->EPITCR));
}

/* Definite interrupt hander function */
static irqreturn_t epit2_irq(int irq, void *dev_id)
{
	int val;
	epit_reg *dev = (epit_reg *)dev_id;
	
	val = readl(&(dev->EPITSR));
	if(val & (1 <<0))	{	/* judge compare event */
		pwm_imx6_disable(pwmdev);
		atomic_set(&pwmdev->flag, 1);
		if (pwmdev->async_queue)
			kill_fasync(&pwmdev->async_queue, SIGUSR1, POLL_IN);
	}

	clearinter(dev);
	epit_enable(dev, 0);  /* exe only one */
	return IRQ_HANDLED;
}

/* Init epit2 register */
static int init_epit(pwm_dev *pwmdev)
{
	u32 val;
	struct clk *timer_clk;
	char name[15];
	unsigned long rate;
	unsigned int ret = 0;
	
	pwmdev->nd = of_find_node_by_path("/soc/aips-bus@02000000/epit@020d4000");
//	countsdev.nd = of_find_compatible_node(NULL, NULL, "fsl,imx6q-epit2");
	if(pwmdev->nd) {
		reg = of_iomap(pwmdev->nd, 0);
		pwmdev->irq = irq_of_parse_and_map(pwmdev->nd, 0);
//		printk("irq = %d\r\n", countsdev.irq);
//		printk("&reg.EPITCR = %x\r\n", &reg->EPITCR);
//		printk("&reg.EPITSR = %x\r\n", &reg->EPITSR);
//		printk("&reg.EPITLR = %x\r\n", &reg->EPITLR);
//		printk("&reg.EPITCMPR = %x\r\n", &reg->EPITCMPR);
//		printk("&reg.EPITCNR = %x\r\n", &reg->EPITCNR);
	} else {
			printk("can not find countsdev.nd!!!\n");
			return -1;
	}
	
	/* enable epit2 clock with per */
	timer_clk = clk_get_sys("imx-epit.2", "per");
     	if (IS_ERR(timer_clk)) {
     	        printk("Faild to get timer_clk !\r\n");
                    return -1;
            }
	 rate = clk_get_rate(timer_clk);
	 printk(KERN_INFO"rate = %u\n", rate);
     clk_prepare_enable(timer_clk);
	 
	/* clearing register EPITCR */
	writel(0, &(reg->EPITCR));

	/* Init register EPITCR */				
     val = EPITCR_CLKSRC_PERIPHERAL|EPITCR_IOVW|EPITCR_WAITEN|EPITCR_STOPEN;
     val |= (EPITCR_RLD | EPITCR_OCIEN | EPITCR_ENMOD);
     writel(val, &(reg->EPITCR));
	 printk("EPITCR = %x\r\n", readl(&(reg->EPITCR)));
	 
	 /* set value of load */		 
//     writel(0, &(reg->EPITLR));
		
	/* set value register EPITCMPR, it will generate interrupt when the EPITCNR = EPITCMPR */  
     writel(0, &(reg->EPITCMPR));


	/* request irq */
     memset(pwmdev->name, 0, sizeof(name));
     sprintf(pwmdev->name, "epit2_timer");
     ret = request_irq(pwmdev->irq, epit2_irq, IRQF_TRIGGER_NONE, pwmdev->name, reg);
     	if(ret < 0) {
			return -EFAULT;
	}

	 return 0;
}


/* Set frac and timer timeout */
/* Tout(s) = ((frac +1 )* value) / Tclk   */
static void epit_set_count(epit_reg *reg, unsigned int frac, unsigned int value)
{
	u32 val;

	if(frac > 0xFFF)  
		frac = 0xFFF;
	val = readl(&(reg->EPITCR));
	val |= EPITCR_PRESC(frac);
	writel(val, &(reg->EPITCR));

	 /* set value of load */	
	writel(value, &(reg->EPITLR)); 
}


/******************************************************************************/
/*
 *  unify API with PWM in kernel
 *  set/get/enable/disbale PWM parament by ususal API interface 
 */
 
/* update pwm paraments */
static void pwm_imx6_update(pwm_dev *pwmdev)
{
    pwm_config(pwmdev->pwm, pwmdev->pwminfo.duty_ns, pwmdev->pwminfo.period_ns);
}

/* enable pwm */
static void pwm_imx6_enable(pwm_dev *pwmdev)
{
    pwm_enable(pwmdev->pwm);
}

/* disable pwm */
static void pwm_imx6_disable(pwm_dev *pwmdev)
{
    //set duty_ns = 0 before shutdowm pwm
    pwmdev->pwminfo.duty_ns= 0;
    pwm_imx6_update(pwmdev);
    pwm_disable(pwmdev->pwm);
}

/* set pwm period_ns */
static void pwm_imx6_set_period(pwm_dev *pwmdev, unsigned int period_ns)
{
    pwmdev->pwminfo.period_ns = period_ns;
    pwm_set_period(pwmdev->pwm, pwmdev->pwminfo.period_ns);
}

/* get pwm period_ns */
static unsigned int pwm_imx6_get_period(pwm_dev *pwmdev)
{
    return pwm_get_period(pwmdev->pwm);
}


/******************************************************************************/
/* init filp_operation functions */
static int pwm_imx6_open(struct inode *inode, struct file *filp)
{
    filp->private_data = pwmdev;   
    return 0;
}

static ssize_t pwm_imx6_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	int ret = 0;
	unsigned char tem_flag = 0;
	pwm_dev *dev = (pwm_dev *)filp->private_data;

	tem_flag = atomic_read(&dev->flag);
	atomic_set(&dev->flag, 0);
	ret = copy_to_user(buf, &tem_flag, sizeof(tem_flag));
	if (ret < 0) {
		printk("kernel read failed!!!\r\n");
		return -EINVAL;
	}
	
	return 0;
}

static int pwm_imx6_fasync(int fd, struct file *filp, int on)
{
	int ret;
	
	pwm_dev *dev = (pwm_dev *)filp->private_data;
	ret = fasync_helper(fd, filp, on, &dev->async_queue);
	if (ret < 0)
		return -EIO;
	return 0;
}

static long pwm_imx6_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    unsigned int ret;
    filp->private_data = pwmdev;
    
    switch (cmd)
    {
        case PWM_B_SET_PARA:
			 // tripple args to be checked
			 ret = copy_from_user(&paraConfigs, (struct pwm_para_config __user *)arg,
					    					 sizeof(struct pwm_para_config));
			 if (ret) {
					printk("copy user failed\n");
					return -EFAULT;
			 }
			 
          		             pwmdev->pwminfo.polarity = paraConfigs.c_polarity;
			 pwmdev->pwminfo.period_ns = paraConfigs.c_period_ns;
			 pwmdev->pwminfo.duty_ns  = paraConfigs.c_duty_ns;
			 pwmdev->ms_counts = (unsigned long)paraConfigs.c_counts;
			 
			 if(pwmdev->ms_counts < 30000) { /* prevent overflow interrupt */
				 pwmdev->ms_counts *= 66000;	
				 pwmdev->ms_counts -= 66*200; /* compensation 200 us */
				 epit_set_count(reg, 0,  pwmdev->ms_counts); /* set paraConfigs.c_counts(ms) interrupt */
			} else if (pwmdev->ms_counts < 100000) {
				 pwmdev->ms_counts /= 1000;
				 pwmdev->ms_counts *= 100000;	
				 pwmdev->ms_counts -= 2000; /* compensation 200 us */
				 epit_set_count(reg, 659,  pwmdev->ms_counts); /* set paraConfigs.c_counts(ms) interrupt */
			 } else {
			 	 pwmdev->ms_counts /= 100000;
				 pwmdev->ms_counts *= 2000000;	
				 pwmdev->ms_counts -= 4; /* compensation 200 us */
				 epit_set_count(reg, 3299,  pwmdev->ms_counts); /* set paraConfigs.c_counts(ms) interrupt */
			 }
				 
			 pwm_set_polarity(pwmdev->pwm, pwmdev->pwminfo.polarity);
			 pwm_imx6_update(pwmdev);
			 pwm_imx6_enable(pwmdev);	 			           
			 epit_enable(reg, 1);
             break;
            
        case PWM_B_GET_PARA:
			 paraConfigs.c_polarity = pwmdev->pwminfo.polarity;
			 paraConfigs.c_period_ns = pwmdev->pwminfo.period_ns;
			 paraConfigs.c_duty_ns = pwmdev->pwminfo.duty_ns;
			 
             ret = copy_to_user((struct pwm_para_config __user *)arg, &paraConfigs, 
					    					 sizeof(struct pwm_para_config));
        
             if (ret) {
                 	printk("copy_to_user failed.");
                 	return -EFAULT;
             }
             break;
            
        case PWM_B_ENABLE:
             pwm_imx6_enable(pwmdev);
             break;
            
        case PWM_B_DISABLE:
            pwm_imx6_disable(pwmdev);
            break;
            
        default:
            printk("pwmdev: cmd error!\n");
            return -EFAULT;
    }
    
    return 0;
}


static int pwm_imx6_release(struct inode *inode, struct file *filp)
{
    filp->private_data = pwmdev;
    atomic_inc(&pwmdev->flag);  
//  pwm_imx6_disable(pwmdev);   pwm disable when call close function in use spcae
    
    return pwm_imx6_fasync(-1, filp, 0);;
}

struct file_operations pwm_imx6_fops = {
    .owner = THIS_MODULE,
    .open = pwm_imx6_open,
    .release = pwm_imx6_release,
    .unlocked_ioctl = pwm_imx6_ioctl,
    .fasync = pwm_imx6_fasync,
    .read = pwm_imx6_read,
};


/*********	 get pwm-b informations from device tree   *********/
static ssize_t pwm_imx6_parse_dt(struct platform_device *pdev)
{
	struct device_node *node = pdev->dev.of_node;
    int ret;

    if (!node) {
        dev_err(&pdev->dev, "pwm-b: Device Tree node missing\n");
        return -EINVAL;
    }
    
    ret = of_property_read_u32(node, "pwmchannel", &pwmdev->pwminfo.channel);
    if (ret < 0) {
        dev_err(&pdev->dev, "pwm-b: pwmchannel missing\n");
        return ret;
    }

    ret = of_property_read_u32(node, "period_ns", &pwmdev->pwminfo.period_ns);
    if (ret < 0) {
        dev_err(&pdev->dev, "pwm-b: period_ns missing\n");
        return ret;
    }
    
    ret = of_property_read_u32(node, "duty_ns", &pwmdev->pwminfo.duty_ns);
    if (ret < 0) {
        dev_err(&pdev->dev, "pwm-b: duty_ns missing\n");
        return ret;
    }

    return 0;
}



/********************  probe function  **************************/
static int pwm_probe(struct platform_device *pdev)
{
	int ret;
	pwmdev = kzalloc(sizeof(pwm_dev), GFP_KERNEL);
	if (!pwmdev) {
		return -ENOMEM;
	}
	
	/* creater device number */
	if (pwmdev->major) {   /* definite device number */
		pwmdev->devid = MKDEV(pwmdev->major, 0);
		register_chrdev_region(pwmdev->devid, PWM_DEVICE, 
			PWM_DEV_NAME);
		} else {			/* without definite device number */
		alloc_chrdev_region(&pwmdev->devid, 0, PWM_DEVICE,  
			PWM_DEV_NAME);
		pwmdev->major = MAJOR(pwmdev->devid);	 /* get major device number */
		pwmdev->minor = MINOR(pwmdev->devid);   /* get minor device number */
		}

		printk(KERN_INFO"pwmdev major=%d,minor=%d\r\n", pwmdev->major,
		pwmdev->minor);


	/* Init cdev  */
	pwmdev->cdev.owner = THIS_MODULE;
	cdev_init(&pwmdev->cdev, &pwm_imx6_fops);

	/* add a cdev */
	ret = cdev_add(&pwmdev->cdev, pwmdev->devid, PWM_DEVICE);
	if(ret < 0) {
		printk(KERN_INFO"error : cdev_add\r\n");
		return ret;
	}

	/* creater a class */
	pwmdev->class = class_create(THIS_MODULE, PWM_DEV_NAME);
	if(IS_ERR(pwmdev->class)) {
		ret = PTR_ERR(pwmdev->class);
		goto error_class_create;
	}

	/* creater /device/PWM_DEV_NAME */
	pwmdev->device = device_create(pwmdev->class, NULL, pwmdev->devid, NULL, PWM_DEV_NAME);
	if(IS_ERR(pwmdev->device)) {
		ret = PTR_ERR(pwmdev->device);
		goto error_device_create;
	}

	/* default paraments pwm from device tree */
            ret = pwm_imx6_parse_dt(pdev);
   	if (ret < 0) {
        		dev_err(&pdev->dev, "pwm-b: Device not found\n");
        		return -ENODEV;
    	}	
 	
	/* request pwm-b device */
   	pwmdev->pwm = pwm_request(pwmdev->pwminfo.channel, "pwm-b");

    	if (IS_ERR(pwmdev->pwm)) {
        		dev_err(&pdev->dev, "pwm-b: unable to request legacy PWM\n");
        		ret = PTR_ERR(pwmdev->pwm);
        		return ret;
    	}
	
	/* set default paraments */
	pwm_imx6_update(pwmdev);

	/* pwm_set_polarity before enable pwm, and after set pwm paraments */
    ret = pwm_set_polarity(pwmdev->pwm, PWM_POLARITY_NORMAL);
    if (ret < 0)
        	printk("pwm set polarity fail, ret = %d\n", ret);

	init_epit(pwmdev);
	
	/* Init flag */
	atomic_set(&pwmdev->flag, 0);

	return 0;

error_device_create:
	class_destroy(pwmdev->class); 
error_class_create:
	cdev_del(&pwmdev->cdev);  
	kfree(pwmdev);
	return ret;	

}


static int pwm_remove(struct platform_device *dev)
{
	free_irq(pwmdev->irq, reg);
	
	/* release map address */
	iounmap(reg);
	
	/* disable pwm_device */
	pwm_imx6_disable(pwmdev);

	/* free pwm_device */
	pwm_free(pwmdev->pwm);
	
	/* logout char device */
	device_destroy(pwmdev->class, pwmdev->devid);

	class_destroy(pwmdev->class);

	cdev_del(&pwmdev->cdev);

	unregister_chrdev_region(pwmdev->devid, PWM_DEVICE);
	
	/* release point to pwmdev after pwmdev->devid */	
	kfree(pwmdev);
	
	printk(KERN_INFO"unregister_chrdev ok !!!\r\n");
	
	return 0;
}


/********************  match table  **************************/
static const struct of_device_id pwm_of_match[] = {
	{ .compatible = "pwm-pulse" },
	{ /* Sentinel */ }
};

MODULE_DEVICE_TABLE(of, pwm_of_match);

static struct platform_driver pwm_driver = {
	.driver = {
			.name = "pwm",
			.of_match_table = pwm_of_match,
	},

	.probe = pwm_probe,
	.remove = pwm_remove,
};


static int __init pwm_driver_init(void)
{
	return platform_driver_register(&pwm_driver);
}

static void __exit pwm_driver_exit(void)
{
	platform_driver_unregister(&pwm_driver);
}


module_init(pwm_driver_init);
module_exit(pwm_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zsj");
MODULE_DESCRIPTION("pwm b send 1 ms pulse!!!");
MODULE_ALIAS("pwm-b");
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值