linux/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
<<
>>
Prefs
   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
  17static 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
  29static void sparx5_phylink_validate(struct phylink_config *config,
  30                                    unsigned long *supported,
  31                                    struct phylink_link_state *state)
  32{
  33        struct sparx5_port *port = netdev_priv(to_net_dev(config->dev));
  34        __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
  35
  36        phylink_set(mask, Autoneg);
  37        phylink_set_port_modes(mask);
  38        phylink_set(mask, Pause);
  39        phylink_set(mask, Asym_Pause);
  40
  41        switch (state->interface) {
  42        case PHY_INTERFACE_MODE_5GBASER:
  43        case PHY_INTERFACE_MODE_10GBASER:
  44        case PHY_INTERFACE_MODE_25GBASER:
  45        case PHY_INTERFACE_MODE_NA:
  46                if (port->conf.bandwidth == SPEED_5000)
  47                        phylink_set(mask, 5000baseT_Full);
  48                if (port->conf.bandwidth == SPEED_10000) {
  49                        phylink_set(mask, 5000baseT_Full);
  50                        phylink_set(mask, 10000baseT_Full);
  51                        phylink_set(mask, 10000baseCR_Full);
  52                        phylink_set(mask, 10000baseSR_Full);
  53                        phylink_set(mask, 10000baseLR_Full);
  54                        phylink_set(mask, 10000baseLRM_Full);
  55                        phylink_set(mask, 10000baseER_Full);
  56                }
  57                if (port->conf.bandwidth == SPEED_25000) {
  58                        phylink_set(mask, 5000baseT_Full);
  59                        phylink_set(mask, 10000baseT_Full);
  60                        phylink_set(mask, 10000baseCR_Full);
  61                        phylink_set(mask, 10000baseSR_Full);
  62                        phylink_set(mask, 10000baseLR_Full);
  63                        phylink_set(mask, 10000baseLRM_Full);
  64                        phylink_set(mask, 10000baseER_Full);
  65                        phylink_set(mask, 25000baseCR_Full);
  66                        phylink_set(mask, 25000baseSR_Full);
  67                }
  68                if (state->interface != PHY_INTERFACE_MODE_NA)
  69                        break;
  70                fallthrough;
  71        case PHY_INTERFACE_MODE_SGMII:
  72        case PHY_INTERFACE_MODE_QSGMII:
  73                phylink_set(mask, 10baseT_Half);
  74                phylink_set(mask, 10baseT_Full);
  75                phylink_set(mask, 100baseT_Half);
  76                phylink_set(mask, 100baseT_Full);
  77                phylink_set(mask, 1000baseT_Full);
  78                phylink_set(mask, 1000baseX_Full);
  79                if (state->interface != PHY_INTERFACE_MODE_NA)
  80                        break;
  81                fallthrough;
  82        case PHY_INTERFACE_MODE_1000BASEX:
  83        case PHY_INTERFACE_MODE_2500BASEX:
  84                if (state->interface != PHY_INTERFACE_MODE_2500BASEX) {
  85                        phylink_set(mask, 1000baseT_Full);
  86                        phylink_set(mask, 1000baseX_Full);
  87                }
  88                if (state->interface == PHY_INTERFACE_MODE_2500BASEX ||
  89                    state->interface == PHY_INTERFACE_MODE_NA) {
  90                        phylink_set(mask, 2500baseT_Full);
  91                        phylink_set(mask, 2500baseX_Full);
  92                }
  93                break;
  94        default:
  95                bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
  96                return;
  97        }
  98        bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
  99        bitmap_and(state->advertising, state->advertising, mask,
 100                   __ETHTOOL_LINK_MODE_MASK_NBITS);
 101}
 102
 103static void sparx5_phylink_mac_config(struct phylink_config *config,
 104                                      unsigned int mode,
 105                                      const struct phylink_link_state *state)
 106{
 107        /* Currently not used */
 108}
 109
 110static void sparx5_phylink_mac_link_up(struct phylink_config *config,
 111                                       struct phy_device *phy,
 112                                       unsigned int mode,
 113                                       phy_interface_t interface,
 114                                       int speed, int duplex,
 115                                       bool tx_pause, bool rx_pause)
 116{
 117        struct sparx5_port *port = netdev_priv(to_net_dev(config->dev));
 118        struct sparx5_port_config conf;
 119        int err;
 120
 121        conf = port->conf;
 122        conf.duplex = duplex;
 123        conf.pause = 0;
 124        conf.pause |= tx_pause ? MLO_PAUSE_TX : 0;
 125        conf.pause |= rx_pause ? MLO_PAUSE_RX : 0;
 126        conf.speed = speed;
 127        /* Configure the port to speed/duplex/pause */
 128        err = sparx5_port_config(port->sparx5, port, &conf);
 129        if (err)
 130                netdev_err(port->ndev, "port config failed: %d\n", err);
 131}
 132
 133static void sparx5_phylink_mac_link_down(struct phylink_config *config,
 134                                         unsigned int mode,
 135                                         phy_interface_t interface)
 136{
 137        /* Currently not used */
 138}
 139
 140static struct sparx5_port *sparx5_pcs_to_port(struct phylink_pcs *pcs)
 141{
 142        return container_of(pcs, struct sparx5_port, phylink_pcs);
 143}
 144
 145static void sparx5_pcs_get_state(struct phylink_pcs *pcs,
 146                                 struct phylink_link_state *state)
 147{
 148        struct sparx5_port *port = sparx5_pcs_to_port(pcs);
 149        struct sparx5_port_status status;
 150
 151        sparx5_get_port_status(port->sparx5, port, &status);
 152        state->link = status.link && !status.link_down;
 153        state->an_complete = status.an_complete;
 154        state->speed = status.speed;
 155        state->duplex = status.duplex;
 156        state->pause = status.pause;
 157}
 158
 159static int sparx5_pcs_config(struct phylink_pcs *pcs,
 160                             unsigned int mode,
 161                             phy_interface_t interface,
 162                             const unsigned long *advertising,
 163                             bool permit_pause_to_mac)
 164{
 165        struct sparx5_port *port = sparx5_pcs_to_port(pcs);
 166        struct sparx5_port_config conf;
 167        int ret = 0;
 168
 169        conf = port->conf;
 170        conf.power_down = false;
 171        conf.portmode = interface;
 172        conf.inband = phylink_autoneg_inband(mode);
 173        conf.autoneg = phylink_test(advertising, Autoneg);
 174        conf.pause_adv = 0;
 175        if (phylink_test(advertising, Pause))
 176                conf.pause_adv |= ADVERTISE_1000XPAUSE;
 177        if (phylink_test(advertising, Asym_Pause))
 178                conf.pause_adv |= ADVERTISE_1000XPSE_ASYM;
 179        if (sparx5_is_baser(interface)) {
 180                if (phylink_test(advertising, FIBRE))
 181                        conf.media = PHY_MEDIA_SR;
 182                else
 183                        conf.media = PHY_MEDIA_DAC;
 184        }
 185        if (!port_conf_has_changed(&port->conf, &conf))
 186                return ret;
 187        /* Enable the PCS matching this interface type */
 188        ret = sparx5_port_pcs_set(port->sparx5, port, &conf);
 189        if (ret)
 190                netdev_err(port->ndev, "port PCS config failed: %d\n", ret);
 191        return ret;
 192}
 193
 194static void sparx5_pcs_aneg_restart(struct phylink_pcs *pcs)
 195{
 196        /* Currently not used */
 197}
 198
 199const struct phylink_pcs_ops sparx5_phylink_pcs_ops = {
 200        .pcs_get_state = sparx5_pcs_get_state,
 201        .pcs_config = sparx5_pcs_config,
 202        .pcs_an_restart = sparx5_pcs_aneg_restart,
 203};
 204
 205const struct phylink_mac_ops sparx5_phylink_mac_ops = {
 206        .validate = sparx5_phylink_validate,
 207        .mac_config = sparx5_phylink_mac_config,
 208        .mac_link_down = sparx5_phylink_mac_link_down,
 209        .mac_link_up = sparx5_phylink_mac_link_up,
 210};
 211