一、本文总体结构
1.看门狗硬件结构及原理
2.“平台设备”与“字符设备”和“misc设备”关系及特点
3.平台设备框架学习及具体设备建立分析
4.看门狗驱动分析及源代码
5.调试
6.参考文献
1.看门狗硬件结构及原理
看门狗分为硬件和软件,2440内部集成一个硬件看门狗(一个定时器电路,可以作为一般TIMER),定时器输出链接到RESET,实现复位功能。当定时器计数值=0时,产生复位信号。
对看门的设置和操作是对其3个寄存器:
WTCON Watchdog timer control register 地址:0x53000000
WTCON Bit Description Initial State
Prescaler value 【15:8】 分为128 64等,需要设置
#define s3c2440_wdt_precaler(x) (x<<8)
#define s3c2440_wdt_precaler256 0xff
Interrupt generation [2] Enable or disable bit of the interrupt.
0 = Disable
1 = Enable
看门狗开启应该是先使能【2】为1,再开启【0】为1
#define S3C2440_WTCON_RSTEN (0x01)
#define S3C2410_WTCON_ENABLE (1<<5)
Reset enable/disable [0]
Enable or disable bit of Watchdog timer output for reset
signal.
1: Assert reset signal of the S3C2440A at watchdog timeout
0: Disable the reset function of the watchdog timer.
时钟选择:
Clock select [4:3] Determine the clock division factor.
00: 16 01 : 32
10: 64 11 : 128
#define S3C2410_WTCON_DIV16 (0<<3)
#define S3C2410_WTCON_DIV32 (1<<3)
#define S3C2410_WTCON_DIV64 (2<<3)
#define S3C2410_WTCON_DIV128 (3<<3)
所以一个看门狗的开启可以是下面:
#define rWTCON (*(volatile unsigned int *)0x53000000)
void s3c2440_wdt_start(void)
{
rWTCON=S3C2410_WTCON_DIV128 | S3C2410_WTCON_ENABLE | s3c2440_wdt_precaler(s3c2440_wdt_precaler256 );
rWTDAT=0X8000;是默认值
rWTCON |=S3C2440_WTCON_RSTEN;
}
看门狗还必须定时”喂狗“就是设置rWTCNT=0X8000;
但是在linux驱动中只能使用虚拟地址.
注:此部分参考ARM2440手册
2.“平台设备”与“字符设备”和“misc设备”关系及特点
平台设备是linux系统提供的一种附加手段,他实现物理、逻辑设备,和linux内核交互信息,只是设备的属性;
字符设备是面向上层用户空间,他对VFS提供接口是其本质;
misc设备是linux把一些字符设备放在一起;(为什么还不太清楚)
总的来说,他们各自相配合完成设备驱动。
平台设备:
分为驱动和设备
驱动是把设备注册到内核
设备是对其资源的抽象
3.平台设备框架学习及具体设备建立分析
在arch/arm/mach-s3c2440/devs.c对系统中所有设备的定义
static struct resource s3c_wdt_resource[] = {
[0] = {
.start = S3C24XX_PA_WATCHDOG,
.end = S3C24XX_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_WDT,
.end = IRQ_WDT,
.flags = IORESOURCE_IRQ,
}
};
struct platform_device s3c_device_wdt = {
.name = "s3c2440-wdt",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_wdt_resource),
.resource = s3c_wdt_resource,
};
在/arch/arm/mach-2440.c
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
&s3c_device_adc,
&s3c_device_eth,
&s3c_device_camif,
&s3c_device_sdi,
&s3c_device_buttons,
&s3c_device_rtc,
&s3c_device_ts,
};
这个是对系统所有设备的数据链,需要用下面的函数加入到系统,以至于我们可以在驱动中使用资源了。
void __init smdk_machine_init(void)
{
/* Configure the LEDs (even if we have no LED support)*/
s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP);
s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP);
s3c2410_gpio_cfgpin(S3C2410_GPF7, S3C2410_GPF7_OUTP);
s3c2410_gpio_setpin(S3C2410_GPF4, 1);
s3c2410_gpio_setpin(S3C2410_GPF5, 1);
s3c2410_gpio_setpin(S3C2410_GPF6, 1);
s3c2410_gpio_setpin(S3C2410_GPF7, 1);
s3c_device_nand.dev.platform_data = &smdk_nand_info;
platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));
s3c2410_pm_init();
}
4.看门狗驱动分析及源代码
初始化函数
static struct platform_driver s3c2410wdt_driver = {
.probe = s3c2410wdt_probe,
.remove = s3c2410wdt_remove,
.shutdown = s3c2410wdt_shutdown,
//.suspend = s3c2410wdt_suspend,
//.resume = s3c2410wdt_resume,
.driver = {
.owner = THIS_MODULE,
.name = "s3c2410-wdt",
},
};
static int __init watchdog_init(void)
{
printk(banner);
return platform_driver_register(&s3c2410wdt_driver);
}
他会调用s3c2410wdt_probe
static int s3c2410wdt_probe(struct platform_device *pdev)
{
struct resource *res;
int started = 0;
int ret;
int size;
/* get the memory region for the watchdog timer */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);这里和我们上面的资源联系
if (res == NULL) {
printk(KERN_INFO PFX "failed to get memory region resouce\n");
return -ENOENT;
}
size = (res->end-res->start)+1;
wdt_mem = request_mem_region(res->start, size, pdev->name);
if (wdt_mem == NULL) {
printk(KERN_INFO PFX "failed to get memory region\n");
return -ENOENT;
}
wdt_base = ioremap(res->start, size);内存-虚拟地址的转换
if (wdt_base == 0) {
printk(KERN_INFO PFX "failed to ioremap() region\n");
return -EINVAL;
}
DBG("probe: mapped wdt_base=%p\n", wdt_base);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
printk(KERN_INFO PFX "failed to get irq resource\n");
return -ENOENT;
}
ret = request_irq(res->start, s3c2410wdt_irq, 0, pdev->name, pdev);
if (ret != 0) {
printk(KERN_INFO PFX "failed to install irq (%d)\n", ret);
return ret;
}
wdt_clock = clk_get(&pdev->dev, "watchdog");
if (wdt_clock == NULL) {
printk(KERN_INFO PFX "failed to find watchdog clock source\n");
return -ENOENT;
}
clk_enable(wdt_clock);
/* see if we can actually set the requested timer margin, and if
* not, try the default value */
if (s3c2410wdt_set_heartbeat(tmr_margin)) {
started = s3c2410wdt_set_heartbeat(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
if (started == 0) {
printk(KERN_INFO PFX "tmr_margin value out of range, default %d used\n",
CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
} else {
printk(KERN_INFO PFX "default timer value is out of range, cannot start\n");
}
}
ret = misc_register(&s3c2410wdt_miscdev);
if (ret) {
printk (KERN_ERR PFX "cannot register miscdev on minor=%d (%d)\n",
WATCHDOG_MINOR, ret);
return ret;
}
if (tmr_atboot && started == 0) {
printk(KERN_INFO PFX "Starting Watchdog Timer\n");
s3c2410wdt_start();
} else if (!tmr_atboot) {
/* if we're not enabling the watchdog, then ensure it is
* disabled if it has been left running from the bootloader
* or other source
s3c2410wdt_stop();
}
return 0;
}
明天继续