171947923SIoana Ciornei // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
271947923SIoana Ciornei /* Copyright 2019 NXP */
371947923SIoana Ciornei 
471947923SIoana Ciornei #include "dpaa2-eth.h"
571947923SIoana Ciornei #include "dpaa2-mac.h"
671947923SIoana Ciornei 
771947923SIoana Ciornei #define phylink_to_dpaa2_mac(config) \
871947923SIoana Ciornei 	container_of((config), struct dpaa2_mac, phylink_config)
971947923SIoana Ciornei 
1071947923SIoana Ciornei static phy_interface_t phy_mode(enum dpmac_eth_if eth_if)
1171947923SIoana Ciornei {
1271947923SIoana Ciornei 	switch (eth_if) {
1371947923SIoana Ciornei 	case DPMAC_ETH_IF_RGMII:
1471947923SIoana Ciornei 		return PHY_INTERFACE_MODE_RGMII;
1571947923SIoana Ciornei 	default:
1671947923SIoana Ciornei 		return -EINVAL;
1771947923SIoana Ciornei 	}
1871947923SIoana Ciornei }
1971947923SIoana Ciornei 
2071947923SIoana Ciornei /* Caller must call of_node_put on the returned value */
2171947923SIoana Ciornei static struct device_node *dpaa2_mac_get_node(u16 dpmac_id)
2271947923SIoana Ciornei {
2371947923SIoana Ciornei 	struct device_node *dpmacs, *dpmac = NULL;
2471947923SIoana Ciornei 	u32 id;
2571947923SIoana Ciornei 	int err;
2671947923SIoana Ciornei 
2771947923SIoana Ciornei 	dpmacs = of_find_node_by_name(NULL, "dpmacs");
2871947923SIoana Ciornei 	if (!dpmacs)
2971947923SIoana Ciornei 		return NULL;
3071947923SIoana Ciornei 
3171947923SIoana Ciornei 	while ((dpmac = of_get_next_child(dpmacs, dpmac)) != NULL) {
3271947923SIoana Ciornei 		err = of_property_read_u32(dpmac, "reg", &id);
3371947923SIoana Ciornei 		if (err)
3471947923SIoana Ciornei 			continue;
3571947923SIoana Ciornei 		if (id == dpmac_id)
3671947923SIoana Ciornei 			break;
3771947923SIoana Ciornei 	}
3871947923SIoana Ciornei 
3971947923SIoana Ciornei 	of_node_put(dpmacs);
4071947923SIoana Ciornei 
4171947923SIoana Ciornei 	return dpmac;
4271947923SIoana Ciornei }
4371947923SIoana Ciornei 
4471947923SIoana Ciornei static int dpaa2_mac_get_if_mode(struct device_node *node,
4571947923SIoana Ciornei 				 struct dpmac_attr attr)
4671947923SIoana Ciornei {
470c65b2b9SAndrew Lunn 	phy_interface_t if_mode;
480c65b2b9SAndrew Lunn 	int err;
4971947923SIoana Ciornei 
500c65b2b9SAndrew Lunn 	err = of_get_phy_mode(node, &if_mode);
510c65b2b9SAndrew Lunn 	if (!err)
5271947923SIoana Ciornei 		return if_mode;
5371947923SIoana Ciornei 
5471947923SIoana Ciornei 	if_mode = phy_mode(attr.eth_if);
5571947923SIoana Ciornei 	if (if_mode >= 0)
5671947923SIoana Ciornei 		return if_mode;
5771947923SIoana Ciornei 
5871947923SIoana Ciornei 	return -ENODEV;
5971947923SIoana Ciornei }
6071947923SIoana Ciornei 
6171947923SIoana Ciornei static bool dpaa2_mac_phy_mode_mismatch(struct dpaa2_mac *mac,
6271947923SIoana Ciornei 					phy_interface_t interface)
6371947923SIoana Ciornei {
6471947923SIoana Ciornei 	switch (interface) {
6571947923SIoana Ciornei 	case PHY_INTERFACE_MODE_RGMII:
6671947923SIoana Ciornei 	case PHY_INTERFACE_MODE_RGMII_ID:
6771947923SIoana Ciornei 	case PHY_INTERFACE_MODE_RGMII_RXID:
6871947923SIoana Ciornei 	case PHY_INTERFACE_MODE_RGMII_TXID:
6971947923SIoana Ciornei 		return (interface != mac->if_mode);
7071947923SIoana Ciornei 	default:
7171947923SIoana Ciornei 		return true;
7271947923SIoana Ciornei 	}
7371947923SIoana Ciornei }
7471947923SIoana Ciornei 
7571947923SIoana Ciornei static void dpaa2_mac_validate(struct phylink_config *config,
7671947923SIoana Ciornei 			       unsigned long *supported,
7771947923SIoana Ciornei 			       struct phylink_link_state *state)
7871947923SIoana Ciornei {
7971947923SIoana Ciornei 	struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config);
8071947923SIoana Ciornei 	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
8171947923SIoana Ciornei 
8271947923SIoana Ciornei 	if (state->interface != PHY_INTERFACE_MODE_NA &&
8371947923SIoana Ciornei 	    dpaa2_mac_phy_mode_mismatch(mac, state->interface)) {
8471947923SIoana Ciornei 		goto empty_set;
8571947923SIoana Ciornei 	}
8671947923SIoana Ciornei 
8771947923SIoana Ciornei 	phylink_set_port_modes(mask);
8871947923SIoana Ciornei 	phylink_set(mask, Autoneg);
8971947923SIoana Ciornei 	phylink_set(mask, Pause);
9071947923SIoana Ciornei 	phylink_set(mask, Asym_Pause);
9171947923SIoana Ciornei 
9271947923SIoana Ciornei 	switch (state->interface) {
9371947923SIoana Ciornei 	case PHY_INTERFACE_MODE_RGMII:
9471947923SIoana Ciornei 	case PHY_INTERFACE_MODE_RGMII_ID:
9571947923SIoana Ciornei 	case PHY_INTERFACE_MODE_RGMII_RXID:
9671947923SIoana Ciornei 	case PHY_INTERFACE_MODE_RGMII_TXID:
9771947923SIoana Ciornei 		phylink_set(mask, 10baseT_Full);
9871947923SIoana Ciornei 		phylink_set(mask, 100baseT_Full);
9971947923SIoana Ciornei 		phylink_set(mask, 1000baseT_Full);
10071947923SIoana Ciornei 		break;
10171947923SIoana Ciornei 	default:
10271947923SIoana Ciornei 		goto empty_set;
10371947923SIoana Ciornei 	}
10471947923SIoana Ciornei 
10571947923SIoana Ciornei 	linkmode_and(supported, supported, mask);
10671947923SIoana Ciornei 	linkmode_and(state->advertising, state->advertising, mask);
10771947923SIoana Ciornei 
10871947923SIoana Ciornei 	return;
10971947923SIoana Ciornei 
11071947923SIoana Ciornei empty_set:
11171947923SIoana Ciornei 	linkmode_zero(supported);
11271947923SIoana Ciornei }
11371947923SIoana Ciornei 
11471947923SIoana Ciornei static void dpaa2_mac_config(struct phylink_config *config, unsigned int mode,
11571947923SIoana Ciornei 			     const struct phylink_link_state *state)
11671947923SIoana Ciornei {
11771947923SIoana Ciornei 	struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config);
11871947923SIoana Ciornei 	struct dpmac_link_state *dpmac_state = &mac->state;
11971947923SIoana Ciornei 	int err;
12071947923SIoana Ciornei 
12171947923SIoana Ciornei 	if (state->speed != SPEED_UNKNOWN)
12271947923SIoana Ciornei 		dpmac_state->rate = state->speed;
12371947923SIoana Ciornei 
12471947923SIoana Ciornei 	if (state->duplex != DUPLEX_UNKNOWN) {
12571947923SIoana Ciornei 		if (!state->duplex)
12671947923SIoana Ciornei 			dpmac_state->options |= DPMAC_LINK_OPT_HALF_DUPLEX;
12771947923SIoana Ciornei 		else
12871947923SIoana Ciornei 			dpmac_state->options &= ~DPMAC_LINK_OPT_HALF_DUPLEX;
12971947923SIoana Ciornei 	}
13071947923SIoana Ciornei 
13171947923SIoana Ciornei 	if (state->an_enabled)
13271947923SIoana Ciornei 		dpmac_state->options |= DPMAC_LINK_OPT_AUTONEG;
13371947923SIoana Ciornei 	else
13471947923SIoana Ciornei 		dpmac_state->options &= ~DPMAC_LINK_OPT_AUTONEG;
13571947923SIoana Ciornei 
13671947923SIoana Ciornei 	if (state->pause & MLO_PAUSE_RX)
13771947923SIoana Ciornei 		dpmac_state->options |= DPMAC_LINK_OPT_PAUSE;
13871947923SIoana Ciornei 	else
13971947923SIoana Ciornei 		dpmac_state->options &= ~DPMAC_LINK_OPT_PAUSE;
14071947923SIoana Ciornei 
14171947923SIoana Ciornei 	if (!!(state->pause & MLO_PAUSE_RX) ^ !!(state->pause & MLO_PAUSE_TX))
14271947923SIoana Ciornei 		dpmac_state->options |= DPMAC_LINK_OPT_ASYM_PAUSE;
14371947923SIoana Ciornei 	else
14471947923SIoana Ciornei 		dpmac_state->options &= ~DPMAC_LINK_OPT_ASYM_PAUSE;
14571947923SIoana Ciornei 
14671947923SIoana Ciornei 	err = dpmac_set_link_state(mac->mc_io, 0,
14771947923SIoana Ciornei 				   mac->mc_dev->mc_handle, dpmac_state);
14871947923SIoana Ciornei 	if (err)
14971947923SIoana Ciornei 		netdev_err(mac->net_dev, "dpmac_set_link_state() = %d\n", err);
15071947923SIoana Ciornei }
15171947923SIoana Ciornei 
15271947923SIoana Ciornei static void dpaa2_mac_link_up(struct phylink_config *config, unsigned int mode,
15371947923SIoana Ciornei 			      phy_interface_t interface, struct phy_device *phy)
15471947923SIoana Ciornei {
15571947923SIoana Ciornei 	struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config);
15671947923SIoana Ciornei 	struct dpmac_link_state *dpmac_state = &mac->state;
15771947923SIoana Ciornei 	int err;
15871947923SIoana Ciornei 
15971947923SIoana Ciornei 	dpmac_state->up = 1;
16071947923SIoana Ciornei 	err = dpmac_set_link_state(mac->mc_io, 0,
16171947923SIoana Ciornei 				   mac->mc_dev->mc_handle, dpmac_state);
16271947923SIoana Ciornei 	if (err)
16371947923SIoana Ciornei 		netdev_err(mac->net_dev, "dpmac_set_link_state() = %d\n", err);
16471947923SIoana Ciornei }
16571947923SIoana Ciornei 
16671947923SIoana Ciornei static void dpaa2_mac_link_down(struct phylink_config *config,
16771947923SIoana Ciornei 				unsigned int mode,
16871947923SIoana Ciornei 				phy_interface_t interface)
16971947923SIoana Ciornei {
17071947923SIoana Ciornei 	struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config);
17171947923SIoana Ciornei 	struct dpmac_link_state *dpmac_state = &mac->state;
17271947923SIoana Ciornei 	int err;
17371947923SIoana Ciornei 
17471947923SIoana Ciornei 	dpmac_state->up = 0;
17571947923SIoana Ciornei 	err = dpmac_set_link_state(mac->mc_io, 0,
17671947923SIoana Ciornei 				   mac->mc_dev->mc_handle, dpmac_state);
17771947923SIoana Ciornei 	if (err)
17871947923SIoana Ciornei 		netdev_err(mac->net_dev, "dpmac_set_link_state() = %d\n", err);
17971947923SIoana Ciornei }
18071947923SIoana Ciornei 
18171947923SIoana Ciornei static const struct phylink_mac_ops dpaa2_mac_phylink_ops = {
18271947923SIoana Ciornei 	.validate = dpaa2_mac_validate,
18371947923SIoana Ciornei 	.mac_config = dpaa2_mac_config,
18471947923SIoana Ciornei 	.mac_link_up = dpaa2_mac_link_up,
18571947923SIoana Ciornei 	.mac_link_down = dpaa2_mac_link_down,
18671947923SIoana Ciornei };
18771947923SIoana Ciornei 
18871947923SIoana Ciornei bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev,
18971947923SIoana Ciornei 			     struct fsl_mc_io *mc_io)
19071947923SIoana Ciornei {
19171947923SIoana Ciornei 	struct dpmac_attr attr;
19271947923SIoana Ciornei 	bool fixed = false;
19371947923SIoana Ciornei 	u16 mc_handle = 0;
19471947923SIoana Ciornei 	int err;
19571947923SIoana Ciornei 
19671947923SIoana Ciornei 	err = dpmac_open(mc_io, 0, dpmac_dev->obj_desc.id,
19771947923SIoana Ciornei 			 &mc_handle);
19871947923SIoana Ciornei 	if (err || !mc_handle)
19971947923SIoana Ciornei 		return false;
20071947923SIoana Ciornei 
20171947923SIoana Ciornei 	err = dpmac_get_attributes(mc_io, 0, mc_handle, &attr);
20271947923SIoana Ciornei 	if (err)
20371947923SIoana Ciornei 		goto out;
20471947923SIoana Ciornei 
20571947923SIoana Ciornei 	if (attr.link_type == DPMAC_LINK_TYPE_FIXED)
20671947923SIoana Ciornei 		fixed = true;
20771947923SIoana Ciornei 
20871947923SIoana Ciornei out:
20971947923SIoana Ciornei 	dpmac_close(mc_io, 0, mc_handle);
21071947923SIoana Ciornei 
21171947923SIoana Ciornei 	return fixed;
21271947923SIoana Ciornei }
21371947923SIoana Ciornei 
21471947923SIoana Ciornei int dpaa2_mac_connect(struct dpaa2_mac *mac)
21571947923SIoana Ciornei {
21671947923SIoana Ciornei 	struct fsl_mc_device *dpmac_dev = mac->mc_dev;
21771947923SIoana Ciornei 	struct net_device *net_dev = mac->net_dev;
21871947923SIoana Ciornei 	struct device_node *dpmac_node;
21971947923SIoana Ciornei 	struct phylink *phylink;
22071947923SIoana Ciornei 	struct dpmac_attr attr;
22171947923SIoana Ciornei 	int err;
22271947923SIoana Ciornei 
22371947923SIoana Ciornei 	err = dpmac_open(mac->mc_io, 0, dpmac_dev->obj_desc.id,
22471947923SIoana Ciornei 			 &dpmac_dev->mc_handle);
22571947923SIoana Ciornei 	if (err || !dpmac_dev->mc_handle) {
22671947923SIoana Ciornei 		netdev_err(net_dev, "dpmac_open() = %d\n", err);
22771947923SIoana Ciornei 		return -ENODEV;
22871947923SIoana Ciornei 	}
22971947923SIoana Ciornei 
23071947923SIoana Ciornei 	err = dpmac_get_attributes(mac->mc_io, 0, dpmac_dev->mc_handle, &attr);
23171947923SIoana Ciornei 	if (err) {
23271947923SIoana Ciornei 		netdev_err(net_dev, "dpmac_get_attributes() = %d\n", err);
23371947923SIoana Ciornei 		goto err_close_dpmac;
23471947923SIoana Ciornei 	}
23571947923SIoana Ciornei 
23671947923SIoana Ciornei 	dpmac_node = dpaa2_mac_get_node(attr.id);
23771947923SIoana Ciornei 	if (!dpmac_node) {
23871947923SIoana Ciornei 		netdev_err(net_dev, "No dpmac@%d node found.\n", attr.id);
23971947923SIoana Ciornei 		err = -ENODEV;
24071947923SIoana Ciornei 		goto err_close_dpmac;
24171947923SIoana Ciornei 	}
24271947923SIoana Ciornei 
24371947923SIoana Ciornei 	err = dpaa2_mac_get_if_mode(dpmac_node, attr);
24471947923SIoana Ciornei 	if (err < 0) {
24571947923SIoana Ciornei 		err = -EINVAL;
24671947923SIoana Ciornei 		goto err_put_node;
24771947923SIoana Ciornei 	}
24871947923SIoana Ciornei 	mac->if_mode = err;
24971947923SIoana Ciornei 
25071947923SIoana Ciornei 	/* The MAC does not have the capability to add RGMII delays so
25171947923SIoana Ciornei 	 * error out if the interface mode requests them and there is no PHY
25271947923SIoana Ciornei 	 * to act upon them
25371947923SIoana Ciornei 	 */
25471947923SIoana Ciornei 	if (of_phy_is_fixed_link(dpmac_node) &&
25571947923SIoana Ciornei 	    (mac->if_mode == PHY_INTERFACE_MODE_RGMII_ID ||
25671947923SIoana Ciornei 	     mac->if_mode == PHY_INTERFACE_MODE_RGMII_RXID ||
25771947923SIoana Ciornei 	     mac->if_mode == PHY_INTERFACE_MODE_RGMII_TXID)) {
25871947923SIoana Ciornei 		netdev_err(net_dev, "RGMII delay not supported\n");
25971947923SIoana Ciornei 		err = -EINVAL;
26071947923SIoana Ciornei 		goto err_put_node;
26171947923SIoana Ciornei 	}
26271947923SIoana Ciornei 
26371947923SIoana Ciornei 	mac->phylink_config.dev = &net_dev->dev;
26471947923SIoana Ciornei 	mac->phylink_config.type = PHYLINK_NETDEV;
26571947923SIoana Ciornei 
26671947923SIoana Ciornei 	phylink = phylink_create(&mac->phylink_config,
26771947923SIoana Ciornei 				 of_fwnode_handle(dpmac_node), mac->if_mode,
26871947923SIoana Ciornei 				 &dpaa2_mac_phylink_ops);
26971947923SIoana Ciornei 	if (IS_ERR(phylink)) {
27071947923SIoana Ciornei 		err = PTR_ERR(phylink);
27171947923SIoana Ciornei 		goto err_put_node;
27271947923SIoana Ciornei 	}
27371947923SIoana Ciornei 	mac->phylink = phylink;
27471947923SIoana Ciornei 
27571947923SIoana Ciornei 	err = phylink_of_phy_connect(mac->phylink, dpmac_node, 0);
27671947923SIoana Ciornei 	if (err) {
27771947923SIoana Ciornei 		netdev_err(net_dev, "phylink_of_phy_connect() = %d\n", err);
27871947923SIoana Ciornei 		goto err_phylink_destroy;
27971947923SIoana Ciornei 	}
28071947923SIoana Ciornei 
28171947923SIoana Ciornei 	of_node_put(dpmac_node);
28271947923SIoana Ciornei 
28371947923SIoana Ciornei 	return 0;
28471947923SIoana Ciornei 
28571947923SIoana Ciornei err_phylink_destroy:
28671947923SIoana Ciornei 	phylink_destroy(mac->phylink);
28771947923SIoana Ciornei err_put_node:
28871947923SIoana Ciornei 	of_node_put(dpmac_node);
28971947923SIoana Ciornei err_close_dpmac:
29071947923SIoana Ciornei 	dpmac_close(mac->mc_io, 0, dpmac_dev->mc_handle);
29171947923SIoana Ciornei 	return err;
29271947923SIoana Ciornei }
29371947923SIoana Ciornei 
29471947923SIoana Ciornei void dpaa2_mac_disconnect(struct dpaa2_mac *mac)
29571947923SIoana Ciornei {
29671947923SIoana Ciornei 	if (!mac->phylink)
29771947923SIoana Ciornei 		return;
29871947923SIoana Ciornei 
29971947923SIoana Ciornei 	phylink_disconnect_phy(mac->phylink);
30071947923SIoana Ciornei 	phylink_destroy(mac->phylink);
30171947923SIoana Ciornei 	dpmac_close(mac->mc_io, 0, mac->mc_dev->mc_handle);
30271947923SIoana Ciornei }
303