A race condition was observed when accessing mapped registers, so add locking to protect against concurrent access. Signed-off-by: Shayne Chen <shayne.chen@xxxxxxxxxxxx> --- .../net/wireless/mediatek/mt76/mt7996/mmio.c | 64 ++++++++++++------- .../wireless/mediatek/mt76/mt7996/mt7996.h | 3 +- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c index c50d89a445e9..ff6a335e1fa7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c @@ -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: diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 8154ad37827f..36d1f247d55a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -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; }; -- 2.39.2