18bd5741eSChristian Lamparter // SPDX-License-Identifier: GPL-2.0
28bd5741eSChristian Lamparter /* Copyright (C) 2019-2020 Linaro Limited */
38bd5741eSChristian Lamparter 
48bd5741eSChristian Lamparter #include <linux/acpi.h>
58bd5741eSChristian Lamparter #include <linux/firmware.h>
68bd5741eSChristian Lamparter #include <linux/module.h>
78bd5741eSChristian Lamparter #include <linux/pci.h>
88bd5741eSChristian Lamparter #include <linux/slab.h>
98bd5741eSChristian Lamparter #include <linux/unaligned/access_ok.h>
108bd5741eSChristian Lamparter 
118bd5741eSChristian Lamparter #include "xhci.h"
128bd5741eSChristian Lamparter #include "xhci-trace.h"
138bd5741eSChristian Lamparter #include "xhci-pci.h"
148bd5741eSChristian Lamparter 
158bd5741eSChristian Lamparter #define RENESAS_FW_VERSION				0x6C
168bd5741eSChristian Lamparter #define RENESAS_ROM_CONFIG				0xF0
178bd5741eSChristian Lamparter #define RENESAS_FW_STATUS				0xF4
188bd5741eSChristian Lamparter #define RENESAS_FW_STATUS_MSB				0xF5
198bd5741eSChristian Lamparter #define RENESAS_ROM_STATUS				0xF6
208bd5741eSChristian Lamparter #define RENESAS_ROM_STATUS_MSB				0xF7
218bd5741eSChristian Lamparter #define RENESAS_DATA0					0xF8
228bd5741eSChristian Lamparter #define RENESAS_DATA1					0xFC
238bd5741eSChristian Lamparter 
248bd5741eSChristian Lamparter #define RENESAS_FW_VERSION_FIELD			GENMASK(23, 7)
258bd5741eSChristian Lamparter #define RENESAS_FW_VERSION_OFFSET			8
268bd5741eSChristian Lamparter 
278bd5741eSChristian Lamparter #define RENESAS_FW_STATUS_DOWNLOAD_ENABLE		BIT(0)
288bd5741eSChristian Lamparter #define RENESAS_FW_STATUS_LOCK				BIT(1)
298bd5741eSChristian Lamparter #define RENESAS_FW_STATUS_RESULT			GENMASK(6, 4)
308bd5741eSChristian Lamparter   #define RENESAS_FW_STATUS_INVALID			0
318bd5741eSChristian Lamparter   #define RENESAS_FW_STATUS_SUCCESS			BIT(4)
328bd5741eSChristian Lamparter   #define RENESAS_FW_STATUS_ERROR			BIT(5)
338bd5741eSChristian Lamparter #define RENESAS_FW_STATUS_SET_DATA0			BIT(8)
348bd5741eSChristian Lamparter #define RENESAS_FW_STATUS_SET_DATA1			BIT(9)
358bd5741eSChristian Lamparter 
368bd5741eSChristian Lamparter #define RENESAS_ROM_STATUS_ACCESS			BIT(0)
378bd5741eSChristian Lamparter #define RENESAS_ROM_STATUS_ERASE			BIT(1)
388bd5741eSChristian Lamparter #define RENESAS_ROM_STATUS_RELOAD			BIT(2)
398bd5741eSChristian Lamparter #define RENESAS_ROM_STATUS_RESULT			GENMASK(6, 4)
408bd5741eSChristian Lamparter   #define RENESAS_ROM_STATUS_NO_RESULT			0
418bd5741eSChristian Lamparter   #define RENESAS_ROM_STATUS_SUCCESS			BIT(4)
428bd5741eSChristian Lamparter   #define RENESAS_ROM_STATUS_ERROR			BIT(5)
438bd5741eSChristian Lamparter #define RENESAS_ROM_STATUS_SET_DATA0			BIT(8)
448bd5741eSChristian Lamparter #define RENESAS_ROM_STATUS_SET_DATA1			BIT(9)
458bd5741eSChristian Lamparter #define RENESAS_ROM_STATUS_ROM_EXISTS			BIT(15)
468bd5741eSChristian Lamparter 
478bd5741eSChristian Lamparter #define RENESAS_ROM_ERASE_MAGIC				0x5A65726F
488bd5741eSChristian Lamparter #define RENESAS_ROM_WRITE_MAGIC				0x53524F4D
498bd5741eSChristian Lamparter 
508bd5741eSChristian Lamparter #define RENESAS_RETRY	10000
518bd5741eSChristian Lamparter #define RENESAS_DELAY	10
528bd5741eSChristian Lamparter 
532478be82SVinod Koul #define ROM_VALID_01 0x2013
542478be82SVinod Koul #define ROM_VALID_02 0x2026
552478be82SVinod Koul 
562478be82SVinod Koul static int renesas_verify_fw_version(struct pci_dev *pdev, u32 version)
572478be82SVinod Koul {
582478be82SVinod Koul 	switch (version) {
592478be82SVinod Koul 	case ROM_VALID_01:
602478be82SVinod Koul 	case ROM_VALID_02:
612478be82SVinod Koul 		return 0;
622478be82SVinod Koul 	}
632478be82SVinod Koul 	dev_err(&pdev->dev, "FW has invalid version :%d\n", version);
642478be82SVinod Koul 	return -EINVAL;
652478be82SVinod Koul }
662478be82SVinod Koul 
678bd5741eSChristian Lamparter static int renesas_fw_download_image(struct pci_dev *dev,
682478be82SVinod Koul 				     const u32 *fw, size_t step, bool rom)
698bd5741eSChristian Lamparter {
708bd5741eSChristian Lamparter 	size_t i;
718bd5741eSChristian Lamparter 	int err;
728bd5741eSChristian Lamparter 	u8 fw_status;
738bd5741eSChristian Lamparter 	bool data0_or_data1;
742478be82SVinod Koul 	u32 status_reg;
752478be82SVinod Koul 
762478be82SVinod Koul 	if (rom)
772478be82SVinod Koul 		status_reg = RENESAS_ROM_STATUS_MSB;
782478be82SVinod Koul 	else
792478be82SVinod Koul 		status_reg = RENESAS_FW_STATUS_MSB;
808bd5741eSChristian Lamparter 
818bd5741eSChristian Lamparter 	/*
828bd5741eSChristian Lamparter 	 * The hardware does alternate between two 32-bit pages.
838bd5741eSChristian Lamparter 	 * (This is because each row of the firmware is 8 bytes).
848bd5741eSChristian Lamparter 	 *
858bd5741eSChristian Lamparter 	 * for even steps we use DATA0, for odd steps DATA1.
868bd5741eSChristian Lamparter 	 */
878bd5741eSChristian Lamparter 	data0_or_data1 = (step & 1) == 1;
888bd5741eSChristian Lamparter 
898bd5741eSChristian Lamparter 	/* step+1. Read "Set DATAX" and confirm it is cleared. */
908bd5741eSChristian Lamparter 	for (i = 0; i < RENESAS_RETRY; i++) {
912478be82SVinod Koul 		err = pci_read_config_byte(dev, status_reg, &fw_status);
928bd5741eSChristian Lamparter 		if (err) {
938bd5741eSChristian Lamparter 			dev_err(&dev->dev, "Read Status failed: %d\n",
948bd5741eSChristian Lamparter 				pcibios_err_to_errno(err));
958bd5741eSChristian Lamparter 			return pcibios_err_to_errno(err);
968bd5741eSChristian Lamparter 		}
978bd5741eSChristian Lamparter 		if (!(fw_status & BIT(data0_or_data1)))
988bd5741eSChristian Lamparter 			break;
998bd5741eSChristian Lamparter 
1008bd5741eSChristian Lamparter 		udelay(RENESAS_DELAY);
1018bd5741eSChristian Lamparter 	}
1028bd5741eSChristian Lamparter 	if (i == RENESAS_RETRY) {
1038bd5741eSChristian Lamparter 		dev_err(&dev->dev, "Timeout for Set DATAX step: %zd\n", step);
1048bd5741eSChristian Lamparter 		return -ETIMEDOUT;
1058bd5741eSChristian Lamparter 	}
1068bd5741eSChristian Lamparter 
1078bd5741eSChristian Lamparter 	/*
1088bd5741eSChristian Lamparter 	 * step+2. Write FW data to "DATAX".
1098bd5741eSChristian Lamparter 	 * "LSB is left" => force little endian
1108bd5741eSChristian Lamparter 	 */
1118bd5741eSChristian Lamparter 	err = pci_write_config_dword(dev, data0_or_data1 ?
1128bd5741eSChristian Lamparter 				     RENESAS_DATA1 : RENESAS_DATA0,
1138bd5741eSChristian Lamparter 				     (__force u32)cpu_to_le32(fw[step]));
1148bd5741eSChristian Lamparter 	if (err) {
1158bd5741eSChristian Lamparter 		dev_err(&dev->dev, "Write to DATAX failed: %d\n",
1168bd5741eSChristian Lamparter 			pcibios_err_to_errno(err));
1178bd5741eSChristian Lamparter 		return pcibios_err_to_errno(err);
1188bd5741eSChristian Lamparter 	}
1198bd5741eSChristian Lamparter 
1208bd5741eSChristian Lamparter 	udelay(100);
1218bd5741eSChristian Lamparter 
1228bd5741eSChristian Lamparter 	/* step+3. Set "Set DATAX". */
1232478be82SVinod Koul 	err = pci_write_config_byte(dev, status_reg, BIT(data0_or_data1));
1248bd5741eSChristian Lamparter 	if (err) {
1258bd5741eSChristian Lamparter 		dev_err(&dev->dev, "Write config for DATAX failed: %d\n",
1268bd5741eSChristian Lamparter 			pcibios_err_to_errno(err));
1278bd5741eSChristian Lamparter 		return pcibios_err_to_errno(err);
1288bd5741eSChristian Lamparter 	}
1298bd5741eSChristian Lamparter 
1308bd5741eSChristian Lamparter 	return 0;
1318bd5741eSChristian Lamparter }
1328bd5741eSChristian Lamparter 
1338bd5741eSChristian Lamparter static int renesas_fw_verify(const void *fw_data,
1348bd5741eSChristian Lamparter 			     size_t length)
1358bd5741eSChristian Lamparter {
1368bd5741eSChristian Lamparter 	u16 fw_version_pointer;
1378bd5741eSChristian Lamparter 	u16 fw_version;
1388bd5741eSChristian Lamparter 
1398bd5741eSChristian Lamparter 	/*
1408bd5741eSChristian Lamparter 	 * The Firmware's Data Format is describe in
1418bd5741eSChristian Lamparter 	 * "6.3 Data Format" R19UH0078EJ0500 Rev.5.00 page 124
1428bd5741eSChristian Lamparter 	 */
1438bd5741eSChristian Lamparter 
1448bd5741eSChristian Lamparter 	/*
1458bd5741eSChristian Lamparter 	 * The bootrom chips of the big brother have sizes up to 64k, let's
1468bd5741eSChristian Lamparter 	 * assume that's the biggest the firmware can get.
1478bd5741eSChristian Lamparter 	 */
1488bd5741eSChristian Lamparter 	if (length < 0x1000 || length >= 0x10000) {
1498bd5741eSChristian Lamparter 		pr_err("firmware is size %zd is not (4k - 64k).",
1508bd5741eSChristian Lamparter 			length);
1518bd5741eSChristian Lamparter 		return -EINVAL;
1528bd5741eSChristian Lamparter 	}
1538bd5741eSChristian Lamparter 
1548bd5741eSChristian Lamparter 	/* The First 2 bytes are fixed value (55aa). "LSB on Left" */
1558bd5741eSChristian Lamparter 	if (get_unaligned_le16(fw_data) != 0x55aa) {
1568bd5741eSChristian Lamparter 		pr_err("no valid firmware header found.");
1578bd5741eSChristian Lamparter 		return -EINVAL;
1588bd5741eSChristian Lamparter 	}
1598bd5741eSChristian Lamparter 
1608bd5741eSChristian Lamparter 	/* verify the firmware version position and print it. */
1618bd5741eSChristian Lamparter 	fw_version_pointer = get_unaligned_le16(fw_data + 4);
1628bd5741eSChristian Lamparter 	if (fw_version_pointer + 2 >= length) {
1638bd5741eSChristian Lamparter 		pr_err("fw ver pointer is outside of the firmware image");
1648bd5741eSChristian Lamparter 		return -EINVAL;
1658bd5741eSChristian Lamparter 	}
1668bd5741eSChristian Lamparter 
1678bd5741eSChristian Lamparter 	fw_version = get_unaligned_le16(fw_data + fw_version_pointer);
1688bd5741eSChristian Lamparter 	pr_err("got firmware version: %02x.", fw_version);
1698bd5741eSChristian Lamparter 
1708bd5741eSChristian Lamparter 	return 0;
1718bd5741eSChristian Lamparter }
1728bd5741eSChristian Lamparter 
1732478be82SVinod Koul static bool renesas_check_rom(struct pci_dev *pdev)
1742478be82SVinod Koul {
1752478be82SVinod Koul 	u16 rom_status;
1762478be82SVinod Koul 	int retval;
1772478be82SVinod Koul 
1782478be82SVinod Koul 	/* Check if external ROM exists */
1792478be82SVinod Koul 	retval = pci_read_config_word(pdev, RENESAS_ROM_STATUS, &rom_status);
1802478be82SVinod Koul 	if (retval)
1812478be82SVinod Koul 		return false;
1822478be82SVinod Koul 
1832478be82SVinod Koul 	rom_status &= RENESAS_ROM_STATUS_ROM_EXISTS;
1842478be82SVinod Koul 	if (rom_status) {
1852478be82SVinod Koul 		dev_dbg(&pdev->dev, "External ROM exists\n");
1862478be82SVinod Koul 		return true; /* External ROM exists */
1872478be82SVinod Koul 	}
1882478be82SVinod Koul 
1892478be82SVinod Koul 	return false;
1902478be82SVinod Koul }
1912478be82SVinod Koul 
1922478be82SVinod Koul static int renesas_check_rom_state(struct pci_dev *pdev)
1932478be82SVinod Koul {
1942478be82SVinod Koul 	u16 rom_state;
1952478be82SVinod Koul 	u32 version;
1962478be82SVinod Koul 	int err;
1972478be82SVinod Koul 
1982478be82SVinod Koul 	/* check FW version */
1992478be82SVinod Koul 	err = pci_read_config_dword(pdev, RENESAS_FW_VERSION, &version);
2002478be82SVinod Koul 	if (err)
2012478be82SVinod Koul 		return pcibios_err_to_errno(err);
2022478be82SVinod Koul 
2032478be82SVinod Koul 	version &= RENESAS_FW_VERSION_FIELD;
2042478be82SVinod Koul 	version = version >> RENESAS_FW_VERSION_OFFSET;
2052478be82SVinod Koul 
2062478be82SVinod Koul 	err = renesas_verify_fw_version(pdev, version);
2072478be82SVinod Koul 	if (err)
2082478be82SVinod Koul 		return err;
2092478be82SVinod Koul 
2102478be82SVinod Koul 	/*
2112478be82SVinod Koul 	 * Test if ROM is present and loaded, if so we can skip everything
2122478be82SVinod Koul 	 */
2132478be82SVinod Koul 	err = pci_read_config_word(pdev, RENESAS_ROM_STATUS, &rom_state);
2142478be82SVinod Koul 	if (err)
2152478be82SVinod Koul 		return pcibios_err_to_errno(err);
2162478be82SVinod Koul 
2172478be82SVinod Koul 	if (rom_state & BIT(15)) {
2182478be82SVinod Koul 		/* ROM exists */
2192478be82SVinod Koul 		dev_dbg(&pdev->dev, "ROM exists\n");
2202478be82SVinod Koul 
2212478be82SVinod Koul 		/* Check the "Result Code" Bits (6:4) and act accordingly */
2222478be82SVinod Koul 		switch (rom_state & RENESAS_ROM_STATUS_RESULT) {
2232478be82SVinod Koul 		case RENESAS_ROM_STATUS_SUCCESS:
2242478be82SVinod Koul 			return 0;
2252478be82SVinod Koul 
2262478be82SVinod Koul 		case RENESAS_ROM_STATUS_NO_RESULT: /* No result yet */
2272478be82SVinod Koul 			return 0;
2282478be82SVinod Koul 
2292478be82SVinod Koul 		case RENESAS_ROM_STATUS_ERROR: /* Error State */
2302478be82SVinod Koul 		default: /* All other states are marked as "Reserved states" */
2312478be82SVinod Koul 			dev_err(&pdev->dev, "Invalid ROM..");
2322478be82SVinod Koul 			break;
2332478be82SVinod Koul 		}
2342478be82SVinod Koul 	}
2352478be82SVinod Koul 
2362478be82SVinod Koul 	return -EIO;
2372478be82SVinod Koul }
2382478be82SVinod Koul 
2398bd5741eSChristian Lamparter static int renesas_fw_check_running(struct pci_dev *pdev)
2408bd5741eSChristian Lamparter {
2418bd5741eSChristian Lamparter 	u8 fw_state;
2422478be82SVinod Koul 	int err;
2432478be82SVinod Koul 
2442478be82SVinod Koul 	/* Check if device has ROM and loaded, if so skip everything */
2452478be82SVinod Koul 	err = renesas_check_rom(pdev);
2462478be82SVinod Koul 	if (err) { /* we have rom */
2472478be82SVinod Koul 		err = renesas_check_rom_state(pdev);
2482478be82SVinod Koul 		if (!err)
2492478be82SVinod Koul 			return err;
2502478be82SVinod Koul 	}
2518bd5741eSChristian Lamparter 
2528bd5741eSChristian Lamparter 	/*
2538bd5741eSChristian Lamparter 	 * Test if the device is actually needing the firmware. As most
2548bd5741eSChristian Lamparter 	 * BIOSes will initialize the device for us. If the device is
2558bd5741eSChristian Lamparter 	 * initialized.
2568bd5741eSChristian Lamparter 	 */
2578bd5741eSChristian Lamparter 	err = pci_read_config_byte(pdev, RENESAS_FW_STATUS, &fw_state);
2588bd5741eSChristian Lamparter 	if (err)
2598bd5741eSChristian Lamparter 		return pcibios_err_to_errno(err);
2608bd5741eSChristian Lamparter 
2618bd5741eSChristian Lamparter 	/*
2628bd5741eSChristian Lamparter 	 * Check if "FW Download Lock" is locked. If it is and the FW is
2638bd5741eSChristian Lamparter 	 * ready we can simply continue. If the FW is not ready, we have
2648bd5741eSChristian Lamparter 	 * to give up.
2658bd5741eSChristian Lamparter 	 */
2668bd5741eSChristian Lamparter 	if (fw_state & RENESAS_FW_STATUS_LOCK) {
2678bd5741eSChristian Lamparter 		dev_dbg(&pdev->dev, "FW Download Lock is engaged.");
2688bd5741eSChristian Lamparter 
2698bd5741eSChristian Lamparter 		if (fw_state & RENESAS_FW_STATUS_SUCCESS)
2708bd5741eSChristian Lamparter 			return 0;
2718bd5741eSChristian Lamparter 
2728bd5741eSChristian Lamparter 		dev_err(&pdev->dev,
2738bd5741eSChristian Lamparter 			"FW Download Lock is set and FW is not ready. Giving Up.");
2748bd5741eSChristian Lamparter 		return -EIO;
2758bd5741eSChristian Lamparter 	}
2768bd5741eSChristian Lamparter 
2778bd5741eSChristian Lamparter 	/*
2788bd5741eSChristian Lamparter 	 * Check if "FW Download Enable" is set. If someone (us?) tampered
2798bd5741eSChristian Lamparter 	 * with it and it can't be reset, we have to give up too... and
2808bd5741eSChristian Lamparter 	 * ask for a forgiveness and a reboot.
2818bd5741eSChristian Lamparter 	 */
2828bd5741eSChristian Lamparter 	if (fw_state & RENESAS_FW_STATUS_DOWNLOAD_ENABLE) {
2838bd5741eSChristian Lamparter 		dev_err(&pdev->dev,
2848bd5741eSChristian Lamparter 			"FW Download Enable is stale. Giving Up (poweroff/reboot needed).");
2858bd5741eSChristian Lamparter 		return -EIO;
2868bd5741eSChristian Lamparter 	}
2878bd5741eSChristian Lamparter 
2888bd5741eSChristian Lamparter 	/* Otherwise, Check the "Result Code" Bits (6:4) and act accordingly */
2898bd5741eSChristian Lamparter 	switch (fw_state & RENESAS_FW_STATUS_RESULT) {
2908bd5741eSChristian Lamparter 	case 0: /* No result yet */
2918bd5741eSChristian Lamparter 		dev_dbg(&pdev->dev, "FW is not ready/loaded yet.");
2928bd5741eSChristian Lamparter 
2938bd5741eSChristian Lamparter 		/* tell the caller, that this device needs the firmware. */
2948bd5741eSChristian Lamparter 		return 1;
2958bd5741eSChristian Lamparter 
2968bd5741eSChristian Lamparter 	case RENESAS_FW_STATUS_SUCCESS: /* Success, device should be working. */
2978bd5741eSChristian Lamparter 		dev_dbg(&pdev->dev, "FW is ready.");
2988bd5741eSChristian Lamparter 		return 0;
2998bd5741eSChristian Lamparter 
3008bd5741eSChristian Lamparter 	case RENESAS_FW_STATUS_ERROR: /* Error State */
3018bd5741eSChristian Lamparter 		dev_err(&pdev->dev,
3028bd5741eSChristian Lamparter 			"hardware is in an error state. Giving up (poweroff/reboot needed).");
3038bd5741eSChristian Lamparter 		return -ENODEV;
3048bd5741eSChristian Lamparter 
3058bd5741eSChristian Lamparter 	default: /* All other states are marked as "Reserved states" */
3068bd5741eSChristian Lamparter 		dev_err(&pdev->dev,
3078bd5741eSChristian Lamparter 			"hardware is in an invalid state %lx. Giving up (poweroff/reboot needed).",
3088bd5741eSChristian Lamparter 			(fw_state & RENESAS_FW_STATUS_RESULT) >> 4);
3098bd5741eSChristian Lamparter 		return -EINVAL;
3108bd5741eSChristian Lamparter 	}
3118bd5741eSChristian Lamparter }
3128bd5741eSChristian Lamparter 
3138bd5741eSChristian Lamparter static int renesas_fw_download(struct pci_dev *pdev,
3148bd5741eSChristian Lamparter 			       const struct firmware *fw)
3158bd5741eSChristian Lamparter {
3168bd5741eSChristian Lamparter 	const u32 *fw_data = (const u32 *)fw->data;
3178bd5741eSChristian Lamparter 	size_t i;
3188bd5741eSChristian Lamparter 	int err;
3198bd5741eSChristian Lamparter 	u8 fw_status;
3208bd5741eSChristian Lamparter 
3218bd5741eSChristian Lamparter 	/*
3228bd5741eSChristian Lamparter 	 * For more information and the big picture: please look at the
3238bd5741eSChristian Lamparter 	 * "Firmware Download Sequence" in "7.1 FW Download Interface"
3248bd5741eSChristian Lamparter 	 * of R19UH0078EJ0500 Rev.5.00 page 131
3258bd5741eSChristian Lamparter 	 */
3268bd5741eSChristian Lamparter 
3278bd5741eSChristian Lamparter 	/*
3288bd5741eSChristian Lamparter 	 * 0. Set "FW Download Enable" bit in the
3298bd5741eSChristian Lamparter 	 * "FW Download Control & Status Register" at 0xF4
3308bd5741eSChristian Lamparter 	 */
3318bd5741eSChristian Lamparter 	err = pci_write_config_byte(pdev, RENESAS_FW_STATUS,
3328bd5741eSChristian Lamparter 				    RENESAS_FW_STATUS_DOWNLOAD_ENABLE);
3338bd5741eSChristian Lamparter 	if (err)
3348bd5741eSChristian Lamparter 		return pcibios_err_to_errno(err);
3358bd5741eSChristian Lamparter 
3368bd5741eSChristian Lamparter 	/* 1 - 10 follow one step after the other. */
3378bd5741eSChristian Lamparter 	for (i = 0; i < fw->size / 4; i++) {
3382478be82SVinod Koul 		err = renesas_fw_download_image(pdev, fw_data, i, false);
3398bd5741eSChristian Lamparter 		if (err) {
3408bd5741eSChristian Lamparter 			dev_err(&pdev->dev,
3418bd5741eSChristian Lamparter 				"Firmware Download Step %zd failed at position %zd bytes with (%d).",
3428bd5741eSChristian Lamparter 				i, i * 4, err);
3438bd5741eSChristian Lamparter 			return err;
3448bd5741eSChristian Lamparter 		}
3458bd5741eSChristian Lamparter 	}
3468bd5741eSChristian Lamparter 
3478bd5741eSChristian Lamparter 	/*
3488bd5741eSChristian Lamparter 	 * This sequence continues until the last data is written to
3498bd5741eSChristian Lamparter 	 * "DATA0" or "DATA1". Naturally, we wait until "SET DATA0/1"
3508bd5741eSChristian Lamparter 	 * is cleared by the hardware beforehand.
3518bd5741eSChristian Lamparter 	 */
3528bd5741eSChristian Lamparter 	for (i = 0; i < RENESAS_RETRY; i++) {
3538bd5741eSChristian Lamparter 		err = pci_read_config_byte(pdev, RENESAS_FW_STATUS_MSB,
3548bd5741eSChristian Lamparter 					   &fw_status);
3558bd5741eSChristian Lamparter 		if (err)
3568bd5741eSChristian Lamparter 			return pcibios_err_to_errno(err);
3578bd5741eSChristian Lamparter 		if (!(fw_status & (BIT(0) | BIT(1))))
3588bd5741eSChristian Lamparter 			break;
3598bd5741eSChristian Lamparter 
3608bd5741eSChristian Lamparter 		udelay(RENESAS_DELAY);
3618bd5741eSChristian Lamparter 	}
3628bd5741eSChristian Lamparter 	if (i == RENESAS_RETRY)
3638bd5741eSChristian Lamparter 		dev_warn(&pdev->dev, "Final Firmware Download step timed out.");
3648bd5741eSChristian Lamparter 
3658bd5741eSChristian Lamparter 	/*
3668bd5741eSChristian Lamparter 	 * 11. After finishing writing the last data of FW, the
3678bd5741eSChristian Lamparter 	 * System Software must clear "FW Download Enable"
3688bd5741eSChristian Lamparter 	 */
3698bd5741eSChristian Lamparter 	err = pci_write_config_byte(pdev, RENESAS_FW_STATUS, 0);
3708bd5741eSChristian Lamparter 	if (err)
3718bd5741eSChristian Lamparter 		return pcibios_err_to_errno(err);
3728bd5741eSChristian Lamparter 
3738bd5741eSChristian Lamparter 	/* 12. Read "Result Code" and confirm it is good. */
3748bd5741eSChristian Lamparter 	for (i = 0; i < RENESAS_RETRY; i++) {
3758bd5741eSChristian Lamparter 		err = pci_read_config_byte(pdev, RENESAS_FW_STATUS, &fw_status);
3768bd5741eSChristian Lamparter 		if (err)
3778bd5741eSChristian Lamparter 			return pcibios_err_to_errno(err);
3788bd5741eSChristian Lamparter 		if (fw_status & RENESAS_FW_STATUS_SUCCESS)
3798bd5741eSChristian Lamparter 			break;
3808bd5741eSChristian Lamparter 
3818bd5741eSChristian Lamparter 		udelay(RENESAS_DELAY);
3828bd5741eSChristian Lamparter 	}
3838bd5741eSChristian Lamparter 	if (i == RENESAS_RETRY) {
3848bd5741eSChristian Lamparter 		/* Timed out / Error - let's see if we can fix this */
3858bd5741eSChristian Lamparter 		err = renesas_fw_check_running(pdev);
3868bd5741eSChristian Lamparter 		switch (err) {
3878bd5741eSChristian Lamparter 		case 0: /*
3888bd5741eSChristian Lamparter 			 * we shouldn't end up here.
3898bd5741eSChristian Lamparter 			 * maybe it took a little bit longer.
3908bd5741eSChristian Lamparter 			 * But all should be well?
3918bd5741eSChristian Lamparter 			 */
3928bd5741eSChristian Lamparter 			break;
3938bd5741eSChristian Lamparter 
3948bd5741eSChristian Lamparter 		case 1: /* (No result yet! */
3958bd5741eSChristian Lamparter 			dev_err(&pdev->dev, "FW Load timedout");
3968bd5741eSChristian Lamparter 			return -ETIMEDOUT;
3978bd5741eSChristian Lamparter 
3988bd5741eSChristian Lamparter 		default:
3998bd5741eSChristian Lamparter 			return err;
4008bd5741eSChristian Lamparter 		}
4018bd5741eSChristian Lamparter 	}
4028bd5741eSChristian Lamparter 
4038bd5741eSChristian Lamparter 	return 0;
4048bd5741eSChristian Lamparter }
4058bd5741eSChristian Lamparter 
4062478be82SVinod Koul static void renesas_rom_erase(struct pci_dev *pdev)
4072478be82SVinod Koul {
4082478be82SVinod Koul 	int retval, i;
4092478be82SVinod Koul 	u8 status;
4102478be82SVinod Koul 
4112478be82SVinod Koul 	dev_dbg(&pdev->dev, "Performing ROM Erase...\n");
4122478be82SVinod Koul 	retval = pci_write_config_dword(pdev, RENESAS_DATA0,
4132478be82SVinod Koul 					RENESAS_ROM_ERASE_MAGIC);
4142478be82SVinod Koul 	if (retval) {
4152478be82SVinod Koul 		dev_err(&pdev->dev, "ROM erase, magic word write failed: %d\n",
4162478be82SVinod Koul 			pcibios_err_to_errno(retval));
4172478be82SVinod Koul 		return;
4182478be82SVinod Koul 	}
4192478be82SVinod Koul 
4202478be82SVinod Koul 	retval = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status);
4212478be82SVinod Koul 	if (retval) {
4222478be82SVinod Koul 		dev_err(&pdev->dev, "ROM status read failed: %d\n",
4232478be82SVinod Koul 			pcibios_err_to_errno(retval));
4242478be82SVinod Koul 		return;
4252478be82SVinod Koul 	}
4262478be82SVinod Koul 	status |= RENESAS_ROM_STATUS_ERASE;
4272478be82SVinod Koul 	retval = pci_write_config_byte(pdev, RENESAS_ROM_STATUS, status);
4282478be82SVinod Koul 	if (retval) {
4292478be82SVinod Koul 		dev_err(&pdev->dev, "ROM erase set word write failed\n");
4302478be82SVinod Koul 		return;
4312478be82SVinod Koul 	}
4322478be82SVinod Koul 
4332478be82SVinod Koul 	/* sleep a bit while ROM is erased */
4342478be82SVinod Koul 	msleep(20);
4352478be82SVinod Koul 
4362478be82SVinod Koul 	for (i = 0; i < RENESAS_RETRY; i++) {
4372478be82SVinod Koul 		retval = pci_read_config_byte(pdev, RENESAS_ROM_STATUS,
4382478be82SVinod Koul 					      &status);
4392478be82SVinod Koul 		status &= RENESAS_ROM_STATUS_ERASE;
4402478be82SVinod Koul 		if (!status)
4412478be82SVinod Koul 			break;
4422478be82SVinod Koul 
4432478be82SVinod Koul 		mdelay(RENESAS_DELAY);
4442478be82SVinod Koul 	}
4452478be82SVinod Koul 
4462478be82SVinod Koul 	if (i == RENESAS_RETRY)
4472478be82SVinod Koul 		dev_dbg(&pdev->dev, "Chip erase timedout: %x\n", status);
4482478be82SVinod Koul 
4492478be82SVinod Koul 	dev_dbg(&pdev->dev, "ROM Erase... Done success\n");
4502478be82SVinod Koul }
4512478be82SVinod Koul 
4522478be82SVinod Koul static bool renesas_setup_rom(struct pci_dev *pdev, const struct firmware *fw)
4532478be82SVinod Koul {
4542478be82SVinod Koul 	const u32 *fw_data = (const u32 *)fw->data;
4552478be82SVinod Koul 	int err, i;
4562478be82SVinod Koul 	u8 status;
4572478be82SVinod Koul 
4582478be82SVinod Koul 	/* 2. Write magic word to Data0 */
4592478be82SVinod Koul 	err = pci_write_config_dword(pdev, RENESAS_DATA0,
4602478be82SVinod Koul 				     RENESAS_ROM_WRITE_MAGIC);
4612478be82SVinod Koul 	if (err)
4622478be82SVinod Koul 		return false;
4632478be82SVinod Koul 
4642478be82SVinod Koul 	/* 3. Set External ROM access */
4652478be82SVinod Koul 	err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS,
4662478be82SVinod Koul 				    RENESAS_ROM_STATUS_ACCESS);
4672478be82SVinod Koul 	if (err)
4682478be82SVinod Koul 		goto remove_bypass;
4692478be82SVinod Koul 
4702478be82SVinod Koul 	/* 4. Check the result */
4712478be82SVinod Koul 	err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status);
4722478be82SVinod Koul 	if (err)
4732478be82SVinod Koul 		goto remove_bypass;
4742478be82SVinod Koul 	status &= GENMASK(6, 4);
4752478be82SVinod Koul 	if (status) {
4762478be82SVinod Koul 		dev_err(&pdev->dev,
4772478be82SVinod Koul 			"setting external rom failed: %x\n", status);
4782478be82SVinod Koul 		goto remove_bypass;
4792478be82SVinod Koul 	}
4802478be82SVinod Koul 
4812478be82SVinod Koul 	/* 5 to 16 Write FW to DATA0/1 while checking SetData0/1 */
4822478be82SVinod Koul 	for (i = 0; i < fw->size / 4; i++) {
4832478be82SVinod Koul 		err = renesas_fw_download_image(pdev, fw_data, i, true);
4842478be82SVinod Koul 		if (err) {
4852478be82SVinod Koul 			dev_err(&pdev->dev,
4862478be82SVinod Koul 				"ROM Download Step %d failed at position %d bytes with (%d)\n",
4872478be82SVinod Koul 				 i, i * 4, err);
4882478be82SVinod Koul 			goto remove_bypass;
4892478be82SVinod Koul 		}
4902478be82SVinod Koul 	}
4912478be82SVinod Koul 
4922478be82SVinod Koul 	/*
4932478be82SVinod Koul 	 * wait till DATA0/1 is cleared
4942478be82SVinod Koul 	 */
4952478be82SVinod Koul 	for (i = 0; i < RENESAS_RETRY; i++) {
4962478be82SVinod Koul 		err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS_MSB,
4972478be82SVinod Koul 					   &status);
4982478be82SVinod Koul 		if (err)
4992478be82SVinod Koul 			goto remove_bypass;
5002478be82SVinod Koul 		if (!(status & (BIT(0) | BIT(1))))
5012478be82SVinod Koul 			break;
5022478be82SVinod Koul 
5032478be82SVinod Koul 		udelay(RENESAS_DELAY);
5042478be82SVinod Koul 	}
5052478be82SVinod Koul 	if (i == RENESAS_RETRY) {
5062478be82SVinod Koul 		dev_err(&pdev->dev, "Final Firmware ROM Download step timed out\n");
5072478be82SVinod Koul 		goto remove_bypass;
5082478be82SVinod Koul 	}
5092478be82SVinod Koul 
5102478be82SVinod Koul 	/* 17. Remove bypass */
5112478be82SVinod Koul 	err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS, 0);
5122478be82SVinod Koul 	if (err)
5132478be82SVinod Koul 		return false;
5142478be82SVinod Koul 
5152478be82SVinod Koul 	udelay(10);
5162478be82SVinod Koul 
5172478be82SVinod Koul 	/* 18. check result */
5182478be82SVinod Koul 	for (i = 0; i < RENESAS_RETRY; i++) {
5192478be82SVinod Koul 		err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status);
5202478be82SVinod Koul 		if (err) {
5212478be82SVinod Koul 			dev_err(&pdev->dev, "Read ROM status failed:%d\n",
5222478be82SVinod Koul 				pcibios_err_to_errno(err));
5232478be82SVinod Koul 			return false;
5242478be82SVinod Koul 		}
5252478be82SVinod Koul 		status &= RENESAS_ROM_STATUS_RESULT;
5262478be82SVinod Koul 		if (status ==  RENESAS_ROM_STATUS_SUCCESS) {
5272478be82SVinod Koul 			dev_dbg(&pdev->dev, "Download ROM success\n");
5282478be82SVinod Koul 			break;
5292478be82SVinod Koul 		}
5302478be82SVinod Koul 		udelay(RENESAS_DELAY);
5312478be82SVinod Koul 	}
5322478be82SVinod Koul 	if (i == RENESAS_RETRY) { /* Timed out */
5332478be82SVinod Koul 		dev_err(&pdev->dev,
5342478be82SVinod Koul 			"Download to external ROM TO: %x\n", status);
5352478be82SVinod Koul 		return false;
5362478be82SVinod Koul 	}
5372478be82SVinod Koul 
5382478be82SVinod Koul 	dev_dbg(&pdev->dev, "Download to external ROM succeeded\n");
5392478be82SVinod Koul 
5402478be82SVinod Koul 	/* Last step set Reload */
5412478be82SVinod Koul 	err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS,
5422478be82SVinod Koul 				    RENESAS_ROM_STATUS_RELOAD);
5432478be82SVinod Koul 	if (err) {
5442478be82SVinod Koul 		dev_err(&pdev->dev, "Set ROM execute failed: %d\n",
5452478be82SVinod Koul 			pcibios_err_to_errno(err));
5462478be82SVinod Koul 		return false;
5472478be82SVinod Koul 	}
5482478be82SVinod Koul 
5492478be82SVinod Koul 	/*
5502478be82SVinod Koul 	 * wait till Reload is cleared
5512478be82SVinod Koul 	 */
5522478be82SVinod Koul 	for (i = 0; i < RENESAS_RETRY; i++) {
5532478be82SVinod Koul 		err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status);
5542478be82SVinod Koul 		if (err)
5552478be82SVinod Koul 			return false;
5562478be82SVinod Koul 		if (!(status & RENESAS_ROM_STATUS_RELOAD))
5572478be82SVinod Koul 			break;
5582478be82SVinod Koul 
5592478be82SVinod Koul 		udelay(RENESAS_DELAY);
5602478be82SVinod Koul 	}
5612478be82SVinod Koul 	if (i == RENESAS_RETRY) {
5622478be82SVinod Koul 		dev_err(&pdev->dev, "ROM Exec timed out: %x\n", status);
5632478be82SVinod Koul 		return false;
5642478be82SVinod Koul 	}
5652478be82SVinod Koul 
5662478be82SVinod Koul 	return true;
5672478be82SVinod Koul 
5682478be82SVinod Koul remove_bypass:
5692478be82SVinod Koul 	pci_write_config_byte(pdev, RENESAS_ROM_STATUS, 0);
5702478be82SVinod Koul 	return false;
5712478be82SVinod Koul }
5722478be82SVinod Koul 
5738bd5741eSChristian Lamparter static int renesas_load_fw(struct pci_dev *pdev, const struct firmware *fw)
5748bd5741eSChristian Lamparter {
5758bd5741eSChristian Lamparter 	int err = 0;
5762478be82SVinod Koul 	bool rom;
5772478be82SVinod Koul 
5782478be82SVinod Koul 	/* Check if the device has external ROM */
5792478be82SVinod Koul 	rom = renesas_check_rom(pdev);
5802478be82SVinod Koul 	if (rom) {
5812478be82SVinod Koul 		/* perform chip erase first */
5822478be82SVinod Koul 		renesas_rom_erase(pdev);
5832478be82SVinod Koul 
5842478be82SVinod Koul 		/* lets try loading fw on ROM first */
5852478be82SVinod Koul 		rom = renesas_setup_rom(pdev, fw);
5862478be82SVinod Koul 		if (!rom) {
5872478be82SVinod Koul 			dev_dbg(&pdev->dev,
5882478be82SVinod Koul 				"ROM load failed, falling back on FW load\n");
5892478be82SVinod Koul 		} else {
5902478be82SVinod Koul 			dev_dbg(&pdev->dev,
5912478be82SVinod Koul 				"ROM load success\n");
5922478be82SVinod Koul 			goto exit;
5932478be82SVinod Koul 		}
5942478be82SVinod Koul 	}
5958bd5741eSChristian Lamparter 
5968bd5741eSChristian Lamparter 	err = renesas_fw_download(pdev, fw);
5972478be82SVinod Koul 
5982478be82SVinod Koul exit:
5998bd5741eSChristian Lamparter 	if (err)
6008bd5741eSChristian Lamparter 		dev_err(&pdev->dev, "firmware failed to download (%d).", err);
6018bd5741eSChristian Lamparter 	return err;
6028bd5741eSChristian Lamparter }
6038bd5741eSChristian Lamparter 
6048bd5741eSChristian Lamparter int renesas_xhci_check_request_fw(struct pci_dev *pdev,
6058bd5741eSChristian Lamparter 				  const struct pci_device_id *id)
6068bd5741eSChristian Lamparter {
6078bd5741eSChristian Lamparter 	struct xhci_driver_data *driver_data =
6088bd5741eSChristian Lamparter 			(struct xhci_driver_data *)id->driver_data;
6098bd5741eSChristian Lamparter 	const char *fw_name = driver_data->firmware;
6108bd5741eSChristian Lamparter 	const struct firmware *fw;
6118bd5741eSChristian Lamparter 	int err;
6128bd5741eSChristian Lamparter 
6138bd5741eSChristian Lamparter 	err = renesas_fw_check_running(pdev);
6148bd5741eSChristian Lamparter 	/* Continue ahead, if the firmware is already running. */
6158bd5741eSChristian Lamparter 	if (err == 0)
6168bd5741eSChristian Lamparter 		return 0;
6178bd5741eSChristian Lamparter 
6188bd5741eSChristian Lamparter 	if (err != 1)
6198bd5741eSChristian Lamparter 		return err;
6208bd5741eSChristian Lamparter 
6218bd5741eSChristian Lamparter 	pci_dev_get(pdev);
6228bd5741eSChristian Lamparter 	err = request_firmware(&fw, fw_name, &pdev->dev);
6238bd5741eSChristian Lamparter 	pci_dev_put(pdev);
6248bd5741eSChristian Lamparter 	if (err) {
6258bd5741eSChristian Lamparter 		dev_err(&pdev->dev, "request_firmware failed: %d\n", err);
6268bd5741eSChristian Lamparter 		return err;
6278bd5741eSChristian Lamparter 	}
6288bd5741eSChristian Lamparter 
6298bd5741eSChristian Lamparter 	err = renesas_fw_verify(fw->data, fw->size);
6308bd5741eSChristian Lamparter 	if (err)
6318bd5741eSChristian Lamparter 		goto exit;
6328bd5741eSChristian Lamparter 
6338bd5741eSChristian Lamparter 	err = renesas_load_fw(pdev, fw);
6348bd5741eSChristian Lamparter exit:
6358bd5741eSChristian Lamparter 	release_firmware(fw);
6368bd5741eSChristian Lamparter 	return err;
6378bd5741eSChristian Lamparter }
6388bd5741eSChristian Lamparter EXPORT_SYMBOL_GPL(renesas_xhci_check_request_fw);
6398bd5741eSChristian Lamparter 
6408bd5741eSChristian Lamparter void renesas_xhci_pci_exit(struct pci_dev *dev)
6418bd5741eSChristian Lamparter {
6428bd5741eSChristian Lamparter }
6438bd5741eSChristian Lamparter EXPORT_SYMBOL_GPL(renesas_xhci_pci_exit);
6448bd5741eSChristian Lamparter 
6458bd5741eSChristian Lamparter MODULE_LICENSE("GPL v2");
646