Blame view

drivers/watchdog/davinci_wdt.c 6.65 KB
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  /*
   * drivers/char/watchdog/davinci_wdt.c
   *
   * Watchdog driver for DaVinci DM644x/DM646x processors
   *
   * Copyright (C) 2006 Texas Instruments.
   *
   * 2007 (c) MontaVista Software, Inc. This file is licensed under
   * the terms of the GNU General Public License version 2. This program
   * is licensed "as is" without any warranty of any kind, whether express
   * or implied.
   */
  
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/fs.h>
  #include <linux/miscdevice.h>
  #include <linux/watchdog.h>
  #include <linux/init.h>
  #include <linux/bitops.h>
  #include <linux/platform_device.h>
  #include <linux/spinlock.h>
f78b0a8f2   Alan Cox   [WATCHDOG 08/57] ...
25
26
  #include <linux/uaccess.h>
  #include <linux/io.h>
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
27
  #include <linux/device.h>
9fd868f44   Kevin Hilman   [WATCHDOG] davinc...
28
  #include <linux/clk.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
29
  #include <linux/slab.h>
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
  
  #define MODULE_NAME "DAVINCI-WDT: "
  
  #define DEFAULT_HEARTBEAT 60
  #define MAX_HEARTBEAT     600	/* really the max margin is 264/27MHz*/
  
  /* Timer register set definition */
  #define PID12	(0x0)
  #define EMUMGT	(0x4)
  #define TIM12	(0x10)
  #define TIM34	(0x14)
  #define PRD12	(0x18)
  #define PRD34	(0x1C)
  #define TCR	(0x20)
  #define TGCR	(0x24)
  #define WDTCR	(0x28)
  
  /* TCR bit definitions */
  #define ENAMODE12_DISABLED	(0 << 6)
  #define ENAMODE12_ONESHOT	(1 << 6)
  #define ENAMODE12_PERIODIC	(2 << 6)
  
  /* TGCR bit definitions */
  #define TIM12RS_UNRESET		(1 << 0)
  #define TIM34RS_UNRESET		(1 << 1)
  #define TIMMODE_64BIT_WDOG      (2 << 2)
  
  /* WDTCR bit definitions */
  #define WDEN			(1 << 14)
  #define WDFLAG			(1 << 15)
  #define WDKEY_SEQ0		(0xa5c6 << 16)
  #define WDKEY_SEQ1		(0xda7e << 16)
  
  static int heartbeat = DEFAULT_HEARTBEAT;
c7dfd0cca   Alexey Dobriyan   [WATCHDOG] spin_l...
64
  static DEFINE_SPINLOCK(io_lock);
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
65
66
67
68
69
70
71
72
  static unsigned long wdt_status;
  #define WDT_IN_USE        0
  #define WDT_OK_TO_CLOSE   1
  #define WDT_REGION_INITED 2
  #define WDT_DEVICE_INITED 3
  
  static struct resource	*wdt_mem;
  static void __iomem	*wdt_base;
