Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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(struct timer_list *unused);
 71static DEFINE_TIMER(timer, wdt_timer_tick);
 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(struct timer_list *unused)
 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	wdt_dev.parent = &pdev->dev;
210	watchdog_set_nowayout(&wdt_dev, nowayout);
211	if (readl(wdt_mem) & VIA_WDT_FIRED)
212		wdt_dev.bootstatus |= WDIOF_CARDRESET;
213
214	ret = watchdog_register_device(&wdt_dev);
215	if (ret)
216		goto err_out_iounmap;
217
218	/* start triggering, in case of watchdog already enabled by BIOS */
219	mod_timer(&timer, jiffies + WDT_HEARTBEAT);
220	return 0;
221
222err_out_iounmap:
223	iounmap(wdt_mem);
224err_out_release:
225	release_mem_region(mmio, VIA_WDT_MMIO_LEN);
226err_out_resource:
227	release_resource(&wdt_res);
228err_out_disable_device:
229	pci_disable_device(pdev);
230	return ret;
231}
232
233static void wdt_remove(struct pci_dev *pdev)
234{
235	watchdog_unregister_device(&wdt_dev);
236	del_timer_sync(&timer);
237	iounmap(wdt_mem);
238	release_mem_region(mmio, VIA_WDT_MMIO_LEN);
239	release_resource(&wdt_res);
240	pci_disable_device(pdev);
241}
242
243static const struct pci_device_id wdt_pci_table[] = {
244	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_CX700) },
245	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX800) },
246	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) },
247	{ 0 }
248};
249
250static struct pci_driver wdt_driver = {
251	.name		= "via_wdt",
252	.id_table	= wdt_pci_table,
253	.probe		= wdt_probe,
254	.remove		= wdt_remove,
255};
256
257module_pci_driver(wdt_driver);
258
259MODULE_AUTHOR("Marc Vertes");
260MODULE_DESCRIPTION("Driver for watchdog timer on VIA chipset");
261MODULE_LICENSE("GPL");