1 /* 2 * Copyright (C) Nokia Corporation 3 * 4 * Written by Timo Kokkonen <timo.t.kokkonen at nokia.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20 21 #include <linux/module.h> 22 #include <linux/types.h> 23 #include <linux/slab.h> 24 #include <linux/kernel.h> 25 #include <linux/watchdog.h> 26 #include <linux/platform_device.h> 27 #include <linux/mfd/twl.h> 28 29 #define TWL4030_WATCHDOG_CFG_REG_OFFS 0x3 30 31 static bool nowayout = WATCHDOG_NOWAYOUT; 32 module_param(nowayout, bool, 0); 33 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " 34 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 35 36 static int twl4030_wdt_write(unsigned char val) 37 { 38 return twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, val, 39 TWL4030_WATCHDOG_CFG_REG_OFFS); 40 } 41 42 static int twl4030_wdt_start(struct watchdog_device *wdt) 43 { 44 return twl4030_wdt_write(wdt->timeout + 1); 45 } 46 47 static int twl4030_wdt_stop(struct watchdog_device *wdt) 48 { 49 return twl4030_wdt_write(0); 50 } 51 52 static int twl4030_wdt_set_timeout(struct watchdog_device *wdt, 53 unsigned int timeout) 54 { 55 wdt->timeout = timeout; 56 return 0; 57 } 58 59 static const struct watchdog_info twl4030_wdt_info = { 60 .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, 61 .identity = "TWL4030 Watchdog", 62 }; 63 64 static const struct watchdog_ops twl4030_wdt_ops = { 65 .owner = THIS_MODULE, 66 .start = twl4030_wdt_start, 67 .stop = twl4030_wdt_stop, 68 .set_timeout = twl4030_wdt_set_timeout, 69 }; 70 71 static int twl4030_wdt_probe(struct platform_device *pdev) 72 { 73 struct device *dev = &pdev->dev; 74 struct watchdog_device *wdt; 75 76 wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); 77 if (!wdt) 78 return -ENOMEM; 79 80 wdt->info = &twl4030_wdt_info; 81 wdt->ops = &twl4030_wdt_ops; 82 wdt->status = 0; 83 wdt->timeout = 30; 84 wdt->min_timeout = 1; 85 wdt->max_timeout = 30; 86 wdt->parent = dev; 87 88 watchdog_set_nowayout(wdt, nowayout); 89 platform_set_drvdata(pdev, wdt); 90 91 twl4030_wdt_stop(wdt); 92 93 return devm_watchdog_register_device(dev, wdt); 94 } 95 96 #ifdef CONFIG_PM 97 static int twl4030_wdt_suspend(struct platform_device *pdev, pm_message_t state) 98 { 99 struct watchdog_device *wdt = platform_get_drvdata(pdev); 100 if (watchdog_active(wdt)) 101 return twl4030_wdt_stop(wdt); 102 103 return 0; 104 } 105 106 static int twl4030_wdt_resume(struct platform_device *pdev) 107 { 108 struct watchdog_device *wdt = platform_get_drvdata(pdev); 109 if (watchdog_active(wdt)) 110 return twl4030_wdt_start(wdt); 111 112 return 0; 113 } 114 #else 115 #define twl4030_wdt_suspend NULL 116 #define twl4030_wdt_resume NULL 117 #endif 118 119 static const struct of_device_id twl_wdt_of_match[] = { 120 { .compatible = "ti,twl4030-wdt", }, 121 { }, 122 }; 123 MODULE_DEVICE_TABLE(of, twl_wdt_of_match); 124 125 static struct platform_driver twl4030_wdt_driver = { 126 .probe = twl4030_wdt_probe, 127 .suspend = twl4030_wdt_suspend, 128 .resume = twl4030_wdt_resume, 129 .driver = { 130 .name = "twl4030_wdt", 131 .of_match_table = twl_wdt_of_match, 132 }, 133 }; 134 135 module_platform_driver(twl4030_wdt_driver); 136 137 MODULE_AUTHOR("Nokia Corporation"); 138 MODULE_LICENSE("GPL"); 139 MODULE_ALIAS("platform:twl4030_wdt"); 140 141