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>
10 #define SGMII_CLOCK_PERIOD_NS 8 /* PCS is clocked at 125 MHz */
11 #define LINK_TIMER_VAL(ns) ((u32)((ns) / SGMII_CLOCK_PERIOD_NS))
13 #define SGMII_AN_LINK_TIMER_NS 1600000 /* defined by SGMII spec */
15 #define LINK_TIMER_LO 0x12
16 #define LINK_TIMER_HI 0x13
18 #define IF_MODE_SGMII_EN BIT(0)
19 #define IF_MODE_USE_SGMII_AN BIT(1)
20 #define IF_MODE_SPEED(x) (((x) << 2) & GENMASK(3, 2))
21 #define IF_MODE_SPEED_MSK GENMASK(3, 2)
22 #define IF_MODE_HALF_DUPLEX BIT(4)
31 #define phylink_pcs_to_lynx(pl_pcs) container_of((pl_pcs), struct lynx_pcs, pcs)
33 static void lynx_pcs_get_state_usxgmii(struct mdio_device
*pcs
,
34 struct phylink_link_state
*state
)
36 struct mii_bus
*bus
= pcs
->bus
;
40 status
= mdiobus_c45_read(bus
, addr
, MDIO_MMD_VEND2
, MII_BMSR
);
44 state
->link
= !!(status
& MDIO_STAT1_LSTATUS
);
45 state
->an_complete
= !!(status
& MDIO_AN_STAT1_COMPLETE
);
46 if (!state
->link
|| !state
->an_complete
)
49 lpa
= mdiobus_c45_read(bus
, addr
, MDIO_MMD_VEND2
, MII_LPA
);
53 phylink_decode_usxgmii_word(state
, lpa
);
56 static void lynx_pcs_get_state_2500basex(struct mdio_device
*pcs
,
57 struct phylink_link_state
*state
)
59 struct mii_bus
*bus
= pcs
->bus
;
63 bmsr
= mdiobus_read(bus
, addr
, MII_BMSR
);
64 lpa
= mdiobus_read(bus
, addr
, MII_LPA
);
65 if (bmsr
< 0 || lpa
< 0) {
70 state
->link
= !!(bmsr
& BMSR_LSTATUS
);
71 state
->an_complete
= !!(bmsr
& BMSR_ANEGCOMPLETE
);
75 state
->speed
= SPEED_2500
;
76 state
->pause
|= MLO_PAUSE_TX
| MLO_PAUSE_RX
;
77 state
->duplex
= DUPLEX_FULL
;
80 static void lynx_pcs_get_state(struct phylink_pcs
*pcs
,
81 struct phylink_link_state
*state
)
83 struct lynx_pcs
*lynx
= phylink_pcs_to_lynx(pcs
);
85 switch (state
->interface
) {
86 case PHY_INTERFACE_MODE_SGMII
:
87 case PHY_INTERFACE_MODE_QSGMII
:
88 phylink_mii_c22_pcs_get_state(lynx
->mdio
, state
);
90 case PHY_INTERFACE_MODE_2500BASEX
:
91 lynx_pcs_get_state_2500basex(lynx
->mdio
, state
);
93 case PHY_INTERFACE_MODE_USXGMII
:
94 lynx_pcs_get_state_usxgmii(lynx
->mdio
, state
);
96 case PHY_INTERFACE_MODE_10GBASER
:
97 phylink_mii_c45_pcs_get_state(lynx
->mdio
, state
);
103 dev_dbg(&lynx
->mdio
->dev
,
104 "mode=%s/%s/%s link=%u an_enabled=%u an_complete=%u\n",
105 phy_modes(state
->interface
),
106 phy_speed_to_str(state
->speed
),
107 phy_duplex_to_str(state
->duplex
),
108 state
->link
, state
->an_enabled
, state
->an_complete
);
111 static int lynx_pcs_config_sgmii(struct mdio_device
*pcs
, unsigned int mode
,
112 const unsigned long *advertising
)
114 struct mii_bus
*bus
= pcs
->bus
;
115 int addr
= pcs
->addr
;
119 if_mode
= IF_MODE_SGMII_EN
;
120 if (mode
== MLO_AN_INBAND
) {
123 if_mode
|= IF_MODE_USE_SGMII_AN
;
125 /* Adjust link timer for SGMII */
126 link_timer
= LINK_TIMER_VAL(SGMII_AN_LINK_TIMER_NS
);
127 mdiobus_write(bus
, addr
, LINK_TIMER_LO
, link_timer
& 0xffff);
128 mdiobus_write(bus
, addr
, LINK_TIMER_HI
, link_timer
>> 16);
130 err
= mdiobus_modify(bus
, addr
, IF_MODE
,
131 IF_MODE_SGMII_EN
| IF_MODE_USE_SGMII_AN
,
136 return phylink_mii_c22_pcs_config(pcs
, mode
, PHY_INTERFACE_MODE_SGMII
,
140 static int lynx_pcs_config_usxgmii(struct mdio_device
*pcs
, unsigned int mode
,
141 const unsigned long *advertising
)
143 struct mii_bus
*bus
= pcs
->bus
;
144 int addr
= pcs
->addr
;
146 if (!phylink_autoneg_inband(mode
)) {
147 dev_err(&pcs
->dev
, "USXGMII only supports in-band AN for now\n");
151 /* Configure device ability for the USXGMII Replicator */
152 return mdiobus_c45_write(bus
, addr
, MDIO_MMD_VEND2
, MII_ADVERTISE
,
153 MDIO_USXGMII_10G
| MDIO_USXGMII_LINK
|
154 MDIO_USXGMII_FULL_DUPLEX
|
155 ADVERTISE_SGMII
| ADVERTISE_LPACK
);
158 static int lynx_pcs_config(struct phylink_pcs
*pcs
, unsigned int mode
,
159 phy_interface_t ifmode
,
160 const unsigned long *advertising
,
163 struct lynx_pcs
*lynx
= phylink_pcs_to_lynx(pcs
);
166 case PHY_INTERFACE_MODE_SGMII
:
167 case PHY_INTERFACE_MODE_QSGMII
:
168 return lynx_pcs_config_sgmii(lynx
->mdio
, mode
, advertising
);
169 case PHY_INTERFACE_MODE_2500BASEX
:
170 if (phylink_autoneg_inband(mode
)) {
171 dev_err(&lynx
->mdio
->dev
,
172 "AN not supported on 3.125GHz SerDes lane\n");
176 case PHY_INTERFACE_MODE_USXGMII
:
177 return lynx_pcs_config_usxgmii(lynx
->mdio
, mode
, advertising
);
178 case PHY_INTERFACE_MODE_10GBASER
:
179 /* Nothing to do here for 10GBASER */
188 static void lynx_pcs_link_up_sgmii(struct mdio_device
*pcs
, unsigned int mode
,
189 int speed
, int duplex
)
191 struct mii_bus
*bus
= pcs
->bus
;
192 u16 if_mode
= 0, sgmii_speed
;
193 int addr
= pcs
->addr
;
195 /* The PCS needs to be configured manually only
196 * when not operating on in-band mode
198 if (mode
== MLO_AN_INBAND
)
201 if (duplex
== DUPLEX_HALF
)
202 if_mode
|= IF_MODE_HALF_DUPLEX
;
206 sgmii_speed
= SGMII_SPEED_1000
;
209 sgmii_speed
= SGMII_SPEED_100
;
212 sgmii_speed
= SGMII_SPEED_10
;
215 /* Silently don't do anything */
218 dev_err(&pcs
->dev
, "Invalid PCS speed %d\n", speed
);
221 if_mode
|= IF_MODE_SPEED(sgmii_speed
);
223 mdiobus_modify(bus
, addr
, IF_MODE
,
224 IF_MODE_HALF_DUPLEX
| IF_MODE_SPEED_MSK
,
228 /* 2500Base-X is SerDes protocol 7 on Felix and 6 on ENETC. It is a SerDes lane
229 * clocked at 3.125 GHz which encodes symbols with 8b/10b and does not have
230 * auto-negotiation of any link parameters. Electrically it is compatible with
231 * a single lane of XAUI.
232 * The hardware reference manual wants to call this mode SGMII, but it isn't
233 * really, since the fundamental features of SGMII:
234 * - Downgrading the link speed by duplicating symbols
237 * The speed is configured at 1000 in the IF_MODE because the clock frequency
238 * is actually given by a PLL configured in the Reset Configuration Word (RCW).
239 * Since there is no difference between fixed speed SGMII w/o AN and 802.3z w/o
240 * AN, we call this PHY interface type 2500Base-X. In case a PHY negotiates a
241 * lower link speed on line side, the system-side interface remains fixed at
242 * 2500 Mbps and we do rate adaptation through pause frames.
244 static void lynx_pcs_link_up_2500basex(struct mdio_device
*pcs
,
246 int speed
, int duplex
)
248 struct mii_bus
*bus
= pcs
->bus
;
249 int addr
= pcs
->addr
;
252 if (mode
== MLO_AN_INBAND
) {
253 dev_err(&pcs
->dev
, "AN not supported for 2500BaseX\n");
257 if (duplex
== DUPLEX_HALF
)
258 if_mode
|= IF_MODE_HALF_DUPLEX
;
259 if_mode
|= IF_MODE_SPEED(SGMII_SPEED_2500
);
261 mdiobus_modify(bus
, addr
, IF_MODE
,
262 IF_MODE_HALF_DUPLEX
| IF_MODE_SPEED_MSK
,
266 static void lynx_pcs_link_up(struct phylink_pcs
*pcs
, unsigned int mode
,
267 phy_interface_t interface
,
268 int speed
, int duplex
)
270 struct lynx_pcs
*lynx
= phylink_pcs_to_lynx(pcs
);
273 case PHY_INTERFACE_MODE_SGMII
:
274 case PHY_INTERFACE_MODE_QSGMII
:
275 lynx_pcs_link_up_sgmii(lynx
->mdio
, mode
, speed
, duplex
);
277 case PHY_INTERFACE_MODE_2500BASEX
:
278 lynx_pcs_link_up_2500basex(lynx
->mdio
, mode
, speed
, duplex
);
280 case PHY_INTERFACE_MODE_USXGMII
:
281 /* At the moment, only in-band AN is supported for USXGMII
282 * so nothing to do in link_up
290 static const struct phylink_pcs_ops lynx_pcs_phylink_ops
= {
291 .pcs_get_state
= lynx_pcs_get_state
,
292 .pcs_config
= lynx_pcs_config
,
293 .pcs_link_up
= lynx_pcs_link_up
,
296 struct lynx_pcs
*lynx_pcs_create(struct mdio_device
*mdio
)
298 struct lynx_pcs
*lynx_pcs
;
300 lynx_pcs
= kzalloc(sizeof(*lynx_pcs
), GFP_KERNEL
);
304 lynx_pcs
->mdio
= mdio
;
305 lynx_pcs
->pcs
.ops
= &lynx_pcs_phylink_ops
;
306 lynx_pcs
->pcs
.poll
= true;
310 EXPORT_SYMBOL(lynx_pcs_create
);
312 void lynx_pcs_destroy(struct lynx_pcs
*pcs
)
316 EXPORT_SYMBOL(lynx_pcs_destroy
);
318 MODULE_LICENSE("Dual BSD/GPL");