Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Driver for Linear Technology LTC4215 I2C Hot Swap Controller
0004  *
0005  * Copyright (C) 2009 Ira W. Snyder <iws@ovro.caltech.edu>
0006  *
0007  * Datasheet:
0008  * http://www.linear.com/pc/downloadDocument.do?navId=H0,C1,C1003,C1006,C1163,P17572,D12697
0009  */
0010 
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <linux/init.h>
0014 #include <linux/err.h>
0015 #include <linux/slab.h>
0016 #include <linux/i2c.h>
0017 #include <linux/hwmon.h>
0018 #include <linux/hwmon-sysfs.h>
0019 #include <linux/jiffies.h>
0020 
0021 /* Here are names of the chip's registers (a.k.a. commands) */
0022 enum ltc4215_cmd {
0023     LTC4215_CONTROL         = 0x00, /* rw */
0024     LTC4215_ALERT           = 0x01, /* rw */
0025     LTC4215_STATUS          = 0x02, /* ro */
0026     LTC4215_FAULT           = 0x03, /* rw */
0027     LTC4215_SENSE           = 0x04, /* rw */
0028     LTC4215_SOURCE          = 0x05, /* rw */
0029     LTC4215_ADIN            = 0x06, /* rw */
0030 };
0031 
0032 struct ltc4215_data {
0033     struct i2c_client *client;
0034 
0035     struct mutex update_lock;
0036     bool valid;
0037     unsigned long last_updated; /* in jiffies */
0038 
0039     /* Registers */
0040     u8 regs[7];
0041 };
0042 
0043 static struct ltc4215_data *ltc4215_update_device(struct device *dev)
0044 {
0045     struct ltc4215_data *data = dev_get_drvdata(dev);
0046     struct i2c_client *client = data->client;
0047     s32 val;
0048     int i;
0049 
0050     mutex_lock(&data->update_lock);
0051 
0052     /* The chip's A/D updates 10 times per second */
0053     if (time_after(jiffies, data->last_updated + HZ / 10) || !data->valid) {
0054 
0055         dev_dbg(&client->dev, "Starting ltc4215 update\n");
0056 
0057         /* Read all registers */
0058         for (i = 0; i < ARRAY_SIZE(data->regs); i++) {
0059             val = i2c_smbus_read_byte_data(client, i);
0060             if (unlikely(val < 0))
0061                 data->regs[i] = 0;
0062             else
0063                 data->regs[i] = val;
0064         }
0065 
0066         data->last_updated = jiffies;
0067         data->valid = true;
0068     }
0069 
0070     mutex_unlock(&data->update_lock);
0071 
0072     return data;
0073 }
0074 
0075 /* Return the voltage from the given register in millivolts */
0076 static int ltc4215_get_voltage(struct device *dev, u8 reg)
0077 {
0078     struct ltc4215_data *data = ltc4215_update_device(dev);
0079     const u8 regval = data->regs[reg];
0080     u32 voltage = 0;
0081 
0082     switch (reg) {
0083     case LTC4215_SENSE:
0084         /* 151 uV per increment */
0085         voltage = regval * 151 / 1000;
0086         break;
0087     case LTC4215_SOURCE:
0088         /* 60.5 mV per increment */
0089         voltage = regval * 605 / 10;
0090         break;
0091     case LTC4215_ADIN:
0092         /*
0093          * The ADIN input is divided by 12.5, and has 4.82 mV
0094          * per increment, so we have the additional multiply
0095          */
0096         voltage = regval * 482 * 125 / 1000;
0097         break;
0098     default:
0099         /* If we get here, the developer messed up */
0100         WARN_ON_ONCE(1);
0101         break;
0102     }
0103 
0104     return voltage;
0105 }
0106 
0107 /* Return the current from the sense resistor in mA */
0108 static unsigned int ltc4215_get_current(struct device *dev)
0109 {
0110     struct ltc4215_data *data = ltc4215_update_device(dev);
0111 
0112     /*
0113      * The strange looking conversions that follow are fixed-point
0114      * math, since we cannot do floating point in the kernel.
0115      *
0116      * Step 1: convert sense register to microVolts
0117      * Step 2: convert voltage to milliAmperes
0118      *
0119      * If you play around with the V=IR equation, you come up with
0120      * the following: X uV / Y mOhm == Z mA
0121      *
0122      * With the resistors that are fractions of a milliOhm, we multiply
0123      * the voltage and resistance by 10, to shift the decimal point.
0124      * Now we can use the normal division operator again.
0125      */
0126 
0127     /* Calculate voltage in microVolts (151 uV per increment) */
0128     const unsigned int voltage = data->regs[LTC4215_SENSE] * 151;
0129 
0130     /* Calculate current in milliAmperes (4 milliOhm sense resistor) */
0131     const unsigned int curr = voltage / 4;
0132 
0133     return curr;
0134 }
0135 
0136 static ssize_t ltc4215_voltage_show(struct device *dev,
0137                     struct device_attribute *da, char *buf)
0138 {
0139     struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
0140     const int voltage = ltc4215_get_voltage(dev, attr->index);
0141 
0142     return sysfs_emit(buf, "%d\n", voltage);
0143 }
0144 
0145 static ssize_t ltc4215_current_show(struct device *dev,
0146                     struct device_attribute *da, char *buf)
0147 {
0148     const unsigned int curr = ltc4215_get_current(dev);
0149 
0150     return sysfs_emit(buf, "%u\n", curr);
0151 }
0152 
0153 static ssize_t ltc4215_power_show(struct device *dev,
0154                   struct device_attribute *da, char *buf)
0155 {
0156     const unsigned int curr = ltc4215_get_current(dev);
0157     const int output_voltage = ltc4215_get_voltage(dev, LTC4215_ADIN);
0158 
0159     /* current in mA * voltage in mV == power in uW */
0160     const unsigned int power = abs(output_voltage * curr);
0161 
0162     return sysfs_emit(buf, "%u\n", power);
0163 }
0164 
0165 static ssize_t ltc4215_alarm_show(struct device *dev,
0166                   struct device_attribute *da, char *buf)
0167 {
0168     struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
0169     struct ltc4215_data *data = ltc4215_update_device(dev);
0170     const u8 reg = data->regs[LTC4215_STATUS];
0171     const u32 mask = attr->index;
0172 
0173     return sysfs_emit(buf, "%u\n", !!(reg & mask));
0174 }
0175 
0176 /*
0177  * These macros are used below in constructing device attribute objects
0178  * for use with sysfs_create_group() to make a sysfs device file
0179  * for each register.
0180  */
0181 
0182 /* Construct a sensor_device_attribute structure for each register */
0183 
0184 /* Current */
0185 static SENSOR_DEVICE_ATTR_RO(curr1_input, ltc4215_current, 0);
0186 static SENSOR_DEVICE_ATTR_RO(curr1_max_alarm, ltc4215_alarm, 1 << 2);
0187 
0188 /* Power (virtual) */
0189 static SENSOR_DEVICE_ATTR_RO(power1_input, ltc4215_power, 0);
0190 
0191 /* Input Voltage */
0192 static SENSOR_DEVICE_ATTR_RO(in1_input, ltc4215_voltage, LTC4215_ADIN);
0193 static SENSOR_DEVICE_ATTR_RO(in1_max_alarm, ltc4215_alarm, 1 << 0);
0194 static SENSOR_DEVICE_ATTR_RO(in1_min_alarm, ltc4215_alarm, 1 << 1);
0195 
0196 /* Output Voltage */
0197 static SENSOR_DEVICE_ATTR_RO(in2_input, ltc4215_voltage, LTC4215_SOURCE);
0198 static SENSOR_DEVICE_ATTR_RO(in2_min_alarm, ltc4215_alarm, 1 << 3);
0199 
0200 /*
0201  * Finally, construct an array of pointers to members of the above objects,
0202  * as required for sysfs_create_group()
0203  */
0204 static struct attribute *ltc4215_attrs[] = {
0205     &sensor_dev_attr_curr1_input.dev_attr.attr,
0206     &sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
0207 
0208     &sensor_dev_attr_power1_input.dev_attr.attr,
0209 
0210     &sensor_dev_attr_in1_input.dev_attr.attr,
0211     &sensor_dev_attr_in1_max_alarm.dev_attr.attr,
0212     &sensor_dev_attr_in1_min_alarm.dev_attr.attr,
0213 
0214     &sensor_dev_attr_in2_input.dev_attr.attr,
0215     &sensor_dev_attr_in2_min_alarm.dev_attr.attr,
0216 
0217     NULL,
0218 };
0219 ATTRIBUTE_GROUPS(ltc4215);
0220 
0221 static int ltc4215_probe(struct i2c_client *client)
0222 {
0223     struct i2c_adapter *adapter = client->adapter;
0224     struct device *dev = &client->dev;
0225     struct ltc4215_data *data;
0226     struct device *hwmon_dev;
0227 
0228     if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
0229         return -ENODEV;
0230 
0231     data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
0232     if (!data)
0233         return -ENOMEM;
0234 
0235     data->client = client;
0236     mutex_init(&data->update_lock);
0237 
0238     /* Initialize the LTC4215 chip */
0239     i2c_smbus_write_byte_data(client, LTC4215_FAULT, 0x00);
0240 
0241     hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
0242                                data,
0243                                ltc4215_groups);
0244     return PTR_ERR_OR_ZERO(hwmon_dev);
0245 }
0246 
0247 static const struct i2c_device_id ltc4215_id[] = {
0248     { "ltc4215", 0 },
0249     { }
0250 };
0251 MODULE_DEVICE_TABLE(i2c, ltc4215_id);
0252 
0253 /* This is the driver that will be inserted */
0254 static struct i2c_driver ltc4215_driver = {
0255     .driver = {
0256         .name   = "ltc4215",
0257     },
0258     .probe_new  = ltc4215_probe,
0259     .id_table   = ltc4215_id,
0260 };
0261 
0262 module_i2c_driver(ltc4215_driver);
0263 
0264 MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
0265 MODULE_DESCRIPTION("LTC4215 driver");
0266 MODULE_LICENSE("GPL");