0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/delay.h>
0009 #include <linux/io.h>
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/of.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/watchdog.h>
0015
0016 static bool nowayout = WATCHDOG_NOWAYOUT;
0017 module_param(nowayout, bool, 0);
0018 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
0019 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0020
0021 struct aspeed_wdt {
0022 struct watchdog_device wdd;
0023 void __iomem *base;
0024 u32 ctrl;
0025 };
0026
0027 struct aspeed_wdt_config {
0028 u32 ext_pulse_width_mask;
0029 };
0030
0031 static const struct aspeed_wdt_config ast2400_config = {
0032 .ext_pulse_width_mask = 0xff,
0033 };
0034
0035 static const struct aspeed_wdt_config ast2500_config = {
0036 .ext_pulse_width_mask = 0xfffff,
0037 };
0038
0039 static const struct of_device_id aspeed_wdt_of_table[] = {
0040 { .compatible = "aspeed,ast2400-wdt", .data = &ast2400_config },
0041 { .compatible = "aspeed,ast2500-wdt", .data = &ast2500_config },
0042 { .compatible = "aspeed,ast2600-wdt", .data = &ast2500_config },
0043 { },
0044 };
0045 MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
0046
0047 #define WDT_STATUS 0x00
0048 #define WDT_RELOAD_VALUE 0x04
0049 #define WDT_RESTART 0x08
0050 #define WDT_CTRL 0x0C
0051 #define WDT_CTRL_BOOT_SECONDARY BIT(7)
0052 #define WDT_CTRL_RESET_MODE_SOC (0x00 << 5)
0053 #define WDT_CTRL_RESET_MODE_FULL_CHIP (0x01 << 5)
0054 #define WDT_CTRL_RESET_MODE_ARM_CPU (0x10 << 5)
0055 #define WDT_CTRL_1MHZ_CLK BIT(4)
0056 #define WDT_CTRL_WDT_EXT BIT(3)
0057 #define WDT_CTRL_WDT_INTR BIT(2)
0058 #define WDT_CTRL_RESET_SYSTEM BIT(1)
0059 #define WDT_CTRL_ENABLE BIT(0)
0060 #define WDT_TIMEOUT_STATUS 0x10
0061 #define WDT_TIMEOUT_STATUS_BOOT_SECONDARY BIT(1)
0062 #define WDT_CLEAR_TIMEOUT_STATUS 0x14
0063 #define WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION BIT(0)
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089 #define WDT_RESET_WIDTH 0x18
0090 #define WDT_RESET_WIDTH_ACTIVE_HIGH BIT(31)
0091 #define WDT_ACTIVE_HIGH_MAGIC (0xA5 << 24)
0092 #define WDT_ACTIVE_LOW_MAGIC (0x5A << 24)
0093 #define WDT_RESET_WIDTH_PUSH_PULL BIT(30)
0094 #define WDT_PUSH_PULL_MAGIC (0xA8 << 24)
0095 #define WDT_OPEN_DRAIN_MAGIC (0x8A << 24)
0096
0097 #define WDT_RESTART_MAGIC 0x4755
0098
0099
0100 #define WDT_MAX_TIMEOUT_MS 4294967
0101 #define WDT_DEFAULT_TIMEOUT 30
0102 #define WDT_RATE_1MHZ 1000000
0103
0104 static struct aspeed_wdt *to_aspeed_wdt(struct watchdog_device *wdd)
0105 {
0106 return container_of(wdd, struct aspeed_wdt, wdd);
0107 }
0108
0109 static void aspeed_wdt_enable(struct aspeed_wdt *wdt, int count)
0110 {
0111 wdt->ctrl |= WDT_CTRL_ENABLE;
0112
0113 writel(0, wdt->base + WDT_CTRL);
0114 writel(count, wdt->base + WDT_RELOAD_VALUE);
0115 writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART);
0116 writel(wdt->ctrl, wdt->base + WDT_CTRL);
0117 }
0118
0119 static int aspeed_wdt_start(struct watchdog_device *wdd)
0120 {
0121 struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
0122
0123 aspeed_wdt_enable(wdt, wdd->timeout * WDT_RATE_1MHZ);
0124
0125 return 0;
0126 }
0127
0128 static int aspeed_wdt_stop(struct watchdog_device *wdd)
0129 {
0130 struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
0131
0132 wdt->ctrl &= ~WDT_CTRL_ENABLE;
0133 writel(wdt->ctrl, wdt->base + WDT_CTRL);
0134
0135 return 0;
0136 }
0137
0138 static int aspeed_wdt_ping(struct watchdog_device *wdd)
0139 {
0140 struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
0141
0142 writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART);
0143
0144 return 0;
0145 }
0146
0147 static int aspeed_wdt_set_timeout(struct watchdog_device *wdd,
0148 unsigned int timeout)
0149 {
0150 struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
0151 u32 actual;
0152
0153 wdd->timeout = timeout;
0154
0155 actual = min(timeout, wdd->max_hw_heartbeat_ms / 1000);
0156
0157 writel(actual * WDT_RATE_1MHZ, wdt->base + WDT_RELOAD_VALUE);
0158 writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART);
0159
0160 return 0;
0161 }
0162
0163 static int aspeed_wdt_restart(struct watchdog_device *wdd,
0164 unsigned long action, void *data)
0165 {
0166 struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
0167
0168 wdt->ctrl &= ~WDT_CTRL_BOOT_SECONDARY;
0169 aspeed_wdt_enable(wdt, 128 * WDT_RATE_1MHZ / 1000);
0170
0171 mdelay(1000);
0172
0173 return 0;
0174 }
0175
0176
0177 static ssize_t access_cs0_show(struct device *dev,
0178 struct device_attribute *attr, char *buf)
0179 {
0180 struct aspeed_wdt *wdt = dev_get_drvdata(dev);
0181 u32 status = readl(wdt->base + WDT_TIMEOUT_STATUS);
0182
0183 return sysfs_emit(buf, "%u\n",
0184 !(status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY));
0185 }
0186
0187 static ssize_t access_cs0_store(struct device *dev,
0188 struct device_attribute *attr, const char *buf,
0189 size_t size)
0190 {
0191 struct aspeed_wdt *wdt = dev_get_drvdata(dev);
0192 unsigned long val;
0193
0194 if (kstrtoul(buf, 10, &val))
0195 return -EINVAL;
0196
0197 if (val)
0198 writel(WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION,
0199 wdt->base + WDT_CLEAR_TIMEOUT_STATUS);
0200
0201 return size;
0202 }
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221
0222 static DEVICE_ATTR_RW(access_cs0);
0223
0224 static struct attribute *bswitch_attrs[] = {
0225 &dev_attr_access_cs0.attr,
0226 NULL
0227 };
0228 ATTRIBUTE_GROUPS(bswitch);
0229
0230 static const struct watchdog_ops aspeed_wdt_ops = {
0231 .start = aspeed_wdt_start,
0232 .stop = aspeed_wdt_stop,
0233 .ping = aspeed_wdt_ping,
0234 .set_timeout = aspeed_wdt_set_timeout,
0235 .restart = aspeed_wdt_restart,
0236 .owner = THIS_MODULE,
0237 };
0238
0239 static const struct watchdog_info aspeed_wdt_info = {
0240 .options = WDIOF_KEEPALIVEPING
0241 | WDIOF_MAGICCLOSE
0242 | WDIOF_SETTIMEOUT,
0243 .identity = KBUILD_MODNAME,
0244 };
0245
0246 static int aspeed_wdt_probe(struct platform_device *pdev)
0247 {
0248 struct device *dev = &pdev->dev;
0249 const struct aspeed_wdt_config *config;
0250 const struct of_device_id *ofdid;
0251 struct aspeed_wdt *wdt;
0252 struct device_node *np;
0253 const char *reset_type;
0254 u32 duration;
0255 u32 status;
0256 int ret;
0257
0258 wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
0259 if (!wdt)
0260 return -ENOMEM;
0261
0262 wdt->base = devm_platform_ioremap_resource(pdev, 0);
0263 if (IS_ERR(wdt->base))
0264 return PTR_ERR(wdt->base);
0265
0266 wdt->wdd.info = &aspeed_wdt_info;
0267 wdt->wdd.ops = &aspeed_wdt_ops;
0268 wdt->wdd.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT_MS;
0269 wdt->wdd.parent = dev;
0270
0271 wdt->wdd.timeout = WDT_DEFAULT_TIMEOUT;
0272 watchdog_init_timeout(&wdt->wdd, 0, dev);
0273
0274 watchdog_set_nowayout(&wdt->wdd, nowayout);
0275
0276 np = dev->of_node;
0277
0278 ofdid = of_match_node(aspeed_wdt_of_table, np);
0279 if (!ofdid)
0280 return -EINVAL;
0281 config = ofdid->data;
0282
0283
0284
0285
0286
0287
0288
0289
0290
0291 if (of_device_is_compatible(np, "aspeed,ast2400-wdt"))
0292 wdt->ctrl = WDT_CTRL_1MHZ_CLK;
0293
0294
0295
0296
0297
0298 ret = of_property_read_string(np, "aspeed,reset-type", &reset_type);
0299 if (ret) {
0300 wdt->ctrl |= WDT_CTRL_RESET_MODE_SOC | WDT_CTRL_RESET_SYSTEM;
0301 } else {
0302 if (!strcmp(reset_type, "cpu"))
0303 wdt->ctrl |= WDT_CTRL_RESET_MODE_ARM_CPU |
0304 WDT_CTRL_RESET_SYSTEM;
0305 else if (!strcmp(reset_type, "soc"))
0306 wdt->ctrl |= WDT_CTRL_RESET_MODE_SOC |
0307 WDT_CTRL_RESET_SYSTEM;
0308 else if (!strcmp(reset_type, "system"))
0309 wdt->ctrl |= WDT_CTRL_RESET_MODE_FULL_CHIP |
0310 WDT_CTRL_RESET_SYSTEM;
0311 else if (strcmp(reset_type, "none"))
0312 return -EINVAL;
0313 }
0314 if (of_property_read_bool(np, "aspeed,external-signal"))
0315 wdt->ctrl |= WDT_CTRL_WDT_EXT;
0316 if (of_property_read_bool(np, "aspeed,alt-boot"))
0317 wdt->ctrl |= WDT_CTRL_BOOT_SECONDARY;
0318
0319 if (readl(wdt->base + WDT_CTRL) & WDT_CTRL_ENABLE) {
0320
0321
0322
0323
0324
0325
0326 aspeed_wdt_start(&wdt->wdd);
0327 set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
0328 }
0329
0330 if ((of_device_is_compatible(np, "aspeed,ast2500-wdt")) ||
0331 (of_device_is_compatible(np, "aspeed,ast2600-wdt"))) {
0332 u32 reg = readl(wdt->base + WDT_RESET_WIDTH);
0333
0334 reg &= config->ext_pulse_width_mask;
0335 if (of_property_read_bool(np, "aspeed,ext-push-pull"))
0336 reg |= WDT_PUSH_PULL_MAGIC;
0337 else
0338 reg |= WDT_OPEN_DRAIN_MAGIC;
0339
0340 writel(reg, wdt->base + WDT_RESET_WIDTH);
0341
0342 reg &= config->ext_pulse_width_mask;
0343 if (of_property_read_bool(np, "aspeed,ext-active-high"))
0344 reg |= WDT_ACTIVE_HIGH_MAGIC;
0345 else
0346 reg |= WDT_ACTIVE_LOW_MAGIC;
0347
0348 writel(reg, wdt->base + WDT_RESET_WIDTH);
0349 }
0350
0351 if (!of_property_read_u32(np, "aspeed,ext-pulse-duration", &duration)) {
0352 u32 max_duration = config->ext_pulse_width_mask + 1;
0353
0354 if (duration == 0 || duration > max_duration) {
0355 dev_err(dev, "Invalid pulse duration: %uus\n",
0356 duration);
0357 duration = max(1U, min(max_duration, duration));
0358 dev_info(dev, "Pulse duration set to %uus\n",
0359 duration);
0360 }
0361
0362
0363
0364
0365
0366
0367
0368
0369
0370
0371
0372
0373
0374 writel(duration - 1, wdt->base + WDT_RESET_WIDTH);
0375 }
0376
0377 status = readl(wdt->base + WDT_TIMEOUT_STATUS);
0378 if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY) {
0379 wdt->wdd.bootstatus = WDIOF_CARDRESET;
0380
0381 if (of_device_is_compatible(np, "aspeed,ast2400-wdt") ||
0382 of_device_is_compatible(np, "aspeed,ast2500-wdt"))
0383 wdt->wdd.groups = bswitch_groups;
0384 }
0385
0386 dev_set_drvdata(dev, wdt);
0387
0388 return devm_watchdog_register_device(dev, &wdt->wdd);
0389 }
0390
0391 static struct platform_driver aspeed_watchdog_driver = {
0392 .probe = aspeed_wdt_probe,
0393 .driver = {
0394 .name = KBUILD_MODNAME,
0395 .of_match_table = of_match_ptr(aspeed_wdt_of_table),
0396 },
0397 };
0398
0399 static int __init aspeed_wdt_init(void)
0400 {
0401 return platform_driver_register(&aspeed_watchdog_driver);
0402 }
0403 arch_initcall(aspeed_wdt_init);
0404
0405 static void __exit aspeed_wdt_exit(void)
0406 {
0407 platform_driver_unregister(&aspeed_watchdog_driver);
0408 }
0409 module_exit(aspeed_wdt_exit);
0410
0411 MODULE_DESCRIPTION("Aspeed Watchdog Driver");
0412 MODULE_LICENSE("GPL");