hwmon: (pmbus/tps53679) Add support for TPS536C7

TPS536C7 is a dual-channel D-CAP+ step-down controller whose channels are
distributed across a configurable number of phases (N+M, up to 12 total).
Whether channel B (PMBus page 1) exists depends on the phase configuration
register, so the page count is derived at probe time.

Factor the TPS53676 phase-map parsing into a shared helper and reuse it
for TPS536C7. Only aggregate per-channel telemetry is exposed: leave
info->phases[] unset so the PMBus core never programs the PHASE selector
on every read (and because TPS536C7 can place up to 12 phases on channel
A, exceeding PMBUS_PHASES). Report VOUT in linear format like TPS53676.

Since the core never programs PHASE, set it to 0xff on each populated
page so READ_IOUT reports the aggregate channel current, and verify it
(PHASE can be write-protected). Fail identification if aggregate
selection cannot be established, since otherwise a single phase current
could be reported as the channel total.

A configuration with no channel-A phase is rejected with -EOPNOTSUPP:
the PMBus core models pages as the contiguous range 0..pages-1 and
cannot represent a channel-A-absent topology.

On the tested TPS536C7 (device ID 54 49 53 6c 70 00, CAPABILITY 0xd0),
block reads of IC_DEVICE_ID and USER_DATA_03 return -EBADMSG with PEC
enabled, while retrying those two reads without PEC returns the expected
6- and 24-byte payloads. Standard byte/word telemetry keeps using PEC,
so only those two identification reads fall back to no-PEC.

Co-developed-by: Senthil Muniyappan <smuniyappan@juniper.net>
Co-developed-by: Vaibhav Agarwal <avaibhav@juniper.net>
Signed-off-by: Sanman Pradhan <psanman@juniper.net>
Link: https://patch.msgid.link/20260915164823.160977-4-sanman.pradhan@hpe.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
Sanman Pradhan 2026-09-15 16:48:46 +00:00 committed by Guenter Roeck
parent 1001be13fa
commit f173bf7161
3 changed files with 134 additions and 19 deletions

View File

@ -75,6 +75,14 @@ Supported chips:
Datasheet: Available under NDA
* Texas Instruments TPS536C7
Prefix: 'tps536c7'
Addresses scanned: -
Datasheet: https://www.ti.com/lit/gpn/TPS536C7
Authors:
Vadim Pasternak <vadimp@mellanox.com>
@ -85,7 +93,12 @@ Description
-----------
Chips in this series are multi-phase step-down converters with one or two
output channels and up to 8 phases per channel.
output channels and up to 12 phases in total, depending on the device.
For TPS536C7 only aggregate per-channel telemetry is exposed; the
individual per-phase output currents the hardware can report are not.
Attributes for the second output channel are present only when channel B
is configured.
Usage Notes
@ -124,7 +137,7 @@ in1_crit_alarm Input voltage critical high alarm.
in[N]_label "vout[1-2]"
- TPS53647, TPS53667: N=2
- TPS53622, TPS53659, TPS53679, TPS53688: N=2,3
- TPS53622, TPS53659, TPS53679, TPS53688, TPS536C7: N=2,3
in[N]_input Measured output voltage.
@ -151,7 +164,7 @@ in[N]_crit_alarm Output voltage critical high alarm.
temp[N]_input Measured temperature.
- TPS53647, TPS53667: N=1
- TPS53622, TPS53659, TPS53679, TPS53681, TPS53688: N=1,2
- TPS53622, TPS53659, TPS53679, TPS53681, TPS53688, TPS536C7: N=1,2
temp[N]_max Maximum temperature.
@ -168,7 +181,7 @@ power1_input Measured input power.
power[N]_label "pout[1-2]".
- TPS53647, TPS53667: N=2
- TPS53622, TPS53659, TPS53676, TPS53679, TPS53681, TPS53688: N=2,3
- TPS53622, TPS53659, TPS53676, TPS53679, TPS53681, TPS53688, TPS536C7: N=2,3
power[N]_input Measured output power.
@ -191,7 +204,7 @@ curr[N]_label "iout[1-2]" or "iout1.[0-5]".
telemetry supported on TPS53676 and TPS53681 only.
- TPS53647, TPS53667: N=2
- TPS53622, TPS53659, TPS53679, TPS53688: N=2,3
- TPS53622, TPS53659, TPS53679, TPS53688, TPS536C7: N=2,3
- TPS53676: N=2-8
- TPS53681: N=2-9

View File

@ -813,7 +813,7 @@ config SENSORS_TPS53679
help
If you say yes here you get hardware monitoring support for TI
TPS53622, TPS53647, TPS53659, TPS53667, TPS53676, TPS53679, TPS53681,
TPS53685, and TPS53688.
TPS53685, TPS53688, and TPS536C7.
This driver can also be built as a module. If so, the module will
be called tps53679.

View File

