static int do_download_mode()

in serial/io_ti.c [1065:1375]


static int do_download_mode(struct edgeport_serial *serial,
		const struct firmware *fw)
{
	struct device *dev = &serial->serial->interface->dev;
	int status = 0;
	int start_address;
	struct edge_ti_manuf_descriptor *ti_manuf_desc;
	int download_cur_ver;
	int download_new_ver;
	struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data;
	struct ti_i2c_desc *rom_desc;

	dev_dbg(dev, "%s - RUNNING IN DOWNLOAD MODE\n", __func__);

	status = check_i2c_image(serial);
	if (status) {
		dev_dbg(dev, "%s - DOWNLOAD MODE -- BAD I2C\n", __func__);
		return status;
	}

	/*
	 * Validate Hardware version number
	 * Read Manufacturing Descriptor from TI Based Edgeport
	 */
	ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL);
	if (!ti_manuf_desc)
		return -ENOMEM;

	status = get_manuf_info(serial, (u8 *)ti_manuf_desc);
	if (status) {
		kfree(ti_manuf_desc);
		return status;
	}

	/* Check version number of ION descriptor */
	if (!ignore_cpu_rev && ti_cpu_rev(ti_manuf_desc) < 2) {
		dev_dbg(dev, "%s - Wrong CPU Rev %d (Must be 2)\n",
			__func__, ti_cpu_rev(ti_manuf_desc));
		kfree(ti_manuf_desc);
		return -EINVAL;
	}

	rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL);
	if (!rom_desc) {
		kfree(ti_manuf_desc);
		return -ENOMEM;
	}

	/* Search for type 2 record (firmware record) */
	start_address = get_descriptor_addr(serial,
			I2C_DESC_TYPE_FIRMWARE_BASIC, rom_desc);
	if (start_address != 0) {
		struct ti_i2c_firmware_rec *firmware_version;
		u8 *record;

		dev_dbg(dev, "%s - Found Type FIRMWARE (Type 2) record\n",
				__func__);

		firmware_version = kmalloc(sizeof(*firmware_version),
							GFP_KERNEL);
		if (!firmware_version) {
			kfree(rom_desc);
			kfree(ti_manuf_desc);
			return -ENOMEM;
		}

		/*
		 * Validate version number
		 * Read the descriptor data
		 */
		status = read_rom(serial, start_address +
				sizeof(struct ti_i2c_desc),
				sizeof(struct ti_i2c_firmware_rec),
				(u8 *)firmware_version);
		if (status) {
			kfree(firmware_version);
			kfree(rom_desc);
			kfree(ti_manuf_desc);
			return status;
		}

		/*
		 * Check version number of download with current
		 * version in I2c
		 */
		download_cur_ver = (firmware_version->Ver_Major << 8) +
				   (firmware_version->Ver_Minor);
		download_new_ver = (fw_hdr->major_version << 8) +
				   (fw_hdr->minor_version);

		dev_dbg(dev, "%s - >> FW Versions Device %d.%d  Driver %d.%d\n",
			__func__, firmware_version->Ver_Major,
			firmware_version->Ver_Minor,
			fw_hdr->major_version, fw_hdr->minor_version);

		/*
		 * Check if we have an old version in the I2C and
		 * update if necessary
		 */
		if (download_cur_ver < download_new_ver) {
			dev_dbg(dev, "%s - Update I2C dld from %d.%d to %d.%d\n",
				__func__,
				firmware_version->Ver_Major,
				firmware_version->Ver_Minor,
				fw_hdr->major_version,
				fw_hdr->minor_version);

			record = kmalloc(1, GFP_KERNEL);
			if (!record) {
				kfree(firmware_version);
				kfree(rom_desc);
				kfree(ti_manuf_desc);
				return -ENOMEM;
			}
			/*
			 * In order to update the I2C firmware we must
			 * change the type 2 record to type 0xF2. This
			 * will force the UMP to come up in Boot Mode.
			 * Then while in boot mode, the driver will
			 * download the latest firmware (padded to
			 * 15.5k) into the UMP ram. Finally when the
			 * device comes back up in download mode the
			 * driver will cause the new firmware to be
			 * copied from the UMP Ram to I2C and the
			 * firmware will update the record type from
			 * 0xf2 to 0x02.
			 */
			*record = I2C_DESC_TYPE_FIRMWARE_BLANK;

			/*
			 * Change the I2C Firmware record type to
			 * 0xf2 to trigger an update
			 */
			status = write_rom(serial, start_address,
					sizeof(*record), record);
			if (status) {
				kfree(record);
				kfree(firmware_version);
				kfree(rom_desc);
				kfree(ti_manuf_desc);
				return status;
			}

			/*
			 * verify the write -- must do this in order
			 * for write to complete before we do the
			 * hardware reset
			 */
			status = read_rom(serial,
						start_address,
						sizeof(*record),
						record);
			if (status) {
				kfree(record);
				kfree(firmware_version);
				kfree(rom_desc);
				kfree(ti_manuf_desc);
				return status;
			}

			if (*record != I2C_DESC_TYPE_FIRMWARE_BLANK) {
				dev_err(dev, "%s - error resetting device\n",
						__func__);
				kfree(record);
				kfree(firmware_version);
				kfree(rom_desc);
				kfree(ti_manuf_desc);
				return -ENODEV;
			}

			dev_dbg(dev, "%s - HARDWARE RESET\n", __func__);

			/* Reset UMP -- Back to BOOT MODE */
			status = ti_vsend_sync(serial->serial->dev,
					UMPC_HARDWARE_RESET,
					0, 0, NULL, 0,
					TI_VSEND_TIMEOUT_DEFAULT);

			dev_dbg(dev, "%s - HARDWARE RESET return %d\n",
					__func__, status);

			/* return an error on purpose. */
			kfree(record);
			kfree(firmware_version);
			kfree(rom_desc);
			kfree(ti_manuf_desc);
			return -ENODEV;
		}
		/* Same or newer fw version is already loaded */
		serial->fw_version = download_cur_ver;
		kfree(firmware_version);
	}
	/* Search for type 0xF2 record (firmware blank record) */
	else {
		start_address = get_descriptor_addr(serial,
				I2C_DESC_TYPE_FIRMWARE_BLANK, rom_desc);
		if (start_address != 0) {
#define HEADER_SIZE	(sizeof(struct ti_i2c_desc) + \
				sizeof(struct ti_i2c_firmware_rec))
			u8 *header;
			u8 *vheader;

			header = kmalloc(HEADER_SIZE, GFP_KERNEL);
			if (!header) {
				kfree(rom_desc);
				kfree(ti_manuf_desc);
				return -ENOMEM;
			}

			vheader = kmalloc(HEADER_SIZE, GFP_KERNEL);
			if (!vheader) {
				kfree(header);
				kfree(rom_desc);
				kfree(ti_manuf_desc);
				return -ENOMEM;
			}

			dev_dbg(dev, "%s - Found Type BLANK FIRMWARE (Type F2) record\n",
					__func__);

			/*
			 * In order to update the I2C firmware we must change
			 * the type 2 record to type 0xF2. This will force the
			 * UMP to come up in Boot Mode.  Then while in boot
			 * mode, the driver will download the latest firmware
			 * (padded to 15.5k) into the UMP ram. Finally when the
			 * device comes back up in download mode the driver
			 * will cause the new firmware to be copied from the
			 * UMP Ram to I2C and the firmware will update the
			 * record type from 0xf2 to 0x02.
			 */
			status = build_i2c_fw_hdr(header, fw);
			if (status) {
				kfree(vheader);
				kfree(header);
				kfree(rom_desc);
				kfree(ti_manuf_desc);
				return -EINVAL;
			}

			/*
			 * Update I2C with type 0xf2 record with correct
			 * size and checksum
			 */
			status = write_rom(serial,
						start_address,
						HEADER_SIZE,
						header);
			if (status) {
				kfree(vheader);
				kfree(header);
				kfree(rom_desc);
				kfree(ti_manuf_desc);
				return -EINVAL;
			}

			/*
			 * verify the write -- must do this in order for
			 * write to complete before we do the hardware reset
			 */
			status = read_rom(serial, start_address,
							HEADER_SIZE, vheader);

			if (status) {
				dev_dbg(dev, "%s - can't read header back\n",
						__func__);
				kfree(vheader);
				kfree(header);
				kfree(rom_desc);
				kfree(ti_manuf_desc);
				return status;
			}
			if (memcmp(vheader, header, HEADER_SIZE)) {
				dev_dbg(dev, "%s - write download record failed\n",
						__func__);
				kfree(vheader);
				kfree(header);
				kfree(rom_desc);
				kfree(ti_manuf_desc);
				return -EINVAL;
			}

			kfree(vheader);
			kfree(header);

			dev_dbg(dev, "%s - Start firmware update\n", __func__);

			/* Tell firmware to copy download image into I2C */
			status = ti_vsend_sync(serial->serial->dev,
					UMPC_COPY_DNLD_TO_I2C,
					0, 0, NULL, 0,
					TI_VSEND_TIMEOUT_FW_DOWNLOAD);

			dev_dbg(dev, "%s - Update complete 0x%x\n", __func__,
					status);
			if (status) {
				dev_err(dev,
					"%s - UMPC_COPY_DNLD_TO_I2C failed\n",
					__func__);
				kfree(rom_desc);
				kfree(ti_manuf_desc);
				return status;
			}
		}
	}

	/* The device is running the download code */
	kfree(rom_desc);
	kfree(ti_manuf_desc);
	return 0;
}