/* addresses of memory segments in the phy */ #define DRAM_BASE_ADDR 0x3FFE0000 #define IRAM_BASE_ADDR 0x40000000
/* firmware image format constants */ #define VERSION_STRING_SIZE 0x40 #define VERSION_STRING_OFFSET 0x0200 /* primary offset is written at an offset from the start of the fw blob */ #define PRIMARY_OFFSET_OFFSET 0x8 /* primary offset needs to be then added to a base offset */ #define PRIMARY_OFFSET_SHIFT 12 #define PRIMARY_OFFSET(x) ((x) << PRIMARY_OFFSET_SHIFT) #define HEADER_OFFSET 0x300
/* AQR firmware doesn't have fixed offsets for iram and dram section * but instead provide an header with the offset to use on reading * and parsing the firmware. * * AQR firmware can't be trusted and each offset is validated to be * not negative and be in the size of the firmware itself.
*/ staticbool aqr_fw_validate_get(size_t size, size_t offset, size_t get_size)
{ return offset + get_size <= size;
}
/* We assume and enforce the size to be word aligned. * If a firmware that is not word aligned is found, please report upstream.
*/ for (pos = 0; pos < len; pos += sizeof(u32)) {
u8 crc_data[4];
u32 word;
/* FW data is always stored in little-endian */
word = get_unaligned_le32((const u32 *)(data + pos));
/* Word is swapped internally and MAILBOX CRC is calculated * using big-endian order. Mimic what the PHY does to have a * matching CRC...
*/
crc_data[0] = word >> 24;
crc_data[1] = word >> 16;
crc_data[2] = word >> 8;
crc_data[3] = word;
/* ...calculate CRC as we load data... */
crc = crc_itu_t(crc, crc_data, sizeof(crc_data));
} /* ...gets CRC from MAILBOX after we have loaded the entire section... */
up_crc = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE2); /* ...and make sure it does match our calculated CRC */ if (crc != up_crc) {
phydev_err(phydev, "CRC mismatch: calculated 0x%04x PHY 0x%04x\n",
crc, up_crc); return -EINVAL;
}
/* extract saved CRC at the end of the fw * CRC is saved in big-endian as PHY is BE
*/
ret = aqr_fw_get_be16(data, size - sizeof(u16), size, &read_crc); if (ret) {
phydev_err(phydev, "bad firmware CRC in firmware\n"); return ret;
}
calculated_crc = crc_itu_t(0, data, size - sizeof(u16)); if (read_crc != calculated_crc) {
phydev_err(phydev, "bad firmware CRC: file 0x%04x calculated 0x%04x\n",
read_crc, calculated_crc); return -EINVAL;
}
/* Get the primary offset to extract DRAM and IRAM sections. */
ret = aqr_fw_get_le16(data, PRIMARY_OFFSET_OFFSET, size, &read_primary_offset); if (ret) {
phydev_err(phydev, "bad primary offset in firmware\n"); return ret;
}
primary_offset = PRIMARY_OFFSET(read_primary_offset);
/* Find the DRAM and IRAM sections within the firmware file. * Make sure the fw_header is correctly in the firmware.
*/ if (!aqr_fw_validate_get(size, primary_offset + HEADER_OFFSET, sizeof(struct aqr_fw_header))) {
phydev_err(phydev, "bad fw_header in firmware\n"); return -EINVAL;
}
/* offset are in LE and values needs to be converted to cpu endian */
ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
offsetof(struct aqr_fw_header, iram_offset),
size, &iram_offset); if (ret) {
phydev_err(phydev, "bad iram offset in firmware\n"); return ret;
}
ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
offsetof(struct aqr_fw_header, iram_size),
size, &iram_size); if (ret) {
phydev_err(phydev, "invalid iram size in firmware\n"); return ret;
}
ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
offsetof(struct aqr_fw_header, dram_offset),
size, &dram_offset); if (ret) {
phydev_err(phydev, "bad dram offset in firmware\n"); return ret;
}
ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
offsetof(struct aqr_fw_header, dram_size),
size, &dram_size); if (ret) {
phydev_err(phydev, "invalid dram size in firmware\n"); return ret;
}
/* Increment the offset with the primary offset. * Validate iram/dram offset and size.
*/
iram_offset += primary_offset; if (iram_size % sizeof(u32)) {
phydev_err(phydev, "iram size if not aligned to word size. Please report this upstream!\n"); return -EINVAL;
} if (!aqr_fw_validate_get(size, iram_offset, iram_size)) {
phydev_err(phydev, "invalid iram offset for iram size\n"); return -EINVAL;
}
dram_offset += primary_offset; if (dram_size % sizeof(u32)) {
phydev_err(phydev, "dram size if not aligned to word size. Please report this upstream!\n"); return -EINVAL;
} if (!aqr_fw_validate_get(size, dram_offset, dram_size)) {
phydev_err(phydev, "invalid iram offset for iram size\n"); return -EINVAL;
}
phydev_dbg(phydev, "loading DRAM 0x%08x from offset=%d size=%d\n",
DRAM_BASE_ADDR, dram_offset, dram_size);
ret = aqr_fw_load_memory(phydev, DRAM_BASE_ADDR, data + dram_offset,
dram_size); if (ret) return ret;
phydev_dbg(phydev, "loading IRAM 0x%08x from offset=%d size=%d\n",
IRAM_BASE_ADDR, iram_offset, iram_size);
ret = aqr_fw_load_memory(phydev, IRAM_BASE_ADDR, data + iram_offset,
iram_size); if (ret) return ret;
/* make sure soft reset and low power mode are clear */
phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_SC,
VEND1_GLOBAL_SC_SOFT_RESET | VEND1_GLOBAL_SC_LOW_POWER);
/* Release the microprocessor. UP_RESET must be held for 100 usec. */
phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
VEND1_GLOBAL_CONTROL2_UP_RUN_STALL |
VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD |
VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST);
usleep_range(UP_RESET_SLEEP, UP_RESET_SLEEP * 2);
ret = device_property_read_string(dev, "firmware-name", &fw_name); if (ret) {
phydev_err(phydev, "failed to read firmware-name: %d\n", ret); return ret;
}
ret = request_firmware(&fw, fw_name, dev); if (ret) {
phydev_err(phydev, "failed to find FW file %s (%d)\n",
fw_name, ret); return ret;
}
ret = aqr_fw_boot(phydev, fw->data, fw->size, AQR_FW_SRC_FS); if (ret)
phydev_err(phydev, "firmware loading failed: %d\n", ret);
release_firmware(fw);
return ret;
}
int aqr_firmware_load(struct phy_device *phydev)
{ int ret;
/* Check if the firmware is not already loaded by polling * the current version returned by the PHY.
*/
ret = aqr_wait_reset_complete(phydev); switch (ret) { case 0: /* Some firmware is loaded => do nothing */ return 0; case -ETIMEDOUT: /* VEND1_GLOBAL_FW_ID still reads 0 after 2 seconds of polling. * We don't have full confidence that no firmware is loaded (in * theory it might just not have loaded yet), but we will * assume that, and load a new image.
*/
ret = aqr_firmware_load_nvmem(phydev); if (!ret) return ret;
ret = aqr_firmware_load_fs(phydev); if (ret) return ret; break; default: /* PHY read error, propagate it to the caller */ return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.