xref: /openbmc/linux/drivers/hwmon/ltc4260.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
269e1ded6SGuenter Roeck /*
369e1ded6SGuenter Roeck  * Driver for Linear Technology LTC4260 I2C Positive Voltage Hot Swap Controller
469e1ded6SGuenter Roeck  *
569e1ded6SGuenter Roeck  * Copyright (c) 2014 Guenter Roeck
669e1ded6SGuenter Roeck  */
769e1ded6SGuenter Roeck 
869e1ded6SGuenter Roeck #include <linux/kernel.h>
969e1ded6SGuenter Roeck #include <linux/module.h>
1069e1ded6SGuenter Roeck #include <linux/err.h>
1169e1ded6SGuenter Roeck #include <linux/slab.h>
1269e1ded6SGuenter Roeck #include <linux/i2c.h>
1369e1ded6SGuenter Roeck #include <linux/hwmon.h>
1469e1ded6SGuenter Roeck #include <linux/hwmon-sysfs.h>
1569e1ded6SGuenter Roeck #include <linux/jiffies.h>
1669e1ded6SGuenter Roeck #include <linux/regmap.h>
1769e1ded6SGuenter Roeck 
1869e1ded6SGuenter Roeck /* chip registers */
1969e1ded6SGuenter Roeck #define LTC4260_CONTROL	0x00
2069e1ded6SGuenter Roeck #define LTC4260_ALERT	0x01
2169e1ded6SGuenter Roeck #define LTC4260_STATUS	0x02
2269e1ded6SGuenter Roeck #define LTC4260_FAULT	0x03
2369e1ded6SGuenter Roeck #define LTC4260_SENSE	0x04
2469e1ded6SGuenter Roeck #define LTC4260_SOURCE	0x05
2569e1ded6SGuenter Roeck #define LTC4260_ADIN	0x06
2669e1ded6SGuenter Roeck 
2769e1ded6SGuenter Roeck /*
2869e1ded6SGuenter Roeck  * Fault register bits
2969e1ded6SGuenter Roeck  */
3069e1ded6SGuenter Roeck #define FAULT_OV	(1 << 0)
3169e1ded6SGuenter Roeck #define FAULT_UV	(1 << 1)
3269e1ded6SGuenter Roeck #define FAULT_OC	(1 << 2)
3369e1ded6SGuenter Roeck #define FAULT_POWER_BAD	(1 << 3)
3469e1ded6SGuenter Roeck #define FAULT_FET_SHORT	(1 << 5)
3569e1ded6SGuenter Roeck 
3669e1ded6SGuenter Roeck /* Return the voltage from the given register in mV or mA */
ltc4260_get_value(struct device * dev,u8 reg)3769e1ded6SGuenter Roeck static int ltc4260_get_value(struct device *dev, u8 reg)
3869e1ded6SGuenter Roeck {
3969e1ded6SGuenter Roeck 	struct regmap *regmap = dev_get_drvdata(dev);
4069e1ded6SGuenter Roeck 	unsigned int val;
4169e1ded6SGuenter Roeck 	int ret;
4269e1ded6SGuenter Roeck 
4369e1ded6SGuenter Roeck 	ret = regmap_read(regmap, reg, &val);
4469e1ded6SGuenter Roeck 	if (ret < 0)
4569e1ded6SGuenter Roeck 		return ret;
4669e1ded6SGuenter Roeck 
4769e1ded6SGuenter Roeck 	switch (reg) {
4869e1ded6SGuenter Roeck 	case LTC4260_ADIN:
4969e1ded6SGuenter Roeck 		/* 10 mV resolution. Convert to mV. */
5069e1ded6SGuenter Roeck 		val = val * 10;
5169e1ded6SGuenter Roeck 		break;
5269e1ded6SGuenter Roeck 	case LTC4260_SOURCE:
5369e1ded6SGuenter Roeck 		/* 400 mV resolution. Convert to mV. */
5469e1ded6SGuenter Roeck 		val = val * 400;
5569e1ded6SGuenter Roeck 		break;
5669e1ded6SGuenter Roeck 	case LTC4260_SENSE:
5769e1ded6SGuenter Roeck 		/*
5869e1ded6SGuenter Roeck 		 * 300 uV resolution. Convert to current as measured with
5969e1ded6SGuenter Roeck 		 * an 1 mOhm sense resistor, in mA. If a different sense
6069e1ded6SGuenter Roeck 		 * resistor is installed, calculate the actual current by
6169e1ded6SGuenter Roeck 		 * dividing the reported current by the sense resistor value
6269e1ded6SGuenter Roeck 		 * in mOhm.
6369e1ded6SGuenter Roeck 		 */
6469e1ded6SGuenter Roeck 		val = val * 300;
6569e1ded6SGuenter Roeck 		break;
6669e1ded6SGuenter Roeck 	default:
6769e1ded6SGuenter Roeck 		return -EINVAL;
6869e1ded6SGuenter Roeck 	}
6969e1ded6SGuenter Roeck 
7069e1ded6SGuenter Roeck 	return val;
7169e1ded6SGuenter Roeck }
7269e1ded6SGuenter Roeck 
ltc4260_value_show(struct device * dev,struct device_attribute * da,char * buf)733d628b29SGuenter Roeck static ssize_t ltc4260_value_show(struct device *dev,
7469e1ded6SGuenter Roeck 				  struct device_attribute *da, char *buf)
7569e1ded6SGuenter Roeck {
7669e1ded6SGuenter Roeck 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
7769e1ded6SGuenter Roeck 	int value;
7869e1ded6SGuenter Roeck 
7969e1ded6SGuenter Roeck 	value = ltc4260_get_value(dev, attr->index);
8069e1ded6SGuenter Roeck 	if (value < 0)
8169e1ded6SGuenter Roeck 		return value;
821f4d4af4SGuenter Roeck 	return sysfs_emit(buf, "%d\n", value);
8369e1ded6SGuenter Roeck }
8469e1ded6SGuenter Roeck 
ltc4260_bool_show(struct device * dev,struct device_attribute * da,char * buf)853d628b29SGuenter Roeck static ssize_t ltc4260_bool_show(struct device *dev,
8669e1ded6SGuenter Roeck 				 struct device_attribute *da, char *buf)
8769e1ded6SGuenter Roeck {
8869e1ded6SGuenter Roeck 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
8969e1ded6SGuenter Roeck 	struct regmap *regmap = dev_get_drvdata(dev);
9069e1ded6SGuenter Roeck 	unsigned int fault;
9169e1ded6SGuenter Roeck 	int ret;
9269e1ded6SGuenter Roeck 
9369e1ded6SGuenter Roeck 	ret = regmap_read(regmap, LTC4260_FAULT, &fault);
9469e1ded6SGuenter Roeck 	if (ret < 0)
9569e1ded6SGuenter Roeck 		return ret;
9669e1ded6SGuenter Roeck 
9769e1ded6SGuenter Roeck 	fault &= attr->index;
9869e1ded6SGuenter Roeck 	if (fault)		/* Clear reported faults in chip register */
9969e1ded6SGuenter Roeck 		regmap_update_bits(regmap, LTC4260_FAULT, attr->index, 0);
10069e1ded6SGuenter Roeck 
1011f4d4af4SGuenter Roeck 	return sysfs_emit(buf, "%d\n", !!fault);
10269e1ded6SGuenter Roeck }
10369e1ded6SGuenter Roeck 
10469e1ded6SGuenter Roeck /* Voltages */
1053d628b29SGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in1_input, ltc4260_value, LTC4260_SOURCE);
1063d628b29SGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in2_input, ltc4260_value, LTC4260_ADIN);
10769e1ded6SGuenter Roeck 
10869e1ded6SGuenter Roeck /*
10969e1ded6SGuenter Roeck  * Voltage alarms
11069e1ded6SGuenter Roeck  * UV/OV faults are associated with the input voltage, and the POWER BAD and
11169e1ded6SGuenter Roeck  * FET SHORT faults are associated with the output voltage.
11269e1ded6SGuenter Roeck  */
1133d628b29SGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in1_min_alarm, ltc4260_bool, FAULT_UV);
1143d628b29SGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in1_max_alarm, ltc4260_bool, FAULT_OV);
1153d628b29SGuenter Roeck static SENSOR_DEVICE_ATTR_RO(in2_alarm, ltc4260_bool,
11669e1ded6SGuenter Roeck 			     FAULT_POWER_BAD | FAULT_FET_SHORT);
11769e1ded6SGuenter Roeck 
11869e1ded6SGuenter Roeck /* Current (via sense resistor) */
1193d628b29SGuenter Roeck static SENSOR_DEVICE_ATTR_RO(curr1_input, ltc4260_value, LTC4260_SENSE);
12069e1ded6SGuenter Roeck 
12169e1ded6SGuenter Roeck /* Overcurrent alarm */
1223d628b29SGuenter Roeck static SENSOR_DEVICE_ATTR_RO(curr1_max_alarm, ltc4260_bool, FAULT_OC);
12369e1ded6SGuenter Roeck 
12469e1ded6SGuenter Roeck static struct attribute *ltc4260_attrs[] = {
12569e1ded6SGuenter Roeck 	&sensor_dev_attr_in1_input.dev_attr.attr,
12669e1ded6SGuenter Roeck 	&sensor_dev_attr_in1_min_alarm.dev_attr.attr,
12769e1ded6SGuenter Roeck 	&sensor_dev_attr_in1_max_alarm.dev_attr.attr,
12869e1ded6SGuenter Roeck 	&sensor_dev_attr_in2_input.dev_attr.attr,
12969e1ded6SGuenter Roeck 	&sensor_dev_attr_in2_alarm.dev_attr.attr,
13069e1ded6SGuenter Roeck 
13169e1ded6SGuenter Roeck 	&sensor_dev_attr_curr1_input.dev_attr.attr,
13269e1ded6SGuenter Roeck 	&sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
13369e1ded6SGuenter Roeck 
13469e1ded6SGuenter Roeck 	NULL,
13569e1ded6SGuenter Roeck };
13669e1ded6SGuenter Roeck ATTRIBUTE_GROUPS(ltc4260);
13769e1ded6SGuenter Roeck 
138034b44b4SAxel Lin static const struct regmap_config ltc4260_regmap_config = {
13969e1ded6SGuenter Roeck 	.reg_bits = 8,
14069e1ded6SGuenter Roeck 	.val_bits = 8,
14169e1ded6SGuenter Roeck 	.max_register = LTC4260_ADIN,
14269e1ded6SGuenter Roeck };
14369e1ded6SGuenter Roeck 
ltc4260_probe(struct i2c_client * client)14467487038SStephen Kitt static int ltc4260_probe(struct i2c_client *client)
14569e1ded6SGuenter Roeck {
14669e1ded6SGuenter Roeck 	struct device *dev = &client->dev;
14769e1ded6SGuenter Roeck 	struct device *hwmon_dev;
14869e1ded6SGuenter Roeck 	struct regmap *regmap;
14969e1ded6SGuenter Roeck 
15069e1ded6SGuenter Roeck 	regmap = devm_regmap_init_i2c(client, &ltc4260_regmap_config);
15169e1ded6SGuenter Roeck 	if (IS_ERR(regmap)) {
15269e1ded6SGuenter Roeck 		dev_err(dev, "failed to allocate register map\n");
15369e1ded6SGuenter Roeck 		return PTR_ERR(regmap);
15469e1ded6SGuenter Roeck 	}
15569e1ded6SGuenter Roeck 
15669e1ded6SGuenter Roeck 	/* Clear faults */
15769e1ded6SGuenter Roeck 	regmap_write(regmap, LTC4260_FAULT, 0x00);
15869e1ded6SGuenter Roeck 
15969e1ded6SGuenter Roeck 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
16069e1ded6SGuenter Roeck 							   regmap,
16169e1ded6SGuenter Roeck 							   ltc4260_groups);
16269e1ded6SGuenter Roeck 	return PTR_ERR_OR_ZERO(hwmon_dev);
16369e1ded6SGuenter Roeck }
16469e1ded6SGuenter Roeck 
16569e1ded6SGuenter Roeck static const struct i2c_device_id ltc4260_id[] = {
16669e1ded6SGuenter Roeck 	{"ltc4260", 0},
16769e1ded6SGuenter Roeck 	{ }
16869e1ded6SGuenter Roeck };
16969e1ded6SGuenter Roeck 
17069e1ded6SGuenter Roeck MODULE_DEVICE_TABLE(i2c, ltc4260_id);
17169e1ded6SGuenter Roeck 
17269e1ded6SGuenter Roeck static struct i2c_driver ltc4260_driver = {
17369e1ded6SGuenter Roeck 	.driver = {
17469e1ded6SGuenter Roeck 		   .name = "ltc4260",
17569e1ded6SGuenter Roeck 		   },
176*1975d167SUwe Kleine-König 	.probe = ltc4260_probe,
17769e1ded6SGuenter Roeck 	.id_table = ltc4260_id,
17869e1ded6SGuenter Roeck };
17969e1ded6SGuenter Roeck 
18069e1ded6SGuenter Roeck module_i2c_driver(ltc4260_driver);
18169e1ded6SGuenter Roeck 
18269e1ded6SGuenter Roeck MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
18369e1ded6SGuenter Roeck MODULE_DESCRIPTION("LTC4260 driver");
18469e1ded6SGuenter Roeck MODULE_LICENSE("GPL");
185