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