mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
hwmon: (pmbus/tps25990) Add TPS1689 support
Extend the existing TPS25990 driver to support the TPS1689 eFuse, as both devices share the same command interface and functionality. Update the documentation to include TPS1689 support. Signed-off-by: Stoyan Bogdanov <sbogdanov@baylibre.com> Link: https://patch.msgid.link/20260817101455.3526260-4-sbogdanov@baylibre.com Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
parent
5d41343dc3
commit
d49c6acb34
|
|
@ -9,26 +9,31 @@ Supported chips:
|
|||
|
||||
Prefix: 'tps25990'
|
||||
|
||||
* Datasheet
|
||||
Datasheet: Publicly available at Texas Instruments website: https://www.ti.com/lit/gpn/tps25990
|
||||
|
||||
Publicly available at Texas Instruments website: https://www.ti.com/lit/gpn/tps25990
|
||||
* TI TPS1689
|
||||
|
||||
Prefix: 'tps1689'
|
||||
|
||||
Datasheet: Publicly available at Texas Instruments website: https://www.ti.com/lit/gpn/tps1689
|
||||
|
||||
Author:
|
||||
|
||||
Jerome Brunet <jbrunet@baylibre.com>
|
||||
Stoyan Bogdanov <sbogdanov@baylibre.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver implements support for TI TPS25990 eFuse.
|
||||
This driver implements support for TI TPS25990 and TI TPS1689 eFuse chips.
|
||||
This is an integrated, high-current circuit protection and power
|
||||
management device with PMBUS interface
|
||||
|
||||
Device compliant with:
|
||||
Devices are compliant with:
|
||||
|
||||
- PMBus rev 1.3 interface.
|
||||
|
||||
Device supports direct format for reading input voltages,
|
||||
Devices supports direct format for reading input voltages,
|
||||
output voltage, input current, input power and temperature.
|
||||
|
||||
Due to the specificities of the chip, all history reset attributes
|
||||
|
|
|
|||
|
|
@ -47,7 +47,14 @@
|
|||
PK_MIN_AVG_RST_AVG | \
|
||||
PK_MIN_AVG_RST_MIN)
|
||||
|
||||
#define TPS1689_VIN_OV_RANGE_SEL_MASK GENMASK(7, 6)
|
||||
#define TPS1689_VIN_VOV_MASK GENMASK(5, 0)
|
||||
#define TPS1689_VIN_SCALING 251
|
||||
#define TPS1689_VIN_VOV_STEP_MV 250
|
||||
#define TPS1689_VIN_RANGE_SPAN_MV 16000
|
||||
|
||||
enum chips {
|
||||
tps1689,
|
||||
tps25990,
|
||||
};
|
||||
|
||||
|
|
@ -105,6 +112,8 @@ static int tps25990_mfr_write_protect_get(struct i2c_client *client)
|
|||
static int tps25990_read_word_data(struct i2c_client *client,
|
||||
int page, int phase, int reg)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct tps25990_data *data = container_of(info, struct tps25990_data, info);
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
|
|
@ -193,9 +202,18 @@ static int tps25990_read_word_data(struct i2c_client *client,
|
|||
ret = pmbus_read_word_data(client, page, phase, reg);
|
||||
if (ret < 0)
|
||||
break;
|
||||
ret = DIV_ROUND_CLOSEST(ret * TPS25990_VIN_OVF_NUM,
|
||||
TPS25990_VIN_OVF_DIV);
|
||||
ret += TPS25990_VIN_OVF_OFF;
|
||||
if (data->chip_id == tps25990) {
|
||||
ret = DIV_ROUND_CLOSEST(ret * TPS25990_VIN_OVF_NUM,
|
||||
TPS25990_VIN_OVF_DIV);
|
||||
ret += TPS25990_VIN_OVF_OFF;
|
||||
} else if (data->chip_id == tps1689) {
|
||||
int rng = (FIELD_GET(TPS1689_VIN_OV_RANGE_SEL_MASK, ret) + 1) *
|
||||
TPS1689_VIN_RANGE_SPAN_MV;
|
||||
int vov = FIELD_GET(TPS1689_VIN_VOV_MASK, ret) * TPS1689_VIN_VOV_STEP_MV;
|
||||
|
||||
ret = DIV_ROUND_CLOSEST(rng + vov - TPS1689_VIN_RANGE_SPAN_MV,
|
||||
TPS1689_VIN_SCALING);
|
||||
}
|
||||
break;
|
||||
|
||||
case PMBUS_IIN_OC_FAULT_LIMIT:
|
||||
|
|
@ -238,6 +256,8 @@ static int tps25990_read_word_data(struct i2c_client *client,
|
|||
static int tps25990_write_word_data(struct i2c_client *client,
|
||||
int page, int reg, u16 value)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct tps25990_data *data = container_of(info, struct tps25990_data, info);
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
|
|
@ -249,26 +269,52 @@ static int tps25990_write_word_data(struct i2c_client *client,
|
|||
case PMBUS_OT_WARN_LIMIT:
|
||||
case PMBUS_OT_FAULT_LIMIT:
|
||||
case PMBUS_PIN_OP_WARN_LIMIT:
|
||||
value >>= TPS25990_8B_SHIFT;
|
||||
value = clamp_val((s16)value, 0, S16_MAX) >> TPS25990_8B_SHIFT;
|
||||
value = clamp_val(value, 0, 0xff);
|
||||
ret = pmbus_write_word_data(client, page, reg, value);
|
||||
break;
|
||||
|
||||
case PMBUS_VIN_OV_FAULT_LIMIT:
|
||||
value -= TPS25990_VIN_OVF_OFF;
|
||||
value = DIV_ROUND_CLOSEST(((unsigned int)value) * TPS25990_VIN_OVF_DIV,
|
||||
TPS25990_VIN_OVF_NUM);
|
||||
value = clamp_val(value, 0, 0xf);
|
||||
if ((s16)value < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (data->chip_id == tps25990) {
|
||||
int tmp = (int)value - TPS25990_VIN_OVF_OFF;
|
||||
|
||||
tmp = clamp_val(tmp, 0, INT_MAX);
|
||||
value = DIV_ROUND_CLOSEST((unsigned int)tmp * TPS25990_VIN_OVF_DIV,
|
||||
TPS25990_VIN_OVF_NUM);
|
||||
value = clamp_val(value, 0, 0xf);
|
||||
} else if (data->chip_id == tps1689) {
|
||||
u32 scaled_value = value * TPS1689_VIN_SCALING + TPS1689_VIN_RANGE_SPAN_MV;
|
||||
u32 rng_idx = scaled_value / TPS1689_VIN_RANGE_SPAN_MV;
|
||||
u32 ov_set;
|
||||
|
||||
rng_idx = clamp_val(rng_idx, 1,
|
||||
FIELD_MAX(TPS1689_VIN_OV_RANGE_SEL_MASK) + 1);
|
||||
ov_set = scaled_value - (TPS1689_VIN_RANGE_SPAN_MV * rng_idx);
|
||||
ov_set = min_t(u32, ov_set / TPS1689_VIN_VOV_STEP_MV,
|
||||
FIELD_MAX(TPS1689_VIN_VOV_MASK));
|
||||
value = FIELD_PREP(TPS1689_VIN_OV_RANGE_SEL_MASK, rng_idx - 1) |
|
||||
FIELD_PREP(TPS1689_VIN_VOV_MASK, ov_set);
|
||||
}
|
||||
ret = pmbus_write_word_data(client, page, reg, value);
|
||||
break;
|
||||
|
||||
case PMBUS_IIN_OC_FAULT_LIMIT:
|
||||
value -= TPS25990_IIN_OCF_OFF;
|
||||
value = DIV_ROUND_CLOSEST(((unsigned int)value) * TPS25990_IIN_OCF_DIV,
|
||||
case PMBUS_IIN_OC_FAULT_LIMIT: {
|
||||
int tmp;
|
||||
|
||||
if ((s16)value < 0)
|
||||
return -EINVAL;
|
||||
|
||||
tmp = (int)value - TPS25990_IIN_OCF_OFF;
|
||||
tmp = clamp_val(tmp, 0, INT_MAX);
|
||||
value = DIV_ROUND_CLOSEST((unsigned int)tmp * TPS25990_IIN_OCF_DIV,
|
||||
TPS25990_IIN_OCF_NUM);
|
||||
value = clamp_val(value, 0, 0x3f);
|
||||
ret = pmbus_write_byte_data(client, page, TPS25990_VIREF, value);
|
||||
break;
|
||||
}
|
||||
|
||||
case PMBUS_VIRT_SAMPLES:
|
||||
value = clamp_val(value, 1, 1 << PK_MIN_AVG_AVG_CNT);
|
||||
|
|
@ -347,6 +393,60 @@ static const struct regulator_desc tps25990_reg_desc[] = {
|
|||
#endif
|
||||
|
||||
static const struct pmbus_driver_info tps25990_base_info[] = {
|
||||
[tps1689] = {
|
||||
.pages = 1,
|
||||
.format[PSC_VOLTAGE_IN] = direct,
|
||||
.m[PSC_VOLTAGE_IN] = 3984,
|
||||
.b[PSC_VOLTAGE_IN] = -63750,
|
||||
.R[PSC_VOLTAGE_IN] = -3,
|
||||
.format[PSC_VOLTAGE_OUT] = direct,
|
||||
.m[PSC_VOLTAGE_OUT] = 1166,
|
||||
.b[PSC_VOLTAGE_OUT] = 0,
|
||||
.R[PSC_VOLTAGE_OUT] = -2,
|
||||
.format[PSC_TEMPERATURE] = direct,
|
||||
.m[PSC_TEMPERATURE] = 140,
|
||||
.b[PSC_TEMPERATURE] = 32103,
|
||||
.R[PSC_TEMPERATURE] = -2,
|
||||
/*
|
||||
* Current and Power measurement depends on the ohm value
|
||||
* of Rimon. m is multiplied by 1000 below to have an integer
|
||||
* and -3 is added to R to compensate.
|
||||
*/
|
||||
.format[PSC_CURRENT_IN] = direct,
|
||||
.m[PSC_CURRENT_IN] = 9548,
|
||||
.b[PSC_CURRENT_IN] = 0,
|
||||
.R[PSC_CURRENT_IN] = -6,
|
||||
.format[PSC_CURRENT_OUT] = direct,
|
||||
.m[PSC_CURRENT_OUT] = 24347,
|
||||
.b[PSC_CURRENT_OUT] = 0,
|
||||
.R[PSC_CURRENT_OUT] = -3,
|
||||
.format[PSC_POWER] = direct,
|
||||
.m[PSC_POWER] = 2775,
|
||||
.b[PSC_POWER] = 0,
|
||||
.R[PSC_POWER] = -4,
|
||||
.func[0] = (PMBUS_HAVE_VIN |
|
||||
PMBUS_HAVE_VOUT |
|
||||
PMBUS_HAVE_VMON |
|
||||
PMBUS_HAVE_IIN |
|
||||
PMBUS_HAVE_IOUT |
|
||||
PMBUS_HAVE_PIN |
|
||||
PMBUS_HAVE_TEMP |
|
||||
PMBUS_HAVE_STATUS_VOUT |
|
||||
PMBUS_HAVE_STATUS_IOUT |
|
||||
PMBUS_HAVE_STATUS_INPUT |
|
||||
PMBUS_HAVE_STATUS_TEMP |
|
||||
PMBUS_HAVE_SAMPLES),
|
||||
|
||||
.read_word_data = tps25990_read_word_data,
|
||||
.write_word_data = tps25990_write_word_data,
|
||||
.read_byte_data = tps25990_read_byte_data,
|
||||
.write_byte_data = tps25990_write_byte_data,
|
||||
|
||||
#if IS_ENABLED(CONFIG_SENSORS_TPS25990_REGULATOR)
|
||||
.reg_desc = tps25990_reg_desc,
|
||||
.num_regulators = ARRAY_SIZE(tps25990_reg_desc),
|
||||
#endif
|
||||
},
|
||||
[tps25990] = {
|
||||
.pages = 1,
|
||||
.format[PSC_VOLTAGE_IN] = direct,
|
||||
|
|
@ -389,7 +489,6 @@ static const struct pmbus_driver_info tps25990_base_info[] = {
|
|||
.write_word_data = tps25990_write_word_data,
|
||||
.read_byte_data = tps25990_read_byte_data,
|
||||
.write_byte_data = tps25990_write_byte_data,
|
||||
|
||||
#if IS_ENABLED(CONFIG_SENSORS_TPS25990_REGULATOR)
|
||||
.reg_desc = tps25990_reg_desc,
|
||||
.num_regulators = ARRAY_SIZE(tps25990_reg_desc),
|
||||
|
|
@ -398,12 +497,14 @@ static const struct pmbus_driver_info tps25990_base_info[] = {
|
|||
};
|
||||
|
||||
static const struct i2c_device_id tps25990_i2c_id[] = {
|
||||
{ .name = "tps1689", .driver_data = tps1689 },
|
||||
{ .name = "tps25990", .driver_data = tps25990 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tps25990_i2c_id);
|
||||
|
||||
static const struct of_device_id tps25990_of_match[] = {
|
||||
{ .compatible = "ti,tps1689", .data = (void *)tps1689 },
|
||||
{ .compatible = "ti,tps25990", .data = (void *)tps25990 },
|
||||
{}
|
||||
};
|
||||
|
|
@ -438,6 +539,7 @@ static int tps25990_probe(struct i2c_client *client)
|
|||
|
||||
/* Adapt the current and power scale for each instance */
|
||||
tps25990_set_m(&data->info.m[PSC_CURRENT_IN], rimon);
|
||||
tps25990_set_m(&data->info.m[PSC_CURRENT_OUT], rimon);
|
||||
tps25990_set_m(&data->info.m[PSC_POWER], rimon);
|
||||
|
||||
return pmbus_do_probe(client, &data->info);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user