.. | .. |
---|
| 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 = { |
---|