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