b6684d721023f35799c18fa412328b63506bd222
[openwrt/openwrt.git] /
1 From 60df02b6ea4581d72eb7a3ab7204504a54059b72 Mon Sep 17 00:00:00 2001
2 From: Ansuel Smith <ansuelsmth@gmail.com>
3 Date: Fri, 14 May 2021 23:00:08 +0200
4 Subject: [PATCH] net: dsa: qca8k: dsa: qca8k: protect MASTER busy_wait with
5 mdio mutex
6
7 MDIO_MASTER operation have a dedicated busy wait that is not protected
8 by the mdio mutex. This can cause situation where the MASTER operation
9 is done and a normal operation is executed between the MASTER read/write
10 and the MASTER busy_wait. Rework the qca8k_mdio_read/write function to
11 address this issue by binding the lock for the whole MASTER operation
12 and not only the mdio read/write common operation.
13
14 Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
15 Reviewed-by: Andrew Lunn <andrew@lunn.ch>
16 Signed-off-by: David S. Miller <davem@davemloft.net>
17 ---
18 drivers/net/dsa/qca8k.c | 68 +++++++++++++++++++++++++++++++++--------
19 1 file changed, 55 insertions(+), 13 deletions(-)
20
21 --- a/drivers/net/dsa/qca8k.c
22 +++ b/drivers/net/dsa/qca8k.c
23 @@ -628,8 +628,31 @@ qca8k_port_to_phy(int port)
24 }
25
26 static int
27 +qca8k_mdio_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
28 +{
29 + u16 r1, r2, page;
30 + u32 val;
31 + int ret;
32 +
33 + qca8k_split_addr(reg, &r1, &r2, &page);
34 +
35 + ret = read_poll_timeout(qca8k_mii_read32, val, !(val & mask), 0,
36 + QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
37 + priv->bus, 0x10 | r2, r1);
38 +
39 + /* Check if qca8k_read has failed for a different reason
40 + * before returnting -ETIMEDOUT
41 + */
42 + if (ret < 0 && val < 0)
43 + return val;
44 +
45 + return ret;
46 +}
47 +
48 +static int
49 qca8k_mdio_write(struct qca8k_priv *priv, int port, u32 regnum, u16 data)
50 {
51 + u16 r1, r2, page;
52 u32 phy, val;
53 int ret;
54
55 @@ -645,12 +668,21 @@ qca8k_mdio_write(struct qca8k_priv *priv
56 QCA8K_MDIO_MASTER_REG_ADDR(regnum) |
57 QCA8K_MDIO_MASTER_DATA(data);
58
59 - ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
60 + qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
61 +
62 + mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
63 +
64 + ret = qca8k_set_page(priv->bus, page);
65 if (ret)
66 - return ret;
67 + goto exit;
68 +
69 + qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val);
70
71 - ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
72 - QCA8K_MDIO_MASTER_BUSY);
73 + ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
74 + QCA8K_MDIO_MASTER_BUSY);
75 +
76 +exit:
77 + mutex_unlock(&priv->bus->mdio_lock);
78
79 /* even if the busy_wait timeouts try to clear the MASTER_EN */
80 qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
81 @@ -662,6 +694,7 @@ qca8k_mdio_write(struct qca8k_priv *priv
82 static int
83 qca8k_mdio_read(struct qca8k_priv *priv, int port, u32 regnum)
84 {
85 + u16 r1, r2, page;
86 u32 phy, val;
87 int ret;
88
89 @@ -676,21 +709,30 @@ qca8k_mdio_read(struct qca8k_priv *priv,
90 QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
91 QCA8K_MDIO_MASTER_REG_ADDR(regnum);
92
93 - ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val);
94 - if (ret)
95 - return ret;
96 + qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
97 +
98 + mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
99
100 - ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
101 - QCA8K_MDIO_MASTER_BUSY);
102 + ret = qca8k_set_page(priv->bus, page);
103 if (ret)
104 - return ret;
105 + goto exit;
106
107 - val = qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL);
108 - if (val < 0)
109 - return val;
110 + qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val);
111 +
112 + ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL,
113 + QCA8K_MDIO_MASTER_BUSY);
114 + if (ret)
115 + goto exit;
116
117 + val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1);
118 val &= QCA8K_MDIO_MASTER_DATA_MASK;
119
120 +exit:
121 + mutex_unlock(&priv->bus->mdio_lock);
122 +
123 + if (val >= 0)
124 + val &= QCA8K_MDIO_MASTER_DATA_MASK;
125 +
126 /* even if the busy_wait timeouts try to clear the MASTER_EN */
127 qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
128 QCA8K_MDIO_MASTER_EN);