1*d0173278SGuenter Roeck // SPDX-License-Identifier: GPL-2.0+ 23d3a6d18SAaro Koskinen /* 33d3a6d18SAaro Koskinen * Retu watchdog driver 43d3a6d18SAaro Koskinen * 53d3a6d18SAaro Koskinen * Copyright (C) 2004, 2005 Nokia Corporation 63d3a6d18SAaro Koskinen * 73d3a6d18SAaro Koskinen * Based on code written by Amit Kucheria and Michael Buesch. 83d3a6d18SAaro Koskinen * Rewritten by Aaro Koskinen. 93d3a6d18SAaro Koskinen */ 103d3a6d18SAaro Koskinen 113d3a6d18SAaro Koskinen #include <linux/slab.h> 123d3a6d18SAaro Koskinen #include <linux/errno.h> 133d3a6d18SAaro Koskinen #include <linux/device.h> 143d3a6d18SAaro Koskinen #include <linux/kernel.h> 153d3a6d18SAaro Koskinen #include <linux/module.h> 163d3a6d18SAaro Koskinen #include <linux/mfd/retu.h> 173d3a6d18SAaro Koskinen #include <linux/watchdog.h> 183d3a6d18SAaro Koskinen #include <linux/platform_device.h> 193d3a6d18SAaro Koskinen 203d3a6d18SAaro Koskinen /* Watchdog timer values in seconds */ 213d3a6d18SAaro Koskinen #define RETU_WDT_MAX_TIMER 63 223d3a6d18SAaro Koskinen 233d3a6d18SAaro Koskinen struct retu_wdt_dev { 243d3a6d18SAaro Koskinen struct retu_dev *rdev; 253d3a6d18SAaro Koskinen struct device *dev; 263d3a6d18SAaro Koskinen struct delayed_work ping_work; 273d3a6d18SAaro Koskinen }; 283d3a6d18SAaro Koskinen 293d3a6d18SAaro Koskinen /* 303d3a6d18SAaro Koskinen * Since Retu watchdog cannot be disabled in hardware, we must kick it 313d3a6d18SAaro Koskinen * with a timer until userspace watchdog software takes over. If 323d3a6d18SAaro Koskinen * CONFIG_WATCHDOG_NOWAYOUT is set, we never start the feeding. 333d3a6d18SAaro Koskinen */ 343d3a6d18SAaro Koskinen static void retu_wdt_ping_enable(struct retu_wdt_dev *wdev) 353d3a6d18SAaro Koskinen { 363d3a6d18SAaro Koskinen retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER); 373d3a6d18SAaro Koskinen schedule_delayed_work(&wdev->ping_work, 383d3a6d18SAaro Koskinen round_jiffies_relative(RETU_WDT_MAX_TIMER * HZ / 2)); 393d3a6d18SAaro Koskinen } 403d3a6d18SAaro Koskinen 413d3a6d18SAaro Koskinen static void retu_wdt_ping_disable(struct retu_wdt_dev *wdev) 423d3a6d18SAaro Koskinen { 433d3a6d18SAaro Koskinen retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER); 443d3a6d18SAaro Koskinen cancel_delayed_work_sync(&wdev->ping_work); 453d3a6d18SAaro Koskinen } 463d3a6d18SAaro Koskinen 473d3a6d18SAaro Koskinen static void retu_wdt_ping_work(struct work_struct *work) 483d3a6d18SAaro Koskinen { 493d3a6d18SAaro Koskinen struct retu_wdt_dev *wdev = container_of(to_delayed_work(work), 503d3a6d18SAaro Koskinen struct retu_wdt_dev, ping_work); 513d3a6d18SAaro Koskinen retu_wdt_ping_enable(wdev); 523d3a6d18SAaro Koskinen } 533d3a6d18SAaro Koskinen 543d3a6d18SAaro Koskinen static int retu_wdt_start(struct watchdog_device *wdog) 553d3a6d18SAaro Koskinen { 563d3a6d18SAaro Koskinen struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog); 573d3a6d18SAaro Koskinen 583d3a6d18SAaro Koskinen retu_wdt_ping_disable(wdev); 593d3a6d18SAaro Koskinen 603d3a6d18SAaro Koskinen return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout); 613d3a6d18SAaro Koskinen } 623d3a6d18SAaro Koskinen 633d3a6d18SAaro Koskinen static int retu_wdt_stop(struct watchdog_device *wdog) 643d3a6d18SAaro Koskinen { 653d3a6d18SAaro Koskinen struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog); 663d3a6d18SAaro Koskinen 673d3a6d18SAaro Koskinen retu_wdt_ping_enable(wdev); 683d3a6d18SAaro Koskinen 693d3a6d18SAaro Koskinen return 0; 703d3a6d18SAaro Koskinen } 713d3a6d18SAaro Koskinen 723d3a6d18SAaro Koskinen static int retu_wdt_ping(struct watchdog_device *wdog) 733d3a6d18SAaro Koskinen { 743d3a6d18SAaro Koskinen struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog); 753d3a6d18SAaro Koskinen 763d3a6d18SAaro Koskinen return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout); 773d3a6d18SAaro Koskinen } 783d3a6d18SAaro Koskinen 793d3a6d18SAaro Koskinen static int retu_wdt_set_timeout(struct watchdog_device *wdog, 803d3a6d18SAaro Koskinen unsigned int timeout) 813d3a6d18SAaro Koskinen { 823d3a6d18SAaro Koskinen struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog); 833d3a6d18SAaro Koskinen 843d3a6d18SAaro Koskinen wdog->timeout = timeout; 853d3a6d18SAaro Koskinen return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout); 863d3a6d18SAaro Koskinen } 873d3a6d18SAaro Koskinen 883d3a6d18SAaro Koskinen static const struct watchdog_info retu_wdt_info = { 89fb1cbeaeSTony Lindgren .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, 903d3a6d18SAaro Koskinen .identity = "Retu watchdog", 913d3a6d18SAaro Koskinen }; 923d3a6d18SAaro Koskinen 933d3a6d18SAaro Koskinen static const struct watchdog_ops retu_wdt_ops = { 943d3a6d18SAaro Koskinen .owner = THIS_MODULE, 953d3a6d18SAaro Koskinen .start = retu_wdt_start, 963d3a6d18SAaro Koskinen .stop = retu_wdt_stop, 973d3a6d18SAaro Koskinen .ping = retu_wdt_ping, 983d3a6d18SAaro Koskinen .set_timeout = retu_wdt_set_timeout, 993d3a6d18SAaro Koskinen }; 1003d3a6d18SAaro Koskinen 1013d3a6d18SAaro Koskinen static int retu_wdt_probe(struct platform_device *pdev) 1023d3a6d18SAaro Koskinen { 1033d3a6d18SAaro Koskinen struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent); 1043d3a6d18SAaro Koskinen bool nowayout = WATCHDOG_NOWAYOUT; 1053d3a6d18SAaro Koskinen struct watchdog_device *retu_wdt; 1063d3a6d18SAaro Koskinen struct retu_wdt_dev *wdev; 1073d3a6d18SAaro Koskinen int ret; 1083d3a6d18SAaro Koskinen 1093d3a6d18SAaro Koskinen retu_wdt = devm_kzalloc(&pdev->dev, sizeof(*retu_wdt), GFP_KERNEL); 1103d3a6d18SAaro Koskinen if (!retu_wdt) 1113d3a6d18SAaro Koskinen return -ENOMEM; 1123d3a6d18SAaro Koskinen 1133d3a6d18SAaro Koskinen wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); 1143d3a6d18SAaro Koskinen if (!wdev) 1153d3a6d18SAaro Koskinen return -ENOMEM; 1163d3a6d18SAaro Koskinen 1173d3a6d18SAaro Koskinen retu_wdt->info = &retu_wdt_info; 1183d3a6d18SAaro Koskinen retu_wdt->ops = &retu_wdt_ops; 1193d3a6d18SAaro Koskinen retu_wdt->timeout = RETU_WDT_MAX_TIMER; 1203d3a6d18SAaro Koskinen retu_wdt->min_timeout = 0; 1213d3a6d18SAaro Koskinen retu_wdt->max_timeout = RETU_WDT_MAX_TIMER; 1226551881cSPratyush Anand retu_wdt->parent = &pdev->dev; 1233d3a6d18SAaro Koskinen 1243d3a6d18SAaro Koskinen watchdog_set_drvdata(retu_wdt, wdev); 1253d3a6d18SAaro Koskinen watchdog_set_nowayout(retu_wdt, nowayout); 1263d3a6d18SAaro Koskinen 1273d3a6d18SAaro Koskinen wdev->rdev = rdev; 1283d3a6d18SAaro Koskinen wdev->dev = &pdev->dev; 1293d3a6d18SAaro Koskinen 1303d3a6d18SAaro Koskinen INIT_DELAYED_WORK(&wdev->ping_work, retu_wdt_ping_work); 1313d3a6d18SAaro Koskinen 1323d3a6d18SAaro Koskinen ret = watchdog_register_device(retu_wdt); 1333d3a6d18SAaro Koskinen if (ret < 0) 1343d3a6d18SAaro Koskinen return ret; 1353d3a6d18SAaro Koskinen 1363d3a6d18SAaro Koskinen if (nowayout) 1373d3a6d18SAaro Koskinen retu_wdt_ping(retu_wdt); 1383d3a6d18SAaro Koskinen else 1393d3a6d18SAaro Koskinen retu_wdt_ping_enable(wdev); 1403d3a6d18SAaro Koskinen 1413d3a6d18SAaro Koskinen platform_set_drvdata(pdev, retu_wdt); 1423d3a6d18SAaro Koskinen 1433d3a6d18SAaro Koskinen return 0; 1443d3a6d18SAaro Koskinen } 1453d3a6d18SAaro Koskinen 1463d3a6d18SAaro Koskinen static int retu_wdt_remove(struct platform_device *pdev) 1473d3a6d18SAaro Koskinen { 1483d3a6d18SAaro Koskinen struct watchdog_device *wdog = platform_get_drvdata(pdev); 1493d3a6d18SAaro Koskinen struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog); 1503d3a6d18SAaro Koskinen 1513d3a6d18SAaro Koskinen watchdog_unregister_device(wdog); 1523d3a6d18SAaro Koskinen cancel_delayed_work_sync(&wdev->ping_work); 1533d3a6d18SAaro Koskinen 1543d3a6d18SAaro Koskinen return 0; 1553d3a6d18SAaro Koskinen } 1563d3a6d18SAaro Koskinen 1573d3a6d18SAaro Koskinen static struct platform_driver retu_wdt_driver = { 1583d3a6d18SAaro Koskinen .probe = retu_wdt_probe, 1593d3a6d18SAaro Koskinen .remove = retu_wdt_remove, 1603d3a6d18SAaro Koskinen .driver = { 1613d3a6d18SAaro Koskinen .name = "retu-wdt", 1623d3a6d18SAaro Koskinen }, 1633d3a6d18SAaro Koskinen }; 1643d3a6d18SAaro Koskinen module_platform_driver(retu_wdt_driver); 1653d3a6d18SAaro Koskinen 1663d3a6d18SAaro Koskinen MODULE_ALIAS("platform:retu-wdt"); 1673d3a6d18SAaro Koskinen MODULE_DESCRIPTION("Retu watchdog"); 1683d3a6d18SAaro Koskinen MODULE_AUTHOR("Amit Kucheria"); 1693d3a6d18SAaro Koskinen MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>"); 1703d3a6d18SAaro Koskinen MODULE_LICENSE("GPL"); 171