xref: /linux/drivers/net/phy/aquantia/aquantia_firmware.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
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  */
aqr_fw_validate_get(size_t size,size_t offset,size_t get_size)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 
aqr_fw_get_be16(const u8 * data,size_t offset,size_t size,u16 * value)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 
aqr_fw_get_le16(const u8 * data,size_t offset,size_t size,u16 * value)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 
aqr_fw_get_le24(const u8 * data,size_t offset,size_t size,u32 * value)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 */
aqr_fw_load_memory(struct phy_device * phydev,u32 addr,const u8 * data,size_t len)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 
aqr_fw_boot(struct phy_device * phydev,const u8 * data,size_t size,enum aqr_fw_src fw_src)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 
aqr_firmware_load_nvmem(struct phy_device * phydev)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 
aqr_firmware_load_fs(struct phy_device * phydev)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 
aqr_firmware_load(struct phy_device * phydev)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