summaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c
blob: e4ac59480514b6037192aebefafb705a58ffe7d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// SPDX-License-Identifier: GPL-2.0+

#include <linux/module.h>
#include <linux/phylink.h>
#include <linux/device.h>
#include <linux/netdevice.h>
#include <linux/phy/phy.h>
#include <linux/sfp.h>

#include "lan966x_main.h"

static struct phylink_pcs *lan966x_phylink_mac_select(struct phylink_config *config,
						      phy_interface_t interface)
{
	struct lan966x_port *port = netdev_priv(to_net_dev(config->dev));

	return &port->phylink_pcs;
}

static void lan966x_phylink_mac_config(struct phylink_config *config,
				       unsigned int mode,
				       const struct phylink_link_state *state)
{
}

static int lan966x_phylink_mac_prepare(struct phylink_config *config,
				       unsigned int mode,
				       phy_interface_t iface)
{
	struct lan966x_port *port = netdev_priv(to_net_dev(config->dev));
	phy_interface_t serdes_mode = iface;
	int err;

	if (port->serdes) {
		err = phy_set_mode_ext(port->serdes, PHY_MODE_ETHERNET,
				       serdes_mode);
		if (err) {
			netdev_err(to_net_dev(config->dev),
				   "Could not set mode of SerDes\n");
			return err;
		}
	}

	return 0;
}

static void lan966x_phylink_mac_link_up(struct phylink_config *config,
					struct phy_device *phy,
					unsigned int mode,
					phy_interface_t interface,
					int speed, int duplex,
					bool tx_pause, bool rx_pause)
{
	struct lan966x_port *port = netdev_priv(to_net_dev(config->dev));
	struct lan966x_port_config *port_config = &port->config;

	port_config->duplex = duplex;
	port_config->speed = speed;
	port_config->pause = 0;
	port_config->pause |= tx_pause ? MLO_PAUSE_TX : 0;
	port_config->pause |= rx_pause ? MLO_PAUSE_RX : 0;

	if (phy_interface_mode_is_rgmii(interface))
		phy_set_speed(port->serdes, speed);

	lan966x_port_config_up(port);
}

static void lan966x_phylink_mac_link_down(struct phylink_config *config,
					  unsigned int mode,
					  phy_interface_t interface)
{
	struct lan966x_port *port = netdev_priv(to_net_dev(config->dev));
	struct lan966x *lan966x = port->lan966x;

	lan966x_port_config_down(port);

	/* Take PCS out of reset */
	lan_rmw(DEV_CLOCK_CFG_PCS_RX_RST_SET(0) |
		DEV_CLOCK_CFG_PCS_TX_RST_SET(0),
		DEV_CLOCK_CFG_PCS_RX_RST |
		DEV_CLOCK_CFG_PCS_TX_RST,
		lan966x, DEV_CLOCK_CFG(port->chip_port));
}

static struct lan966x_port *lan966x_pcs_to_port(struct phylink_pcs *pcs)
{
	return container_of(pcs, struct lan966x_port, phylink_pcs);
}

static void lan966x_pcs_get_state(struct phylink_pcs *pcs,
				  struct phylink_link_state *state)
{
	struct lan966x_port *port = lan966x_pcs_to_port(pcs);

	lan966x_port_status_get(port, state);
}

static int lan966x_pcs_config(struct phylink_pcs *pcs,
			      unsigned int mode,
			      phy_interface_t interface,
			      const unsigned long *advertising,
			      bool permit_pause_to_mac)
{
	struct lan966x_port *port = lan966x_pcs_to_port(pcs);
	struct lan966x_port_config config;
	int ret;

	config = port->config;
	config.portmode = interface;
	config.inband = phylink_autoneg_inband(mode);
	config.autoneg = phylink_test(advertising, Autoneg);
	config.advertising = advertising;

	ret = lan966x_port_pcs_set(port, &config);
	if (ret)
		netdev_err(port->dev, "port PCS config failed: %d\n", ret);

	return ret;
}

static void lan966x_pcs_aneg_restart(struct phylink_pcs *pcs)
{
	/* Currently not used */
}

const struct phylink_mac_ops lan966x_phylink_mac_ops = {
	.validate = phylink_generic_validate,
	.mac_select_pcs = lan966x_phylink_mac_select,
	.mac_config = lan966x_phylink_mac_config,
	.mac_prepare = lan966x_phylink_mac_prepare,
	.mac_link_down = lan966x_phylink_mac_link_down,
	.mac_link_up = lan966x_phylink_mac_link_up,
};

const struct phylink_pcs_ops lan966x_phylink_pcs_ops = {
	.pcs_get_state = lan966x_pcs_get_state,
	.pcs_config = lan966x_pcs_config,
	.pcs_an_restart = lan966x_pcs_aneg_restart,
};