1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * CZ.NIC's Turris Omnia MCU driver 4 * 5 * 2024 by Marek Behún <kabel@kernel.org> 6 */ 7 8 #include <linux/array_size.h> 9 #include <linux/bits.h> 10 #include <linux/device.h> 11 #include <linux/errno.h> 12 #include <linux/hex.h> 13 #include <linux/i2c.h> 14 #include <linux/module.h> 15 #include <linux/string.h> 16 #include <linux/sysfs.h> 17 #include <linux/types.h> 18 19 #include <linux/turris-omnia-mcu-interface.h> 20 #include "turris-omnia-mcu.h" 21 22 #define OMNIA_FW_VERSION_LEN 20 23 #define OMNIA_FW_VERSION_HEX_LEN (2 * OMNIA_FW_VERSION_LEN + 1) 24 #define OMNIA_BOARD_INFO_LEN 16 25 26 int omnia_cmd_write_read(const struct i2c_client *client, 27 void *cmd, unsigned int cmd_len, 28 void *reply, unsigned int reply_len) 29 { 30 struct i2c_msg msgs[2]; 31 int ret, num; 32 33 msgs[0].addr = client->addr; 34 msgs[0].flags = 0; 35 msgs[0].len = cmd_len; 36 msgs[0].buf = cmd; 37 num = 1; 38 39 if (reply_len) { 40 msgs[1].addr = client->addr; 41 msgs[1].flags = I2C_M_RD; 42 msgs[1].len = reply_len; 43 msgs[1].buf = reply; 44 num++; 45 } 46 47 ret = i2c_transfer(client->adapter, msgs, num); 48 if (ret < 0) 49 return ret; 50 if (ret != num) 51 return -EIO; 52 53 return 0; 54 } 55 EXPORT_SYMBOL_GPL(omnia_cmd_write_read); 56 57 static int omnia_get_version_hash(struct omnia_mcu *mcu, bool bootloader, 58 char version[static OMNIA_FW_VERSION_HEX_LEN]) 59 { 60 u8 reply[OMNIA_FW_VERSION_LEN]; 61 char *p; 62 int err; 63 64 err = omnia_cmd_read(mcu->client, 65 bootloader ? OMNIA_CMD_GET_FW_VERSION_BOOT 66 : OMNIA_CMD_GET_FW_VERSION_APP, 67 reply, sizeof(reply)); 68 if (err) 69 return err; 70 71 p = bin2hex(version, reply, OMNIA_FW_VERSION_LEN); 72 *p = '\0'; 73 74 return 0; 75 } 76 77 static ssize_t fw_version_hash_show(struct device *dev, char *buf, 78 bool bootloader) 79 { 80 struct omnia_mcu *mcu = dev_get_drvdata(dev); 81 char version[OMNIA_FW_VERSION_HEX_LEN]; 82 int err; 83 84 err = omnia_get_version_hash(mcu, bootloader, version); 85 if (err) 86 return err; 87 88 return sysfs_emit(buf, "%s\n", version); 89 } 90 91 static ssize_t fw_version_hash_application_show(struct device *dev, 92 struct device_attribute *a, 93 char *buf) 94 { 95 return fw_version_hash_show(dev, buf, false); 96 } 97 static DEVICE_ATTR_RO(fw_version_hash_application); 98 99 static ssize_t fw_version_hash_bootloader_show(struct device *dev, 100 struct device_attribute *a, 101 char *buf) 102 { 103 return fw_version_hash_show(dev, buf, true); 104 } 105 static DEVICE_ATTR_RO(fw_version_hash_bootloader); 106 107 static ssize_t fw_features_show(struct device *dev, struct device_attribute *a, 108 char *buf) 109 { 110 struct omnia_mcu *mcu = dev_get_drvdata(dev); 111 112 return sysfs_emit(buf, "0x%x\n", mcu->features); 113 } 114 static DEVICE_ATTR_RO(fw_features); 115 116 static ssize_t mcu_type_show(struct device *dev, struct device_attribute *a, 117 char *buf) 118 { 119 struct omnia_mcu *mcu = dev_get_drvdata(dev); 120 121 return sysfs_emit(buf, "%s\n", mcu->type); 122 } 123 static DEVICE_ATTR_RO(mcu_type); 124 125 static ssize_t reset_selector_show(struct device *dev, 126 struct device_attribute *a, char *buf) 127 { 128 u8 reply; 129 int err; 130 131 err = omnia_cmd_read_u8(to_i2c_client(dev), OMNIA_CMD_GET_RESET, 132 &reply); 133 if (err) 134 return err; 135 136 return sysfs_emit(buf, "%d\n", reply); 137 } 138 static DEVICE_ATTR_RO(reset_selector); 139 140 static ssize_t serial_number_show(struct device *dev, 141 struct device_attribute *a, char *buf) 142 { 143 struct omnia_mcu *mcu = dev_get_drvdata(dev); 144 145 return sysfs_emit(buf, "%016llX\n", mcu->board_serial_number); 146 } 147 static DEVICE_ATTR_RO(serial_number); 148 149 static ssize_t first_mac_address_show(struct device *dev, 150 struct device_attribute *a, char *buf) 151 { 152 struct omnia_mcu *mcu = dev_get_drvdata(dev); 153 154 return sysfs_emit(buf, "%pM\n", mcu->board_first_mac); 155 } 156 static DEVICE_ATTR_RO(first_mac_address); 157 158 static ssize_t board_revision_show(struct device *dev, 159 struct device_attribute *a, char *buf) 160 { 161 struct omnia_mcu *mcu = dev_get_drvdata(dev); 162 163 return sysfs_emit(buf, "%u\n", mcu->board_revision); 164 } 165 static DEVICE_ATTR_RO(board_revision); 166 167 static struct attribute *omnia_mcu_base_attrs[] = { 168 &dev_attr_fw_version_hash_application.attr, 169 &dev_attr_fw_version_hash_bootloader.attr, 170 &dev_attr_fw_features.attr, 171 &dev_attr_mcu_type.attr, 172 &dev_attr_reset_selector.attr, 173 &dev_attr_serial_number.attr, 174 &dev_attr_first_mac_address.attr, 175 &dev_attr_board_revision.attr, 176 NULL 177 }; 178 179 static umode_t omnia_mcu_base_attrs_visible(struct kobject *kobj, 180 struct attribute *a, int n) 181 { 182 struct device *dev = kobj_to_dev(kobj); 183 struct omnia_mcu *mcu = dev_get_drvdata(dev); 184 185 if ((a == &dev_attr_serial_number.attr || 186 a == &dev_attr_first_mac_address.attr || 187 a == &dev_attr_board_revision.attr) && 188 !(mcu->features & OMNIA_FEAT_BOARD_INFO)) 189 return 0; 190 191 return a->mode; 192 } 193 194 static const struct attribute_group omnia_mcu_base_group = { 195 .attrs = omnia_mcu_base_attrs, 196 .is_visible = omnia_mcu_base_attrs_visible, 197 }; 198 199 static const struct attribute_group *omnia_mcu_groups[] = { 200 &omnia_mcu_base_group, 201 #ifdef CONFIG_TURRIS_OMNIA_MCU_GPIO 202 &omnia_mcu_gpio_group, 203 #endif 204 #ifdef CONFIG_TURRIS_OMNIA_MCU_SYSOFF_WAKEUP 205 &omnia_mcu_poweroff_group, 206 #endif 207 NULL 208 }; 209 210 static void omnia_mcu_print_version_hash(struct omnia_mcu *mcu, bool bootloader) 211 { 212 const char *type = bootloader ? "bootloader" : "application"; 213 struct device *dev = &mcu->client->dev; 214 char version[OMNIA_FW_VERSION_HEX_LEN]; 215 int err; 216 217 err = omnia_get_version_hash(mcu, bootloader, version); 218 if (err) { 219 dev_err(dev, "Cannot read MCU %s firmware version: %d\n", 220 type, err); 221 return; 222 } 223 224 dev_info(dev, "MCU %s firmware version hash: %s\n", type, version); 225 } 226 227 static const char *omnia_status_to_mcu_type(u16 status) 228 { 229 switch (status & OMNIA_STS_MCU_TYPE_MASK) { 230 case OMNIA_STS_MCU_TYPE_STM32: 231 return "STM32"; 232 case OMNIA_STS_MCU_TYPE_GD32: 233 return "GD32"; 234 case OMNIA_STS_MCU_TYPE_MKL: 235 return "MKL"; 236 default: 237 return "unknown"; 238 } 239 } 240 241 static void omnia_info_missing_feature(struct device *dev, const char *feature) 242 { 243 dev_info(dev, 244 "Your board's MCU firmware does not support the %s feature.\n", 245 feature); 246 } 247 248 static int omnia_mcu_read_features(struct omnia_mcu *mcu) 249 { 250 static const struct { 251 u16 mask; 252 const char *name; 253 } features[] = { 254 #define _DEF_FEAT(_n, _m) { OMNIA_FEAT_ ## _n, _m } 255 _DEF_FEAT(EXT_CMDS, "extended control and status"), 256 _DEF_FEAT(WDT_PING, "watchdog pinging"), 257 _DEF_FEAT(LED_STATE_EXT_MASK, "peripheral LED pins reading"), 258 _DEF_FEAT(NEW_INT_API, "new interrupt API"), 259 _DEF_FEAT(POWEROFF_WAKEUP, "poweroff and wakeup"), 260 _DEF_FEAT(TRNG, "true random number generator"), 261 _DEF_FEAT(BRIGHTNESS_INT, "LED panel brightness change interrupt"), 262 _DEF_FEAT(LED_GAMMA_CORRECTION, "LED gamma correction"), 263 #undef _DEF_FEAT 264 }; 265 struct i2c_client *client = mcu->client; 266 struct device *dev = &client->dev; 267 bool suggest_fw_upgrade = false; 268 u16 status; 269 int err; 270 271 /* status word holds MCU type, which we need below */ 272 err = omnia_cmd_read_u16(client, OMNIA_CMD_GET_STATUS_WORD, &status); 273 if (err) 274 return err; 275 276 /* 277 * Check whether MCU firmware supports the OMNIA_CMD_GET_FEATURES 278 * command. 279 */ 280 if (status & OMNIA_STS_FEATURES_SUPPORTED) { 281 /* try read 32-bit features */ 282 err = omnia_cmd_read_u32(client, OMNIA_CMD_GET_FEATURES, 283 &mcu->features); 284 if (err) { 285 /* try read 16-bit features */ 286 u16 features16; 287 288 err = omnia_cmd_read_u16(client, OMNIA_CMD_GET_FEATURES, 289 &features16); 290 if (err) 291 return err; 292 293 mcu->features = features16; 294 } else { 295 if (mcu->features & OMNIA_FEAT_FROM_BIT_16_INVALID) 296 mcu->features &= GENMASK(15, 0); 297 } 298 } else { 299 dev_info(dev, 300 "Your board's MCU firmware does not support feature reading.\n"); 301 suggest_fw_upgrade = true; 302 } 303 304 mcu->type = omnia_status_to_mcu_type(status); 305 dev_info(dev, "MCU type %s%s\n", mcu->type, 306 (mcu->features & OMNIA_FEAT_PERIPH_MCU) ? 307 ", with peripheral resets wired" : ""); 308 309 omnia_mcu_print_version_hash(mcu, true); 310 311 if (mcu->features & OMNIA_FEAT_BOOTLOADER) 312 dev_warn(dev, 313 "MCU is running bootloader firmware. Was firmware upgrade interrupted?\n"); 314 else 315 omnia_mcu_print_version_hash(mcu, false); 316 317 for (unsigned int i = 0; i < ARRAY_SIZE(features); i++) { 318 if (mcu->features & features[i].mask) 319 continue; 320 321 omnia_info_missing_feature(dev, features[i].name); 322 suggest_fw_upgrade = true; 323 } 324 325 if (suggest_fw_upgrade) 326 dev_info(dev, 327 "Consider upgrading MCU firmware with the omnia-mcutool utility.\n"); 328 329 return 0; 330 } 331 332 static int omnia_mcu_read_board_info(struct omnia_mcu *mcu) 333 { 334 u8 reply[1 + OMNIA_BOARD_INFO_LEN]; 335 int err; 336 337 err = omnia_cmd_read(mcu->client, OMNIA_CMD_BOARD_INFO_GET, reply, 338 sizeof(reply)); 339 if (err) 340 return err; 341 342 if (reply[0] != OMNIA_BOARD_INFO_LEN) 343 return -EIO; 344 345 mcu->board_serial_number = get_unaligned_le64(&reply[1]); 346 347 /* we can't use ether_addr_copy() because reply is not u16-aligned */ 348 memcpy(mcu->board_first_mac, &reply[9], sizeof(mcu->board_first_mac)); 349 350 mcu->board_revision = reply[15]; 351 352 return 0; 353 } 354 355 static int omnia_mcu_probe(struct i2c_client *client) 356 { 357 struct device *dev = &client->dev; 358 struct omnia_mcu *mcu; 359 int err; 360 361 if (!client->irq) 362 return dev_err_probe(dev, -EINVAL, "IRQ resource not found\n"); 363 364 mcu = devm_kzalloc(dev, sizeof(*mcu), GFP_KERNEL); 365 if (!mcu) 366 return -ENOMEM; 367 368 mcu->client = client; 369 i2c_set_clientdata(client, mcu); 370 371 err = omnia_mcu_read_features(mcu); 372 if (err) 373 return dev_err_probe(dev, err, 374 "Cannot determine MCU supported features\n"); 375 376 if (mcu->features & OMNIA_FEAT_BOARD_INFO) { 377 err = omnia_mcu_read_board_info(mcu); 378 if (err) 379 return dev_err_probe(dev, err, 380 "Cannot read board info\n"); 381 } 382 383 err = omnia_mcu_register_sys_off_and_wakeup(mcu); 384 if (err) 385 return err; 386 387 err = omnia_mcu_register_watchdog(mcu); 388 if (err) 389 return err; 390 391 err = omnia_mcu_register_gpiochip(mcu); 392 if (err) 393 return err; 394 395 return omnia_mcu_register_trng(mcu); 396 } 397 398 static const struct of_device_id of_omnia_mcu_match[] = { 399 { .compatible = "cznic,turris-omnia-mcu" }, 400 {} 401 }; 402 403 static struct i2c_driver omnia_mcu_driver = { 404 .probe = omnia_mcu_probe, 405 .driver = { 406 .name = "turris-omnia-mcu", 407 .of_match_table = of_omnia_mcu_match, 408 .dev_groups = omnia_mcu_groups, 409 }, 410 }; 411 module_i2c_driver(omnia_mcu_driver); 412 413 MODULE_AUTHOR("Marek Behun <kabel@kernel.org>"); 414 MODULE_DESCRIPTION("CZ.NIC's Turris Omnia MCU"); 415 MODULE_LICENSE("GPL"); 416