linux/drivers/hwmon/ltc4215.c
<<
>>
Prefs
   1/*
   2 * Driver for Linear Technology LTC4215 I2C Hot Swap Controller
   3 *
   4 * Copyright (C) 2009 Ira W. Snyder <iws@ovro.caltech.edu>
   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; version 2 of the License.
   9 *
  10 * Datasheet:
  11 * http://www.linear.com/pc/downloadDocument.do?navId=H0,C1,C1003,C1006,C1163,P17572,D12697
  12 */
  13
  14#include <linux/kernel.h>
  15#include <linux/module.h>
  16#include <linux/init.h>
  17#include <linux/err.h>
  18#include <linux/slab.h>
  19#include <linux/i2c.h>
  20#include <linux/hwmon.h>
  21#include <linux/hwmon-sysfs.h>
  22
  23static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
  24
  25/* Insmod parameters */
  26I2C_CLIENT_INSMOD_1(ltc4215);
  27
  28/* Here are names of the chip's registers (a.k.a. commands) */
  29enum ltc4215_cmd {
  30        LTC4215_CONTROL                 = 0x00, /* rw */
  31        LTC4215_ALERT                   = 0x01, /* rw */
  32        LTC4215_STATUS                  = 0x02, /* ro */
  33        LTC4215_FAULT                   = 0x03, /* rw */
  34        LTC4215_SENSE                   = 0x04, /* rw */
  35        LTC4215_SOURCE                  = 0x05, /* rw */
  36        LTC4215_ADIN                    = 0x06, /* rw */
  37};
  38
  39struct ltc4215_data {
  40        struct device *hwmon_dev;
  41
  42        struct mutex update_lock;
  43        bool valid;
  44        unsigned long last_updated; /* in jiffies */
  45
  46        /* Registers */
  47        u8 regs[7];
  48};
  49
  50static struct ltc4215_data *ltc4215_update_device(struct device *dev)
  51{
  52        struct i2c_client *client = to_i2c_client(dev);
  53        struct ltc4215_data *data = i2c_get_clientdata(client);
  54        s32 val;
  55        int i;
  56
  57        mutex_lock(&data->update_lock);
  58
  59        /* The chip's A/D updates 10 times per second */
  60        if (time_after(jiffies, data->last_updated + HZ / 10) || !data->valid) {
  61
  62                dev_dbg(&client->dev, "Starting ltc4215 update\n");
  63
  64                /* Read all registers */
  65                for (i = 0; i < ARRAY_SIZE(data->regs); i++) {
  66                        val = i2c_smbus_read_byte_data(client, i);
  67                        if (unlikely(val < 0))
  68                                data->regs[i] = 0;
  69                        else
  70                                data->regs[i] = val;
  71                }
  72
  73                data->last_updated = jiffies;
  74                data->valid = 1;
  75        }
  76
  77        mutex_unlock(&data->update_lock);
  78
  79        return data;
  80}
  81
  82/* Return the voltage from the given register in millivolts */
  83static int ltc4215_get_voltage(struct device *dev, u8 reg)
  84{
  85        struct ltc4215_data *data = ltc4215_update_device(dev);
  86        const u8 regval = data->regs[reg];
  87        u32 voltage = 0;
  88
  89        switch (reg) {
  90        case LTC4215_SENSE:
  91                /* 151 uV per increment */
  92                voltage = regval * 151 / 1000;
  93                break;
  94        case LTC4215_SOURCE:
  95                /* 60.5 mV per increment */
  96                voltage = regval * 605 / 10;
  97                break;
  98        case LTC4215_ADIN:
  99                /* The ADIN input is divided by 12.5, and has 4.82 mV
 100                 * per increment, so we have the additional multiply */
 101                voltage = regval * 482 * 125 / 1000;
 102                break;
 103        default:
 104                /* If we get here, the developer messed up */
 105                WARN_ON_ONCE(1);
 106                break;
 107        }
 108
 109        return voltage;
 110}
 111
 112/* Return the current from the sense resistor in mA */
 113static unsigned int ltc4215_get_current(struct device *dev)
 114{
 115        struct ltc4215_data *data = ltc4215_update_device(dev);
 116
 117        /* The strange looking conversions that follow are fixed-point
 118         * math, since we cannot do floating point in the kernel.
 119         *
 120         * Step 1: convert sense register to microVolts
 121         * Step 2: convert voltage to milliAmperes
 122         *
 123         * If you play around with the V=IR equation, you come up with
 124         * the following: X uV / Y mOhm == Z mA
 125         *
 126         * With the resistors that are fractions of a milliOhm, we multiply
 127         * the voltage and resistance by 10, to shift the decimal point.
 128         * Now we can use the normal division operator again.
 129         */
 130
 131        /* Calculate voltage in microVolts (151 uV per increment) */
 132        const unsigned int voltage = data->regs[LTC4215_SENSE] * 151;
 133
 134        /* Calculate current in milliAmperes (4 milliOhm sense resistor) */
 135        const unsigned int curr = voltage / 4;
 136
 137        return curr;
 138}
 139
 140static ssize_t ltc4215_show_voltage(struct device *dev,
 141                                    struct device_attribute *da,
 142                                    char *buf)
 143{
 144        struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
 145        const int voltage = ltc4215_get_voltage(dev, attr->index);
 146
 147        return snprintf(buf, PAGE_SIZE, "%d\n", voltage);
 148}
 149
 150static ssize_t ltc4215_show_current(struct device *dev,
 151                                    struct device_attribute *da,
 152                                    char *buf)
 153{
 154        const unsigned int curr = ltc4215_get_current(dev);
 155
 156        return snprintf(buf, PAGE_SIZE, "%u\n", curr);
 157}
 158
 159static ssize_t ltc4215_show_power(struct device *dev,
 160                                  struct device_attribute *da,
 161                                  char *buf)
 162{
 163        const unsigned int curr = ltc4215_get_current(dev);
 164        const int output_voltage = ltc4215_get_voltage(dev, LTC4215_ADIN);
 165
 166        /* current in mA * voltage in mV == power in uW */
 167        const unsigned int power = abs(output_voltage * curr);
 168
 169        return snprintf(buf, PAGE_SIZE, "%u\n", power);
 170}
 171
 172static ssize_t ltc4215_show_alarm(struct device *dev,
 173                                          struct device_attribute *da,
 174                                          char *buf)
 175{
 176        struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da);
 177        struct ltc4215_data *data = ltc4215_update_device(dev);
 178        const u8 reg = data->regs[attr->index];
 179        const u32 mask = attr->nr;
 180
 181        return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0);
 182}
 183
 184/* These macros are used below in constructing device attribute objects
 185 * for use with sysfs_create_group() to make a sysfs device file
 186 * for each register.
 187 */
 188
 189#define LTC4215_VOLTAGE(name, ltc4215_cmd_idx) \
 190        static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
 191        ltc4215_show_voltage, NULL, ltc4215_cmd_idx)
 192
 193#define LTC4215_CURRENT(name) \
 194        static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
 195        ltc4215_show_current, NULL, 0);
 196
 197#define LTC4215_POWER(name) \
 198        static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
 199        ltc4215_show_power, NULL, 0);
 200
 201#define LTC4215_ALARM(name, mask, reg) \
 202        static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \
 203        ltc4215_show_alarm, NULL, (mask), reg)
 204
 205/* Construct a sensor_device_attribute structure for each register */
 206
 207/* Current */
 208LTC4215_CURRENT(curr1_input);
 209LTC4215_ALARM(curr1_max_alarm,  (1 << 2),       LTC4215_STATUS);
 210
 211/* Power (virtual) */
 212LTC4215_POWER(power1_input);
 213LTC4215_ALARM(power1_alarm,     (1 << 3),       LTC4215_STATUS);
 214
 215/* Input Voltage */
 216LTC4215_VOLTAGE(in1_input,                      LTC4215_ADIN);
 217LTC4215_ALARM(in1_max_alarm,    (1 << 0),       LTC4215_STATUS);
 218LTC4215_ALARM(in1_min_alarm,    (1 << 1),       LTC4215_STATUS);
 219
 220/* Output Voltage */
 221LTC4215_VOLTAGE(in2_input,                      LTC4215_SOURCE);
 222
 223/* Finally, construct an array of pointers to members of the above objects,
 224 * as required for sysfs_create_group()
 225 */
 226static struct attribute *ltc4215_attributes[] = {
 227        &sensor_dev_attr_curr1_input.dev_attr.attr,
 228        &sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
 229
 230        &sensor_dev_attr_power1_input.dev_attr.attr,
 231        &sensor_dev_attr_power1_alarm.dev_attr.attr,
 232
 233        &sensor_dev_attr_in1_input.dev_attr.attr,
 234        &sensor_dev_attr_in1_max_alarm.dev_attr.attr,
 235        &sensor_dev_attr_in1_min_alarm.dev_attr.attr,
 236
 237        &sensor_dev_attr_in2_input.dev_attr.attr,
 238
 239        NULL,
 240};
 241
 242static const struct attribute_group ltc4215_group = {
 243        .attrs = ltc4215_attributes,
 244};
 245
 246static int ltc4215_probe(struct i2c_client *client,
 247                         const struct i2c_device_id *id)
 248{
 249        struct ltc4215_data *data;
 250        int ret;
 251
 252        data = kzalloc(sizeof(*data), GFP_KERNEL);
 253        if (!data) {
 254                ret = -ENOMEM;
 255                goto out_kzalloc;
 256        }
 257
 258        i2c_set_clientdata(client, data);
 259        mutex_init(&data->update_lock);
 260
 261        /* Initialize the LTC4215 chip */
 262        /* TODO */
 263
 264        /* Register sysfs hooks */
 265        ret = sysfs_create_group(&client->dev.kobj, &ltc4215_group);
 266        if (ret)
 267                goto out_sysfs_create_group;
 268
 269        data->hwmon_dev = hwmon_device_register(&client->dev);
 270        if (IS_ERR(data->hwmon_dev)) {
 271                ret = PTR_ERR(data->hwmon_dev);
 272                goto out_hwmon_device_register;
 273        }
 274
 275        return 0;
 276
 277out_hwmon_device_register:
 278        sysfs_remove_group(&client->dev.kobj, &ltc4215_group);
 279out_sysfs_create_group:
 280        kfree(data);
 281out_kzalloc:
 282        return ret;
 283}
 284
 285static int ltc4215_remove(struct i2c_client *client)
 286{
 287        struct ltc4215_data *data = i2c_get_clientdata(client);
 288
 289        hwmon_device_unregister(data->hwmon_dev);
 290        sysfs_remove_group(&client->dev.kobj, &ltc4215_group);
 291
 292        kfree(data);
 293
 294        return 0;
 295}
 296
 297static int ltc4215_detect(struct i2c_client *client,
 298                          int kind,
 299                          struct i2c_board_info *info)
 300{
 301        struct i2c_adapter *adapter = client->adapter;
 302
 303        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
 304                return -ENODEV;
 305
 306        if (kind < 0) {         /* probed detection - check the chip type */
 307                s32 v;          /* 8 bits from the chip, or -ERRNO */
 308
 309                /*
 310                 * Register 0x01 bit b7 is reserved, expect 0
 311                 * Register 0x03 bit b6 and b7 are reserved, expect 0
 312                 */
 313                v = i2c_smbus_read_byte_data(client, LTC4215_ALERT);
 314                if (v < 0 || (v & (1 << 7)) != 0)
 315                        return -ENODEV;
 316
 317                v = i2c_smbus_read_byte_data(client, LTC4215_FAULT);
 318                if (v < 0 || (v & ((1 << 6) | (1 << 7))) != 0)
 319                                return -ENODEV;
 320        }
 321
 322        strlcpy(info->type, "ltc4215", I2C_NAME_SIZE);
 323        dev_info(&adapter->dev, "ltc4215 %s at address 0x%02x\n",
 324                        kind < 0 ? "probed" : "forced",
 325                        client->addr);
 326
 327        return 0;
 328}
 329
 330static const struct i2c_device_id ltc4215_id[] = {
 331        { "ltc4215", ltc4215 },
 332        { }
 333};
 334MODULE_DEVICE_TABLE(i2c, ltc4215_id);
 335
 336/* This is the driver that will be inserted */
 337static struct i2c_driver ltc4215_driver = {
 338        .class          = I2C_CLASS_HWMON,
 339        .driver = {
 340                .name   = "ltc4215",
 341        },
 342        .probe          = ltc4215_probe,
 343        .remove         = ltc4215_remove,
 344        .id_table       = ltc4215_id,
 345        .detect         = ltc4215_detect,
 346        .address_data   = &addr_data,
 347};
 348
 349static int __init ltc4215_init(void)
 350{
 351        return i2c_add_driver(&ltc4215_driver);
 352}
 353
 354static void __exit ltc4215_exit(void)
 355{
 356        i2c_del_driver(&ltc4215_driver);
 357}
 358
 359MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
 360MODULE_DESCRIPTION("LTC4215 driver");
 361MODULE_LICENSE("GPL");
 362
 363module_init(ltc4215_init);
 364module_exit(ltc4215_exit);
 365