mt7628/7688 PWM驱动

     MT7628/MT7688中的pwm,datasheet写的很糟糕,不像三星,还给你来个编程的流程图,配置哪个寄存器都会告诉你,一坨寄存器丢给你..

     它的PWM分为两种模式,OLD和new,这里我们用简单的OLD模式,NEW模式没研究过,手册不详细,搞不懂NEW模式...

      

     PWM的主要寄存器如下,

     


                其中OLD模式要用到的寄存器有PWM_ENABLE, PWMX_CON, PWMX_GDURATION,PWMX_WAVE_NUM,PWMX_DATA_WIDTH,PWMX_THRESH.

                其中PWM_ENABLE用于使能各个PWM,PWM_CON寄存去如下:

                


             选择OLD模式,STOP_BITS寄存器忽略,GUARD_VALUE和IDEL_VAL需要配置0或1, 剩下的就是时钟配置了.

              其他几个寄存器,GUARD_DURATION这个配置了好像对应OLD模式不会生效.所以整个PWM波形由PWM_DATA_WIDTH,PWM_THRESH和PWM_WAVE_NUM来设定;其中DATA_WIDTH为一个周期的时间值,当然这个值是以PWM时钟为单位的.PWM_THRESH为所谓的占空比,若DATA_WIDTH为1000,PWM_THRESH设置为500,则输出方波,高低电平比相同. PWM_WAVE_NUM为发送多少个这种PWM信号。

         驱动如下:

         

         

#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/module.h>
#include <linux/device.h> 		
#include <linux/cdev.h>
#include <linux/interrupt.h>  
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/poll.h>   
#include <linux/fcntl.h>
#include <linux/spinlock.h>

#include "sooall_pwm.h"

MODULE_LICENSE("GPL");

#define RALINK_CLK_CFG    0xB0000030 
#define RALINK_AGPIO_CFG  0xB000003C
#define RALINK_GPIOMODE   0xB0000060 

#define RALINK_PWM_BASE   0xB0005000 
#define RALINK_PWM_ENABLE    RALINK_PWM_BASE 

#define PWM_MODE_BIT      15 
#define PWM_GVAL_BIT      8 
#define PWM_IVAL_BIT      7  

enum {
	PWM_REG_CON,
	PWM_REG_GDUR = 0x0C,
	PWM_REG_WNUM = 0x28,
	PWM_REG_DWID = 0x2C,
	PWM_REG_THRE = 0x30,
	PWM_REG_SNDNUM = 0x34,
}PWM_REG_OFF;

#define PWM_NUM  4 
u32 PWM_REG[PWM_NUM] = {
	(RALINK_PWM_BASE + 0x10),    /* pwm0 base */
	(RALINK_PWM_BASE + 0x50),    /* pwm1 base */
	(RALINK_PWM_BASE + 0x90),    /* pwm2 base */
	(RALINK_PWM_BASE + 0xD0)     /* pwm3 base */
};

#define NAME  "sooall_pwm"

int pwm_major;
int pwm_minor  = 0;
int pwm_device_cnt = 1;
struct cdev pwm_cdev;

static struct class  *pwm_class;
static struct device *pwm_device;

spinlock_t pwm_lock;

static void sooall_pwm_cfg(struct pwm_cfg *cfg)
{
	u32 value;
	unsigned long  flags;
	u32 basereg;

	basereg = PWM_REG[cfg->no];

	spin_lock_irqsave(&pwm_lock, flags);
	
	/* 1. set the pwm control register */
	value  = le32_to_cpu(*(volatile u32 *)(basereg + PWM_REG_CON));
	/* old mode */
	value |= (1 << PWM_MODE_BIT);
	/* set the idel val and guard val */
	value &= ~((1 << PWM_IVAL_BIT) | (1 << PWM_GVAL_BIT));
	value |= ((cfg->idelval & 0x1) << PWM_IVAL_BIT);
	value |= ((cfg->guardval & 0x1) << PWM_GVAL_BIT);
	/* set the source clk */
	if (cfg->clksrc == PWM_CLK_100HZ) {
		value &= ~(1<<3);		
	} else {
		value |= (1<<3);	
	}
    /* set the clk div */
	value &= ~0x7;
	value |= (0x7 & cfg->clkdiv);	
	*(volatile u32 *)(basereg + PWM_REG_CON) = cpu_to_le32(value);	

	/* 2. set the guard duration val */
	value  = le32_to_cpu(*(volatile u32 *)(basereg + PWM_REG_GDUR));
	value &= ~(0xffff);
	value |= (cfg->guarddur & 0xffff);
	*(volatile u32 *)(basereg + PWM_REG_GDUR) = cpu_to_le32(value);	
	
	
	/* 3. set the wave num val */
	value  = le32_to_cpu(*(volatile u32 *)(basereg + PWM_REG_WNUM));
	value &= ~(0xffff);
	value |= (cfg->wavenum & 0xffff);
	*(volatile u32 *)(basereg + PWM_REG_WNUM) = cpu_to_le32(value);	
	
	
	/* 4. set the data width val */
	value  = le32_to_cpu(*(volatile u32 *)(basereg + PWM_REG_DWID));
	value &= ~(0x1fff);
	value |= (cfg->datawidth & 0x1fff);
	*(volatile u32 *)(basereg + PWM_REG_DWID) = cpu_to_le32(value);	
	
	
	/* 5. set the thresh  val */
	value  = le32_to_cpu(*(volatile u32 *)(basereg + PWM_REG_THRE));
	value &= ~(0x1fff);
	value |= (cfg->threshold & 0x1fff);
	*(volatile u32 *)(basereg + PWM_REG_THRE) = cpu_to_le32(value);	

	spin_unlock_irqrestore(&pwm_lock, flags);
}

