|
@@ -8,11 +8,8 @@
|
|
* Based on EP93xx wdt driver
|
|
* Based on EP93xx wdt driver
|
|
*/
|
|
*/
|
|
|
|
|
|
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
-
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/module.h>
|
|
-#include <linux/fs.h>
|
|
|
|
-#include <linux/miscdevice.h>
|
|
|
|
|
|
+#include <linux/bitops.h>
|
|
#include <linux/watchdog.h>
|
|
#include <linux/watchdog.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/uaccess.h>
|
|
@@ -40,169 +37,128 @@
|
|
* essentially the following two magic passwords need to be written to allow
|
|
* essentially the following two magic passwords need to be written to allow
|
|
* IO access to the WDT core
|
|
* IO access to the WDT core
|
|
*/
|
|
*/
|
|
-#define LTQ_WDT_PW1 0x00BE0000
|
|
|
|
-#define LTQ_WDT_PW2 0x00DC0000
|
|
|
|
-
|
|
|
|
-#define LTQ_WDT_CR 0x0 /* watchdog control register */
|
|
|
|
-#define LTQ_WDT_SR 0x8 /* watchdog status register */
|
|
|
|
|
|
+#define LTQ_WDT_CR_PW1 0x00BE0000
|
|
|
|
+#define LTQ_WDT_CR_PW2 0x00DC0000
|
|
|
|
+
|
|
|
|
+#define LTQ_WDT_CR 0x0 /* watchdog control register */
|
|
|
|
+#define LTQ_WDT_CR_GEN BIT(31) /* enable bit */
|
|
|
|
+/* Pre-warning limit set to 1/16 of max WDT period */
|
|
|
|
+#define LTQ_WDT_CR_PWL (0x3 << 26)
|
|
|
|
+/* set clock divider to 0x40000 */
|
|
|
|
+#define LTQ_WDT_CR_CLKDIV (0x3 << 24)
|
|
|
|
+#define LTQ_WDT_CR_PW_MASK GENMASK(23, 16) /* Password field */
|
|
|
|
+#define LTQ_WDT_CR_MAX_TIMEOUT ((1 << 16) - 1) /* The reload field is 16 bit */
|
|
|
|
+#define LTQ_WDT_SR 0x8 /* watchdog status register */
|
|
|
|
+#define LTQ_WDT_SR_EN BIT(31) /* Enable */
|
|
|
|
+#define LTQ_WDT_SR_VALUE_MASK GENMASK(15, 0) /* Timer value */
|
|
|
|
|
|
-#define LTQ_WDT_SR_EN (0x1 << 31) /* enable bit */
|
|
|
|
-#define LTQ_WDT_SR_PWD (0x3 << 26) /* turn on power */
|
|
|
|
-#define LTQ_WDT_SR_CLKDIV (0x3 << 24) /* turn on clock and set */
|
|
|
|
- /* divider to 0x40000 */
|
|
|
|
#define LTQ_WDT_DIVIDER 0x40000
|
|
#define LTQ_WDT_DIVIDER 0x40000
|
|
-#define LTQ_MAX_TIMEOUT ((1 << 16) - 1) /* the reload field is 16 bit */
|
|
|
|
|
|
|
|
static bool nowayout = WATCHDOG_NOWAYOUT;
|
|
static bool nowayout = WATCHDOG_NOWAYOUT;
|
|
|
|
|
|
-static void __iomem *ltq_wdt_membase;
|
|
|
|
-static unsigned long ltq_io_region_clk_rate;
|
|
|
|
|
|
+struct ltq_wdt_hw {
|
|
|
|
+ int (*bootstatus_get)(struct device *dev);
|
|
|
|
+};
|
|
|
|
|
|
-static unsigned long ltq_wdt_bootstatus;
|
|
|
|
-static unsigned long ltq_wdt_in_use;
|
|
|
|
-static int ltq_wdt_timeout = 30;
|
|
|
|
-static int ltq_wdt_ok_to_close;
|
|
|
|
|
|
+struct ltq_wdt_priv {
|
|
|
|
+ struct watchdog_device wdt;
|
|
|
|
+ void __iomem *membase;
|
|
|
|
+ unsigned long clk_rate;
|
|
|
|
+};
|
|
|
|
|
|
-static void
|
|
|
|
-ltq_wdt_enable(void)
|
|
|
|
|
|
+static u32 ltq_wdt_r32(struct ltq_wdt_priv *priv, u32 offset)
|
|
{
|
|
{
|
|
- unsigned long int timeout = ltq_wdt_timeout *
|
|
|
|
- (ltq_io_region_clk_rate / LTQ_WDT_DIVIDER) + 0x1000;
|
|
|
|
- if (timeout > LTQ_MAX_TIMEOUT)
|
|
|
|
- timeout = LTQ_MAX_TIMEOUT;
|
|
|
|
-
|
|
|
|
- /* write the first password magic */
|
|
|
|
- ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR);
|
|
|
|
- /* write the second magic plus the configuration and new timeout */
|
|
|
|
- ltq_w32(LTQ_WDT_SR_EN | LTQ_WDT_SR_PWD | LTQ_WDT_SR_CLKDIV |
|
|
|
|
- LTQ_WDT_PW2 | timeout, ltq_wdt_membase + LTQ_WDT_CR);
|
|
|
|
|
|
+ return __raw_readl(priv->membase + offset);
|
|
}
|
|
}
|
|
|
|
|
|
-static void
|
|
|
|
-ltq_wdt_disable(void)
|
|
|
|
|
|
+static void ltq_wdt_w32(struct ltq_wdt_priv *priv, u32 val, u32 offset)
|
|
{
|
|
{
|
|
- /* write the first password magic */
|
|
|
|
- ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR);
|
|
|
|
- /*
|
|
|
|
- * write the second password magic with no config
|
|
|
|
- * this turns the watchdog off
|
|
|
|
- */
|
|
|
|
- ltq_w32(LTQ_WDT_PW2, ltq_wdt_membase + LTQ_WDT_CR);
|
|
|
|
|
|
+ __raw_writel(val, priv->membase + offset);
|
|
}
|
|
}
|
|
|
|
|
|
-static ssize_t
|
|
|
|
-ltq_wdt_write(struct file *file, const char __user *data,
|
|
|
|
- size_t len, loff_t *ppos)
|
|
|
|
|
|
+static void ltq_wdt_mask(struct ltq_wdt_priv *priv, u32 clear, u32 set,
|
|
|
|
+ u32 offset)
|
|
{
|
|
{
|
|
- if (len) {
|
|
|
|
- if (!nowayout) {
|
|
|
|
- size_t i;
|
|
|
|
-
|
|
|
|
- ltq_wdt_ok_to_close = 0;
|
|
|
|
- for (i = 0; i != len; i++) {
|
|
|
|
- char c;
|
|
|
|
-
|
|
|
|
- if (get_user(c, data + i))
|
|
|
|
- return -EFAULT;
|
|
|
|
- if (c == 'V')
|
|
|
|
- ltq_wdt_ok_to_close = 1;
|
|
|
|
- else
|
|
|
|
- ltq_wdt_ok_to_close = 0;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- ltq_wdt_enable();
|
|
|
|
- }
|
|
|
|
|
|
+ u32 val = ltq_wdt_r32(priv, offset);
|
|
|
|
|
|
- return len;
|
|
|
|
|
|
+ val &= ~(clear);
|
|
|
|
+ val |= set;
|
|
|
|
+ ltq_wdt_w32(priv, val, offset);
|
|
}
|
|
}
|
|
|
|
|
|
-static struct watchdog_info ident = {
|
|
|
|
|
|
+static struct ltq_wdt_priv *ltq_wdt_get_priv(struct watchdog_device *wdt)
|
|
|
|
+{
|
|
|
|
+ return container_of(wdt, struct ltq_wdt_priv, wdt);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct watchdog_info ltq_wdt_info = {
|
|
.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
|
|
.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
|
|
- WDIOF_CARDRESET,
|
|
|
|
|
|
+ WDIOF_CARDRESET,
|
|
.identity = "ltq_wdt",
|
|
.identity = "ltq_wdt",
|
|
};
|
|
};
|
|
|
|
|
|
-static long
|
|
|
|
-ltq_wdt_ioctl(struct file *file,
|
|
|
|
- unsigned int cmd, unsigned long arg)
|
|
|
|
|
|
+static int ltq_wdt_start(struct watchdog_device *wdt)
|
|
{
|
|
{
|
|
- int ret = -ENOTTY;
|
|
|
|
-
|
|
|
|
- switch (cmd) {
|
|
|
|
- case WDIOC_GETSUPPORT:
|
|
|
|
- ret = copy_to_user((struct watchdog_info __user *)arg, &ident,
|
|
|
|
- sizeof(ident)) ? -EFAULT : 0;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case WDIOC_GETBOOTSTATUS:
|
|
|
|
- ret = put_user(ltq_wdt_bootstatus, (int __user *)arg);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case WDIOC_GETSTATUS:
|
|
|
|
- ret = put_user(0, (int __user *)arg);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case WDIOC_SETTIMEOUT:
|
|
|
|
- ret = get_user(ltq_wdt_timeout, (int __user *)arg);
|
|
|
|
- if (!ret)
|
|
|
|
- ltq_wdt_enable();
|
|
|
|
- /* intentional drop through */
|
|
|
|
- case WDIOC_GETTIMEOUT:
|
|
|
|
- ret = put_user(ltq_wdt_timeout, (int __user *)arg);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case WDIOC_KEEPALIVE:
|
|
|
|
- ltq_wdt_enable();
|
|
|
|
- ret = 0;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- return ret;
|
|
|
|
|
|
+ struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt);
|
|
|
|
+ u32 timeout;
|
|
|
|
+
|
|
|
|
+ timeout = wdt->timeout * priv->clk_rate;
|
|
|
|
+
|
|
|
|
+ ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK, LTQ_WDT_CR_PW1, LTQ_WDT_CR);
|
|
|
|
+ /* write the second magic plus the configuration and new timeout */
|
|
|
|
+ ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK | LTQ_WDT_CR_MAX_TIMEOUT,
|
|
|
|
+ LTQ_WDT_CR_GEN | LTQ_WDT_CR_PWL | LTQ_WDT_CR_CLKDIV |
|
|
|
|
+ LTQ_WDT_CR_PW2 | timeout,
|
|
|
|
+ LTQ_WDT_CR);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static int
|
|
|
|
-ltq_wdt_open(struct inode *inode, struct file *file)
|
|
|
|
|
|
+static int ltq_wdt_stop(struct watchdog_device *wdt)
|
|
{
|
|
{
|
|
- if (test_and_set_bit(0, <q_wdt_in_use))
|
|
|
|
- return -EBUSY;
|
|
|
|
- ltq_wdt_in_use = 1;
|
|
|
|
- ltq_wdt_enable();
|
|
|
|
|
|
+ struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt);
|
|
|
|
|
|
- return nonseekable_open(inode, file);
|
|
|
|
|
|
+ ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK, LTQ_WDT_CR_PW1, LTQ_WDT_CR);
|
|
|
|
+ ltq_wdt_mask(priv, LTQ_WDT_CR_GEN | LTQ_WDT_CR_PW_MASK,
|
|
|
|
+ LTQ_WDT_CR_PW2, LTQ_WDT_CR);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static int
|
|
|
|
-ltq_wdt_release(struct inode *inode, struct file *file)
|
|
|
|
|
|
+static int ltq_wdt_ping(struct watchdog_device *wdt)
|
|
{
|
|
{
|
|
- if (ltq_wdt_ok_to_close)
|
|
|
|
- ltq_wdt_disable();
|
|
|
|
- else
|
|
|
|
- pr_err("watchdog closed without warning\n");
|
|
|
|
- ltq_wdt_ok_to_close = 0;
|
|
|
|
- clear_bit(0, <q_wdt_in_use);
|
|
|
|
|
|
+ struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt);
|
|
|
|
+ u32 timeout;
|
|
|
|
+
|
|
|
|
+ timeout = wdt->timeout * priv->clk_rate;
|
|
|
|
+
|
|
|
|
+ ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK, LTQ_WDT_CR_PW1, LTQ_WDT_CR);
|
|
|
|
+ /* write the second magic plus the configuration and new timeout */
|
|
|
|
+ ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK | LTQ_WDT_CR_MAX_TIMEOUT,
|
|
|
|
+ LTQ_WDT_CR_PW2 | timeout, LTQ_WDT_CR);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static const struct file_operations ltq_wdt_fops = {
|
|
|
|
- .owner = THIS_MODULE,
|
|
|
|
- .write = ltq_wdt_write,
|
|
|
|
- .unlocked_ioctl = ltq_wdt_ioctl,
|
|
|
|
- .open = ltq_wdt_open,
|
|
|
|
- .release = ltq_wdt_release,
|
|
|
|
- .llseek = no_llseek,
|
|
|
|
-};
|
|
|
|
|
|
+static unsigned int ltq_wdt_get_timeleft(struct watchdog_device *wdt)
|
|
|
|
+{
|
|
|
|
+ struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt);
|
|
|
|
+ u64 timeout;
|
|
|
|
|
|
-static struct miscdevice ltq_wdt_miscdev = {
|
|
|
|
- .minor = WATCHDOG_MINOR,
|
|
|
|
- .name = "watchdog",
|
|
|
|
- .fops = <q_wdt_fops,
|
|
|
|
-};
|
|
|
|
|
|
+ timeout = ltq_wdt_r32(priv, LTQ_WDT_SR) & LTQ_WDT_SR_VALUE_MASK;
|
|
|
|
+ return do_div(timeout, priv->clk_rate);
|
|
|
|
+}
|
|
|
|
|
|
-typedef int (*ltq_wdt_bootstatus_set)(struct platform_device *pdev);
|
|
|
|
|
|
+static const struct watchdog_ops ltq_wdt_ops = {
|
|
|
|
+ .owner = THIS_MODULE,
|
|
|
|
+ .start = ltq_wdt_start,
|
|
|
|
+ .stop = ltq_wdt_stop,
|
|
|
|
+ .ping = ltq_wdt_ping,
|
|
|
|
+ .get_timeleft = ltq_wdt_get_timeleft,
|
|
|
|
+};
|
|
|
|
|
|
-static int ltq_wdt_bootstatus_xrx(struct platform_device *pdev)
|
|
|
|
|
|
+static int ltq_wdt_xrx_bootstatus_get(struct device *dev)
|
|
{
|
|
{
|
|
- struct device *dev = &pdev->dev;
|
|
|
|
struct regmap *rcu_regmap;
|
|
struct regmap *rcu_regmap;
|
|
u32 val;
|
|
u32 val;
|
|
int err;
|
|
int err;
|
|
@@ -216,14 +172,13 @@ static int ltq_wdt_bootstatus_xrx(struct platform_device *pdev)
|
|
return err;
|
|
return err;
|
|
|
|
|
|
if (val & LTQ_XRX_RCU_RST_STAT_WDT)
|
|
if (val & LTQ_XRX_RCU_RST_STAT_WDT)
|
|
- ltq_wdt_bootstatus = WDIOF_CARDRESET;
|
|
|
|
|
|
+ return WDIOF_CARDRESET;
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static int ltq_wdt_bootstatus_falcon(struct platform_device *pdev)
|
|
|
|
|
|
+static int ltq_wdt_falcon_bootstatus_get(struct device *dev)
|
|
{
|
|
{
|
|
- struct device *dev = &pdev->dev;
|
|
|
|
struct regmap *rcu_regmap;
|
|
struct regmap *rcu_regmap;
|
|
u32 val;
|
|
u32 val;
|
|
int err;
|
|
int err;
|
|
@@ -238,62 +193,90 @@ static int ltq_wdt_bootstatus_falcon(struct platform_device *pdev)
|
|
return err;
|
|
return err;
|
|
|
|
|
|
if ((val & LTQ_FALCON_SYS1_CPU0RS_MASK) == LTQ_FALCON_SYS1_CPU0RS_WDT)
|
|
if ((val & LTQ_FALCON_SYS1_CPU0RS_MASK) == LTQ_FALCON_SYS1_CPU0RS_WDT)
|
|
- ltq_wdt_bootstatus = WDIOF_CARDRESET;
|
|
|
|
|
|
+ return WDIOF_CARDRESET;
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static int
|
|
|
|
-ltq_wdt_probe(struct platform_device *pdev)
|
|
|
|
|
|
+static int ltq_wdt_probe(struct platform_device *pdev)
|
|
{
|
|
{
|
|
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
|
|
+ struct device *dev = &pdev->dev;
|
|
|
|
+ struct ltq_wdt_priv *priv;
|
|
|
|
+ struct watchdog_device *wdt;
|
|
|
|
+ struct resource *res;
|
|
struct clk *clk;
|
|
struct clk *clk;
|
|
- ltq_wdt_bootstatus_set ltq_wdt_bootstatus_set;
|
|
|
|
|
|
+ const struct ltq_wdt_hw *ltq_wdt_hw;
|
|
int ret;
|
|
int ret;
|
|
|
|
+ u32 status;
|
|
|
|
|
|
- ltq_wdt_membase = devm_ioremap_resource(&pdev->dev, res);
|
|
|
|
- if (IS_ERR(ltq_wdt_membase))
|
|
|
|
- return PTR_ERR(ltq_wdt_membase);
|
|
|
|
|
|
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
|
|
|
+ if (!priv)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
|
|
- ltq_wdt_bootstatus_set = of_device_get_match_data(&pdev->dev);
|
|
|
|
- if (ltq_wdt_bootstatus_set) {
|
|
|
|
- ret = ltq_wdt_bootstatus_set(pdev);
|
|
|
|
- if (ret)
|
|
|
|
- return ret;
|
|
|
|
- }
|
|
|
|
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
+ priv->membase = devm_ioremap_resource(dev, res);
|
|
|
|
+ if (IS_ERR(priv->membase))
|
|
|
|
+ return PTR_ERR(priv->membase);
|
|
|
|
|
|
/* we do not need to enable the clock as it is always running */
|
|
/* we do not need to enable the clock as it is always running */
|
|
clk = clk_get_io();
|
|
clk = clk_get_io();
|
|
- if (IS_ERR(clk)) {
|
|
|
|
- dev_err(&pdev->dev, "Failed to get clock\n");
|
|
|
|
- return -ENOENT;
|
|
|
|
|
|
+ priv->clk_rate = clk_get_rate(clk) / LTQ_WDT_DIVIDER;
|
|
|
|
+ if (!priv->clk_rate) {
|
|
|
|
+ dev_err(dev, "clock rate less than divider %i\n",
|
|
|
|
+ LTQ_WDT_DIVIDER);
|
|
|
|
+ return -EINVAL;
|
|
}
|
|
}
|
|
- ltq_io_region_clk_rate = clk_get_rate(clk);
|
|
|
|
- clk_put(clk);
|
|
|
|
|
|
|
|
- dev_info(&pdev->dev, "Init done\n");
|
|
|
|
- return misc_register(<q_wdt_miscdev);
|
|
|
|
-}
|
|
|
|
|
|
+ wdt = &priv->wdt;
|
|
|
|
+ wdt->info = <q_wdt_info;
|
|
|
|
+ wdt->ops = <q_wdt_ops;
|
|
|
|
+ wdt->min_timeout = 1;
|
|
|
|
+ wdt->max_timeout = LTQ_WDT_CR_MAX_TIMEOUT / priv->clk_rate;
|
|
|
|
+ wdt->timeout = wdt->max_timeout;
|
|
|
|
+ wdt->parent = dev;
|
|
|
|
+
|
|
|
|
+ ltq_wdt_hw = of_device_get_match_data(dev);
|
|
|
|
+ if (ltq_wdt_hw && ltq_wdt_hw->bootstatus_get) {
|
|
|
|
+ ret = ltq_wdt_hw->bootstatus_get(dev);
|
|
|
|
+ if (ret >= 0)
|
|
|
|
+ wdt->bootstatus = ret;
|
|
|
|
+ }
|
|
|
|
|
|
-static int
|
|
|
|
-ltq_wdt_remove(struct platform_device *pdev)
|
|
|
|
-{
|
|
|
|
- misc_deregister(<q_wdt_miscdev);
|
|
|
|
|
|
+ watchdog_set_nowayout(wdt, nowayout);
|
|
|
|
+ watchdog_init_timeout(wdt, 0, dev);
|
|
|
|
+
|
|
|
|
+ status = ltq_wdt_r32(priv, LTQ_WDT_SR);
|
|
|
|
+ if (status & LTQ_WDT_SR_EN) {
|
|
|
|
+ /*
|
|
|
|
+ * If the watchdog is already running overwrite it with our
|
|
|
|
+ * new settings. Stop is not needed as the start call will
|
|
|
|
+ * replace all settings anyway.
|
|
|
|
+ */
|
|
|
|
+ ltq_wdt_start(wdt);
|
|
|
|
+ set_bit(WDOG_HW_RUNNING, &wdt->status);
|
|
|
|
+ }
|
|
|
|
|
|
- return 0;
|
|
|
|
|
|
+ return devm_watchdog_register_device(dev, wdt);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static const struct ltq_wdt_hw ltq_wdt_xrx100 = {
|
|
|
|
+ .bootstatus_get = ltq_wdt_xrx_bootstatus_get,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const struct ltq_wdt_hw ltq_wdt_falcon = {
|
|
|
|
+ .bootstatus_get = ltq_wdt_falcon_bootstatus_get,
|
|
|
|
+};
|
|
|
|
+
|
|
static const struct of_device_id ltq_wdt_match[] = {
|
|
static const struct of_device_id ltq_wdt_match[] = {
|
|
- { .compatible = "lantiq,wdt", .data = NULL},
|
|
|
|
- { .compatible = "lantiq,xrx100-wdt", .data = ltq_wdt_bootstatus_xrx },
|
|
|
|
- { .compatible = "lantiq,falcon-wdt", .data = ltq_wdt_bootstatus_falcon },
|
|
|
|
|
|
+ { .compatible = "lantiq,wdt", .data = NULL },
|
|
|
|
+ { .compatible = "lantiq,xrx100-wdt", .data = <q_wdt_xrx100 },
|
|
|
|
+ { .compatible = "lantiq,falcon-wdt", .data = <q_wdt_falcon },
|
|
{},
|
|
{},
|
|
};
|
|
};
|
|
MODULE_DEVICE_TABLE(of, ltq_wdt_match);
|
|
MODULE_DEVICE_TABLE(of, ltq_wdt_match);
|
|
|
|
|
|
static struct platform_driver ltq_wdt_driver = {
|
|
static struct platform_driver ltq_wdt_driver = {
|
|
.probe = ltq_wdt_probe,
|
|
.probe = ltq_wdt_probe,
|
|
- .remove = ltq_wdt_remove,
|
|
|
|
.driver = {
|
|
.driver = {
|
|
.name = "wdt",
|
|
.name = "wdt",
|
|
.of_match_table = ltq_wdt_match,
|
|
.of_match_table = ltq_wdt_match,
|