一、硬件平台: 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
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); } }