Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /* Microchip Sparx5 Switch driver
0003  *
0004  * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
0005  */
0006 
0007 #include <linux/module.h>
0008 #include <linux/phylink.h>
0009 #include <linux/device.h>
0010 #include <linux/netdevice.h>
0011 #include <linux/sfp.h>
0012 
0013 #include "sparx5_main_regs.h"
0014 #include "sparx5_main.h"
0015 #include "sparx5_port.h"
0016 
0017 static bool port_conf_has_changed(struct sparx5_port_config *a, struct sparx5_port_config *b)
0018 {
0019     if (a->speed != b->speed ||
0020         a->portmode != b->portmode ||
0021         a->autoneg != b->autoneg ||
0022         a->pause_adv != b->pause_adv ||
0023         a->power_down != b->power_down ||
0024         a->media != b->media)
0025         return true;
0026     return false;
0027 }
0028 
0029 static struct phylink_pcs *
0030 sparx5_phylink_mac_select_pcs(struct phylink_config *config,
0031                   phy_interface_t interface)
0032 {
0033     struct sparx5_port *port = netdev_priv(to_net_dev(config->dev));
0034 
0035     return &port->phylink_pcs;
0036 }
0037 
0038 static void sparx5_phylink_mac_config(struct phylink_config *config,
0039                       unsigned int mode,
0040                       const struct phylink_link_state *state)
0041 {
0042     /* Currently not used */
0043 }
0044 
0045 static void sparx5_phylink_mac_link_up(struct phylink_config *config,
0046                        struct phy_device *phy,
0047                        unsigned int mode,
0048                        phy_interface_t interface,
0049                        int speed, int duplex,
0050                        bool tx_pause, bool rx_pause)
0051 {
0052     struct sparx5_port *port = netdev_priv(to_net_dev(config->dev));
0053     struct sparx5_port_config conf;
0054     int err;
0055 
0056     conf = port->conf;
0057     conf.duplex = duplex;
0058     conf.pause = 0;
0059     conf.pause |= tx_pause ? MLO_PAUSE_TX : 0;
0060     conf.pause |= rx_pause ? MLO_PAUSE_RX : 0;
0061     conf.speed = speed;
0062     /* Configure the port to speed/duplex/pause */
0063     err = sparx5_port_config(port->sparx5, port, &conf);
0064     if (err)
0065         netdev_err(port->ndev, "port config failed: %d\n", err);
0066 }
0067 
0068 static void sparx5_phylink_mac_link_down(struct phylink_config *config,
0069                      unsigned int mode,
0070                      phy_interface_t interface)
0071 {
0072     /* Currently not used */
0073 }
0074 
0075 static struct sparx5_port *sparx5_pcs_to_port(struct phylink_pcs *pcs)
0076 {
0077     return container_of(pcs, struct sparx5_port, phylink_pcs);
0078 }
0079 
0080 static void sparx5_pcs_get_state(struct phylink_pcs *pcs,
0081                  struct phylink_link_state *state)
0082 {
0083     struct sparx5_port *port = sparx5_pcs_to_port(pcs);
0084     struct sparx5_port_status status;
0085 
0086     sparx5_get_port_status(port->sparx5, port, &status);
0087     state->link = status.link && !status.link_down;
0088     state->an_complete = status.an_complete;
0089     state->speed = status.speed;
0090     state->duplex = status.duplex;
0091     state->pause = status.pause;
0092 }
0093 
0094 static int sparx5_pcs_config(struct phylink_pcs *pcs,
0095                  unsigned int mode,
0096                  phy_interface_t interface,
0097                  const unsigned long *advertising,
0098                  bool permit_pause_to_mac)
0099 {
0100     struct sparx5_port *port = sparx5_pcs_to_port(pcs);
0101     struct sparx5_port_config conf;
0102     int ret = 0;
0103 
0104     conf = port->conf;
0105     conf.power_down = false;
0106     conf.portmode = interface;
0107     conf.inband = phylink_autoneg_inband(mode);
0108     conf.autoneg = phylink_test(advertising, Autoneg);
0109     conf.pause_adv = 0;
0110     if (phylink_test(advertising, Pause))
0111         conf.pause_adv |= ADVERTISE_1000XPAUSE;
0112     if (phylink_test(advertising, Asym_Pause))
0113         conf.pause_adv |= ADVERTISE_1000XPSE_ASYM;
0114     if (sparx5_is_baser(interface)) {
0115         if (phylink_test(advertising, FIBRE))
0116             conf.media = PHY_MEDIA_SR;
0117         else
0118             conf.media = PHY_MEDIA_DAC;
0119     }
0120     if (!port_conf_has_changed(&port->conf, &conf))
0121         return ret;
0122     /* Enable the PCS matching this interface type */
0123     ret = sparx5_port_pcs_set(port->sparx5, port, &conf);
0124     if (ret)
0125         netdev_err(port->ndev, "port PCS config failed: %d\n", ret);
0126     return ret;
0127 }
0128 
0129 static void sparx5_pcs_aneg_restart(struct phylink_pcs *pcs)
0130 {
0131     /* Currently not used */
0132 }
0133 
0134 const struct phylink_pcs_ops sparx5_phylink_pcs_ops = {
0135     .pcs_get_state = sparx5_pcs_get_state,
0136     .pcs_config = sparx5_pcs_config,
0137     .pcs_an_restart = sparx5_pcs_aneg_restart,
0138 };
0139 
0140 const struct phylink_mac_ops sparx5_phylink_mac_ops = {
0141     .validate = phylink_generic_validate,
0142     .mac_select_pcs = sparx5_phylink_mac_select_pcs,
0143     .mac_config = sparx5_phylink_mac_config,
0144     .mac_link_down = sparx5_phylink_mac_link_down,
0145     .mac_link_up = sparx5_phylink_mac_link_up,
0146 };