]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
wifi: rtw88: usb: Upload the firmware in bigger chunks
authorBitterblue Smith <rtl8821cerfe2@gmail.com>
Sat, 10 May 2025 12:22:24 +0000 (15:22 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 27 Jun 2025 10:11:15 +0000 (11:11 +0100)
commit 80fe0bc1659c0ccc79d082e426fa376be5df9c04 upstream.

RTL8811AU stops responding during the firmware download on some systems:

[  809.256440] rtw_8821au 5-2.1:1.0: Firmware version 42.4.0, H2C version 0
[  812.759142] rtw_8821au 5-2.1:1.0 wlp48s0f4u2u1: renamed from wlan0
[  837.315388] rtw_8821au 1-4:1.0: write register 0x1ef4 failed with -110
[  867.524259] rtw_8821au 1-4:1.0: write register 0x1ef8 failed with -110
[  868.930976] rtw_8821au 5-2.1:1.0 wlp48s0f4u2u1: entered promiscuous mode
[  897.730952] rtw_8821au 1-4:1.0: write register 0x1efc failed with -110

Maybe it takes too long when writing the firmware 4 bytes at a time.

Write 196 bytes at a time for RTL8821AU, RTL8811AU, and RTL8812AU,
and 254 bytes at a time for RTL8723DU. These are the sizes used in
their official drivers. Tested with all these chips.

Cc: stable@vger.kernel.org
Link: https://github.com/lwfinger/rtw88/issues/344
Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
Acked-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/43f1daad-3ec0-4a3b-a50c-9cd9eb2c2f52@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/net/wireless/realtek/rtw88/hci.h
drivers/net/wireless/realtek/rtw88/mac.c
drivers/net/wireless/realtek/rtw88/mac.h
drivers/net/wireless/realtek/rtw88/pci.c
drivers/net/wireless/realtek/rtw88/sdio.c
drivers/net/wireless/realtek/rtw88/usb.c

index 96aeda26014e200d02aaae307a9488e0b31fbdb0..d4bee9c3ecfeabee9067e86e8874c865620103fd 100644 (file)
@@ -19,6 +19,8 @@ struct rtw_hci_ops {
        void (*link_ps)(struct rtw_dev *rtwdev, bool enter);
        void (*interface_cfg)(struct rtw_dev *rtwdev);
        void (*dynamic_rx_agg)(struct rtw_dev *rtwdev, bool enable);
+       void (*write_firmware_page)(struct rtw_dev *rtwdev, u32 page,
+                                   const u8 *data, u32 size);
 
        int (*write_data_rsvd_page)(struct rtw_dev *rtwdev, u8 *buf, u32 size);
        int (*write_data_h2c)(struct rtw_dev *rtwdev, u8 *buf, u32 size);
@@ -79,6 +81,12 @@ static inline void rtw_hci_dynamic_rx_agg(struct rtw_dev *rtwdev, bool enable)
                rtwdev->hci.ops->dynamic_rx_agg(rtwdev, enable);
 }
 
+static inline void rtw_hci_write_firmware_page(struct rtw_dev *rtwdev, u32 page,
+                                              const u8 *data, u32 size)
+{
+       rtwdev->hci.ops->write_firmware_page(rtwdev, page, data, size);
+}
+
 static inline int
 rtw_hci_write_data_rsvd_page(struct rtw_dev *rtwdev, u8 *buf, u32 size)
 {
index d1c4f5cdcb21dafdb7b213df03db50e42a25cdb9..efb1da198e74c9c2893bd82ec2fde37dc06b85bd 100644 (file)
@@ -854,8 +854,8 @@ fwdl_ready:
        }
 }
 
-static void
-write_firmware_page(struct rtw_dev *rtwdev, u32 page, const u8 *data, u32 size)
+void rtw_write_firmware_page(struct rtw_dev *rtwdev, u32 page,
+                            const u8 *data, u32 size)
 {
        u32 val32;
        u32 block_nr;
@@ -885,6 +885,7 @@ write_firmware_page(struct rtw_dev *rtwdev, u32 page, const u8 *data, u32 size)
                rtw_write32(rtwdev, write_addr, le32_to_cpu(remain_data));
        }
 }
+EXPORT_SYMBOL(rtw_write_firmware_page);
 
 static int
 download_firmware_legacy(struct rtw_dev *rtwdev, const u8 *data, u32 size)
@@ -902,11 +903,13 @@ download_firmware_legacy(struct rtw_dev *rtwdev, const u8 *data, u32 size)
        rtw_write8_set(rtwdev, REG_MCUFW_CTRL, BIT_FWDL_CHK_RPT);
 
        for (page = 0; page < total_page; page++) {
-               write_firmware_page(rtwdev, page, data, DLFW_PAGE_SIZE_LEGACY);
+               rtw_hci_write_firmware_page(rtwdev, page, data,
+                                           DLFW_PAGE_SIZE_LEGACY);
                data += DLFW_PAGE_SIZE_LEGACY;
        }
        if (last_page_size)
