180e45b1eSTimo Kokkonen /* 280e45b1eSTimo Kokkonen * Copyright (C) Nokia Corporation 380e45b1eSTimo Kokkonen * 480e45b1eSTimo Kokkonen * Written by Timo Kokkonen <timo.t.kokkonen at nokia.com> 580e45b1eSTimo Kokkonen * 680e45b1eSTimo Kokkonen * This program is free software; you can redistribute it and/or modify 780e45b1eSTimo Kokkonen * it under the terms of the GNU General Public License as published by 880e45b1eSTimo Kokkonen * the Free Software Foundation; either version 2 of the License, or 980e45b1eSTimo Kokkonen * (at your option) any later version. 1080e45b1eSTimo Kokkonen * 1180e45b1eSTimo Kokkonen * This program is distributed in the hope that it will be useful, 1280e45b1eSTimo Kokkonen * but WITHOUT ANY WARRANTY; without even the implied warranty of 1380e45b1eSTimo Kokkonen * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1480e45b1eSTimo Kokkonen * GNU General Public License for more details. 1580e45b1eSTimo Kokkonen * 1680e45b1eSTimo Kokkonen * You should have received a copy of the GNU General Public License 1780e45b1eSTimo Kokkonen * along with this program; if not, write to the Free Software 1880e45b1eSTimo Kokkonen * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 1980e45b1eSTimo Kokkonen */ 2080e45b1eSTimo Kokkonen 2180e45b1eSTimo Kokkonen #include <linux/module.h> 2280e45b1eSTimo Kokkonen #include <linux/types.h> 235a0e3ad6STejun Heo #include <linux/slab.h> 2480e45b1eSTimo Kokkonen #include <linux/kernel.h> 2580e45b1eSTimo Kokkonen #include <linux/watchdog.h> 2680e45b1eSTimo Kokkonen #include <linux/platform_device.h> 27b07682b6SSantosh Shilimkar #include <linux/i2c/twl.h> 2880e45b1eSTimo Kokkonen 2980e45b1eSTimo Kokkonen #define TWL4030_WATCHDOG_CFG_REG_OFFS 0x3 3080e45b1eSTimo Kokkonen 3186a1e189SWim Van Sebroeck static bool nowayout = WATCHDOG_NOWAYOUT; 3286a1e189SWim Van Sebroeck module_param(nowayout, bool, 0); 3380e45b1eSTimo Kokkonen MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " 3480e45b1eSTimo Kokkonen "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 3580e45b1eSTimo Kokkonen 3680e45b1eSTimo Kokkonen static int twl4030_wdt_write(unsigned char val) 3780e45b1eSTimo Kokkonen { 38*2bc3f62fSPeter Ujfalusi return twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, val, 3980e45b1eSTimo Kokkonen TWL4030_WATCHDOG_CFG_REG_OFFS); 4080e45b1eSTimo Kokkonen } 4180e45b1eSTimo Kokkonen 42b2c4e4b2SJarkko Nikula static int twl4030_wdt_start(struct watchdog_device *wdt) 4380e45b1eSTimo Kokkonen { 44b2c4e4b2SJarkko Nikula return twl4030_wdt_write(wdt->timeout + 1); 4580e45b1eSTimo Kokkonen } 4680e45b1eSTimo Kokkonen 47b2c4e4b2SJarkko Nikula static int twl4030_wdt_stop(struct watchdog_device *wdt) 4880e45b1eSTimo Kokkonen { 4980e45b1eSTimo Kokkonen return twl4030_wdt_write(0); 5080e45b1eSTimo Kokkonen } 5180e45b1eSTimo Kokkonen 52b2c4e4b2SJarkko Nikula static int twl4030_wdt_set_timeout(struct watchdog_device *wdt, 53b2c4e4b2SJarkko Nikula unsigned int timeout) 5480e45b1eSTimo Kokkonen { 55b2c4e4b2SJarkko Nikula wdt->timeout = timeout; 56b2c4e4b2SJarkko Nikula return 0; 5780e45b1eSTimo Kokkonen } 5880e45b1eSTimo Kokkonen 59b2c4e4b2SJarkko Nikula static const struct watchdog_info twl4030_wdt_info = { 60b2c4e4b2SJarkko Nikula .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, 6180e45b1eSTimo Kokkonen .identity = "TWL4030 Watchdog", 6280e45b1eSTimo Kokkonen }; 6380e45b1eSTimo Kokkonen 64b2c4e4b2SJarkko Nikula static const struct watchdog_ops twl4030_wdt_ops = { 6580e45b1eSTimo Kokkonen .owner = THIS_MODULE, 66b2c4e4b2SJarkko Nikula .start = twl4030_wdt_start, 67b2c4e4b2SJarkko Nikula .stop = twl4030_wdt_stop, 68b2c4e4b2SJarkko Nikula .set_timeout = twl4030_wdt_set_timeout, 6980e45b1eSTimo Kokkonen }; 7080e45b1eSTimo Kokkonen 712d991a16SBill Pemberton static int twl4030_wdt_probe(struct platform_device *pdev) 7280e45b1eSTimo Kokkonen { 7380e45b1eSTimo Kokkonen int ret = 0; 74b2c4e4b2SJarkko Nikula struct watchdog_device *wdt; 7580e45b1eSTimo Kokkonen 76b2c4e4b2SJarkko Nikula wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); 7780e45b1eSTimo Kokkonen if (!wdt) 7880e45b1eSTimo Kokkonen return -ENOMEM; 7980e45b1eSTimo Kokkonen 80b2c4e4b2SJarkko Nikula wdt->info = &twl4030_wdt_info; 81b2c4e4b2SJarkko Nikula wdt->ops = &twl4030_wdt_ops; 82b2c4e4b2SJarkko Nikula wdt->status = 0; 83b2c4e4b2SJarkko Nikula wdt->timeout = 30; 84b2c4e4b2SJarkko Nikula wdt->min_timeout = 1; 85b2c4e4b2SJarkko Nikula wdt->max_timeout = 30; 8680e45b1eSTimo Kokkonen 87b2c4e4b2SJarkko Nikula watchdog_set_nowayout(wdt, nowayout); 8880e45b1eSTimo Kokkonen platform_set_drvdata(pdev, wdt); 8980e45b1eSTimo Kokkonen 90b2c4e4b2SJarkko Nikula twl4030_wdt_stop(wdt); 9180e45b1eSTimo Kokkonen 92b2c4e4b2SJarkko Nikula ret = watchdog_register_device(wdt); 9380e45b1eSTimo Kokkonen if (ret) { 9480e45b1eSTimo Kokkonen platform_set_drvdata(pdev, NULL); 9580e45b1eSTimo Kokkonen return ret; 9680e45b1eSTimo Kokkonen } 97b2c4e4b2SJarkko Nikula 9880e45b1eSTimo Kokkonen return 0; 9980e45b1eSTimo Kokkonen } 10080e45b1eSTimo Kokkonen 1014b12b896SBill Pemberton static int twl4030_wdt_remove(struct platform_device *pdev) 10280e45b1eSTimo Kokkonen { 103b2c4e4b2SJarkko Nikula struct watchdog_device *wdt = platform_get_drvdata(pdev); 10480e45b1eSTimo Kokkonen 105b2c4e4b2SJarkko Nikula watchdog_unregister_device(wdt); 10680e45b1eSTimo Kokkonen platform_set_drvdata(pdev, NULL); 10780e45b1eSTimo Kokkonen 10880e45b1eSTimo Kokkonen return 0; 10980e45b1eSTimo Kokkonen } 11080e45b1eSTimo Kokkonen 11180e45b1eSTimo Kokkonen #ifdef CONFIG_PM 11280e45b1eSTimo Kokkonen static int twl4030_wdt_suspend(struct platform_device *pdev, pm_message_t state) 11380e45b1eSTimo Kokkonen { 114b2c4e4b2SJarkko Nikula struct watchdog_device *wdt = platform_get_drvdata(pdev); 115b2c4e4b2SJarkko Nikula if (watchdog_active(wdt)) 116b2c4e4b2SJarkko Nikula return twl4030_wdt_stop(wdt); 11780e45b1eSTimo Kokkonen 11880e45b1eSTimo Kokkonen return 0; 11980e45b1eSTimo Kokkonen } 12080e45b1eSTimo Kokkonen 12180e45b1eSTimo Kokkonen static int twl4030_wdt_resume(struct platform_device *pdev) 12280e45b1eSTimo Kokkonen { 123b2c4e4b2SJarkko Nikula struct watchdog_device *wdt = platform_get_drvdata(pdev); 124b2c4e4b2SJarkko Nikula if (watchdog_active(wdt)) 125b2c4e4b2SJarkko Nikula return twl4030_wdt_start(wdt); 12680e45b1eSTimo Kokkonen 12780e45b1eSTimo Kokkonen return 0; 12880e45b1eSTimo Kokkonen } 12980e45b1eSTimo Kokkonen #else 13080e45b1eSTimo Kokkonen #define twl4030_wdt_suspend NULL 13180e45b1eSTimo Kokkonen #define twl4030_wdt_resume NULL 13280e45b1eSTimo Kokkonen #endif 13380e45b1eSTimo Kokkonen 13480e45b1eSTimo Kokkonen static struct platform_driver twl4030_wdt_driver = { 13580e45b1eSTimo Kokkonen .probe = twl4030_wdt_probe, 13682268714SBill Pemberton .remove = twl4030_wdt_remove, 13780e45b1eSTimo Kokkonen .suspend = twl4030_wdt_suspend, 13880e45b1eSTimo Kokkonen .resume = twl4030_wdt_resume, 13980e45b1eSTimo Kokkonen .driver = { 14080e45b1eSTimo Kokkonen .owner = THIS_MODULE, 14180e45b1eSTimo Kokkonen .name = "twl4030_wdt", 14280e45b1eSTimo Kokkonen }, 14380e45b1eSTimo Kokkonen }; 14480e45b1eSTimo Kokkonen 145b8ec6118SAxel Lin module_platform_driver(twl4030_wdt_driver); 14680e45b1eSTimo Kokkonen 14780e45b1eSTimo Kokkonen MODULE_AUTHOR("Nokia Corporation"); 14880e45b1eSTimo Kokkonen MODULE_LICENSE("GPL"); 14980e45b1eSTimo Kokkonen MODULE_ALIAS("platform:twl4030_wdt"); 15080e45b1eSTimo Kokkonen 151