xref: /openbmc/linux/drivers/watchdog/mlx_wdt.c (revision dd29cb4b)
1c60923ddSMichael Shych // SPDX-License-Identifier: GPL-2.0+
2c60923ddSMichael Shych /*
3c60923ddSMichael Shych  * Mellanox watchdog driver
4c60923ddSMichael Shych  *
5c60923ddSMichael Shych  * Copyright (C) 2019 Mellanox Technologies
6c60923ddSMichael Shych  * Copyright (C) 2019 Michael Shych <mshych@mellanox.com>
7c60923ddSMichael Shych  */
8c60923ddSMichael Shych 
9c60923ddSMichael Shych #include <linux/bitops.h>
10c60923ddSMichael Shych #include <linux/device.h>
11c60923ddSMichael Shych #include <linux/errno.h>
12c60923ddSMichael Shych #include <linux/log2.h>
13c60923ddSMichael Shych #include <linux/module.h>
14c60923ddSMichael Shych #include <linux/platform_data/mlxreg.h>
15c60923ddSMichael Shych #include <linux/platform_device.h>
16c60923ddSMichael Shych #include <linux/regmap.h>
17c60923ddSMichael Shych #include <linux/spinlock.h>
18c60923ddSMichael Shych #include <linux/types.h>
19c60923ddSMichael Shych #include <linux/watchdog.h>
20c60923ddSMichael Shych 
21c60923ddSMichael Shych #define MLXREG_WDT_CLOCK_SCALE		1000
22c60923ddSMichael Shych #define MLXREG_WDT_MAX_TIMEOUT_TYPE1	32
23c60923ddSMichael Shych #define MLXREG_WDT_MAX_TIMEOUT_TYPE2	255
24eee85114SMichael Shych #define MLXREG_WDT_MAX_TIMEOUT_TYPE3	65535
25c60923ddSMichael Shych #define MLXREG_WDT_MIN_TIMEOUT		1
26c60923ddSMichael Shych #define MLXREG_WDT_OPTIONS_BASE (WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | \
27c60923ddSMichael Shych 				 WDIOF_SETTIMEOUT)
28c60923ddSMichael Shych 
29c60923ddSMichael Shych /**
30c60923ddSMichael Shych  * struct mlxreg_wdt - wd private data:
31c60923ddSMichael Shych  *
32c60923ddSMichael Shych  * @wdd:	watchdog device;
33c60923ddSMichael Shych  * @device:	basic device;
34c60923ddSMichael Shych  * @pdata:	data received from platform driver;
35c60923ddSMichael Shych  * @regmap:	register map of parent device;
36c60923ddSMichael Shych  * @timeout:	defined timeout in sec.;
37c60923ddSMichael Shych  * @action_idx:	index for direct access to action register;
38c60923ddSMichael Shych  * @timeout_idx:index for direct access to TO register;
39c60923ddSMichael Shych  * @tleft_idx:	index for direct access to time left register;
40c60923ddSMichael Shych  * @ping_idx:	index for direct access to ping register;
41c60923ddSMichael Shych  * @reset_idx:	index for direct access to reset cause register;
42c60923ddSMichael Shych  * @wd_type:	watchdog HW type;
43c60923ddSMichael Shych  */
44c60923ddSMichael Shych struct mlxreg_wdt {
45c60923ddSMichael Shych 	struct watchdog_device wdd;
46c60923ddSMichael Shych 	struct mlxreg_core_platform_data *pdata;
47c60923ddSMichael Shych 	void *regmap;
48c60923ddSMichael Shych 	int action_idx;
49c60923ddSMichael Shych 	int timeout_idx;
50c60923ddSMichael Shych 	int tleft_idx;
51c60923ddSMichael Shych 	int ping_idx;
52c60923ddSMichael Shych 	int reset_idx;
53eee85114SMichael Shych 	int regmap_val_sz;
54c60923ddSMichael Shych 	enum mlxreg_wdt_type wdt_type;
55c60923ddSMichael Shych };
56c60923ddSMichael Shych 
mlxreg_wdt_check_card_reset(struct mlxreg_wdt * wdt)57c60923ddSMichael Shych static void mlxreg_wdt_check_card_reset(struct mlxreg_wdt *wdt)
58c60923ddSMichael Shych {
59c60923ddSMichael Shych 	struct mlxreg_core_data *reg_data;
60c60923ddSMichael Shych 	u32 regval;
61c60923ddSMichael Shych 	int rc;
62c60923ddSMichael Shych 
63c60923ddSMichael Shych 	if (wdt->reset_idx == -EINVAL)
64c60923ddSMichael Shych 		return;
65c60923ddSMichael Shych 
66c60923ddSMichael Shych 	if (!(wdt->wdd.info->options & WDIOF_CARDRESET))
67c60923ddSMichael Shych 		return;
68c60923ddSMichael Shych 
69c60923ddSMichael Shych 	reg_data = &wdt->pdata->data[wdt->reset_idx];
70c60923ddSMichael Shych 	rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
71c60923ddSMichael Shych 	if (!rc) {
72c60923ddSMichael Shych 		if (regval & ~reg_data->mask) {
73c60923ddSMichael Shych 			wdt->wdd.bootstatus = WDIOF_CARDRESET;
74c60923ddSMichael Shych 			dev_info(wdt->wdd.parent,
75c60923ddSMichael Shych 				 "watchdog previously reset the CPU\n");
76c60923ddSMichael Shych 		}
77c60923ddSMichael Shych 	}
78c60923ddSMichael Shych }
79c60923ddSMichael Shych 
mlxreg_wdt_start(struct watchdog_device * wdd)80c60923ddSMichael Shych static int mlxreg_wdt_start(struct watchdog_device *wdd)
81c60923ddSMichael Shych {
82c60923ddSMichael Shych 	struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
83c60923ddSMichael Shych 	struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->action_idx];
84c60923ddSMichael Shych 
85c60923ddSMichael Shych 	return regmap_update_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
86c60923ddSMichael Shych 				  BIT(reg_data->bit));
87c60923ddSMichael Shych }
88c60923ddSMichael Shych 
mlxreg_wdt_stop(struct watchdog_device * wdd)89c60923ddSMichael Shych static int mlxreg_wdt_stop(struct watchdog_device *wdd)
90c60923ddSMichael Shych {
91c60923ddSMichael Shych 	struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
92c60923ddSMichael Shych 	struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->action_idx];
93c60923ddSMichael Shych 
94c60923ddSMichael Shych 	return regmap_update_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
95c60923ddSMichael Shych 				  ~BIT(reg_data->bit));
96c60923ddSMichael Shych }
97c60923ddSMichael Shych 
mlxreg_wdt_ping(struct watchdog_device * wdd)98c60923ddSMichael Shych static int mlxreg_wdt_ping(struct watchdog_device *wdd)
99c60923ddSMichael Shych {
100c60923ddSMichael Shych 	struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
101c60923ddSMichael Shych 	struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->ping_idx];
102c60923ddSMichael Shych 
103*dd29cb4bSPhilipp Zabel 	return regmap_write_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
104*dd29cb4bSPhilipp Zabel 				 BIT(reg_data->bit));
105c60923ddSMichael Shych }
106c60923ddSMichael Shych 
mlxreg_wdt_set_timeout(struct watchdog_device * wdd,unsigned int timeout)107c60923ddSMichael Shych static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd,
108c60923ddSMichael Shych 				  unsigned int timeout)
109c60923ddSMichael Shych {
110c60923ddSMichael Shych 	struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
111c60923ddSMichael Shych 	struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->timeout_idx];
112c60923ddSMichael Shych 	u32 regval, set_time, hw_timeout;
113c60923ddSMichael Shych 	int rc;
114c60923ddSMichael Shych 
115eee85114SMichael Shych 	switch (wdt->wdt_type) {
116eee85114SMichael Shych 	case MLX_WDT_TYPE1:
117c60923ddSMichael Shych 		rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
118c60923ddSMichael Shych 		if (rc)
119c60923ddSMichael Shych 			return rc;
120c60923ddSMichael Shych 
121c60923ddSMichael Shych 		hw_timeout = order_base_2(timeout * MLXREG_WDT_CLOCK_SCALE);
122c60923ddSMichael Shych 		regval = (regval & reg_data->mask) | hw_timeout;
123c60923ddSMichael Shych 		/* Rowndown to actual closest number of sec. */
124c60923ddSMichael Shych 		set_time = BIT(hw_timeout) / MLXREG_WDT_CLOCK_SCALE;
125eee85114SMichael Shych 		rc = regmap_write(wdt->regmap, reg_data->reg, regval);
126eee85114SMichael Shych 		break;
127eee85114SMichael Shych 	case MLX_WDT_TYPE2:
128c60923ddSMichael Shych 		set_time = timeout;
129eee85114SMichael Shych 		rc = regmap_write(wdt->regmap, reg_data->reg, timeout);
130eee85114SMichael Shych 		break;
131eee85114SMichael Shych 	case MLX_WDT_TYPE3:
132eee85114SMichael Shych 		/* WD_TYPE3 has 2B set time register */
133eee85114SMichael Shych 		set_time = timeout;
134eee85114SMichael Shych 		if (wdt->regmap_val_sz == 1) {
135eee85114SMichael Shych 			regval = timeout & 0xff;
136eee85114SMichael Shych 			rc = regmap_write(wdt->regmap, reg_data->reg, regval);
137eee85114SMichael Shych 			if (!rc) {
138eee85114SMichael Shych 				regval = (timeout & 0xff00) >> 8;
139eee85114SMichael Shych 				rc = regmap_write(wdt->regmap,
140eee85114SMichael Shych 						reg_data->reg + 1, regval);
141eee85114SMichael Shych 			}
142eee85114SMichael Shych 		} else {
143eee85114SMichael Shych 			rc = regmap_write(wdt->regmap, reg_data->reg, timeout);
144eee85114SMichael Shych 		}
145eee85114SMichael Shych 		break;
146eee85114SMichael Shych 	default:
147eee85114SMichael Shych 		return -EINVAL;
148c60923ddSMichael Shych 	}
149c60923ddSMichael Shych 
150c60923ddSMichael Shych 	wdd->timeout = set_time;
151c60923ddSMichael Shych 	if (!rc) {
152c60923ddSMichael Shych 		/*
153c60923ddSMichael Shych 		 * Restart watchdog with new timeout period
154c60923ddSMichael Shych 		 * if watchdog is already started.
155c60923ddSMichael Shych 		 */
156c60923ddSMichael Shych 		if (watchdog_active(wdd)) {
157c60923ddSMichael Shych 			rc = mlxreg_wdt_stop(wdd);
158c60923ddSMichael Shych 			if (!rc)
159c60923ddSMichael Shych 				rc = mlxreg_wdt_start(wdd);
160c60923ddSMichael Shych 		}
161c60923ddSMichael Shych 	}
162c60923ddSMichael Shych 
163c60923ddSMichael Shych 	return rc;
164c60923ddSMichael Shych }
165c60923ddSMichael Shych 
mlxreg_wdt_get_timeleft(struct watchdog_device * wdd)166c60923ddSMichael Shych static unsigned int mlxreg_wdt_get_timeleft(struct watchdog_device *wdd)
167c60923ddSMichael Shych {
168c60923ddSMichael Shych 	struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
169c60923ddSMichael Shych 	struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->tleft_idx];
170eee85114SMichael Shych 	u32 regval, msb, lsb;
171c60923ddSMichael Shych 	int rc;
172c60923ddSMichael Shych 
173eee85114SMichael Shych 	if (wdt->wdt_type == MLX_WDT_TYPE2) {
174c60923ddSMichael Shych 		rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
175eee85114SMichael Shych 	} else {
176eee85114SMichael Shych 		/* WD_TYPE3 has 2 byte timeleft register */
177eee85114SMichael Shych 		if (wdt->regmap_val_sz == 1) {
178eee85114SMichael Shych 			rc = regmap_read(wdt->regmap, reg_data->reg, &lsb);
179eee85114SMichael Shych 			if (!rc) {
180eee85114SMichael Shych 				rc = regmap_read(wdt->regmap,
181eee85114SMichael Shych 						reg_data->reg + 1, &msb);
182eee85114SMichael Shych 				regval = (msb & 0xff) << 8 | (lsb & 0xff);
183eee85114SMichael Shych 			}
184eee85114SMichael Shych 		} else {
185eee85114SMichael Shych 			rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
186eee85114SMichael Shych 		}
187eee85114SMichael Shych 	}
188eee85114SMichael Shych 
189c60923ddSMichael Shych 	/* Return 0 timeleft in case of failure register read. */
190c60923ddSMichael Shych 	return rc == 0 ? regval : 0;
191c60923ddSMichael Shych }
192c60923ddSMichael Shych 
193c60923ddSMichael Shych static const struct watchdog_ops mlxreg_wdt_ops_type1 = {
194c60923ddSMichael Shych 	.start		= mlxreg_wdt_start,
195c60923ddSMichael Shych 	.stop		= mlxreg_wdt_stop,
196c60923ddSMichael Shych 	.ping		= mlxreg_wdt_ping,
197c60923ddSMichael Shych 	.set_timeout	= mlxreg_wdt_set_timeout,
198c60923ddSMichael Shych 	.owner		= THIS_MODULE,
199c60923ddSMichael Shych };
200c60923ddSMichael Shych 
201c60923ddSMichael Shych static const struct watchdog_ops mlxreg_wdt_ops_type2 = {
202c60923ddSMichael Shych 	.start		= mlxreg_wdt_start,
203c60923ddSMichael Shych 	.stop		= mlxreg_wdt_stop,
204c60923ddSMichael Shych 	.ping		= mlxreg_wdt_ping,
205c60923ddSMichael Shych 	.set_timeout	= mlxreg_wdt_set_timeout,
206c60923ddSMichael Shych 	.get_timeleft	= mlxreg_wdt_get_timeleft,
207c60923ddSMichael Shych 	.owner		= THIS_MODULE,
208c60923ddSMichael Shych };
209c60923ddSMichael Shych 
210c60923ddSMichael Shych static const struct watchdog_info mlxreg_wdt_main_info = {
211c60923ddSMichael Shych 	.options	= MLXREG_WDT_OPTIONS_BASE
212c60923ddSMichael Shych 			| WDIOF_CARDRESET,
213c60923ddSMichael Shych 	.identity	= "mlx-wdt-main",
214c60923ddSMichael Shych };
215c60923ddSMichael Shych 
216c60923ddSMichael Shych static const struct watchdog_info mlxreg_wdt_aux_info = {
217c60923ddSMichael Shych 	.options	= MLXREG_WDT_OPTIONS_BASE
218c60923ddSMichael Shych 			| WDIOF_ALARMONLY,
219c60923ddSMichael Shych 	.identity	= "mlx-wdt-aux",
220c60923ddSMichael Shych };
221c60923ddSMichael Shych 
mlxreg_wdt_config(struct mlxreg_wdt * wdt,struct mlxreg_core_platform_data * pdata)222c60923ddSMichael Shych static void mlxreg_wdt_config(struct mlxreg_wdt *wdt,
223c60923ddSMichael Shych 			      struct mlxreg_core_platform_data *pdata)
224c60923ddSMichael Shych {
225c60923ddSMichael Shych 	struct mlxreg_core_data *data = pdata->data;
226c60923ddSMichael Shych 	int i;
227c60923ddSMichael Shych 
228c60923ddSMichael Shych 	wdt->reset_idx = -EINVAL;
229c60923ddSMichael Shych 	for (i = 0; i < pdata->counter; i++, data++) {
230c60923ddSMichael Shych 		if (strnstr(data->label, "action", sizeof(data->label)))
231c60923ddSMichael Shych 			wdt->action_idx = i;
232c60923ddSMichael Shych 		else if (strnstr(data->label, "timeout", sizeof(data->label)))
233c60923ddSMichael Shych 			wdt->timeout_idx = i;
234c60923ddSMichael Shych 		else if (strnstr(data->label, "timeleft", sizeof(data->label)))
235c60923ddSMichael Shych 			wdt->tleft_idx = i;
236c60923ddSMichael Shych 		else if (strnstr(data->label, "ping", sizeof(data->label)))
237c60923ddSMichael Shych 			wdt->ping_idx = i;
238c60923ddSMichael Shych 		else if (strnstr(data->label, "reset", sizeof(data->label)))
239c60923ddSMichael Shych 			wdt->reset_idx = i;
240c60923ddSMichael Shych 	}
241c60923ddSMichael Shych 
242c60923ddSMichael Shych 	wdt->pdata = pdata;
243c60923ddSMichael Shych 	if (strnstr(pdata->identity, mlxreg_wdt_main_info.identity,
244c60923ddSMichael Shych 		    sizeof(mlxreg_wdt_main_info.identity)))
245c60923ddSMichael Shych 		wdt->wdd.info = &mlxreg_wdt_main_info;
246c60923ddSMichael Shych 	else
247c60923ddSMichael Shych 		wdt->wdd.info = &mlxreg_wdt_aux_info;
248c60923ddSMichael Shych 
249c60923ddSMichael Shych 	wdt->wdt_type = pdata->version;
250eee85114SMichael Shych 	switch (wdt->wdt_type) {
251eee85114SMichael Shych 	case MLX_WDT_TYPE1:
252c60923ddSMichael Shych 		wdt->wdd.ops = &mlxreg_wdt_ops_type1;
253c60923ddSMichael Shych 		wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE1;
254eee85114SMichael Shych 		break;
255eee85114SMichael Shych 	case MLX_WDT_TYPE2:
256eee85114SMichael Shych 		wdt->wdd.ops = &mlxreg_wdt_ops_type2;
257eee85114SMichael Shych 		wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE2;
258eee85114SMichael Shych 		break;
259eee85114SMichael Shych 	case MLX_WDT_TYPE3:
260eee85114SMichael Shych 		wdt->wdd.ops = &mlxreg_wdt_ops_type2;
261eee85114SMichael Shych 		wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE3;
262eee85114SMichael Shych 		break;
263eee85114SMichael Shych 	default:
264eee85114SMichael Shych 		break;
265c60923ddSMichael Shych 	}
266eee85114SMichael Shych 
267c60923ddSMichael Shych 	wdt->wdd.min_timeout = MLXREG_WDT_MIN_TIMEOUT;
268c60923ddSMichael Shych }
269c60923ddSMichael Shych 
mlxreg_wdt_init_timeout(struct mlxreg_wdt * wdt,struct mlxreg_core_platform_data * pdata)270c60923ddSMichael Shych static int mlxreg_wdt_init_timeout(struct mlxreg_wdt *wdt,
271c60923ddSMichael Shych 				   struct mlxreg_core_platform_data *pdata)
272c60923ddSMichael Shych {
273c60923ddSMichael Shych 	u32 timeout;
274c60923ddSMichael Shych 
275c60923ddSMichael Shych 	timeout = pdata->data[wdt->timeout_idx].health_cntr;
276c60923ddSMichael Shych 	return mlxreg_wdt_set_timeout(&wdt->wdd, timeout);
277c60923ddSMichael Shych }
278c60923ddSMichael Shych 
mlxreg_wdt_probe(struct platform_device * pdev)279c60923ddSMichael Shych static int mlxreg_wdt_probe(struct platform_device *pdev)
280c60923ddSMichael Shych {
281099e3039SGuenter Roeck 	struct device *dev = &pdev->dev;
282c60923ddSMichael Shych 	struct mlxreg_core_platform_data *pdata;
283c60923ddSMichael Shych 	struct mlxreg_wdt *wdt;
284c60923ddSMichael Shych 	int rc;
285c60923ddSMichael Shych 
286099e3039SGuenter Roeck 	pdata = dev_get_platdata(dev);
287c60923ddSMichael Shych 	if (!pdata) {
288099e3039SGuenter Roeck 		dev_err(dev, "Failed to get platform data.\n");
289c60923ddSMichael Shych 		return -EINVAL;
290c60923ddSMichael Shych 	}
291099e3039SGuenter Roeck 	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
292c60923ddSMichael Shych 	if (!wdt)
293c60923ddSMichael Shych 		return -ENOMEM;
294c60923ddSMichael Shych 
295099e3039SGuenter Roeck 	wdt->wdd.parent = dev;
296c60923ddSMichael Shych 	wdt->regmap = pdata->regmap;
297eee85114SMichael Shych 	rc = regmap_get_val_bytes(wdt->regmap);
298eee85114SMichael Shych 	if (rc < 0)
299eee85114SMichael Shych 		return -EINVAL;
300eee85114SMichael Shych 
301eee85114SMichael Shych 	wdt->regmap_val_sz = rc;
302c60923ddSMichael Shych 	mlxreg_wdt_config(wdt, pdata);
303c60923ddSMichael Shych 
304c60923ddSMichael Shych 	if ((pdata->features & MLXREG_CORE_WD_FEATURE_NOWAYOUT))
305c60923ddSMichael Shych 		watchdog_set_nowayout(&wdt->wdd, WATCHDOG_NOWAYOUT);
306c60923ddSMichael Shych 	watchdog_stop_on_reboot(&wdt->wdd);
307c60923ddSMichael Shych 	watchdog_stop_on_unregister(&wdt->wdd);
308c60923ddSMichael Shych 	watchdog_set_drvdata(&wdt->wdd, wdt);
309c60923ddSMichael Shych 	rc = mlxreg_wdt_init_timeout(wdt, pdata);
310c60923ddSMichael Shych 	if (rc)
311c60923ddSMichael Shych 		goto register_error;
312c60923ddSMichael Shych 
313c60923ddSMichael Shych 	if ((pdata->features & MLXREG_CORE_WD_FEATURE_START_AT_BOOT)) {
314c60923ddSMichael Shych 		rc = mlxreg_wdt_start(&wdt->wdd);
315c60923ddSMichael Shych 		if (rc)
316c60923ddSMichael Shych 			goto register_error;
317c60923ddSMichael Shych 		set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
318c60923ddSMichael Shych 	}
319c60923ddSMichael Shych 	mlxreg_wdt_check_card_reset(wdt);
320099e3039SGuenter Roeck 	rc = devm_watchdog_register_device(dev, &wdt->wdd);
321c60923ddSMichael Shych 
322c60923ddSMichael Shych register_error:
323c60923ddSMichael Shych 	if (rc)
324099e3039SGuenter Roeck 		dev_err(dev, "Cannot register watchdog device (err=%d)\n", rc);
325c60923ddSMichael Shych 	return rc;
326c60923ddSMichael Shych }
327c60923ddSMichael Shych 
328c60923ddSMichael Shych static struct platform_driver mlxreg_wdt_driver = {
329c60923ddSMichael Shych 	.probe	= mlxreg_wdt_probe,
330c60923ddSMichael Shych 	.driver	= {
331c60923ddSMichael Shych 			.name = "mlx-wdt",
332c60923ddSMichael Shych 	},
333c60923ddSMichael Shych };
334c60923ddSMichael Shych 
335c60923ddSMichael Shych module_platform_driver(mlxreg_wdt_driver);
336c60923ddSMichael Shych 
337c60923ddSMichael Shych MODULE_AUTHOR("Michael Shych <michaelsh@mellanox.com>");
338c60923ddSMichael Shych MODULE_DESCRIPTION("Mellanox watchdog driver");
339c60923ddSMichael Shych MODULE_LICENSE("GPL");
340c60923ddSMichael Shych MODULE_ALIAS("platform:mlx-wdt");
341