diff options
Diffstat (limited to 'drivers/mfd/menf21bmc.c')
-rw-r--r-- | drivers/mfd/menf21bmc.c | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/drivers/mfd/menf21bmc.c b/drivers/mfd/menf21bmc.c new file mode 100644 index 000000000000..1c274345820c --- /dev/null +++ b/drivers/mfd/menf21bmc.c | |||
@@ -0,0 +1,132 @@ | |||
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 = 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 int menf21bmc_remove(struct i2c_client *client) | ||
110 | { | ||
111 | mfd_remove_devices(&client->dev); | ||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | static const struct i2c_device_id menf21bmc_id_table[] = { | ||
116 | { "menf21bmc" }, | ||
117 | { } | ||
118 | }; | ||
119 | MODULE_DEVICE_TABLE(i2c, menf21bmc_id_table); | ||
120 | |||
121 | static struct i2c_driver menf21bmc_driver = { | ||
122 | .driver.name = "menf21bmc", | ||
123 | .id_table = menf21bmc_id_table, | ||
124 | .probe = menf21bmc_probe, | ||
125 | .remove = menf21bmc_remove, | ||
126 | }; | ||
127 | |||
128 | module_i2c_driver(menf21bmc_driver); | ||
129 | |||
130 | MODULE_DESCRIPTION("MEN 14F021P00 BMC mfd core driver"); | ||
131 | MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>"); | ||
132 | MODULE_LICENSE("GPL v2"); | ||