11a59d1b8SThomas 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> 12*099d387eSDmitry Torokhov #include <linux/mod_devicetable.h> 1380e45b1eSTimo Kokkonen #include <linux/watchdog.h> 1480e45b1eSTimo Kokkonen #include <linux/platform_device.h> 15a2054256SWolfram Sang #include <linux/mfd/twl.h> 1680e45b1eSTimo Kokkonen 1780e45b1eSTimo Kokkonen #define TWL4030_WATCHDOG_CFG_REG_OFFS 0x3 1880e45b1eSTimo Kokkonen 1986a1e189SWim Van Sebroeck static bool nowayout = WATCHDOG_NOWAYOUT; 2086a1e189SWim Van Sebroeck module_param(nowayout, bool, 0); 2180e45b1eSTimo Kokkonen MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " 2280e45b1eSTimo Kokkonen "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 2380e45b1eSTimo Kokkonen 2480e45b1eSTimo Kokkonen static int twl4030_wdt_write(unsigned char val) 2580e45b1eSTimo Kokkonen { 262bc3f62fSPeter Ujfalusi return twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, val, 2780e45b1eSTimo Kokkonen TWL4030_WATCHDOG_CFG_REG_OFFS); 2880e45b1eSTimo Kokkonen } 2980e45b1eSTimo Kokkonen 30b2c4e4b2SJarkko Nikula static int twl4030_wdt_start(struct watchdog_device *wdt) 3180e45b1eSTimo Kokkonen { 32b2c4e4b2SJarkko Nikula return twl4030_wdt_write(wdt->timeout + 1); 3380e45b1eSTimo Kokkonen } 3480e45b1eSTimo Kokkonen 35b2c4e4b2SJarkko Nikula static int twl4030_wdt_stop(struct watchdog_device *wdt) 3680e45b1eSTimo Kokkonen { 3780e45b1eSTimo Kokkonen return twl4030_wdt_write(0); 3880e45b1eSTimo Kokkonen } 3980e45b1eSTimo Kokkonen 40b2c4e4b2SJarkko Nikula static int twl4030_wdt_set_timeout(struct watchdog_device *wdt, 41b2c4e4b2SJarkko Nikula unsigned int timeout) 4280e45b1eSTimo Kokkonen { 43b2c4e4b2SJarkko Nikula wdt->timeout = timeout; 44b2c4e4b2SJarkko Nikula return 0; 4580e45b1eSTimo Kokkonen } 4680e45b1eSTimo Kokkonen 47b2c4e4b2SJarkko Nikula static const struct watchdog_info twl4030_wdt_info = { 48fb1cbeaeSTony Lindgren .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, 4980e45b1eSTimo Kokkonen .identity = "TWL4030 Watchdog", 5080e45b1eSTimo Kokkonen }; 5180e45b1eSTimo Kokkonen 52b2c4e4b2SJarkko Nikula static const struct watchdog_ops twl4030_wdt_ops = { 5380e45b1eSTimo Kokkonen .owner = THIS_MODULE, 54b2c4e4b2SJarkko Nikula .start = twl4030_wdt_start, 55b2c4e4b2SJarkko Nikula .stop = twl4030_wdt_stop, 56b2c4e4b2SJarkko Nikula .set_timeout = twl4030_wdt_set_timeout, 5780e45b1eSTimo Kokkonen }; 5880e45b1eSTimo Kokkonen 592d991a16SBill Pemberton static int twl4030_wdt_probe(struct platform_device *pdev) 6080e45b1eSTimo Kokkonen { 61b42488bcSGuenter Roeck struct device *dev = &pdev->dev; 62b2c4e4b2SJarkko Nikula struct watchdog_device *wdt; 6380e45b1eSTimo Kokkonen 64b42488bcSGuenter Roeck wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); 6580e45b1eSTimo Kokkonen if (!wdt) 6680e45b1eSTimo Kokkonen return -ENOMEM; 6780e45b1eSTimo Kokkonen 68b2c4e4b2SJarkko Nikula wdt->info = &twl4030_wdt_info; 69b2c4e4b2SJarkko Nikula wdt->ops = &twl4030_wdt_ops; 70b2c4e4b2SJarkko Nikula wdt->status = 0; 71b2c4e4b2SJarkko Nikula wdt->timeout = 30; 72b2c4e4b2SJarkko Nikula wdt->min_timeout = 1; 73b2c4e4b2SJarkko Nikula wdt->max_timeout = 30; 74b42488bcSGuenter Roeck wdt->parent = dev; 7580e45b1eSTimo Kokkonen 76b2c4e4b2SJarkko Nikula watchdog_set_nowayout(wdt, nowayout); 7780e45b1eSTimo Kokkonen platform_set_drvdata(pdev, wdt); 7880e45b1eSTimo Kokkonen 79b2c4e4b2SJarkko Nikula twl4030_wdt_stop(wdt); 8080e45b1eSTimo Kokkonen 81b42488bcSGuenter Roeck return devm_watchdog_register_device(dev, wdt); 8280e45b1eSTimo Kokkonen } 8380e45b1eSTimo Kokkonen 8480e45b1eSTimo Kokkonen #ifdef CONFIG_PM 8580e45b1eSTimo Kokkonen static int twl4030_wdt_suspend(struct platform_device *pdev, pm_message_t state) 8680e45b1eSTimo Kokkonen { 87b2c4e4b2SJarkko Nikula struct watchdog_device *wdt = platform_get_drvdata(pdev); 88b2c4e4b2SJarkko Nikula if (watchdog_active(wdt)) 89b2c4e4b2SJarkko Nikula return twl4030_wdt_stop(wdt); 9080e45b1eSTimo Kokkonen 9180e45b1eSTimo Kokkonen return 0; 9280e45b1eSTimo Kokkonen } 9380e45b1eSTimo Kokkonen 9480e45b1eSTimo Kokkonen static int twl4030_wdt_resume(struct platform_device *pdev) 9580e45b1eSTimo Kokkonen { 96b2c4e4b2SJarkko Nikula struct watchdog_device *wdt = platform_get_drvdata(pdev); 97b2c4e4b2SJarkko Nikula if (watchdog_active(wdt)) 98b2c4e4b2SJarkko Nikula return twl4030_wdt_start(wdt); 9980e45b1eSTimo Kokkonen 10080e45b1eSTimo Kokkonen return 0; 10180e45b1eSTimo Kokkonen } 10280e45b1eSTimo Kokkonen #else 10380e45b1eSTimo Kokkonen #define twl4030_wdt_suspend NULL 10480e45b1eSTimo Kokkonen #define twl4030_wdt_resume NULL 10580e45b1eSTimo Kokkonen #endif 10680e45b1eSTimo Kokkonen 1078899b8d9SAaro Koskinen static const struct of_device_id twl_wdt_of_match[] = { 1088899b8d9SAaro Koskinen { .compatible = "ti,twl4030-wdt", }, 1098899b8d9SAaro Koskinen { }, 1108899b8d9SAaro Koskinen }; 1118899b8d9SAaro Koskinen MODULE_DEVICE_TABLE(of, twl_wdt_of_match); 1128899b8d9SAaro Koskinen 11380e45b1eSTimo Kokkonen static struct platform_driver twl4030_wdt_driver = { 11480e45b1eSTimo Kokkonen .probe = twl4030_wdt_probe, 11580e45b1eSTimo Kokkonen .suspend = twl4030_wdt_suspend, 11680e45b1eSTimo Kokkonen .resume = twl4030_wdt_resume, 11780e45b1eSTimo Kokkonen .driver = { 11880e45b1eSTimo Kokkonen .name = "twl4030_wdt", 1198899b8d9SAaro Koskinen .of_match_table = twl_wdt_of_match, 12080e45b1eSTimo Kokkonen }, 12180e45b1eSTimo Kokkonen }; 12280e45b1eSTimo Kokkonen 123b8ec6118SAxel Lin module_platform_driver(twl4030_wdt_driver); 12480e45b1eSTimo Kokkonen 12580e45b1eSTimo Kokkonen MODULE_AUTHOR("Nokia Corporation"); 12680e45b1eSTimo Kokkonen MODULE_LICENSE("GPL"); 12780e45b1eSTimo Kokkonen MODULE_ALIAS("platform:twl4030_wdt"); 12880e45b1eSTimo Kokkonen 129