Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hwmon: Driver for Linear Technologies LTC4222

Driver Linear Technologies LTC4222 Dual Hot Swap Controller
The driver currently only supports voltage monitoring, not voltage control.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>

+250
+12
drivers/hwmon/Kconfig
··· 798 798 This driver can also be built as a module. If so, the module will 799 799 be called ltc4215. 800 800 801 + config SENSORS_LTC4222 802 + tristate "Linear Technology LTC4222" 803 + depends on I2C 804 + select REGMAP_I2C 805 + default n 806 + help 807 + If you say yes here you get support for Linear Technology LTC4222 808 + Dual Hot Swap Controller I2C interface. 809 + 810 + This driver can also be built as a module. If so, the module will 811 + be called ltc4222. 812 + 801 813 config SENSORS_LTC4245 802 814 tristate "Linear Technology LTC4245" 803 815 depends on I2C
+1
drivers/hwmon/Makefile
··· 97 97 obj-$(CONFIG_SENSORS_LM95245) += lm95245.o 98 98 obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o 99 99 obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o 100 + obj-$(CONFIG_SENSORS_LTC4222) += ltc4222.o 100 101 obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o 101 102 obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o 102 103 obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
+237
drivers/hwmon/ltc4222.c
··· 1 + /* 2 + * Driver for Linear Technology LTC4222 Dual Hot Swap controller 3 + * 4 + * Copyright (c) 2014 Guenter Roeck 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation; either version 2 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * This program is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + */ 16 + 17 + #include <linux/kernel.h> 18 + #include <linux/module.h> 19 + #include <linux/err.h> 20 + #include <linux/slab.h> 21 + #include <linux/bitops.h> 22 + #include <linux/i2c.h> 23 + #include <linux/hwmon.h> 24 + #include <linux/hwmon-sysfs.h> 25 + #include <linux/jiffies.h> 26 + #include <linux/regmap.h> 27 + 28 + /* chip registers */ 29 + 30 + #define LTC4222_CONTROL1 0xd0 31 + #define LTC4222_ALERT1 0xd1 32 + #define LTC4222_STATUS1 0xd2 33 + #define LTC4222_FAULT1 0xd3 34 + #define LTC4222_CONTROL2 0xd4 35 + #define LTC4222_ALERT2 0xd5 36 + #define LTC4222_STATUS2 0xd6 37 + #define LTC4222_FAULT2 0xd7 38 + #define LTC4222_SOURCE1 0xd8 39 + #define LTC4222_SOURCE2 0xda 40 + #define LTC4222_ADIN1 0xdc 41 + #define LTC4222_ADIN2 0xde 42 + #define LTC4222_SENSE1 0xe0 43 + #define LTC4222_SENSE2 0xe2 44 + #define LTC4222_ADC_CONTROL 0xe4 45 + 46 + /* 47 + * Fault register bits 48 + */ 49 + #define FAULT_OV BIT(0) 50 + #define FAULT_UV BIT(1) 51 + #define FAULT_OC BIT(2) 52 + #define FAULT_POWER_BAD BIT(3) 53 + #define FAULT_FET_BAD BIT(5) 54 + 55 + /* Return the voltage from the given register in mV or mA */ 56 + static int ltc4222_get_value(struct device *dev, u8 reg) 57 + { 58 + struct regmap *regmap = dev_get_drvdata(dev); 59 + unsigned int val; 60 + u8 buf[2]; 61 + int ret; 62 + 63 + ret = regmap_bulk_read(regmap, reg, buf, 2); 64 + if (ret < 0) 65 + return ret; 66 + 67 + val = ((buf[0] << 8) + buf[1]) >> 6; 68 + 69 + switch (reg) { 70 + case LTC4222_ADIN1: 71 + case LTC4222_ADIN2: 72 + /* 1.25 mV resolution. Convert to mV. */ 73 + val = DIV_ROUND_CLOSEST(val * 5, 4); 74 + break; 75 + case LTC4222_SOURCE1: 76 + case LTC4222_SOURCE2: 77 + /* 31.25 mV resolution. Convert to mV. */ 78 + val = DIV_ROUND_CLOSEST(val * 125, 4); 79 + break; 80 + case LTC4222_SENSE1: 81 + case LTC4222_SENSE2: 82 + /* 83 + * 62.5 uV resolution. Convert to current as measured with 84 + * an 1 mOhm sense resistor, in mA. If a different sense 85 + * resistor is installed, calculate the actual current by 86 + * dividing the reported current by the sense resistor value 87 + * in mOhm. 88 + */ 89 + val = DIV_ROUND_CLOSEST(val * 125, 2); 90 + break; 91 + default: 92 + return -EINVAL; 93 + } 94 + return val; 95 + } 96 + 97 + static ssize_t ltc4222_show_value(struct device *dev, 98 + struct device_attribute *da, char *buf) 99 + { 100 + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 101 + int value; 102 + 103 + value = ltc4222_get_value(dev, attr->index); 104 + if (value < 0) 105 + return value; 106 + return snprintf(buf, PAGE_SIZE, "%d\n", value); 107 + } 108 + 109 + static ssize_t ltc4222_show_bool(struct device *dev, 110 + struct device_attribute *da, char *buf) 111 + { 112 + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da); 113 + struct regmap *regmap = dev_get_drvdata(dev); 114 + unsigned int fault; 115 + int ret; 116 + 117 + ret = regmap_read(regmap, attr->nr, &fault); 118 + if (ret < 0) 119 + return ret; 120 + fault &= attr->index; 121 + if (fault) /* Clear reported faults in chip register */ 122 + regmap_update_bits(regmap, attr->nr, attr->index, 0); 123 + 124 + return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); 125 + } 126 + 127 + /* Voltages */ 128 + static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4222_show_value, NULL, 129 + LTC4222_SOURCE1); 130 + static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4222_show_value, NULL, 131 + LTC4222_ADIN1); 132 + static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc4222_show_value, NULL, 133 + LTC4222_SOURCE2); 134 + static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc4222_show_value, NULL, 135 + LTC4222_ADIN2); 136 + 137 + /* 138 + * Voltage alarms 139 + * UV/OV faults are associated with the input voltage, and power bad and fet 140 + * faults are associated with the output voltage. 141 + */ 142 + static SENSOR_DEVICE_ATTR_2(in1_min_alarm, S_IRUGO, ltc4222_show_bool, NULL, 143 + LTC4222_FAULT1, FAULT_UV); 144 + static SENSOR_DEVICE_ATTR_2(in1_max_alarm, S_IRUGO, ltc4222_show_bool, NULL, 145 + LTC4222_FAULT1, FAULT_OV); 146 + static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, ltc4222_show_bool, NULL, 147 + LTC4222_FAULT1, FAULT_POWER_BAD | FAULT_FET_BAD); 148 + 149 + static SENSOR_DEVICE_ATTR_2(in3_min_alarm, S_IRUGO, ltc4222_show_bool, NULL, 150 + LTC4222_FAULT2, FAULT_UV); 151 + static SENSOR_DEVICE_ATTR_2(in3_max_alarm, S_IRUGO, ltc4222_show_bool, NULL, 152 + LTC4222_FAULT2, FAULT_OV); 153 + static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, ltc4222_show_bool, NULL, 154 + LTC4222_FAULT2, FAULT_POWER_BAD | FAULT_FET_BAD); 155 + 156 + /* Current (via sense resistor) */ 157 + static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4222_show_value, NULL, 158 + LTC4222_SENSE1); 159 + static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc4222_show_value, NULL, 160 + LTC4222_SENSE2); 161 + 162 + /* Overcurrent alarm */ 163 + static SENSOR_DEVICE_ATTR_2(curr1_max_alarm, S_IRUGO, ltc4222_show_bool, NULL, 164 + LTC4222_FAULT1, FAULT_OC); 165 + static SENSOR_DEVICE_ATTR_2(curr2_max_alarm, S_IRUGO, ltc4222_show_bool, NULL, 166 + LTC4222_FAULT2, FAULT_OC); 167 + 168 + static struct attribute *ltc4222_attrs[] = { 169 + &sensor_dev_attr_in1_input.dev_attr.attr, 170 + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, 171 + &sensor_dev_attr_in1_max_alarm.dev_attr.attr, 172 + &sensor_dev_attr_in2_input.dev_attr.attr, 173 + &sensor_dev_attr_in2_alarm.dev_attr.attr, 174 + &sensor_dev_attr_in3_input.dev_attr.attr, 175 + &sensor_dev_attr_in3_min_alarm.dev_attr.attr, 176 + &sensor_dev_attr_in3_max_alarm.dev_attr.attr, 177 + &sensor_dev_attr_in4_input.dev_attr.attr, 178 + &sensor_dev_attr_in4_alarm.dev_attr.attr, 179 + 180 + &sensor_dev_attr_curr1_input.dev_attr.attr, 181 + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, 182 + &sensor_dev_attr_curr2_input.dev_attr.attr, 183 + &sensor_dev_attr_curr2_max_alarm.dev_attr.attr, 184 + 185 + NULL, 186 + }; 187 + ATTRIBUTE_GROUPS(ltc4222); 188 + 189 + static struct regmap_config ltc4222_regmap_config = { 190 + .reg_bits = 8, 191 + .val_bits = 8, 192 + .max_register = LTC4222_ADC_CONTROL, 193 + }; 194 + 195 + static int ltc4222_probe(struct i2c_client *client, 196 + const struct i2c_device_id *id) 197 + { 198 + struct device *dev = &client->dev; 199 + struct device *hwmon_dev; 200 + struct regmap *regmap; 201 + 202 + regmap = devm_regmap_init_i2c(client, &ltc4222_regmap_config); 203 + if (IS_ERR(regmap)) { 204 + dev_err(dev, "failed to allocate register map\n"); 205 + return PTR_ERR(regmap); 206 + } 207 + 208 + /* Clear faults */ 209 + regmap_write(regmap, LTC4222_FAULT1, 0x00); 210 + regmap_write(regmap, LTC4222_FAULT2, 0x00); 211 + 212 + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, 213 + regmap, 214 + ltc4222_groups); 215 + return PTR_ERR_OR_ZERO(hwmon_dev); 216 + } 217 + 218 + static const struct i2c_device_id ltc4222_id[] = { 219 + {"ltc4222", 0}, 220 + { } 221 + }; 222 + 223 + MODULE_DEVICE_TABLE(i2c, ltc4222_id); 224 + 225 + static struct i2c_driver ltc4222_driver = { 226 + .driver = { 227 + .name = "ltc4222", 228 + }, 229 + .probe = ltc4222_probe, 230 + .id_table = ltc4222_id, 231 + }; 232 + 233 + module_i2c_driver(ltc4222_driver); 234 + 235 + MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); 236 + MODULE_DESCRIPTION("LTC4222 driver"); 237 + MODULE_LICENSE("GPL");