Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * drivers/watchdog/m54xx_wdt.c
0003  *
0004  * Watchdog driver for ColdFire MCF547x & MCF548x processors
0005  * Copyright 2010 (c) Philippe De Muyter <phdm@macqel.be>
0006  *
0007  * Adapted from the IXP4xx watchdog driver, which carries these notices:
0008  *
0009  *  Author: Deepak Saxena <dsaxena@plexity.net>
0010  *
0011  *  Copyright 2004 (c) MontaVista, Software, Inc.
0012  *  Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
0013  *
0014  * This file is licensed under  the terms of the GNU General Public
0015  * License version 2. This program is licensed "as is" without any
0016  * warranty of any kind, whether express or implied.
0017  */
0018 
0019 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0020 
0021 #include <linux/module.h>
0022 #include <linux/moduleparam.h>
0023 #include <linux/types.h>
0024 #include <linux/kernel.h>
0025 #include <linux/fs.h>
0026 #include <linux/miscdevice.h>
0027 #include <linux/watchdog.h>
0028 #include <linux/init.h>
0029 #include <linux/bitops.h>
0030 #include <linux/ioport.h>
0031 #include <linux/uaccess.h>
0032 #include <linux/io.h>
0033 
0034 #include <asm/coldfire.h>
0035 #include <asm/m54xxsim.h>
0036 #include <asm/m54xxgpt.h>
0037 
0038 static bool nowayout = WATCHDOG_NOWAYOUT;
0039 static unsigned int heartbeat = 30; /* (secs) Default is 0.5 minute */
0040 static unsigned long wdt_status;
0041 
0042 #define WDT_IN_USE      0
0043 #define WDT_OK_TO_CLOSE     1
0044 
0045 static void wdt_enable(void)
0046 {
0047     unsigned int gms0;
0048 
0049     /* preserve GPIO usage, if any */
0050     gms0 = __raw_readl(MCF_GPT_GMS0);
0051     if (gms0 & MCF_GPT_GMS_TMS_GPIO)
0052         gms0 &= (MCF_GPT_GMS_TMS_GPIO | MCF_GPT_GMS_GPIO_MASK
0053                             | MCF_GPT_GMS_OD);
0054     else
0055         gms0 = MCF_GPT_GMS_TMS_GPIO | MCF_GPT_GMS_OD;
0056     __raw_writel(gms0, MCF_GPT_GMS0);
0057     __raw_writel(MCF_GPT_GCIR_PRE(heartbeat*(MCF_BUSCLK/0xffff)) |
0058             MCF_GPT_GCIR_CNT(0xffff), MCF_GPT_GCIR0);
0059     gms0 |= MCF_GPT_GMS_OCPW(0xA5) | MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE;
0060     __raw_writel(gms0, MCF_GPT_GMS0);
0061 }
0062 
0063 static void wdt_disable(void)
0064 {
0065     unsigned int gms0;
0066 
0067     /* disable watchdog */
0068     gms0 = __raw_readl(MCF_GPT_GMS0);
0069     gms0 &= ~(MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE);
0070     __raw_writel(gms0, MCF_GPT_GMS0);
0071 }
0072 
0073 static void wdt_keepalive(void)
0074 {
0075     unsigned int gms0;
0076 
0077     gms0 = __raw_readl(MCF_GPT_GMS0);
0078     gms0 |= MCF_GPT_GMS_OCPW(0xA5);
0079     __raw_writel(gms0, MCF_GPT_GMS0);
0080 }
0081 
0082 static int m54xx_wdt_open(struct inode *inode, struct file *file)
0083 {
0084     if (test_and_set_bit(WDT_IN_USE, &wdt_status))
0085         return -EBUSY;
0086 
0087     clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
0088     wdt_enable();
0089     return stream_open(inode, file);
0090 }
0091 
0092 static ssize_t m54xx_wdt_write(struct file *file, const char *data,
0093                         size_t len, loff_t *ppos)
0094 {
0095     if (len) {
0096         if (!nowayout) {
0097             size_t i;
0098 
0099             clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
0100 
0101             for (i = 0; i != len; i++) {
0102                 char c;
0103 
0104                 if (get_user(c, data + i))
0105                     return -EFAULT;
0106                 if (c == 'V')
0107                     set_bit(WDT_OK_TO_CLOSE, &wdt_status);
0108             }
0109         }
0110         wdt_keepalive();
0111     }
0112     return len;
0113 }
0114 
0115 static const struct watchdog_info ident = {
0116     .options    = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
0117                 WDIOF_KEEPALIVEPING,
0118     .identity   = "Coldfire M54xx Watchdog",
0119 };
0120 
0121 static long m54xx_wdt_ioctl(struct file *file, unsigned int cmd,
0122                              unsigned long arg)
0123 {
0124     int ret = -ENOTTY;
0125     int time;
0126 
0127     switch (cmd) {
0128     case WDIOC_GETSUPPORT:
0129         ret = copy_to_user((struct watchdog_info *)arg, &ident,
0130                    sizeof(ident)) ? -EFAULT : 0;
0131         break;
0132 
0133     case WDIOC_GETSTATUS:
0134         ret = put_user(0, (int *)arg);
0135         break;
0136 
0137     case WDIOC_GETBOOTSTATUS:
0138         ret = put_user(0, (int *)arg);
0139         break;
0140 
0141     case WDIOC_KEEPALIVE:
0142         wdt_keepalive();
0143         ret = 0;
0144         break;
0145 
0146     case WDIOC_SETTIMEOUT:
0147         ret = get_user(time, (int *)arg);
0148         if (ret)
0149             break;
0150 
0151         if (time <= 0 || time > 30) {
0152             ret = -EINVAL;
0153             break;
0154         }
0155 
0156         heartbeat = time;
0157         wdt_enable();
0158         fallthrough;
0159 
0160     case WDIOC_GETTIMEOUT:
0161         ret = put_user(heartbeat, (int *)arg);
0162         break;
0163     }
0164     return ret;
0165 }
0166 
0167 static int m54xx_wdt_release(struct inode *inode, struct file *file)
0168 {
0169     if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
0170         wdt_disable();
0171     else {
0172         pr_crit("Device closed unexpectedly - timer will not stop\n");
0173         wdt_keepalive();
0174     }
0175     clear_bit(WDT_IN_USE, &wdt_status);
0176     clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
0177 
0178     return 0;
0179 }
0180 
0181 
0182 static const struct file_operations m54xx_wdt_fops = {
0183     .owner      = THIS_MODULE,
0184     .llseek     = no_llseek,
0185     .write      = m54xx_wdt_write,
0186     .unlocked_ioctl = m54xx_wdt_ioctl,
0187     .compat_ioctl   = compat_ptr_ioctl,
0188     .open       = m54xx_wdt_open,
0189     .release    = m54xx_wdt_release,
0190 };
0191 
0192 static struct miscdevice m54xx_wdt_miscdev = {
0193     .minor      = WATCHDOG_MINOR,
0194     .name       = "watchdog",
0195     .fops       = &m54xx_wdt_fops,
0196 };
0197 
0198 static int __init m54xx_wdt_init(void)
0199 {
0200     if (!request_mem_region(MCF_GPT_GCIR0, 4, "Coldfire M54xx Watchdog")) {
0201         pr_warn("I/O region busy\n");
0202         return -EBUSY;
0203     }
0204     pr_info("driver is loaded\n");
0205 
0206     return misc_register(&m54xx_wdt_miscdev);
0207 }
0208 
0209 static void __exit m54xx_wdt_exit(void)
0210 {
0211     misc_deregister(&m54xx_wdt_miscdev);
0212     release_mem_region(MCF_GPT_GCIR0, 4);
0213 }
0214 
0215 module_init(m54xx_wdt_init);
0216 module_exit(m54xx_wdt_exit);
0217 
0218 MODULE_AUTHOR("Philippe De Muyter <phdm@macqel.be>");
0219 MODULE_DESCRIPTION("Coldfire M54xx Watchdog");
0220 
0221 module_param(heartbeat, int, 0);
0222 MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 30s)");
0223 
0224 module_param(nowayout, bool, 0);
0225 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
0226 
0227 MODULE_LICENSE("GPL");