64a5bf6229019ab0149987881196c7bc49ae9535
[openwrt/staging/stintel.git] /
1 From 7de372abe7a4b5b380fdbeedd268445f234990c8 Mon Sep 17 00:00:00 2001
2 From: Lei Wei <quic_leiwei@quicinc.com>
3 Date: Mon, 29 Jan 2024 11:39:36 +0800
4 Subject: [PATCH] net: pcs: qcom-ipq9574: add changes not submitted upstream
5
6 Was ("net: pcs: Add driver for Qualcomm IPQ UNIPHY PCS").
7
8 The UNIPHY hardware block in Qualcomm's IPQ SoC based boards enables
9 PCS and XPCS functions, and helps in interfacing the Ethernet MAC in
10 IPQ SoC to external PHYs.
11
12 This patch adds the PCS driver support for the UNIPHY hardware used in
13 IPQ SoC based boards. Support for SGMII/QSGMII/PSGMII and USXGMII
14 interface modes are added in the driver.
15
16 Change-Id: Id2c8f993f121098f7b02186b53770b75bb539a93
17 Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
18 Alex G: Rebase original patch on top of 20250207 uniphy submission
19 Remove mutex that is not required according to
20 https://lore.kernel.org/lkml/Z3ZwURgIErzpzpEr@shell.armlinux.org.uk/
21 Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
22 ---
23 drivers/net/pcs/pcs-qcom-ipq9574.c | 180 +++++++++++++++++++++++-
24 include/linux/pcs/pcs-qcom-ipq-uniphy.h | 13 ++
25 2 files changed, 192 insertions(+), 1 deletion(-)
26 create mode 100644 include/linux/pcs/pcs-qcom-ipq-uniphy.h
27
28 --- a/drivers/net/pcs/pcs-qcom-ipq9574.c
29 +++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
30 @@ -9,10 +9,12 @@
31 #include <linux/of.h>
32 #include <linux/of_platform.h>
33 #include <linux/pcs/pcs-qcom-ipq9574.h>
34 +#include <linux/pcs/pcs-qcom-ipq-uniphy.h>
35 #include <linux/phy.h>
36 #include <linux/phylink.h>
37 #include <linux/platform_device.h>
38 #include <linux/regmap.h>
39 +#include <linux/reset.h>
40
41 #include <dt-bindings/net/qcom,ipq9574-pcs.h>
42
43 @@ -26,6 +28,7 @@
44 #define PCS_MODE_SEL_MASK GENMASK(12, 8)
45 #define PCS_MODE_SGMII FIELD_PREP(PCS_MODE_SEL_MASK, 0x4)
46 #define PCS_MODE_QSGMII FIELD_PREP(PCS_MODE_SEL_MASK, 0x1)
47 +#define PCS_MODE_PSGMII FIELD_PREP(PCS_MODE_SEL_MASK, 0x2)
48 #define PCS_MODE_XPCS FIELD_PREP(PCS_MODE_SEL_MASK, 0x10)
49
50 #define PCS_MII_CTRL(x) (0x480 + 0x18 * (x))
51 @@ -43,6 +46,8 @@
52 #define PCS_MII_STS_SPEED_10 0
53 #define PCS_MII_STS_SPEED_100 1
54 #define PCS_MII_STS_SPEED_1000 2
55 +#define PCS_MII_STS_PAUSE_TX_EN BIT(1)
56 +#define PCS_MII_STS_PAUSE_RX_EN BIT(0)
57
58 #define PCS_PLL_RESET 0x780
59 #define PCS_ANA_SW_RESET BIT(6)
60 @@ -95,12 +100,35 @@ struct ipq_pcs_mii {
61 struct clk *tx_clk;
62 };
63
64 +/* UNIPHY PCS reset ID */
65 +enum {
66 + PCS_SYS_RESET,
67 + PCS_AHB_RESET,
68 + XPCS_RESET,
69 + PCS_RESET_MAX
70 +};
71 +
72 +/* UNIPHY PCS reset name */
73 +static const char *const pcs_reset_name[PCS_RESET_MAX] = {
74 + "sys",
75 + "ahb",
76 + "xpcs",
77 +};
78 +
79 +/* UNIPHY PCS channel clock ID */
80 +enum {
81 + PCS_CH_RX_CLK,
82 + PCS_CH_TX_CLK,
83 + PCS_CH_CLK_MAX
84 +};
85 +
86 /* PCS private data */
87 struct ipq_pcs {
88 struct device *dev;
89 void __iomem *base;
90 struct regmap *regmap;
91 phy_interface_t interface;
92 + struct reset_control *reset[PCS_RESET_MAX];
93
94 /* RX clock supplied to NSSCC */
95 struct clk_hw rx_hw;
96 @@ -150,6 +178,11 @@ static void ipq_pcs_get_state_sgmii(stru
97 state->duplex = DUPLEX_FULL;
98 else
99 state->duplex = DUPLEX_HALF;
100 +
101 + if (val & PCS_MII_STS_PAUSE_TX_EN)
102 + state->pause |= MLO_PAUSE_TX;
103 + if (val & PCS_MII_STS_PAUSE_RX_EN)
104 + state->pause |= MLO_PAUSE_RX;
105 }
106
107 static void ipq_pcs_get_state_usxgmii(struct ipq_pcs *qpcs,
108 @@ -203,6 +236,9 @@ static int ipq_pcs_config_mode(struct ip
109 unsigned int val;
110 int ret;
111
112 + /* Assert XPCS reset */
113 + reset_control_assert(qpcs->reset[XPCS_RESET]);
114 +
115 /* Configure PCS interface mode */
116 switch (interface) {
117 case PHY_INTERFACE_MODE_SGMII:
118 @@ -211,11 +247,16 @@ static int ipq_pcs_config_mode(struct ip
119 case PHY_INTERFACE_MODE_QSGMII:
120 val = PCS_MODE_QSGMII;
121 break;
122 + case PHY_INTERFACE_MODE_PSGMII:
123 + val = PCS_MODE_PSGMII;
124 + break;
125 case PHY_INTERFACE_MODE_USXGMII:
126 val = PCS_MODE_XPCS;
127 rate = 312500000;
128 break;
129 default:
130 + dev_err(qpcs->dev,
131 + "interface %s not supported\n", phy_modes(interface));
132 return -EOPNOTSUPP;
133 }
134
135 @@ -300,6 +341,9 @@ static int ipq_pcs_config_usxgmii(struct
136 if (ret)
137 return ret;
138
139 + /* Deassert XPCS and configure XPCS USXGMII */
140 + reset_control_deassert(qpcs->reset[XPCS_RESET]);
141 +
142 ret = regmap_set_bits(qpcs->regmap, XPCS_DIG_CTRL, XPCS_USXG_EN);
143 if (ret)
144 return ret;
145 @@ -311,6 +355,91 @@ static int ipq_pcs_config_usxgmii(struct
146 return regmap_set_bits(qpcs->regmap, XPCS_MII_CTRL, XPCS_MII_AN_EN);
147 }
148
149 +static unsigned long ipq_unipcs_clock_rate_get_gmii(int speed)
150 +{
151 + unsigned long rate = 0;
152 +
153 + switch (speed) {
154 + case SPEED_1000:
155 + rate = 125000000;
156 + break;
157 + case SPEED_100:
158 + rate = 25000000;
159 + break;
160 + case SPEED_10:
161 + rate = 2500000;
162 + break;
163 + default:
164 + break;
165 + }
166 +
167 + return rate;
168 +}
169 +
170 +static unsigned long ipq_unipcs_clock_rate_get_xgmii(int speed)
171 +{
172 + unsigned long rate = 0;
173 +
174 + switch (speed) {
175 + case SPEED_10000:
176 + rate = 312500000;
177 + break;
178 + case SPEED_5000:
179 + rate = 156250000;
180 + break;
181 + case SPEED_2500:
182 + rate = 78125000;
183 + break;
184 + case SPEED_1000:
185 + rate = 125000000;
186 + break;
187 + case SPEED_100:
188 + rate = 12500000;
189 + break;
190 + case SPEED_10:
191 + rate = 1250000;
192 + break;
193 + default:
194 + break;
195 + }
196 +
197 + return rate;
198 +}
199 +
200 +static void
201 +ipq_unipcs_link_up_clock_rate_set(struct ipq_pcs_mii *qunipcs_ch,
202 + phy_interface_t interface,
203 + int speed)
204 +{
205 + struct ipq_pcs *qpcs = qunipcs_ch->qpcs;
206 + unsigned long rate = 0;
207 +
208 + switch (interface) {
209 + case PHY_INTERFACE_MODE_SGMII:
210 + case PHY_INTERFACE_MODE_QSGMII:
211 + case PHY_INTERFACE_MODE_PSGMII:
212 + rate = ipq_unipcs_clock_rate_get_gmii(speed);
213 + break;
214 + case PHY_INTERFACE_MODE_USXGMII:
215 + rate = ipq_unipcs_clock_rate_get_xgmii(speed);
216 + break;
217 + default:
218 + dev_err(qpcs->dev,
219 + "interface %s not supported\n", phy_modes(interface));
220 + return;
221 + }
222 +
223 + if (rate == 0) {
224 + dev_err(qpcs->dev, "Invalid PCS clock rate\n");
225 + return;
226 + }
227 +
228 + clk_set_rate(qunipcs_ch->rx_clk, rate);
229 + clk_set_rate(qunipcs_ch->tx_clk, rate);
230 +
231 + fsleep(10000);
232 +}
233 +
234 static int ipq_pcs_link_up_config_sgmii(struct ipq_pcs *qpcs,
235 int index,
236 unsigned int neg_mode,
237 @@ -467,6 +596,7 @@ static void ipq_pcs_get_state(struct phy
238 switch (state->interface) {
239 case PHY_INTERFACE_MODE_SGMII:
240 case PHY_INTERFACE_MODE_QSGMII:
241 + case PHY_INTERFACE_MODE_PSGMII:
242 ipq_pcs_get_state_sgmii(qpcs, index, state);
243 break;
244 case PHY_INTERFACE_MODE_USXGMII:
245 @@ -497,10 +627,13 @@ static int ipq_pcs_config(struct phylink
246 switch (interface) {
247 case PHY_INTERFACE_MODE_SGMII:
248 case PHY_INTERFACE_MODE_QSGMII:
249 + case PHY_INTERFACE_MODE_PSGMII:
250 return ipq_pcs_config_sgmii(qpcs, index, neg_mode, interface);
251 case PHY_INTERFACE_MODE_USXGMII:
252 return ipq_pcs_config_usxgmii(qpcs);
253 default:
254 + dev_err(qpcs->dev,
255 + "interface %s not supported\n", phy_modes(interface));
256 return -EOPNOTSUPP;
257 };
258 }
259 @@ -515,9 +648,14 @@ static void ipq_pcs_link_up(struct phyli
260 int index = qpcs_mii->index;
261 int ret;
262
263 + /* Configure PCS channel interface clock rate */
264 + ipq_unipcs_link_up_clock_rate_set(qpcs_mii, interface, speed);
265 +
266 + /* Configure PCS speed and reset PCS adapter */
267 switch (interface) {
268 case PHY_INTERFACE_MODE_SGMII:
269 case PHY_INTERFACE_MODE_QSGMII:
270 + case PHY_INTERFACE_MODE_PSGMII:
271 ret = ipq_pcs_link_up_config_sgmii(qpcs, index,
272 neg_mode, speed);
273 break;
274 @@ -525,6 +663,8 @@ static void ipq_pcs_link_up(struct phyli
275 ret = ipq_pcs_link_up_config_usxgmii(qpcs, speed);
276 break;
277 default:
278 + dev_err(qpcs->dev,
279 + "interface %s not supported\n", phy_modes(interface));
280 return;
281 }
282
283 @@ -735,12 +875,38 @@ static const struct regmap_config ipq_pc
284 .fast_io = true,
285 };
286
287 +/**
288 + * ipq_unipcs_create() - Create Qualcomm IPQ UNIPHY PCS
289 + * @np: Device tree node to the PCS
290 + *
291 + * Description: Create a phylink PCS instance for a PCS node @np.
292 + *
293 + * Return: A pointer to the phylink PCS instance or an error-pointer value.
294 + */
295 +struct phylink_pcs *ipq_unipcs_create(struct device_node *np)
296 +{
297 + return ipq_pcs_get(np);
298 +}
299 +EXPORT_SYMBOL(ipq_unipcs_create);
300 +
301 +/**
302 + * ipq_unipcs_destroy() - Destroy Qualcomm IPQ UNIPHY PCS
303 + * @pcs: PCS instance
304 + *
305 + * Description: Destroy a phylink PCS instance.
306 + */
307 +void ipq_unipcs_destroy(struct phylink_pcs *pcs)
308 +{
309 + ipq_pcs_put(pcs);
310 +}
311 +EXPORT_SYMBOL(ipq_unipcs_destroy);
312 +
313 static int ipq9574_pcs_probe(struct platform_device *pdev)
314 {
315 struct device *dev = &pdev->dev;
316 struct ipq_pcs *qpcs;
317 struct clk *clk;
318 - int ret;
319 + int i, ret;
320
321 qpcs = devm_kzalloc(dev, sizeof(*qpcs), GFP_KERNEL);
322 if (!qpcs)
323 @@ -762,11 +928,23 @@ static int ipq9574_pcs_probe(struct plat
324 if (IS_ERR(clk))
325 return dev_err_probe(dev, PTR_ERR(clk),
326 "Failed to enable SYS clock\n");
327 + clk_set_rate(clk, 24000000);
328
329 clk = devm_clk_get_enabled(dev, "ahb");
330 if (IS_ERR(clk))
331 return dev_err_probe(dev, PTR_ERR(clk),
332 "Failed to enable AHB clock\n");
333 + clk_set_rate(clk, 100000000);
334 +
335 + for (i = 0; i < PCS_RESET_MAX; i++) {
336 + qpcs->reset[i] =
337 + devm_reset_control_get_optional_exclusive(dev,
338 + pcs_reset_name[i]);
339 +
340 + if (IS_ERR(qpcs->reset[i]))
341 + dev_err(dev, "Failed to get the reset ID %s\n",
342 + pcs_reset_name[i]);
343 + }
344
345 ret = ipq_pcs_clk_register(qpcs);
346 if (ret)
347 --- /dev/null
348 +++ b/include/linux/pcs/pcs-qcom-ipq-uniphy.h
349 @@ -0,0 +1,13 @@
350 +/* SPDX-License-Identifier: GPL-2.0-only */
351 +/*
352 + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
353 + *
354 + */
355 +
356 +#ifndef __LINUX_PCS_QCOM_IPQ_UNIPHY_H
357 +#define __LINUX_PCS_QCOM_IPQ_UNIPHY_H
358 +
359 +struct phylink_pcs *ipq_unipcs_create(struct device_node *np);
360 +void ipq_unipcs_destroy(struct phylink_pcs *pcs);
361 +
362 +#endif /* __LINUX_PCS_QCOM_IPQ_UNIPHY_H */