linux/drivers/watchdog/aspeed_wdt.c
<<
>>
Prefs
   1/*
   2 * Copyright 2016 IBM Corporation
   3 *
   4 * Joel Stanley <joel@jms.id.au>
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License
   8 * as published by the Free Software Foundation; either version
   9 * 2 of the License, or (at your option) any later version.
  10 */
  11
  12#include <linux/delay.h>
  13#include <linux/io.h>
  14#include <linux/kernel.h>
  15#include <linux/module.h>
  16#include <linux/of.h>
  17#include <linux/platform_device.h>
  18#include <linux/watchdog.h>
  19
  20struct aspeed_wdt {
  21        struct watchdog_device  wdd;
  22        void __iomem            *base;
  23        u32                     ctrl;
  24};
  25
  26struct aspeed_wdt_config {
  27        u32 ext_pulse_width_mask;
  28};
  29
  30static const struct aspeed_wdt_config ast2400_config = {
  31        .ext_pulse_width_mask = 0xff,
  32};
  33
  34static const struct aspeed_wdt_config ast2500_config = {
  35        .ext_pulse_width_mask = 0xfffff,
  36};
  37
  38static const struct of_device_id aspeed_wdt_of_table[] = {
  39        { .compatible = "aspeed,ast2400-wdt", .data = &ast2400_config },
  40        { .compatible = "aspeed,ast2500-wdt", .data = &ast2500_config },
  41        { },
  42};
  43MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
  44
  45#define WDT_STATUS              0x00
  46#define WDT_RELOAD_VALUE        0x04
  47#define WDT_RESTART             0x08
  48#define WDT_CTRL                0x0C
  49#define   WDT_CTRL_BOOT_SECONDARY       BIT(7)
  50#define   WDT_CTRL_RESET_MODE_SOC       (0x00 << 5)
  51#define   WDT_CTRL_RESET_MODE_FULL_CHIP (0x01 << 5)
  52#define   WDT_CTRL_RESET_MODE_ARM_CPU   (0x10 << 5)
  53#define   WDT_CTRL_1MHZ_CLK             BIT(4)
  54#define   WDT_CTRL_WDT_EXT              BIT(3)
  55#define   WDT_CTRL_WDT_INTR             BIT(2)
  56#define   WDT_CTRL_RESET_SYSTEM         BIT(1)
  57#define   WDT_CTRL_ENABLE               BIT(0)
  58#define WDT_TIMEOUT_STATUS      0x10
  59#define   WDT_TIMEOUT_STATUS_BOOT_SECONDARY     BIT(1)
  60
  61/*
  62 * WDT_RESET_WIDTH controls the characteristics of the external pulse (if
  63 * enabled), specifically:
  64 *
  65 * * Pulse duration
  66 * * Drive mode: push-pull vs open-drain
  67 * * Polarity: Active high or active low
  68 *
  69 * Pulse duration configuration is available on both the AST2400 and AST2500,
  70 * though the field changes between SoCs:
  71 *
  72 * AST2400: Bits 7:0
  73 * AST2500: Bits 19:0
  74 *
  75 * This difference is captured in struct aspeed_wdt_config.
  76 *
  77 * The AST2500 exposes the drive mode and polarity options, but not in a
  78 * regular fashion. For read purposes, bit 31 represents active high or low,
  79 * and bit 30 represents push-pull or open-drain. With respect to write, magic
  80 * values need to be written to the top byte to change the state of the drive
  81 * mode and polarity bits. Any other value written to the top byte has no
  82 * effect on the state of the drive mode or polarity bits. However, the pulse
  83 * width value must be preserved (as desired) if written.
  84 */
  85#define WDT_RESET_WIDTH         0x18
  86#define   WDT_RESET_WIDTH_ACTIVE_HIGH   BIT(31)
  87#define     WDT_ACTIVE_HIGH_MAGIC       (0xA5 << 24)
  88#define     WDT_ACTIVE_LOW_MAGIC        (0x5A << 24)
  89#define   WDT_RESET_WIDTH_PUSH_PULL     BIT(30)
  90#define     WDT_PUSH_PULL_MAGIC         (0xA8 << 24)
  91#define     WDT_OPEN_DRAIN_MAGIC        (0x8A << 24)
  92
  93#define WDT_RESTART_MAGIC       0x4755
  94
  95/* 32 bits at 1MHz, in milliseconds */
  96#define WDT_MAX_TIMEOUT_MS      4294967
  97#define WDT_DEFAULT_TIMEOUT     30
  98#define WDT_RATE_1MHZ           1000000
  99
 100static struct aspeed_wdt *to_aspeed_wdt(struct watchdog_device *wdd)
 101{
 102        return container_of(wdd, struct aspeed_wdt, wdd);
 103}
 104
 105static void aspeed_wdt_enable(struct aspeed_wdt *wdt, int count)
 106{
 107        wdt->ctrl |= WDT_CTRL_ENABLE;
 108
 109        writel(0, wdt->base + WDT_CTRL);
 110        writel(count, wdt->base + WDT_RELOAD_VALUE);
 111        writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART);
 112        writel(wdt->ctrl, wdt->base + WDT_CTRL);
 113}
 114
 115static int aspeed_wdt_start(struct watchdog_device *wdd)
 116{
 117        struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
 118
 119        aspeed_wdt_enable(wdt, wdd->timeout * WDT_RATE_1MHZ);
 120
 121        return 0;
 122}
 123
 124static int aspeed_wdt_stop(struct watchdog_device *wdd)
 125{
 126        struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
 127
 128        wdt->ctrl &= ~WDT_CTRL_ENABLE;
 129        writel(wdt->ctrl, wdt->base + WDT_CTRL);
 130
 131        return 0;
 132}
 133
 134static int aspeed_wdt_ping(struct watchdog_device *wdd)
 135{
 136        struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
 137
 138        writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART);
 139
 140        return 0;
 141}
 142
 143static int aspeed_wdt_set_timeout(struct watchdog_device *wdd,
 144                                  unsigned int timeout)
 145{
 146        struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
 147        u32 actual;
 148
 149        wdd->timeout = timeout;
 150
 151        actual = min(timeout, wdd->max_hw_heartbeat_ms * 1000);
 152
 153        writel(actual * WDT_RATE_1MHZ, wdt->base + WDT_RELOAD_VALUE);
 154        writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART);
 155
 156        return 0;
 157}
 158
 159static int aspeed_wdt_restart(struct watchdog_device *wdd,
 160                              unsigned long action, void *data)
 161{
 162        struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
 163
 164        wdt->ctrl &= ~WDT_CTRL_BOOT_SECONDARY;
 165        aspeed_wdt_enable(wdt, 128 * WDT_RATE_1MHZ / 1000);
 166
 167        mdelay(1000);
 168
 169        return 0;
 170}
 171
 172static const struct watchdog_ops aspeed_wdt_ops = {
 173        .start          = aspeed_wdt_start,
 174        .stop           = aspeed_wdt_stop,
 175        .ping           = aspeed_wdt_ping,
 176        .set_timeout    = aspeed_wdt_set_timeout,
 177        .restart        = aspeed_wdt_restart,
 178        .owner          = THIS_MODULE,
 179};
 180
 181static const struct watchdog_info aspeed_wdt_info = {
 182        .options        = WDIOF_KEEPALIVEPING
 183                        | WDIOF_MAGICCLOSE
 184                        | WDIOF_SETTIMEOUT,
 185        .identity       = KBUILD_MODNAME,
 186};
 187
 188static int aspeed_wdt_probe(struct platform_device *pdev)
 189{
 190        const struct aspeed_wdt_config *config;
 191        const struct of_device_id *ofdid;
 192        struct aspeed_wdt *wdt;
 193        struct resource *res;
 194        struct device_node *np;
 195        const char *reset_type;
 196        u32 duration;
 197        u32 status;
 198        int ret;
 199
 200        wdt = devm_kzalloc(&pdev->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(&pdev->dev, res);
 206        if (IS_ERR(wdt->base))
 207                return PTR_ERR(wdt->base);
 208
 209        /*
 210         * The ast2400 wdt can run at PCLK, or 1MHz. The ast2500 only
 211         * runs at 1MHz. We chose to always run at 1MHz, as there's no
 212         * good reason to have a faster watchdog counter.
 213         */
 214        wdt->wdd.info = &aspeed_wdt_info;
 215        wdt->wdd.ops = &aspeed_wdt_ops;
 216        wdt->wdd.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT_MS;
 217        wdt->wdd.parent = &pdev->dev;
 218
 219        wdt->wdd.timeout = WDT_DEFAULT_TIMEOUT;
 220        watchdog_init_timeout(&wdt->wdd, 0, &pdev->dev);
 221
 222        np = pdev->dev.of_node;
 223
 224        ofdid = of_match_node(aspeed_wdt_of_table, np);
 225        if (!ofdid)
 226                return -EINVAL;
 227        config = ofdid->data;
 228
 229        wdt->ctrl = WDT_CTRL_1MHZ_CLK;
 230
 231        /*
 232         * Control reset on a per-device basis to ensure the
 233         * host is not affected by a BMC reboot
 234         */
 235        ret = of_property_read_string(np, "aspeed,reset-type", &reset_type);
 236        if (ret) {
 237                wdt->ctrl |= WDT_CTRL_RESET_MODE_SOC | WDT_CTRL_RESET_SYSTEM;
 238        } else {
 239                if (!strcmp(reset_type, "cpu"))
 240                        wdt->ctrl |= WDT_CTRL_RESET_MODE_ARM_CPU |
 241                                     WDT_CTRL_RESET_SYSTEM;
 242                else if (!strcmp(reset_type, "soc"))
 243                        wdt->ctrl |= WDT_CTRL_RESET_MODE_SOC |
 244                                     WDT_CTRL_RESET_SYSTEM;
 245                else if (!strcmp(reset_type, "system"))
 246                        wdt->ctrl |= WDT_CTRL_RESET_MODE_FULL_CHIP |
 247                                     WDT_CTRL_RESET_SYSTEM;
 248                else if (strcmp(reset_type, "none"))
 249                        return -EINVAL;
 250        }
 251        if (of_property_read_bool(np, "aspeed,external-signal"))
 252                wdt->ctrl |= WDT_CTRL_WDT_EXT;
 253        if (of_property_read_bool(np, "aspeed,alt-boot"))
 254                wdt->ctrl |= WDT_CTRL_BOOT_SECONDARY;
 255
 256        if (readl(wdt->base + WDT_CTRL) & WDT_CTRL_ENABLE)  {
 257                /*
 258                 * The watchdog is running, but invoke aspeed_wdt_start() to
 259                 * write wdt->ctrl to WDT_CTRL to ensure the watchdog's
 260                 * configuration conforms to the driver's expectations.
 261                 * Primarily, ensure we're using the 1MHz clock source.
 262                 */
 263                aspeed_wdt_start(&wdt->wdd);
 264                set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
 265        }
 266
 267        if (of_device_is_compatible(np, "aspeed,ast2500-wdt")) {
 268                u32 reg = readl(wdt->base + WDT_RESET_WIDTH);
 269
 270                reg &= config->ext_pulse_width_mask;
 271                if (of_property_read_bool(np, "aspeed,ext-push-pull"))
 272                        reg |= WDT_PUSH_PULL_MAGIC;
 273                else
 274                        reg |= WDT_OPEN_DRAIN_MAGIC;
 275
 276                writel(reg, wdt->base + WDT_RESET_WIDTH);
 277
 278                reg &= config->ext_pulse_width_mask;
 279                if (of_property_read_bool(np, "aspeed,ext-active-high"))
 280                        reg |= WDT_ACTIVE_HIGH_MAGIC;
 281                else
 282                        reg |= WDT_ACTIVE_LOW_MAGIC;
 283
 284                writel(reg, wdt->base + WDT_RESET_WIDTH);
 285        }
 286
 287        if (!of_property_read_u32(np, "aspeed,ext-pulse-duration", &duration)) {
 288                u32 max_duration = config->ext_pulse_width_mask + 1;
 289
 290                if (duration == 0 || duration > max_duration) {
 291                        dev_err(&pdev->dev, "Invalid pulse duration: %uus\n",
 292                                        duration);
 293                        duration = max(1U, min(max_duration, duration));
 294                        dev_info(&pdev->dev, "Pulse duration set to %uus\n",
 295                                        duration);
 296                }
 297
 298                /*
 299                 * The watchdog is always configured with a 1MHz source, so
 300                 * there is no need to scale the microsecond value. However we
 301                 * need to offset it - from the datasheet:
 302                 *
 303                 * "This register decides the asserting duration of wdt_ext and
 304                 * wdt_rstarm signal. The default value is 0xFF. It means the
 305                 * default asserting duration of wdt_ext and wdt_rstarm is
 306                 * 256us."
 307                 *
 308                 * This implies a value of 0 gives a 1us pulse.
 309                 */
 310                writel(duration - 1, wdt->base + WDT_RESET_WIDTH);
 311        }
 312
 313        status = readl(wdt->base + WDT_TIMEOUT_STATUS);
 314        if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY)
 315                wdt->wdd.bootstatus = WDIOF_CARDRESET;
 316
 317        ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdd);
 318        if (ret) {
 319                dev_err(&pdev->dev, "failed to register\n");
 320                return ret;
 321        }
 322
 323        return 0;
 324}
 325
 326static struct platform_driver aspeed_watchdog_driver = {
 327        .probe = aspeed_wdt_probe,
 328        .driver = {
 329                .name = KBUILD_MODNAME,
 330                .of_match_table = of_match_ptr(aspeed_wdt_of_table),
 331        },
 332};
 333
 334static int __init aspeed_wdt_init(void)
 335{
 336        return platform_driver_register(&aspeed_watchdog_driver);
 337}
 338arch_initcall(aspeed_wdt_init);
 339
 340static void __exit aspeed_wdt_exit(void)
 341{
 342        platform_driver_unregister(&aspeed_watchdog_driver);
 343}
 344module_exit(aspeed_wdt_exit);
 345
 346MODULE_DESCRIPTION("Aspeed Watchdog Driver");
 347MODULE_LICENSE("GPL");
 348