9fd868f44   Kevin Hilman   [WATCHDOG] davinc...
73
  struct clk		*wdt_clk;
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
74
75
76
77
78
79
  
  static void wdt_service(void)
  {
  	spin_lock(&io_lock);
  
  	/* put watchdog in service state */
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
80
  	iowrite32(WDKEY_SEQ0, wdt_base + WDTCR);
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
81
  	/* put watchdog in active state */
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
82
  	iowrite32(WDKEY_SEQ1, wdt_base + WDTCR);
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
83
84
85
86
87
88
89
90
  
  	spin_unlock(&io_lock);
  }
  
  static void wdt_enable(void)
  {
  	u32 tgcr;
  	u32 timer_margin;
9fd868f44   Kevin Hilman   [WATCHDOG] davinc...
91
92
93
  	unsigned long wdt_freq;
  
  	wdt_freq = clk_get_rate(wdt_clk);
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
94
95
96
97
  
  	spin_lock(&io_lock);
  
  	/* disable, internal clock source */
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
98
  	iowrite32(0, wdt_base + TCR);
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
99
  	/* reset timer, set mode to 64-bit watchdog, and unreset */
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
100
  	iowrite32(0, wdt_base + TGCR);
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
101
  	tgcr = TIMMODE_64BIT_WDOG | TIM12RS_UNRESET | TIM34RS_UNRESET;
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
102
  	iowrite32(tgcr, wdt_base + TGCR);
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
103
  	/* clear counter regs */
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
104
105
  	iowrite32(0, wdt_base + TIM12);
  	iowrite32(0, wdt_base + TIM34);
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
106
  	/* set timeout period */
9fd868f44   Kevin Hilman   [WATCHDOG] davinc...
107
  	timer_margin = (((u64)heartbeat * wdt_freq) & 0xffffffff);
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
108
  	iowrite32(timer_margin, wdt_base + PRD12);
9fd868f44   Kevin Hilman   [WATCHDOG] davinc...
109
  	timer_margin = (((u64)heartbeat * wdt_freq) >> 32);
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
110
  	iowrite32(timer_margin, wdt_base + PRD34);
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
111
  	/* enable run continuously */
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
112
  	iowrite32(ENAMODE12_PERIODIC, wdt_base + TCR);
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
113
114
115
116
117
  	/* Once the WDT is in pre-active state write to
  	 * TIM12, TIM34, PRD12, PRD34, TCR, TGCR, WDTCR are
  	 * write protected (except for the WDKEY field)
  	 */
  	/* put watchdog in pre-active state */
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
118
  	iowrite32(WDKEY_SEQ0 | WDEN, wdt_base + WDTCR);
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
119
  	/* put watchdog in active state */
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
120
  	iowrite32(WDKEY_SEQ1 | WDEN, wdt_base + WDTCR);
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
  
  	spin_unlock(&io_lock);
  }
  
  static int davinci_wdt_open(struct inode *inode, struct file *file)
  {
  	if (test_and_set_bit(WDT_IN_USE, &wdt_status))
  		return -EBUSY;
  
  	wdt_enable();
  
  	return nonseekable_open(inode, file);
  }
  
  static ssize_t
  davinci_wdt_write(struct file *file, const char *data, size_t len,
  		  loff_t *ppos)
  {
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
139
140
141
142
143
  	if (len)
  		wdt_service();
  
  	return len;
  }
42747d712   Wim Van Sebroeck   [WATCHDOG] watchd...
144
  static const struct watchdog_info ident = {
f1a08cc9a   Wim Van Sebroeck   [WATCHDOG] davinc...
145
  	.options = WDIOF_KEEPALIVEPING,
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
146
147
  	.identity = "DaVinci Watchdog",
  };
f78b0a8f2   Alan Cox   [WATCHDOG 08/57] ...
148
149
  static long davinci_wdt_ioctl(struct file *file,
  					unsigned int cmd, unsigned long arg)
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
150
151
152
153
154
155
156
157
158
159
  {
  	int ret = -ENOTTY;
  
  	switch (cmd) {
  	case WDIOC_GETSUPPORT:
  		ret = copy_to_user((struct watchdog_info *)arg, &ident,
  				   sizeof(ident)) ? -EFAULT : 0;
  		break;
  
  	case WDIOC_GETSTATUS:
f1a08cc9a   Wim Van Sebroeck   [WATCHDOG] davinc...
160
  	case WDIOC_GETBOOTSTATUS:
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
161
162
  		ret = put_user(0, (int *)arg);
  		break;
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
163
164
165
166
  	case WDIOC_KEEPALIVE:
  		wdt_service();
  		ret = 0;
  		break;
0c06090c9   Wim Van Sebroeck   [WATCHDOG] Coding...
167
168
169
170
  
  	case WDIOC_GETTIMEOUT:
  		ret = put_user(heartbeat, (int *)arg);
  		break;
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
  	}
  	return ret;
  }
  
  static int davinci_wdt_release(struct inode *inode, struct file *file)
  {
  	wdt_service();
  	clear_bit(WDT_IN_USE, &wdt_status);
  
  	return 0;
  }
  
  static const struct file_operations davinci_wdt_fops = {
  	.owner = THIS_MODULE,
  	.llseek = no_llseek,
  	.write = davinci_wdt_write,
f78b0a8f2   Alan Cox   [WATCHDOG 08/57] ...
187
  	.unlocked_ioctl = davinci_wdt_ioctl,
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
188
189
190
191
192
193
194
195
196
  	.open = davinci_wdt_open,
  	.release = davinci_wdt_release,
  };
  
  static struct miscdevice davinci_wdt_miscdev = {
  	.minor = WATCHDOG_MINOR,
  	.name = "watchdog",
  	.fops = &davinci_wdt_fops,
  };
