1 // SPDX-License-Identifier: GPL-2.0
3 * Watchdog driver for the MEN z069 IP-Core
5 * Copyright (C) 2018 Johannes Thumshirn <jth@kernel.org>
8 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <linux/watchdog.h>
14 struct watchdog_device wdt
;
19 #define MEN_Z069_WTR 0x10
20 #define MEN_Z069_WTR_WDEN BIT(15)
21 #define MEN_Z069_WTR_WDET_MASK 0x7fff
22 #define MEN_Z069_WVR 0x14
24 #define MEN_Z069_TIMER_FREQ 500 /* 500 Hz */
25 #define MEN_Z069_WDT_COUNTER_MIN 1
26 #define MEN_Z069_WDT_COUNTER_MAX 0x7fff
27 #define MEN_Z069_DEFAULT_TIMEOUT 30
29 static bool nowayout
= WATCHDOG_NOWAYOUT
;
30 module_param(nowayout
, bool, 0);
31 MODULE_PARM_DESC(nowayout
, "Watchdog cannot be stopped once started (default="
32 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
34 static int men_z069_wdt_start(struct watchdog_device
*wdt
)
36 struct men_z069_drv
*drv
= watchdog_get_drvdata(wdt
);
39 val
= readw(drv
->base
+ MEN_Z069_WTR
);
40 val
|= MEN_Z069_WTR_WDEN
;
41 writew(val
, drv
->base
+ MEN_Z069_WTR
);
46 static int men_z069_wdt_stop(struct watchdog_device
*wdt
)
48 struct men_z069_drv
*drv
= watchdog_get_drvdata(wdt
);
51 val
= readw(drv
->base
+ MEN_Z069_WTR
);
52 val
&= ~MEN_Z069_WTR_WDEN
;
53 writew(val
, drv
->base
+ MEN_Z069_WTR
);
58 static int men_z069_wdt_ping(struct watchdog_device
*wdt
)
60 struct men_z069_drv
*drv
= watchdog_get_drvdata(wdt
);
63 /* The watchdog trigger value toggles between 0x5555 and 0xaaaa */
64 val
= readw(drv
->base
+ MEN_Z069_WVR
);
66 writew(val
, drv
->base
+ MEN_Z069_WVR
);
71 static int men_z069_wdt_set_timeout(struct watchdog_device
*wdt
,
74 struct men_z069_drv
*drv
= watchdog_get_drvdata(wdt
);
77 wdt
->timeout
= timeout
;
78 val
= timeout
* MEN_Z069_TIMER_FREQ
;
80 reg
= readw(drv
->base
+ MEN_Z069_WTR
);
81 ena
= reg
& MEN_Z069_WTR_WDEN
;
83 writew(reg
, drv
->base
+ MEN_Z069_WTR
);
88 static const struct watchdog_info men_z069_info
= {
89 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
,
90 .identity
= "MEN z069 Watchdog",
93 static const struct watchdog_ops men_z069_ops
= {
95 .start
= men_z069_wdt_start
,
96 .stop
= men_z069_wdt_stop
,
97 .ping
= men_z069_wdt_ping
,
98 .set_timeout
= men_z069_wdt_set_timeout
,
101 static int men_z069_probe(struct mcb_device
*dev
,
102 const struct mcb_device_id
*id
)
104 struct men_z069_drv
*drv
;
105 struct resource
*mem
;
107 drv
= devm_kzalloc(&dev
->dev
, sizeof(struct men_z069_drv
), GFP_KERNEL
);
111 mem
= mcb_request_mem(dev
, "z069-wdt");
115 drv
->base
= devm_ioremap(&dev
->dev
, mem
->start
, resource_size(mem
));
116 if (drv
->base
== NULL
)
120 drv
->wdt
.info
= &men_z069_info
;
121 drv
->wdt
.ops
= &men_z069_ops
;
122 drv
->wdt
.timeout
= MEN_Z069_DEFAULT_TIMEOUT
;
123 drv
->wdt
.min_timeout
= 1;
124 drv
->wdt
.max_timeout
= MEN_Z069_WDT_COUNTER_MAX
/ MEN_Z069_TIMER_FREQ
;
126 watchdog_init_timeout(&drv
->wdt
, 0, &dev
->dev
);
127 watchdog_set_nowayout(&drv
->wdt
, nowayout
);
128 watchdog_set_drvdata(&drv
->wdt
, drv
);
129 drv
->wdt
.parent
= &dev
->dev
;
130 mcb_set_drvdata(dev
, drv
);
132 return watchdog_register_device(&drv
->wdt
);
135 mcb_release_mem(mem
);
139 static void men_z069_remove(struct mcb_device
*dev
)
141 struct men_z069_drv
*drv
= mcb_get_drvdata(dev
);
143 watchdog_unregister_device(&drv
->wdt
);
144 mcb_release_mem(drv
->mem
);
147 static const struct mcb_device_id men_z069_ids
[] = {
151 MODULE_DEVICE_TABLE(mcb
, men_z069_ids
);
153 static struct mcb_driver men_z069_driver
= {
157 .probe
= men_z069_probe
,
158 .remove
= men_z069_remove
,
159 .id_table
= men_z069_ids
,
161 module_mcb_driver(men_z069_driver
);
163 MODULE_AUTHOR("Johannes Thumshirn <jth@kernel.org>");
164 MODULE_DESCRIPTION("Watchdog driver for the MEN z069 IP-Core");
165 MODULE_LICENSE("GPL v2");
166 MODULE_ALIAS("mcb:16z069");
167 MODULE_IMPORT_NS("MCB");