Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions meta-opencentauri/images/files/sw-description
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ software =
},
{
name = "setargs_mainline";
value = "setenv bootargs console=ttyS0,115200 earlycon=uart8250,mmio32,0x02500000 root=${mmc_root} rootwait consoleblank=0";
value = "setenv bootargs console=ttyS0,115200 earlycon=uart8250,mmio32,0x02500000 root=${mmc_root} rootwait consoleblank=0 usbcore.old_scheme_first=1 usbcore.use_both_schemes=0";
},
{
name = "bootcmd_";
Expand Down Expand Up @@ -144,7 +144,7 @@ software =
},
{
name = "setargs_mainline";
value = "setenv bootargs console=ttyS0,115200 earlycon=uart8250,mmio32,0x02500000 root=${mmc_root} rootwait consoleblank=0";
value = "setenv bootargs console=ttyS0,115200 earlycon=uart8250,mmio32,0x02500000 root=${mmc_root} rootwait consoleblank=0 usbcore.old_scheme_first=1 usbcore.use_both_schemes=0";
},
{
name = "bootcmd_";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ swu_mode=mainline
systemAB_next=A
boot_part=4
mmc_root=/dev/mmcblk0p5
setargs_mainline=setenv bootargs console=ttyS0,115200 earlycon=uart8250,mmio32,0x02500000 root=${mmc_root} rootwait
setargs_mainline=setenv bootargs console=ttyS0,115200 earlycon=uart8250,mmio32,0x02500000 root=${mmc_root} rootwait usbcore.old_scheme_first=1 usbcore.use_both_schemes=0
bootcmd_mainline=run setargs_mainline; fatload mmc 0:${boot_part} 43000000 zImage; fatload mmc 0:${boot_part} 43800000 elegoo-centauri-carbon1.dtb; bootz 43000000 - 43800000
bootcmd=if test x${swu_mode} = xmainline; then run bootcmd_mainline; else run bootcmd_stock; fi
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
From 5c30578f0583ad23dcd9ce39806c8b799ce42ae2 Mon Sep 17 00:00:00 2001
From: OpenCentauri <dev@opencentauri.org>
Date: Sun, 19 Jul 2026 22:30:00 -0400
Subject: [PATCH] usb: quirks: use slow reset for QinHeng 1a86:8091 hub

The Elegoo Centauri Carbon 1 connects its RTL8821CU Wi-Fi module below a
QinHeng 1a86:8091 USB 2.0 hub. The module can fail SET_ADDRESS with
-EPROTO after a warm reboot.

Give devices below this hub an additional 100 ms of recovery time after a
port reset. This uses the existing USB_QUIRK_HUB_SLOW_RESET mechanism and
does not disconnect or power-cycle the hub and its other children.

Upstream-Status: Inappropriate [board-specific workaround pending wider hardware validation]
Signed-off-by: OpenCentauri <dev@opencentauri.org>
---
drivers/usb/core/quirks.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 6cf233837ae7..b7f4c857101c 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -491,6 +491,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Terminus Technology Inc. Hub */
{ USB_DEVICE(0x1a40, 0x0101), .driver_info = USB_QUIRK_HUB_SLOW_RESET },

+ /* QinHeng hub on the Elegoo Centauri Carbon 1 */
+ { USB_DEVICE(0x1a86, 0x8091), .driver_info = USB_QUIRK_HUB_SLOW_RESET },
+
/* Corsair K70 RGB */
{ USB_DEVICE(0x1b1c, 0x1b13), .driver_info = USB_QUIRK_DELAY_INIT |
USB_QUIRK_DELAY_CTRL_MSG },
--
2.50.1

Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ SRC_URI:append:elegoo-centauri-carbon1 = " \
file://0001-dt-bindings-pwm-Add-binding-for-Allwinner-D1-T113-S3.patch \
file://0002-pwm-Add-Allwinner-s-D1-T113-S3-R329-SoCs-PWM-support.patch \
file://0003-riscv-dts-allwinner-d1-Add-pwm-node.patch \
file://0004-usb-quirks-slow-reset-qinheng-8091.patch \
"
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
From 3c651ab09db5ad2000bd437305277fdb11ee6aa9 Mon Sep 17 00:00:00 2001
From: OpenCentauri Developers <dev@opencentauri.org>
Date: Sun, 19 Jul 2026 19:30:00 -0400
Subject: [PATCH] wifi: rtw88: retry failed USB register control messages

The RTL8821CU device can intermittently return -EPROTO during the vendor
control transfers used to start its firmware. A blanket delay after every
successful transfer improves the recurrence interval, but adds about 18
seconds to boot and does not prevent the failure.

Retry only -EPROTO failures, using 5, 20, 80, 320, and 1280 ms waits before
five successive retries. This has no delay on the normal successful path. If
all retries fail, latch the transport as broken so firmware initialization
fails without issuing and logging thousands of additional control requests to
an unresponsive device. The diagnostic includes both the operation and
original register address; this is especially important for the RTL8821C
reg-sec write, whose fixed target register 0x4e0 previously hid the access
that triggered it.

Signed-off-by: OpenCentauri Developers <dev@opencentauri.org>
---
usb.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
usb.h | 3 +++
2 files changed, 69 insertions(+), 12 deletions(-)

diff --git a/usb.c b/usb.c
index 5d3d5a5..cc656d0 100644
--- a/usb.c
+++ b/usb.c
@@ -2,6 +2,7 @@
/* Copyright(c) 2018-2019 Realtek Corporation
*/

+#include <linux/delay.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/mutex.h>
@@ -20,6 +21,56 @@ module_param_named(switch_usb_mode, rtw_switch_usb_mode, bool, 0644);
MODULE_PARM_DESC(switch_usb_mode,
"Set to N to disable switching to USB 3 mode to avoid potential interference in the 2.4 GHz band (default: Y)");

+#define RTW_USB_CTRL_RETRIES 5
+
+static const unsigned int rtw_usb_ctrl_retry_delay_us[] = {
+ 5000,
+ 20000,
+ 80000,
+ 320000,
+ 1280000,
+};
+
+static int rtw_usb_control_msg(struct rtw_dev *rtwdev, unsigned int pipe,
+ u8 request, u8 requesttype, u16 value,
+ u16 index, void *data, u16 size, int timeout,
+ const char *operation, u32 addr)
+{
+ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev);
+ struct usb_device *udev = rtwusb->udev;
+ int attempt;
+ int ret;
+
+ if (atomic_read(&rtwusb->ctrl_broken))
+ return -EPROTO;
+
+ for (attempt = 0; attempt <= RTW_USB_CTRL_RETRIES; attempt++) {
+ ret = usb_control_msg(udev, pipe, request, requesttype, value,
+ index, data, size, timeout);
+ if (ret != -EPROTO)
+ break;
+
+ if (attempt == RTW_USB_CTRL_RETRIES)
+ break;
+
+ usleep_range(rtw_usb_ctrl_retry_delay_us[attempt],
+ rtw_usb_ctrl_retry_delay_us[attempt] + 1000);
+ }
+
+ if (attempt && ret >= 0) {
+ rtw_warn_once(rtwdev,
+ "USB control %s for reg 0x%x recovered after %d retries\n",
+ operation, addr, attempt);
+ } else if (ret == -EPROTO &&
+ atomic_cmpxchg(&rtwusb->ctrl_broken, 0, 1) == 0) {
+ rtw_err(rtwdev,
+ "USB control %s for reg 0x%x failed with %d after %u retries; suppressing further register transfers\n",
+ operation, addr, ret, RTW_USB_CTRL_RETRIES);
+ }
+
+ return ret;
+}
+
#define RTW_USB_MAX_RXQ_LEN 512