b6bf291f1   Wim Van Sebroeck   [WATCHDOG] move p...
197
  static int __devinit davinci_wdt_probe(struct platform_device *pdev)
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
198
199
  {
  	int ret = 0, size;
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
200
  	struct device *dev = &pdev->dev;
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
201

9fd868f44   Kevin Hilman   [WATCHDOG] davinc...
202
203
204
205
206
  	wdt_clk = clk_get(dev, NULL);
  	if (WARN_ON(IS_ERR(wdt_clk)))
  		return PTR_ERR(wdt_clk);
  
  	clk_enable(wdt_clk);
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
207
208
  	if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
  		heartbeat = DEFAULT_HEARTBEAT;
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
209
210
  	dev_info(dev, "heartbeat %d sec
  ", heartbeat);
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
211

f712eacf0   Julia Lawall   watchdog: Convert...
212
213
  	wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	if (wdt_mem == NULL) {
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
214
215
  		dev_err(dev, "failed to get memory region resource
  ");
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
216
217
  		return -ENOENT;
  	}
f712eacf0   Julia Lawall   watchdog: Convert...
218
219
  	size = resource_size(wdt_mem);
  	if (!request_mem_region(wdt_mem->start, size, pdev->name)) {
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
220
221
  		dev_err(dev, "failed to get memory region
  ");
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
222
223
  		return -ENOENT;
  	}
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
224

f712eacf0   Julia Lawall   watchdog: Convert...
225
  	wdt_base = ioremap(wdt_mem->start, size);
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
226
227
228
  	if (!wdt_base) {
  		dev_err(dev, "failed to map memory region
  ");
f712eacf0   Julia Lawall   watchdog: Convert...
229
230
  		release_mem_region(wdt_mem->start, size);
  		wdt_mem = NULL;
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
231
232
  		return -ENOMEM;
  	}
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
233
234
235
  
  	ret = misc_register(&davinci_wdt_miscdev);
  	if (ret < 0) {
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
236
237
  		dev_err(dev, "cannot register misc device
  ");
f712eacf0   Julia Lawall   watchdog: Convert...
238
239
  		release_mem_region(wdt_mem->start, size);
  		wdt_mem = NULL;
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
240
241
242
  	} else {
  		set_bit(WDT_DEVICE_INITED, &wdt_status);
  	}
371d3525e   Kevin Hilman   [WATCHDOG] davinc...
243
  	iounmap(wdt_base);
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
244
245
  	return ret;
  }
b6bf291f1   Wim Van Sebroeck   [WATCHDOG] move p...
246
  static int __devexit davinci_wdt_remove(struct platform_device *pdev)
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
247
248
249
  {
  	misc_deregister(&davinci_wdt_miscdev);
  	if (wdt_mem) {
f712eacf0   Julia Lawall   watchdog: Convert...
250
  		release_mem_region(wdt_mem->start, resource_size(wdt_mem));
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
251
252
  		wdt_mem = NULL;
  	}
9fd868f44   Kevin Hilman   [WATCHDOG] davinc...
253
254
255
  
  	clk_disable(wdt_clk);
  	clk_put(wdt_clk);
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
256
257
258
259
260
261
  	return 0;
  }
  
  static struct platform_driver platform_wdt_driver = {
  	.driver = {
  		.name = "watchdog",
f37d193c7   Kay Sievers   watchdog: fix pla...
262
  		.owner	= THIS_MODULE,
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
263
264
  	},
  	.probe = davinci_wdt_probe,
b6bf291f1   Wim Van Sebroeck   [WATCHDOG] move p...
265
  	.remove = __devexit_p(davinci_wdt_remove),
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
266
  };
b8ec61189   Axel Lin   watchdog: convert...
267
  module_platform_driver(platform_wdt_driver);
7d831bf59   Vladimir Barinov   [WATCHDOG] davinc...
268
269
270
271
272
273
274
275
276
277
278
279
  
  MODULE_AUTHOR("Texas Instruments");
  MODULE_DESCRIPTION("DaVinci Watchdog Driver");
  
  module_param(heartbeat, int, 0);
  MODULE_PARM_DESC(heartbeat,
  		 "Watchdog heartbeat period in seconds from 1 to "
  		 __MODULE_STRING(MAX_HEARTBEAT) ", default "
  		 __MODULE_STRING(DEFAULT_HEARTBEAT));
  
  MODULE_LICENSE("GPL");
  MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
f37d193c7   Kay Sievers   watchdog: fix pla...
280
  MODULE_ALIAS("platform:watchdog");