aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/hwmon/lineage-pem77
-rw-r--r--drivers/hwmon/Kconfig11
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/lineage-pem.c589
4 files changed, 678 insertions, 0 deletions
diff --git a/Documentation/hwmon/lineage-pem b/Documentation/hwmon/lineage-pem
new file mode 100644
index 00000000000..2ba5ed12685
--- /dev/null
+++ b/Documentation/hwmon/lineage-pem
@@ -0,0 +1,77 @@
1Kernel driver lineage-pem
2=========================
3
4Supported devices:
5 * Lineage Compact Power Line Power Entry Modules
6 Prefix: 'lineage-pem'
7 Addresses scanned: -
8 Documentation:
9 http://www.lineagepower.com/oem/pdf/CPLI2C.pdf
10
11Author: Guenter Roeck <guenter.roeck@ericsson.com>
12
13
14Description
15-----------
16
17This driver supports various Lineage Compact Power Line DC/DC and AC/DC
18converters such as CP1800, CP2000AC, CP2000DC, CP2100DC, and others.
19
20Lineage CPL power entry modules are nominally PMBus compliant. However, most
21standard PMBus commands are not supported. Specifically, all hardware monitoring
22and status reporting commands are non-standard. For this reason, a standard
23PMBus driver can not be used.
24
25
26Usage Notes
27-----------
28
29This driver does not probe for Lineage CPL devices, since there is no register
30which can be safely used to identify the chip. You will have to instantiate
31the devices explicitly.
32
33Example: the following will load the driver for a Lineage PEM at address 0x40
34on I2C bus #1:
35$ modprobe lineage-pem
36$ echo lineage-pem 0x40 > /sys/bus/i2c/devices/i2c-1/new_device
37
38All Lineage CPL power entry modules have a built-in I2C bus master selector
39(PCA9541). To ensure device access, this driver should only be used as client
40driver to the pca9541 I2C master selector driver.
41
42
43Sysfs entries
44-------------
45
46All Lineage CPL devices report output voltage and device temperature as well as
47alarms for output voltage, temperature, input voltage, input current, input power,
48and fan status.
49
50Input voltage, input current, input power, and fan speed measurement is only
51supported on newer devices. The driver detects if those attributes are supported,
52and only creates respective sysfs entries if they are.
53
54in1_input Output voltage (mV)
55in1_min_alarm Output undervoltage alarm
56in1_max_alarm Output overvoltage alarm
57in1_crit Output voltage critical alarm
58
59in2_input Input voltage (mV, optional)
60in2_alarm Input voltage alarm
61
62curr1_input Input current (mA, optional)
63curr1_alarm Input overcurrent alarm
64
65power1_input Input power (uW, optional)
66power1_alarm Input power alarm
67
68fan1_input Fan 1 speed (rpm, optional)
69fan2_input Fan 2 speed (rpm, optional)
70fan3_input Fan 3 speed (rpm, optional)
71
72temp1_input
73temp1_max
74temp1_crit
75temp1_alarm
76temp1_crit_alarm
77temp1_fault
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 3bd993b0a3c..d4ffc605208 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -467,6 +467,17 @@ config SENSORS_JC42
467 This driver can also be built as a module. If so, the module 467 This driver can also be built as a module. If so, the module
468 will be called jc42. 468 will be called jc42.
469 469
470config SENSORS_LINEAGE
471 tristate "Lineage Compact Power Line Power Entry Module"
472 depends on I2C && EXPERIMENTAL
473 help
474 If you say yes here you get support for the Lineage Compact Power Line
475 series of DC/DC and AC/DC converters such as CP1800, CP2000AC,
476 CP2000DC, CP2725, and others.
477
478 This driver can also be built as a module. If so, the module
479 will be called lineage-pem.
480
470config SENSORS_LM63 481config SENSORS_LM63
471 tristate "National Semiconductor LM63 and LM64" 482 tristate "National Semiconductor LM63 and LM64"
472 depends on I2C 483 depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index f69783150b7..46819818ef5 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_JC42) += jc42.o
62obj-$(CONFIG_SENSORS_JZ4740) += jz4740-hwmon.o 62obj-$(CONFIG_SENSORS_JZ4740) += jz4740-hwmon.o
63obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o 63obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o
64obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o 64obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o
65obj-$(CONFIG_SENSORS_LINEAGE) += lineage-pem.o
65obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o 66obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
66obj-$(CONFIG_SENSORS_LIS3_SPI) += lis3lv02d.o lis3lv02d_spi.o 67obj-$(CONFIG_SENSORS_LIS3_SPI) += lis3lv02d.o lis3lv02d_spi.o
67obj-$(CONFIG_SENSORS_LIS3_I2C) += lis3lv02d.o lis3lv02d_i2c.o 68obj-$(CONFIG_SENSORS_LIS3_I2C) += lis3lv02d.o lis3lv02d_i2c.o
diff --git a/drivers/hwmon/lineage-pem.c b/drivers/hwmon/lineage-pem.c
new file mode 100644
index 00000000000..d39ee24e52f
--- /dev/null
+++ b/drivers/hwmon/lineage-pem.c
@@ -0,0 +1,589 @@
1/*
2 * Driver for Lineage Compact Power Line series of power entry modules.
3 *
4 * Copyright (C) 2010, 2011 Ericsson AB.
5 *
6 * Documentation:
7 * http://www.lineagepower.com/oem/pdf/CPLI2C.pdf
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24#include <linux/kernel.h>
25#include <linux/module.h>
26#include <linux/init.h>
27#include <linux/err.h>
28#include <linux/slab.h>
29#include <linux/i2c.h>
30#include <linux/hwmon.h>
31#include <linux/hwmon-sysfs.h>
32
33/*
34 * This driver supports various Lineage Compact Power Line DC/DC and AC/DC
35 * converters such as CP1800, CP2000AC, CP2000DC, CP2100DC, and others.
36 *
37 * The devices are nominally PMBus compliant. However, most standard PMBus
38 * commands are not supported. Specifically, all hardware monitoring and
39 * status reporting commands are non-standard. For this reason, a standard
40 * PMBus driver can not be used.
41 *
42 * All Lineage CPL devices have a built-in I2C bus master selector (PCA9541).
43 * To ensure device access, this driver should only be used as client driver
44 * to the pca9541 I2C master selector driver.
45 */
46
47/* Command codes */
48#define PEM_OPERATION 0x01
49#define PEM_CLEAR_INFO_FLAGS 0x03
50#define PEM_VOUT_COMMAND 0x21
51#define PEM_VOUT_OV_FAULT_LIMIT 0x40
52#define PEM_READ_DATA_STRING 0xd0
53#define PEM_READ_INPUT_STRING 0xdc
54#define PEM_READ_FIRMWARE_REV 0xdd
55#define PEM_READ_RUN_TIMER 0xde
56#define PEM_FAN_HI_SPEED 0xdf
57#define PEM_FAN_NORMAL_SPEED 0xe0
58#define PEM_READ_FAN_SPEED 0xe1
59
60/* offsets in data string */
61#define PEM_DATA_STATUS_2 0
62#define PEM_DATA_STATUS_1 1
63#define PEM_DATA_ALARM_2 2
64#define PEM_DATA_ALARM_1 3
65#define PEM_DATA_VOUT_LSB 4
66#define PEM_DATA_VOUT_MSB 5
67#define PEM_DATA_CURRENT 6
68#define PEM_DATA_TEMP 7
69
70/* Virtual entries, to report constants */
71#define PEM_DATA_TEMP_MAX 10
72#define PEM_DATA_TEMP_CRIT 11
73
74/* offsets in input string */
75#define PEM_INPUT_VOLTAGE 0
76#define PEM_INPUT_POWER_LSB 1
77#define PEM_INPUT_POWER_MSB 2
78
79/* offsets in fan data */
80#define PEM_FAN_ADJUSTMENT 0
81#define PEM_FAN_FAN1 1
82#define PEM_FAN_FAN2 2
83#define PEM_FAN_FAN3 3
84
85/* Status register bits */
86#define STS1_OUTPUT_ON (1 << 0)
87#define STS1_LEDS_FLASHING (1 << 1)
88#define STS1_EXT_FAULT (1 << 2)
89#define STS1_SERVICE_LED_ON (1 << 3)
90#define STS1_SHUTDOWN_OCCURRED (1 << 4)
91#define STS1_INT_FAULT (1 << 5)
92#define STS1_ISOLATION_TEST_OK (1 << 6)
93
94#define STS2_ENABLE_PIN_HI (1 << 0)
95#define STS2_DATA_OUT_RANGE (1 << 1)
96#define STS2_RESTARTED_OK (1 << 1)
97#define STS2_ISOLATION_TEST_FAIL (1 << 3)
98#define STS2_HIGH_POWER_CAP (1 << 4)
99#define STS2_INVALID_INSTR (1 << 5)
100#define STS2_WILL_RESTART (1 << 6)
101#define STS2_PEC_ERR (1 << 7)
102
103/* Alarm register bits */
104#define ALRM1_VIN_OUT_LIMIT (1 << 0)
105#define ALRM1_VOUT_OUT_LIMIT (1 << 1)
106#define ALRM1_OV_VOLT_SHUTDOWN (1 << 2)
107#define ALRM1_VIN_OVERCURRENT (1 << 3)
108#define ALRM1_TEMP_WARNING (1 << 4)
109#define ALRM1_TEMP_SHUTDOWN (1 << 5)
110#define ALRM1_PRIMARY_FAULT (1 << 6)
111#define ALRM1_POWER_LIMIT (1 << 7)
112
113#define ALRM2_5V_OUT_LIMIT (1 << 1)
114#define ALRM2_TEMP_FAULT (1 << 2)
115#define ALRM2_OV_LOW (1 << 3)
116#define ALRM2_DCDC_TEMP_HIGH (1 << 4)
117#define ALRM2_PRI_TEMP_HIGH (1 << 5)
118#define ALRM2_NO_PRIMARY (1 << 6)
119#define ALRM2_FAN_FAULT (1 << 7)
120
121#define FIRMWARE_REV_LEN 4
122#define DATA_STRING_LEN 9
123#define INPUT_STRING_LEN 5 /* 4 for most devices */
124#define FAN_SPEED_LEN 5
125
126struct pem_data {
127 struct device *hwmon_dev;
128
129 struct mutex update_lock;
130 bool valid;
131 bool fans_supported;
132 int input_length;
133 unsigned long last_updated; /* in jiffies */
134
135 u8 firmware_rev[FIRMWARE_REV_LEN];
136 u8 data_string[DATA_STRING_LEN];
137 u8 input_string[INPUT_STRING_LEN];
138 u8 fan_speed[FAN_SPEED_LEN];
139};
140
141static int pem_read_block(struct i2c_client *client, u8 command, u8 *data,
142 int data_len)
143{
144 u8 block_buffer[I2C_SMBUS_BLOCK_MAX];
145 int result;
146
147 result = i2c_smbus_read_block_data(client, command, block_buffer);
148 if (unlikely(result < 0))
149 goto abort;
150 if (unlikely(result == 0xff || result != data_len)) {
151 result = -EIO;
152 goto abort;
153 }
154 memcpy(data, block_buffer, data_len);
155 result = 0;
156abort:
157 return result;
158}
159
160static struct pem_data *pem_update_device(struct device *dev)
161{
162 struct i2c_client *client = to_i2c_client(dev);
163 struct pem_data *data = i2c_get_clientdata(client);
164 struct pem_data *ret = data;
165
166 mutex_lock(&data->update_lock);
167
168 if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
169 int result;
170
171 /* Read data string */
172 result = pem_read_block(client, PEM_READ_DATA_STRING,
173 data->data_string,
174 sizeof(data->data_string));
175 if (unlikely(result < 0)) {
176 ret = ERR_PTR(result);
177 goto abort;
178 }
179
180 /* Read input string */
181 if (data->input_length) {
182 result = pem_read_block(client, PEM_READ_INPUT_STRING,
183 data->input_string,
184 data->input_length);
185 if (unlikely(result < 0)) {
186 ret = ERR_PTR(result);
187 goto abort;
188 }
189 }
190
191 /* Read fan speeds */
192 if (data->fans_supported) {
193 result = pem_read_block(client, PEM_READ_FAN_SPEED,
194 data->fan_speed,
195 sizeof(data->fan_speed));
196 if (unlikely(result < 0)) {
197 ret = ERR_PTR(result);
198 goto abort;
199 }
200 }
201
202 i2c_smbus_write_byte(client, PEM_CLEAR_INFO_FLAGS);
203
204 data->last_updated = jiffies;
205 data->valid = 1;
206 }
207abort:
208 mutex_unlock(&data->update_lock);
209 return ret;
210}
211
212static long pem_get_data(u8 *data, int len, int index)
213{
214 long val;
215
216 switch (index) {
217 case PEM_DATA_VOUT_LSB:
218 val = (data[index] + (data[index+1] << 8)) * 5 / 2;
219 break;
220 case PEM_DATA_CURRENT:
221 val = data[index] * 200;
222 break;
223 case PEM_DATA_TEMP:
224 val = data[index] * 1000;
225 break;
226 case PEM_DATA_TEMP_MAX:
227 val = 97 * 1000; /* 97 degrees C per datasheet */
228 break;
229 case PEM_DATA_TEMP_CRIT:
230 val = 107 * 1000; /* 107 degrees C per datasheet */
231 break;
232 default:
233 WARN_ON_ONCE(1);
234 val = 0;
235 }
236 return val;
237}
238
239static long pem_get_input(u8 *data, int len, int index)
240{
241 long val;
242
243 switch (index) {
244 case PEM_INPUT_VOLTAGE:
245 if (len == INPUT_STRING_LEN)
246 val = (data[index] + (data[index+1] << 8) - 75) * 1000;
247 else
248 val = (data[index] - 75) * 1000;
249 break;
250 case PEM_INPUT_POWER_LSB:
251 if (len == INPUT_STRING_LEN)
252 index++;
253 val = (data[index] + (data[index+1] << 8)) * 1000000L;
254 break;
255 default:
256 WARN_ON_ONCE(1);
257 val = 0;
258 }
259 return val;
260}
261
262static long pem_get_fan(u8 *data, int len, int index)
263{
264 long val;
265
266 switch (index) {
267 case PEM_FAN_FAN1:
268 case PEM_FAN_FAN2:
269 case PEM_FAN_FAN3:
270 val = data[index] * 100;
271 break;
272 default:
273 WARN_ON_ONCE(1);
274 val = 0;
275 }
276 return val;
277}
278
279/*
280 * Show boolean, either a fault or an alarm.
281 * .nr points to the register, .index is the bit mask to check
282 */
283static ssize_t pem_show_bool(struct device *dev,
284 struct device_attribute *da, char *buf)
285{
286 struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da);
287 struct pem_data *data = pem_update_device(dev);
288 u8 status;
289
290 if (IS_ERR(data))
291 return PTR_ERR(data);
292
293 status = data->data_string[attr->nr] & attr->index;
294 return snprintf(buf, PAGE_SIZE, "%d\n", !!status);
295}
296
297static ssize_t pem_show_data(struct device *dev, struct device_attribute *da,
298 char *buf)
299{
300 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
301 struct pem_data *data = pem_update_device(dev);
302 long value;
303
304 if (IS_ERR(data))
305 return PTR_ERR(data);
306
307 value = pem_get_data(data->data_string, sizeof(data->data_string),
308 attr->index);
309
310 return snprintf(buf, PAGE_SIZE, "%ld\n", value);
311}
312
313static ssize_t pem_show_input(struct device *dev, struct device_attribute *da,
314 char *buf)
315{
316 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
317 struct pem_data *data = pem_update_device(dev);
318 long value;
319
320 if (IS_ERR(data))
321 return PTR_ERR(data);
322
323 value = pem_get_input(data->input_string, sizeof(data->input_string),
324 attr->index);
325
326 return snprintf(buf, PAGE_SIZE, "%ld\n", value);
327}
328
329static ssize_t pem_show_fan(struct device *dev, struct device_attribute *da,
330 char *buf)
331{
332 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
333 struct pem_data *data = pem_update_device(dev);
334 long value;
335
336 if (IS_ERR(data))
337 return PTR_ERR(data);
338
339 value = pem_get_fan(data->fan_speed, sizeof(data->fan_speed),
340 attr->index);
341
342 return snprintf(buf, PAGE_SIZE, "%ld\n", value);
343}
344
345/* Voltages */
346static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, pem_show_data, NULL,
347 PEM_DATA_VOUT_LSB);
348static SENSOR_DEVICE_ATTR_2(in1_min_alarm, S_IRUGO, pem_show_bool, NULL,
349 PEM_DATA_ALARM_2, ALRM2_OV_LOW);
350static SENSOR_DEVICE_ATTR_2(in1_max_alarm, S_IRUGO, pem_show_bool, NULL,
351 PEM_DATA_ALARM_1, ALRM1_VOUT_OUT_LIMIT);
352static SENSOR_DEVICE_ATTR_2(in1_crit_alarm, S_IRUGO, pem_show_bool, NULL,
353 PEM_DATA_ALARM_1, ALRM1_OV_VOLT_SHUTDOWN);
354static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, pem_show_input, NULL,
355 PEM_INPUT_VOLTAGE);
356static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, pem_show_bool, NULL,
357 PEM_DATA_ALARM_1,
358 ALRM1_VIN_OUT_LIMIT | ALRM1_PRIMARY_FAULT);
359
360/* Currents */
361static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, pem_show_data, NULL,
362 PEM_DATA_CURRENT);
363static SENSOR_DEVICE_ATTR_2(curr1_alarm, S_IRUGO, pem_show_bool, NULL,
364 PEM_DATA_ALARM_1, ALRM1_VIN_OVERCURRENT);
365
366/* Power */
367static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, pem_show_input, NULL,
368 PEM_INPUT_POWER_LSB);
369static SENSOR_DEVICE_ATTR_2(power1_alarm, S_IRUGO, pem_show_bool, NULL,
370 PEM_DATA_ALARM_1, ALRM1_POWER_LIMIT);
371
372/* Fans */
373static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, pem_show_fan, NULL,
374 PEM_FAN_FAN1);
375static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, pem_show_fan, NULL,
376 PEM_FAN_FAN2);
377static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, pem_show_fan, NULL,
378 PEM_FAN_FAN3);
379static SENSOR_DEVICE_ATTR_2(fan1_alarm, S_IRUGO, pem_show_bool, NULL,
380 PEM_DATA_ALARM_2, ALRM2_FAN_FAULT);
381
382/* Temperatures */
383static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, pem_show_data, NULL,
384 PEM_DATA_TEMP);
385static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, pem_show_data, NULL,
386 PEM_DATA_TEMP_MAX);
387static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, pem_show_data, NULL,
388 PEM_DATA_TEMP_CRIT);
389static SENSOR_DEVICE_ATTR_2(temp1_alarm, S_IRUGO, pem_show_bool, NULL,
390 PEM_DATA_ALARM_1, ALRM1_TEMP_WARNING);
391static SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO, pem_show_bool, NULL,
392 PEM_DATA_ALARM_1, ALRM1_TEMP_SHUTDOWN);
393static SENSOR_DEVICE_ATTR_2(temp1_fault, S_IRUGO, pem_show_bool, NULL,
394 PEM_DATA_ALARM_2, ALRM2_TEMP_FAULT);
395
396static struct attribute *pem_attributes[] = {
397 &sensor_dev_attr_in1_input.dev_attr.attr,
398 &sensor_dev_attr_in1_min_alarm.dev_attr.attr,
399 &sensor_dev_attr_in1_max_alarm.dev_attr.attr,
400 &sensor_dev_attr_in1_crit_alarm.dev_attr.attr,
401 &sensor_dev_attr_in2_alarm.dev_attr.attr,
402
403 &sensor_dev_attr_curr1_alarm.dev_attr.attr,
404
405 &sensor_dev_attr_power1_alarm.dev_attr.attr,
406
407 &sensor_dev_attr_fan1_alarm.dev_attr.attr,
408
409 &sensor_dev_attr_temp1_input.dev_attr.attr,
410 &sensor_dev_attr_temp1_max.dev_attr.attr,
411 &sensor_dev_attr_temp1_crit.dev_attr.attr,
412 &sensor_dev_attr_temp1_alarm.dev_attr.attr,
413 &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
414 &sensor_dev_attr_temp1_fault.dev_attr.attr,
415
416 NULL,
417};
418
419static const struct attribute_group pem_group = {
420 .attrs = pem_attributes,
421};
422
423static struct attribute *pem_input_attributes[] = {
424 &sensor_dev_attr_in2_input.dev_attr.attr,
425 &sensor_dev_attr_curr1_input.dev_attr.attr,
426 &sensor_dev_attr_power1_input.dev_attr.attr,
427};
428
429static const struct attribute_group pem_input_group = {
430 .attrs = pem_input_attributes,
431};
432
433static struct attribute *pem_fan_attributes[] = {
434 &sensor_dev_attr_fan1_input.dev_attr.attr,
435 &sensor_dev_attr_fan2_input.dev_attr.attr,
436 &sensor_dev_attr_fan3_input.dev_attr.attr,
437};
438
439static const struct attribute_group pem_fan_group = {
440 .attrs = pem_fan_attributes,
441};
442
443static int pem_probe(struct i2c_client *client,
444 const struct i2c_device_id *id)
445{
446 struct i2c_adapter *adapter = client->adapter;
447 struct pem_data *data;
448 int ret;
449
450 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BLOCK_DATA
451 | I2C_FUNC_SMBUS_WRITE_BYTE))
452 return -ENODEV;
453
454 data = kzalloc(sizeof(*data), GFP_KERNEL);
455 if (!data)
456 return -ENOMEM;
457
458 i2c_set_clientdata(client, data);
459 mutex_init(&data->update_lock);
460
461 /*
462 * We use the next two commands to determine if the device is really
463 * there.
464 */
465 ret = pem_read_block(client, PEM_READ_FIRMWARE_REV,
466 data->firmware_rev, sizeof(data->firmware_rev));
467 if (ret < 0)
468 goto out_kfree;
469
470 ret = i2c_smbus_write_byte(client, PEM_CLEAR_INFO_FLAGS);
471 if (ret < 0)
472 goto out_kfree;
473
474 dev_info(&client->dev, "Firmware revision %d.%d.%d\n",
475 data->firmware_rev[0], data->firmware_rev[1],
476 data->firmware_rev[2]);
477
478 /* Register sysfs hooks */
479 ret = sysfs_create_group(&client->dev.kobj, &pem_group);
480 if (ret)
481 goto out_kfree;
482
483 /*
484 * Check if input readings are supported.
485 * This is the case if we can read input data,
486 * and if the returned data is not all zeros.
487 * Note that input alarms are always supported.
488 */
489 ret = pem_read_block(client, PEM_READ_INPUT_STRING,
490 data->input_string,
491 sizeof(data->input_string) - 1);
492 if (!ret && (data->input_string[0] || data->input_string[1] ||
493 data->input_string[2]))
494 data->input_length = sizeof(data->input_string) - 1;
495 else if (ret < 0) {
496 /* Input string is one byte longer for some devices */
497 ret = pem_read_block(client, PEM_READ_INPUT_STRING,
498 data->input_string,
499 sizeof(data->input_string));
500 if (!ret && (data->input_string[0] || data->input_string[1] ||
501 data->input_string[2] || data->input_string[3]))
502 data->input_length = sizeof(data->input_string);
503 }
504 ret = 0;
505 if (data->input_length) {
506 ret = sysfs_create_group(&client->dev.kobj, &pem_input_group);
507 if (ret)
508 goto out_remove_groups;
509 }
510
511 /*
512 * Check if fan speed readings are supported.
513 * This is the case if we can read fan speed data,
514 * and if the returned data is not all zeros.
515 * Note that the fan alarm is always supported.
516 */
517 ret = pem_read_block(client, PEM_READ_FAN_SPEED,
518 data->fan_speed,
519 sizeof(data->fan_speed));
520 if (!ret && (data->fan_speed[0] || data->fan_speed[1] ||
521 data->fan_speed[2] || data->fan_speed[3])) {
522 data->fans_supported = true;
523 ret = sysfs_create_group(&client->dev.kobj, &pem_fan_group);
524 if (ret)
525 goto out_remove_groups;
526 }
527
528 data->hwmon_dev = hwmon_device_register(&client->dev);
529 if (IS_ERR(data->hwmon_dev)) {
530 ret = PTR_ERR(data->hwmon_dev);
531 goto out_remove_groups;
532 }
533
534 return 0;
535
536out_remove_groups:
537 sysfs_remove_group(&client->dev.kobj, &pem_input_group);
538 sysfs_remove_group(&client->dev.kobj, &pem_fan_group);
539 sysfs_remove_group(&client->dev.kobj, &pem_group);
540out_kfree:
541 kfree(data);
542 return ret;
543}
544
545static int pem_remove(struct i2c_client *client)
546{
547 struct pem_data *data = i2c_get_clientdata(client);
548
549 hwmon_device_unregister(data->hwmon_dev);
550
551 sysfs_remove_group(&client->dev.kobj, &pem_input_group);
552 sysfs_remove_group(&client->dev.kobj, &pem_fan_group);
553 sysfs_remove_group(&client->dev.kobj, &pem_group);
554
555 kfree(data);
556 return 0;
557}
558
559static const struct i2c_device_id pem_id[] = {
560 {"lineage_pem", 0},
561 {}
562};
563MODULE_DEVICE_TABLE(i2c, pem_id);
564
565static struct i2c_driver pem_driver = {
566 .driver = {
567 .name = "lineage_pem",
568 },
569 .probe = pem_probe,
570 .remove = pem_remove,
571 .id_table = pem_id,
572};
573
574static int __init pem_init(void)
575{
576 return i2c_add_driver(&pem_driver);
577}
578
579static void __exit pem_exit(void)
580{
581 i2c_del_driver(&pem_driver);
582}
583
584MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>");
585MODULE_DESCRIPTION("Lineage CPL PEM hardware monitoring driver");
586MODULE_LICENSE("GPL");
587
588module_init(pem_init);
589module_exit(pem_exit);