[PATCH v4 5/5] net: phy: aquantia: use generic firmware loader
Beiyan Yun
root at infi.wang
Fri Oct 31 17:09:56 CET 2025
Hi Daniel,
> On 31 Oct 2025, at 11:41 PM, Daniel Golle <daniel at makrotopia.org> wrote:
>
> On Fri, Oct 31, 2025 at 11:21:07PM +0800, Beiyan Yun wrote:
>> Aquantia PHYs are being used w/o SPI flash in some routers recently.
>> Current firmware loader only attempts to load from FS on top of MMC,
>> limiting the use on many devices.
>>
>> Removed the old firmware loader, migrate to generic firmware loader to
>> allow a wider range and runtime override of firmware source. (e.g., USB).
>>
>> Tested on Buffalo WXR18000BE10P with UBIFS.
>
> Does the Buffalo WXR18000BE10P use UBIFS as rootfs out-of-the-box, and
> include the Aquatia firmware there?
>
No, it’s part of my WIP porting and ubootmod, as a) vendor layout is (unfortunately) UBI on NMBM, and b) for some hardware and/or Linux quirks I can’t make one of the two CUX3410 work without a soft PHY reset. Bring it up in U-Boot not only solves this, but also gives us two more usable port in U-Boot as a bonus.
I opened a OpenWrt forum thread at https://forum.openwrt.org/t/adding-support-for-buffalo-wxr18000be10p-mediatek-mt7988a/239795 if you’re interested.
> I'm asking because most of the devices supported in OpenWrt don't use
> UBIFS on UBI but typically use a squashfs volume, or even a squashfs
> filesystem or cpio.gz embedded into the uImage.FIT which is stored
> directly on a UBI volume (and not as a file within UBIFS).
>
I agree, UBIFS here is more about PoC, and a single `firmware` UBI part is comparable to similar implementation like some Airoha board's `en8811-fw`. We might read from overlay, though that could be too complicated in U-Boot?
> Hence it would be crucial to make sure that using
> request_firmware_into_buf_via_script()
> works for those devices which do not store the firmware on a filesystem
> which is directly accessible within U-Boot.
> Imho that's also better than using a __weak symbol as it would allow
> implementing the board-specific method for acquiring the firmware as a
> script rather than having to write C code for each board.
>
FW_LOADER support (request_firmware_into_buf_via_script) is available since patch v2 ;)
>>
>> Signed-off-by: Beiyan Yun <root at infi.wang>
>> ---
>>
>> Changes in v4:
>> - Split firmware upload helpers change
>> - Reorder `aquantia_read_fw`
>> - Make `aquantia_read_fw` weak to allow overide
>> - Rename exit label in `aquantia_read_fw`
>> - Kconfig polish
>>
>> Changes in v3:
>> - Select FW_LOADER with PHY_AQUANTIA_UPLOAD_FW
>>
>> Changes in v2:
>> - Add support for script based loader
>>
>> drivers/net/phy/Kconfig | 28 +++++----
>> drivers/net/phy/aquantia.c | 122 ++++++++++++++++++++++---------------
>> 2 files changed, 91 insertions(+), 59 deletions(-)
>>
>> diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
>> index 018be98705a..4a74a0d4e8c 100644
>> --- a/drivers/net/phy/Kconfig
>> +++ b/drivers/net/phy/Kconfig
>> @@ -1,4 +1,3 @@
>> -
>> config BITBANGMII
>> bool "Bit-banged ethernet MII management channel support"
>>
>> @@ -91,23 +90,30 @@ menuconfig PHY_AQUANTIA
>> config PHY_AQUANTIA_UPLOAD_FW
>> bool "Aquantia firmware loading support"
>> depends on PHY_AQUANTIA
>> + select FS_LOADER
>> + select FW_LOADER
>> help
>> - Aquantia PHYs use firmware which can be either loaded automatically
>> - from storage directly attached to the phy or loaded by the boot loader
>> - via MDIO commands. The firmware is loaded from a file, specified by
>> - the PHY_AQUANTIA_FW_PART and PHY_AQUANTIA_FW_NAME options.
>> + Aquantia PHYs use firmware which can be either loaded automatically
>> + from storage directly attached to the phy or loaded by the boot loader
>> + via MDIO commands.
>> +
>> + This option enables loading the firmware using the generic
>> + firmware loader framework.
>>
>> -config PHY_AQUANTIA_FW_PART
>> - string "Aquantia firmware partition"
>> +config PHY_AQUANTIA_FW_MAX_SIZE
>> + hex "Max firmware size"
>> depends on PHY_AQUANTIA_UPLOAD_FW
>> + default 0x80000
>> help
>> - Partition containing the firmware file.
>> + The maximum size of the Aquantia PHY firmware. This is used to
>> + allocate a buffer to load the firmware into.
>>
>> -config PHY_AQUANTIA_FW_NAME
>> - string "Aquantia firmware filename"
>> +config PHY_AQUANTIA_FW_LOADER_SCRIPT
>> + string "Aquantia firmware loader script"
>> depends on PHY_AQUANTIA_UPLOAD_FW
>> + default "aqr_phy_load_firmware"
>> help
>> - Firmware filename.
>> + The firmware loading script variable name.
>>
>> config PHY_ATHEROS
>> bool "Atheros Ethernet PHYs support"
>> diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c
>> index 461d4b07a40..934bd17eadd 100644
>> --- a/drivers/net/phy/aquantia.c
>> +++ b/drivers/net/phy/aquantia.c
>> @@ -17,6 +17,10 @@
>> #include <malloc.h>
>> #include <asm/byteorder.h>
>> #include <fs.h>
>> +#if (IS_ENABLED(CONFIG_PHY_AQUANTIA_UPLOAD_FW))
>> +#include <fs_loader.h>
>> +#include <fw_loader.h>
>> +#endif
>>
>> #define AQUNTIA_10G_CTL 0x20
>> #define AQUNTIA_VENDOR_P1 0xc400
>> @@ -127,51 +131,67 @@ struct fw_header {
>>
>> #pragma pack()
>>
>> -#if defined(CONFIG_PHY_AQUANTIA_UPLOAD_FW)
>> -static int aquantia_read_fw(u8 **fw_addr, size_t *fw_length)
>> +#if (IS_ENABLED(CONFIG_PHY_AQUANTIA_UPLOAD_FW))
>> +int __weak aquantia_read_fw(struct phy_device *phydev, u8 **fw_addr,
>> + size_t *fw_length)
>> {
>> - loff_t length, read;
>> int ret;
>> - void *addr = NULL;
>> -
>> - *fw_addr = NULL;
>> - *fw_length = 0;
>> - debug("Loading Aquantia microcode from %s %s\n",
>> - CONFIG_PHY_AQUANTIA_FW_PART, CONFIG_PHY_AQUANTIA_FW_NAME);
>> - ret = fs_set_blk_dev("mmc", CONFIG_PHY_AQUANTIA_FW_PART, FS_TYPE_ANY);
>> - if (ret < 0)
>> - goto cleanup;
>> -
>> - ret = fs_size(CONFIG_PHY_AQUANTIA_FW_NAME, &length);
>> - if (ret < 0)
>> - goto cleanup;
>> -
>> - addr = malloc(length);
>> - if (!addr) {
>> - ret = -ENOMEM;
>> - goto cleanup;
>> + ofnode node;
>> + struct udevice *loader_dev;
>> + const char *fw_name;
>> + u8 *tmp_fw_addr;
>> + size_t tmp_fw_length;
>> +
>> + tmp_fw_addr = malloc(CONFIG_PHY_AQUANTIA_FW_MAX_SIZE);
>> + if (!tmp_fw_addr) {
>> + printf("Failed to allocate memory for firmware\n");
>> + return -ENOMEM;
>> }
>>
>> - ret = fs_set_blk_dev("mmc", CONFIG_PHY_AQUANTIA_FW_PART, FS_TYPE_ANY);
>> - if (ret < 0)
>> - goto cleanup;
>> -
>> - ret = fs_read(CONFIG_PHY_AQUANTIA_FW_NAME, (ulong)addr, 0, length,
>> - &read);
>> - if (ret < 0)
>> - goto cleanup;
>> -
>> - *fw_addr = addr;
>> - *fw_length = length;
>> - debug("Found Aquantia microcode.\n");
>> -
>> -cleanup:
>> - if (ret < 0) {
>> - printf("loading firmware file %s %s failed with error %d\n",
>> - CONFIG_PHY_AQUANTIA_FW_PART, CONFIG_PHY_AQUANTIA_FW_NAME,
>> - ret);
>> - free(addr);
>> + /* First, try to load firmware via script */
>> + ret = request_firmware_into_buf_via_script(
>> + tmp_fw_addr, CONFIG_PHY_AQUANTIA_FW_MAX_SIZE,
>> + CONFIG_PHY_AQUANTIA_FW_LOADER_SCRIPT, &tmp_fw_length);
>> + if (ret) {
>> + /* Fallback to DT specified firmware */
>> + node = phy_get_ofnode(phydev);
>> + if (!ofnode_valid(node)) {
>> + printf("Failed to get PHY node\n");
>> + ret = -EINVAL;
>> + goto fail_free;
>> + }
>> +
>> + fw_name = ofnode_read_string(node, "firmware-name");
>> + if (!fw_name) {
>> + printf("Failed to get firmware name\n");
>> + ret = -ENOENT;
>> + goto fail_free;
>> + }
>> +
>> + ret = get_fs_loader(&loader_dev);
>> + if (ret) {
>> + printf("Failed to get fs_loader instance: %d\n", ret);
>> + goto fail_free;
>> + }
>> +
>> + ret = request_firmware_into_buf(loader_dev, fw_name,
>> + tmp_fw_addr,
>> + CONFIG_PHY_AQUANTIA_FW_MAX_SIZE,
>> + 0);
>> + if (ret < 0) {
>> + printf("Failed to load firmware %s: %d\n", fw_name,
>> + ret);
>> + goto fail_free;
>> + }
>> + tmp_fw_length = ret;
>> }
>> +
>> + *fw_addr = tmp_fw_addr;
>> + *fw_length = tmp_fw_length;
>> + return 1;
>> +
>> +fail_free:
>> + free(tmp_fw_addr);
>> return ret;
>> }
>>
>> @@ -227,6 +247,11 @@ static int aquantia_do_upload_firmware(struct phy_device *phydev,
>> u32 primary_offset, iram_offset, iram_size, dram_offset, dram_size;
>> const struct fw_header *header;
>>
>> + if (!addr || !fw_length) {
>> + printf("%s: Invalid firmware data\n", phydev->dev->name);
>> + return -EINVAL;
>> + }
>> +
>> read_crc = (addr[fw_length - 2] << 8) | addr[fw_length - 1];
>> calculated_crc = crc16_ccitt(0, addr, fw_length - 2);
>> if (read_crc != calculated_crc) {
>> @@ -290,17 +315,18 @@ static int aquantia_do_upload_firmware(struct phy_device *phydev,
>>
>> static int aquantia_upload_firmware(struct phy_device *phydev)
>> {
>> - int ret;
>> - u8 *addr = NULL;
>> - size_t fw_length = 0;
>> + int ret, fwrc;
>> + u8 *fw_addr = NULL;
>> + size_t fw_length;
>>
>> - ret = aquantia_read_fw(&addr, &fw_length);
>> - if (ret != 0)
>> - return ret;
>> + fwrc = aquantia_read_fw(phydev, &fw_addr, &fw_length);
>> + if (fwrc < 0)
>> + return fwrc;
>>
>> - ret = aquantia_do_upload_firmware(phydev, addr, fw_length);
>> + ret = aquantia_do_upload_firmware(phydev, fw_addr, fw_length);
>>
>> - free(addr);
>> + if (fwrc > 0)
>> + free(fw_addr);
>>
>> return ret;
>> }
>> --
>> 2.47.3
>>
More information about the U-Boot
mailing list