hwmon: (pmbus/lx1308) Add support for LX1308

Add support for the Luxshare LX1308, a high-efficiency 12V 860W
DC/DC power module. The module operates from 40-60V input voltage.

Signed-off-by: Brian Chiang <chiang.brian@inventec.com>
Link: https://lore.kernel.org/r/20260428-add-support-lx1308-v2-2-90f115954143@inventec.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
Brian Chiang
2026-04-28 12:19:29 +00:00
committed by Guenter Roeck
parent 66b4973f9a
commit d71d17d347
5 changed files with 306 additions and 0 deletions

View File

@@ -146,6 +146,7 @@ Hardware Monitoring Kernel Drivers
ltc4261
ltc4282
ltc4286
lx1308
macsmc-hwmon
max127
max15301

View File

@@ -0,0 +1,90 @@
.. SPDX-License-Identifier: GPL-2.0-or-later
Kernel driver lx1308
====================
Supported chips:
* Luxshare LX1308
Prefixes: 'lx1308'
Addresses scanned: -
Datasheet: Datasheet is not publicly available.
Author: Brian Chiang <chiang.brian@inventec.com>
Description
-----------
The LX1308 is a high-efficiency, non-isolated, regulated 12V, 860W,
digital DC/DC power module. The module operates from a 40V to 60V DC
primary bus and provides a 12V regulated output voltage. It can deliver
up to 860W continuous and 1300W in transient.
The module has slow OCP and fast OCP. If the module output current is higher
than slow OCP set point and the lasting time is also longer than the delay,
the module will shut down and retry 3 time, if the fault still exists then
module enter latch mode.
If the module output current is higher than fast OCP set point then it shut
down and enter latch mode.
The driver is a client driver to the core PMBus driver.
Please see Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
Usage Notes
-----------
This driver does not auto-detect devices. You will have to instantiate the
devices explicitly. Please see Documentation/i2c/instantiating-devices.rst for
details.
Sysfs entries
-------------
======================= ======================================================
curr1_alarm Input current alarm
curr1_input Input current (IIN)
curr1_label "iin"
curr2_crit Output over current fault threshold (slow OCP, 60ms delay)
curr2_crit_alarm Output over current fault alarm
curr2_input Output current (IOUT)
curr2_label "iout1"
curr2_max Output over current warning threshold (slow OCP, 60ms delay)
curr2_max_alarm Output over current warning alarm
in1_crit Input over voltage fault threshold
in1_crit_alarm Input over voltage fault alarm
in1_input Input voltage (VIN)
in1_label "vin"
in1_lcrit Input under voltage fault threshold
in1_lcrit_alarm Input under voltage fault alarm
in1_max Input over voltage warning threshold
in1_max_alarm Input over voltage warning alarm
in1_min Input under voltage warning threshold
in1_min_alarm Input under voltage warning alarm
in2_crit Output over voltage fault threshold
in2_crit_alarm Output over voltage fault alarm
in2_input Output voltage (VOUT)
in2_label "vout1"
in2_lcrit Output under voltage fault threshold
in2_lcrit_alarm Output under voltage fault alarm
in2_max Output over voltage warning threshold
in2_max_alarm Output over voltage warning alarm
in2_min Output under voltage warning threshold
in2_min_alarm Output under voltage warning alarm
power1_alarm Input power alarm
power1_input Input power (PIN)
power1_label "pin"
power2_input Output power (POUT)
power2_label "pout1"
temp1_crit Over temperature fault threshold
temp1_crit_alarm Over temperature fault alarm
temp1_input Module hot spot temperature
temp1_max Over temperature warning threshold
temp1_max_alarm Over temperature warning alarm
======================= ======================================================

View File

@@ -311,6 +311,16 @@ config SENSORS_LTC4286
If you say yes here you get hardware monitoring support for Analog
Devices LTC4286.
config SENSORS_LX1308
tristate "Luxshare LX1308 DC/DC Power Module"
help
If you say yes here you get hardware monitoring support for
Luxshare LX1308, a high-efficiency 12V 860W DC/DC power module
with PMBus interface.
This driver can also be built as a module. If so, the module will
be called lx1308.
config SENSORS_MAX15301
tristate "Maxim MAX15301"
help

View File

@@ -30,6 +30,7 @@ obj-$(CONFIG_SENSORS_LT7182S) += lt7182s.o
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o
obj-$(CONFIG_SENSORS_LTC4286) += ltc4286.o
obj-$(CONFIG_SENSORS_LX1308) += lx1308.o
obj-$(CONFIG_SENSORS_MAX15301) += max15301.o
obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
obj-$(CONFIG_SENSORS_MAX16601) += max16601.o

View File

