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 void sparx5_phylink_mac_config(struct phylink_config *config, 30 unsigned int mode, 31 const struct phylink_link_state *state) 32 { 33 /* Currently not used */ 34 } 35 36 static void sparx5_phylink_mac_link_up(struct phylink_config *config, 37 struct phy_device *phy, 38 unsigned int mode, 39 phy_interface_t interface, 40 int speed, int duplex, 41 bool tx_pause, bool rx_pause) 42 { 43 struct sparx5_port *port = netdev_priv(to_net_dev(config->dev)); 44 struct sparx5_port_config conf; 45 int err; 46 47 conf = port->conf; 48 conf.duplex = duplex; 49 conf.pause = 0; 50 conf.pause |= tx_pause ? MLO_PAUSE_TX : 0; 51 conf.pause |= rx_pause ? MLO_PAUSE_RX : 0; 52 conf.speed = speed; 53 /* Configure the port to speed/duplex/pause */ 54 err = sparx5_port_config(port->sparx5, port, &conf); 55 if (err) 56 netdev_err(port->ndev, "port config failed: %d\n", err); 57 } 58 59 static void sparx5_phylink_mac_link_down(struct phylink_config *config, 60 unsigned int mode, 61 phy_interface_t interface) 62 { 63 /* Currently not used */ 64 } 65 66 static struct sparx5_port *sparx5_pcs_to_port(struct phylink_pcs *pcs) 67 { 68 return container_of(pcs, struct sparx5_port, phylink_pcs); 69 } 70 71 static void sparx5_pcs_get_state(struct phylink_pcs *pcs, 72 struct phylink_link_state *state) 73 { 74 struct sparx5_port *port = sparx5_pcs_to_port(pcs); 75 struct sparx5_port_status status; 76 77 sparx5_get_port_status(port->sparx5, port, &status); 78 state->link = status.link && !status.link_down; 79 state->an_complete = status.an_complete; 80 state->speed = status.speed; 81 state->duplex = status.duplex; 82 state->pause = status.pause; 83 } 84 85 static int sparx5_pcs_config(struct phylink_pcs *pcs, 86 unsigned int mode, 87 phy_interface_t interface, 88 const unsigned long *advertising, 89 bool permit_pause_to_mac) 90 { 91 struct sparx5_port *port = sparx5_pcs_to_port(pcs); 92 struct sparx5_port_config conf; 93 int ret = 0; 94 95 conf = port->conf; 96 conf.power_down = false; 97 conf.portmode = interface; 98 conf.inband = phylink_autoneg_inband(mode); 99 conf.autoneg = phylink_test(advertising, Autoneg); 100 conf.pause_adv = 0; 101 if (phylink_test(advertising, Pause)) 102 conf.pause_adv |= ADVERTISE_1000XPAUSE; 103 if (phylink_test(advertising, Asym_Pause)) 104 conf.pause_adv |= ADVERTISE_1000XPSE_ASYM; 105 if (sparx5_is_baser(interface)) { 106 if (phylink_test(advertising, FIBRE)) 107 conf.media = PHY_MEDIA_SR; 108 else 109 conf.media = PHY_MEDIA_DAC; 110 } 111 if (!port_conf_has_changed(&port->conf, &conf)) 112 return ret; 113 /* Enable the PCS matching this interface type */ 114 ret = sparx5_port_pcs_set(port->sparx5, port, &conf); 115 if (ret) 116 netdev_err(port->ndev, "port PCS config failed: %d\n", ret); 117 return ret; 118 } 119 120 static void sparx5_pcs_aneg_restart(struct phylink_pcs *pcs) 121 { 122 /* Currently not used */ 123 } 124 125 const struct phylink_pcs_ops sparx5_phylink_pcs_ops = { 126 .pcs_get_state = sparx5_pcs_get_state, 127 .pcs_config = sparx5_pcs_config, 128 .pcs_an_restart = sparx5_pcs_aneg_restart, 129 }; 130 131 const struct phylink_mac_ops sparx5_phylink_mac_ops = { 132 .validate = phylink_generic_validate, 133 .mac_config = sparx5_phylink_mac_config, 134 .mac_link_down = sparx5_phylink_mac_link_down, 135 .mac_link_up = sparx5_phylink_mac_link_up, 136 }; 137