-               write_firmware_page(rtwdev, page, data, last_page_size);
+               rtw_hci_write_firmware_page(rtwdev, page, data,
+                                           last_page_size);
 
        if (!check_hw_ready(rtwdev, REG_MCUFW_CTRL, BIT_FWDL_CHK_RPT, 1)) {
                rtw_err(rtwdev, "failed to check download firmware report\n");
index 58c3dccc14bb512cd9a4f86b0fe9f83c2302ff6d..737c6d5d8da72335aaf62d69e450d1ce9e3ec059 100644 (file)
@@ -32,6 +32,8 @@ void rtw_set_channel_mac(struct rtw_dev *rtwdev, u8 channel, u8 bw,
                         u8 primary_ch_idx);
 int rtw_mac_power_on(struct rtw_dev *rtwdev);
 void rtw_mac_power_off(struct rtw_dev *rtwdev);
+void rtw_write_firmware_page(struct rtw_dev *rtwdev, u32 page,
+                            const u8 *data, u32 size);
 int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw);
 int rtw_mac_init(struct rtw_dev *rtwdev);
 void rtw_mac_flush_queues(struct rtw_dev *rtwdev, u32 queues, bool drop);
index 0b9b8807af2cb0307880fde5927a2b66d43fc418..fab9bb9257dd946798d1e85b881e71dd606ae752 100644 (file)
@@ -12,6 +12,7 @@
 #include "fw.h"
 #include "ps.h"
 #include "debug.h"
+#include "mac.h"
 
 static bool rtw_disable_msi;
 static bool rtw_pci_disable_aspm;
@@ -1602,6 +1603,7 @@ static struct rtw_hci_ops rtw_pci_ops = {
        .link_ps = rtw_pci_link_ps,
        .interface_cfg = rtw_pci_interface_cfg,
        .dynamic_rx_agg = NULL,
+       .write_firmware_page = rtw_write_firmware_page,
 
        .read8 = rtw_pci_read8,
        .read16 = rtw_pci_read16,
index 5b8e88c9759d1282a7212cd347237fa907c080f1..787fa09fd063a9d1fac6ebee3c562cf197f93d3d 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/mmc/host.h>
 #include <linux/mmc/sdio_func.h>
 #include "main.h"
+#include "mac.h"
 #include "debug.h"
 #include "fw.h"
 #include "ps.h"
@@ -1155,6 +1156,7 @@ static struct rtw_hci_ops rtw_sdio_ops = {
        .link_ps = rtw_sdio_link_ps,
        .interface_cfg = rtw_sdio_interface_cfg,
        .dynamic_rx_agg = NULL,
+       .write_firmware_page = rtw_write_firmware_page,
 
        .read8 = rtw_sdio_read8,
        .read16 = rtw_sdio_read16,
index 07695294767acbcfd0a44d4c47c5db06a2a37e79..e0dda272abb515ca37302fe8c8bb51770e1c137e 100644 (file)
@@ -164,6 +164,60 @@ static void rtw_usb_write32(struct rtw_dev *rtwdev, u32 addr, u32 val)
        rtw_usb_write(rtwdev, addr, val, 4);
 }
 
+static void rtw_usb_write_firmware_page(struct rtw_dev *rtwdev, u32 page,
+                                       const u8 *data, u32 size)
+{
+       struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev);
+       struct usb_device *udev = rtwusb->udev;
+       u32 addr = FW_START_ADDR_LEGACY;
+       u8 *data_dup, *buf;
+       u32 n, block_size;
+       int ret;
+
+       switch (rtwdev->chip->id) {
+       case RTW_CHIP_TYPE_8723D:
+               block_size = 254;
+               break;
+       default:
+               block_size = 196;
+               break;
+       }
+
+       data_dup = kmemdup(data, size, GFP_KERNEL);
+       if (!data_dup)
+               return;
+
+       buf = data_dup;
+
+       rtw_write32_mask(rtwdev, REG_MCUFW_CTRL, BIT_ROM_PGE, page);
+
+       while (size > 0) {
+               if (size >= block_size)
+                       n = block_size;
+               else if (size >= 8)
+                       n = 8;
+               else
+                       n = 1;
+
+               ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                                     RTW_USB_CMD_REQ, RTW_USB_CMD_WRITE,
+                                     addr, 0, buf, n, 500);
+               if (ret != n) {
+                       if (ret != -ENODEV)
+                               rtw_err(rtwdev,
+                                       "write 0x%x len %d failed: %d\n",
+                                       addr, n, ret);
+                       break;
+               }
+
+               addr += n;
+               buf += n;
+               size -= n;
+       }
+
+       kfree(data_dup);
+}
+
 static int dma_mapping_to_ep(enum rtw_dma_mapping dma_mapping)
 {
        switch (dma_mapping) {
@@ -815,6 +869,7 @@ static struct rtw_hci_ops rtw_usb_ops = {
        .link_ps = rtw_usb_link_ps,
        .interface_cfg = rtw_usb_interface_cfg,
        .dynamic_rx_agg = rtw_usb_dynamic_rx_agg,
+       .write_firmware_page = rtw_usb_write_firmware_page,
 
        .write8  = rtw_usb_write8,
        .write16 = rtw_usb_write16,