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