Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Driver for Texas Instruments INA238 power monitor chip
0004  * Datasheet: https://www.ti.com/product/ina238
0005  *
0006  * Copyright (C) 2021 Nathan Rossi <nathan.rossi@digi.com>
0007  */
0008 
0009 #include <linux/err.h>
0010 #include <linux/hwmon.h>
0011 #include <linux/i2c.h>
0012 #include <linux/init.h>
0013 #include <linux/kernel.h>
0014 #include <linux/module.h>
0015 #include <linux/of.h>
0016 #include <linux/regmap.h>
0017 
0018 #include <linux/platform_data/ina2xx.h>
0019 
0020 /* INA238 register definitions */
0021 #define INA238_CONFIG           0x0
0022 #define INA238_ADC_CONFIG       0x1
0023 #define INA238_SHUNT_CALIBRATION    0x2
0024 #define INA238_SHUNT_VOLTAGE        0x4
0025 #define INA238_BUS_VOLTAGE      0x5
0026 #define INA238_DIE_TEMP         0x6
0027 #define INA238_CURRENT          0x7
0028 #define INA238_POWER            0x8
0029 #define INA238_DIAG_ALERT       0xb
0030 #define INA238_SHUNT_OVER_VOLTAGE   0xc
0031 #define INA238_SHUNT_UNDER_VOLTAGE  0xd
0032 #define INA238_BUS_OVER_VOLTAGE     0xe
0033 #define INA238_BUS_UNDER_VOLTAGE    0xf
0034 #define INA238_TEMP_LIMIT       0x10
0035 #define INA238_POWER_LIMIT      0x11
0036 #define INA238_DEVICE_ID        0x3f
0037 
0038 #define INA238_CONFIG_ADCRANGE      BIT(4)
0039 
0040 #define INA238_DIAG_ALERT_TMPOL     BIT(7)
0041 #define INA238_DIAG_ALERT_SHNTOL    BIT(6)
0042 #define INA238_DIAG_ALERT_SHNTUL    BIT(5)
0043 #define INA238_DIAG_ALERT_BUSOL     BIT(4)
0044 #define INA238_DIAG_ALERT_BUSUL     BIT(3)
0045 #define INA238_DIAG_ALERT_POL       BIT(2)
0046 
0047 #define INA238_REGISTERS        0x11
0048 
0049 #define INA238_RSHUNT_DEFAULT       10000 /* uOhm */
0050 
0051 /* Default configuration of device on reset. */
0052 #define INA238_CONFIG_DEFAULT       0
0053 /* 16 sample averaging, 1052us conversion time, continuous mode */
0054 #define INA238_ADC_CONFIG_DEFAULT   0xfb6a
0055 /* Configure alerts to be based on averaged value (SLOWALERT) */
0056 #define INA238_DIAG_ALERT_DEFAULT   0x2000
0057 /*
0058  * This driver uses a fixed calibration value in order to scale current/power
0059  * based on a fixed shunt resistor value. This allows for conversion within the
0060  * device to avoid integer limits whilst current/power accuracy is scaled
0061  * relative to the shunt resistor value within the driver. This is similar to
0062  * how the ina2xx driver handles current/power scaling.
0063  *
0064  * The end result of this is that increasing shunt values (from a fixed 20 mOhm
0065  * shunt) increase the effective current/power accuracy whilst limiting the
0066  * range and decreasing shunt values decrease the effective accuracy but
0067  * increase the range.
0068  *
0069  * The value of the Current register is calculated given the following:
0070  *   Current (A) = (shunt voltage register * 5) * calibration / 81920
0071  *
0072  * The maximum shunt voltage is 163.835 mV (0x7fff, ADC_RANGE = 0, gain = 4).
0073  * With the maximum current value of 0x7fff and a fixed shunt value results in
0074  * a calibration value of 16384 (0x4000).
0075  *
0076  *   0x7fff = (0x7fff * 5) * calibration / 81920
0077  *   calibration = 0x4000
0078  *
0079  * Equivalent calibration is applied for the Power register (maximum value for
0080  * bus voltage is 102396.875 mV, 0x7fff), where the maximum power that can
0081  * occur is ~16776192 uW (register value 0x147a8):
0082  *
0083  * This scaling means the resulting values for Current and Power registers need
0084  * to be scaled by the difference between the fixed shunt resistor and the
0085  * actual shunt resistor:
0086  *
0087  *  shunt = 0x4000 / (819.2 * 10^6) / 0.001 = 20000 uOhms (with 1mA/lsb)
0088  *
0089  *  Current (mA) = register value * 20000 / rshunt / 4 * gain
0090  *  Power (W) = 0.2 * register value * 20000 / rshunt / 4 * gain
0091  */
0092 #define INA238_CALIBRATION_VALUE    16384
0093 #define INA238_FIXED_SHUNT      20000
0094 
0095 #define INA238_SHUNT_VOLTAGE_LSB    5 /* 5 uV/lsb */
0096 #define INA238_BUS_VOLTAGE_LSB      3125 /* 3.125 mV/lsb */
0097 #define INA238_DIE_TEMP_LSB     125 /* 125 mC/lsb */
0098 
0099 static struct regmap_config ina238_regmap_config = {
0100     .max_register = INA238_REGISTERS,
0101     .reg_bits = 8,
0102     .val_bits = 16,
0103 };
0104 
0105 struct ina238_data {
0106     struct i2c_client *client;
0107     struct mutex config_lock;
0108     struct regmap *regmap;
0109     u32 rshunt;
0110     int gain;
0111 };
0112 
0113 static int ina238_read_reg24(const struct i2c_client *client, u8 reg, u32 *val)
0114 {
0115     u8 data[3];
0116     int err;
0117 
0118     /* 24-bit register read */
0119     err = i2c_smbus_read_i2c_block_data(client, reg, 3, data);
0120     if (err < 0)
0121         return err;
0122     if (err != 3)
0123         return -EIO;
0124     *val = (data[0] << 16) | (data[1] << 8) | data[2];
0125 
0126     return 0;
0127 }
0128 
0129 static int ina238_read_in(struct device *dev, u32 attr, int channel,
0130               long *val)
0131 {
0132     struct ina238_data *data = dev_get_drvdata(dev);
0133     int reg, mask;
0134     int regval;
0135     int err;
0136 
0137     switch (channel) {
0138     case 0:
0139         switch (attr) {
0140         case hwmon_in_input:
0141             reg = INA238_SHUNT_VOLTAGE;
0142             break;
0143         case hwmon_in_max:
0144             reg = INA238_SHUNT_OVER_VOLTAGE;
0145             break;
0146         case hwmon_in_min:
0147             reg = INA238_SHUNT_UNDER_VOLTAGE;
0148             break;
0149         case hwmon_in_max_alarm:
0150             reg = INA238_DIAG_ALERT;
0151             mask = INA238_DIAG_ALERT_SHNTOL;
0152             break;
0153         case hwmon_in_min_alarm:
0154             reg = INA238_DIAG_ALERT;
0155             mask = INA238_DIAG_ALERT_SHNTUL;
0156             break;
0157         default:
0158             return -EOPNOTSUPP;
0159         }
0160         break;
0161     case 1:
0162         switch (attr) {
0163         case hwmon_in_input:
0164             reg = INA238_BUS_VOLTAGE;
0165             break;
0166         case hwmon_in_max:
0167             reg = INA238_BUS_OVER_VOLTAGE;
0168             break;
0169         case hwmon_in_min:
0170             reg = INA238_BUS_UNDER_VOLTAGE;
0171             break;
0172         case hwmon_in_max_alarm:
0173             reg = INA238_DIAG_ALERT;
0174             mask = INA238_DIAG_ALERT_BUSOL;
0175             break;
0176         case hwmon_in_min_alarm:
0177             reg = INA238_DIAG_ALERT;
0178             mask = INA238_DIAG_ALERT_BUSUL;
0179             break;
0180         default:
0181             return -EOPNOTSUPP;
0182         }
0183         break;
0184     default:
0185         return -EOPNOTSUPP;
0186     }
0187 
0188     err = regmap_read(data->regmap, reg, &regval);
0189     if (err < 0)
0190         return err;
0191 
0192     switch (attr) {
0193     case hwmon_in_input:
0194     case hwmon_in_max:
0195     case hwmon_in_min:
0196         /* signed register, value in mV */
0197         regval = (s16)regval;
0198         if (channel == 0)
0199             /* gain of 1 -> LSB / 4 */
0200             *val = (regval * INA238_SHUNT_VOLTAGE_LSB) /
0201                    (1000 * (4 - data->gain + 1));
0202         else
0203             *val = (regval * INA238_BUS_VOLTAGE_LSB) / 1000;
0204         break;
0205     case hwmon_in_max_alarm:
0206     case hwmon_in_min_alarm:
0207         *val = !!(regval & mask);
0208         break;
0209     }
0210 
0211     return 0;
0212 }
0213 
0214 static int ina238_write_in(struct device *dev, u32 attr, int channel,
0215                long val)
0216 {
0217     struct ina238_data *data = dev_get_drvdata(dev);
0218     int regval;
0219 
0220     if (attr != hwmon_in_max && attr != hwmon_in_min)
0221         return -EOPNOTSUPP;
0222 
0223     /* convert decimal to register value */
0224     switch (channel) {
0225     case 0:
0226         /* signed value, clamp to max range +/-163 mV */
0227         regval = clamp_val(val, -163, 163);
0228         regval = (regval * 1000 * (4 - data->gain + 1)) /
0229              INA238_SHUNT_VOLTAGE_LSB;
0230         regval = clamp_val(regval, S16_MIN, S16_MAX);
0231 
0232         switch (attr) {
0233         case hwmon_in_max:
0234             return regmap_write(data->regmap,
0235                         INA238_SHUNT_OVER_VOLTAGE, regval);
0236         case hwmon_in_min:
0237             return regmap_write(data->regmap,
0238                         INA238_SHUNT_UNDER_VOLTAGE, regval);
0239         default:
0240             return -EOPNOTSUPP;
0241         }
0242     case 1:
0243         /* signed value, positive values only. Clamp to max 102.396 V */
0244         regval = clamp_val(val, 0, 102396);
0245         regval = (regval * 1000) / INA238_BUS_VOLTAGE_LSB;
0246         regval = clamp_val(regval, 0, S16_MAX);
0247 
0248         switch (attr) {
0249         case hwmon_in_max:
0250             return regmap_write(data->regmap,
0251                         INA238_BUS_OVER_VOLTAGE, regval);
0252         case hwmon_in_min:
0253             return regmap_write(data->regmap,
0254                         INA238_BUS_UNDER_VOLTAGE, regval);
0255         default:
0256             return -EOPNOTSUPP;
0257         }
0258     default:
0259         return -EOPNOTSUPP;
0260     }
0261 }
0262 
0263 static int ina238_read_current(struct device *dev, u32 attr, long *val)
0264 {
0265     struct ina238_data *data = dev_get_drvdata(dev);
0266     int regval;
0267     int err;
0268 
0269     switch (attr) {
0270     case hwmon_curr_input:
0271         err = regmap_read(data->regmap, INA238_CURRENT, &regval);
0272         if (err < 0)
0273             return err;
0274 
0275         /* Signed register, fixed 1mA current lsb. result in mA */
0276         *val = div_s64((s16)regval * INA238_FIXED_SHUNT * data->gain,
0277                    data->rshunt * 4);
0278         break;
0279     default:
0280         return -EOPNOTSUPP;
0281     }
0282 
0283     return 0;
0284 }
0285 
0286 static int ina238_read_power(struct device *dev, u32 attr, long *val)
0287 {
0288     struct ina238_data *data = dev_get_drvdata(dev);
0289     long long power;
0290     int regval;
0291     int err;
0292 
0293     switch (attr) {
0294     case hwmon_power_input:
0295         err = ina238_read_reg24(data->client, INA238_POWER, &regval);
0296         if (err)
0297             return err;
0298 
0299         /* Fixed 1mA lsb, scaled by 1000000 to have result in uW */
0300         power = div_u64(regval * 1000ULL * INA238_FIXED_SHUNT *
0301                 data->gain, 20 * data->rshunt);
0302         /* Clamp value to maximum value of long */
0303         *val = clamp_val(power, 0, LONG_MAX);
0304         break;
0305     case hwmon_power_max:
0306         err = regmap_read(data->regmap, INA238_POWER_LIMIT, &regval);
0307         if (err)
0308             return err;
0309 
0310         /*
0311          * Truncated 24-bit compare register, lower 8-bits are
0312          * truncated. Same conversion to/from uW as POWER register.
0313          */
0314         power = div_u64((regval << 8) * 1000ULL * INA238_FIXED_SHUNT *
0315                    data->gain, 20 * data->rshunt);
0316         /* Clamp value to maximum value of long */
0317         *val = clamp_val(power, 0, LONG_MAX);
0318         break;
0319     case hwmon_power_max_alarm:
0320         err = regmap_read(data->regmap, INA238_DIAG_ALERT, &regval);
0321         if (err)
0322             return err;
0323 
0324         *val = !!(regval & INA238_DIAG_ALERT_POL);
0325         break;
0326     default:
0327         return -EOPNOTSUPP;
0328     }
0329 
0330     return 0;
0331 }
0332 
0333 static int ina238_write_power(struct device *dev, u32 attr, long val)
0334 {
0335     struct ina238_data *data = dev_get_drvdata(dev);
0336     long regval;
0337 
0338     if (attr != hwmon_power_max)
0339         return -EOPNOTSUPP;
0340 
0341     /*
0342      * Unsigned postive values. Compared against the 24-bit power register,
0343      * lower 8-bits are truncated. Same conversion to/from uW as POWER
0344      * register.
0345      */
0346     regval = clamp_val(val, 0, LONG_MAX);
0347     regval = div_u64(val * 20ULL * data->rshunt,
0348              1000ULL * INA238_FIXED_SHUNT * data->gain);
0349     regval = clamp_val(regval >> 8, 0, U16_MAX);
0350 
0351     return regmap_write(data->regmap, INA238_POWER_LIMIT, regval);
0352 }
0353 
0354 static int ina238_read_temp(struct device *dev, u32 attr, long *val)
0355 {
0356     struct ina238_data *data = dev_get_drvdata(dev);
0357     int regval;
0358     int err;
0359 
0360     switch (attr) {
0361     case hwmon_temp_input:
0362         err = regmap_read(data->regmap, INA238_DIE_TEMP, &regval);
0363         if (err)
0364             return err;
0365 
0366         /* Signed, bits 15-4 of register, result in mC */
0367         *val = ((s16)regval >> 4) * INA238_DIE_TEMP_LSB;
0368         break;
0369     case hwmon_temp_max:
0370         err = regmap_read(data->regmap, INA238_TEMP_LIMIT, &regval);
0371         if (err)
0372             return err;
0373 
0374         /* Signed, bits 15-4 of register, result in mC */
0375         *val = ((s16)regval >> 4) * INA238_DIE_TEMP_LSB;
0376         break;
0377     case hwmon_temp_max_alarm:
0378         err = regmap_read(data->regmap, INA238_DIAG_ALERT, &regval);
0379         if (err)
0380             return err;
0381 
0382         *val = !!(regval & INA238_DIAG_ALERT_TMPOL);
0383         break;
0384     default:
0385         return -EOPNOTSUPP;
0386     }
0387 
0388     return 0;
0389 }
0390 
0391 static int ina238_write_temp(struct device *dev, u32 attr, long val)
0392 {
0393     struct ina238_data *data = dev_get_drvdata(dev);
0394     int regval;
0395 
0396     if (attr != hwmon_temp_max)
0397         return -EOPNOTSUPP;
0398 
0399     /* Signed, bits 15-4 of register */
0400     regval = (val / INA238_DIE_TEMP_LSB) << 4;
0401     regval = clamp_val(regval, S16_MIN, S16_MAX) & 0xfff0;
0402 
0403     return regmap_write(data->regmap, INA238_TEMP_LIMIT, regval);
0404 }
0405 
0406 static int ina238_read(struct device *dev, enum hwmon_sensor_types type,
0407                u32 attr, int channel, long *val)
0408 {
0409     switch (type) {
0410     case hwmon_in:
0411         return ina238_read_in(dev, attr, channel, val);
0412     case hwmon_curr:
0413         return ina238_read_current(dev, attr, val);
0414     case hwmon_power:
0415         return ina238_read_power(dev, attr, val);
0416     case hwmon_temp:
0417         return ina238_read_temp(dev, attr, val);
0418     default:
0419         return -EOPNOTSUPP;
0420     }
0421     return 0;
0422 }
0423 
0424 static int ina238_write(struct device *dev, enum hwmon_sensor_types type,
0425                u32 attr, int channel, long val)
0426 {
0427     struct ina238_data *data = dev_get_drvdata(dev);
0428     int err;
0429 
0430     mutex_lock(&data->config_lock);
0431 
0432     switch (type) {
0433     case hwmon_in:
0434         err = ina238_write_in(dev, attr, channel, val);
0435         break;
0436     case hwmon_power:
0437         err = ina238_write_power(dev, attr, val);
0438         break;
0439     case hwmon_temp:
0440         err = ina238_write_temp(dev, attr, val);
0441         break;
0442     default:
0443         err = -EOPNOTSUPP;
0444         break;
0445     }
0446 
0447     mutex_unlock(&data->config_lock);
0448     return err;
0449 }
0450 
0451 static umode_t ina238_is_visible(const void *drvdata,
0452                  enum hwmon_sensor_types type,
0453                  u32 attr, int channel)
0454 {
0455     switch (type) {
0456     case hwmon_in:
0457         switch (attr) {
0458         case hwmon_in_input:
0459         case hwmon_in_max_alarm:
0460         case hwmon_in_min_alarm:
0461             return 0444;
0462         case hwmon_in_max:
0463         case hwmon_in_min:
0464             return 0644;
0465         default:
0466             return 0;
0467         }
0468     case hwmon_curr:
0469         switch (attr) {
0470         case hwmon_curr_input:
0471             return 0444;
0472         default:
0473             return 0;
0474         }
0475     case hwmon_power:
0476         switch (attr) {
0477         case hwmon_power_input:
0478         case hwmon_power_max_alarm:
0479             return 0444;
0480         case hwmon_power_max:
0481             return 0644;
0482         default:
0483             return 0;
0484         }
0485     case hwmon_temp:
0486         switch (attr) {
0487         case hwmon_temp_input:
0488         case hwmon_temp_max_alarm:
0489             return 0444;
0490         case hwmon_temp_max:
0491             return 0644;
0492         default:
0493             return 0;
0494         }
0495     default:
0496         return 0;
0497     }
0498 }
0499 
0500 #define INA238_HWMON_IN_CONFIG (HWMON_I_INPUT | \
0501                 HWMON_I_MAX | HWMON_I_MAX_ALARM | \
0502                 HWMON_I_MIN | HWMON_I_MIN_ALARM)
0503 
0504 static const struct hwmon_channel_info *ina238_info[] = {
0505     HWMON_CHANNEL_INFO(in,
0506                /* 0: shunt voltage */
0507                INA238_HWMON_IN_CONFIG,
0508                /* 1: bus voltage */
0509                INA238_HWMON_IN_CONFIG),
0510     HWMON_CHANNEL_INFO(curr,
0511                /* 0: current through shunt */
0512                HWMON_C_INPUT),
0513     HWMON_CHANNEL_INFO(power,
0514                /* 0: power */
0515                HWMON_P_INPUT | HWMON_P_MAX | HWMON_P_MAX_ALARM),
0516     HWMON_CHANNEL_INFO(temp,
0517                /* 0: die temperature */
0518                HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_ALARM),
0519     NULL
0520 };
0521 
0522 static const struct hwmon_ops ina238_hwmon_ops = {
0523     .is_visible = ina238_is_visible,
0524     .read = ina238_read,
0525     .write = ina238_write,
0526 };
0527 
0528 static const struct hwmon_chip_info ina238_chip_info = {
0529     .ops = &ina238_hwmon_ops,
0530     .info = ina238_info,
0531 };
0532 
0533 static int ina238_probe(struct i2c_client *client)
0534 {
0535     struct ina2xx_platform_data *pdata = dev_get_platdata(&client->dev);
0536     struct device *dev = &client->dev;
0537     struct device *hwmon_dev;
0538     struct ina238_data *data;
0539     int config;
0540     int ret;
0541 
0542     data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
0543     if (!data)
0544         return -ENOMEM;
0545 
0546     data->client = client;
0547     mutex_init(&data->config_lock);
0548 
0549     data->regmap = devm_regmap_init_i2c(client, &ina238_regmap_config);
0550     if (IS_ERR(data->regmap)) {
0551         dev_err(dev, "failed to allocate register map\n");
0552         return PTR_ERR(data->regmap);
0553     }
0554 
0555     /* load shunt value */
0556     data->rshunt = INA238_RSHUNT_DEFAULT;
0557     if (device_property_read_u32(dev, "shunt-resistor", &data->rshunt) < 0 && pdata)
0558         data->rshunt = pdata->shunt_uohms;
0559     if (data->rshunt == 0) {
0560         dev_err(dev, "invalid shunt resister value %u\n", data->rshunt);
0561         return -EINVAL;
0562     }
0563 
0564     /* load shunt gain value */
0565     if (device_property_read_u32(dev, "ti,shunt-gain", &data->gain) < 0)
0566         data->gain = 4; /* Default of ADCRANGE = 0 */
0567     if (data->gain != 1 && data->gain != 4) {
0568         dev_err(dev, "invalid shunt gain value %u\n", data->gain);
0569         return -EINVAL;
0570     }
0571 
0572     /* Setup CONFIG register */
0573     config = INA238_CONFIG_DEFAULT;
0574     if (data->gain == 1)
0575         config |= INA238_CONFIG_ADCRANGE; /* ADCRANGE = 1 is /1 */
0576     ret = regmap_write(data->regmap, INA238_CONFIG, config);
0577     if (ret < 0) {
0578         dev_err(dev, "error configuring the device: %d\n", ret);
0579         return -ENODEV;
0580     }
0581 
0582     /* Setup ADC_CONFIG register */
0583     ret = regmap_write(data->regmap, INA238_ADC_CONFIG,
0584                INA238_ADC_CONFIG_DEFAULT);
0585     if (ret < 0) {
0586         dev_err(dev, "error configuring the device: %d\n", ret);
0587         return -ENODEV;
0588     }
0589 
0590     /* Setup SHUNT_CALIBRATION register with fixed value */
0591     ret = regmap_write(data->regmap, INA238_SHUNT_CALIBRATION,
0592                INA238_CALIBRATION_VALUE);
0593     if (ret < 0) {
0594         dev_err(dev, "error configuring the device: %d\n", ret);
0595         return -ENODEV;
0596     }
0597 
0598     /* Setup alert/alarm configuration */
0599     ret = regmap_write(data->regmap, INA238_DIAG_ALERT,
0600                INA238_DIAG_ALERT_DEFAULT);
0601     if (ret < 0) {
0602         dev_err(dev, "error configuring the device: %d\n", ret);
0603         return -ENODEV;
0604     }
0605 
0606     hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
0607                              &ina238_chip_info,
0608                              NULL);
0609     if (IS_ERR(hwmon_dev))
0610         return PTR_ERR(hwmon_dev);
0611 
0612     dev_info(dev, "power monitor %s (Rshunt = %u uOhm, gain = %u)\n",
0613          client->name, data->rshunt, data->gain);
0614 
0615     return 0;
0616 }
0617 
0618 static const struct i2c_device_id ina238_id[] = {
0619     { "ina238", 0 },
0620     { }
0621 };
0622 MODULE_DEVICE_TABLE(i2c, ina238_id);
0623 
0624 static const struct of_device_id __maybe_unused ina238_of_match[] = {
0625     { .compatible = "ti,ina238" },
0626     { },
0627 };
0628 MODULE_DEVICE_TABLE(of, ina238_of_match);
0629 
0630 static struct i2c_driver ina238_driver = {
0631     .class      = I2C_CLASS_HWMON,
0632     .driver = {
0633         .name   = "ina238",
0634         .of_match_table = of_match_ptr(ina238_of_match),
0635     },
0636     .probe_new  = ina238_probe,
0637     .id_table   = ina238_id,
0638 };
0639 
0640 module_i2c_driver(ina238_driver);
0641 
0642 MODULE_AUTHOR("Nathan Rossi <nathan.rossi@digi.com>");
0643 MODULE_DESCRIPTION("ina238 driver");
0644 MODULE_LICENSE("GPL");