1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * MEN 14F021P00 Board Management Controller (BMC) Watchdog Driver.
5 * Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH
8 #include <linux/kernel.h>
9 #include <linux/device.h>
10 #include <linux/module.h>
11 #include <linux/watchdog.h>
12 #include <linux/platform_device.h>
13 #include <linux/i2c.h>
15 #define DEVNAME "menf21bmc_wdt"
17 #define BMC_CMD_WD_ON 0x11
18 #define BMC_CMD_WD_OFF 0x12
19 #define BMC_CMD_WD_TRIG 0x13
20 #define BMC_CMD_WD_TIME 0x14
21 #define BMC_CMD_WD_STATE 0x17
22 #define BMC_WD_OFF_VAL 0x69
23 #define BMC_CMD_RST_RSN 0x92
25 #define BMC_WD_TIMEOUT_MIN 1 /* in sec */
26 #define BMC_WD_TIMEOUT_MAX 6553 /* in sec */
28 static bool nowayout
= WATCHDOG_NOWAYOUT
;
29 module_param(nowayout
, bool, 0);
30 MODULE_PARM_DESC(nowayout
, "Watchdog cannot be stopped once started (default="
31 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
33 struct menf21bmc_wdt
{
34 struct watchdog_device wdt
;
35 struct i2c_client
*i2c_client
;
38 static int menf21bmc_wdt_set_bootstatus(struct menf21bmc_wdt
*data
)
42 rst_rsn
= i2c_smbus_read_byte_data(data
->i2c_client
, BMC_CMD_RST_RSN
);
47 data
->wdt
.bootstatus
|= WDIOF_CARDRESET
;
48 else if (rst_rsn
== 0x05)
49 data
->wdt
.bootstatus
|= WDIOF_EXTERN1
;
50 else if (rst_rsn
== 0x06)
51 data
->wdt
.bootstatus
|= WDIOF_EXTERN2
;
52 else if (rst_rsn
== 0x0A)
53 data
->wdt
.bootstatus
|= WDIOF_POWERUNDER
;
58 static int menf21bmc_wdt_start(struct watchdog_device
*wdt
)
60 struct menf21bmc_wdt
*drv_data
= watchdog_get_drvdata(wdt
);
62 return i2c_smbus_write_byte(drv_data
->i2c_client
, BMC_CMD_WD_ON
);
65 static int menf21bmc_wdt_stop(struct watchdog_device
*wdt
)
67 struct menf21bmc_wdt
*drv_data
= watchdog_get_drvdata(wdt
);
69 return i2c_smbus_write_byte_data(drv_data
->i2c_client
,
70 BMC_CMD_WD_OFF
, BMC_WD_OFF_VAL
);
74 menf21bmc_wdt_settimeout(struct watchdog_device
*wdt
, unsigned int timeout
)
77 struct menf21bmc_wdt
*drv_data
= watchdog_get_drvdata(wdt
);
80 * BMC Watchdog does have a resolution of 100ms.
81 * Watchdog API defines the timeout in seconds, so we have to
84 ret
= i2c_smbus_write_word_data(drv_data
->i2c_client
,
85 BMC_CMD_WD_TIME
, timeout
* 10);
89 wdt
->timeout
= timeout
;
94 static int menf21bmc_wdt_ping(struct watchdog_device
*wdt
)
96 struct menf21bmc_wdt
*drv_data
= watchdog_get_drvdata(wdt
);
98 return i2c_smbus_write_byte(drv_data
->i2c_client
, BMC_CMD_WD_TRIG
);
101 static const struct watchdog_info menf21bmc_wdt_info
= {
102 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
,
106 static const struct watchdog_ops menf21bmc_wdt_ops
= {
107 .owner
= THIS_MODULE
,
108 .start
= menf21bmc_wdt_start
,
109 .stop
= menf21bmc_wdt_stop
,
110 .ping
= menf21bmc_wdt_ping
,
111 .set_timeout
= menf21bmc_wdt_settimeout
,
114 static int menf21bmc_wdt_probe(struct platform_device
*pdev
)
116 struct device
*dev
= &pdev
->dev
;
117 int ret
, bmc_timeout
;
118 struct menf21bmc_wdt
*drv_data
;
119 struct i2c_client
*i2c_client
= to_i2c_client(dev
->parent
);
121 drv_data
= devm_kzalloc(dev
, sizeof(struct menf21bmc_wdt
), GFP_KERNEL
);
125 drv_data
->wdt
.ops
= &menf21bmc_wdt_ops
;
126 drv_data
->wdt
.info
= &menf21bmc_wdt_info
;
127 drv_data
->wdt
.min_timeout
= BMC_WD_TIMEOUT_MIN
;
128 drv_data
->wdt
.max_timeout
= BMC_WD_TIMEOUT_MAX
;
129 drv_data
->wdt
.parent
= dev
;
130 drv_data
->i2c_client
= i2c_client
;
133 * Get the current wdt timeout value from the BMC because
134 * the BMC will save the value set before if the system restarts.
136 bmc_timeout
= i2c_smbus_read_word_data(drv_data
->i2c_client
,
138 if (bmc_timeout
< 0) {
139 dev_err(dev
, "failed to get current WDT timeout\n");
143 watchdog_init_timeout(&drv_data
->wdt
, bmc_timeout
/ 10, dev
);
144 watchdog_set_nowayout(&drv_data
->wdt
, nowayout
);
145 watchdog_set_drvdata(&drv_data
->wdt
, drv_data
);
146 platform_set_drvdata(pdev
, drv_data
);
148 ret
= menf21bmc_wdt_set_bootstatus(drv_data
);
150 dev_err(dev
, "failed to set Watchdog bootstatus\n");
154 ret
= devm_watchdog_register_device(dev
, &drv_data
->wdt
);
158 dev_info(dev
, "MEN 14F021P00 BMC Watchdog device enabled\n");
163 static void menf21bmc_wdt_shutdown(struct platform_device
*pdev
)
165 struct menf21bmc_wdt
*drv_data
= platform_get_drvdata(pdev
);
167 i2c_smbus_write_word_data(drv_data
->i2c_client
,
168 BMC_CMD_WD_OFF
, BMC_WD_OFF_VAL
);
171 static struct platform_driver menf21bmc_wdt
= {
175 .probe
= menf21bmc_wdt_probe
,
176 .shutdown
= menf21bmc_wdt_shutdown
,
179 module_platform_driver(menf21bmc_wdt
);
181 MODULE_DESCRIPTION("MEN 14F021P00 BMC Watchdog driver");
182 MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
183 MODULE_LICENSE("GPL v2");
184 MODULE_ALIAS("platform:menf21bmc_wdt");