Blame view

drivers/watchdog/lantiq_wdt.c 5.74 KB
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
1
2
3
4
5
6
7
8
  /*
   *  This program is free software; you can redistribute it and/or modify it
   *  under the terms of the GNU General Public License version 2 as published
   *  by the Free Software Foundation.
   *
   *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
   *  Based on EP93xx wdt driver
   */
27c766aaa   Joe Perches   watchdog: Use pr_...
9
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
10
11
12
13
  #include <linux/module.h>
  #include <linux/fs.h>
  #include <linux/miscdevice.h>
  #include <linux/watchdog.h>
cdb861214   John Crispin   watchdog: MIPS: l...
14
  #include <linux/of_platform.h>
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
15
16
17
  #include <linux/uaccess.h>
  #include <linux/clk.h>
  #include <linux/io.h>
cdb861214   John Crispin   watchdog: MIPS: l...
18
  #include <lantiq_soc.h>
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
19

cdb861214   John Crispin   watchdog: MIPS: l...
20
21
  /*
   * Section 3.4 of the datasheet
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
   * The password sequence protects the WDT control register from unintended
   * write actions, which might cause malfunction of the WDT.
   *
   * essentially the following two magic passwords need to be written to allow
   * IO access to the WDT core
   */
  #define LTQ_WDT_PW1		0x00BE0000
  #define LTQ_WDT_PW2		0x00DC0000
  
  #define LTQ_WDT_CR		0x0	/* watchdog control register */
  #define LTQ_WDT_SR		0x8	/* watchdog status register */
  
  #define LTQ_WDT_SR_EN		(0x1 << 31)	/* enable bit */
  #define LTQ_WDT_SR_PWD		(0x3 << 26)	/* turn on power */
  #define LTQ_WDT_SR_CLKDIV	(0x3 << 24)	/* turn on clock and set */
  						/* divider to 0x40000 */
  #define LTQ_WDT_DIVIDER		0x40000
  #define LTQ_MAX_TIMEOUT		((1 << 16) - 1)	/* the reload field is 16 bit */
86a1e1896   Wim Van Sebroeck   watchdog: nowayou...
40
  static bool nowayout = WATCHDOG_NOWAYOUT;
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
41
42
43
44
45
46
47
48
49
50
51
52
  
  static void __iomem *ltq_wdt_membase;
  static unsigned long ltq_io_region_clk_rate;
  
  static unsigned long ltq_wdt_bootstatus;
  static unsigned long ltq_wdt_in_use;
  static int ltq_wdt_timeout = 30;
  static int ltq_wdt_ok_to_close;
  
  static void
  ltq_wdt_enable(void)
  {
9cfce47b1   John Crispin   watchdog: lantiq:...
53
  	unsigned long int timeout = ltq_wdt_timeout *
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
54
  			(ltq_io_region_clk_rate / LTQ_WDT_DIVIDER) + 0x1000;
9cfce47b1   John Crispin   watchdog: lantiq:...
55
56
  	if (timeout > LTQ_MAX_TIMEOUT)
  		timeout = LTQ_MAX_TIMEOUT;
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
57
58
59
60
61
  
  	/* write the first password magic */
  	ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR);
  	/* write the second magic plus the configuration and new timeout */
  	ltq_w32(LTQ_WDT_SR_EN | LTQ_WDT_SR_PWD | LTQ_WDT_SR_CLKDIV |
9cfce47b1   John Crispin   watchdog: lantiq:...
62
  		LTQ_WDT_PW2 | timeout, ltq_wdt_membase + LTQ_WDT_CR);
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
63
64
65
66
67
68
69
  }
  
  static void
  ltq_wdt_disable(void)
  {
  	/* write the first password magic */
  	ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR);
