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		linkmode_zero(supported);
96		return;
97	}
98	linkmode_and(supported, supported, mask);
99	linkmode_and(state->advertising, state->advertising, mask);
100}
101
102static void sparx5_phylink_mac_config(struct phylink_config *config,
103				      unsigned int mode,
104				      const struct phylink_link_state *state)
105{
106	/* Currently not used */
107}
108
109static void sparx5_phylink_mac_link_up(struct phylink_config *config,
110				       struct phy_device *phy,
111				       unsigned int mode,
112				       phy_interface_t interface,
113				       int speed, int duplex,
114				       bool tx_pause, bool rx_pause)
115{
116	struct sparx5_port *port = netdev_priv(to_net_dev(config->dev));
117	struct sparx5_port_config conf;
118	int err;
119
120	conf = port->conf;
121	conf.duplex = duplex;
122	conf.pause = 0;
123	conf.pause |= tx_pause ? MLO_PAUSE_TX : 0;
124	conf.pause |= rx_pause ? MLO_PAUSE_RX : 0;
125	conf.speed = speed;
126	/* Configure the port to speed/duplex/pause */
127	err = sparx5_port_config(port->sparx5, port, &conf);
128	if (err)
129		netdev_err(port->ndev, "port config failed: %d\n", err);
130}
131
132static void sparx5_phylink_mac_link_down(struct phylink_config *config,
133					 unsigned int mode,
134					 phy_interface_t interface)
135{
136	/* Currently not used */
137}
138
139static struct sparx5_port *sparx5_pcs_to_port(struct phylink_pcs *pcs)
140{
141	return container_of(pcs, struct sparx5_port, phylink_pcs);
142}
143
144static void sparx5_pcs_get_state(struct phylink_pcs *pcs,
145				 struct phylink_link_state *state)
146{
147	struct sparx5_port *port = sparx5_pcs_to_port(pcs);
148	struct sparx5_port_status status;
149
150	sparx5_get_port_status(port->sparx5, port, &status);
151	state->link = status.link && !status.link_down;
152	state->an_complete = status.an_complete;
153	state->speed = status.speed;
154	state->duplex = status.duplex;
155	state->pause = status.pause;
156}
157
158static int sparx5_pcs_config(struct phylink_pcs *pcs,
159			     unsigned int mode,
160			     phy_interface_t interface,
161			     const unsigned long *advertising,
162			     bool permit_pause_to_mac)
163{
164	struct sparx5_port *port = sparx5_pcs_to_port(pcs);
165	struct sparx5_port_config conf;
166	int ret = 0;
167
168	conf = port->conf;
169	conf.power_down = false;
170	conf.portmode = interface;
171	conf.inband = phylink_autoneg_inband(mode);
172	conf.autoneg = phylink_test(advertising, Autoneg);
173	conf.pause_adv = 0;
174	if (phylink_test(advertising, Pause))
175		conf.pause_adv |= ADVERTISE_1000XPAUSE;
176	if (phylink_test(advertising, Asym_Pause))
177		conf.pause_adv |= ADVERTISE_1000XPSE_ASYM;
178	if (sparx5_is_baser(interface)) {
179		if (phylink_test(advertising, FIBRE))
180			conf.media = PHY_MEDIA_SR;
181		else
182			conf.media = PHY_MEDIA_DAC;
183	}
184	if (!port_conf_has_changed(&port->conf, &conf))
185		return ret;
186	/* Enable the PCS matching this interface type */
187	ret = sparx5_port_pcs_set(port->sparx5, port, &conf);
188	if (ret)
189		netdev_err(port->ndev, "port PCS config failed: %d\n", ret);
190	return ret;
191}
192
193static void sparx5_pcs_aneg_restart(struct phylink_pcs *pcs)
194{
195	/* Currently not used */
196}
197
198const struct phylink_pcs_ops sparx5_phylink_pcs_ops = {
199	.pcs_get_state = sparx5_pcs_get_state,
200	.pcs_config = sparx5_pcs_config,
201	.pcs_an_restart = sparx5_pcs_aneg_restart,
202};
203
204const struct phylink_mac_ops sparx5_phylink_mac_ops = {
205	.validate = sparx5_phylink_validate,
206	.mac_config = sparx5_phylink_mac_config,
207	.mac_link_down = sparx5_phylink_mac_link_down,
208	.mac_link_up = sparx5_phylink_mac_link_up,
209};
210