Linux Audio

Check our new training course

Loading...
v3.1
  1/*
  2 * PIKA FPGA based Watchdog Timer
  3 *
  4 * Copyright (c) 2008 PIKA Technologies
  5 *   Sean MacLennan <smaclennan@pikatech.com>
  6 */
  7
 
 
  8#include <linux/init.h>
  9#include <linux/errno.h>
 10#include <linux/module.h>
 11#include <linux/moduleparam.h>
 12#include <linux/types.h>
 13#include <linux/kernel.h>
 14#include <linux/fs.h>
 15#include <linux/miscdevice.h>
 16#include <linux/watchdog.h>
 17#include <linux/reboot.h>
 18#include <linux/jiffies.h>
 19#include <linux/timer.h>
 20#include <linux/bitops.h>
 21#include <linux/uaccess.h>
 22#include <linux/io.h>
 
 23#include <linux/of_platform.h>
 24
 25#define DRV_NAME "PIKA-WDT"
 26#define PFX DRV_NAME ": "
 27
 28/* Hardware timeout in seconds */
 29#define WDT_HW_TIMEOUT 2
 30
 31/* Timer heartbeat (500ms) */
 32#define WDT_TIMEOUT	(HZ/2)
 33
 34/* User land timeout */
 35#define WDT_HEARTBEAT 15
 36static int heartbeat = WDT_HEARTBEAT;
 37module_param(heartbeat, int, 0);
 38MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
 39	"(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
 40
 41static int nowayout = WATCHDOG_NOWAYOUT;
 42module_param(nowayout, int, 0);
 43MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
 44	"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 45
 46static struct {
 47	void __iomem *fpga;
 48	unsigned long next_heartbeat;	/* the next_heartbeat for the timer */
 49	unsigned long open;
 50	char expect_close;
 51	int bootstatus;
 52	struct timer_list timer;	/* The timer that pings the watchdog */
 53} pikawdt_private;
 54
 55static struct watchdog_info ident = {
 56	.identity	= DRV_NAME,
 57	.options	= WDIOF_CARDRESET |
 58			  WDIOF_SETTIMEOUT |
 59			  WDIOF_KEEPALIVEPING |
 60			  WDIOF_MAGICCLOSE,
 61};
 62
 63/*
 64 * Reload the watchdog timer.  (ie, pat the watchdog)
 65 */
 66static inline void pikawdt_reset(void)
 67{
 68	/* -- FPGA: Reset Control Register (32bit R/W) (Offset: 0x14) --
 69	 * Bit 7,    WTCHDG_EN: When set to 1, the watchdog timer is enabled.
 70	 *           Once enabled, it cannot be disabled. The watchdog can be
 71	 *           kicked by performing any write access to the reset
 72	 *           control register (this register).
 73	 * Bit 8-11, WTCHDG_TIMEOUT_SEC: Sets the watchdog timeout value in
 74	 *           seconds. Valid ranges are 1 to 15 seconds. The value can
 75	 *           be modified dynamically.
 76	 */
 77	unsigned reset = in_be32(pikawdt_private.fpga + 0x14);
 78	/* enable with max timeout - 15 seconds */
 79	reset |= (1 << 7) + (WDT_HW_TIMEOUT << 8);
 80	out_be32(pikawdt_private.fpga + 0x14, reset);
 81}
 82
 83/*
 84 * Timer tick
 85 */
 86static void pikawdt_ping(unsigned long data)
 87{
 88	if (time_before(jiffies, pikawdt_private.next_heartbeat) ||
 89			(!nowayout && !pikawdt_private.open)) {
 90		pikawdt_reset();
 91		mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT);
 92	} else
 93		printk(KERN_CRIT PFX "I will reset your machine !\n");
 94}
 95
 96
 97static void pikawdt_keepalive(void)
 98{
 99	pikawdt_private.next_heartbeat = jiffies + heartbeat * HZ;
100}
101
102static void pikawdt_start(void)
103{
104	pikawdt_keepalive();
105	mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT);
106}
107
108/*
109 * Watchdog device is opened, and watchdog starts running.
110 */
111static int pikawdt_open(struct inode *inode, struct file *file)
112{
113	/* /dev/watchdog can only be opened once */
114	if (test_and_set_bit(0, &pikawdt_private.open))
115		return -EBUSY;
116
117	pikawdt_start();
118
119	return nonseekable_open(inode, file);
120}
121
122/*
123 * Close the watchdog device.
124 */
125static int pikawdt_release(struct inode *inode, struct file *file)
126{
127	/* stop internal ping */
128	if (!pikawdt_private.expect_close)
129		del_timer(&pikawdt_private.timer);
130
131	clear_bit(0, &pikawdt_private.open);
132	pikawdt_private.expect_close = 0;
133	return 0;
134}
135
136/*
137 * Pat the watchdog whenever device is written to.
138 */
139static ssize_t pikawdt_write(struct file *file, const char __user *data,
140			     size_t len, loff_t *ppos)
141{
142	if (!len)
143		return 0;
144
145	/* Scan for magic character */
146	if (!nowayout) {
147		size_t i;
148
149		pikawdt_private.expect_close = 0;
150
151		for (i = 0; i < len; i++) {
152			char c;
153			if (get_user(c, data + i))
154				return -EFAULT;
155			if (c == 'V') {
156				pikawdt_private.expect_close = 42;
157				break;
158			}
159		}
160	}
161
162	pikawdt_keepalive();
163
164	return len;
165}
166
167/*
168 * Handle commands from user-space.
169 */
170static long pikawdt_ioctl(struct file *file,
171		unsigned int cmd, unsigned long arg)
172{
173	void __user *argp = (void __user *)arg;
174	int __user *p = argp;
175	int new_value;
176
177	switch (cmd) {
178	case WDIOC_GETSUPPORT:
179		return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
180
181	case WDIOC_GETSTATUS:
182		return put_user(0, p);
183
184	case WDIOC_GETBOOTSTATUS:
185		return put_user(pikawdt_private.bootstatus, p);
186
187	case WDIOC_KEEPALIVE:
188		pikawdt_keepalive();
189		return 0;
190
191	case WDIOC_SETTIMEOUT:
192		if (get_user(new_value, p))
193			return -EFAULT;
194
195		heartbeat = new_value;
196		pikawdt_keepalive();
197
198		return put_user(new_value, p);  /* return current value */
199
200	case WDIOC_GETTIMEOUT:
201		return put_user(heartbeat, p);
202	}
203	return -ENOTTY;
204}
205
206
207static const struct file_operations pikawdt_fops = {
208	.owner		= THIS_MODULE,
209	.llseek		= no_llseek,
210	.open		= pikawdt_open,
211	.release	= pikawdt_release,
212	.write		= pikawdt_write,
213	.unlocked_ioctl	= pikawdt_ioctl,
214};
215
216static struct miscdevice pikawdt_miscdev = {
217	.minor	= WATCHDOG_MINOR,
218	.name	= "watchdog",
219	.fops	= &pikawdt_fops,
220};
221
222static int __init pikawdt_init(void)
223{
224	struct device_node *np;
225	void __iomem *fpga;
226	static u32 post1;
227	int ret;
228
229	np = of_find_compatible_node(NULL, NULL, "pika,fpga");
230	if (np == NULL) {
231		printk(KERN_ERR PFX "Unable to find fpga.\n");
232		return -ENOENT;
233	}
234
235	pikawdt_private.fpga = of_iomap(np, 0);
236	of_node_put(np);
237	if (pikawdt_private.fpga == NULL) {
238		printk(KERN_ERR PFX "Unable to map fpga.\n");
239		return -ENOMEM;
240	}
241
242	ident.firmware_version = in_be32(pikawdt_private.fpga + 0x1c) & 0xffff;
243
244	/* POST information is in the sd area. */
245	np = of_find_compatible_node(NULL, NULL, "pika,fpga-sd");
246	if (np == NULL) {
247		printk(KERN_ERR PFX "Unable to find fpga-sd.\n");
248		ret = -ENOENT;
249		goto out;
250	}
251
252	fpga = of_iomap(np, 0);
253	of_node_put(np);
254	if (fpga == NULL) {
255		printk(KERN_ERR PFX "Unable to map fpga-sd.\n");
256		ret = -ENOMEM;
257		goto out;
258	}
259
260	/* -- FPGA: POST Test Results Register 1 (32bit R/W) (Offset: 0x4040) --
261	 * Bit 31,   WDOG: Set to 1 when the last reset was caused by a watchdog
262	 *           timeout.
263	 */
264	post1 = in_be32(fpga + 0x40);
265	if (post1 & 0x80000000)
266		pikawdt_private.bootstatus = WDIOF_CARDRESET;
267
268	iounmap(fpga);
269
270	setup_timer(&pikawdt_private.timer, pikawdt_ping, 0);
271
272	ret = misc_register(&pikawdt_miscdev);
273	if (ret) {
274		printk(KERN_ERR PFX "Unable to register miscdev.\n");
275		goto out;
276	}
277
278	printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
279							heartbeat, nowayout);
280	return 0;
281
282out:
283	iounmap(pikawdt_private.fpga);
284	return ret;
285}
286
287static void __exit pikawdt_exit(void)
288{
289	misc_deregister(&pikawdt_miscdev);
290
291	iounmap(pikawdt_private.fpga);
292}
293
294module_init(pikawdt_init);
295module_exit(pikawdt_exit);
296
297MODULE_AUTHOR("Sean MacLennan <smaclennan@pikatech.com>");
298MODULE_DESCRIPTION("PIKA FPGA based Watchdog Timer");
299MODULE_LICENSE("GPL");
300MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
301
v3.15
  1/*
  2 * PIKA FPGA based Watchdog Timer
  3 *
  4 * Copyright (c) 2008 PIKA Technologies
  5 *   Sean MacLennan <smaclennan@pikatech.com>
  6 */
  7
  8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  9
 10#include <linux/init.h>
 11#include <linux/errno.h>
 12#include <linux/module.h>
 13#include <linux/moduleparam.h>
 14#include <linux/types.h>
 15#include <linux/kernel.h>
 16#include <linux/fs.h>
 17#include <linux/miscdevice.h>
 18#include <linux/watchdog.h>
 19#include <linux/reboot.h>
 20#include <linux/jiffies.h>
 21#include <linux/timer.h>
 22#include <linux/bitops.h>
 23#include <linux/uaccess.h>
 24#include <linux/io.h>
 25#include <linux/of_address.h>
 26#include <linux/of_platform.h>
 27
 28#define DRV_NAME "PIKA-WDT"
 
 29
 30/* Hardware timeout in seconds */
 31#define WDT_HW_TIMEOUT 2
 32
 33/* Timer heartbeat (500ms) */
 34#define WDT_TIMEOUT	(HZ/2)
 35
 36/* User land timeout */
 37#define WDT_HEARTBEAT 15
 38static int heartbeat = WDT_HEARTBEAT;
 39module_param(heartbeat, int, 0);
 40MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
 41	"(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
 42
 43static bool nowayout = WATCHDOG_NOWAYOUT;
 44module_param(nowayout, bool, 0);
 45MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
 46	"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 47
 48static struct {
 49	void __iomem *fpga;
 50	unsigned long next_heartbeat;	/* the next_heartbeat for the timer */
 51	unsigned long open;
 52	char expect_close;
 53	int bootstatus;
 54	struct timer_list timer;	/* The timer that pings the watchdog */
 55} pikawdt_private;
 56
 57static struct watchdog_info ident = {
 58	.identity	= DRV_NAME,
 59	.options	= WDIOF_CARDRESET |
 60			  WDIOF_SETTIMEOUT |
 61			  WDIOF_KEEPALIVEPING |
 62			  WDIOF_MAGICCLOSE,
 63};
 64
 65/*
 66 * Reload the watchdog timer.  (ie, pat the watchdog)
 67 */
 68static inline void pikawdt_reset(void)
 69{
 70	/* -- FPGA: Reset Control Register (32bit R/W) (Offset: 0x14) --
 71	 * Bit 7,    WTCHDG_EN: When set to 1, the watchdog timer is enabled.
 72	 *           Once enabled, it cannot be disabled. The watchdog can be
 73	 *           kicked by performing any write access to the reset
 74	 *           control register (this register).
 75	 * Bit 8-11, WTCHDG_TIMEOUT_SEC: Sets the watchdog timeout value in
 76	 *           seconds. Valid ranges are 1 to 15 seconds. The value can
 77	 *           be modified dynamically.
 78	 */
 79	unsigned reset = in_be32(pikawdt_private.fpga + 0x14);
 80	/* enable with max timeout - 15 seconds */
 81	reset |= (1 << 7) + (WDT_HW_TIMEOUT << 8);
 82	out_be32(pikawdt_private.fpga + 0x14, reset);
 83}
 84
 85/*
 86 * Timer tick
 87 */
 88static void pikawdt_ping(unsigned long data)
 89{
 90	if (time_before(jiffies, pikawdt_private.next_heartbeat) ||
 91			(!nowayout && !pikawdt_private.open)) {
 92		pikawdt_reset();
 93		mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT);
 94	} else
 95		pr_crit("I will reset your machine !\n");
 96}
 97
 98
 99static void pikawdt_keepalive(void)
