| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 3 | | - * under the terms of the GNU General Public License version 2 as published |
|---|
| 4 | | - * by the Free Software Foundation. |
|---|
| 5 | 3 | * |
|---|
| 6 | 4 | * Copyright (C) 2010 John Crispin <john@phrozen.org> |
|---|
| 7 | 5 | * Copyright (C) 2017 Hauke Mehrtens <hauke@hauke-m.de> |
|---|
| 8 | 6 | * Based on EP93xx wdt driver |
|---|
| 9 | 7 | */ |
|---|
| 10 | 8 | |
|---|
| 11 | | -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
|---|
| 12 | | - |
|---|
| 13 | 9 | #include <linux/module.h> |
|---|
| 14 | | -#include <linux/fs.h> |
|---|
| 15 | | -#include <linux/miscdevice.h> |
|---|
| 10 | +#include <linux/bitops.h> |
|---|
| 16 | 11 | #include <linux/watchdog.h> |
|---|
| 17 | 12 | #include <linux/of_platform.h> |
|---|
| 18 | 13 | #include <linux/uaccess.h> |
|---|
| .. | .. |
|---|
| 40 | 35 | * essentially the following two magic passwords need to be written to allow |
|---|
| 41 | 36 | * IO access to the WDT core |
|---|
| 42 | 37 | */ |
|---|
| 43 | | -#define LTQ_WDT_PW1 0x00BE0000 |
|---|
| 44 | | -#define LTQ_WDT_PW2 0x00DC0000 |
|---|
| 38 | +#define LTQ_WDT_CR_PW1 0x00BE0000 |
|---|
| 39 | +#define LTQ_WDT_CR_PW2 0x00DC0000 |
|---|
| 45 | 40 | |
|---|
| 46 | | -#define LTQ_WDT_CR 0x0 /* watchdog control register */ |
|---|
| 47 | | -#define LTQ_WDT_SR 0x8 /* watchdog status register */ |
|---|
| 41 | +#define LTQ_WDT_CR 0x0 /* watchdog control register */ |
|---|
| 42 | +#define LTQ_WDT_CR_GEN BIT(31) /* enable bit */ |
|---|
| 43 | +/* Pre-warning limit set to 1/16 of max WDT period */ |
|---|
| 44 | +#define LTQ_WDT_CR_PWL (0x3 << 26) |
|---|
| 45 | +/* set clock divider to 0x40000 */ |
|---|
| 46 | +#define LTQ_WDT_CR_CLKDIV (0x3 << 24) |
|---|
| 47 | +#define LTQ_WDT_CR_PW_MASK GENMASK(23, 16) /* Password field */ |
|---|
| 48 | +#define LTQ_WDT_CR_MAX_TIMEOUT ((1 << 16) - 1) /* The reload field is 16 bit */ |
|---|
| 49 | +#define LTQ_WDT_SR 0x8 /* watchdog status register */ |
|---|
| 50 | +#define LTQ_WDT_SR_EN BIT(31) /* Enable */ |
|---|
| 51 | +#define LTQ_WDT_SR_VALUE_MASK GENMASK(15, 0) /* Timer value */ |
|---|
| 48 | 52 | |
|---|
| 49 | | -#define LTQ_WDT_SR_EN (0x1 << 31) /* enable bit */ |
|---|
| 50 | | -#define LTQ_WDT_SR_PWD (0x3 << 26) /* turn on power */ |
|---|
| 51 | | -#define LTQ_WDT_SR_CLKDIV (0x3 << 24) /* turn on clock and set */ |
|---|
| 52 | | - /* divider to 0x40000 */ |
|---|
| 53 | 53 | #define LTQ_WDT_DIVIDER 0x40000 |
|---|
| 54 | | -#define LTQ_MAX_TIMEOUT ((1 << 16) - 1) /* the reload field is 16 bit */ |
|---|
| 55 | 54 | |
|---|
| 56 | 55 | static bool nowayout = WATCHDOG_NOWAYOUT; |
|---|
| 57 | 56 | |
|---|
| 58 | | -static void __iomem *ltq_wdt_membase; |
|---|
| 59 | | -static unsigned long ltq_io_region_clk_rate; |
|---|
| 57 | +struct ltq_wdt_hw { |
|---|
| 58 | + int (*bootstatus_get)(struct device *dev); |
|---|
| 59 | +}; |
|---|
| 60 | 60 | |
|---|
| 61 | | -static unsigned long ltq_wdt_bootstatus; |
|---|
| 62 | | -static unsigned long ltq_wdt_in_use; |
|---|
| 63 | | -static int ltq_wdt_timeout = 30; |
|---|
| 64 | | -static int ltq_wdt_ok_to_close; |
|---|
| 61 | +struct ltq_wdt_priv { |
|---|
| 62 | + struct watchdog_device wdt; |
|---|
| 63 | + void __iomem *membase; |
|---|
| 64 | + unsigned long clk_rate; |
|---|
| 65 | +}; |
|---|
| 65 | 66 | |
|---|
| 66 | | -static void |
|---|
| 67 | | -ltq_wdt_enable(void) |
|---|
| 67 | +static u32 ltq_wdt_r32(struct ltq_wdt_priv *priv, u32 offset) |
|---|
| 68 | 68 | { |
|---|
| 69 | | - unsigned long int timeout = ltq_wdt_timeout * |
|---|
| 70 | | - (ltq_io_region_clk_rate / LTQ_WDT_DIVIDER) + 0x1000; |
|---|
| 71 | | - if (timeout > LTQ_MAX_TIMEOUT) |
|---|
| 72 | | - timeout = LTQ_MAX_TIMEOUT; |
|---|
| 73 | | - |
|---|
| 74 | | - /* write the first password magic */ |
|---|
| 75 | | - ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR); |
|---|
| 76 | | - /* write the second magic plus the configuration and new timeout */ |
|---|
| 77 | | - ltq_w32(LTQ_WDT_SR_EN | LTQ_WDT_SR_PWD | LTQ_WDT_SR_CLKDIV | |
|---|
| 78 | | - LTQ_WDT_PW2 | timeout, ltq_wdt_membase + LTQ_WDT_CR); |
|---|
| 69 | + return __raw_readl(priv->membase + offset); |
|---|
| 79 | 70 | } |
|---|
| 80 | 71 | |
|---|
| 81 | | -static void |
|---|
| 82 | | -ltq_wdt_disable(void) |
|---|
| 72 | +static void ltq_wdt_w32(struct ltq_wdt_priv *priv, u32 val, u32 offset) |
|---|
| 83 | 73 | { |
|---|
| 84 | | - /* write the first password magic */ |
|---|
| 85 | | - ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR); |
|---|
| 86 | | - /* |
|---|
| 87 | | - * write the second password magic with no config |
|---|
| 88 | | - * this turns the watchdog off |
|---|
| 89 | | - */ |
|---|
| 90 | | - ltq_w32(LTQ_WDT_PW2, ltq_wdt_membase + LTQ_WDT_CR); |
|---|
| 74 | + __raw_writel(val, priv->membase + offset); |
|---|
| 91 | 75 | } |
|---|
| 92 | 76 | |
|---|
| 93 | | -static ssize_t |
|---|
| 94 | | -ltq_wdt_write(struct file *file, const char __user *data, |
|---|
| 95 | | - size_t len, loff_t *ppos) |
|---|
| 77 | +static void ltq_wdt_mask(struct ltq_wdt_priv *priv, u32 clear, u32 set, |
|---|
| 78 | + u32 offset) |
|---|
| 96 | 79 | { |
|---|
| 97 | | - if (len) { |
|---|
| 98 | | - if (!nowayout) { |
|---|
| 99 | | - size_t i; |
|---|
| 80 | + u32 val = ltq_wdt_r32(priv, offset); |
|---|
| 100 | 81 | |
|---|
| 101 | | - ltq_wdt_ok_to_close = 0; |
|---|
| 102 | | - for (i = 0; i != len; i++) { |
|---|
| 103 | | - char c; |
|---|
| 104 | | - |
|---|
| 105 | | - if (get_user(c, data + i)) |
|---|
| 106 | | - return -EFAULT; |
|---|
| 107 | | - if (c == 'V') |
|---|
| 108 | | - ltq_wdt_ok_to_close = 1; |
|---|
| 109 | | - else |
|---|
| 110 | | - ltq_wdt_ok_to_close = 0; |
|---|
| 111 | | - } |
|---|
| 112 | | - } |
|---|
| 113 | | - ltq_wdt_enable(); |
|---|
| 114 | | - } |
|---|
| 115 | | - |
|---|
| 116 | | - return len; |
|---|
| 82 | + val &= ~(clear); |
|---|
| 83 | + val |= set; |
|---|
| 84 | + ltq_wdt_w32(priv, val, offset); |
|---|
| 117 | 85 | } |
|---|
| 118 | 86 | |
|---|
| 119 | | -static struct watchdog_info ident = { |
|---|
| 87 | +static struct ltq_wdt_priv *ltq_wdt_get_priv(struct watchdog_device *wdt) |
|---|
| 88 | +{ |
|---|
| 89 | + return container_of(wdt, struct ltq_wdt_priv, wdt); |
|---|
| 90 | +} |
|---|
| 91 | + |
|---|
| 92 | +static struct watchdog_info ltq_wdt_info = { |
|---|
| 120 | 93 | .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | |
|---|
| 121 | | - WDIOF_CARDRESET, |
|---|
| 94 | + WDIOF_CARDRESET, |
|---|
| 122 | 95 | .identity = "ltq_wdt", |
|---|
| 123 | 96 | }; |
|---|
| 124 | 97 | |
|---|
| 125 | | -static long |
|---|
| 126 | | -ltq_wdt_ioctl(struct file *file, |
|---|
| 127 | | - unsigned int cmd, unsigned long arg) |
|---|
| 98 | +static int ltq_wdt_start(struct watchdog_device *wdt) |
|---|
| 128 | 99 | { |
|---|
| 129 | | - int ret = -ENOTTY; |
|---|
| 100 | + struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt); |
|---|
| 101 | + u32 timeout; |
|---|
| 130 | 102 | |
|---|
| 131 | | - switch (cmd) { |
|---|
| 132 | | - case WDIOC_GETSUPPORT: |
|---|
| 133 | | - ret = copy_to_user((struct watchdog_info __user *)arg, &ident, |
|---|
| 134 | | - sizeof(ident)) ? -EFAULT : 0; |
|---|
| 135 | | - break; |
|---|
| 103 | + timeout = wdt->timeout * priv->clk_rate; |
|---|
| 136 | 104 | |
|---|
| 137 | | - case WDIOC_GETBOOTSTATUS: |
|---|
| 138 | | - ret = put_user(ltq_wdt_bootstatus, (int __user *)arg); |
|---|
| 139 | | - break; |
|---|
| 140 | | - |
|---|
| 141 | | - case WDIOC_GETSTATUS: |
|---|
| 142 | | - ret = put_user(0, (int __user *)arg); |
|---|
| 143 | | - break; |
|---|
| 144 | | - |
|---|
| 145 | | - case WDIOC_SETTIMEOUT: |
|---|
| 146 | | - ret = get_user(ltq_wdt_timeout, (int __user *)arg); |
|---|
| 147 | | - if (!ret) |
|---|
| 148 | | - ltq_wdt_enable(); |
|---|
| 149 | | - /* intentional drop through */ |
|---|
| 150 | | - case WDIOC_GETTIMEOUT: |
|---|
| 151 | | - ret = put_user(ltq_wdt_timeout, (int __user *)arg); |
|---|
| 152 | | - break; |
|---|
| 153 | | - |
|---|
| 154 | | - case WDIOC_KEEPALIVE: |
|---|
| 155 | | - ltq_wdt_enable(); |
|---|
| 156 | | - ret = 0; |
|---|
| 157 | | - break; |
|---|
| 158 | | - } |
|---|
| 159 | | - return ret; |
|---|
| 160 | | -} |
|---|
| 161 | | - |
|---|
| 162 | | -static int |
|---|
| 163 | | -ltq_wdt_open(struct inode *inode, struct file *file) |
|---|
| 164 | | -{ |
|---|
| 165 | | - if (test_and_set_bit(0, <q_wdt_in_use)) |
|---|
| 166 | | - return -EBUSY; |
|---|
| 167 | | - ltq_wdt_in_use = 1; |
|---|
| 168 | | - ltq_wdt_enable(); |
|---|
| 169 | | - |
|---|
| 170 | | - return nonseekable_open(inode, file); |
|---|
| 171 | | -} |
|---|
| 172 | | - |
|---|
| 173 | | -static int |
|---|
| 174 | | -ltq_wdt_release(struct inode *inode, struct file *file) |
|---|
| 175 | | -{ |
|---|
| 176 | | - if (ltq_wdt_ok_to_close) |
|---|
| 177 | | - ltq_wdt_disable(); |
|---|
| 178 | | - else |
|---|
| 179 | | - pr_err("watchdog closed without warning\n"); |
|---|
| 180 | | - ltq_wdt_ok_to_close = 0; |
|---|
| 181 | | - clear_bit(0, <q_wdt_in_use); |
|---|
| 105 | + ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK, LTQ_WDT_CR_PW1, LTQ_WDT_CR); |
|---|
| 106 | + /* write the second magic plus the configuration and new timeout */ |
|---|
| 107 | + ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK | LTQ_WDT_CR_MAX_TIMEOUT, |
|---|
| 108 | + LTQ_WDT_CR_GEN | LTQ_WDT_CR_PWL | LTQ_WDT_CR_CLKDIV | |
|---|
| 109 | + LTQ_WDT_CR_PW2 | timeout, |
|---|
| 110 | + LTQ_WDT_CR); |
|---|
| 182 | 111 | |
|---|
| 183 | 112 | return 0; |
|---|
| 184 | 113 | } |
|---|
| 185 | 114 | |
|---|
| 186 | | -static const struct file_operations ltq_wdt_fops = { |
|---|
| 187 | | - .owner = THIS_MODULE, |
|---|
| 188 | | - .write = ltq_wdt_write, |
|---|
| 189 | | - .unlocked_ioctl = ltq_wdt_ioctl, |
|---|
| 190 | | - .open = ltq_wdt_open, |
|---|
| 191 | | - .release = ltq_wdt_release, |
|---|
| 192 | | - .llseek = no_llseek, |
|---|
| 193 | | -}; |
|---|
| 194 | | - |
|---|
| 195 | | -static struct miscdevice ltq_wdt_miscdev = { |
|---|
| 196 | | - .minor = WATCHDOG_MINOR, |
|---|
| 197 | | - .name = "watchdog", |
|---|
| 198 | | - .fops = <q_wdt_fops, |
|---|
| 199 | | -}; |
|---|
| 200 | | - |
|---|
| 201 | | -typedef int (*ltq_wdt_bootstatus_set)(struct platform_device *pdev); |
|---|
| 202 | | - |
|---|
| 203 | | -static int ltq_wdt_bootstatus_xrx(struct platform_device *pdev) |
|---|
| 115 | +static int ltq_wdt_stop(struct watchdog_device *wdt) |
|---|
| 204 | 116 | { |
|---|
| 205 | | - struct device *dev = &pdev->dev; |
|---|
| 117 | + struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt); |
|---|
| 118 | + |
|---|
| 119 | + ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK, LTQ_WDT_CR_PW1, LTQ_WDT_CR); |
|---|
| 120 | + ltq_wdt_mask(priv, LTQ_WDT_CR_GEN | LTQ_WDT_CR_PW_MASK, |
|---|
| 121 | + LTQ_WDT_CR_PW2, LTQ_WDT_CR); |
|---|
| 122 | + |
|---|
| 123 | + return 0; |
|---|
| 124 | +} |
|---|
| 125 | + |
|---|
| 126 | +static int ltq_wdt_ping(struct watchdog_device *wdt) |
|---|
| 127 | +{ |
|---|
| 128 | + struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt); |
|---|
| 129 | + u32 timeout; |
|---|
| 130 | + |
|---|
| 131 | + timeout = wdt->timeout * priv->clk_rate; |
|---|
| 132 | + |
|---|
| 133 | + ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK, LTQ_WDT_CR_PW1, LTQ_WDT_CR); |
|---|
| 134 | + /* write the second magic plus the configuration and new timeout */ |
|---|
| 135 | + ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK | LTQ_WDT_CR_MAX_TIMEOUT, |
|---|
| 136 | + LTQ_WDT_CR_PW2 | timeout, LTQ_WDT_CR); |
|---|
| 137 | + |
|---|
| 138 | + return 0; |
|---|
| 139 | +} |
|---|
| 140 | + |
|---|
| 141 | +static unsigned int ltq_wdt_get_timeleft(struct watchdog_device *wdt) |
|---|
| 142 | +{ |
|---|
| 143 | + struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt); |
|---|
| 144 | + u64 timeout; |
|---|
| 145 | + |
|---|
| 146 | + timeout = ltq_wdt_r32(priv, LTQ_WDT_SR) & LTQ_WDT_SR_VALUE_MASK; |
|---|
| 147 | + return do_div(timeout, priv->clk_rate); |
|---|
| 148 | +} |
|---|
| 149 | + |
|---|
| 150 | +static const struct watchdog_ops ltq_wdt_ops = { |
|---|
| 151 | + .owner = THIS_MODULE, |
|---|
| 152 | + .start = ltq_wdt_start, |
|---|
| 153 | + .stop = ltq_wdt_stop, |
|---|
| 154 | + .ping = ltq_wdt_ping, |
|---|
| 155 | + .get_timeleft = ltq_wdt_get_timeleft, |
|---|
| 156 | +}; |
|---|
| 157 | + |
|---|
| 158 | +static int ltq_wdt_xrx_bootstatus_get(struct device *dev) |
|---|
| 159 | +{ |
|---|
| 206 | 160 | struct regmap *rcu_regmap; |
|---|
| 207 | 161 | u32 val; |
|---|
| 208 | 162 | int err; |
|---|
| .. | .. |
|---|
| 216 | 170 | return err; |
|---|
| 217 | 171 | |
|---|
| 218 | 172 | if (val & LTQ_XRX_RCU_RST_STAT_WDT) |
|---|
| 219 | | - ltq_wdt_bootstatus = WDIOF_CARDRESET; |
|---|
| 173 | + return WDIOF_CARDRESET; |
|---|
| 220 | 174 | |
|---|
| 221 | 175 | return 0; |
|---|
| 222 | 176 | } |
|---|
| 223 | 177 | |
|---|
| 224 | | -static int ltq_wdt_bootstatus_falcon(struct platform_device *pdev) |
|---|
| 178 | +static int ltq_wdt_falcon_bootstatus_get(struct device *dev) |
|---|
| 225 | 179 | { |
|---|
| 226 | | - struct device *dev = &pdev->dev; |
|---|
| 227 | 180 | struct regmap *rcu_regmap; |
|---|
| 228 | 181 | u32 val; |
|---|
| 229 | 182 | int err; |
|---|
| .. | .. |
|---|
| 238 | 191 | return err; |
|---|
| 239 | 192 | |
|---|
| 240 | 193 | if ((val & LTQ_FALCON_SYS1_CPU0RS_MASK) == LTQ_FALCON_SYS1_CPU0RS_WDT) |
|---|
| 241 | | - ltq_wdt_bootstatus = WDIOF_CARDRESET; |
|---|
| 194 | + return WDIOF_CARDRESET; |
|---|
| 242 | 195 | |
|---|
| 243 | 196 | return 0; |
|---|
| 244 | 197 | } |
|---|
| 245 | 198 | |
|---|
| 246 | | -static int |
|---|
| 247 | | -ltq_wdt_probe(struct platform_device *pdev) |
|---|
| 199 | +static int ltq_wdt_probe(struct platform_device *pdev) |
|---|
| 248 | 200 | { |
|---|
| 249 | | - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 201 | + struct device *dev = &pdev->dev; |
|---|
| 202 | + struct ltq_wdt_priv *priv; |
|---|
| 203 | + struct watchdog_device *wdt; |
|---|
| 250 | 204 | struct clk *clk; |
|---|
| 251 | | - ltq_wdt_bootstatus_set ltq_wdt_bootstatus_set; |
|---|
| 205 | + const struct ltq_wdt_hw *ltq_wdt_hw; |
|---|
| 252 | 206 | int ret; |
|---|
| 207 | + u32 status; |
|---|
| 253 | 208 | |
|---|
| 254 | | - ltq_wdt_membase = devm_ioremap_resource(&pdev->dev, res); |
|---|
| 255 | | - if (IS_ERR(ltq_wdt_membase)) |
|---|
| 256 | | - return PTR_ERR(ltq_wdt_membase); |
|---|
| 209 | + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
|---|
| 210 | + if (!priv) |
|---|
| 211 | + return -ENOMEM; |
|---|
| 257 | 212 | |
|---|
| 258 | | - ltq_wdt_bootstatus_set = of_device_get_match_data(&pdev->dev); |
|---|
| 259 | | - if (ltq_wdt_bootstatus_set) { |
|---|
| 260 | | - ret = ltq_wdt_bootstatus_set(pdev); |
|---|
| 261 | | - if (ret) |
|---|
| 262 | | - return ret; |
|---|
| 263 | | - } |
|---|
| 213 | + priv->membase = devm_platform_ioremap_resource(pdev, 0); |
|---|
| 214 | + if (IS_ERR(priv->membase)) |
|---|
| 215 | + return PTR_ERR(priv->membase); |
|---|
| 264 | 216 | |
|---|
| 265 | 217 | /* we do not need to enable the clock as it is always running */ |
|---|
| 266 | 218 | clk = clk_get_io(); |
|---|
| 267 | | - if (IS_ERR(clk)) { |
|---|
| 268 | | - dev_err(&pdev->dev, "Failed to get clock\n"); |
|---|
| 269 | | - return -ENOENT; |
|---|
| 219 | + priv->clk_rate = clk_get_rate(clk) / LTQ_WDT_DIVIDER; |
|---|
| 220 | + if (!priv->clk_rate) { |
|---|
| 221 | + dev_err(dev, "clock rate less than divider %i\n", |
|---|
| 222 | + LTQ_WDT_DIVIDER); |
|---|
| 223 | + return -EINVAL; |
|---|
| 270 | 224 | } |
|---|
| 271 | | - ltq_io_region_clk_rate = clk_get_rate(clk); |
|---|
| 272 | | - clk_put(clk); |
|---|
| 273 | 225 | |
|---|
| 274 | | - dev_info(&pdev->dev, "Init done\n"); |
|---|
| 275 | | - return misc_register(<q_wdt_miscdev); |
|---|
| 226 | + wdt = &priv->wdt; |
|---|
| 227 | + wdt->info = <q_wdt_info; |
|---|
| 228 | + wdt->ops = <q_wdt_ops; |
|---|
| 229 | + wdt->min_timeout = 1; |
|---|
| 230 | + wdt->max_timeout = LTQ_WDT_CR_MAX_TIMEOUT / priv->clk_rate; |
|---|
| 231 | + wdt->timeout = wdt->max_timeout; |
|---|
| 232 | + wdt->parent = dev; |
|---|
| 233 | + |
|---|
| 234 | + ltq_wdt_hw = of_device_get_match_data(dev); |
|---|
| 235 | + if (ltq_wdt_hw && ltq_wdt_hw->bootstatus_get) { |
|---|
| 236 | + ret = ltq_wdt_hw->bootstatus_get(dev); |
|---|
| 237 | + if (ret >= 0) |
|---|
| 238 | + wdt->bootstatus = ret; |
|---|
| 239 | + } |
|---|
| 240 | + |
|---|
| 241 | + watchdog_set_nowayout(wdt, nowayout); |
|---|
| 242 | + watchdog_init_timeout(wdt, 0, dev); |
|---|
| 243 | + |
|---|
| 244 | + status = ltq_wdt_r32(priv, LTQ_WDT_SR); |
|---|
| 245 | + if (status & LTQ_WDT_SR_EN) { |
|---|
| 246 | + /* |
|---|
| 247 | + * If the watchdog is already running overwrite it with our |
|---|
| 248 | + * new settings. Stop is not needed as the start call will |
|---|
| 249 | + * replace all settings anyway. |
|---|
| 250 | + */ |
|---|
| 251 | + ltq_wdt_start(wdt); |
|---|
| 252 | + set_bit(WDOG_HW_RUNNING, &wdt->status); |
|---|
| 253 | + } |
|---|
| 254 | + |
|---|
| 255 | + return devm_watchdog_register_device(dev, wdt); |
|---|
| 276 | 256 | } |
|---|
| 277 | 257 | |
|---|
| 278 | | -static int |
|---|
| 279 | | -ltq_wdt_remove(struct platform_device *pdev) |
|---|
| 280 | | -{ |
|---|
| 281 | | - misc_deregister(<q_wdt_miscdev); |
|---|
| 258 | +static const struct ltq_wdt_hw ltq_wdt_xrx100 = { |
|---|
| 259 | + .bootstatus_get = ltq_wdt_xrx_bootstatus_get, |
|---|
| 260 | +}; |
|---|
| 282 | 261 | |
|---|
| 283 | | - return 0; |
|---|
| 284 | | -} |
|---|
| 262 | +static const struct ltq_wdt_hw ltq_wdt_falcon = { |
|---|
| 263 | + .bootstatus_get = ltq_wdt_falcon_bootstatus_get, |
|---|
| 264 | +}; |
|---|
| 285 | 265 | |
|---|
| 286 | 266 | static const struct of_device_id ltq_wdt_match[] = { |
|---|
| 287 | | - { .compatible = "lantiq,wdt", .data = NULL}, |
|---|
| 288 | | - { .compatible = "lantiq,xrx100-wdt", .data = ltq_wdt_bootstatus_xrx }, |
|---|
| 289 | | - { .compatible = "lantiq,falcon-wdt", .data = ltq_wdt_bootstatus_falcon }, |
|---|
| 267 | + { .compatible = "lantiq,wdt", .data = NULL }, |
|---|
| 268 | + { .compatible = "lantiq,xrx100-wdt", .data = <q_wdt_xrx100 }, |
|---|
| 269 | + { .compatible = "lantiq,falcon-wdt", .data = <q_wdt_falcon }, |
|---|
| 290 | 270 | {}, |
|---|
| 291 | 271 | }; |
|---|
| 292 | 272 | MODULE_DEVICE_TABLE(of, ltq_wdt_match); |
|---|
| 293 | 273 | |
|---|
| 294 | 274 | static struct platform_driver ltq_wdt_driver = { |
|---|
| 295 | 275 | .probe = ltq_wdt_probe, |
|---|
| 296 | | - .remove = ltq_wdt_remove, |
|---|
| 297 | 276 | .driver = { |
|---|
| 298 | 277 | .name = "wdt", |
|---|
| 299 | 278 | .of_match_table = ltq_wdt_match, |
|---|