天天看點

Linux看門狗驅動程式設計(二) 代碼設計

先給出頭檔案、宏定義及變量定義

#include <linux/module.h>

#include <linux/moduleparam.h>

#include <linux/types.h>

#include <linux/timer.h>

#include <linux/miscdevice.h>

#include <linux/watchdog.h>

#include <linux/fs.h>

#include <linux/init.h>

#include <linux/platform_device.h>

#include <linux/interrupt.h>

#include <linux/clk.h>

#include <asm/uaccess.h>

#include <asm/io.h>

#include <asm/arch/map.h>

#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (10)

volatile unsigned long *wtcon, *wtdat, *wtcnt;

static struct resource *wdt_mem;

static struct clk *wdt_clock;

static void __iomem *wdt_base;

1.    定義并初始化看門狗的platfom_driver,編寫入口出口函數

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)

{

return platform_driver_register(&s3c2410wdt_driver); //注冊platform driver

}

static void __exit watchdog_exit(void)

{

platform_driver_unregister(&s3c2410wdt_driver); 登出platform driver

}

module_init(watchdog_init);

module_exit(watchdog_exit);

MODULE_LICENSE("GPL");    

注意platfom_driver的名字“s3c2410-wdt”要和platform_device的名字一緻。入口和出口函數隻需要注冊和登出platform_driver。

2.    定義并初始化看門狗雜項裝置和檔案接口

static const struct file_operations s3c2410wdt_fops = {

.owner = THIS_MODULE,

.write = s3c2410wdt_write,

.open = s3c2410wdt_open,

.release = s3c2410wdt_release,

};

static struct miscdevice s3c2410wdt_miscdev = {

.minor = WATCHDOG_MINOR,

.name = "watchdog",

.fops = &s3c2410wdt_fops,

};

3.    看門狗硬體操作函數:包含啟動和停止看門狗、喂狗、設定逾時時間,代碼如下:

static int s3c2410wdt_keepalive(void)

{

writel(wdt_count, wtcnt);

return 0;

}

static int s3c2410wdt_stop(void)

{

unsigned long val;

val = readl(wtcon);

val &= ~( (1<<5) | (1<<0) );

writel(val, wtcon);

return 0;

}

static int s3c2410wdt_start(void)

{

unsigned long val;

s3c2410wdt_stop();

val = readl(wtcon);

val |= (1<<5) | (3<<3);

val &= ~(1<<2);

val |= (0x01);

writel(wdt_count, wtdat);

writel(wdt_count, wtcnt);

writel(val, wtcon);

return 0;

}

static int s3c2410wdt_set_heartbeat(int timeout)

{

unsigned int freq = clk_get_rate(wdt_clock);

unsigned int count;

unsigned int divisor = 1;

unsigned long val;

if (timeout < 1)

return -EINVAL;

freq /= 128;

count = timeout * freq;

printk("count=%d, timeout=%d, freq=%d\n", count, timeout, freq);

if (count >= 0x10000) {

for (divisor = 1; divisor <= 0x100; divisor++) {

if ((count / divisor) < 0x10000)

break;

}

if ((count / divisor) >= 0x10000) {

printk("timeout %d too big\n", timeout);

return -EINVAL;

}

}

count /= divisor;

val = readl(wtcon);

val &= ~(0xff00);

val |= ((divisor-1) << 8);

writel(count, wtdat);

writel(val, wtcon);

return 0;

}

4.    probe函數實作

static int s3c2410wdt_probe(struct platform_device *pdev)

