root/drivers/watchdog/sa1100_wdt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sa1100dog_open
  2. sa1100dog_release
  3. sa1100dog_write
  4. sa1100dog_ioctl
  5. sa1100dog_init
  6. sa1100dog_exit

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  *      Watchdog driver for the SA11x0/PXA2xx
   4  *
   5  *      (c) Copyright 2000 Oleg Drokin <green@crimea.edu>
   6  *          Based on SoftDog driver by Alan Cox <alan@lxorguk.ukuu.org.uk>
   7  *
   8  *      Neither Oleg Drokin nor iXcelerator.com admit liability nor provide
   9  *      warranty for any of this software. This material is provided
  10  *      "AS-IS" and at no charge.
  11  *
  12  *      (c) Copyright 2000           Oleg Drokin <green@crimea.edu>
  13  *
  14  *      27/11/2000 Initial release
  15  */
  16 
  17 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  18 
  19 #include <linux/module.h>
  20 #include <linux/moduleparam.h>
  21 #include <linux/clk.h>
  22 #include <linux/types.h>
  23 #include <linux/kernel.h>
  24 #include <linux/fs.h>
  25 #include <linux/miscdevice.h>
  26 #include <linux/watchdog.h>
  27 #include <linux/init.h>
  28 #include <linux/io.h>
  29 #include <linux/bitops.h>
  30 #include <linux/uaccess.h>
  31 #include <linux/timex.h>
  32 
  33 #ifdef CONFIG_ARCH_PXA
  34 #include <mach/regs-ost.h>
  35 #endif
  36 
  37 #include <mach/reset.h>
  38 #include <mach/hardware.h>
  39 
  40 static unsigned long oscr_freq;
  41 static unsigned long sa1100wdt_users;
  42 static unsigned int pre_margin;
  43 static int boot_status;
  44 
  45 /*
  46  *      Allow only one person to hold it open
  47  */
  48 static int sa1100dog_open(struct inode *inode, struct file *file)
  49 {
  50         if (test_and_set_bit(1, &sa1100wdt_users))
  51                 return -EBUSY;
  52 
  53         /* Activate SA1100 Watchdog timer */
  54         writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3);
  55         writel_relaxed(OSSR_M3, OSSR);
  56         writel_relaxed(OWER_WME, OWER);
  57         writel_relaxed(readl_relaxed(OIER) | OIER_E3, OIER);
  58         return stream_open(inode, file);
  59 }
  60 
  61 /*
  62  * The watchdog cannot be disabled.
  63  *
  64  * Previous comments suggested that turning off the interrupt by
  65  * clearing OIER[E3] would prevent the watchdog timing out but this
  66  * does not appear to be true (at least on the PXA255).
  67  */
  68 static int sa1100dog_release(struct inode *inode, struct file *file)
  69 {
  70         pr_crit("Device closed - timer will not stop\n");
  71         clear_bit(1, &sa1100wdt_users);
  72         return 0;
  73 }
  74 
  75 static ssize_t sa1100dog_write(struct file *file, const char __user *data,
  76                                                 size_t len, loff_t *ppos)
  77 {
  78         if (len)
  79                 /* Refresh OSMR3 timer. */
  80                 writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3);
  81         return len;
  82 }
  83 
  84 static const struct watchdog_info ident = {
  85         .options        = WDIOF_CARDRESET | WDIOF_SETTIMEOUT
  86                                 | WDIOF_KEEPALIVEPING,
  87         .identity       = "SA1100/PXA255 Watchdog",
  88         .firmware_version       = 1,
  89 };
  90 
  91 static long sa1100dog_ioctl(struct file *file, unsigned int cmd,
  92                                                         unsigned long arg)
  93 {
  94         int ret = -ENOTTY;
  95         int time;
  96         void __user *argp = (void __user *)arg;
  97         int __user *p = argp;
  98 
  99         switch (cmd) {
 100         case WDIOC_GETSUPPORT:
 101                 ret = copy_to_user(argp, &ident,
 102                                    sizeof(ident)) ? -EFAULT : 0;
 103                 break;
 104 
 105         case WDIOC_GETSTATUS:
 106                 ret = put_user(0, p);
 107                 break;
 108 
 109         case WDIOC_GETBOOTSTATUS:
 110                 ret = put_user(boot_status, p);
 111                 break;
 112 
 113         case WDIOC_KEEPALIVE:
 114                 writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3);
 115                 ret = 0;
 116                 break;
 117 
 118         case WDIOC_SETTIMEOUT:
 119                 ret = get_user(time, p);
 120                 if (ret)
 121                         break;
 122 
 123                 if (time <= 0 || (oscr_freq * (long long)time >= 0xffffffff)) {
 124                         ret = -EINVAL;
 125                         break;
 126                 }
 127 
 128                 pre_margin = oscr_freq * time;
 129                 writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3);
 130                 /*fall through*/
 131 
 132         case WDIOC_GETTIMEOUT:
 133                 ret = put_user(pre_margin / oscr_freq, p);
 134                 break;
 135         }
 136         return ret;
 137 }
 138 
 139 static const struct file_operations sa1100dog_fops = {
 140         .owner          = THIS_MODULE,
 141         .llseek         = no_llseek,
 142         .write          = sa1100dog_write,
 143         .unlocked_ioctl = sa1100dog_ioctl,
 144         .open           = sa1100dog_open,
 145         .release        = sa1100dog_release,
 146 };
 147 
 148 static struct miscdevice sa1100dog_miscdev = {
 149         .minor          = WATCHDOG_MINOR,
 150         .name           = "watchdog",
 151         .fops           = &sa1100dog_fops,
 152 };
 153 
 154 static int margin __initdata = 60;              /* (secs) Default is 1 minute */
 155 static struct clk *clk;
 156 
 157 static int __init sa1100dog_init(void)
 158 {
 159         int ret;
 160 
 161         clk = clk_get(NULL, "OSTIMER0");
 162         if (IS_ERR(clk)) {
 163                 pr_err("SA1100/PXA2xx Watchdog Timer: clock not found: %d\n",
 164                        (int) PTR_ERR(clk));
 165                 return PTR_ERR(clk);
 166         }
 167 
 168         ret = clk_prepare_enable(clk);
 169         if (ret) {
 170                 pr_err("SA1100/PXA2xx Watchdog Timer: clock failed to prepare+enable: %d\n",
 171                        ret);
 172                 goto err;
 173         }
 174 
 175         oscr_freq = clk_get_rate(clk);
 176 
 177         /*
 178          * Read the reset status, and save it for later.  If
 179          * we suspend, RCSR will be cleared, and the watchdog
 180          * reset reason will be lost.
 181          */
 182         boot_status = (reset_status & RESET_STATUS_WATCHDOG) ?
 183                                 WDIOF_CARDRESET : 0;
 184         pre_margin = oscr_freq * margin;
 185 
 186         ret = misc_register(&sa1100dog_miscdev);
 187         if (ret == 0) {
 188                 pr_info("SA1100/PXA2xx Watchdog Timer: timer margin %d sec\n",
 189                         margin);
 190                 return 0;
 191         }
 192 
 193         clk_disable_unprepare(clk);
 194 err:
 195         clk_put(clk);
 196         return ret;
 197 }
 198 
 199 static void __exit sa1100dog_exit(void)
 200 {
 201         misc_deregister(&sa1100dog_miscdev);
 202         clk_disable_unprepare(clk);
 203         clk_put(clk);
 204 }
 205 
 206 module_init(sa1100dog_init);
 207 module_exit(sa1100dog_exit);
 208 
 209 MODULE_AUTHOR("Oleg Drokin <green@crimea.edu>");
 210 MODULE_DESCRIPTION("SA1100/PXA2xx Watchdog");
 211 
 212 module_param(margin, int, 0);
 213 MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)");
 214 
 215 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */