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