struct rtw_usb_txcb {
@@ -60,11 +111,12 @@ static void rtw_usb_reg_sec(struct rtw_dev *rtwdev, u32 addr, __le32 *data)
if (!reg_on_section)
return;

- status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- RTW_USB_CMD_REQ, RTW_USB_CMD_WRITE,
- t_reg, 0, data, t_len, 500);
+ status = rtw_usb_control_msg(rtwdev, usb_sndctrlpipe(udev, 0),
+ RTW_USB_CMD_REQ, RTW_USB_CMD_WRITE,
+ t_reg, 0, data, t_len, 500,
+ "reg-sec write", addr);

- if (status != t_len && status != -ENODEV)
+ if (status != t_len && status != -ENODEV && status != -EPROTO)
rtw_err(rtwdev, "%s: reg 0x%x, usb write %u fail, status: %d\n",
__func__, t_reg, t_len, status);
}
@@ -87,10 +139,11 @@ static u32 rtw_usb_read(struct rtw_dev *rtwdev, u32 addr, u16 len)

data = &rtwusb->usb_data[idx];

- ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
- RTW_USB_CMD_REQ, RTW_USB_CMD_READ, addr,
- RTW_USB_VENQT_CMD_IDX, data, len, 1000);
- if (ret < 0 && ret != -ENODEV && count++ < 4)
+ ret = rtw_usb_control_msg(rtwdev, usb_rcvctrlpipe(udev, 0),
+ RTW_USB_CMD_REQ, RTW_USB_CMD_READ, addr,
+ RTW_USB_VENQT_CMD_IDX, data, len, 1000,
+ "read", addr);
+ if (ret < 0 && ret != -ENODEV && ret != -EPROTO && count++ < 4)
rtw_err(rtwdev, "read register 0x%x failed with %d\n",
addr, ret);

