aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorMarc Vertes <marc.vertes@sigfox.com>2011-12-05 11:00:23 -0500
committerWim Van Sebroeck <wim@iguana.be>2012-01-06 09:33:41 -0500
commitdc3c56b703dad4aec8a9b3dd86f03a90d0c26a2d (patch)
tree2d8324bab71ccdf394e64fe0728522ad566c6f27 /drivers
parent86955e2bcb320bf8f271443cb7b03896fc3cbd67 (diff)
watchdog: new driver for VIA chipsets
New driver for the hardware watchdog timer on VIA chipsets. This driver uses the new watchdog framework. PnP must be enabled in BIOS to get full control of watchdog registers. The timer code has been added by Wim Van Sebroeck. Tested on a Artigo A1100, VX855 chipset. Signed-off-by: Marc Vertes <marc.vertes@sigfox.com> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/watchdog/Kconfig13
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/via_wdt.c267
3 files changed, 281 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 79fd606b7cd5..877b107f77a7 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -772,6 +772,19 @@ config SMSC37B787_WDT
772 772
773 Most people will say N. 773 Most people will say N.
774 774
775config VIA_WDT
776 tristate "VIA Watchdog Timer"
777 depends on X86
778 select WATCHDOG_CORE
779 ---help---
780 This is the driver for the hardware watchdog timer on VIA
781 southbridge chipset CX700, VX800/VX820 or VX855/VX875.
782
783 To compile this driver as a module, choose M here; the module
784 will be called via_wdt.
785
786 Most people will say N.
787
775config W83627HF_WDT 788config W83627HF_WDT
776 tristate "W83627HF/W83627DHG Watchdog Timer" 789 tristate "W83627HF/W83627DHG Watchdog Timer"
777 depends on X86 790 depends on X86
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index fe893e91935b..e8f479a16402 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -99,6 +99,7 @@ obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o
99obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o 99obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o
100obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o 100obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o
101obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o 101obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
102obj-$(CONFIG_VIA_WDT) += via_wdt.o
102obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o 103obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
103obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o 104obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o
104obj-$(CONFIG_W83697UG_WDT) += w83697ug_wdt.o 105obj-$(CONFIG_W83697UG_WDT) += w83697ug_wdt.o
diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c
new file mode 100644
index 000000000000..026b4bbfa0aa
--- /dev/null
+++ b/drivers/watchdog/via_wdt.c
@@ -0,0 +1,267 @@
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#include <linux/device.h>
14#include <linux/io.h>
15#include <linux/jiffies.h>
16#include <linux/module.h>
17#include <linux/pci.h>
18#include <linux/timer.h>
19#include <linux/watchdog.h>
20
21/* Configuration registers relative to the pci device */
22#define VIA_WDT_MMIO_BASE 0xe8 /* MMIO region base address */
23#define VIA_WDT_CONF 0xec /* watchdog enable state */
24
25/* Relevant bits for the VIA_WDT_CONF register */
26#define VIA_WDT_CONF_ENABLE 0x01 /* 1: enable watchdog */
27#define VIA_WDT_CONF_MMIO 0x02 /* 1: enable watchdog MMIO */
28
29/*
30 * The MMIO region contains the watchog control register and the
31 * hardware timer counter.
32 */
33#define VIA_WDT_MMIO_LEN 8 /* MMIO region length in bytes */
34#define VIA_WDT_CTL 0 /* MMIO addr+0: state/control reg. */
35#define VIA_WDT_COUNT 4 /* MMIO addr+4: timer counter reg. */
36
37/* Bits for the VIA_WDT_CTL register */
38#define VIA_WDT_RUNNING 0x01 /* 0: stop, 1: running */
39#define VIA_WDT_FIRED 0x02 /* 1: restarted by expired watchdog */
40#define VIA_WDT_PWROFF 0x04 /* 0: reset, 1: poweroff */
41#define VIA_WDT_DISABLED 0x08 /* 1: timer is disabled */
42#define VIA_WDT_TRIGGER 0x80 /* 1: start a new countdown */
43
44/* Hardware heartbeat in seconds */
45#define WDT_HW_HEARTBEAT 1
46
47/* Timer heartbeat (500ms) */
48#define WDT_HEARTBEAT (HZ/2) /* should be <= ((WDT_HW_HEARTBEAT*HZ)/2) */
49
50/* User space timeout in seconds */
51#define WDT_TIMEOUT_MAX 1023 /* approx. 17 min. */
52#define WDT_TIMEOUT 60
53static int timeout = WDT_TIMEOUT;
54module_param(timeout, int, 0);
55MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, between 1 and 1023 "
56 "(default = " __MODULE_STRING(WDT_TIMEOUT) ")");
57
58static int nowayout = WATCHDOG_NOWAYOUT;
59module_param(nowayout, int, 0);
60MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
61 "(default = " __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
62
63static struct watchdog_device wdt_dev;
64static struct resource wdt_res;
65static void __iomem *wdt_mem;
66static unsigned int mmio;
67static void wdt_timer_tick(unsigned long data);
68static DEFINE_TIMER(timer, wdt_timer_tick, 0, 0);
69 /* The timer that pings the watchdog */
70static unsigned long next_heartbeat; /* the next_heartbeat for the timer */
71
72static inline void wdt_reset(void)
73{
74 unsigned int ctl = readl(wdt_mem);
75
76 writel(ctl | VIA_WDT_TRIGGER, wdt_mem);
77}
78
79/*
80 * Timer tick: the timer will make sure that the watchdog timer hardware
81 * is being reset in time. The conditions to do this are:
82 * 1) the watchog timer has been started and /dev/watchdog is open
83 * and there is still time left before userspace should send the
84 * next heartbeat/ping. (note: the internal heartbeat is much smaller
85 * then the external/userspace heartbeat).
86 * 2) the watchdog timer has been stopped by userspace.
87 */
88static void wdt_timer_tick(unsigned long data)
89{
90 if (time_before(jiffies, next_heartbeat) ||
91 (!test_bit(WDOG_ACTIVE, &wdt_dev.status))) {
92 wdt_reset();
93 mod_timer(&timer, jiffies + WDT_HEARTBEAT);
94 } else
95 pr_crit("I will reboot your machine !\n");
96}
97
98static int wdt_ping(struct watchdog_device *wdd)
99{
100 /* calculate when the next userspace timeout will be */
101 next_heartbeat = jiffies + timeout * HZ;
102 return 0;
103}
104
105static int wdt_start(struct watchdog_device *wdd)
106{
107 unsigned int ctl = readl(wdt_mem);
108
109 writel(timeout, wdt_mem + VIA_WDT_COUNT);
110 writel(ctl | VIA_WDT_RUNNING | VIA_WDT_TRIGGER, wdt_mem);
111 wdt_ping(wdd);
112 mod_timer(&timer, jiffies + WDT_HEARTBEAT);
113 return 0;
114}
115
116static int wdt_stop(struct watchdog_device *wdd)
117{
118 unsigned int ctl = readl(wdt_mem);
119
120 writel(ctl & ~VIA_WDT_RUNNING, wdt_mem);
121 return 0;
122}
123
124static int wdt_set_timeout(struct watchdog_device *wdd,
125 unsigned int new_timeout)
126{
127 if (new_timeout < 1 || new_timeout > WDT_TIMEOUT_MAX)
128 return -EINVAL;
129 writel(new_timeout, wdt_mem + VIA_WDT_COUNT);
130 timeout = new_timeout;
131 return 0;
132}
133
134static const struct watchdog_info wdt_info = {
135 .identity = "VIA watchdog",
136 .options = WDIOF_CARDRESET |
137 WDIOF_SETTIMEOUT |
138 WDIOF_MAGICCLOSE |
139 WDIOF_KEEPALIVEPING,
140};
141
142static const struct watchdog_ops wdt_ops = {
143 .owner = THIS_MODULE,
144 .start = wdt_start,
145 .stop = wdt_stop,
146 .ping = wdt_ping,
147 .set_timeout = wdt_set_timeout,
148};
149
150static struct watchdog_device wdt_dev = {
151 .info = &wdt_info,
152 .ops = &wdt_ops,
153};
154
155static int __devinit wdt_probe(struct pci_dev *pdev,
156 const struct pci_device_id *ent)
157{
158 unsigned char conf;
159 int ret = -ENODEV;
160
161 if (pci_enable_device(pdev)) {
162 dev_err(&pdev->dev, "cannot enable PCI device\n");
163 return -ENODEV;
164 }
165
166 /*
167 * Allocate a MMIO region which contains watchdog control register
168 * and counter, then configure the watchdog to use this region.
169 * This is possible only if PnP is properly enabled in BIOS.
170 * If not, the watchdog must be configured in BIOS manually.
171 */
172 if (allocate_resource(&iomem_resource, &wdt_res, VIA_WDT_MMIO_LEN,
173 0xf0000000, 0xffffff00, 0xff, NULL, NULL)) {
174 dev_err(&pdev->dev, "MMIO allocation failed\n");
175 goto err_out_disable_device;
176 }
177
178 pci_write_config_dword(pdev, VIA_WDT_MMIO_BASE, wdt_res.start);
179 pci_read_config_byte(pdev, VIA_WDT_CONF, &conf);
180 conf |= VIA_WDT_CONF_ENABLE | VIA_WDT_CONF_MMIO;
181 pci_write_config_byte(pdev, VIA_WDT_CONF, conf);
182
183 pci_read_config_dword(pdev, VIA_WDT_MMIO_BASE, &mmio);
184 if (mmio) {
185 dev_info(&pdev->dev, "VIA Chipset watchdog MMIO: %x\n", mmio);
186 } else {
187 dev_err(&pdev->dev, "MMIO setting failed. Check BIOS.\n");
188 goto err_out_resource;
189 }
190
191 if (!request_mem_region(mmio, VIA_WDT_MMIO_LEN, "via_wdt")) {
192 dev_err(&pdev->dev, "MMIO region busy\n");
193 goto err_out_resource;
194 }
195
196 wdt_mem = ioremap(mmio, VIA_WDT_MMIO_LEN);
197 if (wdt_mem == NULL) {
198 dev_err(&pdev->dev, "cannot remap VIA wdt MMIO registers\n");
199 goto err_out_release;
200 }
201
202 wdt_dev.timeout = timeout;
203 watchdog_set_nowayout(&wdt_dev, nowayout);
204 if (readl(wdt_mem) & VIA_WDT_FIRED)
205 wdt_dev.bootstatus |= WDIOF_CARDRESET;
206
207 ret = watchdog_register_device(&wdt_dev);
208 if (ret)
209 goto err_out_iounmap;
210
211 /* start triggering, in case of watchdog already enabled by BIOS */
212 mod_timer(&timer, jiffies + WDT_HEARTBEAT);
213 return 0;
214
215err_out_iounmap:
216 iounmap(wdt_mem);
217err_out_release:
218 release_mem_region(mmio, VIA_WDT_MMIO_LEN);
219err_out_resource:
220 release_resource(&wdt_res);
221err_out_disable_device:
222 pci_disable_device(pdev);
223 return ret;
224}
225
226static void __devexit wdt_remove(struct pci_dev *pdev)
227{
228 watchdog_unregister_device(&wdt_dev);
229 del_timer(&timer);
230 iounmap(wdt_mem);
231 release_mem_region(mmio, VIA_WDT_MMIO_LEN);
232 release_resource(&wdt_res);
233 pci_disable_device(pdev);
234}
235
236DEFINE_PCI_DEVICE_TABLE(wdt_pci_table) = {
237 { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_CX700) },
238 { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX800) },
239 { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) },
240 { 0 }
241};
242
243static struct pci_driver wdt_driver = {
244 .name = "via_wdt",
245 .id_table = wdt_pci_table,
246 .probe = wdt_probe,
247 .remove = __devexit_p(wdt_remove),
248};
249
250static int __init wdt_init(void)
251{
252 if (timeout < 1 || timeout > WDT_TIMEOUT_MAX)
253 timeout = WDT_TIMEOUT;
254 return pci_register_driver(&wdt_driver);
255}
256
257static void __exit wdt_exit(void)
258{
259 pci_unregister_driver(&wdt_driver);
260}
261
262module_init(wdt_init);
263module_exit(wdt_exit);
264
265MODULE_AUTHOR("Marc Vertes");
266MODULE_DESCRIPTION("Driver for watchdog timer on VIA chipset");
267MODULE_LICENSE("GPL");