diff options
Diffstat (limited to 'drivers/thunderbolt/eeprom.c')
-rw-r--r-- | drivers/thunderbolt/eeprom.c | 105 |
1 files changed, 80 insertions, 25 deletions
diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c index dd03d3096653..46d0906a3070 100644 --- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -277,6 +277,16 @@ struct tb_drom_entry_port { u8 unknown4:2; } __packed; +/* USB4 product descriptor */ +struct tb_drom_entry_desc { + struct tb_drom_entry_header header; + u16 bcdUSBSpec; + u16 idVendor; + u16 idProduct; + u16 bcdProductFWRevision; + u32 TID; + u8 productHWRevision; +}; /** * tb_drom_read_uid_only() - Read UID directly from DROM @@ -329,6 +339,16 @@ static int tb_drom_parse_entry_generic(struct tb_switch *sw, if (!sw->device_name) return -ENOMEM; break; + case 9: { + const struct tb_drom_entry_desc *desc = + (const struct tb_drom_entry_desc *)entry; + + if (!sw->vendor && !sw->device) { + sw->vendor = desc->idVendor; + sw->device = desc->idProduct; + } + break; + } } return 0; @@ -521,6 +541,51 @@ static int tb_drom_read_n(struct tb_switch *sw, u16 offset, u8 *val, return tb_eeprom_read_n(sw, offset, val, count); } +static int tb_drom_parse(struct tb_switch *sw) +{ + const struct tb_drom_header *header = + (const struct tb_drom_header *)sw->drom; + u32 crc; + + crc = tb_crc8((u8 *) &header->uid, 8); + if (crc != header->uid_crc8) { + tb_sw_warn(sw, + "DROM UID CRC8 mismatch (expected: %#x, got: %#x), aborting\n", + header->uid_crc8, crc); + return -EINVAL; + } + if (!sw->uid) + sw->uid = header->uid; + sw->vendor = header->vendor_id; + sw->device = header->model_id; + + crc = tb_crc32(sw->drom + TB_DROM_DATA_START, header->data_len); + if (crc != header->data_crc32) { + tb_sw_warn(sw, + "DROM data CRC32 mismatch (expected: %#x, got: %#x), continuing\n", + header->data_crc32, crc); + } + + return tb_drom_parse_entries(sw); +} + +static int usb4_drom_parse(struct tb_switch *sw) +{ + const struct tb_drom_header *header = + (const struct tb_drom_header *)sw->drom; + u32 crc; + + crc = tb_crc32(sw->drom + TB_DROM_DATA_START, header->data_len); + if (crc != header->data_crc32) { + tb_sw_warn(sw, + "DROM data CRC32 mismatch (expected: %#x, got: %#x), aborting\n", + header->data_crc32, crc); + return -EINVAL; + } + + return tb_drom_parse_entries(sw); +} + /** * tb_drom_read() - Copy DROM to sw->drom and parse it * @sw: Router whose DROM to read and parse @@ -534,7 +599,6 @@ static int tb_drom_read_n(struct tb_switch *sw, u16 offset, u8 *val, int tb_drom_read(struct tb_switch *sw) { u16 size; - u32 crc; struct tb_drom_header *header; int res, retries = 1; @@ -599,31 +663,21 @@ parse: goto err; } - crc = tb_crc8((u8 *) &header->uid, 8); - if (crc != header->uid_crc8) { - tb_sw_warn(sw, - "drom uid crc8 mismatch (expected: %#x, got: %#x), aborting\n", - header->uid_crc8, crc); - goto err; - } - if (!sw->uid) - sw->uid = header->uid; - sw->vendor = header->vendor_id; - sw->device = header->model_id; - tb_check_quirks(sw); + tb_sw_dbg(sw, "DROM version: %d\n", header->device_rom_revision); - crc = tb_crc32(sw->drom + TB_DROM_DATA_START, header->data_len); - if (crc != header->data_crc32) { - tb_sw_warn(sw, - "drom data crc32 mismatch (expected: %#x, got: %#x), continuing\n", - header->data_crc32, crc); + switch (header->device_rom_revision) { + case 3: + res = usb4_drom_parse(sw); + break; + default: + tb_sw_warn(sw, "DROM device_rom_revision %#x unknown\n", + header->device_rom_revision); + fallthrough; + case 1: + res = tb_drom_parse(sw); + break; } - if (header->device_rom_revision > 2) - tb_sw_warn(sw, "drom device_rom_revision %#x unknown\n", - header->device_rom_revision); - - res = tb_drom_parse_entries(sw); /* If the DROM parsing fails, wait a moment and retry once */ if (res == -EILSEQ && retries--) { tb_sw_warn(sw, "parsing DROM failed, retrying\n"); @@ -633,10 +687,11 @@ parse: goto parse; } - return res; + if (!res) + return 0; + err: kfree(sw->drom); sw->drom = NULL; return -EIO; - } |