1 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
3 * Lynx PCS MDIO helpers
6 #include <linux/mdio.h>
7 #include <linux/phylink.h>
8 #include <linux/pcs-lynx.h>
9 #include <linux/property.h>
11 #define SGMII_CLOCK_PERIOD_NS 8 /* PCS is clocked at 125 MHz */
12 #define LINK_TIMER_VAL(ns) ((u32)((ns) / SGMII_CLOCK_PERIOD_NS))
14 #define LINK_TIMER_LO 0x12
15 #define LINK_TIMER_HI 0x13
17 #define IF_MODE_SGMII_EN BIT(0)
18 #define IF_MODE_USE_SGMII_AN BIT(1)
19 #define IF_MODE_SPEED(x) (((x) << 2) & GENMASK(3, 2))
20 #define IF_MODE_SPEED_MSK GENMASK(3, 2)
21 #define IF_MODE_HALF_DUPLEX BIT(4)
24 struct phylink_pcs pcs
;
25 struct mdio_device
*mdio
;
35 #define phylink_pcs_to_lynx(pl_pcs) container_of((pl_pcs), struct lynx_pcs, pcs)
36 #define lynx_to_phylink_pcs(lynx) (&(lynx)->pcs)
38 static void lynx_pcs_get_state_usxgmii(struct mdio_device
*pcs
,
39 struct phylink_link_state
*state
)
41 struct mii_bus
*bus
= pcs
->bus
;
45 status
= mdiobus_c45_read(bus
, addr
, MDIO_MMD_VEND2
, MII_BMSR
);
49 state
->link
= !!(status
& MDIO_STAT1_LSTATUS
);
50 state
->an_complete
= !!(status
& MDIO_AN_STAT1_COMPLETE
);
51 if (!state
->link
|| !state
->an_complete
)
54 lpa
= mdiobus_c45_read(bus
, addr
, MDIO_MMD_VEND2
, MII_LPA
);
58 phylink_decode_usxgmii_word(state
, lpa
);
61 static void lynx_pcs_get_state_2500basex(struct mdio_device
*pcs
,
62 struct phylink_link_state
*state
)
66 bmsr
= mdiodev_read(pcs
, MII_BMSR
);
72 state
->link
= !!(bmsr
& BMSR_LSTATUS
);
73 state
->an_complete
= !!(bmsr
& BMSR_ANEGCOMPLETE
);
77 state
->speed
= SPEED_2500
;
78 state
->pause
|= MLO_PAUSE_TX
| MLO_PAUSE_RX
;
79 state
->duplex
= DUPLEX_FULL
;
82 static void lynx_pcs_get_state(struct phylink_pcs
*pcs
,
83 struct phylink_link_state
*state
)
85 struct lynx_pcs
*lynx
= phylink_pcs_to_lynx(pcs
);
87 switch (state
->interface
) {
88 case PHY_INTERFACE_MODE_1000BASEX
:
89 case PHY_INTERFACE_MODE_SGMII
:
90 case PHY_INTERFACE_MODE_QSGMII
:
91 phylink_mii_c22_pcs_get_state(lynx
->mdio
, state
);
93 case PHY_INTERFACE_MODE_2500BASEX
:
94 lynx_pcs_get_state_2500basex(lynx
->mdio
, state
);
96 case PHY_INTERFACE_MODE_USXGMII
:
97 lynx_pcs_get_state_usxgmii(lynx
->mdio
, state
);
99 case PHY_INTERFACE_MODE_10GBASER
:
100 phylink_mii_c45_pcs_get_state(lynx
->mdio
, state
);
106 dev_dbg(&lynx
->mdio
->dev
,
107 "mode=%s/%s/%s link=%u an_complete=%u\n",
108 phy_modes(state
->interface
),
109 phy_speed_to_str(state
->speed
),
110 phy_duplex_to_str(state
->duplex
),
111 state
->link
, state
->an_complete
);
114 static int lynx_pcs_config_giga(struct mdio_device
*pcs
,
115 phy_interface_t interface
,
116 const unsigned long *advertising
,
117 unsigned int neg_mode
)
124 link_timer_ns
= phylink_get_link_timer_ns(interface
);
125 if (link_timer_ns
> 0) {
126 link_timer
= LINK_TIMER_VAL(link_timer_ns
);
128 mdiodev_write(pcs
, LINK_TIMER_LO
, link_timer
& 0xffff);
129 mdiodev_write(pcs
, LINK_TIMER_HI
, link_timer
>> 16);
132 if (interface
== PHY_INTERFACE_MODE_1000BASEX
) {
135 /* SGMII and QSGMII */
136 if_mode
= IF_MODE_SGMII_EN
;
137 if (neg_mode
== PHYLINK_PCS_NEG_INBAND_ENABLED
)
138 if_mode
|= IF_MODE_USE_SGMII_AN
;
141 err
= mdiodev_modify(pcs
, IF_MODE
,
142 IF_MODE_SGMII_EN
| IF_MODE_USE_SGMII_AN
,
147 return phylink_mii_c22_pcs_config(pcs
, interface
, advertising
,
151 static int lynx_pcs_config_usxgmii(struct mdio_device
*pcs
,
152 const unsigned long *advertising
,
153 unsigned int neg_mode
)
155 struct mii_bus
*bus
= pcs
->bus
;
156 int addr
= pcs
->addr
;
158 if (neg_mode
!= PHYLINK_PCS_NEG_INBAND_ENABLED
) {
159 dev_err(&pcs
->dev
, "USXGMII only supports in-band AN for now\n");
163 /* Configure device ability for the USXGMII Replicator */
164 return mdiobus_c45_write(bus
, addr
, MDIO_MMD_VEND2
, MII_ADVERTISE
,
165 MDIO_USXGMII_10G
| MDIO_USXGMII_LINK
|
166 MDIO_USXGMII_FULL_DUPLEX
|
167 ADVERTISE_SGMII
| ADVERTISE_LPACK
);
170 static int lynx_pcs_config(struct phylink_pcs
*pcs
, unsigned int neg_mode
,
171 phy_interface_t ifmode
,
172 const unsigned long *advertising
, bool permit
)
174 struct lynx_pcs
*lynx
= phylink_pcs_to_lynx(pcs
);
177 case PHY_INTERFACE_MODE_1000BASEX
:
178 case PHY_INTERFACE_MODE_SGMII
:
179 case PHY_INTERFACE_MODE_QSGMII
:
180 return lynx_pcs_config_giga(lynx
->mdio
, ifmode
, advertising
,
182 case PHY_INTERFACE_MODE_2500BASEX
:
183 if (neg_mode
== PHYLINK_PCS_NEG_INBAND_ENABLED
) {
184 dev_err(&lynx
->mdio
->dev
,
185 "AN not supported on 3.125GHz SerDes lane\n");
189 case PHY_INTERFACE_MODE_USXGMII
:
190 return lynx_pcs_config_usxgmii(lynx
->mdio
, advertising
,
192 case PHY_INTERFACE_MODE_10GBASER
:
193 /* Nothing to do here for 10GBASER */
202 static void lynx_pcs_an_restart(struct phylink_pcs
*pcs
)
204 struct lynx_pcs
*lynx
= phylink_pcs_to_lynx(pcs
);
206 phylink_mii_c22_pcs_an_restart(lynx
->mdio
);
209 static void lynx_pcs_link_up_sgmii(struct mdio_device
*pcs
,
210 unsigned int neg_mode
,
211 int speed
, int duplex
)
213 u16 if_mode
= 0, sgmii_speed
;
215 /* The PCS needs to be configured manually only
216 * when not operating on in-band mode
218 if (neg_mode
== PHYLINK_PCS_NEG_INBAND_ENABLED
)
221 if (duplex
== DUPLEX_HALF
)
222 if_mode
|= IF_MODE_HALF_DUPLEX
;
226 sgmii_speed
= SGMII_SPEED_1000
;
229 sgmii_speed
= SGMII_SPEED_100
;
232 sgmii_speed
= SGMII_SPEED_10
;
235 /* Silently don't do anything */
238 dev_err(&pcs
->dev
, "Invalid PCS speed %d\n", speed
);
241 if_mode
|= IF_MODE_SPEED(sgmii_speed
);
243 mdiodev_modify(pcs
, IF_MODE
,
244 IF_MODE_HALF_DUPLEX
| IF_MODE_SPEED_MSK
,
248 /* 2500Base-X is SerDes protocol 7 on Felix and 6 on ENETC. It is a SerDes lane
249 * clocked at 3.125 GHz which encodes symbols with 8b/10b and does not have
250 * auto-negotiation of any link parameters. Electrically it is compatible with
251 * a single lane of XAUI.
252 * The hardware reference manual wants to call this mode SGMII, but it isn't
253 * really, since the fundamental features of SGMII:
254 * - Downgrading the link speed by duplicating symbols
257 * The speed is configured at 1000 in the IF_MODE because the clock frequency
258 * is actually given by a PLL configured in the Reset Configuration Word (RCW).
259 * Since there is no difference between fixed speed SGMII w/o AN and 802.3z w/o
260 * AN, we call this PHY interface type 2500Base-X. In case a PHY negotiates a
261 * lower link speed on line side, the system-side interface remains fixed at
262 * 2500 Mbps and we do rate adaptation through pause frames.
264 static void lynx_pcs_link_up_2500basex(struct mdio_device
*pcs
,
265 unsigned int neg_mode
,
266 int speed
, int duplex
)
270 if (neg_mode
== PHYLINK_PCS_NEG_INBAND_ENABLED
) {
271 dev_err(&pcs
->dev
, "AN not supported for 2500BaseX\n");
275 if (duplex
== DUPLEX_HALF
)
276 if_mode
|= IF_MODE_HALF_DUPLEX
;
277 if_mode
|= IF_MODE_SPEED(SGMII_SPEED_2500
);
279 mdiodev_modify(pcs
, IF_MODE
,
280 IF_MODE_HALF_DUPLEX
| IF_MODE_SPEED_MSK
,
284 static void lynx_pcs_link_up(struct phylink_pcs
*pcs
, unsigned int neg_mode
,
285 phy_interface_t interface
,
286 int speed
, int duplex
)
288 struct lynx_pcs
*lynx
= phylink_pcs_to_lynx(pcs
);
291 case PHY_INTERFACE_MODE_SGMII
:
292 case PHY_INTERFACE_MODE_QSGMII
:
293 lynx_pcs_link_up_sgmii(lynx
->mdio
, neg_mode
, speed
, duplex
);
295 case PHY_INTERFACE_MODE_2500BASEX
:
296 lynx_pcs_link_up_2500basex(lynx
->mdio
, neg_mode
, speed
, duplex
);
298 case PHY_INTERFACE_MODE_USXGMII
:
299 /* At the moment, only in-band AN is supported for USXGMII
300 * so nothing to do in link_up
308 static const struct phylink_pcs_ops lynx_pcs_phylink_ops
= {
309 .pcs_get_state
= lynx_pcs_get_state
,
310 .pcs_config
= lynx_pcs_config
,
311 .pcs_an_restart
= lynx_pcs_an_restart
,
312 .pcs_link_up
= lynx_pcs_link_up
,
315 static struct phylink_pcs
*lynx_pcs_create(struct mdio_device
*mdio
)
317 struct lynx_pcs
*lynx
;
319 lynx
= kzalloc(sizeof(*lynx
), GFP_KERNEL
);
321 return ERR_PTR(-ENOMEM
);
323 mdio_device_get(mdio
);
325 lynx
->pcs
.ops
= &lynx_pcs_phylink_ops
;
326 lynx
->pcs
.neg_mode
= true;
327 lynx
->pcs
.poll
= true;
329 return lynx_to_phylink_pcs(lynx
);
332 struct phylink_pcs
*lynx_pcs_create_mdiodev(struct mii_bus
*bus
, int addr
)
334 struct mdio_device
*mdio
;
335 struct phylink_pcs
*pcs
;
337 mdio
= mdio_device_create(bus
, addr
);
339 return ERR_CAST(mdio
);
341 pcs
= lynx_pcs_create(mdio
);
343 /* lynx_create() has taken a refcount on the mdiodev if it was
344 * successful. If lynx_create() fails, this will free the mdio
345 * device here. In any case, we don't need to hold our reference
346 * anymore, and putting it here will allow mdio_device_put() in
347 * lynx_destroy() to automatically free the mdio device.
349 mdio_device_put(mdio
);
353 EXPORT_SYMBOL(lynx_pcs_create_mdiodev
);
356 * lynx_pcs_create_fwnode() creates a lynx PCS instance from the fwnode
357 * device indicated by node.
360 * -ENODEV if the fwnode is marked unavailable
361 * -EPROBE_DEFER if we fail to find the device
362 * -ENOMEM if we fail to allocate memory
363 * pointer to a phylink_pcs on success
365 struct phylink_pcs
*lynx_pcs_create_fwnode(struct fwnode_handle
*node
)
367 struct mdio_device
*mdio
;
368 struct phylink_pcs
*pcs
;
370 if (!fwnode_device_is_available(node
))
371 return ERR_PTR(-ENODEV
);
373 mdio
= fwnode_mdio_find_device(node
);
375 return ERR_PTR(-EPROBE_DEFER
);
377 pcs
= lynx_pcs_create(mdio
);
379 /* lynx_create() has taken a refcount on the mdiodev if it was
380 * successful. If lynx_create() fails, this will free the mdio
381 * device here. In any case, we don't need to hold our reference
382 * anymore, and putting it here will allow mdio_device_put() in
383 * lynx_destroy() to automatically free the mdio device.
385 mdio_device_put(mdio
);
389 EXPORT_SYMBOL_GPL(lynx_pcs_create_fwnode
);
391 void lynx_pcs_destroy(struct phylink_pcs
*pcs
)
393 struct lynx_pcs
*lynx
= phylink_pcs_to_lynx(pcs
);
395 mdio_device_put(lynx
->mdio
);
398 EXPORT_SYMBOL(lynx_pcs_destroy
);
400 MODULE_DESCRIPTION("NXP Lynx PCS phylink library");
401 MODULE_LICENSE("Dual BSD/GPL");