1 // SPDX-License-Identifier: GPL-2.0+
2 /* Microchip Sparx5 Switch driver
4 * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
7 #include <linux/module.h>
8 #include <linux/phylink.h>
9 #include <linux/device.h>
10 #include <linux/netdevice.h>
11 #include <linux/sfp.h>
13 #include "sparx5_main_regs.h"
14 #include "sparx5_main.h"
15 #include "sparx5_port.h"
17 static bool port_conf_has_changed(struct sparx5_port_config
*a
, struct sparx5_port_config
*b
)
19 if (a
->speed
!= b
->speed
||
20 a
->portmode
!= b
->portmode
||
21 a
->autoneg
!= b
->autoneg
||
22 a
->pause_adv
!= b
->pause_adv
||
23 a
->power_down
!= b
->power_down
||
29 static struct phylink_pcs
*
30 sparx5_phylink_mac_select_pcs(struct phylink_config
*config
,
31 phy_interface_t interface
)
33 struct sparx5_port
*port
= netdev_priv(to_net_dev(config
->dev
));
35 return &port
->phylink_pcs
;
38 static void sparx5_phylink_mac_config(struct phylink_config
*config
,
40 const struct phylink_link_state
*state
)
42 /* Currently not used */
45 static void sparx5_phylink_mac_link_up(struct phylink_config
*config
,
46 struct phy_device
*phy
,
48 phy_interface_t interface
,
49 int speed
, int duplex
,
50 bool tx_pause
, bool rx_pause
)
52 struct sparx5_port
*port
= netdev_priv(to_net_dev(config
->dev
));
53 struct sparx5_port_config conf
;
59 conf
.pause
|= tx_pause
? MLO_PAUSE_TX
: 0;
60 conf
.pause
|= rx_pause
? MLO_PAUSE_RX
: 0;
62 /* Configure the port to speed/duplex/pause */
63 err
= sparx5_port_config(port
->sparx5
, port
, &conf
);
65 netdev_err(port
->ndev
, "port config failed: %d\n", err
);
68 static void sparx5_phylink_mac_link_down(struct phylink_config
*config
,
70 phy_interface_t interface
)
72 /* Currently not used */
75 static struct sparx5_port
*sparx5_pcs_to_port(struct phylink_pcs
*pcs
)
77 return container_of(pcs
, struct sparx5_port
, phylink_pcs
);
80 static void sparx5_pcs_get_state(struct phylink_pcs
*pcs
,
81 struct phylink_link_state
*state
)
83 struct sparx5_port
*port
= sparx5_pcs_to_port(pcs
);
84 struct sparx5_port_status status
;
86 sparx5_get_port_status(port
->sparx5
, port
, &status
);
87 state
->link
= status
.link
&& !status
.link_down
;
88 state
->an_complete
= status
.an_complete
;
89 state
->speed
= status
.speed
;
90 state
->duplex
= status
.duplex
;
91 state
->pause
= status
.pause
;
94 static int sparx5_pcs_config(struct phylink_pcs
*pcs
, unsigned int neg_mode
,
95 phy_interface_t interface
,
96 const unsigned long *advertising
,
97 bool permit_pause_to_mac
)
99 struct sparx5_port
*port
= sparx5_pcs_to_port(pcs
);
100 struct sparx5_port_config conf
;
104 conf
.power_down
= false;
105 conf
.portmode
= interface
;
106 conf
.inband
= neg_mode
== PHYLINK_PCS_NEG_INBAND_DISABLED
||
107 neg_mode
== PHYLINK_PCS_NEG_INBAND_ENABLED
;
108 conf
.autoneg
= neg_mode
== PHYLINK_PCS_NEG_INBAND_ENABLED
;
110 if (phylink_test(advertising
, Pause
))
111 conf
.pause_adv
|= ADVERTISE_1000XPAUSE
;
112 if (phylink_test(advertising
, Asym_Pause
))
113 conf
.pause_adv
|= ADVERTISE_1000XPSE_ASYM
;
114 if (sparx5_is_baser(interface
)) {
115 if (phylink_test(advertising
, FIBRE
))
116 conf
.media
= PHY_MEDIA_SR
;
118 conf
.media
= PHY_MEDIA_DAC
;
120 if (!port_conf_has_changed(&port
->conf
, &conf
))
122 /* Enable the PCS matching this interface type */
123 ret
= sparx5_port_pcs_set(port
->sparx5
, port
, &conf
);
125 netdev_err(port
->ndev
, "port PCS config failed: %d\n", ret
);
129 static void sparx5_pcs_aneg_restart(struct phylink_pcs
*pcs
)
131 /* Currently not used */
134 const struct phylink_pcs_ops sparx5_phylink_pcs_ops
= {
135 .pcs_get_state
= sparx5_pcs_get_state
,
136 .pcs_config
= sparx5_pcs_config
,
137 .pcs_an_restart
= sparx5_pcs_aneg_restart
,
140 const struct phylink_mac_ops sparx5_phylink_mac_ops
= {
141 .mac_select_pcs
= sparx5_phylink_mac_select_pcs
,
142 .mac_config
= sparx5_phylink_mac_config
,
143 .mac_link_down
= sparx5_phylink_mac_link_down
,
144 .mac_link_up
= sparx5_phylink_mac_link_up
,