| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame] | 1 | /* | 
|  | 2 | *  MEN 14F021P00 Board Management Controller (BMC) MFD Core Driver. | 
|  | 3 | * | 
|  | 4 | *  Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH | 
|  | 5 | * | 
|  | 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. | 
|  | 10 | */ | 
|  | 11 |  | 
|  | 12 | #include <linux/kernel.h> | 
|  | 13 | #include <linux/device.h> | 
|  | 14 | #include <linux/module.h> | 
|  | 15 | #include <linux/i2c.h> | 
|  | 16 | #include <linux/mfd/core.h> | 
|  | 17 |  | 
|  | 18 | #define BMC_CMD_WDT_EXIT_PROD	0x18 | 
|  | 19 | #define BMC_CMD_WDT_PROD_STAT	0x19 | 
|  | 20 | #define BMC_CMD_REV_MAJOR	0x80 | 
|  | 21 | #define BMC_CMD_REV_MINOR	0x81 | 
|  | 22 | #define BMC_CMD_REV_MAIN	0x82 | 
|  | 23 |  | 
|  | 24 | static struct mfd_cell menf21bmc_cell[] = { | 
|  | 25 | { .name = "menf21bmc_wdt", }, | 
|  | 26 | { .name = "menf21bmc_led", }, | 
|  | 27 | { .name = "menf21bmc_hwmon", } | 
|  | 28 | }; | 
|  | 29 |  | 
|  | 30 | static int menf21bmc_wdt_exit_prod_mode(struct i2c_client *client) | 
|  | 31 | { | 
|  | 32 | int val, ret; | 
|  | 33 |  | 
|  | 34 | val = i2c_smbus_read_byte_data(client, BMC_CMD_WDT_PROD_STAT); | 
|  | 35 | if (val < 0) | 
|  | 36 | return val; | 
|  | 37 |  | 
|  | 38 | /* | 
|  | 39 | * Production mode should be not active after delivery of the Board. | 
|  | 40 | * To be sure we check it, inform the user and exit the mode | 
|  | 41 | * if active. | 
|  | 42 | */ | 
|  | 43 | if (val == 0x00) { | 
|  | 44 | dev_info(&client->dev, | 
|  | 45 | "BMC in production mode. Exit production mode\n"); | 
|  | 46 |  | 
|  | 47 | ret = i2c_smbus_write_byte(client, BMC_CMD_WDT_EXIT_PROD); | 
|  | 48 | if (ret < 0) | 
|  | 49 | return ret; | 
|  | 50 | } | 
|  | 51 |  | 
|  | 52 | return 0; | 
|  | 53 | } | 
|  | 54 |  | 
|  | 55 | static int | 
|  | 56 | menf21bmc_probe(struct i2c_client *client, const struct i2c_device_id *ids) | 
|  | 57 | { | 
|  | 58 | int rev_major, rev_minor, rev_main; | 
|  | 59 | int ret; | 
|  | 60 |  | 
|  | 61 | ret = i2c_check_functionality(client->adapter, | 
|  | 62 | I2C_FUNC_SMBUS_BYTE_DATA | | 
|  | 63 | I2C_FUNC_SMBUS_WORD_DATA | | 
|  | 64 | I2C_FUNC_SMBUS_BYTE); | 
|  | 65 | if (!ret) | 
|  | 66 | return -ENODEV; | 
|  | 67 |  | 
|  | 68 | rev_major = i2c_smbus_read_word_data(client, BMC_CMD_REV_MAJOR); | 
|  | 69 | if (rev_major < 0) { | 
|  | 70 | dev_err(&client->dev, "failed to get BMC major revision\n"); | 
|  | 71 | return rev_major; | 
|  | 72 | } | 
|  | 73 |  | 
|  | 74 | rev_minor = i2c_smbus_read_word_data(client, BMC_CMD_REV_MINOR); | 
|  | 75 | if (rev_minor < 0) { | 
|  | 76 | dev_err(&client->dev, "failed to get BMC minor revision\n"); | 
|  | 77 | return rev_minor; | 
|  | 78 | } | 
|  | 79 |  | 
|  | 80 | rev_main = i2c_smbus_read_word_data(client, BMC_CMD_REV_MAIN); | 
|  | 81 | if (rev_main < 0) { | 
|  | 82 | dev_err(&client->dev, "failed to get BMC main revision\n"); | 
|  | 83 | return rev_main; | 
|  | 84 | } | 
|  | 85 |  | 
|  | 86 | dev_info(&client->dev, "FW Revision: %02d.%02d.%02d\n", | 
|  | 87 | rev_major, rev_minor, rev_main); | 
|  | 88 |  | 
|  | 89 | /* | 
|  | 90 | * We have to exit the Production Mode of the BMC to activate the | 
|  | 91 | * Watchdog functionality and the BIOS life sign monitoring. | 
|  | 92 | */ | 
|  | 93 | ret = menf21bmc_wdt_exit_prod_mode(client); | 
|  | 94 | if (ret < 0) { | 
|  | 95 | dev_err(&client->dev, "failed to leave production mode\n"); | 
|  | 96 | return ret; | 
|  | 97 | } | 
|  | 98 |  | 
|  | 99 | ret = devm_mfd_add_devices(&client->dev, 0, menf21bmc_cell, | 
|  | 100 | ARRAY_SIZE(menf21bmc_cell), NULL, 0, NULL); | 
|  | 101 | if (ret < 0) { | 
|  | 102 | dev_err(&client->dev, "failed to add BMC sub-devices\n"); | 
|  | 103 | return ret; | 
|  | 104 | } | 
|  | 105 |  | 
|  | 106 | return 0; | 
|  | 107 | } | 
|  | 108 |  | 
|  | 109 | static const struct i2c_device_id menf21bmc_id_table[] = { | 
|  | 110 | { "menf21bmc" }, | 
|  | 111 | { } | 
|  | 112 | }; | 
|  | 113 | MODULE_DEVICE_TABLE(i2c, menf21bmc_id_table); | 
|  | 114 |  | 
|  | 115 | static struct i2c_driver menf21bmc_driver = { | 
|  | 116 | .driver.name	= "menf21bmc", | 
|  | 117 | .id_table	= menf21bmc_id_table, | 
|  | 118 | .probe		= menf21bmc_probe, | 
|  | 119 | }; | 
|  | 120 |  | 
|  | 121 | module_i2c_driver(menf21bmc_driver); | 
|  | 122 |  | 
|  | 123 | MODULE_DESCRIPTION("MEN 14F021P00 BMC mfd core driver"); | 
|  | 124 | MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>"); | 
|  | 125 | MODULE_LICENSE("GPL v2"); |