1 /* 2 * drivers/watchdog/orion_wdt.c 3 * 4 * Watchdog driver for Orion/Kirkwood processors 5 * 6 * Author: Sylver Bruneau <sylver.bruneau@googlemail.com> 7 * 8 * This file is licensed under the terms of the GNU General Public 9 * License version 2. This program is licensed "as is" without any 10 * warranty of any kind, whether express or implied. 11 */ 12 13 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 14 15 #include <linux/module.h> 16 #include <linux/moduleparam.h> 17 #include <linux/types.h> 18 #include <linux/kernel.h> 19 #include <linux/miscdevice.h> 20 #include <linux/platform_device.h> 21 #include <linux/watchdog.h> 22 #include <linux/init.h> 23 #include <linux/io.h> 24 #include <linux/spinlock.h> 25 #include <linux/clk.h> 26 #include <linux/err.h> 27 #include <linux/of.h> 28 #include <mach/bridge-regs.h> 29 30 /* 31 * Watchdog timer block registers. 32 */ 33 #define TIMER_CTRL 0x0000 34 #define WDT_EN 0x0010 35 #define WDT_VAL 0x0024 36 37 #define WDT_MAX_CYCLE_COUNT 0xffffffff 38 #define WDT_IN_USE 0 39 #define WDT_OK_TO_CLOSE 1 40 41 #define WDT_RESET_OUT_EN BIT(1) 42 #define WDT_INT_REQ BIT(3) 43 44 static bool nowayout = WATCHDOG_NOWAYOUT; 45 static int heartbeat = -1; /* module parameter (seconds) */ 46 static unsigned int wdt_max_duration; /* (seconds) */ 47 static struct clk *clk; 48 static unsigned int wdt_tclk; 49 static void __iomem *wdt_reg; 50 static DEFINE_SPINLOCK(wdt_lock); 51 52 static int orion_wdt_ping(struct watchdog_device *wdt_dev) 53 { 54 spin_lock(&wdt_lock); 55 56 /* Reload watchdog duration */ 57 writel(wdt_tclk * wdt_dev->timeout, wdt_reg + WDT_VAL); 58 59 spin_unlock(&wdt_lock); 60 return 0; 61 } 62 63 static int orion_wdt_start(struct watchdog_device *wdt_dev) 64 { 65 u32 reg; 66 67 spin_lock(&wdt_lock); 68 69 /* Set watchdog duration */ 70 writel(wdt_tclk * wdt_dev->timeout, wdt_reg + WDT_VAL); 71 72 /* Clear watchdog timer interrupt */ 73 writel(~WDT_INT_REQ, BRIDGE_CAUSE); 74 75 /* Enable watchdog timer */ 76 reg = readl(wdt_reg + TIMER_CTRL); 77 reg |= WDT_EN; 78 writel(reg, wdt_reg + TIMER_CTRL); 79 80 /* Enable reset on watchdog */ 81 reg = readl(RSTOUTn_MASK); 82 reg |= WDT_RESET_OUT_EN; 83 writel(reg, RSTOUTn_MASK); 84 85 spin_unlock(&wdt_lock); 86 return 0; 87 } 88 89 static int orion_wdt_stop(struct watchdog_device *wdt_dev) 90 { 91 u32 reg; 92 93 spin_lock(&wdt_lock); 94 95 /* Disable reset on watchdog */ 96 reg = readl(RSTOUTn_MASK); 97 reg &= ~WDT_RESET_OUT_EN; 98 writel(reg, RSTOUTn_MASK); 99 100 /* Disable watchdog timer */ 101 reg = readl(wdt_reg + TIMER_CTRL); 102 reg &= ~WDT_EN; 103 writel(reg, wdt_reg + TIMER_CTRL); 104 105 spin_unlock(&wdt_lock); 106 return 0; 107 } 108 109 static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev) 110 { 111 unsigned int time_left; 112 113 spin_lock(&wdt_lock); 114 time_left = readl(wdt_reg + WDT_VAL) / wdt_tclk; 115 spin_unlock(&wdt_lock); 116 117 return time_left; 118 } 119 120 static int orion_wdt_set_timeout(struct watchdog_device *wdt_dev, 121 unsigned int timeout) 122 { 123 wdt_dev->timeout = timeout; 124 return 0; 125 } 126 127 static const struct watchdog_info orion_wdt_info = { 128 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 129 .identity = "Orion Watchdog", 130 }; 131 132 static const struct watchdog_ops orion_wdt_ops = { 133 .owner = THIS_MODULE, 134 .start = orion_wdt_start, 135 .stop = orion_wdt_stop, 136 .ping = orion_wdt_ping, 137 .set_timeout = orion_wdt_set_timeout, 138 .get_timeleft = orion_wdt_get_timeleft, 139 }; 140 141 static struct watchdog_device orion_wdt = { 142 .info = &orion_wdt_info, 143 .ops = &orion_wdt_ops, 144 .min_timeout = 1, 145 }; 146 147 static int orion_wdt_probe(struct platform_device *pdev) 148 { 149 struct resource *res; 150 int ret; 151 152 clk = devm_clk_get(&pdev->dev, NULL); 153 if (IS_ERR(clk)) { 154 dev_err(&pdev->dev, "Orion Watchdog missing clock\n"); 155 return -ENODEV; 156 } 157 clk_prepare_enable(clk); 158 wdt_tclk = clk_get_rate(clk); 159 160 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 161 if (!res) 162 return -ENODEV; 163 wdt_reg = devm_ioremap(&pdev->dev, res->start, resource_size(res)); 164 if (!wdt_reg) 165 return -ENOMEM; 166 167 wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk; 168 169 orion_wdt.timeout = wdt_max_duration; 170 orion_wdt.max_timeout = wdt_max_duration; 171 watchdog_init_timeout(&orion_wdt, heartbeat, &pdev->dev); 172 173 watchdog_set_nowayout(&orion_wdt, nowayout); 174 ret = watchdog_register_device(&orion_wdt); 175 if (ret) { 176 clk_disable_unprepare(clk); 177 return ret; 178 } 179 180 pr_info("Initial timeout %d sec%s\n", 181 orion_wdt.timeout, nowayout ? ", nowayout" : ""); 182 return 0; 183 } 184 185 static int orion_wdt_remove(struct platform_device *pdev) 186 { 187 watchdog_unregister_device(&orion_wdt); 188 clk_disable_unprepare(clk); 189 return 0; 190 } 191 192 static void orion_wdt_shutdown(struct platform_device *pdev) 193 { 194 orion_wdt_stop(&orion_wdt); 195 } 196 197 static const struct of_device_id orion_wdt_of_match_table[] = { 198 { .compatible = "marvell,orion-wdt", }, 199 {}, 200 }; 201 MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table); 202 203 static struct platform_driver orion_wdt_driver = { 204 .probe = orion_wdt_probe, 205 .remove = orion_wdt_remove, 206 .shutdown = orion_wdt_shutdown, 207 .driver = { 208 .owner = THIS_MODULE, 209 .name = "orion_wdt", 210 .of_match_table = of_match_ptr(orion_wdt_of_match_table), 211 }, 212 }; 213 214 module_platform_driver(orion_wdt_driver); 215 216 MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>"); 217 MODULE_DESCRIPTION("Orion Processor Watchdog"); 218 219 module_param(heartbeat, int, 0); 220 MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds"); 221 222 module_param(nowayout, bool, 0); 223 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 224 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 225 226 MODULE_LICENSE("GPL"); 227 MODULE_ALIAS("platform:orion_wdt"); 228 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 229