Merge tag 'pci-v3.15-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaa...
[deliverable/linux.git] / drivers / watchdog / orion_wdt.c
CommitLineData
22ac9232 1/*
3b937a7d 2 * drivers/watchdog/orion_wdt.c
22ac9232 3 *
3b937a7d 4 * Watchdog driver for Orion/Kirkwood processors
22ac9232
SB
5 *
6 * Author: Sylver Bruneau <sylver.bruneau@googlemail.com>
7 *
8 * This file is licensed under the terms of the GNU General Public
9 * License version 2. This program is licensed "as is" without any
10 * warranty of any kind, whether express or implied.
11 */
12
27c766aa
JP
13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14
22ac9232
SB
15#include <linux/module.h>
16#include <linux/moduleparam.h>
17#include <linux/types.h>
18#include <linux/kernel.h>
9e058d4f 19#include <linux/platform_device.h>
22ac9232 20#include <linux/watchdog.h>
22ac9232 21#include <linux/io.h>
6d0f0dfd 22#include <linux/spinlock.h>
4f04be62 23#include <linux/clk.h>
0dd6e484 24#include <linux/err.h>
1e7bad0f 25#include <linux/of.h>
fdd8b079 26#include <mach/bridge-regs.h>
22ac9232
SB
27
28/*
29 * Watchdog timer block registers.
30 */
a855a7ce 31#define TIMER_CTRL 0x0000
0dd6e484 32#define WDT_EN 0x0010
a855a7ce 33#define WDT_VAL 0x0024
22ac9232 34
9e058d4f 35#define WDT_MAX_CYCLE_COUNT 0xffffffff
22ac9232
SB
36#define WDT_IN_USE 0
37#define WDT_OK_TO_CLOSE 1
38
fa142ff5
RK
39#define WDT_RESET_OUT_EN BIT(1)
40#define WDT_INT_REQ BIT(3)
41
86a1e189 42static bool nowayout = WATCHDOG_NOWAYOUT;
9e058d4f
TR
43static int heartbeat = -1; /* module parameter (seconds) */
44static unsigned int wdt_max_duration; /* (seconds) */
4f04be62 45static struct clk *clk;
9e058d4f 46static unsigned int wdt_tclk;
a855a7ce 47static void __iomem *wdt_reg;
1334f329 48static DEFINE_SPINLOCK(wdt_lock);
22ac9232 49
0dd6e484 50static int orion_wdt_ping(struct watchdog_device *wdt_dev)
df6707b2
TR
51{
52 spin_lock(&wdt_lock);
53
54 /* Reload watchdog duration */
0dd6e484 55 writel(wdt_tclk * wdt_dev->timeout, wdt_reg + WDT_VAL);
df6707b2
TR
56
57 spin_unlock(&wdt_lock);
0dd6e484 58 return 0;
df6707b2
TR
59}
60
0dd6e484 61static int orion_wdt_start(struct watchdog_device *wdt_dev)
22ac9232
SB
62{
63 u32 reg;
64
6d0f0dfd
WVS
65 spin_lock(&wdt_lock);
66
22ac9232 67 /* Set watchdog duration */
0dd6e484 68 writel(wdt_tclk * wdt_dev->timeout, wdt_reg + WDT_VAL);
22ac9232
SB
69
70 /* Clear watchdog timer interrupt */
6910ceb5 71 writel(~WDT_INT_REQ, BRIDGE_CAUSE);
22ac9232
SB
72
73 /* Enable watchdog timer */
a855a7ce 74 reg = readl(wdt_reg + TIMER_CTRL);
22ac9232 75 reg |= WDT_EN;
a855a7ce 76 writel(reg, wdt_reg + TIMER_CTRL);
22ac9232
SB
77
78 /* Enable reset on watchdog */
6462c616
TR
79 reg = readl(RSTOUTn_MASK);
80 reg |= WDT_RESET_OUT_EN;
81 writel(reg, RSTOUTn_MASK);
6d0f0dfd
WVS
82
83 spin_unlock(&wdt_lock);
0dd6e484 84 return 0;
22ac9232
SB
85}
86
0dd6e484 87static int orion_wdt_stop(struct watchdog_device *wdt_dev)
22ac9232
SB
88{
89 u32 reg;
90
6d0f0dfd
WVS
91 spin_lock(&wdt_lock);
92
22ac9232 93 /* Disable reset on watchdog */
6462c616
TR
94 reg = readl(RSTOUTn_MASK);
95 reg &= ~WDT_RESET_OUT_EN;
96 writel(reg, RSTOUTn_MASK);
22ac9232
SB
97
98 /* Disable watchdog timer */
a855a7ce 99 reg = readl(wdt_reg + TIMER_CTRL);
22ac9232 100 reg &= ~WDT_EN;
a855a7ce 101 writel(reg, wdt_reg + TIMER_CTRL);
6d0f0dfd
WVS
102
103 spin_unlock(&wdt_lock);
0dd6e484 104 return 0;
6d0f0dfd
WVS
105}
106
0dd6e484 107static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev)
6d0f0dfd 108{
0dd6e484
AL
109 unsigned int time_left;
110
6d0f0dfd 111 spin_lock(&wdt_lock);
0dd6e484 112 time_left = readl(wdt_reg + WDT_VAL) / wdt_tclk;
6d0f0dfd 113 spin_unlock(&wdt_lock);
22ac9232 114
0dd6e484 115 return time_left;
22ac9232
SB
116}
117
0dd6e484
AL
118static int orion_wdt_set_timeout(struct watchdog_device *wdt_dev,
119 unsigned int timeout)
22ac9232 120{
0dd6e484 121 wdt_dev->timeout = timeout;
df6707b2
TR
122 return 0;
123}
124
0dd6e484
AL
125static const struct watchdog_info orion_wdt_info = {
126 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
127 .identity = "Orion Watchdog",
22ac9232
SB
128};
129
0dd6e484
AL
130static const struct watchdog_ops orion_wdt_ops = {
131 .owner = THIS_MODULE,
132 .start = orion_wdt_start,
133 .stop = orion_wdt_stop,
134 .ping = orion_wdt_ping,
135 .set_timeout = orion_wdt_set_timeout,
136 .get_timeleft = orion_wdt_get_timeleft,
22ac9232
SB
137};
138
0dd6e484
AL
139static struct watchdog_device orion_wdt = {
140 .info = &orion_wdt_info,
141 .ops = &orion_wdt_ops,
c1fd5f64 142 .min_timeout = 1,
22ac9232
SB
143};
144
2d991a16 145static int orion_wdt_probe(struct platform_device *pdev)
22ac9232 146{
a855a7ce 147 struct resource *res;
22ac9232
SB
148 int ret;
149
0dd6e484 150 clk = devm_clk_get(&pdev->dev, NULL);
4f04be62 151 if (IS_ERR(clk)) {
0dd6e484 152 dev_err(&pdev->dev, "Orion Watchdog missing clock\n");
9e058d4f
TR
153 return -ENODEV;
154 }
4f04be62
AL
155 clk_prepare_enable(clk);
156 wdt_tclk = clk_get_rate(clk);
9e058d4f 157
a855a7ce 158 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
8c4c419c
JG
159 if (!res)
160 return -ENODEV;
0dd6e484
AL
161 wdt_reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
162 if (!wdt_reg)
163 return -ENOMEM;
9e058d4f
TR
164
165 wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk;
0dd6e484 166
c1fd5f64 167 orion_wdt.timeout = wdt_max_duration;
0dd6e484 168 orion_wdt.max_timeout = wdt_max_duration;
c1fd5f64 169 watchdog_init_timeout(&orion_wdt, heartbeat, &pdev->dev);
0dd6e484
AL
170
171 watchdog_set_nowayout(&orion_wdt, nowayout);
172 ret = watchdog_register_device(&orion_wdt);
173 if (ret) {
174 clk_disable_unprepare(clk);
9e058d4f 175 return ret;
0dd6e484 176 }
9e058d4f 177
27c766aa 178 pr_info("Initial timeout %d sec%s\n",
c1fd5f64 179 orion_wdt.timeout, nowayout ? ", nowayout" : "");
9e058d4f
TR
180 return 0;
181}
182
4b12b896 183static int orion_wdt_remove(struct platform_device *pdev)
9e058d4f 184{
0dd6e484 185 watchdog_unregister_device(&orion_wdt);
4f04be62 186 clk_disable_unprepare(clk);
0dd6e484 187 return 0;
22ac9232
SB
188}
189
3b937a7d 190static void orion_wdt_shutdown(struct platform_device *pdev)
df6707b2 191{
0dd6e484 192 orion_wdt_stop(&orion_wdt);
df6707b2
TR
193}
194
1d131368 195static const struct of_device_id orion_wdt_of_match_table[] = {
1e7bad0f
AL
196 { .compatible = "marvell,orion-wdt", },
197 {},
198};
199MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table);
200
3b937a7d
NP
201static struct platform_driver orion_wdt_driver = {
202 .probe = orion_wdt_probe,
82268714 203 .remove = orion_wdt_remove,
3b937a7d 204 .shutdown = orion_wdt_shutdown,
9e058d4f
TR
205 .driver = {
206 .owner = THIS_MODULE,
3b937a7d 207 .name = "orion_wdt",
85eee819 208 .of_match_table = orion_wdt_of_match_table,
9e058d4f
TR
209 },
210};
211
b8ec6118 212module_platform_driver(orion_wdt_driver);
22ac9232
SB
213
214MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>");
3b937a7d 215MODULE_DESCRIPTION("Orion Processor Watchdog");
22ac9232
SB
216
217module_param(heartbeat, int, 0);
df6707b2 218MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds");
22ac9232 219
86a1e189 220module_param(nowayout, bool, 0);
df6707b2
TR
221MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
222 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
22ac9232
SB
223
224MODULE_LICENSE("GPL");
f3ea733e 225MODULE_ALIAS("platform:orion_wdt");
This page took 0.405393 seconds and 5 git commands to generate.