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