Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ce8a341

Browse files
committed
Merge tag 'tags/fpga-wdt-250619' into linux-6.12-mchp
FPGA wdt updates for 250619 * watchdog: microchip: Add support for MPFS WDT (Daire McNamara 2025-05-20) * dt-bindings: watchdog: microchip: Add bindings for MPFS Watchdogs (Daire McNamara 2025-05-20) Signed-off-by: Conor Dooley <[email protected]>
2 parents ea39a1e + a8c2dbe commit ce8a341

File tree

4 files changed

+310
-0
lines changed

4 files changed

+310
-0
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
2+
%YAML 1.2
3+
---
4+
$id: http://devicetree.org/schemas/watchdog/microchip,mpfs-wdt.yaml#
5+
$schema: http://devicetree.org/meta-schemas/core.yaml#
6+
7+
title: Microchip PolarFire SoC Watchdog Timer
8+
9+
allOf:
10+
- $ref: "watchdog.yaml#"
11+
12+
maintainers:
13+
- Daire McNamara <[email protected]>
14+
15+
properties:
16+
compatible:
17+
const: microchip,mpfs-wdt
18+
19+
reg:
20+
maxItems: 1
21+
22+
clocks:
23+
maxItems: 1
24+
items:
25+
- description: The MPFS watchdog uses a 24-bit counter that counts every 256 PCLK cycles,
26+
providing a maximum watchdog refresh rate of 13.74 seconds (assuming a
27+
312.5 MHz APB clock rate), 14.32 seconds (assuming a 300 MHz ABP clock rate),
28+
or 28.63 seconds (assuming a 150 MHz ABP clock rate).
29+
30+
interrupts:
31+
maxItems: 2
32+
items:
33+
- description: an mvrp interrupt indicates that the watchdog is in the period between its
34+
mvrp counter and its trig counter. This indicates the watchdog can be
35+
serviced in this period.
36+
- description: a trig interrupt indicates that the watchdog is in the period between its
37+
trig counter (a maximum of 2^11 - 1 ticks) and 0. The watchdog cannot be stopped
38+
but any emergency actions can be triggered by handling this interrupt.
39+
40+
interrupt-names:
41+
maxItems: 2
42+
items:
43+
- const: mvrp
44+
- const: trig
45+
46+
required:
47+
- compatible
48+
- reg
49+
- clocks
50+
- interrupts
51+
- interrupt-names
52+
53+
unevaluatedProperties: false
54+
55+
examples:
56+
- |
57+
watchdog@20001000 {
58+
compatible = "microchip,mpfs-wdt";
59+
reg = <0x20001000 0x1000>;
60+
interrupts = <100>, <105>;
61+
clocks = <&pclk>;
62+
};
63+
64+
...

drivers/watchdog/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,18 @@ config DA9052_WATCHDOG
217217
Alternatively say M to compile the driver as a module,
218218
which will be called da9052_wdt.
219219

220+
config MPFS_WATCHDOG
221+
tristate "Microchip PolarFire SoC Watchdog"
222+
depends on ARCH_MICROCHIP_POLARFIRE || COMPILE_TEST
223+
select WATCHDOG_CORE
224+
help
225+
Support for the watchdog in the PolarFire SoC. Watchdog trigger
226+
cause system reset.
227+
228+
Say Y here to include support for the PolarFire watchdog.
229+
Alternatively say M to compile the driver as a module,
230+
which will be called mpfs_wdt.
231+
220232
config DA9055_WATCHDOG
221233
tristate "Dialog Semiconductor DA9055 Watchdog"
222234
depends on MFD_DA9055 || COMPILE_TEST

drivers/watchdog/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ obj-$(CONFIG_PSERIES_WDT) += pseries-wdt.o
196196
obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
197197

198198
# RISC-V Architecture
199+
obj-$(CONFIG_MPFS_WATCHDOG) += mpfs_wdt.o
199200
obj-$(CONFIG_STARFIVE_WATCHDOG) += starfive-wdt.o
200201

201202
# S390 Architecture

