Blame view

drivers/watchdog/atlas7_wdt.c 5.38 KB
f50a7f3d9   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
b466ee895   Guo Zeng   watchdog: atlas7:...
2
3
4
5
  /*
   * Watchdog driver for CSR Atlas7
   *
   * Copyright (c) 2015 Cambridge Silicon Radio Limited, a CSR plc group company.
b466ee895   Guo Zeng   watchdog: atlas7:...
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
64
65
66
67
68
69
70
71
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
   */
  
  #include <linux/clk.h>
  #include <linux/io.h>
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/of.h>
  #include <linux/platform_device.h>
  #include <linux/watchdog.h>
  
  #define ATLAS7_TIMER_WDT_INDEX		5
  #define ATLAS7_WDT_DEFAULT_TIMEOUT	20
  
  #define ATLAS7_WDT_CNT_CTRL	(0 + 4 * ATLAS7_TIMER_WDT_INDEX)
  #define ATLAS7_WDT_CNT_MATCH	(0x18 + 4 * ATLAS7_TIMER_WDT_INDEX)
  #define ATLAS7_WDT_CNT		(0x48 +  4 * ATLAS7_TIMER_WDT_INDEX)
  #define ATLAS7_WDT_CNT_EN	(BIT(0) | BIT(1))
  #define ATLAS7_WDT_EN		0x64
  
  static unsigned int timeout = ATLAS7_WDT_DEFAULT_TIMEOUT;
  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) ")");
  
  struct atlas7_wdog {
  	struct device *dev;
  	void __iomem *base;
  	unsigned long tick_rate;
  	struct clk *clk;
  };
  
  static unsigned int atlas7_wdt_gettimeleft(struct watchdog_device *wdd)
  {
  	struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd);
  	u32 counter, match, delta;
  
  	counter = readl(wdt->base + ATLAS7_WDT_CNT);
  	match = readl(wdt->base + ATLAS7_WDT_CNT_MATCH);
  	delta = match - counter;
  
  	return  delta / wdt->tick_rate;
  }
  
  static int atlas7_wdt_ping(struct watchdog_device *wdd)
  {
  	struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd);
  	u32 counter, match, delta;
  
  	counter = readl(wdt->base + ATLAS7_WDT_CNT);
  	delta = wdd->timeout * wdt->tick_rate;
  	match = counter + delta;
  
  	writel(match, wdt->base + ATLAS7_WDT_CNT_MATCH);
  
  	return 0;
  }
  
  static int atlas7_wdt_enable(struct watchdog_device *wdd)
  {
  	struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd);
  
  	atlas7_wdt_ping(wdd);
  
  	writel(readl(wdt->base + ATLAS7_WDT_CNT_CTRL) | ATLAS7_WDT_CNT_EN,
  	      wdt->base + ATLAS7_WDT_CNT_CTRL);
  	writel(1, wdt->base + ATLAS7_WDT_EN);
  
  	return 0;
  }
  
  static int atlas7_wdt_disable(struct watchdog_device *wdd)
  {
  	struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd);
  
  	writel(0, wdt->base + ATLAS7_WDT_EN);
  	writel(readl(wdt->base + ATLAS7_WDT_CNT_CTRL) & ~ATLAS7_WDT_CNT_EN,
  	      wdt->base + ATLAS7_WDT_CNT_CTRL);
  
  	return 0;
  }
  
  static int atlas7_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
  {
  	wdd->timeout = to;
  
  	return 0;
  }
  
  #define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
  
  static const struct watchdog_info atlas7_wdt_ident = {
  	.options = OPTIONS,
  	.firmware_version = 0,
  	.identity = "atlas7 Watchdog",
  };
b893e344b   Bhumika Goyal   watchdog: constif...
106
  static const struct watchdog_ops atlas7_wdt_ops = {
b466ee895   Guo Zeng   watchdog: atlas7:...
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
  	.owner = THIS_MODULE,
  	.start = atlas7_wdt_enable,
  	.stop = atlas7_wdt_disable,
  	.get_timeleft = atlas7_wdt_gettimeleft,
  	.ping = atlas7_wdt_ping,
  	.set_timeout = atlas7_wdt_settimeout,
  };
  
  static struct watchdog_device atlas7_wdd = {
  	.info = &atlas7_wdt_ident,
  	.ops = &atlas7_wdt_ops,
  	.timeout = ATLAS7_WDT_DEFAULT_TIMEOUT,
  };
  
  static const struct of_device_id atlas7_wdt_ids[] = {
  	{ .compatible = "sirf,atlas7-tick"},
  	{}
  };
f332ce5d8   Guenter Roeck   watchdog: atlas7_...
125
126
127
128
  static void atlas7_clk_disable_unprepare(void *data)
  {
  	clk_disable_unprepare(data);
  }
