Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Dimitri Fedrau and committed by
Uwe Kleine-König
a02b105f 28517c8b

+224
+1
Documentation/hwmon/index.rst
··· 167 167 max77705 168 168 max8688 169 169 mc13783-adc 170 + mc33xs2410_hwmon 170 171 mc34vr500 171 172 mcp3021 172 173 menf21bmc
+34
Documentation/hwmon/mc33xs2410_hwmon.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + Kernel driver mc33xs2410_hwmon 4 + ============================== 5 + 6 + Supported devices: 7 + 8 + * NXPs MC33XS2410 9 + 10 + Datasheet: https://www.nxp.com/docs/en/data-sheet/MC33XS2410.pdf 11 + 12 + Authors: 13 + 14 + Dimitri Fedrau <dimitri.fedrau@liebherr.com> 15 + 16 + Description 17 + ----------- 18 + 19 + The MC33XS2410 is a four channel self-protected high-side switch featuring 20 + hardware monitoring functions such as temperature, current and voltages for each 21 + of the four channels. 22 + 23 + Sysfs entries 24 + ------------- 25 + 26 + ======================= ====================================================== 27 + temp1_label "Central die temperature" 28 + temp1_input Measured temperature of central die 29 + 30 + temp[2-5]_label "Channel [1-4] temperature" 31 + temp[2-5]_input Measured temperature of a single channel 32 + temp[2-5]_alarm Temperature alarm 33 + temp[2-5]_max Maximal temperature 34 + ======================= ======================================================
+10
drivers/hwmon/Kconfig
··· 700 700 help 701 701 Support for the A/D converter on MC13783 and MC13892 PMIC. 702 702 703 + config SENSORS_MC33XS2410 704 + tristate "MC33XS2410 HWMON support" 705 + depends on PWM_MC33XS2410 706 + help 707 + If you say yes here you get hardware monitoring support for 708 + MC33XS2410. 709 + 710 + This driver can also be built as a module. If so, the module 711 + will be called mc33xs2410_hwmon. 712 + 703 713 config SENSORS_FSCHMD 704 714 tristate "Fujitsu Siemens Computers sensor chips" 705 715 depends on (X86 || COMPILE_TEST) && I2C
+1
drivers/hwmon/Makefile
··· 165 165 obj-$(CONFIG_MAX31827) += max31827.o 166 166 obj-$(CONFIG_SENSORS_MAX77705) += max77705-hwmon.o 167 167 obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o 168 + obj-$(CONFIG_SENSORS_MC33XS2410) += mc33xs2410_hwmon.o 168 169 obj-$(CONFIG_SENSORS_MC34VR500) += mc34vr500.o 169 170 obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o 170 171 obj-$(CONFIG_SENSORS_TC654) += tc654.o
+178
drivers/hwmon/mc33xs2410_hwmon.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2025 Liebherr-Electronics and Drives GmbH 4 + */ 5 + 6 + #include <linux/auxiliary_bus.h> 7 + #include <linux/bitfield.h> 8 + #include <linux/bitops.h> 9 + #include <linux/hwmon.h> 10 + #include <linux/mc33xs2410.h> 11 + #include <linux/module.h> 12 + 13 + /* ctrl registers */ 14 + 15 + #define MC33XS2410_TEMP_WT 0x29 16 + #define MC33XS2410_TEMP_WT_MASK GENMASK(7, 0) 17 + 18 + /* diag registers */ 19 + 20 + /* chan in { 1 ... 4 } */ 21 + #define MC33XS2410_OUT_STA(chan) (0x02 + (chan) - 1) 22 + #define MC33XS2410_OUT_STA_OTW BIT(8) 23 + 24 + #define MC33XS2410_TS_TEMP_DIE 0x26 25 + #define MC33XS2410_TS_TEMP_MASK GENMASK(9, 0) 26 + 27 + /* chan in { 1 ... 4 } */ 28 + #define MC33XS2410_TS_TEMP(chan) (0x2f + (chan) - 1) 29 + 30 + static const struct hwmon_channel_info * const mc33xs2410_hwmon_info[] = { 31 + HWMON_CHANNEL_INFO(temp, 32 + HWMON_T_LABEL | HWMON_T_INPUT, 33 + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | 34 + HWMON_T_ALARM, 35 + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | 36 + HWMON_T_ALARM, 37 + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | 38 + HWMON_T_ALARM, 39 + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | 40 + HWMON_T_ALARM), 41 + NULL, 42 + }; 43 + 44 + static umode_t mc33xs2410_hwmon_is_visible(const void *data, 45 + enum hwmon_sensor_types type, 46 + u32 attr, int channel) 47 + { 48 + switch (attr) { 49 + case hwmon_temp_input: 50 + case hwmon_temp_alarm: 51 + case hwmon_temp_label: 52 + return 0444; 53 + case hwmon_temp_max: 54 + return 0644; 55 + default: 56 + return 0; 57 + } 58 + } 59 + 60 + static int mc33xs2410_hwmon_read(struct device *dev, 61 + enum hwmon_sensor_types type, 62 + u32 attr, int channel, long *val) 63 + { 64 + struct spi_device *spi = dev_get_drvdata(dev); 65 + u16 reg_val; 66 + int ret; 67 + u8 reg; 68 + 69 + switch (attr) { 70 + case hwmon_temp_input: 71 + reg = (channel == 0) ? MC33XS2410_TS_TEMP_DIE : 72 + MC33XS2410_TS_TEMP(channel); 73 + ret = mc33xs2410_read_reg_diag(spi, reg, &reg_val); 74 + if (ret < 0) 75 + return ret; 76 + 77 + /* LSB is 0.25 degree celsius */ 78 + *val = FIELD_GET(MC33XS2410_TS_TEMP_MASK, reg_val) * 250 - 40000; 79 + return 0; 80 + case hwmon_temp_alarm: 81 + ret = mc33xs2410_read_reg_diag(spi, MC33XS2410_OUT_STA(channel), 82 + &reg_val); 83 + if (ret < 0) 84 + return ret; 85 + 86 + *val = FIELD_GET(MC33XS2410_OUT_STA_OTW, reg_val); 87 + return 0; 88 + case hwmon_temp_max: 89 + ret = mc33xs2410_read_reg_ctrl(spi, MC33XS2410_TEMP_WT, &reg_val); 90 + if (ret < 0) 91 + return ret; 92 + 93 + /* LSB is 1 degree celsius */ 94 + *val = FIELD_GET(MC33XS2410_TEMP_WT_MASK, reg_val) * 1000 - 40000; 95 + return 0; 96 + default: 97 + return -EOPNOTSUPP; 98 + } 99 + } 100 + 101 + static int mc33xs2410_hwmon_write(struct device *dev, 102 + enum hwmon_sensor_types type, u32 attr, 103 + int channel, long val) 104 + { 105 + struct spi_device *spi = dev_get_drvdata(dev); 106 + 107 + switch (attr) { 108 + case hwmon_temp_max: 109 + val = clamp_val(val, -40000, 215000); 110 + 111 + /* LSB is 1 degree celsius */ 112 + val = (val / 1000) + 40; 113 + return mc33xs2410_modify_reg(spi, MC33XS2410_TEMP_WT, 114 + MC33XS2410_TEMP_WT_MASK, val); 115 + default: 116 + return -EOPNOTSUPP; 117 + } 118 + } 119 + 120 + static const char *const mc33xs2410_temp_label[] = { 121 + "Central die temperature", 122 + "Channel 1 temperature", 123 + "Channel 2 temperature", 124 + "Channel 3 temperature", 125 + "Channel 4 temperature", 126 + }; 127 + 128 + static int mc33xs2410_read_string(struct device *dev, 129 + enum hwmon_sensor_types type, 130 + u32 attr, int channel, const char **str) 131 + { 132 + *str = mc33xs2410_temp_label[channel]; 133 + 134 + return 0; 135 + } 136 + 137 + static const struct hwmon_ops mc33xs2410_hwmon_hwmon_ops = { 138 + .is_visible = mc33xs2410_hwmon_is_visible, 139 + .read = mc33xs2410_hwmon_read, 140 + .read_string = mc33xs2410_read_string, 141 + .write = mc33xs2410_hwmon_write, 142 + }; 143 + 144 + static const struct hwmon_chip_info mc33xs2410_hwmon_chip_info = { 145 + .ops = &mc33xs2410_hwmon_hwmon_ops, 146 + .info = mc33xs2410_hwmon_info, 147 + }; 148 + 149 + static int mc33xs2410_hwmon_probe(struct auxiliary_device *adev, 150 + const struct auxiliary_device_id *id) 151 + { 152 + struct device *dev = &adev->dev; 153 + struct spi_device *spi = container_of(dev->parent, struct spi_device, dev); 154 + struct device *hwmon; 155 + 156 + hwmon = devm_hwmon_device_register_with_info(dev, NULL, spi, 157 + &mc33xs2410_hwmon_chip_info, 158 + NULL); 159 + return PTR_ERR_OR_ZERO(hwmon); 160 + } 161 + 162 + static const struct auxiliary_device_id mc33xs2410_hwmon_ids[] = { 163 + { 164 + .name = "pwm_mc33xs2410.hwmon", 165 + }, 166 + { } 167 + }; 168 + MODULE_DEVICE_TABLE(auxiliary, mc33xs2410_hwmon_ids); 169 + 170 + static struct auxiliary_driver mc33xs2410_hwmon_driver = { 171 + .probe = mc33xs2410_hwmon_probe, 172 + .id_table = mc33xs2410_hwmon_ids, 173 + }; 174 + module_auxiliary_driver(mc33xs2410_hwmon_driver); 175 + 176 + MODULE_DESCRIPTION("NXP MC33XS2410 hwmon driver"); 177 + MODULE_AUTHOR("Dimitri Fedrau <dimitri.fedrau@liebherr.com>"); 178 + MODULE_LICENSE("GPL");