hwmon: add support for MC33XS2410 hardware monitoring

The device is able to monitor temperature, voltage and current of each of
the four outputs. Add basic support for monitoring the temperature of the
four outputs and the die temperature.

Signed-off-by: Dimitri Fedrau <dimitri.fedrau@liebherr.com>
Acked-by: Guenter Roeck <linux@roeck-us.net>
Link: https://lore.kernel.org/r/20250723-mc33xs2410-hwmon-v5-2-f62aab71cd59@liebherr.com
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
pull/1309/head
Dimitri Fedrau 2025-07-23 19:34:57 +02:00 committed by Uwe Kleine-König
parent 28517c8b62
commit a02b105fe9
5 changed files with 224 additions and 0 deletions

View File

@ -167,6 +167,7 @@ Hardware Monitoring Kernel Drivers
max77705
max8688
mc13783-adc
mc33xs2410_hwmon
mc34vr500
mcp3021
menf21bmc

View File

@ -0,0 +1,34 @@
.. SPDX-License-Identifier: GPL-2.0
Kernel driver mc33xs2410_hwmon
==============================
Supported devices:
* NXPs MC33XS2410
Datasheet: https://www.nxp.com/docs/en/data-sheet/MC33XS2410.pdf
Authors:
Dimitri Fedrau <dimitri.fedrau@liebherr.com>
Description
-----------
The MC33XS2410 is a four channel self-protected high-side switch featuring
hardware monitoring functions such as temperature, current and voltages for each
of the four channels.
Sysfs entries
-------------
======================= ======================================================
temp1_label "Central die temperature"
temp1_input Measured temperature of central die
temp[2-5]_label "Channel [1-4] temperature"
temp[2-5]_input Measured temperature of a single channel
temp[2-5]_alarm Temperature alarm
temp[2-5]_max Maximal temperature
======================= ======================================================

View File

@ -700,6 +700,16 @@ config SENSORS_MC13783_ADC
help
Support for the A/D converter on MC13783 and MC13892 PMIC.
config SENSORS_MC33XS2410
tristate "MC33XS2410 HWMON support"
depends on PWM_MC33XS2410
help
If you say yes here you get hardware monitoring support for
MC33XS2410.
This driver can also be built as a module. If so, the module
will be called mc33xs2410_hwmon.
config SENSORS_FSCHMD
tristate "Fujitsu Siemens Computers sensor chips"
depends on (X86 || COMPILE_TEST) && I2C

View File

@ -165,6 +165,7 @@ obj-$(CONFIG_SENSORS_MAX31790) += max31790.o
obj-$(CONFIG_MAX31827) += max31827.o
obj-$(CONFIG_SENSORS_MAX77705) += max77705-hwmon.o
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
obj-$(CONFIG_SENSORS_MC33XS2410) += mc33xs2410_hwmon.o
obj-$(CONFIG_SENSORS_MC34VR500) += mc34vr500.o
obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
obj-$(CONFIG_SENSORS_TC654) += tc654.o

View File

