188cce427SPhilippe De Muyter /* 29b9c63ffSPhilippe De Muyter * drivers/watchdog/m54xx_wdt.c 388cce427SPhilippe De Muyter * 49b9c63ffSPhilippe De Muyter * Watchdog driver for ColdFire MCF547x & MCF548x processors 588cce427SPhilippe De Muyter * Copyright 2010 (c) Philippe De Muyter <phdm@macqel.be> 688cce427SPhilippe De Muyter * 788cce427SPhilippe De Muyter * Adapted from the IXP4xx watchdog driver, which carries these notices: 888cce427SPhilippe De Muyter * 988cce427SPhilippe De Muyter * Author: Deepak Saxena <dsaxena@plexity.net> 1088cce427SPhilippe De Muyter * 1188cce427SPhilippe De Muyter * Copyright 2004 (c) MontaVista, Software, Inc. 1288cce427SPhilippe De Muyter * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu> 1388cce427SPhilippe De Muyter * 1488cce427SPhilippe De Muyter * This file is licensed under the terms of the GNU General Public 1588cce427SPhilippe De Muyter * License version 2. This program is licensed "as is" without any 1688cce427SPhilippe De Muyter * warranty of any kind, whether express or implied. 1788cce427SPhilippe De Muyter */ 1888cce427SPhilippe De Muyter 1927c766aaSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 2027c766aaSJoe Perches 2188cce427SPhilippe De Muyter #include <linux/module.h> 2288cce427SPhilippe De Muyter #include <linux/moduleparam.h> 2388cce427SPhilippe De Muyter #include <linux/types.h> 2488cce427SPhilippe De Muyter #include <linux/kernel.h> 2588cce427SPhilippe De Muyter #include <linux/fs.h> 2688cce427SPhilippe De Muyter #include <linux/miscdevice.h> 2788cce427SPhilippe De Muyter #include <linux/watchdog.h> 2888cce427SPhilippe De Muyter #include <linux/init.h> 2988cce427SPhilippe De Muyter #include <linux/bitops.h> 3088cce427SPhilippe De Muyter #include <linux/ioport.h> 3188cce427SPhilippe De Muyter #include <linux/uaccess.h> 32072cb8b6SThomas Gleixner #include <linux/io.h> 3388cce427SPhilippe De Muyter 3488cce427SPhilippe De Muyter #include <asm/coldfire.h> 359b9c63ffSPhilippe De Muyter #include <asm/m54xxsim.h> 369b9c63ffSPhilippe De Muyter #include <asm/m54xxgpt.h> 3788cce427SPhilippe De Muyter 3886a1e189SWim Van Sebroeck static bool nowayout = WATCHDOG_NOWAYOUT; 3988cce427SPhilippe De Muyter static unsigned int heartbeat = 30; /* (secs) Default is 0.5 minute */ 4088cce427SPhilippe De Muyter static unsigned long wdt_status; 4188cce427SPhilippe De Muyter 4288cce427SPhilippe De Muyter #define WDT_IN_USE 0 4388cce427SPhilippe De Muyter #define WDT_OK_TO_CLOSE 1 4488cce427SPhilippe De Muyter 4588cce427SPhilippe De Muyter static void wdt_enable(void) 4688cce427SPhilippe De Muyter { 4788cce427SPhilippe De Muyter unsigned int gms0; 4888cce427SPhilippe De Muyter 4988cce427SPhilippe De Muyter /* preserve GPIO usage, if any */ 50944c3d81SGreg Ungerer gms0 = __raw_readl(MCF_GPT_GMS0); 5188cce427SPhilippe De Muyter if (gms0 & MCF_GPT_GMS_TMS_GPIO) 5288cce427SPhilippe De Muyter gms0 &= (MCF_GPT_GMS_TMS_GPIO | MCF_GPT_GMS_GPIO_MASK 5388cce427SPhilippe De Muyter | MCF_GPT_GMS_OD); 5488cce427SPhilippe De Muyter else 5588cce427SPhilippe De Muyter gms0 = MCF_GPT_GMS_TMS_GPIO | MCF_GPT_GMS_OD; 56944c3d81SGreg Ungerer __raw_writel(gms0, MCF_GPT_GMS0); 5788cce427SPhilippe De Muyter __raw_writel(MCF_GPT_GCIR_PRE(heartbeat*(MCF_BUSCLK/0xffff)) | 58944c3d81SGreg Ungerer MCF_GPT_GCIR_CNT(0xffff), MCF_GPT_GCIR0); 5988cce427SPhilippe De Muyter gms0 |= MCF_GPT_GMS_OCPW(0xA5) | MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE; 60944c3d81SGreg Ungerer __raw_writel(gms0, MCF_GPT_GMS0); 6188cce427SPhilippe De Muyter } 6288cce427SPhilippe De Muyter 6388cce427SPhilippe De Muyter static void wdt_disable(void) 6488cce427SPhilippe De Muyter { 6588cce427SPhilippe De Muyter unsigned int gms0; 6688cce427SPhilippe De Muyter 6788cce427SPhilippe De Muyter /* disable watchdog */ 68944c3d81SGreg Ungerer gms0 = __raw_readl(MCF_GPT_GMS0); 6988cce427SPhilippe De Muyter gms0 &= ~(MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE); 70944c3d81SGreg Ungerer __raw_writel(gms0, MCF_GPT_GMS0); 7188cce427SPhilippe De Muyter } 7288cce427SPhilippe De Muyter 7388cce427SPhilippe De Muyter static void wdt_keepalive(void) 7488cce427SPhilippe De Muyter { 7588cce427SPhilippe De Muyter unsigned int gms0; 7688cce427SPhilippe De Muyter 77944c3d81SGreg Ungerer gms0 = __raw_readl(MCF_GPT_GMS0); 7888cce427SPhilippe De Muyter gms0 |= MCF_GPT_GMS_OCPW(0xA5); 79944c3d81SGreg Ungerer __raw_writel(gms0, MCF_GPT_GMS0); 8088cce427SPhilippe De Muyter } 8188cce427SPhilippe De Muyter 829b9c63ffSPhilippe De Muyter static int m54xx_wdt_open(struct inode *inode, struct file *file) 8388cce427SPhilippe De Muyter { 8488cce427SPhilippe De Muyter if (test_and_set_bit(WDT_IN_USE, &wdt_status)) 8588cce427SPhilippe De Muyter return -EBUSY; 8688cce427SPhilippe De Muyter 8788cce427SPhilippe De Muyter clear_bit(WDT_OK_TO_CLOSE, &wdt_status); 8888cce427SPhilippe De Muyter wdt_enable(); 89c5bf68feSKirill Smelkov return stream_open(inode, file); 9088cce427SPhilippe De Muyter } 9188cce427SPhilippe De Muyter 929b9c63ffSPhilippe De Muyter static ssize_t m54xx_wdt_write(struct file *file, const char *data, 9388cce427SPhilippe De Muyter size_t len, loff_t *ppos) 9488cce427SPhilippe De Muyter { 9588cce427SPhilippe De Muyter if (len) { 9688cce427SPhilippe De Muyter if (!nowayout) { 9788cce427SPhilippe De Muyter size_t i; 9888cce427SPhilippe De Muyter 9988cce427SPhilippe De Muyter clear_bit(WDT_OK_TO_CLOSE, &wdt_status); 10088cce427SPhilippe De Muyter 10188cce427SPhilippe De Muyter for (i = 0; i != len; i++) { 10288cce427SPhilippe De Muyter char c; 10388cce427SPhilippe De Muyter 10488cce427SPhilippe De Muyter if (get_user(c, data + i)) 10588cce427SPhilippe De Muyter return -EFAULT; 10688cce427SPhilippe De Muyter if (c == 'V') 10788cce427SPhilippe De Muyter set_bit(WDT_OK_TO_CLOSE, &wdt_status); 10888cce427SPhilippe De Muyter } 10988cce427SPhilippe De Muyter } 11088cce427SPhilippe De Muyter wdt_keepalive(); 11188cce427SPhilippe De Muyter } 11288cce427SPhilippe De Muyter return len; 11388cce427SPhilippe De Muyter } 11488cce427SPhilippe De Muyter 11588cce427SPhilippe De Muyter static const struct watchdog_info ident = { 11688cce427SPhilippe De Muyter .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | 11788cce427SPhilippe De Muyter WDIOF_KEEPALIVEPING, 1189b9c63ffSPhilippe De Muyter .identity = "Coldfire M54xx Watchdog", 11988cce427SPhilippe De Muyter }; 12088cce427SPhilippe De Muyter 1219b9c63ffSPhilippe De Muyter static long m54xx_wdt_ioctl(struct file *file, unsigned int cmd, 12288cce427SPhilippe De Muyter unsigned long arg) 12388cce427SPhilippe De Muyter { 12488cce427SPhilippe De Muyter int ret = -ENOTTY; 12588cce427SPhilippe De Muyter int time; 12688cce427SPhilippe De Muyter 12788cce427SPhilippe De Muyter switch (cmd) { 12888cce427SPhilippe De Muyter case WDIOC_GETSUPPORT: 12988cce427SPhilippe De Muyter ret = copy_to_user((struct watchdog_info *)arg, &ident, 13088cce427SPhilippe De Muyter sizeof(ident)) ? -EFAULT : 0; 13188cce427SPhilippe De Muyter break; 13288cce427SPhilippe De Muyter 13388cce427SPhilippe De Muyter case WDIOC_GETSTATUS: 13488cce427SPhilippe De Muyter ret = put_user(0, (int *)arg); 13588cce427SPhilippe De Muyter break; 13688cce427SPhilippe De Muyter 13788cce427SPhilippe De Muyter case WDIOC_GETBOOTSTATUS: 13888cce427SPhilippe De Muyter ret = put_user(0, (int *)arg); 13988cce427SPhilippe De Muyter break; 14088cce427SPhilippe De Muyter 14188cce427SPhilippe De Muyter case WDIOC_KEEPALIVE: 14288cce427SPhilippe De Muyter wdt_keepalive(); 14388cce427SPhilippe De Muyter ret = 0; 14488cce427SPhilippe De Muyter break; 14588cce427SPhilippe De Muyter 14688cce427SPhilippe De Muyter case WDIOC_SETTIMEOUT: 14788cce427SPhilippe De Muyter ret = get_user(time, (int *)arg); 14888cce427SPhilippe De Muyter if (ret) 14988cce427SPhilippe De Muyter break; 15088cce427SPhilippe De Muyter 15188cce427SPhilippe De Muyter if (time <= 0 || time > 30) { 15288cce427SPhilippe De Muyter ret = -EINVAL; 15388cce427SPhilippe De Muyter break; 15488cce427SPhilippe De Muyter } 15588cce427SPhilippe De Muyter 15688cce427SPhilippe De Muyter heartbeat = time; 15788cce427SPhilippe De Muyter wdt_enable(); 158*bd490f82SGustavo A. R. Silva fallthrough; 15988cce427SPhilippe De Muyter 16088cce427SPhilippe De Muyter case WDIOC_GETTIMEOUT: 16188cce427SPhilippe De Muyter ret = put_user(heartbeat, (int *)arg); 16288cce427SPhilippe De Muyter break; 16388cce427SPhilippe De Muyter } 16488cce427SPhilippe De Muyter return ret; 16588cce427SPhilippe De Muyter } 16688cce427SPhilippe De Muyter 1679b9c63ffSPhilippe De Muyter static int m54xx_wdt_release(struct inode *inode, struct file *file) 16888cce427SPhilippe De Muyter { 16988cce427SPhilippe De Muyter if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) 17088cce427SPhilippe De Muyter wdt_disable(); 17188cce427SPhilippe De Muyter else { 17227c766aaSJoe Perches pr_crit("Device closed unexpectedly - timer will not stop\n"); 17388cce427SPhilippe De Muyter wdt_keepalive(); 17488cce427SPhilippe De Muyter } 17588cce427SPhilippe De Muyter clear_bit(WDT_IN_USE, &wdt_status); 17688cce427SPhilippe De Muyter clear_bit(WDT_OK_TO_CLOSE, &wdt_status); 17788cce427SPhilippe De Muyter 17888cce427SPhilippe De Muyter return 0; 17988cce427SPhilippe De Muyter } 18088cce427SPhilippe De Muyter 18188cce427SPhilippe De Muyter 1829b9c63ffSPhilippe De Muyter static const struct file_operations m54xx_wdt_fops = { 18388cce427SPhilippe De Muyter .owner = THIS_MODULE, 18488cce427SPhilippe De Muyter .llseek = no_llseek, 1859b9c63ffSPhilippe De Muyter .write = m54xx_wdt_write, 1869b9c63ffSPhilippe De Muyter .unlocked_ioctl = m54xx_wdt_ioctl, 187b6dfb247SArnd Bergmann .compat_ioctl = compat_ptr_ioctl, 1889b9c63ffSPhilippe De Muyter .open = m54xx_wdt_open, 1899b9c63ffSPhilippe De Muyter .release = m54xx_wdt_release, 19088cce427SPhilippe De Muyter }; 19188cce427SPhilippe De Muyter 1929b9c63ffSPhilippe De Muyter static struct miscdevice m54xx_wdt_miscdev = { 19388cce427SPhilippe De Muyter .minor = WATCHDOG_MINOR, 19488cce427SPhilippe De Muyter .name = "watchdog", 1959b9c63ffSPhilippe De Muyter .fops = &m54xx_wdt_fops, 19688cce427SPhilippe De Muyter }; 19788cce427SPhilippe De Muyter 1989b9c63ffSPhilippe De Muyter static int __init m54xx_wdt_init(void) 19988cce427SPhilippe De Muyter { 200944c3d81SGreg Ungerer if (!request_mem_region(MCF_GPT_GCIR0, 4, "Coldfire M54xx Watchdog")) { 20127c766aaSJoe Perches pr_warn("I/O region busy\n"); 20288cce427SPhilippe De Muyter return -EBUSY; 20388cce427SPhilippe De Muyter } 20427c766aaSJoe Perches pr_info("driver is loaded\n"); 20588cce427SPhilippe De Muyter 2069b9c63ffSPhilippe De Muyter return misc_register(&m54xx_wdt_miscdev); 20788cce427SPhilippe De Muyter } 20888cce427SPhilippe De Muyter 2099b9c63ffSPhilippe De Muyter static void __exit m54xx_wdt_exit(void) 21088cce427SPhilippe De Muyter { 2119b9c63ffSPhilippe De Muyter misc_deregister(&m54xx_wdt_miscdev); 212944c3d81SGreg Ungerer release_mem_region(MCF_GPT_GCIR0, 4); 21388cce427SPhilippe De Muyter } 21488cce427SPhilippe De Muyter 2159b9c63ffSPhilippe De Muyter module_init(m54xx_wdt_init); 2169b9c63ffSPhilippe De Muyter module_exit(m54xx_wdt_exit); 21788cce427SPhilippe De Muyter 21888cce427SPhilippe De Muyter MODULE_AUTHOR("Philippe De Muyter <phdm@macqel.be>"); 2199b9c63ffSPhilippe De Muyter MODULE_DESCRIPTION("Coldfire M54xx Watchdog"); 22088cce427SPhilippe De Muyter 22188cce427SPhilippe De Muyter module_param(heartbeat, int, 0); 22288cce427SPhilippe De Muyter MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 30s)"); 22388cce427SPhilippe De Muyter 22486a1e189SWim Van Sebroeck module_param(nowayout, bool, 0); 22588cce427SPhilippe De Muyter MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); 22688cce427SPhilippe De Muyter 22788cce427SPhilippe De Muyter MODULE_LICENSE("GPL"); 228