static void sooall_pwm_enable(int no)
{
	u32 value;
	unsigned long  flags;

	printk(KERN_INFO NAME "enable pwm%d\n", no);

	spin_lock_irqsave(&pwm_lock, flags);
	value  = le32_to_cpu(*(volatile u32 *)(RALINK_PWM_ENABLE));
	value  |= (1 << no);
	*(volatile u32 *)(RALINK_PWM_ENABLE) = cpu_to_le32(value);	
	spin_unlock_irqrestore(&pwm_lock, flags);
}

static void sooall_pwm_disable(int no)
{
	u32 value;
	unsigned long  flags;
	
	printk(KERN_INFO NAME "disable pwm%d\n", no);

	spin_lock_irqsave(&pwm_lock, flags);
	value  = le32_to_cpu(*(volatile u32 *)(RALINK_PWM_ENABLE));
	value  &= ~(1 << no);
	*(volatile u32 *)(RALINK_PWM_ENABLE) = cpu_to_le32(value);	
	spin_unlock_irqrestore(&pwm_lock, flags);
}

static void sooall_pwm_getsndnum(struct pwm_cfg *cfg)
{
	u32 value;
	unsigned long  flags;
	u32 regbase = PWM_REG[cfg->no];
	
	spin_lock_irqsave(&pwm_lock, flags);
	value  = le32_to_cpu(*(volatile u32 *)(regbase + PWM_REG_SNDNUM));
	cfg->wavenum = value;
	spin_unlock_irqrestore(&pwm_lock, flags);
}

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)
long sooall_pwm_ioctl(struct file *file, unsigned int req,
		unsigned long arg)
#else
int sooall_pwm_ioctl(struct inode *inode, struct file *file, unsigned int req,
		unsigned long arg)
#endif
{
	switch (req) {
	case PWM_ENABLE:
		sooall_pwm_enable(((struct pwm_cfg *)arg)->no);
		break;
	case PWM_DISABLE:
		sooall_pwm_disable(((struct pwm_cfg *)arg)->no);
		break;
	case PWM_CONFIGURE:
		sooall_pwm_cfg((struct pwm_cfg *)arg);
		break;
	case PWM_GETSNDNUM:
		sooall_pwm_getsndnum((struct pwm_cfg *)arg);
		break;
	default:
		return -ENOIOCTLCMD;
	}
	
	return 0;
}

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

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

static const struct file_operations pwm_fops = {
	.owner		= THIS_MODULE,
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)
	unlocked_ioctl:sooall_pwm_ioctl,
#else
	ioctl:sooall_pwm_ioctl,
#endif 
	.open		= sooall_pwm_open,
	.release    = sooall_pwm_close,
};

static int setup_chrdev(void)
{
	dev_t dev;
	int err = 0;

	if (pwm_major) {
		dev = MKDEV(pwm_major, 0);				
		err = register_chrdev_region(dev, pwm_device_cnt, NAME);
	} else {
		err = alloc_chrdev_region(&dev, 0, pwm_device_cnt, NAME);	
		pwm_major = MAJOR(dev);
	}
	
	if (err < 0) {
		printk(KERN_ERR NAME "get device number failed\n");
		return -1;
	}
	
	cdev_init(&pwm_cdev, &pwm_fops);
	pwm_cdev.owner = THIS_MODULE;
	pwm_cdev.ops = &pwm_fops;	
	err = cdev_add(&pwm_cdev, dev, pwm_device_cnt);
	if (err < 0) {
		printk(KERN_ERR NAME "cdev_add failed\n");	
		unregister_chrdev_region(dev, pwm_device_cnt);
		return -1;	
	}
	
	return 0;
}

static void clean_chrdev(void)
{
	dev_t dev = MKDEV(pwm_major, 0);

	cdev_del(&pwm_cdev);
	unregister_chrdev_region(dev, pwm_device_cnt);
}

