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