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
6 Was ("net: pcs: Add driver for Qualcomm IPQ UNIPHY PCS").
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.
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.
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>
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
28 --- a/drivers/net/pcs/pcs-qcom-ipq9574.c
29 +++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
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>
41 #include <dt-bindings/net/qcom,ipq9574-pcs.h>
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)
50 #define PCS_MII_CTRL(x) (0x480 + 0x18 * (x))
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)
58 #define PCS_PLL_RESET 0x780
59 #define PCS_ANA_SW_RESET BIT(6)
60 @@ -95,12 +100,35 @@ struct ipq_pcs_mii {
64 +/* UNIPHY PCS reset ID */
72 +/* UNIPHY PCS reset name */
73 +static const char *const pcs_reset_name[PCS_RESET_MAX] = {
79 +/* UNIPHY PCS channel clock ID */
86 /* PCS private data */
90 struct regmap *regmap;
91 phy_interface_t interface;
92 + struct reset_control *reset[PCS_RESET_MAX];
94 /* RX clock supplied to NSSCC */
96 @@ -150,6 +178,11 @@ static void ipq_pcs_get_state_sgmii(stru
97 state->duplex = DUPLEX_FULL;
99 state->duplex = DUPLEX_HALF;
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;
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
112 + /* Assert XPCS reset */
113 + reset_control_assert(qpcs->reset[XPCS_RESET]);
115 /* Configure PCS interface mode */
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;
122 + case PHY_INTERFACE_MODE_PSGMII:
123 + val = PCS_MODE_PSGMII;
125 case PHY_INTERFACE_MODE_USXGMII:
131 + "interface %s not supported\n", phy_modes(interface));
135 @@ -300,6 +341,9 @@ static int ipq_pcs_config_usxgmii(struct
139 + /* Deassert XPCS and configure XPCS USXGMII */
140 + reset_control_deassert(qpcs->reset[XPCS_RESET]);
142 ret = regmap_set_bits(qpcs->regmap, XPCS_DIG_CTRL, XPCS_USXG_EN);
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);
149 +static unsigned long ipq_unipcs_clock_rate_get_gmii(int speed)
151 + unsigned long rate = 0;
170 +static unsigned long ipq_unipcs_clock_rate_get_xgmii(int speed)
172 + unsigned long rate = 0;
201 +ipq_unipcs_link_up_clock_rate_set(struct ipq_pcs_mii *qunipcs_ch,
202 + phy_interface_t interface,
205 + struct ipq_pcs *qpcs = qunipcs_ch->qpcs;
206 + unsigned long rate = 0;
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);
214 + case PHY_INTERFACE_MODE_USXGMII:
215 + rate = ipq_unipcs_clock_rate_get_xgmii(speed);
219 + "interface %s not supported\n", phy_modes(interface));
224 + dev_err(qpcs->dev, "Invalid PCS clock rate\n");
228 + clk_set_rate(qunipcs_ch->rx_clk, rate);
229 + clk_set_rate(qunipcs_ch->tx_clk, rate);
234 static int ipq_pcs_link_up_config_sgmii(struct ipq_pcs *qpcs,
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);
244 case PHY_INTERFACE_MODE_USXGMII:
245 @@ -497,10 +627,13 @@ static int ipq_pcs_config(struct phylink
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);
255 + "interface %s not supported\n", phy_modes(interface));
259 @@ -515,9 +648,14 @@ static void ipq_pcs_link_up(struct phyli
260 int index = qpcs_mii->index;
263 + /* Configure PCS channel interface clock rate */
264 + ipq_unipcs_link_up_clock_rate_set(qpcs_mii, interface, speed);
266 + /* Configure PCS speed and reset PCS adapter */
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,
274 @@ -525,6 +663,8 @@ static void ipq_pcs_link_up(struct phyli
275 ret = ipq_pcs_link_up_config_usxgmii(qpcs, speed);
279 + "interface %s not supported\n", phy_modes(interface));
283 @@ -735,12 +875,38 @@ static const struct regmap_config ipq_pc
288 + * ipq_unipcs_create() - Create Qualcomm IPQ UNIPHY PCS
289 + * @np: Device tree node to the PCS
291 + * Description: Create a phylink PCS instance for a PCS node @np.
293 + * Return: A pointer to the phylink PCS instance or an error-pointer value.
295 +struct phylink_pcs *ipq_unipcs_create(struct device_node *np)
297 + return ipq_pcs_get(np);
299 +EXPORT_SYMBOL(ipq_unipcs_create);
302 + * ipq_unipcs_destroy() - Destroy Qualcomm IPQ UNIPHY PCS
303 + * @pcs: PCS instance
305 + * Description: Destroy a phylink PCS instance.
307 +void ipq_unipcs_destroy(struct phylink_pcs *pcs)
311 +EXPORT_SYMBOL(ipq_unipcs_destroy);
313 static int ipq9574_pcs_probe(struct platform_device *pdev)
315 struct device *dev = &pdev->dev;
316 struct ipq_pcs *qpcs;
321 qpcs = devm_kzalloc(dev, sizeof(*qpcs), GFP_KERNEL);
323 @@ -762,11 +928,23 @@ static int ipq9574_pcs_probe(struct plat
325 return dev_err_probe(dev, PTR_ERR(clk),
326 "Failed to enable SYS clock\n");
327 + clk_set_rate(clk, 24000000);
329 clk = devm_clk_get_enabled(dev, "ahb");
331 return dev_err_probe(dev, PTR_ERR(clk),
332 "Failed to enable AHB clock\n");
333 + clk_set_rate(clk, 100000000);
335 + for (i = 0; i < PCS_RESET_MAX; i++) {
337 + devm_reset_control_get_optional_exclusive(dev,
338 + pcs_reset_name[i]);
340 + if (IS_ERR(qpcs->reset[i]))
341 + dev_err(dev, "Failed to get the reset ID %s\n",
342 + pcs_reset_name[i]);
345 ret = ipq_pcs_clk_register(qpcs);
348 +++ b/include/linux/pcs/pcs-qcom-ipq-uniphy.h
350 +/* SPDX-License-Identifier: GPL-2.0-only */
352 + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
356 +#ifndef __LINUX_PCS_QCOM_IPQ_UNIPHY_H
357 +#define __LINUX_PCS_QCOM_IPQ_UNIPHY_H
359 +struct phylink_pcs *ipq_unipcs_create(struct device_node *np);
360 +void ipq_unipcs_destroy(struct phylink_pcs *pcs);
362 +#endif /* __LINUX_PCS_QCOM_IPQ_UNIPHY_H */