1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * System monitoring driver for DA9052 PMICs. 4 * 5 * Copyright(c) 2012 Dialog Semiconductor Ltd. 6 * 7 * Author: Anthony Olech <Anthony.Olech@diasemi.com> 8 * 9 */ 10 11 #include <linux/module.h> 12 #include <linux/delay.h> 13 #include <linux/uaccess.h> 14 #include <linux/platform_device.h> 15 #include <linux/time.h> 16 #include <linux/watchdog.h> 17 #include <linux/types.h> 18 #include <linux/kernel.h> 19 #include <linux/jiffies.h> 20 21 #include <linux/mfd/da9052/reg.h> 22 #include <linux/mfd/da9052/da9052.h> 23 24 #define DA9052_DEF_TIMEOUT 4 25 #define DA9052_TWDMIN 256 26 27 struct da9052_wdt_data { 28 struct watchdog_device wdt; 29 struct da9052 *da9052; 30 unsigned long jpast; 31 }; 32 33 static bool nowayout = WATCHDOG_NOWAYOUT; 34 module_param(nowayout, bool, 0); 35 MODULE_PARM_DESC(nowayout, 36 "Watchdog cannot be stopped once started (default=" 37 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 38 39 static int timeout; 40 module_param(timeout, int, 0); 41 MODULE_PARM_DESC(timeout, 42 "Watchdog timeout in seconds. (default = " 43 __MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")"); 44 45 static const struct { 46 u8 reg_val; 47 int time; /* Seconds */ 48 } da9052_wdt_maps[] = { 49 { 1, 2 }, 50 { 2, 4 }, 51 { 3, 8 }, 52 { 4, 16 }, 53 { 5, 32 }, 54 { 5, 33 }, /* Actual time 32.768s so included both 32s and 33s */ 55 { 6, 65 }, 56 { 6, 66 }, /* Actual time 65.536s so include both, 65s and 66s */ 57 { 7, 131 }, 58 }; 59 60 61 static int da9052_wdt_set_timeout(struct watchdog_device *wdt_dev, 62 unsigned int timeout) 63 { 64 struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); 65 struct da9052 *da9052 = driver_data->da9052; 66 int ret, i; 67 68 /* 69 * Disable the Watchdog timer before setting 70 * new time out. 71 */ 72 ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, 73 DA9052_CONTROLD_TWDSCALE, 0); 74 if (ret < 0) { 75 dev_err(da9052->dev, "Failed to disable watchdog bit, %d\n", 76 ret); 77 return ret; 78 } 79 if (timeout) { 80 /* 81 * To change the timeout, da9052 needs to 82 * be disabled for at least 150 us. 83 */ 84 udelay(150); 85 86 /* Set the desired timeout */ 87 for (i = 0; i < ARRAY_SIZE(da9052_wdt_maps); i++) 88 if (da9052_wdt_maps[i].time == timeout) 89 break; 90 91 if (i == ARRAY_SIZE(da9052_wdt_maps)) 92 ret = -EINVAL; 93 else 94 ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, 95 DA9052_CONTROLD_TWDSCALE, 96 da9052_wdt_maps[i].reg_val); 97 if (ret < 0) { 98 dev_err(da9052->dev, 99 "Failed to update timescale bit, %d\n", ret); 100 return ret; 101 } 102 103 wdt_dev->timeout = timeout; 104 driver_data->jpast = jiffies; 105 } 106 107 return 0; 108 } 109 110 static int da9052_wdt_start(struct watchdog_device *wdt_dev) 111 { 112 return da9052_wdt_set_timeout(wdt_dev, wdt_dev->timeout); 113 } 114 115 static int da9052_wdt_stop(struct watchdog_device *wdt_dev) 116 { 117 return da9052_wdt_set_timeout(wdt_dev, 0); 118 } 119 120 static int da9052_wdt_ping(struct watchdog_device *wdt_dev) 121 { 122 struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); 123 struct da9052 *da9052 = driver_data->da9052; 124 unsigned long msec, jnow = jiffies; 125 int ret; 126 127 /* 128 * We have a minimum time for watchdog window called TWDMIN. A write 129 * to the watchdog before this elapsed time should cause an error. 130 */ 131 msec = (jnow - driver_data->jpast) * 1000/HZ; 132 if (msec < DA9052_TWDMIN) 133 mdelay(msec); 134 135 /* Reset the watchdog timer */ 136 ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, 137 DA9052_CONTROLD_WATCHDOG, 1 << 7); 138 if (ret < 0) 139 return ret; 140 141 /* 142 * FIXME: Reset the watchdog core, in general PMIC 143 * is supposed to do this 144 */ 145 return da9052_reg_update(da9052, DA9052_CONTROL_D_REG, 146 DA9052_CONTROLD_WATCHDOG, 0 << 7); 147 } 148 149 static const struct watchdog_info da9052_wdt_info = { 150 .options = WDIOF_SETTIMEOUT | 151 WDIOF_KEEPALIVEPING | 152 WDIOF_CARDRESET | 153 WDIOF_OVERHEAT | 154 WDIOF_POWERUNDER, 155 .identity = "DA9052 Watchdog", 156 }; 157 158 static const struct watchdog_ops da9052_wdt_ops = { 159 .owner = THIS_MODULE, 160 .start = da9052_wdt_start, 161 .stop = da9052_wdt_stop, 162 .ping = da9052_wdt_ping, 163 .set_timeout = da9052_wdt_set_timeout, 164 }; 165 166 167 static int da9052_wdt_probe(struct platform_device *pdev) 168 { 169 struct device *dev = &pdev->dev; 170 struct da9052 *da9052 = dev_get_drvdata(dev->parent); 171 struct da9052_wdt_data *driver_data; 172 struct watchdog_device *da9052_wdt; 173 int ret; 174 175 driver_data = devm_kzalloc(dev, sizeof(*driver_data), GFP_KERNEL); 176 if (!driver_data) 177 return -ENOMEM; 178 driver_data->da9052 = da9052; 179 180 da9052_wdt = &driver_data->wdt; 181 182 da9052_wdt->timeout = DA9052_DEF_TIMEOUT; 183 da9052_wdt->min_hw_heartbeat_ms = DA9052_TWDMIN; 184 da9052_wdt->info = &da9052_wdt_info; 185 da9052_wdt->ops = &da9052_wdt_ops; 186 da9052_wdt->parent = dev; 187 watchdog_set_drvdata(da9052_wdt, driver_data); 188 watchdog_init_timeout(da9052_wdt, timeout, dev); 189 watchdog_set_nowayout(da9052_wdt, nowayout); 190 191 if (da9052->fault_log & DA9052_FAULTLOG_TWDERROR) 192 da9052_wdt->bootstatus |= WDIOF_CARDRESET; 193 if (da9052->fault_log & DA9052_FAULTLOG_TEMPOVER) 194 da9052_wdt->bootstatus |= WDIOF_OVERHEAT; 195 if (da9052->fault_log & DA9052_FAULTLOG_VDDFAULT) 196 da9052_wdt->bootstatus |= WDIOF_POWERUNDER; 197 198 ret = da9052_reg_read(da9052, DA9052_CONTROL_D_REG); 199 if (ret < 0) 200 return ret; 201 202 /* Check if FW enabled the watchdog */ 203 if (ret & DA9052_CONTROLD_TWDSCALE) { 204 /* Ensure proper initialization */ 205 da9052_wdt_start(da9052_wdt); 206 set_bit(WDOG_HW_RUNNING, &da9052_wdt->status); 207 } 208 209 return devm_watchdog_register_device(dev, &driver_data->wdt); 210 } 211 212 static struct platform_driver da9052_wdt_driver = { 213 .probe = da9052_wdt_probe, 214 .driver = { 215 .name = "da9052-watchdog", 216 }, 217 }; 218 219 module_platform_driver(da9052_wdt_driver); 220 221 MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>"); 222 MODULE_DESCRIPTION("DA9052 SM Device Driver"); 223 MODULE_LICENSE("GPL"); 224 MODULE_ALIAS("platform:da9052-watchdog"); 225