1 From 240ae5e0ca2ed858e25d7da6d5291d9c1f2c660a Mon Sep 17 00:00:00 2001
2 From: Lei Wei <quic_leiwei@quicinc.com>
3 Date: Fri, 7 Feb 2025 23:53:14 +0800
4 Subject: [PATCH] net: pcs: qcom-ipq9574: Add PCS instantiation and phylink
7 This patch adds the following PCS functionality for the PCS driver
10 a.) Parses PCS MII DT nodes and instantiate each MII PCS instance.
11 b.) Exports PCS instance get and put APIs. The network driver calls
12 the PCS get API to get and associate the PCS instance with the port
14 c.) PCS phylink operations for SGMII/QSGMII interface modes.
16 Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
18 drivers/net/pcs/pcs-qcom-ipq9574.c | 469 +++++++++++++++++++++++++++
19 include/linux/pcs/pcs-qcom-ipq9574.h | 15 +
20 2 files changed, 484 insertions(+)
21 create mode 100644 include/linux/pcs/pcs-qcom-ipq9574.h
23 --- a/drivers/net/pcs/pcs-qcom-ipq9574.c
24 +++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
26 #include <linux/clk.h>
27 #include <linux/clk-provider.h>
28 #include <linux/device.h>
29 +#include <linux/of.h>
30 +#include <linux/of_platform.h>
31 +#include <linux/pcs/pcs-qcom-ipq9574.h>
32 #include <linux/phy.h>
33 +#include <linux/phylink.h>
34 #include <linux/platform_device.h>
35 #include <linux/regmap.h>
37 #include <dt-bindings/net/qcom,ipq9574-pcs.h>
39 +/* Maximum number of MIIs per PCS instance. There are 5 MIIs for PSGMII. */
40 +#define PCS_MAX_MII_NRS 5
42 +#define PCS_CALIBRATION 0x1e0
43 +#define PCS_CALIBRATION_DONE BIT(7)
45 +#define PCS_MODE_CTRL 0x46c
46 +#define PCS_MODE_SEL_MASK GENMASK(12, 8)
47 +#define PCS_MODE_SGMII FIELD_PREP(PCS_MODE_SEL_MASK, 0x4)
48 +#define PCS_MODE_QSGMII FIELD_PREP(PCS_MODE_SEL_MASK, 0x1)
50 +#define PCS_MII_CTRL(x) (0x480 + 0x18 * (x))
51 +#define PCS_MII_ADPT_RESET BIT(11)
52 +#define PCS_MII_FORCE_MODE BIT(3)
53 +#define PCS_MII_SPEED_MASK GENMASK(2, 1)
54 +#define PCS_MII_SPEED_1000 FIELD_PREP(PCS_MII_SPEED_MASK, 0x2)
55 +#define PCS_MII_SPEED_100 FIELD_PREP(PCS_MII_SPEED_MASK, 0x1)
56 +#define PCS_MII_SPEED_10 FIELD_PREP(PCS_MII_SPEED_MASK, 0x0)
58 +#define PCS_MII_STS(x) (0x488 + 0x18 * (x))
59 +#define PCS_MII_LINK_STS BIT(7)
60 +#define PCS_MII_STS_DUPLEX_FULL BIT(6)
61 +#define PCS_MII_STS_SPEED_MASK GENMASK(5, 4)
62 +#define PCS_MII_STS_SPEED_10 0
63 +#define PCS_MII_STS_SPEED_100 1
64 +#define PCS_MII_STS_SPEED_1000 2
66 +#define PCS_PLL_RESET 0x780
67 +#define PCS_ANA_SW_RESET BIT(6)
69 #define XPCS_INDIRECT_ADDR 0x8000
70 #define XPCS_INDIRECT_AHB_ADDR 0x83fc
71 #define XPCS_INDIRECT_ADDR_H GENMASK(20, 8)
73 FIELD_PREP(GENMASK(9, 2), \
74 FIELD_GET(XPCS_INDIRECT_ADDR_L, reg)))
76 +/* Per PCS MII private data */
78 + struct ipq_pcs *qpcs;
79 + struct phylink_pcs pcs;
82 + /* RX clock from NSSCC to PCS MII */
84 + /* TX clock from NSSCC to PCS MII */
88 /* PCS private data */
91 @@ -31,8 +77,359 @@ struct ipq_pcs {
93 /* TX clock supplied to NSSCC */
96 + struct ipq_pcs_mii *qpcs_mii[PCS_MAX_MII_NRS];
99 +#define phylink_pcs_to_qpcs_mii(_pcs) \
100 + container_of(_pcs, struct ipq_pcs_mii, pcs)
102 +static void ipq_pcs_get_state_sgmii(struct ipq_pcs *qpcs,
104 + struct phylink_link_state *state)
109 + ret = regmap_read(qpcs->regmap, PCS_MII_STS(index), &val);
115 + state->link = !!(val & PCS_MII_LINK_STS);
120 + switch (FIELD_GET(PCS_MII_STS_SPEED_MASK, val)) {
121 + case PCS_MII_STS_SPEED_1000:
122 + state->speed = SPEED_1000;
124 + case PCS_MII_STS_SPEED_100:
125 + state->speed = SPEED_100;
127 + case PCS_MII_STS_SPEED_10:
128 + state->speed = SPEED_10;
131 + state->link = false;
135 + if (val & PCS_MII_STS_DUPLEX_FULL)
136 + state->duplex = DUPLEX_FULL;
138 + state->duplex = DUPLEX_HALF;
141 +static int ipq_pcs_config_mode(struct ipq_pcs *qpcs,
142 + phy_interface_t interface)
147 + /* Configure PCS interface mode */
148 + switch (interface) {
149 + case PHY_INTERFACE_MODE_SGMII:
150 + val = PCS_MODE_SGMII;
152 + case PHY_INTERFACE_MODE_QSGMII:
153 + val = PCS_MODE_QSGMII;
156 + return -EOPNOTSUPP;
159 + ret = regmap_update_bits(qpcs->regmap, PCS_MODE_CTRL,
160 + PCS_MODE_SEL_MASK, val);
164 + /* PCS PLL reset */
165 + ret = regmap_clear_bits(qpcs->regmap, PCS_PLL_RESET, PCS_ANA_SW_RESET);
170 + ret = regmap_set_bits(qpcs->regmap, PCS_PLL_RESET, PCS_ANA_SW_RESET);
174 + /* Wait for calibration completion */
175 + ret = regmap_read_poll_timeout(qpcs->regmap, PCS_CALIBRATION,
176 + val, val & PCS_CALIBRATION_DONE,
179 + dev_err(qpcs->dev, "PCS calibration timed-out\n");
183 + qpcs->interface = interface;
188 +static int ipq_pcs_config_sgmii(struct ipq_pcs *qpcs,
190 + unsigned int neg_mode,
191 + phy_interface_t interface)
195 + /* Configure the PCS mode if required */
196 + if (qpcs->interface != interface) {
197 + ret = ipq_pcs_config_mode(qpcs, interface);
202 + /* Nothing to do here as in-band autoneg mode is enabled
203 + * by default for each PCS MII port.
205 + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
208 + /* Set force speed mode */
209 + return regmap_set_bits(qpcs->regmap,
210 + PCS_MII_CTRL(index), PCS_MII_FORCE_MODE);
213 +static int ipq_pcs_link_up_config_sgmii(struct ipq_pcs *qpcs,
215 + unsigned int neg_mode,
221 + /* PCS speed need not be configured if in-band autoneg is enabled */
222 + if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) {
223 + /* PCS speed set for force mode */
226 + val = PCS_MII_SPEED_1000;
229 + val = PCS_MII_SPEED_100;
232 + val = PCS_MII_SPEED_10;
235 + dev_err(qpcs->dev, "Invalid SGMII speed %d\n", speed);
239 + ret = regmap_update_bits(qpcs->regmap, PCS_MII_CTRL(index),
240 + PCS_MII_SPEED_MASK, val);
245 + /* PCS adapter reset */
246 + ret = regmap_clear_bits(qpcs->regmap,
247 + PCS_MII_CTRL(index), PCS_MII_ADPT_RESET);
251 + return regmap_set_bits(qpcs->regmap,
252 + PCS_MII_CTRL(index), PCS_MII_ADPT_RESET);
255 +static int ipq_pcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
256 + const struct phylink_link_state *state)
258 + switch (state->interface) {
259 + case PHY_INTERFACE_MODE_SGMII:
260 + case PHY_INTERFACE_MODE_QSGMII:
267 +static unsigned int ipq_pcs_inband_caps(struct phylink_pcs *pcs,
268 + phy_interface_t interface)
270 + switch (interface) {
271 + case PHY_INTERFACE_MODE_SGMII:
272 + case PHY_INTERFACE_MODE_QSGMII:
273 + return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
279 +static int ipq_pcs_enable(struct phylink_pcs *pcs)
281 + struct ipq_pcs_mii *qpcs_mii = phylink_pcs_to_qpcs_mii(pcs);
282 + struct ipq_pcs *qpcs = qpcs_mii->qpcs;
283 + int index = qpcs_mii->index;
286 + ret = clk_prepare_enable(qpcs_mii->rx_clk);
288 + dev_err(qpcs->dev, "Failed to enable MII %d RX clock\n", index);
292 + ret = clk_prepare_enable(qpcs_mii->tx_clk);
294 + /* This is a fatal event since phylink does not support unwinding
295 + * the state back for this error. So, we only report the error
296 + * and do not disable the clocks.
298 + dev_err(qpcs->dev, "Failed to enable MII %d TX clock\n", index);
305 +static void ipq_pcs_disable(struct phylink_pcs *pcs)
307 + struct ipq_pcs_mii *qpcs_mii = phylink_pcs_to_qpcs_mii(pcs);
309 + clk_disable_unprepare(qpcs_mii->rx_clk);
310 + clk_disable_unprepare(qpcs_mii->tx_clk);
313 +static void ipq_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
314 + struct phylink_link_state *state)
316 + struct ipq_pcs_mii *qpcs_mii = phylink_pcs_to_qpcs_mii(pcs);
317 + struct ipq_pcs *qpcs = qpcs_mii->qpcs;
318 + int index = qpcs_mii->index;
320 + switch (state->interface) {
321 + case PHY_INTERFACE_MODE_SGMII:
322 + case PHY_INTERFACE_MODE_QSGMII:
323 + ipq_pcs_get_state_sgmii(qpcs, index, state);
329 + dev_dbg_ratelimited(qpcs->dev,
330 + "mode=%s/%s/%s link=%u\n",
331 + phy_modes(state->interface),
332 + phy_speed_to_str(state->speed),
333 + phy_duplex_to_str(state->duplex),
337 +static int ipq_pcs_config(struct phylink_pcs *pcs,
338 + unsigned int neg_mode,
339 + phy_interface_t interface,
340 + const unsigned long *advertising,
343 + struct ipq_pcs_mii *qpcs_mii = phylink_pcs_to_qpcs_mii(pcs);
344 + struct ipq_pcs *qpcs = qpcs_mii->qpcs;
345 + int index = qpcs_mii->index;
347 + switch (interface) {
348 + case PHY_INTERFACE_MODE_SGMII:
349 + case PHY_INTERFACE_MODE_QSGMII:
350 + return ipq_pcs_config_sgmii(qpcs, index, neg_mode, interface);
352 + return -EOPNOTSUPP;
356 +static void ipq_pcs_link_up(struct phylink_pcs *pcs,
357 + unsigned int neg_mode,
358 + phy_interface_t interface,
359 + int speed, int duplex)
361 + struct ipq_pcs_mii *qpcs_mii = phylink_pcs_to_qpcs_mii(pcs);
362 + struct ipq_pcs *qpcs = qpcs_mii->qpcs;
363 + int index = qpcs_mii->index;
366 + switch (interface) {
367 + case PHY_INTERFACE_MODE_SGMII:
368 + case PHY_INTERFACE_MODE_QSGMII:
369 + ret = ipq_pcs_link_up_config_sgmii(qpcs, index,
377 + dev_err(qpcs->dev, "PCS link up fail for interface %s\n",
378 + phy_modes(interface));
381 +static const struct phylink_pcs_ops ipq_pcs_phylink_ops = {
382 + .pcs_validate = ipq_pcs_validate,
383 + .pcs_inband_caps = ipq_pcs_inband_caps,
384 + .pcs_enable = ipq_pcs_enable,
385 + .pcs_disable = ipq_pcs_disable,
386 + .pcs_get_state = ipq_pcs_get_state,
387 + .pcs_config = ipq_pcs_config,
388 + .pcs_link_up = ipq_pcs_link_up,
391 +/* Parse the PCS MII DT nodes which are child nodes of the PCS node,
392 + * and instantiate each MII PCS instance.
394 +static int ipq_pcs_create_miis(struct ipq_pcs *qpcs)
396 + struct device *dev = qpcs->dev;
397 + struct ipq_pcs_mii *qpcs_mii;
398 + struct device_node *mii_np;
402 + for_each_available_child_of_node(dev->of_node, mii_np) {
403 + ret = of_property_read_u32(mii_np, "reg", &index);
405 + dev_err(dev, "Failed to read MII index\n");
406 + of_node_put(mii_np);
410 + if (index >= PCS_MAX_MII_NRS) {
411 + dev_err(dev, "Invalid MII index\n");
412 + of_node_put(mii_np);
416 + qpcs_mii = devm_kzalloc(dev, sizeof(*qpcs_mii), GFP_KERNEL);
418 + of_node_put(mii_np);
422 + qpcs_mii->qpcs = qpcs;
423 + qpcs_mii->index = index;
424 + qpcs_mii->pcs.ops = &ipq_pcs_phylink_ops;
425 + qpcs_mii->pcs.neg_mode = true;
426 + qpcs_mii->pcs.poll = true;
428 + qpcs_mii->rx_clk = devm_get_clk_from_child(dev, mii_np, "rx");
429 + if (IS_ERR(qpcs_mii->rx_clk)) {
430 + of_node_put(mii_np);
431 + return dev_err_probe(dev, PTR_ERR(qpcs_mii->rx_clk),
432 + "Failed to get MII %d RX clock\n", index);
435 + qpcs_mii->tx_clk = devm_get_clk_from_child(dev, mii_np, "tx");
436 + if (IS_ERR(qpcs_mii->tx_clk)) {
437 + of_node_put(mii_np);
438 + return dev_err_probe(dev, PTR_ERR(qpcs_mii->tx_clk),
439 + "Failed to get MII %d TX clock\n", index);
442 + qpcs->qpcs_mii[index] = qpcs_mii;
448 static unsigned long ipq_pcs_clk_rate_get(struct ipq_pcs *qpcs)
450 switch (qpcs->interface) {
451 @@ -219,6 +616,10 @@ static int ipq9574_pcs_probe(struct plat
455 + ret = ipq_pcs_create_miis(qpcs);
459 platform_set_drvdata(pdev, qpcs);
462 @@ -230,6 +631,74 @@ static const struct of_device_id ipq9574
464 MODULE_DEVICE_TABLE(of, ipq9574_pcs_of_mtable);
467 + * ipq_pcs_get() - Get the IPQ PCS MII instance
468 + * @np: Device tree node to the PCS MII
470 + * Description: Get the phylink PCS instance for the given PCS MII node @np.
471 + * This instance is associated with the specific MII of the PCS and the
472 + * corresponding Ethernet netdevice.
474 + * Return: A pointer to the phylink PCS instance or an error-pointer value.
476 +struct phylink_pcs *ipq_pcs_get(struct device_node *np)
478 + struct platform_device *pdev;
479 + struct ipq_pcs_mii *qpcs_mii;
480 + struct ipq_pcs *qpcs;
483 + if (of_property_read_u32(np, "reg", &index))
484 + return ERR_PTR(-EINVAL);
486 + if (index >= PCS_MAX_MII_NRS)
487 + return ERR_PTR(-EINVAL);
489 + if (!of_match_node(ipq9574_pcs_of_mtable, np->parent))
490 + return ERR_PTR(-EINVAL);
492 + /* Get the parent device */
493 + pdev = of_find_device_by_node(np->parent);
495 + return ERR_PTR(-ENODEV);
497 + qpcs = platform_get_drvdata(pdev);
499 + put_device(&pdev->dev);
501 + /* If probe is not yet completed, return DEFER to
502 + * the dependent driver.
504 + return ERR_PTR(-EPROBE_DEFER);
507 + qpcs_mii = qpcs->qpcs_mii[index];
509 + put_device(&pdev->dev);
510 + return ERR_PTR(-ENOENT);
513 + return &qpcs_mii->pcs;
515 +EXPORT_SYMBOL(ipq_pcs_get);
518 + * ipq_pcs_put() - Release the IPQ PCS MII instance
519 + * @pcs: PCS instance
521 + * Description: Release a phylink PCS instance.
523 +void ipq_pcs_put(struct phylink_pcs *pcs)
525 + struct ipq_pcs_mii *qpcs_mii = phylink_pcs_to_qpcs_mii(pcs);
527 + /* Put reference taken by of_find_device_by_node() in
530 + put_device(qpcs_mii->qpcs->dev);
532 +EXPORT_SYMBOL(ipq_pcs_put);
534 static struct platform_driver ipq9574_pcs_driver = {
536 .name = "ipq9574_pcs",
538 +++ b/include/linux/pcs/pcs-qcom-ipq9574.h
540 +/* SPDX-License-Identifier: GPL-2.0-only */
542 + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
545 +#ifndef __LINUX_PCS_QCOM_IPQ9574_H
546 +#define __LINUX_PCS_QCOM_IPQ9574_H
551 +struct phylink_pcs *ipq_pcs_get(struct device_node *np);
552 +void ipq_pcs_put(struct phylink_pcs *pcs);
554 +#endif /* __LINUX_PCS_QCOM_IPQ9574_H */