自己写了一个的简单的 for smdk2410 的watchdog driver (完全依照datasheet ,并没有抄袭
s3c2410-wdt.c ) , 可是运行的时候 ,有问题:
# ./wdt_tt
in wdt_disable
readw ok ,tmp=0x0000
COUNTER_VALUE=18300
S3C2410_WTCNT address = c5000008
the value written S3C2410_WTCON=0xff00
Unable to handle kernel NULL pointer dereference at virtual address 00000032
pgd = c3d60000
[00000032] *pgd=338e9031, *pte=00000000, *ppte=00000000
Internal error: Oops: 13 [#1]
Modules linked in: wdt_s3c
CPU: 0
PC is at do_sys_open+0x70/0xe0
LR is at mntput_no_expire+0x24/0x84
pc : [<c007d730>] lr : [<c009ace4>] Not tainted
sp : c38e7f70 ip : c38e7ea8 fp : c38e7f94
r10: 4014f60c r9 : c38e6000 r8 : c38e0000
r7 : 00000003 r6 : 0000002a r5 : beb4feec r4 : 00000000
r3 : 00000000 r2 : a0000013 r1 : a0000093 r0 : 0000002a
Flags: nzcv IRQs on FIQs on Mode SVC_32 Segment user
Control: 717F
Table: 33D60000 DAC: 00000015
Process wdt_tt (pid: 263, stack limit = 0xc38e6250)
Stack: (0xc38e7f70 to 0xc38e8000)
7f60: 00008550 beb4fee4 000085e4 00000001
7f80: 00000005 c0026f44 c38e7fa4 c38e7f98 c007d7d8 c007d6d0 00000000 c38e7fa8
7fa0: c0026da0 c007d7c4 beb4fee4 000085e4 000086f8 00000000 beb4feec 00000000
7fc0: beb4fee4 000085e4 00000001 00008648 00008550 00000000 4014f60c beb4feb8
7fe0: 00000000 beb4fe94 00002680 400ee830 60000010 000086f8 fbffff7f ffffffff
Backtrace:
[<c007d6c0>] (do_sys_open+0x0/0xe0) from [<c007d7d8>]
(sys_open+0x24/0x28)
r8 = C0026F44 r7 = 00000005 r6 = 00000001 r5 = 000085E4
r4 = BEB4FEE4
[<c007d7b4>] (sys_open+0x0/0x28) from [<c0026da0>]
(ret_fast_syscall+0x0/0x2c)
Code: e1a00007 ebfffe9e e1a07006 ea000014 (e5900008)
Segmentation fault
#
----
上面的wdt_tt的源码是:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(int argc,char **argv)
{
int fd = 0;
int n =0;
fd = open("/dev/wdt",O_RDONLY );
printf("fd=%d/n",fd);
if(fd < 0) {
perror("/dev/wdt");
return -1;
}
sleep(10);
close(fd);
return 0;
}
dirver的源码是
/* S3C2410-wdt.c
* CopyRight reserved by BobZhang
href="mailto:[email protected]">[email protected]
href="mailto:[email protected]">[email protected]
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/clk.h>
#include <asm/hardware.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/rtc.h>
#include <linux/miscdevice.h> //一定要有这个
#include <asm/mach/time.h>
#include <asm/arch/regs-watchdog.h> //专门定义smdk2410 watchdog 寄存器的header
file
#undef S3C24XX_VA_WATCHDOG
#define S3C24XX_VA_WATCHDOG s3c_wdt_base
static u32 heartbeat = 12; //自己定义的心跳值,这个value应该会有个上限
#define PCLK 50000000
#define PRESCALER 255
#define DIVISION_FACTOR 128 //最最大的
//#define COUNTER_VALUE heartbeat * ( 50000000 / (PRESCALER + 1) / DIVISION_FACTOR )
static unsigned long wdt_status;
#define WDT_IN_USE 0
static void __iomem *s3c_wdt_base;
static struct resource *s3c_wdt_mem;
/* 这个是一定要的,因为在regs-watchdog.h 里面都定义 ,把S3C24XX_VA_WATCHDOG 定义成
这里得到的实际的 s3c_wdt_base ,就可以实际控制硬件了。
#define S3C2410_WDOGREG(x) ((x) + S3C24XX_VA_WATCHDOG)
#define S3C2410_WTCON S3C2410_WDOGREG(0x00)
#define S3C2410_WTDAT S3C2410_WDOGREG(0x04)
#define S3C2410_WTCNT S3C2410_WDOGREG(0x08)
*/
//#define S3C2410_WTCON_RSTEN (0x01)
//#define S3C2410_WTCON_INTEN (1<<2)
//#define S3C2410_WTCON_ENABLE (1<<5)
struct resource *platform_get_resource(struct platform_device *dev, unsigned int
type,unsigned int num);
static void wdt_disable(void)
{
u32 tmp;
printk("in wdt_disable /n");
tmp = readl(S3C2410_WTCON); //only 低6bit是起作用的,其他的无用,所以用readl
printk("readl ok ,tmp=0x%04x/n",tmp);
writel(tmp & ~S3C2410_WTCON_ENABLE, S3C2410_WTCON);
}
static void wdt_enable(void)
{
u32 tmp; //255 //128
u16 COUNTER_VALUE = heartbeat * ( 50000000 / (PRESCALER + 1) / DIVISION_FACTOR );
wdt_disable();
printk("COUNTER_VALUE=%hu/n",COUNTER_VALUE);
printk("S3C2410_WTCNT address = %p/n",S3C2410_WTCNT);
//写入 S3C2410_WTDAT
writel(COUNTER_VALUE, S3C2410_WTDAT);
//写入S3C2410_WTCNT
writel(COUNTER_VALUE, S3C2410_WTCNT);
tmp = readl(S3C2410_WTCON);
tmp &= ~0xff00; //bit8~bit15 清零,然后写入PRESCALER
tmp |= (PRESCALER << 8);
printk("the value written S3C2410_WTCON=0x%04x/n",tmp);
writel(tmp, S3C2410_WTCON);
//重新eanble reset
tmp = readl(S3C2410_WTCON);
writel(tmp |S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN , S3C2410_WTCON);
}
static int s3c_wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(WDT_IN_USE, &wdt_status))
return -EBUSY;
wdt_enable();
}
static int s3c_wdt_release(struct inode *inode, struct file *file)
{
clear_bit(WDT_IN_USE, &wdt_status);
wdt_disable();
return 0;
}
//
//static struct watchdog_info ident = {
// .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE |
// WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
// .identity = "s3c Watchdog",
//};
static const struct file_operations s3c_wdt_fops =
{
.owner = THIS_MODULE,
// .write = s3c_wdt_write,
// .ioctl = s3c_wdt_ioctl,
.open = s3c_wdt_open,
.release = s3c_wdt_release,
};
static struct miscdevice s3c_wdt_miscdev =
{
.minor = WATCHDOG_MINOR, //130
.name = "watchdog_s3c_bob",
.fops = &s3c_wdt_fops,
};
//probe函数很简单, 就是
// 1>取得resource_wdt
// 2> request_mem_region
// 3> ioremap()
// 失败不要紧,关键要作好错误处理
static int s3c_wdt_probe(struct platform_device *pdev)
{
struct resource *res;
int ret = 0;
//#define IORESOURCE_MEM 0x00000200
/* get the memory region */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //根据watchdog的情况作特殊处理
if (res == NULL) {
printk("failed to get memory region resource/n");
return -ENOENT;
}
s3c_wdt_mem = request_mem_region(res->start, res->end-res->start+1,
pdev->name);
if (s3c_wdt_mem == NULL) {
printk("failed to reserve memory region/n");
ret = -ENOENT;
goto err_nores;
}
s3c_wdt_base = ioremap(res->start, res->end - res->start + 1);
if (s3c_wdt_base == NULL) {
printk("WDT s3c2410 failed ioremap()/n");
ret = -EINVAL;
goto err_nomap;
}
/* 可能要作一些s3c2410 WDT 的初始化的一些动作 */
/* 开始注册watchdog 相应的函数, 是作为misc设备进行注册 */
ret = misc_register(&s3c_wdt_miscdev);
if (ret == 0)
printk("S3C2410_bob Watchdog Timer: heartbeat %d sec/n", heartbeat);
else
printk("S3C2410_bob Watchdog Timer misc_register failure ,error=%d/n",ret);
return ret;
err_nomap:
release_resource(s3c_wdt_mem);
err_nores:
return ret;
}
static struct platform_driver s3c_wdtdrv = {
.probe = s3c_wdt_probe,
// .remove = s3c_wdt_remove,
// .suspend = s3c_wdt_suspend,
// .resume = s3c_wdt_resume,
.driver = {
.name = "s3c2410-wdt",
.owner = THIS_MODULE,
},
};
static char __initdata banner[] = "S3C2410a Watchdog, (c) 2007 BobZhang/n";
static int __init s3c_wdt_init(void)
{
printk(banner);
return platform_driver_register(&s3c_wdtdrv);
}
static void __exit s3c_wdt_exit(void)
{
platform_driver_unregister(&s3c_wdtdrv);
}
module_init(s3c_wdt_init);
module_exit(s3c_wdt_exit);
MODULE_DESCRIPTION("Samsung S3C Watchdog Driver , BobZhang write , only for
testing");
MODULE_AUTHOR("BobZhang
href="mailto:[email protected]>");">[email protected]>");
MODULE_LICENSE("GPL");
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
我是这样计算watchdog到底支持几秒的:
t_watchdog = 1/( PCLK / (Prescaler value + 1) / Division_factor )
其实 ,最后的 要写入寄存器的counter = ( PCLK / (Prescaler value + 1) / Division_factor ) * 用户要求的秒数Tmax
设用户想要设置的秒数为Tmax
则满足关系式:
Tmax * counter < 2^16
即: Tmax * ( PCLK / (Prescaler value + 1) / Division_factor ) <2^16
Tmax < 2^16 / ( PCLK / (Prescaler value + 1) / Division_factor )
即 Tmax < (2^16 * (Prescaler value + 1) * Division_factor) / PCLK
要想求出 Tmax的最大的值 , 即让 Prescaler value 和 Division_factor 取最大值即可。
而PCLK式常数 = 50000000
Tmax < (2^16 * (255 + 1) * 128) / 50000000
Tmax < 42.9
所以最终用户能设置的最大的值是42 秒, 而且 Prescaler 寄存器要设置成 255
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
找到原因了,
原来
static int s3c_wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(WDT_IN_USE, &wdt_status))
return -EBUSY;
wdt_enable();
return 0; //之前忘记 return 了。 ^_^。
}
static void wdt_enable(void)
{
u32 tmp; //255 //128
u16 COUNTER_VALUE = heartbeat * ( 50000000 / (PRESCALER + 1) / DIVISION_FACTOR );
//u16 COUNTER_VALUE = (1 << 16) -10;
printk("COUNTER_VALUE=%hu/n",COUNTER_VALUE);
printk("S3C2410_WTCNT address = %p/n",S3C2410_WTCNT);
writel(COUNTER_VALUE, S3C2410_WTDAT);
writel(COUNTER_VALUE, S3C2410_WTCNT);
tmp = readl(S3C2410_WTCON);
tmp &= ~0xff00; //bit8~bit15
tmp |= (PRESCALER << 8);
//这里是后加的,
tmp &= ~(0x03 << 3);
tmp |= 0x03 << 3; //11b is 128 division
printk("the value written S3C2410_WTCON=0x%04x/n",tmp);
writel(tmp, S3C2410_WTCON);
//ÖØÐÂeanble reset
tmp = readl(S3C2410_WTCON);
writel(tmp |S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN , S3C2410_WTCON);
//test,
tmp = readl(S3C2410_WTCNT);
printk("S3C2410_WTCNT value = %hu/n",tmp);
}
--------------------
watchdog的datasheet还是最简单的, 所以就安排新人 完全靠自己的理解, 不要看原有的c code 自己写, 只有这样才能真正自己写出一个driver来 。 但是这样安排还是要有个前期铺垫 , 之前仔细的领 新人 分析了 RTC的代码和datasheet ,其实RTC的datasheet 还是比Watchdog 复杂的, 是driver新手入门的好突破口。
正好自己也从来没有看过现有的watchdog的实现c 代码, 也自己写写看 , 其实也是挺锻炼人的 。 都说写driver 是天下文章一大抄 , 不过 终归还是 要培养逼着眼睛自己写的能力啊。
写driver ,看懂datasheet确实是很高的要求, 哪怕是最简单的datasheet 也要有第一次的仔细看 , 看明白了, 以后看别的datasheet 就彻底容易了。