Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  MEN 14F021P00 Board Management Controller (BMC) MFD Core 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/i2c.h>
0012 #include <linux/mfd/core.h>
0013 
0014 #define BMC_CMD_WDT_EXIT_PROD   0x18
0015 #define BMC_CMD_WDT_PROD_STAT   0x19
0016 #define BMC_CMD_REV_MAJOR   0x80
0017 #define BMC_CMD_REV_MINOR   0x81
0018 #define BMC_CMD_REV_MAIN    0x82
0019 
0020 static struct mfd_cell menf21bmc_cell[] = {
0021     { .name = "menf21bmc_wdt", },
0022     { .name = "menf21bmc_led", },
0023     { .name = "menf21bmc_hwmon", }
0024 };
0025 
0026 static int menf21bmc_wdt_exit_prod_mode(struct i2c_client *client)
0027 {
0028     int val, ret;
0029 
0030     val = i2c_smbus_read_byte_data(client, BMC_CMD_WDT_PROD_STAT);
0031     if (val < 0)
0032         return val;
0033 
0034     /*
0035      * Production mode should be not active after delivery of the Board.
0036      * To be sure we check it, inform the user and exit the mode
0037      * if active.
0038      */
0039     if (val == 0x00) {
0040         dev_info(&client->dev,
0041             "BMC in production mode. Exit production mode\n");
0042 
0043         ret = i2c_smbus_write_byte(client, BMC_CMD_WDT_EXIT_PROD);
0044         if (ret < 0)
0045             return ret;
0046     }
0047 
0048     return 0;
0049 }
0050 
0051 static int
0052 menf21bmc_probe(struct i2c_client *client, const struct i2c_device_id *ids)
0053 {
0054     int rev_major, rev_minor, rev_main;
0055     int ret;
0056 
0057     ret = i2c_check_functionality(client->adapter,
0058                       I2C_FUNC_SMBUS_BYTE_DATA |
0059                       I2C_FUNC_SMBUS_WORD_DATA |
0060                       I2C_FUNC_SMBUS_BYTE);
0061     if (!ret)
0062         return -ENODEV;
0063 
0064     rev_major = i2c_smbus_read_word_data(client, BMC_CMD_REV_MAJOR);
0065     if (rev_major < 0) {
0066         dev_err(&client->dev, "failed to get BMC major revision\n");
0067         return rev_major;
0068     }
0069 
0070     rev_minor = i2c_smbus_read_word_data(client, BMC_CMD_REV_MINOR);
0071     if (rev_minor < 0) {
0072         dev_err(&client->dev, "failed to get BMC minor revision\n");
0073         return rev_minor;
0074     }
0075 
0076     rev_main = i2c_smbus_read_word_data(client, BMC_CMD_REV_MAIN);
0077     if (rev_main < 0) {
0078         dev_err(&client->dev, "failed to get BMC main revision\n");
0079         return rev_main;
0080     }
0081 
0082     dev_info(&client->dev, "FW Revision: %02d.%02d.%02d\n",
0083          rev_major, rev_minor, rev_main);
0084 
0085     /*
0086      * We have to exit the Production Mode of the BMC to activate the
0087      * Watchdog functionality and the BIOS life sign monitoring.
0088      */
0089     ret = menf21bmc_wdt_exit_prod_mode(client);
0090     if (ret < 0) {
0091         dev_err(&client->dev, "failed to leave production mode\n");
0092         return ret;
0093     }
0094 
0095     ret = devm_mfd_add_devices(&client->dev, 0, menf21bmc_cell,
0096                    ARRAY_SIZE(menf21bmc_cell), NULL, 0, NULL);
0097     if (ret < 0) {
0098         dev_err(&client->dev, "failed to add BMC sub-devices\n");
0099         return ret;
0100     }
0101 
0102     return 0;
0103 }
0104 
0105 static const struct i2c_device_id menf21bmc_id_table[] = {
0106     { "menf21bmc" },
0107     { }
0108 };
0109 MODULE_DEVICE_TABLE(i2c, menf21bmc_id_table);
0110 
0111 static struct i2c_driver menf21bmc_driver = {
0112     .driver.name    = "menf21bmc",
0113     .id_table   = menf21bmc_id_table,
0114     .probe      = menf21bmc_probe,
0115 };
0116 
0117 module_i2c_driver(menf21bmc_driver);
0118 
0119 MODULE_DESCRIPTION("MEN 14F021P00 BMC mfd core driver");
0120 MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
0121 MODULE_LICENSE("GPL v2");