xref: /linux/drivers/hwmon/mc33xs2410_hwmon.c (revision f38b7512903a50eaeb300e9c8d9448187dd3959c)
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 
mc33xs2410_hwmon_is_visible(const void * data,enum hwmon_sensor_types type,u32 attr,int channel)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 
mc33xs2410_hwmon_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)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 
mc33xs2410_hwmon_write(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long val)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 
mc33xs2410_read_string(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,const char ** str)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 
mc33xs2410_hwmon_probe(struct auxiliary_device * adev,const struct auxiliary_device_id * id)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");
179