100{
101	pikawdt_private.next_heartbeat = jiffies + heartbeat * HZ;
102}
103
104static void pikawdt_start(void)
105{
106	pikawdt_keepalive();
107	mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT);
108}
109
110/*
111 * Watchdog device is opened, and watchdog starts running.
112 */
113static int pikawdt_open(struct inode *inode, struct file *file)
114{
115	/* /dev/watchdog can only be opened once */
116	if (test_and_set_bit(0, &pikawdt_private.open))
117		return -EBUSY;
118
119	pikawdt_start();
120
121	return nonseekable_open(inode, file);
122}
123
124/*
125 * Close the watchdog device.
126 */
127static int pikawdt_release(struct inode *inode, struct file *file)
128{
129	/* stop internal ping */
130	if (!pikawdt_private.expect_close)
131		del_timer(&pikawdt_private.timer);
132
133	clear_bit(0, &pikawdt_private.open);
134	pikawdt_private.expect_close = 0;
135	return 0;
136}
137
138/*
139 * Pat the watchdog whenever device is written to.
140 */
141static ssize_t pikawdt_write(struct file *file, const char __user *data,
142			     size_t len, loff_t *ppos)
143{
144	if (!len)
145		return 0;
146
147	/* Scan for magic character */
148	if (!nowayout) {
149		size_t i;
150
151		pikawdt_private.expect_close = 0;
152
153		for (i = 0; i < len; i++) {
154			char c;
155			if (get_user(c, data + i))
156				return -EFAULT;
157			if (c == 'V') {
158				pikawdt_private.expect_close = 42;
159				break;
160			}
161		}
162	}
163
164	pikawdt_keepalive();
165
166	return len;
167}
168
169/*
170 * Handle commands from user-space.
171 */
172static long pikawdt_ioctl(struct file *file,
173		unsigned int cmd, unsigned long arg)
174{
175	void __user *argp = (void __user *)arg;
176	int __user *p = argp;
177	int new_value;
178
179	switch (cmd) {
180	case WDIOC_GETSUPPORT:
181		return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
182
183	case WDIOC_GETSTATUS:
184		return put_user(0, p);
185
186	case WDIOC_GETBOOTSTATUS:
187		return put_user(pikawdt_private.bootstatus, p);
188
189	case WDIOC_KEEPALIVE:
190		pikawdt_keepalive();
191		return 0;
192
193	case WDIOC_SETTIMEOUT:
194		if (get_user(new_value, p))
195			return -EFAULT;
196
197		heartbeat = new_value;
198		pikawdt_keepalive();
199
200		return put_user(new_value, p);  /* return current value */
201
202	case WDIOC_GETTIMEOUT:
203		return put_user(heartbeat, p);
204	}
205	return -ENOTTY;
206}
207
208
209static const struct file_operations pikawdt_fops = {
210	.owner		= THIS_MODULE,
211	.llseek		= no_llseek,
212	.open		= pikawdt_open,
213	.release	= pikawdt_release,
214	.write		= pikawdt_write,
215	.unlocked_ioctl	= pikawdt_ioctl,
216};
217
218static struct miscdevice pikawdt_miscdev = {
219	.minor	= WATCHDOG_MINOR,
220	.name	= "watchdog",
221	.fops	= &pikawdt_fops,
222};
223
224static int __init pikawdt_init(void)
225{
226	struct device_node *np;
227	void __iomem *fpga;
228	static u32 post1;
229	int ret;
230
231	np = of_find_compatible_node(NULL, NULL, "pika,fpga");
232	if (np == NULL) {
233		pr_err("Unable to find fpga\n");
234		return -ENOENT;
235	}
236
237	pikawdt_private.fpga = of_iomap(np, 0);
238	of_node_put(np);
239	if (pikawdt_private.fpga == NULL) {
240		pr_err("Unable to map fpga\n");
241		return -ENOMEM;
242	}
243
244	ident.firmware_version = in_be32(pikawdt_private.fpga + 0x1c) & 0xffff;
245
246	/* POST information is in the sd area. */
247	np = of_find_compatible_node(NULL, NULL, "pika,fpga-sd");
248	if (np == NULL) {
249		pr_err("Unable to find fpga-sd\n");
250		ret = -ENOENT;
251		goto out;
252	}
253
254	fpga = of_iomap(np, 0);
255	of_node_put(np);
256	if (fpga == NULL) {
257		pr_err("Unable to map fpga-sd\n");
258		ret = -ENOMEM;
259		goto out;
260	}
261
262	/* -- FPGA: POST Test Results Register 1 (32bit R/W) (Offset: 0x4040) --
263	 * Bit 31,   WDOG: Set to 1 when the last reset was caused by a watchdog
264	 *           timeout.
265	 */
266	post1 = in_be32(fpga + 0x40);
267	if (post1 & 0x80000000)
268		pikawdt_private.bootstatus = WDIOF_CARDRESET;
269
270	iounmap(fpga);
271
272	setup_timer(&pikawdt_private.timer, pikawdt_ping, 0);
273
274	ret = misc_register(&pikawdt_miscdev);
275	if (ret) {
276		pr_err("Unable to register miscdev\n");
277		goto out;
278	}
279
280	pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
281		heartbeat, nowayout);
282	return 0;
283
284out:
285	iounmap(pikawdt_private.fpga);
286	return ret;
287}
288
289static void __exit pikawdt_exit(void)
290{
291	misc_deregister(&pikawdt_miscdev);
292
293	iounmap(pikawdt_private.fpga);
294}
295
296module_init(pikawdt_init);
297module_exit(pikawdt_exit);
298
299MODULE_AUTHOR("Sean MacLennan <smaclennan@pikatech.com>");
300MODULE_DESCRIPTION("PIKA FPGA based Watchdog Timer");
301MODULE_LICENSE("GPL");