xref: /openbmc/linux/drivers/hwmon/ltc4260.c (revision 034b44b4a22371c5bcbfb602a5420f3b75939dd8)
169e1ded6SGuenter Roeck /*
269e1ded6SGuenter Roeck  * Driver for Linear Technology LTC4260 I2C Positive Voltage Hot Swap Controller
369e1ded6SGuenter Roeck  *
469e1ded6SGuenter Roeck  * Copyright (c) 2014 Guenter Roeck
569e1ded6SGuenter Roeck  *
669e1ded6SGuenter Roeck  * This program is free software; you can redistribute it and/or modify
769e1ded6SGuenter Roeck  * it under the terms of the GNU General Public License as published by
869e1ded6SGuenter Roeck  * the Free Software Foundation; either version 2 of the License, or
969e1ded6SGuenter Roeck  * (at your option) any later version.
1069e1ded6SGuenter Roeck  *
1169e1ded6SGuenter Roeck  * This program is distributed in the hope that it will be useful,
1269e1ded6SGuenter Roeck  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1369e1ded6SGuenter Roeck  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1469e1ded6SGuenter Roeck  * GNU General Public License for more details.
1569e1ded6SGuenter Roeck  */
1669e1ded6SGuenter Roeck 
1769e1ded6SGuenter Roeck #include <linux/kernel.h>
1869e1ded6SGuenter Roeck #include <linux/module.h>
1969e1ded6SGuenter Roeck #include <linux/err.h>
2069e1ded6SGuenter Roeck #include <linux/slab.h>
2169e1ded6SGuenter Roeck #include <linux/i2c.h>
2269e1ded6SGuenter Roeck #include <linux/hwmon.h>
2369e1ded6SGuenter Roeck #include <linux/hwmon-sysfs.h>
2469e1ded6SGuenter Roeck #include <linux/jiffies.h>
2569e1ded6SGuenter Roeck #include <linux/regmap.h>
2669e1ded6SGuenter Roeck 
2769e1ded6SGuenter Roeck /* chip registers */
2869e1ded6SGuenter Roeck #define LTC4260_CONTROL	0x00
2969e1ded6SGuenter Roeck #define LTC4260_ALERT	0x01
3069e1ded6SGuenter Roeck #define LTC4260_STATUS	0x02
3169e1ded6SGuenter Roeck #define LTC4260_FAULT	0x03
3269e1ded6SGuenter Roeck #define LTC4260_SENSE	0x04
3369e1ded6SGuenter Roeck #define LTC4260_SOURCE	0x05
3469e1ded6SGuenter Roeck #define LTC4260_ADIN	0x06
3569e1ded6SGuenter Roeck 
3669e1ded6SGuenter Roeck /*
3769e1ded6SGuenter Roeck  * Fault register bits
3869e1ded6SGuenter Roeck  */
3969e1ded6SGuenter Roeck #define FAULT_OV	(1 << 0)
4069e1ded6SGuenter Roeck #define FAULT_UV	(1 << 1)
4169e1ded6SGuenter Roeck #define FAULT_OC	(1 << 2)
4269e1ded6SGuenter Roeck #define FAULT_POWER_BAD	(1 << 3)
4369e1ded6SGuenter Roeck #define FAULT_FET_SHORT	(1 << 5)
4469e1ded6SGuenter Roeck 
4569e1ded6SGuenter Roeck /* Return the voltage from the given register in mV or mA */
4669e1ded6SGuenter Roeck static int ltc4260_get_value(struct device *dev, u8 reg)
4769e1ded6SGuenter Roeck {
4869e1ded6SGuenter Roeck 	struct regmap *regmap = dev_get_drvdata(dev);
4969e1ded6SGuenter Roeck 	unsigned int val;
5069e1ded6SGuenter Roeck 	int ret;
5169e1ded6SGuenter Roeck 
5269e1ded6SGuenter Roeck 	ret = regmap_read(regmap, reg, &val);
5369e1ded6SGuenter Roeck 	if (ret < 0)
5469e1ded6SGuenter Roeck 		return ret;
5569e1ded6SGuenter Roeck 
5669e1ded6SGuenter Roeck 	switch (reg) {
5769e1ded6SGuenter Roeck 	case LTC4260_ADIN:
5869e1ded6SGuenter Roeck 		/* 10 mV resolution. Convert to mV. */
5969e1ded6SGuenter Roeck 		val = val * 10;
6069e1ded6SGuenter Roeck 		break;
6169e1ded6SGuenter Roeck 	case LTC4260_SOURCE:
6269e1ded6SGuenter Roeck 		/* 400 mV resolution. Convert to mV. */
6369e1ded6SGuenter Roeck 		val = val * 400;
6469e1ded6SGuenter Roeck 		break;
6569e1ded6SGuenter Roeck 	case LTC4260_SENSE:
6669e1ded6SGuenter Roeck 		/*
6769e1ded6SGuenter Roeck 		 * 300 uV resolution. Convert to current as measured with
6869e1ded6SGuenter Roeck 		 * an 1 mOhm sense resistor, in mA. If a different sense
6969e1ded6SGuenter Roeck 		 * resistor is installed, calculate the actual current by
7069e1ded6SGuenter Roeck 		 * dividing the reported current by the sense resistor value
7169e1ded6SGuenter Roeck 		 * in mOhm.
7269e1ded6SGuenter Roeck 		 */
7369e1ded6SGuenter Roeck 		val = val * 300;
7469e1ded6SGuenter Roeck 		break;
7569e1ded6SGuenter Roeck 	default:
7669e1ded6SGuenter Roeck 		return -EINVAL;
7769e1ded6SGuenter Roeck 	}
7869e1ded6SGuenter Roeck 
7969e1ded6SGuenter Roeck 	return val;
8069e1ded6SGuenter Roeck }
8169e1ded6SGuenter Roeck 
8269e1ded6SGuenter Roeck static ssize_t ltc4260_show_value(struct device *dev,
8369e1ded6SGuenter Roeck 				  struct device_attribute *da, char *buf)
8469e1ded6SGuenter Roeck {
8569e1ded6SGuenter Roeck 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
8669e1ded6SGuenter Roeck 	int value;
8769e1ded6SGuenter Roeck 
8869e1ded6SGuenter Roeck 	value = ltc4260_get_value(dev, attr->index);
8969e1ded6SGuenter Roeck 	if (value < 0)
9069e1ded6SGuenter Roeck 		return value;
9169e1ded6SGuenter Roeck 	return snprintf(buf, PAGE_SIZE, "%d\n", value);
9269e1ded6SGuenter Roeck }
9369e1ded6SGuenter Roeck 
9469e1ded6SGuenter Roeck static ssize_t ltc4260_show_bool(struct device *dev,
9569e1ded6SGuenter Roeck 				 struct device_attribute *da, char *buf)
9669e1ded6SGuenter Roeck {
9769e1ded6SGuenter Roeck 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
9869e1ded6SGuenter Roeck 	struct regmap *regmap = dev_get_drvdata(dev);
9969e1ded6SGuenter Roeck 	unsigned int fault;
10069e1ded6SGuenter Roeck 	int ret;
10169e1ded6SGuenter Roeck 
10269e1ded6SGuenter Roeck 	ret = regmap_read(regmap, LTC4260_FAULT, &fault);
10369e1ded6SGuenter Roeck 	if (ret < 0)
10469e1ded6SGuenter Roeck 		return ret;
10569e1ded6SGuenter Roeck 
10669e1ded6SGuenter Roeck 	fault &= attr->index;
10769e1ded6SGuenter Roeck 	if (fault)		/* Clear reported faults in chip register */
10869e1ded6SGuenter Roeck 		regmap_update_bits(regmap, LTC4260_FAULT, attr->index, 0);
10969e1ded6SGuenter Roeck 
11069e1ded6SGuenter Roeck 	return snprintf(buf, PAGE_SIZE, "%d\n", !!fault);
11169e1ded6SGuenter Roeck }
11269e1ded6SGuenter Roeck 
11369e1ded6SGuenter Roeck /* Voltages */
11469e1ded6SGuenter Roeck static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4260_show_value, NULL,
11569e1ded6SGuenter Roeck 			  LTC4260_SOURCE);
11669e1ded6SGuenter Roeck static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4260_show_value, NULL,
11769e1ded6SGuenter Roeck 			  LTC4260_ADIN);
11869e1ded6SGuenter Roeck 
11969e1ded6SGuenter Roeck /*
12069e1ded6SGuenter Roeck  * Voltage alarms
12169e1ded6SGuenter Roeck  * UV/OV faults are associated with the input voltage, and the POWER BAD and
12269e1ded6SGuenter Roeck  * FET SHORT faults are associated with the output voltage.
12369e1ded6SGuenter Roeck  */
12469e1ded6SGuenter Roeck static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc4260_show_bool, NULL,
12569e1ded6SGuenter Roeck 			  FAULT_UV);
12669e1ded6SGuenter Roeck static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc4260_show_bool, NULL,
12769e1ded6SGuenter Roeck 			  FAULT_OV);
12869e1ded6SGuenter Roeck static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, ltc4260_show_bool, NULL,
12969e1ded6SGuenter Roeck 			  FAULT_POWER_BAD | FAULT_FET_SHORT);
13069e1ded6SGuenter Roeck 
13169e1ded6SGuenter Roeck /* Current (via sense resistor) */
13269e1ded6SGuenter Roeck static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4260_show_value, NULL,
13369e1ded6SGuenter Roeck 			  LTC4260_SENSE);
13469e1ded6SGuenter Roeck 
13569e1ded6SGuenter Roeck /* Overcurrent alarm */
13669e1ded6SGuenter Roeck static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc4260_show_bool, NULL,
13769e1ded6SGuenter Roeck 			  FAULT_OC);
13869e1ded6SGuenter Roeck 
13969e1ded6SGuenter Roeck static struct attribute *ltc4260_attrs[] = {
14069e1ded6SGuenter Roeck 	&sensor_dev_attr_in1_input.dev_attr.attr,
14169e1ded6SGuenter Roeck 	&sensor_dev_attr_in1_min_alarm.dev_attr.attr,
14269e1ded6SGuenter Roeck 	&sensor_dev_attr_in1_max_alarm.dev_attr.attr,
14369e1ded6SGuenter Roeck 	&sensor_dev_attr_in2_input.dev_attr.attr,
14469e1ded6SGuenter Roeck 	&sensor_dev_attr_in2_alarm.dev_attr.attr,
14569e1ded6SGuenter Roeck 
14669e1ded6SGuenter Roeck 	&sensor_dev_attr_curr1_input.dev_attr.attr,
14769e1ded6SGuenter Roeck 	&sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
14869e1ded6SGuenter Roeck 
14969e1ded6SGuenter Roeck 	NULL,
15069e1ded6SGuenter Roeck };
15169e1ded6SGuenter Roeck ATTRIBUTE_GROUPS(ltc4260);
15269e1ded6SGuenter Roeck 
153*034b44b4SAxel Lin static const struct regmap_config ltc4260_regmap_config = {
15469e1ded6SGuenter Roeck 	.reg_bits = 8,
15569e1ded6SGuenter Roeck 	.val_bits = 8,
15669e1ded6SGuenter Roeck 	.max_register = LTC4260_ADIN,
15769e1ded6SGuenter Roeck };
15869e1ded6SGuenter Roeck 
15969e1ded6SGuenter Roeck static int ltc4260_probe(struct i2c_client *client,
16069e1ded6SGuenter Roeck 			 const struct i2c_device_id *id)
16169e1ded6SGuenter Roeck {
16269e1ded6SGuenter Roeck 	struct device *dev = &client->dev;
16369e1ded6SGuenter Roeck 	struct device *hwmon_dev;
16469e1ded6SGuenter Roeck 	struct regmap *regmap;
16569e1ded6SGuenter Roeck 
16669e1ded6SGuenter Roeck 	regmap = devm_regmap_init_i2c(client, &ltc4260_regmap_config);
16769e1ded6SGuenter Roeck 	if (IS_ERR(regmap)) {
16869e1ded6SGuenter Roeck 		dev_err(dev, "failed to allocate register map\n");
16969e1ded6SGuenter Roeck 		return PTR_ERR(regmap);
17069e1ded6SGuenter Roeck 	}
17169e1ded6SGuenter Roeck 
17269e1ded6SGuenter Roeck 	/* Clear faults */
17369e1ded6SGuenter Roeck 	regmap_write(regmap, LTC4260_FAULT, 0x00);
17469e1ded6SGuenter Roeck 
17569e1ded6SGuenter Roeck 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
17669e1ded6SGuenter Roeck 							   regmap,
17769e1ded6SGuenter Roeck 							   ltc4260_groups);
17869e1ded6SGuenter Roeck 	return PTR_ERR_OR_ZERO(hwmon_dev);
17969e1ded6SGuenter Roeck }
18069e1ded6SGuenter Roeck 
18169e1ded6SGuenter Roeck static const struct i2c_device_id ltc4260_id[] = {
18269e1ded6SGuenter Roeck 	{"ltc4260", 0},
18369e1ded6SGuenter Roeck 	{ }
18469e1ded6SGuenter Roeck };
18569e1ded6SGuenter Roeck 
18669e1ded6SGuenter Roeck MODULE_DEVICE_TABLE(i2c, ltc4260_id);
18769e1ded6SGuenter Roeck 
18869e1ded6SGuenter Roeck static struct i2c_driver ltc4260_driver = {
18969e1ded6SGuenter Roeck 	.driver = {
19069e1ded6SGuenter Roeck 		   .name = "ltc4260",
19169e1ded6SGuenter Roeck 		   },
19269e1ded6SGuenter Roeck 	.probe = ltc4260_probe,
19369e1ded6SGuenter Roeck 	.id_table = ltc4260_id,
19469e1ded6SGuenter Roeck };
19569e1ded6SGuenter Roeck 
19669e1ded6SGuenter Roeck module_i2c_driver(ltc4260_driver);
19769e1ded6SGuenter Roeck 
19869e1ded6SGuenter Roeck MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
19969e1ded6SGuenter Roeck MODULE_DESCRIPTION("LTC4260 driver");
20069e1ded6SGuenter Roeck MODULE_LICENSE("GPL");
201