drivers/net/phy/aquantia/aquantia_firmware.c

Source file repositories/reference/linux-study-clean/drivers/net/phy/aquantia/aquantia_firmware.c

File Facts

System
Linux kernel
Corpus path
drivers/net/phy/aquantia/aquantia_firmware.c
Extension
.c
Size
11181 bytes
Lines
386
Domain
Driver Families
Bucket
drivers/net
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct aqr_fw_header {
	u32 padding;
	u8 iram_offset[3];
	u8 iram_size[3];
	u8 dram_offset[3];
	u8 dram_size[3];
} __packed;

enum aqr_fw_src {
	AQR_FW_SRC_NVMEM = 0,
	AQR_FW_SRC_FS,
};

static const char * const aqr_fw_src_string[] = {
	[AQR_FW_SRC_NVMEM] = "NVMEM",
	[AQR_FW_SRC_FS] = "FS",
};

/* 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.
 */
static bool aqr_fw_validate_get(size_t size, size_t offset, size_t get_size)
{
	return offset + get_size <= size;
}

static int aqr_fw_get_be16(const u8 *data, size_t offset, size_t size, u16 *value)
{
	if (!aqr_fw_validate_get(size, offset, sizeof(u16)))
		return -EINVAL;

	*value = get_unaligned_be16(data + offset);

	return 0;
}

static int aqr_fw_get_le16(const u8 *data, size_t offset, size_t size, u16 *value)
{
	if (!aqr_fw_validate_get(size, offset, sizeof(u16)))
		return -EINVAL;

	*value = get_unaligned_le16(data + offset);

	return 0;
}

static int aqr_fw_get_le24(const u8 *data, size_t offset, size_t size, u32 *value)
{
	if (!aqr_fw_validate_get(size, offset, sizeof(u8) * 3))
		return -EINVAL;

	*value = get_unaligned_le24(data + offset);

	return 0;
}

/* load data into the phy's memory */
static int aqr_fw_load_memory(struct phy_device *phydev, u32 addr,
			      const u8 *data, size_t len)
{
	u16 crc = 0, up_crc;
	size_t pos;

	phy_write_mmd(phydev, MDIO_MMD_VEND1,
		      VEND1_GLOBAL_MAILBOX_INTERFACE1,
		      VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET);
	phy_write_mmd(phydev, MDIO_MMD_VEND1,
		      VEND1_GLOBAL_MAILBOX_INTERFACE3,
		      VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(addr));
	phy_write_mmd(phydev, MDIO_MMD_VEND1,
		      VEND1_GLOBAL_MAILBOX_INTERFACE4,
		      VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(addr));

	/* 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));

		phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE5,
			      VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(word));
		phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE6,

Annotation

Implementation Notes