Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  MEN 14F021P00 Board Management Controller (BMC) Watchdog Driver.
0004  *
0005  *  Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH
0006  */
0007 
0008 #include <linux/kernel.h>
0009 #include <linux/device.h>
0010 #include <linux/module.h>
0011 #include <linux/watchdog.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/i2c.h>
0014 
0015 #define DEVNAME "menf21bmc_wdt"
0016 
0017 #define BMC_CMD_WD_ON       0x11
0018 #define BMC_CMD_WD_OFF      0x12
0019 #define BMC_CMD_WD_TRIG     0x13
0020 #define BMC_CMD_WD_TIME     0x14
0021 #define BMC_CMD_WD_STATE    0x17
0022 #define BMC_WD_OFF_VAL      0x69
0023 #define BMC_CMD_RST_RSN     0x92
0024 
0025 #define BMC_WD_TIMEOUT_MIN  1   /* in sec */
0026 #define BMC_WD_TIMEOUT_MAX  6553    /* in sec */
0027 
0028 static bool nowayout = WATCHDOG_NOWAYOUT;
0029 module_param(nowayout, bool, 0);
0030 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
0031                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0032 
0033 struct menf21bmc_wdt {
0034     struct watchdog_device wdt;
0035     struct i2c_client *i2c_client;
0036 };
0037 
0038 static int menf21bmc_wdt_set_bootstatus(struct menf21bmc_wdt *data)
0039 {
0040     int rst_rsn;
0041 
0042     rst_rsn = i2c_smbus_read_byte_data(data->i2c_client, BMC_CMD_RST_RSN);
0043     if (rst_rsn < 0)
0044         return rst_rsn;
0045 
0046     if (rst_rsn == 0x02)
0047         data->wdt.bootstatus |= WDIOF_CARDRESET;
0048     else if (rst_rsn == 0x05)
0049         data->wdt.bootstatus |= WDIOF_EXTERN1;
0050     else if (rst_rsn == 0x06)
0051         data->wdt.bootstatus |= WDIOF_EXTERN2;
0052     else if (rst_rsn == 0x0A)
0053         data->wdt.bootstatus |= WDIOF_POWERUNDER;
0054 
0055     return 0;
0056 }
0057 
0058 static int menf21bmc_wdt_start(struct watchdog_device *wdt)
0059 {
0060     struct menf21bmc_wdt *drv_data = watchdog_get_drvdata(wdt);
0061 
0062     return i2c_smbus_write_byte(drv_data->i2c_client, BMC_CMD_WD_ON);
0063 }
0064 
0065 static int menf21bmc_wdt_stop(struct watchdog_device *wdt)
0066 {
0067     struct menf21bmc_wdt *drv_data = watchdog_get_drvdata(wdt);
0068 
0069     return i2c_smbus_write_byte_data(drv_data->i2c_client,
0070                      BMC_CMD_WD_OFF, BMC_WD_OFF_VAL);
0071 }
0072 
0073 static int
0074 menf21bmc_wdt_settimeout(struct watchdog_device *wdt, unsigned int timeout)
0075 {
0076     int ret;
0077     struct menf21bmc_wdt *drv_data = watchdog_get_drvdata(wdt);
0078 
0079     /*
0080      *  BMC Watchdog does have a resolution of 100ms.
0081      *  Watchdog API defines the timeout in seconds, so we have to
0082      *  multiply the value.
0083      */
0084     ret = i2c_smbus_write_word_data(drv_data->i2c_client,
0085                     BMC_CMD_WD_TIME, timeout * 10);
0086     if (ret < 0)
0087         return ret;
0088 
0089     wdt->timeout = timeout;
0090 
0091     return 0;
0092 }
0093 
0094 static int menf21bmc_wdt_ping(struct watchdog_device *wdt)
0095 {
0096     struct menf21bmc_wdt *drv_data = watchdog_get_drvdata(wdt);
0097 
0098     return i2c_smbus_write_byte(drv_data->i2c_client, BMC_CMD_WD_TRIG);
0099 }
0100 
0101 static const struct watchdog_info menf21bmc_wdt_info = {
0102     .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
0103     .identity = DEVNAME,
0104 };
0105 
0106 static const struct watchdog_ops menf21bmc_wdt_ops = {
0107     .owner      = THIS_MODULE,
0108     .start      = menf21bmc_wdt_start,
0109     .stop       = menf21bmc_wdt_stop,
0110     .ping       = menf21bmc_wdt_ping,
0111     .set_timeout    = menf21bmc_wdt_settimeout,
0112 };
0113 
0114 static int menf21bmc_wdt_probe(struct platform_device *pdev)
0115 {
0116     struct device *dev = &pdev->dev;
0117     int ret, bmc_timeout;
0118     struct menf21bmc_wdt *drv_data;
0119     struct i2c_client *i2c_client = to_i2c_client(dev->parent);
0120 
0121     drv_data = devm_kzalloc(dev, sizeof(struct menf21bmc_wdt), GFP_KERNEL);
0122     if (!drv_data)
0123         return -ENOMEM;
0124 
0125     drv_data->wdt.ops = &menf21bmc_wdt_ops;
0126     drv_data->wdt.info = &menf21bmc_wdt_info;
0127     drv_data->wdt.min_timeout = BMC_WD_TIMEOUT_MIN;
0128     drv_data->wdt.max_timeout = BMC_WD_TIMEOUT_MAX;
0129     drv_data->wdt.parent = dev;
0130     drv_data->i2c_client = i2c_client;
0131 
0132     /*
0133      * Get the current wdt timeout value from the BMC because
0134      * the BMC will save the value set before if the system restarts.
0135      */
0136     bmc_timeout = i2c_smbus_read_word_data(drv_data->i2c_client,
0137                            BMC_CMD_WD_TIME);
0138     if (bmc_timeout < 0) {
0139         dev_err(dev, "failed to get current WDT timeout\n");
0140         return bmc_timeout;
0141     }
0142 
0143     watchdog_init_timeout(&drv_data->wdt, bmc_timeout / 10, dev);
0144     watchdog_set_nowayout(&drv_data->wdt, nowayout);
0145     watchdog_set_drvdata(&drv_data->wdt, drv_data);
0146     platform_set_drvdata(pdev, drv_data);
0147 
0148     ret = menf21bmc_wdt_set_bootstatus(drv_data);
0149     if (ret < 0) {
0150         dev_err(dev, "failed to set Watchdog bootstatus\n");
0151         return ret;
0152     }
0153 
0154     ret = devm_watchdog_register_device(dev, &drv_data->wdt);
0155     if (ret)
0156         return ret;
0157 
0158     dev_info(dev, "MEN 14F021P00 BMC Watchdog device enabled\n");
0159 
0160     return 0;
0161 }
0162 
0163 static void menf21bmc_wdt_shutdown(struct platform_device *pdev)
0164 {
0165     struct menf21bmc_wdt *drv_data = platform_get_drvdata(pdev);
0166 
0167     i2c_smbus_write_word_data(drv_data->i2c_client,
0168                   BMC_CMD_WD_OFF, BMC_WD_OFF_VAL);
0169 }
0170 
0171 static struct  platform_driver menf21bmc_wdt = {
0172     .driver     = {
0173         .name   = DEVNAME,
0174     },
0175     .probe      = menf21bmc_wdt_probe,
0176     .shutdown   = menf21bmc_wdt_shutdown,
0177 };
0178 
0179 module_platform_driver(menf21bmc_wdt);
0180 
0181 MODULE_DESCRIPTION("MEN 14F021P00 BMC Watchdog driver");
0182 MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
0183 MODULE_LICENSE("GPL v2");
0184 MODULE_ALIAS("platform:menf21bmc_wdt");