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