{

struct resource *res;

int started = 0;

int ret;

int size;

printk("find wdt\n");

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

if (res == NULL) {

               printk("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("failed to get memory region\n");

                ret = -ENOENT;

                goto err_req;

        }

wdt_base = ioremap(res->start, size);//實體位址映射到虛拟位址

if (wdt_base == NULL) {

                printk("failed to ioremap() region\n");

                ret = -EINVAL;

                goto err_req;

        }

wtcon = wdt_base;

wtdat = wtcon + 1;

wtcnt = wtdat + 1;

printk("probe: mapped wtcon=%p, wtdat=%p, wtcnt=%p \n", wtcon, wtdat, wtcnt);

wdt_clock = clk_get(&pdev->dev, "watchdog");

if (IS_ERR(wdt_clock)) {

printk("failed to find watchdog clock source\n");

ret = PTR_ERR(wdt_clock);

goto err_map;

}

clk_enable(wdt_clock);

started = s3c2410wdt_set_heartbeat(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);

if (started == 0) {

printk("tmr_margin value out of range, default %d used\n",

      CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);

else {

printk("default timer value is out of range, cannot start\n");

}

ret = misc_register(&s3c2410wdt_miscdev);

if (ret) {

printk ("cannot register miscdev on minor=%d (%d)\n",

WATCHDOG_MINOR, ret);

goto err_clk;

}

s3c2410wdt_stop();

printk("Stopping Watchdog Timer\n");

return 0;

 err_clk:

clk_disable(wdt_clock);

clk_put(wdt_clock);

 err_map:

iounmap(wdt_base);

 err_req:

release_resource(wdt_mem);

kfree(wdt_mem);

return ret;

}

probe函數中擷取platform_device提供的資源,并将實體記憶體映射到虛拟記憶體,設定看門狗3個寄存器的位址;

為了使看門狗工作,還需要擷取并使能看門狗時鐘,設定逾時時間;

為了實作看門狗的檔案接口,還需要将其注冊為一個混雜裝置;

最後為了避免上電後自動重新開機,我們先要将看門狗關閉。

5.    platform_driver其他成員函數代碼:

static int s3c2410wdt_remove(struct platform_device *dev)

{

release_resource(wdt_mem);

kfree(wdt_mem);

wdt_mem = NULL;

clk_disable(wdt_clock);

clk_put(wdt_clock);

wdt_clock = NULL;

iounmap(wdt_base);

misc_deregister(&s3c2410wdt_miscdev);

return 0;

}

static void s3c2410wdt_shutdown(struct platform_device *dev)

{

    s3c2410wdt_stop(); //停止看門狗

}

static unsigned long wtcon_save;

static unsigned long wtdat_save;

static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)

{

wtcon_save = readl(wtcon);

wtdat_save = readl(wtdat);

s3c2410wdt_stop();

return 0;

}

static int s3c2410wdt_resume(struct platform_device *dev)

{

writel(wtdat_save, wtdat);

writel(wtdat_save, wtcnt); 

writel(wtcon_save, wtcon);

printk("watchdog %sabled\n",

      (wtcon_save & (1<<5)) ? "en" : "dis");

return 0;

}

s3c2410wdt_remove函數釋放登出相關資源,s3c2410wdt_shutdown函數關閉看門狗,s3c2410wdt_suspend函數儲存看門狗寄存器值并停止看門狗,s3c2410wdt_resume恢複看門狗寄存器和狀态。

6.    看門狗檔案接口函數:

static int s3c2410wdt_open(struct inode *inode, struct file *file)

{

s3c2410wdt_start();

return 0;

}

static int s3c2410wdt_release(struct inode *inode, struct file *file)

{

s3c2410wdt_stop();

return 0;

}

static ssize_t s3c2410wdt_write(struct file *file, const char __user *data, size_t len, loff_t *ppos)

{

char c;

unsigned long cnt;

cnt = copy_from_user(&c, (void *)data, 1);

if(cnt != 0)

return -1;

if(c == '1')

s3c2410wdt_keepalive();

return 1;

}

open和release函數隻需要簡單的啟動和停止看門狗即可, write函數接收使用者寫的資料,若使用者寫字元'1',則喂狗,寫其他字元則什麼也不做。

繼續閱讀