mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
parent
1001be13fa
commit
f173bf7161
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user