1 // SPDX-License-Identifier: GPL-2.0+ 2 /* Microchip Sparx5 Switch driver 3 * 4 * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries. 5 */ 6 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> 12 13 #include "sparx5_main_regs.h" 14 #include "sparx5_main.h" 15 #include "sparx5_port.h" 16 17 static bool port_conf_has_changed(struct sparx5_port_config *a, struct sparx5_port_config *b) 18 { 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 || 24 a->media != b->media) 25 return true; 26 return false; 27 } 28 29 static struct phylink_pcs * 30 sparx5_phylink_mac_select_pcs(struct phylink_config *config, 31 phy_interface_t interface) 32 { 33 struct sparx5_port *port = netdev_priv(to_net_dev(config->dev)); 34 35 /* Return the PCS for all the modes that require it. */ 36 switch (interface) { 37 case PHY_INTERFACE_MODE_SGMII: 38 case PHY_INTERFACE_MODE_QSGMII: 39 case PHY_INTERFACE_MODE_1000BASEX: 40 case PHY_INTERFACE_MODE_2500BASEX: 41 case PHY_INTERFACE_MODE_5GBASER: 42 case PHY_INTERFACE_MODE_10GBASER: 43 case PHY_INTERFACE_MODE_25GBASER: 44 return &port->phylink_pcs; 45 default: 46 return NULL; 47 } 48 } 49 50 static void sparx5_phylink_mac_config(struct phylink_config *config, 51 unsigned int mode, 52 const struct phylink_link_state *state) 53 { 54 /* Currently not used */ 55 } 56 57 static void sparx5_phylink_mac_link_up(struct phylink_config *config, 58 struct phy_device *phy, 59 unsigned int mode, 60 phy_interface_t interface, 61 int speed, int duplex, 62 bool tx_pause, bool rx_pause) 63 { 64 struct sparx5_port *port = netdev_priv(to_net_dev(config->dev)); 65 struct sparx5_port_config conf; 66 int err; 67 68 conf = port->conf; 69 conf.duplex = duplex; 70 conf.pause = 0; 71 conf.pause |= tx_pause ? MLO_PAUSE_TX : 0; 72 conf.pause |= rx_pause ? MLO_PAUSE_RX : 0; 73 conf.speed = speed; 74 /* Configure the port to speed/duplex/pause */ 75 err = sparx5_port_config(port->sparx5, port, &conf); 76 if (err) 77 netdev_err(port->ndev, "port config failed: %d\n", err); 78 } 79 80 static void sparx5_phylink_mac_link_down(struct phylink_config *config, 81 unsigned int mode, 82 phy_interface_t interface) 83 { 84 /* Currently not used */ 85 } 86 87 static struct sparx5_port *sparx5_pcs_to_port(struct phylink_pcs *pcs) 88 { 89 return container_of(pcs, struct sparx5_port, phylink_pcs); 90 } 91 92 static void sparx5_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode, 93 struct phylink_link_state *state) 94 { 95 struct sparx5_port *port = sparx5_pcs_to_port(pcs); 96 struct sparx5_port_status status; 97 98 sparx5_get_port_status(port->sparx5, port, &status); 99 state->link = status.link && !status.link_down; 100 state->an_complete = status.an_complete; 101 state->speed = status.speed; 102 state->duplex = status.duplex; 103 state->pause = status.pause; 104 } 105 106 static int sparx5_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, 107 phy_interface_t interface, 108 const unsigned long *advertising, 109 bool permit_pause_to_mac) 110 { 111 struct sparx5_port *port = sparx5_pcs_to_port(pcs); 112 struct sparx5_port_config conf; 113 int ret = 0; 114 115 conf = port->conf; 116 conf.power_down = false; 117 conf.portmode = interface; 118 conf.inband = neg_mode == PHYLINK_PCS_NEG_INBAND_DISABLED || 119 neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED; 120 conf.autoneg = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED; 121 conf.pause_adv = 0; 122 if (phylink_test(advertising, Pause)) 123 conf.pause_adv |= ADVERTISE_1000XPAUSE; 124 if (phylink_test(advertising, Asym_Pause)) 125 conf.pause_adv |= ADVERTISE_1000XPSE_ASYM; 126 if (sparx5_is_baser(interface)) { 127 if (phylink_test(advertising, FIBRE)) 128 conf.media = PHY_MEDIA_SR; 129 else 130 conf.media = PHY_MEDIA_DAC; 131 } 132 if (!port_conf_has_changed(&port->conf, &conf)) 133 return ret; 134 /* Enable the PCS matching this interface type */ 135 ret = sparx5_port_pcs_set(port->sparx5, port, &conf); 136 if (ret) 137 netdev_err(port->ndev, "port PCS config failed: %d\n", ret); 138 return ret; 139 } 140 141 static void sparx5_pcs_aneg_restart(struct phylink_pcs *pcs) 142 { 143 /* Currently not used */ 144 } 145 146 const struct phylink_pcs_ops sparx5_phylink_pcs_ops = { 147 .pcs_get_state = sparx5_pcs_get_state, 148 .pcs_config = sparx5_pcs_config, 149 .pcs_an_restart = sparx5_pcs_aneg_restart, 150 }; 151 152 const struct phylink_mac_ops sparx5_phylink_mac_ops = { 153 .mac_select_pcs = sparx5_phylink_mac_select_pcs, 154 .mac_config = sparx5_phylink_mac_config, 155 .mac_link_down = sparx5_phylink_mac_link_down, 156 .mac_link_up = sparx5_phylink_mac_link_up, 157 }; 158