/*
* INA2XX Current and Power Monitors
*
* Copyright 2015 Baylibre SAS.
*
* 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.
*
* Based on linux/drivers/iio/adc/ad7291.c
* Copyright 2010-2011 Analog Devices Inc.
*
* Based on linux/drivers/hwmon/ina2xx.c
* Copyright 2012 Lothar Felten <l-felten@ti.com>
*
* Licensed under the GPL-2 or later.
*
* IIO driver for INA219-220-226-230-231
*
* Configurable 7-bit I2C slave address from 0x40 to 0x4F
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/sysfs.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/sched/task.h>
#include <linux/util_macros.h>
#include <linux/platform_data/ina2xx.h>
/* INA2XX registers definition */
#define INA2XX_CONFIG 0x00
#define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */
#define INA2XX_BUS_VOLTAGE 0x02 /* readonly */
#define INA2XX_POWER 0x03 /* readonly */
#define INA2XX_CURRENT 0x04 /* readonly */
#define INA2XX_CALIBRATION 0x05
#define INA226_MASK_ENABLE 0x06
#define INA226_CVRF BIT(3)
#define INA2XX_MAX_REGISTERS 8
/* settings - depend on use case */
#define INA219_CONFIG_DEFAULT 0x399F /* PGA=1/8, BRNG=32V */
#define INA219_DEFAULT_IT 532
#define INA219_DEFAULT_BRNG 1 /* 32V */
#define INA219_DEFAULT_PGA 125 /* 1000/8 */
#define INA226_CONFIG_DEFAULT 0x4327
#define INA226_DEFAULT_AVG 4
#define INA226_DEFAULT_IT 1110
#define INA2XX_RSHUNT_DEFAULT 10000
/*
* bit masks for reading the settings in the configuration register
* FIXME: use regmap_fields.
*/
#define INA2XX_MODE_MASK GENMASK(3, 0)
/* Gain for VShunt: 1/8 (default), 1/4, 1/2, 1 */
#define INA219_PGA_MASK GENMASK(12, 11)
#define INA219_SHIFT_PGA(val) ((val) << 11)
/* VBus range: 32V (default), 16V */
#define INA219_BRNG_MASK BIT(13)
#define INA219_SHIFT_BRNG(val) ((val) << 13)
/* Averaging for VBus/VShunt/Power */
#define INA226_AVG_MASK GENMASK(11, 9)
#define INA226_SHIFT_AVG(val) ((val) << 9)
/* Integration time for VBus */
#define INA219_ITB_MASK GENMASK(10, 7)
#define INA219_SHIFT_ITB(val) ((val) << 7)
#define INA226_ITB_MASK GENMASK(8, 6)
#define INA226_SHIFT_ITB(val) ((val) << 6)
/* Integration time for VShunt */
#define INA219_ITS_MASK GENMASK(6, 3)
#define INA219_SHIFT_ITS(val) ((val) << 3)
#define INA226_ITS_MASK GENMASK(5, 3)
#define INA226_SHIFT_ITS(val) ((val) << 3)
/* INA219 Bus voltage register, low bits are flags */
#define INA219_OVF BIT(0)
#define INA219_CNVR BIT(1)
#define INA219_BUS_VOLTAGE_SHIFT 3
/* Cosmetic macro giving the sampling period for a full P=UxI cycle */
#define SAMPLING_PERIOD(c) ((c->int_time_vbus + c->int_time_vshunt) \
* c->avg)
static bool ina2xx_is_writeable_reg(struct device *dev, unsigned int reg)
{
return (reg == INA2XX_CONFIG) || (reg > INA2XX_CURRENT);
}
static bool ina2xx_is_volatile_reg(struct device *dev, unsigned int reg)
{
return (reg != INA2XX_CONFIG);
}
static inline bool is_signed_reg(unsigned int reg)
{
return (reg == INA2XX_SHUNT_VOLTAGE) || (reg == INA2XX_CURRENT);
}
static const struct regmap_config ina2xx_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.max_register = INA2XX_MAX_REGISTERS,
.writeable_reg = ina2xx_is_writeable_reg,
.volatile_reg = ina2xx_is_volatile_reg,
};
enum ina2xx_ids { ina219, ina226 };
struct ina2xx_config {
const char *name;
u16 config_default;
int calibration_value;
int shunt_voltage_lsb; /* nV */
int bus_voltage_shift; /* position of lsb */
int bus_voltage_lsb; /* uV */
/* fixed relation between current and power lsb, uW/uA */
int power_lsb_factor;
enum ina2xx_ids chip_id;
};
struct ina2xx_chip_info {
struct regmap *regmap;
struct task_struct *task;
const struct ina2xx_config *config;
struct mutex state_lock;
unsigned int shunt_resistor_uohm;
int avg;
int int_time_vbus; /* Bus voltage integration time uS */
int int_time_vshunt; /* Shunt voltage integration time uS */
int range_vbus; /* Bus voltage maximum in V */
int pga_gain_vshunt; /* Shunt voltage PGA gain */
bool allow_async_readout;
/* data buffer needs space for channel data and timestamp */
struct {
u16 chan[4];
u64 ts __aligned(8);
} scan;
};
static const struct ina2xx_config ina2xx_config[] = {
[ina219] = {
.name = "ina219",
.config_default = INA219_CONFIG_DEFAULT,
.calibration_value = 4096,
.shunt_voltage_lsb = 10000,
.bus_voltage_shift = INA219_BUS_VOLTAGE_SHIFT,
.bus_voltage_lsb = 4000,
.power_lsb_factor = 20,
.chip_id = ina219,
},
[ina226] = {
.name = "ina226",
.config_default = INA226_CONFIG_DEFAULT,
.calibration_value = 2048,
.shunt_voltage_lsb = 2500,
.bus_voltage_shift = 0,
.bus_voltage_lsb = 1250,
.power_lsb_factor = 25,
.chip_id = ina226,
},
};
static int ina2xx_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
int ret;
struct ina2xx_chip_info *chip = iio_priv(indio_dev);
unsigned int regval;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = regmap_read(chip->regmap, chan->address, ®val);
if (ret)
return ret;
if (is_signed_reg(chan->address))
*val = (s16) regval;
else
*val = regval;
if (chan->address == INA2XX_BUS_VOLTAGE)
*val >>= chip->config->