drivers/watchdog/mpfs_wdt.c

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Watchdog driver for Microchip PolarFire SoC watchdog.
4+
*
5+
* Copyright (C) 2025 Microchip Corporation
6+
*/
7+
#include <linux/clk.h>
8+
#include <linux/interrupt.h>
9+
#include <linux/mod_devicetable.h>
10+
#include <linux/platform_device.h>
11+
#include <linux/reboot.h>
12+
#include <linux/watchdog.h>
13+
14+
#define MPFS_WDT_REFRESH 0x0
15+
#define MPFS_WDT_MAGIC_WORD 0xdeadc0de
16+
#define MPFS_WDT_CONTROL 0x4
17+
#define MPFS_WDT_STATUS 0x8
18+
#define MPFS_MVRP_TRIPPED 0x1
19+
#define MPFS_WDOG_TRIPPED 0x2
20+
#define MPFS_WDT_TIME 0xc
21+
#define MPFS_WDT_MSVP 0x10
22+
23+
/* 24-bit counter */
24+
#define MPFS_WDT_LOAD_MAX 0xfffff0
25+
#define MPFS_WDT_LOAD_MIN 1
26+
27+
#define MPFS_WDT_CLK_PRESCALER 256
28+
29+
struct mpfs_wdt {
30+
struct watchdog_device wdd;
31+
struct clk *clk;
32+
unsigned int rate_hz;
33+
int mvrp_irq;
34+
int trig_irq;
35+
void __iomem *base;
36+
};
37+
38+
static void mpfs_wdt_set_timeout_reg(struct watchdog_device *wdog)
39+
{
40+
struct mpfs_wdt *mpfs_wdt = watchdog_get_drvdata(wdog);
41+
u32 trig_counter = wdog->timeout * mpfs_wdt->rate_hz;
42+
43+
writel(trig_counter, mpfs_wdt->base + MPFS_WDT_TIME);
44+
}
45+
46+
static void mpfs_wdt_set_pretimeout_reg(struct watchdog_device *wdog)
47+
{
48+
struct mpfs_wdt *mpfs_wdt = watchdog_get_drvdata(wdog);
49+
u32 mvrp_counter = 0;
50+
u32 mvrp_secs;
51+
52+
if (wdog->pretimeout) {
53+
mvrp_secs = wdog->timeout - wdog->pretimeout;
54+
mvrp_counter = mvrp_secs * mpfs_wdt->rate_hz;
55+
}
56+
57+
writel(mvrp_counter, mpfs_wdt->base + MPFS_WDT_MSVP);
58+
}
59+
60+
static int mpfs_wdt_ping(struct watchdog_device *wdog)
61+
{
62+
struct mpfs_wdt *mpfs_wdt = watchdog_get_drvdata(wdog);
63+
64+
writel(MPFS_WDT_MAGIC_WORD, mpfs_wdt->base + MPFS_WDT_REFRESH);
65+
66+
return 0;
67+
}
68+
69+
static int mpfs_wdt_start(struct watchdog_device *wdog)
70+
{
71+
return mpfs_wdt_ping(wdog);
72+
}
73+
74+
static int mpfs_wdt_stop(struct watchdog_device *wdog)
75+
{
76+
/* can't stop the dog, just return */
77+
return 0;
78+
}
79+
80+
static int mpfs_wdt_set_timeout(struct watchdog_device *wdog, u32 timeout_secs)
81+
{
82+
wdog->timeout = timeout_secs;
83+
mpfs_wdt_set_pretimeout_reg(wdog);
84+
mpfs_wdt_set_timeout_reg(wdog);
85+
86+
return 0;
87+
}
88+
89+
static int mpfs_wdt_set_pretimeout(struct watchdog_device *wdog, u32 timeout_secs)
90+
{
91+
if (timeout_secs > wdog->timeout)
92+
return -EINVAL;
93+
94+
wdog->pretimeout = timeout_secs;
95+
mpfs_wdt_set_pretimeout_reg(wdog);
96+
97+
return 0;
98+
}
99+
100+
static unsigned int mpfs_wdt_get_timeleft(struct watchdog_device *wdog)
101+
{
102+
struct mpfs_wdt *mpfs_wdt = watchdog_get_drvdata(wdog);
103+
u32 time_secs;
104+
105+
time_secs = readl(mpfs_wdt->base + MPFS_WDT_REFRESH) / mpfs_wdt->rate_hz;
106+
107+
return time_secs;
108+
}
109+
110+
static irqreturn_t mpfs_wdt_trig_isr(int irq, void *dev_id)
111+
{
112+
struct mpfs_wdt *mpfs_wdt = dev_id;
113+
114+
writel(MPFS_WDOG_TRIPPED, mpfs_wdt->base + MPFS_WDT_STATUS);
115+
116+
dev_crit(mpfs_wdt->wdd.parent, "Microchip PolarFire SoC wdt tripped\n");
117+
emergency_restart();
118+
119+
return IRQ_HANDLED;
120+
}
121+
122+
static irqreturn_t mpfs_wdt_mvrp_isr(int irq, void *dev_id)
123+
{
124+
struct mpfs_wdt *mpfs_wdt = dev_id;
125+
126+
writel(MPFS_MVRP_TRIPPED, mpfs_wdt->base + MPFS_WDT_STATUS);
127+
watchdog_notify_pretimeout(&mpfs_wdt->wdd);
128+
129+
return IRQ_HANDLED;
130+
}
131+
132+
static const struct watchdog_info mpfs_wdt_info = {
133+
.identity = "Microchip PolarFire SoC Watchdog",
134+
.options = WDIOF_SETTIMEOUT |
135+
WDIOF_PRETIMEOUT |
136+
WDIOF_MAGICCLOSE |
137+
WDIOF_KEEPALIVEPING,
138+
};
139+
140+
static const struct watchdog_ops mpfs_wdt_ops = {
141+
.owner = THIS_MODULE,
142+
.start = mpfs_wdt_start,
143+
.stop = mpfs_wdt_stop,
144+
.ping = mpfs_wdt_ping,
145+
.set_timeout = mpfs_wdt_set_timeout,
146+
.set_pretimeout = mpfs_wdt_set_pretimeout,
147+
.get_timeleft = mpfs_wdt_get_timeleft,
148+
};
149+
150+
static int mpfs_wdt_probe(struct platform_device *pdev)
151+
{
152+
struct device *dev = &pdev->dev;
153+
struct mpfs_wdt *mpfs_wdt;
154+
int ret;
155+
156+
mpfs_wdt = devm_kzalloc(dev, sizeof(*mpfs_wdt), GFP_KERNEL);
157+
if (!mpfs_wdt)
158+
return -ENOMEM;
159+
160+
mpfs_wdt->base = devm_platform_ioremap_resource(pdev, 0);
161+
if (IS_ERR(mpfs_wdt->base))
162+
return PTR_ERR(mpfs_wdt->base);
163+
164+
mpfs_wdt->clk = devm_clk_get_enabled(dev, NULL);
165+
if (IS_ERR(mpfs_wdt->clk))
166+
return dev_err_probe(dev, PTR_ERR(mpfs_wdt->clk), "Failed to get clock\n");
167+
168+
mpfs_wdt->rate_hz = clk_get_rate(mpfs_wdt->clk) / MPFS_WDT_CLK_PRESCALER;
169+
if (!mpfs_wdt->rate_hz)
170+
return dev_err_probe(dev, -EINVAL, "Failed to get clock rate\n");
171+
172+
mpfs_wdt->mvrp_irq = platform_get_irq_byname(pdev, "mvrp");
173+
if (mpfs_wdt->mvrp_irq < 0)
174+
return dev_err_probe(dev, mpfs_wdt->mvrp_irq, "Failed to get IRQ for mvrp\n");
175+
176+
ret = devm_request_irq(dev, mpfs_wdt->mvrp_irq, mpfs_wdt_mvrp_isr, 0,
177+
"mpfs-wdt", mpfs_wdt);
178+
if (ret)
179+
return dev_err_probe(dev, ret, "Failed to request IRQ for trig\n");
180+
181+
mpfs_wdt->trig_irq = platform_get_irq_byname(pdev, "trig");
182+
if (mpfs_wdt->trig_irq < 0)
183+
return dev_err_probe(dev, mpfs_wdt->trig_irq, "Failed to get IRQ for trig\n");
184+
185+
ret = devm_request_irq(dev, mpfs_wdt->trig_irq, mpfs_wdt_trig_isr, 0,
186+
"mpfs-wdt", mpfs_wdt);
187+
if (ret)
188+
return dev_err_probe(dev, ret, "Failed to request IRQ for timeout\n");
189+
190+
mpfs_wdt->wdd.parent = dev;
191+
mpfs_wdt->wdd.info = &mpfs_wdt_info;
192+
mpfs_wdt->wdd.ops = &mpfs_wdt_ops;
193+
mpfs_wdt->wdd.min_timeout = MPFS_WDT_LOAD_MIN / mpfs_wdt->rate_hz;
194+
mpfs_wdt->wdd.max_timeout = MPFS_WDT_LOAD_MAX / mpfs_wdt->rate_hz;
195+
mpfs_wdt->wdd.timeout = MPFS_WDT_LOAD_MAX / mpfs_wdt->rate_hz;
196+
mpfs_wdt->wdd.pretimeout = MPFS_WDT_LOAD_MAX / (mpfs_wdt->rate_hz * 2);
197+
198+
watchdog_set_drvdata(&mpfs_wdt->wdd, mpfs_wdt);
199+
watchdog_set_nowayout(&mpfs_wdt->wdd, true);
200+
watchdog_init_timeout(&mpfs_wdt->wdd, mpfs_wdt->wdd.timeout, dev);
201+
mpfs_wdt_set_pretimeout(&mpfs_wdt->wdd, mpfs_wdt->wdd.pretimeout);
202+
mpfs_wdt_set_timeout(&mpfs_wdt->wdd, mpfs_wdt->wdd.timeout);
203+
204+
ret = devm_watchdog_register_device(dev, &mpfs_wdt->wdd);
205+
if (ret)
206+
return dev_err_probe(dev, ret, "Failed to register watchdog device.\n");
207+
208+
platform_set_drvdata(pdev, mpfs_wdt);
209+
dev_info(dev, "timeout %d sec (nowayout=true)\n",
210+
mpfs_wdt->wdd.timeout);
211+
212+
return 0;
213+
}
214+
215+
static const struct of_device_id mpfs_wdt_match[] = {
216+
{ .compatible = "microchip,mpfs-wdt" },
217+
{ }
218+
};
219+
MODULE_DEVICE_TABLE(of, mpfs_wdt_match);
220+
221+
static struct platform_driver mpfs_wdt_driver = {
222+
.probe = mpfs_wdt_probe,
223+
.driver = {
224+
.name = "mpfs_wdt",
225+
.of_match_table = mpfs_wdt_match,
226+
},
227+
};
228+
229+
module_platform_driver(mpfs_wdt_driver);
230+
231+
MODULE_DESCRIPTION("Microchip PolarFire SoC watchdog driver");
232+
MODULE_AUTHOR("Daire McNamara <[email protected]");
233+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)