diff options
Diffstat (limited to 'target/linux/generic/backport-5.10/735-v5.14-18-net-dsa-qca8k-dsa-qca8k-protect-MASTER-busy_wait-wit.patch')
-rw-r--r-- | target/linux/generic/backport-5.10/735-v5.14-18-net-dsa-qca8k-dsa-qca8k-protect-MASTER-busy_wait-wit.patch | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/target/linux/generic/backport-5.10/735-v5.14-18-net-dsa-qca8k-dsa-qca8k-protect-MASTER-busy_wait-wit.patch b/target/linux/generic/backport-5.10/735-v5.14-18-net-dsa-qca8k-dsa-qca8k-protect-MASTER-busy_wait-wit.patch new file mode 100644 index 0000000000..b6684d7210 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-18-net-dsa-qca8k-dsa-qca8k-protect-MASTER-busy_wait-wit.patch @@ -0,0 +1,128 @@ +From 60df02b6ea4581d72eb7a3ab7204504a54059b72 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 14 May 2021 23:00:08 +0200 +Subject: [PATCH] net: dsa: qca8k: dsa: qca8k: protect MASTER busy_wait with + mdio mutex + +MDIO_MASTER operation have a dedicated busy wait that is not protected +by the mdio mutex. This can cause situation where the MASTER operation +is done and a normal operation is executed between the MASTER read/write +and the MASTER busy_wait. Rework the qca8k_mdio_read/write function to +address this issue by binding the lock for the whole MASTER operation +and not only the mdio read/write common operation. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 68 +++++++++++++++++++++++++++++++++-------- + 1 file changed, 55 insertions(+), 13 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -628,8 +628,31 @@ qca8k_port_to_phy(int port) + } + + static int ++qca8k_mdio_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) ++{ ++ u16 r1, r2, page; ++ u32 val; ++ int ret; ++ ++ qca8k_split_addr(reg, &r1, &r2, &page); ++ ++ ret = read_poll_timeout(qca8k_mii_read32, val, !(val & mask), 0, ++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, ++ priv->bus, 0x10 | r2, r1); ++ ++ /* Check if qca8k_read has failed for a different reason ++ * before returnting -ETIMEDOUT ++ */ ++ if (ret < 0 && val < 0) ++ return val; ++ ++ return ret; ++} ++ ++static int + qca8k_mdio_write(struct qca8k_priv *priv, int port, u32 regnum, u16 data) + { ++ u16 r1, r2, page; + u32 phy, val; + int ret; + +@@ -645,12 +668,21 @@ qca8k_mdio_write(struct qca8k_priv *priv + QCA8K_MDIO_MASTER_REG_ADDR(regnum) | + QCA8K_MDIO_MASTER_DATA(data); + +- ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val); ++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); ++ ++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv->bus, page); + if (ret) +- return ret; ++ goto exit; ++ ++ qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val); + +- ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_BUSY); ++ ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_BUSY); ++ ++exit: ++ mutex_unlock(&priv->bus->mdio_lock); + + /* even if the busy_wait timeouts try to clear the MASTER_EN */ + qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, +@@ -662,6 +694,7 @@ qca8k_mdio_write(struct qca8k_priv *priv + static int + qca8k_mdio_read(struct qca8k_priv *priv, int port, u32 regnum) + { ++ u16 r1, r2, page; + u32 phy, val; + int ret; + +@@ -676,21 +709,30 @@ qca8k_mdio_read(struct qca8k_priv *priv, + QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | + QCA8K_MDIO_MASTER_REG_ADDR(regnum); + +- ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val); +- if (ret) +- return ret; ++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); ++ ++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); + +- ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_BUSY); ++ ret = qca8k_set_page(priv->bus, page); + if (ret) +- return ret; ++ goto exit; + +- val = qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL); +- if (val < 0) +- return val; ++ qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val); ++ ++ ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_BUSY); ++ if (ret) ++ goto exit; + ++ val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1); + val &= QCA8K_MDIO_MASTER_DATA_MASK; + ++exit: ++ mutex_unlock(&priv->bus->mdio_lock); ++ ++ if (val >= 0) ++ val &= QCA8K_MDIO_MASTER_DATA_MASK; ++ + /* even if the busy_wait timeouts try to clear the MASTER_EN */ + qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, + QCA8K_MDIO_MASTER_EN); |