FILE
intel_oc_wdt.c
File preview with syntax highlighting.
linux-6.19.14/drivers/watchdog/intel_oc_wdt.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Intel OC Watchdog driver 4 * 5 * Copyright (C) 2025, Siemens 6 * Author: Diogo Ivo <diogo.ivo@siemens.com> 7 */ 8 9#define DRV_NAME "intel_oc_wdt" 10 11#include <linux/acpi.h> 12#include <linux/bits.h> 13#include <linux/io.h> 14#include <linux/module.h> 15#include <linux/moduleparam.h> 16#include <linux/platform_device.h> 17#include <linux/watchdog.h> 18 19#define INTEL_OC_WDT_TOV GENMASK(9, 0) 20#define INTEL_OC_WDT_MIN_TOV 1 21#define INTEL_OC_WDT_MAX_TOV 1024 22#define INTEL_OC_WDT_DEF_TOV 60 23 24/* 25 * One-time writable lock bit. If set forbids 26 * modification of itself, _TOV and _EN until 27 * next reboot. 28 */ 29#define INTEL_OC_WDT_CTL_LCK BIT(12) 30 31#define INTEL_OC_WDT_EN BIT(14) 32#define INTEL_OC_WDT_NO_ICCSURV_STS BIT(24) 33#define INTEL_OC_WDT_ICCSURV_STS BIT(25) 34#define INTEL_OC_WDT_RLD BIT(31) 35 36#define INTEL_OC_WDT_STS_BITS (INTEL_OC_WDT_NO_ICCSURV_STS | \ 37 INTEL_OC_WDT_ICCSURV_STS) 38 39#define INTEL_OC_WDT_CTRL_REG(wdt) ((wdt)->ctrl_res->start) 40 41struct intel_oc_wdt { 42 struct watchdog_device wdd; 43 struct resource *ctrl_res; 44 struct watchdog_info info; 45 bool locked; 46}; 47 48static int heartbeat; 49module_param(heartbeat, uint, 0); 50MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. (default=" 51 __MODULE_STRING(WDT_HEARTBEAT) ")"); 52 53static bool nowayout = WATCHDOG_NOWAYOUT; 54module_param(nowayout, bool, 0); 55MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 56 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 57 58static int intel_oc_wdt_start(struct watchdog_device *wdd) 59{ 60 struct intel_oc_wdt *oc_wdt = watchdog_get_drvdata(wdd); 61 62 if (oc_wdt->locked) 63 return 0; 64 65 outl(inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)) | INTEL_OC_WDT_EN, 66 INTEL_OC_WDT_CTRL_REG(oc_wdt)); 67 68 return 0; 69} 70 71static int intel_oc_wdt_stop(struct watchdog_device *wdd) 72{ 73 struct intel_oc_wdt *oc_wdt = watchdog_get_drvdata(wdd); 74 75 outl(inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)) & ~INTEL_OC_WDT_EN, 76 INTEL_OC_WDT_CTRL_REG(oc_wdt)); 77 78 return 0; 79} 80 81static int intel_oc_wdt_ping(struct watchdog_device *wdd) 82{ 83 struct intel_oc_wdt *oc_wdt = watchdog_get_drvdata(wdd); 84 85 outl(inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)) | INTEL_OC_WDT_RLD, 86 INTEL_OC_WDT_CTRL_REG(oc_wdt)); 87 88 return 0; 89} 90 91static int intel_oc_wdt_set_timeout(struct watchdog_device *wdd, 92 unsigned int t) 93{ 94 struct intel_oc_wdt *oc_wdt = watchdog_get_drvdata(wdd); 95 96 outl((inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)) & ~INTEL_OC_WDT_TOV) | (t - 1), 97 INTEL_OC_WDT_CTRL_REG(oc_wdt)); 98 99 wdd->timeout = t; 100 101 return 0; 102} 103 104static const struct watchdog_info intel_oc_wdt_info = { 105 .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, 106 .identity = DRV_NAME, 107}; 108 109static const struct watchdog_ops intel_oc_wdt_ops = { 110 .owner = THIS_MODULE, 111 .start = intel_oc_wdt_start, 112 .stop = intel_oc_wdt_stop, 113 .ping = intel_oc_wdt_ping, 114 .set_timeout = intel_oc_wdt_set_timeout, 115}; 116 117static int intel_oc_wdt_setup(struct intel_oc_wdt *oc_wdt) 118{ 119 unsigned long val; 120 121 val = inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)); 122 123 if (val & INTEL_OC_WDT_STS_BITS) 124 oc_wdt->wdd.bootstatus |= WDIOF_CARDRESET; 125 126 oc_wdt->locked = !!(val & INTEL_OC_WDT_CTL_LCK); 127 128 if (val & INTEL_OC_WDT_EN) { 129 /* 130 * No need to issue a ping here to "commit" the new timeout 131 * value to hardware as the watchdog core schedules one 132 * immediately when registering the watchdog. 133 */ 134 set_bit(WDOG_HW_RUNNING, &oc_wdt->wdd.status); 135 136 if (oc_wdt->locked) { 137 /* 138 * Set nowayout unconditionally as we cannot stop 139 * the watchdog. 140 */ 141 nowayout = true; 142 /* 143 * If we are locked read the current timeout value 144 * and inform the core we can't change it. 145 */ 146 oc_wdt->wdd.timeout = (val & INTEL_OC_WDT_TOV) + 1; 147 oc_wdt->info.options &= ~WDIOF_SETTIMEOUT; 148 149 dev_info(oc_wdt->wdd.parent, 150 "Register access locked, heartbeat fixed at: %u s\n", 151 oc_wdt->wdd.timeout); 152 } 153 } else if (oc_wdt->locked) { 154 /* 155 * In case the watchdog is disabled and locked there 156 * is nothing we can do with it so just fail probing. 157 */ 158 return -EACCES; 159 } 160 161 val &= ~INTEL_OC_WDT_TOV; 162 outl(val | (oc_wdt->wdd.timeout - 1), INTEL_OC_WDT_CTRL_REG(oc_wdt)); 163 164 return 0; 165} 166 167static int intel_oc_wdt_probe(struct platform_device *pdev) 168{ 169 struct device *dev = &pdev->dev; 170 struct intel_oc_wdt *oc_wdt; 171 struct watchdog_device *wdd; 172 int ret; 173 174 oc_wdt = devm_kzalloc(&pdev->dev, sizeof(*oc_wdt), GFP_KERNEL); 175 if (!oc_wdt) 176 return -ENOMEM; 177 178 oc_wdt->ctrl_res = platform_get_resource(pdev, IORESOURCE_IO, 0); 179 if (!oc_wdt->ctrl_res) { 180 dev_err(&pdev->dev, "missing I/O resource\n"); 181 return -ENODEV; 182 } 183 184 if (!devm_request_region(&pdev->dev, oc_wdt->ctrl_res->start, 185 resource_size(oc_wdt->ctrl_res), pdev->name)) { 186 dev_err(dev, "resource %pR already in use, device disabled\n", 187 oc_wdt->ctrl_res); 188 return -EBUSY; 189 } 190 191 wdd = &oc_wdt->wdd; 192 wdd->min_timeout = INTEL_OC_WDT_MIN_TOV; 193 wdd->max_timeout = INTEL_OC_WDT_MAX_TOV; 194 wdd->timeout = INTEL_OC_WDT_DEF_TOV; 195 oc_wdt->info = intel_oc_wdt_info; 196 wdd->info = &oc_wdt->info; 197 wdd->ops = &intel_oc_wdt_ops; 198 wdd->parent = dev; 199 200 watchdog_init_timeout(wdd, heartbeat, dev); 201 202 ret = intel_oc_wdt_setup(oc_wdt); 203 if (ret) 204 return ret; 205 206 watchdog_set_drvdata(wdd, oc_wdt); 207 watchdog_set_nowayout(wdd, nowayout); 208 watchdog_stop_on_reboot(wdd); 209 watchdog_stop_on_unregister(wdd); 210 211 return devm_watchdog_register_device(dev, wdd); 212} 213 214static const struct acpi_device_id intel_oc_wdt_match[] = { 215 { "INT3F0D" }, 216 { "INTC1099" }, 217 { }, 218}; 219MODULE_DEVICE_TABLE(acpi, intel_oc_wdt_match); 220 221static struct platform_driver intel_oc_wdt_platform_driver = { 222 .driver = { 223 .name = DRV_NAME, 224 .acpi_match_table = intel_oc_wdt_match, 225 }, 226 .probe = intel_oc_wdt_probe, 227}; 228 229module_platform_driver(intel_oc_wdt_platform_driver); 230 231MODULE_AUTHOR("Diogo Ivo <diogo.ivo@siemens.com>"); 232MODULE_LICENSE("GPL"); 233MODULE_DESCRIPTION("Intel OC Watchdog driver");
~/
Read-only file preview.