From 36d251b78392407963e1bab022fb4d716176857e Mon Sep 17 00:00:00 2001 From: Suraj Jaiswal Date: Fri, 26 Mar 2021 12:59:48 +0530 Subject: [PATCH 1/4] net: stmmac: boot up KPI changes Read early ethernet dtsi flag to enable this feature. Disable autoneg and configure MAC and PHY with 100 Mbps link speed. Read MAC addr, IPv4 and IPv6 addresses from kernel cmd line parameters in order to assign these to ethernet interface. Change-Id: Ibeeebb73b4cbbd0950a1bff065297c4e58be671d Acked-by: Nagarjuna Chaganti Signed-off-by: Suraj Jaiswal --- .../admin-guide/kernel-parameters.rst | 6 + .../admin-guide/kernel-parameters.txt | 6 + .../stmicro/stmmac/dwmac-qcom-ethqos.c | 376 +++++++++++++++++- .../stmicro/stmmac/dwmac-qcom-ethqos.h | 32 ++ drivers/net/ethernet/stmicro/stmmac/stmmac.h | 6 +- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 76 ++++ .../net/ethernet/stmicro/stmmac/stmmac_mdio.c | 3 + 7 files changed, 502 insertions(+), 3 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.rst b/Documentation/admin-guide/kernel-parameters.rst index 6d421694d98e..ea5f45a164c9 100644 --- a/Documentation/admin-guide/kernel-parameters.rst +++ b/Documentation/admin-guide/kernel-parameters.rst @@ -211,3 +211,9 @@ Todo ---- Add more DRM drivers. + +eipv4= [KNL] Sets ipv4 address at boot up for early ethernet. + +eipv6= [KNL] Sets ipv6 address at boot up for early ethernet. + +ermac= [KNL] Sets mac address at boot up for early ethernet. \ No newline at end of file diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index db2300e69ffb..9f2970285e75 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -5508,3 +5508,9 @@ memory, and other data can't be written using xmon commands. off xmon is disabled. + + eipv4= [KNL] Sets ipv4 address at boot up for early ethernet. + + eipv6= [KNL] Sets ipv6 address at boot up for early ethernet. + + ermac= [KNL] Sets mac address at boot up for early ethernet. diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c index 1e5e7dd3cc1b..d70ad30f8057 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c @@ -22,6 +22,8 @@ #include #include #include +#include + #include "stmmac.h" #include "stmmac_platform.h" #include "dwmac-qcom-ethqos.h" @@ -117,6 +119,10 @@ bool phy_intr_en; struct emac_emb_smmu_cb_ctx emac_emb_smmu_ctx = {0}; struct qcom_ethqos *pethqos; +static unsigned char dev_addr[ETH_ALEN] = { + 0, 0x55, 0x7b, 0xb5, 0x7d, 0xf7}; +static struct ip_params pparams = {"", "", "", ""}; + static inline unsigned int dwmac_qcom_get_eth_type(unsigned char *buf) { return @@ -283,6 +289,168 @@ int ethqos_handle_prv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return ret; } +static int __init set_early_ethernet_ipv4(char *ipv4_addr_in) +{ + int ret = 1; + + pparams.is_valid_ipv4_addr = false; + + if (!ipv4_addr_in) + return ret; + + strlcpy(pparams.ipv4_addr_str, + ipv4_addr_in, sizeof(pparams.ipv4_addr_str)); + ETHQOSDBG("Early ethernet IPv4 addr: %s\n", pparams.ipv4_addr_str); + + ret = in4_pton(pparams.ipv4_addr_str, -1, + (u8 *)&pparams.ipv4_addr.s_addr, -1, NULL); + if (ret != 1 || pparams.ipv4_addr.s_addr == 0) { + ETHQOSERR("Invalid ipv4 address programmed: %s\n", + ipv4_addr_in); + return ret; + } + + pparams.is_valid_ipv4_addr = true; + return ret; +} + +__setup("eipv4=", set_early_ethernet_ipv4); + +static int __init set_early_ethernet_ipv6(char *ipv6_addr_in) +{ + int ret = 1; + + pparams.is_valid_ipv6_addr = false; + + if (!ipv6_addr_in) + return ret; + + strlcpy(pparams.ipv6_addr_str, + ipv6_addr_in, sizeof(pparams.ipv6_addr_str)); + ETHQOSDBG("Early ethernet IPv6 addr: %s\n", pparams.ipv6_addr_str); + + ret = in6_pton(pparams.ipv6_addr_str, -1, + (u8 *)&pparams.ipv6_addr.ifr6_addr.s6_addr32, -1, NULL); + if (ret != 1 || !pparams.ipv6_addr.ifr6_addr.s6_addr32) { + ETHQOSERR("Invalid ipv6 address programmed: %s\n", + ipv6_addr_in); + return ret; + } + + pparams.is_valid_ipv6_addr = true; + return ret; +} + +__setup("eipv6=", set_early_ethernet_ipv6); + +static int __init set_early_ethernet_mac(char *mac_addr) +{ + bool valid_mac = false; + + pparams.is_valid_mac_addr = false; + if (!mac_addr) + return 1; + + valid_mac = mac_pton(mac_addr, pparams.mac_addr); + if (!valid_mac) + goto fail; + + valid_mac = is_valid_ether_addr(pparams.mac_addr); + if (!valid_mac) + goto fail; + + pparams.is_valid_mac_addr = true; + return 0; + +fail: + ETHQOSERR("Invalid Mac address programmed: %s\n", mac_addr); + return 1; +} + +__setup("ermac=", set_early_ethernet_mac); + +static int qcom_ethqos_add_ipaddr(struct ip_params *ip_info, + struct net_device *dev) +{ + int res = 0; + struct ifreq ir; + struct sockaddr_in *sin = (void *)&ir.ifr_ifru.ifru_addr; + struct net *net = dev_net(dev); + + if (!net || !net->genl_sock || !net->genl_sock->sk_socket) { + ETHQOSINFO("Sock is null, unable to assign ipv4 address\n"); + return res; + } + /*For valid Ipv4 address*/ + memset(&ir, 0, sizeof(ir)); + memcpy(&sin->sin_addr.s_addr, &ip_info->ipv4_addr, + sizeof(sin->sin_addr.s_addr)); + + strlcpy(ir.ifr_ifrn.ifrn_name, + dev->name, sizeof(ir.ifr_ifrn.ifrn_name)); + sin->sin_family = AF_INET; + sin->sin_port = 0; + + res = inet_ioctl(net->genl_sock->sk_socket, + SIOCSIFADDR, (unsigned long)(void *)&ir); + if (res) { + ETHQOSERR("can't setup IPv4 address!: %d\r\n", res); + } else { + ETHQOSINFO("Assigned IPv4 address: %s\r\n", + ip_info->ipv4_addr_str); +#ifdef CONFIG_MSM_BOOT_TIME_MARKER +place_marker("M - Etherent Assigned IPv4 address"); +#endif + } + return res; +} + +static int qcom_ethqos_add_ipv6addr(struct ip_params *ip_info, + struct net_device *dev) +{ + int ret = -EFAULT; + struct in6_ifreq ir6; + char *prefix; + struct net *net = dev_net(dev); + /*For valid IPv6 address*/ + + if (!net || !net->genl_sock || !net->genl_sock->sk_socket) + ETHQOSERR("Sock is null, unable to assign ipv6 address\n"); + + if (!net->ipv6.devconf_dflt) { + ETHQOSERR("ipv6.devconf_dflt is null, schedule wq\n"); + schedule_delayed_work(&pethqos->ipv6_addr_assign_wq, + msecs_to_jiffies(1000)); + return ret; + } + memset(&ir6, 0, sizeof(ir6)); + memcpy(&ir6, &ip_info->ipv6_addr, sizeof(struct in6_ifreq)); + ir6.ifr6_ifindex = dev->ifindex; + + prefix = strnchr(ip_info->ipv6_addr_str, + strlen(ip_info->ipv6_addr_str), '/'); + + if (!prefix) { + ir6.ifr6_prefixlen = 0; + } else { + kstrtoul(prefix + 1, 0, (unsigned long *)&ir6.ifr6_prefixlen); + if (ir6.ifr6_prefixlen > 128) + ir6.ifr6_prefixlen = 0; + } + ret = inet6_ioctl(net->genl_sock->sk_socket, + SIOCSIFADDR, (unsigned long)(void *)&ir6); + if (ret) { + ETHQOSDBG("Can't setup IPv6 address!\r\n"); + } else { + ETHQOSDBG("Assigned IPv6 address: %s\r\n", + ip_info->ipv6_addr_str); +#ifdef CONFIG_MSM_BOOT_TIME_MARKER + place_marker("M - Ethernet Assigned IPv6 address"); +#endif + } + return ret; +} + static int rgmii_readl(struct qcom_ethqos *ethqos, unsigned int offset) { return readl(ethqos->rgmii_base + offset); @@ -1048,6 +1216,127 @@ void qcom_ethqos_request_phy_wol(struct plat_stmmacenet_data *plat) } } +static void qcom_ethqos_bringup_iface(struct work_struct *work) +{ + struct platform_device *pdev = NULL; + struct net_device *ndev = NULL; + struct qcom_ethqos *ethqos = + container_of(work, struct qcom_ethqos, early_eth); + + ETHQOSINFO("entry\n"); + if (!ethqos) + return; + pdev = ethqos->pdev; + if (!pdev) + return; + ndev = platform_get_drvdata(pdev); + if (!ndev || netif_running(ndev)) + return; + rtnl_lock(); + if (dev_change_flags(ndev, ndev->flags | IFF_UP, NULL) < 0) + ETHQOSINFO("ERROR\n"); + rtnl_unlock(); + ETHQOSINFO("exit\n"); +} + +static void ethqos_is_ipv4_NW_stack_ready(struct work_struct *work) +{ + struct delayed_work *dwork; + struct qcom_ethqos *ethqos; + struct platform_device *pdev = NULL; + struct net_device *ndev = NULL; + int ret; + + ETHQOSINFO("\n"); + dwork = container_of(work, struct delayed_work, work); + ethqos = container_of(dwork, struct qcom_ethqos, ipv4_addr_assign_wq); + + if (!ethqos) + return; + + pdev = ethqos->pdev; + + if (!pdev) + return; + + ndev = platform_get_drvdata(pdev); + + ret = qcom_ethqos_add_ipaddr(&pparams, ndev); + if (ret) + return; + + cancel_delayed_work_sync(ðqos->ipv4_addr_assign_wq); + flush_delayed_work(ðqos->ipv4_addr_assign_wq); +} + +static void ethqos_is_ipv6_NW_stack_ready(struct work_struct *work) +{ + struct delayed_work *dwork; + struct qcom_ethqos *ethqos; + struct platform_device *pdev = NULL; + struct net_device *ndev = NULL; + int ret; + + ETHQOSINFO("\n"); + dwork = container_of(work, struct delayed_work, work); + ethqos = container_of(dwork, struct qcom_ethqos, ipv6_addr_assign_wq); + + if (!ethqos) + return; + + pdev = ethqos->pdev; + + if (!pdev) + return; + + ndev = platform_get_drvdata(pdev); + + ret = qcom_ethqos_add_ipv6addr(&pparams, ndev); + if (ret) + return; + + cancel_delayed_work_sync(ðqos->ipv6_addr_assign_wq); + flush_delayed_work(ðqos->ipv6_addr_assign_wq); +} + +static int ethqos_set_early_eth_param(struct stmmac_priv *priv, + struct qcom_ethqos *ethqos) +{ + int ret = 0; + + if (priv->plat && priv->plat->mdio_bus_data) + priv->plat->mdio_bus_data->phy_mask = + priv->plat->mdio_bus_data->phy_mask | DUPLEX_FULL | SPEED_100; + + priv->plat->max_speed = SPEED_100; + priv->early_eth = ethqos->early_eth_enabled; + qcom_ethqos_add_ipaddr(&pparams, priv->dev); + + if (pparams.is_valid_ipv4_addr) { + INIT_DELAYED_WORK(ðqos->ipv4_addr_assign_wq, + ethqos_is_ipv4_NW_stack_ready); + ret = qcom_ethqos_add_ipaddr(&pparams, priv->dev); + if (ret) + schedule_delayed_work(ðqos->ipv4_addr_assign_wq, + msecs_to_jiffies(1000)); + } + + if (pparams.is_valid_ipv6_addr) { + INIT_DELAYED_WORK(ðqos->ipv6_addr_assign_wq, + ethqos_is_ipv6_NW_stack_ready); + ret = qcom_ethqos_add_ipv6addr(&pparams, priv->dev); + if (ret) + schedule_delayed_work(ðqos->ipv6_addr_assign_wq, + msecs_to_jiffies(1000)); + } + + if (pparams.is_valid_mac_addr) { + ether_addr_copy(dev_addr, pparams.mac_addr); + memcpy(priv->dev->dev_addr, dev_addr, ETH_ALEN); + } + return ret; +} + static int qcom_ethqos_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -1056,10 +1345,16 @@ static int qcom_ethqos_probe(struct platform_device *pdev) struct qcom_ethqos *ethqos = NULL; struct resource *res = NULL; int ret; + struct net_device *ndev; + struct stmmac_priv *priv; if (of_device_is_compatible(pdev->dev.of_node, "qcom,emac-smmu-embedded")) return emac_emb_smmu_cb_probe(pdev); + +#ifdef CONFIG_MSM_BOOT_TIME_MARKER + place_marker("M - Ethernet probe start"); +#endif ret = stmmac_get_platform_resources(pdev, &stmmac_res); if (ret) return ret; @@ -1081,7 +1376,6 @@ static int qcom_ethqos_probe(struct platform_device *pdev) return PTR_ERR(plat_dat); } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rgmii"); ethqos->rgmii_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(ethqos->rgmii_base)) { @@ -1102,6 +1396,22 @@ static int qcom_ethqos_probe(struct platform_device *pdev) if (ret) goto err_mem; + /*Initialize Early ethernet to false*/ + ethqos->early_eth_enabled = false; + + /*Check for valid mac, ip address to enable Early eth*/ + if (pparams.is_valid_mac_addr && + (pparams.is_valid_ipv4_addr || pparams.is_valid_ipv6_addr)) { + /* For 1000BASE-T mode, auto-negotiation is required and + * always used to establish a link. + * Configure phy and MAC in 100Mbps mode with autoneg + * disable as link up takes more time with autoneg + * enabled. + */ + ethqos->early_eth_enabled = true; + ETHQOSINFO("Early ethernet is enabled\n"); + } + ethqos->speed = SPEED_10; ethqos_update_rgmii_clk_and_bus_cfg(ethqos, SPEED_10); ethqos_set_func_clk_en(ethqos); @@ -1168,6 +1478,19 @@ static int qcom_ethqos_probe(struct platform_device *pdev) } pethqos = ethqos; + + ndev = dev_get_drvdata(ðqos->pdev->dev); + priv = netdev_priv(ndev); + + if (ethqos->early_eth_enabled) { + /* Initialize work*/ + INIT_WORK(ðqos->early_eth, + qcom_ethqos_bringup_iface); + /* Queue the work*/ + queue_work(system_wq, ðqos->early_eth); + /*Set early eth parameters*/ + ethqos_set_early_eth_param(priv, ethqos); + } return ret; err_clk: @@ -1276,7 +1599,56 @@ static struct platform_driver qcom_ethqos_driver = { .of_match_table = of_match_ptr(qcom_ethqos_match), }, }; -module_platform_driver(qcom_ethqos_driver); + +static int __init qcom_ethqos_init_module(void) +{ + int ret = 0; + + ETHQOSINFO("\n"); + + ret = platform_driver_register(&qcom_ethqos_driver); + if (ret < 0) { + ETHQOSINFO("qcom-ethqos: Driver registration failed"); + return ret; + } + + ETHQOSINFO("\n"); + + return ret; +} + +static void __exit qcom_ethqos_exit_module(void) +{ + ETHQOSINFO("\n"); + + platform_driver_unregister(&qcom_ethqos_driver); + + ETHQOSINFO("\n"); +} + +/*! + * \brief Macro to register the driver registration function. + * + * \details A module always begin with either the init_module or the function + * you specify with module_init call. This is the entry function for modules; + * it tells the kernel what functionality the module provides and sets up the + * kernel to run the module's functions when they're needed. Once it does this, + * entry function returns and the module does nothing until the kernel wants + * to do something with the code that the module provides. + */ + +module_init(qcom_ethqos_init_module) + +/*! + * \brief Macro to register the driver un-registration function. + * + * \details All modules end by calling either cleanup_module or the function + * you specify with the module_exit call. This is the exit function for modules; + * it undoes whatever entry function did. It unregisters the functionality + * that the entry function registered. + */ + +module_exit(qcom_ethqos_exit_module) MODULE_DESCRIPTION("Qualcomm ETHQOS driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h index a1e1404613f6..4c7ad48f5645 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h @@ -6,6 +6,18 @@ //#include +#include +#include + +#include +#include +#include + +#include + +#define QCOM_ETH_QOS_MAC_ADDR_LEN 6 +#define QCOM_ETH_QOS_MAC_ADDR_STR_LEN 18 + #define DRV_NAME "qcom-ethqos" #define ETHQOSDBG(fmt, args...) \ pr_debug(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args) @@ -162,6 +174,13 @@ struct qcom_ethqos { int clks_suspended; /* Structure which holds done and wait members */ struct completion clk_enable_done; + /* early ethernet parameters */ + struct work_struct early_eth; + struct delayed_work ipv4_addr_assign_wq; + struct delayed_work ipv6_addr_assign_wq; + bool early_eth_enabled; + /* Key Performance Indicators */ + bool print_kpi; }; struct pps_cfg { @@ -191,6 +210,19 @@ struct pps_info { int channel_no; }; +struct ip_params { + unsigned char mac_addr[QCOM_ETH_QOS_MAC_ADDR_LEN]; + bool is_valid_mac_addr; + char link_speed[32]; + bool is_valid_link_speed; + char ipv4_addr_str[32]; + struct in_addr ipv4_addr; + bool is_valid_ipv4_addr; + char ipv6_addr_str[48]; + struct in6_ifreq ipv6_addr; + bool is_valid_ipv6_addr; +}; + int ethqos_init_reqgulators(struct qcom_ethqos *ethqos); void ethqos_disable_regulators(struct qcom_ethqos *ethqos); int ethqos_init_gpio(struct qcom_ethqos *ethqos); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index e1ec5adb1cd3..ae50e640c682 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -22,6 +22,9 @@ #include #include #include +#ifdef CONFIG_MSM_BOOT_TIME_MARKER +#include +#endif struct stmmac_resources { void __iomem *addr; @@ -215,7 +218,8 @@ struct stmmac_priv { void __iomem *mmcaddr; void __iomem *ptpaddr; unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; - + bool boot_kpi; + bool early_eth; #ifdef CONFIG_DEBUG_FS struct dentry *dbgfs_dir; #endif diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index b35585c7c165..939c81af1b06 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -117,6 +117,8 @@ static void stmmac_exit_fs(struct net_device *dev); #define STMMAC_COAL_TIMER(x) (jiffies + usecs_to_jiffies(x)) +static void stmmac_set_speed100(struct phy_device *phydev); + /** * stmmac_verify_args - verify the driver parameters. * Description: it checks the driver parameters and set a default in case of @@ -867,14 +869,46 @@ static void stmmac_validate(struct phylink_config *config, phylink_set(mask, 1000baseT_Half); } + /* Early ethernet settings to bring up link in 100M, + * Auto neg Off with full duplex link. + */ + if (max_speed == SPEED_100 && priv->early_eth) { + priv->phydev->autoneg = AUTONEG_DISABLE; + priv->phydev->speed = SPEED_100; + priv->phydev->duplex = DUPLEX_FULL; + + phylink_set(mac_supported, 100baseT_Full); + phylink_set(mac_supported, TP); + phylink_set(mac_supported, MII); + phylink_set(mac_supported, 10baseT_Full); + phylink_clear(mac_supported, Autoneg); bitmap_and(supported, supported, mac_supported, __ETHTOOL_LINK_MODE_MASK_NBITS); bitmap_andnot(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); + + phylink_clear(mac_supported, Autoneg); bitmap_and(state->advertising, state->advertising, mac_supported, __ETHTOOL_LINK_MODE_MASK_NBITS); bitmap_andnot(state->advertising, state->advertising, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); + + pr_info(" qcom-ethqos: %s early eth setting successful\n", + __func__); + + stmmac_set_speed100(priv->phydev); + } else { + bitmap_and(supported, supported, mac_supported, + __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_andnot(supported, supported, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, + mac_supported, + __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_andnot(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + } + } static int stmmac_mac_link_state(struct phylink_config *config, @@ -970,6 +1004,12 @@ static void stmmac_mac_link_up(struct phylink_config *config, priv->eee_enabled = stmmac_eee_init(priv); stmmac_set_eee_pls(priv, priv->hw, true); } +#ifdef CONFIG_MSM_BOOT_TIME_MARKER + if (phy->link == 1 && !priv->boot_kpi) { + place_marker("M - Ethernet is Ready.Link is UP"); + priv->boot_kpi = true; + } +#endif } static const struct phylink_mac_ops stmmac_phylink_mac_ops = { @@ -1006,6 +1046,30 @@ static void stmmac_check_pcs_mode(struct stmmac_priv *priv) } } +static void stmmac_set_speed100(struct phy_device *phydev) +{ + u16 bmcr_val, ctrl1000_val, adv_val; + + /* Disable 1000M mode */ + ctrl1000_val = phy_read(phydev, MII_CTRL1000); + ctrl1000_val &= ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL); + phy_write(phydev, MII_CTRL1000, ctrl1000_val); + + /* Disable 100M mode */ + adv_val = phy_read(phydev, MII_ADVERTISE); + adv_val &= ~(ADVERTISE_100HALF); + phy_write(phydev, MII_ADVERTISE, adv_val); + + /* Disable autoneg */ + bmcr_val = phy_read(phydev, MII_BMCR); + bmcr_val &= ~(BMCR_ANENABLE); + phy_write(phydev, MII_BMCR, bmcr_val); + + bmcr_val = phy_read(phydev, MII_BMCR); + bmcr_val |= BMCR_ANRESTART; + phy_write(phydev, MII_BMCR, bmcr_val); +} + /** * stmmac_init_phy - PHY initialization * @dev: net device structure @@ -1020,6 +1084,8 @@ static int stmmac_init_phy(struct net_device *dev) struct device_node *node; int ret; + priv->boot_kpi = false; + node = priv->plat->phylink_node; if (node) @@ -1061,6 +1127,8 @@ static int stmmac_init_phy(struct net_device *dev) priv->phydev->irq = PHY_POLL; } } + pr_info(" qcom-ethqos: %s early eth setting stmmac init\n", + __func__); return ret; } @@ -1938,6 +2006,10 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue) } else { priv->dev->stats.tx_packets++; priv->xstats.tx_pkt_n++; +#ifdef CONFIG_MSM_BOOT_TIME_MARKER +if (priv->dev->stats.tx_packets == 1) + place_marker("M - Ethernet first packet transmitted"); +#endif } stmmac_get_tx_hwtstamp(priv, p, skb); } @@ -3719,6 +3791,10 @@ read_again: napi_gro_receive(&ch->rx_napi, skb); priv->dev->stats.rx_packets++; +#ifdef CONFIG_MSM_BOOT_TIME_MARKER + if (priv->dev->stats.rx_packets == 1) + place_marker("M - Ethernet first packet received"); +#endif priv->dev->stats.rx_bytes += len; count++; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index e331f514dbe0..7433d1b81328 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -269,6 +269,9 @@ int stmmac_mdio_reset(struct mii_bus *bus) #ifdef CONFIG_DWMAC_QCOM_ETHQOS active_high = false; #endif + if (priv->early_eth) + return 0; + #ifdef CONFIG_OF if (priv->device->of_node) { struct gpio_desc *reset_gpio; From 9e3455ea5ed0b5039aae8ad0d597e95718f4dc74 Mon Sep 17 00:00:00 2001 From: Suraj Jaiswal Date: Wed, 6 May 2020 19:51:18 +0530 Subject: [PATCH 2/4] net: stmmac: Fixed ethool speed issue Don't modify emac supported capabilities during early ethernet boot up. Only update advertise values otherwise speed can't be changed using ethtool. Change-Id: Id25ddd17a81fefd4d17116f0ff7237272f2607c0 Acked-by: Nagarjuna Chaganti Signed-off-by: Suraj Jaiswal --- .../stmicro/stmmac/dwmac-qcom-ethqos.c | 1 - drivers/net/ethernet/stmicro/stmmac/stmmac.h | 1 + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 41 ++++++++----------- 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c index d70ad30f8057..924fc8ac2482 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c @@ -1308,7 +1308,6 @@ static int ethqos_set_early_eth_param(struct stmmac_priv *priv, priv->plat->mdio_bus_data->phy_mask = priv->plat->mdio_bus_data->phy_mask | DUPLEX_FULL | SPEED_100; - priv->plat->max_speed = SPEED_100; priv->early_eth = ethqos->early_eth_enabled; qcom_ethqos_add_ipaddr(&pparams, priv->dev); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index ae50e640c682..f0bcd1c61d6f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -220,6 +220,7 @@ struct stmmac_priv { unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; bool boot_kpi; bool early_eth; + bool early_eth_config_set; #ifdef CONFIG_DEBUG_FS struct dentry *dbgfs_dir; #endif diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 939c81af1b06..b690ec2af6bd 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -869,25 +869,23 @@ static void stmmac_validate(struct phylink_config *config, phylink_set(mask, 1000baseT_Half); } - /* Early ethernet settings to bring up link in 100M, - * Auto neg Off with full duplex link. - */ - if (max_speed == SPEED_100 && priv->early_eth) { - priv->phydev->autoneg = AUTONEG_DISABLE; - priv->phydev->speed = SPEED_100; - priv->phydev->duplex = DUPLEX_FULL; - - phylink_set(mac_supported, 100baseT_Full); - phylink_set(mac_supported, TP); - phylink_set(mac_supported, MII); - phylink_set(mac_supported, 10baseT_Full); - phylink_clear(mac_supported, Autoneg); bitmap_and(supported, supported, mac_supported, __ETHTOOL_LINK_MODE_MASK_NBITS); bitmap_andnot(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, mac_supported, + __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_andnot(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); - phylink_clear(mac_supported, Autoneg); + /* Early ethernet settings to bring up link in 100M, + * Auto neg Off with full duplex link. + */ + if (priv->early_eth && !priv->early_eth_config_set) { + priv->phydev->autoneg = AUTONEG_DISABLE; + priv->phydev->speed = SPEED_100; + priv->phydev->duplex = DUPLEX_FULL; + phylink_clear(mac_supported, 1000baseT_Full); bitmap_and(state->advertising, state->advertising, mac_supported, __ETHTOOL_LINK_MODE_MASK_NBITS); bitmap_andnot(state->advertising, state->advertising, mask, @@ -897,16 +895,11 @@ static void stmmac_validate(struct phylink_config *config, __func__); stmmac_set_speed100(priv->phydev); - } else { - bitmap_and(supported, supported, mac_supported, - __ETHTOOL_LINK_MODE_MASK_NBITS); - bitmap_andnot(supported, supported, mask, - __ETHTOOL_LINK_MODE_MASK_NBITS); - bitmap_and(state->advertising, state->advertising, - mac_supported, - __ETHTOOL_LINK_MODE_MASK_NBITS); - bitmap_andnot(state->advertising, state->advertising, mask, - __ETHTOOL_LINK_MODE_MASK_NBITS); + /* Validate method will also be called + * when we change speed using ethtool. + * Add check to avoid multiple calls + */ + priv->early_eth_config_set = 1; } } From ef3a98b15702643df6ce1e7f65253486d361b0cd Mon Sep 17 00:00:00 2001 From: Suraj Jaiswal Date: Tue, 15 Dec 2020 18:09:00 +0530 Subject: [PATCH 3/4] net: stmmac: Add debugfs entries Add RGMII dump and phy register dump in debugfs. Change-Id: Ib6e2f015d4fe682d25f1f61c3d2d40a69d64e794 Signed-off-by: Suraj Jaiswal --- .../stmicro/stmmac/dwmac-qcom-ethqos.c | 177 +++++++++++++++++- .../stmicro/stmmac/dwmac-qcom-ethqos.h | 18 +- 2 files changed, 193 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c index 924fc8ac2482..80fa7ba9f764 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -115,6 +114,7 @@ #define MICREL_LINK_UP_INTR_STATUS BIT(0) bool phy_intr_en; +void *ipc_emac_log_ctxt; struct emac_emb_smmu_cb_ctx emac_emb_smmu_ctx = {0}; struct qcom_ethqos *pethqos; @@ -1336,6 +1336,173 @@ static int ethqos_set_early_eth_param(struct stmmac_priv *priv, return ret; } +static ssize_t read_phy_reg_dump(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct qcom_ethqos *ethqos = file->private_data; + unsigned int len = 0, buf_len = 2000; + char *buf; + ssize_t ret_cnt; + int phydata = 0; + int i = 0; + + struct platform_device *pdev = ethqos->pdev; + struct net_device *dev = platform_get_drvdata(pdev); + struct stmmac_priv *priv = netdev_priv(dev); + + if (!ethqos || !dev->phydev) { + ETHQOSERR("NULL Pointer\n"); + return -EINVAL; + } + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len += scnprintf(buf + len, buf_len - len, + "\n************* PHY Reg dump *************\n"); + + for (i = 0; i < 32; i++) { + phydata = ethqos_mdio_read(priv, priv->plat->phy_addr, i); + len += scnprintf(buf + len, buf_len - len, + "MII Register (%#x) = %#x\n", + i, phydata); + } + + if (len > buf_len) { + ETHQOSERR("(len > buf_len) buffer not sufficient\n"); + len = buf_len; + } + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + return ret_cnt; +} + +static ssize_t read_rgmii_reg_dump(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct qcom_ethqos *ethqos = file->private_data; + unsigned int len = 0, buf_len = 2000; + char *buf; + ssize_t ret_cnt; + int rgmii_data = 0; + struct platform_device *pdev = ethqos->pdev; + + struct net_device *dev = platform_get_drvdata(pdev); + + if (!ethqos || !dev->phydev) { + ETHQOSERR("NULL Pointer\n"); + return -EINVAL; + } + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len += scnprintf(buf + len, buf_len - len, + "\n************* RGMII Reg dump *************\n"); + rgmii_data = rgmii_readl(ethqos, RGMII_IO_MACRO_CONFIG); + len += scnprintf(buf + len, buf_len - len, + "RGMII_IO_MACRO_CONFIG Register = %#x\n", + rgmii_data); + rgmii_data = rgmii_readl(ethqos, SDCC_HC_REG_DLL_CONFIG); + len += scnprintf(buf + len, buf_len - len, + "SDCC_HC_REG_DLL_CONFIG Register = %#x\n", + rgmii_data); + rgmii_data = rgmii_readl(ethqos, SDCC_HC_REG_DDR_CONFIG); + len += scnprintf(buf + len, buf_len - len, + "SDCC_HC_REG_DDR_CONFIG Register = %#x\n", + rgmii_data); + rgmii_data = rgmii_readl(ethqos, SDCC_HC_REG_DLL_CONFIG2); + len += scnprintf(buf + len, buf_len - len, + "SDCC_HC_REG_DLL_CONFIG2 Register = %#x\n", + rgmii_data); + rgmii_data = rgmii_readl(ethqos, SDC4_STATUS); + len += scnprintf(buf + len, buf_len - len, + "SDC4_STATUS Register = %#x\n", + rgmii_data); + rgmii_data = rgmii_readl(ethqos, SDCC_USR_CTL); + len += scnprintf(buf + len, buf_len - len, + "SDCC_USR_CTL Register = %#x\n", + rgmii_data); + rgmii_data = rgmii_readl(ethqos, RGMII_IO_MACRO_CONFIG2); + len += scnprintf(buf + len, buf_len - len, + "RGMII_IO_MACRO_CONFIG2 Register = %#x\n", + rgmii_data); + rgmii_data = rgmii_readl(ethqos, RGMII_IO_MACRO_DEBUG1); + len += scnprintf(buf + len, buf_len - len, + "RGMII_IO_MACRO_DEBUG1 Register = %#x\n", + rgmii_data); + rgmii_data = rgmii_readl(ethqos, EMAC_SYSTEM_LOW_POWER_DEBUG); + len += scnprintf(buf + len, buf_len - len, + "EMAC_SYSTEM_LOW_POWER_DEBUG Register = %#x\n", + rgmii_data); + + if (len > buf_len) { + ETHQOSERR("(len > buf_len) buffer not sufficient\n"); + len = buf_len; + } + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + return ret_cnt; +} + +static const struct file_operations fops_phy_reg_dump = { + .read = read_phy_reg_dump, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static const struct file_operations fops_rgmii_reg_dump = { + .read = read_rgmii_reg_dump, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ethqos_create_debugfs(struct qcom_ethqos *ethqos) +{ + static struct dentry *phy_reg_dump; + static struct dentry *rgmii_reg_dump; + + if (!ethqos) { + ETHQOSERR("Null Param %s\n", __func__); + return -ENOMEM; + } + + ethqos->debugfs_dir = debugfs_create_dir("eth", NULL); + + if (!ethqos->debugfs_dir || IS_ERR(ethqos->debugfs_dir)) { + ETHQOSERR("Can't create debugfs dir\n"); + return -ENOMEM; + } + + phy_reg_dump = debugfs_create_file("phy_reg_dump", 0400, + ethqos->debugfs_dir, ethqos, + &fops_phy_reg_dump); + if (!phy_reg_dump || IS_ERR(phy_reg_dump)) { + ETHQOSERR("Can't create phy_dump %p\n", phy_reg_dump); + goto fail; + } + + rgmii_reg_dump = debugfs_create_file("rgmii_reg_dump", 0400, + ethqos->debugfs_dir, ethqos, + &fops_rgmii_reg_dump); + if (!rgmii_reg_dump || IS_ERR(rgmii_reg_dump)) { + ETHQOSERR("Can't create rgmii_dump %p\n", rgmii_reg_dump); + goto fail; + } + return 0; + +fail: + debugfs_remove_recursive(ethqos->debugfs_dir); + return -ENOMEM; +} + static int qcom_ethqos_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -1354,6 +1521,13 @@ static int qcom_ethqos_probe(struct platform_device *pdev) #ifdef CONFIG_MSM_BOOT_TIME_MARKER place_marker("M - Ethernet probe start"); #endif + + ipc_emac_log_ctxt = ipc_log_context_create(IPCLOG_STATE_PAGES, + "emac", 0); + if (!ipc_emac_log_ctxt) + ETHQOSERR("Error creating logging context for emac\n"); + else + ETHQOSDBG("IPC logging has been enabled for emac\n"); ret = stmmac_get_platform_resources(pdev, &stmmac_res); if (ret) return ret; @@ -1477,6 +1651,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev) } pethqos = ethqos; + ethqos_create_debugfs(ethqos); ndev = dev_get_drvdata(ðqos->pdev->dev); priv = netdev_priv(ndev); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h index 4c7ad48f5645..b3ef4ff72b65 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h @@ -5,6 +5,13 @@ #define _DWMAC_QCOM_ETHQOS_H //#include +#include + +extern void *ipc_emac_log_ctxt; + +#define IPCLOG_STATE_PAGES 50 +#define __FILENAME__ (strrchr(__FILE__, '/') ? \ + strrchr(__FILE__, '/') + 1 : __FILE__) #include #include @@ -22,7 +29,14 @@ #define ETHQOSDBG(fmt, args...) \ pr_debug(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args) #define ETHQOSERR(fmt, args...) \ - pr_err(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args) +do {\ + pr_err(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args);\ + if (ipc_emac_log_ctxt) { \ + ipc_log_string(ipc_emac_log_ctxt, \ + "%s: %s[%u]:[emac] ERROR:" fmt, __FILENAME__,\ + __func__, __LINE__, ## args); \ + } \ +} while (0) #define ETHQOSINFO(fmt, args...) \ pr_info(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args) #define RGMII_IO_MACRO_CONFIG 0x0 @@ -181,6 +195,8 @@ struct qcom_ethqos { bool early_eth_enabled; /* Key Performance Indicators */ bool print_kpi; + + struct dentry *debugfs_dir; }; struct pps_cfg { From 3eee6b90d9d53f4f15c80f8464e902c4b30f42e8 Mon Sep 17 00:00:00 2001 From: Suraj Jaiswal Date: Thu, 17 Dec 2020 15:48:59 +0530 Subject: [PATCH 4/4] net: stmmac: Add per queue ethtool stats Add packet count stats for rx and tx for each queue in ethtool. Change-Id: I5f2c2f68fb87c9cb8436f2c589b0884404e47233 Signed-off-by: Suraj Jaiswal --- drivers/net/ethernet/stmicro/stmmac/common.h | 2 ++ drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 9 +++++++++ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 ++ 3 files changed, 13 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 2d4729f390ff..9b5b479e7452 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -91,6 +91,8 @@ struct stmmac_extra_stats { unsigned long threshold; unsigned long tx_pkt_n; unsigned long rx_pkt_n; + unsigned long q_tx_pkt_n[MTL_MAX_TX_QUEUES]; + unsigned long q_rx_pkt_n[MTL_MAX_RX_QUEUES]; unsigned long normal_irq_n; unsigned long rx_normal_irq_n; unsigned long napi_poll; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 1a768837ca72..dd6b76fde1a2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -82,7 +82,16 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = { STMMAC_STAT(rx_early_irq), STMMAC_STAT(threshold), STMMAC_STAT(tx_pkt_n), + STMMAC_STAT(q_tx_pkt_n[0]), + STMMAC_STAT(q_tx_pkt_n[1]), + STMMAC_STAT(q_tx_pkt_n[2]), + STMMAC_STAT(q_tx_pkt_n[3]), + STMMAC_STAT(q_tx_pkt_n[4]), STMMAC_STAT(rx_pkt_n), + STMMAC_STAT(q_rx_pkt_n[0]), + STMMAC_STAT(q_rx_pkt_n[1]), + STMMAC_STAT(q_rx_pkt_n[2]), + STMMAC_STAT(q_rx_pkt_n[3]), STMMAC_STAT(normal_irq_n), STMMAC_STAT(rx_normal_irq_n), STMMAC_STAT(napi_poll), diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index b690ec2af6bd..097a6d84dff2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1999,6 +1999,7 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue) } else { priv->dev->stats.tx_packets++; priv->xstats.tx_pkt_n++; + priv->xstats.q_tx_pkt_n[queue]++; #ifdef CONFIG_MSM_BOOT_TIME_MARKER if (priv->dev->stats.tx_packets == 1) place_marker("M - Ethernet first packet transmitted"); @@ -3802,6 +3803,7 @@ read_again: stmmac_rx_refill(priv, queue); priv->xstats.rx_pkt_n += count; + priv->xstats.q_rx_pkt_n[queue] += count; return count; }