1 // SPDX-License-Identifier: GPL-2.0-only
3 * Advantech Embedded Controller Watchdog Driver
5 * This driver supports Advantech products with ITE based Embedded Controller.
6 * It does not support Advantech products with other ECs or without EC.
8 * Copyright (C) 2022 Advantech Europe B.V.
11 #include <linux/delay.h>
13 #include <linux/isa.h>
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/moduleparam.h>
17 #include <linux/watchdog.h>
19 #define DRIVER_NAME "advantech_ec_wdt"
22 #define EC_BASE_ADDR 0x299
23 #define EC_ADDR_EXTENT 2
25 /* EC minimum IO access delay in ms */
26 #define EC_MIN_DELAY 10
28 /* EC interface definitions */
29 #define EC_ADDR_CMD (EC_BASE_ADDR + 1)
30 #define EC_ADDR_DATA EC_BASE_ADDR
31 #define EC_CMD_EC_PROBE 0x30
32 #define EC_CMD_COMM 0x89
33 #define EC_CMD_WDT_START 0x28
34 #define EC_CMD_WDT_STOP 0x29
35 #define EC_CMD_WDT_RESET 0x2A
36 #define EC_DAT_EN_DLY_H 0x58
37 #define EC_DAT_EN_DLY_L 0x59
38 #define EC_DAT_RST_DLY_H 0x5E
39 #define EC_DAT_RST_DLY_L 0x5F
42 /* module parameters */
44 #define MAX_TIME 6000 /* 100 minutes */
45 #define DEFAULT_TIME 60
47 static unsigned int timeout
;
48 static ktime_t ec_timestamp
;
50 module_param(timeout
, uint
, 0);
51 MODULE_PARM_DESC(timeout
,
52 "Default Watchdog timer setting (" __MODULE_STRING(DEFAULT_TIME
) "s). The range is from " __MODULE_STRING(MIN_TIME
) " to " __MODULE_STRING(MAX_TIME
) ".");
54 static void adv_ec_wdt_timing_gate(void)
56 ktime_t time_cur
, time_delta
;
58 /* ensure minimum delay between IO accesses*/
59 time_cur
= ktime_get();
60 time_delta
= ktime_to_ms(ktime_sub(time_cur
, ec_timestamp
));
61 if (time_delta
< EC_MIN_DELAY
) {
62 time_delta
= EC_MIN_DELAY
- time_delta
;
63 usleep_range(time_delta
* 1000, (time_delta
+ 1) * 1000);
65 ec_timestamp
= ktime_get();
68 static void adv_ec_wdt_outb(unsigned char value
, unsigned short port
)
70 adv_ec_wdt_timing_gate();
74 static unsigned char adv_ec_wdt_inb(unsigned short port
)
76 adv_ec_wdt_timing_gate();
80 static int adv_ec_wdt_ping(struct watchdog_device
*wdd
)
82 adv_ec_wdt_outb(EC_CMD_WDT_RESET
, EC_ADDR_CMD
);
86 static int adv_ec_wdt_set_timeout(struct watchdog_device
*wdd
, unsigned int t
)
90 /* scale time to EC 100 ms base */
93 /* reset enable delay, just in case it was set by BIOS etc. */
94 adv_ec_wdt_outb(EC_CMD_COMM
, EC_ADDR_CMD
);
95 adv_ec_wdt_outb(EC_DAT_EN_DLY_H
, EC_ADDR_DATA
);
96 adv_ec_wdt_outb(0, EC_ADDR_DATA
);
98 adv_ec_wdt_outb(EC_CMD_COMM
, EC_ADDR_CMD
);
99 adv_ec_wdt_outb(EC_DAT_EN_DLY_L
, EC_ADDR_DATA
);
100 adv_ec_wdt_outb(0, EC_ADDR_DATA
);
102 /* set reset delay */
103 adv_ec_wdt_outb(EC_CMD_COMM
, EC_ADDR_CMD
);
104 adv_ec_wdt_outb(EC_DAT_RST_DLY_H
, EC_ADDR_DATA
);
105 adv_ec_wdt_outb(val
>> 8, EC_ADDR_DATA
);
107 adv_ec_wdt_outb(EC_CMD_COMM
, EC_ADDR_CMD
);
108 adv_ec_wdt_outb(EC_DAT_RST_DLY_L
, EC_ADDR_DATA
);
109 adv_ec_wdt_outb(val
& 0xFF, EC_ADDR_DATA
);
115 static int adv_ec_wdt_start(struct watchdog_device
*wdd
)
117 adv_ec_wdt_set_timeout(wdd
, wdd
->timeout
);
118 adv_ec_wdt_outb(EC_CMD_WDT_START
, EC_ADDR_CMD
);
123 static int adv_ec_wdt_stop(struct watchdog_device
*wdd
)
125 adv_ec_wdt_outb(EC_CMD_WDT_STOP
, EC_ADDR_CMD
);
130 static const struct watchdog_info adv_ec_wdt_info
= {
131 .identity
= DRIVER_NAME
,
132 .options
= WDIOF_SETTIMEOUT
|
137 static const struct watchdog_ops adv_ec_wdt_ops
= {
138 .owner
= THIS_MODULE
,
139 .start
= adv_ec_wdt_start
,
140 .stop
= adv_ec_wdt_stop
,
141 .ping
= adv_ec_wdt_ping
,
142 .set_timeout
= adv_ec_wdt_set_timeout
,
145 static struct watchdog_device adv_ec_wdt_dev
= {
146 .info
= &adv_ec_wdt_info
,
147 .ops
= &adv_ec_wdt_ops
,
148 .min_timeout
= MIN_TIME
,
149 .max_timeout
= MAX_TIME
,
150 .timeout
= DEFAULT_TIME
,
153 static int adv_ec_wdt_probe(struct device
*dev
, unsigned int id
)
155 if (!devm_request_region(dev
, EC_BASE_ADDR
, EC_ADDR_EXTENT
, dev_name(dev
))) {
156 dev_err(dev
, "Unable to lock port addresses (0x%X-0x%X)\n",
157 EC_BASE_ADDR
, EC_BASE_ADDR
+ EC_ADDR_EXTENT
);
161 watchdog_init_timeout(&adv_ec_wdt_dev
, timeout
, dev
);
162 watchdog_stop_on_reboot(&adv_ec_wdt_dev
);
163 watchdog_stop_on_unregister(&adv_ec_wdt_dev
);
165 return devm_watchdog_register_device(dev
, &adv_ec_wdt_dev
);
168 static struct isa_driver adv_ec_wdt_driver
= {
169 .probe
= adv_ec_wdt_probe
,
175 static int __init
adv_ec_wdt_init(void)
179 /* quick probe for EC */
180 if (!request_region(EC_BASE_ADDR
, EC_ADDR_EXTENT
, DRIVER_NAME
))
183 adv_ec_wdt_outb(EC_CMD_EC_PROBE
, EC_ADDR_CMD
);
184 val
= adv_ec_wdt_inb(EC_ADDR_DATA
);
185 release_region(EC_BASE_ADDR
, EC_ADDR_EXTENT
);
190 return isa_register_driver(&adv_ec_wdt_driver
, 1);
193 static void __exit
adv_ec_wdt_exit(void)
195 isa_unregister_driver(&adv_ec_wdt_driver
);
198 module_init(adv_ec_wdt_init
);
199 module_exit(adv_ec_wdt_exit
);
201 MODULE_AUTHOR("Thomas Kastner <thomas.kastner@advantech.com>");
202 MODULE_DESCRIPTION("Advantech Embedded Controller Watchdog Device Driver");
203 MODULE_LICENSE("GPL");
204 MODULE_VERSION("20221019");
205 MODULE_ALIAS("isa:" DRIVER_NAME
);