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