1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (C) 2022 Hewlett-Packard Enterprise Development Company, L.P. */
4 #include <linux/delay.h>
6 #include <linux/module.h>
7 #include <linux/platform_device.h>
8 #include <linux/types.h>
9 #include <linux/watchdog.h>
11 #define MASK_WDGCS_ENABLE 0x01
12 #define MASK_WDGCS_RELOAD 0x04
13 #define MASK_WDGCS_NMIEN 0x08
14 #define MASK_WDGCS_WARN 0x80
16 #define WDT_MAX_TIMEOUT_MS 655350
17 #define WDT_DEFAULT_TIMEOUT 30
18 #define SECS_TO_WDOG_TICKS(x) ((x) * 100)
19 #define WDOG_TICKS_TO_SECS(x) ((x) / 100)
21 #define GXP_WDT_CNT_OFS 0x10
22 #define GXP_WDT_CTRL_OFS 0x16
26 struct watchdog_device wdd
;
29 static void gxp_wdt_enable_reload(struct gxp_wdt
*drvdata
)
33 val
= readb(drvdata
->base
+ GXP_WDT_CTRL_OFS
);
34 val
|= (MASK_WDGCS_ENABLE
| MASK_WDGCS_RELOAD
);
35 writeb(val
, drvdata
->base
+ GXP_WDT_CTRL_OFS
);
38 static int gxp_wdt_start(struct watchdog_device
*wdd
)
40 struct gxp_wdt
*drvdata
= watchdog_get_drvdata(wdd
);
42 writew(SECS_TO_WDOG_TICKS(wdd
->timeout
), drvdata
->base
+ GXP_WDT_CNT_OFS
);
43 gxp_wdt_enable_reload(drvdata
);
47 static int gxp_wdt_stop(struct watchdog_device
*wdd
)
49 struct gxp_wdt
*drvdata
= watchdog_get_drvdata(wdd
);
52 val
= readb_relaxed(drvdata
->base
+ GXP_WDT_CTRL_OFS
);
53 val
&= ~MASK_WDGCS_ENABLE
;
54 writeb(val
, drvdata
->base
+ GXP_WDT_CTRL_OFS
);
58 static int gxp_wdt_set_timeout(struct watchdog_device
*wdd
,
61 struct gxp_wdt
*drvdata
= watchdog_get_drvdata(wdd
);
64 wdd
->timeout
= timeout
;
65 actual
= min(timeout
* 100, wdd
->max_hw_heartbeat_ms
/ 10);
66 writew(actual
, drvdata
->base
+ GXP_WDT_CNT_OFS
);
71 static unsigned int gxp_wdt_get_timeleft(struct watchdog_device
*wdd
)
73 struct gxp_wdt
*drvdata
= watchdog_get_drvdata(wdd
);
74 u32 val
= readw(drvdata
->base
+ GXP_WDT_CNT_OFS
);
76 return WDOG_TICKS_TO_SECS(val
);
79 static int gxp_wdt_ping(struct watchdog_device
*wdd
)
81 struct gxp_wdt
*drvdata
= watchdog_get_drvdata(wdd
);
83 gxp_wdt_enable_reload(drvdata
);
87 static int gxp_restart(struct watchdog_device
*wdd
, unsigned long action
,
90 struct gxp_wdt
*drvdata
= watchdog_get_drvdata(wdd
);
92 writew(1, drvdata
->base
+ GXP_WDT_CNT_OFS
);
93 gxp_wdt_enable_reload(drvdata
);
98 static const struct watchdog_ops gxp_wdt_ops
= {
100 .start
= gxp_wdt_start
,
101 .stop
= gxp_wdt_stop
,
102 .ping
= gxp_wdt_ping
,
103 .set_timeout
= gxp_wdt_set_timeout
,
104 .get_timeleft
= gxp_wdt_get_timeleft
,
105 .restart
= gxp_restart
,
108 static const struct watchdog_info gxp_wdt_info
= {
109 .options
= WDIOF_SETTIMEOUT
| WDIOF_MAGICCLOSE
| WDIOF_KEEPALIVEPING
,
110 .identity
= "HPE GXP Watchdog timer",
113 static int gxp_wdt_probe(struct platform_device
*pdev
)
115 struct device
*dev
= &pdev
->dev
;
116 struct gxp_wdt
*drvdata
;
120 drvdata
= devm_kzalloc(dev
, sizeof(struct gxp_wdt
), GFP_KERNEL
);
125 * The register area where the timer and watchdog reside is disarranged.
126 * Hence mapping individual register blocks for the timer and watchdog
127 * is not recommended as they would have access to each others
128 * registers. Based on feedback the watchdog is no longer part of the
129 * device tree file and the timer driver now creates the watchdog as a
130 * child device. During the watchdogs creation, the timer driver passes
131 * the base address to the watchdog over the private interface.
134 drvdata
->base
= (void __iomem
*)dev
->platform_data
;
136 drvdata
->wdd
.info
= &gxp_wdt_info
;
137 drvdata
->wdd
.ops
= &gxp_wdt_ops
;
138 drvdata
->wdd
.max_hw_heartbeat_ms
= WDT_MAX_TIMEOUT_MS
;
139 drvdata
->wdd
.parent
= dev
;
140 drvdata
->wdd
.timeout
= WDT_DEFAULT_TIMEOUT
;
142 watchdog_set_drvdata(&drvdata
->wdd
, drvdata
);
143 watchdog_set_nowayout(&drvdata
->wdd
, WATCHDOG_NOWAYOUT
);
145 val
= readb(drvdata
->base
+ GXP_WDT_CTRL_OFS
);
147 if (val
& MASK_WDGCS_ENABLE
)
148 set_bit(WDOG_HW_RUNNING
, &drvdata
->wdd
.status
);
150 watchdog_set_restart_priority(&drvdata
->wdd
, 128);
152 watchdog_stop_on_reboot(&drvdata
->wdd
);
153 err
= devm_watchdog_register_device(dev
, &drvdata
->wdd
);
157 dev_info(dev
, "HPE GXP watchdog timer");
162 static struct platform_driver gxp_wdt_driver
= {
163 .probe
= gxp_wdt_probe
,
168 module_platform_driver(gxp_wdt_driver
);
170 MODULE_AUTHOR("Nick Hawkins <nick.hawkins@hpe.com>");
171 MODULE_AUTHOR("Jean-Marie Verdun <verdun@hpe.com>");
172 MODULE_DESCRIPTION("Driver for GXP watchdog timer");
173 MODULE_LICENSE("GPL");