@@ -137,10 +190,11 @@ static void rtw_usb_write(struct rtw_dev *rtwdev, u32 addr, u32 val, int len)

*data = cpu_to_le32(val);

- ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- RTW_USB_CMD_REQ, RTW_USB_CMD_WRITE,
- addr, 0, data, len, 500);
- if (ret < 0 && ret != -ENODEV && count++ < 4)
+ ret = rtw_usb_control_msg(rtwdev, usb_sndctrlpipe(udev, 0),
+ RTW_USB_CMD_REQ, RTW_USB_CMD_WRITE,
+ addr, 0, data, len, 500,
+ "write", addr);
+ if (ret < 0 && ret != -ENODEV && ret != -EPROTO && count++ < 4)
rtw_err(rtwdev, "write register 0x%x failed with %d\n",
addr, ret);

diff --git a/usb.h b/usb.h
index ae0af4f..06e48c4 100644
--- a/usb.h
+++ b/usb.h
@@ -5,6 +5,8 @@
#ifndef __RTW_USB_H_
#define __RTW_USB_H_

+#include <linux/atomic.h>
+
#define FW_8192C_START_ADDRESS 0x1000
#define FW_8192C_END_ADDRESS 0x5fff

@@ -69,6 +71,7 @@ struct rtw_usb {
spinlock_t usb_lock;
__le32 *usb_data;
unsigned int usb_data_index;
+ atomic_t ctrl_broken;

u8 pipe_interrupt;
u8 pipe_in;
--
2.48.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
From 89cf1e6b90e6d663386ddbda7a07d79d09ff036c Mon Sep 17 00:00:00 2001
From: OpenCentauri Developers <dev@opencentauri.org>
Date: Sun, 19 Jul 2026 21:00:00 -0400
Subject: [PATCH] wifi: rtw88: serialize USB vendor control requests

Realtek's 8821cu vendor driver protects all vendor control requests with
usb_vendor_req_mutex. Its source explicitly notes that retries must be
atomically submitted relative to other requests. rtw88 only protects the
shared buffer index; it releases that spinlock before submitting the request,
so register requests and their RTL8821C reg-sec companion can interleave.

Add equivalent serialization. Keep each primary register request and its
0x4e0 reg-sec write in one critical section. Serialize firmware-page control
messages on the same mutex, but do not retry them because the vendor driver
warns that firmware download is checksummed and must be retried as a complete
download rather than as an individual fragment.

Signed-off-by: OpenCentauri Developers <dev@opencentauri.org>
---
usb.c | 16 +++++++++++++++-
usb.h | 3 +++
2 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/usb.c b/usb.c
index 76ee172..b9a5734 100644
--- a/usb.c
+++ b/usb.c
@@ -128,8 +128,11 @@ static u32 rtw_usb_read(struct rtw_dev *rtwdev, u32 addr, u16 len)
__le32 *data;
unsigned long flags;
int idx, ret;
+ u32 value;
static int count;

+ mutex_lock(&rtwusb->ctrl_mutex);
+
spin_lock_irqsave(&rtwusb->usb_lock, flags);

idx = rtwusb->usb_data_index;
@@ -152,7 +155,10 @@ static u32 rtw_usb_read(struct rtw_dev *rtwdev, u32 addr, u16 len)
rtwdev->chip->id == RTW_CHIP_TYPE_8821C)
rtw_usb_reg_sec(rtwdev, addr, data);

