1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * MEN 14F021P00 Board Management Controller (BMC) MFD Core Driver. |
4 | * |
5 | * Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH |
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 |
52 | menf21bmc_probe(struct i2c_client *client) |
53 | { |
54 | int rev_major, rev_minor, rev_main; |
55 | int ret; |
56 | |
57 | ret = i2c_check_functionality(adap: 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 | |
95 | ret = devm_mfd_add_devices(dev: &client->dev, id: 0, cells: menf21bmc_cell, |
96 | ARRAY_SIZE(menf21bmc_cell), NULL, irq_base: 0, NULL); |
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 | |
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, |
114 | .probe = menf21bmc_probe, |
115 | }; |
116 | |
117 | module_i2c_driver(menf21bmc_driver); |
118 | |
119 | MODULE_DESCRIPTION("MEN 14F021P00 BMC mfd core driver" ); |
120 | MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>" ); |
121 | MODULE_LICENSE("GPL v2" ); |
122 | |