Blame view
drivers/watchdog/mpc8xxx_wdt.c
6.82 KB
2874c5fd2 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
fabbfb9e8 [PATCH] powerpc: ... |
2 |
/* |
0d7b10140 [WATCHDOG] mpc8xx... |
3 |
* mpc8xxx_wdt.c - MPC8xx/MPC83xx/MPC86xx watchdog userspace interface |
fabbfb9e8 [PATCH] powerpc: ... |
4 5 |
* * Authors: Dave Updegraff <dave@cray.org> |
5f3b27569 watchdog: cleanup... |
6 7 8 |
* Kumar Gala <galak@kernel.crashing.org> * Attribution: from 83xx_wst: Florian Schirmer <jolt@tuxbox.org> * ..and from sc520_wdt |
500c919e3 [WATCHDOG] mpc83x... |
9 10 |
* Copyright (c) 2008 MontaVista Software, Inc. * Anton Vorontsov <avorontsov@ru.mvista.com> |
fabbfb9e8 [PATCH] powerpc: ... |
11 12 13 |
* * Note: it appears that you can only actually ENABLE or DISABLE the thing * once after POR. Once enabled, you cannot disable, and vice versa. |
fabbfb9e8 [PATCH] powerpc: ... |
14 |
*/ |
fabbfb9e8 [PATCH] powerpc: ... |
15 16 17 |
#include <linux/fs.h> #include <linux/init.h> #include <linux/kernel.h> |
5af507300 drivers: clean-up... |
18 |
#include <linux/of_address.h> |
ef8ab12ec [WATCHDOG] mpc83x... |
19 |
#include <linux/of_platform.h> |
fabbfb9e8 [PATCH] powerpc: ... |
20 21 |
#include <linux/module.h> #include <linux/watchdog.h> |
f26ef3dc6 [WATCHDOG 26/57] ... |
22 23 |
#include <linux/io.h> #include <linux/uaccess.h> |
ef8ab12ec [WATCHDOG] mpc83x... |
24 |
#include <sysdev/fsl_soc.h> |
fabbfb9e8 [PATCH] powerpc: ... |
25 |
|
19ce9490a watchdog: mpc8xxx... |
26 |
#define WATCHDOG_TIMEOUT 10 |
59ca1b0d1 [WATCHDOG] mpc8xx... |
27 |
struct mpc8xxx_wdt { |
fabbfb9e8 [PATCH] powerpc: ... |
28 29 30 |
__be32 res0; __be32 swcrr; /* System watchdog control register */ #define SWCRR_SWTC 0xFFFF0000 /* Software Watchdog Time Count. */ |
19ce9490a watchdog: mpc8xxx... |
31 |
#define SWCRR_SWF 0x00000008 /* Software Watchdog Freeze (mpc8xx). */ |
fabbfb9e8 [PATCH] powerpc: ... |
32 33 34 35 36 37 38 39 |
#define SWCRR_SWEN 0x00000004 /* Watchdog Enable bit. */ #define SWCRR_SWRI 0x00000002 /* Software Watchdog Reset/Interrupt Select bit.*/ #define SWCRR_SWPR 0x00000001 /* Software Watchdog Counter Prescale bit. */ __be32 swcnr; /* System watchdog count register */ u8 res1[2]; __be16 swsrr; /* System watchdog service register */ u8 res2[0xF0]; }; |
59ca1b0d1 [WATCHDOG] mpc8xx... |
40 |
struct mpc8xxx_wdt_type { |
500c919e3 [WATCHDOG] mpc83x... |
41 42 |
int prescaler; bool hw_enabled; |
38e48b718 watchdog: mpc8xxx... |
43 |
u32 rsr_mask; |
500c919e3 [WATCHDOG] mpc83x... |
44 |
}; |
7997ebad4 watchdog: mpc8xxx... |
45 46 47 |
struct mpc8xxx_wdt_ddata { struct mpc8xxx_wdt __iomem *base; struct watchdog_device wdd; |
7997ebad4 watchdog: mpc8xxx... |
48 |
spinlock_t lock; |
19ce9490a watchdog: mpc8xxx... |
49 |
u16 swtc; |
7997ebad4 watchdog: mpc8xxx... |
50 |
}; |
fabbfb9e8 [PATCH] powerpc: ... |
51 |
|
19ce9490a watchdog: mpc8xxx... |
52 |
static u16 timeout; |
fabbfb9e8 [PATCH] powerpc: ... |
53 |
module_param(timeout, ushort, 0); |
f26ef3dc6 [WATCHDOG 26/57] ... |
54 |
MODULE_PARM_DESC(timeout, |
19ce9490a watchdog: mpc8xxx... |
55 56 |
"Watchdog timeout in seconds. (1<timeout<65535, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); |
fabbfb9e8 [PATCH] powerpc: ... |
57 |
|
90ab5ee94 module_param: mak... |
58 |
static bool reset = 1; |
fabbfb9e8 [PATCH] powerpc: ... |
59 |
module_param(reset, bool, 0); |
f26ef3dc6 [WATCHDOG 26/57] ... |
60 61 |
MODULE_PARM_DESC(reset, "Watchdog Interrupt/Reset Mode. 0 = interrupt, 1 = reset"); |
fabbfb9e8 [PATCH] powerpc: ... |
62 |
|
86a1e1896 watchdog: nowayou... |
63 64 |
static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0); |
500c919e3 [WATCHDOG] mpc83x... |
65 66 |
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
7997ebad4 watchdog: mpc8xxx... |
67 |
static void mpc8xxx_wdt_keepalive(struct mpc8xxx_wdt_ddata *ddata) |
fabbfb9e8 [PATCH] powerpc: ... |
68 69 |
{ /* Ping the WDT */ |
7997ebad4 watchdog: mpc8xxx... |
70 71 72 73 |
spin_lock(&ddata->lock); out_be16(&ddata->base->swsrr, 0x556c); out_be16(&ddata->base->swsrr, 0xaa39); spin_unlock(&ddata->lock); |
fabbfb9e8 [PATCH] powerpc: ... |
74 |
} |
d5cfaf0a8 watchdog: mpc8xxx... |
75 |
static int mpc8xxx_wdt_start(struct watchdog_device *w) |
fabbfb9e8 [PATCH] powerpc: ... |
76 |
{ |
7997ebad4 watchdog: mpc8xxx... |
77 78 |
struct mpc8xxx_wdt_ddata *ddata = container_of(w, struct mpc8xxx_wdt_ddata, wdd); |
19ce9490a watchdog: mpc8xxx... |
79 |
u32 tmp = in_be32(&ddata->base->swcrr); |
fabbfb9e8 [PATCH] powerpc: ... |
80 81 |
/* Good, fire up the show */ |
19ce9490a watchdog: mpc8xxx... |
82 83 |
tmp &= ~(SWCRR_SWTC | SWCRR_SWF | SWCRR_SWEN | SWCRR_SWRI | SWCRR_SWPR); tmp |= SWCRR_SWEN | SWCRR_SWPR | (ddata->swtc << 16); |
fabbfb9e8 [PATCH] powerpc: ... |
84 85 |
if (reset) tmp |= SWCRR_SWRI; |
7997ebad4 watchdog: mpc8xxx... |
86 |
out_be32(&ddata->base->swcrr, tmp); |
fabbfb9e8 [PATCH] powerpc: ... |
87 |
|
19ce9490a watchdog: mpc8xxx... |
88 89 90 91 92 93 |
tmp = in_be32(&ddata->base->swcrr); if (!(tmp & SWCRR_SWEN)) return -EOPNOTSUPP; ddata->swtc = tmp >> 16; set_bit(WDOG_HW_RUNNING, &ddata->wdd.status); |
500c919e3 [WATCHDOG] mpc83x... |
94 |
|
d5cfaf0a8 watchdog: mpc8xxx... |
95 |
return 0; |
fabbfb9e8 [PATCH] powerpc: ... |
96 |
} |
d5cfaf0a8 watchdog: mpc8xxx... |
97 |
static int mpc8xxx_wdt_ping(struct watchdog_device *w) |
fabbfb9e8 [PATCH] powerpc: ... |
98 |
{ |
7997ebad4 watchdog: mpc8xxx... |
99 100 101 102 |
struct mpc8xxx_wdt_ddata *ddata = container_of(w, struct mpc8xxx_wdt_ddata, wdd); mpc8xxx_wdt_keepalive(ddata); |
fabbfb9e8 [PATCH] powerpc: ... |
103 104 |
return 0; } |
d5cfaf0a8 watchdog: mpc8xxx... |
105 |
static struct watchdog_info mpc8xxx_wdt_info = { |
19ce9490a watchdog: mpc8xxx... |
106 |
.options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT, |
d5cfaf0a8 watchdog: mpc8xxx... |
107 108 |
.firmware_version = 1, .identity = "MPC8xxx", |
fabbfb9e8 [PATCH] powerpc: ... |
109 |
}; |
d5cfaf0a8 watchdog: mpc8xxx... |
110 111 112 113 |
static struct watchdog_ops mpc8xxx_wdt_ops = { .owner = THIS_MODULE, .start = mpc8xxx_wdt_start, .ping = mpc8xxx_wdt_ping, |
d5cfaf0a8 watchdog: mpc8xxx... |
114 |
}; |
2d991a164 watchdog: remove ... |
115 |
static int mpc8xxx_wdt_probe(struct platform_device *ofdev) |
fabbfb9e8 [PATCH] powerpc: ... |
116 |
{ |
fabbfb9e8 [PATCH] powerpc: ... |
117 |
int ret; |
de5f71222 watchdog: mpc8xxx... |
118 |
struct resource *res; |
639397e48 watchdog/mpc8xxx:... |
119 |
const struct mpc8xxx_wdt_type *wdt_type; |
7997ebad4 watchdog: mpc8xxx... |
120 |
struct mpc8xxx_wdt_ddata *ddata; |
ef8ab12ec [WATCHDOG] mpc83x... |
121 |
u32 freq = fsl_get_sys_freq(); |
500c919e3 [WATCHDOG] mpc83x... |
122 |
bool enabled; |
79b10e09b watchdog: mpc8xxx... |
123 |
struct device *dev = &ofdev->dev; |
fabbfb9e8 [PATCH] powerpc: ... |
124 |
|
79b10e09b watchdog: mpc8xxx... |
125 |
wdt_type = of_device_get_match_data(dev); |
f0ded83b9 watchdog: mpc8xxx... |
126 |
if (!wdt_type) |
1c48a5c93 dt: Eliminate of_... |
127 |
return -EINVAL; |
1c48a5c93 dt: Eliminate of_... |
128 |
|
ef8ab12ec [WATCHDOG] mpc83x... |
129 130 |
if (!freq || freq == -1) return -EINVAL; |
fabbfb9e8 [PATCH] powerpc: ... |
131 |
|
79b10e09b watchdog: mpc8xxx... |
132 |
ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); |
7997ebad4 watchdog: mpc8xxx... |
133 134 |
if (!ddata) return -ENOMEM; |
0f0a6a285 watchdog: Convert... |
135 |
ddata->base = devm_platform_ioremap_resource(ofdev, 0); |
7997ebad4 watchdog: mpc8xxx... |
136 137 |
if (IS_ERR(ddata->base)) return PTR_ERR(ddata->base); |
fabbfb9e8 [PATCH] powerpc: ... |
138 |
|
7997ebad4 watchdog: mpc8xxx... |
139 |
enabled = in_be32(&ddata->base->swcrr) & SWCRR_SWEN; |
500c919e3 [WATCHDOG] mpc83x... |
140 |
if (!enabled && wdt_type->hw_enabled) { |
79b10e09b watchdog: mpc8xxx... |
141 142 |
dev_info(dev, "could not be enabled in software "); |
72cd501e6 watchdog: mpc8xxx... |
143 |
return -ENODEV; |
500c919e3 [WATCHDOG] mpc83x... |
144 |
} |
38e48b718 watchdog: mpc8xxx... |
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
res = platform_get_resource(ofdev, IORESOURCE_MEM, 1); if (res) { bool status; u32 __iomem *rsr = ioremap(res->start, resource_size(res)); if (!rsr) return -ENOMEM; status = in_be32(rsr) & wdt_type->rsr_mask; ddata->wdd.bootstatus = status ? WDIOF_CARDRESET : 0; /* clear reset status bits related to watchdog timer */ out_be32(rsr, wdt_type->rsr_mask); iounmap(rsr); dev_info(dev, "Last boot was %scaused by watchdog ", status ? "" : "not "); } |
7997ebad4 watchdog: mpc8xxx... |
163 |
spin_lock_init(&ddata->lock); |
7997ebad4 watchdog: mpc8xxx... |
164 165 166 |
ddata->wdd.info = &mpc8xxx_wdt_info, ddata->wdd.ops = &mpc8xxx_wdt_ops, |
19ce9490a watchdog: mpc8xxx... |
167 |
ddata->wdd.timeout = WATCHDOG_TIMEOUT; |
79b10e09b watchdog: mpc8xxx... |
168 |
watchdog_init_timeout(&ddata->wdd, timeout, dev); |
50ffb53ef watchdog: mpc8xxx... |
169 |
|
7997ebad4 watchdog: mpc8xxx... |
170 |
watchdog_set_nowayout(&ddata->wdd, nowayout); |
50ffb53ef watchdog: mpc8xxx... |
171 |
|
19ce9490a watchdog: mpc8xxx... |
172 173 |
ddata->swtc = min(ddata->wdd.timeout * freq / wdt_type->prescaler, 0xffffU); |
500c919e3 [WATCHDOG] mpc83x... |
174 175 176 |
/* * If the watchdog was previously enabled or we're running on |
59ca1b0d1 [WATCHDOG] mpc8xx... |
177 |
* MPC8xxx, we should ping the wdt from the kernel until the |
500c919e3 [WATCHDOG] mpc83x... |
178 179 180 |
* userspace handles it. */ if (enabled) |
19ce9490a watchdog: mpc8xxx... |
181 182 183 184 185 186 187 |
mpc8xxx_wdt_start(&ddata->wdd); ddata->wdd.max_hw_heartbeat_ms = (ddata->swtc * wdt_type->prescaler) / (freq / 1000); ddata->wdd.min_timeout = ddata->wdd.max_hw_heartbeat_ms / 1000; if (ddata->wdd.timeout < ddata->wdd.min_timeout) ddata->wdd.timeout = ddata->wdd.min_timeout; |
81df6db68 watchdog: mpc8xxx... |
188 |
ret = devm_watchdog_register_device(dev, &ddata->wdd); |
a23902732 watchdog: mpc8xxx... |
189 |
if (ret) |
19ce9490a watchdog: mpc8xxx... |
190 |
return ret; |
19ce9490a watchdog: mpc8xxx... |
191 |
|
79b10e09b watchdog: mpc8xxx... |
192 193 194 195 |
dev_info(dev, "WDT driver for MPC8xxx initialized. mode:%s timeout=%d sec ", reset ? "reset" : "interrupt", ddata->wdd.timeout); |
7997ebad4 watchdog: mpc8xxx... |
196 197 |
platform_set_drvdata(ofdev, ddata); |
fabbfb9e8 [PATCH] powerpc: ... |
198 |
return 0; |
fabbfb9e8 [PATCH] powerpc: ... |
199 |
} |
59ca1b0d1 [WATCHDOG] mpc8xx... |
200 |
static const struct of_device_id mpc8xxx_wdt_match[] = { |
ef8ab12ec [WATCHDOG] mpc83x... |
201 202 |
{ .compatible = "mpc83xx_wdt", |
59ca1b0d1 [WATCHDOG] mpc8xx... |
203 |
.data = &(struct mpc8xxx_wdt_type) { |
500c919e3 [WATCHDOG] mpc83x... |
204 |
.prescaler = 0x10000, |
38e48b718 watchdog: mpc8xxx... |
205 |
.rsr_mask = BIT(3), /* RSR Bit SWRS */ |
500c919e3 [WATCHDOG] mpc83x... |
206 207 208 209 |
}, }, { .compatible = "fsl,mpc8610-wdt", |
59ca1b0d1 [WATCHDOG] mpc8xx... |
210 |
.data = &(struct mpc8xxx_wdt_type) { |
500c919e3 [WATCHDOG] mpc83x... |
211 212 |
.prescaler = 0x10000, .hw_enabled = true, |
38e48b718 watchdog: mpc8xxx... |
213 |
.rsr_mask = BIT(20), /* RSTRSCR Bit WDT_RR */ |
500c919e3 [WATCHDOG] mpc83x... |
214 |
}, |
ef8ab12ec [WATCHDOG] mpc83x... |
215 |
}, |
0d7b10140 [WATCHDOG] mpc8xx... |
216 217 218 219 |
{ .compatible = "fsl,mpc823-wdt", .data = &(struct mpc8xxx_wdt_type) { .prescaler = 0x800, |
4af897fa9 watchdog: mpc8xxx... |
220 |
.hw_enabled = true, |
38e48b718 watchdog: mpc8xxx... |
221 |
.rsr_mask = BIT(28), /* RSR Bit SWRS */ |
0d7b10140 [WATCHDOG] mpc8xx... |
222 223 |
}, }, |
ef8ab12ec [WATCHDOG] mpc83x... |
224 225 |
{}, }; |
59ca1b0d1 [WATCHDOG] mpc8xx... |
226 |
MODULE_DEVICE_TABLE(of, mpc8xxx_wdt_match); |
ef8ab12ec [WATCHDOG] mpc83x... |
227 |
|
1c48a5c93 dt: Eliminate of_... |
228 |
static struct platform_driver mpc8xxx_wdt_driver = { |
59ca1b0d1 [WATCHDOG] mpc8xx... |
229 |
.probe = mpc8xxx_wdt_probe, |
4018294b5 of: Remove duplic... |
230 231 |
.driver = { .name = "mpc8xxx_wdt", |
4018294b5 of: Remove duplic... |
232 |
.of_match_table = mpc8xxx_wdt_match, |
fabbfb9e8 [PATCH] powerpc: ... |
233 234 |
}, }; |
59ca1b0d1 [WATCHDOG] mpc8xx... |
235 |
static int __init mpc8xxx_wdt_init(void) |
fabbfb9e8 [PATCH] powerpc: ... |
236 |
{ |
1c48a5c93 dt: Eliminate of_... |
237 |
return platform_driver_register(&mpc8xxx_wdt_driver); |
fabbfb9e8 [PATCH] powerpc: ... |
238 |
} |
0d7b10140 [WATCHDOG] mpc8xx... |
239 |
arch_initcall(mpc8xxx_wdt_init); |
fabbfb9e8 [PATCH] powerpc: ... |
240 |
|
59ca1b0d1 [WATCHDOG] mpc8xx... |
241 |
static void __exit mpc8xxx_wdt_exit(void) |
fabbfb9e8 [PATCH] powerpc: ... |
242 |
{ |
1c48a5c93 dt: Eliminate of_... |
243 |
platform_driver_unregister(&mpc8xxx_wdt_driver); |
fabbfb9e8 [PATCH] powerpc: ... |
244 |
} |
59ca1b0d1 [WATCHDOG] mpc8xx... |
245 |
module_exit(mpc8xxx_wdt_exit); |
fabbfb9e8 [PATCH] powerpc: ... |
246 247 |
MODULE_AUTHOR("Dave Updegraff, Kumar Gala"); |
0d7b10140 [WATCHDOG] mpc8xx... |
248 249 |
MODULE_DESCRIPTION("Driver for watchdog timer in MPC8xx/MPC83xx/MPC86xx " "uProcessors"); |
fabbfb9e8 [PATCH] powerpc: ... |
250 |
MODULE_LICENSE("GPL"); |