2 * MEN 14F021P00 Board Management Controller (BMC) Watchdog Driver.
4 * Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
12 #include <linux/kernel.h>
13 #include <linux/device.h>
14 #include <linux/module.h>
15 #include <linux/watchdog.h>
16 #include <linux/platform_device.h>
17 #include <linux/i2c.h>
19 #define DEVNAME "menf21bmc_wdt"
21 #define BMC_CMD_WD_ON 0x11
22 #define BMC_CMD_WD_OFF 0x12
23 #define BMC_CMD_WD_TRIG 0x13
24 #define BMC_CMD_WD_TIME 0x14
25 #define BMC_CMD_WD_STATE 0x17
26 #define BMC_WD_OFF_VAL 0x69
27 #define BMC_CMD_RST_RSN 0x92
29 #define BMC_WD_TIMEOUT_MIN 1 /* in sec */
30 #define BMC_WD_TIMEOUT_MAX 6553 /* in sec */
32 static bool nowayout
= WATCHDOG_NOWAYOUT
;
33 module_param(nowayout
, bool, 0);
34 MODULE_PARM_DESC(nowayout
, "Watchdog cannot be stopped once started (default="
35 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
37 struct menf21bmc_wdt
{
38 struct watchdog_device wdt
;
39 struct i2c_client
*i2c_client
;
42 static int menf21bmc_wdt_set_bootstatus(struct menf21bmc_wdt
*data
)
46 rst_rsn
= i2c_smbus_read_byte_data(data
->i2c_client
, BMC_CMD_RST_RSN
);
51 data
->wdt
.bootstatus
|= WDIOF_CARDRESET
;
52 else if (rst_rsn
== 0x05)
53 data
->wdt
.bootstatus
|= WDIOF_EXTERN1
;
54 else if (rst_rsn
== 0x06)
55 data
->wdt
.bootstatus
|= WDIOF_EXTERN2
;
56 else if (rst_rsn
== 0x0A)
57 data
->wdt
.bootstatus
|= WDIOF_POWERUNDER
;
62 static int menf21bmc_wdt_start(struct watchdog_device
*wdt
)
64 struct menf21bmc_wdt
*drv_data
= watchdog_get_drvdata(wdt
);
66 return i2c_smbus_write_byte(drv_data
->i2c_client
, BMC_CMD_WD_ON
);
69 static int menf21bmc_wdt_stop(struct watchdog_device
*wdt
)
71 struct menf21bmc_wdt
*drv_data
= watchdog_get_drvdata(wdt
);
73 return i2c_smbus_write_byte_data(drv_data
->i2c_client
,
74 BMC_CMD_WD_OFF
, BMC_WD_OFF_VAL
);
78 menf21bmc_wdt_settimeout(struct watchdog_device
*wdt
, unsigned int timeout
)
81 struct menf21bmc_wdt
*drv_data
= watchdog_get_drvdata(wdt
);
84 * BMC Watchdog does have a resolution of 100ms.
85 * Watchdog API defines the timeout in seconds, so we have to
88 ret
= i2c_smbus_write_word_data(drv_data
->i2c_client
,
89 BMC_CMD_WD_TIME
, timeout
* 10);
93 wdt
->timeout
= timeout
;
98 static int menf21bmc_wdt_ping(struct watchdog_device
*wdt
)
100 struct menf21bmc_wdt
*drv_data
= watchdog_get_drvdata(wdt
);
102 return i2c_smbus_write_byte(drv_data
->i2c_client
, BMC_CMD_WD_TRIG
);
105 static const struct watchdog_info menf21bmc_wdt_info
= {
106 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
,
110 static const struct watchdog_ops menf21bmc_wdt_ops
= {
111 .owner
= THIS_MODULE
,
112 .start
= menf21bmc_wdt_start
,
113 .stop
= menf21bmc_wdt_stop
,
114 .ping
= menf21bmc_wdt_ping
,
115 .set_timeout
= menf21bmc_wdt_settimeout
,
118 static int menf21bmc_wdt_probe(struct platform_device
*pdev
)
120 int ret
, bmc_timeout
;
121 struct menf21bmc_wdt
*drv_data
;
122 struct i2c_client
*i2c_client
= to_i2c_client(pdev
->dev
.parent
);
124 drv_data
= devm_kzalloc(&pdev
->dev
,
125 sizeof(struct menf21bmc_wdt
), GFP_KERNEL
);
129 drv_data
->wdt
.ops
= &menf21bmc_wdt_ops
;
130 drv_data
->wdt
.info
= &menf21bmc_wdt_info
;
131 drv_data
->wdt
.min_timeout
= BMC_WD_TIMEOUT_MIN
;
132 drv_data
->wdt
.max_timeout
= BMC_WD_TIMEOUT_MAX
;
133 drv_data
->i2c_client
= i2c_client
;
136 * Get the current wdt timeout value from the BMC because
137 * the BMC will save the value set before if the system restarts.
139 bmc_timeout
= i2c_smbus_read_word_data(drv_data
->i2c_client
,
141 if (bmc_timeout
< 0) {
142 dev_err(&pdev
->dev
, "failed to get current WDT timeout\n");
146 watchdog_init_timeout(&drv_data
->wdt
, bmc_timeout
/ 10, &pdev
->dev
);
147 watchdog_set_nowayout(&drv_data
->wdt
, nowayout
);
148 watchdog_set_drvdata(&drv_data
->wdt
, drv_data
);
149 platform_set_drvdata(pdev
, drv_data
);
151 ret
= menf21bmc_wdt_set_bootstatus(drv_data
);
153 dev_err(&pdev
->dev
, "failed to set Watchdog bootstatus\n");
157 ret
= watchdog_register_device(&drv_data
->wdt
);
159 dev_err(&pdev
->dev
, "failed to register Watchdog device\n");
163 dev_info(&pdev
->dev
, "MEN 14F021P00 BMC Watchdog device enabled\n");
168 static int menf21bmc_wdt_remove(struct platform_device
*pdev
)
170 struct menf21bmc_wdt
*drv_data
= platform_get_drvdata(pdev
);
173 "Unregister MEN 14F021P00 BMC Watchdog device, board may reset\n");
175 watchdog_unregister_device(&drv_data
->wdt
);
180 static void menf21bmc_wdt_shutdown(struct platform_device
*pdev
)
182 struct menf21bmc_wdt
*drv_data
= platform_get_drvdata(pdev
);
184 i2c_smbus_write_word_data(drv_data
->i2c_client
,
185 BMC_CMD_WD_OFF
, BMC_WD_OFF_VAL
);
188 static struct platform_driver menf21bmc_wdt
= {
192 .probe
= menf21bmc_wdt_probe
,
193 .remove
= menf21bmc_wdt_remove
,
194 .shutdown
= menf21bmc_wdt_shutdown
,
197 module_platform_driver(menf21bmc_wdt
);
199 MODULE_DESCRIPTION("MEN 14F021P00 BMC Watchdog driver");
200 MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
201 MODULE_LICENSE("GPL v2");
202 MODULE_ALIAS("platform:menf21bmc_wdt");