blob: e707c4797f76e57d8e89eb9d5249728c409a58ad [file] [log] [blame]
Guenter Roeckd0173272019-06-20 09:28:46 -07001// SPDX-License-Identifier: GPL-2.0+
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +02002/*
Wim Van Sebroeckcb711a12009-11-15 13:44:54 +00003 * intel TCO Watchdog Driver
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +02004 *
Wim Van Sebroeckdeb91972011-10-19 23:59:26 +02005 * (c) Copyright 2006-2011 Wim Van Sebroeck <wim@iguana.be>.
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +02006 *
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +02007 * Neither Wim Van Sebroeck nor Iguana vzw. admit liability nor
8 * provide warranty for any of this software. This material is
9 * provided "AS-IS" and at no charge.
10 *
11 * The TCO watchdog is implemented in the following I/O controller hubs:
12 * (See the intel documentation on http://developer.intel.com.)
Wim Van Sebroeckcb711a12009-11-15 13:44:54 +000013 * document number 290655-003, 290677-014: 82801AA (ICH), 82801AB (ICHO)
14 * document number 290687-002, 298242-027: 82801BA (ICH2)
15 * document number 290733-003, 290739-013: 82801CA (ICH3-S)
16 * document number 290716-001, 290718-007: 82801CAM (ICH3-M)
17 * document number 290744-001, 290745-025: 82801DB (ICH4)
18 * document number 252337-001, 252663-008: 82801DBM (ICH4-M)
19 * document number 273599-001, 273645-002: 82801E (C-ICH)
20 * document number 252516-001, 252517-028: 82801EB (ICH5), 82801ER (ICH5R)
21 * document number 300641-004, 300884-013: 6300ESB
22 * document number 301473-002, 301474-026: 82801F (ICH6)
23 * document number 313082-001, 313075-006: 631xESB, 632xESB
24 * document number 307013-003, 307014-024: 82801G (ICH7)
Wim Van Sebroeckd38bd472010-12-31 14:10:45 +000025 * document number 322896-001, 322897-001: NM10
Wim Van Sebroeckcb711a12009-11-15 13:44:54 +000026 * document number 313056-003, 313057-017: 82801H (ICH8)
27 * document number 316972-004, 316973-012: 82801I (ICH9)
28 * document number 319973-002, 319974-002: 82801J (ICH10)
Seth Heasley3c9d8ec2010-01-14 20:58:05 +000029 * document number 322169-001, 322170-003: 5 Series, 3400 Series (PCH)
Imre Kaloz4946f832009-12-07 20:42:26 +010030 * document number 320066-003, 320257-008: EP80597 (IICH)
Seth Heasley203f8d82011-01-07 17:11:08 -080031 * document number 324645-001, 324646-001: Cougar Point (CPT)
Seth Heasleyc54fb812010-11-17 12:15:08 -070032 * document number TBD : Patsburg (PBG)
Seth Heasley203f8d82011-01-07 17:11:08 -080033 * document number TBD : DH89xxCC
Seth Heasleyaa1f46522011-04-20 10:56:20 -070034 * document number TBD : Panther Point
Seth Heasley84e83c22012-01-23 16:40:55 -080035 * document number TBD : Lynx Point
James Ralston7fb9c1a2012-08-09 09:46:13 -070036 * document number TBD : Lynx Point-LP
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +020037 */
38
39/*
40 * Includes, defines, variables, module parameters, ...
41 */
42
Joe Perches27c766a2012-02-15 15:06:19 -080043#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
44
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +020045/* Module and version information */
Wim Van Sebroeck7944d3a2008-08-06 20:19:41 +000046#define DRV_NAME "iTCO_wdt"
Peter Tyser24b3a162014-03-10 16:34:55 -050047#define DRV_VERSION "1.11"
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +020048
49/* Includes */
Rafael J. Wysockif321c9cb2015-04-03 15:25:04 +020050#include <linux/acpi.h> /* For ACPI support */
Mika Westerbergda23b6f2019-08-31 17:24:01 +030051#include <linux/bits.h> /* For BIT() */
Wim Van Sebroeck3836cc02006-06-30 08:44:53 +020052#include <linux/module.h> /* For module specific items */
53#include <linux/moduleparam.h> /* For new moduleparam's */
54#include <linux/types.h> /* For standard types (like size_t) */
55#include <linux/errno.h> /* For the -ENODEV/... values */
56#include <linux/kernel.h> /* For printk/panic/... */
Wim Van Sebroeck3836cc02006-06-30 08:44:53 +020057#include <linux/watchdog.h> /* For the watchdog specific items */
Wim Van Sebroeck3836cc02006-06-30 08:44:53 +020058#include <linux/init.h> /* For __init/__exit/... */
59#include <linux/fs.h> /* For file operations */
60#include <linux/platform_device.h> /* For platform_driver framework */
61#include <linux/pci.h> /* For pci functions */
62#include <linux/ioport.h> /* For io-port access */
63#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
Alan Cox0e6fa3f2008-05-19 14:06:25 +010064#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
65#include <linux/io.h> /* For inb/outb/... */
Matt Fleming420b54d2015-08-06 13:46:24 +010066#include <linux/platform_data/itco_wdt.h>
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +020067
Alan Cox0e6fa3f2008-05-19 14:06:25 +010068#include "iTCO_vendor.h"
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +020069
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +020070/* Address definitions for the TCO */
Alan Cox0e6fa3f2008-05-19 14:06:25 +010071/* TCO base address */
Guenter Roeckce1b95c2017-01-01 11:11:39 -080072#define TCOBASE(p) ((p)->tco_res->start)
Alan Cox0e6fa3f2008-05-19 14:06:25 +010073/* SMI Control and Enable Register */
Guenter Roeckce1b95c2017-01-01 11:11:39 -080074#define SMI_EN(p) ((p)->smi_res->start)
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +020075
Guenter Roeckce1b95c2017-01-01 11:11:39 -080076#define TCO_RLD(p) (TCOBASE(p) + 0x00) /* TCO Timer Reload/Curr. Value */
77#define TCOv1_TMR(p) (TCOBASE(p) + 0x01) /* TCOv1 Timer Initial Value*/
78#define TCO_DAT_IN(p) (TCOBASE(p) + 0x02) /* TCO Data In Register */
79#define TCO_DAT_OUT(p) (TCOBASE(p) + 0x03) /* TCO Data Out Register */
80#define TCO1_STS(p) (TCOBASE(p) + 0x04) /* TCO1 Status Register */
81#define TCO2_STS(p) (TCOBASE(p) + 0x06) /* TCO2 Status Register */
82#define TCO1_CNT(p) (TCOBASE(p) + 0x08) /* TCO1 Control Register */
83#define TCO2_CNT(p) (TCOBASE(p) + 0x0a) /* TCO2 Control Register */
84#define TCOv2_TMR(p) (TCOBASE(p) + 0x12) /* TCOv2 Timer Initial Value*/
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +020085
86/* internal variables */
Guenter Roeckce1b95c2017-01-01 11:11:39 -080087struct iTCO_wdt_private {
88 struct watchdog_device wddev;
89
Alan Cox0e6fa3f2008-05-19 14:06:25 +010090 /* TCO version/generation */
91 unsigned int iTCO_version;
Aaron Sierra887c8ec2012-04-20 14:14:11 -050092 struct resource *tco_res;
93 struct resource *smi_res;
Peter Tyser24b3a162014-03-10 16:34:55 -050094 /*
95 * NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2),
96 * or memory-mapped PMC register bit 4 (TCO version 3).
97 */
98 struct resource *gcs_pmc_res;
99 unsigned long __iomem *gcs_pmc;
Alan Cox0e6fa3f2008-05-19 14:06:25 +0100100 /* the lock for io operations */
101 spinlock_t io_lock;
102 /* the PCI-device */
Guenter Roeck78e45692017-01-02 09:27:36 -0800103 struct pci_dev *pci_dev;
Rafael J. Wysockif321c9cb2015-04-03 15:25:04 +0200104 /* whether or not the watchdog has been suspended */
105 bool suspended;
Kuppuswamy Sathyanarayanan140c91b22017-04-09 15:00:19 -0700106 /* no reboot API private data */
107 void *no_reboot_priv;
Kuppuswamy Sathyanarayananf583a882017-04-09 15:00:18 -0700108 /* no reboot update function pointer */
109 int (*update_no_reboot_bit)(void *p, bool set);
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800110};
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200111
112/* module parameters */
Wim Van Sebroeckbff23432012-06-09 14:10:28 +0200113#define WATCHDOG_TIMEOUT 30 /* 30 sec default heartbeat */
114static int heartbeat = WATCHDOG_TIMEOUT; /* in seconds */
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200115module_param(heartbeat, int, 0);
Pádraig Brady7e6811d2010-04-19 13:38:25 +0100116MODULE_PARM_DESC(heartbeat, "Watchdog timeout in seconds. "
117 "5..76 (TCO v1) or 3..614 (TCO v2), default="
Wim Van Sebroeckbff23432012-06-09 14:10:28 +0200118 __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200119
Wim Van Sebroeck86a1e182012-03-05 16:51:11 +0100120static bool nowayout = WATCHDOG_NOWAYOUT;
121module_param(nowayout, bool, 0);
Alan Cox0e6fa3f2008-05-19 14:06:25 +0100122MODULE_PARM_DESC(nowayout,
123 "Watchdog cannot be stopped once started (default="
124 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
Wim Van Sebroecke0333512006-11-12 18:05:09 +0100125
Wim Van Sebroeck0d098582011-12-26 15:23:51 +0100126static int turn_SMI_watchdog_clear_off = 1;
Wim Van Sebroeckdeb91972011-10-19 23:59:26 +0200127module_param(turn_SMI_watchdog_clear_off, int, 0);
128MODULE_PARM_DESC(turn_SMI_watchdog_clear_off,
Wim Van Sebroeck0d098582011-12-26 15:23:51 +0100129 "Turn off SMI clearing watchdog (depends on TCO-version)(default=1)");
Wim Van Sebroeckdeb91972011-10-19 23:59:26 +0200130
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200131/*
132 * Some TCO specific functions
133 */
134
Peter Tyser24b3a162014-03-10 16:34:55 -0500135/*
136 * The iTCO v1 and v2's internal timer is stored as ticks which decrement
137 * every 0.6 seconds. v3's internal timer is stored as seconds (some
138 * datasheets incorrectly state 0.6 seconds).
139 */
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800140static inline unsigned int seconds_to_ticks(struct iTCO_wdt_private *p,
141 int secs)
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200142{
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800143 return p->iTCO_version == 3 ? secs : (secs * 10) / 6;
Peter Tyser24b3a162014-03-10 16:34:55 -0500144}
145
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800146static inline unsigned int ticks_to_seconds(struct iTCO_wdt_private *p,
147 int ticks)
Peter Tyser24b3a162014-03-10 16:34:55 -0500148{
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800149 return p->iTCO_version == 3 ? ticks : (ticks * 6) / 10;
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200150}
151
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800152static inline u32 no_reboot_bit(struct iTCO_wdt_private *p)
Matt Fleming2a7a0e92015-08-06 13:46:26 +0100153{
154 u32 enable_bit;
155
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800156 switch (p->iTCO_version) {
Yong, Jonathan3b3a1c82016-06-17 00:33:31 +0000157 case 5:
Matt Fleming2a7a0e92015-08-06 13:46:26 +0100158 case 3:
159 enable_bit = 0x00000010;
160 break;
161 case 2:
162 enable_bit = 0x00000020;
163 break;
164 case 4:
165 case 1:
166 default:
167 enable_bit = 0x00000002;
168 break;
169 }
170
171 return enable_bit;
172}
173
Kuppuswamy Sathyanarayananf583a882017-04-09 15:00:18 -0700174static int update_no_reboot_bit_def(void *priv, bool set)
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200175{
Kuppuswamy Sathyanarayananf583a882017-04-09 15:00:18 -0700176 return 0;
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200177}
178
Kuppuswamy Sathyanarayananf583a882017-04-09 15:00:18 -0700179static int update_no_reboot_bit_pci(void *priv, bool set)
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200180{
Kuppuswamy Sathyanarayananf583a882017-04-09 15:00:18 -0700181 struct iTCO_wdt_private *p = priv;
182 u32 val32 = 0, newval32 = 0;
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200183
Kuppuswamy Sathyanarayananf583a882017-04-09 15:00:18 -0700184 pci_read_config_dword(p->pci_dev, 0xd4, &val32);
185 if (set)
186 val32 |= no_reboot_bit(p);
187 else
188 val32 &= ~no_reboot_bit(p);
189 pci_write_config_dword(p->pci_dev, 0xd4, val32);
190 pci_read_config_dword(p->pci_dev, 0xd4, &newval32);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200191
Kuppuswamy Sathyanarayananf583a882017-04-09 15:00:18 -0700192 /* make sure the update is successful */
193 if (val32 != newval32)
Matt Fleming2a7a0e92015-08-06 13:46:26 +0100194 return -EIO;
195
196 return 0;
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200197}
198
Kuppuswamy Sathyanarayananf583a882017-04-09 15:00:18 -0700199static int update_no_reboot_bit_mem(void *priv, bool set)
200{
201 struct iTCO_wdt_private *p = priv;
202 u32 val32 = 0, newval32 = 0;
203
204 val32 = readl(p->gcs_pmc);
205 if (set)
206 val32 |= no_reboot_bit(p);
207 else
208 val32 &= ~no_reboot_bit(p);
209 writel(val32, p->gcs_pmc);
210 newval32 = readl(p->gcs_pmc);
211
212 /* make sure the update is successful */
213 if (val32 != newval32)
214 return -EIO;
215
216 return 0;
217}
218
Mika Westerbergda23b6f2019-08-31 17:24:01 +0300219static int update_no_reboot_bit_cnt(void *priv, bool set)
220{
221 struct iTCO_wdt_private *p = priv;
222 u16 val, newval;
223
224 val = inw(TCO1_CNT(p));
225 if (set)
226 val |= BIT(0);
227 else
228 val &= ~BIT(0);
229 outw(val, TCO1_CNT(p));
230 newval = inw(TCO1_CNT(p));
231
232 /* make sure the update is successful */
233 return val != newval ? -EIO : 0;
234}
235
Kuppuswamy Sathyanarayanan140c91b22017-04-09 15:00:19 -0700236static void iTCO_wdt_no_reboot_bit_setup(struct iTCO_wdt_private *p,
237 struct itco_wdt_platform_data *pdata)
Kuppuswamy Sathyanarayananf583a882017-04-09 15:00:18 -0700238{
Kuppuswamy Sathyanarayanan140c91b22017-04-09 15:00:19 -0700239 if (pdata->update_no_reboot_bit) {
240 p->update_no_reboot_bit = pdata->update_no_reboot_bit;
241 p->no_reboot_priv = pdata->no_reboot_priv;
242 return;
243 }
244
Mika Westerbergda23b6f2019-08-31 17:24:01 +0300245 if (p->iTCO_version >= 6)
246 p->update_no_reboot_bit = update_no_reboot_bit_cnt;
247 else if (p->iTCO_version >= 2)
Kuppuswamy Sathyanarayananf583a882017-04-09 15:00:18 -0700248 p->update_no_reboot_bit = update_no_reboot_bit_mem;
249 else if (p->iTCO_version == 1)
250 p->update_no_reboot_bit = update_no_reboot_bit_pci;
251 else
252 p->update_no_reboot_bit = update_no_reboot_bit_def;
Kuppuswamy Sathyanarayanan140c91b22017-04-09 15:00:19 -0700253
254 p->no_reboot_priv = p;
Kuppuswamy Sathyanarayananf583a882017-04-09 15:00:18 -0700255}
256
Wim Van Sebroeckbff23432012-06-09 14:10:28 +0200257static int iTCO_wdt_start(struct watchdog_device *wd_dev)
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200258{
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800259 struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200260 unsigned int val;
261
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800262 spin_lock(&p->io_lock);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200263
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800264 iTCO_vendor_pre_start(p->smi_res, wd_dev->timeout);
Wim Van Sebroecke0333512006-11-12 18:05:09 +0100265
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200266 /* disable chipset's NO_REBOOT bit */
Kuppuswamy Sathyanarayanan140c91b22017-04-09 15:00:19 -0700267 if (p->update_no_reboot_bit(p->no_reboot_priv, false)) {
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800268 spin_unlock(&p->io_lock);
Joe Perches27c766a2012-02-15 15:06:19 -0800269 pr_err("failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS\n");
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200270 return -EIO;
271 }
272
Wim Van Sebroeck7cd5b082008-11-19 19:39:58 +0000273 /* Force the timer to its reload value by writing to the TCO_RLD
274 register */
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800275 if (p->iTCO_version >= 2)
276 outw(0x01, TCO_RLD(p));
277 else if (p->iTCO_version == 1)
278 outb(0x01, TCO_RLD(p));
Wim Van Sebroeck7cd5b082008-11-19 19:39:58 +0000279
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200280 /* Bit 11: TCO Timer Halt -> 0 = The TCO timer is enabled to count */
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800281 val = inw(TCO1_CNT(p));
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200282 val &= 0xf7ff;
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800283 outw(val, TCO1_CNT(p));
284 val = inw(TCO1_CNT(p));
285 spin_unlock(&p->io_lock);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200286
287 if (val & 0x0800)
288 return -1;
289 return 0;
290}
291
Wim Van Sebroeckbff23432012-06-09 14:10:28 +0200292static int iTCO_wdt_stop(struct watchdog_device *wd_dev)
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200293{
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800294 struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200295 unsigned int val;
296
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800297 spin_lock(&p->io_lock);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200298
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800299 iTCO_vendor_pre_stop(p->smi_res);
Wim Van Sebroecke0333512006-11-12 18:05:09 +0100300
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200301 /* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800302 val = inw(TCO1_CNT(p));
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200303 val |= 0x0800;
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800304 outw(val, TCO1_CNT(p));
305 val = inw(TCO1_CNT(p));
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200306
307 /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
Kuppuswamy Sathyanarayanan140c91b22017-04-09 15:00:19 -0700308 p->update_no_reboot_bit(p->no_reboot_priv, true);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200309
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800310 spin_unlock(&p->io_lock);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200311
312 if ((val & 0x0800) == 0)
313 return -1;
314 return 0;
315}
316
Wim Van Sebroeckbff23432012-06-09 14:10:28 +0200317static int iTCO_wdt_ping(struct watchdog_device *wd_dev)
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200318{
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800319 struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200320
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800321 spin_lock(&p->io_lock);
322
Paolo Bonzini1fccb732017-04-05 13:41:15 +0200323 /* Reload the timer by writing to the TCO Timer Counter register */
Wim Van Sebroeckfc61e832017-09-09 17:41:24 +0200324 if (p->iTCO_version >= 2) {
Paolo Bonzini1fccb732017-04-05 13:41:15 +0200325 outw(0x01, TCO_RLD(p));
Wim Van Sebroeckfc61e832017-09-09 17:41:24 +0200326 } else if (p->iTCO_version == 1) {
327 /* Reset the timeout status bit so that the timer
328 * needs to count down twice again before rebooting */
329 outw(0x0008, TCO1_STS(p)); /* write 1 to clear bit */
330
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800331 outb(0x01, TCO_RLD(p));
Wim Van Sebroeckfc61e832017-09-09 17:41:24 +0200332 }
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200333
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800334 spin_unlock(&p->io_lock);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200335 return 0;
336}
337
Wim Van Sebroeckbff23432012-06-09 14:10:28 +0200338static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200339{
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800340 struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200341 unsigned int val16;
342 unsigned char val8;
343 unsigned int tmrval;
344
Wim Van Sebroeckfc61e832017-09-09 17:41:24 +0200345 tmrval = seconds_to_ticks(p, t);
346
347 /* For TCO v1 the timer counts down twice before rebooting */
348 if (p->iTCO_version == 1)
349 tmrval /= 2;
Pádraig Brady7e6811d2010-04-19 13:38:25 +0100350
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200351 /* from the specs: */
352 /* "Values of 0h-3h are ignored and should not be attempted" */
353 if (tmrval < 0x04)
354 return -EINVAL;
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800355 if ((p->iTCO_version >= 2 && tmrval > 0x3ff) ||
356 (p->iTCO_version == 1 && tmrval > 0x03f))
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200357 return -EINVAL;
358
359 /* Write new heartbeat to watchdog */
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800360 if (p->iTCO_version >= 2) {
361 spin_lock(&p->io_lock);
362 val16 = inw(TCOv2_TMR(p));
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200363 val16 &= 0xfc00;
364 val16 |= tmrval;
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800365 outw(val16, TCOv2_TMR(p));
366 val16 = inw(TCOv2_TMR(p));
367 spin_unlock(&p->io_lock);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200368
369 if ((val16 & 0x3ff) != tmrval)
370 return -EINVAL;
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800371 } else if (p->iTCO_version == 1) {
372 spin_lock(&p->io_lock);
373 val8 = inb(TCOv1_TMR(p));
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200374 val8 &= 0xc0;
375 val8 |= (tmrval & 0xff);
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800376 outb(val8, TCOv1_TMR(p));
377 val8 = inb(TCOv1_TMR(p));
378 spin_unlock(&p->io_lock);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200379
380 if ((val8 & 0x3f) != tmrval)
381 return -EINVAL;
382 }
383
Wim Van Sebroeckbff23432012-06-09 14:10:28 +0200384 wd_dev->timeout = t;
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200385 return 0;
386}
387
Wim Van Sebroeckbff23432012-06-09 14:10:28 +0200388static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev)
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200389{
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800390 struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200391 unsigned int val16;
392 unsigned char val8;
Wim Van Sebroeckbff23432012-06-09 14:10:28 +0200393 unsigned int time_left = 0;
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200394
395 /* read the TCO Timer */
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800396 if (p->iTCO_version >= 2) {
397 spin_lock(&p->io_lock);
398 val16 = inw(TCO_RLD(p));
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200399 val16 &= 0x3ff;
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800400 spin_unlock(&p->io_lock);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200401
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800402 time_left = ticks_to_seconds(p, val16);
403 } else if (p->iTCO_version == 1) {
404 spin_lock(&p->io_lock);
405 val8 = inb(TCO_RLD(p));
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200406 val8 &= 0x3f;
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800407 if (!(inw(TCO1_STS(p)) & 0x0008))
408 val8 += (inb(TCOv1_TMR(p)) & 0x3f);
409 spin_unlock(&p->io_lock);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200410
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800411 time_left = ticks_to_seconds(p, val8);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200412 }
Wim Van Sebroeckbff23432012-06-09 14:10:28 +0200413 return time_left;
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200414}
415
416/*
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200417 * Kernel Interfaces
418 */
419
Wim Van Sebroeckbff23432012-06-09 14:10:28 +0200420static const struct watchdog_info ident = {
421 .options = WDIOF_SETTIMEOUT |
422 WDIOF_KEEPALIVEPING |
423 WDIOF_MAGICCLOSE,
424 .firmware_version = 0,
425 .identity = DRV_NAME,
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200426};
427
Wim Van Sebroeckbff23432012-06-09 14:10:28 +0200428static const struct watchdog_ops iTCO_wdt_ops = {
429 .owner = THIS_MODULE,
430 .start = iTCO_wdt_start,
Jingoo Han5f5e1902014-02-27 14:41:42 +0900431 .stop = iTCO_wdt_stop,
432 .ping = iTCO_wdt_ping,
Wim Van Sebroeckbff23432012-06-09 14:10:28 +0200433 .set_timeout = iTCO_wdt_set_timeout,
434 .get_timeleft = iTCO_wdt_get_timeleft,
435};
436
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200437/*
438 * Init & exit routines
439 */
440
Guenter Roeck78e45692017-01-02 09:27:36 -0800441static int iTCO_wdt_probe(struct platform_device *pdev)
Aaron Sierra887c8ec2012-04-20 14:14:11 -0500442{
Guenter Roeck78e45692017-01-02 09:27:36 -0800443 struct device *dev = &pdev->dev;
444 struct itco_wdt_platform_data *pdata = dev_get_platdata(dev);
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800445 struct iTCO_wdt_private *p;
446 unsigned long val32;
447 int ret;
Aaron Sierra887c8ec2012-04-20 14:14:11 -0500448
Matt Fleming420b54d2015-08-06 13:46:24 +0100449 if (!pdata)
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800450 return -ENODEV;
Aaron Sierra887c8ec2012-04-20 14:14:11 -0500451
Guenter Roeck78e45692017-01-02 09:27:36 -0800452 p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL);
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800453 if (!p)
454 return -ENOMEM;
Aaron Sierra887c8ec2012-04-20 14:14:11 -0500455
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800456 spin_lock_init(&p->io_lock);
Aaron Sierra887c8ec2012-04-20 14:14:11 -0500457
Guenter Roeck78e45692017-01-02 09:27:36 -0800458 p->tco_res = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_IO_TCO);
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800459 if (!p->tco_res)
460 return -ENODEV;
Aaron Sierra887c8ec2012-04-20 14:14:11 -0500461
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800462 p->iTCO_version = pdata->version;
Guenter Roeck78e45692017-01-02 09:27:36 -0800463 p->pci_dev = to_pci_dev(dev->parent);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200464
Mika Westerberge42b0c22020-02-26 16:21:21 +0300465 p->smi_res = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_IO_SMI);
466 if (p->smi_res) {
467 /* The TCO logic uses the TCO_EN bit in the SMI_EN register */
468 if (!devm_request_region(dev, p->smi_res->start,
469 resource_size(p->smi_res),
470 pdev->name)) {
471 pr_err("I/O address 0x%04llx already in use, device disabled\n",
472 (u64)SMI_EN(p));
473 return -EBUSY;
474 }
475 } else if (iTCO_vendorsupport ||
476 turn_SMI_watchdog_clear_off >= p->iTCO_version) {
477 pr_err("SMI I/O resource is missing\n");
478 return -ENODEV;
479 }
480
Kuppuswamy Sathyanarayanan140c91b22017-04-09 15:00:19 -0700481 iTCO_wdt_no_reboot_bit_setup(p, pdata);
Kuppuswamy Sathyanarayananf583a882017-04-09 15:00:18 -0700482
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200483 /*
Peter Tyser24b3a162014-03-10 16:34:55 -0500484 * Get the Memory-Mapped GCS or PMC register, we need it for the
485 * NO_REBOOT flag (TCO v2 and v3).
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200486 */
Mika Westerbergda23b6f2019-08-31 17:24:01 +0300487 if (p->iTCO_version >= 2 && p->iTCO_version < 6 &&
488 !pdata->update_no_reboot_bit) {
Guenter Roeck78e45692017-01-02 09:27:36 -0800489 p->gcs_pmc_res = platform_get_resource(pdev,
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800490 IORESOURCE_MEM,
491 ICH_RES_MEM_GCS_PMC);
Guenter Roeck78e45692017-01-02 09:27:36 -0800492 p->gcs_pmc = devm_ioremap_resource(dev, p->gcs_pmc_res);
Guenter Roeckc7bbcc82017-01-01 10:39:09 -0800493 if (IS_ERR(p->gcs_pmc))
494 return PTR_ERR(p->gcs_pmc);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200495 }
496
497 /* Check chipset's NO_REBOOT bit */
Kuppuswamy Sathyanarayanan140c91b22017-04-09 15:00:19 -0700498 if (p->update_no_reboot_bit(p->no_reboot_priv, false) &&
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800499 iTCO_vendor_check_noreboot_on()) {
Joe Perches27c766a2012-02-15 15:06:19 -0800500 pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n");
Guenter Roeckc7bbcc82017-01-01 10:39:09 -0800501 return -ENODEV; /* Cannot reset NO_REBOOT bit */
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200502 }
503
504 /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
Kuppuswamy Sathyanarayanan140c91b22017-04-09 15:00:19 -0700505 p->update_no_reboot_bit(p->no_reboot_priv, true);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200506
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800507 if (turn_SMI_watchdog_clear_off >= p->iTCO_version) {
Aaron Sierra887c8ec2012-04-20 14:14:11 -0500508 /*
509 * Bit 13: TCO_EN -> 0
510 * Disables TCO logic generating an SMI#
511 */
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800512 val32 = inl(SMI_EN(p));
Wim Van Sebroeckdeb91972011-10-19 23:59:26 +0200513 val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800514 outl(val32, SMI_EN(p));
Wim Van Sebroeckdeb91972011-10-19 23:59:26 +0200515 }
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200516
Guenter Roeck78e45692017-01-02 09:27:36 -0800517 if (!devm_request_region(dev, p->tco_res->start,
Guenter Roeckc7bbcc82017-01-01 10:39:09 -0800518 resource_size(p->tco_res),
Guenter Roeck78e45692017-01-02 09:27:36 -0800519 pdev->name)) {
Aaron Sierra887c8ec2012-04-20 14:14:11 -0500520 pr_err("I/O address 0x%04llx already in use, device disabled\n",
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800521 (u64)TCOBASE(p));
Guenter Roeckc7bbcc82017-01-01 10:39:09 -0800522 return -EBUSY;
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200523 }
524
Aaron Sierra887c8ec2012-04-20 14:14:11 -0500525 pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04llx)\n",
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800526 pdata->name, pdata->version, (u64)TCOBASE(p));
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200527
528 /* Clear out the (probably old) status */
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800529 switch (p->iTCO_version) {
Mika Westerbergda23b6f2019-08-31 17:24:01 +0300530 case 6:
Yong, Jonathan3b3a1c82016-06-17 00:33:31 +0000531 case 5:
Matt Fleming2a7a0e92015-08-06 13:46:26 +0100532 case 4:
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800533 outw(0x0008, TCO1_STS(p)); /* Clear the Time Out Status bit */
534 outw(0x0002, TCO2_STS(p)); /* Clear SECOND_TO_STS bit */
Matt Fleming2a7a0e92015-08-06 13:46:26 +0100535 break;
536 case 3:
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800537 outl(0x20008, TCO1_STS(p));
Matt Fleming2a7a0e92015-08-06 13:46:26 +0100538 break;
539 case 2:
540 case 1:
541 default:
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800542 outw(0x0008, TCO1_STS(p)); /* Clear the Time Out Status bit */
543 outw(0x0002, TCO2_STS(p)); /* Clear SECOND_TO_STS bit */
544 outw(0x0004, TCO2_STS(p)); /* Clear BOOT_STS bit */
Matt Fleming2a7a0e92015-08-06 13:46:26 +0100545 break;
Peter Tyser24b3a162014-03-10 16:34:55 -0500546 }
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200547
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800548 p->wddev.info = &ident,
549 p->wddev.ops = &iTCO_wdt_ops,
550 p->wddev.bootstatus = 0;
551 p->wddev.timeout = WATCHDOG_TIMEOUT;
552 watchdog_set_nowayout(&p->wddev, nowayout);
Guenter Roeck78e45692017-01-02 09:27:36 -0800553 p->wddev.parent = dev;
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800554
555 watchdog_set_drvdata(&p->wddev, p);
Guenter Roeck78e45692017-01-02 09:27:36 -0800556 platform_set_drvdata(pdev, p);
Wim Van Sebroeckbff23432012-06-09 14:10:28 +0200557
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200558 /* Make sure the watchdog is not running */
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800559 iTCO_wdt_stop(&p->wddev);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200560
Alan Cox0e6fa3f2008-05-19 14:06:25 +0100561 /* Check that the heartbeat value is within it's range;
562 if not reset to the default */
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800563 if (iTCO_wdt_set_timeout(&p->wddev, heartbeat)) {
564 iTCO_wdt_set_timeout(&p->wddev, WATCHDOG_TIMEOUT);
Wim Van Sebroeckbff23432012-06-09 14:10:28 +0200565 pr_info("timeout value out of range, using %d\n",
566 WATCHDOG_TIMEOUT);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200567 }
568
Guenter Roeckd3d77b52017-01-10 15:21:49 -0800569 watchdog_stop_on_reboot(&p->wddev);
Guenter Roeck77d9f762019-04-08 12:38:41 -0700570 watchdog_stop_on_unregister(&p->wddev);
Guenter Roeck78e45692017-01-02 09:27:36 -0800571 ret = devm_watchdog_register_device(dev, &p->wddev);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200572 if (ret != 0) {
Wim Van Sebroeckbff23432012-06-09 14:10:28 +0200573 pr_err("cannot register watchdog device (err=%d)\n", ret);
Guenter Roeckc7bbcc82017-01-01 10:39:09 -0800574 return ret;
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200575 }
576
Joe Perches27c766a2012-02-15 15:06:19 -0800577 pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
578 heartbeat, nowayout);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200579
580 return 0;
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200581}
582
Rafael J. Wysockif321c9cb2015-04-03 15:25:04 +0200583#ifdef CONFIG_PM_SLEEP
584/*
585 * Suspend-to-idle requires this, because it stops the ticks and timekeeping, so
586 * the watchdog cannot be pinged while in that state. In ACPI sleep states the
587 * watchdog is stopped by the platform firmware.
588 */
589
590#ifdef CONFIG_ACPI
591static inline bool need_suspend(void)
592{
593 return acpi_target_system_state() == ACPI_STATE_S0;
594}
595#else
596static inline bool need_suspend(void) { return true; }
597#endif
598
599static int iTCO_wdt_suspend_noirq(struct device *dev)
600{
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800601 struct iTCO_wdt_private *p = dev_get_drvdata(dev);
Rafael J. Wysockif321c9cb2015-04-03 15:25:04 +0200602 int ret = 0;
603
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800604 p->suspended = false;
605 if (watchdog_active(&p->wddev) && need_suspend()) {
606 ret = iTCO_wdt_stop(&p->wddev);
Rafael J. Wysockif321c9cb2015-04-03 15:25:04 +0200607 if (!ret)
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800608 p->suspended = true;
Rafael J. Wysockif321c9cb2015-04-03 15:25:04 +0200609 }
610 return ret;
611}
612
613static int iTCO_wdt_resume_noirq(struct device *dev)
614{
Guenter Roeckce1b95c2017-01-01 11:11:39 -0800615 struct iTCO_wdt_private *p = dev_get_drvdata(dev);
616
617 if (p->suspended)
618 iTCO_wdt_start(&p->wddev);
Rafael J. Wysockif321c9cb2015-04-03 15:25:04 +0200619
620 return 0;
621}
622
Julia Lawall6e938f62016-08-28 22:26:26 +0200623static const struct dev_pm_ops iTCO_wdt_pm = {
Rafael J. Wysockif321c9cb2015-04-03 15:25:04 +0200624 .suspend_noirq = iTCO_wdt_suspend_noirq,
625 .resume_noirq = iTCO_wdt_resume_noirq,
626};
627
628#define ITCO_WDT_PM_OPS (&iTCO_wdt_pm)
629#else
630#define ITCO_WDT_PM_OPS NULL
631#endif /* CONFIG_PM_SLEEP */
632
Wim Van Sebroeck3836cc02006-06-30 08:44:53 +0200633static struct platform_driver iTCO_wdt_driver = {
634 .probe = iTCO_wdt_probe,
Wim Van Sebroeck3836cc02006-06-30 08:44:53 +0200635 .driver = {
Wim Van Sebroeck3836cc02006-06-30 08:44:53 +0200636 .name = DRV_NAME,
Rafael J. Wysockif321c9cb2015-04-03 15:25:04 +0200637 .pm = ITCO_WDT_PM_OPS,
Wim Van Sebroeck3836cc02006-06-30 08:44:53 +0200638 },
639};
640
641static int __init iTCO_wdt_init_module(void)
642{
Joe Perches27c766a2012-02-15 15:06:19 -0800643 pr_info("Intel TCO WatchDog Timer Driver v%s\n", DRV_VERSION);
Wim Van Sebroeck3836cc02006-06-30 08:44:53 +0200644
Guenter Roeck9616bd22017-01-03 02:43:32 -0800645 return platform_driver_register(&iTCO_wdt_driver);
Wim Van Sebroeck3836cc02006-06-30 08:44:53 +0200646}
647
648static void __exit iTCO_wdt_cleanup_module(void)
649{
Wim Van Sebroeck3836cc02006-06-30 08:44:53 +0200650 platform_driver_unregister(&iTCO_wdt_driver);
Joe Perches27c766a2012-02-15 15:06:19 -0800651 pr_info("Watchdog Module Unloaded\n");
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200652}
653
654module_init(iTCO_wdt_init_module);
655module_exit(iTCO_wdt_cleanup_module);
656
657MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
658MODULE_DESCRIPTION("Intel TCO WatchDog Timer Driver");
Wim Van Sebroeck3836cc02006-06-30 08:44:53 +0200659MODULE_VERSION(DRV_VERSION);
Wim Van Sebroeck9e0ea342006-05-21 14:37:44 +0200660MODULE_LICENSE("GPL");
Jan Beuliche5de32e2012-06-22 16:41:00 +0100661MODULE_ALIAS("platform:" DRV_NAME);