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