]> git.itanic.dy.fi Git - linux-stable/commitdiff
wifi: mt76: mt7996: add locking for accessing mapped registers
authorShayne Chen <shayne.chen@mediatek.com>
Fri, 26 Jan 2024 09:09:21 +0000 (17:09 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 13 Apr 2024 11:10:00 +0000 (13:10 +0200)
[ Upstream commit 3687854d3e7e7fd760d939dd9e5a3520d5ab60fe ]

A race condition was observed when accessing mapped registers, so add
locking to protect against concurrent access.

Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/net/wireless/mediatek/mt76/mt7996/mmio.c
drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h

index 9f2abfa273c9b060a793ae2594963f5c123fc5b0..efd4a767eb37dbecf93e39d1986b9fefb7dc751e 100644 (file)
@@ -140,7 +140,6 @@ static u32 mt7996_reg_map_l1(struct mt7996_dev *dev, u32 addr)
        u32 offset = FIELD_GET(MT_HIF_REMAP_L1_OFFSET, addr);
        u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, addr);
 
-       dev->reg_l1_backup = dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1);
        dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L1,
                          MT_HIF_REMAP_L1_MASK,
                          FIELD_PREP(MT_HIF_REMAP_L1_MASK, base));
@@ -155,7 +154,6 @@ static u32 mt7996_reg_map_l2(struct mt7996_dev *dev, u32 addr)
        u32 offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET, addr);
        u32 base = FIELD_GET(MT_HIF_REMAP_L2_BASE, addr);
 
-       dev->reg_l2_backup = dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L2);
        dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L2,
                          MT_HIF_REMAP_L2_MASK,
                          FIELD_PREP(MT_HIF_REMAP_L2_MASK, base));
@@ -165,26 +163,10 @@ static u32 mt7996_reg_map_l2(struct mt7996_dev *dev, u32 addr)
        return MT_HIF_REMAP_BASE_L2 + offset;
 }
 
-static void mt7996_reg_remap_restore(struct mt7996_dev *dev)
-{
-       /* remap to ori status */
-       if (unlikely(dev->reg_l1_backup)) {
-               dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L1, dev->reg_l1_backup);
-               dev->reg_l1_backup = 0;
-       }
-
-       if (dev->reg_l2_backup) {
-               dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L2, dev->reg_l2_backup);
-               dev->reg_l2_backup = 0;
-       }
-}
-
 static u32 __mt7996_reg_addr(struct mt7996_dev *dev, u32 addr)
 {
        int i;
 
-       mt7996_reg_remap_restore(dev);
-
        if (addr < 0x100000)
                return addr;
 
@@ -201,6 +183,11 @@ static u32 __mt7996_reg_addr(struct mt7996_dev *dev, u32 addr)
                return dev->reg.map[i].mapped + ofs;
        }
 
+       return 0;
+}
+
+static u32 __mt7996_reg_remap_addr(struct mt7996_dev *dev, u32 addr)
+{
        if ((addr >= MT_INFRA_BASE && addr < MT_WFSYS0_PHY_START) ||
            (addr >= MT_WFSYS0_PHY_START && addr < MT_WFSYS1_PHY_START) ||
            (addr >= MT_WFSYS1_PHY_START && addr <= MT_WFSYS1_PHY_END))
@@ -225,28 +212,60 @@ void mt7996_memcpy_fromio(struct mt7996_dev *dev, void *buf, u32 offset,
 {
        u32 addr = __mt7996_reg_addr(dev, offset);
 
-       memcpy_fromio(buf, dev->mt76.mmio.regs + addr, len);
+       if (addr) {
+               memcpy_fromio(buf, dev->mt76.mmio.regs + addr, len);
+               return;
+       }
+
+       spin_lock_bh(&dev->reg_lock);
+       memcpy_fromio(buf, dev->mt76.mmio.regs +
+                          __mt7996_reg_remap_addr(dev, offset), len);
+       spin_unlock_bh(&dev->reg_lock);
 }
 
 static u32 mt7996_rr(struct mt76_dev *mdev, u32 offset)
 {
        struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+       u32 addr = __mt7996_reg_addr(dev, offset), val;
+
+       if (addr)
+               return dev->bus_ops->rr(mdev, addr);
 
-       return dev->bus_ops->rr(mdev, __mt7996_reg_addr(dev, offset));
+       spin_lock_bh(&dev->reg_lock);
+       val = dev->bus_ops->rr(mdev, __mt7996_reg_remap_addr(dev, offset));
+       spin_unlock_bh(&dev->reg_lock);
+
+       return val;
 }
 
 static void mt7996_wr(struct mt76_dev *mdev, u32 offset, u32 val)
 {
        struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+       u32 addr = __mt7996_reg_addr(dev, offset);
 
-       dev->bus_ops->wr(mdev, __mt7996_reg_addr(dev, offset), val);
+       if (addr) {
+               dev->bus_ops->wr(mdev, addr, val);
+               return;
+       }
+
+       spin_lock_bh(&dev->reg_lock);
+       dev->bus_ops->wr(mdev, __mt7996_reg_remap_addr(dev, offset), val);
+       spin_unlock_bh(&dev->reg_lock);
 }
 
 static u32 mt7996_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val)
 {
        struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+       u32 addr = __mt7996_reg_addr(dev, offset);
+
+       if (addr)
+               return dev->bus_ops->rmw(mdev, addr, mask, val);
+
+       spin_lock_bh(&dev->reg_lock);
+       val = dev->bus_ops->rmw(mdev, __mt7996_reg_remap_addr(dev, offset), mask, val);
+       spin_unlock_bh(&dev->reg_lock);
 
-       return dev->bus_ops->rmw(mdev, __mt7996_reg_addr(dev, offset), mask, val);
+       return val;
 }
 
 #ifdef CONFIG_NET_MEDIATEK_SOC_WED
@@ -421,6 +440,7 @@ static int mt7996_mmio_init(struct mt76_dev *mdev,
 
        dev = container_of(mdev, struct mt7996_dev, mt76);
        mt76_mmio_init(&dev->mt76, mem_base);
+       spin_lock_init(&dev->reg_lock);
 
        switch (device_id) {
        case 0x7990:
index 8154ad37827f0871a9793df4a7f19a20b4fb8fe7..36d1f247d55aa12745c8950f3a10b04ad4fe3912 100644 (file)
@@ -325,8 +325,7 @@ struct mt7996_dev {
                u8 n_agrt;
        } twt;
 
-       u32 reg_l1_backup;
-       u32 reg_l2_backup;
+       spinlock_t reg_lock;
 
        u8 wtbl_size_group;
 };