]> git.ipfire.org Git - thirdparty/openwrt.git/blob
f0136e57451be2bdaaa23ddfbc1ef21a33fee085
[thirdparty/openwrt.git] /
1 From d96ec0527b0f5618b3a0757b47606705555ee996 Mon Sep 17 00:00:00 2001
2 From: Lei Wei <quic_leiwei@quicinc.com>
3 Date: Mon, 15 Apr 2024 11:06:02 +0800
4 Subject: [PATCH 15/50] net:pcs: Add 10G_QXGMII interface mode support to IPQ
5 UNIPHY PCS driver
6
7 10G_QXGMII is used when PCS connectes with QCA8084 four ports
8 2.5G PHYs.
9
10 Change-Id: If3dc92a07ac3e51f7c9473fb05fa0668617916fb
11 Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
12 ---
13 drivers/net/pcs/pcs-qcom-ipq-uniphy.c | 174 +++++++++++++++++++++-----
14 1 file changed, 142 insertions(+), 32 deletions(-)
15
16 --- a/drivers/net/pcs/pcs-qcom-ipq-uniphy.c
17 +++ b/drivers/net/pcs/pcs-qcom-ipq-uniphy.c
18 @@ -50,6 +50,9 @@
19 #define PCS_CHANNEL_STS_PAUSE_TX_EN BIT(1)
20 #define PCS_CHANNEL_STS_PAUSE_RX_EN BIT(0)
21
22 +#define PCS_QP_USXG_OPTION 0x584
23 +#define PCS_QP_USXG_GMII_SRC_XPCS BIT(0)
24 +
25 #define PCS_PLL_RESET 0x780
26 #define PCS_ANA_SW_RESET BIT(6)
27
28 @@ -65,10 +68,22 @@
29 #define XPCS_10GBASER_LINK_STS BIT(12)
30
31 #define XPCS_DIG_CTRL 0x38000
32 +#define XPCS_SOFT_RESET BIT(15)
33 #define XPCS_USXG_ADPT_RESET BIT(10)
34 #define XPCS_USXG_EN BIT(9)
35
36 +#define XPCS_KR_CTRL 0x38007
37 +#define XPCS_USXG_MODE_MASK GENMASK(12, 10)
38 +#define XPCS_10G_QXGMII_MODE FIELD_PREP(XPCS_USXG_MODE_MASK, 0x5)
39 +
40 +#define XPCS_DIG_STS 0x3800a
41 +#define XPCS_DIG_STS_AM_COUNT GENMASK(14, 0)
42 +
43 +#define XPCS_CHANNEL_DIG_CTRL(x) (0x1a8000 + 0x10000 * ((x) - 1))
44 +#define XPCS_CHANNEL_USXG_ADPT_RESET BIT(5)
45 +
46 #define XPCS_MII_CTRL 0x1f0000
47 +#define XPCS_CHANNEL_MII_CTRL(x) (0x1a0000 + 0x10000 * ((x) - 1))
48 #define XPCS_MII_AN_EN BIT(12)
49 #define XPCS_DUPLEX_FULL BIT(8)
50 #define XPCS_SPEED_MASK (BIT(13) | BIT(6) | BIT(5))
51 @@ -80,9 +95,11 @@
52 #define XPCS_SPEED_10 0
53
54 #define XPCS_MII_AN_CTRL 0x1f8001
55 +#define XPCS_CHANNEL_MII_AN_CTRL(x) (0x1a8001 + 0x10000 * ((x) - 1))
56 #define XPCS_MII_AN_8BIT BIT(8)
57
58 #define XPCS_MII_AN_INTR_STS 0x1f8002
59 +#define XPCS_CHANNEL_MII_AN_INTR_STS(x) (0x1a8002 + 0x10000 * ((x) - 1))
60 #define XPCS_USXG_AN_LINK_STS BIT(14)
61 #define XPCS_USXG_AN_DUPLEX_FULL BIT(13)
62 #define XPCS_USXG_AN_SPEED_MASK GENMASK(12, 10)
63 @@ -93,6 +110,10 @@
64 #define XPCS_USXG_AN_SPEED_5000 5
65 #define XPCS_USXG_AN_SPEED_10000 3
66
67 +#define XPCS_XAUI_MODE_CTRL 0x1f8004
68 +#define XPCS_CHANNEL_XAUI_MODE_CTRL(x) (0x1a8004 + 0x10000 * ((x) - 1))
69 +#define XPCS_TX_IPG_CHECK_DIS BIT(0)
70 +
71 /* UNIPHY PCS RAW clock ID */
72 enum {
73 PCS_RAW_RX_CLK = 0,
74 @@ -153,6 +174,7 @@ struct ipq_uniphy_pcs {
75 struct device *dev;
76 phy_interface_t interface;
77 struct mutex shared_lock; /* Lock to protect shared config */
78 + spinlock_t reg_lock; /* Lock for register access */
79 struct clk *clk[PCS_CLK_MAX];
80 struct reset_control *reset[PCS_RESET_MAX];
81 struct ipq_unipcs_raw_clk raw_clk[PCS_RAW_CLK_MAX];
82 @@ -215,39 +237,55 @@ static const struct clk_ops ipq_unipcs_r
83
84 static u32 ipq_unipcs_reg_read32(struct ipq_uniphy_pcs *qunipcs, u32 reg)
85 {
86 + u32 val;
87 +
88 /* PCS use direct AHB access while XPCS use indirect AHB access */
89 if (reg >= XPCS_INDIRECT_ADDR) {
90 + /* For XPCS, althrough the register is different for different
91 + * channels, but they use the same indirect AHB address to
92 + * access, so add protects here.
93 + */
94 + spin_lock(&qunipcs->reg_lock);
95 +
96 writel(FIELD_GET(XPCS_INDIRECT_ADDR_H, reg),
97 qunipcs->base + XPCS_INDIRECT_AHB_ADDR);
98 - return readl(qunipcs->base + XPCS_INDIRECT_DATA_ADDR(reg));
99 + val = readl(qunipcs->base + XPCS_INDIRECT_DATA_ADDR(reg));
100 +
101 + spin_unlock(&qunipcs->reg_lock);
102 + return val;
103 } else {
104 return readl(qunipcs->base + reg);
105 }
106 }
107
108 -static void ipq_unipcs_reg_write32(struct ipq_uniphy_pcs *qunipcs,
109 - u32 reg, u32 val)
110 +static void ipq_unipcs_reg_modify32(struct ipq_uniphy_pcs *qunipcs,
111 + u32 reg, u32 mask, u32 set)
112 {
113 + u32 val;
114 +
115 if (reg >= XPCS_INDIRECT_ADDR) {
116 + spin_lock(&qunipcs->reg_lock);
117 +
118 + /* XPCS read */
119 writel(FIELD_GET(XPCS_INDIRECT_ADDR_H, reg),
120 qunipcs->base + XPCS_INDIRECT_AHB_ADDR);
121 + val = readl(qunipcs->base + XPCS_INDIRECT_DATA_ADDR(reg));
122 +
123 + val &= ~mask;
124 + val |= set;
125 +
126 + /* XPCS write */
127 writel(val, qunipcs->base + XPCS_INDIRECT_DATA_ADDR(reg));
128 +
129 + spin_unlock(&qunipcs->reg_lock);
130 } else {
131 + val = readl(qunipcs->base + reg);
132 + val &= ~mask;
133 + val |= set;
134 writel(val, qunipcs->base + reg);
135 }
136 }
137
138 -static void ipq_unipcs_reg_modify32(struct ipq_uniphy_pcs *qunipcs,
139 - u32 reg, u32 mask, u32 set)
140 -{
141 - u32 val;
142 -
143 - val = ipq_unipcs_reg_read32(qunipcs, reg);
144 - val &= ~mask;
145 - val |= set;
146 - ipq_unipcs_reg_write32(qunipcs, reg, val);
147 -}
148 -
149 static void ipq_unipcs_get_state_sgmii(struct ipq_uniphy_pcs *qunipcs,
150 int channel,
151 struct phylink_link_state *state)
152 @@ -305,11 +343,15 @@ static void ipq_unipcs_get_state_2500bas
153 }
154
155 static void ipq_unipcs_get_state_usxgmii(struct ipq_uniphy_pcs *qunipcs,
156 + int channel,
157 struct phylink_link_state *state)
158 {
159 - u32 val;
160 + u32 val, reg;
161 +
162 + reg = (channel == 0) ? XPCS_MII_AN_INTR_STS :
163 + XPCS_CHANNEL_MII_AN_INTR_STS(channel);
164
165 - val = ipq_unipcs_reg_read32(qunipcs, XPCS_MII_AN_INTR_STS);
166 + val = ipq_unipcs_reg_read32(qunipcs, reg);
167
168 state->link = !!(val & XPCS_USXG_AN_LINK_STS);
169
170 @@ -415,6 +457,15 @@ static int ipq_unipcs_config_mode(struct
171 PCS_MODE_SEL_MASK,
172 PCS_MODE_XPCS);
173 break;
174 + case PHY_INTERFACE_MODE_10G_QXGMII:
175 + rate = 312500000;
176 + ipq_unipcs_reg_modify32(qunipcs, PCS_MODE_CTRL,
177 + PCS_MODE_SEL_MASK,
178 + PCS_MODE_XPCS);
179 + ipq_unipcs_reg_modify32(qunipcs, PCS_QP_USXG_OPTION,
180 + PCS_QP_USXG_GMII_SRC_XPCS,
181 + PCS_QP_USXG_GMII_SRC_XPCS);
182 + break;
183 default:
184 dev_err(qunipcs->dev,
185 "interface %s not supported\n", phy_modes(interface));
186 @@ -502,35 +553,82 @@ static int ipq_unipcs_config_2500basex(s
187 }
188
189 static int ipq_unipcs_config_usxgmii(struct ipq_uniphy_pcs *qunipcs,
190 + int channel,
191 unsigned int neg_mode,
192 phy_interface_t interface)
193 {
194 int ret;
195 + u32 reg;
196 +
197 + /* Only in-band autoneg mode is supported currently */
198 + if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED)
199 + return -EOPNOTSUPP;
200 +
201 + if (interface == PHY_INTERFACE_MODE_10G_QXGMII)
202 + mutex_lock(&qunipcs->shared_lock);
203
204 if (qunipcs->interface != interface) {
205 ret = ipq_unipcs_config_mode(qunipcs, interface);
206 if (ret)
207 - return ret;
208 + goto err;
209
210 - /* Deassert XPCS and configure XPCS USXGMII */
211 + /* Deassert XPCS and configure XPCS USXGMII or 10G_QXGMII */
212 reset_control_deassert(qunipcs->reset[XPCS_RESET]);
213
214 ipq_unipcs_reg_modify32(qunipcs, XPCS_DIG_CTRL,
215 XPCS_USXG_EN, XPCS_USXG_EN);
216
217 - if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
218 - ipq_unipcs_reg_modify32(qunipcs, XPCS_MII_AN_CTRL,
219 - XPCS_MII_AN_8BIT,
220 - XPCS_MII_AN_8BIT);
221 -
222 - ipq_unipcs_reg_modify32(qunipcs, XPCS_MII_CTRL,
223 - XPCS_MII_AN_EN, XPCS_MII_AN_EN);
224 + if (interface == PHY_INTERFACE_MODE_10G_QXGMII) {
225 + ipq_unipcs_reg_modify32(qunipcs, XPCS_KR_CTRL,
226 + XPCS_USXG_MODE_MASK,
227 + XPCS_10G_QXGMII_MODE);
228 +
229 + /* Set Alignment Marker Interval */
230 + ipq_unipcs_reg_modify32(qunipcs, XPCS_DIG_STS,
231 + XPCS_DIG_STS_AM_COUNT,
232 + 0x6018);
233 +
234 + ipq_unipcs_reg_modify32(qunipcs, XPCS_DIG_CTRL,
235 + XPCS_SOFT_RESET,
236 + XPCS_SOFT_RESET);
237 }
238
239 qunipcs->interface = interface;
240 }
241
242 + if (interface == PHY_INTERFACE_MODE_10G_QXGMII)
243 + mutex_unlock(&qunipcs->shared_lock);
244 +
245 + /* Disable Tx IPG check for 10G_QXGMII */
246 + if (interface == PHY_INTERFACE_MODE_10G_QXGMII) {
247 + reg = (channel == 0) ? XPCS_XAUI_MODE_CTRL :
248 + XPCS_CHANNEL_XAUI_MODE_CTRL(channel);
249 +
250 + ipq_unipcs_reg_modify32(qunipcs, reg,
251 + XPCS_TX_IPG_CHECK_DIS,
252 + XPCS_TX_IPG_CHECK_DIS);
253 + }
254 +
255 + /* Enable autoneg */
256 + reg = (channel == 0) ? XPCS_MII_AN_CTRL :
257 + XPCS_CHANNEL_MII_AN_CTRL(channel);
258 +
259 + ipq_unipcs_reg_modify32(qunipcs, reg,
260 + XPCS_MII_AN_8BIT, XPCS_MII_AN_8BIT);
261 +
262 + reg = (channel == 0) ? XPCS_MII_CTRL :
263 + XPCS_CHANNEL_MII_CTRL(channel);
264 +
265 + ipq_unipcs_reg_modify32(qunipcs, reg,
266 + XPCS_MII_AN_EN, XPCS_MII_AN_EN);
267 +
268 return 0;
269 +
270 +err:
271 + if (interface == PHY_INTERFACE_MODE_10G_QXGMII)
272 + mutex_unlock(&qunipcs->shared_lock);
273 +
274 + return ret;
275 }
276
277 static int ipq_unipcs_config_10gbaser(struct ipq_uniphy_pcs *qunipcs,
278 @@ -638,6 +736,7 @@ ipq_unipcs_link_up_clock_rate_set(struct
279 break;
280 case PHY_INTERFACE_MODE_USXGMII:
281 case PHY_INTERFACE_MODE_10GBASER:
282 + case PHY_INTERFACE_MODE_10G_QXGMII:
283 rate = ipq_unipcs_clock_rate_get_xgmii(speed);
284 break;
285 default:
286 @@ -713,9 +812,10 @@ static void ipq_unipcs_link_up_config_25
287 }
288
289 static void ipq_unipcs_link_up_config_usxgmii(struct ipq_uniphy_pcs *qunipcs,
290 + int channel,
291 int speed)
292 {
293 - u32 val;
294 + u32 val, reg;
295
296 switch (speed) {
297 case SPEED_10000:
298 @@ -744,14 +844,20 @@ static void ipq_unipcs_link_up_config_us
299 val |= XPCS_DUPLEX_FULL;
300
301 /* Config XPCS speed */
302 - ipq_unipcs_reg_modify32(qunipcs, XPCS_MII_CTRL,
303 + reg = (channel == 0) ? XPCS_MII_CTRL : XPCS_CHANNEL_MII_CTRL(channel);
304 + ipq_unipcs_reg_modify32(qunipcs, reg,
305 XPCS_SPEED_MASK | XPCS_DUPLEX_FULL,
306 val);
307
308 /* XPCS adapter reset */
309 - ipq_unipcs_reg_modify32(qunipcs, XPCS_DIG_CTRL,
310 - XPCS_USXG_ADPT_RESET,
311 - XPCS_USXG_ADPT_RESET);
312 + if (channel == 0)
313 + ipq_unipcs_reg_modify32(qunipcs, XPCS_DIG_CTRL,
314 + XPCS_USXG_ADPT_RESET,
315 + XPCS_USXG_ADPT_RESET);
316 + else
317 + ipq_unipcs_reg_modify32(qunipcs, XPCS_CHANNEL_DIG_CTRL(channel),
318 + XPCS_CHANNEL_USXG_ADPT_RESET,
319 + XPCS_CHANNEL_USXG_ADPT_RESET);
320 }
321
322 static int ipq_unipcs_validate(struct phylink_pcs *pcs,
323 @@ -786,7 +892,8 @@ static void ipq_unipcs_get_state(struct
324 ipq_unipcs_get_state_2500basex(qunipcs, channel, state);
325 break;
326 case PHY_INTERFACE_MODE_USXGMII:
327 - ipq_unipcs_get_state_usxgmii(qunipcs, state);
328 + case PHY_INTERFACE_MODE_10G_QXGMII:
329 + ipq_unipcs_get_state_usxgmii(qunipcs, channel, state);
330 break;
331 case PHY_INTERFACE_MODE_10GBASER:
332 ipq_unipcs_get_state_10gbaser(qunipcs, state);
333 @@ -823,7 +930,8 @@ static int ipq_unipcs_config(struct phyl
334 case PHY_INTERFACE_MODE_2500BASEX:
335 return ipq_unipcs_config_2500basex(qunipcs, interface);
336 case PHY_INTERFACE_MODE_USXGMII:
337 - return ipq_unipcs_config_usxgmii(qunipcs,
338 + case PHY_INTERFACE_MODE_10G_QXGMII:
339 + return ipq_unipcs_config_usxgmii(qunipcs, channel,
340 neg_mode, interface);
341 case PHY_INTERFACE_MODE_10GBASER:
342 return ipq_unipcs_config_10gbaser(qunipcs, interface);
343 @@ -865,7 +973,8 @@ static void ipq_unipcs_link_up(struct ph
344 channel, speed);
345 break;
346 case PHY_INTERFACE_MODE_USXGMII:
347 - ipq_unipcs_link_up_config_usxgmii(qunipcs, speed);
348 + case PHY_INTERFACE_MODE_10G_QXGMII:
349 + ipq_unipcs_link_up_config_usxgmii(qunipcs, channel, speed);
350 break;
351 case PHY_INTERFACE_MODE_10GBASER:
352 break;
353 @@ -1082,6 +1191,7 @@ static int ipq_uniphy_probe(struct platf
354 return ret;
355
356 mutex_init(&priv->shared_lock);
357 + spin_lock_init(&priv->reg_lock);
358
359 platform_set_drvdata(pdev, priv);
360