1*1a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 280e45b1eSTimo Kokkonen /* 380e45b1eSTimo Kokkonen * Copyright (C) Nokia Corporation 480e45b1eSTimo Kokkonen * 580e45b1eSTimo Kokkonen * Written by Timo Kokkonen <timo.t.kokkonen at nokia.com> 680e45b1eSTimo Kokkonen */ 780e45b1eSTimo Kokkonen 880e45b1eSTimo Kokkonen #include <linux/module.h> 980e45b1eSTimo Kokkonen #include <linux/types.h> 105a0e3ad6STejun Heo #include <linux/slab.h> 1180e45b1eSTimo Kokkonen #include <linux/kernel.h> 1280e45b1eSTimo Kokkonen #include <linux/watchdog.h> 1380e45b1eSTimo Kokkonen #include <linux/platform_device.h> 14a2054256SWolfram Sang #include <linux/mfd/twl.h> 1580e45b1eSTimo Kokkonen 1680e45b1eSTimo Kokkonen #define TWL4030_WATCHDOG_CFG_REG_OFFS 0x3 1780e45b1eSTimo Kokkonen 1886a1e189SWim Van Sebroeck static bool nowayout = WATCHDOG_NOWAYOUT; 1986a1e189SWim Van Sebroeck module_param(nowayout, bool, 0); 2080e45b1eSTimo Kokkonen MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " 2180e45b1eSTimo Kokkonen "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 2280e45b1eSTimo Kokkonen 2380e45b1eSTimo Kokkonen static int twl4030_wdt_write(unsigned char val) 2480e45b1eSTimo Kokkonen { 252bc3f62fSPeter Ujfalusi return twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, val, 2680e45b1eSTimo Kokkonen TWL4030_WATCHDOG_CFG_REG_OFFS); 2780e45b1eSTimo Kokkonen } 2880e45b1eSTimo Kokkonen 29b2c4e4b2SJarkko Nikula static int twl4030_wdt_start(struct watchdog_device *wdt) 3080e45b1eSTimo Kokkonen { 31b2c4e4b2SJarkko Nikula return twl4030_wdt_write(wdt->timeout + 1); 3280e45b1eSTimo Kokkonen } 3380e45b1eSTimo Kokkonen 34b2c4e4b2SJarkko Nikula static int twl4030_wdt_stop(struct watchdog_device *wdt) 3580e45b1eSTimo Kokkonen { 3680e45b1eSTimo Kokkonen return twl4030_wdt_write(0); 3780e45b1eSTimo Kokkonen } 3880e45b1eSTimo Kokkonen 39b2c4e4b2SJarkko Nikula static int twl4030_wdt_set_timeout(struct watchdog_device *wdt, 40b2c4e4b2SJarkko Nikula unsigned int timeout) 4180e45b1eSTimo Kokkonen { 42b2c4e4b2SJarkko Nikula wdt->timeout = timeout; 43b2c4e4b2SJarkko Nikula return 0; 4480e45b1eSTimo Kokkonen } 4580e45b1eSTimo Kokkonen 46b2c4e4b2SJarkko Nikula static const struct watchdog_info twl4030_wdt_info = { 47fb1cbeaeSTony Lindgren .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, 4880e45b1eSTimo Kokkonen .identity = "TWL4030 Watchdog", 4980e45b1eSTimo Kokkonen }; 5080e45b1eSTimo Kokkonen 51b2c4e4b2SJarkko Nikula static const struct watchdog_ops twl4030_wdt_ops = { 5280e45b1eSTimo Kokkonen .owner = THIS_MODULE, 53b2c4e4b2SJarkko Nikula .start = twl4030_wdt_start, 54b2c4e4b2SJarkko Nikula .stop = twl4030_wdt_stop, 55b2c4e4b2SJarkko Nikula .set_timeout = twl4030_wdt_set_timeout, 5680e45b1eSTimo Kokkonen }; 5780e45b1eSTimo Kokkonen 582d991a16SBill Pemberton static int twl4030_wdt_probe(struct platform_device *pdev) 5980e45b1eSTimo Kokkonen { 60b42488bcSGuenter Roeck struct device *dev = &pdev->dev; 61b2c4e4b2SJarkko Nikula struct watchdog_device *wdt; 6280e45b1eSTimo Kokkonen 63b42488bcSGuenter Roeck wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); 6480e45b1eSTimo Kokkonen if (!wdt) 6580e45b1eSTimo Kokkonen return -ENOMEM; 6680e45b1eSTimo Kokkonen 67b2c4e4b2SJarkko Nikula wdt->info = &twl4030_wdt_info; 68b2c4e4b2SJarkko Nikula wdt->ops = &twl4030_wdt_ops; 69b2c4e4b2SJarkko Nikula wdt->status = 0; 70b2c4e4b2SJarkko Nikula wdt->timeout = 30; 71b2c4e4b2SJarkko Nikula wdt->min_timeout = 1; 72b2c4e4b2SJarkko Nikula wdt->max_timeout = 30; 73b42488bcSGuenter Roeck wdt->parent = dev; 7480e45b1eSTimo Kokkonen 75b2c4e4b2SJarkko Nikula watchdog_set_nowayout(wdt, nowayout); 7680e45b1eSTimo Kokkonen platform_set_drvdata(pdev, wdt); 7780e45b1eSTimo Kokkonen 78b2c4e4b2SJarkko Nikula twl4030_wdt_stop(wdt); 7980e45b1eSTimo Kokkonen 80b42488bcSGuenter Roeck return devm_watchdog_register_device(dev, wdt); 8180e45b1eSTimo Kokkonen } 8280e45b1eSTimo Kokkonen 8380e45b1eSTimo Kokkonen #ifdef CONFIG_PM 8480e45b1eSTimo Kokkonen static int twl4030_wdt_suspend(struct platform_device *pdev, pm_message_t state) 8580e45b1eSTimo Kokkonen { 86b2c4e4b2SJarkko Nikula struct watchdog_device *wdt = platform_get_drvdata(pdev); 87b2c4e4b2SJarkko Nikula if (watchdog_active(wdt)) 88b2c4e4b2SJarkko Nikula return twl4030_wdt_stop(wdt); 8980e45b1eSTimo Kokkonen 9080e45b1eSTimo Kokkonen return 0; 9180e45b1eSTimo Kokkonen } 9280e45b1eSTimo Kokkonen 9380e45b1eSTimo Kokkonen static int twl4030_wdt_resume(struct platform_device *pdev) 9480e45b1eSTimo Kokkonen { 95b2c4e4b2SJarkko Nikula struct watchdog_device *wdt = platform_get_drvdata(pdev); 96b2c4e4b2SJarkko Nikula if (watchdog_active(wdt)) 97b2c4e4b2SJarkko Nikula return twl4030_wdt_start(wdt); 9880e45b1eSTimo Kokkonen 9980e45b1eSTimo Kokkonen return 0; 10080e45b1eSTimo Kokkonen } 10180e45b1eSTimo Kokkonen #else 10280e45b1eSTimo Kokkonen #define twl4030_wdt_suspend NULL 10380e45b1eSTimo Kokkonen #define twl4030_wdt_resume NULL 10480e45b1eSTimo Kokkonen #endif 10580e45b1eSTimo Kokkonen 1068899b8d9SAaro Koskinen static const struct of_device_id twl_wdt_of_match[] = { 1078899b8d9SAaro Koskinen { .compatible = "ti,twl4030-wdt", }, 1088899b8d9SAaro Koskinen { }, 1098899b8d9SAaro Koskinen }; 1108899b8d9SAaro Koskinen MODULE_DEVICE_TABLE(of, twl_wdt_of_match); 1118899b8d9SAaro Koskinen 11280e45b1eSTimo Kokkonen static struct platform_driver twl4030_wdt_driver = { 11380e45b1eSTimo Kokkonen .probe = twl4030_wdt_probe, 11480e45b1eSTimo Kokkonen .suspend = twl4030_wdt_suspend, 11580e45b1eSTimo Kokkonen .resume = twl4030_wdt_resume, 11680e45b1eSTimo Kokkonen .driver = { 11780e45b1eSTimo Kokkonen .name = "twl4030_wdt", 1188899b8d9SAaro Koskinen .of_match_table = twl_wdt_of_match, 11980e45b1eSTimo Kokkonen }, 12080e45b1eSTimo Kokkonen }; 12180e45b1eSTimo Kokkonen 122b8ec6118SAxel Lin module_platform_driver(twl4030_wdt_driver); 12380e45b1eSTimo Kokkonen 12480e45b1eSTimo Kokkonen MODULE_AUTHOR("Nokia Corporation"); 12580e45b1eSTimo Kokkonen MODULE_LICENSE("GPL"); 12680e45b1eSTimo Kokkonen MODULE_ALIAS("platform:twl4030_wdt"); 12780e45b1eSTimo Kokkonen 128