static void setup_gpio(void)
{
	u32 value;
	int i = 0;
	/* pwm0 pwm1 */
	
	/* enable the pwm clk */
	value  = le32_to_cpu(*(volatile u32 *)(RALINK_CLK_CFG));
	value  |= (1 << 31);
	*(volatile u32 *)(RALINK_CLK_CFG) = cpu_to_le32(value);	
	
	/* set the agpio cfg of ephy_gpio_aio_en */
	value  = le32_to_cpu(*(volatile u32 *)(RALINK_AGPIO_CFG));
	value  |= (0xF<<17);
	*(volatile u32 *)(RALINK_AGPIO_CFG) = cpu_to_le32(value);	

	/* set the pwm  mode */
	value  = le32_to_cpu(*(volatile u32 *)(RALINK_GPIOMODE));
	value  &= ~(3 << 28 | 3 << 30);
	*(volatile u32 *)(RALINK_GPIOMODE) = cpu_to_le32(value);	
	
	/* disable all the pwm */
	for (i = 0; i < PWM_NUM; i++) {
		sooall_pwm_disable(i);	
	}
	
}

static int sooall_pwm_init(void)
{
	int ret = 0;

	spin_lock_init(&pwm_lock);
	
	ret = setup_chrdev();
	if (ret < 0) 
		return -1;

	pwm_class = class_create(THIS_MODULE, NAME);
	if (NULL == pwm_class) {
		printk(KERN_ERR NAME "class_create failed\n");	
		goto dev_clean;
	}

	pwm_device = device_create(pwm_class, NULL, 
			        MKDEV(pwm_major, pwm_minor), NULL, "sooall_pwm");
	if (NULL == pwm_device) {
		printk(KERN_ERR NAME "device_create failed\n");	
		goto class_clean;
	}
		
	setup_gpio();	
		
	printk(KERN_INFO "sooall pwm init success\n"); 
	return 0;

class_clean:
	class_destroy(pwm_class);	
dev_clean:
	clean_chrdev();
	return -1;
}

static void sooall_pwm_exit(void)
{
	device_destroy(pwm_class, MKDEV(pwm_major, pwm_minor));
	class_destroy(pwm_class);	
	clean_chrdev();
	printk(KERN_INFO "sooall pwm exit\n");
}

module_init(sooall_pwm_init);  
module_exit(sooall_pwm_exit);  	
</pre><pre name="code" class="cpp">
头文件如下:
<pre name="code" class="cpp">#ifndef _SOOALL_PWM_H_
#define _SOOALL_PWM_H_

/* the source of the clock */
typedef enum {
	PWM_CLK_100KHZ,  
	PWM_CLK_40MHZ
}PWM_CLK_SRC;

/* clock div */
typedef enum {
	PWM_CLI_DIV0 = 0,
	PWM_CLK_DIV2,
	PWM_CLK_DIV4,
	PWM_CLK_DIV8,
	PWM_CLK_DIV16,
	PWM_CLK_DIV32,
	PWM_CLK_DIV64,
	PWM_CLK_DIV128,
}PWM_CLK_DIV;

struct pwm_cfg {
	int no;
	PWM_CLK_SRC    clksrc;
	PWM_CLK_DIV    clkdiv;
	unsigned char  idelval;
	unsigned char  guardval;
	unsigned short guarddur;
	unsigned short wavenum;
	unsigned short datawidth;
	unsigned short threshold;
};

/* ioctl */
#define PWM_ENABLE      0
#define PWM_DISABLE     1  
#define PWM_CONFIGURE   2 
#define PWM_GETSNDNUM   3
#endif 



测试文件如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "sooall_pwm.h"

#define PWM_DEV "/dev/sooall_pwm"

int main(int argc, char *argv)
{
	int ret = -1;
	int pwm_fd;
	int pwmno;
	struct pwm_cfg  cfg;

	pwm_fd = open(PWM_DEV, O_RDWR);
	if (pwm_fd < 0) {
		printf("open pwm fd failed\n");
		return -1;
	}
	
	cfg.no        =   0;    /* pwm0 */
	cfg.clksrc    =   PWM_CLK_40KHZ; 
	cfg.clkdiv    =   PWM_CLK_DIV2;
	cfg.idelval   =   0;  
	cfg.guardval  =   0;
	cfg.guarddur  =   0; 
	cfg.wavenum   =   0;  /* forever loop */
	cfg.datawidth =   1000;
	cfg.threshold =   500; 

	ioctl(pwm_fd, PWM_CONFIGURE, &cfg);	
	ioctl(pwm_fd, PWM_ENABLE, &cfg);
		
	while (1) {
		static int cnt = 0;
		sleep(5);
		ioctl(pwm_fd, PWM_GETSNDNUM, &cfg);
		printf("send wave num = %d\n", cfg.wavenum);
		cnt++;
		if (cnt == 10) {
			ioctl(pwm_fd, PWM_DISABLE, &cfg);
			break;
		}
	}
	
	return 0;
}


发布了3 篇原创文章 · 获赞 1 · 访问量 9619
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览