aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Mayer <markus.mayer@linaro.org>2013-11-22 17:56:03 -0500
committerWim Van Sebroeck <wim@iguana.be>2014-01-28 15:13:50 -0500
commit6adb730dc2085c16c52a2f991cc1661e4a7fd6d5 (patch)
tree629e9b34c2d55eb452eeedcbe2a733c8272943e1
parent2c34d59916bd82efe6544f39ec162e8c9236009d (diff)
watchdog: bcm281xx: Watchdog Driver
This commit adds support for the watchdog timer used on the BCM281xx family of SoCs. Signed-off-by: Markus Mayer <markus.mayer@linaro.org> Reviewed-by: Matt Porter <matt.porter@linaro.org> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
-rw-r--r--arch/arm/configs/bcm_defconfig2
-rw-r--r--drivers/watchdog/Kconfig12
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/bcm_kona_wdt.c268
4 files changed, 283 insertions, 0 deletions
diff --git a/arch/arm/configs/bcm_defconfig b/arch/arm/configs/bcm_defconfig
index 2c38fdf1951d..fc3afd887a26 100644
--- a/arch/arm/configs/bcm_defconfig
+++ b/arch/arm/configs/bcm_defconfig
@@ -126,3 +126,5 @@ CONFIG_CRC7=y
126CONFIG_XZ_DEC=y 126CONFIG_XZ_DEC=y
127CONFIG_AVERAGE=y 127CONFIG_AVERAGE=y
128CONFIG_PINCTRL_CAPRI=y 128CONFIG_PINCTRL_CAPRI=y
129CONFIG_WATCHDOG=y
130CONFIG_BCM_KONA_WDT=y
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 5be6e919f785..042e4521a8b8 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1139,6 +1139,18 @@ config BCM2835_WDT
1139 To compile this driver as a loadable module, choose M here. 1139 To compile this driver as a loadable module, choose M here.
1140 The module will be called bcm2835_wdt. 1140 The module will be called bcm2835_wdt.
1141 1141
1142config BCM_KONA_WDT
1143 tristate "BCM Kona Watchdog"
1144 depends on ARCH_BCM
1145 select WATCHDOG_CORE
1146 help
1147 Support for the watchdog timer on the following Broadcom BCM281xx
1148 family, which includes BCM11130, BCM11140, BCM11351, BCM28145 and
1149 BCM28155 variants.
1150
1151 Say 'Y' or 'M' here to enable the driver. The module will be called
1152 bcm_kona_wdt.
1153
1142config LANTIQ_WDT 1154config LANTIQ_WDT
1143 tristate "Lantiq SoC watchdog" 1155 tristate "Lantiq SoC watchdog"
1144 depends on LANTIQ 1156 depends on LANTIQ
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 91bd95a64baf..af2251682214 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
57obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o 57obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
58obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o 58obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
59obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o 59obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
60obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
60 61
61# AVR32 Architecture 62# AVR32 Architecture
62obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o 63obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
diff --git a/drivers/watchdog/bcm_kona_wdt.c b/drivers/watchdog/bcm_kona_wdt.c
new file mode 100644
index 000000000000..7e41a83eb45e
--- /dev/null
+++ b/drivers/watchdog/bcm_kona_wdt.c
@@ -0,0 +1,268 @@
1/*
2 * Copyright (C) 2013 Broadcom Corporation
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation version 2.
7 *
8 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
9 * kind, whether express or implied; without even the implied warranty
10 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/delay.h>
15#include <linux/err.h>
16#include <linux/io.h>
17#include <linux/module.h>
18#include <linux/of_address.h>
19#include <linux/platform_device.h>
20#include <linux/watchdog.h>
21
22#define SECWDOG_CTRL_REG 0x00000000
23#define SECWDOG_COUNT_REG 0x00000004
24
25#define SECWDOG_RESERVED_MASK 0x1dffffff
26#define SECWDOG_WD_LOAD_FLAG 0x10000000
27#define SECWDOG_EN_MASK 0x08000000
28#define SECWDOG_SRSTEN_MASK 0x04000000
29#define SECWDOG_RES_MASK 0x00f00000
30#define SECWDOG_COUNT_MASK 0x000fffff
31
32#define SECWDOG_MAX_COUNT SECWDOG_COUNT_MASK
33#define SECWDOG_CLKS_SHIFT 20
34#define SECWDOG_MAX_RES 15
35#define SECWDOG_DEFAULT_RESOLUTION 4
36#define SECWDOG_MAX_TRY 1000
37
38#define SECS_TO_TICKS(x, w) ((x) << (w)->resolution)
39#define TICKS_TO_SECS(x, w) ((x) >> (w)->resolution)
40
41#define BCM_KONA_WDT_NAME "bcm_kona_wdt"
42
43struct bcm_kona_wdt {
44 void __iomem *base;
45 /*
46 * One watchdog tick is 1/(2^resolution) seconds. Resolution can take
47 * the values 0-15, meaning one tick can be 1s to 30.52us. Our default
48 * resolution of 4 means one tick is 62.5ms.
49 *
50 * The watchdog counter is 20 bits. Depending on resolution, the maximum
51 * counter value of 0xfffff expires after about 12 days (resolution 0)
52 * down to only 32s (resolution 15). The default resolution of 4 gives
53 * us a maximum of about 18 hours and 12 minutes before the watchdog
54 * times out.
55 */
56 int resolution;
57 spinlock_t lock;
58};
59
60static int secure_register_read(void __iomem *addr)
61{
62 uint32_t val;
63 unsigned count = 0;
64
65 /*
66 * If the WD_LOAD_FLAG is set, the watchdog counter field is being
67 * updated in hardware. Once the WD timer is updated in hardware, it
68 * gets cleared.
69 */
70 do {
71 if (unlikely(count > 1))
72 udelay(5);
73 val = readl_relaxed(addr);
74 count++;
75 } while ((val & SECWDOG_WD_LOAD_FLAG) && count < SECWDOG_MAX_TRY);
76
77 /* This is the only place we return a negative value. */
78 if (val & SECWDOG_WD_LOAD_FLAG)
79 return -ETIMEDOUT;
80
81 /* We always mask out reserved bits. */
82 val &= SECWDOG_RESERVED_MASK;
83
84 return val;
85}
86
87static int bcm_kona_wdt_ctrl_reg_modify(struct bcm_kona_wdt *wdt,
88 unsigned mask, unsigned newval)
89{
90 int val;
91 unsigned long flags;
92 int ret = 0;
93
94 spin_lock_irqsave(&wdt->lock, flags);
95
96 val = secure_register_read(wdt->base + SECWDOG_CTRL_REG);
97 if (val < 0) {
98 ret = val;
99 } else {
100 val &= ~mask;
101 val |= newval;
102 writel_relaxed(val, wdt->base + SECWDOG_CTRL_REG);
103 }
104
105 spin_unlock_irqrestore(&wdt->lock, flags);
106
107 return ret;
108}
109
110static int bcm_kona_wdt_set_resolution_reg(struct bcm_kona_wdt *wdt)
111{
112 if (wdt->resolution > SECWDOG_MAX_RES)
113 return -EINVAL;
114
115 return bcm_kona_wdt_ctrl_reg_modify(wdt, SECWDOG_RES_MASK,
116 wdt->resolution << SECWDOG_CLKS_SHIFT);
117}
118
119static int bcm_kona_wdt_set_timeout_reg(struct watchdog_device *wdog,
120 unsigned watchdog_flags)
121{
122 struct bcm_kona_wdt *wdt = watchdog_get_drvdata(wdog);
123
124 return bcm_kona_wdt_ctrl_reg_modify(wdt, SECWDOG_COUNT_MASK,
125 SECS_TO_TICKS(wdog->timeout, wdt) |
126 watchdog_flags);
127}
128
129static int bcm_kona_wdt_set_timeout(struct watchdog_device *wdog,
130 unsigned int t)
131{
132 wdog->timeout = t;
133 return 0;
134}
135
136static unsigned int bcm_kona_wdt_get_timeleft(struct watchdog_device *wdog)
137{
138 struct bcm_kona_wdt *wdt = watchdog_get_drvdata(wdog);
139 int val;
140 unsigned long flags;
141
142 spin_lock_irqsave(&wdt->lock, flags);
143 val = secure_register_read(wdt->base + SECWDOG_COUNT_REG);
144 spin_unlock_irqrestore(&wdt->lock, flags);
145
146 if (val < 0)
147 return val;
148
149 return TICKS_TO_SECS(val & SECWDOG_COUNT_MASK, wdt);
150}
151
152static int bcm_kona_wdt_start(struct watchdog_device *wdog)
153{
154 return bcm_kona_wdt_set_timeout_reg(wdog,
155 SECWDOG_EN_MASK | SECWDOG_SRSTEN_MASK);
156}
157
158static int bcm_kona_wdt_stop(struct watchdog_device *wdog)
159{
160 struct bcm_kona_wdt *wdt = watchdog_get_drvdata(wdog);
161
162 return bcm_kona_wdt_ctrl_reg_modify(wdt, SECWDOG_EN_MASK |
163 SECWDOG_SRSTEN_MASK, 0);
164}
165
166static struct watchdog_ops bcm_kona_wdt_ops = {
167 .owner = THIS_MODULE,
168 .start = bcm_kona_wdt_start,
169 .stop = bcm_kona_wdt_stop,
170 .set_timeout = bcm_kona_wdt_set_timeout,
171 .get_timeleft = bcm_kona_wdt_get_timeleft,
172};
173
174static struct watchdog_info bcm_kona_wdt_info = {
175 .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
176 WDIOF_KEEPALIVEPING,
177 .identity = "Broadcom Kona Watchdog Timer",
178};
179
180static struct watchdog_device bcm_kona_wdt_wdd = {
181 .info = &bcm_kona_wdt_info,
182 .ops = &bcm_kona_wdt_ops,
183 .min_timeout = 1,
184 .max_timeout = SECWDOG_MAX_COUNT >> SECWDOG_DEFAULT_RESOLUTION,
185 .timeout = SECWDOG_MAX_COUNT >> SECWDOG_DEFAULT_RESOLUTION,
186};
187
188static void bcm_kona_wdt_shutdown(struct platform_device *pdev)
189{
190 bcm_kona_wdt_stop(&bcm_kona_wdt_wdd);
191}
192
193static int bcm_kona_wdt_probe(struct platform_device *pdev)
194{
195 struct device *dev = &pdev->dev;
196 struct bcm_kona_wdt *wdt;
197 struct resource *res;
198 int ret;
199
200 wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
201 if (!wdt)
202 return -ENOMEM;
203
204 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
205 wdt->base = devm_ioremap_resource(dev, res);
206 if (IS_ERR(wdt->base))
207 return -ENODEV;
208
209 wdt->resolution = SECWDOG_DEFAULT_RESOLUTION;
210 ret = bcm_kona_wdt_set_resolution_reg(wdt);
211 if (ret) {
212 dev_err(dev, "Failed to set resolution (error: %d)", ret);
213 return ret;
214 }
215
216 spin_lock_init(&wdt->lock);
217 platform_set_drvdata(pdev, wdt);
218 watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt);
219
220 ret = bcm_kona_wdt_set_timeout_reg(&bcm_kona_wdt_wdd, 0);
221 if (ret) {
222 dev_err(dev, "Failed set watchdog timeout");
223 return ret;
224 }
225
226 ret = watchdog_register_device(&bcm_kona_wdt_wdd);
227 if (ret) {
228 dev_err(dev, "Failed to register watchdog device");
229 return ret;
230 }
231
232 dev_dbg(dev, "Broadcom Kona Watchdog Timer");
233
234 return 0;
235}
236
237static int bcm_kona_wdt_remove(struct platform_device *pdev)
238{
239 bcm_kona_wdt_shutdown(pdev);
240 watchdog_unregister_device(&bcm_kona_wdt_wdd);
241 dev_dbg(&pdev->dev, "Watchdog driver disabled");
242
243 return 0;
244}
245
246static const struct of_device_id bcm_kona_wdt_of_match[] = {
247 { .compatible = "brcm,kona-wdt", },
248 {},
249};
250MODULE_DEVICE_TABLE(of, bcm_kona_wdt_of_match);
251
252static struct platform_driver bcm_kona_wdt_driver = {
253 .driver = {
254 .name = BCM_KONA_WDT_NAME,
255 .owner = THIS_MODULE,
256 .of_match_table = bcm_kona_wdt_of_match,
257 },
258 .probe = bcm_kona_wdt_probe,
259 .remove = bcm_kona_wdt_remove,
260 .shutdown = bcm_kona_wdt_shutdown,
261};
262
263module_platform_driver(bcm_kona_wdt_driver);
264
265MODULE_ALIAS("platform:" BCM_KONA_WDT_NAME);
266MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
267MODULE_DESCRIPTION("Broadcom Kona Watchdog Driver");
268MODULE_LICENSE("GPL v2");