aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Werner <andreas.werner@men.de>2014-08-27 13:53:06 -0400
committerLee Jones <lee.jones@linaro.org>2014-09-24 10:36:33 -0400
commit964356938fcd3c0001a786f55b9f0a0fbe47656a (patch)
tree8c1ae018b122c0600bf1fd08eeb4b74a67165801
parent38433639af915deeb0b0e28462dd740ce57b72fd (diff)
hwmon: (menf21bmc) Introduce MEN14F021P00 BMC HWMON driver
Added driver to support the 14F021P00 BMC Hardware Monitoring. The BMC is a Board Management Controller including monitoring of the board voltages. Signed-off-by: Andreas Werner <andreas.werner@men.de> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Lee Jones <lee.jones@linaro.org>
-rw-r--r--Documentation/hwmon/menf21bmc50
-rw-r--r--drivers/hwmon/Kconfig10
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/menf21bmc_hwmon.c230
4 files changed, 291 insertions, 0 deletions
diff --git a/Documentation/hwmon/menf21bmc b/Documentation/hwmon/menf21bmc
new file mode 100644
index 000000000000..2a273a065c5e
--- /dev/null
+++ b/Documentation/hwmon/menf21bmc
@@ -0,0 +1,50 @@
1Kernel driver menf21bmc_hwmon
2=============================
3
4Supported chips:
5 * MEN 14F021P00
6 Prefix: 'menf21bmc_hwmon'
7 Adresses scanned: -
8
9Author: Andreas Werner <andreas.werner@men.de>
10
11Description
12-----------
13
14The menf21bmc is a Board Management Controller (BMC) which provides an I2C
15interface to the host to access the features implemented in the BMC.
16
17This driver gives access to the voltage monitoring feature of the main
18voltages of the board.
19The voltage sensors are connected to the ADC inputs of the BMC which is
20a PIC16F917 Mikrocontroller.
21
22Usage Notes
23-----------
24
25This driver is part of the MFD driver named "menf21bmc" and does
26not auto-detect devices.
27You will have to instantiate the MFD driver explicitly.
28Please see Documentation/i2c/instantiating-devices for
29details.
30
31Sysfs entries
32-------------
33
34The following attributes are supported. All attributes are read only
35The Limits are read once by the driver.
36
37in0_input +3.3V input voltage
38in1_input +5.0V input voltage
39in2_input +12.0V input voltage
40in3_input +5V Standby input voltage
41in4_input VBAT (on board battery)
42
43in[0-4]_min Minimum voltage limit
44in[0-4]_max Maximum voltage limit
45
46in0_label "MON_3_3V"
47in1_label "MON_5V"
48in2_label "MON_12V"
49in3_label "5V_STANDBY"
50in4_label "VBAT"
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index f00d048aa583..c08d172bbab4 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -839,6 +839,16 @@ config SENSORS_MCP3021
839 This driver can also be built as a module. If so, the module 839 This driver can also be built as a module. If so, the module
840 will be called mcp3021. 840 will be called mcp3021.
841 841
842config SENSORS_MENF21BMC_HWMON
843 tristate "MEN 14F021P00 BMC Hardware Monitoring"
844 depends on MFD_MENF21BMC
845 help
846 Say Y here to include support for the MEN 14F021P00 BMC
847 hardware monitoring.
848
849 This driver can also be built as a module. If so the module
850 will be called menf21bmc_hwmon.
851
842config SENSORS_ADCXX 852config SENSORS_ADCXX
843 tristate "National Semiconductor ADCxxxSxxx" 853 tristate "National Semiconductor ADCxxxSxxx"
844 depends on SPI_MASTER 854 depends on SPI_MASTER
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index be28152c9848..c90a7611efaa 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -115,6 +115,7 @@ obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
115obj-$(CONFIG_SENSORS_MAX6697) += max6697.o 115obj-$(CONFIG_SENSORS_MAX6697) += max6697.o
116obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o 116obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
117obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o 117obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
118obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
118obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o 119obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o
119obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o 120obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o
120obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o 121obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
diff --git a/drivers/hwmon/menf21bmc_hwmon.c b/drivers/hwmon/menf21bmc_hwmon.c
new file mode 100644
index 000000000000..c92229d321c9
--- /dev/null
+++ b/drivers/hwmon/menf21bmc_hwmon.c
@@ -0,0 +1,230 @@
1/*
2 * MEN 14F021P00 Board Management Controller (BMC) hwmon driver.
3 *
4 * This is the core hwmon driver of the MEN 14F021P00 BMC.
5 * The BMC monitors the board voltages which can be access with this
6 * driver through sysfs.
7 *
8 * Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version.
14 */
15
16#include <linux/module.h>
17#include <linux/kernel.h>
18#include <linux/platform_device.h>
19#include <linux/hwmon.h>
20#include <linux/hwmon-sysfs.h>
21#include <linux/jiffies.h>
22#include <linux/slab.h>
23#include <linux/i2c.h>
24
25#define DRV_NAME "menf21bmc_hwmon"
26
27#define BMC_VOLT_COUNT 5
28#define MENF21BMC_V33 0
29#define MENF21BMC_V5 1
30#define MENF21BMC_V12 2
31#define MENF21BMC_V5_SB 3
32#define MENF21BMC_VBAT 4
33
34#define IDX_TO_VOLT_MIN_CMD(idx) (0x40 + idx)
35#define IDX_TO_VOLT_MAX_CMD(idx) (0x50 + idx)
36#define IDX_TO_VOLT_INP_CMD(idx) (0x60 + idx)
37
38struct menf21bmc_hwmon {
39 bool valid;
40 struct i2c_client *i2c_client;
41 unsigned long last_update;
42 int in_val[BMC_VOLT_COUNT];
43 int in_min[BMC_VOLT_COUNT];
44 int in_max[BMC_VOLT_COUNT];
45};
46
47static const char *const input_names[] = {
48 [MENF21BMC_V33] = "MON_3_3V",
49 [MENF21BMC_V5] = "MON_5V",
50 [MENF21BMC_V12] = "MON_12V",
51 [MENF21BMC_V5_SB] = "5V_STANDBY",
52 [MENF21BMC_VBAT] = "VBAT"
53};
54
55static struct menf21bmc_hwmon *menf21bmc_hwmon_update(struct device *dev)
56{
57 int i;
58 int val;
59 struct menf21bmc_hwmon *drv_data = dev_get_drvdata(dev);
60 struct menf21bmc_hwmon *data_ret = drv_data;
61
62 if (time_after(jiffies, drv_data->last_update + HZ)
63 || !drv_data->valid) {
64 for (i = 0; i < BMC_VOLT_COUNT; i++) {
65 val = i2c_smbus_read_word_data(drv_data->i2c_client,
66 IDX_TO_VOLT_INP_CMD(i));
67 if (val < 0) {
68 data_ret = ERR_PTR(val);
69 goto abort;
70 }
71 drv_data->in_val[i] = val;
72 }
73 drv_data->last_update = jiffies;
74 drv_data->valid = true;
75 }
76abort:
77 return data_ret;
78}
79
80static int menf21bmc_hwmon_get_volt_limits(struct menf21bmc_hwmon *drv_data)
81{
82 int i, val;
83
84 for (i = 0; i < BMC_VOLT_COUNT; i++) {
85 val = i2c_smbus_read_word_data(drv_data->i2c_client,
86 IDX_TO_VOLT_MIN_CMD(i));
87 if (val < 0)
88 return val;
89
90 drv_data->in_min[i] = val;
91
92 val = i2c_smbus_read_word_data(drv_data->i2c_client,
93 IDX_TO_VOLT_MAX_CMD(i));
94 if (val < 0)
95 return val;
96
97 drv_data->in_max[i] = val;
98 }
99 return 0;
100}
101
102static ssize_t
103show_label(struct device *dev, struct device_attribute *devattr, char *buf)
104{
105 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
106
107 return sprintf(buf, "%s\n", input_names[attr->index]);
108}
109
110static ssize_t
111show_in(struct device *dev, struct device_attribute *devattr, char *buf)
112{
113 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
114 struct menf21bmc_hwmon *drv_data = menf21bmc_hwmon_update(dev);
115
116 if (IS_ERR(drv_data))
117 return PTR_ERR(drv_data);
118
119 return sprintf(buf, "%d\n", drv_data->in_val[attr->index]);
120}
121
122static ssize_t
123show_min(struct device *dev, struct device_attribute *devattr, char *buf)
124{
125 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
126 struct menf21bmc_hwmon *drv_data = dev_get_drvdata(dev);
127
128 return sprintf(buf, "%d\n", drv_data->in_min[attr->index]);
129}
130
131static ssize_t
132show_max(struct device *dev, struct device_attribute *devattr, char *buf)
133{
134 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
135 struct menf21bmc_hwmon *drv_data = dev_get_drvdata(dev);
136
137 return sprintf(buf, "%d\n", drv_data->in_max[attr->index]);
138}
139
140#define create_voltage_sysfs(idx) \
141static SENSOR_DEVICE_ATTR(in##idx##_input, S_IRUGO, \
142 show_in, NULL, idx); \
143static SENSOR_DEVICE_ATTR(in##idx##_min, S_IRUGO, \
144 show_min, NULL, idx); \
145static SENSOR_DEVICE_ATTR(in##idx##_max, S_IRUGO, \
146 show_max, NULL, idx); \
147static SENSOR_DEVICE_ATTR(in##idx##_label, S_IRUGO, \
148 show_label, NULL, idx);
149
150create_voltage_sysfs(0);
151create_voltage_sysfs(1);
152create_voltage_sysfs(2);
153create_voltage_sysfs(3);
154create_voltage_sysfs(4);
155
156static struct attribute *menf21bmc_hwmon_attrs[] = {
157 &sensor_dev_attr_in0_input.dev_attr.attr,
158 &sensor_dev_attr_in0_min.dev_attr.attr,
159 &sensor_dev_attr_in0_max.dev_attr.attr,
160 &sensor_dev_attr_in0_label.dev_attr.attr,
161
162 &sensor_dev_attr_in1_input.dev_attr.attr,
163 &sensor_dev_attr_in1_min.dev_attr.attr,
164 &sensor_dev_attr_in1_max.dev_attr.attr,
165 &sensor_dev_attr_in1_label.dev_attr.attr,
166
167 &sensor_dev_attr_in2_input.dev_attr.attr,
168 &sensor_dev_attr_in2_min.dev_attr.attr,
169 &sensor_dev_attr_in2_max.dev_attr.attr,
170 &sensor_dev_attr_in2_label.dev_attr.attr,
171
172 &sensor_dev_attr_in3_input.dev_attr.attr,
173 &sensor_dev_attr_in3_min.dev_attr.attr,
174 &sensor_dev_attr_in3_max.dev_attr.attr,
175 &sensor_dev_attr_in3_label.dev_attr.attr,
176
177 &sensor_dev_attr_in4_input.dev_attr.attr,
178 &sensor_dev_attr_in4_min.dev_attr.attr,
179 &sensor_dev_attr_in4_max.dev_attr.attr,
180 &sensor_dev_attr_in4_label.dev_attr.attr,
181 NULL
182};
183
184ATTRIBUTE_GROUPS(menf21bmc_hwmon);
185
186static int menf21bmc_hwmon_probe(struct platform_device *pdev)
187{
188 int ret;
189 struct menf21bmc_hwmon *drv_data;
190 struct i2c_client *i2c_client = to_i2c_client(pdev->dev.parent);
191 struct device *hwmon_dev;
192
193 drv_data = devm_kzalloc(&pdev->dev, sizeof(struct menf21bmc_hwmon),
194 GFP_KERNEL);
195 if (!drv_data)
196 return -ENOMEM;
197
198 drv_data->i2c_client = i2c_client;
199
200 ret = menf21bmc_hwmon_get_volt_limits(drv_data);
201 if (ret) {
202 dev_err(&pdev->dev, "failed to read sensor limits");
203 return ret;
204 }
205
206 hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev,
207 "menf21bmc", drv_data,
208 menf21bmc_hwmon_groups);
209 if (IS_ERR(hwmon_dev))
210 return PTR_ERR(hwmon_dev);
211
212 dev_info(&pdev->dev, "MEN 14F021P00 BMC hwmon device enabled");
213
214 return 0;
215}
216
217static struct platform_driver menf21bmc_hwmon = {
218 .probe = menf21bmc_hwmon_probe,
219 .driver = {
220 .name = DRV_NAME,
221 .owner = THIS_MODULE,
222 },
223};
224
225module_platform_driver(menf21bmc_hwmon);
226
227MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
228MODULE_DESCRIPTION("MEN 14F021P00 BMC hwmon");
229MODULE_LICENSE("GPL v2");
230MODULE_ALIAS("platform:menf21bmc_hwmon");