1/*
2 * VIA Chipset Watchdog Driver
3 *
4 * Copyright (C) 2011 Sigfox
5 * License terms: GNU General Public License (GPL) version 2
6 * Author: Marc Vertes <marc.vertes@sigfox.com>
7 * Based on a preliminary version from Harald Welte <HaraldWelte@viatech.com>
8 * Timer code by Wim Van Sebroeck <wim@iguana.be>
9 *
10 * Caveat: PnP must be enabled in BIOS to allow full access to watchdog
11 * control registers. If not, the watchdog must be configured in BIOS manually.
12 */
13
14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15
16#include <linux/device.h>
17#include <linux/io.h>
18#include <linux/jiffies.h>
19#include <linux/module.h>
20#include <linux/pci.h>
21#include <linux/timer.h>
22#include <linux/watchdog.h>
23
24/* Configuration registers relative to the pci device */
25#define VIA_WDT_MMIO_BASE	0xe8	/* MMIO region base address */
26#define VIA_WDT_CONF		0xec	/* watchdog enable state */
27
28/* Relevant bits for the VIA_WDT_CONF register */
29#define VIA_WDT_CONF_ENABLE	0x01	/* 1: enable watchdog */
30#define VIA_WDT_CONF_MMIO	0x02	/* 1: enable watchdog MMIO */
31
32/*
33 * The MMIO region contains the watchog control register and the
34 * hardware timer counter.
35 */
36#define VIA_WDT_MMIO_LEN	8	/* MMIO region length in bytes */
37#define VIA_WDT_CTL		0	/* MMIO addr+0: state/control reg. */
38#define VIA_WDT_COUNT		4	/* MMIO addr+4: timer counter reg. */
39
40/* Bits for the VIA_WDT_CTL register */
41#define VIA_WDT_RUNNING		0x01	/* 0: stop, 1: running */
42#define VIA_WDT_FIRED		0x02	/* 1: restarted by expired watchdog */
43#define VIA_WDT_PWROFF		0x04	/* 0: reset, 1: poweroff */
44#define VIA_WDT_DISABLED	0x08	/* 1: timer is disabled */
45#define VIA_WDT_TRIGGER		0x80	/* 1: start a new countdown */
46
47/* Hardware heartbeat in seconds */
48#define WDT_HW_HEARTBEAT 1
49
50/* Timer heartbeat (500ms) */
51#define WDT_HEARTBEAT	(HZ/2)	/* should be <= ((WDT_HW_HEARTBEAT*HZ)/2) */
52
53/* User space timeout in seconds */
54#define WDT_TIMEOUT_MAX	1023	/* approx. 17 min. */
55#define WDT_TIMEOUT	60
56static int timeout = WDT_TIMEOUT;
57module_param(timeout, int, 0);
58MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, between 1 and 1023 "
59	"(default = " __MODULE_STRING(WDT_TIMEOUT) ")");
60
61static bool nowayout = WATCHDOG_NOWAYOUT;
62module_param(nowayout, bool, 0);
63MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
64	"(default = " __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
65
66static struct watchdog_device wdt_dev;
67static struct resource wdt_res;
68static void __iomem *wdt_mem;
69static unsigned int mmio;
70static void wdt_timer_tick(unsigned long data);
71static DEFINE_TIMER(timer, wdt_timer_tick, 0, 0);
72					/* The timer that pings the watchdog */
73static unsigned long next_heartbeat;	/* the next_heartbeat for the timer */
74
75static inline void wdt_reset(void)
76{
77	unsigned int ctl = readl(wdt_mem);
78
79	writel(ctl | VIA_WDT_TRIGGER, wdt_mem);
80}
81
82/*
83 * Timer tick: the timer will make sure that the watchdog timer hardware
84 * is being reset in time. The conditions to do this are:
85 *  1) the watchog timer has been started and /dev/watchdog is open
86 *     and there is still time left before userspace should send the
87 *     next heartbeat/ping. (note: the internal heartbeat is much smaller
88 *     then the external/userspace heartbeat).
89 *  2) the watchdog timer has been stopped by userspace.
90 */
91static void wdt_timer_tick(unsigned long data)
92{
93	if (time_before(jiffies, next_heartbeat) ||
94	   (!watchdog_active(&wdt_dev))) {
95		wdt_reset();
96		mod_timer(&timer, jiffies + WDT_HEARTBEAT);
97	} else
98		pr_crit("I will reboot your machine !\n");
99}
100
101static int wdt_ping(struct watchdog_device *wdd)
102{
103	/* calculate when the next userspace timeout will be */
104	next_heartbeat = jiffies + wdd->timeout * HZ;
105	return 0;
106}
107
108static int wdt_start(struct watchdog_device *wdd)
109{
110	unsigned int ctl = readl(wdt_mem);
111
112	writel(wdd->timeout, wdt_mem + VIA_WDT_COUNT);
113	writel(ctl | VIA_WDT_RUNNING | VIA_WDT_TRIGGER, wdt_mem);
114	wdt_ping(wdd);
115	mod_timer(&timer, jiffies + WDT_HEARTBEAT);
116	return 0;
117}
118
119static int wdt_stop(struct watchdog_device *wdd)
120{
121	unsigned int ctl = readl(wdt_mem);
122
123	writel(ctl & ~VIA_WDT_RUNNING, wdt_mem);
124	return 0;
125}
126
127static int wdt_set_timeout(struct watchdog_device *wdd,
128			   unsigned int new_timeout)
129{
130	writel(new_timeout, wdt_mem + VIA_WDT_COUNT);
131	wdd->timeout = new_timeout;
132	return 0;
133}
134
135static const struct watchdog_info wdt_info = {
136	.identity =	"VIA watchdog",
137	.options =	WDIOF_CARDRESET |
138			WDIOF_SETTIMEOUT |
139			WDIOF_MAGICCLOSE |
140			WDIOF_KEEPALIVEPING,
141};
142
143static const struct watchdog_ops wdt_ops = {
144	.owner =	THIS_MODULE,
145	.start =	wdt_start,
146	.stop =		wdt_stop,
147	.ping =		wdt_ping,
148	.set_timeout =	wdt_set_timeout,
149};
150
151static struct watchdog_device wdt_dev = {
152	.info =		&wdt_info,
153	.ops =		&wdt_ops,
154	.min_timeout =	1,
155	.max_timeout =	WDT_TIMEOUT_MAX,
156};
157
158static int wdt_probe(struct pci_dev *pdev,
159			       const struct pci_device_id *ent)
160{
161	unsigned char conf;
162	int ret = -ENODEV;
163
164	if (pci_enable_device(pdev)) {
165		dev_err(&pdev->dev, "cannot enable PCI device\n");
166		return -ENODEV;
167	}
168
169	/*
170	 * Allocate a MMIO region which contains watchdog control register
171	 * and counter, then configure the watchdog to use this region.
172	 * This is possible only if PnP is properly enabled in BIOS.
173	 * If not, the watchdog must be configured in BIOS manually.
174	 */
175	if (allocate_resource(&iomem_resource, &wdt_res, VIA_WDT_MMIO_LEN,
176			      0xf0000000, 0xffffff00, 0xff, NULL, NULL)) {
177		dev_err(&pdev->dev, "MMIO allocation failed\n");
178		goto err_out_disable_device;
179	}
180
181	pci_write_config_dword(pdev, VIA_WDT_MMIO_BASE, wdt_res.start);
182	pci_read_config_byte(pdev, VIA_WDT_CONF, &conf);
183	conf |= VIA_WDT_CONF_ENABLE | VIA_WDT_CONF_MMIO;
184	pci_write_config_byte(pdev, VIA_WDT_CONF, conf);
185
186	pci_read_config_dword(pdev, VIA_WDT_MMIO_BASE, &mmio);
187	if (mmio) {
188		dev_info(&pdev->dev, "VIA Chipset watchdog MMIO: %x\n", mmio);
189	} else {
190		dev_err(&pdev->dev, "MMIO setting failed. Check BIOS.\n");
191		goto err_out_resource;
192	}
193
194	if (!request_mem_region(mmio, VIA_WDT_MMIO_LEN, "via_wdt")) {
195		dev_err(&pdev->dev, "MMIO region busy\n");
196		goto err_out_resource;
197	}
198
199	wdt_mem = ioremap(mmio, VIA_WDT_MMIO_LEN);
200	if (wdt_mem == NULL) {
201		dev_err(&pdev->dev, "cannot remap VIA wdt MMIO registers\n");
202		goto err_out_release;
203	}
204
205	if (timeout < 1 || timeout > WDT_TIMEOUT_MAX)
206		timeout = WDT_TIMEOUT;
207
208	wdt_dev.timeout = timeout;
209	watchdog_set_nowayout(&wdt_dev, nowayout);
210	if (readl(wdt_mem) & VIA_WDT_FIRED)
211		wdt_dev.bootstatus |= WDIOF_CARDRESET;
212
213	ret = watchdog_register_device(&wdt_dev);
214	if (ret)
215		goto err_out_iounmap;
216
217	/* start triggering, in case of watchdog already enabled by BIOS */
218	mod_timer(&timer, jiffies + WDT_HEARTBEAT);
219	return 0;
220
221err_out_iounmap:
222	iounmap(wdt_mem);
223err_out_release:
224	release_mem_region(mmio, VIA_WDT_MMIO_LEN);
225err_out_resource:
226	release_resource(&wdt_res);
227err_out_disable_device:
228	pci_disable_device(pdev);
229	return ret;
230}
231
232static void wdt_remove(struct pci_dev *pdev)
233{
234	watchdog_unregister_device(&wdt_dev);
235	del_timer_sync(&timer);
236	iounmap(wdt_mem);
237	release_mem_region(mmio, VIA_WDT_MMIO_LEN);
238	release_resource(&wdt_res);
239	pci_disable_device(pdev);
240}
241
242static const struct pci_device_id wdt_pci_table[] = {
243	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_CX700) },
244	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX800) },
245	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) },
246	{ 0 }
247};
248
249static struct pci_driver wdt_driver = {
250	.name		= "via_wdt",
251	.id_table	= wdt_pci_table,
252	.probe		= wdt_probe,
253	.remove		= wdt_remove,
254};
255
256module_pci_driver(wdt_driver);
257
258MODULE_AUTHOR("Marc Vertes");
259MODULE_DESCRIPTION("Driver for watchdog timer on VIA chipset");
260MODULE_LICENSE("GPL");
261