File Index
Files and directories
Sign in
FILE

qnap-mcu-hwmon.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hwmon/qnap-mcu-hwmon.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2
  3/*
  4 * Driver for hwmon elements found on QNAP-MCU devices
  5 *
  6 * Copyright (C) 2024 Heiko Stuebner <heiko@sntech.de>
  7 */
  8
  9#include <linux/hwmon.h>
 10#include <linux/mfd/qnap-mcu.h>
 11#include <linux/module.h>
 12#include <linux/platform_device.h>
 13#include <linux/property.h>
 14#include <linux/thermal.h>
 15
 16struct qnap_mcu_hwmon {
 17	struct qnap_mcu *mcu;
 18	struct device *dev;
 19
 20	unsigned int pwm_min;
 21	unsigned int pwm_max;
 22
 23	struct fwnode_handle *fan_node;
 24	unsigned int fan_state;
 25	unsigned int fan_max_state;
 26	unsigned int *fan_cooling_levels;
 27
 28	struct thermal_cooling_device *cdev;
 29	struct hwmon_chip_info info;
 30};
 31
 32static int qnap_mcu_hwmon_get_rpm(struct qnap_mcu_hwmon *hwm)
 33{
 34	static const u8 cmd[] = { '@', 'F', 'A' };
 35	u8 reply[6];
 36	int ret;
 37
 38	/* poll the fan rpm */
 39	ret = qnap_mcu_exec(hwm->mcu, cmd, sizeof(cmd), reply, sizeof(reply));
 40	if (ret)
 41		return ret;
 42
 43	/* First 2 bytes must mirror the sent command */
 44	if (memcmp(cmd, reply, 2))
 45		return -EIO;
 46
 47	return reply[4] * 30;
 48}
 49
 50static int qnap_mcu_hwmon_get_pwm(struct qnap_mcu_hwmon *hwm)
 51{
 52	static const u8 cmd[] = { '@', 'F', 'Z', '0' }; /* 0 = fan-id? */
 53	u8 reply[4];
 54	int ret;
 55
 56	/* poll the fan pwm */
 57	ret = qnap_mcu_exec(hwm->mcu, cmd, sizeof(cmd), reply, sizeof(reply));
 58	if (ret)
 59		return ret;
 60
 61	/* First 3 bytes must mirror the sent command */
 62	if (memcmp(cmd, reply, 3))
 63		return -EIO;
 64
 65	return reply[3];
 66}
 67
 68static int qnap_mcu_hwmon_set_pwm(struct qnap_mcu_hwmon *hwm, u8 pwm)
 69{
 70	const u8 cmd[] = { '@', 'F', 'W', '0', pwm }; /* 0 = fan-id?, pwm 0-255 */
 71
 72	/* set the fan pwm */
 73	return qnap_mcu_exec_with_ack(hwm->mcu, cmd, sizeof(cmd));
 74}
 75
 76static int qnap_mcu_hwmon_get_temp(struct qnap_mcu_hwmon *hwm)
 77{
 78	static const u8 cmd[] = { '@', 'T', '3' };
 79	u8 reply[4];
 80	int ret;
 81
 82	/* poll the fan rpm */
 83	ret = qnap_mcu_exec(hwm->mcu, cmd, sizeof(cmd), reply, sizeof(reply));
 84	if (ret)
 85		return ret;
 86
 87	/* First bytes must mirror the sent command */
 88	if (memcmp(cmd, reply, sizeof(cmd)))
 89		return -EIO;
 90
 91	/*
 92	 * There is an unknown bit set in bit7.
 93	 * Bits [6:0] report the actual temperature as returned by the
 94	 * original qnap firmware-tools, so just drop bit7 for now.
 95	 */
 96	return (reply[3] & 0x7f) * 1000;
 97}
 98
 99static int qnap_mcu_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
