162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (C) 2019-2020 Linaro Limited */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/acpi.h> 562306a36Sopenharmony_ci#include <linux/firmware.h> 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/pci.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <asm/unaligned.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "xhci.h" 1262306a36Sopenharmony_ci#include "xhci-trace.h" 1362306a36Sopenharmony_ci#include "xhci-pci.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define RENESAS_FW_VERSION 0x6C 1662306a36Sopenharmony_ci#define RENESAS_ROM_CONFIG 0xF0 1762306a36Sopenharmony_ci#define RENESAS_FW_STATUS 0xF4 1862306a36Sopenharmony_ci#define RENESAS_FW_STATUS_MSB 0xF5 1962306a36Sopenharmony_ci#define RENESAS_ROM_STATUS 0xF6 2062306a36Sopenharmony_ci#define RENESAS_ROM_STATUS_MSB 0xF7 2162306a36Sopenharmony_ci#define RENESAS_DATA0 0xF8 2262306a36Sopenharmony_ci#define RENESAS_DATA1 0xFC 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define RENESAS_FW_VERSION_FIELD GENMASK(23, 7) 2562306a36Sopenharmony_ci#define RENESAS_FW_VERSION_OFFSET 8 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define RENESAS_FW_STATUS_DOWNLOAD_ENABLE BIT(0) 2862306a36Sopenharmony_ci#define RENESAS_FW_STATUS_LOCK BIT(1) 2962306a36Sopenharmony_ci#define RENESAS_FW_STATUS_RESULT GENMASK(6, 4) 3062306a36Sopenharmony_ci #define RENESAS_FW_STATUS_INVALID 0 3162306a36Sopenharmony_ci #define RENESAS_FW_STATUS_SUCCESS BIT(4) 3262306a36Sopenharmony_ci #define RENESAS_FW_STATUS_ERROR BIT(5) 3362306a36Sopenharmony_ci#define RENESAS_FW_STATUS_SET_DATA0 BIT(8) 3462306a36Sopenharmony_ci#define RENESAS_FW_STATUS_SET_DATA1 BIT(9) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define RENESAS_ROM_STATUS_ACCESS BIT(0) 3762306a36Sopenharmony_ci#define RENESAS_ROM_STATUS_ERASE BIT(1) 3862306a36Sopenharmony_ci#define RENESAS_ROM_STATUS_RELOAD BIT(2) 3962306a36Sopenharmony_ci#define RENESAS_ROM_STATUS_RESULT GENMASK(6, 4) 4062306a36Sopenharmony_ci #define RENESAS_ROM_STATUS_NO_RESULT 0 4162306a36Sopenharmony_ci #define RENESAS_ROM_STATUS_SUCCESS BIT(4) 4262306a36Sopenharmony_ci #define RENESAS_ROM_STATUS_ERROR BIT(5) 4362306a36Sopenharmony_ci#define RENESAS_ROM_STATUS_SET_DATA0 BIT(8) 4462306a36Sopenharmony_ci#define RENESAS_ROM_STATUS_SET_DATA1 BIT(9) 4562306a36Sopenharmony_ci#define RENESAS_ROM_STATUS_ROM_EXISTS BIT(15) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define RENESAS_ROM_ERASE_MAGIC 0x5A65726F 4862306a36Sopenharmony_ci#define RENESAS_ROM_WRITE_MAGIC 0x53524F4D 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define RENESAS_RETRY 10000 5162306a36Sopenharmony_ci#define RENESAS_DELAY 10 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic int renesas_fw_download_image(struct pci_dev *dev, 5462306a36Sopenharmony_ci const u32 *fw, size_t step, bool rom) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci size_t i; 5762306a36Sopenharmony_ci int err; 5862306a36Sopenharmony_ci u8 fw_status; 5962306a36Sopenharmony_ci bool data0_or_data1; 6062306a36Sopenharmony_ci u32 status_reg; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (rom) 6362306a36Sopenharmony_ci status_reg = RENESAS_ROM_STATUS_MSB; 6462306a36Sopenharmony_ci else 6562306a36Sopenharmony_ci status_reg = RENESAS_FW_STATUS_MSB; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* 6862306a36Sopenharmony_ci * The hardware does alternate between two 32-bit pages. 6962306a36Sopenharmony_ci * (This is because each row of the firmware is 8 bytes). 7062306a36Sopenharmony_ci * 7162306a36Sopenharmony_ci * for even steps we use DATA0, for odd steps DATA1. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci data0_or_data1 = (step & 1) == 1; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* step+1. Read "Set DATAX" and confirm it is cleared. */ 7662306a36Sopenharmony_ci for (i = 0; i < RENESAS_RETRY; i++) { 7762306a36Sopenharmony_ci err = pci_read_config_byte(dev, status_reg, &fw_status); 7862306a36Sopenharmony_ci if (err) { 7962306a36Sopenharmony_ci dev_err(&dev->dev, "Read Status failed: %d\n", 8062306a36Sopenharmony_ci pcibios_err_to_errno(err)); 8162306a36Sopenharmony_ci return pcibios_err_to_errno(err); 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci if (!(fw_status & BIT(data0_or_data1))) 8462306a36Sopenharmony_ci break; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci udelay(RENESAS_DELAY); 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci if (i == RENESAS_RETRY) { 8962306a36Sopenharmony_ci dev_err(&dev->dev, "Timeout for Set DATAX step: %zd\n", step); 9062306a36Sopenharmony_ci return -ETIMEDOUT; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* 9462306a36Sopenharmony_ci * step+2. Write FW data to "DATAX". 9562306a36Sopenharmony_ci * "LSB is left" => force little endian 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ci err = pci_write_config_dword(dev, data0_or_data1 ? 9862306a36Sopenharmony_ci RENESAS_DATA1 : RENESAS_DATA0, 9962306a36Sopenharmony_ci (__force u32)cpu_to_le32(fw[step])); 10062306a36Sopenharmony_ci if (err) { 10162306a36Sopenharmony_ci dev_err(&dev->dev, "Write to DATAX failed: %d\n", 10262306a36Sopenharmony_ci pcibios_err_to_errno(err)); 10362306a36Sopenharmony_ci return pcibios_err_to_errno(err); 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci udelay(100); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* step+3. Set "Set DATAX". */ 10962306a36Sopenharmony_ci err = pci_write_config_byte(dev, status_reg, BIT(data0_or_data1)); 11062306a36Sopenharmony_ci if (err) { 11162306a36Sopenharmony_ci dev_err(&dev->dev, "Write config for DATAX failed: %d\n", 11262306a36Sopenharmony_ci pcibios_err_to_errno(err)); 11362306a36Sopenharmony_ci return pcibios_err_to_errno(err); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int renesas_fw_verify(const void *fw_data, 12062306a36Sopenharmony_ci size_t length) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci u16 fw_version_pointer; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* 12562306a36Sopenharmony_ci * The Firmware's Data Format is describe in 12662306a36Sopenharmony_ci * "6.3 Data Format" R19UH0078EJ0500 Rev.5.00 page 124 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* 13062306a36Sopenharmony_ci * The bootrom chips of the big brother have sizes up to 64k, let's 13162306a36Sopenharmony_ci * assume that's the biggest the firmware can get. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci if (length < 0x1000 || length >= 0x10000) { 13462306a36Sopenharmony_ci pr_err("firmware is size %zd is not (4k - 64k).", 13562306a36Sopenharmony_ci length); 13662306a36Sopenharmony_ci return -EINVAL; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* The First 2 bytes are fixed value (55aa). "LSB on Left" */ 14062306a36Sopenharmony_ci if (get_unaligned_le16(fw_data) != 0x55aa) { 14162306a36Sopenharmony_ci pr_err("no valid firmware header found."); 14262306a36Sopenharmony_ci return -EINVAL; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* verify the firmware version position and print it. */ 14662306a36Sopenharmony_ci fw_version_pointer = get_unaligned_le16(fw_data + 4); 14762306a36Sopenharmony_ci if (fw_version_pointer + 2 >= length) { 14862306a36Sopenharmony_ci pr_err("fw ver pointer is outside of the firmware image"); 14962306a36Sopenharmony_ci return -EINVAL; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic bool renesas_check_rom(struct pci_dev *pdev) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci u16 rom_status; 15862306a36Sopenharmony_ci int retval; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Check if external ROM exists */ 16162306a36Sopenharmony_ci retval = pci_read_config_word(pdev, RENESAS_ROM_STATUS, &rom_status); 16262306a36Sopenharmony_ci if (retval) 16362306a36Sopenharmony_ci return false; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci rom_status &= RENESAS_ROM_STATUS_ROM_EXISTS; 16662306a36Sopenharmony_ci if (rom_status) { 16762306a36Sopenharmony_ci dev_dbg(&pdev->dev, "External ROM exists\n"); 16862306a36Sopenharmony_ci return true; /* External ROM exists */ 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return false; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int renesas_check_rom_state(struct pci_dev *pdev) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci u16 rom_state; 17762306a36Sopenharmony_ci u32 version; 17862306a36Sopenharmony_ci int err; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* check FW version */ 18162306a36Sopenharmony_ci err = pci_read_config_dword(pdev, RENESAS_FW_VERSION, &version); 18262306a36Sopenharmony_ci if (err) 18362306a36Sopenharmony_ci return pcibios_err_to_errno(err); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci version &= RENESAS_FW_VERSION_FIELD; 18662306a36Sopenharmony_ci version = version >> RENESAS_FW_VERSION_OFFSET; 18762306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Found ROM version: %x\n", version); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* 19062306a36Sopenharmony_ci * Test if ROM is present and loaded, if so we can skip everything 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_ci err = pci_read_config_word(pdev, RENESAS_ROM_STATUS, &rom_state); 19362306a36Sopenharmony_ci if (err) 19462306a36Sopenharmony_ci return pcibios_err_to_errno(err); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (rom_state & RENESAS_ROM_STATUS_ROM_EXISTS) { 19762306a36Sopenharmony_ci /* ROM exists */ 19862306a36Sopenharmony_ci dev_dbg(&pdev->dev, "ROM exists\n"); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* Check the "Result Code" Bits (6:4) and act accordingly */ 20162306a36Sopenharmony_ci switch (rom_state & RENESAS_ROM_STATUS_RESULT) { 20262306a36Sopenharmony_ci case RENESAS_ROM_STATUS_SUCCESS: 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci case RENESAS_ROM_STATUS_NO_RESULT: /* No result yet */ 20662306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Unknown ROM status ...\n"); 20762306a36Sopenharmony_ci return -ENOENT; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci case RENESAS_ROM_STATUS_ERROR: /* Error State */ 21062306a36Sopenharmony_ci default: /* All other states are marked as "Reserved states" */ 21162306a36Sopenharmony_ci dev_err(&pdev->dev, "Invalid ROM.."); 21262306a36Sopenharmony_ci break; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return -EIO; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic int renesas_fw_check_running(struct pci_dev *pdev) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci u8 fw_state; 22262306a36Sopenharmony_ci int err; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * Test if the device is actually needing the firmware. As most 22662306a36Sopenharmony_ci * BIOSes will initialize the device for us. If the device is 22762306a36Sopenharmony_ci * initialized. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci err = pci_read_config_byte(pdev, RENESAS_FW_STATUS, &fw_state); 23062306a36Sopenharmony_ci if (err) 23162306a36Sopenharmony_ci return pcibios_err_to_errno(err); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* 23462306a36Sopenharmony_ci * Check if "FW Download Lock" is locked. If it is and the FW is 23562306a36Sopenharmony_ci * ready we can simply continue. If the FW is not ready, we have 23662306a36Sopenharmony_ci * to give up. 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ci if (fw_state & RENESAS_FW_STATUS_LOCK) { 23962306a36Sopenharmony_ci dev_dbg(&pdev->dev, "FW Download Lock is engaged."); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (fw_state & RENESAS_FW_STATUS_SUCCESS) 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci dev_err(&pdev->dev, 24562306a36Sopenharmony_ci "FW Download Lock is set and FW is not ready. Giving Up."); 24662306a36Sopenharmony_ci return -EIO; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* 25062306a36Sopenharmony_ci * Check if "FW Download Enable" is set. If someone (us?) tampered 25162306a36Sopenharmony_ci * with it and it can't be reset, we have to give up too... and 25262306a36Sopenharmony_ci * ask for a forgiveness and a reboot. 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_ci if (fw_state & RENESAS_FW_STATUS_DOWNLOAD_ENABLE) { 25562306a36Sopenharmony_ci dev_err(&pdev->dev, 25662306a36Sopenharmony_ci "FW Download Enable is stale. Giving Up (poweroff/reboot needed)."); 25762306a36Sopenharmony_ci return -EIO; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Otherwise, Check the "Result Code" Bits (6:4) and act accordingly */ 26162306a36Sopenharmony_ci switch (fw_state & RENESAS_FW_STATUS_RESULT) { 26262306a36Sopenharmony_ci case 0: /* No result yet */ 26362306a36Sopenharmony_ci dev_dbg(&pdev->dev, "FW is not ready/loaded yet."); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* tell the caller, that this device needs the firmware. */ 26662306a36Sopenharmony_ci return 1; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci case RENESAS_FW_STATUS_SUCCESS: /* Success, device should be working. */ 26962306a36Sopenharmony_ci dev_dbg(&pdev->dev, "FW is ready."); 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci case RENESAS_FW_STATUS_ERROR: /* Error State */ 27362306a36Sopenharmony_ci dev_err(&pdev->dev, 27462306a36Sopenharmony_ci "hardware is in an error state. Giving up (poweroff/reboot needed)."); 27562306a36Sopenharmony_ci return -ENODEV; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci default: /* All other states are marked as "Reserved states" */ 27862306a36Sopenharmony_ci dev_err(&pdev->dev, 27962306a36Sopenharmony_ci "hardware is in an invalid state %lx. Giving up (poweroff/reboot needed).", 28062306a36Sopenharmony_ci (fw_state & RENESAS_FW_STATUS_RESULT) >> 4); 28162306a36Sopenharmony_ci return -EINVAL; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int renesas_fw_download(struct pci_dev *pdev, 28662306a36Sopenharmony_ci const struct firmware *fw) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci const u32 *fw_data = (const u32 *)fw->data; 28962306a36Sopenharmony_ci size_t i; 29062306a36Sopenharmony_ci int err; 29162306a36Sopenharmony_ci u8 fw_status; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* 29462306a36Sopenharmony_ci * For more information and the big picture: please look at the 29562306a36Sopenharmony_ci * "Firmware Download Sequence" in "7.1 FW Download Interface" 29662306a36Sopenharmony_ci * of R19UH0078EJ0500 Rev.5.00 page 131 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* 30062306a36Sopenharmony_ci * 0. Set "FW Download Enable" bit in the 30162306a36Sopenharmony_ci * "FW Download Control & Status Register" at 0xF4 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_ci err = pci_write_config_byte(pdev, RENESAS_FW_STATUS, 30462306a36Sopenharmony_ci RENESAS_FW_STATUS_DOWNLOAD_ENABLE); 30562306a36Sopenharmony_ci if (err) 30662306a36Sopenharmony_ci return pcibios_err_to_errno(err); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* 1 - 10 follow one step after the other. */ 30962306a36Sopenharmony_ci for (i = 0; i < fw->size / 4; i++) { 31062306a36Sopenharmony_ci err = renesas_fw_download_image(pdev, fw_data, i, false); 31162306a36Sopenharmony_ci if (err) { 31262306a36Sopenharmony_ci dev_err(&pdev->dev, 31362306a36Sopenharmony_ci "Firmware Download Step %zd failed at position %zd bytes with (%d).", 31462306a36Sopenharmony_ci i, i * 4, err); 31562306a36Sopenharmony_ci return err; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* 32062306a36Sopenharmony_ci * This sequence continues until the last data is written to 32162306a36Sopenharmony_ci * "DATA0" or "DATA1". Naturally, we wait until "SET DATA0/1" 32262306a36Sopenharmony_ci * is cleared by the hardware beforehand. 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_ci for (i = 0; i < RENESAS_RETRY; i++) { 32562306a36Sopenharmony_ci err = pci_read_config_byte(pdev, RENESAS_FW_STATUS_MSB, 32662306a36Sopenharmony_ci &fw_status); 32762306a36Sopenharmony_ci if (err) 32862306a36Sopenharmony_ci return pcibios_err_to_errno(err); 32962306a36Sopenharmony_ci if (!(fw_status & (BIT(0) | BIT(1)))) 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci udelay(RENESAS_DELAY); 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci if (i == RENESAS_RETRY) 33562306a36Sopenharmony_ci dev_warn(&pdev->dev, "Final Firmware Download step timed out."); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* 33862306a36Sopenharmony_ci * 11. After finishing writing the last data of FW, the 33962306a36Sopenharmony_ci * System Software must clear "FW Download Enable" 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_ci err = pci_write_config_byte(pdev, RENESAS_FW_STATUS, 0); 34262306a36Sopenharmony_ci if (err) 34362306a36Sopenharmony_ci return pcibios_err_to_errno(err); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* 12. Read "Result Code" and confirm it is good. */ 34662306a36Sopenharmony_ci for (i = 0; i < RENESAS_RETRY; i++) { 34762306a36Sopenharmony_ci err = pci_read_config_byte(pdev, RENESAS_FW_STATUS, &fw_status); 34862306a36Sopenharmony_ci if (err) 34962306a36Sopenharmony_ci return pcibios_err_to_errno(err); 35062306a36Sopenharmony_ci if (fw_status & RENESAS_FW_STATUS_SUCCESS) 35162306a36Sopenharmony_ci break; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci udelay(RENESAS_DELAY); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci if (i == RENESAS_RETRY) { 35662306a36Sopenharmony_ci /* Timed out / Error - let's see if we can fix this */ 35762306a36Sopenharmony_ci err = renesas_fw_check_running(pdev); 35862306a36Sopenharmony_ci switch (err) { 35962306a36Sopenharmony_ci case 0: /* 36062306a36Sopenharmony_ci * we shouldn't end up here. 36162306a36Sopenharmony_ci * maybe it took a little bit longer. 36262306a36Sopenharmony_ci * But all should be well? 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci case 1: /* (No result yet! */ 36762306a36Sopenharmony_ci dev_err(&pdev->dev, "FW Load timedout"); 36862306a36Sopenharmony_ci return -ETIMEDOUT; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci default: 37162306a36Sopenharmony_ci return err; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci return 0; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic void renesas_rom_erase(struct pci_dev *pdev) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci int retval, i; 38162306a36Sopenharmony_ci u8 status; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Performing ROM Erase...\n"); 38462306a36Sopenharmony_ci retval = pci_write_config_dword(pdev, RENESAS_DATA0, 38562306a36Sopenharmony_ci RENESAS_ROM_ERASE_MAGIC); 38662306a36Sopenharmony_ci if (retval) { 38762306a36Sopenharmony_ci dev_err(&pdev->dev, "ROM erase, magic word write failed: %d\n", 38862306a36Sopenharmony_ci pcibios_err_to_errno(retval)); 38962306a36Sopenharmony_ci return; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci retval = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status); 39362306a36Sopenharmony_ci if (retval) { 39462306a36Sopenharmony_ci dev_err(&pdev->dev, "ROM status read failed: %d\n", 39562306a36Sopenharmony_ci pcibios_err_to_errno(retval)); 39662306a36Sopenharmony_ci return; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci status |= RENESAS_ROM_STATUS_ERASE; 39962306a36Sopenharmony_ci retval = pci_write_config_byte(pdev, RENESAS_ROM_STATUS, status); 40062306a36Sopenharmony_ci if (retval) { 40162306a36Sopenharmony_ci dev_err(&pdev->dev, "ROM erase set word write failed\n"); 40262306a36Sopenharmony_ci return; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* sleep a bit while ROM is erased */ 40662306a36Sopenharmony_ci msleep(20); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci for (i = 0; i < RENESAS_RETRY; i++) { 40962306a36Sopenharmony_ci retval = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, 41062306a36Sopenharmony_ci &status); 41162306a36Sopenharmony_ci status &= RENESAS_ROM_STATUS_ERASE; 41262306a36Sopenharmony_ci if (!status) 41362306a36Sopenharmony_ci break; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci mdelay(RENESAS_DELAY); 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (i == RENESAS_RETRY) 41962306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Chip erase timedout: %x\n", status); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "ROM Erase... Done success\n"); 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic bool renesas_setup_rom(struct pci_dev *pdev, const struct firmware *fw) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci const u32 *fw_data = (const u32 *)fw->data; 42762306a36Sopenharmony_ci int err, i; 42862306a36Sopenharmony_ci u8 status; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* 2. Write magic word to Data0 */ 43162306a36Sopenharmony_ci err = pci_write_config_dword(pdev, RENESAS_DATA0, 43262306a36Sopenharmony_ci RENESAS_ROM_WRITE_MAGIC); 43362306a36Sopenharmony_ci if (err) 43462306a36Sopenharmony_ci return false; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* 3. Set External ROM access */ 43762306a36Sopenharmony_ci err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS, 43862306a36Sopenharmony_ci RENESAS_ROM_STATUS_ACCESS); 43962306a36Sopenharmony_ci if (err) 44062306a36Sopenharmony_ci goto remove_bypass; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* 4. Check the result */ 44362306a36Sopenharmony_ci err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status); 44462306a36Sopenharmony_ci if (err) 44562306a36Sopenharmony_ci goto remove_bypass; 44662306a36Sopenharmony_ci status &= GENMASK(6, 4); 44762306a36Sopenharmony_ci if (status) { 44862306a36Sopenharmony_ci dev_err(&pdev->dev, 44962306a36Sopenharmony_ci "setting external rom failed: %x\n", status); 45062306a36Sopenharmony_ci goto remove_bypass; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* 5 to 16 Write FW to DATA0/1 while checking SetData0/1 */ 45462306a36Sopenharmony_ci for (i = 0; i < fw->size / 4; i++) { 45562306a36Sopenharmony_ci err = renesas_fw_download_image(pdev, fw_data, i, true); 45662306a36Sopenharmony_ci if (err) { 45762306a36Sopenharmony_ci dev_err(&pdev->dev, 45862306a36Sopenharmony_ci "ROM Download Step %d failed at position %d bytes with (%d)\n", 45962306a36Sopenharmony_ci i, i * 4, err); 46062306a36Sopenharmony_ci goto remove_bypass; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* 46562306a36Sopenharmony_ci * wait till DATA0/1 is cleared 46662306a36Sopenharmony_ci */ 46762306a36Sopenharmony_ci for (i = 0; i < RENESAS_RETRY; i++) { 46862306a36Sopenharmony_ci err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS_MSB, 46962306a36Sopenharmony_ci &status); 47062306a36Sopenharmony_ci if (err) 47162306a36Sopenharmony_ci goto remove_bypass; 47262306a36Sopenharmony_ci if (!(status & (BIT(0) | BIT(1)))) 47362306a36Sopenharmony_ci break; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci udelay(RENESAS_DELAY); 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci if (i == RENESAS_RETRY) { 47862306a36Sopenharmony_ci dev_err(&pdev->dev, "Final Firmware ROM Download step timed out\n"); 47962306a36Sopenharmony_ci goto remove_bypass; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* 17. Remove bypass */ 48362306a36Sopenharmony_ci err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS, 0); 48462306a36Sopenharmony_ci if (err) 48562306a36Sopenharmony_ci return false; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci udelay(10); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* 18. check result */ 49062306a36Sopenharmony_ci for (i = 0; i < RENESAS_RETRY; i++) { 49162306a36Sopenharmony_ci err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status); 49262306a36Sopenharmony_ci if (err) { 49362306a36Sopenharmony_ci dev_err(&pdev->dev, "Read ROM status failed:%d\n", 49462306a36Sopenharmony_ci pcibios_err_to_errno(err)); 49562306a36Sopenharmony_ci return false; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci status &= RENESAS_ROM_STATUS_RESULT; 49862306a36Sopenharmony_ci if (status == RENESAS_ROM_STATUS_SUCCESS) { 49962306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Download ROM success\n"); 50062306a36Sopenharmony_ci break; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci udelay(RENESAS_DELAY); 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci if (i == RENESAS_RETRY) { /* Timed out */ 50562306a36Sopenharmony_ci dev_err(&pdev->dev, 50662306a36Sopenharmony_ci "Download to external ROM TO: %x\n", status); 50762306a36Sopenharmony_ci return false; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Download to external ROM succeeded\n"); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* Last step set Reload */ 51362306a36Sopenharmony_ci err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS, 51462306a36Sopenharmony_ci RENESAS_ROM_STATUS_RELOAD); 51562306a36Sopenharmony_ci if (err) { 51662306a36Sopenharmony_ci dev_err(&pdev->dev, "Set ROM execute failed: %d\n", 51762306a36Sopenharmony_ci pcibios_err_to_errno(err)); 51862306a36Sopenharmony_ci return false; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* 52262306a36Sopenharmony_ci * wait till Reload is cleared 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_ci for (i = 0; i < RENESAS_RETRY; i++) { 52562306a36Sopenharmony_ci err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status); 52662306a36Sopenharmony_ci if (err) 52762306a36Sopenharmony_ci return false; 52862306a36Sopenharmony_ci if (!(status & RENESAS_ROM_STATUS_RELOAD)) 52962306a36Sopenharmony_ci break; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci udelay(RENESAS_DELAY); 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci if (i == RENESAS_RETRY) { 53462306a36Sopenharmony_ci dev_err(&pdev->dev, "ROM Exec timed out: %x\n", status); 53562306a36Sopenharmony_ci return false; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci return true; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ciremove_bypass: 54162306a36Sopenharmony_ci pci_write_config_byte(pdev, RENESAS_ROM_STATUS, 0); 54262306a36Sopenharmony_ci return false; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic int renesas_load_fw(struct pci_dev *pdev, const struct firmware *fw) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci int err = 0; 54862306a36Sopenharmony_ci bool rom; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* Check if the device has external ROM */ 55162306a36Sopenharmony_ci rom = renesas_check_rom(pdev); 55262306a36Sopenharmony_ci if (rom) { 55362306a36Sopenharmony_ci /* perform chip erase first */ 55462306a36Sopenharmony_ci renesas_rom_erase(pdev); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* lets try loading fw on ROM first */ 55762306a36Sopenharmony_ci rom = renesas_setup_rom(pdev, fw); 55862306a36Sopenharmony_ci if (!rom) { 55962306a36Sopenharmony_ci dev_dbg(&pdev->dev, 56062306a36Sopenharmony_ci "ROM load failed, falling back on FW load\n"); 56162306a36Sopenharmony_ci } else { 56262306a36Sopenharmony_ci dev_dbg(&pdev->dev, 56362306a36Sopenharmony_ci "ROM load success\n"); 56462306a36Sopenharmony_ci goto exit; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci err = renesas_fw_download(pdev, fw); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ciexit: 57162306a36Sopenharmony_ci if (err) 57262306a36Sopenharmony_ci dev_err(&pdev->dev, "firmware failed to download (%d).", err); 57362306a36Sopenharmony_ci return err; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ciint renesas_xhci_check_request_fw(struct pci_dev *pdev, 57762306a36Sopenharmony_ci const struct pci_device_id *id) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct xhci_driver_data *driver_data = 58062306a36Sopenharmony_ci (struct xhci_driver_data *)id->driver_data; 58162306a36Sopenharmony_ci const char *fw_name = driver_data->firmware; 58262306a36Sopenharmony_ci const struct firmware *fw; 58362306a36Sopenharmony_ci bool has_rom; 58462306a36Sopenharmony_ci int err; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* Check if device has ROM and loaded, if so skip everything */ 58762306a36Sopenharmony_ci has_rom = renesas_check_rom(pdev); 58862306a36Sopenharmony_ci if (has_rom) { 58962306a36Sopenharmony_ci err = renesas_check_rom_state(pdev); 59062306a36Sopenharmony_ci if (!err) 59162306a36Sopenharmony_ci return 0; 59262306a36Sopenharmony_ci else if (err != -ENOENT) 59362306a36Sopenharmony_ci has_rom = false; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci err = renesas_fw_check_running(pdev); 59762306a36Sopenharmony_ci /* Continue ahead, if the firmware is already running. */ 59862306a36Sopenharmony_ci if (!err) 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* no firmware interface available */ 60262306a36Sopenharmony_ci if (err != 1) 60362306a36Sopenharmony_ci return has_rom ? 0 : err; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci pci_dev_get(pdev); 60662306a36Sopenharmony_ci err = firmware_request_nowarn(&fw, fw_name, &pdev->dev); 60762306a36Sopenharmony_ci pci_dev_put(pdev); 60862306a36Sopenharmony_ci if (err) { 60962306a36Sopenharmony_ci if (has_rom) { 61062306a36Sopenharmony_ci dev_info(&pdev->dev, "failed to load firmware %s, fallback to ROM\n", 61162306a36Sopenharmony_ci fw_name); 61262306a36Sopenharmony_ci return 0; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to load firmware %s: %d\n", 61562306a36Sopenharmony_ci fw_name, err); 61662306a36Sopenharmony_ci return err; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci err = renesas_fw_verify(fw->data, fw->size); 62062306a36Sopenharmony_ci if (err) 62162306a36Sopenharmony_ci goto exit; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci err = renesas_load_fw(pdev, fw); 62462306a36Sopenharmony_ciexit: 62562306a36Sopenharmony_ci release_firmware(fw); 62662306a36Sopenharmony_ci return err; 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(renesas_xhci_check_request_fw); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 631