@ -0,0 +1,178 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2025 Liebherr-Electronics and Drives GmbH
*/
#include <linux/auxiliary_bus.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/hwmon.h>
#include <linux/mc33xs2410.h>
#include <linux/module.h>
/* ctrl registers */
#define MC33XS2410_TEMP_WT 0x29
#define MC33XS2410_TEMP_WT_MASK GENMASK(7, 0)
/* diag registers */
/* chan in { 1 ... 4 } */
#define MC33XS2410_OUT_STA(chan) (0x02 + (chan) - 1)
#define MC33XS2410_OUT_STA_OTW BIT(8)
#define MC33XS2410_TS_TEMP_DIE 0x26
#define MC33XS2410_TS_TEMP_MASK GENMASK(9, 0)
/* chan in { 1 ... 4 } */
#define MC33XS2410_TS_TEMP(chan) (0x2f + (chan) - 1)
static const struct hwmon_channel_info * const mc33xs2410_hwmon_info[] = {
HWMON_CHANNEL_INFO(temp,
HWMON_T_LABEL | HWMON_T_INPUT,
HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX |
HWMON_T_ALARM,
HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX |
HWMON_T_ALARM,
HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX |
HWMON_T_ALARM,
HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX |
HWMON_T_ALARM),
NULL,
};
static umode_t mc33xs2410_hwmon_is_visible(const void *data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
switch (attr) {
case hwmon_temp_input:
case hwmon_temp_alarm:
case hwmon_temp_label:
return 0444;
case hwmon_temp_max:
return 0644;
default:
return 0;
}
}
static int mc33xs2410_hwmon_read(struct device *dev,
enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct spi_device *spi = dev_get_drvdata(dev);
u16 reg_val;
int ret;
u8 reg;
switch (attr) {
case hwmon_temp_input:
reg = (channel == 0) ? MC33XS2410_TS_TEMP_DIE :
MC33XS2410_TS_TEMP(channel);
ret = mc33xs2410_read_reg_diag(spi, reg, &reg_val);
if (ret < 0)
return ret;
/* LSB is 0.25 degree celsius */
*val = FIELD_GET(MC33XS2410_TS_TEMP_MASK, reg_val) * 250 - 40000;
return 0;
case hwmon_temp_alarm:
ret = mc33xs2410_read_reg_diag(spi, MC33XS2410_OUT_STA(channel),
&reg_val);
if (ret < 0)
return ret;
*val = FIELD_GET(MC33XS2410_OUT_STA_OTW, reg_val);
return 0;
case hwmon_temp_max:
ret = mc33xs2410_read_reg_ctrl(spi, MC33XS2410_TEMP_WT, &reg_val);
if (ret < 0)
return ret;
/* LSB is 1 degree celsius */
*val = FIELD_GET(MC33XS2410_TEMP_WT_MASK, reg_val) * 1000 - 40000;
return 0;
default:
return -EOPNOTSUPP;
}
}
static int mc33xs2410_hwmon_write(struct device *dev,
enum hwmon_sensor_types type, u32 attr,
int channel, long val)
{
struct spi_device *spi = dev_get_drvdata(dev);
switch (attr) {
case hwmon_temp_max:
val = clamp_val(val, -40000, 215000);
/* LSB is 1 degree celsius */
val = (val / 1000) + 40;
return mc33xs2410_modify_reg(spi, MC33XS2410_TEMP_WT,
MC33XS2410_TEMP_WT_MASK, val);
default:
return -EOPNOTSUPP;
}
}
static const char *const mc33xs2410_temp_label[] = {
"Central die temperature",
"Channel 1 temperature",
"Channel 2 temperature",
"Channel 3 temperature",
"Channel 4 temperature",
};
static int mc33xs2410_read_string(struct device *dev,
enum hwmon_sensor_types type,
u32 attr, int channel, const char **str)
{
*str = mc33xs2410_temp_label[channel];
return 0;
}
static const struct hwmon_ops mc33xs2410_hwmon_hwmon_ops = {
.is_visible = mc33xs2410_hwmon_is_visible,
.read = mc33xs2410_hwmon_read,
.read_string = mc33xs2410_read_string,
.write = mc33xs2410_hwmon_write,
};
static const struct hwmon_chip_info mc33xs2410_hwmon_chip_info = {
.ops = &mc33xs2410_hwmon_hwmon_ops,
.info = mc33xs2410_hwmon_info,
};
static int mc33xs2410_hwmon_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
struct device *dev = &adev->dev;
struct spi_device *spi = container_of(dev->parent, struct spi_device, dev);
struct device *hwmon;
hwmon = devm_hwmon_device_register_with_info(dev, NULL, spi,
&mc33xs2410_hwmon_chip_info,
NULL);
return PTR_ERR_OR_ZERO(hwmon);
}
static const struct auxiliary_device_id mc33xs2410_hwmon_ids[] = {
{
.name = "pwm_mc33xs2410.hwmon",
},
{ }
};
MODULE_DEVICE_TABLE(auxiliary, mc33xs2410_hwmon_ids);
static struct auxiliary_driver mc33xs2410_hwmon_driver = {
.probe = mc33xs2410_hwmon_probe,
.id_table = mc33xs2410_hwmon_ids,
};
module_auxiliary_driver(mc33xs2410_hwmon_driver);
MODULE_DESCRIPTION("NXP MC33XS2410 hwmon driver");
MODULE_AUTHOR("Dimitri Fedrau <dimitri.fedrau@liebherr.com>");
MODULE_LICENSE("GPL");