0001
0002
0003
0004
0005
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
0036
0037
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
0087
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");