linux/drivers/watchdog/aspeed_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright 2016 IBM Corporation
   4 *
   5 * Joel Stanley <joel@jms.id.au>
   6 */
   7
   8#include <linux/delay.h>
   9#include <linux/io.h>
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/of.h>
  13#include <linux/platform_device.h>
  14#include <linux/watchdog.h>
  15
  16struct aspeed_wdt {
  17        struct watchdog_device  wdd;
  18        void __iomem            *base;
  19        u32                     ctrl;
  20};
  21
  22struct aspeed_wdt_config {
  23        u32 ext_pulse_width_mask;
  24};
  25
  26static const struct aspeed_wdt_config ast2400_config = {
  27        .ext_pulse_width_mask = 0xff,
  28};
  29
  30static const struct aspeed_wdt_config ast2500_config = {
  31        .ext_pulse_width_mask = 0xfffff,
  32};
  33
  34static const struct of_device_id aspeed_wdt_of_table[] = {
  35        { .compatible = "aspeed,ast2400-wdt", .data = &ast2400_config },
  36        { .compatible = "aspeed,ast2500-wdt", .data = &ast2500_config },
  37        { .compatible = "aspeed,ast2600-wdt", .data = &ast2500_config },
  38        { },
  39};
  40MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
  41
  42#define WDT_STATUS              0x00
  43#define WDT_RELOAD_VALUE        0x04
  44#define WDT_RESTART             0x08
  45#define WDT_CTRL                0x0C
  46#define   WDT_CTRL_BOOT_SECONDARY       BIT(7)
  47#define   WDT_CTRL_RESET_MODE_SOC       (0x00 << 5)
  48#define   WDT_CTRL_RESET_MODE_FULL_CHIP (0x01 << 5)
  49#define   WDT_CTRL_RESET_MODE_ARM_CPU   (0x10 << 5)
  50#define   WDT_CTRL_1MHZ_CLK             BIT(4)
  51#define   WDT_CTRL_WDT_EXT              BIT(3)
  52#define   WDT_CTRL_WDT_INTR             BIT(2)
  53#define   WDT_CTRL_RESET_SYSTEM         BIT(1)
  54#define   WDT_CTRL_ENABLE               BIT(0)
  55#define WDT_TIMEOUT_STATUS      0x10
  56#define   WDT_TIMEOUT_STATUS_BOOT_SECONDARY     BIT(1)
  57#define WDT_CLEAR_TIMEOUT_STATUS        0x14
  58#define   WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION     BIT(0)
  59
  60/*
  61 * WDT_RESET_WIDTH controls the characteristics of the external pulse (if
  62 * enabled), specifically:
  63 *
  64 * * Pulse duration
  65 * * Drive mode: push-pull vs open-drain
  66 * * Polarity: Active high or active low
  67 *
  68 * Pulse duration configuration is available on both the AST2400 and AST2500,
  69 * though the field changes between SoCs:
  70 *
  71 * AST2400: Bits 7:0
  72 * AST2500: Bits 19:0
  73 *
  74 * This difference is captured in struct aspeed_wdt_config.
  75 *
  76 * The AST2500 exposes the drive mode and polarity options, but not in a
  77 * regular fashion. For read purposes, bit 31 represents active high or low,
  78 * and bit 30 represents push-pull or open-drain. With respect to write, magic
  79 * values need to be written to the top byte to change the state of the drive
  80 * mode and polarity bits. Any other value written to the top byte has no
  81 * effect on the state of the drive mode or polarity bits. However, the pulse
  82 * width value must be preserved (as desired) if written.
  83 */
  84#define WDT_RESET_WIDTH         0x18
  85#define   WDT_RESET_WIDTH_ACTIVE_HIGH   BIT(31)
  86#define     WDT_ACTIVE_HIGH_MAGIC       (0xA5 << 24)
  87#define     WDT_ACTIVE_LOW_MAGIC        (0x5A << 24)
  88#define   WDT_RESET_WIDTH_PUSH_PULL     BIT(30)
  89#define     WDT_PUSH_PULL_MAGIC         (0xA8 << 24)
  90#define     WDT_OPEN_DRAIN_MAGIC        (0x8A << 24)
  91
  92#define WDT_RESTART_MAGIC       0x4755
  93
  94/* 32 bits at 1MHz, in milliseconds */
  95#define WDT_MAX_TIMEOUT_MS      4294967
  96#define WDT_DEFAULT_TIMEOUT     30
  97#define WDT_RATE_1MHZ           1000000
  98
  99static struct aspeed_wdt *to_aspeed_wdt(struct watchdog_device *wdd)
 100{
 101        return container_of(wdd, struct aspeed_wdt, wdd);
 102}
 103
 104static void aspeed_wdt_enable(struct aspeed_wdt *wdt, int count)
 105{
 106        wdt->ctrl |= WDT_CTRL_ENABLE;
 107
 108        writel(0, wdt->base + WDT_CTRL);
 109        writel(count, wdt->base + WDT_RELOAD_VALUE);
 110        writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART);
 111        writel(wdt->ctrl, wdt->base + WDT_CTRL);
 112}
 113
 114static int aspeed_wdt_start(struct watchdog_device *wdd)
 115{
 116        struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
 117
 118        aspeed_wdt_enable(wdt, wdd->timeout * WDT_RATE_1MHZ);
 119
 120        return 0;
 121}
 122
 123static int aspeed_wdt_stop(struct watchdog_device *wdd)
 124{
 125        struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
 126
 127        wdt->ctrl &= ~WDT_CTRL_ENABLE;
 128        writel(wdt->ctrl, wdt->base + WDT_CTRL);
 129
 130        return 0;
 131}
 132
 133static int aspeed_wdt_ping(struct watchdog_device *wdd)
 134{
 135        struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
 136
 137        writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART);
 138
 139        return 0;
 140}
 141
 142static int aspeed_wdt_set_timeout(struct watchdog_device *wdd,
 143                                  unsigned int timeout)
 144{
 145        struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
 146        u32 actual;
 147
 148        wdd->timeout = timeout;
 149
 150        actual = min(timeout, wdd->max_hw_heartbeat_ms / 1000);
 151
 152        writel(actual * WDT_RATE_1MHZ, wdt->base + WDT_RELOAD_VALUE);
 153        writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART);
 154
 155        return 0;
 156}
 157
 158static int aspeed_wdt_restart(struct watchdog_device *wdd,
 159                              unsigned long action, void *data)
 160{
 161        struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
 162
 163        wdt->ctrl &= ~WDT_CTRL_BOOT_SECONDARY;
 164        aspeed_wdt_enable(wdt, 128 * WDT_RATE_1MHZ / 1000);
 165
 166        mdelay(1000);
 167
 168        return 0;
 169}
 170
 171/* access_cs0 shows if cs0 is accessible, hence the reverted bit */
 172static ssize_t access_cs0_show(struct device *dev,
 173                               struct device_attribute *attr, char *buf)
 174{
 175        struct aspeed_wdt *wdt = dev_get_drvdata(dev);
 176        u32 status = readl(wdt->base + WDT_TIMEOUT_STATUS);
 177
 178        return sysfs_emit(buf, "%u\n",
 179                          !(status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY));
 180}
 181
 182static ssize_t access_cs0_store(struct device *dev,
 183                                struct device_attribute *attr, const char *buf,
 184                                size_t size)
 185{
 186        struct aspeed_wdt *wdt = dev_get_drvdata(dev);
 187        unsigned long val;
 188
 189        if (kstrtoul(buf, 10, &val))
 190                return -EINVAL;
 191
 192        if (val)
 193                writel(WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION,
 194                       wdt->base + WDT_CLEAR_TIMEOUT_STATUS);
 195
 196        return size;
 197}
 198
 199/*
 200 * This attribute exists only if the system has booted from the alternate
 201 * flash with 'alt-boot' option.
 202 *
 203 * At alternate flash the 'access_cs0' sysfs node provides:
 204 *   ast2400: a way to get access to the primary SPI flash chip at CS0
 205 *            after booting from the alternate chip at CS1.
 206 *   ast2500: a way to restore the normal address mapping from
 207 *            (CS0->CS1, CS1->CS0) to (CS0->CS0, CS1->CS1).
 208 *
 209 * Clearing the boot code selection and timeout counter also resets to the
 210 * initial state the chip select line mapping. When the SoC is in normal
 211 * mapping state (i.e. booted from CS0), clearing those bits does nothing for
 212 * both versions of the SoC. For alternate boot mode (booted from CS1 due to
 213 * wdt2 expiration) the behavior differs as described above.
 214 *
 215 * This option can be used with wdt2 (watchdog1) only.
 216 */
 217static DEVICE_ATTR_RW(access_cs0);
 218
 219static struct attribute *bswitch_attrs[] = {
 220        &dev_attr_access_cs0.attr,
 221        NULL
 222};
 223ATTRIBUTE_GROUPS(bswitch);
 224
 225static const struct watchdog_ops aspeed_wdt_ops = {
 226        .start          = aspeed_wdt_start,
 227        .stop           = aspeed_wdt_stop,
 228        .ping           = aspeed_wdt_ping,
 229        .set_timeout    = aspeed_wdt_set_timeout,
 230        .restart        = aspeed_wdt_restart,
 231        .owner          = THIS_MODULE,
 232};
 233
 234static const struct watchdog_info aspeed_wdt_info = {
 235        .options        = WDIOF_KEEPALIVEPING
 236                        | WDIOF_MAGICCLOSE
 237                        | WDIOF_SETTIMEOUT,
 238        .identity       = KBUILD_MODNAME,
 239};
 240
 241static int aspeed_wdt_probe(struct platform_device *pdev)
 242{
 243        struct device *dev = &pdev->dev;
 244        const struct aspeed_wdt_config *config;
 245        const struct of_device_id *ofdid;
 246        struct aspeed_wdt *wdt;
 247        struct device_node *np;
 248        const char *reset_type;
 249        u32 duration;
 250        u32 status;
 251        int ret;
 252
 253        wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
 254        if (!wdt)
 255                return -ENOMEM;
 256
 257        wdt->base = devm_platform_ioremap_resource(pdev, 0);
 258        if (IS_ERR(wdt->base))
 259                return PTR_ERR(wdt->base);
 260
 261        wdt->wdd.info = &aspeed_wdt_info;
 262        wdt->wdd.ops = &aspeed_wdt_ops;
 263        wdt->wdd.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT_MS;
 264        wdt->wdd.parent = dev;
 265
 266        wdt->wdd.timeout = WDT_DEFAULT_TIMEOUT;
 267        watchdog_init_timeout(&wdt->wdd, 0, dev);
 268
 269        np = dev->of_node;
 270
 271        ofdid = of_match_node(aspeed_wdt_of_table, np);
 272        if (!ofdid)
 273                return -EINVAL;
 274        config = ofdid->data;
 275
 276        /*
 277         * On clock rates:
 278         *  - ast2400 wdt can run at PCLK, or 1MHz
 279         *  - ast2500 only runs at 1MHz, hard coding bit 4 to 1
 280         *  - ast2600 always runs at 1MHz
 281         *
 282         * Set the ast2400 to run at 1MHz as it simplifies the driver.
 283         */
 284        if (of_device_is_compatible(np, "aspeed,ast2400-wdt"))
 285                wdt->ctrl = WDT_CTRL_1MHZ_CLK;
 286
 287        /*
 288         * Control reset on a per-device basis to ensure the
 289         * host is not affected by a BMC reboot
 290         */
 291        ret = of_property_read_string(np, "aspeed,reset-type", &reset_type);
 292        if (ret) {
 293                wdt->ctrl |= WDT_CTRL_RESET_MODE_SOC | WDT_CTRL_RESET_SYSTEM;
 294        } else {
 295                if (!strcmp(reset_type, "cpu"))
 296                        wdt->ctrl |= WDT_CTRL_RESET_MODE_ARM_CPU |
 297                                     WDT_CTRL_RESET_SYSTEM;
 298                else if (!strcmp(reset_type, "soc"))
 299                        wdt->ctrl |= WDT_CTRL_RESET_MODE_SOC |
 300                                     WDT_CTRL_RESET_SYSTEM;
 301                else if (!strcmp(reset_type, "system"))
 302                        wdt->ctrl |= WDT_CTRL_RESET_MODE_FULL_CHIP |
 303                                     WDT_CTRL_RESET_SYSTEM;
 304                else if (strcmp(reset_type, "none"))
 305                        return -EINVAL;
 306        }
 307        if (of_property_read_bool(np, "aspeed,external-signal"))
 308                wdt->ctrl |= WDT_CTRL_WDT_EXT;
 309        if (of_property_read_bool(np, "aspeed,alt-boot"))
 310                wdt->ctrl |= WDT_CTRL_BOOT_SECONDARY;
 311
 312        if (readl(wdt->base + WDT_CTRL) & WDT_CTRL_ENABLE)  {
 313                /*
 314                 * The watchdog is running, but invoke aspeed_wdt_start() to
 315                 * write wdt->ctrl to WDT_CTRL to ensure the watchdog's
 316                 * configuration conforms to the driver's expectations.
 317                 * Primarily, ensure we're using the 1MHz clock source.
 318                 */
 319                aspeed_wdt_start(&wdt->wdd);
 320                set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
 321        }
 322
 323        if ((of_device_is_compatible(np, "aspeed,ast2500-wdt")) ||
 324                (of_device_is_compatible(np, "aspeed,ast2600-wdt"))) {
 325                u32 reg = readl(wdt->base + WDT_RESET_WIDTH);
 326
 327                reg &= config->ext_pulse_width_mask;
 328                if (of_property_read_bool(np, "aspeed,ext-push-pull"))
 329                        reg |= WDT_PUSH_PULL_MAGIC;
 330                else
 331                        reg |= WDT_OPEN_DRAIN_MAGIC;
 332
 333                writel(reg, wdt->base + WDT_RESET_WIDTH);
 334
 335                reg &= config->ext_pulse_width_mask;
 336                if (of_property_read_bool(np, "aspeed,ext-active-high"))
 337                        reg |= WDT_ACTIVE_HIGH_MAGIC;
 338                else
 339                        reg |= WDT_ACTIVE_LOW_MAGIC;
 340
 341                writel(reg, wdt->base + WDT_RESET_WIDTH);
 342        }
 343
 344        if (!of_property_read_u32(np, "aspeed,ext-pulse-duration", &duration)) {
 345                u32 max_duration = config->ext_pulse_width_mask + 1;
 346
 347                if (duration == 0 || duration > max_duration) {
 348                        dev_err(dev, "Invalid pulse duration: %uus\n",
 349                                duration);
 350                        duration = max(1U, min(max_duration, duration));
 351                        dev_info(dev, "Pulse duration set to %uus\n",
 352                                 duration);
 353                }
 354
 355                /*
 356                 * The watchdog is always configured with a 1MHz source, so
 357                 * there is no need to scale the microsecond value. However we
 358                 * need to offset it - from the datasheet:
 359                 *
 360                 * "This register decides the asserting duration of wdt_ext and
 361                 * wdt_rstarm signal. The default value is 0xFF. It means the
 362                 * default asserting duration of wdt_ext and wdt_rstarm is
 363                 * 256us."
 364                 *
 365                 * This implies a value of 0 gives a 1us pulse.
 366                 */
 367                writel(duration - 1, wdt->base + WDT_RESET_WIDTH);
 368        }
 369
 370        status = readl(wdt->base + WDT_TIMEOUT_STATUS);
 371        if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY) {
 372                wdt->wdd.bootstatus = WDIOF_CARDRESET;
 373
 374                if (of_device_is_compatible(np, "aspeed,ast2400-wdt") ||
 375                    of_device_is_compatible(np, "aspeed,ast2500-wdt"))
 376                        wdt->wdd.groups = bswitch_groups;
 377        }
 378
 379        dev_set_drvdata(dev, wdt);
 380
 381        return devm_watchdog_register_device(dev, &wdt->wdd);
 382}
 383
 384static struct platform_driver aspeed_watchdog_driver = {
 385        .probe = aspeed_wdt_probe,
 386        .driver = {
 387                .name = KBUILD_MODNAME,
 388                .of_match_table = of_match_ptr(aspeed_wdt_of_table),
 389        },
 390};
 391
 392static int __init aspeed_wdt_init(void)
 393{
 394        return platform_driver_register(&aspeed_watchdog_driver);
 395}
 396arch_initcall(aspeed_wdt_init);
 397
 398static void __exit aspeed_wdt_exit(void)
 399{
 400        platform_driver_unregister(&aspeed_watchdog_driver);
 401}
 402module_exit(aspeed_wdt_exit);
 403
 404MODULE_DESCRIPTION("Aspeed Watchdog Driver");
 405MODULE_LICENSE("GPL");
 406