Commit 6adb730dc2085c16c52a2f991cc1661e4a7fd6d5

Authored by Markus Mayer
Committed by Wim Van Sebroeck
1 parent 2c34d59916

watchdog: bcm281xx: Watchdog Driver

This commit adds support for the watchdog timer used on the BCM281xx
family of SoCs.

Signed-off-by: Markus Mayer <markus.mayer@linaro.org>
Reviewed-by: Matt Porter <matt.porter@linaro.org>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>

Showing 4 changed files with 283 additions and 0 deletions Side-by-side Diff

arch/arm/configs/bcm_defconfig
... ... @@ -126,4 +126,6 @@
126 126 CONFIG_XZ_DEC=y
127 127 CONFIG_AVERAGE=y
128 128 CONFIG_PINCTRL_CAPRI=y
  129 +CONFIG_WATCHDOG=y
  130 +CONFIG_BCM_KONA_WDT=y
drivers/watchdog/Kconfig
... ... @@ -1139,6 +1139,18 @@
1139 1139 To compile this driver as a loadable module, choose M here.
1140 1140 The module will be called bcm2835_wdt.
1141 1141  
  1142 +config BCM_KONA_WDT
  1143 + tristate "BCM Kona Watchdog"
  1144 + depends on ARCH_BCM
  1145 + select WATCHDOG_CORE
  1146 + help
  1147 + Support for the watchdog timer on the following Broadcom BCM281xx
  1148 + family, which includes BCM11130, BCM11140, BCM11351, BCM28145 and
  1149 + BCM28155 variants.
  1150 +
  1151 + Say 'Y' or 'M' here to enable the driver. The module will be called
  1152 + bcm_kona_wdt.
  1153 +
1142 1154 config LANTIQ_WDT
1143 1155 tristate "Lantiq SoC watchdog"
1144 1156 depends on LANTIQ
drivers/watchdog/Makefile
... ... @@ -57,6 +57,7 @@
57 57 obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
58 58 obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
59 59 obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
  60 +obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
