162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
262306a36Sopenharmony_ci/* Copyright 2019 NXP */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/acpi.h>
562306a36Sopenharmony_ci#include <linux/pcs-lynx.h>
662306a36Sopenharmony_ci#include <linux/phy/phy.h>
762306a36Sopenharmony_ci#include <linux/property.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "dpaa2-eth.h"
1062306a36Sopenharmony_ci#include "dpaa2-mac.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define phylink_to_dpaa2_mac(config) \
1362306a36Sopenharmony_ci	container_of((config), struct dpaa2_mac, phylink_config)
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define DPMAC_PROTOCOL_CHANGE_VER_MAJOR		4
1662306a36Sopenharmony_ci#define DPMAC_PROTOCOL_CHANGE_VER_MINOR		8
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define DPAA2_MAC_FEATURE_PROTOCOL_CHANGE	BIT(0)
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic int dpaa2_mac_cmp_ver(struct dpaa2_mac *mac,
2162306a36Sopenharmony_ci			     u16 ver_major, u16 ver_minor)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	if (mac->ver_major == ver_major)
2462306a36Sopenharmony_ci		return mac->ver_minor - ver_minor;
2562306a36Sopenharmony_ci	return mac->ver_major - ver_major;
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic void dpaa2_mac_detect_features(struct dpaa2_mac *mac)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	mac->features = 0;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (dpaa2_mac_cmp_ver(mac, DPMAC_PROTOCOL_CHANGE_VER_MAJOR,
3362306a36Sopenharmony_ci			      DPMAC_PROTOCOL_CHANGE_VER_MINOR) >= 0)
3462306a36Sopenharmony_ci		mac->features |= DPAA2_MAC_FEATURE_PROTOCOL_CHANGE;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic int phy_mode(enum dpmac_eth_if eth_if, phy_interface_t *if_mode)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	*if_mode = PHY_INTERFACE_MODE_NA;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	switch (eth_if) {
4262306a36Sopenharmony_ci	case DPMAC_ETH_IF_RGMII:
4362306a36Sopenharmony_ci		*if_mode = PHY_INTERFACE_MODE_RGMII;
4462306a36Sopenharmony_ci		break;
4562306a36Sopenharmony_ci	case DPMAC_ETH_IF_USXGMII:
4662306a36Sopenharmony_ci		*if_mode = PHY_INTERFACE_MODE_USXGMII;
4762306a36Sopenharmony_ci		break;
4862306a36Sopenharmony_ci	case DPMAC_ETH_IF_QSGMII:
4962306a36Sopenharmony_ci		*if_mode = PHY_INTERFACE_MODE_QSGMII;
5062306a36Sopenharmony_ci		break;
5162306a36Sopenharmony_ci	case DPMAC_ETH_IF_SGMII:
5262306a36Sopenharmony_ci		*if_mode = PHY_INTERFACE_MODE_SGMII;
5362306a36Sopenharmony_ci		break;
5462306a36Sopenharmony_ci	case DPMAC_ETH_IF_XFI:
5562306a36Sopenharmony_ci		*if_mode = PHY_INTERFACE_MODE_10GBASER;
5662306a36Sopenharmony_ci		break;
5762306a36Sopenharmony_ci	case DPMAC_ETH_IF_CAUI:
5862306a36Sopenharmony_ci		*if_mode = PHY_INTERFACE_MODE_25GBASER;
5962306a36Sopenharmony_ci		break;
6062306a36Sopenharmony_ci	default:
6162306a36Sopenharmony_ci		return -EINVAL;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	return 0;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic enum dpmac_eth_if dpmac_eth_if_mode(phy_interface_t if_mode)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	switch (if_mode) {
7062306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII:
7162306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_ID:
7262306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_RXID:
7362306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_RGMII_TXID:
7462306a36Sopenharmony_ci		return DPMAC_ETH_IF_RGMII;
7562306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_USXGMII:
7662306a36Sopenharmony_ci		return DPMAC_ETH_IF_USXGMII;
7762306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_QSGMII:
7862306a36Sopenharmony_ci		return DPMAC_ETH_IF_QSGMII;
7962306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_SGMII:
8062306a36Sopenharmony_ci		return DPMAC_ETH_IF_SGMII;
8162306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_10GBASER:
8262306a36Sopenharmony_ci		return DPMAC_ETH_IF_XFI;
8362306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_1000BASEX:
8462306a36Sopenharmony_ci		return DPMAC_ETH_IF_1000BASEX;
8562306a36Sopenharmony_ci	case PHY_INTERFACE_MODE_25GBASER:
8662306a36Sopenharmony_ci		return DPMAC_ETH_IF_CAUI;
8762306a36Sopenharmony_ci	default:
8862306a36Sopenharmony_ci		return DPMAC_ETH_IF_MII;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic struct fwnode_handle *dpaa2_mac_get_node(struct device *dev,
9362306a36Sopenharmony_ci						u16 dpmac_id)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct fwnode_handle *fwnode, *parent = NULL, *child  = NULL;
9662306a36Sopenharmony_ci	struct device_node *dpmacs = NULL;
9762306a36Sopenharmony_ci	int err;
9862306a36Sopenharmony_ci	u32 id;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	fwnode = dev_fwnode(dev->parent);
10162306a36Sopenharmony_ci	if (is_of_node(fwnode)) {
10262306a36Sopenharmony_ci		dpmacs = of_find_node_by_name(NULL, "dpmacs");
10362306a36Sopenharmony_ci		if (!dpmacs)
10462306a36Sopenharmony_ci			return NULL;
10562306a36Sopenharmony_ci		parent = of_fwnode_handle(dpmacs);
10662306a36Sopenharmony_ci	} else if (is_acpi_node(fwnode)) {
10762306a36Sopenharmony_ci		parent = fwnode;
10862306a36Sopenharmony_ci	} else {
10962306a36Sopenharmony_ci		/* The root dprc device didn't yet get to finalize it's probe,
11062306a36Sopenharmony_ci		 * thus the fwnode field is not yet set. Defer probe if we are
11162306a36Sopenharmony_ci		 * facing this situation.
11262306a36Sopenharmony_ci		 */
11362306a36Sopenharmony_ci		dev_dbg(dev, "dprc not finished probing\n");
11462306a36Sopenharmony_ci		return ERR_PTR(-EPROBE_DEFER);
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	fwnode_for_each_child_node(parent, child) {
11862306a36Sopenharmony_ci		err = -EINVAL;
11962306a36Sopenharmony_ci		if (is_acpi_device_node(child))
12062306a36Sopenharmony_ci			err = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), &id);
12162306a36Sopenharmony_ci		else if (is_of_node(child))
12262306a36Sopenharmony_ci			err = of_property_read_u32(to_of_node(child), "reg", &id);
12362306a36Sopenharmony_ci		if (err)
12462306a36Sopenharmony_ci			continue;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci		if (id == dpmac_id) {
12762306a36Sopenharmony_ci			of_node_put(dpmacs);
12862306a36Sopenharmony_ci			return child;
12962306a36Sopenharmony_ci		}
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci	of_node_put(dpmacs);
13262306a36Sopenharmony_ci	return NULL;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic int dpaa2_mac_get_if_mode(struct fwnode_handle *dpmac_node,
13662306a36Sopenharmony_ci				 struct dpmac_attr attr)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	phy_interface_t if_mode;
13962306a36Sopenharmony_ci	int err;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	err = fwnode_get_phy_mode(dpmac_node);
14262306a36Sopenharmony_ci	if (err > 0)
14362306a36Sopenharmony_ci		return err;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	err = phy_mode(attr.eth_if, &if_mode);
14662306a36Sopenharmony_ci	if (!err)
14762306a36Sopenharmony_ci		return if_mode;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return err;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic struct phylink_pcs *dpaa2_mac_select_pcs(struct phylink_config *config,
15362306a36Sopenharmony_ci						phy_interface_t interface)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	return mac->pcs;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic void dpaa2_mac_config(struct phylink_config *config, unsigned int mode,
16162306a36Sopenharmony_ci			     const struct phylink_link_state *state)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config);
16462306a36Sopenharmony_ci	struct dpmac_link_state *dpmac_state = &mac->state;
16562306a36Sopenharmony_ci	int err;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
16862306a36Sopenharmony_ci			      state->advertising))
16962306a36Sopenharmony_ci		dpmac_state->options |= DPMAC_LINK_OPT_AUTONEG;
17062306a36Sopenharmony_ci	else
17162306a36Sopenharmony_ci		dpmac_state->options &= ~DPMAC_LINK_OPT_AUTONEG;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	err = dpmac_set_link_state(mac->mc_io, 0,
17462306a36Sopenharmony_ci				   mac->mc_dev->mc_handle, dpmac_state);
17562306a36Sopenharmony_ci	if (err)
17662306a36Sopenharmony_ci		netdev_err(mac->net_dev, "%s: dpmac_set_link_state() = %d\n",
17762306a36Sopenharmony_ci			   __func__, err);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (!mac->serdes_phy)
18062306a36Sopenharmony_ci		return;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* This happens only if we support changing of protocol at runtime */
18362306a36Sopenharmony_ci	err = dpmac_set_protocol(mac->mc_io, 0, mac->mc_dev->mc_handle,
18462306a36Sopenharmony_ci				 dpmac_eth_if_mode(state->interface));
18562306a36Sopenharmony_ci	if (err)
18662306a36Sopenharmony_ci		netdev_err(mac->net_dev,  "dpmac_set_protocol() = %d\n", err);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	err = phy_set_mode_ext(mac->serdes_phy, PHY_MODE_ETHERNET, state->interface);
18962306a36Sopenharmony_ci	if (err)
19062306a36Sopenharmony_ci		netdev_err(mac->net_dev, "phy_set_mode_ext() = %d\n", err);
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic void dpaa2_mac_link_up(struct phylink_config *config,
19462306a36Sopenharmony_ci			      struct phy_device *phy,
19562306a36Sopenharmony_ci			      unsigned int mode, phy_interface_t interface,
19662306a36Sopenharmony_ci			      int speed, int duplex,
19762306a36Sopenharmony_ci			      bool tx_pause, bool rx_pause)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config);
20062306a36Sopenharmony_ci	struct dpmac_link_state *dpmac_state = &mac->state;
20162306a36Sopenharmony_ci	int err;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	dpmac_state->up = 1;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	dpmac_state->rate = speed;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	if (duplex == DUPLEX_HALF)
20862306a36Sopenharmony_ci		dpmac_state->options |= DPMAC_LINK_OPT_HALF_DUPLEX;
20962306a36Sopenharmony_ci	else if (duplex == DUPLEX_FULL)
21062306a36Sopenharmony_ci		dpmac_state->options &= ~DPMAC_LINK_OPT_HALF_DUPLEX;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (rx_pause)
21362306a36Sopenharmony_ci		dpmac_state->options |= DPMAC_LINK_OPT_PAUSE;
21462306a36Sopenharmony_ci	else
21562306a36Sopenharmony_ci		dpmac_state->options &= ~DPMAC_LINK_OPT_PAUSE;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	if (rx_pause ^ tx_pause)
21862306a36Sopenharmony_ci		dpmac_state->options |= DPMAC_LINK_OPT_ASYM_PAUSE;
21962306a36Sopenharmony_ci	else
22062306a36Sopenharmony_ci		dpmac_state->options &= ~DPMAC_LINK_OPT_ASYM_PAUSE;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	err = dpmac_set_link_state(mac->mc_io, 0,
22362306a36Sopenharmony_ci				   mac->mc_dev->mc_handle, dpmac_state);
22462306a36Sopenharmony_ci	if (err)
22562306a36Sopenharmony_ci		netdev_err(mac->net_dev, "%s: dpmac_set_link_state() = %d\n",
22662306a36Sopenharmony_ci			   __func__, err);
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic void dpaa2_mac_link_down(struct phylink_config *config,
23062306a36Sopenharmony_ci				unsigned int mode,
23162306a36Sopenharmony_ci				phy_interface_t interface)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config);
23462306a36Sopenharmony_ci	struct dpmac_link_state *dpmac_state = &mac->state;
23562306a36Sopenharmony_ci	int err;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	dpmac_state->up = 0;
23862306a36Sopenharmony_ci	err = dpmac_set_link_state(mac->mc_io, 0,
23962306a36Sopenharmony_ci				   mac->mc_dev->mc_handle, dpmac_state);
24062306a36Sopenharmony_ci	if (err)
24162306a36Sopenharmony_ci		netdev_err(mac->net_dev, "dpmac_set_link_state() = %d\n", err);
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic const struct phylink_mac_ops dpaa2_mac_phylink_ops = {
24562306a36Sopenharmony_ci	.mac_select_pcs = dpaa2_mac_select_pcs,
24662306a36Sopenharmony_ci	.mac_config = dpaa2_mac_config,
24762306a36Sopenharmony_ci	.mac_link_up = dpaa2_mac_link_up,
24862306a36Sopenharmony_ci	.mac_link_down = dpaa2_mac_link_down,
24962306a36Sopenharmony_ci};
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic int dpaa2_pcs_create(struct dpaa2_mac *mac,
25262306a36Sopenharmony_ci			    struct fwnode_handle *dpmac_node,
25362306a36Sopenharmony_ci			    int id)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	struct fwnode_handle *node;
25662306a36Sopenharmony_ci	struct phylink_pcs *pcs;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	node = fwnode_find_reference(dpmac_node, "pcs-handle", 0);
25962306a36Sopenharmony_ci	if (IS_ERR(node)) {
26062306a36Sopenharmony_ci		/* do not error out on old DTS files */
26162306a36Sopenharmony_ci		netdev_warn(mac->net_dev, "pcs-handle node not found\n");
26262306a36Sopenharmony_ci		return 0;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	pcs = lynx_pcs_create_fwnode(node);
26662306a36Sopenharmony_ci	fwnode_handle_put(node);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (pcs == ERR_PTR(-EPROBE_DEFER)) {
26962306a36Sopenharmony_ci		netdev_dbg(mac->net_dev, "missing PCS device\n");
27062306a36Sopenharmony_ci		return -EPROBE_DEFER;
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	if (pcs == ERR_PTR(-ENODEV)) {
27462306a36Sopenharmony_ci		netdev_err(mac->net_dev, "pcs-handle node not available\n");
27562306a36Sopenharmony_ci		return PTR_ERR(pcs);
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (IS_ERR(pcs)) {
27962306a36Sopenharmony_ci		netdev_err(mac->net_dev,
28062306a36Sopenharmony_ci			   "lynx_pcs_create_fwnode() failed: %pe\n", pcs);
28162306a36Sopenharmony_ci		return PTR_ERR(pcs);
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	mac->pcs = pcs;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	return 0;
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic void dpaa2_pcs_destroy(struct dpaa2_mac *mac)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	struct phylink_pcs *phylink_pcs = mac->pcs;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (phylink_pcs) {
29462306a36Sopenharmony_ci		lynx_pcs_destroy(phylink_pcs);
29562306a36Sopenharmony_ci		mac->pcs = NULL;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic void dpaa2_mac_set_supported_interfaces(struct dpaa2_mac *mac)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	int intf, err;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	/* We support the current interface mode, and if we have a PCS
30462306a36Sopenharmony_ci	 * similar interface modes that do not require the SerDes lane to be
30562306a36Sopenharmony_ci	 * reconfigured.
30662306a36Sopenharmony_ci	 */
30762306a36Sopenharmony_ci	__set_bit(mac->if_mode, mac->phylink_config.supported_interfaces);
30862306a36Sopenharmony_ci	if (mac->pcs) {
30962306a36Sopenharmony_ci		switch (mac->if_mode) {
31062306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_1000BASEX:
31162306a36Sopenharmony_ci		case PHY_INTERFACE_MODE_SGMII:
31262306a36Sopenharmony_ci			__set_bit(PHY_INTERFACE_MODE_1000BASEX,
31362306a36Sopenharmony_ci				  mac->phylink_config.supported_interfaces);
31462306a36Sopenharmony_ci			__set_bit(PHY_INTERFACE_MODE_SGMII,
31562306a36Sopenharmony_ci				  mac->phylink_config.supported_interfaces);
31662306a36Sopenharmony_ci			break;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		default:
31962306a36Sopenharmony_ci			break;
32062306a36Sopenharmony_ci		}
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (!mac->serdes_phy)
32462306a36Sopenharmony_ci		return;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	/* In case we have access to the SerDes phy/lane, then ask the SerDes
32762306a36Sopenharmony_ci	 * driver what interfaces are supported based on the current PLL
32862306a36Sopenharmony_ci	 * configuration.
32962306a36Sopenharmony_ci	 */
33062306a36Sopenharmony_ci	for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) {
33162306a36Sopenharmony_ci		if (intf == PHY_INTERFACE_MODE_NA)
33262306a36Sopenharmony_ci			continue;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci		err = phy_validate(mac->serdes_phy, PHY_MODE_ETHERNET, intf, NULL);
33562306a36Sopenharmony_ci		if (err)
33662306a36Sopenharmony_ci			continue;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci		__set_bit(intf, mac->phylink_config.supported_interfaces);
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_civoid dpaa2_mac_start(struct dpaa2_mac *mac)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	ASSERT_RTNL();
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	if (mac->serdes_phy)
34762306a36Sopenharmony_ci		phy_power_on(mac->serdes_phy);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	phylink_start(mac->phylink);
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_civoid dpaa2_mac_stop(struct dpaa2_mac *mac)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	ASSERT_RTNL();
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	phylink_stop(mac->phylink);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	if (mac->serdes_phy)
35962306a36Sopenharmony_ci		phy_power_off(mac->serdes_phy);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ciint dpaa2_mac_connect(struct dpaa2_mac *mac)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	struct net_device *net_dev = mac->net_dev;
36562306a36Sopenharmony_ci	struct fwnode_handle *dpmac_node;
36662306a36Sopenharmony_ci	struct phy *serdes_phy = NULL;
36762306a36Sopenharmony_ci	struct phylink *phylink;
36862306a36Sopenharmony_ci	int err;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	mac->if_link_type = mac->attr.link_type;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	dpmac_node = mac->fw_node;
37362306a36Sopenharmony_ci	if (!dpmac_node) {
37462306a36Sopenharmony_ci		netdev_err(net_dev, "No dpmac@%d node found.\n", mac->attr.id);
37562306a36Sopenharmony_ci		return -ENODEV;
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	err = dpaa2_mac_get_if_mode(dpmac_node, mac->attr);
37962306a36Sopenharmony_ci	if (err < 0)
38062306a36Sopenharmony_ci		return -EINVAL;
38162306a36Sopenharmony_ci	mac->if_mode = err;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	if (mac->features & DPAA2_MAC_FEATURE_PROTOCOL_CHANGE &&
38462306a36Sopenharmony_ci	    !phy_interface_mode_is_rgmii(mac->if_mode) &&
38562306a36Sopenharmony_ci	    is_of_node(dpmac_node)) {
38662306a36Sopenharmony_ci		serdes_phy = of_phy_get(to_of_node(dpmac_node), NULL);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		if (serdes_phy == ERR_PTR(-ENODEV))
38962306a36Sopenharmony_ci			serdes_phy = NULL;
39062306a36Sopenharmony_ci		else if (IS_ERR(serdes_phy))
39162306a36Sopenharmony_ci			return PTR_ERR(serdes_phy);
39262306a36Sopenharmony_ci		else
39362306a36Sopenharmony_ci			phy_init(serdes_phy);
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci	mac->serdes_phy = serdes_phy;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	/* The MAC does not have the capability to add RGMII delays so
39862306a36Sopenharmony_ci	 * error out if the interface mode requests them and there is no PHY
39962306a36Sopenharmony_ci	 * to act upon them
40062306a36Sopenharmony_ci	 */
40162306a36Sopenharmony_ci	if (of_phy_is_fixed_link(to_of_node(dpmac_node)) &&
40262306a36Sopenharmony_ci	    (mac->if_mode == PHY_INTERFACE_MODE_RGMII_ID ||
40362306a36Sopenharmony_ci	     mac->if_mode == PHY_INTERFACE_MODE_RGMII_RXID ||
40462306a36Sopenharmony_ci	     mac->if_mode == PHY_INTERFACE_MODE_RGMII_TXID)) {
40562306a36Sopenharmony_ci		netdev_err(net_dev, "RGMII delay not supported\n");
40662306a36Sopenharmony_ci		return -EINVAL;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if ((mac->attr.link_type == DPMAC_LINK_TYPE_PHY &&
41062306a36Sopenharmony_ci	     mac->attr.eth_if != DPMAC_ETH_IF_RGMII) ||
41162306a36Sopenharmony_ci	    mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE) {
41262306a36Sopenharmony_ci		err = dpaa2_pcs_create(mac, dpmac_node, mac->attr.id);
41362306a36Sopenharmony_ci		if (err)
41462306a36Sopenharmony_ci			return err;
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	memset(&mac->phylink_config, 0, sizeof(mac->phylink_config));
41862306a36Sopenharmony_ci	mac->phylink_config.dev = &net_dev->dev;
41962306a36Sopenharmony_ci	mac->phylink_config.type = PHYLINK_NETDEV;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	mac->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE |
42262306a36Sopenharmony_ci		MAC_10FD | MAC_100FD | MAC_1000FD | MAC_2500FD | MAC_5000FD |
42362306a36Sopenharmony_ci		MAC_10000FD | MAC_25000FD;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	dpaa2_mac_set_supported_interfaces(mac);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	phylink = phylink_create(&mac->phylink_config,
42862306a36Sopenharmony_ci				 dpmac_node, mac->if_mode,
42962306a36Sopenharmony_ci				 &dpaa2_mac_phylink_ops);
43062306a36Sopenharmony_ci	if (IS_ERR(phylink)) {
43162306a36Sopenharmony_ci		err = PTR_ERR(phylink);
43262306a36Sopenharmony_ci		goto err_pcs_destroy;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci	mac->phylink = phylink;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	rtnl_lock();
43762306a36Sopenharmony_ci	err = phylink_fwnode_phy_connect(mac->phylink, dpmac_node, 0);
43862306a36Sopenharmony_ci	rtnl_unlock();
43962306a36Sopenharmony_ci	if (err) {
44062306a36Sopenharmony_ci		netdev_err(net_dev, "phylink_fwnode_phy_connect() = %d\n", err);
44162306a36Sopenharmony_ci		goto err_phylink_destroy;
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	return 0;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cierr_phylink_destroy:
44762306a36Sopenharmony_ci	phylink_destroy(mac->phylink);
44862306a36Sopenharmony_cierr_pcs_destroy:
44962306a36Sopenharmony_ci	dpaa2_pcs_destroy(mac);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	return err;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_civoid dpaa2_mac_disconnect(struct dpaa2_mac *mac)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	rtnl_lock();
45762306a36Sopenharmony_ci	phylink_disconnect_phy(mac->phylink);
45862306a36Sopenharmony_ci	rtnl_unlock();
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	phylink_destroy(mac->phylink);
46162306a36Sopenharmony_ci	dpaa2_pcs_destroy(mac);
46262306a36Sopenharmony_ci	of_phy_put(mac->serdes_phy);
46362306a36Sopenharmony_ci	mac->serdes_phy = NULL;
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ciint dpaa2_mac_open(struct dpaa2_mac *mac)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	struct fsl_mc_device *dpmac_dev = mac->mc_dev;
46962306a36Sopenharmony_ci	struct net_device *net_dev = mac->net_dev;
47062306a36Sopenharmony_ci	struct fwnode_handle *fw_node;
47162306a36Sopenharmony_ci	int err;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	err = dpmac_open(mac->mc_io, 0, dpmac_dev->obj_desc.id,
47462306a36Sopenharmony_ci			 &dpmac_dev->mc_handle);
47562306a36Sopenharmony_ci	if (err || !dpmac_dev->mc_handle) {
47662306a36Sopenharmony_ci		netdev_err(net_dev, "dpmac_open() = %d\n", err);
47762306a36Sopenharmony_ci		return -ENODEV;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	err = dpmac_get_attributes(mac->mc_io, 0, dpmac_dev->mc_handle,
48162306a36Sopenharmony_ci				   &mac->attr);
48262306a36Sopenharmony_ci	if (err) {
48362306a36Sopenharmony_ci		netdev_err(net_dev, "dpmac_get_attributes() = %d\n", err);
48462306a36Sopenharmony_ci		goto err_close_dpmac;
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	err = dpmac_get_api_version(mac->mc_io, 0, &mac->ver_major, &mac->ver_minor);
48862306a36Sopenharmony_ci	if (err) {
48962306a36Sopenharmony_ci		netdev_err(net_dev, "dpmac_get_api_version() = %d\n", err);
49062306a36Sopenharmony_ci		goto err_close_dpmac;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	dpaa2_mac_detect_features(mac);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	/* Find the device node representing the MAC device and link the device
49662306a36Sopenharmony_ci	 * behind the associated netdev to it.
49762306a36Sopenharmony_ci	 */
49862306a36Sopenharmony_ci	fw_node = dpaa2_mac_get_node(&mac->mc_dev->dev, mac->attr.id);
49962306a36Sopenharmony_ci	if (IS_ERR(fw_node)) {
50062306a36Sopenharmony_ci		err = PTR_ERR(fw_node);
50162306a36Sopenharmony_ci		goto err_close_dpmac;
50262306a36Sopenharmony_ci	}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	mac->fw_node = fw_node;
50562306a36Sopenharmony_ci	net_dev->dev.of_node = to_of_node(mac->fw_node);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	return 0;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cierr_close_dpmac:
51062306a36Sopenharmony_ci	dpmac_close(mac->mc_io, 0, dpmac_dev->mc_handle);
51162306a36Sopenharmony_ci	return err;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_civoid dpaa2_mac_close(struct dpaa2_mac *mac)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	struct fsl_mc_device *dpmac_dev = mac->mc_dev;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	dpmac_close(mac->mc_io, 0, dpmac_dev->mc_handle);
51962306a36Sopenharmony_ci	if (mac->fw_node)
52062306a36Sopenharmony_ci		fwnode_handle_put(mac->fw_node);
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic char dpaa2_mac_ethtool_stats[][ETH_GSTRING_LEN] = {
52462306a36Sopenharmony_ci	[DPMAC_CNT_ING_ALL_FRAME]		= "[mac] rx all frames",
52562306a36Sopenharmony_ci	[DPMAC_CNT_ING_GOOD_FRAME]		= "[mac] rx frames ok",
52662306a36Sopenharmony_ci	[DPMAC_CNT_ING_ERR_FRAME]		= "[mac] rx frame errors",
52762306a36Sopenharmony_ci	[DPMAC_CNT_ING_FRAME_DISCARD]		= "[mac] rx frame discards",
52862306a36Sopenharmony_ci	[DPMAC_CNT_ING_UCAST_FRAME]		= "[mac] rx u-cast",
52962306a36Sopenharmony_ci	[DPMAC_CNT_ING_BCAST_FRAME]		= "[mac] rx b-cast",
53062306a36Sopenharmony_ci	[DPMAC_CNT_ING_MCAST_FRAME]		= "[mac] rx m-cast",
53162306a36Sopenharmony_ci	[DPMAC_CNT_ING_FRAME_64]		= "[mac] rx 64 bytes",
53262306a36Sopenharmony_ci	[DPMAC_CNT_ING_FRAME_127]		= "[mac] rx 65-127 bytes",
53362306a36Sopenharmony_ci	[DPMAC_CNT_ING_FRAME_255]		= "[mac] rx 128-255 bytes",
53462306a36Sopenharmony_ci	[DPMAC_CNT_ING_FRAME_511]		= "[mac] rx 256-511 bytes",
53562306a36Sopenharmony_ci	[DPMAC_CNT_ING_FRAME_1023]		= "[mac] rx 512-1023 bytes",
53662306a36Sopenharmony_ci	[DPMAC_CNT_ING_FRAME_1518]		= "[mac] rx 1024-1518 bytes",
53762306a36Sopenharmony_ci	[DPMAC_CNT_ING_FRAME_1519_MAX]		= "[mac] rx 1519-max bytes",
53862306a36Sopenharmony_ci	[DPMAC_CNT_ING_FRAG]			= "[mac] rx frags",
53962306a36Sopenharmony_ci	[DPMAC_CNT_ING_JABBER]			= "[mac] rx jabber",
54062306a36Sopenharmony_ci	[DPMAC_CNT_ING_ALIGN_ERR]		= "[mac] rx align errors",
54162306a36Sopenharmony_ci	[DPMAC_CNT_ING_OVERSIZED]		= "[mac] rx oversized",
54262306a36Sopenharmony_ci	[DPMAC_CNT_ING_VALID_PAUSE_FRAME]	= "[mac] rx pause",
54362306a36Sopenharmony_ci	[DPMAC_CNT_ING_BYTE]			= "[mac] rx bytes",
54462306a36Sopenharmony_ci	[DPMAC_CNT_EGR_GOOD_FRAME]		= "[mac] tx frames ok",
54562306a36Sopenharmony_ci	[DPMAC_CNT_EGR_UCAST_FRAME]		= "[mac] tx u-cast",
54662306a36Sopenharmony_ci	[DPMAC_CNT_EGR_MCAST_FRAME]		= "[mac] tx m-cast",
54762306a36Sopenharmony_ci	[DPMAC_CNT_EGR_BCAST_FRAME]		= "[mac] tx b-cast",
54862306a36Sopenharmony_ci	[DPMAC_CNT_EGR_ERR_FRAME]		= "[mac] tx frame errors",
54962306a36Sopenharmony_ci	[DPMAC_CNT_EGR_UNDERSIZED]		= "[mac] tx undersized",
55062306a36Sopenharmony_ci	[DPMAC_CNT_EGR_VALID_PAUSE_FRAME]	= "[mac] tx b-pause",
55162306a36Sopenharmony_ci	[DPMAC_CNT_EGR_BYTE]			= "[mac] tx bytes",
55262306a36Sopenharmony_ci};
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci#define DPAA2_MAC_NUM_STATS	ARRAY_SIZE(dpaa2_mac_ethtool_stats)
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ciint dpaa2_mac_get_sset_count(void)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	return DPAA2_MAC_NUM_STATS;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_civoid dpaa2_mac_get_strings(u8 *data)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	u8 *p = data;
56462306a36Sopenharmony_ci	int i;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	for (i = 0; i < DPAA2_MAC_NUM_STATS; i++) {
56762306a36Sopenharmony_ci		strscpy(p, dpaa2_mac_ethtool_stats[i], ETH_GSTRING_LEN);
56862306a36Sopenharmony_ci		p += ETH_GSTRING_LEN;
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_civoid dpaa2_mac_get_ethtool_stats(struct dpaa2_mac *mac, u64 *data)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	struct fsl_mc_device *dpmac_dev = mac->mc_dev;
57562306a36Sopenharmony_ci	int i, err;
57662306a36Sopenharmony_ci	u64 value;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	for (i = 0; i < DPAA2_MAC_NUM_STATS; i++) {
57962306a36Sopenharmony_ci		err = dpmac_get_counter(mac->mc_io, 0, dpmac_dev->mc_handle,
58062306a36Sopenharmony_ci					i, &value);
58162306a36Sopenharmony_ci		if (err) {
58262306a36Sopenharmony_ci			netdev_err_once(mac->net_dev,
58362306a36Sopenharmony_ci					"dpmac_get_counter error %d\n", err);
58462306a36Sopenharmony_ci			*(data + i) = U64_MAX;
58562306a36Sopenharmony_ci			continue;
58662306a36Sopenharmony_ci		}
58762306a36Sopenharmony_ci		*(data + i) = value;
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci}
590