[PATCH AUTOSEL 6.18-5.10] wifi: mt76: mmio_*_copy fix byte order and alignment
Sasha Levin
sashal at kernel.org
Mon Dec 8 16:15:04 PST 2025
From: Caleb James DeLisle <cjd at cjdns.fr>
[ Upstream commit 2df00805f7dbaa46b60c682aad0d76270b7ba266 ]
Update functions which copy to and from MMIO to load bytes as Little
Endian, and also support unaligned buffers.
PCI devices almost universally use Little Endian ordering for MMIO
registers, mt76 is no exception. PCI hardware that is designed to work
with Big Endian CPUs often (but not always) "helps" by transparently
byte-swapping MMIO reads and writes on the wire. If this is enabled
then it cannot be turned off for a single write. On hardware which does
not support this, writel() does the swap in software. When we are
transferring arbitrary bytes to MMIO space, we need them to arrive in
the same order they were in memory, so when the hardware swaps them
this is a problem. Rather than care about how our PCI host controller
works, we instead load bytes as Little Endian - so on a Big Endian
machine this will reverse them, then we use writel() which will put
them back in the right order again. This way we do not make it our
business whether the swapping is done in software or hardware.
Furthermore, inspection of the code shows that these functions are
often called with stack-allocated u8 arrays which have no alignment
guarantees so we now use (get|put)_unaligned_le32().
Fixes this issue:
mt76x2e 0000:02:00.0: ROM patch build: 20141115060606a
mt76x2e 0000:02:00.0: Firmware Version: 0.0.00
mt76x2e 0000:02:00.0: Build: 1
mt76x2e 0000:02:00.0: Build Time: 201607111443____
mt76x2e 0000:02:00.0: Firmware failed to start
mt76x2e 0000:02:00.0: probe with driver mt76x2e failed with error -145
Tested on:
SmartFiber XP8421-B (Big Endian MIPS 34Kc)
- MT7612 -> 5g / ap / psk2
- MT7603 -> 2g / sta / psk2
- MT7603 -> 2g / ap / psk2
TpLink Archer v1200v-v2 (Big Endian MIPS 34Kc)
- MT7613 -> 5g / ap / psk2
- MT7603 -> 2g / sta / psk2
Signed-off-by: Caleb James DeLisle <cjd at cjdns.fr>
Link: https://patch.msgid.link/20251029184143.3991388-1-cjd@cjdns.fr
Signed-off-by: Felix Fietkau <nbd at nbd.name>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
LLM Generated explanations, may be completely bogus:
## ANALYSIS SUMMARY
### 1. COMMIT MESSAGE ANALYSIS
**Bug Description:** The commit fixes two issues in mt76 WiFi driver's
MMIO copy functions:
1. **Byte order issue on Big Endian systems:** The original code uses
`__iowrite32_copy()` and `__ioread32_copy()` which internally use
`__raw_writel()`/`__raw_readl()` - these do NOT perform byte swapping
2. **Unaligned buffer issue:** Stack-allocated u8 arrays may not be
4-byte aligned
**User-visible failure:** Complete driver probe failure with "Firmware
failed to start" error -145 on Big Endian systems. WiFi is totally non-
functional.
**Testing:** Tested on real hardware - multiple MT76 devices on Big
Endian MIPS platforms (SmartFiber, TP-Link Archer).
**Notable:** No `Fixes:` or `Cc: stable` tags present.
### 2. CODE CHANGE ANALYSIS
**Root cause:** The generic `__iowrite32_copy()` function (in
`lib/iomap_copy.c:20-28`) uses `__raw_writel()` which writes bytes in
host order without endian conversion. On Big Endian systems, bytes
arrive at the PCI device in the wrong order.
**The fix:**
- Replaces `__iowrite32_copy()` with explicit loop using `writel()` +
`get_unaligned_le32()`
- Replaces `__ioread32_copy()` with explicit loop using `readl()` +
`put_unaligned_le32()`
**Why it's correct:**
1. `get_unaligned_le32()` reads 4 bytes interpreting them as little-
endian (safe for unaligned access)
2. `writel()` handles endian conversion (host→LE for PCI wire format)
3. Combined: bytes arrive at hardware in correct order regardless of
host endianness
4. On Little Endian systems: behavior is functionally equivalent (no
regression risk)
**Header dependency:** `<linux/unaligned.h>` is included transitively
via `mac80211.h` → `mt76.h` → `mmio.c`
### 3. CLASSIFICATION
- **Type:** Bug fix (not a feature)
- **Severity:** HIGH - Complete driver failure on affected systems
- **Scope:** mt76 WiFi devices on Big Endian architectures (MIPS
routers, embedded systems)
- **Security:** Not a security issue
### 4. SCOPE AND RISK ASSESSMENT
| Factor | Assessment |
|--------|------------|
| Lines changed | ~10 lines (very small) |
| Files affected | 1 file (mmio.c) |
| Complexity | Low - uses standard kernel APIs |
| Risk of regression | Low - functionally equivalent on LE systems |
| Self-contained | Yes - no dependencies on other commits |
### 5. USER IMPACT
- **Affected users:** Big Endian systems with mt76 WiFi (OpenWrt
routers, embedded MIPS devices)
- **Impact severity:** CRITICAL for affected users - WiFi doesn't work
at all
- **Impact scope:** Minority use case (BE systems are uncommon)
### 6. STABILITY INDICATORS
- ✅ Signed off by subsystem maintainer (Felix Fietkau)
- ✅ Tested on multiple real hardware platforms
- ✅ Uses well-established kernel primitives
- ⚠️ No Reviewed-by tags from others
- ⚠️ No explicit stable request
### 7. DEPENDENCY CHECK
- ✅ Self-contained patch
- ✅ No prerequisite commits needed
- ✅ Code path exists in all stable trees with mt76 (since 2019)
## VERDICT
**Arguments FOR backporting:**
- Fixes a complete driver failure that makes WiFi unusable on Big Endian
systems
- Small, surgical fix (~10 lines) with well-defined scope
- Uses standard, well-tested kernel APIs (`writel`,
`get_unaligned_le32`)
- Self-contained with no dependencies
- Accepted by maintainer
- Functionally equivalent on Little Endian systems (no regression risk)
**Arguments AGAINST backporting:**
- No explicit `Cc: stable` tag from maintainer
- No `Fixes:` tag indicating when bug was introduced
- Big Endian systems are a minority use case
**Conclusion:** Despite the lack of explicit stable tagging, this commit
fixes a severe, user-visible bug (complete driver failure) with a small,
obviously correct fix using standard kernel patterns. The risk is low
and the benefit is high for affected users. Big Endian MIPS platforms
with mt76 WiFi (common in OpenWrt/embedded space) deserve working WiFi
support.
**YES**
drivers/net/wireless/mediatek/mt76/mmio.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mmio.c b/drivers/net/wireless/mediatek/mt76/mmio.c
index cd2e9737c3bf9..865ec910f05a4 100644
--- a/drivers/net/wireless/mediatek/mt76/mmio.c
+++ b/drivers/net/wireless/mediatek/mt76/mmio.c
@@ -33,13 +33,21 @@ static u32 mt76_mmio_rmw(struct mt76_dev *dev, u32 offset, u32 mask, u32 val)
static void mt76_mmio_write_copy(struct mt76_dev *dev, u32 offset,
const void *data, int len)
{
- __iowrite32_copy(dev->mmio.regs + offset, data, DIV_ROUND_UP(len, 4));
+ int i;
+
+ for (i = 0; i < ALIGN(len, 4); i += 4)
+ writel(get_unaligned_le32(data + i),
+ dev->mmio.regs + offset + i);
}
static void mt76_mmio_read_copy(struct mt76_dev *dev, u32 offset,
void *data, int len)
{
- __ioread32_copy(data, dev->mmio.regs + offset, DIV_ROUND_UP(len, 4));
+ int i;
+
+ for (i = 0; i < ALIGN(len, 4); i += 4)
+ put_unaligned_le32(readl(dev->mmio.regs + offset + i),
+ data + i);
}
static int mt76_mmio_wr_rp(struct mt76_dev *dev, u32 base,
--
2.51.0
More information about the linux-arm-kernel
mailing list