ed58d927b9bc9cd17e59170ebf959b25df403d31
[openwrt/staging/stintel.git] /
1 From fcd1c53b460aa39cfd15f842126af62b27a4fad5 Mon Sep 17 00:00:00 2001
2 From: Lei Wei <quic_leiwei@quicinc.com>
3 Date: Tue, 2 Apr 2024 18:28:42 +0800
4 Subject: [PATCH 13/50] net: pcs: Add 2500BASEX interface mode support to IPQ
5 UNIPHY PCS driver
6
7 2500BASEX mode is used when PCS connects with QCA8386 switch in a fixed
8 2500M link. It is also used when PCS connectes with QCA8081 PHY which
9 works at 2500M link speed. In addition, it can be also used when PCS
10 connects with a 2.5G SFP module.
11
12 Change-Id: I3fe61113c1b3685debc20659736a9488216a029d
13 Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
14 ---
15 drivers/net/pcs/pcs-qcom-ipq-uniphy.c | 95 +++++++++++++++++++++++++++
16 1 file changed, 95 insertions(+)
17
18 diff --git a/drivers/net/pcs/pcs-qcom-ipq-uniphy.c b/drivers/net/pcs/pcs-qcom-ipq-uniphy.c
19 index 68a1715531ef..ed9c55a6c0fa 100644
20 --- a/drivers/net/pcs/pcs-qcom-ipq-uniphy.c
21 +++ b/drivers/net/pcs/pcs-qcom-ipq-uniphy.c
22 @@ -25,6 +25,7 @@
23 #define PCS_MODE_SGMII FIELD_PREP(PCS_MODE_SEL_MASK, 0x4)
24 #define PCS_MODE_QSGMII FIELD_PREP(PCS_MODE_SEL_MASK, 0x1)
25 #define PCS_MODE_PSGMII FIELD_PREP(PCS_MODE_SEL_MASK, 0x2)
26 +#define PCS_MODE_SGMII_PLUS FIELD_PREP(PCS_MODE_SEL_MASK, 0x8)
27 #define PCS_MODE_XPCS FIELD_PREP(PCS_MODE_SEL_MASK, 0x10)
28 #define PCS_MODE_AN_MODE BIT(0)
29
30 @@ -282,6 +283,24 @@ static void ipq_unipcs_get_state_sgmii(struct ipq_uniphy_pcs *qunipcs,
31 state->pause |= MLO_PAUSE_RX;
32 }
33
34 +static void ipq_unipcs_get_state_2500basex(struct ipq_uniphy_pcs *qunipcs,
35 + int channel,
36 + struct phylink_link_state *state)
37 +{
38 + u32 val;
39 +
40 + val = ipq_unipcs_reg_read32(qunipcs, PCS_CHANNEL_STS(channel));
41 +
42 + state->link = !!(val & PCS_CHANNEL_LINK_STS);
43 +
44 + if (!state->link)
45 + return;
46 +
47 + state->speed = SPEED_2500;
48 + state->duplex = DUPLEX_FULL;
49 + state->pause |= MLO_PAUSE_TXRX_MASK;
50 +}
51 +
52 static void ipq_unipcs_get_state_usxgmii(struct ipq_uniphy_pcs *qunipcs,
53 struct phylink_link_state *state)
54 {
55 @@ -373,6 +392,12 @@ static int ipq_unipcs_config_mode(struct ipq_uniphy_pcs *qunipcs,
56 PCS_MODE_SEL_MASK | PCS_MODE_AN_MODE,
57 PCS_MODE_PSGMII);
58 break;
59 + case PHY_INTERFACE_MODE_2500BASEX:
60 + rate = 312500000;
61 + ipq_unipcs_reg_modify32(qunipcs, PCS_MODE_CTRL,
62 + PCS_MODE_SEL_MASK,
63 + PCS_MODE_SGMII_PLUS);
64 + break;
65 case PHY_INTERFACE_MODE_USXGMII:
66 case PHY_INTERFACE_MODE_10GBASER:
67 rate = 312500000;
68 @@ -450,6 +475,22 @@ static int ipq_unipcs_config_sgmii(struct ipq_uniphy_pcs *qunipcs,
69 return ret;
70 }
71
72 +static int ipq_unipcs_config_2500basex(struct ipq_uniphy_pcs *qunipcs,
73 + phy_interface_t interface)
74 +{
75 + int ret;
76 +
77 + if (qunipcs->interface != interface) {
78 + ret = ipq_unipcs_config_mode(qunipcs, interface);
79 + if (ret)
80 + return ret;
81 +
82 + qunipcs->interface = interface;
83 + }
84 +
85 + return 0;
86 +}
87 +
88 static int ipq_unipcs_config_usxgmii(struct ipq_uniphy_pcs *qunipcs,
89 unsigned int neg_mode,
90 phy_interface_t interface)
91 @@ -522,6 +563,21 @@ static unsigned long ipq_unipcs_clock_rate_get_gmii(int speed)
92 return rate;
93 }
94
95 +static unsigned long ipq_unipcs_clock_rate_get_gmiiplus(int speed)
96 +{
97 + unsigned long rate = 0;
98 +
99 + switch (speed) {
100 + case SPEED_2500:
101 + rate = 312500000;
102 + break;
103 + default:
104 + break;
105 + }
106 +
107 + return rate;
108 +}
109 +
110 static unsigned long ipq_unipcs_clock_rate_get_xgmii(int speed)
111 {
112 unsigned long rate = 0;
113 @@ -566,6 +622,9 @@ ipq_unipcs_link_up_clock_rate_set(struct ipq_uniphy_pcs_ch *qunipcs_ch,
114 case PHY_INTERFACE_MODE_PSGMII:
115 rate = ipq_unipcs_clock_rate_get_gmii(speed);
116 break;
117 + case PHY_INTERFACE_MODE_2500BASEX:
118 + rate = ipq_unipcs_clock_rate_get_gmiiplus(speed);
119 + break;
120 case PHY_INTERFACE_MODE_USXGMII:
121 case PHY_INTERFACE_MODE_10GBASER:
122 rate = ipq_unipcs_clock_rate_get_xgmii(speed);
123 @@ -627,6 +686,21 @@ static void ipq_unipcs_link_up_config_sgmii(struct ipq_uniphy_pcs *qunipcs,
124 PCS_CHANNEL_ADPT_RESET);
125 }
126
127 +static void ipq_unipcs_link_up_config_2500basex(struct ipq_uniphy_pcs *qunipcs,
128 + int channel,
129 + int speed)
130 +{
131 + /* 2500BASEX do not support autoneg and do not need to
132 + * configure PCS speed, only reset PCS adapter here.
133 + */
134 + ipq_unipcs_reg_modify32(qunipcs, PCS_CHANNEL_CTRL(channel),
135 + PCS_CHANNEL_ADPT_RESET,
136 + 0);
137 + ipq_unipcs_reg_modify32(qunipcs, PCS_CHANNEL_CTRL(channel),
138 + PCS_CHANNEL_ADPT_RESET,
139 + PCS_CHANNEL_ADPT_RESET);
140 +}
141 +
142 static void ipq_unipcs_link_up_config_usxgmii(struct ipq_uniphy_pcs *qunipcs,
143 int speed)
144 {
145 @@ -669,6 +743,17 @@ static void ipq_unipcs_link_up_config_usxgmii(struct ipq_uniphy_pcs *qunipcs,
146 XPCS_USXG_ADPT_RESET);
147 }
148
149 +static int ipq_unipcs_validate(struct phylink_pcs *pcs,
150 + unsigned long *supported,
151 + const struct phylink_link_state *state)
152 +{
153 + /* In-band autoneg is not supported for 2500BASEX */
154 + if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
155 + phylink_clear(supported, Autoneg);
156 +
157 + return 0;
158 +}
159 +
160 static void ipq_unipcs_get_state(struct phylink_pcs *pcs,
161 struct phylink_link_state *state)
162 {
163 @@ -682,6 +767,9 @@ static void ipq_unipcs_get_state(struct phylink_pcs *pcs,
164 case PHY_INTERFACE_MODE_PSGMII:
165 ipq_unipcs_get_state_sgmii(qunipcs, channel, state);
166 break;
167 + case PHY_INTERFACE_MODE_2500BASEX:
168 + ipq_unipcs_get_state_2500basex(qunipcs, channel, state);
169 + break;
170 case PHY_INTERFACE_MODE_USXGMII:
171 ipq_unipcs_get_state_usxgmii(qunipcs, state);
172 break;
173 @@ -716,6 +804,8 @@ static int ipq_unipcs_config(struct phylink_pcs *pcs,
174 case PHY_INTERFACE_MODE_PSGMII:
175 return ipq_unipcs_config_sgmii(qunipcs, channel,
176 neg_mode, interface);
177 + case PHY_INTERFACE_MODE_2500BASEX:
178 + return ipq_unipcs_config_2500basex(qunipcs, interface);
179 case PHY_INTERFACE_MODE_USXGMII:
180 return ipq_unipcs_config_usxgmii(qunipcs,
181 neg_mode, interface);
182 @@ -748,6 +838,10 @@ static void ipq_unipcs_link_up(struct phylink_pcs *pcs,
183 ipq_unipcs_link_up_config_sgmii(qunipcs, channel,
184 neg_mode, speed);
185 break;
186 + case PHY_INTERFACE_MODE_2500BASEX:
187 + ipq_unipcs_link_up_config_2500basex(qunipcs,
188 + channel, speed);
189 + break;
190 case PHY_INTERFACE_MODE_USXGMII:
191 ipq_unipcs_link_up_config_usxgmii(qunipcs, speed);
192 break;
193 @@ -761,6 +855,7 @@ static void ipq_unipcs_link_up(struct phylink_pcs *pcs,
194 }
195
196 static const struct phylink_pcs_ops ipq_unipcs_phylink_ops = {
197 + .pcs_validate = ipq_unipcs_validate,
198 .pcs_get_state = ipq_unipcs_get_state,
199 .pcs_config = ipq_unipcs_config,
200 .pcs_link_up = ipq_unipcs_link_up,
201 --
202 2.45.2
203