blob: 3fc457bc16db3f596b2f4064cc094a6ecb19b69b [file] [log] [blame]
Thomas Gleixner2874c5f2019-05-27 08:55:01 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Kumar Galafabbfb92006-01-14 13:20:50 -08002/*
Anton Vorontsov0d7b1012008-07-03 23:51:36 -07003 * mpc8xxx_wdt.c - MPC8xx/MPC83xx/MPC86xx watchdog userspace interface
Kumar Galafabbfb92006-01-14 13:20:50 -08004 *
5 * Authors: Dave Updegraff <dave@cray.org>
Wim Van Sebroeck5f3b2752011-02-23 20:04:38 +00006 * Kumar Gala <galak@kernel.crashing.org>
7 * Attribution: from 83xx_wst: Florian Schirmer <jolt@tuxbox.org>
8 * ..and from sc520_wdt
Anton Vorontsov500c9192008-07-03 23:51:34 -07009 * Copyright (c) 2008 MontaVista Software, Inc.
10 * Anton Vorontsov <avorontsov@ru.mvista.com>
Kumar Galafabbfb92006-01-14 13:20:50 -080011 *
12 * Note: it appears that you can only actually ENABLE or DISABLE the thing
13 * once after POR. Once enabled, you cannot disable, and vice versa.
Kumar Galafabbfb92006-01-14 13:20:50 -080014 */
15
Kumar Galafabbfb92006-01-14 13:20:50 -080016#include <linux/fs.h>
17#include <linux/init.h>
18#include <linux/kernel.h>
Rob Herring5af50732013-09-17 14:28:33 -050019#include <linux/of_address.h>
Anton Vorontsovef8ab122008-07-03 23:51:32 -070020#include <linux/of_platform.h>
Kumar Galafabbfb92006-01-14 13:20:50 -080021#include <linux/module.h>
22#include <linux/watchdog.h>
Alan Coxf26ef3d2008-05-19 14:07:09 +010023#include <linux/io.h>
24#include <linux/uaccess.h>
Anton Vorontsovef8ab122008-07-03 23:51:32 -070025#include <sysdev/fsl_soc.h>
Kumar Galafabbfb92006-01-14 13:20:50 -080026
Christophe Leroy19ce9492017-11-08 15:39:44 +010027#define WATCHDOG_TIMEOUT 10
28
Anton Vorontsov59ca1b02008-07-03 23:51:35 -070029struct mpc8xxx_wdt {
Kumar Galafabbfb92006-01-14 13:20:50 -080030 __be32 res0;
31 __be32 swcrr; /* System watchdog control register */
32#define SWCRR_SWTC 0xFFFF0000 /* Software Watchdog Time Count. */
Christophe Leroy19ce9492017-11-08 15:39:44 +010033#define SWCRR_SWF 0x00000008 /* Software Watchdog Freeze (mpc8xx). */
Kumar Galafabbfb92006-01-14 13:20:50 -080034#define SWCRR_SWEN 0x00000004 /* Watchdog Enable bit. */
35#define SWCRR_SWRI 0x00000002 /* Software Watchdog Reset/Interrupt Select bit.*/
36#define SWCRR_SWPR 0x00000001 /* Software Watchdog Counter Prescale bit. */
37 __be32 swcnr; /* System watchdog count register */
38 u8 res1[2];
39 __be16 swsrr; /* System watchdog service register */
40 u8 res2[0xF0];
41};
42
Anton Vorontsov59ca1b02008-07-03 23:51:35 -070043struct mpc8xxx_wdt_type {
Anton Vorontsov500c9192008-07-03 23:51:34 -070044 int prescaler;
45 bool hw_enabled;
Christophe Leroy38e48b72018-09-17 06:22:50 +000046 u32 rsr_mask;
Anton Vorontsov500c9192008-07-03 23:51:34 -070047};
48
Uwe Kleine-König7997eba2015-08-12 10:15:56 +020049struct mpc8xxx_wdt_ddata {
50 struct mpc8xxx_wdt __iomem *base;
51 struct watchdog_device wdd;
Uwe Kleine-König7997eba2015-08-12 10:15:56 +020052 spinlock_t lock;
Christophe Leroy19ce9492017-11-08 15:39:44 +010053 u16 swtc;
Uwe Kleine-König7997eba2015-08-12 10:15:56 +020054};
Kumar Galafabbfb92006-01-14 13:20:50 -080055
Christophe Leroy19ce9492017-11-08 15:39:44 +010056static u16 timeout;
Kumar Galafabbfb92006-01-14 13:20:50 -080057module_param(timeout, ushort, 0);
Alan Coxf26ef3d2008-05-19 14:07:09 +010058MODULE_PARM_DESC(timeout,
Christophe Leroy19ce9492017-11-08 15:39:44 +010059 "Watchdog timeout in seconds. (1<timeout<65535, default="
60 __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
Kumar Galafabbfb92006-01-14 13:20:50 -080061
Rusty Russell90ab5ee2012-01-13 09:32:20 +103062static bool reset = 1;
Kumar Galafabbfb92006-01-14 13:20:50 -080063module_param(reset, bool, 0);
Alan Coxf26ef3d2008-05-19 14:07:09 +010064MODULE_PARM_DESC(reset,
65 "Watchdog Interrupt/Reset Mode. 0 = interrupt, 1 = reset");
Kumar Galafabbfb92006-01-14 13:20:50 -080066
Wim Van Sebroeck86a1e182012-03-05 16:51:11 +010067static bool nowayout = WATCHDOG_NOWAYOUT;
68module_param(nowayout, bool, 0);
Anton Vorontsov500c9192008-07-03 23:51:34 -070069MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
70 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
71
Uwe Kleine-König7997eba2015-08-12 10:15:56 +020072static void mpc8xxx_wdt_keepalive(struct mpc8xxx_wdt_ddata *ddata)
Kumar Galafabbfb92006-01-14 13:20:50 -080073{
74 /* Ping the WDT */
Uwe Kleine-König7997eba2015-08-12 10:15:56 +020075 spin_lock(&ddata->lock);
76 out_be16(&ddata->base->swsrr, 0x556c);
77 out_be16(&ddata->base->swsrr, 0xaa39);
78 spin_unlock(&ddata->lock);
Kumar Galafabbfb92006-01-14 13:20:50 -080079}
80
Christophe Leroyd5cfaf02013-12-04 07:32:14 +010081static int mpc8xxx_wdt_start(struct watchdog_device *w)
Kumar Galafabbfb92006-01-14 13:20:50 -080082{
Uwe Kleine-König7997eba2015-08-12 10:15:56 +020083 struct mpc8xxx_wdt_ddata *ddata =
84 container_of(w, struct mpc8xxx_wdt_ddata, wdd);
Christophe Leroy19ce9492017-11-08 15:39:44 +010085 u32 tmp = in_be32(&ddata->base->swcrr);
Kumar Galafabbfb92006-01-14 13:20:50 -080086
87 /* Good, fire up the show */
Christophe Leroy19ce9492017-11-08 15:39:44 +010088 tmp &= ~(SWCRR_SWTC | SWCRR_SWF | SWCRR_SWEN | SWCRR_SWRI | SWCRR_SWPR);
89 tmp |= SWCRR_SWEN | SWCRR_SWPR | (ddata->swtc << 16);
90
Kumar Galafabbfb92006-01-14 13:20:50 -080091 if (reset)
92 tmp |= SWCRR_SWRI;
93
Uwe Kleine-König7997eba2015-08-12 10:15:56 +020094 out_be32(&ddata->base->swcrr, tmp);
Kumar Galafabbfb92006-01-14 13:20:50 -080095
Christophe Leroy19ce9492017-11-08 15:39:44 +010096 tmp = in_be32(&ddata->base->swcrr);
97 if (!(tmp & SWCRR_SWEN))
98 return -EOPNOTSUPP;
99
100 ddata->swtc = tmp >> 16;
101 set_bit(WDOG_HW_RUNNING, &ddata->wdd.status);
Anton Vorontsov500c9192008-07-03 23:51:34 -0700102
Kumar Galafabbfb92006-01-14 13:20:50 -0800103 return 0;
104}
105
Christophe Leroyd5cfaf02013-12-04 07:32:14 +0100106static int mpc8xxx_wdt_ping(struct watchdog_device *w)
Kumar Galafabbfb92006-01-14 13:20:50 -0800107{
Uwe Kleine-König7997eba2015-08-12 10:15:56 +0200108 struct mpc8xxx_wdt_ddata *ddata =
109 container_of(w, struct mpc8xxx_wdt_ddata, wdd);
110
111 mpc8xxx_wdt_keepalive(ddata);
Christophe Leroyd5cfaf02013-12-04 07:32:14 +0100112 return 0;
Kumar Galafabbfb92006-01-14 13:20:50 -0800113}
114
Christophe Leroyd5cfaf02013-12-04 07:32:14 +0100115static struct watchdog_info mpc8xxx_wdt_info = {
Christophe Leroy19ce9492017-11-08 15:39:44 +0100116 .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT,
Christophe Leroyd5cfaf02013-12-04 07:32:14 +0100117 .firmware_version = 1,
118 .identity = "MPC8xxx",
Kumar Galafabbfb92006-01-14 13:20:50 -0800119};
120
Christophe Leroyd5cfaf02013-12-04 07:32:14 +0100121static struct watchdog_ops mpc8xxx_wdt_ops = {
122 .owner = THIS_MODULE,
123 .start = mpc8xxx_wdt_start,
124 .ping = mpc8xxx_wdt_ping,
Christophe Leroyd5cfaf02013-12-04 07:32:14 +0100125};
126
Bill Pemberton2d991a12012-11-19 13:21:41 -0500127static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
Kumar Galafabbfb92006-01-14 13:20:50 -0800128{
Kumar Galafabbfb92006-01-14 13:20:50 -0800129 int ret;
Uwe Kleine-Königde5f7122015-08-12 10:15:55 +0200130 struct resource *res;
Arnd Bergmann639397e2012-05-21 21:57:39 +0200131 const struct mpc8xxx_wdt_type *wdt_type;
Uwe Kleine-König7997eba2015-08-12 10:15:56 +0200132 struct mpc8xxx_wdt_ddata *ddata;
Anton Vorontsovef8ab122008-07-03 23:51:32 -0700133 u32 freq = fsl_get_sys_freq();
Anton Vorontsov500c9192008-07-03 23:51:34 -0700134 bool enabled;
Christophe Leroy79b10e02018-09-17 06:22:47 +0000135 struct device *dev = &ofdev->dev;
Kumar Galafabbfb92006-01-14 13:20:50 -0800136
Christophe Leroy79b10e02018-09-17 06:22:47 +0000137 wdt_type = of_device_get_match_data(dev);
Uwe Kleine-Königf0ded832015-08-12 10:15:54 +0200138 if (!wdt_type)
Grant Likely1c48a5c2011-02-17 02:43:24 -0700139 return -EINVAL;
Grant Likely1c48a5c2011-02-17 02:43:24 -0700140
Anton Vorontsovef8ab122008-07-03 23:51:32 -0700141 if (!freq || freq == -1)
142 return -EINVAL;
Kumar Galafabbfb92006-01-14 13:20:50 -0800143
Christophe Leroy79b10e02018-09-17 06:22:47 +0000144 ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
Uwe Kleine-König7997eba2015-08-12 10:15:56 +0200145 if (!ddata)
146 return -ENOMEM;
Kumar Galafabbfb92006-01-14 13:20:50 -0800147
Guenter Roeck0f0a6a22019-04-02 12:01:53 -0700148 ddata->base = devm_platform_ioremap_resource(ofdev, 0);
Uwe Kleine-König7997eba2015-08-12 10:15:56 +0200149 if (IS_ERR(ddata->base))
150 return PTR_ERR(ddata->base);
151
152 enabled = in_be32(&ddata->base->swcrr) & SWCRR_SWEN;
Anton Vorontsov500c9192008-07-03 23:51:34 -0700153 if (!enabled && wdt_type->hw_enabled) {
Christophe Leroy79b10e02018-09-17 06:22:47 +0000154 dev_info(dev, "could not be enabled in software\n");
Uwe Kleine-König72cd5012015-08-12 10:15:57 +0200155 return -ENODEV;
Anton Vorontsov500c9192008-07-03 23:51:34 -0700156 }
157
Christophe Leroy38e48b72018-09-17 06:22:50 +0000158 res = platform_get_resource(ofdev, IORESOURCE_MEM, 1);
159 if (res) {
160 bool status;
161 u32 __iomem *rsr = ioremap(res->start, resource_size(res));
162
163 if (!rsr)
164 return -ENOMEM;
165
166 status = in_be32(rsr) & wdt_type->rsr_mask;
167 ddata->wdd.bootstatus = status ? WDIOF_CARDRESET : 0;
168 /* clear reset status bits related to watchdog timer */
169 out_be32(rsr, wdt_type->rsr_mask);
170 iounmap(rsr);
171
172 dev_info(dev, "Last boot was %scaused by watchdog\n",
173 status ? "" : "not ");
174 }
175
Uwe Kleine-König7997eba2015-08-12 10:15:56 +0200176 spin_lock_init(&ddata->lock);
Uwe Kleine-König7997eba2015-08-12 10:15:56 +0200177
178 ddata->wdd.info = &mpc8xxx_wdt_info,
179 ddata->wdd.ops = &mpc8xxx_wdt_ops,
180
Christophe Leroy19ce9492017-11-08 15:39:44 +0100181 ddata->wdd.timeout = WATCHDOG_TIMEOUT;
Christophe Leroy79b10e02018-09-17 06:22:47 +0000182 watchdog_init_timeout(&ddata->wdd, timeout, dev);
Uwe Kleine-König50ffb532015-08-12 10:15:53 +0200183
Uwe Kleine-König7997eba2015-08-12 10:15:56 +0200184 watchdog_set_nowayout(&ddata->wdd, nowayout);
Uwe Kleine-König50ffb532015-08-12 10:15:53 +0200185
Christophe Leroy19ce9492017-11-08 15:39:44 +0100186 ddata->swtc = min(ddata->wdd.timeout * freq / wdt_type->prescaler,
187 0xffffU);
Anton Vorontsov500c9192008-07-03 23:51:34 -0700188
189 /*
190 * If the watchdog was previously enabled or we're running on
Anton Vorontsov59ca1b02008-07-03 23:51:35 -0700191 * MPC8xxx, we should ping the wdt from the kernel until the
Anton Vorontsov500c9192008-07-03 23:51:34 -0700192 * userspace handles it.
193 */
194 if (enabled)
Christophe Leroy19ce9492017-11-08 15:39:44 +0100195 mpc8xxx_wdt_start(&ddata->wdd);
196
197 ddata->wdd.max_hw_heartbeat_ms = (ddata->swtc * wdt_type->prescaler) /
198 (freq / 1000);
199 ddata->wdd.min_timeout = ddata->wdd.max_hw_heartbeat_ms / 1000;
200 if (ddata->wdd.timeout < ddata->wdd.min_timeout)
201 ddata->wdd.timeout = ddata->wdd.min_timeout;
202
Guenter Roeck81df6db2019-04-10 09:28:02 -0700203 ret = devm_watchdog_register_device(dev, &ddata->wdd);
Wolfram Sanga2390272019-05-18 23:27:40 +0200204 if (ret)
Christophe Leroy19ce9492017-11-08 15:39:44 +0100205 return ret;
Christophe Leroy19ce9492017-11-08 15:39:44 +0100206
Christophe Leroy79b10e02018-09-17 06:22:47 +0000207 dev_info(dev,
208 "WDT driver for MPC8xxx initialized. mode:%s timeout=%d sec\n",
209 reset ? "reset" : "interrupt", ddata->wdd.timeout);
Uwe Kleine-König7997eba2015-08-12 10:15:56 +0200210
211 platform_set_drvdata(ofdev, ddata);
Kumar Galafabbfb92006-01-14 13:20:50 -0800212 return 0;
Kumar Galafabbfb92006-01-14 13:20:50 -0800213}
214
Anton Vorontsov59ca1b02008-07-03 23:51:35 -0700215static const struct of_device_id mpc8xxx_wdt_match[] = {
Anton Vorontsovef8ab122008-07-03 23:51:32 -0700216 {
217 .compatible = "mpc83xx_wdt",
Anton Vorontsov59ca1b02008-07-03 23:51:35 -0700218 .data = &(struct mpc8xxx_wdt_type) {
Anton Vorontsov500c9192008-07-03 23:51:34 -0700219 .prescaler = 0x10000,
Christophe Leroy38e48b72018-09-17 06:22:50 +0000220 .rsr_mask = BIT(3), /* RSR Bit SWRS */
Anton Vorontsov500c9192008-07-03 23:51:34 -0700221 },
222 },
223 {
224 .compatible = "fsl,mpc8610-wdt",
Anton Vorontsov59ca1b02008-07-03 23:51:35 -0700225 .data = &(struct mpc8xxx_wdt_type) {
Anton Vorontsov500c9192008-07-03 23:51:34 -0700226 .prescaler = 0x10000,
227 .hw_enabled = true,
Christophe Leroy38e48b72018-09-17 06:22:50 +0000228 .rsr_mask = BIT(20), /* RSTRSCR Bit WDT_RR */
Anton Vorontsov500c9192008-07-03 23:51:34 -0700229 },
Anton Vorontsovef8ab122008-07-03 23:51:32 -0700230 },
Anton Vorontsov0d7b1012008-07-03 23:51:36 -0700231 {
232 .compatible = "fsl,mpc823-wdt",
233 .data = &(struct mpc8xxx_wdt_type) {
234 .prescaler = 0x800,
Christophe Leroy4af897f2013-11-30 16:45:40 +0100235 .hw_enabled = true,
Christophe Leroy38e48b72018-09-17 06:22:50 +0000236 .rsr_mask = BIT(28), /* RSR Bit SWRS */
Anton Vorontsov0d7b1012008-07-03 23:51:36 -0700237 },
238 },
Anton Vorontsovef8ab122008-07-03 23:51:32 -0700239 {},
240};
Anton Vorontsov59ca1b02008-07-03 23:51:35 -0700241MODULE_DEVICE_TABLE(of, mpc8xxx_wdt_match);
Anton Vorontsovef8ab122008-07-03 23:51:32 -0700242
Grant Likely1c48a5c2011-02-17 02:43:24 -0700243static struct platform_driver mpc8xxx_wdt_driver = {
Anton Vorontsov59ca1b02008-07-03 23:51:35 -0700244 .probe = mpc8xxx_wdt_probe,
Grant Likely40182942010-04-13 16:13:02 -0700245 .driver = {
246 .name = "mpc8xxx_wdt",
Grant Likely40182942010-04-13 16:13:02 -0700247 .of_match_table = mpc8xxx_wdt_match,
Kumar Galafabbfb92006-01-14 13:20:50 -0800248 },
249};
250
Anton Vorontsov59ca1b02008-07-03 23:51:35 -0700251static int __init mpc8xxx_wdt_init(void)
Kumar Galafabbfb92006-01-14 13:20:50 -0800252{
Grant Likely1c48a5c2011-02-17 02:43:24 -0700253 return platform_driver_register(&mpc8xxx_wdt_driver);
Kumar Galafabbfb92006-01-14 13:20:50 -0800254}
Anton Vorontsov0d7b1012008-07-03 23:51:36 -0700255arch_initcall(mpc8xxx_wdt_init);
Kumar Galafabbfb92006-01-14 13:20:50 -0800256
Anton Vorontsov59ca1b02008-07-03 23:51:35 -0700257static void __exit mpc8xxx_wdt_exit(void)
Kumar Galafabbfb92006-01-14 13:20:50 -0800258{
Grant Likely1c48a5c2011-02-17 02:43:24 -0700259 platform_driver_unregister(&mpc8xxx_wdt_driver);
Kumar Galafabbfb92006-01-14 13:20:50 -0800260}
Anton Vorontsov59ca1b02008-07-03 23:51:35 -0700261module_exit(mpc8xxx_wdt_exit);
Kumar Galafabbfb92006-01-14 13:20:50 -0800262
263MODULE_AUTHOR("Dave Updegraff, Kumar Gala");
Anton Vorontsov0d7b1012008-07-03 23:51:36 -0700264MODULE_DESCRIPTION("Driver for watchdog timer in MPC8xx/MPC83xx/MPC86xx "
265 "uProcessors");
Kumar Galafabbfb92006-01-14 13:20:50 -0800266MODULE_LICENSE("GPL");