1e93984ebSRobert Marko // SPDX-License-Identifier: GPL-2.0 2e93984ebSRobert Marko 3e93984ebSRobert Marko #include <linux/bitfield.h> 4e93984ebSRobert Marko #include <linux/of.h> 5e93984ebSRobert Marko #include <linux/firmware.h> 66c8e2407SStephen Rothwell #include <linux/crc-itu-t.h> 7e93984ebSRobert Marko #include <linux/nvmem-consumer.h> 8e93984ebSRobert Marko 9e93984ebSRobert Marko #include <asm/unaligned.h> 10e93984ebSRobert Marko 11e93984ebSRobert Marko #include "aquantia.h" 12e93984ebSRobert Marko 13e93984ebSRobert Marko #define UP_RESET_SLEEP 100 14e93984ebSRobert Marko 15e93984ebSRobert Marko /* addresses of memory segments in the phy */ 16e93984ebSRobert Marko #define DRAM_BASE_ADDR 0x3FFE0000 17e93984ebSRobert Marko #define IRAM_BASE_ADDR 0x40000000 18e93984ebSRobert Marko 19e93984ebSRobert Marko /* firmware image format constants */ 20e93984ebSRobert Marko #define VERSION_STRING_SIZE 0x40 21e93984ebSRobert Marko #define VERSION_STRING_OFFSET 0x0200 22e93984ebSRobert Marko /* primary offset is written at an offset from the start of the fw blob */ 23e93984ebSRobert Marko #define PRIMARY_OFFSET_OFFSET 0x8 24e93984ebSRobert Marko /* primary offset needs to be then added to a base offset */ 25e93984ebSRobert Marko #define PRIMARY_OFFSET_SHIFT 12 26e93984ebSRobert Marko #define PRIMARY_OFFSET(x) ((x) << PRIMARY_OFFSET_SHIFT) 27e93984ebSRobert Marko #define HEADER_OFFSET 0x300 28e93984ebSRobert Marko 29e93984ebSRobert Marko struct aqr_fw_header { 30e93984ebSRobert Marko u32 padding; 31e93984ebSRobert Marko u8 iram_offset[3]; 32e93984ebSRobert Marko u8 iram_size[3]; 33e93984ebSRobert Marko u8 dram_offset[3]; 34e93984ebSRobert Marko u8 dram_size[3]; 35e93984ebSRobert Marko } __packed; 36e93984ebSRobert Marko 37e93984ebSRobert Marko enum aqr_fw_src { 38e93984ebSRobert Marko AQR_FW_SRC_NVMEM = 0, 39e93984ebSRobert Marko AQR_FW_SRC_FS, 40e93984ebSRobert Marko }; 41e93984ebSRobert Marko 42e93984ebSRobert Marko static const char * const aqr_fw_src_string[] = { 43e93984ebSRobert Marko [AQR_FW_SRC_NVMEM] = "NVMEM", 44e93984ebSRobert Marko [AQR_FW_SRC_FS] = "FS", 45e93984ebSRobert Marko }; 46e93984ebSRobert Marko 47e93984ebSRobert Marko /* AQR firmware doesn't have fixed offsets for iram and dram section 48e93984ebSRobert Marko * but instead provide an header with the offset to use on reading 49e93984ebSRobert Marko * and parsing the firmware. 50e93984ebSRobert Marko * 51e93984ebSRobert Marko * AQR firmware can't be trusted and each offset is validated to be 52e93984ebSRobert Marko * not negative and be in the size of the firmware itself. 53e93984ebSRobert Marko */ 54e93984ebSRobert Marko static bool aqr_fw_validate_get(size_t size, size_t offset, size_t get_size) 55e93984ebSRobert Marko { 56e93984ebSRobert Marko return offset + get_size <= size; 57e93984ebSRobert Marko } 58e93984ebSRobert Marko 59e93984ebSRobert Marko static int aqr_fw_get_be16(const u8 *data, size_t offset, size_t size, u16 *value) 60e93984ebSRobert Marko { 61e93984ebSRobert Marko if (!aqr_fw_validate_get(size, offset, sizeof(u16))) 62e93984ebSRobert Marko return -EINVAL; 63e93984ebSRobert Marko 64e93984ebSRobert Marko *value = get_unaligned_be16(data + offset); 65e93984ebSRobert Marko 66e93984ebSRobert Marko return 0; 67e93984ebSRobert Marko } 68e93984ebSRobert Marko 69e93984ebSRobert Marko static int aqr_fw_get_le16(const u8 *data, size_t offset, size_t size, u16 *value) 70e93984ebSRobert Marko { 71e93984ebSRobert Marko if (!aqr_fw_validate_get(size, offset, sizeof(u16))) 72e93984ebSRobert Marko return -EINVAL; 73e93984ebSRobert Marko 74e93984ebSRobert Marko *value = get_unaligned_le16(data + offset); 75e93984ebSRobert Marko 76e93984ebSRobert Marko return 0; 77e93984ebSRobert Marko } 78e93984ebSRobert Marko 79e93984ebSRobert Marko static int aqr_fw_get_le24(const u8 *data, size_t offset, size_t size, u32 *value) 80e93984ebSRobert Marko { 81e93984ebSRobert Marko if (!aqr_fw_validate_get(size, offset, sizeof(u8) * 3)) 82e93984ebSRobert Marko return -EINVAL; 83e93984ebSRobert Marko 84e93984ebSRobert Marko *value = get_unaligned_le24(data + offset); 85e93984ebSRobert Marko 86e93984ebSRobert Marko return 0; 87e93984ebSRobert Marko } 88e93984ebSRobert Marko 89e93984ebSRobert Marko /* load data into the phy's memory */ 90e93984ebSRobert Marko static int aqr_fw_load_memory(struct phy_device *phydev, u32 addr, 91e93984ebSRobert Marko const u8 *data, size_t len) 92e93984ebSRobert Marko { 93e93984ebSRobert Marko u16 crc = 0, up_crc; 94e93984ebSRobert Marko size_t pos; 95e93984ebSRobert Marko 96e93984ebSRobert Marko phy_write_mmd(phydev, MDIO_MMD_VEND1, 97e93984ebSRobert Marko VEND1_GLOBAL_MAILBOX_INTERFACE1, 98e93984ebSRobert Marko VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET); 99e93984ebSRobert Marko phy_write_mmd(phydev, MDIO_MMD_VEND1, 100e93984ebSRobert Marko VEND1_GLOBAL_MAILBOX_INTERFACE3, 101e93984ebSRobert Marko VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(addr)); 102e93984ebSRobert Marko phy_write_mmd(phydev, MDIO_MMD_VEND1, 103e93984ebSRobert Marko VEND1_GLOBAL_MAILBOX_INTERFACE4, 104e93984ebSRobert Marko VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(addr)); 105e93984ebSRobert Marko 106e93984ebSRobert Marko /* We assume and enforce the size to be word aligned. 107e93984ebSRobert Marko * If a firmware that is not word aligned is found, please report upstream. 108e93984ebSRobert Marko */ 109e93984ebSRobert Marko for (pos = 0; pos < len; pos += sizeof(u32)) { 1107edce370SChristian Marangi u8 crc_data[4]; 111e93984ebSRobert Marko u32 word; 112e93984ebSRobert Marko 113e93984ebSRobert Marko /* FW data is always stored in little-endian */ 1147edce370SChristian Marangi word = get_unaligned_le32((const u32 *)(data + pos)); 115e93984ebSRobert Marko 116e93984ebSRobert Marko phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE5, 117e93984ebSRobert Marko VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(word)); 118e93984ebSRobert Marko phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE6, 119e93984ebSRobert Marko VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(word)); 120e93984ebSRobert Marko 121e93984ebSRobert Marko phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE1, 122e93984ebSRobert Marko VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE | 123e93984ebSRobert Marko VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE); 124e93984ebSRobert Marko 1257edce370SChristian Marangi /* Word is swapped internally and MAILBOX CRC is calculated 1267edce370SChristian Marangi * using big-endian order. Mimic what the PHY does to have a 1277edce370SChristian Marangi * matching CRC... 128e93984ebSRobert Marko */ 1297edce370SChristian Marangi crc_data[0] = word >> 24; 1307edce370SChristian Marangi crc_data[1] = word >> 16; 1317edce370SChristian Marangi crc_data[2] = word >> 8; 1327edce370SChristian Marangi crc_data[3] = word; 133e93984ebSRobert Marko 1347edce370SChristian Marangi /* ...calculate CRC as we load data... */ 1356c8e2407SStephen Rothwell crc = crc_itu_t(crc, crc_data, sizeof(crc_data)); 1367edce370SChristian Marangi } 1377edce370SChristian Marangi /* ...gets CRC from MAILBOX after we have loaded the entire section... */ 138e93984ebSRobert Marko up_crc = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE2); 1397edce370SChristian Marangi /* ...and make sure it does match our calculated CRC */ 140e93984ebSRobert Marko if (crc != up_crc) { 141e93984ebSRobert Marko phydev_err(phydev, "CRC mismatch: calculated 0x%04x PHY 0x%04x\n", 142e93984ebSRobert Marko crc, up_crc); 143e93984ebSRobert Marko return -EINVAL; 144e93984ebSRobert Marko } 145e93984ebSRobert Marko 146e93984ebSRobert Marko return 0; 147e93984ebSRobert Marko } 148e93984ebSRobert Marko 149e93984ebSRobert Marko static int aqr_fw_boot(struct phy_device *phydev, const u8 *data, size_t size, 150e93984ebSRobert Marko enum aqr_fw_src fw_src) 151e93984ebSRobert Marko { 152e93984ebSRobert Marko u16 calculated_crc, read_crc, read_primary_offset; 153e93984ebSRobert Marko u32 iram_offset = 0, iram_size = 0; 154e93984ebSRobert Marko u32 dram_offset = 0, dram_size = 0; 155e93984ebSRobert Marko char version[VERSION_STRING_SIZE]; 156e93984ebSRobert Marko u32 primary_offset = 0; 157e93984ebSRobert Marko int ret; 158e93984ebSRobert Marko 159e93984ebSRobert Marko /* extract saved CRC at the end of the fw 160e93984ebSRobert Marko * CRC is saved in big-endian as PHY is BE 161e93984ebSRobert Marko */ 162e93984ebSRobert Marko ret = aqr_fw_get_be16(data, size - sizeof(u16), size, &read_crc); 163e93984ebSRobert Marko if (ret) { 164e93984ebSRobert Marko phydev_err(phydev, "bad firmware CRC in firmware\n"); 165e93984ebSRobert Marko return ret; 166e93984ebSRobert Marko } 1676c8e2407SStephen Rothwell calculated_crc = crc_itu_t(0, data, size - sizeof(u16)); 168e93984ebSRobert Marko if (read_crc != calculated_crc) { 169e93984ebSRobert Marko phydev_err(phydev, "bad firmware CRC: file 0x%04x calculated 0x%04x\n", 170e93984ebSRobert Marko read_crc, calculated_crc); 171e93984ebSRobert Marko return -EINVAL; 172e93984ebSRobert Marko } 173e93984ebSRobert Marko 174e93984ebSRobert Marko /* Get the primary offset to extract DRAM and IRAM sections. */ 175e93984ebSRobert Marko ret = aqr_fw_get_le16(data, PRIMARY_OFFSET_OFFSET, size, &read_primary_offset); 176e93984ebSRobert Marko if (ret) { 177e93984ebSRobert Marko phydev_err(phydev, "bad primary offset in firmware\n"); 178e93984ebSRobert Marko return ret; 179e93984ebSRobert Marko } 180e93984ebSRobert Marko primary_offset = PRIMARY_OFFSET(read_primary_offset); 181e93984ebSRobert Marko 182e93984ebSRobert Marko /* Find the DRAM and IRAM sections within the firmware file. 183e93984ebSRobert Marko * Make sure the fw_header is correctly in the firmware. 184e93984ebSRobert Marko */ 185e93984ebSRobert Marko if (!aqr_fw_validate_get(size, primary_offset + HEADER_OFFSET, 186e93984ebSRobert Marko sizeof(struct aqr_fw_header))) { 187e93984ebSRobert Marko phydev_err(phydev, "bad fw_header in firmware\n"); 188e93984ebSRobert Marko return -EINVAL; 189e93984ebSRobert Marko } 190e93984ebSRobert Marko 191e93984ebSRobert Marko /* offset are in LE and values needs to be converted to cpu endian */ 192e93984ebSRobert Marko ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + 193e93984ebSRobert Marko offsetof(struct aqr_fw_header, iram_offset), 194e93984ebSRobert Marko size, &iram_offset); 195e93984ebSRobert Marko if (ret) { 196e93984ebSRobert Marko phydev_err(phydev, "bad iram offset in firmware\n"); 197e93984ebSRobert Marko return ret; 198e93984ebSRobert Marko } 199e93984ebSRobert Marko ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + 200e93984ebSRobert Marko offsetof(struct aqr_fw_header, iram_size), 201e93984ebSRobert Marko size, &iram_size); 202e93984ebSRobert Marko if (ret) { 203e93984ebSRobert Marko phydev_err(phydev, "invalid iram size in firmware\n"); 204e93984ebSRobert Marko return ret; 205e93984ebSRobert Marko } 206e93984ebSRobert Marko ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + 207e93984ebSRobert Marko offsetof(struct aqr_fw_header, dram_offset), 208e93984ebSRobert Marko size, &dram_offset); 209e93984ebSRobert Marko if (ret) { 210e93984ebSRobert Marko phydev_err(phydev, "bad dram offset in firmware\n"); 211e93984ebSRobert Marko return ret; 212e93984ebSRobert Marko } 213e93984ebSRobert Marko ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + 214e93984ebSRobert Marko offsetof(struct aqr_fw_header, dram_size), 215e93984ebSRobert Marko size, &dram_size); 216e93984ebSRobert Marko if (ret) { 217e93984ebSRobert Marko phydev_err(phydev, "invalid dram size in firmware\n"); 218e93984ebSRobert Marko return ret; 219e93984ebSRobert Marko } 220e93984ebSRobert Marko 221e93984ebSRobert Marko /* Increment the offset with the primary offset. 222e93984ebSRobert Marko * Validate iram/dram offset and size. 223e93984ebSRobert Marko */ 224e93984ebSRobert Marko iram_offset += primary_offset; 225e93984ebSRobert Marko if (iram_size % sizeof(u32)) { 226e93984ebSRobert Marko phydev_err(phydev, "iram size if not aligned to word size. Please report this upstream!\n"); 227e93984ebSRobert Marko return -EINVAL; 228e93984ebSRobert Marko } 229e93984ebSRobert Marko if (!aqr_fw_validate_get(size, iram_offset, iram_size)) { 230e93984ebSRobert Marko phydev_err(phydev, "invalid iram offset for iram size\n"); 231e93984ebSRobert Marko return -EINVAL; 232e93984ebSRobert Marko } 233e93984ebSRobert Marko 234e93984ebSRobert Marko dram_offset += primary_offset; 235e93984ebSRobert Marko if (dram_size % sizeof(u32)) { 236e93984ebSRobert Marko phydev_err(phydev, "dram size if not aligned to word size. Please report this upstream!\n"); 237e93984ebSRobert Marko return -EINVAL; 238e93984ebSRobert Marko } 239e93984ebSRobert Marko if (!aqr_fw_validate_get(size, dram_offset, dram_size)) { 240e93984ebSRobert Marko phydev_err(phydev, "invalid iram offset for iram size\n"); 241e93984ebSRobert Marko return -EINVAL; 242e93984ebSRobert Marko } 243e93984ebSRobert Marko 244e93984ebSRobert Marko phydev_dbg(phydev, "primary %d IRAM offset=%d size=%d DRAM offset=%d size=%d\n", 245e93984ebSRobert Marko primary_offset, iram_offset, iram_size, dram_offset, dram_size); 246e93984ebSRobert Marko 247e93984ebSRobert Marko if (!aqr_fw_validate_get(size, dram_offset + VERSION_STRING_OFFSET, 248e93984ebSRobert Marko VERSION_STRING_SIZE)) { 249e93984ebSRobert Marko phydev_err(phydev, "invalid version in firmware\n"); 250e93984ebSRobert Marko return -EINVAL; 251e93984ebSRobert Marko } 252e93984ebSRobert Marko strscpy(version, (char *)data + dram_offset + VERSION_STRING_OFFSET, 253e93984ebSRobert Marko VERSION_STRING_SIZE); 254e93984ebSRobert Marko if (version[0] == '\0') { 255e93984ebSRobert Marko phydev_err(phydev, "invalid version in firmware\n"); 256e93984ebSRobert Marko return -EINVAL; 257e93984ebSRobert Marko } 258e93984ebSRobert Marko phydev_info(phydev, "loading firmware version '%s' from '%s'\n", version, 259e93984ebSRobert Marko aqr_fw_src_string[fw_src]); 260e93984ebSRobert Marko 261e93984ebSRobert Marko /* stall the microcprocessor */ 262e93984ebSRobert Marko phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2, 263e93984ebSRobert Marko VEND1_GLOBAL_CONTROL2_UP_RUN_STALL | VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD); 264e93984ebSRobert Marko 265e93984ebSRobert Marko phydev_dbg(phydev, "loading DRAM 0x%08x from offset=%d size=%d\n", 266e93984ebSRobert Marko DRAM_BASE_ADDR, dram_offset, dram_size); 267e93984ebSRobert Marko ret = aqr_fw_load_memory(phydev, DRAM_BASE_ADDR, data + dram_offset, 268e93984ebSRobert Marko dram_size); 269e93984ebSRobert Marko if (ret) 270e93984ebSRobert Marko return ret; 271e93984ebSRobert Marko 272e93984ebSRobert Marko phydev_dbg(phydev, "loading IRAM 0x%08x from offset=%d size=%d\n", 273e93984ebSRobert Marko IRAM_BASE_ADDR, iram_offset, iram_size); 274e93984ebSRobert Marko ret = aqr_fw_load_memory(phydev, IRAM_BASE_ADDR, data + iram_offset, 275e93984ebSRobert Marko iram_size); 276e93984ebSRobert Marko if (ret) 277e93984ebSRobert Marko return ret; 278e93984ebSRobert Marko 279e93984ebSRobert Marko /* make sure soft reset and low power mode are clear */ 280e93984ebSRobert Marko phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_SC, 281e93984ebSRobert Marko VEND1_GLOBAL_SC_SOFT_RESET | VEND1_GLOBAL_SC_LOW_POWER); 282e93984ebSRobert Marko 283e93984ebSRobert Marko /* Release the microprocessor. UP_RESET must be held for 100 usec. */ 284e93984ebSRobert Marko phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2, 285e93984ebSRobert Marko VEND1_GLOBAL_CONTROL2_UP_RUN_STALL | 286e93984ebSRobert Marko VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD | 287e93984ebSRobert Marko VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST); 288e93984ebSRobert Marko usleep_range(UP_RESET_SLEEP, UP_RESET_SLEEP * 2); 289e93984ebSRobert Marko 290e93984ebSRobert Marko phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2, 291e93984ebSRobert Marko VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD); 292e93984ebSRobert Marko 293e93984ebSRobert Marko return 0; 294e93984ebSRobert Marko } 295e93984ebSRobert Marko 296e93984ebSRobert Marko static int aqr_firmware_load_nvmem(struct phy_device *phydev) 297e93984ebSRobert Marko { 298e93984ebSRobert Marko struct nvmem_cell *cell; 299e93984ebSRobert Marko size_t size; 300e93984ebSRobert Marko u8 *buf; 301e93984ebSRobert Marko int ret; 302e93984ebSRobert Marko 303e93984ebSRobert Marko cell = nvmem_cell_get(&phydev->mdio.dev, "firmware"); 304e93984ebSRobert Marko if (IS_ERR(cell)) 305e93984ebSRobert Marko return PTR_ERR(cell); 306e93984ebSRobert Marko 307e93984ebSRobert Marko buf = nvmem_cell_read(cell, &size); 308e93984ebSRobert Marko if (IS_ERR(buf)) { 309e93984ebSRobert Marko ret = PTR_ERR(buf); 310e93984ebSRobert Marko goto exit; 311e93984ebSRobert Marko } 312e93984ebSRobert Marko 313e93984ebSRobert Marko ret = aqr_fw_boot(phydev, buf, size, AQR_FW_SRC_NVMEM); 314e93984ebSRobert Marko if (ret) 315e93984ebSRobert Marko phydev_err(phydev, "firmware loading failed: %d\n", ret); 316e93984ebSRobert Marko 317e93984ebSRobert Marko kfree(buf); 318e93984ebSRobert Marko exit: 319e93984ebSRobert Marko nvmem_cell_put(cell); 320e93984ebSRobert Marko 321e93984ebSRobert Marko return ret; 322e93984ebSRobert Marko } 323e93984ebSRobert Marko 324e93984ebSRobert Marko static int aqr_firmware_load_fs(struct phy_device *phydev) 325e93984ebSRobert Marko { 326e93984ebSRobert Marko struct device *dev = &phydev->mdio.dev; 327e93984ebSRobert Marko const struct firmware *fw; 328e93984ebSRobert Marko const char *fw_name; 329e93984ebSRobert Marko int ret; 330e93984ebSRobert Marko 331e93984ebSRobert Marko ret = of_property_read_string(dev->of_node, "firmware-name", 332e93984ebSRobert Marko &fw_name); 333e93984ebSRobert Marko if (ret) 334e93984ebSRobert Marko return ret; 335e93984ebSRobert Marko 336e93984ebSRobert Marko ret = request_firmware(&fw, fw_name, dev); 337e93984ebSRobert Marko if (ret) { 338e93984ebSRobert Marko phydev_err(phydev, "failed to find FW file %s (%d)\n", 339e93984ebSRobert Marko fw_name, ret); 340e93984ebSRobert Marko return ret; 341e93984ebSRobert Marko } 342e93984ebSRobert Marko 343e93984ebSRobert Marko ret = aqr_fw_boot(phydev, fw->data, fw->size, AQR_FW_SRC_FS); 344e93984ebSRobert Marko if (ret) 345e93984ebSRobert Marko phydev_err(phydev, "firmware loading failed: %d\n", ret); 346e93984ebSRobert Marko 347e93984ebSRobert Marko release_firmware(fw); 348e93984ebSRobert Marko 349e93984ebSRobert Marko return ret; 350e93984ebSRobert Marko } 351e93984ebSRobert Marko 352e93984ebSRobert Marko int aqr_firmware_load(struct phy_device *phydev) 353e93984ebSRobert Marko { 354e93984ebSRobert Marko int ret; 355e93984ebSRobert Marko 356*ad649a1fSBartosz Golaszewski ret = aqr_wait_reset_complete(phydev); 357*ad649a1fSBartosz Golaszewski if (ret) 358*ad649a1fSBartosz Golaszewski return ret; 359*ad649a1fSBartosz Golaszewski 360e93984ebSRobert Marko /* Check if the firmware is not already loaded by pooling 361e93984ebSRobert Marko * the current version returned by the PHY. If 0 is returned, 362e93984ebSRobert Marko * no firmware is loaded. 363e93984ebSRobert Marko */ 364e93984ebSRobert Marko ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID); 365e93984ebSRobert Marko if (ret > 0) 366e93984ebSRobert Marko goto exit; 367e93984ebSRobert Marko 368e93984ebSRobert Marko ret = aqr_firmware_load_nvmem(phydev); 369e93984ebSRobert Marko if (!ret) 370e93984ebSRobert Marko goto exit; 371e93984ebSRobert Marko 372e93984ebSRobert Marko ret = aqr_firmware_load_fs(phydev); 373e93984ebSRobert Marko if (ret) 374e93984ebSRobert Marko return ret; 375e93984ebSRobert Marko 376e93984ebSRobert Marko exit: 377e93984ebSRobert Marko return 0; 378e93984ebSRobert Marko } 379