| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright 2016 IBM Corporation |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * 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 | 6 | */ |
|---|
| 11 | 7 | |
|---|
| 12 | 8 | #include <linux/delay.h> |
|---|
| .. | .. |
|---|
| 58 | 54 | #define WDT_CTRL_ENABLE BIT(0) |
|---|
| 59 | 55 | #define WDT_TIMEOUT_STATUS 0x10 |
|---|
| 60 | 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) |
|---|
| 61 | 59 | |
|---|
| 62 | 60 | /* |
|---|
| 63 | 61 | * WDT_RESET_WIDTH controls the characteristics of the external pulse (if |
|---|
| .. | .. |
|---|
| 170 | 168 | return 0; |
|---|
| 171 | 169 | } |
|---|
| 172 | 170 | |
|---|
| 171 | +/* access_cs0 shows if cs0 is accessible, hence the reverted bit */ |
|---|
| 172 | +static 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 sprintf(buf, "%u\n", |
|---|
| 179 | + !(status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY)); |
|---|
| 180 | +} |
|---|
| 181 | + |
|---|
| 182 | +static 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 | + */ |
|---|
| 217 | +static DEVICE_ATTR_RW(access_cs0); |
|---|
| 218 | + |
|---|
| 219 | +static struct attribute *bswitch_attrs[] = { |
|---|
| 220 | + &dev_attr_access_cs0.attr, |
|---|
| 221 | + NULL |
|---|
| 222 | +}; |
|---|
| 223 | +ATTRIBUTE_GROUPS(bswitch); |
|---|
| 224 | + |
|---|
| 173 | 225 | static const struct watchdog_ops aspeed_wdt_ops = { |
|---|
| 174 | 226 | .start = aspeed_wdt_start, |
|---|
| 175 | 227 | .stop = aspeed_wdt_stop, |
|---|
| .. | .. |
|---|
| 188 | 240 | |
|---|
| 189 | 241 | static int aspeed_wdt_probe(struct platform_device *pdev) |
|---|
| 190 | 242 | { |
|---|
| 243 | + struct device *dev = &pdev->dev; |
|---|
| 191 | 244 | const struct aspeed_wdt_config *config; |
|---|
| 192 | 245 | const struct of_device_id *ofdid; |
|---|
| 193 | 246 | struct aspeed_wdt *wdt; |
|---|
| 194 | | - struct resource *res; |
|---|
| 195 | 247 | struct device_node *np; |
|---|
| 196 | 248 | const char *reset_type; |
|---|
| 197 | 249 | u32 duration; |
|---|
| 198 | 250 | u32 status; |
|---|
| 199 | 251 | int ret; |
|---|
| 200 | 252 | |
|---|
| 201 | | - wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); |
|---|
| 253 | + wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); |
|---|
| 202 | 254 | if (!wdt) |
|---|
| 203 | 255 | return -ENOMEM; |
|---|
| 204 | 256 | |
|---|
| 205 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 206 | | - wdt->base = devm_ioremap_resource(&pdev->dev, res); |
|---|
| 257 | + wdt->base = devm_platform_ioremap_resource(pdev, 0); |
|---|
| 207 | 258 | if (IS_ERR(wdt->base)) |
|---|
| 208 | 259 | return PTR_ERR(wdt->base); |
|---|
| 209 | 260 | |
|---|
| 210 | 261 | wdt->wdd.info = &aspeed_wdt_info; |
|---|
| 211 | 262 | wdt->wdd.ops = &aspeed_wdt_ops; |
|---|
| 212 | 263 | wdt->wdd.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT_MS; |
|---|
| 213 | | - wdt->wdd.parent = &pdev->dev; |
|---|
| 264 | + wdt->wdd.parent = dev; |
|---|
| 214 | 265 | |
|---|
| 215 | 266 | wdt->wdd.timeout = WDT_DEFAULT_TIMEOUT; |
|---|
| 216 | | - watchdog_init_timeout(&wdt->wdd, 0, &pdev->dev); |
|---|
| 267 | + watchdog_init_timeout(&wdt->wdd, 0, dev); |
|---|
| 217 | 268 | |
|---|
| 218 | | - np = pdev->dev.of_node; |
|---|
| 269 | + np = dev->of_node; |
|---|
| 219 | 270 | |
|---|
| 220 | 271 | ofdid = of_match_node(aspeed_wdt_of_table, np); |
|---|
| 221 | 272 | if (!ofdid) |
|---|
| .. | .. |
|---|
| 294 | 345 | u32 max_duration = config->ext_pulse_width_mask + 1; |
|---|
| 295 | 346 | |
|---|
| 296 | 347 | if (duration == 0 || duration > max_duration) { |
|---|
| 297 | | - dev_err(&pdev->dev, "Invalid pulse duration: %uus\n", |
|---|
| 298 | | - duration); |
|---|
| 348 | + dev_err(dev, "Invalid pulse duration: %uus\n", |
|---|
| 349 | + duration); |
|---|
| 299 | 350 | duration = max(1U, min(max_duration, duration)); |
|---|
| 300 | | - dev_info(&pdev->dev, "Pulse duration set to %uus\n", |
|---|
| 301 | | - duration); |
|---|
| 351 | + dev_info(dev, "Pulse duration set to %uus\n", |
|---|
| 352 | + duration); |
|---|
| 302 | 353 | } |
|---|
| 303 | 354 | |
|---|
| 304 | 355 | /* |
|---|
| .. | .. |
|---|
| 317 | 368 | } |
|---|
| 318 | 369 | |
|---|
| 319 | 370 | status = readl(wdt->base + WDT_TIMEOUT_STATUS); |
|---|
| 320 | | - if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY) |
|---|
| 371 | + if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY) { |
|---|
| 321 | 372 | wdt->wdd.bootstatus = WDIOF_CARDRESET; |
|---|
| 322 | 373 | |
|---|
| 323 | | - ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdd); |
|---|
| 324 | | - if (ret) { |
|---|
| 325 | | - dev_err(&pdev->dev, "failed to register\n"); |
|---|
| 326 | | - return ret; |
|---|
| 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; |
|---|
| 327 | 377 | } |
|---|
| 328 | 378 | |
|---|
| 329 | | - return 0; |
|---|
| 379 | + dev_set_drvdata(dev, wdt); |
|---|
| 380 | + |
|---|
| 381 | + return devm_watchdog_register_device(dev, &wdt->wdd); |
|---|
| 330 | 382 | } |
|---|
| 331 | 383 | |
|---|
| 332 | 384 | static struct platform_driver aspeed_watchdog_driver = { |
|---|