xref: /openbmc/linux/drivers/hwmon/ltc2990.c (revision a6282d170339f86e05fd24bdf3104ca644a4b3a4)
1df922703SMike Looijmans /*
2df922703SMike Looijmans  * Driver for Linear Technology LTC2990 power monitor
3df922703SMike Looijmans  *
4df922703SMike Looijmans  * Copyright (C) 2014 Topic Embedded Products
5df922703SMike Looijmans  * Author: Mike Looijmans <mike.looijmans@topic.nl>
6df922703SMike Looijmans  *
7df922703SMike Looijmans  * License: GPLv2
8df922703SMike Looijmans  *
9df922703SMike Looijmans  * This driver assumes the chip is wired as a dual current monitor, and
10df922703SMike Looijmans  * reports the voltage drop across two series resistors. It also reports
11df922703SMike Looijmans  * the chip's internal temperature and Vcc power supply voltage.
12df922703SMike Looijmans  */
13df922703SMike Looijmans 
14*a6282d17STom Levens #include <linux/bitops.h>
15df922703SMike Looijmans #include <linux/err.h>
16df922703SMike Looijmans #include <linux/hwmon.h>
17df922703SMike Looijmans #include <linux/hwmon-sysfs.h>
18df922703SMike Looijmans #include <linux/i2c.h>
19df922703SMike Looijmans #include <linux/kernel.h>
20df922703SMike Looijmans #include <linux/module.h>
21df922703SMike Looijmans 
22df922703SMike Looijmans #define LTC2990_STATUS	0x00
23df922703SMike Looijmans #define LTC2990_CONTROL	0x01
24df922703SMike Looijmans #define LTC2990_TRIGGER	0x02
25df922703SMike Looijmans #define LTC2990_TINT_MSB	0x04
26df922703SMike Looijmans #define LTC2990_V1_MSB	0x06
27df922703SMike Looijmans #define LTC2990_V2_MSB	0x08
28df922703SMike Looijmans #define LTC2990_V3_MSB	0x0A
29df922703SMike Looijmans #define LTC2990_V4_MSB	0x0C
30df922703SMike Looijmans #define LTC2990_VCC_MSB	0x0E
31df922703SMike Looijmans 
32df922703SMike Looijmans #define LTC2990_CONTROL_KELVIN		BIT(7)
33df922703SMike Looijmans #define LTC2990_CONTROL_SINGLE		BIT(6)
34df922703SMike Looijmans #define LTC2990_CONTROL_MEASURE_ALL	(0x3 << 3)
35df922703SMike Looijmans #define LTC2990_CONTROL_MODE_CURRENT	0x06
36df922703SMike Looijmans #define LTC2990_CONTROL_MODE_VOLTAGE	0x07
37df922703SMike Looijmans 
38df922703SMike Looijmans /* Return the converted value from the given register in uV or mC */
39df922703SMike Looijmans static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, int *result)
40df922703SMike Looijmans {
41df922703SMike Looijmans 	int val;
42df922703SMike Looijmans 
43df922703SMike Looijmans 	val = i2c_smbus_read_word_swapped(i2c, reg);
44df922703SMike Looijmans 	if (unlikely(val < 0))
45df922703SMike Looijmans 		return val;
46df922703SMike Looijmans 
47df922703SMike Looijmans 	switch (reg) {
48df922703SMike Looijmans 	case LTC2990_TINT_MSB:
49df922703SMike Looijmans 		/* internal temp, 0.0625 degrees/LSB, 13-bit  */
50*a6282d17STom Levens 		*result = sign_extend32(val, 12) * 1000 / 16;
51df922703SMike Looijmans 		break;
52df922703SMike Looijmans 	case LTC2990_V1_MSB:
53df922703SMike Looijmans 	case LTC2990_V3_MSB:
54df922703SMike Looijmans 		 /* Vx-Vy, 19.42uV/LSB. Depends on mode. */
55*a6282d17STom Levens 		*result = sign_extend32(val, 14) * 1942 / 100;
56df922703SMike Looijmans 		break;
57df922703SMike Looijmans 	case LTC2990_VCC_MSB:
58df922703SMike Looijmans 		/* Vcc, 305.18μV/LSB, 2.5V offset */
59*a6282d17STom Levens 		*result = sign_extend32(val, 14) * 30518 / (100 * 1000) + 2500;
60df922703SMike Looijmans 		break;
61df922703SMike Looijmans 	default:
62df922703SMike Looijmans 		return -EINVAL; /* won't happen, keep compiler happy */
63df922703SMike Looijmans 	}
64df922703SMike Looijmans 
65df922703SMike Looijmans 	return 0;
66df922703SMike Looijmans }
67df922703SMike Looijmans 
68df922703SMike Looijmans static ssize_t ltc2990_show_value(struct device *dev,
69df922703SMike Looijmans 				  struct device_attribute *da, char *buf)
70df922703SMike Looijmans {
71df922703SMike Looijmans 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
72df922703SMike Looijmans 	int value;
73df922703SMike Looijmans 	int ret;
74df922703SMike Looijmans 
75df922703SMike Looijmans 	ret = ltc2990_get_value(dev_get_drvdata(dev), attr->index, &value);
76df922703SMike Looijmans 	if (unlikely(ret < 0))
77df922703SMike Looijmans 		return ret;
78df922703SMike Looijmans 
79df922703SMike Looijmans 	return snprintf(buf, PAGE_SIZE, "%d\n", value);
80df922703SMike Looijmans }
81df922703SMike Looijmans 
82df922703SMike Looijmans static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ltc2990_show_value, NULL,
83df922703SMike Looijmans 			  LTC2990_TINT_MSB);
84df922703SMike Looijmans static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc2990_show_value, NULL,
85df922703SMike Looijmans 			  LTC2990_V1_MSB);
86df922703SMike Looijmans static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc2990_show_value, NULL,
87df922703SMike Looijmans 			  LTC2990_V3_MSB);
88df922703SMike Looijmans static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ltc2990_show_value, NULL,
89df922703SMike Looijmans 			  LTC2990_VCC_MSB);
90df922703SMike Looijmans 
91df922703SMike Looijmans static struct attribute *ltc2990_attrs[] = {
92df922703SMike Looijmans 	&sensor_dev_attr_temp1_input.dev_attr.attr,
93df922703SMike Looijmans 	&sensor_dev_attr_curr1_input.dev_attr.attr,
94df922703SMike Looijmans 	&sensor_dev_attr_curr2_input.dev_attr.attr,
95df922703SMike Looijmans 	&sensor_dev_attr_in0_input.dev_attr.attr,
96df922703SMike Looijmans 	NULL,
97df922703SMike Looijmans };
98df922703SMike Looijmans ATTRIBUTE_GROUPS(ltc2990);
99df922703SMike Looijmans 
100df922703SMike Looijmans static int ltc2990_i2c_probe(struct i2c_client *i2c,
101df922703SMike Looijmans 			     const struct i2c_device_id *id)
102df922703SMike Looijmans {
103df922703SMike Looijmans 	int ret;
104df922703SMike Looijmans 	struct device *hwmon_dev;
105df922703SMike Looijmans 
106df922703SMike Looijmans 	if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
107df922703SMike Looijmans 				     I2C_FUNC_SMBUS_WORD_DATA))
108df922703SMike Looijmans 		return -ENODEV;
109df922703SMike Looijmans 
110df922703SMike Looijmans 	/* Setup continuous mode, current monitor */
111df922703SMike Looijmans 	ret = i2c_smbus_write_byte_data(i2c, LTC2990_CONTROL,
112df922703SMike Looijmans 					LTC2990_CONTROL_MEASURE_ALL |
113df922703SMike Looijmans 					LTC2990_CONTROL_MODE_CURRENT);
114df922703SMike Looijmans 	if (ret < 0) {
115df922703SMike Looijmans 		dev_err(&i2c->dev, "Error: Failed to set control mode.\n");
116df922703SMike Looijmans 		return ret;
117df922703SMike Looijmans 	}
118df922703SMike Looijmans 	/* Trigger once to start continuous conversion */
119df922703SMike Looijmans 	ret = i2c_smbus_write_byte_data(i2c, LTC2990_TRIGGER, 1);
120df922703SMike Looijmans 	if (ret < 0) {
121df922703SMike Looijmans 		dev_err(&i2c->dev, "Error: Failed to start acquisition.\n");
122df922703SMike Looijmans 		return ret;
123df922703SMike Looijmans 	}
124df922703SMike Looijmans 
125df922703SMike Looijmans 	hwmon_dev = devm_hwmon_device_register_with_groups(&i2c->dev,
126df922703SMike Looijmans 							   i2c->name,
127df922703SMike Looijmans 							   i2c,
128df922703SMike Looijmans 							   ltc2990_groups);
129df922703SMike Looijmans 
130df922703SMike Looijmans 	return PTR_ERR_OR_ZERO(hwmon_dev);
131df922703SMike Looijmans }
132df922703SMike Looijmans 
133df922703SMike Looijmans static const struct i2c_device_id ltc2990_i2c_id[] = {
134df922703SMike Looijmans 	{ "ltc2990", 0 },
135df922703SMike Looijmans 	{}
136df922703SMike Looijmans };
137df922703SMike Looijmans MODULE_DEVICE_TABLE(i2c, ltc2990_i2c_id);
138df922703SMike Looijmans 
139df922703SMike Looijmans static struct i2c_driver ltc2990_i2c_driver = {
140df922703SMike Looijmans 	.driver = {
141df922703SMike Looijmans 		.name = "ltc2990",
142df922703SMike Looijmans 	},
143df922703SMike Looijmans 	.probe    = ltc2990_i2c_probe,
144df922703SMike Looijmans 	.id_table = ltc2990_i2c_id,
145df922703SMike Looijmans };
146df922703SMike Looijmans 
147df922703SMike Looijmans module_i2c_driver(ltc2990_i2c_driver);
148df922703SMike Looijmans 
149df922703SMike Looijmans MODULE_DESCRIPTION("LTC2990 Sensor Driver");
150df922703SMike Looijmans MODULE_AUTHOR("Topic Embedded Products");
151df922703SMike Looijmans MODULE_LICENSE("GPL v2");
152