xref: /linux/drivers/mfd/qnap-mcu.c (revision 7db28abbea0f7dc1ec4fdfdc149db5fbd9e4c994)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Core driver for the microcontroller unit in QNAP NAS devices that is
4  * connected via a dedicated UART port.
5  *
6  * Copyright (C) 2024 Heiko Stuebner <heiko@sntech.de>
7  */
8 
9 #include <linux/cleanup.h>
10 #include <linux/export.h>
11 #include <linux/mfd/core.h>
12 #include <linux/mfd/qnap-mcu.h>
13 #include <linux/module.h>
14 #include <linux/of.h>
15 #include <linux/reboot.h>
16 #include <linux/serdev.h>
17 #include <linux/slab.h>
18 
19 /* The longest command found so far is 5 bytes long */
20 #define QNAP_MCU_MAX_CMD_SIZE		5
21 #define QNAP_MCU_MAX_DATA_SIZE		36
22 #define QNAP_MCU_ERROR_SIZE		2
23 #define QNAP_MCU_CHECKSUM_SIZE		1
24 
25 #define QNAP_MCU_RX_BUFFER_SIZE		\
26 		(QNAP_MCU_MAX_DATA_SIZE + QNAP_MCU_CHECKSUM_SIZE)
27 
28 #define QNAP_MCU_TX_BUFFER_SIZE		\
29 		(QNAP_MCU_MAX_CMD_SIZE + QNAP_MCU_CHECKSUM_SIZE)
30 
31 #define QNAP_MCU_ACK_LEN		2
32 #define QNAP_MCU_VERSION_LEN		4
33 
34 #define QNAP_MCU_TIMEOUT_MS		500
35 
36 /**
37  * struct qnap_mcu_reply - Reply to a command
38  *
39  * @data:	Buffer to store reply payload in
40  * @length:	Expected reply length, including the checksum
41  * @received:	Received number of bytes, so far
42  * @done:	Triggered when the entire reply has been received
43  */
44 struct qnap_mcu_reply {
45 	u8 *data;
46 	size_t length;
47 	size_t received;
48 	struct completion done;
49 };
50 
51 /**
52  * struct qnap_mcu - QNAP NAS embedded controller
53  *
54  * @serdev:	Pointer to underlying serdev
55  * @bus_lock:	Lock to serialize access to the device
56  * @reply:	Reply data structure
57  * @variant:	Device variant specific information
58  * @version:	MCU firmware version
59  * @rx:		Receive buffer the reply is assembled in
60  */
61 struct qnap_mcu {
62 	struct serdev_device *serdev;
63 	struct mutex bus_lock;
64 	struct qnap_mcu_reply reply;
65 	const struct qnap_mcu_variant *variant;
66 	u8 version[QNAP_MCU_VERSION_LEN];
67 	u8 rx[QNAP_MCU_RX_BUFFER_SIZE];
68 };
69 
70 /*
71  * The QNAP-MCU uses a basic XOR checksum.
72  * It is always the last byte and XORs the whole previous message.
73  */
74 static u8 qnap_mcu_csum(const u8 *buf, size_t size)
75 {
76 	u8 csum = 0;
77 
78 	while (size--)
79 		csum ^= *buf++;
80 
81 	return csum;
82 }
83 
84 static bool qnap_mcu_verify_checksum(const u8 *buf, size_t size)
85 {
86 	u8 crc = qnap_mcu_csum(buf, size - QNAP_MCU_CHECKSUM_SIZE);
87 
88 	return crc == buf[size - QNAP_MCU_CHECKSUM_SIZE];
89 }
90 
91 static int qnap_mcu_write(struct qnap_mcu *mcu, const u8 *data, u8 data_size)
92 {
93 	unsigned char tx[QNAP_MCU_TX_BUFFER_SIZE];
94 	size_t length = data_size + QNAP_MCU_CHECKSUM_SIZE;
95 
96 	if (length > sizeof(tx)) {
97 		dev_err(&mcu->serdev->dev, "data too big for transmit buffer");
98 		return -EINVAL;
99 	}
100 
101 	memcpy(tx, data, data_size);
102 	tx[data_size] = qnap_mcu_csum(data, data_size);
103 
104 	serdev_device_write_flush(mcu->serdev);
105 
106 	return serdev_device_write(mcu->serdev, tx, length, HZ);
107 }
108 
109 static bool qnap_mcu_is_error_msg(size_t size)
110 {
111 	return (size == QNAP_MCU_ERROR_SIZE + QNAP_MCU_CHECKSUM_SIZE);
112 }
113 
114 static bool qnap_mcu_reply_is_generic_error(unsigned char *buf, size_t size)
115 {
116 	if (!qnap_mcu_is_error_msg(size))
117 		return false;
118 
119 	if (buf[0] == '@' && buf[1] == '9')
120 		return true;
121 
122 	return false;
123 }
124 
125 static bool qnap_mcu_reply_is_checksum_error(unsigned char *buf, size_t size)
126 {
127 	if (!qnap_mcu_is_error_msg(size))
128 		return false;
129 
130 	if (buf[0] == '@' && buf[1] == '8')
131 		return true;
132 
133 	return false;
134 }
135 
136 static bool qnap_mcu_reply_is_any_error(struct qnap_mcu *mcu, unsigned char *buf, size_t size)
137 {
138 	if (qnap_mcu_reply_is_generic_error(buf, size)) {
139 		dev_err(&mcu->serdev->dev, "Controller sent generic error response\n");
140 		return true;
141 	}
142 
143 	if (qnap_mcu_reply_is_checksum_error(buf, size)) {
144 		dev_err(&mcu->serdev->dev, "Controller received invalid checksum for the command\n");
145 		return true;
146 	}
147 
148 	return false;
149 }
150 
151 static size_t qnap_mcu_receive_buf(struct serdev_device *serdev, const u8 *buf, size_t size)
152 {
153 	struct device *dev = &serdev->dev;
154 	struct qnap_mcu *mcu = dev_get_drvdata(dev);
155 	struct qnap_mcu_reply *reply = &mcu->reply;
156 	const u8 *src = buf;
157 	const u8 *end = buf + size;
158 
159 	if (!reply->length) {
160 		dev_warn(dev, "Received %zu bytes, we were not waiting for\n", size);
161 		return size;
162 	}
163 
164 	while (src < end) {
165 		reply->data[reply->received] = *src++;
166 		reply->received++;
167 
168 		if (reply->received == reply->length) {
169 			/* We don't expect any characters from the device now */
170 			reply->length = 0;
171 
172 			complete(&reply->done);
173 
174 			/*
175 			 * We report the consumed number of bytes. If there
176 			 * are still bytes remaining (though there shouldn't)
177 			 * the serdev layer will re-execute this handler with
178 			 * the remainder of the Rx bytes.
179 			 */
180 			return src - buf;
181 		}
182 	}
183 
184 	/*
185 	 * We received everything the uart had to offer for now.
186 	 * This could mean that either the uart will send more in a 2nd
187 	 * receive run, or that the MCU cut the reply short because it
188 	 * sent an error code instead of the expected reply.
189 	 *
190 	 * So check if the received data has the correct size for an error
191 	 * reply and if it matches, is an actual error code.
192 	 */
193 	if (qnap_mcu_is_error_msg(reply->received) &&
194 	    qnap_mcu_verify_checksum(reply->data, reply->received) &&
195 	    qnap_mcu_reply_is_any_error(mcu, reply->data, reply->received)) {
196 		/* The reply was an error code, we're done */
197 		reply->length = 0;
198 
199 		complete(&reply->done);
200 	}
201 
202 	/*
203 	 * The only way to get out of the above loop and end up here
204 	 * is through consuming all of the supplied data, so here we
205 	 * report that we processed it all.
206 	 */
207 	return size;
208 }
209 
210 static const struct serdev_device_ops qnap_mcu_serdev_device_ops = {
211 	.receive_buf  = qnap_mcu_receive_buf,
212 	.write_wakeup = serdev_device_write_wakeup,
213 };
214 
215 int qnap_mcu_exec(struct qnap_mcu *mcu,
216 		  const u8 *cmd_data, size_t cmd_data_size,
217 		  u8 *reply_data, size_t reply_data_size)
218 {
219 	size_t length = reply_data_size + QNAP_MCU_CHECKSUM_SIZE;
220 	struct qnap_mcu_reply *reply = &mcu->reply;
221 	int ret = 0;
222 
223 	if (length > sizeof(mcu->rx)) {
224 		dev_err(&mcu->serdev->dev, "expected data too big for receive buffer");
225 		return -EINVAL;
226 	}
227 
228 	guard(mutex)(&mcu->bus_lock);
229 
230 	reply->data = mcu->rx;
231 	reply->length = length;
232 	reply->received = 0;
233 	reinit_completion(&reply->done);
234 
235 	ret = qnap_mcu_write(mcu, cmd_data, cmd_data_size);
236 	if (ret < 0)
237 		return ret;
238 
239 	serdev_device_wait_until_sent(mcu->serdev, msecs_to_jiffies(QNAP_MCU_TIMEOUT_MS));
240 
241 	if (!wait_for_completion_timeout(&reply->done, msecs_to_jiffies(QNAP_MCU_TIMEOUT_MS))) {
242 		dev_err(&mcu->serdev->dev, "Command timeout\n");
243 		return -ETIMEDOUT;
244 	}
245 
246 	if (!qnap_mcu_verify_checksum(mcu->rx, reply->received)) {
247 		dev_err(&mcu->serdev->dev, "Invalid Checksum received from controller\n");
248 		return -EPROTO;
249 	}
250 
251 	if (qnap_mcu_reply_is_any_error(mcu, mcu->rx, reply->received))
252 		return -EPROTO;
253 
254 	memcpy(reply_data, mcu->rx, reply_data_size);
255 
256 	return 0;
257 }
258 EXPORT_SYMBOL_GPL(qnap_mcu_exec);
259 
260 int qnap_mcu_exec_with_ack(struct qnap_mcu *mcu,
261 			   const u8 *cmd_data, size_t cmd_data_size)
262 {
263 	u8 ack[QNAP_MCU_ACK_LEN];
264 	int ret;
265 
266 	ret = qnap_mcu_exec(mcu, cmd_data, cmd_data_size, ack, sizeof(ack));
267 	if (ret)
268 		return ret;
269 
270 	/* Should return @0 */
271 	if (ack[0] != '@' || ack[1] != '0') {
272 		dev_err(&mcu->serdev->dev, "Did not receive ack\n");
273 		return -EIO;
274 	}
275 
276 	return 0;
277 }
278 EXPORT_SYMBOL_GPL(qnap_mcu_exec_with_ack);
279 
280 static int qnap_mcu_get_version(struct qnap_mcu *mcu)
281 {
282 	const u8 cmd[] = { '%', 'V' };
283 	u8 rx[14];
284 	int ret;
285 
286 	/* Reply is the 2 command-bytes + 4 bytes describing the version */
287 	ret = qnap_mcu_exec(mcu, cmd, sizeof(cmd), rx, QNAP_MCU_VERSION_LEN + 2);
288 	if (ret)
289 		return ret;
290 
291 	memcpy(mcu->version, &rx[2], QNAP_MCU_VERSION_LEN);
292 
293 	return 0;
294 }
295 
296 /*
297  * The MCU controls power to the peripherals but not the CPU.
298  *
299  * So using the PMIC to power off the system keeps the MCU and hard-drives
300  * running. This also then prevents the system from turning back on until
301  * the MCU is turned off by unplugging the power cable.
302  * Turning off the MCU alone on the other hand turns off the hard drives,
303  * LEDs, etc while the main SoC stays running - including its network ports.
304  */
305 static int qnap_mcu_power_off(struct sys_off_data *data)
306 {
307 	const u8 cmd[] = { '@', 'C', '0' };
308 	struct qnap_mcu *mcu = data->cb_data;
309 	int ret;
310 
311 	ret = qnap_mcu_exec_with_ack(mcu, cmd, sizeof(cmd));
312 	if (ret) {
313 		dev_err(&mcu->serdev->dev, "MCU poweroff failed %d\n", ret);
314 		return NOTIFY_STOP;
315 	}
316 
317 	return NOTIFY_DONE;
318 }
319 
320 static const struct qnap_mcu_variant qnap_ts133_mcu = {
321 	.baud_rate = 115200,
322 	.num_drives = 1,
323 	.fan_pwm_min = 51,  /* Specified in original model.conf */
324 	.fan_pwm_max = 255,
325 	.usb_led = false,
326 };
327 
328 static const struct qnap_mcu_variant qnap_ts233_mcu = {
329 	.baud_rate = 115200,
330 	.num_drives = 2,
331 	.fan_pwm_min = 51,  /* Specified in original model.conf */
332 	.fan_pwm_max = 255,
333 	.usb_led = true,
334 };
335 
336 static const struct qnap_mcu_variant qnap_ts433_mcu = {
337 	.baud_rate = 115200,
338 	.num_drives = 4,
339 	.fan_pwm_min = 51,  /* Specified in original model.conf */
340 	.fan_pwm_max = 255,
341 	.usb_led = true,
342 };
343 
344 static struct mfd_cell qnap_mcu_cells[] = {
345 	{ .name = "qnap-mcu-eeprom", },
346 	{ .name = "qnap-mcu-input", },
347 	{ .name = "qnap-mcu-leds", },
348 	{ .name = "qnap-mcu-hwmon", }
349 };
350 
351 static int qnap_mcu_probe(struct serdev_device *serdev)
352 {
353 	struct device *dev = &serdev->dev;
354 	struct qnap_mcu *mcu;
355 	int ret;
356 
357 	mcu = devm_kzalloc(dev, sizeof(*mcu), GFP_KERNEL);
358 	if (!mcu)
359 		return -ENOMEM;
360 
361 	mcu->serdev = serdev;
362 	dev_set_drvdata(dev, mcu);
363 
364 	mcu->variant = of_device_get_match_data(dev);
365 	if (!mcu->variant)
366 		return -ENODEV;
367 
368 	mutex_init(&mcu->bus_lock);
369 	init_completion(&mcu->reply.done);
370 
371 	serdev_device_set_client_ops(serdev, &qnap_mcu_serdev_device_ops);
372 	ret = devm_serdev_device_open(dev, serdev);
373 	if (ret)
374 		return ret;
375 
376 	serdev_device_set_baudrate(serdev, mcu->variant->baud_rate);
377 	serdev_device_set_flow_control(serdev, false);
378 
379 	ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
380 	if (ret)
381 		return dev_err_probe(dev, ret, "Failed to set parity\n");
382 
383 	ret = qnap_mcu_get_version(mcu);
384 	if (ret)
385 		return ret;
386 
387 	ret = devm_register_sys_off_handler(dev,
388 					    SYS_OFF_MODE_POWER_OFF_PREPARE,
389 					    SYS_OFF_PRIO_DEFAULT,
390 					    &qnap_mcu_power_off, mcu);
391 	if (ret)
392 		return dev_err_probe(dev, ret,
393 				     "Failed to register poweroff handler\n");
394 
395 	for (int i = 0; i < ARRAY_SIZE(qnap_mcu_cells); i++) {
396 		qnap_mcu_cells[i].platform_data = mcu->variant;
397 		qnap_mcu_cells[i].pdata_size = sizeof(*mcu->variant);
398 	}
399 
400 	ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, qnap_mcu_cells,
401 				   ARRAY_SIZE(qnap_mcu_cells), NULL, 0, NULL);
402 	if (ret)
403 		return dev_err_probe(dev, ret, "Failed to add child devices\n");
404 
405 	return 0;
406 }
407 
408 static const struct of_device_id qnap_mcu_dt_ids[] = {
409 	{ .compatible = "qnap,ts133-mcu", .data = &qnap_ts133_mcu },
410 	{ .compatible = "qnap,ts233-mcu", .data = &qnap_ts233_mcu },
411 	{ .compatible = "qnap,ts433-mcu", .data = &qnap_ts433_mcu },
412 	{ /* sentinel */ }
413 };
414 MODULE_DEVICE_TABLE(of, qnap_mcu_dt_ids);
415 
416 static struct serdev_device_driver qnap_mcu_drv = {
417 	.probe = qnap_mcu_probe,
418 	.driver = {
419 		.name = "qnap-mcu",
420 		.of_match_table = qnap_mcu_dt_ids,
421 	},
422 };
423 module_serdev_device_driver(qnap_mcu_drv);
424 
425 MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
426 MODULE_DESCRIPTION("QNAP MCU core driver");
427 MODULE_LICENSE("GPL");
428