1 /* 2 * txx9wdt: A Hardware Watchdog Driver for TXx9 SoCs 3 * 4 * Copyright (C) 2007 Atsushi Nemoto <anemo@mba.ocn.ne.jp> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 */ 10 11 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12 13 #include <linux/module.h> 14 #include <linux/moduleparam.h> 15 #include <linux/types.h> 16 #include <linux/miscdevice.h> 17 #include <linux/watchdog.h> 18 #include <linux/init.h> 19 #include <linux/platform_device.h> 20 #include <linux/clk.h> 21 #include <linux/err.h> 22 #include <linux/io.h> 23 #include <asm/txx9tmr.h> 24 25 #define WD_TIMER_CCD 7 /* 1/256 */ 26 #define WD_TIMER_CLK (clk_get_rate(txx9_imclk) / (2 << WD_TIMER_CCD)) 27 #define WD_MAX_TIMEOUT ((0xffffffff >> (32 - TXX9_TIMER_BITS)) / WD_TIMER_CLK) 28 #define TIMER_MARGIN 60 /* Default is 60 seconds */ 29 30 static unsigned int timeout = TIMER_MARGIN; /* in seconds */ 31 module_param(timeout, uint, 0); 32 MODULE_PARM_DESC(timeout, 33 "Watchdog timeout in seconds. " 34 "(0<timeout<((2^" __MODULE_STRING(TXX9_TIMER_BITS) ")/(IMCLK/256)), " 35 "default=" __MODULE_STRING(TIMER_MARGIN) ")"); 36 37 static bool nowayout = WATCHDOG_NOWAYOUT; 38 module_param(nowayout, bool, 0); 39 MODULE_PARM_DESC(nowayout, 40 "Watchdog cannot be stopped once started " 41 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 42 43 static struct txx9_tmr_reg __iomem *txx9wdt_reg; 44 static struct clk *txx9_imclk; 45 static DEFINE_SPINLOCK(txx9_lock); 46 47 static int txx9wdt_ping(struct watchdog_device *wdt_dev) 48 { 49 spin_lock(&txx9_lock); 50 __raw_writel(TXx9_TMWTMR_TWIE | TXx9_TMWTMR_TWC, &txx9wdt_reg->wtmr); 51 spin_unlock(&txx9_lock); 52 return 0; 53 } 54 55 static int txx9wdt_start(struct watchdog_device *wdt_dev) 56 { 57 spin_lock(&txx9_lock); 58 __raw_writel(WD_TIMER_CLK * wdt_dev->timeout, &txx9wdt_reg->cpra); 59 __raw_writel(WD_TIMER_CCD, &txx9wdt_reg->ccdr); 60 __raw_writel(0, &txx9wdt_reg->tisr); /* clear pending interrupt */ 61 __raw_writel(TXx9_TMTCR_TCE | TXx9_TMTCR_CCDE | TXx9_TMTCR_TMODE_WDOG, 62 &txx9wdt_reg->tcr); 63 __raw_writel(TXx9_TMWTMR_TWIE | TXx9_TMWTMR_TWC, &txx9wdt_reg->wtmr); 64 spin_unlock(&txx9_lock); 65 return 0; 66 } 67 68 static int txx9wdt_stop(struct watchdog_device *wdt_dev) 69 { 70 spin_lock(&txx9_lock); 71 __raw_writel(TXx9_TMWTMR_WDIS, &txx9wdt_reg->wtmr); 72 __raw_writel(__raw_readl(&txx9wdt_reg->tcr) & ~TXx9_TMTCR_TCE, 73 &txx9wdt_reg->tcr); 74 spin_unlock(&txx9_lock); 75 return 0; 76 } 77 78 static int txx9wdt_set_timeout(struct watchdog_device *wdt_dev, 79 unsigned int new_timeout) 80 { 81 wdt_dev->timeout = new_timeout; 82 txx9wdt_stop(wdt_dev); 83 txx9wdt_start(wdt_dev); 84 return 0; 85 } 86 87 static const struct watchdog_info txx9wdt_info = { 88 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 89 .identity = "Hardware Watchdog for TXx9", 90 }; 91 92 static const struct watchdog_ops txx9wdt_ops = { 93 .owner = THIS_MODULE, 94 .start = txx9wdt_start, 95 .stop = txx9wdt_stop, 96 .ping = txx9wdt_ping, 97 .set_timeout = txx9wdt_set_timeout, 98 }; 99 100 static struct watchdog_device txx9wdt = { 101 .info = &txx9wdt_info, 102 .ops = &txx9wdt_ops, 103 }; 104 105 static int __init txx9wdt_probe(struct platform_device *dev) 106 { 107 struct resource *res; 108 int ret; 109 110 txx9_imclk = clk_get(NULL, "imbus_clk"); 111 if (IS_ERR(txx9_imclk)) { 112 ret = PTR_ERR(txx9_imclk); 113 txx9_imclk = NULL; 114 goto exit; 115 } 116 ret = clk_enable(txx9_imclk); 117 if (ret) { 118 clk_put(txx9_imclk); 119 txx9_imclk = NULL; 120 goto exit; 121 } 122 123 res = platform_get_resource(dev, IORESOURCE_MEM, 0); 124 txx9wdt_reg = devm_request_and_ioremap(&dev->dev, res); 125 if (!txx9wdt_reg) { 126 ret = -EBUSY; 127 goto exit; 128 } 129 130 if (timeout < 1 || timeout > WD_MAX_TIMEOUT) 131 timeout = TIMER_MARGIN; 132 txx9wdt.timeout = timeout; 133 txx9wdt.min_timeout = 1; 134 txx9wdt.max_timeout = WD_MAX_TIMEOUT; 135 watchdog_set_nowayout(&txx9wdt, nowayout); 136 137 ret = watchdog_register_device(&txx9wdt); 138 if (ret) 139 goto exit; 140 141 pr_info("Hardware Watchdog Timer: timeout=%d sec (max %ld) (nowayout= %d)\n", 142 timeout, WD_MAX_TIMEOUT, nowayout); 143 144 return 0; 145 exit: 146 if (txx9_imclk) { 147 clk_disable(txx9_imclk); 148 clk_put(txx9_imclk); 149 } 150 return ret; 151 } 152 153 static int __exit txx9wdt_remove(struct platform_device *dev) 154 { 155 watchdog_unregister_device(&txx9wdt); 156 clk_disable(txx9_imclk); 157 clk_put(txx9_imclk); 158 return 0; 159 } 160 161 static void txx9wdt_shutdown(struct platform_device *dev) 162 { 163 txx9wdt_stop(&txx9wdt); 164 } 165 166 static struct platform_driver txx9wdt_driver = { 167 .remove = __exit_p(txx9wdt_remove), 168 .shutdown = txx9wdt_shutdown, 169 .driver = { 170 .name = "txx9wdt", 171 .owner = THIS_MODULE, 172 }, 173 }; 174 175 static int __init watchdog_init(void) 176 { 177 return platform_driver_probe(&txx9wdt_driver, txx9wdt_probe); 178 } 179 180 static void __exit watchdog_exit(void) 181 { 182 platform_driver_unregister(&txx9wdt_driver); 183 } 184 185 module_init(watchdog_init); 186 module_exit(watchdog_exit); 187 188 MODULE_DESCRIPTION("TXx9 Watchdog Driver"); 189 MODULE_LICENSE("GPL"); 190 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 191 MODULE_ALIAS("platform:txx9wdt"); 192