diff options
author | Anshul Jain <anshulj@nvidia.com> | 2012-10-31 11:44:20 -0700 |
---|---|---|
committer | Rohan Somvanshi <rsomvanshi@nvidia.com> | 2012-11-09 06:05:30 -0800 |
commit | 5ad8d50e26515797a31fb8d2195dab32b52bab34 (patch) | |
tree | 18da7506478ea3b298cc91f586d7708352bcd27e /drivers | |
parent | b6404b2d78b03faff7d2ff8fdd669edc2ad7b0cf (diff) |
hwmon: ina219: INA219 driver with more sysfs
This new driver allows state change of the INA219 device using this sysds node:
To turn the ina219 device on,
echo 1 > /<i2c device path>/cur_state
To turn it off:
echo 0 > /<i2c device path>/cur_state
It has new sysfs nodes current2_input and power2_input that contains
current and power calculated using shunt resistor and voltage drop
across shunt resistor.
This driver is backward compatible with the old driver.
Bug 1160868
Change-Id: I5e05947e8e3b20a85f02188a48ea4441218e27a8
Signed-off-by: Anshul Jain <anshulj@nvidia.com>
Reviewed-on: http://git-master/r/160070
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Tested-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hwmon/ina219.c | 376 |
1 files changed, 283 insertions, 93 deletions
diff --git a/drivers/hwmon/ina219.c b/drivers/hwmon/ina219.c index fda40f4f4fba..a06516a65d42 100644 --- a/drivers/hwmon/ina219.c +++ b/drivers/hwmon/ina219.c @@ -51,21 +51,10 @@ #define INA219_CURRENT 4 #define INA219_CAL 5 -/* - INA219 Sensor defines - Config info for ina219s - D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 - rst BRNG PG1 PG0 BADC4 .-.BADC1 SADC4 - SADC1 MODE3 - MODE1 - reset D15 - bus_range=0 (d13) - pga_gain=0 (D12:D11) - bus_adc_setting=0x3 (d10:D7) 12-bit w/o oversampling (532uS) - shunt_adc_setting=0xb (D6:D3) 8x oversampling (4.26ms) - mode=0x7 (D2:D0) continuous shunt & bus -*/ -#define INA219_CONFIG_DATA 0x1df #define INA219_RESET 0x8000 +#define busv_register_to_mv(x) (((x) >> 3) * 4) +#define shuntv_register_to_uv(x) ((x) * 10) struct power_mon_data { s32 voltage; s32 currentInMillis; @@ -78,8 +67,12 @@ struct ina219_data { struct ina219_platform_data *pInfo; struct power_mon_data pm_data; struct mutex mutex; + int state; }; +#define STOPPED 0 +#define RUNNING 1 + /* Set non-zero to enable debug prints */ #define INA219_DEBUG_PRINTS 0 @@ -89,13 +82,6 @@ struct ina219_data { #define DEBUG_INA219(x) #endif -static s16 reorder_bytes(s16 a) -{ - s16 ret = ((a >> 8) & 0xff) | ((a & 0xff) << 8); - return ret; -} - -/* set ina219 to power down mode */ static s32 power_down_INA219(struct i2c_client *client) { s32 retval; @@ -105,6 +91,27 @@ static s32 power_down_INA219(struct i2c_client *client) return retval; } +static s32 power_up_INA219(struct i2c_client *client, u16 config_data) +{ + s32 retval; + struct ina219_data *data = i2c_get_clientdata(client); + retval = i2c_smbus_write_word_data(client, INA219_CONFIG, + __constant_cpu_to_be16(config_data)); + if (retval < 0) + goto error; + + retval = i2c_smbus_write_word_data(client, INA219_CAL, + __constant_cpu_to_be16(data->pInfo->calibration_data)); + if (retval < 0) + goto error; + + return 0; +error: + dev_err(&client->dev, "power up failure sts: 0x%x\n", retval); + return retval; + +} + static s32 show_rail_name(struct device *dev, struct device_attribute *attr, char *buf) @@ -120,48 +127,140 @@ static s32 show_voltage(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct ina219_data *data = i2c_get_clientdata(client); - s32 retval; s32 voltage_mV; + int cur_state; - /* fill config data */ - retval = i2c_smbus_write_word_data(client, INA219_CONFIG, - reorder_bytes(INA219_CONFIG_DATA)); - if (retval < 0) { - dev_err(dev, "config data write failed sts: 0x%x\n", retval); - goto error; - } - - /* fill calibration data */ - retval = i2c_smbus_write_word_data(client, INA219_CAL, - reorder_bytes(data->pInfo->calibration_data)); - if (retval < 0) { - dev_err(dev, "calib data write failed sts: 0x%x\n", retval); - goto error; - } + mutex_lock(&data->mutex); + cur_state = data->state; + if (data->state == STOPPED) + if (power_up_INA219(client, data->pInfo->trig_conf) < 0) + goto error; /* getting voltage readings in milli volts*/ voltage_mV = - reorder_bytes(i2c_smbus_read_word_data(client, + (s16)be16_to_cpu(i2c_smbus_read_word_data(client, INA219_VOLTAGE)); + DEBUG_INA219(("Ina219 voltage reg Value: 0x%x\n", voltage_mV)); if (voltage_mV < 0) goto error; - voltage_mV = voltage_mV >> 1; + voltage_mV = busv_register_to_mv(voltage_mV); DEBUG_INA219(("Ina219 voltage in mv: %d\n", voltage_mV)); - /* set ina219 to power down mode */ - retval = power_down_INA219(client); - if (retval < 0) - goto error; - DEBUG_INA219(("%s volt = %d\n", __func__, voltage_mV)); + + if (cur_state == STOPPED) + if (power_down_INA219(client) < 0) + goto error; + + mutex_unlock(&data->mutex); return sprintf(buf, "%d mV\n", voltage_mV); error: + mutex_unlock(&data->mutex); dev_err(dev, "%s: failed\n", __func__); - return retval; + return -EAGAIN; +} + +static s32 show_shunt_voltage(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ina219_data *data = i2c_get_clientdata(client); + s32 voltage_uV; + int cur_state; + mutex_lock(&data->mutex); + cur_state = data->state; + if (data->state == STOPPED) + if (power_up_INA219(client, data->pInfo->trig_conf) < 0) + goto error; + /* getting voltage readings in milli volts*/ + voltage_uV = + be16_to_cpu(i2c_smbus_read_word_data(client, + INA219_SHUNT)); + + DEBUG_INA219(("Ina219 voltage reg Value: 0x%x\n", voltage_uV)); + if (voltage_uV < 0) + goto error; + voltage_uV = shuntv_register_to_uv(voltage_uV); + + if (cur_state == STOPPED) + if (power_down_INA219(client) < 0) + goto error; + + mutex_unlock(&data->mutex); + + DEBUG_INA219(("%s volt = %d\n", __func__, voltage_uV)); + return sprintf(buf, "%d uV\n", voltage_uV); +error: + mutex_unlock(&data->mutex); + dev_err(dev, "%s: failed\n", __func__); + return -EAGAIN; } +static s32 show_power2(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ina219_data *data = i2c_get_clientdata(client); + s32 power_mW; + s32 voltage_shunt_uV; + s32 voltage_bus_mV; + s32 inverse_shunt_resistor; + int cur_state; +#if INA219_DEBUG_PRINTS + s32 power_raw; +#endif + + mutex_lock(&data->mutex); + cur_state = data->state; + if (data->state == STOPPED) + if (power_up_INA219(client, data->pInfo->trig_conf) < 0) + goto error; + + voltage_shunt_uV = be16_to_cpu(i2c_smbus_read_word_data(client, + INA219_SHUNT)); + if (voltage_shunt_uV < 0) + goto error; + voltage_shunt_uV = shuntv_register_to_uv(voltage_shunt_uV); + + voltage_bus_mV = be16_to_cpu(i2c_smbus_read_word_data(client, + INA219_VOLTAGE)); + if (voltage_bus_mV < 0) + goto error; + + voltage_bus_mV = busv_register_to_mv(voltage_bus_mV); + /*avoid overflow*/ + inverse_shunt_resistor = 1000/(data->pInfo->shunt_resistor); + power_mW = voltage_shunt_uV * inverse_shunt_resistor; /*current uAmps*/ + power_mW = power_mW / 1000; /*current mAmps*/ + power_mW = power_mW * (voltage_bus_mV); /*Power uW*/ + power_mW = power_mW / 1000; /*Power mW*/ + +#if INA219_DEBUG_PRINTS + power_raw = be16_to_cpu(i2c_smbus_read_word_data(client, INA219_POWER)); + power_raw *= data->pInfo->power_lsb; + power_raw /= data->pInfo->precision_multiplier; + DEBUG_INA219(("INA219: power_mW: %d, power_raw:%d\n", power_mW, + power_raw)); +#endif + if (cur_state == STOPPED) + if (power_down_INA219(client) < 0) + goto error; + + + mutex_unlock(&data->mutex); + DEBUG_INA219(("%s pow = %d\n", __func__, power_mW)); + return sprintf(buf, "%d mW\n", power_mW); +error: + mutex_unlock(&data->mutex); + dev_err(dev, "%s: failed\n", __func__); + return -EAGAIN; +} + +/*This function is kept to support INA219 on cardhu*/ static s32 show_power(struct device *dev, struct device_attribute *attr, char *buf) @@ -172,28 +271,22 @@ static s32 show_power(struct device *dev, s32 power_mW; s32 voltage_mV; s32 overflow, conversion; + int cur_state; - /* fill config data */ - retval = i2c_smbus_write_word_data(client, INA219_CONFIG, - reorder_bytes(INA219_CONFIG_DATA)); - if (retval < 0) { - dev_err(dev, "config data write failed sts: 0x%x\n", retval); - goto error; - } - - /* fill calib data */ - retval = i2c_smbus_write_word_data(client, INA219_CAL, - reorder_bytes(data->pInfo->calibration_data)); - if (retval < 0) { - dev_err(dev, "calibration data write failed sts: 0x%x\n", - retval); - goto error; + mutex_lock(&data->mutex); + cur_state = data->state; + if (data->state == STOPPED) { + if (power_up_INA219(client, data->pInfo->trig_conf) < 0) + goto error; + } else { + mutex_unlock(&data->mutex); + return show_power2(dev, attr, buf); } /* check if the readings are valid */ do { /* read power register to clear conversion bit */ - retval = reorder_bytes(i2c_smbus_read_word_data(client, + retval = be16_to_cpu(i2c_smbus_read_word_data(client, INA219_POWER)); if (retval < 0) { dev_err(dev, "CNVR bit clearing failure sts: 0x%x\n", @@ -202,20 +295,20 @@ static s32 show_power(struct device *dev, } voltage_mV = - reorder_bytes(i2c_smbus_read_word_data(client, + be16_to_cpu(i2c_smbus_read_word_data(client, INA219_VOLTAGE)); DEBUG_INA219(("Ina219 voltage reg Value: 0x%x\n", voltage_mV)); overflow = voltage_mV & 1; if (overflow) { dev_err(dev, "overflow error\n"); - return 0; + goto error; } conversion = (voltage_mV >> 1) & 1; DEBUG_INA219(("\n ina219 CNVR value:%d", conversion)); } while (!conversion); /* getting power readings in milli watts*/ - power_mW = reorder_bytes(i2c_smbus_read_word_data(client, + power_mW = be16_to_cpu(i2c_smbus_read_word_data(client, INA219_POWER)); DEBUG_INA219(("Ina219 power Reg: 0x%x\n", power_mW)); power_mW *= data->pInfo->power_lsb; @@ -229,14 +322,66 @@ static s32 show_power(struct device *dev, retval = power_down_INA219(client); if (retval < 0) goto error; - + mutex_unlock(&data->mutex); DEBUG_INA219(("%s pow = %d\n", __func__, power_mW)); return sprintf(buf, "%d mW\n", power_mW); error: + mutex_unlock(&data->mutex); dev_err(dev, "%s: failed\n", __func__); return retval; } +static s32 show_current2(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ina219_data *data = i2c_get_clientdata(client); + s32 current_mA; + s32 voltage_uV; + s32 inverse_shunt_resistor; + int cur_state; +#if INA219_DEBUG_PRINTS + s32 current_raw; +#endif + mutex_lock(&data->mutex); + cur_state = data->state; + if (data->state == STOPPED) + if (power_up_INA219(client, data->pInfo->trig_conf) < 0) + goto error; + + voltage_uV = + (s16)be16_to_cpu(i2c_smbus_read_word_data(client, + INA219_SHUNT)); + if (voltage_uV < 0) + goto error; + inverse_shunt_resistor = 1000/(data->pInfo->shunt_resistor); + voltage_uV = shuntv_register_to_uv(voltage_uV); + current_mA = voltage_uV * inverse_shunt_resistor; + current_mA = current_mA / 1000; + +#if INA219_DEBUG_PRINTS + current_raw = + (s16)be16_to_cpu(i2c_smbus_read_word_data(client, + INA219_CURRENT)); + current_raw *= data->pInfo->power_lsb; + current_raw /= data->pInfo->divisor; + current_raw /= data->pInfo->precision_multiplier; + + DEBUG_INA219(("%s current = %d current_raw=%d\n", __func__, current_mA, + current_raw)); +#endif + if (cur_state == STOPPED) + if (power_down_INA219(client) < 0) + goto error; + mutex_unlock(&data->mutex); + return sprintf(buf, "%d mA\n", current_mA); +error: + dev_err(dev, "%s: failed\n", __func__); + mutex_unlock(&data->mutex); + return -EAGAIN; +} +/*This function is kept to support INA219 on cardhu*/ static s32 show_current(struct device *dev, struct device_attribute *attr, char *buf) @@ -247,28 +392,22 @@ static s32 show_current(struct device *dev, s32 current_mA; s32 voltage_mV; s32 overflow, conversion; + int cur_state; - /* fill config data */ - retval = i2c_smbus_write_word_data(client, INA219_CONFIG, - reorder_bytes(INA219_CONFIG_DATA)); - if (retval < 0) { - dev_err(dev, "config data write failed sts: 0x%x\n", retval); - goto error; - } + mutex_lock(&data->mutex); - /* fill calib data */ - retval = i2c_smbus_write_word_data(client, INA219_CAL, - reorder_bytes(data->pInfo->calibration_data)); - if (retval < 0) { - dev_err(dev, "calibration data write failed sts: 0x%x\n", - retval); - goto error; + cur_state = data->state; + if (data->state == STOPPED) { + if (power_up_INA219(client, data->pInfo->trig_conf) < 0) + goto error; + } else { + mutex_unlock(&data->mutex); + show_current2(dev, attr, buf); } - /* check if the readings are valid */ do { /* read power register to clear conversion bit */ - retval = reorder_bytes(i2c_smbus_read_word_data(client, + retval = be16_to_cpu(i2c_smbus_read_word_data(client, INA219_POWER)); if (retval < 0) { dev_err(dev, "CNVR bit clearing failure sts: 0x%x\n", @@ -277,20 +416,20 @@ static s32 show_current(struct device *dev, } voltage_mV = - reorder_bytes(i2c_smbus_read_word_data(client, + be16_to_cpu(i2c_smbus_read_word_data(client, INA219_VOLTAGE)); DEBUG_INA219(("Ina219 voltage reg Value: 0x%x\n", voltage_mV)); overflow = voltage_mV & 1; if (overflow) { dev_err(dev, "overflow error\n"); - return 0; + goto error; } conversion = (voltage_mV >> 1) & 1; DEBUG_INA219(("\n ina219 CNVR value:%d", conversion)); } while (!conversion); /* getting current readings in milli amps*/ - current_mA = reorder_bytes(i2c_smbus_read_word_data(client, + current_mA = be16_to_cpu(i2c_smbus_read_word_data(client, INA219_CURRENT)); DEBUG_INA219(("Ina219 current Reg: 0x%x\n", current_mA)); if (current_mA < 0) @@ -301,24 +440,74 @@ static s32 show_current(struct device *dev, current_mA /= data->pInfo->precision_multiplier; DEBUG_INA219(("Ina219 current Value: %d\n", current_mA)); - /* set ina219 to power down mode */ - retval = power_down_INA219(client); - if (retval < 0) - goto error; + if (cur_state == STOPPED) + if (power_down_INA219(client) < 0) + goto error; + mutex_unlock(&data->mutex); DEBUG_INA219(("%s current = %d\n", __func__, current_mA)); return sprintf(buf, "%d mA\n", current_mA); error: + mutex_unlock(&data->mutex); dev_err(dev, "%s: failed\n", __func__); return retval; } +static int ina219_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ina219_data *data = i2c_get_clientdata(client); + int count; + mutex_lock(&data->mutex); + count = sprintf(buf, "%d\n", data->state); + mutex_unlock(&data->mutex); + return count; +} + +static int ina219_state_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ina219_data *data = i2c_get_clientdata(client); + int retval = -1; + long new; + retval = kstrtol(buf, 10, &new); + if (retval < 0 || new > INT_MAX || new < INT_MIN) + return -EINVAL; + mutex_lock(&data->mutex); + + if ((new > 0) && (data->state == STOPPED)) + retval = power_up_INA219(client, data->pInfo->cont_conf); + else if ((new == 0) && (data->state == RUNNING)) + retval = power_down_INA219(client); + + if (retval < 0) { + dev_err(dev, "Error in switching INA on/off!"); + mutex_unlock(&data->mutex); + return -EAGAIN; + } + + if (new) + data->state = RUNNING; + else + data->state = STOPPED; + + mutex_unlock(&data->mutex); + return 1; +} + static struct sensor_device_attribute ina219[] = { + SENSOR_ATTR(shunt_voltage, S_IRUGO, show_shunt_voltage, NULL, 0), SENSOR_ATTR(rail_name, S_IRUGO, show_rail_name, NULL, 0), SENSOR_ATTR(in1_input, S_IRUGO, show_voltage, NULL, 0), SENSOR_ATTR(curr1_input, S_IRUGO, show_current, NULL, 0), + SENSOR_ATTR(curr2_input, S_IRUGO, show_current2, NULL, 0), SENSOR_ATTR(power1_input, S_IRUGO, show_power, NULL, 0), + SENSOR_ATTR(power2_input, S_IRUGO, show_power2, NULL, 0), + SENSOR_ATTR(cur_state, 0644, ina219_state_show, ina219_state_set, 0), }; static int __devinit ina219_probe(struct i2c_client *client, @@ -336,9 +525,11 @@ static int __devinit ina219_probe(struct i2c_client *client, i2c_set_clientdata(client, data); data->pInfo = client->dev.platform_data; mutex_init(&data->mutex); + power_down_INA219(client); + data->state = STOPPED; /* reset ina219 */ err = i2c_smbus_write_word_data(client, INA219_CONFIG, - reorder_bytes(INA219_RESET)); + __constant_cpu_to_be16(INA219_RESET)); if (err < 0) { dev_err(&client->dev, "ina219 reset failure status: 0x%x\n", err); @@ -359,11 +550,6 @@ static int __devinit ina219_probe(struct i2c_client *client, goto exit_remove; } - /* set ina219 to power down mode */ - err = power_down_INA219(client); - if (err < 0) - goto exit_remove; - return 0; exit_remove: @@ -379,6 +565,10 @@ static int __devexit ina219_remove(struct i2c_client *client) { u8 i; struct ina219_data *data = i2c_get_clientdata(client); + mutex_lock(&data->mutex); + power_down_INA219(client); + data->state = STOPPED; + mutex_unlock(&data->mutex); hwmon_device_unregister(data->hwmon_dev); for (i = 0; i < ARRAY_SIZE(ina219); i++) device_remove_file(&client->dev, &ina219[i].dev_attr); |