@@ -0,0 +1,204 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include "pmbus.h"
#define LX1308_MFR_IOUT_OCP3_FAULT 0xBE
#define LX1308_MFR_IOUT_OCP3_WARN 0xBF
/*
* Decode a Linear11-encoded word to an integer value.
* Linear11 format: bits[15:11] = signed 5-bit exponent,
* bits[10:0] = signed 11-bit mantissa. Result = mant * 2^exp.
*/
static inline int linear11_to_int(u16 word)
{
s16 exp = ((s16)word) >> 11;
s16 mant = ((s16)((word & 0x7ff) << 5)) >> 5;
return (exp >= 0) ? (int)((u32)mant << exp) : (mant >> -exp);
}
static int lx1308_read_word_data(struct i2c_client *client, int page,
int phase, int reg)
{
int ret;
if (page > 0)
return -ENXIO;
switch (reg) {
/*
* The LX1308 OCP3 registers (slow OCP, 60ms delay) use a
* manufacturer-specific U8.0 format. Read the byte value N and present
* it as a Linear11 word with exponent 0.
*/
case PMBUS_IOUT_OC_FAULT_LIMIT:
ret = i2c_smbus_read_byte_data(client, LX1308_MFR_IOUT_OCP3_FAULT);
if (ret < 0)
break;
ret &= 0x7FF;
break;
case PMBUS_IOUT_OC_WARN_LIMIT:
ret = i2c_smbus_read_byte_data(client, LX1308_MFR_IOUT_OCP3_WARN);
if (ret < 0)
break;
ret &= 0x7FF;
break;
/*
* The following registers are not implemented by the LX1308. Return
* -ENXIO to suppress the corresponding sysfs attributes.
*/
case PMBUS_IIN_OC_WARN_LIMIT:
case PMBUS_IIN_OC_FAULT_LIMIT:
case PMBUS_IOUT_UC_FAULT_LIMIT:
case PMBUS_PIN_OP_WARN_LIMIT:
case PMBUS_POUT_OP_WARN_LIMIT:
case PMBUS_UT_WARN_LIMIT:
case PMBUS_UT_FAULT_LIMIT:
case PMBUS_MFR_IIN_MAX:
case PMBUS_MFR_IOUT_MAX:
case PMBUS_MFR_VIN_MIN:
case PMBUS_MFR_VIN_MAX:
case PMBUS_MFR_VOUT_MIN:
case PMBUS_MFR_VOUT_MAX:
case PMBUS_MFR_PIN_MAX:
case PMBUS_MFR_POUT_MAX:
case PMBUS_MFR_MAX_TEMP_1:
ret = -ENXIO;
break;
default:
ret = -ENODATA;
break;
}
return ret;
}
static int lx1308_write_word_data(struct i2c_client *client, int page,
int reg, u16 word)
{
int ret;
if (page > 0)
return -ENXIO;
switch (reg) {
case PMBUS_IOUT_OC_FAULT_LIMIT:
/*
* Decode Linear11 word from pmbus_core back to a plain integer
* and write as the U8.0 byte the device expects.
*/
ret = i2c_smbus_write_byte_data(client, LX1308_MFR_IOUT_OCP3_FAULT,
clamp_val(linear11_to_int(word), 0, 255));
break;
case PMBUS_IOUT_OC_WARN_LIMIT:
ret = i2c_smbus_write_byte_data(client, LX1308_MFR_IOUT_OCP3_WARN,
clamp_val(linear11_to_int(word), 0, 255));
break;
default:
ret = -ENODATA;
break;
}
return ret;
}
static struct pmbus_driver_info lx1308_info = {
.pages = 1,
.format[PSC_VOLTAGE_IN] = linear,
.format[PSC_VOLTAGE_OUT] = linear,
.format[PSC_CURRENT_IN] = linear,
.format[PSC_CURRENT_OUT] = linear,
.format[PSC_POWER] = linear,
.format[PSC_TEMPERATURE] = linear,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_PIN | PMBUS_HAVE_POUT
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP
| PMBUS_HAVE_STATUS_INPUT,
.read_word_data = lx1308_read_word_data,
.write_word_data = lx1308_write_word_data,
};
static const struct of_device_id lx1308_of_match[] = {
{ .compatible = "luxshare,lx1308" },
{ }
};
MODULE_DEVICE_TABLE(of, lx1308_of_match);
static const struct i2c_device_id lx1308_id[] = {
{ "lx1308" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lx1308_id);
static int lx1308_probe(struct i2c_client *client)
{
u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
const struct i2c_device_id *mid;
int ret;
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
if (ret < 0)
return dev_err_probe(&client->dev, ret,
"Failed to read manufacturer id\n");
buf[ret] = '\0';
if (ret != 12 || strncmp(buf, "LUXSHARE", 8))
return dev_err_probe(&client->dev, -ENODEV,
"Unsupported Manufacturer ID '%s'\n", buf);
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
if (ret < 0)
return dev_err_probe(&client->dev, ret,
"Failed to read Manufacturer Model\n");
buf[ret] = '\0';
for (mid = lx1308_id; mid->name[0]; mid++) {
if (!strncasecmp(mid->name, buf, strlen(mid->name)))
break;
}
if (!mid->name[0])
return dev_err_probe(&client->dev, -ENODEV,
"Unsupported Manufacturer Model '%s'\n", buf);
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_REVISION, buf);
if (ret < 0)
return dev_err_probe(&client->dev, ret,
"Failed to read Manufacturer Revision\n");
buf[ret] = '\0';
if (ret != 12 || buf[0] != 'V')
return dev_err_probe(&client->dev, -ENODEV,
"Unsupported Manufacturer Revision '%s'\n", buf);
return pmbus_do_probe(client, &lx1308_info);
}
static struct i2c_driver lx1308_driver = {
.driver = {
.name = "lx1308",
.of_match_table = lx1308_of_match,
},
.probe = lx1308_probe,
.id_table = lx1308_id,
};
module_i2c_driver(lx1308_driver);
MODULE_AUTHOR("Brian Chiang <chiang.brian@inventec.com>");
MODULE_DESCRIPTION("PMBus driver for Luxshare LX1308");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("PMBUS");