@ -17,13 +17,20 @@
enum chips {
tps53622, tps53647, tps53659, tps53667, tps53676, tps53679, tps53681,
tps53685, tps53688
tps53685, tps53688, tps536c7
};
#define TPS53647_PAGE_NUM 1
#define TPS53676_USER_DATA_03 0xb3
#define TPS536XX_PHASE_CONFIG 0xb3
#define TPS53676_MAX_PHASES 7
#define TPS536C7_MAX_PHASES 12
#define TPS536XX_PHASE_ENABLE BIT(7)
#define TPS536XX_PHASE_PAGE BIT(4)
#define TPS53676_DEVICE_ID "TI\x53\x67\x60\x00"
#define TPS536C7_DEVICE_ID "TI\x53\x6c\x70\x00"
#define TPS53679_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */
#define TPS53679_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */
@ -166,34 +173,72 @@ static int tps53681_identify(struct i2c_client *client,
TPS53681_DEVICE_ID);
}
static int tps53676_identify(struct i2c_client *client,
struct pmbus_driver_info *info)
static int tps536xx_read_block(struct i2c_client *client, u8 reg, u8 *buf,
bool retry_without_pec)
{
int ret = i2c_smbus_read_block_data(client, reg, buf);
/*
* Some TPS536C7 samples return an invalid PEC on the device-ID and
* phase-configuration block reads. Retry once without PEC; ordinary
* telemetry keeps using PEC.
*/
if (ret == -EBADMSG && retry_without_pec &&
(client->flags & I2C_CLIENT_PEC)) {
client->flags &= ~I2C_CLIENT_PEC;
ret = i2c_smbus_read_block_data(client, reg, buf);
client->flags |= I2C_CLIENT_PEC;
}
return ret;
}
static int tps536xx_read_phases(struct i2c_client *client,
const char *device_id, int max_phases,
bool retry_without_pec, int *phases_a,
int *phases_b)
{
u8 buf[I2C_SMBUS_BLOCK_MAX];
int phases_a = 0, phases_b = 0;
int i, ret;
ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf);
ret = tps536xx_read_block(client, PMBUS_IC_DEVICE_ID, buf,
retry_without_pec);
if (ret < 0)
return ret;
if (ret != 6 || memcmp(buf, "TI\x53\x67\x60\x00", 6)) {
if (ret != 6 || memcmp(buf, device_id, 6)) {
dev_err(&client->dev, "Unexpected device ID: %*ph\n", ret, buf);
return -ENODEV;
}
ret = i2c_smbus_read_block_data(client, TPS53676_USER_DATA_03, buf);
ret = tps536xx_read_block(client, TPS536XX_PHASE_CONFIG, buf,
retry_without_pec);
if (ret < 0)
return ret;
if (ret != 24)
return -EIO;
for (i = 0; i < 2 * TPS53676_MAX_PHASES; i += 2) {
if (buf[i + 1] & 0x80) {
if (buf[i] & BIT(4))
phases_b++;
*phases_a = 0;
*phases_b = 0;
for (i = 0; i < 2 * max_phases; i += 2) {
if (buf[i + 1] & TPS536XX_PHASE_ENABLE) {
if (buf[i] & TPS536XX_PHASE_PAGE)
(*phases_b)++;
else
phases_a++;
(*phases_a)++;
}
}
return 0;
}
static int tps53676_identify(struct i2c_client *client,
struct pmbus_driver_info *info)
{
int phases_a, phases_b, ret;
ret = tps536xx_read_phases(client, TPS53676_DEVICE_ID,
TPS53676_MAX_PHASES, false,
&phases_a, &phases_b);
if (ret)
return ret;
info->format[PSC_VOLTAGE_OUT] = linear;
info->pages = 1;
@ -214,6 +259,58 @@ static int tps53676_identify(struct i2c_client *client,
return 0;
}
static int tps536c7_identify(struct i2c_client *client,
struct pmbus_driver_info *info)
{
int phases_a, phases_b, page, ret;
ret = tps536xx_read_phases(client, TPS536C7_DEVICE_ID,
TPS536C7_MAX_PHASES, true,
&phases_a, &phases_b);
if (ret)
return ret;
if (!phases_a) {
dev_err(&client->dev,
"TPS536C7 without channel A is not supported\n");
return -EOPNOTSUPP;
}
info->format[PSC_VOLTAGE_OUT] = linear;
/*
* TPS536C7 can place up to 12 phases on channel A, which exceeds
* PMBUS_PHASES. Report aggregate per-channel telemetry only and do
* not populate info->phases[].
*/
info->pages = phases_b ? 2 : 1;
/*
* With info->phases[] left unset the PMBus core never programs the
* PHASE selector, so make sure each page reports the aggregate
* current (PHASE = 0xff) rather than whatever a previous boot left.
*/
for (page = 0; page < info->pages; page++) {
ret = pmbus_read_byte_data(client, page, PMBUS_PHASE);
if (ret < 0)
return ret;
if (ret == 0xff)
continue;
ret = pmbus_write_byte_data(client, page, PMBUS_PHASE, 0xff);
if (ret < 0)
return ret;
/* PHASE may be write-protected; confirm it actually changed. */
ret = pmbus_read_byte_data(client, page, PMBUS_PHASE);
if (ret < 0)
return ret;
if (ret != 0xff) {
dev_err(&client->dev,
"failed to select aggregate PHASE on page %d\n",
page);
return -EIO;
}
}
return 0;
}
static int tps53681_read_word_data(struct i2c_client *client, int page,
int phase, int reg)
{
@ -295,6 +392,9 @@ static int tps53679_probe(struct i2c_client *client)
info->pages = TPS53679_PAGE_NUM;
info->identify = tps53685_identify;
break;
case tps536c7:
info->identify = tps536c7_identify;
break;
default:
return -ENODEV;
}
@ -313,6 +413,7 @@ static const struct i2c_device_id tps53679_id[] = {
{ .name = "tps53681", .driver_data = tps53681 },
{ .name = "tps53685", .driver_data = tps53685 },
{ .name = "tps53688", .driver_data = tps53688 },
{ .name = "tps536c7", .driver_data = tps536c7 },
{ }
};
@ -328,6 +429,7 @@ static const struct of_device_id __maybe_unused tps53679_of_match[] = {
{.compatible = "ti,tps53681", .data = (void *)tps53681},
{.compatible = "ti,tps53685", .data = (void *)tps53685},
{.compatible = "ti,tps53688", .data = (void *)tps53688},
{.compatible = "ti,tps536c7", .data = (void *)tps536c7},
{}
};
MODULE_DEVICE_TABLE(of, tps53679_of_match);