1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 *	Advantech Embedded Controller Watchdog Driver
4 *
5 *	This driver supports Advantech products with ITE based Embedded Controller.
6 *	It does not support Advantech products with other ECs or without EC.
7 *
8 *	Copyright (C) 2022 Advantech Europe B.V.
9 */
10
11#include <linux/delay.h>
12#include <linux/io.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>
18
19#define DRIVER_NAME		"advantech_ec_wdt"
20
21/* EC IO region */
22#define EC_BASE_ADDR		0x299
23#define EC_ADDR_EXTENT		2
24
25/* EC minimum IO access delay in ms */
26#define EC_MIN_DELAY		10
27
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
40#define EC_MAGIC		0x95
41
42/* module parameters */
43#define MIN_TIME		1
44#define MAX_TIME		6000 /* 100 minutes */
45#define DEFAULT_TIME		60
46
47static unsigned int timeout;
48static ktime_t ec_timestamp;
49
50module_param(timeout, uint, 0);
51MODULE_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) ".");
53
54static void adv_ec_wdt_timing_gate(void)
55{
56	ktime_t time_cur, time_delta;
57
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);
64	}
65	ec_timestamp = ktime_get();
66}
67
68static void adv_ec_wdt_outb(unsigned char value, unsigned short port)
69{
70	adv_ec_wdt_timing_gate();
71	outb(value, port);
72}
73
74static unsigned char adv_ec_wdt_inb(unsigned short port)
75{
76	adv_ec_wdt_timing_gate();
77	return inb(port);
78}
79
80static int adv_ec_wdt_ping(struct watchdog_device *wdd)
81{
82	adv_ec_wdt_outb(EC_CMD_WDT_RESET, EC_ADDR_CMD);
83	return 0;
84}
85
86static int adv_ec_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
87{
88	unsigned int val;
89
90	/* scale time to EC 100 ms base */
91	val = t * 10;
92
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);
97
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);
101
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);
106
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);
110
111	wdd->timeout = t;
112	return 0;
113}
114
115static int adv_ec_wdt_start(struct watchdog_device *wdd)
116{
117	adv_ec_wdt_set_timeout(wdd, wdd->timeout);
118	adv_ec_wdt_outb(EC_CMD_WDT_START, EC_ADDR_CMD);
119
120	return 0;
121}
122
123static int adv_ec_wdt_stop(struct watchdog_device *wdd)
124{
125	adv_ec_wdt_outb(EC_CMD_WDT_STOP, EC_ADDR_CMD);
126
127	return 0;
128}
129
130static const struct watchdog_info adv_ec_wdt_info = {
131	.identity =	DRIVER_NAME,
132	.options =	WDIOF_SETTIMEOUT |
133			WDIOF_MAGICCLOSE |
134			WDIOF_KEEPALIVEPING,
135};
136
137static 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,
143};
144
145static 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,
151};
152
153static int adv_ec_wdt_probe(struct device *dev, unsigned int id)
154{
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);
158		return -EBUSY;
159	}
160
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);
164
165	return devm_watchdog_register_device(dev, &adv_ec_wdt_dev);
166}
167
168static struct isa_driver adv_ec_wdt_driver = {
169	.probe		= adv_ec_wdt_probe,
170	.driver		= {
171	.name		= DRIVER_NAME,
172	},
173};
174
175static int __init adv_ec_wdt_init(void)
176{
177	unsigned int val;
178
179	/* quick probe for EC */
180	if (!request_region(EC_BASE_ADDR, EC_ADDR_EXTENT, DRIVER_NAME))
181		return -EBUSY;
182
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);
186
187	if (val != EC_MAGIC)
188		return -ENODEV;
189
190	return isa_register_driver(&adv_ec_wdt_driver, 1);
191}
192
193static void __exit adv_ec_wdt_exit(void)
194{
195	isa_unregister_driver(&adv_ec_wdt_driver);
196}
197
198module_init(adv_ec_wdt_init);
199module_exit(adv_ec_wdt_exit);
200
201MODULE_AUTHOR("Thomas Kastner <thomas.kastner@advantech.com>");
202MODULE_DESCRIPTION("Advantech Embedded Controller Watchdog Device Driver");
203MODULE_LICENSE("GPL");
204MODULE_VERSION("20221019");
205MODULE_ALIAS("isa:" DRIVER_NAME);
206