eurotechwdt.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:11k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  * Eurotech CPU-1220/1410 on board WDT driver for Linux 2.4.x
  3.  *
  4.  * (c) Copyright 2001 Ascensit <support@ascensit.com>
  5.  * (c) Copyright 2001 Rodolfo Giometti <giometti@ascensit.com>
  6.  *
  7.  * Based on wdt.c.
  8.  * Original copyright messages:
  9.  *
  10.  *      (c) Copyright 1996-1997 Alan Cox <alan@redhat.com>, All Rights Reserved.
  11.  *                              http://www.redhat.com
  12.  *
  13.  *      This program is free software; you can redistribute it and/or
  14.  *      modify it under the terms of the GNU General Public License
  15.  *      as published by the Free Software Foundation; either version
  16.  *      2 of the License, or (at your option) any later version.
  17.  *
  18.  *      Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
  19.  *      warranty for any of this software. This material is provided
  20.  *      "AS-IS" and at no charge.
  21.  *
  22.  *      (c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>*
  23.  */
  24. #include <linux/config.h>
  25. #include <linux/module.h>
  26. #include <linux/version.h>
  27. #include <linux/types.h>
  28. #include <linux/errno.h>
  29. #include <linux/kernel.h>
  30. #include <linux/sched.h>
  31. #include <linux/smp_lock.h>
  32. #include <linux/miscdevice.h>
  33. #include <linux/watchdog.h>
  34. #include <linux/slab.h>
  35. #include <linux/ioport.h>
  36. #include <linux/fcntl.h>
  37. #include <asm/io.h>
  38. #include <asm/uaccess.h>
  39. #include <asm/system.h>
  40. #include <linux/notifier.h>
  41. #include <linux/reboot.h>
  42. #include <linux/init.h>
  43. #include <linux/spinlock.h>
  44. #include <linux/smp_lock.h>
  45. static int eurwdt_is_open;
  46. static int eurwdt_timeout; 
  47. static spinlock_t eurwdt_lock;
  48.  
  49. /*
  50.  *      You must set these - there is no sane way to probe for this board.
  51.  *      You can use wdt=x,y to set these now.
  52.  */
  53.  
  54. static int io = 0x3f0;
  55. static int irq = 10;
  56. static char *ev = "int";
  57.  
  58. #define WDT_TIMEOUT 60                /* 1 minute */
  59. /*
  60.  * Some symbolic names 
  61.  */
  62. #define WDT_CTRL_REG 0x30
  63. #define WDT_OUTPIN_CFG 0xe2
  64.    #define WDT_EVENT_INT    0x00
  65.    #define WDT_EVENT_REBOOT    0x08
  66. #define WDT_UNIT_SEL 0xf1
  67.    #define WDT_UNIT_SECS    0x80
  68. #define WDT_TIMEOUT_VAL 0xf2
  69. #define WDT_TIMER_CFG 0xf3
  70.  
  71. #ifndef MODULE
  72. /**
  73.  *      eurwdt_setup:
  74.  *      @str: command line string
  75.  *
  76.  *      Setup options. The board isn't really probe-able so we have to
  77.  *      get the user to tell us the configuration. Sane people build it
  78.  *      modular but the others come here.
  79.  */
  80.  
  81. static int __init eurwdt_setup(char *str)
  82. {
  83.    int ints[4];
  84.  
  85.    str = get_options (str, ARRAY_SIZE(ints), ints);
  86.  
  87.    if (ints[0] > 0) {
  88.       io = ints[1];
  89.       if (ints[0] > 1)
  90.          irq = ints[2];
  91.    }
  92.  
  93.    return 1;
  94. }
  95.  
  96. __setup("wdt=", eurwdt_setup);
  97. #endif /* !MODULE */
  98.  
  99. MODULE_PARM(io, "i");
  100. MODULE_PARM_DESC(io, "Eurotech WDT io port (default=0x3f0)");
  101. MODULE_PARM(irq, "i");
  102. MODULE_PARM_DESC(irq, "Eurotech WDT irq (default=10)");
  103. MODULE_PARM(ev, "s");
  104. MODULE_PARM_DESC(ev, "Eurotech WDT event type (default is `reboot')");
  105. /*
  106.  *      Programming support
  107.  */
  108. static inline void eurwdt_write_reg(u8 index, u8 data)
  109. {
  110.    outb(index, io);
  111.    outb(data, io+1);
  112. }
  113. static inline void eurwdt_lock_chip(void)
  114. {
  115.    outb(0xaa, io);
  116. }
  117. static inline void eurwdt_unlock_chip(void)
  118. {
  119.    outb(0x55, io);
  120.    eurwdt_write_reg(0x07, 0x08);   /* set the logical device */
  121. }
  122. static inline void eurwdt_set_timeout(int timeout)
  123. {
  124.    eurwdt_write_reg(WDT_TIMEOUT_VAL, (u8) timeout);
  125. }
  126. static inline void eurwdt_disable_timer(void)
  127. {
  128.    eurwdt_set_timeout(0);
  129. }
  130.  
  131. static void eurwdt_activate_timer(void)
  132. {
  133.    eurwdt_disable_timer();
  134.    eurwdt_write_reg(WDT_CTRL_REG, 0x01);      /* activate the WDT */
  135.    eurwdt_write_reg(WDT_OUTPIN_CFG, !strcmp("int", ev) ?
  136.                                     WDT_EVENT_INT : WDT_EVENT_REBOOT);
  137.    /* Setting interrupt line */
  138.    if (irq == 2 || irq > 15 || irq < 0) {
  139.       printk(KERN_ERR ": invalid irq numbern");
  140.       irq = 0;   /* if invalid we disable interrupt */
  141.    }
  142.    if (irq == 0)
  143.       printk(KERN_INFO ": interrupt disabledn");
  144.    eurwdt_write_reg(WDT_TIMER_CFG, irq<<4);
  145.    eurwdt_write_reg(WDT_UNIT_SEL, WDT_UNIT_SECS);   /* we use seconds */
  146.    eurwdt_set_timeout(0);                           /* the default timeout */ 
  147. }
  148. /*
  149.  *      Kernel methods.
  150.  */
  151.  
  152. void eurwdt_interrupt(int irq, void *dev_id, struct pt_regs *regs)
  153. {
  154.    printk(KERN_CRIT "timeout WDT timeoutn");
  155.  
  156. #ifdef ONLY_TESTING
  157.    printk(KERN_CRIT "Would Reboot.n");
  158. #else
  159.    printk(KERN_CRIT "Initiating system reboot.n");
  160.    machine_restart(NULL);
  161. #endif
  162. }
  163. /**
  164.  *      eurwdt_ping:
  165.  *
  166.  *      Reload counter one with the watchdog timeout.
  167.  */
  168.  
  169. static void eurwdt_ping(void)
  170. {
  171.    /* Write the watchdog default value */
  172.    eurwdt_set_timeout(eurwdt_timeout);
  173. }
  174.  
  175. /**
  176.  *      eurwdt_write:
  177.  *      @file: file handle to the watchdog
  178.  *      @buf: buffer to write (unused as data does not matter here
  179.  *      @count: count of bytes
  180.  *      @ppos: pointer to the position to write. No seeks allowed
  181.  *
  182.  *      A write to a watchdog device is defined as a keepalive signal. Any
  183.  *      write of data will do, as we we don't define content meaning.
  184.  */
  185.  
  186. static ssize_t eurwdt_write(struct file *file, const char *buf, size_t count,
  187. loff_t *ppos)
  188. {
  189.    /*  Can't seek (pwrite) on this device  */
  190.    if (ppos != &file->f_pos)
  191.       return -ESPIPE;
  192.  
  193.    if (count) {
  194.       eurwdt_ping();   /* the default timeout */
  195.       return 1;
  196.    }
  197.    return 0;
  198. }
  199. /**
  200.  *      eurwdt_ioctl:
  201.  *      @inode: inode of the device
  202.  *      @file: file handle to the device
  203.  *      @cmd: watchdog command
  204.  *      @arg: argument pointer
  205.  *
  206.  *      The watchdog API defines a common set of functions for all watchdogs
  207.  *      according to their available features.
  208.  */
  209.  
  210. static int eurwdt_ioctl(struct inode *inode, struct file *file,
  211.         unsigned int cmd, unsigned long arg)
  212. {
  213.    static struct watchdog_info ident = {
  214.       options : WDIOF_CARDRESET | WDIOF_SETTIMEOUT,
  215.       firmware_version : 1,
  216.       identity : "WDT Eurotech CPU-1220/1410"
  217.    };
  218.    int time;
  219.  
  220.    switch(cmd) {
  221.       default:
  222.          return -ENOTTY;
  223.       case WDIOC_GETSUPPORT:
  224.          return copy_to_user((struct watchdog_info *)arg, &ident,
  225.                sizeof(ident)) ? -EFAULT : 0;
  226.  
  227.       case WDIOC_GETBOOTSTATUS:
  228.          return put_user(0, (int *) arg);
  229.       case WDIOC_KEEPALIVE:
  230.          eurwdt_ping();
  231.          return 0;
  232.       case WDIOC_SETTIMEOUT:
  233.          if (copy_from_user(&time, (int *) arg, sizeof(int)))
  234.             return -EFAULT;
  235.          /* Sanity check */
  236.          if (time < 0 || time > 255)
  237.             return -EINVAL;
  238.          eurwdt_timeout = time; 
  239.          eurwdt_set_timeout(time); 
  240.  /* Fall */
  241.       case WDIOC_GETTIMEOUT:
  242.  return put_user(eurwdt_timeout, (int *)arg);
  243.    }
  244. }
  245. /**
  246.  *      eurwdt_open:
  247.  *      @inode: inode of device
  248.  *      @file: file handle to device
  249.  *
  250.  *      The misc device has been opened. The watchdog device is single
  251.  *      open and on opening we load the counter.
  252.  */
  253.  
  254. static int eurwdt_open(struct inode *inode, struct file *file)
  255. {
  256.    switch (MINOR(inode->i_rdev)) {
  257.       case WATCHDOG_MINOR:
  258.          spin_lock(&eurwdt_lock);
  259.          if (eurwdt_is_open) {
  260.             spin_unlock(&eurwdt_lock);
  261.             return -EBUSY;
  262.          }
  263.          eurwdt_is_open = 1;
  264.          eurwdt_timeout = WDT_TIMEOUT;   /* initial timeout */
  265.          /* Activate the WDT */
  266.          eurwdt_activate_timer(); 
  267.             
  268.          spin_unlock(&eurwdt_lock);
  269.          MOD_INC_USE_COUNT;
  270.          return 0;
  271.          case TEMP_MINOR:
  272.             return 0;
  273.          default:
  274.             return -ENODEV;
  275.    }
  276. }
  277.  
  278. /**
  279.  *      eurwdt_release:
  280.  *      @inode: inode to board
  281.  *      @file: file handle to board
  282.  *
  283.  *      The watchdog has a configurable API. There is a religious dispute
  284.  *      between people who want their watchdog to be able to shut down and
  285.  *      those who want to be sure if the watchdog manager dies the machine
  286.  *      reboots. In the former case we disable the counters, in the latter
  287.  *      case you have to open it again very soon.
  288.  */
  289.  
  290. static int eurwdt_release(struct inode *inode, struct file *file)
  291. {
  292.    if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) {
  293. #ifndef CONFIG_WATCHDOG_NOWAYOUT
  294.       eurwdt_disable_timer();
  295. #endif
  296.       eurwdt_is_open = 0;
  297.       MOD_DEC_USE_COUNT;
  298.    }
  299.    return 0;
  300. }
  301.  
  302. /**
  303.  *      eurwdt_notify_sys:
  304.  *      @this: our notifier block
  305.  *      @code: the event being reported
  306.  *      @unused: unused
  307.  *
  308.  *      Our notifier is called on system shutdowns. We want to turn the card
  309.  *      off at reboot otherwise the machine will reboot again during memory
  310.  *      test or worse yet during the following fsck. This would suck, in fact
  311.  *      trust me - if it happens it does suck.
  312.  */
  313.  
  314. static int eurwdt_notify_sys(struct notifier_block *this, unsigned long code,
  315.         void *unused)
  316. {
  317.    if (code == SYS_DOWN || code == SYS_HALT) {
  318.       /* Turn the card off */
  319.       eurwdt_disable_timer();
  320.    }
  321.    return NOTIFY_DONE;
  322. }
  323.  
  324. /*
  325.  *      Kernel Interfaces
  326.  */
  327.  
  328.  
  329. static struct file_operations eurwdt_fops = {
  330.         owner:          THIS_MODULE,
  331.         llseek:         no_llseek,
  332.         write:          eurwdt_write,
  333.         ioctl:          eurwdt_ioctl,
  334.         open:           eurwdt_open,
  335.         release:        eurwdt_release,
  336. };
  337. static struct miscdevice eurwdt_miscdev =
  338. {
  339.         WATCHDOG_MINOR,
  340.         "watchdog",
  341.         &eurwdt_fops
  342. };
  343.  
  344. /*
  345.  *      The WDT card needs to learn about soft shutdowns in order to
  346.  *      turn the timebomb registers off.
  347.  */
  348.  
  349. static struct notifier_block eurwdt_notifier =
  350. {
  351.         eurwdt_notify_sys,
  352.         NULL,
  353.         0
  354. };
  355.  
  356. /**
  357.  *      cleanup_module:
  358.  *
  359.  *      Unload the watchdog. You cannot do this with any file handles open.
  360.  *      If your watchdog is set to continue ticking on close and you unload
  361.  *      it, well it keeps ticking. We won't get the interrupt but the board
  362.  *      will not touch PC memory so all is fine. You just have to load a new
  363.  *      module in 60 seconds or reboot.
  364.  */
  365.  
  366. static void __exit eurwdt_exit(void)
  367. {
  368.    eurwdt_lock_chip();
  369.    misc_deregister(&eurwdt_miscdev);
  370.    unregister_reboot_notifier(&eurwdt_notifier);
  371.    release_region(io, 2);
  372.    free_irq(irq, NULL);
  373. }
  374.  
  375. /**
  376.  *      eurwdt_init:
  377.  *
  378.  *      Set up the WDT watchdog board. After grabbing the resources 
  379.  *      we require we need also to unlock the device.
  380.  *      The open() function will actually kick the board off.
  381.  */
  382.  
  383. static int __init eurwdt_init(void)
  384. {
  385.    int ret;
  386.  
  387.    ret = misc_register(&eurwdt_miscdev);
  388.    if (ret) {
  389.       printk(KERN_ERR "eurwdt: can't misc_register on minor=%dn",
  390.             WATCHDOG_MINOR);
  391.       goto out;
  392.    }
  393.    ret = request_irq(irq, eurwdt_interrupt, SA_INTERRUPT, "eurwdt", NULL);
  394.    if(ret) {
  395.       printk(KERN_ERR "eurwdt: IRQ %d is not free.n", irq);
  396.       goto outmisc;
  397.    }
  398.    if (!request_region(io, 2, "eurwdt")) {
  399.        printk(KERN_ERR "eurwdt: IO %X is not free.n", io);
  400.        ret = -EBUSY;
  401.        goto outirq;
  402.    }
  403.    ret = register_reboot_notifier(&eurwdt_notifier);
  404.    if (ret) {
  405.       printk(KERN_ERR "eurwdt: can't register reboot notifier (err=%d)n", ret);
  406.       goto outreg;
  407.    }
  408.    eurwdt_unlock_chip();
  409.  
  410.    ret = 0;
  411.    printk(KERN_INFO "Eurotech WDT driver 0.01 at %X (Interrupt %d)"
  412.                     " - timeout event: %sn", 
  413.          io, irq, (!strcmp("int", ev) ? "int" : "reboot"));
  414.    spin_lock_init(&eurwdt_lock);
  415.    out:
  416.       return ret;
  417.  
  418.    outreg:
  419.       release_region(io, 2);
  420.    outirq:
  421.       free_irq(irq, NULL);
  422.    outmisc:
  423.       misc_deregister(&eurwdt_miscdev);
  424.       goto out;
  425. }
  426.  
  427. module_init(eurwdt_init);
  428. module_exit(eurwdt_exit);
  429.  
  430. MODULE_AUTHOR("Rodolfo Giometti");
  431. MODULE_DESCRIPTION("Driver for Eurotech CPU-1220/1410 on board watchdog");
  432. MODULE_LICENSE("GPL");
  433. EXPORT_NO_SYMBOLS;