1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) ST-Ericsson SA 2011-2013 4 * 5 * Author: Mathieu Poirier <mathieu.poirier@linaro.org> for ST-Ericsson 6 * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson 7 */ 8 9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 10 11#include <linux/module.h> 12#include <linux/kernel.h> 13#include <linux/moduleparam.h> 14#include <linux/err.h> 15#include <linux/uaccess.h> 16#include <linux/watchdog.h> 17#include <linux/platform_device.h> 18#include <linux/platform_data/ux500_wdt.h> 19 20#include <linux/mfd/dbx500-prcmu.h> 21 22#define WATCHDOG_TIMEOUT 600 /* 10 minutes */ 23 24#define WATCHDOG_MIN 0 25#define WATCHDOG_MAX28 268435 /* 28 bit resolution in ms == 268435.455 s */ 26#define WATCHDOG_MAX32 4294967 /* 32 bit resolution in ms == 4294967.295 s */ 27 28static unsigned int timeout = WATCHDOG_TIMEOUT; 29module_param(timeout, uint, 0); 30MODULE_PARM_DESC(timeout, 31 "Watchdog timeout in seconds. default=" 32 __MODULE_STRING(WATCHDOG_TIMEOUT) "."); 33 34static bool nowayout = WATCHDOG_NOWAYOUT; 35module_param(nowayout, bool, 0); 36MODULE_PARM_DESC(nowayout, 37 "Watchdog cannot be stopped once started (default=" 38 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 39 40static int ux500_wdt_start(struct watchdog_device *wdd) 41{ 42 return prcmu_enable_a9wdog(PRCMU_WDOG_ALL); 43} 44 45static int ux500_wdt_stop(struct watchdog_device *wdd) 46{ 47 return prcmu_disable_a9wdog(PRCMU_WDOG_ALL); 48} 49 50static int ux500_wdt_keepalive(struct watchdog_device *wdd) 51{ 52 return prcmu_kick_a9wdog(PRCMU_WDOG_ALL); 53} 54 55static int ux500_wdt_set_timeout(struct watchdog_device *wdd, 56 unsigned int timeout) 57{ 58 ux500_wdt_stop(wdd); 59 prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); 60 ux500_wdt_start(wdd); 61 62 return 0; 63} 64 65static const struct watchdog_info ux500_wdt_info = { 66 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 67 .identity = "Ux500 WDT", 68 .firmware_version = 1, 69}; 70 71static const struct watchdog_ops ux500_wdt_ops = { 72 .owner = THIS_MODULE, 73 .start = ux500_wdt_start, 74 .stop = ux500_wdt_stop, 75 .ping = ux500_wdt_keepalive, 76 .set_timeout = ux500_wdt_set_timeout, 77}; 78 79static struct watchdog_device ux500_wdt = { 80 .info = &ux500_wdt_info, 81 .ops = &ux500_wdt_ops, 82 .min_timeout = WATCHDOG_MIN, 83 .max_timeout = WATCHDOG_MAX32, 84}; 85 86static int ux500_wdt_probe(struct platform_device *pdev) 87{ 88 struct device *dev = &pdev->dev; 89 int ret; 90 struct ux500_wdt_data *pdata = dev_get_platdata(dev); 91 92 if (pdata) { 93 if (pdata->timeout > 0) 94 timeout = pdata->timeout; 95 if (pdata->has_28_bits_resolution) 96 ux500_wdt.max_timeout = WATCHDOG_MAX28; 97 } 98 99 ux500_wdt.parent = dev; 100 watchdog_set_nowayout(&ux500_wdt, nowayout); 101 102 /* disable auto off on sleep */ 103 prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); 104 105 /* set HW initial value */ 106 prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); 107 108 ret = devm_watchdog_register_device(dev, &ux500_wdt); 109 if (ret) 110 return ret; 111 112 dev_info(dev, "initialized\n"); 113 114 return 0; 115} 116 117#ifdef CONFIG_PM 118static int ux500_wdt_suspend(struct platform_device *pdev, 119 pm_message_t state) 120{ 121 if (watchdog_active(&ux500_wdt)) { 122 ux500_wdt_stop(&ux500_wdt); 123 prcmu_config_a9wdog(PRCMU_WDOG_CPU1, true); 124 125 prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); 126 ux500_wdt_start(&ux500_wdt); 127 } 128 return 0; 129} 130 131static int ux500_wdt_resume(struct platform_device *pdev) 132{ 133 if (watchdog_active(&ux500_wdt)) { 134 ux500_wdt_stop(&ux500_wdt); 135 prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); 136 137 prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); 138 ux500_wdt_start(&ux500_wdt); 139 } 140 return 0; 141} 142#else 143#define ux500_wdt_suspend NULL 144#define ux500_wdt_resume NULL 145#endif 146 147static struct platform_driver ux500_wdt_driver = { 148 .probe = ux500_wdt_probe, 149 .suspend = ux500_wdt_suspend, 150 .resume = ux500_wdt_resume, 151 .driver = { 152 .name = "ux500_wdt", 153 }, 154}; 155 156module_platform_driver(ux500_wdt_driver); 157 158MODULE_AUTHOR("Jonas Aaberg <jonas.aberg@stericsson.com>"); 159MODULE_DESCRIPTION("Ux500 Watchdog Driver"); 160MODULE_LICENSE("GPL"); 161MODULE_ALIAS("platform:ux500_wdt"); 162