cdb861214   John Crispin   watchdog: MIPS: l...
70
71
  	/*
  	 * write the second password magic with no config
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
  	 * this turns the watchdog off
  	 */
  	ltq_w32(LTQ_WDT_PW2, ltq_wdt_membase + LTQ_WDT_CR);
  }
  
  static ssize_t
  ltq_wdt_write(struct file *file, const char __user *data,
  		size_t len, loff_t *ppos)
  {
  	if (len) {
  		if (!nowayout) {
  			size_t i;
  
  			ltq_wdt_ok_to_close = 0;
  			for (i = 0; i != len; i++) {
  				char c;
  
  				if (get_user(c, data + i))
  					return -EFAULT;
  				if (c == 'V')
  					ltq_wdt_ok_to_close = 1;
  				else
  					ltq_wdt_ok_to_close = 0;
  			}
  		}
  		ltq_wdt_enable();
  	}
  
  	return len;
  }
  
  static struct watchdog_info ident = {
  	.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
  			WDIOF_CARDRESET,
  	.identity = "ltq_wdt",
  };
  
  static long
  ltq_wdt_ioctl(struct file *file,
  		unsigned int cmd, unsigned long arg)
  {
  	int ret = -ENOTTY;
  
  	switch (cmd) {
  	case WDIOC_GETSUPPORT:
  		ret = copy_to_user((struct watchdog_info __user *)arg, &ident,
  				sizeof(ident)) ? -EFAULT : 0;
  		break;
  
  	case WDIOC_GETBOOTSTATUS:
  		ret = put_user(ltq_wdt_bootstatus, (int __user *)arg);
  		break;
  
  	case WDIOC_GETSTATUS:
  		ret = put_user(0, (int __user *)arg);
  		break;
  
  	case WDIOC_SETTIMEOUT:
  		ret = get_user(ltq_wdt_timeout, (int __user *)arg);
  		if (!ret)
  			ltq_wdt_enable();
  		/* intentional drop through */
  	case WDIOC_GETTIMEOUT:
  		ret = put_user(ltq_wdt_timeout, (int __user *)arg);
  		break;
  
  	case WDIOC_KEEPALIVE:
  		ltq_wdt_enable();
  		ret = 0;
  		break;
  	}
  	return ret;
  }
  
  static int
  ltq_wdt_open(struct inode *inode, struct file *file)
  {
  	if (test_and_set_bit(0, &ltq_wdt_in_use))
  		return -EBUSY;
  	ltq_wdt_in_use = 1;
  	ltq_wdt_enable();
  
  	return nonseekable_open(inode, file);
  }
  
  static int
  ltq_wdt_release(struct inode *inode, struct file *file)
  {
  	if (ltq_wdt_ok_to_close)
  		ltq_wdt_disable();
  	else
27c766aaa   Joe Perches   watchdog: Use pr_...
163
164
  		pr_err("watchdog closed without warning
  ");
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
  	ltq_wdt_ok_to_close = 0;
  	clear_bit(0, &ltq_wdt_in_use);
  
  	return 0;
  }
  
  static const struct file_operations ltq_wdt_fops = {
  	.owner		= THIS_MODULE,
  	.write		= ltq_wdt_write,
  	.unlocked_ioctl	= ltq_wdt_ioctl,
  	.open		= ltq_wdt_open,
  	.release	= ltq_wdt_release,
  	.llseek		= no_llseek,
  };
  
  static struct miscdevice ltq_wdt_miscdev = {
  	.minor	= WATCHDOG_MINOR,
  	.name	= "watchdog",
  	.fops	= &ltq_wdt_fops,
  };
2d991a164   Bill Pemberton   watchdog: remove ...
185
  static int
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
186
187
188
189
  ltq_wdt_probe(struct platform_device *pdev)
  {
  	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	struct clk *clk;
4c271bb67   Thierry Reding   watchdog: Convert...
190
191
192
  	ltq_wdt_membase = devm_ioremap_resource(&pdev->dev, res);
  	if (IS_ERR(ltq_wdt_membase))
  		return PTR_ERR(ltq_wdt_membase);
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
193
194
  
  	/* we do not need to enable the clock as it is always running */
cdb861214   John Crispin   watchdog: MIPS: l...
195
196
197
198
199
200
  	clk = clk_get_io();
  	if (IS_ERR(clk)) {
  		dev_err(&pdev->dev, "Failed to get clock
  ");
  		return -ENOENT;
  	}
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
201
202
  	ltq_io_region_clk_rate = clk_get_rate(clk);
  	clk_put(clk);
cdb861214   John Crispin   watchdog: MIPS: l...
203
  	/* find out if the watchdog caused the last reboot */
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
204
205
  	if (ltq_reset_cause() == LTQ_RST_CAUSE_WDTRST)
  		ltq_wdt_bootstatus = WDIOF_CARDRESET;
cdb861214   John Crispin   watchdog: MIPS: l...
206
207
  	dev_info(&pdev->dev, "Init done
  ");
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
208
209
  	return misc_register(&ltq_wdt_miscdev);
  }
4b12b896c   Bill Pemberton   watchdog: remove ...
210
  static int
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
211
212
213
  ltq_wdt_remove(struct platform_device *pdev)
  {
  	misc_deregister(&ltq_wdt_miscdev);
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
214
215
  	return 0;
  }
cdb861214   John Crispin   watchdog: MIPS: l...
216
217
218
219
220
  static const struct of_device_id ltq_wdt_match[] = {
  	{ .compatible = "lantiq,wdt" },
  	{},
  };
  MODULE_DEVICE_TABLE(of, ltq_wdt_match);
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
221
222
  
  static struct platform_driver ltq_wdt_driver = {
cdb861214   John Crispin   watchdog: MIPS: l...
223
  	.probe = ltq_wdt_probe,
82268714b   Bill Pemberton   watchdog: remove ...
224
  	.remove = ltq_wdt_remove,
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
225
  	.driver = {
cdb861214   John Crispin   watchdog: MIPS: l...
226
  		.name = "wdt",
cdb861214   John Crispin   watchdog: MIPS: l...
227
  		.of_match_table = ltq_wdt_match,
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
228
229
  	},
  };
cdb861214   John Crispin   watchdog: MIPS: l...
230
  module_platform_driver(ltq_wdt_driver);
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
231

86a1e1896   Wim Van Sebroeck   watchdog: nowayou...
232
  module_param(nowayout, bool, 0);
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
233
  MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
2f58b8d04   John Crispin   MIPS: Lantiq: Add...
234
235
236
  MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
  MODULE_DESCRIPTION("Lantiq SoC Watchdog");
  MODULE_LICENSE("GPL");