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
7 10G_QXGMII is used when PCS connectes with QCA8084 four ports
10 Change-Id: If3dc92a07ac3e51f7c9473fb05fa0668617916fb
11 Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
13 drivers/net/pcs/pcs-qcom-ipq-uniphy.c | 174 +++++++++++++++++++++-----
14 1 file changed, 142 insertions(+), 32 deletions(-)
16 diff --git a/drivers/net/pcs/pcs-qcom-ipq-uniphy.c b/drivers/net/pcs/pcs-qcom-ipq-uniphy.c
17 index 820d197744e8..a98180c91632 100644
18 --- a/drivers/net/pcs/pcs-qcom-ipq-uniphy.c
19 +++ b/drivers/net/pcs/pcs-qcom-ipq-uniphy.c
21 #define PCS_CHANNEL_STS_PAUSE_TX_EN BIT(1)
22 #define PCS_CHANNEL_STS_PAUSE_RX_EN BIT(0)
24 +#define PCS_QP_USXG_OPTION 0x584
25 +#define PCS_QP_USXG_GMII_SRC_XPCS BIT(0)
27 #define PCS_PLL_RESET 0x780
28 #define PCS_ANA_SW_RESET BIT(6)
31 #define XPCS_10GBASER_LINK_STS BIT(12)
33 #define XPCS_DIG_CTRL 0x38000
34 +#define XPCS_SOFT_RESET BIT(15)
35 #define XPCS_USXG_ADPT_RESET BIT(10)
36 #define XPCS_USXG_EN BIT(9)
38 +#define XPCS_KR_CTRL 0x38007
39 +#define XPCS_USXG_MODE_MASK GENMASK(12, 10)
40 +#define XPCS_10G_QXGMII_MODE FIELD_PREP(XPCS_USXG_MODE_MASK, 0x5)
42 +#define XPCS_DIG_STS 0x3800a
43 +#define XPCS_DIG_STS_AM_COUNT GENMASK(14, 0)
45 +#define XPCS_CHANNEL_DIG_CTRL(x) (0x1a8000 + 0x10000 * ((x) - 1))
46 +#define XPCS_CHANNEL_USXG_ADPT_RESET BIT(5)
48 #define XPCS_MII_CTRL 0x1f0000
49 +#define XPCS_CHANNEL_MII_CTRL(x) (0x1a0000 + 0x10000 * ((x) - 1))
50 #define XPCS_MII_AN_EN BIT(12)
51 #define XPCS_DUPLEX_FULL BIT(8)
52 #define XPCS_SPEED_MASK (BIT(13) | BIT(6) | BIT(5))
54 #define XPCS_SPEED_10 0
56 #define XPCS_MII_AN_CTRL 0x1f8001
57 +#define XPCS_CHANNEL_MII_AN_CTRL(x) (0x1a8001 + 0x10000 * ((x) - 1))
58 #define XPCS_MII_AN_8BIT BIT(8)
60 #define XPCS_MII_AN_INTR_STS 0x1f8002
61 +#define XPCS_CHANNEL_MII_AN_INTR_STS(x) (0x1a8002 + 0x10000 * ((x) - 1))
62 #define XPCS_USXG_AN_LINK_STS BIT(14)
63 #define XPCS_USXG_AN_DUPLEX_FULL BIT(13)
64 #define XPCS_USXG_AN_SPEED_MASK GENMASK(12, 10)
66 #define XPCS_USXG_AN_SPEED_5000 5
67 #define XPCS_USXG_AN_SPEED_10000 3
69 +#define XPCS_XAUI_MODE_CTRL 0x1f8004
70 +#define XPCS_CHANNEL_XAUI_MODE_CTRL(x) (0x1a8004 + 0x10000 * ((x) - 1))
71 +#define XPCS_TX_IPG_CHECK_DIS BIT(0)
73 /* UNIPHY PCS RAW clock ID */
76 @@ -153,6 +174,7 @@ struct ipq_uniphy_pcs {
78 phy_interface_t interface;
79 struct mutex shared_lock; /* Lock to protect shared config */
80 + spinlock_t reg_lock; /* Lock for register access */
81 struct clk *clk[PCS_CLK_MAX];
82 struct reset_control *reset[PCS_RESET_MAX];
83 struct ipq_unipcs_raw_clk raw_clk[PCS_RAW_CLK_MAX];
84 @@ -215,39 +237,55 @@ static const struct clk_ops ipq_unipcs_raw_clk_ops = {
86 static u32 ipq_unipcs_reg_read32(struct ipq_uniphy_pcs *qunipcs, u32 reg)
90 /* PCS use direct AHB access while XPCS use indirect AHB access */
91 if (reg >= XPCS_INDIRECT_ADDR) {
92 + /* For XPCS, althrough the register is different for different
93 + * channels, but they use the same indirect AHB address to
94 + * access, so add protects here.
96 + spin_lock(&qunipcs->reg_lock);
98 writel(FIELD_GET(XPCS_INDIRECT_ADDR_H, reg),
99 qunipcs->base + XPCS_INDIRECT_AHB_ADDR);
100 - return readl(qunipcs->base + XPCS_INDIRECT_DATA_ADDR(reg));
101 + val = readl(qunipcs->base + XPCS_INDIRECT_DATA_ADDR(reg));
103 + spin_unlock(&qunipcs->reg_lock);
106 return readl(qunipcs->base + reg);
110 -static void ipq_unipcs_reg_write32(struct ipq_uniphy_pcs *qunipcs,
112 +static void ipq_unipcs_reg_modify32(struct ipq_uniphy_pcs *qunipcs,
113 + u32 reg, u32 mask, u32 set)
117 if (reg >= XPCS_INDIRECT_ADDR) {
118 + spin_lock(&qunipcs->reg_lock);
121 writel(FIELD_GET(XPCS_INDIRECT_ADDR_H, reg),
122 qunipcs->base + XPCS_INDIRECT_AHB_ADDR);
123 + val = readl(qunipcs->base + XPCS_INDIRECT_DATA_ADDR(reg));
129 writel(val, qunipcs->base + XPCS_INDIRECT_DATA_ADDR(reg));
131 + spin_unlock(&qunipcs->reg_lock);
133 + val = readl(qunipcs->base + reg);
136 writel(val, qunipcs->base + reg);
140 -static void ipq_unipcs_reg_modify32(struct ipq_uniphy_pcs *qunipcs,
141 - u32 reg, u32 mask, u32 set)
145 - val = ipq_unipcs_reg_read32(qunipcs, reg);
148 - ipq_unipcs_reg_write32(qunipcs, reg, val);
151 static void ipq_unipcs_get_state_sgmii(struct ipq_uniphy_pcs *qunipcs,
153 struct phylink_link_state *state)
154 @@ -305,11 +343,15 @@ static void ipq_unipcs_get_state_2500basex(struct ipq_uniphy_pcs *qunipcs,
157 static void ipq_unipcs_get_state_usxgmii(struct ipq_uniphy_pcs *qunipcs,
159 struct phylink_link_state *state)
164 + reg = (channel == 0) ? XPCS_MII_AN_INTR_STS :
165 + XPCS_CHANNEL_MII_AN_INTR_STS(channel);
167 - val = ipq_unipcs_reg_read32(qunipcs, XPCS_MII_AN_INTR_STS);
168 + val = ipq_unipcs_reg_read32(qunipcs, reg);
170 state->link = !!(val & XPCS_USXG_AN_LINK_STS);
172 @@ -415,6 +457,15 @@ static int ipq_unipcs_config_mode(struct ipq_uniphy_pcs *qunipcs,
176 + case PHY_INTERFACE_MODE_10G_QXGMII:
178 + ipq_unipcs_reg_modify32(qunipcs, PCS_MODE_CTRL,
181 + ipq_unipcs_reg_modify32(qunipcs, PCS_QP_USXG_OPTION,
182 + PCS_QP_USXG_GMII_SRC_XPCS,
183 + PCS_QP_USXG_GMII_SRC_XPCS);
186 dev_err(qunipcs->dev,
187 "interface %s not supported\n", phy_modes(interface));
188 @@ -502,35 +553,82 @@ static int ipq_unipcs_config_2500basex(struct ipq_uniphy_pcs *qunipcs,
191 static int ipq_unipcs_config_usxgmii(struct ipq_uniphy_pcs *qunipcs,
193 unsigned int neg_mode,
194 phy_interface_t interface)
199 + /* Only in-band autoneg mode is supported currently */
200 + if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED)
201 + return -EOPNOTSUPP;
203 + if (interface == PHY_INTERFACE_MODE_10G_QXGMII)
204 + mutex_lock(&qunipcs->shared_lock);
206 if (qunipcs->interface != interface) {
207 ret = ipq_unipcs_config_mode(qunipcs, interface);
212 - /* Deassert XPCS and configure XPCS USXGMII */
213 + /* Deassert XPCS and configure XPCS USXGMII or 10G_QXGMII */
214 reset_control_deassert(qunipcs->reset[XPCS_RESET]);
216 ipq_unipcs_reg_modify32(qunipcs, XPCS_DIG_CTRL,
217 XPCS_USXG_EN, XPCS_USXG_EN);
219 - if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
220 - ipq_unipcs_reg_modify32(qunipcs, XPCS_MII_AN_CTRL,
223 + if (interface == PHY_INTERFACE_MODE_10G_QXGMII) {
224 + ipq_unipcs_reg_modify32(qunipcs, XPCS_KR_CTRL,
225 + XPCS_USXG_MODE_MASK,
226 + XPCS_10G_QXGMII_MODE);
228 + /* Set Alignment Marker Interval */
229 + ipq_unipcs_reg_modify32(qunipcs, XPCS_DIG_STS,
230 + XPCS_DIG_STS_AM_COUNT,
233 - ipq_unipcs_reg_modify32(qunipcs, XPCS_MII_CTRL,
234 - XPCS_MII_AN_EN, XPCS_MII_AN_EN);
235 + ipq_unipcs_reg_modify32(qunipcs, XPCS_DIG_CTRL,
240 qunipcs->interface = interface;
243 + if (interface == PHY_INTERFACE_MODE_10G_QXGMII)
244 + mutex_unlock(&qunipcs->shared_lock);
246 + /* Disable Tx IPG check for 10G_QXGMII */
247 + if (interface == PHY_INTERFACE_MODE_10G_QXGMII) {
248 + reg = (channel == 0) ? XPCS_XAUI_MODE_CTRL :
249 + XPCS_CHANNEL_XAUI_MODE_CTRL(channel);
251 + ipq_unipcs_reg_modify32(qunipcs, reg,
252 + XPCS_TX_IPG_CHECK_DIS,
253 + XPCS_TX_IPG_CHECK_DIS);
256 + /* Enable autoneg */
257 + reg = (channel == 0) ? XPCS_MII_AN_CTRL :
258 + XPCS_CHANNEL_MII_AN_CTRL(channel);
260 + ipq_unipcs_reg_modify32(qunipcs, reg,
261 + XPCS_MII_AN_8BIT, XPCS_MII_AN_8BIT);
263 + reg = (channel == 0) ? XPCS_MII_CTRL :
264 + XPCS_CHANNEL_MII_CTRL(channel);
266 + ipq_unipcs_reg_modify32(qunipcs, reg,
267 + XPCS_MII_AN_EN, XPCS_MII_AN_EN);
272 + if (interface == PHY_INTERFACE_MODE_10G_QXGMII)
273 + mutex_unlock(&qunipcs->shared_lock);
278 static int ipq_unipcs_config_10gbaser(struct ipq_uniphy_pcs *qunipcs,
279 @@ -638,6 +736,7 @@ ipq_unipcs_link_up_clock_rate_set(struct ipq_uniphy_pcs_ch *qunipcs_ch,
281 case PHY_INTERFACE_MODE_USXGMII:
282 case PHY_INTERFACE_MODE_10GBASER:
283 + case PHY_INTERFACE_MODE_10G_QXGMII:
284 rate = ipq_unipcs_clock_rate_get_xgmii(speed);
287 @@ -713,9 +812,10 @@ static void ipq_unipcs_link_up_config_2500basex(struct ipq_uniphy_pcs *qunipcs,
290 static void ipq_unipcs_link_up_config_usxgmii(struct ipq_uniphy_pcs *qunipcs,
299 @@ -744,14 +844,20 @@ static void ipq_unipcs_link_up_config_usxgmii(struct ipq_uniphy_pcs *qunipcs,
300 val |= XPCS_DUPLEX_FULL;
302 /* Config XPCS speed */
303 - ipq_unipcs_reg_modify32(qunipcs, XPCS_MII_CTRL,
304 + reg = (channel == 0) ? XPCS_MII_CTRL : XPCS_CHANNEL_MII_CTRL(channel);
305 + ipq_unipcs_reg_modify32(qunipcs, reg,
306 XPCS_SPEED_MASK | XPCS_DUPLEX_FULL,
309 /* XPCS adapter reset */
310 - ipq_unipcs_reg_modify32(qunipcs, XPCS_DIG_CTRL,
311 - XPCS_USXG_ADPT_RESET,
312 - XPCS_USXG_ADPT_RESET);
314 + ipq_unipcs_reg_modify32(qunipcs, XPCS_DIG_CTRL,
315 + XPCS_USXG_ADPT_RESET,
316 + XPCS_USXG_ADPT_RESET);
318 + ipq_unipcs_reg_modify32(qunipcs, XPCS_CHANNEL_DIG_CTRL(channel),
319 + XPCS_CHANNEL_USXG_ADPT_RESET,
320 + XPCS_CHANNEL_USXG_ADPT_RESET);
323 static int ipq_unipcs_validate(struct phylink_pcs *pcs,
324 @@ -786,7 +892,8 @@ static void ipq_unipcs_get_state(struct phylink_pcs *pcs,
325 ipq_unipcs_get_state_2500basex(qunipcs, channel, state);
327 case PHY_INTERFACE_MODE_USXGMII:
328 - ipq_unipcs_get_state_usxgmii(qunipcs, state);
329 + case PHY_INTERFACE_MODE_10G_QXGMII:
330 + ipq_unipcs_get_state_usxgmii(qunipcs, channel, state);
332 case PHY_INTERFACE_MODE_10GBASER:
333 ipq_unipcs_get_state_10gbaser(qunipcs, state);
334 @@ -823,7 +930,8 @@ static int ipq_unipcs_config(struct phylink_pcs *pcs,
335 case PHY_INTERFACE_MODE_2500BASEX:
336 return ipq_unipcs_config_2500basex(qunipcs, interface);
337 case PHY_INTERFACE_MODE_USXGMII:
338 - return ipq_unipcs_config_usxgmii(qunipcs,
339 + case PHY_INTERFACE_MODE_10G_QXGMII:
340 + return ipq_unipcs_config_usxgmii(qunipcs, channel,
341 neg_mode, interface);
342 case PHY_INTERFACE_MODE_10GBASER:
343 return ipq_unipcs_config_10gbaser(qunipcs, interface);
344 @@ -865,7 +973,8 @@ static void ipq_unipcs_link_up(struct phylink_pcs *pcs,
347 case PHY_INTERFACE_MODE_USXGMII:
348 - ipq_unipcs_link_up_config_usxgmii(qunipcs, speed);
349 + case PHY_INTERFACE_MODE_10G_QXGMII:
350 + ipq_unipcs_link_up_config_usxgmii(qunipcs, channel, speed);
352 case PHY_INTERFACE_MODE_10GBASER:
354 @@ -1082,6 +1191,7 @@ static int ipq_uniphy_probe(struct platform_device *pdev)
357 mutex_init(&priv->shared_lock);
358 + spin_lock_init(&priv->reg_lock);
360 platform_set_drvdata(pdev, priv);