- return le32_to_cpu(*data);
+ value = le32_to_cpu(*data);
+ mutex_unlock(&rtwusb->ctrl_mutex);
+
+ return value;
}

static u8 rtw_usb_read8(struct rtw_dev *rtwdev, u32 addr)
@@ -179,6 +185,8 @@ static void rtw_usb_write(struct rtw_dev *rtwdev, u32 addr, u32 val, int len)
int idx, ret;
static int count;

+ mutex_lock(&rtwusb->ctrl_mutex);
+
spin_lock_irqsave(&rtwusb->usb_lock, flags);

idx = rtwusb->usb_data_index;
@@ -202,6 +210,8 @@ static void rtw_usb_write(struct rtw_dev *rtwdev, u32 addr, u32 val, int len)
rtwdev->chip->id == RTW_CHIP_TYPE_8822B ||
rtwdev->chip->id == RTW_CHIP_TYPE_8821C)
rtw_usb_reg_sec(rtwdev, addr, data);
+
+ mutex_unlock(&rtwusb->ctrl_mutex);
}

static void rtw_usb_write8(struct rtw_dev *rtwdev, u32 addr, u8 val)
@@ -254,9 +264,11 @@ static void rtw_usb_write_firmware_page(struct rtw_dev *rtwdev, u32 page,
else
n = 1;

+ mutex_lock(&rtwusb->ctrl_mutex);
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
RTW_USB_CMD_REQ, RTW_USB_CMD_WRITE,
addr, 0, buf, n, 500);
+ mutex_unlock(&rtwusb->ctrl_mutex);
if (ret != n) {
if (ret != -ENODEV)
rtw_err(rtwdev,
@@ -1151,6 +1163,8 @@ static int rtw_usb_intf_init(struct rtw_dev *rtwdev,

SET_IEEE80211_DEV(rtwdev->hw, &intf->dev);
spin_lock_init(&rtwusb->usb_lock);
+ mutex_init(&rtwusb->ctrl_mutex);
+ atomic_set(&rtwusb->ctrl_broken, 0);

return 0;
}
diff --git a/usb.h b/usb.h
index 06e48c4..228e02a 100644
--- a/usb.h
+++ b/usb.h
@@ -7,5 +7,7 @@
#include <linux/atomic.h>

+#include <linux/mutex.h>
+
#define FW_8192C_START_ADDRESS 0x1000
#define FW_8192C_END_ADDRESS 0x5fff

@@ -71,6 +73,7 @@ struct rtw_usb {
spinlock_t usb_lock;
__le32 *usb_data;
unsigned int usb_data_index;
+ struct mutex ctrl_mutex;
atomic_t ctrl_broken;

u8 pipe_interrupt;
--
2.48.1
2 changes: 2 additions & 0 deletions meta-opencentauri/recipes-kernel/rtw88/rtw88_git.bb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ inherit module
SRCREV = "4e777cb6088f95c16ab3da9458ef78db43010d04"
SRC_URI = "git://github.com/lwfinger/rtw88.git;protocol=https;branch=master \
file://0001-disable-led-support.patch \
file://0002-usb-retry-failed-control-messages.patch \
file://0003-usb-serialize-vendor-control-requests.patch \
"

S = "${WORKDIR}/git"
Expand Down
Loading