b466ee895   Guo Zeng   watchdog: atlas7:...
129
130
  static int atlas7_wdt_probe(struct platform_device *pdev)
  {
f332ce5d8   Guenter Roeck   watchdog: atlas7_...
131
  	struct device *dev = &pdev->dev;
b466ee895   Guo Zeng   watchdog: atlas7:...
132
  	struct atlas7_wdog *wdt;
b466ee895   Guo Zeng   watchdog: atlas7:...
133
134
  	struct clk *clk;
  	int ret;
f332ce5d8   Guenter Roeck   watchdog: atlas7_...
135
  	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
b466ee895   Guo Zeng   watchdog: atlas7:...
136
137
  	if (!wdt)
  		return -ENOMEM;
0f0a6a285   Guenter Roeck   watchdog: Convert...
138
  	wdt->base = devm_platform_ioremap_resource(pdev, 0);
b466ee895   Guo Zeng   watchdog: atlas7:...
139
140
  	if (IS_ERR(wdt->base))
  		return PTR_ERR(wdt->base);
f332ce5d8   Guenter Roeck   watchdog: atlas7_...
141
  	clk = devm_clk_get(dev, NULL);
b466ee895   Guo Zeng   watchdog: atlas7:...
142
143
144
145
  	if (IS_ERR(clk))
  		return PTR_ERR(clk);
  	ret = clk_prepare_enable(clk);
  	if (ret) {
f332ce5d8   Guenter Roeck   watchdog: atlas7_...
146
147
148
  		dev_err(dev, "clk enable failed
  ");
  		return ret;
b466ee895   Guo Zeng   watchdog: atlas7:...
149
  	}
f332ce5d8   Guenter Roeck   watchdog: atlas7_...
150
151
152
  	ret = devm_add_action_or_reset(dev, atlas7_clk_disable_unprepare, clk);
  	if (ret)
  		return ret;
b466ee895   Guo Zeng   watchdog: atlas7:...
153
154
155
156
157
  
  	/* disable watchdog hardware */
  	writel(0, wdt->base + ATLAS7_WDT_CNT_CTRL);
  
  	wdt->tick_rate = clk_get_rate(clk);
f332ce5d8   Guenter Roeck   watchdog: atlas7_...
158
159
  	if (!wdt->tick_rate)
  		return -EINVAL;
ccc8208d0   Wolfram Sang   watchdog: atlas7_...
160

b466ee895   Guo Zeng   watchdog: atlas7:...
161
162
163
  	wdt->clk = clk;
  	atlas7_wdd.min_timeout = 1;
  	atlas7_wdd.max_timeout = UINT_MAX / wdt->tick_rate;
f332ce5d8   Guenter Roeck   watchdog: atlas7_...
164
  	watchdog_init_timeout(&atlas7_wdd, 0, dev);
b466ee895   Guo Zeng   watchdog: atlas7:...
165
166
167
168
  	watchdog_set_nowayout(&atlas7_wdd, nowayout);
  
  	watchdog_set_drvdata(&atlas7_wdd, wdt);
  	platform_set_drvdata(pdev, &atlas7_wdd);
f332ce5d8   Guenter Roeck   watchdog: atlas7_...
169
170
171
  	watchdog_stop_on_reboot(&atlas7_wdd);
  	watchdog_stop_on_unregister(&atlas7_wdd);
  	return devm_watchdog_register_device(dev, &atlas7_wdd);
b466ee895   Guo Zeng   watchdog: atlas7:...
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
  }
  
  static int __maybe_unused atlas7_wdt_suspend(struct device *dev)
  {
  	/*
  	 * NOTE:timer controller registers settings are saved
  	 * and restored back by the timer-atlas7.c
  	 */
  	return 0;
  }
  
  static int __maybe_unused atlas7_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-atlas7.c, so we need not
  	 * update WD settings except refreshing timeout.
  	 */
  	atlas7_wdt_ping(wdd);
  
  	return 0;
  }
  
  static SIMPLE_DEV_PM_OPS(atlas7_wdt_pm_ops,
  		atlas7_wdt_suspend, atlas7_wdt_resume);
  
  MODULE_DEVICE_TABLE(of, atlas7_wdt_ids);
  
  static struct platform_driver atlas7_wdt_driver = {
  	.driver = {
  		.name = "atlas7-wdt",
  		.pm = &atlas7_wdt_pm_ops,
  		.of_match_table	= atlas7_wdt_ids,
  	},
  	.probe = atlas7_wdt_probe,
b466ee895   Guo Zeng   watchdog: atlas7:...
209
210
211
212
213
214
215
  };
  module_platform_driver(atlas7_wdt_driver);
  
  MODULE_DESCRIPTION("CSRatlas7 watchdog driver");
  MODULE_AUTHOR("Guo Zeng <Guo.Zeng@csr.com>");
  MODULE_LICENSE("GPL v2");
  MODULE_ALIAS("platform:atlas7-wdt");