171947923SIoana Ciornei // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
271947923SIoana Ciornei /* Copyright 2019 NXP */
371947923SIoana Ciornei 
43264f599SCalvin Johnson #include <linux/acpi.h>
5e7026f15SColin Foster #include <linux/pcs-lynx.h>
6f978fe85SIoana Ciornei #include <linux/phy/phy.h>
73264f599SCalvin Johnson #include <linux/property.h>
83264f599SCalvin Johnson 
971947923SIoana Ciornei #include "dpaa2-eth.h"
1071947923SIoana Ciornei #include "dpaa2-mac.h"
1171947923SIoana Ciornei 
1271947923SIoana Ciornei #define phylink_to_dpaa2_mac(config) \
1371947923SIoana Ciornei 	container_of((config), struct dpaa2_mac, phylink_config)
1471947923SIoana Ciornei 
15dff95381SIoana Ciornei #define DPMAC_PROTOCOL_CHANGE_VER_MAJOR		4
16dff95381SIoana Ciornei #define DPMAC_PROTOCOL_CHANGE_VER_MINOR		8
17dff95381SIoana Ciornei 
18dff95381SIoana Ciornei #define DPAA2_MAC_FEATURE_PROTOCOL_CHANGE	BIT(0)
19dff95381SIoana Ciornei 
dpaa2_mac_cmp_ver(struct dpaa2_mac * mac,u16 ver_major,u16 ver_minor)20dff95381SIoana Ciornei static int dpaa2_mac_cmp_ver(struct dpaa2_mac *mac,
21dff95381SIoana Ciornei 			     u16 ver_major, u16 ver_minor)
22dff95381SIoana Ciornei {
23dff95381SIoana Ciornei 	if (mac->ver_major == ver_major)
24dff95381SIoana Ciornei 		return mac->ver_minor - ver_minor;
25dff95381SIoana Ciornei 	return mac->ver_major - ver_major;
26dff95381SIoana Ciornei }
27dff95381SIoana Ciornei 
dpaa2_mac_detect_features(struct dpaa2_mac * mac)28dff95381SIoana Ciornei static void dpaa2_mac_detect_features(struct dpaa2_mac *mac)
29dff95381SIoana Ciornei {
30dff95381SIoana Ciornei 	mac->features = 0;
31dff95381SIoana Ciornei 
32dff95381SIoana Ciornei 	if (dpaa2_mac_cmp_ver(mac, DPMAC_PROTOCOL_CHANGE_VER_MAJOR,
33dff95381SIoana Ciornei 			      DPMAC_PROTOCOL_CHANGE_VER_MINOR) >= 0)
34dff95381SIoana Ciornei 		mac->features |= DPAA2_MAC_FEATURE_PROTOCOL_CHANGE;
35dff95381SIoana Ciornei }
36dff95381SIoana Ciornei 
phy_mode(enum dpmac_eth_if eth_if,phy_interface_t * if_mode)37226df3efSIoana Ciornei static int phy_mode(enum dpmac_eth_if eth_if, phy_interface_t *if_mode)
3871947923SIoana Ciornei {
39226df3efSIoana Ciornei 	*if_mode = PHY_INTERFACE_MODE_NA;
40226df3efSIoana Ciornei 
4171947923SIoana Ciornei 	switch (eth_if) {
4271947923SIoana Ciornei 	case DPMAC_ETH_IF_RGMII:
43226df3efSIoana Ciornei 		*if_mode = PHY_INTERFACE_MODE_RGMII;
44226df3efSIoana Ciornei 		break;
4594ae899bSIoana Ciornei 	case DPMAC_ETH_IF_USXGMII:
4694ae899bSIoana Ciornei 		*if_mode = PHY_INTERFACE_MODE_USXGMII;
4794ae899bSIoana Ciornei 		break;
4894ae899bSIoana Ciornei 	case DPMAC_ETH_IF_QSGMII:
4994ae899bSIoana Ciornei 		*if_mode = PHY_INTERFACE_MODE_QSGMII;
5094ae899bSIoana Ciornei 		break;
5194ae899bSIoana Ciornei 	case DPMAC_ETH_IF_SGMII:
5294ae899bSIoana Ciornei 		*if_mode = PHY_INTERFACE_MODE_SGMII;
5394ae899bSIoana Ciornei 		break;
5494ae899bSIoana Ciornei 	case DPMAC_ETH_IF_XFI:
5594ae899bSIoana Ciornei 		*if_mode = PHY_INTERFACE_MODE_10GBASER;
5694ae899bSIoana Ciornei 		break;
57*9a43827eSJosua Mayer 	case DPMAC_ETH_IF_CAUI:
58*9a43827eSJosua Mayer 		*if_mode = PHY_INTERFACE_MODE_25GBASER;
59*9a43827eSJosua Mayer 		break;
6071947923SIoana Ciornei 	default:
6171947923SIoana Ciornei 		return -EINVAL;
6271947923SIoana Ciornei 	}
63226df3efSIoana Ciornei 
64226df3efSIoana Ciornei 	return 0;
6571947923SIoana Ciornei }
6671947923SIoana Ciornei 
dpmac_eth_if_mode(phy_interface_t if_mode)67f978fe85SIoana Ciornei static enum dpmac_eth_if dpmac_eth_if_mode(phy_interface_t if_mode)
68f978fe85SIoana Ciornei {
69f978fe85SIoana Ciornei 	switch (if_mode) {
70f978fe85SIoana Ciornei 	case PHY_INTERFACE_MODE_RGMII:
71f978fe85SIoana Ciornei 	case PHY_INTERFACE_MODE_RGMII_ID:
72f978fe85SIoana Ciornei 	case PHY_INTERFACE_MODE_RGMII_RXID:
73f978fe85SIoana Ciornei 	case PHY_INTERFACE_MODE_RGMII_TXID:
74f978fe85SIoana Ciornei 		return DPMAC_ETH_IF_RGMII;
75f978fe85SIoana Ciornei 	case PHY_INTERFACE_MODE_USXGMII:
76f978fe85SIoana Ciornei 		return DPMAC_ETH_IF_USXGMII;
77f978fe85SIoana Ciornei 	case PHY_INTERFACE_MODE_QSGMII:
78f978fe85SIoana Ciornei 		return DPMAC_ETH_IF_QSGMII;
79f978fe85SIoana Ciornei 	case PHY_INTERFACE_MODE_SGMII:
80f978fe85SIoana Ciornei 		return DPMAC_ETH_IF_SGMII;
81f978fe85SIoana Ciornei 	case PHY_INTERFACE_MODE_10GBASER:
82f978fe85SIoana Ciornei 		return DPMAC_ETH_IF_XFI;
83f978fe85SIoana Ciornei 	case PHY_INTERFACE_MODE_1000BASEX:
84f978fe85SIoana Ciornei 		return DPMAC_ETH_IF_1000BASEX;
85*9a43827eSJosua Mayer 	case PHY_INTERFACE_MODE_25GBASER:
86*9a43827eSJosua Mayer 		return DPMAC_ETH_IF_CAUI;
87f978fe85SIoana Ciornei 	default:
88f978fe85SIoana Ciornei 		return DPMAC_ETH_IF_MII;
89f978fe85SIoana Ciornei 	}
90f978fe85SIoana Ciornei }
91f978fe85SIoana Ciornei 
dpaa2_mac_get_node(struct device * dev,u16 dpmac_id)923264f599SCalvin Johnson static struct fwnode_handle *dpaa2_mac_get_node(struct device *dev,
933264f599SCalvin Johnson 						u16 dpmac_id)
9471947923SIoana Ciornei {
955b1e38c0SRobert-Ionut Alexa 	struct fwnode_handle *fwnode, *parent = NULL, *child  = NULL;
963264f599SCalvin Johnson 	struct device_node *dpmacs = NULL;
9771947923SIoana Ciornei 	int err;
983264f599SCalvin Johnson 	u32 id;
9971947923SIoana Ciornei 
1003264f599SCalvin Johnson 	fwnode = dev_fwnode(dev->parent);
1013264f599SCalvin Johnson 	if (is_of_node(fwnode)) {
10271947923SIoana Ciornei 		dpmacs = of_find_node_by_name(NULL, "dpmacs");
10371947923SIoana Ciornei 		if (!dpmacs)
10471947923SIoana Ciornei 			return NULL;
1053264f599SCalvin Johnson 		parent = of_fwnode_handle(dpmacs);
1063264f599SCalvin Johnson 	} else if (is_acpi_node(fwnode)) {
1073264f599SCalvin Johnson 		parent = fwnode;
1084e30e98cSIoana Ciornei 	} else {
1094e30e98cSIoana Ciornei 		/* The root dprc device didn't yet get to finalize it's probe,
1104e30e98cSIoana Ciornei 		 * thus the fwnode field is not yet set. Defer probe if we are
1114e30e98cSIoana Ciornei 		 * facing this situation.
1124e30e98cSIoana Ciornei 		 */
11337fe9b98SSean Anderson 		dev_dbg(dev, "dprc not finished probing\n");
1144e30e98cSIoana Ciornei 		return ERR_PTR(-EPROBE_DEFER);
1153264f599SCalvin Johnson 	}
11671947923SIoana Ciornei 
1173264f599SCalvin Johnson 	fwnode_for_each_child_node(parent, child) {
1183264f599SCalvin Johnson 		err = -EINVAL;
1193264f599SCalvin Johnson 		if (is_acpi_device_node(child))
1203264f599SCalvin Johnson 			err = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), &id);
1213264f599SCalvin Johnson 		else if (is_of_node(child))
1223264f599SCalvin Johnson 			err = of_property_read_u32(to_of_node(child), "reg", &id);
12371947923SIoana Ciornei 		if (err)
12471947923SIoana Ciornei 			continue;
12571947923SIoana Ciornei 
1263264f599SCalvin Johnson 		if (id == dpmac_id) {
12771947923SIoana Ciornei 			of_node_put(dpmacs);
1283264f599SCalvin Johnson 			return child;
1293264f599SCalvin Johnson 		}
1303264f599SCalvin Johnson 	}
1313264f599SCalvin Johnson 	of_node_put(dpmacs);
1323264f599SCalvin Johnson 	return NULL;
13371947923SIoana Ciornei }
13471947923SIoana Ciornei 
dpaa2_mac_get_if_mode(struct fwnode_handle * dpmac_node,struct dpmac_attr attr)1353264f599SCalvin Johnson static int dpaa2_mac_get_if_mode(struct fwnode_handle *dpmac_node,
13671947923SIoana Ciornei 				 struct dpmac_attr attr)
13771947923SIoana Ciornei {
1380c65b2b9SAndrew Lunn 	phy_interface_t if_mode;
1390c65b2b9SAndrew Lunn 	int err;
14071947923SIoana Ciornei 
1413264f599SCalvin Johnson 	err = fwnode_get_phy_mode(dpmac_node);
1423264f599SCalvin Johnson 	if (err > 0)
1433264f599SCalvin Johnson 		return err;
14471947923SIoana Ciornei 
145226df3efSIoana Ciornei 	err = phy_mode(attr.eth_if, &if_mode);
146226df3efSIoana Ciornei 	if (!err)
14771947923SIoana Ciornei 		return if_mode;
14871947923SIoana Ciornei 
149226df3efSIoana Ciornei 	return err;
15071947923SIoana Ciornei }
15171947923SIoana Ciornei 
dpaa2_mac_select_pcs(struct phylink_config * config,phy_interface_t interface)152c592286aSRussell King (Oracle) static struct phylink_pcs *dpaa2_mac_select_pcs(struct phylink_config *config,
153c592286aSRussell King (Oracle) 						phy_interface_t interface)
154c592286aSRussell King (Oracle) {
155c592286aSRussell King (Oracle) 	struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config);
156c592286aSRussell King (Oracle) 
157c592286aSRussell King (Oracle) 	return mac->pcs;
158c592286aSRussell King (Oracle) }
159c592286aSRussell King (Oracle) 
dpaa2_mac_config(struct phylink_config * config,unsigned int mode,const struct phylink_link_state * state)16071947923SIoana Ciornei static void dpaa2_mac_config(struct phylink_config *config, unsigned int mode,
16171947923SIoana Ciornei 			     const struct phylink_link_state *state)
16271947923SIoana Ciornei {
16371947923SIoana Ciornei 	struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config);
16471947923SIoana Ciornei 	struct dpmac_link_state *dpmac_state = &mac->state;
16571947923SIoana Ciornei 	int err;
16671947923SIoana Ciornei 
16799d0f3a1SRussell King (Oracle) 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
16899d0f3a1SRussell King (Oracle) 			      state->advertising))
16971947923SIoana Ciornei 		dpmac_state->options |= DPMAC_LINK_OPT_AUTONEG;
17071947923SIoana Ciornei 	else
17171947923SIoana Ciornei 		dpmac_state->options &= ~DPMAC_LINK_OPT_AUTONEG;
17271947923SIoana Ciornei 
17371947923SIoana Ciornei 	err = dpmac_set_link_state(mac->mc_io, 0,
17471947923SIoana Ciornei 				   mac->mc_dev->mc_handle, dpmac_state);
17571947923SIoana Ciornei 	if (err)
17637556a4aSRussell King 		netdev_err(mac->net_dev, "%s: dpmac_set_link_state() = %d\n",
17737556a4aSRussell King 			   __func__, err);
178f978fe85SIoana Ciornei 
179f978fe85SIoana Ciornei 	if (!mac->serdes_phy)
180f978fe85SIoana Ciornei 		return;
181f978fe85SIoana Ciornei 
182f978fe85SIoana Ciornei 	/* This happens only if we support changing of protocol at runtime */
183f978fe85SIoana Ciornei 	err = dpmac_set_protocol(mac->mc_io, 0, mac->mc_dev->mc_handle,
184f978fe85SIoana Ciornei 				 dpmac_eth_if_mode(state->interface));
185f978fe85SIoana Ciornei 	if (err)
186f978fe85SIoana Ciornei 		netdev_err(mac->net_dev,  "dpmac_set_protocol() = %d\n", err);
187f978fe85SIoana Ciornei 
188f978fe85SIoana Ciornei 	err = phy_set_mode_ext(mac->serdes_phy, PHY_MODE_ETHERNET, state->interface);
189f978fe85SIoana Ciornei 	if (err)
190f978fe85SIoana Ciornei 		netdev_err(mac->net_dev, "phy_set_mode_ext() = %d\n", err);
19171947923SIoana Ciornei }
19271947923SIoana Ciornei 
dpaa2_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)19391a208f2SRussell King static void dpaa2_mac_link_up(struct phylink_config *config,
19491a208f2SRussell King 			      struct phy_device *phy,
19591a208f2SRussell King 			      unsigned int mode, phy_interface_t interface,
19691a208f2SRussell King 			      int speed, int duplex,
19791a208f2SRussell King 			      bool tx_pause, bool rx_pause)
19871947923SIoana Ciornei {
19971947923SIoana Ciornei 	struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config);
20071947923SIoana Ciornei 	struct dpmac_link_state *dpmac_state = &mac->state;
20171947923SIoana Ciornei 	int err;
20271947923SIoana Ciornei 
20371947923SIoana Ciornei 	dpmac_state->up = 1;
20437556a4aSRussell King 
20537556a4aSRussell King 	dpmac_state->rate = speed;
20637556a4aSRussell King 
20737556a4aSRussell King 	if (duplex == DUPLEX_HALF)
20837556a4aSRussell King 		dpmac_state->options |= DPMAC_LINK_OPT_HALF_DUPLEX;
20937556a4aSRussell King 	else if (duplex == DUPLEX_FULL)
21037556a4aSRussell King 		dpmac_state->options &= ~DPMAC_LINK_OPT_HALF_DUPLEX;
21137556a4aSRussell King 
21237556a4aSRussell King 	if (rx_pause)
21337556a4aSRussell King 		dpmac_state->options |= DPMAC_LINK_OPT_PAUSE;
21437556a4aSRussell King 	else
21537556a4aSRussell King 		dpmac_state->options &= ~DPMAC_LINK_OPT_PAUSE;
21637556a4aSRussell King 
21737556a4aSRussell King 	if (rx_pause ^ tx_pause)
21837556a4aSRussell King 		dpmac_state->options |= DPMAC_LINK_OPT_ASYM_PAUSE;
21937556a4aSRussell King 	else
22037556a4aSRussell King 		dpmac_state->options &= ~DPMAC_LINK_OPT_ASYM_PAUSE;
22137556a4aSRussell King 
22271947923SIoana Ciornei 	err = dpmac_set_link_state(mac->mc_io, 0,
22371947923SIoana Ciornei 				   mac->mc_dev->mc_handle, dpmac_state);
22471947923SIoana Ciornei 	if (err)
22537556a4aSRussell King 		netdev_err(mac->net_dev, "%s: dpmac_set_link_state() = %d\n",
22637556a4aSRussell King 			   __func__, err);
22771947923SIoana Ciornei }
22871947923SIoana Ciornei 
dpaa2_mac_link_down(struct phylink_config * config,unsigned int mode,phy_interface_t interface)22971947923SIoana Ciornei static void dpaa2_mac_link_down(struct phylink_config *config,
23071947923SIoana Ciornei 				unsigned int mode,
23171947923SIoana Ciornei 				phy_interface_t interface)
23271947923SIoana Ciornei {
23371947923SIoana Ciornei 	struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config);
23471947923SIoana Ciornei 	struct dpmac_link_state *dpmac_state = &mac->state;
23571947923SIoana Ciornei 	int err;
23671947923SIoana Ciornei 
23771947923SIoana Ciornei 	dpmac_state->up = 0;
23871947923SIoana Ciornei 	err = dpmac_set_link_state(mac->mc_io, 0,
23971947923SIoana Ciornei 				   mac->mc_dev->mc_handle, dpmac_state);
24071947923SIoana Ciornei 	if (err)
24171947923SIoana Ciornei 		netdev_err(mac->net_dev, "dpmac_set_link_state() = %d\n", err);
24271947923SIoana Ciornei }
24371947923SIoana Ciornei 
24471947923SIoana Ciornei static const struct phylink_mac_ops dpaa2_mac_phylink_ops = {
245c592286aSRussell King (Oracle) 	.mac_select_pcs = dpaa2_mac_select_pcs,
24671947923SIoana Ciornei 	.mac_config = dpaa2_mac_config,
24771947923SIoana Ciornei 	.mac_link_up = dpaa2_mac_link_up,
24871947923SIoana Ciornei 	.mac_link_down = dpaa2_mac_link_down,
24971947923SIoana Ciornei };
25071947923SIoana Ciornei 
dpaa2_pcs_create(struct dpaa2_mac * mac,struct fwnode_handle * dpmac_node,int id)25194ae899bSIoana Ciornei static int dpaa2_pcs_create(struct dpaa2_mac *mac,
2523264f599SCalvin Johnson 			    struct fwnode_handle *dpmac_node,
2533264f599SCalvin Johnson 			    int id)
25494ae899bSIoana Ciornei {
25594ae899bSIoana Ciornei 	struct fwnode_handle *node;
2563264f599SCalvin Johnson 	struct phylink_pcs *pcs;
25794ae899bSIoana Ciornei 
2583264f599SCalvin Johnson 	node = fwnode_find_reference(dpmac_node, "pcs-handle", 0);
2593264f599SCalvin Johnson 	if (IS_ERR(node)) {
26094ae899bSIoana Ciornei 		/* do not error out on old DTS files */
26194ae899bSIoana Ciornei 		netdev_warn(mac->net_dev, "pcs-handle node not found\n");
26294ae899bSIoana Ciornei 		return 0;
26394ae899bSIoana Ciornei 	}
26494ae899bSIoana Ciornei 
2653264f599SCalvin Johnson 	pcs = lynx_pcs_create_fwnode(node);
26694ae899bSIoana Ciornei 	fwnode_handle_put(node);
2673264f599SCalvin Johnson 
26894ae899bSIoana Ciornei 	if (pcs == ERR_PTR(-EPROBE_DEFER)) {
26994ae899bSIoana Ciornei 		netdev_dbg(mac->net_dev, "missing PCS device\n");
27094ae899bSIoana Ciornei 		return -EPROBE_DEFER;
2713264f599SCalvin Johnson 	}
2723264f599SCalvin Johnson 
27337fe9b98SSean Anderson 	if (pcs == ERR_PTR(-ENODEV)) {
27437fe9b98SSean Anderson 		netdev_err(mac->net_dev, "pcs-handle node not available\n");
27594ae899bSIoana Ciornei 		return PTR_ERR(pcs);
27637fe9b98SSean Anderson 	}
27794ae899bSIoana Ciornei 
27894ae899bSIoana Ciornei 	if (IS_ERR(pcs)) {
27994ae899bSIoana Ciornei 		netdev_err(mac->net_dev,
28094ae899bSIoana Ciornei 			   "lynx_pcs_create_fwnode() failed: %pe\n", pcs);
28194ae899bSIoana Ciornei 		return PTR_ERR(pcs);
28294ae899bSIoana Ciornei 	}
28394ae899bSIoana Ciornei 
28494ae899bSIoana Ciornei 	mac->pcs = pcs;
28594ae899bSIoana Ciornei 
28694ae899bSIoana Ciornei 	return 0;
28794ae899bSIoana Ciornei }
28894ae899bSIoana Ciornei 
dpaa2_pcs_destroy(struct dpaa2_mac * mac)28994ae899bSIoana Ciornei static void dpaa2_pcs_destroy(struct dpaa2_mac *mac)
290e7026f15SColin Foster {
29194ae899bSIoana Ciornei 	struct phylink_pcs *phylink_pcs = mac->pcs;
292e7026f15SColin Foster 
293e7026f15SColin Foster 	if (phylink_pcs) {
294e7026f15SColin Foster 		lynx_pcs_destroy(phylink_pcs);
295e7026f15SColin Foster 		mac->pcs = NULL;
296e7026f15SColin Foster 	}
29794ae899bSIoana Ciornei }
29894ae899bSIoana Ciornei 
dpaa2_mac_set_supported_interfaces(struct dpaa2_mac * mac)29994ae899bSIoana Ciornei static void dpaa2_mac_set_supported_interfaces(struct dpaa2_mac *mac)
30094ae899bSIoana Ciornei {
30194ae899bSIoana Ciornei 	int intf, err;
302aa95c371SIoana Ciornei 
303aa95c371SIoana Ciornei 	/* We support the current interface mode, and if we have a PCS
304f978fe85SIoana Ciornei 	 * similar interface modes that do not require the SerDes lane to be
305f978fe85SIoana Ciornei 	 * reconfigured.
306aa95c371SIoana Ciornei 	 */
307aa95c371SIoana Ciornei 	__set_bit(mac->if_mode, mac->phylink_config.supported_interfaces);
308aa95c371SIoana Ciornei 	if (mac->pcs) {
309aa95c371SIoana Ciornei 		switch (mac->if_mode) {
310aa95c371SIoana Ciornei 		case PHY_INTERFACE_MODE_1000BASEX:
311aa95c371SIoana Ciornei 		case PHY_INTERFACE_MODE_SGMII:
312aa95c371SIoana Ciornei 			__set_bit(PHY_INTERFACE_MODE_1000BASEX,
313aa95c371SIoana Ciornei 				  mac->phylink_config.supported_interfaces);
314aa95c371SIoana Ciornei 			__set_bit(PHY_INTERFACE_MODE_SGMII,
315aa95c371SIoana Ciornei 				  mac->phylink_config.supported_interfaces);
316aa95c371SIoana Ciornei 			break;
317aa95c371SIoana Ciornei 
318aa95c371SIoana Ciornei 		default:
319aa95c371SIoana Ciornei 			break;
320aa95c371SIoana Ciornei 		}
321aa95c371SIoana Ciornei 	}
322aa95c371SIoana Ciornei 
323aa95c371SIoana Ciornei 	if (!mac->serdes_phy)
324aa95c371SIoana Ciornei 		return;
325f978fe85SIoana Ciornei 
326f978fe85SIoana Ciornei 	/* In case we have access to the SerDes phy/lane, then ask the SerDes
327f978fe85SIoana Ciornei 	 * driver what interfaces are supported based on the current PLL
328f978fe85SIoana Ciornei 	 * configuration.
329f978fe85SIoana Ciornei 	 */
330f978fe85SIoana Ciornei 	for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) {
331f978fe85SIoana Ciornei 		if (intf == PHY_INTERFACE_MODE_NA)
332f978fe85SIoana Ciornei 			continue;
333f978fe85SIoana Ciornei 
334f978fe85SIoana Ciornei 		err = phy_validate(mac->serdes_phy, PHY_MODE_ETHERNET, intf, NULL);
335f978fe85SIoana Ciornei 		if (err)
336f978fe85SIoana Ciornei 			continue;
337f978fe85SIoana Ciornei 
338f978fe85SIoana Ciornei 		__set_bit(intf, mac->phylink_config.supported_interfaces);
339f978fe85SIoana Ciornei 	}
340f978fe85SIoana Ciornei }
341f978fe85SIoana Ciornei 
dpaa2_mac_start(struct dpaa2_mac * mac)342f978fe85SIoana Ciornei void dpaa2_mac_start(struct dpaa2_mac *mac)
343f978fe85SIoana Ciornei {
344f978fe85SIoana Ciornei 	ASSERT_RTNL();
345f978fe85SIoana Ciornei 
346f978fe85SIoana Ciornei 	if (mac->serdes_phy)
34738533388SVladimir Oltean 		phy_power_on(mac->serdes_phy);
34838533388SVladimir Oltean 
349f978fe85SIoana Ciornei 	phylink_start(mac->phylink);
350f978fe85SIoana Ciornei }
35138533388SVladimir Oltean 
dpaa2_mac_stop(struct dpaa2_mac * mac)35238533388SVladimir Oltean void dpaa2_mac_stop(struct dpaa2_mac *mac)
353f978fe85SIoana Ciornei {
354f978fe85SIoana Ciornei 	ASSERT_RTNL();
355f978fe85SIoana Ciornei 
356f978fe85SIoana Ciornei 	phylink_stop(mac->phylink);
35738533388SVladimir Oltean 
35838533388SVladimir Oltean 	if (mac->serdes_phy)
35938533388SVladimir Oltean 		phy_power_off(mac->serdes_phy);
36038533388SVladimir Oltean }
361f978fe85SIoana Ciornei 
dpaa2_mac_connect(struct dpaa2_mac * mac)362f978fe85SIoana Ciornei int dpaa2_mac_connect(struct dpaa2_mac *mac)
363aa95c371SIoana Ciornei {
364aa95c371SIoana Ciornei 	struct net_device *net_dev = mac->net_dev;
36571947923SIoana Ciornei 	struct fwnode_handle *dpmac_node;
36671947923SIoana Ciornei 	struct phy *serdes_phy = NULL;
36771947923SIoana Ciornei 	struct phylink *phylink;
3683264f599SCalvin Johnson 	int err;
369f978fe85SIoana Ciornei 
37071947923SIoana Ciornei 	mac->if_link_type = mac->attr.link_type;
37171947923SIoana Ciornei 
37271947923SIoana Ciornei 	dpmac_node = mac->fw_node;
373095dca16SIoana Ciornei 	if (!dpmac_node) {
374095dca16SIoana Ciornei 		netdev_err(net_dev, "No dpmac@%d node found.\n", mac->attr.id);
3753264f599SCalvin Johnson 		return -ENODEV;
376095dca16SIoana Ciornei 	}
377095dca16SIoana Ciornei 
37871947923SIoana Ciornei 	err = dpaa2_mac_get_if_mode(dpmac_node, mac->attr);
37971947923SIoana Ciornei 	if (err < 0)
38071947923SIoana Ciornei 		return -EINVAL;
381095dca16SIoana Ciornei 	mac->if_mode = err;
382b193f2edSIoana Ciornei 
383b193f2edSIoana Ciornei 	if (mac->features & DPAA2_MAC_FEATURE_PROTOCOL_CHANGE &&
38471947923SIoana Ciornei 	    !phy_interface_mode_is_rgmii(mac->if_mode) &&
38571947923SIoana Ciornei 	    is_of_node(dpmac_node)) {
386f978fe85SIoana Ciornei 		serdes_phy = of_phy_get(to_of_node(dpmac_node), NULL);
387f978fe85SIoana Ciornei 
388f978fe85SIoana Ciornei 		if (serdes_phy == ERR_PTR(-ENODEV))
389f978fe85SIoana Ciornei 			serdes_phy = NULL;
390f978fe85SIoana Ciornei 		else if (IS_ERR(serdes_phy))
391f978fe85SIoana Ciornei 			return PTR_ERR(serdes_phy);
392f978fe85SIoana Ciornei 		else
393f978fe85SIoana Ciornei 			phy_init(serdes_phy);
394f978fe85SIoana Ciornei 	}
395f978fe85SIoana Ciornei 	mac->serdes_phy = serdes_phy;
396f978fe85SIoana Ciornei 
397f978fe85SIoana Ciornei 	/* The MAC does not have the capability to add RGMII delays so
398f978fe85SIoana Ciornei 	 * error out if the interface mode requests them and there is no PHY
399f978fe85SIoana Ciornei 	 * to act upon them
40071947923SIoana Ciornei 	 */
40171947923SIoana Ciornei 	if (of_phy_is_fixed_link(to_of_node(dpmac_node)) &&
40271947923SIoana Ciornei 	    (mac->if_mode == PHY_INTERFACE_MODE_RGMII_ID ||
40371947923SIoana Ciornei 	     mac->if_mode == PHY_INTERFACE_MODE_RGMII_RXID ||
4043264f599SCalvin Johnson 	     mac->if_mode == PHY_INTERFACE_MODE_RGMII_TXID)) {
40571947923SIoana Ciornei 		netdev_err(net_dev, "RGMII delay not supported\n");
40671947923SIoana Ciornei 		return -EINVAL;
40771947923SIoana Ciornei 	}
40871947923SIoana Ciornei 
409b193f2edSIoana Ciornei 	if ((mac->attr.link_type == DPMAC_LINK_TYPE_PHY &&
41071947923SIoana Ciornei 	     mac->attr.eth_if != DPMAC_ETH_IF_RGMII) ||
41171947923SIoana Ciornei 	    mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE) {
412085f1776SRussell King 		err = dpaa2_pcs_create(mac, dpmac_node, mac->attr.id);
413085f1776SRussell King 		if (err)
414085f1776SRussell King 			return err;
415095dca16SIoana Ciornei 	}
41694ae899bSIoana Ciornei 
417b193f2edSIoana Ciornei 	memset(&mac->phylink_config, 0, sizeof(mac->phylink_config));
41894ae899bSIoana Ciornei 	mac->phylink_config.dev = &net_dev->dev;
41994ae899bSIoana Ciornei 	mac->phylink_config.type = PHYLINK_NETDEV;
42015d0b14cSRussell King 
42171947923SIoana Ciornei 	mac->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE |
42271947923SIoana Ciornei 		MAC_10FD | MAC_100FD | MAC_1000FD | MAC_2500FD | MAC_5000FD |
42371947923SIoana Ciornei 		MAC_10000FD | MAC_25000FD;
4246d386f66SRussell King (Oracle) 
4256d386f66SRussell King (Oracle) 	dpaa2_mac_set_supported_interfaces(mac);
426*9a43827eSJosua Mayer 
4276d386f66SRussell King (Oracle) 	phylink = phylink_create(&mac->phylink_config,
428aa95c371SIoana Ciornei 				 dpmac_node, mac->if_mode,
42915d0b14cSRussell King 				 &dpaa2_mac_phylink_ops);
43071947923SIoana Ciornei 	if (IS_ERR(phylink)) {
4313264f599SCalvin Johnson 		err = PTR_ERR(phylink);
43271947923SIoana Ciornei 		goto err_pcs_destroy;
43371947923SIoana Ciornei 	}
43471947923SIoana Ciornei 	mac->phylink = phylink;
43594ae899bSIoana Ciornei 
43671947923SIoana Ciornei 	rtnl_lock();
43771947923SIoana Ciornei 	err = phylink_fwnode_phy_connect(mac->phylink, dpmac_node, 0);
43871947923SIoana Ciornei 	rtnl_unlock();
43987db82cbSVladimir Oltean 	if (err) {
4403264f599SCalvin Johnson 		netdev_err(net_dev, "phylink_fwnode_phy_connect() = %d\n", err);
44187db82cbSVladimir Oltean 		goto err_phylink_destroy;
44271947923SIoana Ciornei 	}
4433264f599SCalvin Johnson 
44471947923SIoana Ciornei 	return 0;
44571947923SIoana Ciornei 
44671947923SIoana Ciornei err_phylink_destroy:
44771947923SIoana Ciornei 	phylink_destroy(mac->phylink);
44871947923SIoana Ciornei err_pcs_destroy:
44971947923SIoana Ciornei 	dpaa2_pcs_destroy(mac);
45071947923SIoana Ciornei 
45194ae899bSIoana Ciornei 	return err;
45294ae899bSIoana Ciornei }
453095dca16SIoana Ciornei 
dpaa2_mac_disconnect(struct dpaa2_mac * mac)45471947923SIoana Ciornei void dpaa2_mac_disconnect(struct dpaa2_mac *mac)
45571947923SIoana Ciornei {
45671947923SIoana Ciornei 	rtnl_lock();
45771947923SIoana Ciornei 	phylink_disconnect_phy(mac->phylink);
45871947923SIoana Ciornei 	rtnl_unlock();
45987db82cbSVladimir Oltean 
46071947923SIoana Ciornei 	phylink_destroy(mac->phylink);
46187db82cbSVladimir Oltean 	dpaa2_pcs_destroy(mac);
46287db82cbSVladimir Oltean 	of_phy_put(mac->serdes_phy);
46371947923SIoana Ciornei 	mac->serdes_phy = NULL;
46494ae899bSIoana Ciornei }
465f978fe85SIoana Ciornei 
dpaa2_mac_open(struct dpaa2_mac * mac)466f978fe85SIoana Ciornei int dpaa2_mac_open(struct dpaa2_mac *mac)
467095dca16SIoana Ciornei {
46894ae899bSIoana Ciornei 	struct fsl_mc_device *dpmac_dev = mac->mc_dev;
469095dca16SIoana Ciornei 	struct net_device *net_dev = mac->net_dev;
470095dca16SIoana Ciornei 	struct fwnode_handle *fw_node;
471095dca16SIoana Ciornei 	int err;
472095dca16SIoana Ciornei 
4734e30e98cSIoana Ciornei 	err = dpmac_open(mac->mc_io, 0, dpmac_dev->obj_desc.id,
474095dca16SIoana Ciornei 			 &dpmac_dev->mc_handle);
475095dca16SIoana Ciornei 	if (err || !dpmac_dev->mc_handle) {
476095dca16SIoana Ciornei 		netdev_err(net_dev, "dpmac_open() = %d\n", err);
477095dca16SIoana Ciornei 		return -ENODEV;
478095dca16SIoana Ciornei 	}
479095dca16SIoana Ciornei 
480095dca16SIoana Ciornei 	err = dpmac_get_attributes(mac->mc_io, 0, dpmac_dev->mc_handle,
481095dca16SIoana Ciornei 				   &mac->attr);
482095dca16SIoana Ciornei 	if (err) {
483095dca16SIoana Ciornei 		netdev_err(net_dev, "dpmac_get_attributes() = %d\n", err);
484095dca16SIoana Ciornei 		goto err_close_dpmac;
485095dca16SIoana Ciornei 	}
486095dca16SIoana Ciornei 
487095dca16SIoana Ciornei 	err = dpmac_get_api_version(mac->mc_io, 0, &mac->ver_major, &mac->ver_minor);
488095dca16SIoana Ciornei 	if (err) {
489095dca16SIoana Ciornei 		netdev_err(net_dev, "dpmac_get_api_version() = %d\n", err);
490dff95381SIoana Ciornei 		goto err_close_dpmac;
491dff95381SIoana Ciornei 	}
492dff95381SIoana Ciornei 
493dff95381SIoana Ciornei 	dpaa2_mac_detect_features(mac);
494dff95381SIoana Ciornei 
495dff95381SIoana Ciornei 	/* Find the device node representing the MAC device and link the device
496dff95381SIoana Ciornei 	 * behind the associated netdev to it.
497dff95381SIoana Ciornei 	 */
498b193f2edSIoana Ciornei 	fw_node = dpaa2_mac_get_node(&mac->mc_dev->dev, mac->attr.id);
499b193f2edSIoana Ciornei 	if (IS_ERR(fw_node)) {
500b193f2edSIoana Ciornei 		err = PTR_ERR(fw_node);
5014e30e98cSIoana Ciornei 		goto err_close_dpmac;
5024e30e98cSIoana Ciornei 	}
5034e30e98cSIoana Ciornei 
5044e30e98cSIoana Ciornei 	mac->fw_node = fw_node;
5054e30e98cSIoana Ciornei 	net_dev->dev.of_node = to_of_node(mac->fw_node);
5064e30e98cSIoana Ciornei 
5074e30e98cSIoana Ciornei 	return 0;
5083264f599SCalvin Johnson 
509b193f2edSIoana Ciornei err_close_dpmac:
510095dca16SIoana Ciornei 	dpmac_close(mac->mc_io, 0, dpmac_dev->mc_handle);
511095dca16SIoana Ciornei 	return err;
512095dca16SIoana Ciornei }
513095dca16SIoana Ciornei 
dpaa2_mac_close(struct dpaa2_mac * mac)514095dca16SIoana Ciornei void dpaa2_mac_close(struct dpaa2_mac *mac)
515095dca16SIoana Ciornei {
516095dca16SIoana Ciornei 	struct fsl_mc_device *dpmac_dev = mac->mc_dev;
517095dca16SIoana Ciornei 
518095dca16SIoana Ciornei 	dpmac_close(mac->mc_io, 0, dpmac_dev->mc_handle);
519095dca16SIoana Ciornei 	if (mac->fw_node)
520095dca16SIoana Ciornei 		fwnode_handle_put(mac->fw_node);
521095dca16SIoana Ciornei }
5223264f599SCalvin Johnson 
5233264f599SCalvin Johnson static char dpaa2_mac_ethtool_stats[][ETH_GSTRING_LEN] = {
52471947923SIoana Ciornei 	[DPMAC_CNT_ING_ALL_FRAME]		= "[mac] rx all frames",
525991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_GOOD_FRAME]		= "[mac] rx frames ok",
526991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_ERR_FRAME]		= "[mac] rx frame errors",
527991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_FRAME_DISCARD]		= "[mac] rx frame discards",
528991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_UCAST_FRAME]		= "[mac] rx u-cast",
529991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_BCAST_FRAME]		= "[mac] rx b-cast",
530991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_MCAST_FRAME]		= "[mac] rx m-cast",
531991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_FRAME_64]		= "[mac] rx 64 bytes",
532991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_FRAME_127]		= "[mac] rx 65-127 bytes",
533991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_FRAME_255]		= "[mac] rx 128-255 bytes",
534991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_FRAME_511]		= "[mac] rx 256-511 bytes",
535991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_FRAME_1023]		= "[mac] rx 512-1023 bytes",
536991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_FRAME_1518]		= "[mac] rx 1024-1518 bytes",
537991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_FRAME_1519_MAX]		= "[mac] rx 1519-max bytes",
538991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_FRAG]			= "[mac] rx frags",
539991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_JABBER]			= "[mac] rx jabber",
540991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_ALIGN_ERR]		= "[mac] rx align errors",
541991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_OVERSIZED]		= "[mac] rx oversized",
542991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_VALID_PAUSE_FRAME]	= "[mac] rx pause",
543991df1fbSIoana Ciornei 	[DPMAC_CNT_ING_BYTE]			= "[mac] rx bytes",
544991df1fbSIoana Ciornei 	[DPMAC_CNT_EGR_GOOD_FRAME]		= "[mac] tx frames ok",
545991df1fbSIoana Ciornei 	[DPMAC_CNT_EGR_UCAST_FRAME]		= "[mac] tx u-cast",
546991df1fbSIoana Ciornei 	[DPMAC_CNT_EGR_MCAST_FRAME]		= "[mac] tx m-cast",
547991df1fbSIoana Ciornei 	[DPMAC_CNT_EGR_BCAST_FRAME]		= "[mac] tx b-cast",
548991df1fbSIoana Ciornei 	[DPMAC_CNT_EGR_ERR_FRAME]		= "[mac] tx frame errors",
549991df1fbSIoana Ciornei 	[DPMAC_CNT_EGR_UNDERSIZED]		= "[mac] tx undersized",
550991df1fbSIoana Ciornei 	[DPMAC_CNT_EGR_VALID_PAUSE_FRAME]	= "[mac] tx b-pause",
551991df1fbSIoana Ciornei 	[DPMAC_CNT_EGR_BYTE]			= "[mac] tx bytes",
552991df1fbSIoana Ciornei };
553991df1fbSIoana Ciornei 
554991df1fbSIoana Ciornei #define DPAA2_MAC_NUM_STATS	ARRAY_SIZE(dpaa2_mac_ethtool_stats)
555991df1fbSIoana Ciornei 
dpaa2_mac_get_sset_count(void)556991df1fbSIoana Ciornei int dpaa2_mac_get_sset_count(void)
557991df1fbSIoana Ciornei {
558991df1fbSIoana Ciornei 	return DPAA2_MAC_NUM_STATS;
559991df1fbSIoana Ciornei }
560991df1fbSIoana Ciornei 
dpaa2_mac_get_strings(u8 * data)561991df1fbSIoana Ciornei void dpaa2_mac_get_strings(u8 *data)
562991df1fbSIoana Ciornei {
563991df1fbSIoana Ciornei 	u8 *p = data;
564991df1fbSIoana Ciornei 	int i;
565991df1fbSIoana Ciornei 
566991df1fbSIoana Ciornei 	for (i = 0; i < DPAA2_MAC_NUM_STATS; i++) {
567991df1fbSIoana Ciornei 		strscpy(p, dpaa2_mac_ethtool_stats[i], ETH_GSTRING_LEN);
568991df1fbSIoana Ciornei 		p += ETH_GSTRING_LEN;
569991df1fbSIoana Ciornei 	}
570f029c781SWolfram Sang }
571991df1fbSIoana Ciornei 
dpaa2_mac_get_ethtool_stats(struct dpaa2_mac * mac,u64 * data)572991df1fbSIoana Ciornei void dpaa2_mac_get_ethtool_stats(struct dpaa2_mac *mac, u64 *data)
573991df1fbSIoana Ciornei {
574991df1fbSIoana Ciornei 	struct fsl_mc_device *dpmac_dev = mac->mc_dev;
575991df1fbSIoana Ciornei 	int i, err;
576991df1fbSIoana Ciornei 	u64 value;
577991df1fbSIoana Ciornei 
578991df1fbSIoana Ciornei 	for (i = 0; i < DPAA2_MAC_NUM_STATS; i++) {
579991df1fbSIoana Ciornei 		err = dpmac_get_counter(mac->mc_io, 0, dpmac_dev->mc_handle,
580991df1fbSIoana Ciornei 					i, &value);
581991df1fbSIoana Ciornei 		if (err) {
582991df1fbSIoana Ciornei 			netdev_err_once(mac->net_dev,
583991df1fbSIoana Ciornei 					"dpmac_get_counter error %d\n", err);
584991df1fbSIoana Ciornei 			*(data + i) = U64_MAX;
585991df1fbSIoana Ciornei 			continue;
586991df1fbSIoana Ciornei 		}
587991df1fbSIoana Ciornei 		*(data + i) = value;
588991df1fbSIoana Ciornei 	}
589991df1fbSIoana Ciornei }
590991df1fbSIoana Ciornei