xref: /linux/drivers/hwmon/cgbc-hwmon.c (revision 09b1704f5b02c18dd02b21343530463fcfc92c54)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * cgbc-hwmon - Congatec Board Controller hardware monitoring driver
4  *
5  * Copyright (C) 2024 Thomas Richard <thomas.richard@bootlin.com>
6  */
7 
8 #include <linux/bitfield.h>
9 #include <linux/device.h>
10 #include <linux/hwmon.h>
11 #include <linux/mfd/cgbc.h>
12 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 
15 #define CGBC_HWMON_CMD_SENSOR		0x77
16 #define CGBC_HWMON_CMD_SENSOR_DATA_SIZE	0x05
17 
18 #define CGBC_HWMON_TYPE_MASK	GENMASK(6, 5)
19 #define CGBC_HWMON_ID_MASK	GENMASK(4, 0)
20 #define CGBC_HWMON_ACTIVE_BIT	BIT(7)
21 
22 struct cgbc_hwmon_sensor {
23 	enum hwmon_sensor_types type;
24 	bool active;
25 	unsigned int index;
26 	unsigned int channel;
27 	const char *label;
28 };
29 
30 struct cgbc_hwmon_data {
31 	struct cgbc_device_data *cgbc;
32 	unsigned int nb_sensors;
33 	struct cgbc_hwmon_sensor *sensors;
34 };
35 
36 enum cgbc_sensor_types {
37 	CGBC_HWMON_TYPE_TEMP = 1,
38 	CGBC_HWMON_TYPE_IN,
39 	CGBC_HWMON_TYPE_FAN
40 };
41 
42 static const char * const cgbc_hwmon_labels_temp[] = {
43 	"CPU Temperature",
44 	"Box Temperature",
45 	"Ambient Temperature",
46 	"Board Temperature",
47 	"Carrier Temperature",
48 	"Chipset Temperature",
49 	"Video Temperature",
50 	"Other Temperature",
51 	"TOPDIM Temperature",
52 	"BOTTOMDIM Temperature",
53 };
54 
55 static const struct {
56 	enum hwmon_sensor_types type;
57 	const char *label;
58 } cgbc_hwmon_labels_in[] = {
59 	{ hwmon_in, "CPU Voltage" },
60 	{ hwmon_in, "DC Runtime Voltage" },
61 	{ hwmon_in, "DC Standby Voltage" },
62 	{ hwmon_in, "CMOS Battery Voltage" },
63 	{ hwmon_in, "Battery Voltage" },
64 	{ hwmon_in, "AC Voltage" },
65 	{ hwmon_in, "Other Voltage" },
66 	{ hwmon_in, "5V Voltage" },
67 	{ hwmon_in, "5V Standby Voltage" },
68 	{ hwmon_in, "3V3 Voltage" },
69 	{ hwmon_in, "3V3 Standby Voltage" },
70 	{ hwmon_in, "VCore A Voltage" },
71 	{ hwmon_in, "VCore B Voltage" },
72 	{ hwmon_in, "12V Voltage" },
73 	{ hwmon_curr, "DC Current" },
74 	{ hwmon_curr, "5V Current" },
75 	{ hwmon_curr, "12V Current" },
76 };
77 
78 #define CGBC_HWMON_NB_IN_SENSORS	14
79 
80 static const char * const cgbc_hwmon_labels_fan[] = {
81 	"CPU Fan",
82 	"Box Fan",
83 	"Ambient Fan",
84 	"Chipset Fan",
85 	"Video Fan",
86 	"Other Fan",
87 };
88 
89 static int cgbc_hwmon_cmd(struct cgbc_device_data *cgbc, u8 index, u8 *data)
90 {
91 	u8 cmd[2] = {CGBC_HWMON_CMD_SENSOR, index};
92 
93 	return cgbc_command(cgbc, cmd, sizeof(cmd), data, CGBC_HWMON_CMD_SENSOR_DATA_SIZE, NULL);
94 }
95 
96 static int cgbc_hwmon_probe_sensors(struct device *dev, struct cgbc_hwmon_data *hwmon)
97 {
98 	struct cgbc_device_data *cgbc = hwmon->cgbc;
99 	struct cgbc_hwmon_sensor *sensor = hwmon->sensors;
100 	u8 data[CGBC_HWMON_CMD_SENSOR_DATA_SIZE], nb_sensors, i;
101 	int ret;
102 
103 	ret = cgbc_hwmon_cmd(cgbc, 0, &data[0]);
104 	if (ret)
105 		return ret;
106 
107 	nb_sensors = data[0];
108 
109 	hwmon->sensors = devm_kzalloc(dev, sizeof(*hwmon->sensors) * nb_sensors, GFP_KERNEL);
110 	if (!hwmon->sensors)
111 		return -ENOMEM;
112 
113 	sensor = hwmon->sensors;
114 
115 	for (i = 0; i < nb_sensors; i++) {
116 		enum cgbc_sensor_types type;
117 		unsigned int channel;
118 
119 		/*
120 		 * No need to request data for the first sensor.
121 		 * We got data for the first sensor when we ask the number of sensors to the Board
122 		 * Controller.
123 		 */
124 		if (i) {
125 			ret = cgbc_hwmon_cmd(cgbc, i, &data[0]);
126 			if (ret)
127 				return ret;
128 		}
129 
130 		type = FIELD_GET(CGBC_HWMON_TYPE_MASK, data[1]);
131 		channel = FIELD_GET(CGBC_HWMON_ID_MASK, data[1]) - 1;
132 
133 		if (type == CGBC_HWMON_TYPE_TEMP && channel < ARRAY_SIZE(cgbc_hwmon_labels_temp)) {
134 			sensor->type = hwmon_temp;
135 			sensor->label = cgbc_hwmon_labels_temp[channel];
136 		} else if (type == CGBC_HWMON_TYPE_IN &&
137 			   channel < ARRAY_SIZE(cgbc_hwmon_labels_in)) {
138 			/*
139 			 * The Board Controller doesn't differentiate current and voltage sensors.
140 			 * Get the sensor type from cgbc_hwmon_labels_in[channel].type instead.
141 			 */
142 			sensor->type = cgbc_hwmon_labels_in[channel].type;
143 			sensor->label = cgbc_hwmon_labels_in[channel].label;
144 		} else if (type == CGBC_HWMON_TYPE_FAN &&
145 			   channel < ARRAY_SIZE(cgbc_hwmon_labels_fan)) {
146 			sensor->type = hwmon_fan;
147 			sensor->label = cgbc_hwmon_labels_fan[channel];
148 		} else {
149 			dev_warn(dev, "Board Controller returned an unknown sensor (type=%d, channel=%d), ignore it",
150 				 type, channel);
151 			continue;
152 		}
153 
154 		sensor->active = FIELD_GET(CGBC_HWMON_ACTIVE_BIT, data[1]);
155 		sensor->channel = channel;
156 		sensor->index = i;
157 		sensor++;
158 		hwmon->nb_sensors++;
159 	}
160 
161 	return 0;
162 }
163 
164 static struct cgbc_hwmon_sensor *cgbc_hwmon_find_sensor(struct cgbc_hwmon_data *hwmon,
165 							enum hwmon_sensor_types type, int channel)
166 {
167 	struct cgbc_hwmon_sensor *sensor = NULL;
168 	int i;
169 
170 	/*
171 	 * The Board Controller doesn't differentiate current and voltage sensors.
172 	 * The channel value (from the Board Controller point of view) shall be computed for current
173 	 * sensors.
174 	 */
175 	if (type == hwmon_curr)
176 		channel += CGBC_HWMON_NB_IN_SENSORS;
177 
178 	for (i = 0; i < hwmon->nb_sensors; i++) {
179 		if (hwmon->sensors[i].type == type && hwmon->sensors[i].channel == channel) {
180 			sensor = &hwmon->sensors[i];
181 			break;
182 		}
183 	}
184 
185 	return sensor;
186 }
187 
188 static int cgbc_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
189 			   long *val)
190 {
191 	struct cgbc_hwmon_data *hwmon = dev_get_drvdata(dev);
192 	struct cgbc_hwmon_sensor *sensor = cgbc_hwmon_find_sensor(hwmon, type, channel);
193 	struct cgbc_device_data *cgbc = hwmon->cgbc;
194 	u8 data[CGBC_HWMON_CMD_SENSOR_DATA_SIZE];
195 	int ret;
196 
197 	ret = cgbc_hwmon_cmd(cgbc, sensor->index, &data[0]);
198 	if (ret)
199 		return ret;
200 
201 	*val = (data[3] << 8) | data[2];
202 
203 	/*
204 	 * For the Board Controller 1lsb = 0.1 degree centigrade.
205 	 * Other units are as expected.
206 	 */
207 	if (sensor->type == hwmon_temp)
208 		*val *= 100;
209 
210 	return 0;
211 }
212 
213 static umode_t cgbc_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr,
214 				     int channel)
215 {
216 	struct cgbc_hwmon_data *data = (struct cgbc_hwmon_data *)_data;
217 	struct cgbc_hwmon_sensor *sensor;
218 
219 	sensor = cgbc_hwmon_find_sensor(data, type, channel);
220 	if (!sensor)
221 		return 0;
222 
223 	return sensor->active ? 0444 : 0;
224 }
225 
226 static int cgbc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
227 				  int channel, const char **str)
228 {
229 	struct cgbc_hwmon_data *hwmon = dev_get_drvdata(dev);
230 	struct cgbc_hwmon_sensor *sensor = cgbc_hwmon_find_sensor(hwmon, type, channel);
231 
232 	*str = sensor->label;
233 
234 	return 0;
235 }
236 
237 static const struct hwmon_channel_info * const cgbc_hwmon_info[] = {
238 	HWMON_CHANNEL_INFO(temp,
239 			   HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
240 			   HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
241 			   HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
242 			   HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
243 			   HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL),
244 	HWMON_CHANNEL_INFO(in,
245 			   HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
246 			   HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
247 			   HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
248 			   HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
249 			   HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
250 			   HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
251 			   HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL),
252 	HWMON_CHANNEL_INFO(curr,
253 			   HWMON_C_INPUT | HWMON_C_LABEL, HWMON_C_INPUT | HWMON_C_LABEL,
254 			   HWMON_C_INPUT | HWMON_C_LABEL),
255 	HWMON_CHANNEL_INFO(fan,
256 			   HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL,
257 			   HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL,
258 			   HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL),
259 	NULL
260 };
261 
262 static const struct hwmon_ops cgbc_hwmon_ops = {
263 	.is_visible = cgbc_hwmon_is_visible,
264 	.read = cgbc_hwmon_read,
265 	.read_string = cgbc_hwmon_read_string,
266 };
267 
268 static const struct hwmon_chip_info cgbc_chip_info = {
269 	.ops = &cgbc_hwmon_ops,
270 	.info = cgbc_hwmon_info,
271 };
272 
273 static int cgbc_hwmon_probe(struct platform_device *pdev)
274 {
275 	struct cgbc_device_data *cgbc = dev_get_drvdata(pdev->dev.parent);
276 	struct device *dev = &pdev->dev;
277 	struct cgbc_hwmon_data *data;
278 	struct device *hwmon_dev;
279 	int ret;
280 
281 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
282 	if (!data)
283 		return -ENOMEM;
284 
285 	data->cgbc = cgbc;
286 
287 	ret = cgbc_hwmon_probe_sensors(dev, data);
288 	if (ret)
289 		return dev_err_probe(dev, ret, "Failed to probe sensors");
290 
291 	hwmon_dev = devm_hwmon_device_register_with_info(dev, "cgbc_hwmon", data, &cgbc_chip_info,
292 							 NULL);
293 	return PTR_ERR_OR_ZERO(hwmon_dev);
294 }
295 
296 static struct platform_driver cgbc_hwmon_driver = {
297 	.driver = {
298 		.name = "cgbc-hwmon",
299 	},
300 	.probe = cgbc_hwmon_probe,
301 };
302 
303 module_platform_driver(cgbc_hwmon_driver);
304 
305 MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>");
306 MODULE_DESCRIPTION("Congatec Board Controller Hardware Monitoring Driver");
307 MODULE_LICENSE("GPL");
308