mtd: fix build with GCC 14
[openwrt/openwrt.git] / target / linux / generic / backport-5.15 / 737-03-v6.7-net-phy-aquantia-add-firmware-load-support.patch
1 From e93984ebc1c82bd34f7a1b3391efaceee0a8ae96 Mon Sep 17 00:00:00 2001
2 From: Robert Marko <robimarko@gmail.com>
3 Date: Tue, 14 Nov 2023 15:08:43 +0100
4 Subject: [PATCH 3/3] net: phy: aquantia: add firmware load support
5
6 Aquantia PHY-s require firmware to be loaded before they start operating.
7 It can be automatically loaded in case when there is a SPI-NOR connected
8 to Aquantia PHY-s or can be loaded from the host via MDIO.
9
10 This patch adds support for loading the firmware via MDIO as in most cases
11 there is no SPI-NOR being used to save on cost.
12 Firmware loading code itself is ported from mainline U-boot with cleanups.
13
14 The firmware has mixed values both in big and little endian.
15 PHY core itself is big-endian but it expects values to be in little-endian.
16 The firmware is little-endian but CRC-16 value for it is stored at the end
17 of firmware in big-endian.
18
19 It seems the PHY does the conversion internally from firmware that is
20 little-endian to the PHY that is big-endian on using the mailbox
21 but mailbox returns a big-endian CRC-16 to verify the written data
22 integrity.
23
24 Co-developed-by: Christian Marangi <ansuelsmth@gmail.com>
25 Signed-off-by: Robert Marko <robimarko@gmail.com>
26 Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
27 Reviewed-by: Andrew Lunn <andrew@lunn.ch>
28 Signed-off-by: David S. Miller <davem@davemloft.net>
29 ---
30 drivers/net/phy/aquantia/Kconfig | 1 +
31 drivers/net/phy/aquantia/Makefile | 2 +-
32 drivers/net/phy/aquantia/aquantia.h | 32 ++
33 drivers/net/phy/aquantia/aquantia_firmware.c | 370 +++++++++++++++++++
34 drivers/net/phy/aquantia/aquantia_main.c | 6 +
35 5 files changed, 410 insertions(+), 1 deletion(-)
36 create mode 100644 drivers/net/phy/aquantia/aquantia_firmware.c
37
38 --- a/drivers/net/phy/aquantia/Kconfig
39 +++ b/drivers/net/phy/aquantia/Kconfig
40 @@ -1,5 +1,6 @@
41 # SPDX-License-Identifier: GPL-2.0-only
42 config AQUANTIA_PHY
43 tristate "Aquantia PHYs"
44 + select CRC_CCITT
45 help
46 Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405
47 --- a/drivers/net/phy/aquantia/Makefile
48 +++ b/drivers/net/phy/aquantia/Makefile
49 @@ -1,5 +1,5 @@
50 # SPDX-License-Identifier: GPL-2.0
51 -aquantia-objs += aquantia_main.o
52 +aquantia-objs += aquantia_main.o aquantia_firmware.o
53 ifdef CONFIG_HWMON
54 aquantia-objs += aquantia_hwmon.o
55 endif
56 --- a/drivers/net/phy/aquantia/aquantia.h
57 +++ b/drivers/net/phy/aquantia/aquantia.h
58 @@ -10,10 +10,35 @@
59 #include <linux/phy.h>
60
61 /* Vendor specific 1, MDIO_MMD_VEND1 */
62 +#define VEND1_GLOBAL_SC 0x0
63 +#define VEND1_GLOBAL_SC_SOFT_RESET BIT(15)
64 +#define VEND1_GLOBAL_SC_LOW_POWER BIT(11)
65 +
66 #define VEND1_GLOBAL_FW_ID 0x0020
67 #define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8)
68 #define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0)
69
70 +#define VEND1_GLOBAL_MAILBOX_INTERFACE1 0x0200
71 +#define VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE BIT(15)
72 +#define VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE BIT(14)
73 +#define VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET BIT(12)
74 +#define VEND1_GLOBAL_MAILBOX_INTERFACE1_BUSY BIT(8)
75 +
76 +#define VEND1_GLOBAL_MAILBOX_INTERFACE2 0x0201
77 +#define VEND1_GLOBAL_MAILBOX_INTERFACE3 0x0202
78 +#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK GENMASK(15, 0)
79 +#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK, (u16)((x) >> 16))
80 +#define VEND1_GLOBAL_MAILBOX_INTERFACE4 0x0203
81 +#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK GENMASK(15, 2)
82 +#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK, (u16)(x))
83 +
84 +#define VEND1_GLOBAL_MAILBOX_INTERFACE5 0x0204
85 +#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK GENMASK(15, 0)
86 +#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK, (u16)((x) >> 16))
87 +#define VEND1_GLOBAL_MAILBOX_INTERFACE6 0x0205
88 +#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK GENMASK(15, 0)
89 +#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK, (u16)(x))
90 +
91 /* The following registers all have similar layouts; first the registers... */
92 #define VEND1_GLOBAL_CFG_10M 0x0310
93 #define VEND1_GLOBAL_CFG_100M 0x031b
94 @@ -28,6 +53,11 @@
95 #define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2
96
97 /* Vendor specific 1, MDIO_MMD_VEND2 */
98 +#define VEND1_GLOBAL_CONTROL2 0xc001
99 +#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST BIT(15)
100 +#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD BIT(6)
101 +#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL BIT(0)
102 +
103 #define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421
104 #define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422
105 #define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423
106 @@ -83,3 +113,5 @@ int aqr_hwmon_probe(struct phy_device *p
107 #else
108 static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; }
109 #endif
110 +
111 +int aqr_firmware_load(struct phy_device *phydev);
112 --- /dev/null
113 +++ b/drivers/net/phy/aquantia/aquantia_firmware.c
114 @@ -0,0 +1,370 @@
115 +// SPDX-License-Identifier: GPL-2.0
116 +
117 +#include <linux/bitfield.h>
118 +#include <linux/of.h>
119 +#include <linux/firmware.h>
120 +#include <linux/crc-ccitt.h>
121 +#include <linux/nvmem-consumer.h>
122 +
123 +#include <asm/unaligned.h>
124 +
125 +#include "aquantia.h"
126 +
127 +#define UP_RESET_SLEEP 100
128 +
129 +/* addresses of memory segments in the phy */
130 +#define DRAM_BASE_ADDR 0x3FFE0000
131 +#define IRAM_BASE_ADDR 0x40000000
132 +
133 +/* firmware image format constants */
134 +#define VERSION_STRING_SIZE 0x40
135 +#define VERSION_STRING_OFFSET 0x0200
136 +/* primary offset is written at an offset from the start of the fw blob */
137 +#define PRIMARY_OFFSET_OFFSET 0x8
138 +/* primary offset needs to be then added to a base offset */
139 +#define PRIMARY_OFFSET_SHIFT 12
140 +#define PRIMARY_OFFSET(x) ((x) << PRIMARY_OFFSET_SHIFT)
141 +#define HEADER_OFFSET 0x300
142 +
143 +struct aqr_fw_header {
144 + u32 padding;
145 + u8 iram_offset[3];
146 + u8 iram_size[3];
147 + u8 dram_offset[3];
148 + u8 dram_size[3];
149 +} __packed;
150 +
151 +enum aqr_fw_src {
152 + AQR_FW_SRC_NVMEM = 0,
153 + AQR_FW_SRC_FS,
154 +};
155 +
156 +static const char * const aqr_fw_src_string[] = {
157 + [AQR_FW_SRC_NVMEM] = "NVMEM",
158 + [AQR_FW_SRC_FS] = "FS",
159 +};
160 +
161 +/* AQR firmware doesn't have fixed offsets for iram and dram section
162 + * but instead provide an header with the offset to use on reading
163 + * and parsing the firmware.
164 + *
165 + * AQR firmware can't be trusted and each offset is validated to be
166 + * not negative and be in the size of the firmware itself.
167 + */
168 +static bool aqr_fw_validate_get(size_t size, size_t offset, size_t get_size)
169 +{
170 + return offset + get_size <= size;
171 +}
172 +
173 +static int aqr_fw_get_be16(const u8 *data, size_t offset, size_t size, u16 *value)
174 +{
175 + if (!aqr_fw_validate_get(size, offset, sizeof(u16)))
176 + return -EINVAL;
177 +
178 + *value = get_unaligned_be16(data + offset);
179 +
180 + return 0;
181 +}
182 +
183 +static int aqr_fw_get_le16(const u8 *data, size_t offset, size_t size, u16 *value)
184 +{
185 + if (!aqr_fw_validate_get(size, offset, sizeof(u16)))
186 + return -EINVAL;
187 +
188 + *value = get_unaligned_le16(data + offset);
189 +
190 + return 0;
191 +}
192 +
193 +static int aqr_fw_get_le24(const u8 *data, size_t offset, size_t size, u32 *value)
194 +{
195 + if (!aqr_fw_validate_get(size, offset, sizeof(u8) * 3))
196 + return -EINVAL;
197 +
198 + *value = get_unaligned_le24(data + offset);
199 +
200 + return 0;
201 +}
202 +
203 +/* load data into the phy's memory */
204 +static int aqr_fw_load_memory(struct phy_device *phydev, u32 addr,
205 + const u8 *data, size_t len)
206 +{
207 + u16 crc = 0, up_crc;
208 + size_t pos;
209 +
210 + /* PHY expect addr in LE */
211 + addr = (__force u32)cpu_to_le32(addr);
212 +
213 + phy_write_mmd(phydev, MDIO_MMD_VEND1,
214 + VEND1_GLOBAL_MAILBOX_INTERFACE1,
215 + VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET);
216 + phy_write_mmd(phydev, MDIO_MMD_VEND1,
217 + VEND1_GLOBAL_MAILBOX_INTERFACE3,
218 + VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(addr));
219 + phy_write_mmd(phydev, MDIO_MMD_VEND1,
220 + VEND1_GLOBAL_MAILBOX_INTERFACE4,
221 + VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(addr));
222 +
223 + /* We assume and enforce the size to be word aligned.
224 + * If a firmware that is not word aligned is found, please report upstream.
225 + */
226 + for (pos = 0; pos < len; pos += sizeof(u32)) {
227 + u32 word;
228 +
229 + /* FW data is always stored in little-endian */
230 + word = get_unaligned((const u32 *)(data + pos));
231 +
232 + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE5,
233 + VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(word));
234 + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE6,
235 + VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(word));
236 +
237 + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE1,
238 + VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE |
239 + VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE);
240 +
241 + /* calculate CRC as we load data to the mailbox.
242 + * We convert word to big-endian as PHY is BE and mailbox will
243 + * return a BE CRC.
244 + */
245 + word = (__force u32)cpu_to_be32(word);
246 + crc = crc_ccitt_false(crc, (u8 *)&word, sizeof(word));
247 + }
248 +
249 + up_crc = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE2);
250 + if (crc != up_crc) {
251 + phydev_err(phydev, "CRC mismatch: calculated 0x%04x PHY 0x%04x\n",
252 + crc, up_crc);
253 + return -EINVAL;
254 + }
255 +
256 + return 0;
257 +}
258 +
259 +static int aqr_fw_boot(struct phy_device *phydev, const u8 *data, size_t size,
260 + enum aqr_fw_src fw_src)
261 +{
262 + u16 calculated_crc, read_crc, read_primary_offset;
263 + u32 iram_offset = 0, iram_size = 0;
264 + u32 dram_offset = 0, dram_size = 0;
265 + char version[VERSION_STRING_SIZE];
266 + u32 primary_offset = 0;
267 + int ret;
268 +
269 + /* extract saved CRC at the end of the fw
270 + * CRC is saved in big-endian as PHY is BE
271 + */
272 + ret = aqr_fw_get_be16(data, size - sizeof(u16), size, &read_crc);
273 + if (ret) {
274 + phydev_err(phydev, "bad firmware CRC in firmware\n");
275 + return ret;
276 + }
277 + calculated_crc = crc_ccitt_false(0, data, size - sizeof(u16));
278 + if (read_crc != calculated_crc) {
279 + phydev_err(phydev, "bad firmware CRC: file 0x%04x calculated 0x%04x\n",
280 + read_crc, calculated_crc);
281 + return -EINVAL;
282 + }
283 +
284 + /* Get the primary offset to extract DRAM and IRAM sections. */
285 + ret = aqr_fw_get_le16(data, PRIMARY_OFFSET_OFFSET, size, &read_primary_offset);
286 + if (ret) {
287 + phydev_err(phydev, "bad primary offset in firmware\n");
288 + return ret;
289 + }
290 + primary_offset = PRIMARY_OFFSET(read_primary_offset);
291 +
292 + /* Find the DRAM and IRAM sections within the firmware file.
293 + * Make sure the fw_header is correctly in the firmware.
294 + */
295 + if (!aqr_fw_validate_get(size, primary_offset + HEADER_OFFSET,
296 + sizeof(struct aqr_fw_header))) {
297 + phydev_err(phydev, "bad fw_header in firmware\n");
298 + return -EINVAL;
299 + }
300 +
301 + /* offset are in LE and values needs to be converted to cpu endian */
302 + ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
303 + offsetof(struct aqr_fw_header, iram_offset),
304 + size, &iram_offset);
305 + if (ret) {
306 + phydev_err(phydev, "bad iram offset in firmware\n");
307 + return ret;
308 + }
309 + ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
310 + offsetof(struct aqr_fw_header, iram_size),
311 + size, &iram_size);
312 + if (ret) {
313 + phydev_err(phydev, "invalid iram size in firmware\n");
314 + return ret;
315 + }
316 + ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
317 + offsetof(struct aqr_fw_header, dram_offset),
318 + size, &dram_offset);
319 + if (ret) {
320 + phydev_err(phydev, "bad dram offset in firmware\n");
321 + return ret;
322 + }
323 + ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
324 + offsetof(struct aqr_fw_header, dram_size),
325 + size, &dram_size);
326 + if (ret) {
327 + phydev_err(phydev, "invalid dram size in firmware\n");
328 + return ret;
329 + }
330 +
331 + /* Increment the offset with the primary offset.
332 + * Validate iram/dram offset and size.
333 + */
334 + iram_offset += primary_offset;
335 + if (iram_size % sizeof(u32)) {
336 + phydev_err(phydev, "iram size if not aligned to word size. Please report this upstream!\n");
337 + return -EINVAL;
338 + }
339 + if (!aqr_fw_validate_get(size, iram_offset, iram_size)) {
340 + phydev_err(phydev, "invalid iram offset for iram size\n");
341 + return -EINVAL;
342 + }
343 +
344 + dram_offset += primary_offset;
345 + if (dram_size % sizeof(u32)) {
346 + phydev_err(phydev, "dram size if not aligned to word size. Please report this upstream!\n");
347 + return -EINVAL;
348 + }
349 + if (!aqr_fw_validate_get(size, dram_offset, dram_size)) {
350 + phydev_err(phydev, "invalid iram offset for iram size\n");
351 + return -EINVAL;
352 + }
353 +
354 + phydev_dbg(phydev, "primary %d IRAM offset=%d size=%d DRAM offset=%d size=%d\n",
355 + primary_offset, iram_offset, iram_size, dram_offset, dram_size);
356 +
357 + if (!aqr_fw_validate_get(size, dram_offset + VERSION_STRING_OFFSET,
358 + VERSION_STRING_SIZE)) {
359 + phydev_err(phydev, "invalid version in firmware\n");
360 + return -EINVAL;
361 + }
362 + strscpy(version, (char *)data + dram_offset + VERSION_STRING_OFFSET,
363 + VERSION_STRING_SIZE);
364 + if (version[0] == '\0') {
365 + phydev_err(phydev, "invalid version in firmware\n");
366 + return -EINVAL;
367 + }
368 + phydev_info(phydev, "loading firmware version '%s' from '%s'\n", version,
369 + aqr_fw_src_string[fw_src]);
370 +
371 + /* stall the microcprocessor */
372 + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
373 + VEND1_GLOBAL_CONTROL2_UP_RUN_STALL | VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD);
374 +
375 + phydev_dbg(phydev, "loading DRAM 0x%08x from offset=%d size=%d\n",
376 + DRAM_BASE_ADDR, dram_offset, dram_size);
377 + ret = aqr_fw_load_memory(phydev, DRAM_BASE_ADDR, data + dram_offset,
378 + dram_size);
379 + if (ret)
380 + return ret;
381 +
382 + phydev_dbg(phydev, "loading IRAM 0x%08x from offset=%d size=%d\n",
383 + IRAM_BASE_ADDR, iram_offset, iram_size);
384 + ret = aqr_fw_load_memory(phydev, IRAM_BASE_ADDR, data + iram_offset,
385 + iram_size);
386 + if (ret)
387 + return ret;
388 +
389 + /* make sure soft reset and low power mode are clear */
390 + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_SC,
391 + VEND1_GLOBAL_SC_SOFT_RESET | VEND1_GLOBAL_SC_LOW_POWER);
392 +
393 + /* Release the microprocessor. UP_RESET must be held for 100 usec. */
394 + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
395 + VEND1_GLOBAL_CONTROL2_UP_RUN_STALL |
396 + VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD |
397 + VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST);
398 + usleep_range(UP_RESET_SLEEP, UP_RESET_SLEEP * 2);
399 +
400 + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
401 + VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD);
402 +
403 + return 0;
404 +}
405 +
406 +static int aqr_firmware_load_nvmem(struct phy_device *phydev)
407 +{
408 + struct nvmem_cell *cell;
409 + size_t size;
410 + u8 *buf;
411 + int ret;
412 +
413 + cell = nvmem_cell_get(&phydev->mdio.dev, "firmware");
414 + if (IS_ERR(cell))
415 + return PTR_ERR(cell);
416 +
417 + buf = nvmem_cell_read(cell, &size);
418 + if (IS_ERR(buf)) {
419 + ret = PTR_ERR(buf);
420 + goto exit;
421 + }
422 +
423 + ret = aqr_fw_boot(phydev, buf, size, AQR_FW_SRC_NVMEM);
424 + if (ret)
425 + phydev_err(phydev, "firmware loading failed: %d\n", ret);
426 +
427 + kfree(buf);
428 +exit:
429 + nvmem_cell_put(cell);
430 +
431 + return ret;
432 +}
433 +
434 +static int aqr_firmware_load_fs(struct phy_device *phydev)
435 +{
436 + struct device *dev = &phydev->mdio.dev;
437 + const struct firmware *fw;
438 + const char *fw_name;
439 + int ret;
440 +
441 + ret = of_property_read_string(dev->of_node, "firmware-name",
442 + &fw_name);
443 + if (ret)
444 + return ret;
445 +
446 + ret = request_firmware(&fw, fw_name, dev);
447 + if (ret) {
448 + phydev_err(phydev, "failed to find FW file %s (%d)\n",
449 + fw_name, ret);
450 + return ret;
451 + }
452 +
453 + ret = aqr_fw_boot(phydev, fw->data, fw->size, AQR_FW_SRC_FS);
454 + if (ret)
455 + phydev_err(phydev, "firmware loading failed: %d\n", ret);
456 +
457 + release_firmware(fw);
458 +
459 + return ret;
460 +}
461 +
462 +int aqr_firmware_load(struct phy_device *phydev)
463 +{
464 + int ret;
465 +
466 + /* Check if the firmware is not already loaded by pooling
467 + * the current version returned by the PHY. If 0 is returned,
468 + * no firmware is loaded.
469 + */
470 + ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID);
471 + if (ret > 0)
472 + goto exit;
473 +
474 + ret = aqr_firmware_load_nvmem(phydev);
475 + if (!ret)
476 + goto exit;
477 +
478 + ret = aqr_firmware_load_fs(phydev);
479 + if (ret)
480 + return ret;
481 +
482 +exit:
483 + return 0;
484 +}
485 --- a/drivers/net/phy/aquantia/aquantia_main.c
486 +++ b/drivers/net/phy/aquantia/aquantia_main.c
487 @@ -658,11 +658,17 @@ static int aqr107_resume(struct phy_devi
488
489 static int aqr107_probe(struct phy_device *phydev)
490 {
491 + int ret;
492 +
493 phydev->priv = devm_kzalloc(&phydev->mdio.dev,
494 sizeof(struct aqr107_priv), GFP_KERNEL);
495 if (!phydev->priv)
496 return -ENOMEM;
497
498 + ret = aqr_firmware_load(phydev);
499 + if (ret)
500 + return ret;
501 +
502 return aqr_hwmon_probe(phydev);
503 }
504