mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 04:02:20 +00:00
hwmon: (ina226) Add support for SY24655
SY24655: Support for current and voltage detection as well as power calculation. Signed-off-by: Wenliang Yan <wenliang202407@163.com> Message-ID: <20241106150547.2538-1-wenliang202407@163.com> [groeck: Changed order of compatible entries; dropped spurious extra return statement in is_visible(); fixed code problems] Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
parent
0196d07f0e
commit
52172ad87a
@ -63,6 +63,17 @@ Supported chips:
|
||||
|
||||
https://www.ti.com/
|
||||
|
||||
* Silergy SY24655
|
||||
|
||||
Prefix: 'sy24655'
|
||||
|
||||
Addresses: I2C 0x40 - 0x4f
|
||||
|
||||
Datasheet: Publicly available at the Silergy website
|
||||
|
||||
https://us1.silergy.com/
|
||||
|
||||
|
||||
Author: Lothar Felten <lothar.felten@gmail.com>
|
||||
|
||||
Description
|
||||
@ -85,6 +96,11 @@ bus supply voltage.
|
||||
INA260 is a high or low side current and power monitor with integrated shunt
|
||||
resistor.
|
||||
|
||||
The SY24655 is a high- and low-side current shunt and power monitor with an I2C
|
||||
interface. The SY24655 supports both shunt drop and supply voltage, with
|
||||
programmable calibration value and conversion times. The SY24655 can also
|
||||
calculate average power for use in energy conversion.
|
||||
|
||||
The shunt value in micro-ohms can be set via platform data or device tree at
|
||||
compile-time or via the shunt_resistor attribute in sysfs at run-time. Please
|
||||
refer to the Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml for bindings
|
||||
@ -108,8 +124,8 @@ power1_input Power(uW) measurement channel
|
||||
shunt_resistor Shunt resistance(uOhm) channel (not for ina260)
|
||||
======================= ===============================================
|
||||
|
||||
Additional sysfs entries for ina226, ina230, ina231, and ina260
|
||||
---------------------------------------------------------------
|
||||
Additional sysfs entries for ina226, ina230, ina231, ina260, and sy24655
|
||||
------------------------------------------------------------------------
|
||||
|
||||
======================= ====================================================
|
||||
curr1_lcrit Critical low current
|
||||
@ -130,6 +146,13 @@ update_interval data conversion time; affects number of samples used
|
||||
to average results for shunt and bus voltages.
|
||||
======================= ====================================================
|
||||
|
||||
Sysfs entries for sy24655 only
|
||||
------------------------------
|
||||
|
||||
======================= ====================================================
|
||||
power1_average average power from last reading to the present.
|
||||
======================= ====================================================
|
||||
|
||||
.. note::
|
||||
|
||||
- Configure `shunt_resistor` before configure `power1_crit`, because power
|
||||
|
@ -2189,7 +2189,7 @@ config SENSORS_INA2XX
|
||||
select REGMAP_I2C
|
||||
help
|
||||
If you say yes here you get support for INA219, INA220, INA226,
|
||||
INA230, INA231, and INA260 power monitor chips.
|
||||
INA230, INA231, INA260, and SY24655 power monitor chips.
|
||||
|
||||
The INA2xx driver is configured for the default configuration of
|
||||
the part as described in the datasheet.
|
||||
|
@ -51,12 +51,19 @@
|
||||
#define INA226_ALERT_LIMIT 0x07
|
||||
#define INA226_DIE_ID 0xFF
|
||||
|
||||
#define INA2XX_MAX_REGISTERS 8
|
||||
/* SY24655 register definitions */
|
||||
#define SY24655_EIN 0x0A
|
||||
#define SY24655_ACCUM_CONFIG 0x0D
|
||||
#define INA2XX_MAX_REGISTERS 0x0D
|
||||
|
||||
/* settings - depend on use case */
|
||||
#define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */
|
||||
#define INA226_CONFIG_DEFAULT 0x4527 /* averages=16 */
|
||||
#define INA260_CONFIG_DEFAULT 0x6527 /* averages=16 */
|
||||
#define SY24655_CONFIG_DEFAULT 0x4527 /* averages=16 */
|
||||
|
||||
/* (only for sy24655) */
|
||||
#define SY24655_ACCUM_CONFIG_DEFAULT 0x044C /* continuous mode, clear after read*/
|
||||
|
||||
/* worst case is 68.10 ms (~14.6Hz, ina219) */
|
||||
#define INA2XX_CONVERSION_RATE 15
|
||||
@ -97,6 +104,7 @@ static bool ina2xx_writeable_reg(struct device *dev, unsigned int reg)
|
||||
case INA2XX_CALIBRATION:
|
||||
case INA226_MASK_ENABLE:
|
||||
case INA226_ALERT_LIMIT:
|
||||
case SY24655_ACCUM_CONFIG:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@ -127,12 +135,13 @@ static const struct regmap_config ina2xx_regmap_config = {
|
||||
.writeable_reg = ina2xx_writeable_reg,
|
||||
};
|
||||
|
||||
enum ina2xx_ids { ina219, ina226, ina260 };
|
||||
enum ina2xx_ids { ina219, ina226, ina260, sy24655 };
|
||||
|
||||
struct ina2xx_config {
|
||||
u16 config_default;
|
||||
bool has_alerts; /* chip supports alerts and limits */
|
||||
bool has_ishunt; /* chip has internal shunt resistor */
|
||||
bool has_power_average; /* chip has internal shunt resistor */
|
||||
int calibration_value;
|
||||
int shunt_div;
|
||||
int bus_voltage_shift;
|
||||
@ -149,6 +158,7 @@ struct ina2xx_data {
|
||||
long power_lsb_uW;
|
||||
struct mutex config_lock;
|
||||
struct regmap *regmap;
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
static const struct ina2xx_config ina2xx_config[] = {
|
||||
@ -161,6 +171,7 @@ static const struct ina2xx_config ina2xx_config[] = {
|
||||
.power_lsb_factor = 20,
|
||||
.has_alerts = false,
|
||||
.has_ishunt = false,
|
||||
.has_power_average = false,
|
||||
},
|
||||
[ina226] = {
|
||||
.config_default = INA226_CONFIG_DEFAULT,
|
||||
@ -171,6 +182,7 @@ static const struct ina2xx_config ina2xx_config[] = {
|
||||
.power_lsb_factor = 25,
|
||||
.has_alerts = true,
|
||||
.has_ishunt = false,
|
||||
.has_power_average = false,
|
||||
},
|
||||
[ina260] = {
|
||||
.config_default = INA260_CONFIG_DEFAULT,
|
||||
@ -180,6 +192,18 @@ static const struct ina2xx_config ina2xx_config[] = {
|
||||
.power_lsb_factor = 8,
|
||||
.has_alerts = true,
|
||||
.has_ishunt = true,
|
||||
.has_power_average = false,
|
||||
},
|
||||
[sy24655] = {
|
||||
.config_default = SY24655_CONFIG_DEFAULT,
|
||||
.calibration_value = 4096,
|
||||
.shunt_div = 400,
|
||||
.bus_voltage_shift = 0,
|
||||
.bus_voltage_lsb = 1250,
|
||||
.power_lsb_factor = 25,
|
||||
.has_alerts = true,
|
||||
.has_ishunt = false,
|
||||
.has_power_average = true,
|
||||
},
|
||||
};
|
||||
|
||||
@ -485,6 +509,41 @@ static int ina2xx_in_read(struct device *dev, u32 attr, int channel, long *val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configuring the READ_EIN (bit 10) of the ACCUM_CONFIG register to 1
|
||||
* can clear accumulator and sample_count after reading the EIN register.
|
||||
* This way, the average power between the last read and the current
|
||||
* read can be obtained. By combining with accurate time data from
|
||||
* outside, the energy consumption during that period can be calculated.
|
||||
*/
|
||||
static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *val)
|
||||
{
|
||||
u8 template[6];
|
||||
int ret;
|
||||
long accumulator_24, sample_count;
|
||||
|
||||
/* 48-bit register read */
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client, reg, 6, template);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != 6)
|
||||
return -EIO;
|
||||
accumulator_24 = ((template[3] << 16) |
|
||||
(template[4] << 8) |
|
||||
template[5]);
|
||||
sample_count = ((template[0] << 16) |
|
||||
(template[1] << 8) |
|
||||
template[2]);
|
||||
if (sample_count <= 0) {
|
||||
*val = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*val = DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ina2xx_power_read(struct device *dev, u32 attr, long *val)
|
||||
{
|
||||
struct ina2xx_data *data = dev_get_drvdata(dev);
|
||||
@ -492,6 +551,8 @@ static int ina2xx_power_read(struct device *dev, u32 attr, long *val)
|
||||
switch (attr) {
|
||||
case hwmon_power_input:
|
||||
return ina2xx_read_init(dev, INA2XX_POWER, val);
|
||||
case hwmon_power_average:
|
||||
return sy24655_average_power_read(data, SY24655_EIN, val);
|
||||
case hwmon_power_crit:
|
||||
return ina226_alert_limit_read(data, INA226_POWER_OVER_LIMIT_MASK,
|
||||
INA2XX_POWER, val);
|
||||
@ -651,6 +712,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
|
||||
{
|
||||
const struct ina2xx_data *data = _data;
|
||||
bool has_alerts = data->config->has_alerts;
|
||||
bool has_power_average = data->config->has_power_average;
|
||||
enum ina2xx_ids chip = data->chip;
|
||||
|
||||
switch (type) {
|
||||
@ -702,6 +764,10 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
|
||||
if (has_alerts)
|
||||
return 0444;
|
||||
break;
|
||||
case hwmon_power_average:
|
||||
if (has_power_average)
|
||||
return 0444;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -734,7 +800,8 @@ static const struct hwmon_channel_info * const ina2xx_info[] = {
|
||||
HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_CRIT | HWMON_C_CRIT_ALARM |
|
||||
HWMON_C_LCRIT | HWMON_C_LCRIT_ALARM),
|
||||
HWMON_CHANNEL_INFO(power,
|
||||
HWMON_P_INPUT | HWMON_P_CRIT | HWMON_P_CRIT_ALARM),
|
||||
HWMON_P_INPUT | HWMON_P_CRIT | HWMON_P_CRIT_ALARM |
|
||||
HWMON_P_AVERAGE),
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -839,6 +906,19 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data)
|
||||
INA226_ALERT_LATCH_ENABLE |
|
||||
FIELD_PREP(INA226_ALERT_POLARITY, active_high));
|
||||
}
|
||||
if (data->config->has_power_average) {
|
||||
if (data->chip == sy24655) {
|
||||
/*
|
||||
* Initialize the power accumulation method to continuous
|
||||
* mode and clear the EIN register after each read of the
|
||||
* EIN register
|
||||
*/
|
||||
ret = regmap_write(regmap, SY24655_ACCUM_CONFIG,
|
||||
SY24655_ACCUM_CONFIG_DEFAULT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (data->config->has_ishunt)
|
||||
return 0;
|
||||
@ -868,6 +948,7 @@ static int ina2xx_probe(struct i2c_client *client)
|
||||
return -ENOMEM;
|
||||
|
||||
/* set the device type */
|
||||
data->client = client;
|
||||
data->config = &ina2xx_config[chip];
|
||||
data->chip = chip;
|
||||
mutex_init(&data->config_lock);
|
||||
@ -906,11 +987,16 @@ static const struct i2c_device_id ina2xx_id[] = {
|
||||
{ "ina230", ina226 },
|
||||
{ "ina231", ina226 },
|
||||
{ "ina260", ina260 },
|
||||
{ "sy24655", sy24655 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ina2xx_id);
|
||||
|
||||
static const struct of_device_id __maybe_unused ina2xx_of_match[] = {
|
||||
{
|
||||
.compatible = "silergy,sy24655",
|
||||
.data = (void *)sy24655
|
||||
},
|
||||
{
|
||||
.compatible = "ti,ina219",
|
||||
.data = (void *)ina219
|
||||
@ -935,7 +1021,7 @@ static const struct of_device_id __maybe_unused ina2xx_of_match[] = {
|
||||
.compatible = "ti,ina260",
|
||||
.data = (void *)ina260
|
||||
},
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ina2xx_of_match);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user