一、硬件平台: MT7620(A9内核)
二、软件平台:
1、Ubuntu 12.04
2、MT7620 SDK软件开发包(MediaTek_ApSoC_SDK_4320_20150414.tar.bz2)
三、功能简介
对于看门狗以及其他的一些驱动,本人建议编译成模块,采用.ko 文件挂载,如此方便移植和维护。而且与内核分离开,可以节省编译时间,提高效率。
四、修改内容
对于MT7620 软件SDK 开发包,已经含有看门狗驱动。但是,内核看门狗驱动源码,默认没有设置看门狗超时时间的接口,故需要修改代码,增加相应的接口。
五、看门狗程序
1、看门狗头文件
本程序与为SDK开发包源码一致,原始的SDK代码为 " ..\RT288x_SDK\source\linux-2.6.36.x\drivers\watchdog\ralink_wdt.h "
头文件名称 watchdog_driver.h
#include <asm/rt2880/rt_mmap.h>
#ifndef _RALINK_WDT_WANTED
#define _RALINK_WDT_WANTED
#define PHYS_TO_K1(physaddr) KSEG1ADDR(physaddr)
#define sysRegRead(phys) (*(volatile unsigned int *)PHYS_TO_K1(phys))
#define sysRegWrite(phys, val) ((*(volatile unsigned int *)PHYS_TO_K1(phys)) = (val))
#define SYSCFG RALINK_SYSCTL_BASE + 0x10 /* System Configuration Register */
#define SYSCFG1 RALINK_SYSCTL_BASE + 0x14 /* System Configuration Register1 */
#define GPIOMODE RALINK_SYSCTL_BASE + 0x60
#define CLKCFG RALINK_SYSCTL_BASE + 0x30 /* Clock Configuration Register */
#define TMRSTAT (RALINK_TIMER_BASE) /* Timer Status Register */
#if defined (CONFIG_RALINK_RT6855A)
#define TMR1CTL (TMRSTAT + 0x0) /* WDG Timer Control */
#define TMR1LOAD (TMRSTAT + 0x2C) /* WDG Timer Load Value Register */
#define TMR1VAL (TMRSTAT + 0x30) /* WDG Timer Current Value Register */
#define RLDWDOG (TMRSTAT + 0x38) /* Reload Watchdog */
#elif defined (CONFIG_RALINK_MT7621) || defined (CONFIG_RALINK_MT7628)
#define TMR0CTL (TMRSTAT + 0x10) /* Timer0 Control */
#define TMR0LOAD (TMRSTAT + 0x14) /* Timer0 Load Value */
#define TMR0VAL (TMRSTAT + 0x18) /* Timer0 Counter Value */
#define TMR1CTL (TMRSTAT + 0x20) /* WDG Timer Control */
#define TMR1LOAD (TMRSTAT + 0x24) /* WDG Timer Load Value */
#define TMR1VAL (TMRSTAT + 0x28) /* WDG Timer Counter Value */
#define TMR2CTL (TMRSTAT + 0x30) /* Timer1 Control */
#define TMR2LOAD (TMRSTAT + 0x34) /* Timer1 Load Value */
#define TMR2VAL (TMRSTAT + 0x38) /* Timer1 Counter Value */
#else
#define TMR1CTL (TMRSTAT + 0x28) /* Timer1 Control */
#define TMR1LOAD (TMRSTAT + 0x20) /* Timer1 Load Value */
#define TMR1VAL (TMRSTAT + 0x24) /* Timer1 Counter Value */
#endif
#define INTENA (RALINK_INTCL_BASE + 0x34) /* Interrupt Enable */
enum timer_mode {
FREE_RUNNING,
PERIODIC,
TIMEOUT,
WATCHDOG
};
enum timer_clock_freq {
SYS_CLK, /* System clock */
SYS_CLK_DIV4, /* System clock /4 */
SYS_CLK_DIV8, /* System clock /8 */
SYS_CLK_DIV16, /* System clock /16 */
SYS_CLK_DIV32, /* System clock /32 */
SYS_CLK_DIV64, /* System clock /64 */
SYS_CLK_DIV128, /* System clock /128 */
SYS_CLK_DIV256, /* System clock /256 */
SYS_CLK_DIV512, /* System clock /512 */
SYS_CLK_DIV1024, /* System clock /1024 */
SYS_CLK_DIV2048, /* System clock /2048 */
SYS_CLK_DIV4096, /* System clock /4096 */
SYS_CLK_DIV8192, /* System clock /8192 */
SYS_CLK_DIV16384, /* System clock /16384 */
SYS_CLK_DIV32768, /* System clock /32768 */
SYS_CLK_DIV65536 /* System clock /65536 */
};
#endif
2、看门狗程序,参考SDK开发包中的 “ ..\RT288x_SDK\source\linux-2.6.36.x\drivers\watchdog\ralink_wdt.c ” 文件
文件名称为 watchdog_driver.c
// 本程序参考SDK开发包文件
// ..\RT288x_SDK\source\linux-2.6.36.x\drivers\watchdog\ralink_wdt.c
#include <linux/version.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/sgi/mc.h>
#include "watchdog_driver.h"
static int RaWdgAlive;
static int WdgLoadValue;
extern u32 get_surfboard_sysclk(void);
// modify :sky.houfei
#define WATCHDOG_TIMEOUT 90 /* 90 sec default timeout */
static int s_timeout = WATCHDOG_TIMEOUT;
#ifdef CONFIG_WATCHDOG_NOWAYOUT
static int nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
#endif
void SetWdgTimerEbl(unsigned int timer, unsigned int ebl)
{
unsigned int result;
result=sysRegRead(timer);
if(ebl==1){
#if defined (CONFIG_RALINK_RT6855A)
result |= (1<<25) | (1<<5);
#else
result |= (1<<7);
#endif
}else {
#if defined (CONFIG_RALINK_RT6855A)
result &= ~((1<<25)|(1<<5));
#else
result &= ~(1<<7);
#endif
}
sysRegWrite(timer,result);
//timer1 used for watchdog timer
#if defined (CONFIG_RALINK_TIMER_WDG_RESET_OUTPUT)
#if defined (CONFIG_RALINK_RT2880)
if(timer==TMR1CTL) {
result=sysRegRead(CLKCFG);
if(ebl==1){
result |= (1<<9); /* SRAM_CS_N is used as wdg reset */
}else {
result &= ~(1<<9); /* SRAM_CS_N is used as normal func */
}
sysRegWrite(CLKCFG,result);
}
#elif defined (CONFIG_RALINK_RT3052) || defined (CONFIG_RALINK_RT2883)
if(timer==TMR1CTL) {
//the last 4bits in SYSCFG are write only
result=sysRegRead(SYSCFG);
if(ebl==1){
result |= (1<<2); /* SRAM_CS_MODE is used as wdg reset */
}else {
result &= ~(1<<2); /* SRAM_CS_MODE is used as wdg reset */
}
sysRegWrite(SYSCFG,result);
}
#elif defined (CONFIG_RALINK_RT3883)
if(timer==TMR1CTL) {
result=sysRegRead(SYSCFG1);
if(ebl==1){
result |= (1<<2); /* GPIO2 as watch dog reset */
}else {
result &= ~(1<<2);
}
sysRegWrite(SYSCFG1,result);
}
#elif defined (CONFIG_RALINK_RT3352)
if(timer==TMR1CTL) {
//GPIOMODE[22:21]
//2'b00:SPI_CS1
//2'b01:WDG reset output
//2'b10:GPIO mode
result=sysRegRead(GPIOMODE); //GPIOMODE[22:21]
result &= ~(0x3<<21);
if(ebl==1){
result |= (0x1<<21); /* SPI_CS1 as watch dog reset */
}else {
//result |= (0x0<<21); //SPI_CS1
result |= (0x2<<21); //GPIO_mode
}
sysRegWrite(GPIOMODE,result);
}
#elif defined (CONFIG_RALINK_RT5350)
if(timer==TMR1CTL) {
/*
* GPIOMODE[22:21]
* 2'b00:SPI_CS1
* 2'b01:WDG reset output
* 2'b10:GPIO mode
*/
result=sysRegRead(GPIOMODE);
result &= ~(0x3<<21);
if(ebl==1){
result |= (0x1<<21);
}else {
//result |= (0x0<<21); //SPI_CS1
result |= (0x2<<21); //GPIO mode
}
sysRegWrite(GPIOMODE,result);
}
#elif defined (CONFIG_RALINK_MT7620)
if(timer==TMR1CTL) {
result=sysRegRead(GPIOMODE);
/*
* GPIOMODE[22:21] WDT_GPIO_MODE
* 2'b00:Normal
* 2'b01:REFCLK0
* 2'b10:GPIO Mode
*/
result &= ~(0x3<<21);
if(ebl==1){
result |= (0x0<<21);
}else {
result |= (0x2<<21); //GPIO
//result |= (0x1<<21); //REFCLK0
}
sysRegWrite(GPIOMODE,result);
}
#elif defined (CONFIG_RALINK_MT7621) || defined (CONFIG_RALINK_MT7628)
if(timer==TMR1CTL) {
result=sysRegRead(GPIOMODE);
/*
* GPIOMODE[22:21] WDT_GPIO_MODE
* 2'b00:Normal
* 2'b01:REFCLK0
* 2'b10:GPIO Mode
*/
result &= ~(0x3<<21);
if(ebl==1){
result |= (0x0<<21);
}else {
result |= (0x2<<21); //GPIO
//result |= (0x1<<21); //REFCLK0
}
sysRegWrite(GPIOMODE,result);
}
#endif
#endif
}
#if defined (CONFIG_RALINK_MT7621) || defined (CONFIG_RALINK_MT7628)
void SetWdgTimerClock(int prescale)
{
unsigned int result;
result=sysRegRead(TMR1CTL);
result &= 0x0000FFFF;
result |= (prescale << 16); //unit = 1u
sysRegWrite(TMR1CTL, result);
}
void SetTimerMode(unsigned int timer, enum timer_mode mode)
{
}
#else
void SetTimerMode(unsigned int timer, enum timer_mode mode)
{
unsigned int result;
result=sysRegRead(timer);
result &= ~(0x3<<4); //watchdog mode
result=result | (mode << 4);
sysRegWrite(timer,result);
}
void SetWdgTimerClock(unsigned int timer, enum timer_clock_freq prescale)
{
unsigned int result;
result=sysRegRead(timer);
result &= ~0xF;
result=result | (prescale&0xF);
sysRegWrite(timer,result);
}
#endif
static void RaWdgStart(void)
{
#if defined (CONFIG_RALINK_RT6855A)
int HwConf;
#endif
printk(KERN_INFO "Started WatchDog Timer.\n");
SetTimerMode(TMR1CTL,WATCHDOG);
#if defined (CONFIG_RALINK_RT2880) || defined (CONFIG_RALINK_RT2883) || \
defined (CONFIG_RALINK_RT3052) || defined (CONFIG_RALINK_RT3883)
/*
* For user easy configuration, We assume the unit of watch dog timer is 1s,
* so we need to calculate the TMR1LOAD value.
*
* Unit= 1/(SysClk/65536), 1 Sec = (SysClk)/65536
*
*/
SetWdgTimerClock(TMR1CTL,SYS_CLK_DIV65536);
WdgLoadValue = s_timeout * (get_surfboard_sysclk()/65536);
#elif defined (CONFIG_RALINK_RT6855A)
HwConf = sysRegRead(RALINK_SYSCTL_BASE + 0x8c);
if(((HwConf >> 24) & 0x3) == 0) { //SDR
WdgLoadValue = s_timeout * (140 * 1000 * 1000 / 2);
}else {
if(((HwConf >> 26) & 0x1) == 0) {
WdgLoadValue = s_timeout * (233 * 1000 * 1000 / 2);
}else {
WdgLoadValue = s_timeout * (175 * 1000 * 1000 / 2);
}
}
sysRegWrite(TMR1LOAD, WdgLoadValue);
#elif defined (CONFIG_RALINK_MT7621) || defined (CONFIG_RALINK_MT7628)
SetWdgTimerClock(1000); // 1 msec
WdgLoadValue = s_timeout * 1000;
sysRegWrite(TMR1LOAD, WdgLoadValue);
#else
SetWdgTimerClock(TMR1CTL,SYS_CLK_DIV65536);
WdgLoadValue = s_timeout * (40000000/65536); //fixed at 40Mhz
#endif
sysRegWrite(TMR1LOAD, WdgLoadValue);
SetWdgTimerEbl(TMR1CTL,1);
}
static void RaWdgStop(void)
{
SetWdgTimerEbl(TMR1CTL,0);
printk(KERN_INFO "Stopped WatchDog Timer.\n");
}
static void RaWdgReload(void)
{
#if defined (CONFIG_RALINK_RT6855A)
sysRegWrite(RLDWDOG, 1);
#elif defined (CONFIG_RALINK_MT7621) || defined (CONFIG_RALINK_MT7628)
sysRegWrite(TMRSTAT, (1 << 9)); //WDTRST
#else
sysRegWrite(TMR1LOAD, WdgLoadValue);
#endif
}
// add by sky.houfei
// 设置WatchDog周期timeout
static int RaWdgSetHeartbeat(int timeout)
{
#if defined (CONFIG_RALINK_RT6855A)
int HwConf;
#endif
if (timeout > 107)
{
printk("Warn! The timeout value is > 107 second, the max value is 107.Set failed\n");
}
SetTimerMode(TMR1CTL,WATCHDOG);
s_timeout = timeout;
#if defined (CONFIG_RALINK_RT2880) || defined (CONFIG_RALINK_RT2883) || \
defined (CONFIG_RALINK_RT3052) || defined (CONFIG_RALINK_RT3883)
/*
* For user easy configuration, We assume the unit of watch dog timer is 1s,
* so we need to calculate the TMR1LOAD value.
*
* Unit= 1/(SysClk/65536), 1 Sec = (SysClk)/65536
*
*/
SetWdgTimerClock(TMR1CTL,SYS_CLK_DIV65536);
WdgLoadValue = s_timeout * (get_surfboard_sysclk()/65536);
#elif defined (CONFIG_RALINK_RT6855A)
HwConf = sysRegRead(RALINK_SYSCTL_BASE + 0x8c);
if(((HwConf >> 24) & 0x3) == 0) { //SDR
WdgLoadValue = s_timeout * (140 * 1000 * 1000 / 2);
}else {
if(((HwConf >> 26) & 0x1) == 0) {
WdgLoadValue = s_timeout * (233 * 1000 * 1000 / 2);
}else {
WdgLoadValue = s_timeout * (175 * 1000 * 1000 / 2);
}
}
sysRegWrite(TMR1LOAD, WdgLoadValue);
#elif defined (CONFIG_RALINK_MT7621) || defined (CONFIG_RALINK_MT7628)
SetWdgTimerClock(1000); // 1 msec
WdgLoadValue = s_timeout * 1000;
sysRegWrite(TMR1LOAD, WdgLoadValue);
#else
SetWdgTimerClock(TMR1CTL,SYS_CLK_DIV65536);
WdgLoadValue = s_timeout * (40000000/65536); //fixed at 40Mhz
#endif
sysRegWrite(TMR1LOAD, WdgLoadValue);
SetWdgTimerEbl(TMR1CTL,1);
return 0;
}
/*
* Allow only one person to hold it open
*/
static int ralink_open(struct inode *inode, struct file *file)
{
if (RaWdgAlive)
return -EBUSY;
#ifdef CONFIG_WATCHDOG_NOWAYOUT
if (nowayout)
__module_get(THIS_MODULE);
#endif
/* Activate timer */
RaWdgStart();
RaWdgAlive = 1;
return nonseekable_open(inode, file);
}
static int ralink_release(struct inode *inode, struct file *file)
{
/* Shut off the timer.
* Lock it in if it's a module and we defined ...NOWAYOUT */
#ifdef CONFIG_WATCHDOG_NOWAYOUT
if (!nowayout)
RaWdgStop(); /* Turn the WDT off */
#endif
RaWdgAlive = 0;
return 0;
}
static ssize_t ralink_write(struct file *file, const char *data, size_t len, loff_t *ppos)
{
/* Refresh the timer. */
if (len) {
RaWdgReload();
}
return len;
}
static struct watchdog_info ident = {
.options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "Ralink Hardware WatchDog",
};
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)
long ralink_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
#else
static int ralink_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
#endif
{
int options, retval = -EINVAL;
void __user *argp = (void __user *)arg;
int __user *p = argp;
int new_margin;
switch (cmd) {
default:
return -ENOTTY;
case WDIOC_GETSUPPORT:
if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
return -EFAULT;
return 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0,(int *)arg);
case WDIOC_KEEPALIVE:
RaWdgReload();
return 0;
case WDIOC_GETTIMEOUT:
return put_user(s_timeout,(int *)arg);
// add by sky.houfei
// 增加设置超时时间功能
case WDIOC_SETTIMEOUT:
if (get_user(new_margin, p))
{
return -EFAULT;
}
if (RaWdgSetHeartbeat(new_margin))
{
return -EINVAL;
}
RaWdgReload();
return put_user(s_timeout, p);
case WDIOC_SETOPTIONS:
{
if (get_user(options, (int *)arg))
return -EFAULT;
if (options & WDIOS_DISABLECARD) {
RaWdgStop();
retval = 0;
}
if (options & WDIOS_ENABLECARD) {
RaWdgStart();
retval = 0;
}
return retval;
}
}
}
static int ralink_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
{
if (code == SYS_DOWN || code == SYS_HALT)
RaWdgStop(); /* Turn the WDT off */
return NOTIFY_DONE;
}
static const struct file_operations ralink_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = ralink_write,
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)
.unlocked_ioctl = ralink_ioctl,
#else
.ioctl = ralink_ioctl,
#endif
.open = ralink_open,
.release = ralink_release,
};
static struct miscdevice ralink_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &ralink_fops,
};
static struct notifier_block ralink_notifier = {
.notifier_call = ralink_notify_sys,
};
static char banner[] __initdata =
KERN_INFO "Ralink APSoC Hardware Watchdog Timer\n";
static int __init watchdog_init(void)
{
int ret;
ret = register_reboot_notifier(&ralink_notifier);
if (ret) {
printk(KERN_ERR "cannot register reboot notifier (err=%d)\n",
ret);
return ret;
}
ret = misc_register(&ralink_miscdev);
if (ret) {
printk(KERN_ERR "cannot register miscdev on minor=%d (err=%d)\n",
WATCHDOG_MINOR, ret);
unregister_reboot_notifier(&ralink_notifier);
return ret;
}
printk(banner);
return 0;
}
static void __exit watchdog_exit(void)
{
RaWdgStop();
misc_deregister(&ralink_miscdev);
unregister_reboot_notifier(&ralink_notifier);
}
module_init(watchdog_init);
module_exit(watchdog_exit);
MODULE_AUTHOR("sky.houfei");
MODULE_DESCRIPTION("Ralink APSoC Hardware Watchdog Timer");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
3、驱动 makefile
文件名:MakeFile
make 命令,其中 “ /home/sky/RT288x_SDK/source/linux-2.6.36.x/ ”为内核包的路径。
make MIPSLINUXDIR=/home/sky/RT288x_SDK/source/linux-2.6.36.x/
make clean 命令,其中 “ /home/sky/RT288x_SDK/source/linux-2.6.36.x/ ”为内核包的路径。
make MIPSLINUXDIR=/home/sky/RT288x_SDK/source/linux-2.6.36.x/ clean
MakeFile 内容如下:
obj-m = watchdog_driver.o
#K_DIR = /home/sky/RT288x_SDK/source/linux-2.6.36.x/
PWD=$(shell pwd)
all:
make ARCH=mips CROSS_COMPILE="/opt/buildroot-gcc463/usr/bin"/mipsel-linux- -C $(MIPSLINUXDIR) M=$(PWD) modules
clean:
make ARCH=mips CROSS_COMPILE="/opt/buildroot-gcc463/usr/bin"/mipsel-linux- -C $(MIPSLINUXDIR) M=$(PWD) clean
rm -f watchdog_driver.ko
#make command:
#make MIPSLINUXDIR=/home/sky/RT288x_SDK/source/linux-2.6.36.x/
#make MIPSLINUXDIR=/home/sky/RT288x_SDK/source/linux-2.6.36.x/ clean
4、看门狗应用程序
程序名称:watchdog_app.c
/*
* Watchdog Driver Test Program
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/watchdog.h>
#define DEV "/dev/watchdog"
static int s_watchdogFd;
/*
* @brief 看门狗使能。
*/
static void Watchdog_Start(void)
{
int i = WDIOS_ENABLECARD;
ioctl(s_watchdogFd,WDIOC_SETOPTIONS,&i);
}
/*
* @brief 看门狗禁止。
*/
static void Watchdog_Stop(void)
{
int i = WDIOS_DISABLECARD;
ioctl(s_watchdogFd, WDIOC_SETOPTIONS, &i);
}
/*
* @brief 喂狗程序。
*/
static void Watchdog_Feed(void)
{
int dummy;
ioctl(s_watchdogFd, WDIOC_KEEPALIVE, &dummy);
}
/*
* @brief 设置看门狗复位时间。
* @param[in] time, unsigned int, 复位时间,单位为秒。
*/
static void Watchdog_TimeoutSet(int time)
{
ioctl(s_watchdogFd, WDIOC_SETTIMEOUT, &time);
}
/*
* @brief 获取看门狗复位时间。
* @return time, int,复位定时时间,单位为秒。
*/
static int Watchdog_TimeoutGet(void)
{
int time = 0;
ioctl(s_watchdogFd, WDIOC_GETTIMEOUT, &time);
return time;
}
int main(int argc, char *argv[])
{
int flags;
int count = 0;
int i = 0;
s_watchdogFd = open(DEV, O_RDWR);
if (s_watchdogFd == -1)
{
fprintf(stderr, "Watchdog device not enabled.\n");
fprintf(stderr, "Can not find the file /dev/watchdog.\n");
fflush(stderr);
exit(-1);
}
// 读看门狗溢出时间
i = Watchdog_TimeoutGet();
printf("watchdog get timer = %d\n", i);
// 设置看门狗溢出时间
i = 5; // 复位时间5秒
Watchdog_TimeoutSet(i);
printf("watchdog set timer = %d\n", i);
// 读看门狗溢出时间
i = Watchdog_TimeoutGet();
printf("watchdog get timer = %d\n", i);
// 功能说明:
// 1、前面10秒喂狗,看门狗不会复位,用于验证喂狗功能是否正常。
// 2、第11 秒关闭看门狗,用于验证看门狗关闭是否正常(如果不能关闭,则5秒达到看门狗超时后,自动复位)。
// 3、第30秒 看门狗使能,用于验证看门狗使能是否正常,此时不喂狗,5秒后自动复位。
// 如果验证看门狗是否正常,直接把 喂狗、开启、关闭看门狗代码注释即可。
while(1)
{
count++;
if (count < 10)
{
Watchdog_Feed(); // 喂狗程序
}
else if (count == 11)
{
Watchdog_Stop(); // 停止看门狗功能
}
else if (count == 30)
{
Watchdog_Start(); // 开启看门狗功能
}
sleep(1);
printf("count = %d\n", count);
}
}