Explicit support for ina231 added to ina2xx driver
Minor improvements, cleanup and fixes in various drivers -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJU2COzAAoJEMsfJm/On5mBp34QAJq/JBwjBOUSSwA/vn8sEO70 +SFCNf26FnwcARdVrdhc2K0ch7Bv7vpbuxJ/1Iqqv8qBZLMn2Ta2XQyksX/EthJp bYFYXuZimZZp77ElOoVm53XbplkWk2+A34Rq7pDTlYNaZSPsdmDYSOkO7Lr0M9qQ j5GbQ9NpnHseWYrZXsjNfgoyAmgAzGlmLAS0BDS5mUYj7Rv44lURfnjT+Psez22y 2V1UJLvuGwqZ5cdtmpft/E759K8TdAcIDP8afH/4+5Cq9MJdC9Rhm13mRQ1XiegG +gmMuVtgARECLVZIx7bbkBhNHce8IkfC3T4+rK8pCnK2iGyx+5GmigCOuGtmESYE g+WsD1MqL5S78sZFw0wvZI/H6QwTdVLbOdQzJCPRIgYEC0Ss9HmT7rkG99csCrjk FgoJdZ5vQ5H1Ipk7R51pLnuSLXMKlZV9qAOgtboJSYOyvT7X10T73vusMLxqehev 2uFyEDehNplpOdNNPN45cL8nEED6HbU0X4FiGUPI+HntzXBVnaofrpxni9IUtu21 V9t+hq8gBSyRu+yH9HZYlOymPX/yYCp8MP8+daMW5lQA2zYOLGrDougyxkjlnxvJ tup4wCtU5H4SWHNJqiNnahPZ5Z9yDOYBLkMtZZeJx3MgddSFvMnO+plB3I6IMOeU /161vDplvqlQMTVwiiS4 =JZfe -----END PGP SIGNATURE----- Merge tag 'hwmon-for-linus-v3.20' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging Pull hwmon updates from Guenter Roeck: "Explicit support for ina231 added to ina2xx driver. Minor improvements, cleanup and fixes in various drivers" * tag 'hwmon-for-linus-v3.20' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: hwmon: (tmp102) add hibernation callbacks hwmon: (ads2828) Only keep data in device data structure if needed hwmon: (ads2828) Convert to use regmap hwmon: (jc42) Allow negative hysteresis temperatures hwmon: (adc128d818) Do proper sign extension hwmon: (ad7314) Do proper sign extension hwmon: (abx500) Fix format string warnings hwmon: (jc42) Fix integer overflow when writing hysteresis value hwmon: (jc42) Fix integer overflow hwmon: (jc42) Use sign_extend32 for sign extension hwmon: (ina2xx) Add ina231 compatible string hwmon: (ina2xx) use DIV_ROUND_CLOSEST() to avoid rounding errors hwmon: (ina2xx) remove an unnecessary dev_get_drvdata() result check hwmon: (ina2xx) implement update_interval attribute for ina226 hwmon: (ina2xx) make shunt resistance configurable at run-time hwmon: (ina2xx) don't accept shunt values greater than the calibration factor hwmon: (ina2xx) remove a stray new line hwmon: (ina2xx) reinitialize the chip in case it's been reset hwmon: (nct7802) Constify struct regmap_config
This commit is contained in:
commit
5c30c3cc6d
|
@ -26,6 +26,12 @@ Supported chips:
|
|||
Datasheet: Publicly available at the Texas Instruments website
|
||||
http://www.ti.com/
|
||||
|
||||
* Texas Instruments INA231
|
||||
Prefix: 'ina231'
|
||||
Addresses: I2C 0x40 - 0x4f
|
||||
Datasheet: Publicly available at the Texas Instruments website
|
||||
http://www.ti.com/
|
||||
|
||||
Author: Lothar Felten <l-felten@ti.com>
|
||||
|
||||
Description
|
||||
|
@ -41,9 +47,18 @@ interface. The INA220 monitors both shunt drop and supply voltage.
|
|||
The INA226 is a current shunt and power monitor with an I2C interface.
|
||||
The INA226 monitors both a shunt voltage drop and bus supply voltage.
|
||||
|
||||
The INA230 is a high or low side current shunt and power monitor with an I2C
|
||||
interface. The INA230 monitors both a shunt voltage drop and bus supply voltage.
|
||||
INA230 and INA231 are high or low side current shunt and power monitors
|
||||
with an I2C interface. The chips monitor both a shunt voltage drop and
|
||||
bus supply voltage.
|
||||
|
||||
The shunt value in micro-ohms can be set via platform data or device tree.
|
||||
Please refer to the Documentation/devicetree/bindings/i2c/ina2xx.txt for bindings
|
||||
The shunt value in micro-ohms can be set via platform data or device tree at
|
||||
compile-time or via the shunt_resistor attribute in sysfs at run-time. Please
|
||||
refer to the Documentation/devicetree/bindings/i2c/ina2xx.txt for bindings
|
||||
if the device tree is used.
|
||||
|
||||
Additionally ina226 supports update_interval attribute as described in
|
||||
Documentation/hwmon/sysfs-interface. Internally the interval is the sum of
|
||||
bus and shunt voltage conversion times multiplied by the averaging rate. We
|
||||
don't touch the conversion times and only modify the number of averages. The
|
||||
lower limit of the update_interval is 2 ms, the upper limit is 2253 ms.
|
||||
The actual programmed interval may vary from the desired value.
|
||||
|
|
|
@ -1389,6 +1389,7 @@ config SENSORS_ADS1015
|
|||
config SENSORS_ADS7828
|
||||
tristate "Texas Instruments ADS7828 and compatibles"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments ADS7828 and
|
||||
ADS7830 8-channel A/D converters. ADS7828 resolution is 12-bit, while
|
||||
|
@ -1430,8 +1431,8 @@ config SENSORS_INA2XX
|
|||
tristate "Texas Instruments INA219 and compatibles"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for INA219, INA220, INA226, and
|
||||
INA230 power monitor chips.
|
||||
If you say yes here you get support for INA219, INA220, INA226,
|
||||
INA230, and INA231 power monitor chips.
|
||||
|
||||
The INA2xx driver is configured for the default configuration of
|
||||
the part as described in the datasheet.
|
||||
|
|
|
@ -221,7 +221,7 @@ static ssize_t show_min(struct device *dev,
|
|||
struct abx500_temp *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
|
||||
return sprintf(buf, "%ld\n", data->min[attr->index]);
|
||||
return sprintf(buf, "%lu\n", data->min[attr->index]);
|
||||
}
|
||||
|
||||
static ssize_t show_max(struct device *dev,
|
||||
|
@ -230,7 +230,7 @@ static ssize_t show_max(struct device *dev,
|
|||
struct abx500_temp *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
|
||||
return sprintf(buf, "%ld\n", data->max[attr->index]);
|
||||
return sprintf(buf, "%lu\n", data->max[attr->index]);
|
||||
}
|
||||
|
||||
static ssize_t show_max_hyst(struct device *dev,
|
||||
|
@ -239,7 +239,7 @@ static ssize_t show_max_hyst(struct device *dev,
|
|||
struct abx500_temp *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
|
||||
return sprintf(buf, "%ld\n", data->max_hyst[attr->index]);
|
||||
return sprintf(buf, "%lu\n", data->max_hyst[attr->index]);
|
||||
}
|
||||
|
||||
static ssize_t show_min_alarm(struct device *dev,
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
/*
|
||||
* AD7314 temperature masks
|
||||
|
@ -67,7 +68,7 @@ static ssize_t ad7314_show_temperature(struct device *dev,
|
|||
switch (spi_get_device_id(chip->spi_dev)->driver_data) {
|
||||
case ad7314:
|
||||
data = (ret & AD7314_TEMP_MASK) >> AD7314_TEMP_SHIFT;
|
||||
data = (data << 6) >> 6;
|
||||
data = sign_extend32(data, 9);
|
||||
|
||||
return sprintf(buf, "%d\n", 250 * data);
|
||||
case adt7301:
|
||||
|
@ -78,7 +79,7 @@ static ssize_t ad7314_show_temperature(struct device *dev,
|
|||
* register. 1lsb - 31.25 milli degrees centigrade
|
||||
*/
|
||||
data = ret & ADT7301_TEMP_MASK;
|
||||
data = (data << 2) >> 2;
|
||||
data = sign_extend32(data, 13);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
DIV_ROUND_CLOSEST(data * 3125, 100));
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
/* Addresses to scan
|
||||
* The chip also supports addresses 0x35..0x37. Don't scan those addresses
|
||||
|
@ -189,7 +190,7 @@ static ssize_t adc128_show_temp(struct device *dev,
|
|||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
temp = (data->temp[index] << 7) >> 7; /* sign extend */
|
||||
temp = sign_extend32(data->temp[index], 8);
|
||||
return sprintf(buf, "%d\n", temp * 500);/* 0.5 degrees C resolution */
|
||||
}
|
||||
|
||||
|
|
|
@ -30,14 +30,12 @@
|
|||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_data/ads7828.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* The ADS7828 registers */
|
||||
#define ADS7828_NCH 8 /* 8 channels supported */
|
||||
#define ADS7828_CMD_SD_SE 0x80 /* Single ended inputs */
|
||||
#define ADS7828_CMD_PD1 0x04 /* Internal vref OFF && A/D ON */
|
||||
#define ADS7828_CMD_PD3 0x0C /* Internal vref ON && A/D ON */
|
||||
|
@ -50,17 +48,9 @@ enum ads7828_chips { ads7828, ads7830 };
|
|||
|
||||
/* Client specific data */
|
||||
struct ads7828_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex update_lock; /* Mutex protecting updates */
|
||||
unsigned long last_updated; /* Last updated time (in jiffies) */
|
||||
u16 adc_input[ADS7828_NCH]; /* ADS7828_NCH samples */
|
||||
bool valid; /* Validity flag */
|
||||
bool diff_input; /* Differential input */
|
||||
bool ext_vref; /* External voltage reference */
|
||||
unsigned int vref_mv; /* voltage reference value */
|
||||
struct regmap *regmap;
|
||||
u8 cmd_byte; /* Command byte without channel bits */
|
||||
unsigned int lsb_resol; /* Resolution of the ADC sample LSB */
|
||||
s32 (*read_channel)(const struct i2c_client *client, u8 command);
|
||||
};
|
||||
|
||||
/* Command byte C2,C1,C0 - see datasheet */
|
||||
|
@ -69,42 +59,22 @@ static inline u8 ads7828_cmd_byte(u8 cmd, int ch)
|
|||
return cmd | (((ch >> 1) | (ch & 0x01) << 2) << 4);
|
||||
}
|
||||
|
||||
/* Update data for the device (all 8 channels) */
|
||||
static struct ads7828_data *ads7828_update_device(struct device *dev)
|
||||
{
|
||||
struct ads7828_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|
||||
|| !data->valid) {
|
||||
unsigned int ch;
|
||||
dev_dbg(&client->dev, "Starting ads7828 update\n");
|
||||
|
||||
for (ch = 0; ch < ADS7828_NCH; ch++) {
|
||||
u8 cmd = ads7828_cmd_byte(data->cmd_byte, ch);
|
||||
data->adc_input[ch] = data->read_channel(client, cmd);
|
||||
}
|
||||
data->last_updated = jiffies;
|
||||
data->valid = true;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* sysfs callback function */
|
||||
static ssize_t ads7828_show_in(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct ads7828_data *data = ads7828_update_device(dev);
|
||||
unsigned int value = DIV_ROUND_CLOSEST(data->adc_input[attr->index] *
|
||||
data->lsb_resol, 1000);
|
||||
struct ads7828_data *data = dev_get_drvdata(dev);
|
||||
u8 cmd = ads7828_cmd_byte(data->cmd_byte, attr->index);
|
||||
unsigned int regval;
|
||||
int err;
|
||||
|
||||
return sprintf(buf, "%d\n", value);
|
||||
err = regmap_read(data->regmap, cmd, ®val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
DIV_ROUND_CLOSEST(regval * data->lsb_resol, 1000));
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ads7828_show_in, NULL, 0);
|
||||
|
@ -130,6 +100,16 @@ static struct attribute *ads7828_attrs[] = {
|
|||
|
||||
ATTRIBUTE_GROUPS(ads7828);
|
||||
|
||||
static const struct regmap_config ads2828_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
};
|
||||
|
||||
static const struct regmap_config ads2830_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int ads7828_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
@ -137,42 +117,40 @@ static int ads7828_probe(struct i2c_client *client,
|
|||
struct ads7828_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct ads7828_data *data;
|
||||
struct device *hwmon_dev;
|
||||
unsigned int vref_mv = ADS7828_INT_VREF_MV;
|
||||
bool diff_input = false;
|
||||
bool ext_vref = false;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(struct ads7828_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
if (pdata) {
|
||||
data->diff_input = pdata->diff_input;
|
||||
data->ext_vref = pdata->ext_vref;
|
||||
if (data->ext_vref)
|
||||
data->vref_mv = pdata->vref_mv;
|
||||
diff_input = pdata->diff_input;
|
||||
ext_vref = pdata->ext_vref;
|
||||
if (ext_vref && pdata->vref_mv)
|
||||
vref_mv = pdata->vref_mv;
|
||||
}
|
||||
|
||||
/* Bound Vref with min/max values if it was provided */
|
||||
if (data->vref_mv)
|
||||
data->vref_mv = clamp_val(data->vref_mv,
|
||||
ADS7828_EXT_VREF_MV_MIN,
|
||||
ADS7828_EXT_VREF_MV_MAX);
|
||||
else
|
||||
data->vref_mv = ADS7828_INT_VREF_MV;
|
||||
/* Bound Vref with min/max values */
|
||||
vref_mv = clamp_val(vref_mv, ADS7828_EXT_VREF_MV_MIN,
|
||||
ADS7828_EXT_VREF_MV_MAX);
|
||||
|
||||
/* ADS7828 uses 12-bit samples, while ADS7830 is 8-bit */
|
||||
if (id->driver_data == ads7828) {
|
||||
data->lsb_resol = DIV_ROUND_CLOSEST(data->vref_mv * 1000, 4096);
|
||||
data->read_channel = i2c_smbus_read_word_swapped;
|
||||
data->lsb_resol = DIV_ROUND_CLOSEST(vref_mv * 1000, 4096);
|
||||
data->regmap = devm_regmap_init_i2c(client,
|
||||
&ads2828_regmap_config);
|
||||
} else {
|
||||
data->lsb_resol = DIV_ROUND_CLOSEST(data->vref_mv * 1000, 256);
|
||||
data->read_channel = i2c_smbus_read_byte_data;
|
||||
data->lsb_resol = DIV_ROUND_CLOSEST(vref_mv * 1000, 256);
|
||||
data->regmap = devm_regmap_init_i2c(client,
|
||||
&ads2830_regmap_config);
|
||||
}
|
||||
|
||||
data->cmd_byte = data->ext_vref ? ADS7828_CMD_PD1 : ADS7828_CMD_PD3;
|
||||
if (!data->diff_input)
|
||||
data->cmd_byte = ext_vref ? ADS7828_CMD_PD1 : ADS7828_CMD_PD3;
|
||||
if (!diff_input)
|
||||
data->cmd_byte |= ADS7828_CMD_SD_SE;
|
||||
|
||||
data->client = client;
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
||||
data,
|
||||
ads7828_groups);
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/platform_data/ina2xx.h>
|
||||
|
||||
|
@ -51,7 +52,6 @@
|
|||
#define INA226_ALERT_LIMIT 0x07
|
||||
#define INA226_DIE_ID 0xFF
|
||||
|
||||
|
||||
/* register count */
|
||||
#define INA219_REGISTERS 6
|
||||
#define INA226_REGISTERS 8
|
||||
|
@ -64,6 +64,24 @@
|
|||
|
||||
/* worst case is 68.10 ms (~14.6Hz, ina219) */
|
||||
#define INA2XX_CONVERSION_RATE 15
|
||||
#define INA2XX_MAX_DELAY 69 /* worst case delay in ms */
|
||||
|
||||
#define INA2XX_RSHUNT_DEFAULT 10000
|
||||
|
||||
/* bit mask for reading the averaging setting in the configuration register */
|
||||
#define INA226_AVG_RD_MASK 0x0E00
|
||||
|
||||
#define INA226_READ_AVG(reg) (((reg) & INA226_AVG_RD_MASK) >> 9)
|
||||
#define INA226_SHIFT_AVG(val) ((val) << 9)
|
||||
|
||||
/* common attrs, ina226 attrs and NULL */
|
||||
#define INA2XX_MAX_ATTRIBUTE_GROUPS 3
|
||||
|
||||
/*
|
||||
* Both bus voltage and shunt voltage conversion times for ina226 are set
|
||||
* to 0b0100 on POR, which translates to 2200 microseconds in total.
|
||||
*/
|
||||
#define INA226_TOTAL_CONV_TIME_DEFAULT 2200
|
||||
|
||||
enum ina2xx_ids { ina219, ina226 };
|
||||
|
||||
|
@ -81,11 +99,16 @@ struct ina2xx_data {
|
|||
struct i2c_client *client;
|
||||
const struct ina2xx_config *config;
|
||||
|
||||
long rshunt;
|
||||
u16 curr_config;
|
||||
|
||||
struct mutex update_lock;
|
||||
bool valid;
|
||||
unsigned long last_updated;
|
||||
int update_interval; /* in jiffies */
|
||||
|
||||
int kind;
|
||||
const struct attribute_group *groups[INA2XX_MAX_ATTRIBUTE_GROUPS];
|
||||
u16 regs[INA2XX_MAX_REGISTERS];
|
||||
};
|
||||
|
||||
|
@ -110,34 +133,156 @@ static const struct ina2xx_config ina2xx_config[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct ina2xx_data *ina2xx_update_device(struct device *dev)
|
||||
/*
|
||||
* Available averaging rates for ina226. The indices correspond with
|
||||
* the bit values expected by the chip (according to the ina226 datasheet,
|
||||
* table 3 AVG bit settings, found at
|
||||
* http://www.ti.com/lit/ds/symlink/ina226.pdf.
|
||||
*/
|
||||
static const int ina226_avg_tab[] = { 1, 4, 16, 64, 128, 256, 512, 1024 };
|
||||
|
||||
static int ina226_avg_bits(int avg)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Get the closest average from the tab. */
|
||||
for (i = 0; i < ARRAY_SIZE(ina226_avg_tab) - 1; i++) {
|
||||
if (avg <= (ina226_avg_tab[i] + ina226_avg_tab[i + 1]) / 2)
|
||||
break;
|
||||
}
|
||||
|
||||
return i; /* Return 0b0111 for values greater than 1024. */
|
||||
}
|
||||
|
||||
static int ina226_reg_to_interval(u16 config)
|
||||
{
|
||||
int avg = ina226_avg_tab[INA226_READ_AVG(config)];
|
||||
|
||||
/*
|
||||
* Multiply the total conversion time by the number of averages.
|
||||
* Return the result in milliseconds.
|
||||
*/
|
||||
return DIV_ROUND_CLOSEST(avg * INA226_TOTAL_CONV_TIME_DEFAULT, 1000);
|
||||
}
|
||||
|
||||
static u16 ina226_interval_to_reg(int interval, u16 config)
|
||||
{
|
||||
int avg, avg_bits;
|
||||
|
||||
avg = DIV_ROUND_CLOSEST(interval * 1000,
|
||||
INA226_TOTAL_CONV_TIME_DEFAULT);
|
||||
avg_bits = ina226_avg_bits(avg);
|
||||
|
||||
return (config & ~INA226_AVG_RD_MASK) | INA226_SHIFT_AVG(avg_bits);
|
||||
}
|
||||
|
||||
static void ina226_set_update_interval(struct ina2xx_data *data)
|
||||
{
|
||||
int ms;
|
||||
|
||||
ms = ina226_reg_to_interval(data->curr_config);
|
||||
data->update_interval = msecs_to_jiffies(ms);
|
||||
}
|
||||
|
||||
static int ina2xx_calibrate(struct ina2xx_data *data)
|
||||
{
|
||||
u16 val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
|
||||
data->rshunt);
|
||||
|
||||
return i2c_smbus_write_word_swapped(data->client,
|
||||
INA2XX_CALIBRATION, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the configuration and calibration registers.
|
||||
*/
|
||||
static int ina2xx_init(struct ina2xx_data *data)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
int ret;
|
||||
|
||||
/* device configuration */
|
||||
ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
|
||||
data->curr_config);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Set current LSB to 1mA, shunt is in uOhms
|
||||
* (equation 13 in datasheet).
|
||||
*/
|
||||
return ina2xx_calibrate(data);
|
||||
}
|
||||
|
||||
static int ina2xx_do_update(struct device *dev)
|
||||
{
|
||||
struct ina2xx_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
int i, rv, retry;
|
||||
|
||||
dev_dbg(&client->dev, "Starting ina2xx update\n");
|
||||
|
||||
for (retry = 5; retry; retry--) {
|
||||
/* Read all registers */
|
||||
for (i = 0; i < data->config->registers; i++) {
|
||||
rv = i2c_smbus_read_word_swapped(client, i);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
data->regs[i] = rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the current value in the calibration register is 0, the
|
||||
* power and current registers will also remain at 0. In case
|
||||
* the chip has been reset let's check the calibration
|
||||
* register and reinitialize if needed.
|
||||
*/
|
||||
if (data->regs[INA2XX_CALIBRATION] == 0) {
|
||||
dev_warn(dev, "chip not calibrated, reinitializing\n");
|
||||
|
||||
rv = ina2xx_init(data);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
/*
|
||||
* Let's make sure the power and current registers
|
||||
* have been updated before trying again.
|
||||
*/
|
||||
msleep(INA2XX_MAX_DELAY);
|
||||
continue;
|
||||
}
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're here then although all write operations succeeded, the
|
||||
* chip still returns 0 in the calibration register. Nothing more we
|
||||
* can do here.
|
||||
*/
|
||||
dev_err(dev, "unable to reinitialize the chip\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static struct ina2xx_data *ina2xx_update_device(struct device *dev)
|
||||
{
|
||||
struct ina2xx_data *data = dev_get_drvdata(dev);
|
||||
struct ina2xx_data *ret = data;
|
||||
unsigned long after;
|
||||
int rv;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated +
|
||||
HZ / INA2XX_CONVERSION_RATE) || !data->valid) {
|
||||
|
||||
int i;
|
||||
|
||||
dev_dbg(&client->dev, "Starting ina2xx update\n");
|
||||
|
||||
/* Read all registers */
|
||||
for (i = 0; i < data->config->registers; i++) {
|
||||
int rv = i2c_smbus_read_word_swapped(client, i);
|
||||
if (rv < 0) {
|
||||
ret = ERR_PTR(rv);
|
||||
goto abort;
|
||||
}
|
||||
data->regs[i] = rv;
|
||||
}
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
after = data->last_updated + data->update_interval;
|
||||
if (time_after(jiffies, after) || !data->valid) {
|
||||
rv = ina2xx_do_update(dev);
|
||||
if (rv < 0)
|
||||
ret = ERR_PTR(rv);
|
||||
}
|
||||
abort:
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
|
@ -164,6 +309,10 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg)
|
|||
/* signed register, LSB=1mA (selected), in mA */
|
||||
val = (s16)data->regs[reg];
|
||||
break;
|
||||
case INA2XX_CALIBRATION:
|
||||
val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
|
||||
data->regs[reg]);
|
||||
break;
|
||||
default:
|
||||
/* programmer goofed */
|
||||
WARN_ON_ONCE(1);
|
||||
|
@ -187,6 +336,85 @@ static ssize_t ina2xx_show_value(struct device *dev,
|
|||
ina2xx_get_value(data, attr->index));
|
||||
}
|
||||
|
||||
static ssize_t ina2xx_set_shunt(struct device *dev,
|
||||
struct device_attribute *da,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ina2xx_data *data = ina2xx_update_device(dev);
|
||||
unsigned long val;
|
||||
int status;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
status = kstrtoul(buf, 10, &val);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
if (val == 0 ||
|
||||
/* Values greater than the calibration factor make no sense. */
|
||||
val > data->config->calibration_factor)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->rshunt = val;
|
||||
status = ina2xx_calibrate(data);
|
||||
mutex_unlock(&data->update_lock);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t ina226_set_interval(struct device *dev,
|
||||
struct device_attribute *da,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ina2xx_data *data = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
int status;
|
||||
|
||||
status = kstrtoul(buf, 10, &val);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
if (val > INT_MAX || val == 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->curr_config = ina226_interval_to_reg(val,
|
||||
data->regs[INA2XX_CONFIG]);
|
||||
status = i2c_smbus_write_word_swapped(data->client,
|
||||
INA2XX_CONFIG,
|
||||
data->curr_config);
|
||||
|
||||
ina226_set_update_interval(data);
|
||||
/* Make sure the next access re-reads all registers. */
|
||||
data->valid = 0;
|
||||
mutex_unlock(&data->update_lock);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t ina226_show_interval(struct device *dev,
|
||||
struct device_attribute *da, char *buf)
|
||||
{
|
||||
struct ina2xx_data *data = ina2xx_update_device(dev);
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
/*
|
||||
* We don't use data->update_interval here as we want to display
|
||||
* the actual interval used by the chip and jiffies_to_msecs()
|
||||
* doesn't seem to be accurate enough.
|
||||
*/
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
ina226_reg_to_interval(data->regs[INA2XX_CONFIG]));
|
||||
}
|
||||
|
||||
/* shunt voltage */
|
||||
static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ina2xx_show_value, NULL,
|
||||
INA2XX_SHUNT_VOLTAGE);
|
||||
|
@ -203,15 +431,37 @@ static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ina2xx_show_value, NULL,
|
|||
static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ina2xx_show_value, NULL,
|
||||
INA2XX_POWER);
|
||||
|
||||
/* shunt resistance */
|
||||
static SENSOR_DEVICE_ATTR(shunt_resistor, S_IRUGO | S_IWUSR,
|
||||
ina2xx_show_value, ina2xx_set_shunt,
|
||||
INA2XX_CALIBRATION);
|
||||
|
||||
/* update interval (ina226 only) */
|
||||
static SENSOR_DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR,
|
||||
ina226_show_interval, ina226_set_interval, 0);
|
||||
|
||||
/* pointers to created device attributes */
|
||||
static struct attribute *ina2xx_attrs[] = {
|
||||
&sensor_dev_attr_in0_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_curr1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_power1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_shunt_resistor.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(ina2xx);
|
||||
|
||||
static const struct attribute_group ina2xx_group = {
|
||||
.attrs = ina2xx_attrs,
|
||||
};
|
||||
|
||||
static struct attribute *ina226_attrs[] = {
|
||||
&sensor_dev_attr_update_interval.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group ina226_group = {
|
||||
.attrs = ina226_attrs,
|
||||
};
|
||||
|
||||
static int ina2xx_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
|
@ -221,9 +471,8 @@ static int ina2xx_probe(struct i2c_client *client,
|
|||
struct device *dev = &client->dev;
|
||||
struct ina2xx_data *data;
|
||||
struct device *hwmon_dev;
|
||||
long shunt = 10000; /* default shunt value 10mOhms */
|
||||
u32 val;
|
||||
int ret;
|
||||
int ret, group = 0;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -ENODEV;
|
||||
|
@ -234,50 +483,52 @@ static int ina2xx_probe(struct i2c_client *client,
|
|||
|
||||
if (dev_get_platdata(dev)) {
|
||||
pdata = dev_get_platdata(dev);
|
||||
shunt = pdata->shunt_uohms;
|
||||
data->rshunt = pdata->shunt_uohms;
|
||||
} else if (!of_property_read_u32(dev->of_node,
|
||||
"shunt-resistor", &val)) {
|
||||
shunt = val;
|
||||
data->rshunt = val;
|
||||
} else {
|
||||
data->rshunt = INA2XX_RSHUNT_DEFAULT;
|
||||
}
|
||||
|
||||
if (shunt <= 0)
|
||||
return -ENODEV;
|
||||
|
||||
/* set the device type */
|
||||
data->kind = id->driver_data;
|
||||
data->config = &ina2xx_config[data->kind];
|
||||
|
||||
/* device configuration */
|
||||
ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
|
||||
data->config->config_default);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"error writing to the config register: %d", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
data->curr_config = data->config->config_default;
|
||||
data->client = client;
|
||||
|
||||
/*
|
||||
* Set current LSB to 1mA, shunt is in uOhms
|
||||
* (equation 13 in datasheet).
|
||||
* Ina226 has a variable update_interval. For ina219 we
|
||||
* use a constant value.
|
||||
*/
|
||||
ret = i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION,
|
||||
data->config->calibration_factor / shunt);
|
||||
if (data->kind == ina226)
|
||||
ina226_set_update_interval(data);
|
||||
else
|
||||
data->update_interval = HZ / INA2XX_CONVERSION_RATE;
|
||||
|
||||
if (data->rshunt <= 0 ||
|
||||
data->rshunt > data->config->calibration_factor)
|
||||
return -ENODEV;
|
||||
|
||||
ret = ina2xx_init(data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"error writing to the calibration register: %d", ret);
|
||||
dev_err(dev, "error configuring the device: %d\n", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data->client = client;
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
data->groups[group++] = &ina2xx_group;
|
||||
if (data->kind == ina226)
|
||||
data->groups[group++] = &ina226_group;
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
||||
data, ina2xx_groups);
|
||||
data, data->groups);
|
||||
if (IS_ERR(hwmon_dev))
|
||||
return PTR_ERR(hwmon_dev);
|
||||
|
||||
dev_info(dev, "power monitor %s (Rshunt = %li uOhm)\n",
|
||||
id->name, shunt);
|
||||
id->name, data->rshunt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -287,6 +538,7 @@ static const struct i2c_device_id ina2xx_id[] = {
|
|||
{ "ina220", ina219 },
|
||||
{ "ina226", ina226 },
|
||||
{ "ina230", ina226 },
|
||||
{ "ina231", ina226 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ina2xx_id);
|
||||
|
|
|
@ -201,7 +201,7 @@ struct jc42_data {
|
|||
#define JC42_TEMP_MIN 0
|
||||
#define JC42_TEMP_MAX 125000
|
||||
|
||||
static u16 jc42_temp_to_reg(int temp, bool extended)
|
||||
static u16 jc42_temp_to_reg(long temp, bool extended)
|
||||
{
|
||||
int ntemp = clamp_val(temp,
|
||||
extended ? JC42_TEMP_MIN_EXTENDED :
|
||||
|
@ -213,11 +213,7 @@ static u16 jc42_temp_to_reg(int temp, bool extended)
|
|||
|
||||
static int jc42_temp_from_reg(s16 reg)
|
||||
{
|
||||
reg &= 0x1fff;
|
||||
|
||||
/* sign extend register */
|
||||
if (reg & 0x1000)
|
||||
reg |= 0xf000;
|
||||
reg = sign_extend32(reg, 12);
|
||||
|
||||
/* convert from 0.0625 to 0.001 resolution */
|
||||
return reg * 125 / 2;
|
||||
|
@ -308,15 +304,18 @@ static ssize_t set_temp_crit_hyst(struct device *dev,
|
|||
const char *buf, size_t count)
|
||||
{
|
||||
struct jc42_data *data = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
long val;
|
||||
int diff, hyst;
|
||||
int err;
|
||||
int ret = count;
|
||||
|
||||
if (kstrtoul(buf, 10, &val) < 0)
|
||||
if (kstrtol(buf, 10, &val) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
val = clamp_val(val, (data->extended ? JC42_TEMP_MIN_EXTENDED :
|
||||
JC42_TEMP_MIN) - 6000, JC42_TEMP_MAX);
|
||||
diff = jc42_temp_from_reg(data->temp[t_crit]) - val;
|
||||
|
||||
hyst = 0;
|
||||
if (diff > 0) {
|
||||
if (diff < 2250)
|
||||
|
|
|
@ -779,7 +779,7 @@ static bool nct7802_regmap_is_volatile(struct device *dev, unsigned int reg)
|
|||
return reg != REG_BANK && reg <= 0x20;
|
||||
}
|
||||
|
||||
static struct regmap_config nct7802_regmap_config = {
|
||||
static const struct regmap_config nct7802_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
|
|
|
@ -253,7 +253,7 @@ static int tmp102_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tmp102_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
@ -279,17 +279,10 @@ static int tmp102_resume(struct device *dev)
|
|||
config &= ~TMP102_CONF_SD;
|
||||
return i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, config);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops tmp102_dev_pm_ops = {
|
||||
.suspend = tmp102_suspend,
|
||||
.resume = tmp102_resume,
|
||||
};
|
||||
|
||||
#define TMP102_DEV_PM_OPS (&tmp102_dev_pm_ops)
|
||||
#else
|
||||
#define TMP102_DEV_PM_OPS NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(tmp102_dev_pm_ops, tmp102_suspend, tmp102_resume);
|
||||
|
||||
static const struct i2c_device_id tmp102_id[] = {
|
||||
{ "tmp102", 0 },
|
||||
{ }
|
||||
|
@ -298,7 +291,7 @@ MODULE_DEVICE_TABLE(i2c, tmp102_id);
|
|||
|
||||
static struct i2c_driver tmp102_driver = {
|
||||
.driver.name = DRIVER_NAME,
|
||||
.driver.pm = TMP102_DEV_PM_OPS,
|
||||
.driver.pm = &tmp102_dev_pm_ops,
|
||||
.probe = tmp102_probe,
|
||||
.remove = tmp102_remove,
|
||||
.id_table = tmp102_id,
|
||||
|
|
Loading…
Reference in New Issue