Blame view

drivers/watchdog/sirfsoc_wdt.c 5.38 KB
a636cd6c4   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
2
3
4
5
  /*
   * Watchdog driver for CSR SiRFprimaII and SiRFatlasVI
   *
   * Copyright (c) 2013 Cambridge Silicon Radio Limited, a CSR plc group company.
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
6
7
8
9
10
11
12
13
14
   */
  
  #include <linux/module.h>
  #include <linux/watchdog.h>
  #include <linux/platform_device.h>
  #include <linux/moduleparam.h>
  #include <linux/of.h>
  #include <linux/io.h>
  #include <linux/uaccess.h>
b0df38dd3   Uwe Kleine-König   watchdog: sirf: d...
15
  #define CLOCK_FREQ	1000000
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
16
17
18
19
20
21
22
23
24
25
26
27
  #define SIRFSOC_TIMER_COUNTER_LO	0x0000
  #define SIRFSOC_TIMER_MATCH_0		0x0008
  #define SIRFSOC_TIMER_INT_EN		0x0024
  #define SIRFSOC_TIMER_WATCHDOG_EN	0x0028
  #define SIRFSOC_TIMER_LATCH		0x0030
  #define SIRFSOC_TIMER_LATCHED_LO	0x0034
  
  #define SIRFSOC_TIMER_WDT_INDEX		5
  
  #define SIRFSOC_WDT_MIN_TIMEOUT		30		/* 30 secs */
  #define SIRFSOC_WDT_MAX_TIMEOUT		(10 * 60)	/* 10 mins */
  #define SIRFSOC_WDT_DEFAULT_TIMEOUT	30		/* 30 secs */
f70b14540   Marcus Folkesson   watchdog: sirfsoc...
28
  static unsigned int timeout;
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
29
30
31
32
33
34
35
36
  static bool nowayout = WATCHDOG_NOWAYOUT;
  
  module_param(timeout, uint, 0);
  module_param(nowayout, bool, 0);
  
  MODULE_PARM_DESC(timeout, "Default watchdog timeout (in seconds)");
  MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  			__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
b99c87740   Ben Dooks   watchdog: sirf: f...
37
38
39
40
  static void __iomem *sirfsoc_wdt_base(struct watchdog_device *wdd)
  {
  	return (void __iomem __force *)watchdog_get_drvdata(wdd);
  }
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
41
42
43
44
45
  static unsigned int sirfsoc_wdt_gettimeleft(struct watchdog_device *wdd)
  {
  	u32 counter, match;
  	void __iomem *wdt_base;
  	int time_left;
b99c87740   Ben Dooks   watchdog: sirf: f...
46
  	wdt_base = sirfsoc_wdt_base(wdd);
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
47
48
49
50
51
  	counter = readl(wdt_base + SIRFSOC_TIMER_COUNTER_LO);
  	match = readl(wdt_base +
  		SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2));
  
  	time_left = match - counter;
b0df38dd3   Uwe Kleine-König   watchdog: sirf: d...
52
  	return time_left / CLOCK_FREQ;
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
53
54
55
56
57
58
  }
  
  static int sirfsoc_wdt_updatetimeout(struct watchdog_device *wdd)
  {
  	u32 counter, timeout_ticks;
  	void __iomem *wdt_base;
b0df38dd3   Uwe Kleine-König   watchdog: sirf: d...
59
  	timeout_ticks = wdd->timeout * CLOCK_FREQ;
b99c87740   Ben Dooks   watchdog: sirf: f...
60
  	wdt_base = sirfsoc_wdt_base(wdd);
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
  
  	/* Enable the latch before reading the LATCH_LO register */
  	writel(1, wdt_base + SIRFSOC_TIMER_LATCH);
  
  	/* Set the TO value */
  	counter = readl(wdt_base + SIRFSOC_TIMER_LATCHED_LO);
  
  	counter += timeout_ticks;
  
  	writel(counter, wdt_base +
  		SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2));
  
  	return 0;
  }
  
  static int sirfsoc_wdt_enable(struct watchdog_device *wdd)
  {
b99c87740   Ben Dooks   watchdog: sirf: f...
78
  	void __iomem *wdt_base = sirfsoc_wdt_base(wdd);
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
  	sirfsoc_wdt_updatetimeout(wdd);
  
  	/*
  	 * NOTE: If interrupt is not enabled
  	 * then WD-Reset doesn't get generated at all.
  	 */
  	writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN)
  		| (1 << SIRFSOC_TIMER_WDT_INDEX),
  		wdt_base + SIRFSOC_TIMER_INT_EN);
  	writel(1, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN);
  
  	return 0;
  }
  
  static int sirfsoc_wdt_disable(struct watchdog_device *wdd)
  {
b99c87740   Ben Dooks   watchdog: sirf: f...
95
  	void __iomem *wdt_base = sirfsoc_wdt_base(wdd);
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  
  	writel(0, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN);
  	writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN)
  		& (~(1 << SIRFSOC_TIMER_WDT_INDEX)),
  		wdt_base + SIRFSOC_TIMER_INT_EN);
  
  	return 0;
  }
  
  static int sirfsoc_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
  {
  	wdd->timeout = to;
  	sirfsoc_wdt_updatetimeout(wdd);
  
  	return 0;
  }
  
  #define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
  
  static const struct watchdog_info sirfsoc_wdt_ident = {
  	.options          =     OPTIONS,
  	.firmware_version =	0,
  	.identity         =	"SiRFSOC Watchdog",
  };
