in eeprom.c [602:700]
int tb_drom_read(struct tb_switch *sw)
{
u16 size;
struct tb_drom_header *header;
int res, retries = 1;
if (sw->drom)
return 0;
if (tb_route(sw) == 0) {
/*
* Apple's NHI EFI driver supplies a DROM for the root switch
* in a device property. Use it if available.
*/
if (tb_drom_copy_efi(sw, &size) == 0)
goto parse;
/* Non-Apple hardware has the DROM as part of NVM */
if (tb_drom_copy_nvm(sw, &size) == 0)
goto parse;
/*
* USB4 hosts may support reading DROM through router
* operations.
*/
if (tb_switch_is_usb4(sw)) {
usb4_switch_read_uid(sw, &sw->uid);
if (!usb4_copy_host_drom(sw, &size))
goto parse;
} else {
/*
* The root switch contains only a dummy drom
* (header only, no entries). Hardcode the
* configuration here.
*/
tb_drom_read_uid_only(sw, &sw->uid);
}
return 0;
}
res = tb_drom_read_n(sw, 14, (u8 *) &size, 2);
if (res)
return res;
size &= 0x3ff;
size += TB_DROM_DATA_START;
tb_sw_dbg(sw, "reading drom (length: %#x)\n", size);
if (size < sizeof(*header)) {
tb_sw_warn(sw, "drom too small, aborting\n");
return -EIO;
}
sw->drom = kzalloc(size, GFP_KERNEL);
if (!sw->drom)
return -ENOMEM;
res = tb_drom_read_n(sw, 0, sw->drom, size);
if (res)
goto err;
parse:
header = (void *) sw->drom;
if (header->data_len + TB_DROM_DATA_START != size) {
tb_sw_warn(sw, "drom size mismatch, aborting\n");
goto err;
}
tb_sw_dbg(sw, "DROM version: %d\n", header->device_rom_revision);
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 the DROM parsing fails, wait a moment and retry once */
if (res == -EILSEQ && retries--) {
tb_sw_warn(sw, "parsing DROM failed, retrying\n");
msleep(100);
res = tb_drom_read_n(sw, 0, sw->drom, size);
if (!res)
goto parse;
}
if (!res)
return 0;
err:
kfree(sw->drom);
sw->drom = NULL;
return -EIO;
}