* Re: [PATCH 2/3] iio: adc: ina3221: Add support for IIO ADC driver for TI INA3221 [not found] ` <1464784454-7988-2-git-send-email-ldewangan@nvidia.com> @ 2016-06-03 10:06 ` Jonathan Cameron 2016-06-03 10:16 ` Jonathan Cameron ` (2 more replies) 0 siblings, 3 replies; 14+ messages in thread From: Jonathan Cameron @ 2016-06-03 10:06 UTC (permalink / raw) To: Laxman Dewangan, robh+dt, corbet, lars Cc: devicetree, linux-kernel, linux-doc, linux-iio, linux-hwmon, Jean Delvare, Guenter Roeck On 01/06/16 13:34, Laxman Dewangan wrote: > The INA3221 is a three-channel, high-side current and bus voltage monitor > with an I2C interface from Texas Instruments. The INA3221 monitors both > shunt voltage drops and bus supply voltages in addition to having > programmable conversion times and averaging modes for these signals. > The INA3221 offers both critical and warning alerts to detect multiple > programmable out-of-range conditions for each channel. > > Add support for INA3221 SW driver via IIO ADC interface. The device is > register as iio-device and provides interface for voltage/current and power > monitor. Also provide interface for setting oneshot/continuous mode and > critical/warning threshold for the shunt voltage drop. > > Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> Hi Laxman, As ever with any driver lying on the border of IIO and hwmon, please include a short justification of why you need an IIO driver and also cc the hwmon list + maintainers. (cc'd on this reply). I simply won't take a driver where the hwmon maintainers aren't happy. As it stands I'm not seeing obvious reasons in the code for why this should be an IIO device. Funily enough I know this datasheet a little as was evaluating it for use on some boards at the day job a week or so ago. Various comments inline. Major points are: * Don't use 'fake' channels to control events. If the events infrastructure doesn't handle your events, then fix that rather than working around it. * There is a lot of ABI in here concerned with oneshot vs continuous. This seems to me to be more than it should be. We wouldn't expect to see stuff changing as a result of switching between these modes other than wrt to when the data shows up. So I'd expect to not see this directly exposed at all - but rather sit in oneshot unless either: 1) Buffered mode is running (not currently supported) 2) Alerts are on - which I think requires it to be in continuous mode. Other question to my mind is whether we should be reporting vshunt or (using device tree to pass resistance) current. Code looks good, bu these more fundamental bits need sorting. Thanks, Jonathan > --- > drivers/iio/adc/Kconfig | 12 + > drivers/iio/adc/Makefile | 1 + > drivers/iio/adc/ina3221.c | 1175 +++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 1188 insertions(+) > create mode 100644 drivers/iio/adc/ina3221.c > > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig > index 25378c5..65f3c27 100644 > --- a/drivers/iio/adc/Kconfig > +++ b/drivers/iio/adc/Kconfig > @@ -223,6 +223,18 @@ config INA2XX_ADC > Say yes here to build support for TI INA2xx family of Power Monitors. > This driver is mutually exclusive with the HWMON version. > > +config INA3221 > + tristate "TI INA3221 3-Channel Shunt and Bus Voltage Monitor" > + depends on I2C > + select REGMAP_I2C > + help > + INA3221 is Triple-Channel, High-Side Measurement, Shunt and Bus > + Voltage Monitor device from TI. This driver support the reading > + of all channel's voltage/current and power via IIO interface. > + Say yes here to build support for TI INA3221. To compile this > + driver as a module, choose M here: the module will be called > + ina3221. > + > config IMX7D_ADC > tristate "IMX7D ADC driver" > depends on ARCH_MXC || COMPILE_TEST > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile > index 38638d4..c24f455 100644 > --- a/drivers/iio/adc/Makefile > +++ b/drivers/iio/adc/Makefile > @@ -24,6 +24,7 @@ obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o > obj-$(CONFIG_HI8435) += hi8435.o > obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o > obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o > +obj-$(CONFIG_INA3221) += ina3221.o > obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o > obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o > obj-$(CONFIG_MAX1027) += max1027.o > diff --git a/drivers/iio/adc/ina3221.c b/drivers/iio/adc/ina3221.c > new file mode 100644 > index 0000000..a17f688 > --- /dev/null > +++ b/drivers/iio/adc/ina3221.c > @@ -0,0 +1,1175 @@ > +/* > + * IIO driver for INA3221 > + * > + * Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > + > +#include <linux/delay.h> > +#include <linux/i2c.h> > +#include <linux/iio/kfifo_buf.h> > +#include <linux/iio/sysfs.h> > +#include <linux/module.h> > +#include <linux/regmap.h> > +#include <linux/util_macros.h> > + > +/* INA3221 registers definition */ > +#define INA3221_CONFIG 0x00 > +#define INA3221_SHUNT_VOL_CHAN1 0x01 > +#define INA3221_BUS_VOL_CHAN1 0x02 > +#define INA3221_SHUNT_VOL_CHAN2 0x03 > +#define INA3221_BUS_VOL_CHAN2 0x04 > +#define INA3221_SHUNT_VOL_CHAN3 0x05 > +#define INA3221_BUS_VOL_CHAN3 0x06 > +#define INA3221_CRIT_CHAN1 0x07 > +#define INA3221_WARN_CHAN1 0x08 > +#define INA3221_CRIT_CHAN2 0x09 > +#define INA3221_WARN_CHAN2 0x0A > +#define INA3221_CRIT_CHAN3 0x0B > +#define INA3221_WARN_CHAN3 0x0C > +#define INA3221_MASK_ENABLE 0x0F > +#define INA3221_POWER_VALID_UPPER_LIMIT 0x10 > +#define INA3221_POWER_VALID_LOWER_LIMIT 0x11 > +#define INA3221_MAN_ID 0xFE > +#define INA3221_DEV_ID 0xFF > + > +#define INA3221_CONFIG_RESET_MASK BIT(15) > +#define INA3221_CONFIG_RESET_EN BIT(15) > + > +#define INA3221_CONFIG_MODE_MASK GENMASK(2, 0) > +#define INA3221_CONFIG_MODE_POWER_DOWN 0 > + > +#define INA3221_CONFIG_AVG_MASK GENMASK(11, 9) > +#define INA3221_CONFIG_AVG(val) ((val) << 9) > + > +#define INA3221_CONFIG_VBUSCT_MASK GENMASK(8, 6) > +#define INA3221_CONFIG_VBUSCT(val) ((val) << 6) > + > +#define INA3221_CONFIG_SHUNTCT_MASK GENMASK(5, 3) > +#define INA3221_CONFIG_SHUNTCT(val) ((val) << 3) > + > +#define INA3221_REG_MASK_WEN BIT(11) > +#define INA3221_REG_MASK_CEN BIT(10) > +#define INA3221_REG_MASK_CVRF BIT(0) > + > +#define PACK_MODE_CHAN(mode, chan) ((mode) | ((chan) << 8)) > +#define UNPACK_MODE(address) ((address) & 0xFF) > +#define UNPACK_CHAN(address) (((address) >> 8) & 0xFF) > + > +#define INA3221_NUMBER_OF_CHANNELS 3 > +#define INA3221_MAX_CONVERSION_TRIALS 10 > +#define INA3221_CONFIG_AVG_SAMPLE_DEFAULT 4 > +#define INA3221_CONFIG_VBUS_CONV_TIME_DEFAULT 150 > +#define INA3221_CONFIG_SHUNT_CONV_TIME_DEFAULT 150 > + > +#define INA3221_SHUNT_VOL(i) (INA3221_SHUNT_VOL_CHAN1 + (i) * 2) > +#define INA3221_BUS_VOL(i) (INA3221_BUS_VOL_CHAN1 + (i) * 2) > +#define INA3221_CRIT(i) (INA3221_CRIT_CHAN1 + (i) * 2) > +#define INA3221_WARN(i) (INA3221_WARN_CHAN1 + (i) * 2) > + > +static const struct regmap_range ina3221_readable_ranges[] = { > + regmap_reg_range(INA3221_CONFIG, INA3221_POWER_VALID_LOWER_LIMIT), > + regmap_reg_range(INA3221_MAN_ID, INA3221_DEV_ID), > +}; > + > +static const struct regmap_access_table ina3221_readable_table = { > + .yes_ranges = ina3221_readable_ranges, > + .n_yes_ranges = ARRAY_SIZE(ina3221_readable_ranges), > +}; > + > +static const struct regmap_range ina3221_no_writable_ranges[] = { > + regmap_reg_range(INA3221_SHUNT_VOL_CHAN1, INA3221_BUS_VOL_CHAN3), > +}; > + > +static const struct regmap_access_table ina3221_writable_table = { > + .no_ranges = ina3221_no_writable_ranges, > + .n_no_ranges = ARRAY_SIZE(ina3221_no_writable_ranges), > +}; > + > +static const struct regmap_range ina3221_no_volatile_ranges[] = { > + regmap_reg_range(INA3221_CONFIG, INA3221_CONFIG), > + regmap_reg_range(INA3221_CRIT_CHAN1, INA3221_WARN_CHAN3), > + regmap_reg_range(INA3221_POWER_VALID_UPPER_LIMIT, > + INA3221_POWER_VALID_LOWER_LIMIT), > + regmap_reg_range(INA3221_MAN_ID, INA3221_DEV_ID), > +}; > + > +static const struct regmap_access_table ina3221_volatile_table = { > + .no_ranges = ina3221_no_volatile_ranges, > + .n_no_ranges = ARRAY_SIZE(ina3221_no_volatile_ranges), > +}; > + > +static const struct regmap_config ina3221_regmap_config = { > + .reg_bits = 8, > + .val_bits = 16, > + .max_register = INA3221_DEV_ID + 1, > + .rd_table = &ina3221_readable_table, > + .wr_table = &ina3221_writable_table, > + .volatile_table = &ina3221_volatile_table, > +}; > + > +struct ina3221_channel_data { > + const char *name; > + int warn_limits; > + int crit_limits; > + int shunt_resistance; > +}; > + > +struct ina3221_platform_data { > + struct ina3221_channel_data channel_data[INA3221_NUMBER_OF_CHANNELS]; > + bool enable_power; > + int oneshot_avg_sample; > + int oneshot_vbus_conv_time; > + int oneshot_shunt_conv_time; > + int cont_avg_sample; > + int cont_vbus_conv_time; > + int cont_shunt_conv_time; > + int continuous_mode; > + bool warn_alert; > + bool crit_alert; > + int active_channel; > +}; > + > +struct ina3221_chip_info { > + struct device *dev; > + struct regmap *rmap; > + int oneshot_config; > + int continuous_config; > + int continuous_mode; > + struct mutex state_lock; > + struct ina3221_platform_data *pdata; > +}; > + > +enum ina3221_address { > + INA3221_CHANNEL_NAME, > + INA3221_CRIT_CURRENT_LIMIT, > + INA3221_WARN_CURRENT_LIMIT, > + INA3221_MEASURED_VALUE, > + INA3221_OPERATING_MODE, > + INA3221_OVERSAMPLING_RATIO, > + INA3221_VBUS_CONV_TIME, > + INA3221_VSHUNT_CONV_TIME, > + INA3221_CHANNEL_ONESHOT, > + INA3221_CHANNEL_CONTINUOUS, > +}; > + > +static inline int shuntv_register_to_uv(u16 reg) > +{ > + int ret = (s16)reg; > + > + return (ret >> 3) * 40; > +} > + > +static inline u16 uv_to_shuntv_register(s32 uv) > +{ > + return (u16)(uv / 5); > +} > + > +static inline int busv_register_to_mv(u16 reg) > +{ > + int ret = (s16)reg; > + > + return (ret >> 3) * 8; > +} > + > +/* convert shunt voltage register value to current (in mA) */ > +static int shuntv_register_to_ma(u16 reg, int resistance) > +{ > + int uv, ma; > + > + uv = (s16)reg; > + uv = ((uv >> 3) * 40); /* LSB (4th bit) is 40uV */ > + /* > + * calculate uv/resistance with rounding knowing that C99 truncates > + * towards zero > + */ > + if (uv > 0) > + ma = ((uv * 2 / resistance) + 1) / 2; > + else > + ma = ((uv * 2 / resistance) - 1) / 2; > + return ma; > +} > + > +static int ina3221_get_closest_index(const int *list, int n_list, > + int val) > +{ > + if (val > list[n_list - 1] || (val < list[0])) > + return -EINVAL; > + > + return find_closest(val, list, n_list); > +} > + > +/* > + * Available averaging rates for INA3221. The indices correspond with > + * the bit values expected by the chip (according to the INA3221 datasheet, > + * table 3 AVG bit settings, found at > + */ > +static const int ina3221_avg_tab[] = { 1, 4, 16, 64, 128, 256, 512, 1024}; > + > +static int ina3221_set_average(struct ina3221_chip_info *chip, unsigned int val, > + unsigned int *config) > +{ > + int bits; > + > + bits = ina3221_get_closest_index(ina3221_avg_tab, > + ARRAY_SIZE(ina3221_avg_tab), val); > + if (bits < 0) > + return bits; > + > + *config &= ~INA3221_CONFIG_AVG_MASK; > + *config |= INA3221_CONFIG_AVG(bits) & INA3221_CONFIG_AVG_MASK; > + > + return 0; > +} > + > +/* Conversion times in uS */ > +static const int ina3221_conv_time_tab[] = { 140, 204, 332, 588, 1100, > + 2116, 4156, 8244}; > + > +static int ina3221_set_int_time_vbus(struct ina3221_chip_info *chip, > + unsigned int val_us, unsigned int *config) > +{ > + int bits; > + > + bits = ina3221_get_closest_index(ina3221_conv_time_tab, > + ARRAY_SIZE(ina3221_conv_time_tab), > + val_us); > + if (bits < 0) > + return bits; > + > + *config &= ~INA3221_CONFIG_VBUSCT_MASK; > + *config |= INA3221_CONFIG_VBUSCT(bits) & INA3221_CONFIG_VBUSCT_MASK; > + > + return 0; > +} > + > +static int ina3221_set_int_time_vshunt(struct ina3221_chip_info *chip, > + unsigned int val_us, > + unsigned int *config) > +{ > + int bits; > + > + bits = ina3221_get_closest_index(ina3221_conv_time_tab, > + ARRAY_SIZE(ina3221_conv_time_tab), > + val_us); > + if (bits < 0) > + return bits; > + > + *config &= ~INA3221_CONFIG_SHUNTCT_MASK; > + *config |= INA3221_CONFIG_SHUNTCT(bits) & INA3221_CONFIG_SHUNTCT_MASK; > + > + return 0; > +} > + > +static int ina3221_do_one_shot_conversion(struct ina3221_chip_info *chip, > + int chan, u16 *vbus, u16 *vsh) > +{ > + unsigned int value; > + int trials = 0; > + int ret; > + int conv_time; > + > + conv_time = max(chip->pdata->oneshot_vbus_conv_time, > + chip->pdata->oneshot_shunt_conv_time); > + > + ret = regmap_write(chip->rmap, INA3221_CONFIG, chip->oneshot_config); > + if (ret < 0) > + return 0; > + > + /* Read conversion status */ > + do { > + ret = regmap_read(chip->rmap, INA3221_MASK_ENABLE, &value); > + if (ret < 0) { > + dev_err(chip->dev, > + "Failed to read conversion status: %d\n", ret); > + return ret; > + } > + > + if (value & INA3221_REG_MASK_CVRF) > + break; > + usleep_range(conv_time, conv_time * 2); > + } while (++trials < INA3221_MAX_CONVERSION_TRIALS); > + > + if (trials == INA3221_MAX_CONVERSION_TRIALS) { > + dev_err(chip->dev, > + "Conversion not completed for maximum trials\n"); > + return -EAGAIN; > + } > + > + if (vsh) { > + ret = regmap_read(chip->rmap, INA3221_SHUNT_VOL(chan), &value); > + if (ret < 0) > + return ret; > + *vsh = (u16)value; > + } > + > + if (vbus) { > + ret = regmap_read(chip->rmap, INA3221_BUS_VOL(chan), &value); > + if (ret < 0) > + return ret; > + *vbus = (u16)value; > + } > + > + ret = regmap_write(chip->rmap, INA3221_CONFIG, 0); > + if (ret < 0) > + return ret; > + > + return 0; > +} > + > +static int ina3221_read_continuous_conversion(struct ina3221_chip_info *chip, > + int chan, u16 *vbus, u16 *vsh) > +{ > + unsigned int value; > + int ret; > + > + if (vsh) { > + ret = regmap_read(chip->rmap, INA3221_SHUNT_VOL(chan), &value); > + if (ret < 0) > + return ret; > + *vsh = (u16)value; > + } > + > + if (vbus) { > + ret = regmap_read(chip->rmap, INA3221_BUS_VOL(chan), &value); > + if (ret < 0) > + return ret; > + *vbus = (u16)value; > + } > + > + return ret; > +} > + > +static int ina3221_read_vbus_vshunt(struct ina3221_chip_info *chip, > + int ch, u16 *vbus, u16 *vsh) > +{ > + if (chip->continuous_mode) > + return ina3221_read_continuous_conversion(chip, ch, vbus, vsh); > + > + return ina3221_do_one_shot_conversion(chip, ch, vbus, vsh); > +} > + > +static int ina3221_get_channel_voltage(struct ina3221_chip_info *chip, > + int chan, int *voltage_uv) > +{ > + u16 vbus; > + int ret; > + > + ret = ina3221_read_vbus_vshunt(chip, chan, &vbus, NULL); > + if (ret < 0) > + return ret; > + > + *voltage_uv = busv_register_to_mv(vbus) * 1000; > + > + return 0; > +} > + > +static int ina3221_get_channel_current(struct ina3221_chip_info *chip, > + int chan, int *current_ua) > +{ > + int shunt_res = chip->pdata->channel_data[chan].shunt_resistance; > + u16 vsh; > + int ret; > + > + ret = ina3221_do_one_shot_conversion(chip, chan, NULL, &vsh); > + if (ret < 0) > + return ret; > + > + *current_ua = shuntv_register_to_ma(vsh, shunt_res) * 1000; > + > + return 0; > +} > + > +static int ina3221_get_channel_power(struct ina3221_chip_info *chip, > + int chan, int *power_uw) > +{ > + int shunt_res = chip->pdata->channel_data[chan].shunt_resistance; > + u16 vbus, vsh; > + int ret; > + int current_ma, voltage_mv; > + > + ret = ina3221_do_one_shot_conversion(chip, chan, &vbus, &vsh); > + if (ret < 0) > + return ret; > + > + current_ma = shuntv_register_to_ma(vsh, shunt_res); > + voltage_mv = busv_register_to_mv(vbus); > + *power_uw = (voltage_mv * current_ma); > + > + return 0; > +} > + > +static int ina3221_get_channel_critical(struct ina3221_chip_info *chip, > + int chan, int *curr_limit_ua) > +{ > + *curr_limit_ua = chip->pdata->channel_data[chan].crit_limits; > + > + return 0; > +} > + > +static int ina3221_set_channel_critical(struct ina3221_chip_info *chip, > + int chan, int crit_limit_ua) > +{ > + int s_res = chip->pdata->channel_data[chan].shunt_resistance; > + int shunt_volt_limit; > + int crit_limit = crit_limit_ua / 1000; > + int ret; > + > + if (crit_limit < 0) > + return 0; > + > + shunt_volt_limit = crit_limit * s_res; > + shunt_volt_limit = uv_to_shuntv_register(shunt_volt_limit); > + > + ret = regmap_write(chip->rmap, INA3221_CRIT(chan), shunt_volt_limit); > + if (ret < 0) { > + dev_err(chip->dev, "Failed to write critical reg 0x%02x: %d\n", > + INA3221_CRIT(chan), ret); > + return ret; > + } > + > + return 0; > +} > + > +static int ina3221_read_default_channel_critical(struct ina3221_chip_info *chip, > + int chan, int *shunt_curr_ua) > +{ > + int shunt_res = chip->pdata->channel_data[chan].shunt_resistance; > + int shunt_volt; > + unsigned int value; > + int ret; > + > + if (shunt_res <= 0) { > + dev_err(chip->dev, "Channel %d have invalid shunt resistor\n", > + chan); > + return -EINVAL; > + } > + > + ret = regmap_read(chip->rmap, INA3221_CRIT(chan), &value); > + if (ret < 0) { > + dev_err(chip->dev, "Failed to read critical reg 0x%02x: %d\n", > + INA3221_CRIT(chan), ret); > + return ret; > + } > + shunt_volt = shuntv_register_to_uv((u16)value); > + *shunt_curr_ua = (shunt_volt / shunt_res) * 1000; > + > + return 0; > +} > + > +static int ina3221_get_channel_warning(struct ina3221_chip_info *chip, > + int chan, int *curr_limit_ua) > +{ > + *curr_limit_ua = chip->pdata->channel_data[chan].warn_limits; > + > + return 0; > +} > + > +static int ina3221_set_channel_warning(struct ina3221_chip_info *chip, > + int chan, int warn_limit_ua) > +{ > + int s_res = chip->pdata->channel_data[chan].shunt_resistance; > + int shunt_volt_limit; > + int warn_limit = warn_limit_ua / 1000; > + int ret; > + > + if (warn_limit < 0) > + return 0; > + > + shunt_volt_limit = warn_limit * s_res; > + shunt_volt_limit = uv_to_shuntv_register(shunt_volt_limit); > + > + ret = regmap_write(chip->rmap, INA3221_WARN(chan), shunt_volt_limit); > + if (ret < 0) { > + dev_err(chip->dev, "Failed to write warning reg 0x%02x: %d\n", > + INA3221_CRIT(chan), ret); > + return ret; > + } > + > + return 0; > +} > + > +static int ina3221_read_default_channel_warning(struct ina3221_chip_info *chip, > + int chan, int *shunt_curr_ua) > +{ > + int shunt_res = chip->pdata->channel_data[chan].shunt_resistance; > + int shunt_volt; > + unsigned int value; > + int ret; > + > + if (shunt_res <= 0) { > + dev_err(chip->dev, "Channel %d have invalid shunt resistor\n", > + chan); > + return -EINVAL; > + } > + > + ret = regmap_read(chip->rmap, INA3221_WARN(chan), &value); > + if (ret < 0) { > + dev_err(chip->dev, "Failed to read critical reg 0x%02x: %d\n", > + INA3221_CRIT(chan), ret); > + return ret; > + } > + shunt_volt = shuntv_register_to_uv((u16)value); > + *shunt_curr_ua = (shunt_volt / shunt_res) * 1000; > + > + return 0; > +} > + > +static int ina3221_get_operating_mode(struct ina3221_chip_info *chip) > +{ > + return chip->continuous_mode; > +} > + > +static int ina3221_set_operating_mode(struct ina3221_chip_info *chip, > + int is_continuous) > +{ > + unsigned int mask, val; > + int ret; > + > + if (!is_continuous) { > + ret = regmap_write(chip->rmap, INA3221_CONFIG, 0); > + if (ret < 0) { > + dev_err(chip->dev, > + "Failed to set mode of device: %d\n", ret); > + return ret; > + } > + > + goto done; > + } > + > + if ((chip->pdata->warn_alert || chip->pdata->crit_alert)) { > + mask = INA3221_REG_MASK_CEN | INA3221_REG_MASK_WEN; > + val = (chip->pdata->warn_alert) ? INA3221_REG_MASK_WEN : 0; > + val |= (chip->pdata->crit_alert) ? INA3221_REG_MASK_CEN : 0; > + > + ret = regmap_update_bits(chip->rmap, INA3221_MASK_ENABLE, > + mask, val); > + if (ret < 0) { > + dev_err(chip->dev, > + "Failed to enable warn/crit alert: %d\n", ret); > + return ret; > + } > + } > + > + ret = regmap_write(chip->rmap, INA3221_CONFIG, chip->continuous_config); > + if (ret < 0) { > + dev_err(chip->dev, "Failed to set cont mode: %d\n", ret); > + return ret; > + } > + > +done: > + chip->continuous_mode = is_continuous; > + > + return ret; > +} > + > +static int ina3221_read_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *cspec, > + int *val, int *val2, long mask) > +{ > + struct ina3221_chip_info *chip = iio_priv(indio_dev); > + int ch = cspec->channel; > + int ret = -EINVAL; > + > + if (mask != IIO_CHAN_INFO_PROCESSED) > + return -EINVAL; > + > + mutex_lock(&chip->state_lock); > + > + switch (cspec->type) { > + case IIO_VOLTAGE: > + ret = ina3221_get_channel_voltage(chip, ch, val); > + break; > + > + case IIO_CURRENT: > + switch (cspec->address) { > + case INA3221_MEASURED_VALUE: > + ret = ina3221_get_channel_current(chip, ch, val); > + break; > + > + case INA3221_CRIT_CURRENT_LIMIT: > + ret = ina3221_get_channel_critical(chip, ch, val); > + break; > + > + case INA3221_WARN_CURRENT_LIMIT: > + ret = ina3221_get_channel_warning(chip, ch, val); > + break; > + } > + break; > + > + case IIO_POWER: > + ret = ina3221_get_channel_power(chip, ch, val); > + break; > + > + default: > + break; > + } > + > + mutex_unlock(&chip->state_lock); > + > + return (ret < 0) ? ret : IIO_VAL_INT; > +} > + > +static int ina3221_write_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *cspec, > + int val, int val2, long mask) > +{ > + struct ina3221_chip_info *chip = iio_priv(indio_dev); > + int ch = cspec->channel; > + int ret = -EINVAL; > + > + if (mask != IIO_CHAN_INFO_PROCESSED) > + return -EINVAL; > + > + if (cspec->type != IIO_CURRENT) > + return -EINVAL; > + > + mutex_lock(&chip->state_lock); > + switch (cspec->address) { > + case INA3221_CRIT_CURRENT_LIMIT: > + ret = ina3221_set_channel_critical(chip, ch, val); > + if (!ret) > + chip->pdata->channel_data[ch].crit_limits = val; > + break; > + > + case INA3221_WARN_CURRENT_LIMIT: > + ret = ina3221_set_channel_warning(chip, ch, val); > + if (!ret) > + chip->pdata->channel_data[ch].warn_limits = val; > + break; > + > + default: > + break; > + } > + > + mutex_unlock(&chip->state_lock); > + > + return ret; > +} > + > +static ssize_t ina3221_show_channel(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct iio_dev *indio_dev = dev_to_iio_dev(dev); > + struct ina3221_chip_info *chip = iio_priv(indio_dev); > + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); > + int mode = UNPACK_MODE(this_attr->address); > + int address = UNPACK_CHAN(this_attr->address); > + int ret; > + int val; > + > + switch (mode) { > + case INA3221_CHANNEL_NAME: > + return snprintf(buf, PAGE_SIZE, "%s\n", > + chip->pdata->channel_data[address].name); > + > + case INA3221_OPERATING_MODE: > + ret = ina3221_get_operating_mode(chip); > + if (ret) > + return snprintf(buf, PAGE_SIZE, "continuous\n"); > + return snprintf(buf, PAGE_SIZE, "oneshot\n"); > + > + case INA3221_OVERSAMPLING_RATIO: > + if (address == INA3221_CHANNEL_ONESHOT) > + val = chip->pdata->oneshot_avg_sample; > + else > + val = chip->pdata->cont_avg_sample; > + return snprintf(buf, PAGE_SIZE, "%d\n", val); > + > + case INA3221_VBUS_CONV_TIME: > + if (address == INA3221_CHANNEL_ONESHOT) > + val = chip->pdata->oneshot_vbus_conv_time; > + else > + val = chip->pdata->cont_vbus_conv_time; > + return snprintf(buf, PAGE_SIZE, "%d\n", val); > + > + case INA3221_VSHUNT_CONV_TIME: > + if (address == INA3221_CHANNEL_ONESHOT) > + val = chip->pdata->oneshot_shunt_conv_time; > + else > + val = chip->pdata->cont_shunt_conv_time; > + return snprintf(buf, PAGE_SIZE, "%d\n", val); > + > + default: > + break; > + } > + > + return -EINVAL; > +} > + > +static ssize_t ina3221_set_channel(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t len) > +{ > + struct iio_dev *indio_dev = dev_to_iio_dev(dev); > + struct ina3221_chip_info *chip = iio_priv(indio_dev); > + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); > + int mode = UNPACK_MODE(this_attr->address); > + int address = UNPACK_CHAN(this_attr->address); I'd personally use address as an index into an array of static const struct. Leads to slightly easier to read and more extensible code. Minor point though. > + int o_conf, c_conf; > + long val; > + int *cont_param = NULL; > + int ret = -EINVAL; > + > + if (mode == INA3221_OPERATING_MODE) { > + val = ((*buf == 'c') || (*buf == 'C')) ? 1 : 0; > + } else { > + if (kstrtol(buf, 10, &val) < 0) > + return -EINVAL; > + } > + > + mutex_lock(&chip->state_lock); > + > + o_conf = chip->oneshot_config; > + c_conf = chip->continuous_config; > + > + switch (mode) { > + case INA3221_OPERATING_MODE: > + if (chip->continuous_mode == val) > + break; > + > + ret = ina3221_set_operating_mode(chip, val); > + break; > + > + case INA3221_OVERSAMPLING_RATIO: > + if (address == INA3221_CHANNEL_ONESHOT) { > + ret = ina3221_set_average(chip, val, &o_conf); > + if (!ret) > + chip->pdata->oneshot_avg_sample = val; > + } else { > + ret = ina3221_set_average(chip, val, &c_conf); > + if (!ret) > + cont_param = &chip->pdata->cont_avg_sample; > + } > + break; > + > + case INA3221_VBUS_CONV_TIME: > + if (address == INA3221_CHANNEL_ONESHOT) { > + ret = ina3221_set_int_time_vbus(chip, val, &o_conf); > + if (!ret) > + chip->pdata->oneshot_vbus_conv_time = val; > + } else { > + ret = ina3221_set_int_time_vbus(chip, val, &c_conf); > + if (!ret) > + cont_param = &chip->pdata->cont_vbus_conv_time; > + } > + break; > + > + case INA3221_VSHUNT_CONV_TIME: > + if (address == INA3221_CHANNEL_ONESHOT) { > + ret = ina3221_set_int_time_vshunt(chip, val, &o_conf); > + if (!ret) > + chip->pdata->oneshot_shunt_conv_time = val; > + } else { > + ret = ina3221_set_int_time_vshunt(chip, val, &c_conf); > + if (!ret) > + cont_param = &chip->pdata->cont_shunt_conv_time; > + } > + break; > + > + default: > + break; > + } > + > + if (ret < 0) > + goto exit; > + > + chip->oneshot_config = o_conf; > + if (chip->continuous_mode && chip->continuous_config != c_conf) { > + ret = regmap_write(chip->rmap, INA3221_CONFIG, c_conf); > + if (ret < 0) > + goto exit; > + } > + chip->continuous_config = c_conf; > + if (cont_param) > + *cont_param = val; > + > +exit: > + mutex_unlock(&chip->state_lock); > + > + return len; > +} > + > +static IIO_DEVICE_ATTR(rail_name_0, S_IRUGO | S_IWUSR, > + ina3221_show_channel, ina3221_set_channel, > + PACK_MODE_CHAN(INA3221_CHANNEL_NAME, 0)); > + > +static IIO_DEVICE_ATTR(rail_name_1, S_IRUGO | S_IWUSR, > + ina3221_show_channel, ina3221_set_channel, > + PACK_MODE_CHAN(INA3221_CHANNEL_NAME, 1)); > + > +static IIO_DEVICE_ATTR(rail_name_2, S_IRUGO | S_IWUSR, > + ina3221_show_channel, ina3221_set_channel, > + PACK_MODE_CHAN(INA3221_CHANNEL_NAME, 2)); > + > +static IIO_DEVICE_ATTR(operating_mode, S_IRUGO | S_IWUSR, > + ina3221_show_channel, ina3221_set_channel, > + PACK_MODE_CHAN(INA3221_OPERATING_MODE, 0)); > + > +static IIO_DEVICE_ATTR(oneshot_oversampling_ratio, S_IRUGO | S_IWUSR, > + ina3221_show_channel, ina3221_set_channel, > + PACK_MODE_CHAN(INA3221_OVERSAMPLING_RATIO, > + INA3221_CHANNEL_ONESHOT)); > + > +static IIO_DEVICE_ATTR(continuous_oversampling_ratio, S_IRUGO | S_IWUSR, > + ina3221_show_channel, ina3221_set_channel, > + PACK_MODE_CHAN(INA3221_OVERSAMPLING_RATIO, > + INA3221_CHANNEL_CONTINUOUS)); > + > +static IIO_DEVICE_ATTR(oneshot_vbus_conv_time, S_IRUGO | S_IWUSR, > + ina3221_show_channel, ina3221_set_channel, > + PACK_MODE_CHAN(INA3221_VBUS_CONV_TIME, > + INA3221_CHANNEL_ONESHOT)); > + > +static IIO_DEVICE_ATTR(continuous_vbus_conv_time, S_IRUGO | S_IWUSR, > + ina3221_show_channel, ina3221_set_channel, > + PACK_MODE_CHAN(INA3221_VBUS_CONV_TIME, > + INA3221_CHANNEL_CONTINUOUS)); > + > +static IIO_DEVICE_ATTR(oneshot_vshunt_conv_time, S_IRUGO | S_IWUSR, > + ina3221_show_channel, ina3221_set_channel, > + PACK_MODE_CHAN(INA3221_VSHUNT_CONV_TIME, > + INA3221_CHANNEL_ONESHOT)); > + > +static IIO_DEVICE_ATTR(continuous_vshunt_conv_time, S_IRUGO | S_IWUSR, > + ina3221_show_channel, ina3221_set_channel, > + PACK_MODE_CHAN(INA3221_VSHUNT_CONV_TIME, > + INA3221_CHANNEL_CONTINUOUS)); > + > +static struct attribute *ina3221_attributes[] = { > + &iio_dev_attr_rail_name_0.dev_attr.attr, > + &iio_dev_attr_rail_name_1.dev_attr.attr, > + &iio_dev_attr_rail_name_2.dev_attr.attr, > + &iio_dev_attr_oneshot_oversampling_ratio.dev_attr.attr, > + &iio_dev_attr_continuous_oversampling_ratio.dev_attr.attr, > + &iio_dev_attr_oneshot_vbus_conv_time.dev_attr.attr, > + &iio_dev_attr_continuous_vbus_conv_time.dev_attr.attr, > + &iio_dev_attr_oneshot_vshunt_conv_time.dev_attr.attr, > + &iio_dev_attr_continuous_vshunt_conv_time.dev_attr.attr, > + &iio_dev_attr_operating_mode.dev_attr.attr, Was about to complain about docs then noticed patch 3. There's a lot here and it's mostly non standard.. hmm. > + NULL, > +}; > + > +static const struct attribute_group ina3221_groups = { > + .attrs = ina3221_attributes, > +}; > + > +#define channel_type(_type, _add, _channel, _name) { \ > + .type = _type, \ > + .indexed = 1, \ > + .address = _add, \ > + .channel = _channel, \ > + .extend_name = _name, \ > + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) \ > +} > + > +#define channel_spec(ch) \ > + channel_type(IIO_VOLTAGE, 0, ch, NULL), \ > + channel_type(IIO_CURRENT, INA3221_MEASURED_VALUE, ch, NULL), \ > + channel_type(IIO_CURRENT, INA3221_CRIT_CURRENT_LIMIT, ch, "warning"), \ > + channel_type(IIO_CURRENT, INA3221_WARN_CURRENT_LIMIT, ch, "critical"), \ These aren't channels that I can see but rather events on a given channel. There's an issue here as well in that IIO doesn't currently support two events of the same type on a single channel - our event codes have no way of distguishing between them. This needs fixing but we haven't done it yet. Also these particular events do make this seem rather more or a power monitoring chip than we'd normally expect to see in IIO - hence the need for that justification in the patch description ;) > + channel_type(IIO_POWER, 0, ch, NULL) > + > +static const struct iio_chan_spec ina3221_channels_spec[] = { > + channel_spec(0), > + channel_spec(1), > + channel_spec(2), > +}; > + > +static const struct iio_info ina3221_info = { > + .driver_module = THIS_MODULE, > + .attrs = &ina3221_groups, > + .read_raw = ina3221_read_raw, > + .write_raw = ina3221_write_raw, > +}; > + > +static int ina3221_process_pdata(struct ina3221_chip_info *chip, > + struct ina3221_platform_data *pdata) > +{ > + unsigned int o_conf; > + unsigned int c_conf; > + int ret; > + > + o_conf = 0x7 << 12; > + c_conf = pdata->active_channel << 12; > + > + o_conf |= (chip->pdata->enable_power) ? 0x3 : 0x2; > + c_conf |= (chip->pdata->enable_power) ? 0x7 : 0x6; > + > + ret = ina3221_set_average(chip, pdata->oneshot_avg_sample, &o_conf); > + if (ret < 0) > + return ret; > + ret = ina3221_set_average(chip, pdata->cont_avg_sample, &c_conf); > + if (ret < 0) > + return ret; > + > + ret = ina3221_set_int_time_vbus(chip, pdata->oneshot_vbus_conv_time, > + &o_conf); > + if (ret < 0) > + return ret; > + > + ret = ina3221_set_int_time_vbus(chip, pdata->cont_vbus_conv_time, > + &c_conf); > + if (ret < 0) > + return ret; > + > + ret = ina3221_set_int_time_vshunt(chip, pdata->oneshot_shunt_conv_time, > + &o_conf); > + if (ret < 0) > + return ret; > + > + ret = ina3221_set_int_time_vshunt(chip, pdata->cont_shunt_conv_time, > + &c_conf); > + if (ret < 0) > + return ret; > + > + chip->oneshot_config = o_conf; > + chip->continuous_config = c_conf; > + return 0; > +} > + There is a lot of moderately controversial stuff in here - I'll reply to the binding doc instead of here on these though. > +static int ina3221_get_platform_data_dt(struct ina3221_chip_info *chip) > +{ > + struct device *dev = chip->dev; > + struct device_node *np = dev->of_node; > + struct device_node *np_chan; > + struct ina3221_platform_data *pdata; > + struct ina3221_channel_data *cdata; > + char channel_name[20]; > + u32 value; > + int curr_ua; > + int id; > + int ret; > + > + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); > + if (!pdata) > + return -ENOMEM; > + > + chip->pdata = pdata; > + > + ret = of_property_read_u32(np, "one-shot-average-sample", &value); > + if (!ret) > + pdata->oneshot_avg_sample = value; > + else > + pdata->oneshot_avg_sample = INA3221_CONFIG_AVG_SAMPLE_DEFAULT; > + > + ret = of_property_read_u32(np, "one-shot-vbus-conv-time-us", &value); > + if (!ret) > + pdata->oneshot_vbus_conv_time = value; > + else > + pdata->oneshot_vbus_conv_time = > + INA3221_CONFIG_VBUS_CONV_TIME_DEFAULT; > + > + ret = of_property_read_u32(np, "one-shot-shunt-conv-time-us", > + &value); > + if (!ret) > + pdata->oneshot_shunt_conv_time = value; > + else > + pdata->oneshot_shunt_conv_time = > + INA3221_CONFIG_SHUNT_CONV_TIME_DEFAULT; > + > + ret = of_property_read_u32(np, "continuous-average-sample", &value); > + if (!ret) > + pdata->cont_avg_sample = value; > + else > + pdata->cont_avg_sample = INA3221_CONFIG_AVG_SAMPLE_DEFAULT; > + > + ret = of_property_read_u32(np, "continuous-vbus-conv-time-us", &value); > + if (!ret) > + pdata->cont_vbus_conv_time = value; > + else > + pdata->cont_vbus_conv_time = > + INA3221_CONFIG_VBUS_CONV_TIME_DEFAULT; > + > + ret = of_property_read_u32(np, "continuous-shunt-conv-time-us", &value); > + if (!ret) > + pdata->cont_shunt_conv_time = value; > + else > + pdata->cont_shunt_conv_time = > + INA3221_CONFIG_SHUNT_CONV_TIME_DEFAULT; > + > + pdata->enable_power = of_property_read_bool(np, > + "enable-power-monitor"); > + pdata->continuous_mode = of_property_read_bool(np, > + "enable-continuous-mode"); > + pdata->warn_alert = of_property_read_bool(np, "enable-warning-alert"); > + pdata->crit_alert = of_property_read_bool(np, "enable-critical-alert"); > + > + for (id = 0; id < INA3221_NUMBER_OF_CHANNELS; ++id) { > + sprintf(channel_name, "channel%d", id); > + np_chan = of_get_child_by_name(np, channel_name); > + if (!np_chan) > + continue; > + > + cdata = &pdata->channel_data[id]; > + > + ret = of_property_read_string(np_chan, "label", &cdata->name); > + if (ret < 0) { > + dev_err(dev, "Channel %s does not have label\n", > + np_chan->full_name); > + continue; > + } > + > + ret = of_property_read_u32(np_chan, > + "warning-current-limit-microamp", > + &value); > + cdata->warn_limits = (!ret) ? value : ret; > + > + ret = of_property_read_u32(np_chan, > + "critical-current-limit-microamp", > + &value); > + cdata->crit_limits = (!ret) ? value : ret; > + > + ret = of_property_read_u32(np_chan, "shunt-resistor-mohm", > + &value); > + if (!ret) > + cdata->shunt_resistance = value; > + > + pdata->active_channel |= BIT(INA3221_NUMBER_OF_CHANNELS - id - > + 1); > + > + if (cdata->crit_limits < 0) { > + ret = ina3221_read_default_channel_critical(chip, id, > + &curr_ua); > + if (ret < 0) > + return ret; > + cdata->crit_limits = curr_ua; > + } else { > + ret = ina3221_set_channel_critical(chip, id, > + cdata->crit_limits); > + if (ret < 0) > + return ret; > + } > + > + if (cdata->warn_limits < 0) { > + ret = ina3221_read_default_channel_warning(chip, id, > + &curr_ua); > + if (ret < 0) > + return ret; > + cdata->warn_limits = curr_ua; > + } else { > + ret = ina3221_set_channel_warning(chip, id, > + cdata->warn_limits); > + if (ret < 0) > + return ret; > + } > + } > + > + if (!pdata->active_channel) > + return -EINVAL; > + > + ret = ina3221_process_pdata(chip, pdata); > + if (ret < 0) { > + dev_err(chip->dev, "Failed to process platform data: %d\n", > + ret); > + return ret; > + } > + > + return 0; > +} > + > +static int ina3221_do_reset(struct ina3221_chip_info *chip) > +{ > + int ret; > + > + ret = regmap_update_bits(chip->rmap, INA3221_CONFIG, > + INA3221_CONFIG_RESET_MASK, > + INA3221_CONFIG_RESET_EN); > + if (ret < 0) { > + dev_err(chip->dev, "Failed to reset device: %d\n", ret); > + return ret; > + } > + > + ret = regmap_update_bits(chip->rmap, INA3221_CONFIG, > + INA3221_CONFIG_RESET_MASK, 0); > + if (ret < 0) { > + dev_err(chip->dev, "Failed to reset device: %d\n", ret); > + return ret; > + } > + > + return 0; > +} > + > +static int ina3221_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct ina3221_chip_info *chip; > + struct iio_dev *indio_dev; > + int ret; > + > + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); > + if (!indio_dev) > + return -ENOMEM; > + > + chip = iio_priv(indio_dev); > + > + chip->rmap = devm_regmap_init_i2c(client, &ina3221_regmap_config); > + if (IS_ERR(chip->rmap)) { > + ret = PTR_ERR(chip->rmap); > + dev_err(&client->dev, "Failed to initialise regmap: %d\n", ret); > + return ret; > + } > + > + chip->dev = &client->dev; > + mutex_init(&chip->state_lock); > + i2c_set_clientdata(client, indio_dev); > + > + ret = ina3221_do_reset(chip); > + if (ret < 0) > + return ret; > + > + ret = ina3221_get_platform_data_dt(chip); > + if (ret < 0) { > + dev_err(&client->dev, "Failed to get platform data: %d\n", ret); > + return ret; > + } > + > + ret = ina3221_set_operating_mode(chip, chip->pdata->continuous_mode); > + if (ret < 0) > + return ret; > + > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->dev.parent = &client->dev; > + indio_dev->channels = ina3221_channels_spec; > + indio_dev->num_channels = ARRAY_SIZE(ina3221_channels_spec); > + indio_dev->name = id->name; > + indio_dev->info = &ina3221_info; > + > + return iio_device_register(indio_dev); > +} > + > +static int ina3221_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + struct ina3221_chip_info *chip = iio_priv(indio_dev); > + > + iio_device_unregister(indio_dev); > + > + /* Powerdown */ > + return regmap_update_bits(chip->rmap, INA3221_CONFIG, > + INA3221_CONFIG_MODE_MASK, > + INA3221_CONFIG_MODE_POWER_DOWN); > +} > + > +static const struct i2c_device_id ina3221_id[] = { > + {.name = "ina3221"}, > + {}, > +}; > +MODULE_DEVICE_TABLE(i2c, ina3221_id); > + > +static struct i2c_driver ina3221_driver = { > + .driver = { > + .name = "ina3221", > + }, > + .probe = ina3221_probe, > + .remove = ina3221_remove, > + .id_table = ina3221_id, > +}; > +module_i2c_driver(ina3221_driver); > + > +MODULE_DESCRIPTION("Texas Instruments INA3221 ADC driver"); > +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); > +MODULE_LICENSE("GPL v2"); > ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/3] iio: adc: ina3221: Add support for IIO ADC driver for TI INA3221 2016-06-03 10:06 ` [PATCH 2/3] iio: adc: ina3221: Add support for IIO ADC driver for TI INA3221 Jonathan Cameron @ 2016-06-03 10:16 ` Jonathan Cameron 2016-06-03 11:31 ` Laxman Dewangan 2016-06-03 11:26 ` Laxman Dewangan 2016-06-03 13:29 ` Guenter Roeck 2 siblings, 1 reply; 14+ messages in thread From: Jonathan Cameron @ 2016-06-03 10:16 UTC (permalink / raw) To: Laxman Dewangan, robh+dt, corbet, lars Cc: devicetree, linux-kernel, linux-doc, linux-iio, linux-hwmon, Jean Delvare, Guenter Roeck On 03/06/16 11:06, Jonathan Cameron wrote: > On 01/06/16 13:34, Laxman Dewangan wrote: >> The INA3221 is a three-channel, high-side current and bus voltage monitor >> with an I2C interface from Texas Instruments. The INA3221 monitors both >> shunt voltage drops and bus supply voltages in addition to having >> programmable conversion times and averaging modes for these signals. >> The INA3221 offers both critical and warning alerts to detect multiple >> programmable out-of-range conditions for each channel. >> >> Add support for INA3221 SW driver via IIO ADC interface. The device is >> register as iio-device and provides interface for voltage/current and power >> monitor. Also provide interface for setting oneshot/continuous mode and >> critical/warning threshold for the shunt voltage drop. >> >> Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> > Hi Laxman, > > As ever with any driver lying on the border of IIO and hwmon, please include > a short justification of why you need an IIO driver and also cc the > hwmon list + maintainers. (cc'd on this reply). > > I simply won't take a driver where the hwmon maintainers aren't happy. > As it stands I'm not seeing obvious reasons in the code for why this > should be an IIO device. > > Funily enough I know this datasheet a little as was evaluating > it for use on some boards at the day job a week or so ago. > > Various comments inline. Major points are: > * Don't use 'fake' channels to control events. If the events infrastructure > doesn't handle your events, then fix that rather than working around it. > * There is a lot of ABI in here concerned with oneshot vs continuous. > This seems to me to be more than it should be. We wouldn't expect to > see stuff changing as a result of switching between these modes other > than wrt to when the data shows up. So I'd expect to not see this > directly exposed at all - but rather sit in oneshot unless either: > 1) Buffered mode is running (not currently supported) > 2) Alerts are on - which I think requires it to be in continuous mode. > > Other question to my mind is whether we should be reporting vshunt or > (using device tree to pass resistance) current. > > Code looks good, bu these more fundamental bits need sorting. Another minor point - why do the power calculations in driver? no hardware support for it, so why not just leave it to userspace? > > Thanks, > > Jonathan >> --- >> drivers/iio/adc/Kconfig | 12 + >> drivers/iio/adc/Makefile | 1 + >> drivers/iio/adc/ina3221.c | 1175 +++++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 1188 insertions(+) >> create mode 100644 drivers/iio/adc/ina3221.c >> >> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig >> index 25378c5..65f3c27 100644 >> --- a/drivers/iio/adc/Kconfig >> +++ b/drivers/iio/adc/Kconfig >> @@ -223,6 +223,18 @@ config INA2XX_ADC >> Say yes here to build support for TI INA2xx family of Power Monitors. >> This driver is mutually exclusive with the HWMON version. >> >> +config INA3221 >> + tristate "TI INA3221 3-Channel Shunt and Bus Voltage Monitor" >> + depends on I2C >> + select REGMAP_I2C >> + help >> + INA3221 is Triple-Channel, High-Side Measurement, Shunt and Bus >> + Voltage Monitor device from TI. This driver support the reading >> + of all channel's voltage/current and power via IIO interface. >> + Say yes here to build support for TI INA3221. To compile this >> + driver as a module, choose M here: the module will be called >> + ina3221. >> + >> config IMX7D_ADC >> tristate "IMX7D ADC driver" >> depends on ARCH_MXC || COMPILE_TEST >> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile >> index 38638d4..c24f455 100644 >> --- a/drivers/iio/adc/Makefile >> +++ b/drivers/iio/adc/Makefile >> @@ -24,6 +24,7 @@ obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o >> obj-$(CONFIG_HI8435) += hi8435.o >> obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o >> obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o >> +obj-$(CONFIG_INA3221) += ina3221.o >> obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o >> obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o >> obj-$(CONFIG_MAX1027) += max1027.o >> diff --git a/drivers/iio/adc/ina3221.c b/drivers/iio/adc/ina3221.c >> new file mode 100644 >> index 0000000..a17f688 >> --- /dev/null >> +++ b/drivers/iio/adc/ina3221.c >> @@ -0,0 +1,1175 @@ >> +/* >> + * IIO driver for INA3221 >> + * >> + * Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved. >> + * >> + * This program is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License version 2 as >> + * published by the Free Software Foundation. >> + */ >> + >> +#include <linux/delay.h> >> +#include <linux/i2c.h> >> +#include <linux/iio/kfifo_buf.h> >> +#include <linux/iio/sysfs.h> >> +#include <linux/module.h> >> +#include <linux/regmap.h> >> +#include <linux/util_macros.h> >> + >> +/* INA3221 registers definition */ >> +#define INA3221_CONFIG 0x00 >> +#define INA3221_SHUNT_VOL_CHAN1 0x01 >> +#define INA3221_BUS_VOL_CHAN1 0x02 >> +#define INA3221_SHUNT_VOL_CHAN2 0x03 >> +#define INA3221_BUS_VOL_CHAN2 0x04 >> +#define INA3221_SHUNT_VOL_CHAN3 0x05 >> +#define INA3221_BUS_VOL_CHAN3 0x06 >> +#define INA3221_CRIT_CHAN1 0x07 >> +#define INA3221_WARN_CHAN1 0x08 >> +#define INA3221_CRIT_CHAN2 0x09 >> +#define INA3221_WARN_CHAN2 0x0A >> +#define INA3221_CRIT_CHAN3 0x0B >> +#define INA3221_WARN_CHAN3 0x0C >> +#define INA3221_MASK_ENABLE 0x0F >> +#define INA3221_POWER_VALID_UPPER_LIMIT 0x10 >> +#define INA3221_POWER_VALID_LOWER_LIMIT 0x11 >> +#define INA3221_MAN_ID 0xFE >> +#define INA3221_DEV_ID 0xFF >> + >> +#define INA3221_CONFIG_RESET_MASK BIT(15) >> +#define INA3221_CONFIG_RESET_EN BIT(15) >> + >> +#define INA3221_CONFIG_MODE_MASK GENMASK(2, 0) >> +#define INA3221_CONFIG_MODE_POWER_DOWN 0 >> + >> +#define INA3221_CONFIG_AVG_MASK GENMASK(11, 9) >> +#define INA3221_CONFIG_AVG(val) ((val) << 9) >> + >> +#define INA3221_CONFIG_VBUSCT_MASK GENMASK(8, 6) >> +#define INA3221_CONFIG_VBUSCT(val) ((val) << 6) >> + >> +#define INA3221_CONFIG_SHUNTCT_MASK GENMASK(5, 3) >> +#define INA3221_CONFIG_SHUNTCT(val) ((val) << 3) >> + >> +#define INA3221_REG_MASK_WEN BIT(11) >> +#define INA3221_REG_MASK_CEN BIT(10) >> +#define INA3221_REG_MASK_CVRF BIT(0) >> + >> +#define PACK_MODE_CHAN(mode, chan) ((mode) | ((chan) << 8)) >> +#define UNPACK_MODE(address) ((address) & 0xFF) >> +#define UNPACK_CHAN(address) (((address) >> 8) & 0xFF) >> + >> +#define INA3221_NUMBER_OF_CHANNELS 3 >> +#define INA3221_MAX_CONVERSION_TRIALS 10 >> +#define INA3221_CONFIG_AVG_SAMPLE_DEFAULT 4 >> +#define INA3221_CONFIG_VBUS_CONV_TIME_DEFAULT 150 >> +#define INA3221_CONFIG_SHUNT_CONV_TIME_DEFAULT 150 >> + >> +#define INA3221_SHUNT_VOL(i) (INA3221_SHUNT_VOL_CHAN1 + (i) * 2) >> +#define INA3221_BUS_VOL(i) (INA3221_BUS_VOL_CHAN1 + (i) * 2) >> +#define INA3221_CRIT(i) (INA3221_CRIT_CHAN1 + (i) * 2) >> +#define INA3221_WARN(i) (INA3221_WARN_CHAN1 + (i) * 2) >> + >> +static const struct regmap_range ina3221_readable_ranges[] = { >> + regmap_reg_range(INA3221_CONFIG, INA3221_POWER_VALID_LOWER_LIMIT), >> + regmap_reg_range(INA3221_MAN_ID, INA3221_DEV_ID), >> +}; >> + >> +static const struct regmap_access_table ina3221_readable_table = { >> + .yes_ranges = ina3221_readable_ranges, >> + .n_yes_ranges = ARRAY_SIZE(ina3221_readable_ranges), >> +}; >> + >> +static const struct regmap_range ina3221_no_writable_ranges[] = { >> + regmap_reg_range(INA3221_SHUNT_VOL_CHAN1, INA3221_BUS_VOL_CHAN3), >> +}; >> + >> +static const struct regmap_access_table ina3221_writable_table = { >> + .no_ranges = ina3221_no_writable_ranges, >> + .n_no_ranges = ARRAY_SIZE(ina3221_no_writable_ranges), >> +}; >> + >> +static const struct regmap_range ina3221_no_volatile_ranges[] = { >> + regmap_reg_range(INA3221_CONFIG, INA3221_CONFIG), >> + regmap_reg_range(INA3221_CRIT_CHAN1, INA3221_WARN_CHAN3), >> + regmap_reg_range(INA3221_POWER_VALID_UPPER_LIMIT, >> + INA3221_POWER_VALID_LOWER_LIMIT), >> + regmap_reg_range(INA3221_MAN_ID, INA3221_DEV_ID), >> +}; >> + >> +static const struct regmap_access_table ina3221_volatile_table = { >> + .no_ranges = ina3221_no_volatile_ranges, >> + .n_no_ranges = ARRAY_SIZE(ina3221_no_volatile_ranges), >> +}; >> + >> +static const struct regmap_config ina3221_regmap_config = { >> + .reg_bits = 8, >> + .val_bits = 16, >> + .max_register = INA3221_DEV_ID + 1, >> + .rd_table = &ina3221_readable_table, >> + .wr_table = &ina3221_writable_table, >> + .volatile_table = &ina3221_volatile_table, >> +}; >> + >> +struct ina3221_channel_data { >> + const char *name; >> + int warn_limits; >> + int crit_limits; >> + int shunt_resistance; >> +}; >> + >> +struct ina3221_platform_data { >> + struct ina3221_channel_data channel_data[INA3221_NUMBER_OF_CHANNELS]; >> + bool enable_power; >> + int oneshot_avg_sample; >> + int oneshot_vbus_conv_time; >> + int oneshot_shunt_conv_time; >> + int cont_avg_sample; >> + int cont_vbus_conv_time; >> + int cont_shunt_conv_time; >> + int continuous_mode; >> + bool warn_alert; >> + bool crit_alert; >> + int active_channel; >> +}; >> + >> +struct ina3221_chip_info { >> + struct device *dev; >> + struct regmap *rmap; >> + int oneshot_config; >> + int continuous_config; >> + int continuous_mode; >> + struct mutex state_lock; >> + struct ina3221_platform_data *pdata; >> +}; >> + >> +enum ina3221_address { >> + INA3221_CHANNEL_NAME, >> + INA3221_CRIT_CURRENT_LIMIT, >> + INA3221_WARN_CURRENT_LIMIT, >> + INA3221_MEASURED_VALUE, >> + INA3221_OPERATING_MODE, >> + INA3221_OVERSAMPLING_RATIO, >> + INA3221_VBUS_CONV_TIME, >> + INA3221_VSHUNT_CONV_TIME, >> + INA3221_CHANNEL_ONESHOT, >> + INA3221_CHANNEL_CONTINUOUS, >> +}; >> + >> +static inline int shuntv_register_to_uv(u16 reg) >> +{ >> + int ret = (s16)reg; >> + >> + return (ret >> 3) * 40; >> +} >> + >> +static inline u16 uv_to_shuntv_register(s32 uv) >> +{ >> + return (u16)(uv / 5); >> +} >> + >> +static inline int busv_register_to_mv(u16 reg) >> +{ >> + int ret = (s16)reg; >> + >> + return (ret >> 3) * 8; >> +} >> + >> +/* convert shunt voltage register value to current (in mA) */ >> +static int shuntv_register_to_ma(u16 reg, int resistance) >> +{ >> + int uv, ma; >> + >> + uv = (s16)reg; >> + uv = ((uv >> 3) * 40); /* LSB (4th bit) is 40uV */ >> + /* >> + * calculate uv/resistance with rounding knowing that C99 truncates >> + * towards zero >> + */ >> + if (uv > 0) >> + ma = ((uv * 2 / resistance) + 1) / 2; >> + else >> + ma = ((uv * 2 / resistance) - 1) / 2; >> + return ma; >> +} >> + >> +static int ina3221_get_closest_index(const int *list, int n_list, >> + int val) >> +{ >> + if (val > list[n_list - 1] || (val < list[0])) >> + return -EINVAL; >> + >> + return find_closest(val, list, n_list); >> +} >> + >> +/* >> + * Available averaging rates for INA3221. The indices correspond with >> + * the bit values expected by the chip (according to the INA3221 datasheet, >> + * table 3 AVG bit settings, found at >> + */ >> +static const int ina3221_avg_tab[] = { 1, 4, 16, 64, 128, 256, 512, 1024}; >> + >> +static int ina3221_set_average(struct ina3221_chip_info *chip, unsigned int val, >> + unsigned int *config) >> +{ >> + int bits; >> + >> + bits = ina3221_get_closest_index(ina3221_avg_tab, >> + ARRAY_SIZE(ina3221_avg_tab), val); >> + if (bits < 0) >> + return bits; >> + >> + *config &= ~INA3221_CONFIG_AVG_MASK; >> + *config |= INA3221_CONFIG_AVG(bits) & INA3221_CONFIG_AVG_MASK; >> + >> + return 0; >> +} >> + >> +/* Conversion times in uS */ >> +static const int ina3221_conv_time_tab[] = { 140, 204, 332, 588, 1100, >> + 2116, 4156, 8244}; >> + >> +static int ina3221_set_int_time_vbus(struct ina3221_chip_info *chip, >> + unsigned int val_us, unsigned int *config) >> +{ >> + int bits; >> + >> + bits = ina3221_get_closest_index(ina3221_conv_time_tab, >> + ARRAY_SIZE(ina3221_conv_time_tab), >> + val_us); >> + if (bits < 0) >> + return bits; >> + >> + *config &= ~INA3221_CONFIG_VBUSCT_MASK; >> + *config |= INA3221_CONFIG_VBUSCT(bits) & INA3221_CONFIG_VBUSCT_MASK; >> + >> + return 0; >> +} >> + >> +static int ina3221_set_int_time_vshunt(struct ina3221_chip_info *chip, >> + unsigned int val_us, >> + unsigned int *config) >> +{ >> + int bits; >> + >> + bits = ina3221_get_closest_index(ina3221_conv_time_tab, >> + ARRAY_SIZE(ina3221_conv_time_tab), >> + val_us); >> + if (bits < 0) >> + return bits; >> + >> + *config &= ~INA3221_CONFIG_SHUNTCT_MASK; >> + *config |= INA3221_CONFIG_SHUNTCT(bits) & INA3221_CONFIG_SHUNTCT_MASK; >> + >> + return 0; >> +} >> + >> +static int ina3221_do_one_shot_conversion(struct ina3221_chip_info *chip, >> + int chan, u16 *vbus, u16 *vsh) >> +{ >> + unsigned int value; >> + int trials = 0; >> + int ret; >> + int conv_time; >> + >> + conv_time = max(chip->pdata->oneshot_vbus_conv_time, >> + chip->pdata->oneshot_shunt_conv_time); >> + >> + ret = regmap_write(chip->rmap, INA3221_CONFIG, chip->oneshot_config); >> + if (ret < 0) >> + return 0; >> + >> + /* Read conversion status */ >> + do { >> + ret = regmap_read(chip->rmap, INA3221_MASK_ENABLE, &value); >> + if (ret < 0) { >> + dev_err(chip->dev, >> + "Failed to read conversion status: %d\n", ret); >> + return ret; >> + } >> + >> + if (value & INA3221_REG_MASK_CVRF) >> + break; >> + usleep_range(conv_time, conv_time * 2); >> + } while (++trials < INA3221_MAX_CONVERSION_TRIALS); >> + >> + if (trials == INA3221_MAX_CONVERSION_TRIALS) { >> + dev_err(chip->dev, >> + "Conversion not completed for maximum trials\n"); >> + return -EAGAIN; >> + } >> + >> + if (vsh) { >> + ret = regmap_read(chip->rmap, INA3221_SHUNT_VOL(chan), &value); >> + if (ret < 0) >> + return ret; >> + *vsh = (u16)value; >> + } >> + >> + if (vbus) { >> + ret = regmap_read(chip->rmap, INA3221_BUS_VOL(chan), &value); >> + if (ret < 0) >> + return ret; >> + *vbus = (u16)value; >> + } >> + >> + ret = regmap_write(chip->rmap, INA3221_CONFIG, 0); >> + if (ret < 0) >> + return ret; >> + >> + return 0; >> +} >> + >> +static int ina3221_read_continuous_conversion(struct ina3221_chip_info *chip, >> + int chan, u16 *vbus, u16 *vsh) >> +{ >> + unsigned int value; >> + int ret; >> + >> + if (vsh) { >> + ret = regmap_read(chip->rmap, INA3221_SHUNT_VOL(chan), &value); >> + if (ret < 0) >> + return ret; >> + *vsh = (u16)value; >> + } >> + >> + if (vbus) { >> + ret = regmap_read(chip->rmap, INA3221_BUS_VOL(chan), &value); >> + if (ret < 0) >> + return ret; >> + *vbus = (u16)value; >> + } >> + >> + return ret; >> +} >> + >> +static int ina3221_read_vbus_vshunt(struct ina3221_chip_info *chip, >> + int ch, u16 *vbus, u16 *vsh) >> +{ >> + if (chip->continuous_mode) >> + return ina3221_read_continuous_conversion(chip, ch, vbus, vsh); >> + >> + return ina3221_do_one_shot_conversion(chip, ch, vbus, vsh); >> +} >> + >> +static int ina3221_get_channel_voltage(struct ina3221_chip_info *chip, >> + int chan, int *voltage_uv) >> +{ >> + u16 vbus; >> + int ret; >> + >> + ret = ina3221_read_vbus_vshunt(chip, chan, &vbus, NULL); >> + if (ret < 0) >> + return ret; >> + >> + *voltage_uv = busv_register_to_mv(vbus) * 1000; >> + >> + return 0; >> +} >> + >> +static int ina3221_get_channel_current(struct ina3221_chip_info *chip, >> + int chan, int *current_ua) >> +{ >> + int shunt_res = chip->pdata->channel_data[chan].shunt_resistance; >> + u16 vsh; >> + int ret; >> + >> + ret = ina3221_do_one_shot_conversion(chip, chan, NULL, &vsh); >> + if (ret < 0) >> + return ret; >> + >> + *current_ua = shuntv_register_to_ma(vsh, shunt_res) * 1000; >> + >> + return 0; >> +} >> + >> +static int ina3221_get_channel_power(struct ina3221_chip_info *chip, >> + int chan, int *power_uw) >> +{ >> + int shunt_res = chip->pdata->channel_data[chan].shunt_resistance; >> + u16 vbus, vsh; >> + int ret; >> + int current_ma, voltage_mv; >> + >> + ret = ina3221_do_one_shot_conversion(chip, chan, &vbus, &vsh); >> + if (ret < 0) >> + return ret; >> + >> + current_ma = shuntv_register_to_ma(vsh, shunt_res); >> + voltage_mv = busv_register_to_mv(vbus); >> + *power_uw = (voltage_mv * current_ma); >> + >> + return 0; >> +} >> + >> +static int ina3221_get_channel_critical(struct ina3221_chip_info *chip, >> + int chan, int *curr_limit_ua) >> +{ >> + *curr_limit_ua = chip->pdata->channel_data[chan].crit_limits; >> + >> + return 0; >> +} >> + >> +static int ina3221_set_channel_critical(struct ina3221_chip_info *chip, >> + int chan, int crit_limit_ua) >> +{ >> + int s_res = chip->pdata->channel_data[chan].shunt_resistance; >> + int shunt_volt_limit; >> + int crit_limit = crit_limit_ua / 1000; >> + int ret; >> + >> + if (crit_limit < 0) >> + return 0; >> + >> + shunt_volt_limit = crit_limit * s_res; >> + shunt_volt_limit = uv_to_shuntv_register(shunt_volt_limit); >> + >> + ret = regmap_write(chip->rmap, INA3221_CRIT(chan), shunt_volt_limit); >> + if (ret < 0) { >> + dev_err(chip->dev, "Failed to write critical reg 0x%02x: %d\n", >> + INA3221_CRIT(chan), ret); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int ina3221_read_default_channel_critical(struct ina3221_chip_info *chip, >> + int chan, int *shunt_curr_ua) >> +{ >> + int shunt_res = chip->pdata->channel_data[chan].shunt_resistance; >> + int shunt_volt; >> + unsigned int value; >> + int ret; >> + >> + if (shunt_res <= 0) { >> + dev_err(chip->dev, "Channel %d have invalid shunt resistor\n", >> + chan); >> + return -EINVAL; >> + } >> + >> + ret = regmap_read(chip->rmap, INA3221_CRIT(chan), &value); >> + if (ret < 0) { >> + dev_err(chip->dev, "Failed to read critical reg 0x%02x: %d\n", >> + INA3221_CRIT(chan), ret); >> + return ret; >> + } >> + shunt_volt = shuntv_register_to_uv((u16)value); >> + *shunt_curr_ua = (shunt_volt / shunt_res) * 1000; >> + >> + return 0; >> +} >> + >> +static int ina3221_get_channel_warning(struct ina3221_chip_info *chip, >> + int chan, int *curr_limit_ua) >> +{ >> + *curr_limit_ua = chip->pdata->channel_data[chan].warn_limits; >> + >> + return 0; >> +} >> + >> +static int ina3221_set_channel_warning(struct ina3221_chip_info *chip, >> + int chan, int warn_limit_ua) >> +{ >> + int s_res = chip->pdata->channel_data[chan].shunt_resistance; >> + int shunt_volt_limit; >> + int warn_limit = warn_limit_ua / 1000; >> + int ret; >> + >> + if (warn_limit < 0) >> + return 0; >> + >> + shunt_volt_limit = warn_limit * s_res; >> + shunt_volt_limit = uv_to_shuntv_register(shunt_volt_limit); >> + >> + ret = regmap_write(chip->rmap, INA3221_WARN(chan), shunt_volt_limit); >> + if (ret < 0) { >> + dev_err(chip->dev, "Failed to write warning reg 0x%02x: %d\n", >> + INA3221_CRIT(chan), ret); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int ina3221_read_default_channel_warning(struct ina3221_chip_info *chip, >> + int chan, int *shunt_curr_ua) >> +{ >> + int shunt_res = chip->pdata->channel_data[chan].shunt_resistance; >> + int shunt_volt; >> + unsigned int value; >> + int ret; >> + >> + if (shunt_res <= 0) { >> + dev_err(chip->dev, "Channel %d have invalid shunt resistor\n", >> + chan); >> + return -EINVAL; >> + } >> + >> + ret = regmap_read(chip->rmap, INA3221_WARN(chan), &value); >> + if (ret < 0) { >> + dev_err(chip->dev, "Failed to read critical reg 0x%02x: %d\n", >> + INA3221_CRIT(chan), ret); >> + return ret; >> + } >> + shunt_volt = shuntv_register_to_uv((u16)value); >> + *shunt_curr_ua = (shunt_volt / shunt_res) * 1000; >> + >> + return 0; >> +} >> + >> +static int ina3221_get_operating_mode(struct ina3221_chip_info *chip) >> +{ >> + return chip->continuous_mode; >> +} >> + >> +static int ina3221_set_operating_mode(struct ina3221_chip_info *chip, >> + int is_continuous) >> +{ >> + unsigned int mask, val; >> + int ret; >> + >> + if (!is_continuous) { >> + ret = regmap_write(chip->rmap, INA3221_CONFIG, 0); >> + if (ret < 0) { >> + dev_err(chip->dev, >> + "Failed to set mode of device: %d\n", ret); >> + return ret; >> + } >> + >> + goto done; >> + } >> + >> + if ((chip->pdata->warn_alert || chip->pdata->crit_alert)) { >> + mask = INA3221_REG_MASK_CEN | INA3221_REG_MASK_WEN; >> + val = (chip->pdata->warn_alert) ? INA3221_REG_MASK_WEN : 0; >> + val |= (chip->pdata->crit_alert) ? INA3221_REG_MASK_CEN : 0; >> + >> + ret = regmap_update_bits(chip->rmap, INA3221_MASK_ENABLE, >> + mask, val); >> + if (ret < 0) { >> + dev_err(chip->dev, >> + "Failed to enable warn/crit alert: %d\n", ret); >> + return ret; >> + } >> + } >> + >> + ret = regmap_write(chip->rmap, INA3221_CONFIG, chip->continuous_config); >> + if (ret < 0) { >> + dev_err(chip->dev, "Failed to set cont mode: %d\n", ret); >> + return ret; >> + } >> + >> +done: >> + chip->continuous_mode = is_continuous; >> + >> + return ret; >> +} >> + >> +static int ina3221_read_raw(struct iio_dev *indio_dev, >> + struct iio_chan_spec const *cspec, >> + int *val, int *val2, long mask) >> +{ >> + struct ina3221_chip_info *chip = iio_priv(indio_dev); >> + int ch = cspec->channel; >> + int ret = -EINVAL; >> + >> + if (mask != IIO_CHAN_INFO_PROCESSED) >> + return -EINVAL; >> + >> + mutex_lock(&chip->state_lock); >> + >> + switch (cspec->type) { >> + case IIO_VOLTAGE: >> + ret = ina3221_get_channel_voltage(chip, ch, val); >> + break; >> + >> + case IIO_CURRENT: >> + switch (cspec->address) { >> + case INA3221_MEASURED_VALUE: >> + ret = ina3221_get_channel_current(chip, ch, val); >> + break; >> + >> + case INA3221_CRIT_CURRENT_LIMIT: >> + ret = ina3221_get_channel_critical(chip, ch, val); >> + break; >> + >> + case INA3221_WARN_CURRENT_LIMIT: >> + ret = ina3221_get_channel_warning(chip, ch, val); >> + break; >> + } >> + break; >> + >> + case IIO_POWER: >> + ret = ina3221_get_channel_power(chip, ch, val); >> + break; >> + >> + default: >> + break; >> + } >> + >> + mutex_unlock(&chip->state_lock); >> + >> + return (ret < 0) ? ret : IIO_VAL_INT; >> +} >> + >> +static int ina3221_write_raw(struct iio_dev *indio_dev, >> + struct iio_chan_spec const *cspec, >> + int val, int val2, long mask) >> +{ >> + struct ina3221_chip_info *chip = iio_priv(indio_dev); >> + int ch = cspec->channel; >> + int ret = -EINVAL; >> + >> + if (mask != IIO_CHAN_INFO_PROCESSED) >> + return -EINVAL; >> + >> + if (cspec->type != IIO_CURRENT) >> + return -EINVAL; >> + >> + mutex_lock(&chip->state_lock); >> + switch (cspec->address) { >> + case INA3221_CRIT_CURRENT_LIMIT: >> + ret = ina3221_set_channel_critical(chip, ch, val); >> + if (!ret) >> + chip->pdata->channel_data[ch].crit_limits = val; >> + break; >> + >> + case INA3221_WARN_CURRENT_LIMIT: >> + ret = ina3221_set_channel_warning(chip, ch, val); >> + if (!ret) >> + chip->pdata->channel_data[ch].warn_limits = val; >> + break; >> + >> + default: >> + break; >> + } >> + >> + mutex_unlock(&chip->state_lock); >> + >> + return ret; >> +} >> + >> +static ssize_t ina3221_show_channel(struct device *dev, >> + struct device_attribute *attr, char *buf) >> +{ >> + struct iio_dev *indio_dev = dev_to_iio_dev(dev); >> + struct ina3221_chip_info *chip = iio_priv(indio_dev); >> + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); >> + int mode = UNPACK_MODE(this_attr->address); >> + int address = UNPACK_CHAN(this_attr->address); >> + int ret; >> + int val; >> + >> + switch (mode) { >> + case INA3221_CHANNEL_NAME: >> + return snprintf(buf, PAGE_SIZE, "%s\n", >> + chip->pdata->channel_data[address].name); >> + >> + case INA3221_OPERATING_MODE: >> + ret = ina3221_get_operating_mode(chip); >> + if (ret) >> + return snprintf(buf, PAGE_SIZE, "continuous\n"); >> + return snprintf(buf, PAGE_SIZE, "oneshot\n"); >> + >> + case INA3221_OVERSAMPLING_RATIO: >> + if (address == INA3221_CHANNEL_ONESHOT) >> + val = chip->pdata->oneshot_avg_sample; >> + else >> + val = chip->pdata->cont_avg_sample; >> + return snprintf(buf, PAGE_SIZE, "%d\n", val); >> + >> + case INA3221_VBUS_CONV_TIME: >> + if (address == INA3221_CHANNEL_ONESHOT) >> + val = chip->pdata->oneshot_vbus_conv_time; >> + else >> + val = chip->pdata->cont_vbus_conv_time; >> + return snprintf(buf, PAGE_SIZE, "%d\n", val); >> + >> + case INA3221_VSHUNT_CONV_TIME: >> + if (address == INA3221_CHANNEL_ONESHOT) >> + val = chip->pdata->oneshot_shunt_conv_time; >> + else >> + val = chip->pdata->cont_shunt_conv_time; >> + return snprintf(buf, PAGE_SIZE, "%d\n", val); >> + >> + default: >> + break; >> + } >> + >> + return -EINVAL; >> +} >> + >> +static ssize_t ina3221_set_channel(struct device *dev, >> + struct device_attribute *attr, >> + const char *buf, size_t len) >> +{ >> + struct iio_dev *indio_dev = dev_to_iio_dev(dev); >> + struct ina3221_chip_info *chip = iio_priv(indio_dev); >> + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); >> + int mode = UNPACK_MODE(this_attr->address); >> + int address = UNPACK_CHAN(this_attr->address); > I'd personally use address as an index into an array of static const > struct. Leads to slightly easier to read and more extensible code. > > Minor point though. >> + int o_conf, c_conf; >> + long val; >> + int *cont_param = NULL; >> + int ret = -EINVAL; >> + >> + if (mode == INA3221_OPERATING_MODE) { >> + val = ((*buf == 'c') || (*buf == 'C')) ? 1 : 0; >> + } else { >> + if (kstrtol(buf, 10, &val) < 0) >> + return -EINVAL; >> + } >> + >> + mutex_lock(&chip->state_lock); >> + >> + o_conf = chip->oneshot_config; >> + c_conf = chip->continuous_config; >> + >> + switch (mode) { >> + case INA3221_OPERATING_MODE: >> + if (chip->continuous_mode == val) >> + break; >> + >> + ret = ina3221_set_operating_mode(chip, val); >> + break; >> + >> + case INA3221_OVERSAMPLING_RATIO: >> + if (address == INA3221_CHANNEL_ONESHOT) { >> + ret = ina3221_set_average(chip, val, &o_conf); >> + if (!ret) >> + chip->pdata->oneshot_avg_sample = val; >> + } else { >> + ret = ina3221_set_average(chip, val, &c_conf); >> + if (!ret) >> + cont_param = &chip->pdata->cont_avg_sample; >> + } >> + break; >> + >> + case INA3221_VBUS_CONV_TIME: >> + if (address == INA3221_CHANNEL_ONESHOT) { >> + ret = ina3221_set_int_time_vbus(chip, val, &o_conf); >> + if (!ret) >> + chip->pdata->oneshot_vbus_conv_time = val; >> + } else { >> + ret = ina3221_set_int_time_vbus(chip, val, &c_conf); >> + if (!ret) >> + cont_param = &chip->pdata->cont_vbus_conv_time; >> + } >> + break; >> + >> + case INA3221_VSHUNT_CONV_TIME: >> + if (address == INA3221_CHANNEL_ONESHOT) { >> + ret = ina3221_set_int_time_vshunt(chip, val, &o_conf); >> + if (!ret) >> + chip->pdata->oneshot_shunt_conv_time = val; >> + } else { >> + ret = ina3221_set_int_time_vshunt(chip, val, &c_conf); >> + if (!ret) >> + cont_param = &chip->pdata->cont_shunt_conv_time; >> + } >> + break; >> + >> + default: >> + break; >> + } >> + >> + if (ret < 0) >> + goto exit; >> + >> + chip->oneshot_config = o_conf; >> + if (chip->continuous_mode && chip->continuous_config != c_conf) { >> + ret = regmap_write(chip->rmap, INA3221_CONFIG, c_conf); >> + if (ret < 0) >> + goto exit; >> + } >> + chip->continuous_config = c_conf; >> + if (cont_param) >> + *cont_param = val; >> + >> +exit: >> + mutex_unlock(&chip->state_lock); >> + >> + return len; >> +} >> + >> +static IIO_DEVICE_ATTR(rail_name_0, S_IRUGO | S_IWUSR, >> + ina3221_show_channel, ina3221_set_channel, >> + PACK_MODE_CHAN(INA3221_CHANNEL_NAME, 0)); >> + >> +static IIO_DEVICE_ATTR(rail_name_1, S_IRUGO | S_IWUSR, >> + ina3221_show_channel, ina3221_set_channel, >> + PACK_MODE_CHAN(INA3221_CHANNEL_NAME, 1)); >> + >> +static IIO_DEVICE_ATTR(rail_name_2, S_IRUGO | S_IWUSR, >> + ina3221_show_channel, ina3221_set_channel, >> + PACK_MODE_CHAN(INA3221_CHANNEL_NAME, 2)); >> + >> +static IIO_DEVICE_ATTR(operating_mode, S_IRUGO | S_IWUSR, >> + ina3221_show_channel, ina3221_set_channel, >> + PACK_MODE_CHAN(INA3221_OPERATING_MODE, 0)); >> + >> +static IIO_DEVICE_ATTR(oneshot_oversampling_ratio, S_IRUGO | S_IWUSR, >> + ina3221_show_channel, ina3221_set_channel, >> + PACK_MODE_CHAN(INA3221_OVERSAMPLING_RATIO, >> + INA3221_CHANNEL_ONESHOT)); >> + >> +static IIO_DEVICE_ATTR(continuous_oversampling_ratio, S_IRUGO | S_IWUSR, >> + ina3221_show_channel, ina3221_set_channel, >> + PACK_MODE_CHAN(INA3221_OVERSAMPLING_RATIO, >> + INA3221_CHANNEL_CONTINUOUS)); >> + >> +static IIO_DEVICE_ATTR(oneshot_vbus_conv_time, S_IRUGO | S_IWUSR, >> + ina3221_show_channel, ina3221_set_channel, >> + PACK_MODE_CHAN(INA3221_VBUS_CONV_TIME, >> + INA3221_CHANNEL_ONESHOT)); >> + >> +static IIO_DEVICE_ATTR(continuous_vbus_conv_time, S_IRUGO | S_IWUSR, >> + ina3221_show_channel, ina3221_set_channel, >> + PACK_MODE_CHAN(INA3221_VBUS_CONV_TIME, >> + INA3221_CHANNEL_CONTINUOUS)); >> + >> +static IIO_DEVICE_ATTR(oneshot_vshunt_conv_time, S_IRUGO | S_IWUSR, >> + ina3221_show_channel, ina3221_set_channel, >> + PACK_MODE_CHAN(INA3221_VSHUNT_CONV_TIME, >> + INA3221_CHANNEL_ONESHOT)); >> + >> +static IIO_DEVICE_ATTR(continuous_vshunt_conv_time, S_IRUGO | S_IWUSR, >> + ina3221_show_channel, ina3221_set_channel, >> + PACK_MODE_CHAN(INA3221_VSHUNT_CONV_TIME, >> + INA3221_CHANNEL_CONTINUOUS)); >> + >> +static struct attribute *ina3221_attributes[] = { >> + &iio_dev_attr_rail_name_0.dev_attr.attr, >> + &iio_dev_attr_rail_name_1.dev_attr.attr, >> + &iio_dev_attr_rail_name_2.dev_attr.attr, >> + &iio_dev_attr_oneshot_oversampling_ratio.dev_attr.attr, >> + &iio_dev_attr_continuous_oversampling_ratio.dev_attr.attr, >> + &iio_dev_attr_oneshot_vbus_conv_time.dev_attr.attr, >> + &iio_dev_attr_continuous_vbus_conv_time.dev_attr.attr, >> + &iio_dev_attr_oneshot_vshunt_conv_time.dev_attr.attr, >> + &iio_dev_attr_continuous_vshunt_conv_time.dev_attr.attr, >> + &iio_dev_attr_operating_mode.dev_attr.attr, > Was about to complain about docs then noticed patch 3. There's a lot > here and it's mostly non standard.. hmm. >> + NULL, >> +}; >> + >> +static const struct attribute_group ina3221_groups = { >> + .attrs = ina3221_attributes, >> +}; >> + >> +#define channel_type(_type, _add, _channel, _name) { \ >> + .type = _type, \ >> + .indexed = 1, \ >> + .address = _add, \ >> + .channel = _channel, \ >> + .extend_name = _name, \ >> + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) \ >> +} >> + >> +#define channel_spec(ch) \ >> + channel_type(IIO_VOLTAGE, 0, ch, NULL), \ >> + channel_type(IIO_CURRENT, INA3221_MEASURED_VALUE, ch, NULL), \ >> + channel_type(IIO_CURRENT, INA3221_CRIT_CURRENT_LIMIT, ch, "warning"), \ >> + channel_type(IIO_CURRENT, INA3221_WARN_CURRENT_LIMIT, ch, "critical"), \ > These aren't channels that I can see but rather events on a given channel. > There's an issue here as well in that IIO doesn't currently support two > events of the same type on a single channel - our event codes have no > way of distguishing between them. This needs fixing but we haven't done > it yet. > > Also these particular events do make this seem rather more or a power > monitoring chip than we'd normally expect to see in IIO - hence the > need for that justification in the patch description ;) >> + channel_type(IIO_POWER, 0, ch, NULL) >> + >> +static const struct iio_chan_spec ina3221_channels_spec[] = { >> + channel_spec(0), >> + channel_spec(1), >> + channel_spec(2), >> +}; >> + >> +static const struct iio_info ina3221_info = { >> + .driver_module = THIS_MODULE, >> + .attrs = &ina3221_groups, >> + .read_raw = ina3221_read_raw, >> + .write_raw = ina3221_write_raw, >> +}; >> + >> +static int ina3221_process_pdata(struct ina3221_chip_info *chip, >> + struct ina3221_platform_data *pdata) >> +{ >> + unsigned int o_conf; >> + unsigned int c_conf; >> + int ret; >> + >> + o_conf = 0x7 << 12; >> + c_conf = pdata->active_channel << 12; >> + >> + o_conf |= (chip->pdata->enable_power) ? 0x3 : 0x2; >> + c_conf |= (chip->pdata->enable_power) ? 0x7 : 0x6; >> + >> + ret = ina3221_set_average(chip, pdata->oneshot_avg_sample, &o_conf); >> + if (ret < 0) >> + return ret; >> + ret = ina3221_set_average(chip, pdata->cont_avg_sample, &c_conf); >> + if (ret < 0) >> + return ret; >> + >> + ret = ina3221_set_int_time_vbus(chip, pdata->oneshot_vbus_conv_time, >> + &o_conf); >> + if (ret < 0) >> + return ret; >> + >> + ret = ina3221_set_int_time_vbus(chip, pdata->cont_vbus_conv_time, >> + &c_conf); >> + if (ret < 0) >> + return ret; >> + >> + ret = ina3221_set_int_time_vshunt(chip, pdata->oneshot_shunt_conv_time, >> + &o_conf); >> + if (ret < 0) >> + return ret; >> + >> + ret = ina3221_set_int_time_vshunt(chip, pdata->cont_shunt_conv_time, >> + &c_conf); >> + if (ret < 0) >> + return ret; >> + >> + chip->oneshot_config = o_conf; >> + chip->continuous_config = c_conf; >> + return 0; >> +} >> + > There is a lot of moderately controversial stuff in here - I'll reply to > the binding doc instead of here on these though. >> +static int ina3221_get_platform_data_dt(struct ina3221_chip_info *chip) >> +{ >> + struct device *dev = chip->dev; >> + struct device_node *np = dev->of_node; >> + struct device_node *np_chan; >> + struct ina3221_platform_data *pdata; >> + struct ina3221_channel_data *cdata; >> + char channel_name[20]; >> + u32 value; >> + int curr_ua; >> + int id; >> + int ret; >> + >> + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); >> + if (!pdata) >> + return -ENOMEM; >> + >> + chip->pdata = pdata; >> + >> + ret = of_property_read_u32(np, "one-shot-average-sample", &value); >> + if (!ret) >> + pdata->oneshot_avg_sample = value; >> + else >> + pdata->oneshot_avg_sample = INA3221_CONFIG_AVG_SAMPLE_DEFAULT; >> + >> + ret = of_property_read_u32(np, "one-shot-vbus-conv-time-us", &value); >> + if (!ret) >> + pdata->oneshot_vbus_conv_time = value; >> + else >> + pdata->oneshot_vbus_conv_time = >> + INA3221_CONFIG_VBUS_CONV_TIME_DEFAULT; >> + >> + ret = of_property_read_u32(np, "one-shot-shunt-conv-time-us", >> + &value); >> + if (!ret) >> + pdata->oneshot_shunt_conv_time = value; >> + else >> + pdata->oneshot_shunt_conv_time = >> + INA3221_CONFIG_SHUNT_CONV_TIME_DEFAULT; >> + >> + ret = of_property_read_u32(np, "continuous-average-sample", &value); >> + if (!ret) >> + pdata->cont_avg_sample = value; >> + else >> + pdata->cont_avg_sample = INA3221_CONFIG_AVG_SAMPLE_DEFAULT; >> + >> + ret = of_property_read_u32(np, "continuous-vbus-conv-time-us", &value); >> + if (!ret) >> + pdata->cont_vbus_conv_time = value; >> + else >> + pdata->cont_vbus_conv_time = >> + INA3221_CONFIG_VBUS_CONV_TIME_DEFAULT; >> + >> + ret = of_property_read_u32(np, "continuous-shunt-conv-time-us", &value); >> + if (!ret) >> + pdata->cont_shunt_conv_time = value; >> + else >> + pdata->cont_shunt_conv_time = >> + INA3221_CONFIG_SHUNT_CONV_TIME_DEFAULT; >> + >> + pdata->enable_power = of_property_read_bool(np, >> + "enable-power-monitor"); >> + pdata->continuous_mode = of_property_read_bool(np, >> + "enable-continuous-mode"); >> + pdata->warn_alert = of_property_read_bool(np, "enable-warning-alert"); >> + pdata->crit_alert = of_property_read_bool(np, "enable-critical-alert"); >> + >> + for (id = 0; id < INA3221_NUMBER_OF_CHANNELS; ++id) { >> + sprintf(channel_name, "channel%d", id); >> + np_chan = of_get_child_by_name(np, channel_name); >> + if (!np_chan) >> + continue; >> + >> + cdata = &pdata->channel_data[id]; >> + >> + ret = of_property_read_string(np_chan, "label", &cdata->name); >> + if (ret < 0) { >> + dev_err(dev, "Channel %s does not have label\n", >> + np_chan->full_name); >> + continue; >> + } >> + >> + ret = of_property_read_u32(np_chan, >> + "warning-current-limit-microamp", >> + &value); >> + cdata->warn_limits = (!ret) ? value : ret; >> + >> + ret = of_property_read_u32(np_chan, >> + "critical-current-limit-microamp", >> + &value); >> + cdata->crit_limits = (!ret) ? value : ret; >> + >> + ret = of_property_read_u32(np_chan, "shunt-resistor-mohm", >> + &value); >> + if (!ret) >> + cdata->shunt_resistance = value; >> + >> + pdata->active_channel |= BIT(INA3221_NUMBER_OF_CHANNELS - id - >> + 1); >> + >> + if (cdata->crit_limits < 0) { >> + ret = ina3221_read_default_channel_critical(chip, id, >> + &curr_ua); >> + if (ret < 0) >> + return ret; >> + cdata->crit_limits = curr_ua; >> + } else { >> + ret = ina3221_set_channel_critical(chip, id, >> + cdata->crit_limits); >> + if (ret < 0) >> + return ret; >> + } >> + >> + if (cdata->warn_limits < 0) { >> + ret = ina3221_read_default_channel_warning(chip, id, >> + &curr_ua); >> + if (ret < 0) >> + return ret; >> + cdata->warn_limits = curr_ua; >> + } else { >> + ret = ina3221_set_channel_warning(chip, id, >> + cdata->warn_limits); >> + if (ret < 0) >> + return ret; >> + } >> + } >> + >> + if (!pdata->active_channel) >> + return -EINVAL; >> + >> + ret = ina3221_process_pdata(chip, pdata); >> + if (ret < 0) { >> + dev_err(chip->dev, "Failed to process platform data: %d\n", >> + ret); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int ina3221_do_reset(struct ina3221_chip_info *chip) >> +{ >> + int ret; >> + >> + ret = regmap_update_bits(chip->rmap, INA3221_CONFIG, >> + INA3221_CONFIG_RESET_MASK, >> + INA3221_CONFIG_RESET_EN); >> + if (ret < 0) { >> + dev_err(chip->dev, "Failed to reset device: %d\n", ret); >> + return ret; >> + } >> + >> + ret = regmap_update_bits(chip->rmap, INA3221_CONFIG, >> + INA3221_CONFIG_RESET_MASK, 0); >> + if (ret < 0) { >> + dev_err(chip->dev, "Failed to reset device: %d\n", ret); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int ina3221_probe(struct i2c_client *client, >> + const struct i2c_device_id *id) >> +{ >> + struct ina3221_chip_info *chip; >> + struct iio_dev *indio_dev; >> + int ret; >> + >> + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); >> + if (!indio_dev) >> + return -ENOMEM; >> + >> + chip = iio_priv(indio_dev); >> + >> + chip->rmap = devm_regmap_init_i2c(client, &ina3221_regmap_config); >> + if (IS_ERR(chip->rmap)) { >> + ret = PTR_ERR(chip->rmap); >> + dev_err(&client->dev, "Failed to initialise regmap: %d\n", ret); >> + return ret; >> + } >> + >> + chip->dev = &client->dev; >> + mutex_init(&chip->state_lock); >> + i2c_set_clientdata(client, indio_dev); >> + >> + ret = ina3221_do_reset(chip); >> + if (ret < 0) >> + return ret; >> + >> + ret = ina3221_get_platform_data_dt(chip); >> + if (ret < 0) { >> + dev_err(&client->dev, "Failed to get platform data: %d\n", ret); >> + return ret; >> + } >> + >> + ret = ina3221_set_operating_mode(chip, chip->pdata->continuous_mode); >> + if (ret < 0) >> + return ret; >> + >> + indio_dev->modes = INDIO_DIRECT_MODE; >> + indio_dev->dev.parent = &client->dev; >> + indio_dev->channels = ina3221_channels_spec; >> + indio_dev->num_channels = ARRAY_SIZE(ina3221_channels_spec); >> + indio_dev->name = id->name; >> + indio_dev->info = &ina3221_info; >> + >> + return iio_device_register(indio_dev); >> +} >> + >> +static int ina3221_remove(struct i2c_client *client) >> +{ >> + struct iio_dev *indio_dev = i2c_get_clientdata(client); >> + struct ina3221_chip_info *chip = iio_priv(indio_dev); >> + >> + iio_device_unregister(indio_dev); >> + >> + /* Powerdown */ >> + return regmap_update_bits(chip->rmap, INA3221_CONFIG, >> + INA3221_CONFIG_MODE_MASK, >> + INA3221_CONFIG_MODE_POWER_DOWN); >> +} >> + >> +static const struct i2c_device_id ina3221_id[] = { >> + {.name = "ina3221"}, >> + {}, >> +}; >> +MODULE_DEVICE_TABLE(i2c, ina3221_id); >> + >> +static struct i2c_driver ina3221_driver = { >> + .driver = { >> + .name = "ina3221", >> + }, >> + .probe = ina3221_probe, >> + .remove = ina3221_remove, >> + .id_table = ina3221_id, >> +}; >> +module_i2c_driver(ina3221_driver); >> + >> +MODULE_DESCRIPTION("Texas Instruments INA3221 ADC driver"); >> +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); >> +MODULE_LICENSE("GPL v2"); >> > > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/3] iio: adc: ina3221: Add support for IIO ADC driver for TI INA3221 2016-06-03 10:16 ` Jonathan Cameron @ 2016-06-03 11:31 ` Laxman Dewangan 2016-06-03 12:04 ` Jonathan Cameron 0 siblings, 1 reply; 14+ messages in thread From: Laxman Dewangan @ 2016-06-03 11:31 UTC (permalink / raw) To: Jonathan Cameron, robh+dt, corbet, lars Cc: devicetree, linux-kernel, linux-doc, linux-iio, linux-hwmon, Jean Delvare, Guenter Roeck On Friday 03 June 2016 03:46 PM, Jonathan Cameron wrote: > On 03/06/16 11:06, Jonathan Cameron wrote: >> >> Code looks good, bu these more fundamental bits need sorting. > Another minor point - why do the power calculations in driver? > no hardware support for it, so why not just leave it to userspace? Device supports the bus and shunt voltage monitoring. So even no current. Also the warning/critical limit is for the voltage across shunt. So should we only expose the shunt/bus voltage, no power/current? I am thinking that user space should not know the platform and hence shunt resistance and so exposing the current and power on bus is better option. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/3] iio: adc: ina3221: Add support for IIO ADC driver for TI INA3221 2016-06-03 11:31 ` Laxman Dewangan @ 2016-06-03 12:04 ` Jonathan Cameron 2016-06-03 12:03 ` Laxman Dewangan 0 siblings, 1 reply; 14+ messages in thread From: Jonathan Cameron @ 2016-06-03 12:04 UTC (permalink / raw) To: Laxman Dewangan, robh+dt, corbet, lars Cc: devicetree, linux-kernel, linux-doc, linux-iio, linux-hwmon, Jean Delvare, Guenter Roeck On 03/06/16 12:31, Laxman Dewangan wrote: > > On Friday 03 June 2016 03:46 PM, Jonathan Cameron wrote: >> On 03/06/16 11:06, Jonathan Cameron wrote: >>> >>> Code looks good, bu these more fundamental bits need sorting. >> Another minor point - why do the power calculations in driver? >> no hardware support for it, so why not just leave it to userspace? > > Device supports the bus and shunt voltage monitoring. So even no current. Also the warning/critical limit is for the voltage across shunt. > > So should we only expose the shunt/bus voltage, no power/current? > > I am thinking that user space should not know the platform and hence shunt resistance and so exposing the current and power on bus is better option. > I'd go for current and voltage rather than current and power, but otherwise agree. > > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/3] iio: adc: ina3221: Add support for IIO ADC driver for TI INA3221 2016-06-03 12:04 ` Jonathan Cameron @ 2016-06-03 12:03 ` Laxman Dewangan 0 siblings, 0 replies; 14+ messages in thread From: Laxman Dewangan @ 2016-06-03 12:03 UTC (permalink / raw) To: Jonathan Cameron, robh+dt, corbet, lars Cc: devicetree, linux-kernel, linux-doc, linux-iio, linux-hwmon, Jean Delvare, Guenter Roeck On Friday 03 June 2016 05:34 PM, Jonathan Cameron wrote: > On 03/06/16 12:31, Laxman Dewangan wrote: >> On Friday 03 June 2016 03:46 PM, Jonathan Cameron wrote: >>> On 03/06/16 11:06, Jonathan Cameron wrote: >>>> Code looks good, bu these more fundamental bits need sorting. >>> Another minor point - why do the power calculations in driver? >>> no hardware support for it, so why not just leave it to userspace? >> Device supports the bus and shunt voltage monitoring. So even no current. Also the warning/critical limit is for the voltage across shunt. >> >> So should we only expose the shunt/bus voltage, no power/current? >> >> I am thinking that user space should not know the platform and hence shunt resistance and so exposing the current and power on bus is better option. >> > I'd go for current and voltage rather than current and power, but > otherwise agree. OK, I will have the voltage (bus voltage) and current (on bus). User space can get power out of this. Thanks for suggestion. The critical limits will be still in current. And it should be customized sysfs instead of the iio_chan_spec i.e. iio_device attribute interface. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/3] iio: adc: ina3221: Add support for IIO ADC driver for TI INA3221 2016-06-03 10:06 ` [PATCH 2/3] iio: adc: ina3221: Add support for IIO ADC driver for TI INA3221 Jonathan Cameron 2016-06-03 10:16 ` Jonathan Cameron @ 2016-06-03 11:26 ` Laxman Dewangan 2016-06-03 12:09 ` Jonathan Cameron 2016-06-03 13:29 ` Guenter Roeck 2 siblings, 1 reply; 14+ messages in thread From: Laxman Dewangan @ 2016-06-03 11:26 UTC (permalink / raw) To: Jonathan Cameron, robh+dt, corbet, lars Cc: devicetree, linux-kernel, linux-doc, linux-iio, linux-hwmon, Jean Delvare, Guenter Roeck On Friday 03 June 2016 03:36 PM, Jonathan Cameron wrote: > On 01/06/16 13:34, Laxman Dewangan wrote: >> Add support for INA3221 SW driver via IIO ADC interface. The device is >> register as iio-device and provides interface for voltage/current and power >> monitor. Also provide interface for setting oneshot/continuous mode and >> critical/warning threshold for the shunt voltage drop. >> >> Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> > Hi Laxman, > > As ever with any driver lying on the border of IIO and hwmon, please include > a short justification of why you need an IIO driver and also cc the > hwmon list + maintainers. (cc'd on this reply). > > I simply won't take a driver where the hwmon maintainers aren't happy. > As it stands I'm not seeing obvious reasons in the code for why this > should be an IIO device. I thought that all ADC or monitors are going to be part of IIO device framework. I saw the ina2xx which is same (single channel) which was my reference point. > Funily enough I know this datasheet a little as was evaluating > it for use on some boards at the day job a week or so ago. > > Various comments inline. Major points are: > * Don't use 'fake' channels to control events. If the events infrastructure > doesn't handle your events, then fix that rather than working around it. > * There is a lot of ABI in here concerned with oneshot vs continuous. > This seems to me to be more than it should be. We wouldn't expect to > see stuff changing as a result of switching between these modes other > than wrt to when the data shows up. So I'd expect to not see this > directly exposed at all - but rather sit in oneshot unless either: > 1) Buffered mode is running (not currently supported) > 2) Alerts are on - which I think requires it to be in continuous mode. > > Other question to my mind is whether we should be reporting vshunt or > (using device tree to pass resistance) current. This is bus and shunt voltage device for power monitoring. In our platforms, we use this device for bus current and so power monitor. We have two usecases, one is one shot, read when it needs it. And other continuous when we have multiple core running then continuous mode to get the power consumption by rail. Yaah, alert is used only on continuous mode and mainly used for throttling when rail power goes beyond some limit. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/3] iio: adc: ina3221: Add support for IIO ADC driver for TI INA3221 2016-06-03 11:26 ` Laxman Dewangan @ 2016-06-03 12:09 ` Jonathan Cameron 2016-06-03 12:17 ` Laxman Dewangan 0 siblings, 1 reply; 14+ messages in thread From: Jonathan Cameron @ 2016-06-03 12:09 UTC (permalink / raw) To: Laxman Dewangan, robh+dt, corbet, lars Cc: devicetree, linux-kernel, linux-doc, linux-iio, linux-hwmon, Jean Delvare, Guenter Roeck On 03/06/16 12:26, Laxman Dewangan wrote: > > On Friday 03 June 2016 03:36 PM, Jonathan Cameron wrote: >> On 01/06/16 13:34, Laxman Dewangan wrote: >>> Add support for INA3221 SW driver via IIO ADC interface. The device is >>> register as iio-device and provides interface for voltage/current and power >>> monitor. Also provide interface for setting oneshot/continuous mode and >>> critical/warning threshold for the shunt voltage drop. >>> >>> Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> >> Hi Laxman, >> >> As ever with any driver lying on the border of IIO and hwmon, please include >> a short justification of why you need an IIO driver and also cc the >> hwmon list + maintainers. (cc'd on this reply). >> >> I simply won't take a driver where the hwmon maintainers aren't happy. >> As it stands I'm not seeing obvious reasons in the code for why this >> should be an IIO device. > > I thought that all ADC or monitors are going to be part of IIO device > framework. I saw the ina2xx which is same (single channel) which was > my reference point. That had a rather specific use case IIRC - they needed the buffered support to get the data fast enough. > >> Funily enough I know this datasheet a little as was evaluating >> it for use on some boards at the day job a week or so ago. >> >> Various comments inline. Major points are: >> * Don't use 'fake' channels to control events. If the events infrastructure >> doesn't handle your events, then fix that rather than working around it. >> * There is a lot of ABI in here concerned with oneshot vs continuous. >> This seems to me to be more than it should be. We wouldn't expect to >> see stuff changing as a result of switching between these modes other >> than wrt to when the data shows up. So I'd expect to not see this >> directly exposed at all - but rather sit in oneshot unless either: >> 1) Buffered mode is running (not currently supported) >> 2) Alerts are on - which I think requires it to be in continuous mode. >> >> Other question to my mind is whether we should be reporting vshunt or >> (using device tree to pass resistance) current. > > This is bus and shunt voltage device for power monitoring. In our > platforms, we use this device for bus current and so power monitor. > > We have two usecases, one is one shot, read when it needs it. And > other continuous when we have multiple core running then continuous > mode to get the power consumption by rail. That's fine, but continuous should be using the buffered interfaces really as that's there explicitly to support groups of channels captured using a sequencer. Then the abi ends up much more standard which is nice. Also allows for high speed ish continuous monitoring which is what the was I think the point of the single channel driver. > > Yaah, alert is used only on continuous mode and mainly used for > throttling when rail power goes beyond some limit. Of interesting in Linux, or routed directly to hardware? > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/3] iio: adc: ina3221: Add support for IIO ADC driver for TI INA3221 2016-06-03 12:09 ` Jonathan Cameron @ 2016-06-03 12:17 ` Laxman Dewangan 0 siblings, 0 replies; 14+ messages in thread From: Laxman Dewangan @ 2016-06-03 12:17 UTC (permalink / raw) To: Jonathan Cameron, robh+dt, corbet, lars Cc: devicetree, linux-kernel, linux-doc, linux-iio, linux-hwmon, Jean Delvare, Guenter Roeck On Friday 03 June 2016 05:39 PM, Jonathan Cameron wrote: > On 03/06/16 12:26, Laxman Dewangan wrote: >> On Friday 03 June 2016 03:36 PM, Jonathan Cameron wrote: >> >> I thought that all ADC or monitors are going to be part of IIO device >> framework. I saw the ina2xx which is same (single channel) which was >> my reference point. > That had a rather specific use case IIRC - they needed the buffered support > to get the data fast enough. I think in our particular requirements, we dont need the buffering support but HW keep monitor and check with warning/critical threshold to generate HW signal. >>> Funily enough I know this datasheet a little as was evaluating >>> it for use on some boards at the day job a week or so ago. >>> >>> Various comments inline. Major points are: >>> * Don't use 'fake' channels to control events. If the events infrastructure >>> doesn't handle your events, then fix that rather than working around it. >>> * There is a lot of ABI in here concerned with oneshot vs continuous. >>> This seems to me to be more than it should be. We wouldn't expect to >>> see stuff changing as a result of switching between these modes other >>> than wrt to when the data shows up. So I'd expect to not see this >>> directly exposed at all - but rather sit in oneshot unless either: >>> 1) Buffered mode is running (not currently supported) >>> 2) Alerts are on - which I think requires it to be in continuous mode. >>> >>> Other question to my mind is whether we should be reporting vshunt or >>> (using device tree to pass resistance) current. >> This is bus and shunt voltage device for power monitoring. In our >> platforms, we use this device for bus current and so power monitor. >> >> We have two usecases, one is one shot, read when it needs it. And >> other continuous when we have multiple core running then continuous >> mode to get the power consumption by rail. > That's fine, but continuous should be using the buffered interfaces > really as that's there explicitly to support groups of channels > captured using a sequencer. > > Then the abi ends up much more standard which is nice. Also allows > for high speed ish continuous monitoring which is what the was > I think the point of the single channel driver. The requirement for continuous monitoring is to ADC generate alert when the current on bus cross the threshold of warning/critical level so that alert signal can be used for throttling. So in my this particular usecase, we may not need buffered data. >> Yaah, alert is used only on continuous mode and mainly used for >> throttling when rail power goes beyond some limit. > Of interesting in Linux, or routed directly to hardware? Yaah, In some platform this is routed to the hardware for throttling. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/3] iio: adc: ina3221: Add support for IIO ADC driver for TI INA3221 2016-06-03 10:06 ` [PATCH 2/3] iio: adc: ina3221: Add support for IIO ADC driver for TI INA3221 Jonathan Cameron 2016-06-03 10:16 ` Jonathan Cameron 2016-06-03 11:26 ` Laxman Dewangan @ 2016-06-03 13:29 ` Guenter Roeck 2016-06-03 14:14 ` Laxman Dewangan 2 siblings, 1 reply; 14+ messages in thread From: Guenter Roeck @ 2016-06-03 13:29 UTC (permalink / raw) To: Jonathan Cameron, Laxman Dewangan, robh+dt, corbet, lars Cc: devicetree, linux-kernel, linux-doc, linux-iio, linux-hwmon, Jean Delvare On 06/03/2016 03:06 AM, Jonathan Cameron wrote: > On 01/06/16 13:34, Laxman Dewangan wrote: >> The INA3221 is a three-channel, high-side current and bus voltage monitor >> with an I2C interface from Texas Instruments. The INA3221 monitors both >> shunt voltage drops and bus supply voltages in addition to having >> programmable conversion times and averaging modes for these signals. >> The INA3221 offers both critical and warning alerts to detect multiple >> programmable out-of-range conditions for each channel. >> >> Add support for INA3221 SW driver via IIO ADC interface. The device is >> register as iio-device and provides interface for voltage/current and power >> monitor. Also provide interface for setting oneshot/continuous mode and >> critical/warning threshold for the shunt voltage drop. >> >> Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> > Hi Laxman, > > As ever with any driver lying on the border of IIO and hwmon, please include > a short justification of why you need an IIO driver and also cc the > hwmon list + maintainers. (cc'd on this reply). > > I simply won't take a driver where the hwmon maintainers aren't happy. > As it stands I'm not seeing obvious reasons in the code for why this > should be an IIO device. > Me not either. I have a hwmon driver for the same chip pending from Andrew Davis (TI) which I am just about to accept. We had directed Andrew back in April to write a hwmon driver for the chip, which he did. Guenter ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/3] iio: adc: ina3221: Add support for IIO ADC driver for TI INA3221 2016-06-03 13:29 ` Guenter Roeck @ 2016-06-03 14:14 ` Laxman Dewangan 2016-06-03 15:17 ` Andrew F. Davis 0 siblings, 1 reply; 14+ messages in thread From: Laxman Dewangan @ 2016-06-03 14:14 UTC (permalink / raw) To: Guenter Roeck, Jonathan Cameron, robh+dt, corbet, lars Cc: devicetree, linux-kernel, linux-doc, linux-iio, linux-hwmon, Jean Delvare, afd On Friday 03 June 2016 06:59 PM, Guenter Roeck wrote: > On 06/03/2016 03:06 AM, Jonathan Cameron wrote: >> On 01/06/16 13:34, Laxman Dewangan wrote: >>> The INA3221 is a three-channel, high-side current and bus voltage >>> monitor >>> with an I2C interface from Texas Instruments. The INA3221 monitors both >>> shunt voltage drops and bus supply voltages in addition to having >>> programmable conversion times and averaging modes for these signals. >>> The INA3221 offers both critical and warning alerts to detect multiple >>> programmable out-of-range conditions for each channel. >>> >>> Add support for INA3221 SW driver via IIO ADC interface. The device is >>> register as iio-device and provides interface for voltage/current >>> and power >>> monitor. Also provide interface for setting oneshot/continuous mode and >>> critical/warning threshold for the shunt voltage drop. >>> >>> Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> >> Hi Laxman, >> >> As ever with any driver lying on the border of IIO and hwmon, please >> include >> a short justification of why you need an IIO driver and also cc the >> hwmon list + maintainers. (cc'd on this reply). >> >> I simply won't take a driver where the hwmon maintainers aren't happy. >> As it stands I'm not seeing obvious reasons in the code for why this >> should be an IIO device. >> > > Me not either. > > I have a hwmon driver for the same chip pending from Andrew Davis (TI) > which I am just about to accept. We had directed Andrew back in April > to write a hwmon driver for the chip, which he did. > Thanks Guenter, I found the series [PATCH v2 0/2] Add support for INA3221 Triple Current/Voltage Monitors Looks fine to me. I can use the hwmon. However, some of the stuff from my patch are not there which I will add later once original patch applied: - Dynamic mode changes for continuous and one-shot from sysfs. - In one shot, when try to read voltage data, do conversion and then read. Not sure whether exporting the following will help or not. Can you please confirm? - Oversampling time i.e. average sample - conversion time for bus voltage and shunt voltage if default is not suited for system. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/3] iio: adc: ina3221: Add support for IIO ADC driver for TI INA3221 2016-06-03 14:14 ` Laxman Dewangan @ 2016-06-03 15:17 ` Andrew F. Davis 2016-06-07 22:30 ` Guenter Roeck 0 siblings, 1 reply; 14+ messages in thread From: Andrew F. Davis @ 2016-06-03 15:17 UTC (permalink / raw) To: Laxman Dewangan, Guenter Roeck, Jonathan Cameron, robh+dt, corbet, lars Cc: devicetree, linux-kernel, linux-doc, linux-iio, linux-hwmon, Jean Delvare On 06/03/2016 09:14 AM, Laxman Dewangan wrote: > > On Friday 03 June 2016 06:59 PM, Guenter Roeck wrote: >> On 06/03/2016 03:06 AM, Jonathan Cameron wrote: >>> On 01/06/16 13:34, Laxman Dewangan wrote: >>>> The INA3221 is a three-channel, high-side current and bus voltage >>>> monitor >>>> with an I2C interface from Texas Instruments. The INA3221 monitors both >>>> shunt voltage drops and bus supply voltages in addition to having >>>> programmable conversion times and averaging modes for these signals. >>>> The INA3221 offers both critical and warning alerts to detect multiple >>>> programmable out-of-range conditions for each channel. >>>> >>>> Add support for INA3221 SW driver via IIO ADC interface. The device is >>>> register as iio-device and provides interface for voltage/current >>>> and power >>>> monitor. Also provide interface for setting oneshot/continuous mode and >>>> critical/warning threshold for the shunt voltage drop. >>>> >>>> Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> >>> Hi Laxman, >>> >>> As ever with any driver lying on the border of IIO and hwmon, please >>> include >>> a short justification of why you need an IIO driver and also cc the >>> hwmon list + maintainers. (cc'd on this reply). >>> >>> I simply won't take a driver where the hwmon maintainers aren't happy. >>> As it stands I'm not seeing obvious reasons in the code for why this >>> should be an IIO device. >>> >> >> Me not either. >> >> I have a hwmon driver for the same chip pending from Andrew Davis (TI) >> which I am just about to accept. We had directed Andrew back in April >> to write a hwmon driver for the chip, which he did. >> > > Thanks Guenter, I found the series > > [PATCH v2 0/2] Add support for INA3221 Triple Current/Voltage Monitors > Looks fine to me. I can use the hwmon. > If you search even further back you can see I originally went with an IIO driver as well, comparing the IIO and hwmon version for the simple use cases I needed the hwmod version turned out much simpler, so it was probably the right framework for now. > > However, some of the stuff from my patch are not there which I will add > later once original patch applied: > - Dynamic mode changes for continuous and one-shot from sysfs. > - In one shot, when try to read voltage data, do conversion and then read. > My attempt is rather minimal, to be honest I like your stab at this driver better in some ways, especially relating to DT putting each channel in its own node with labels, I think this is a bit more clean and will be more extendable if/when new multi-channel monitor chips are made. > Not sure whether exporting the following will help or not. Can you > please confirm? > - Oversampling time i.e. average sample > - conversion time for bus voltage and shunt voltage if default is not > suited for system. > > These can always be added as needed, but the DT changes are not as easy. I would like it if this got in this cycle but if you think something will be needed to help your improvements, that can not be added incrementally, it would probably be best to get them into the initial DT binding doc now. Thanks, Andrew > > ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/3] iio: adc: ina3221: Add support for IIO ADC driver for TI INA3221 2016-06-03 15:17 ` Andrew F. Davis @ 2016-06-07 22:30 ` Guenter Roeck 2016-06-08 15:04 ` Andrew F. Davis 0 siblings, 1 reply; 14+ messages in thread From: Guenter Roeck @ 2016-06-07 22:30 UTC (permalink / raw) To: Andrew F. Davis Cc: Laxman Dewangan, Jonathan Cameron, robh+dt, corbet, lars, devicetree, linux-kernel, linux-doc, linux-iio, linux-hwmon, Jean Delvare On Fri, Jun 03, 2016 at 10:17:55AM -0500, Andrew F. Davis wrote: > On 06/03/2016 09:14 AM, Laxman Dewangan wrote: > > > > On Friday 03 June 2016 06:59 PM, Guenter Roeck wrote: > >> On 06/03/2016 03:06 AM, Jonathan Cameron wrote: > >>> On 01/06/16 13:34, Laxman Dewangan wrote: > >>>> The INA3221 is a three-channel, high-side current and bus voltage > >>>> monitor > >>>> with an I2C interface from Texas Instruments. The INA3221 monitors both > >>>> shunt voltage drops and bus supply voltages in addition to having > >>>> programmable conversion times and averaging modes for these signals. > >>>> The INA3221 offers both critical and warning alerts to detect multiple > >>>> programmable out-of-range conditions for each channel. > >>>> > >>>> Add support for INA3221 SW driver via IIO ADC interface. The device is > >>>> register as iio-device and provides interface for voltage/current > >>>> and power > >>>> monitor. Also provide interface for setting oneshot/continuous mode and > >>>> critical/warning threshold for the shunt voltage drop. > >>>> > >>>> Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> > >>> Hi Laxman, > >>> > >>> As ever with any driver lying on the border of IIO and hwmon, please > >>> include > >>> a short justification of why you need an IIO driver and also cc the > >>> hwmon list + maintainers. (cc'd on this reply). > >>> > >>> I simply won't take a driver where the hwmon maintainers aren't happy. > >>> As it stands I'm not seeing obvious reasons in the code for why this > >>> should be an IIO device. > >>> > >> > >> Me not either. > >> > >> I have a hwmon driver for the same chip pending from Andrew Davis (TI) > >> which I am just about to accept. We had directed Andrew back in April > >> to write a hwmon driver for the chip, which he did. > >> > > > > Thanks Guenter, I found the series > > > > [PATCH v2 0/2] Add support for INA3221 Triple Current/Voltage Monitors > > Looks fine to me. I can use the hwmon. > > > > If you search even further back you can see I originally went with an > IIO driver as well, comparing the IIO and hwmon version for the simple > use cases I needed the hwmod version turned out much simpler, so it was > probably the right framework for now. > > > > > However, some of the stuff from my patch are not there which I will add > > later once original patch applied: > > - Dynamic mode changes for continuous and one-shot from sysfs. > > - In one shot, when try to read voltage data, do conversion and then read. > > > > My attempt is rather minimal, to be honest I like your stab at this > driver better in some ways, especially relating to DT putting each > channel in its own node with labels, I think this is a bit more clean > and will be more extendable if/when new multi-channel monitor chips are > made. > > > Not sure whether exporting the following will help or not. Can you > > please confirm? > > - Oversampling time i.e. average sample > > - conversion time for bus voltage and shunt voltage if default is not > > suited for system. > > > > > > These can always be added as needed, but the DT changes are not as easy. > I would like it if this got in this cycle but if you think something > will be needed to help your improvements, that can not be added > incrementally, it would probably be best to get them into the initial DT > binding doc now. > Andrew, with this, the hwmon driver is pretty much on hold. What do you want me to do ? Should I wait for DT updates ? Guenter ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/3] iio: adc: ina3221: Add support for IIO ADC driver for TI INA3221 2016-06-07 22:30 ` Guenter Roeck @ 2016-06-08 15:04 ` Andrew F. Davis 2016-06-08 15:37 ` Laxman Dewangan 0 siblings, 1 reply; 14+ messages in thread From: Andrew F. Davis @ 2016-06-08 15:04 UTC (permalink / raw) To: Guenter Roeck Cc: Laxman Dewangan, Jonathan Cameron, robh+dt, corbet, lars, devicetree, linux-kernel, linux-doc, linux-iio, linux-hwmon, Jean Delvare On 06/07/2016 05:30 PM, Guenter Roeck wrote: > On Fri, Jun 03, 2016 at 10:17:55AM -0500, Andrew F. Davis wrote: >> On 06/03/2016 09:14 AM, Laxman Dewangan wrote: >>> >>> On Friday 03 June 2016 06:59 PM, Guenter Roeck wrote: >>>> On 06/03/2016 03:06 AM, Jonathan Cameron wrote: >>>>> On 01/06/16 13:34, Laxman Dewangan wrote: >>>>>> The INA3221 is a three-channel, high-side current and bus voltage >>>>>> monitor >>>>>> with an I2C interface from Texas Instruments. The INA3221 monitors both >>>>>> shunt voltage drops and bus supply voltages in addition to having >>>>>> programmable conversion times and averaging modes for these signals. >>>>>> The INA3221 offers both critical and warning alerts to detect multiple >>>>>> programmable out-of-range conditions for each channel. >>>>>> >>>>>> Add support for INA3221 SW driver via IIO ADC interface. The device is >>>>>> register as iio-device and provides interface for voltage/current >>>>>> and power >>>>>> monitor. Also provide interface for setting oneshot/continuous mode and >>>>>> critical/warning threshold for the shunt voltage drop. >>>>>> >>>>>> Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> >>>>> Hi Laxman, >>>>> >>>>> As ever with any driver lying on the border of IIO and hwmon, please >>>>> include >>>>> a short justification of why you need an IIO driver and also cc the >>>>> hwmon list + maintainers. (cc'd on this reply). >>>>> >>>>> I simply won't take a driver where the hwmon maintainers aren't happy. >>>>> As it stands I'm not seeing obvious reasons in the code for why this >>>>> should be an IIO device. >>>>> >>>> >>>> Me not either. >>>> >>>> I have a hwmon driver for the same chip pending from Andrew Davis (TI) >>>> which I am just about to accept. We had directed Andrew back in April >>>> to write a hwmon driver for the chip, which he did. >>>> >>> >>> Thanks Guenter, I found the series >>> >>> [PATCH v2 0/2] Add support for INA3221 Triple Current/Voltage Monitors >>> Looks fine to me. I can use the hwmon. >>> >> >> If you search even further back you can see I originally went with an >> IIO driver as well, comparing the IIO and hwmon version for the simple >> use cases I needed the hwmod version turned out much simpler, so it was >> probably the right framework for now. >> >>> >>> However, some of the stuff from my patch are not there which I will add >>> later once original patch applied: >>> - Dynamic mode changes for continuous and one-shot from sysfs. >>> - In one shot, when try to read voltage data, do conversion and then read. >>> >> >> My attempt is rather minimal, to be honest I like your stab at this >> driver better in some ways, especially relating to DT putting each >> channel in its own node with labels, I think this is a bit more clean >> and will be more extendable if/when new multi-channel monitor chips are >> made. >> >>> Not sure whether exporting the following will help or not. Can you >>> please confirm? >>> - Oversampling time i.e. average sample >>> - conversion time for bus voltage and shunt voltage if default is not >>> suited for system. >>> >>> >> >> These can always be added as needed, but the DT changes are not as easy. >> I would like it if this got in this cycle but if you think something >> will be needed to help your improvements, that can not be added >> incrementally, it would probably be best to get them into the initial DT >> binding doc now. >> > Andrew, > > with this, the hwmon driver is pretty much on hold. What do you want me to do ? > Should I wait for DT updates ? > If Laxman would like to commit to making these changes I do not problem waiting a bit to get this right the first time. If we don't hear back soon then I'd just take it as is and anything needing to be fixed can be later. Andrew ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/3] iio: adc: ina3221: Add support for IIO ADC driver for TI INA3221 2016-06-08 15:04 ` Andrew F. Davis @ 2016-06-08 15:37 ` Laxman Dewangan 0 siblings, 0 replies; 14+ messages in thread From: Laxman Dewangan @ 2016-06-08 15:37 UTC (permalink / raw) To: Andrew F. Davis, Guenter Roeck Cc: Jonathan Cameron, robh+dt, corbet, lars, devicetree, linux-kernel, linux-doc, linux-iio, linux-hwmon, Jean Delvare On Wednesday 08 June 2016 08:34 PM, Andrew F. Davis wrote: > On 06/07/2016 05:30 PM, Guenter Roeck wrote: >> On Fri, Jun 03, 2016 at 10:17:55AM -0500, Andrew F. Davis wrote: >>> On 06/03/2016 09:14 AM, Laxman Dewangan wrote: >>>> On Friday 03 June 2016 06:59 PM, Guenter Roeck wrote: >>>>> On 06/03/2016 03:06 AM, Jonathan Cameron wrote: >>>>>> On 01/06/16 13:34, Laxman Dewangan wrote: >>>>>>> The INA3221 is a three-channel, high-side current and bus voltage >>>>>>> monitor >>>>>>> with an I2C interface from Texas Instruments. The INA3221 monitors both >>>>>>> shunt voltage drops and bus supply voltages in addition to having >>>>>>> programmable conversion times and averaging modes for these signals. >>>>>>> The INA3221 offers both critical and warning alerts to detect multiple >>>>>>> programmable out-of-range conditions for each channel. >>>>>>> >>>>>>> Add support for INA3221 SW driver via IIO ADC interface. The device is >>>>>>> register as iio-device and provides interface for voltage/current >>>>>>> and power >>>>>>> monitor. Also provide interface for setting oneshot/continuous mode and >>>>>>> critical/warning threshold for the shunt voltage drop. >>>>>>> >>>>>>> Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> >>>>>> Hi Laxman, >>>>>> >>>>>> As ever with any driver lying on the border of IIO and hwmon, please >>>>>> include >>>>>> a short justification of why you need an IIO driver and also cc the >>>>>> hwmon list + maintainers. (cc'd on this reply). >>>>>> >>>>>> I simply won't take a driver where the hwmon maintainers aren't happy. >>>>>> As it stands I'm not seeing obvious reasons in the code for why this >>>>>> should be an IIO device. >>>>>> >>>>> Me not either. >>>>> >>>>> I have a hwmon driver for the same chip pending from Andrew Davis (TI) >>>>> which I am just about to accept. We had directed Andrew back in April >>>>> to write a hwmon driver for the chip, which he did. >>>>> >>>> Thanks Guenter, I found the series >>>> >>>> [PATCH v2 0/2] Add support for INA3221 Triple Current/Voltage Monitors >>>> Looks fine to me. I can use the hwmon. >>>> >>> If you search even further back you can see I originally went with an >>> IIO driver as well, comparing the IIO and hwmon version for the simple >>> use cases I needed the hwmod version turned out much simpler, so it was >>> probably the right framework for now. >>> >>>> However, some of the stuff from my patch are not there which I will add >>>> later once original patch applied: >>>> - Dynamic mode changes for continuous and one-shot from sysfs. >>>> - In one shot, when try to read voltage data, do conversion and then read. >>>> >>> My attempt is rather minimal, to be honest I like your stab at this >>> driver better in some ways, especially relating to DT putting each >>> channel in its own node with labels, I think this is a bit more clean >>> and will be more extendable if/when new multi-channel monitor chips are >>> made. >>> >>>> Not sure whether exporting the following will help or not. Can you >>>> please confirm? >>>> - Oversampling time i.e. average sample >>>> - conversion time for bus voltage and shunt voltage if default is not >>>> suited for system. >>>> >>>> >>> These can always be added as needed, but the DT changes are not as easy. >>> I would like it if this got in this cycle but if you think something >>> will be needed to help your improvements, that can not be added >>> incrementally, it would probably be best to get them into the initial DT >>> binding doc now. >>> >> Andrew, >> >> with this, the hwmon driver is pretty much on hold. What do you want me to do ? >> Should I wait for DT updates ? >> > If Laxman would like to commit to making these changes I do not problem > waiting a bit to get this right the first time. > > If we don't hear back soon then I'd just take it as is and anything > needing to be fixed can be later. > > I will be able to post the patch tomorrow. I think this series can be applied and then on top of it, I will post the dt changes and other my changes. As this is new driver and no one using now, it will be fine to have dt changes if everything complete in one cycle. I will work from the tree on which this applied to post my patches. ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2016-06-08 15:50 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <1464784454-7988-1-git-send-email-ldewangan@nvidia.com>
[not found] ` <1464784454-7988-2-git-send-email-ldewangan@nvidia.com>
2016-06-03 10:06 ` [PATCH 2/3] iio: adc: ina3221: Add support for IIO ADC driver for TI INA3221 Jonathan Cameron
2016-06-03 10:16 ` Jonathan Cameron
2016-06-03 11:31 ` Laxman Dewangan
2016-06-03 12:04 ` Jonathan Cameron
2016-06-03 12:03 ` Laxman Dewangan
2016-06-03 11:26 ` Laxman Dewangan
2016-06-03 12:09 ` Jonathan Cameron
2016-06-03 12:17 ` Laxman Dewangan
2016-06-03 13:29 ` Guenter Roeck
2016-06-03 14:14 ` Laxman Dewangan
2016-06-03 15:17 ` Andrew F. Davis
2016-06-07 22:30 ` Guenter Roeck
2016-06-08 15:04 ` Andrew F. Davis
2016-06-08 15:37 ` Laxman Dewangan
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox