Linux Audio

Check our new training course

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