1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/kernel.h> 4 5 #include <mach/regs-clock.h> //EXYNOS4_CLKGATE_IP_PERIR 6 #include <mach/map.h> //EXYNOS4_PA_WATCHDOG 7 #include <linux/ioport.h> //request_mem_region 8 #include <linux/io.h> //ioremap 9 #include <linux/interrupt.h> //request_irq 10 #include <mach/irqs.h> //EXYNOS4_IRQ_WDT 11 12 #include <linux/miscdevice.h> 13 #include <linux/fs.h> 14 15 #include "wdt.h" 16 17 //内核里已经有看门狗驱动,为了不冲突,必须关闭内核中的看门狗驱动 18 //make menuconfig -> Device Drivers -> S3C2410 Watchdog 关闭 19 20 #define DEVNAME "ldm" 21 22 #define CLKGATE_IP_PERIR (*(volatile u32*)EXYNOS4_CLKGATE_IP_PERIR) 23 24 enum { 25 PCLK = 100000000, //100MHZ 26 PRESCALER = 99, //1级分频为100分频 27 DIV_FACTOR = 0b11, //2级分频为128分频 28 WDT_HZ = PCLK / (PRESCALER + 1) / (16 << DIV_FACTOR), 29 }; 30 31 struct ldm_info { 32 struct miscdevice dev; 33 struct file_operations ops; 34 }; 35 static struct ldm_info ldm; 36 37 struct wdt_reg { 38 u32 con, dat, cnt, clrint; 39 }; 40 static struct wdt_reg *reg; 41 42 static long ldm_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) 43 { 44 switch (cmd) { 45 case WDT_ON: 46 if (arg) { 47 reg->con |= 1 << 5; 48 } else { 49 reg->con &= ~(1 << 5); 50 } 51 break; 52 case WDT_INTERVAL: 53 reg->dat = WDT_HZ * arg / 1000; 54 reg->cnt = WDT_HZ * arg / 1000; 55 break; 56 case WDT_RESET: 57 reg->con |= 1; 58 break; 59 case WDT_INTERRUPT: 60 reg->con &= ~1; 61 reg->con |= 1 << 2; 62 break; 63 default: 64 break; 65 } 66 67 return 0; 68 } 69 70 static irqreturn_t wdt_handler(int irqno, void *data) 71 { 72 printk("%s:%s, %d ", __FILE__, __FUNCTION__, __LINE__); 73 74 //清除中断标志 75 reg->clrint = 0x1234; 76 return IRQ_HANDLED; 77 } 78 79 static int test_init(void) 80 { 81 int ret = 0; 82 83 printk("%s:%s, %d ", __FILE__, __FUNCTION__, __LINE__); 84 85 ldm.dev.minor = MISC_DYNAMIC_MINOR; //次设备号 86 ldm.dev.name = DEVNAME; 87 ldm.dev.fops = &ldm.ops; 88 ldm.ops.unlocked_ioctl = ldm_ioctl; 89 90 ret = misc_register(&ldm.dev); 91 if (ret < 0) { 92 printk(KERN_ERR "misc_register failed "); 93 goto err_misc_register; 94 } 95 96 //根据4412手册P446表格,可知,wdt位于PERI_R总线,时钟源是100MHZ 97 98 //打开wdt所在总线的时钟使能开关,4412手册P487 99 CLKGATE_IP_PERIR |= 1 << 14; 100 101 //申请寄存器的访问范围,通过/proc/iomem 102 if (!request_mem_region(EXYNOS4_PA_WATCHDOG, sizeof(struct wdt_reg), DEVNAME)) { 103 ret = -EBUSY; 104 printk(KERN_ERR "request_mem_region failed "); 105 goto err_request_mem_region; 106 } 107 108 //映射寄存器,得到虚拟地址 109 reg = ioremap(EXYNOS4_PA_WATCHDOG, sizeof(struct wdt_reg)); 110 111 //申请中断 112 ret = request_irq(EXYNOS4_IRQ_WDT, wdt_handler, 0, DEVNAME, NULL); 113 if (ret < 0) { 114 printk(KERN_ERR "request_irq failed "); 115 goto err_request_irq; 116 } 117 118 reg->dat = WDT_HZ / 2; 119 reg->cnt = WDT_HZ / 2; 120 reg->con = PRESCALER << 8 | DIV_FACTOR << 3 | 1 << 2; 121 122 return 0; 123 124 err_request_irq: 125 iounmap(reg); 126 release_mem_region(EXYNOS4_PA_WATCHDOG, sizeof(struct wdt_reg)); 127 err_request_mem_region: 128 CLKGATE_IP_PERIR &= ~(1 << 14); 129 misc_deregister(&ldm.dev); 130 err_misc_register: 131 return ret; 132 } 133 134 static void test_exit(void) 135 { 136 printk("%s:%s, %d ", __FILE__, __FUNCTION__, __LINE__); 137 138 reg->con &= ~(1 << 5); 139 140 free_irq(EXYNOS4_IRQ_WDT, NULL); 141 iounmap(reg); 142 release_mem_region(EXYNOS4_PA_WATCHDOG, sizeof(struct wdt_reg)); 143 144 //关闭wdt所在总线的时钟使能开关 145 CLKGATE_IP_PERIR &= ~(1 << 14); 146 147 misc_deregister(&ldm.dev); 148 } 149 150 module_init(test_init); 151 module_exit(test_exit); 152 153 MODULE_LICENSE("GPL");
.h
1 #pragma once 2 3 #include <linux/ioctl.h> //实际头文件是linux/asm-generic/ioctl.h 4 5 //参考内核文档documentation/ioctl/ioctl-number.txt和ioctl-decoding.txt 6 7 #define WDT_ON _IOW('l', 1, int) 8 #define WDT_INTERVAL _IOW('l', 2, unsigned int) 9 #define WDT_RESET _IO('l', 3) 10 #define WDT_INTERRUPT _IO('l', 4)
.app
1 #include <stdio.h> 2 #include <fcntl.h> 3 #include <unistd.h> 4 #include <stdlib.h> 5 #include <string.h> 6 7 #include "wdt.h" 8 9 int main(int argc, char **argv) 10 { 11 if ((argc != 3) && (argc != 4)) { 12 fprintf(stderr, "Usage: cmd <device file> <request> [interval second] request: on off reset interrupt interval "); 13 return -1; 14 } 15 16 int fd = open(argv[1], O_RDWR); 17 if (fd < 0) { 18 perror("open device"); 19 goto err_open; 20 } 21 22 if (!strcmp(argv[2], "on")) { 23 ioctl(fd, WDT_ON, 1); 24 } else if (!strcmp(argv[2], "off")) { 25 ioctl(fd, WDT_ON, 0); 26 } else if (!strcmp(argv[2], "reset")) { 27 ioctl(fd, WDT_RESET); 28 } else if (!strcmp(argv[2], "interrupt")) { 29 ioctl(fd, WDT_INTERRUPT); 30 } else if (!strcmp(argv[2], "interval")) { 31 ioctl(fd, WDT_INTERVAL, atoi(argv[3])); 32 } else { 33 fprintf(stderr, "unknown request %s ", argv[2]); 34 } 35 36 close(fd); 37 38 return 0; 39 40 err_open: 41 return -1; 42 }