// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2025 Liebherr-Electronics and Drives GmbH */ #include #include #include #include #include #include /* 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, ®_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), ®_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, ®_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 "); MODULE_LICENSE("GPL");