int tb_drom_read()

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;
}