b893e344b   Bhumika Goyal   watchdog: constif...
120
  static const struct watchdog_ops sirfsoc_wdt_ops = {
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
  	.owner = THIS_MODULE,
  	.start = sirfsoc_wdt_enable,
  	.stop = sirfsoc_wdt_disable,
  	.get_timeleft = sirfsoc_wdt_gettimeleft,
  	.ping = sirfsoc_wdt_updatetimeout,
  	.set_timeout = sirfsoc_wdt_settimeout,
  };
  
  static struct watchdog_device sirfsoc_wdd = {
  	.info = &sirfsoc_wdt_ident,
  	.ops = &sirfsoc_wdt_ops,
  	.timeout = SIRFSOC_WDT_DEFAULT_TIMEOUT,
  	.min_timeout = SIRFSOC_WDT_MIN_TIMEOUT,
  	.max_timeout = SIRFSOC_WDT_MAX_TIMEOUT,
  };
  
  static int sirfsoc_wdt_probe(struct platform_device *pdev)
  {
72dbc27c8   Guenter Roeck   watchdog: sirfsoc...
139
  	struct device *dev = &pdev->dev;
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
140
141
  	int ret;
  	void __iomem *base;
0f0a6a285   Guenter Roeck   watchdog: Convert...
142
  	base = devm_platform_ioremap_resource(pdev, 0);
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
143
144
  	if (IS_ERR(base))
  		return PTR_ERR(base);
b99c87740   Ben Dooks   watchdog: sirf: f...
145
  	watchdog_set_drvdata(&sirfsoc_wdd, (__force void *)base);
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
146

72dbc27c8   Guenter Roeck   watchdog: sirfsoc...
147
  	watchdog_init_timeout(&sirfsoc_wdd, timeout, dev);
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
148
  	watchdog_set_nowayout(&sirfsoc_wdd, nowayout);
72dbc27c8   Guenter Roeck   watchdog: sirfsoc...
149
  	sirfsoc_wdd.parent = dev;
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
150

72dbc27c8   Guenter Roeck   watchdog: sirfsoc...
151
152
153
  	watchdog_stop_on_reboot(&sirfsoc_wdd);
  	watchdog_stop_on_unregister(&sirfsoc_wdd);
  	ret = devm_watchdog_register_device(dev, &sirfsoc_wdd);
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
154
155
156
157
158
159
160
  	if (ret)
  		return ret;
  
  	platform_set_drvdata(pdev, &sirfsoc_wdd);
  
  	return 0;
  }
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
  #ifdef	CONFIG_PM_SLEEP
  static int sirfsoc_wdt_suspend(struct device *dev)
  {
  	return 0;
  }
  
  static int sirfsoc_wdt_resume(struct device *dev)
  {
  	struct watchdog_device *wdd = dev_get_drvdata(dev);
  
  	/*
  	 * NOTE: Since timer controller registers settings are saved
  	 * and restored back by the timer-prima2.c, so we need not
  	 * update WD settings except refreshing timeout.
  	 */
  	sirfsoc_wdt_updatetimeout(wdd);
  
  	return 0;
  }
  #endif
  
  static SIMPLE_DEV_PM_OPS(sirfsoc_wdt_pm_ops,
  		sirfsoc_wdt_suspend, sirfsoc_wdt_resume);
  
  static const struct of_device_id sirfsoc_wdt_of_match[] = {
  	{ .compatible = "sirf,prima2-tick"},
  	{},
  };
  MODULE_DEVICE_TABLE(of, sirfsoc_wdt_of_match);
  
  static struct platform_driver sirfsoc_wdt_driver = {
  	.driver = {
  		.name = "sirfsoc-wdt",
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
194
  		.pm = &sirfsoc_wdt_pm_ops,
15edd9eed   Sachin Kamat   watchdog: sirf: R...
195
  		.of_match_table	= sirfsoc_wdt_of_match,
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
196
197
  	},
  	.probe = sirfsoc_wdt_probe,
f0fcbdbf2   Xianglong Du   watchdog: sirf: a...
198
199
200
201
202
203
204
  };
  module_platform_driver(sirfsoc_wdt_driver);
  
  MODULE_DESCRIPTION("SiRF SoC watchdog driver");
  MODULE_AUTHOR("Xianglong Du <Xianglong.Du@csr.com>");
  MODULE_LICENSE("GPL v2");
  MODULE_ALIAS("platform:sirfsoc-wdt");