162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/module.h>
462306a36Sopenharmony_ci#include <linux/phylink.h>
562306a36Sopenharmony_ci#include <linux/device.h>
662306a36Sopenharmony_ci#include <linux/netdevice.h>
762306a36Sopenharmony_ci#include <linux/phy/phy.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "lan966x_main.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic struct phylink_pcs *lan966x_phylink_mac_select(struct phylink_config *config,
1262306a36Sopenharmony_ci						      phy_interface_t interface)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	struct lan966x_port *port = netdev_priv(to_net_dev(config->dev));
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci	return &port->phylink_pcs;
1762306a36Sopenharmony_ci}
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic void lan966x_phylink_mac_config(struct phylink_config *config,
2062306a36Sopenharmony_ci				       unsigned int mode,
2162306a36Sopenharmony_ci				       const struct phylink_link_state *state)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int lan966x_phylink_mac_prepare(struct phylink_config *config,
2662306a36Sopenharmony_ci				       unsigned int mode,
2762306a36Sopenharmony_ci				       phy_interface_t iface)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	struct lan966x_port *port = netdev_priv(to_net_dev(config->dev));
3062306a36Sopenharmony_ci	phy_interface_t serdes_mode = iface;
3162306a36Sopenharmony_ci	int err;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	if (port->serdes) {
3462306a36Sopenharmony_ci		err = phy_set_mode_ext(port->serdes, PHY_MODE_ETHERNET,
3562306a36Sopenharmony_ci				       serdes_mode);
3662306a36Sopenharmony_ci		if (err) {
3762306a36Sopenharmony_ci			netdev_err(to_net_dev(config->dev),
3862306a36Sopenharmony_ci				   "Could not set mode of SerDes\n");
3962306a36Sopenharmony_ci			return err;
4062306a36Sopenharmony_ci		}
4162306a36Sopenharmony_ci	}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	return 0;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic void lan966x_phylink_mac_link_up(struct phylink_config *config,
4762306a36Sopenharmony_ci					struct phy_device *phy,
4862306a36Sopenharmony_ci					unsigned int mode,
4962306a36Sopenharmony_ci					phy_interface_t interface,
5062306a36Sopenharmony_ci					int speed, int duplex,
5162306a36Sopenharmony_ci					bool tx_pause, bool rx_pause)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct lan966x_port *port = netdev_priv(to_net_dev(config->dev));
5462306a36Sopenharmony_ci	struct lan966x_port_config *port_config = &port->config;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	port_config->duplex = duplex;
5762306a36Sopenharmony_ci	port_config->speed = speed;
5862306a36Sopenharmony_ci	port_config->pause = 0;
5962306a36Sopenharmony_ci	port_config->pause |= tx_pause ? MLO_PAUSE_TX : 0;
6062306a36Sopenharmony_ci	port_config->pause |= rx_pause ? MLO_PAUSE_RX : 0;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (phy_interface_mode_is_rgmii(interface))
6362306a36Sopenharmony_ci		phy_set_speed(port->serdes, speed);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	lan966x_port_config_up(port);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic void lan966x_phylink_mac_link_down(struct phylink_config *config,
6962306a36Sopenharmony_ci					  unsigned int mode,
7062306a36Sopenharmony_ci					  phy_interface_t interface)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct lan966x_port *port = netdev_priv(to_net_dev(config->dev));
7362306a36Sopenharmony_ci	struct lan966x *lan966x = port->lan966x;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	lan966x_port_config_down(port);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* Take PCS out of reset */
7862306a36Sopenharmony_ci	lan_rmw(DEV_CLOCK_CFG_PCS_RX_RST_SET(0) |
7962306a36Sopenharmony_ci		DEV_CLOCK_CFG_PCS_TX_RST_SET(0),
8062306a36Sopenharmony_ci		DEV_CLOCK_CFG_PCS_RX_RST |
8162306a36Sopenharmony_ci		DEV_CLOCK_CFG_PCS_TX_RST,
8262306a36Sopenharmony_ci		lan966x, DEV_CLOCK_CFG(port->chip_port));
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic struct lan966x_port *lan966x_pcs_to_port(struct phylink_pcs *pcs)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	return container_of(pcs, struct lan966x_port, phylink_pcs);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic void lan966x_pcs_get_state(struct phylink_pcs *pcs,
9162306a36Sopenharmony_ci				  struct phylink_link_state *state)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	struct lan966x_port *port = lan966x_pcs_to_port(pcs);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	lan966x_port_status_get(port, state);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic int lan966x_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
9962306a36Sopenharmony_ci			      phy_interface_t interface,
10062306a36Sopenharmony_ci			      const unsigned long *advertising,
10162306a36Sopenharmony_ci			      bool permit_pause_to_mac)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct lan966x_port *port = lan966x_pcs_to_port(pcs);
10462306a36Sopenharmony_ci	struct lan966x_port_config config;
10562306a36Sopenharmony_ci	int ret;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	config = port->config;
10862306a36Sopenharmony_ci	config.portmode = interface;
10962306a36Sopenharmony_ci	config.inband = neg_mode & PHYLINK_PCS_NEG_INBAND;
11062306a36Sopenharmony_ci	config.autoneg = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED;
11162306a36Sopenharmony_ci	config.advertising = advertising;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	ret = lan966x_port_pcs_set(port, &config);
11462306a36Sopenharmony_ci	if (ret)
11562306a36Sopenharmony_ci		netdev_err(port->dev, "port PCS config failed: %d\n", ret);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return ret;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic void lan966x_pcs_aneg_restart(struct phylink_pcs *pcs)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	/* Currently not used */
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ciconst struct phylink_mac_ops lan966x_phylink_mac_ops = {
12662306a36Sopenharmony_ci	.mac_select_pcs = lan966x_phylink_mac_select,
12762306a36Sopenharmony_ci	.mac_config = lan966x_phylink_mac_config,
12862306a36Sopenharmony_ci	.mac_prepare = lan966x_phylink_mac_prepare,
12962306a36Sopenharmony_ci	.mac_link_down = lan966x_phylink_mac_link_down,
13062306a36Sopenharmony_ci	.mac_link_up = lan966x_phylink_mac_link_up,
13162306a36Sopenharmony_ci};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ciconst struct phylink_pcs_ops lan966x_phylink_pcs_ops = {
13462306a36Sopenharmony_ci	.pcs_get_state = lan966x_pcs_get_state,
13562306a36Sopenharmony_ci	.pcs_config = lan966x_pcs_config,
13662306a36Sopenharmony_ci	.pcs_an_restart = lan966x_pcs_aneg_restart,
13762306a36Sopenharmony_ci};
138