100				u32 attr, int channel, long val)
101{
102	struct qnap_mcu_hwmon *hwm = dev_get_drvdata(dev);
103
104	switch (attr) {
105	case hwmon_pwm_input:
106		if (val < 0 || val > 255)
107			return -EINVAL;
108
109		if (val != 0)
110			val = clamp_val(val, hwm->pwm_min, hwm->pwm_max);
111
112		return qnap_mcu_hwmon_set_pwm(hwm, val);
113	default:
114		return -EOPNOTSUPP;
115	}
116
117	return 0;
118}
119
120static int qnap_mcu_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
121			       u32 attr, int channel, long *val)
122{
123	struct qnap_mcu_hwmon *hwm = dev_get_drvdata(dev);
124	int ret;
125
126	switch (type) {
127	case hwmon_pwm:
128		switch (attr) {
129		case hwmon_pwm_input:
130			ret = qnap_mcu_hwmon_get_pwm(hwm);
131			if (ret < 0)
132				return ret;
133
134			*val = ret;
135			return 0;
136		default:
137			return -EOPNOTSUPP;
138		}
139	case hwmon_fan:
140		ret = qnap_mcu_hwmon_get_rpm(hwm);
141		if (ret < 0)
142			return ret;
143
144		*val = ret;
145		return 0;
146	case hwmon_temp:
147		ret = qnap_mcu_hwmon_get_temp(hwm);
148		if (ret < 0)
149			return ret;
150
151		*val = ret;
152		return 0;
153	default:
154		return -EOPNOTSUPP;
155	}
156}
157
158static umode_t qnap_mcu_hwmon_is_visible(const void *data,
159					 enum hwmon_sensor_types type,
160					 u32 attr, int channel)
161{
162	switch (type) {
163	case hwmon_temp:
164		return 0444;
165
166	case hwmon_pwm:
167		return 0644;
168
169	case hwmon_fan:
170		return 0444;
171
172	default:
173		return 0;
174	}
175}
176
177static const struct hwmon_ops qnap_mcu_hwmon_hwmon_ops = {
178	.is_visible = qnap_mcu_hwmon_is_visible,
179	.read = qnap_mcu_hwmon_read,
180	.write = qnap_mcu_hwmon_write,
181};
182
183/* thermal cooling device callbacks */
184static int qnap_mcu_hwmon_get_max_state(struct thermal_cooling_device *cdev,
185					unsigned long *state)
186{
187	struct qnap_mcu_hwmon *hwm = cdev->devdata;
188
189	if (!hwm)
190		return -EINVAL;
191
192	*state = hwm->fan_max_state;
193
194	return 0;
195}
196
197static int qnap_mcu_hwmon_get_cur_state(struct thermal_cooling_device *cdev,
198					unsigned long *state)
199{
200	struct qnap_mcu_hwmon *hwm = cdev->devdata;
201
202	if (!hwm)
203		return -EINVAL;
204
205	*state = hwm->fan_state;
206
207	return 0;
208}
209
210static int qnap_mcu_hwmon_set_cur_state(struct thermal_cooling_device *cdev,
211					unsigned long state)
212{
213	struct qnap_mcu_hwmon *hwm = cdev->devdata;
214	int ret;
215
216	if (!hwm || state > hwm->fan_max_state)
217		return -EINVAL;
218
219	if (state == hwm->fan_state)
220		return 0;
221
222	ret = qnap_mcu_hwmon_set_pwm(hwm, hwm->fan_cooling_levels[state]);
223	if (ret)
224		return ret;
225
226	hwm->fan_state = state;
227
228	return ret;
229}
230
231static const struct thermal_cooling_device_ops qnap_mcu_hwmon_cooling_ops = {
232	.get_max_state = qnap_mcu_hwmon_get_max_state,
233	.get_cur_state = qnap_mcu_hwmon_get_cur_state,
234	.set_cur_state = qnap_mcu_hwmon_set_cur_state,
235};
236
237static void devm_fan_node_release(void *data)
238{
239	struct qnap_mcu_hwmon *hwm = data;
240
241	fwnode_handle_put(hwm->fan_node);
242}
243
244static int qnap_mcu_hwmon_get_cooling_data(struct device *dev, struct qnap_mcu_hwmon *hwm)
245{
246	struct fwnode_handle *fwnode;
247	int num, i, ret;
248
249	fwnode = device_get_named_child_node(dev->parent, "fan-0");
250	if (!fwnode)
251		return 0;
252
253	/* if we found the fan-node, we're keeping it until device-unbind */
254	hwm->fan_node = fwnode;
255	ret = devm_add_action_or_reset(dev, devm_fan_node_release, hwm);
256	if (ret)
257		return ret;
258
259	num = fwnode_property_count_u32(fwnode, "cooling-levels");
260	if (num <= 0)
261		return dev_err_probe(dev, num ? : -EINVAL,
262				     "Failed to count elements in 'cooling-levels'\n");
263
264	hwm->fan_cooling_levels = devm_kcalloc(dev, num, sizeof(u32),
265					       GFP_KERNEL);
266	if (!hwm->fan_cooling_levels)
267		return -ENOMEM;
268
269	ret = fwnode_property_read_u32_array(fwnode, "cooling-levels",
270					     hwm->fan_cooling_levels, num);
271	if (ret)
272		return dev_err_probe(dev, ret, "Failed to read 'cooling-levels'\n");
273
274	for (i = 0; i < num; i++) {
275		if (hwm->fan_cooling_levels[i] > hwm->pwm_max)
276			return dev_err_probe(dev, -EINVAL, "fan state[%d]:%d > %d\n", i,
277					     hwm->fan_cooling_levels[i], hwm->pwm_max);
278	}
279
280	hwm->fan_max_state = num - 1;
281
282	return 0;
283}
284
285static const struct hwmon_channel_info * const qnap_mcu_hwmon_channels[] = {
286	HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT),
287	HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
288	HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
289	NULL
290};
291
292static int qnap_mcu_hwmon_probe(struct platform_device *pdev)
293{
294	struct qnap_mcu *mcu = dev_get_drvdata(pdev->dev.parent);
295	const struct qnap_mcu_variant *variant = pdev->dev.platform_data;
296	struct qnap_mcu_hwmon *hwm;
297	struct thermal_cooling_device *cdev;
298	struct device *dev = &pdev->dev;
299	struct device *hwmon;
300	int ret;
301
302	hwm = devm_kzalloc(dev, sizeof(*hwm), GFP_KERNEL);
303	if (!hwm)
304		return -ENOMEM;
305
306	hwm->mcu = mcu;
307	hwm->dev = &pdev->dev;
308	hwm->pwm_min = variant->fan_pwm_min;
309	hwm->pwm_max = variant->fan_pwm_max;
310
311	platform_set_drvdata(pdev, hwm);
312
313	/*
314	 * Set duty cycle to maximum allowed.
315	 */
316	ret = qnap_mcu_hwmon_set_pwm(hwm, hwm->pwm_max);
317	if (ret)
318		return ret;
319
320	hwm->info.ops = &qnap_mcu_hwmon_hwmon_ops;
321	hwm->info.info = qnap_mcu_hwmon_channels;
322
323	ret = qnap_mcu_hwmon_get_cooling_data(dev, hwm);
324	if (ret)
325		return ret;
326
327	hwm->fan_state = hwm->fan_max_state;
328
329	hwmon = devm_hwmon_device_register_with_info(dev, "qnapmcu",
330						     hwm, &hwm->info, NULL);
331	if (IS_ERR(hwmon))
332		return dev_err_probe(dev, PTR_ERR(hwmon), "Failed to register hwmon device\n");
333
334	/*
335	 * Only register cooling device when we found cooling-levels.
336	 * qnap_mcu_hwmon_get_cooling_data() will fail when reading malformed
337	 * levels and only succeed with either no or correct cooling levels.
338	 */
339	if (IS_ENABLED(CONFIG_THERMAL) && hwm->fan_cooling_levels) {
340		cdev = devm_thermal_of_cooling_device_register(dev,
341					to_of_node(hwm->fan_node), "qnap-mcu-hwmon",
342					hwm, &qnap_mcu_hwmon_cooling_ops);
343		if (IS_ERR(cdev))
344			return dev_err_probe(dev, PTR_ERR(cdev),
345				"Failed to register qnap-mcu-hwmon as cooling device\n");
346		hwm->cdev = cdev;
347	}
348
349	return 0;
350}
351
352static struct platform_driver qnap_mcu_hwmon_driver = {
353	.probe = qnap_mcu_hwmon_probe,
354	.driver = {
355		.name = "qnap-mcu-hwmon",
356	},
357};
358module_platform_driver(qnap_mcu_hwmon_driver);
359
360MODULE_ALIAS("platform:qnap-mcu-hwmon");
361MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
362MODULE_DESCRIPTION("QNAP MCU hwmon driver");
363MODULE_LICENSE("GPL");
~/ Read-only file preview.