xref: /openbmc/linux/drivers/hwmon/ltc4260.c (revision 69e1ded651ee31c8a570d58647902a55806d0db8)
1*69e1ded6SGuenter Roeck /*
2*69e1ded6SGuenter Roeck  * Driver for Linear Technology LTC4260 I2C Positive Voltage Hot Swap Controller
3*69e1ded6SGuenter Roeck  *
4*69e1ded6SGuenter Roeck  * Copyright (c) 2014 Guenter Roeck
5*69e1ded6SGuenter Roeck  *
6*69e1ded6SGuenter Roeck  * This program is free software; you can redistribute it and/or modify
7*69e1ded6SGuenter Roeck  * it under the terms of the GNU General Public License as published by
8*69e1ded6SGuenter Roeck  * the Free Software Foundation; either version 2 of the License, or
9*69e1ded6SGuenter Roeck  * (at your option) any later version.
10*69e1ded6SGuenter Roeck  *
11*69e1ded6SGuenter Roeck  * This program is distributed in the hope that it will be useful,
12*69e1ded6SGuenter Roeck  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13*69e1ded6SGuenter Roeck  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*69e1ded6SGuenter Roeck  * GNU General Public License for more details.
15*69e1ded6SGuenter Roeck  */
16*69e1ded6SGuenter Roeck 
17*69e1ded6SGuenter Roeck #include <linux/kernel.h>
18*69e1ded6SGuenter Roeck #include <linux/module.h>
19*69e1ded6SGuenter Roeck #include <linux/err.h>
20*69e1ded6SGuenter Roeck #include <linux/slab.h>
21*69e1ded6SGuenter Roeck #include <linux/i2c.h>
22*69e1ded6SGuenter Roeck #include <linux/hwmon.h>
23*69e1ded6SGuenter Roeck #include <linux/hwmon-sysfs.h>
24*69e1ded6SGuenter Roeck #include <linux/jiffies.h>
25*69e1ded6SGuenter Roeck #include <linux/regmap.h>
26*69e1ded6SGuenter Roeck 
27*69e1ded6SGuenter Roeck /* chip registers */
28*69e1ded6SGuenter Roeck #define LTC4260_CONTROL	0x00
29*69e1ded6SGuenter Roeck #define LTC4260_ALERT	0x01
30*69e1ded6SGuenter Roeck #define LTC4260_STATUS	0x02
31*69e1ded6SGuenter Roeck #define LTC4260_FAULT	0x03
32*69e1ded6SGuenter Roeck #define LTC4260_SENSE	0x04
33*69e1ded6SGuenter Roeck #define LTC4260_SOURCE	0x05
34*69e1ded6SGuenter Roeck #define LTC4260_ADIN	0x06
35*69e1ded6SGuenter Roeck 
36*69e1ded6SGuenter Roeck /*
37*69e1ded6SGuenter Roeck  * Fault register bits
38*69e1ded6SGuenter Roeck  */
39*69e1ded6SGuenter Roeck #define FAULT_OV	(1 << 0)
40*69e1ded6SGuenter Roeck #define FAULT_UV	(1 << 1)
41*69e1ded6SGuenter Roeck #define FAULT_OC	(1 << 2)
42*69e1ded6SGuenter Roeck #define FAULT_POWER_BAD	(1 << 3)
43*69e1ded6SGuenter Roeck #define FAULT_FET_SHORT	(1 << 5)
44*69e1ded6SGuenter Roeck 
45*69e1ded6SGuenter Roeck /* Return the voltage from the given register in mV or mA */
46*69e1ded6SGuenter Roeck static int ltc4260_get_value(struct device *dev, u8 reg)
47*69e1ded6SGuenter Roeck {
48*69e1ded6SGuenter Roeck 	struct regmap *regmap = dev_get_drvdata(dev);
49*69e1ded6SGuenter Roeck 	unsigned int val;
50*69e1ded6SGuenter Roeck 	int ret;
51*69e1ded6SGuenter Roeck 
52*69e1ded6SGuenter Roeck 	ret = regmap_read(regmap, reg, &val);
53*69e1ded6SGuenter Roeck 	if (ret < 0)
54*69e1ded6SGuenter Roeck 		return ret;
55*69e1ded6SGuenter Roeck 
56*69e1ded6SGuenter Roeck 	switch (reg) {
57*69e1ded6SGuenter Roeck 	case LTC4260_ADIN:
58*69e1ded6SGuenter Roeck 		/* 10 mV resolution. Convert to mV. */
59*69e1ded6SGuenter Roeck 		val = val * 10;
60*69e1ded6SGuenter Roeck 		break;
61*69e1ded6SGuenter Roeck 	case LTC4260_SOURCE:
62*69e1ded6SGuenter Roeck 		/* 400 mV resolution. Convert to mV. */
63*69e1ded6SGuenter Roeck 		val = val * 400;
64*69e1ded6SGuenter Roeck 		break;
65*69e1ded6SGuenter Roeck 	case LTC4260_SENSE:
66*69e1ded6SGuenter Roeck 		/*
67*69e1ded6SGuenter Roeck 		 * 300 uV resolution. Convert to current as measured with
68*69e1ded6SGuenter Roeck 		 * an 1 mOhm sense resistor, in mA. If a different sense
69*69e1ded6SGuenter Roeck 		 * resistor is installed, calculate the actual current by
70*69e1ded6SGuenter Roeck 		 * dividing the reported current by the sense resistor value
71*69e1ded6SGuenter Roeck 		 * in mOhm.
72*69e1ded6SGuenter Roeck 		 */
73*69e1ded6SGuenter Roeck 		val = val * 300;
74*69e1ded6SGuenter Roeck 		break;
75*69e1ded6SGuenter Roeck 	default:
76*69e1ded6SGuenter Roeck 		return -EINVAL;
77*69e1ded6SGuenter Roeck 	}
78*69e1ded6SGuenter Roeck 
79*69e1ded6SGuenter Roeck 	return val;
80*69e1ded6SGuenter Roeck }
81*69e1ded6SGuenter Roeck 
82*69e1ded6SGuenter Roeck static ssize_t ltc4260_show_value(struct device *dev,
83*69e1ded6SGuenter Roeck 				  struct device_attribute *da, char *buf)
84*69e1ded6SGuenter Roeck {
85*69e1ded6SGuenter Roeck 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
86*69e1ded6SGuenter Roeck 	int value;
87*69e1ded6SGuenter Roeck 
88*69e1ded6SGuenter Roeck 	value = ltc4260_get_value(dev, attr->index);
89*69e1ded6SGuenter Roeck 	if (value < 0)
90*69e1ded6SGuenter Roeck 		return value;
91*69e1ded6SGuenter Roeck 	return snprintf(buf, PAGE_SIZE, "%d\n", value);
92*69e1ded6SGuenter Roeck }
93*69e1ded6SGuenter Roeck 
94*69e1ded6SGuenter Roeck static ssize_t ltc4260_show_bool(struct device *dev,
95*69e1ded6SGuenter Roeck 				 struct device_attribute *da, char *buf)
96*69e1ded6SGuenter Roeck {
97*69e1ded6SGuenter Roeck 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
98*69e1ded6SGuenter Roeck 	struct regmap *regmap = dev_get_drvdata(dev);
99*69e1ded6SGuenter Roeck 	unsigned int fault;
100*69e1ded6SGuenter Roeck 	int ret;
101*69e1ded6SGuenter Roeck 
102*69e1ded6SGuenter Roeck 	ret = regmap_read(regmap, LTC4260_FAULT, &fault);
103*69e1ded6SGuenter Roeck 	if (ret < 0)
104*69e1ded6SGuenter Roeck 		return ret;
105*69e1ded6SGuenter Roeck 
106*69e1ded6SGuenter Roeck 	fault &= attr->index;
107*69e1ded6SGuenter Roeck 	if (fault)		/* Clear reported faults in chip register */
108*69e1ded6SGuenter Roeck 		regmap_update_bits(regmap, LTC4260_FAULT, attr->index, 0);
109*69e1ded6SGuenter Roeck 
110*69e1ded6SGuenter Roeck 	return snprintf(buf, PAGE_SIZE, "%d\n", !!fault);
111*69e1ded6SGuenter Roeck }
112*69e1ded6SGuenter Roeck 
113*69e1ded6SGuenter Roeck /* Voltages */
114*69e1ded6SGuenter Roeck static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4260_show_value, NULL,
115*69e1ded6SGuenter Roeck 			  LTC4260_SOURCE);
116*69e1ded6SGuenter Roeck static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4260_show_value, NULL,
117*69e1ded6SGuenter Roeck 			  LTC4260_ADIN);
118*69e1ded6SGuenter Roeck 
119*69e1ded6SGuenter Roeck /*
120*69e1ded6SGuenter Roeck  * Voltage alarms
121*69e1ded6SGuenter Roeck  * UV/OV faults are associated with the input voltage, and the POWER BAD and
122*69e1ded6SGuenter Roeck  * FET SHORT faults are associated with the output voltage.
123*69e1ded6SGuenter Roeck  */
124*69e1ded6SGuenter Roeck static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc4260_show_bool, NULL,
125*69e1ded6SGuenter Roeck 			  FAULT_UV);
126*69e1ded6SGuenter Roeck static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc4260_show_bool, NULL,
127*69e1ded6SGuenter Roeck 			  FAULT_OV);
128*69e1ded6SGuenter Roeck static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, ltc4260_show_bool, NULL,
129*69e1ded6SGuenter Roeck 			  FAULT_POWER_BAD | FAULT_FET_SHORT);
130*69e1ded6SGuenter Roeck 
131*69e1ded6SGuenter Roeck /* Current (via sense resistor) */
132*69e1ded6SGuenter Roeck static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4260_show_value, NULL,
133*69e1ded6SGuenter Roeck 			  LTC4260_SENSE);
134*69e1ded6SGuenter Roeck 
135*69e1ded6SGuenter Roeck /* Overcurrent alarm */
136*69e1ded6SGuenter Roeck static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc4260_show_bool, NULL,
137*69e1ded6SGuenter Roeck 			  FAULT_OC);
138*69e1ded6SGuenter Roeck 
139*69e1ded6SGuenter Roeck static struct attribute *ltc4260_attrs[] = {
140*69e1ded6SGuenter Roeck 	&sensor_dev_attr_in1_input.dev_attr.attr,
141*69e1ded6SGuenter Roeck 	&sensor_dev_attr_in1_min_alarm.dev_attr.attr,
142*69e1ded6SGuenter Roeck 	&sensor_dev_attr_in1_max_alarm.dev_attr.attr,
143*69e1ded6SGuenter Roeck 	&sensor_dev_attr_in2_input.dev_attr.attr,
144*69e1ded6SGuenter Roeck 	&sensor_dev_attr_in2_alarm.dev_attr.attr,
145*69e1ded6SGuenter Roeck 
146*69e1ded6SGuenter Roeck 	&sensor_dev_attr_curr1_input.dev_attr.attr,
147*69e1ded6SGuenter Roeck 	&sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
148*69e1ded6SGuenter Roeck 
149*69e1ded6SGuenter Roeck 	NULL,
150*69e1ded6SGuenter Roeck };
151*69e1ded6SGuenter Roeck ATTRIBUTE_GROUPS(ltc4260);
152*69e1ded6SGuenter Roeck 
153*69e1ded6SGuenter Roeck static struct regmap_config ltc4260_regmap_config = {
154*69e1ded6SGuenter Roeck 	.reg_bits = 8,
155*69e1ded6SGuenter Roeck 	.val_bits = 8,
156*69e1ded6SGuenter Roeck 	.max_register = LTC4260_ADIN,
157*69e1ded6SGuenter Roeck };
158*69e1ded6SGuenter Roeck 
159*69e1ded6SGuenter Roeck static int ltc4260_probe(struct i2c_client *client,
160*69e1ded6SGuenter Roeck 			 const struct i2c_device_id *id)
161*69e1ded6SGuenter Roeck {
162*69e1ded6SGuenter Roeck 	struct device *dev = &client->dev;
163*69e1ded6SGuenter Roeck 	struct device *hwmon_dev;
164*69e1ded6SGuenter Roeck 	struct regmap *regmap;
165*69e1ded6SGuenter Roeck 
166*69e1ded6SGuenter Roeck 	regmap = devm_regmap_init_i2c(client, &ltc4260_regmap_config);
167*69e1ded6SGuenter Roeck 	if (IS_ERR(regmap)) {
168*69e1ded6SGuenter Roeck 		dev_err(dev, "failed to allocate register map\n");
169*69e1ded6SGuenter Roeck 		return PTR_ERR(regmap);
170*69e1ded6SGuenter Roeck 	}
171*69e1ded6SGuenter Roeck 
172*69e1ded6SGuenter Roeck 	/* Clear faults */
173*69e1ded6SGuenter Roeck 	regmap_write(regmap, LTC4260_FAULT, 0x00);
174*69e1ded6SGuenter Roeck 
175*69e1ded6SGuenter Roeck 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
176*69e1ded6SGuenter Roeck 							   regmap,
177*69e1ded6SGuenter Roeck 							   ltc4260_groups);
178*69e1ded6SGuenter Roeck 	return PTR_ERR_OR_ZERO(hwmon_dev);
179*69e1ded6SGuenter Roeck }
180*69e1ded6SGuenter Roeck 
181*69e1ded6SGuenter Roeck static const struct i2c_device_id ltc4260_id[] = {
182*69e1ded6SGuenter Roeck 	{"ltc4260", 0},
183*69e1ded6SGuenter Roeck 	{ }
184*69e1ded6SGuenter Roeck };
185*69e1ded6SGuenter Roeck 
186*69e1ded6SGuenter Roeck MODULE_DEVICE_TABLE(i2c, ltc4260_id);
187*69e1ded6SGuenter Roeck 
188*69e1ded6SGuenter Roeck static struct i2c_driver ltc4260_driver = {
189*69e1ded6SGuenter Roeck 	.driver = {
190*69e1ded6SGuenter Roeck 		   .name = "ltc4260",
191*69e1ded6SGuenter Roeck 		   },
192*69e1ded6SGuenter Roeck 	.probe = ltc4260_probe,
193*69e1ded6SGuenter Roeck 	.id_table = ltc4260_id,
194*69e1ded6SGuenter Roeck };
195*69e1ded6SGuenter Roeck 
196*69e1ded6SGuenter Roeck module_i2c_driver(ltc4260_driver);
197*69e1ded6SGuenter Roeck 
198*69e1ded6SGuenter Roeck MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
199*69e1ded6SGuenter Roeck MODULE_DESCRIPTION("LTC4260 driver");
200*69e1ded6SGuenter Roeck MODULE_LICENSE("GPL");
201