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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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);
头文件如下:
#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
#include
#include
#include
#include
#include
#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;
}