0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
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;
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
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
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");