60 61  
61 62 # AVR32 Architecture
62 63 obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
drivers/watchdog/bcm_kona_wdt.c
  1 +/*
  2 + * Copyright (C) 2013 Broadcom Corporation
  3 + *
  4 + * This program is free software; you can redistribute it and/or
  5 + * modify it under the terms of the GNU General Public License as
  6 + * published by the Free Software Foundation version 2.
  7 + *
  8 + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  9 + * kind, whether express or implied; without even the implied warranty
  10 + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11 + * GNU General Public License for more details.
  12 + */
  13 +
  14 +#include <linux/delay.h>
  15 +#include <linux/err.h>
  16 +#include <linux/io.h>
  17 +#include <linux/module.h>
  18 +#include <linux/of_address.h>
  19 +#include <linux/platform_device.h>
  20 +#include <linux/watchdog.h>
  21 +
  22 +#define SECWDOG_CTRL_REG 0x00000000
  23 +#define SECWDOG_COUNT_REG 0x00000004
  24 +
  25 +#define SECWDOG_RESERVED_MASK 0x1dffffff
  26 +#define SECWDOG_WD_LOAD_FLAG 0x10000000
  27 +#define SECWDOG_EN_MASK 0x08000000
  28 +#define SECWDOG_SRSTEN_MASK 0x04000000
  29 +#define SECWDOG_RES_MASK 0x00f00000
  30 +#define SECWDOG_COUNT_MASK 0x000fffff
  31 +
  32 +#define SECWDOG_MAX_COUNT SECWDOG_COUNT_MASK
  33 +#define SECWDOG_CLKS_SHIFT 20
  34 +#define SECWDOG_MAX_RES 15
  35 +#define SECWDOG_DEFAULT_RESOLUTION 4
  36 +#define SECWDOG_MAX_TRY 1000
  37 +
  38 +#define SECS_TO_TICKS(x, w) ((x) << (w)->resolution)
  39 +#define TICKS_TO_SECS(x, w) ((x) >> (w)->resolution)
  40 +
  41 +#define BCM_KONA_WDT_NAME "bcm_kona_wdt"
  42 +
  43 +struct bcm_kona_wdt {
  44 + void __iomem *base;
  45 + /*
  46 + * One watchdog tick is 1/(2^resolution) seconds. Resolution can take
  47 + * the values 0-15, meaning one tick can be 1s to 30.52us. Our default
  48 + * resolution of 4 means one tick is 62.5ms.
  49 + *
  50 + * The watchdog counter is 20 bits. Depending on resolution, the maximum
  51 + * counter value of 0xfffff expires after about 12 days (resolution 0)
  52 + * down to only 32s (resolution 15). The default resolution of 4 gives
  53 + * us a maximum of about 18 hours and 12 minutes before the watchdog
  54 + * times out.
  55 + */
  56 + int resolution;
  57 + spinlock_t lock;
  58 +};
  59 +
  60 +static int secure_register_read(void __iomem *addr)
  61 +{
  62 + uint32_t val;
  63 + unsigned count = 0;
  64 +
  65 + /*
  66 + * If the WD_LOAD_FLAG is set, the watchdog counter field is being
  67 + * updated in hardware. Once the WD timer is updated in hardware, it
  68 + * gets cleared.
  69 + */
  70 + do {
  71 + if (unlikely(count > 1))
  72 + udelay(5);
  73 + val = readl_relaxed(addr);
  74 + count++;
  75 + } while ((val & SECWDOG_WD_LOAD_FLAG) && count < SECWDOG_MAX_TRY);
  76 +
  77 + /* This is the only place we return a negative value. */
  78 + if (val & SECWDOG_WD_LOAD_FLAG)
  79 + return -ETIMEDOUT;
  80 +
  81 + /* We always mask out reserved bits. */
  82 + val &= SECWDOG_RESERVED_MASK;
  83 +
  84 + return val;
  85 +}
  86 +
  87 +static int bcm_kona_wdt_ctrl_reg_modify(struct bcm_kona_wdt *wdt,
  88 + unsigned mask, unsigned newval)
  89 +{
  90 + int val;
  91 + unsigned long flags;
  92 + int ret = 0;
  93 +
  94 + spin_lock_irqsave(&wdt->lock, flags);
  95 +
  96 + val = secure_register_read(wdt->base + SECWDOG_CTRL_REG);
  97 + if (val < 0) {
  98 + ret = val;
  99 + } else {
  100 + val &= ~mask;
  101 + val |= newval;
  102 + writel_relaxed(val, wdt->base + SECWDOG_CTRL_REG);
  103 + }
  104 +
  105 + spin_unlock_irqrestore(&wdt->lock, flags);
  106 +
  107 + return ret;
  108 +}
  109 +
  110 +static int bcm_kona_wdt_set_resolution_reg(struct bcm_kona_wdt *wdt)
  111 +{
  112 + if (wdt->resolution > SECWDOG_MAX_RES)
  113 + return -EINVAL;
  114 +
  115 + return bcm_kona_wdt_ctrl_reg_modify(wdt, SECWDOG_RES_MASK,
  116 + wdt->resolution << SECWDOG_CLKS_SHIFT);
  117 +}
  118 +
  119 +static int bcm_kona_wdt_set_timeout_reg(struct watchdog_device *wdog,
  120 + unsigned watchdog_flags)
  121 +{
  122 + struct bcm_kona_wdt *wdt = watchdog_get_drvdata(wdog);
  123 +
  124 + return bcm_kona_wdt_ctrl_reg_modify(wdt, SECWDOG_COUNT_MASK,
  125 + SECS_TO_TICKS(wdog->timeout, wdt) |
  126 + watchdog_flags);
  127 +}
  128 +
  129 +static int bcm_kona_wdt_set_timeout(struct watchdog_device *wdog,
  130 + unsigned int t)
  131 +{
  132 + wdog->timeout = t;
  133 + return 0;
  134 +}
  135 +
  136 +static unsigned int bcm_kona_wdt_get_timeleft(struct watchdog_device *wdog)
  137 +{
  138 + struct bcm_kona_wdt *wdt = watchdog_get_drvdata(wdog);
  139 + int val;
  140 + unsigned long flags;
  141 +
  142 + spin_lock_irqsave(&wdt->lock, flags);
  143 + val = secure_register_read(wdt->base + SECWDOG_COUNT_REG);
  144 + spin_unlock_irqrestore(&wdt->lock, flags);
  145 +
  146 + if (val < 0)
  147 + return val;
  148 +
  149 + return TICKS_TO_SECS(val & SECWDOG_COUNT_MASK, wdt);
  150 +}
  151 +
  152 +static int bcm_kona_wdt_start(struct watchdog_device *wdog)
  153 +{
  154 + return bcm_kona_wdt_set_timeout_reg(wdog,
  155 + SECWDOG_EN_MASK | SECWDOG_SRSTEN_MASK);
  156 +}
  157 +
  158 +static int bcm_kona_wdt_stop(struct watchdog_device *wdog)
  159 +{
  160 + struct bcm_kona_wdt *wdt = watchdog_get_drvdata(wdog);
  161 +
  162 + return bcm_kona_wdt_ctrl_reg_modify(wdt, SECWDOG_EN_MASK |
  163 + SECWDOG_SRSTEN_MASK, 0);
  164 +}
  165 +
  166 +static struct watchdog_ops bcm_kona_wdt_ops = {
  167 + .owner = THIS_MODULE,
  168 + .start = bcm_kona_wdt_start,
  169 + .stop = bcm_kona_wdt_stop,
  170 + .set_timeout = bcm_kona_wdt_set_timeout,
  171 + .get_timeleft = bcm_kona_wdt_get_timeleft,
  172 +};
  173 +
  174 +static struct watchdog_info bcm_kona_wdt_info = {
  175 + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
  176 + WDIOF_KEEPALIVEPING,
  177 + .identity = "Broadcom Kona Watchdog Timer",
  178 +};
  179 +
  180 +static struct watchdog_device bcm_kona_wdt_wdd = {
  181 + .info = &bcm_kona_wdt_info,
  182 + .ops = &bcm_kona_wdt_ops,
  183 + .min_timeout = 1,
  184 + .max_timeout = SECWDOG_MAX_COUNT >> SECWDOG_DEFAULT_RESOLUTION,
  185 + .timeout = SECWDOG_MAX_COUNT >> SECWDOG_DEFAULT_RESOLUTION,
  186 +};
  187 +
  188 +static void bcm_kona_wdt_shutdown(struct platform_device *pdev)
  189 +{
  190 + bcm_kona_wdt_stop(&bcm_kona_wdt_wdd);
  191 +}
  192 +
  193 +static int bcm_kona_wdt_probe(struct platform_device *pdev)
  194 +{
  195 + struct device *dev = &pdev->dev;
  196 + struct bcm_kona_wdt *wdt;
  197 + struct resource *res;
  198 + int ret;
  199 +
  200 + wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
  201 + if (!wdt)
  202 + return -ENOMEM;
  203 +
  204 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  205 + wdt->base = devm_ioremap_resource(dev, res);
  206 + if (IS_ERR(wdt->base))
  207 + return -ENODEV;
  208 +
  209 + wdt->resolution = SECWDOG_DEFAULT_RESOLUTION;
  210 + ret = bcm_kona_wdt_set_resolution_reg(wdt);
  211 + if (ret) {
  212 + dev_err(dev, "Failed to set resolution (error: %d)", ret);
  213 + return ret;
  214 + }
  215 +
  216 + spin_lock_init(&wdt->lock);
  217 + platform_set_drvdata(pdev, wdt);
  218 + watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt);
  219 +
  220 + ret = bcm_kona_wdt_set_timeout_reg(&bcm_kona_wdt_wdd, 0);
  221 + if (ret) {
  222 + dev_err(dev, "Failed set watchdog timeout");
  223 + return ret;
  224 + }
  225 +
  226 + ret = watchdog_register_device(&bcm_kona_wdt_wdd);
  227 + if (ret) {
  228 + dev_err(dev, "Failed to register watchdog device");
  229 + return ret;
  230 + }
  231 +
  232 + dev_dbg(dev, "Broadcom Kona Watchdog Timer");
  233 +
  234 + return 0;
  235 +}
  236 +
  237 +static int bcm_kona_wdt_remove(struct platform_device *pdev)
  238 +{
  239 + bcm_kona_wdt_shutdown(pdev);
  240 + watchdog_unregister_device(&bcm_kona_wdt_wdd);
  241 + dev_dbg(&pdev->dev, "Watchdog driver disabled");
  242 +
  243 + return 0;
  244 +}
  245 +
  246 +static const struct of_device_id bcm_kona_wdt_of_match[] = {
  247 + { .compatible = "brcm,kona-wdt", },
  248 + {},
  249 +};
  250 +MODULE_DEVICE_TABLE(of, bcm_kona_wdt_of_match);
  251 +
  252 +static struct platform_driver bcm_kona_wdt_driver = {
  253 + .driver = {
  254 + .name = BCM_KONA_WDT_NAME,
  255 + .owner = THIS_MODULE,
  256 + .of_match_table = bcm_kona_wdt_of_match,
  257 + },
  258 + .probe = bcm_kona_wdt_probe,
  259 + .remove = bcm_kona_wdt_remove,
  260 + .shutdown = bcm_kona_wdt_shutdown,
  261 +};
  262 +
  263 +module_platform_driver(bcm_kona_wdt_driver);
  264 +
  265 +MODULE_ALIAS("platform:" BCM_KONA_WDT_NAME);
  266 +MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
  267 +MODULE_DESCRIPTION("Broadcom Kona Watchdog Driver");
  268 +MODULE_LICENSE("GPL v2");