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,
#elseioctl: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;
}