Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

watchdog: Add Aspeed watchdog driver

Provides generic watchdog features as well as reboot support for the
Aspeed SoCs.

Signed-off-by: Joel Stanley <joel@jms.id.au>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>

authored by

Joel Stanley and committed by
Wim Van Sebroeck
efa859f7 0c9444cc

+226
+13
drivers/watchdog/Kconfig
··· 669 669 This driver adds watchdog support for the integrated watchdogs in the 670 670 Renesas R-Car and other SH-Mobile SoCs (usually named RWDT or SWDT). 671 671 672 + config ASPEED_WATCHDOG 673 + tristate "Aspeed 2400 watchdog support" 674 + depends on ARCH_ASPEED || COMPILE_TEST 675 + select WATCHDOG_CORE 676 + help 677 + Say Y here to include support for the watchdog timer 678 + in Apseed BMC SoCs. 679 + 680 + This driver is required to reboot the SoC. 681 + 682 + To compile this driver as a module, choose M here: the 683 + module will be called aspeed_wdt. 684 + 672 685 # AVR32 Architecture 673 686 674 687 config AT32AP700X_WDT
+1
drivers/watchdog/Makefile
··· 74 74 obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o 75 75 obj-$(CONFIG_ATLAS7_WATCHDOG) += atlas7_wdt.o 76 76 obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o 77 + obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o 77 78 78 79 # AVR32 Architecture 79 80 obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
+212
drivers/watchdog/aspeed_wdt.c
··· 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 + 20 + struct aspeed_wdt { 21 + struct watchdog_device wdd; 22 + void __iomem *base; 23 + u32 ctrl; 24 + }; 25 + 26 + static const struct of_device_id aspeed_wdt_of_table[] = { 27 + { .compatible = "aspeed,ast2400-wdt" }, 28 + { .compatible = "aspeed,ast2500-wdt" }, 29 + { }, 30 + }; 31 + MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table); 32 + 33 + #define WDT_STATUS 0x00 34 + #define WDT_RELOAD_VALUE 0x04 35 + #define WDT_RESTART 0x08 36 + #define WDT_CTRL 0x0C 37 + #define WDT_CTRL_RESET_MODE_SOC (0x00 << 5) 38 + #define WDT_CTRL_RESET_MODE_FULL_CHIP (0x01 << 5) 39 + #define WDT_CTRL_1MHZ_CLK BIT(4) 40 + #define WDT_CTRL_WDT_EXT BIT(3) 41 + #define WDT_CTRL_WDT_INTR BIT(2) 42 + #define WDT_CTRL_RESET_SYSTEM BIT(1) 43 + #define WDT_CTRL_ENABLE BIT(0) 44 + 45 + #define WDT_RESTART_MAGIC 0x4755 46 + 47 + /* 32 bits at 1MHz, in milliseconds */ 48 + #define WDT_MAX_TIMEOUT_MS 4294967 49 + #define WDT_DEFAULT_TIMEOUT 30 50 + #define WDT_RATE_1MHZ 1000000 51 + 52 + static struct aspeed_wdt *to_aspeed_wdt(struct watchdog_device *wdd) 53 + { 54 + return container_of(wdd, struct aspeed_wdt, wdd); 55 + } 56 + 57 + static void aspeed_wdt_enable(struct aspeed_wdt *wdt, int count) 58 + { 59 + wdt->ctrl |= WDT_CTRL_ENABLE; 60 + 61 + writel(0, wdt->base + WDT_CTRL); 62 + writel(count, wdt->base + WDT_RELOAD_VALUE); 63 + writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART); 64 + writel(wdt->ctrl, wdt->base + WDT_CTRL); 65 + } 66 + 67 + static int aspeed_wdt_start(struct watchdog_device *wdd) 68 + { 69 + struct aspeed_wdt *wdt = to_aspeed_wdt(wdd); 70 + 71 + aspeed_wdt_enable(wdt, wdd->timeout * WDT_RATE_1MHZ); 72 + 73 + return 0; 74 + } 75 + 76 + static int aspeed_wdt_stop(struct watchdog_device *wdd) 77 + { 78 + struct aspeed_wdt *wdt = to_aspeed_wdt(wdd); 79 + 80 + wdt->ctrl &= ~WDT_CTRL_ENABLE; 81 + writel(wdt->ctrl, wdt->base + WDT_CTRL); 82 + 83 + return 0; 84 + } 85 + 86 + static int aspeed_wdt_ping(struct watchdog_device *wdd) 87 + { 88 + struct aspeed_wdt *wdt = to_aspeed_wdt(wdd); 89 + 90 + writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART); 91 + 92 + return 0; 93 + } 94 + 95 + static int aspeed_wdt_set_timeout(struct watchdog_device *wdd, 96 + unsigned int timeout) 97 + { 98 + struct aspeed_wdt *wdt = to_aspeed_wdt(wdd); 99 + u32 actual; 100 + 101 + wdd->timeout = timeout; 102 + 103 + actual = min(timeout, wdd->max_hw_heartbeat_ms * 1000); 104 + 105 + writel(actual * WDT_RATE_1MHZ, wdt->base + WDT_RELOAD_VALUE); 106 + writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART); 107 + 108 + return 0; 109 + } 110 + 111 + static int aspeed_wdt_restart(struct watchdog_device *wdd, 112 + unsigned long action, void *data) 113 + { 114 + struct aspeed_wdt *wdt = to_aspeed_wdt(wdd); 115 + 116 + aspeed_wdt_enable(wdt, 128 * WDT_RATE_1MHZ / 1000); 117 + 118 + mdelay(1000); 119 + 120 + return 0; 121 + } 122 + 123 + static const struct watchdog_ops aspeed_wdt_ops = { 124 + .start = aspeed_wdt_start, 125 + .stop = aspeed_wdt_stop, 126 + .ping = aspeed_wdt_ping, 127 + .set_timeout = aspeed_wdt_set_timeout, 128 + .restart = aspeed_wdt_restart, 129 + .owner = THIS_MODULE, 130 + }; 131 + 132 + static const struct watchdog_info aspeed_wdt_info = { 133 + .options = WDIOF_KEEPALIVEPING 134 + | WDIOF_MAGICCLOSE 135 + | WDIOF_SETTIMEOUT, 136 + .identity = KBUILD_MODNAME, 137 + }; 138 + 139 + static int aspeed_wdt_remove(struct platform_device *pdev) 140 + { 141 + struct aspeed_wdt *wdt = platform_get_drvdata(pdev); 142 + 143 + watchdog_unregister_device(&wdt->wdd); 144 + 145 + return 0; 146 + } 147 + 148 + static int aspeed_wdt_probe(struct platform_device *pdev) 149 + { 150 + struct aspeed_wdt *wdt; 151 + struct resource *res; 152 + int ret; 153 + 154 + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); 155 + if (!wdt) 156 + return -ENOMEM; 157 + 158 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 159 + wdt->base = devm_ioremap_resource(&pdev->dev, res); 160 + if (IS_ERR(wdt->base)) 161 + return PTR_ERR(wdt->base); 162 + 163 + /* 164 + * The ast2400 wdt can run at PCLK, or 1MHz. The ast2500 only 165 + * runs at 1MHz. We chose to always run at 1MHz, as there's no 166 + * good reason to have a faster watchdog counter. 167 + */ 168 + wdt->wdd.info = &aspeed_wdt_info; 169 + wdt->wdd.ops = &aspeed_wdt_ops; 170 + wdt->wdd.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT_MS; 171 + wdt->wdd.parent = &pdev->dev; 172 + 173 + wdt->wdd.timeout = WDT_DEFAULT_TIMEOUT; 174 + watchdog_init_timeout(&wdt->wdd, 0, &pdev->dev); 175 + 176 + /* 177 + * Control reset on a per-device basis to ensure the 178 + * host is not affected by a BMC reboot, so only reset 179 + * the SOC and not the full chip 180 + */ 181 + wdt->ctrl = WDT_CTRL_RESET_MODE_SOC | 182 + WDT_CTRL_1MHZ_CLK | 183 + WDT_CTRL_RESET_SYSTEM; 184 + 185 + if (readl(wdt->base + WDT_CTRL) & WDT_CTRL_ENABLE) { 186 + aspeed_wdt_start(&wdt->wdd); 187 + set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); 188 + } 189 + 190 + ret = watchdog_register_device(&wdt->wdd); 191 + if (ret) { 192 + dev_err(&pdev->dev, "failed to register\n"); 193 + return ret; 194 + } 195 + 196 + platform_set_drvdata(pdev, wdt); 197 + 198 + return 0; 199 + } 200 + 201 + static struct platform_driver aspeed_watchdog_driver = { 202 + .probe = aspeed_wdt_probe, 203 + .remove = aspeed_wdt_remove, 204 + .driver = { 205 + .name = KBUILD_MODNAME, 206 + .of_match_table = of_match_ptr(aspeed_wdt_of_table), 207 + }, 208 + }; 209 + module_platform_driver(aspeed_watchdog_driver); 210 + 211 + MODULE_DESCRIPTION("Aspeed Watchdog Driver"); 212 + MODULE_LICENSE("GPL");