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_WVR
);
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 struct watchdog_device men_z069_wdt
= {
102 .info
= &men_z069_info
,
103 .ops
= &men_z069_ops
,
104 .timeout
= MEN_Z069_DEFAULT_TIMEOUT
,
106 .max_timeout
= MEN_Z069_WDT_COUNTER_MAX
/ MEN_Z069_TIMER_FREQ
,
109 static int men_z069_probe(struct mcb_device
*dev
,
110 const struct mcb_device_id
*id
)
112 struct men_z069_drv
*drv
;
113 struct resource
*mem
;
115 drv
= devm_kzalloc(&dev
->dev
, sizeof(struct men_z069_drv
), GFP_KERNEL
);
119 mem
= mcb_request_mem(dev
, "z069-wdt");
123 drv
->base
= devm_ioremap(&dev
->dev
, mem
->start
, resource_size(mem
));
124 if (drv
->base
== NULL
)
129 drv
->wdt
= men_z069_wdt
;
130 watchdog_init_timeout(&drv
->wdt
, 0, &dev
->dev
);
131 watchdog_set_nowayout(&drv
->wdt
, nowayout
);
132 watchdog_set_drvdata(&drv
->wdt
, drv
);
133 drv
->wdt
.parent
= &dev
->dev
;
134 mcb_set_drvdata(dev
, drv
);
136 return watchdog_register_device(&men_z069_wdt
);
139 mcb_release_mem(mem
);
143 static void men_z069_remove(struct mcb_device
*dev
)
145 struct men_z069_drv
*drv
= mcb_get_drvdata(dev
);
147 watchdog_unregister_device(&drv
->wdt
);
148 mcb_release_mem(drv
->mem
);
151 static const struct mcb_device_id men_z069_ids
[] = {
155 MODULE_DEVICE_TABLE(mcb
, men_z069_ids
);
157 static struct mcb_driver men_z069_driver
= {
160 .owner
= THIS_MODULE
,
162 .probe
= men_z069_probe
,
163 .remove
= men_z069_remove
,
164 .id_table
= men_z069_ids
,
166 module_mcb_driver(men_z069_driver
);
168 MODULE_AUTHOR("Johannes Thumshirn <jth@kernel.org>");
169 MODULE_LICENSE("GPL v2");
170 MODULE_ALIAS("mcb:16z069");
171 MODULE_IMPORT_NS(MCB
);