From 8f76fef083be1c7533010f82fe9b5a14654ce7bd Mon Sep 17 00:00:00 2001 From: Sunil Paidimarri Date: Tue, 4 Feb 2020 09:40:48 -0800 Subject: [PATCH] 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: Abhishek Chauhan Signed-off-by: Sunil Paidimarri --- .../admin-guide/kernel-parameters.rst | 6 + .../admin-guide/kernel-parameters.txt | 6 + .../stmicro/stmmac/dwmac-qcom-ethqos.c | 388 +++++++++++++++++- .../stmicro/stmmac/dwmac-qcom-ethqos.h | 33 ++ .../ethernet/stmicro/stmmac/dwmac-qcom-ipa.c | 13 +- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 7 +- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 77 +++- .../net/ethernet/stmicro/stmmac/stmmac_mdio.c | 3 + 8 files changed, 522 insertions(+), 11 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.rst b/Documentation/admin-guide/kernel-parameters.rst index b2598cc9834c..db97ab913bf0 100644 --- a/Documentation/admin-guide/kernel-parameters.rst +++ b/Documentation/admin-guide/kernel-parameters.rst @@ -209,3 +209,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 b0fbe2dd12b4..5e81acd01edb 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -5010,3 +5010,9 @@ xirc2ps_cs= [NET,PCMCIA] Format: ,,,,,[,[,[,]]] + + 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/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c index fe52c0adca16..47bb124f18df 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" @@ -33,10 +35,13 @@ bool phy_intr_en; struct qcom_ethqos *pethqos; struct emac_emb_smmu_cb_ctx emac_emb_smmu_ctx = {0}; +static unsigned char dev_addr[ETH_ALEN] = { + 0, 0x55, 0x7b, 0xb5, 0x7d, 0xf7}; void *ipc_emac_log_ctxt; static struct qmp_pkt pkt; static char qmp_buf[MAX_QMP_MSG_SIZE + 1] = {0}; +static struct ip_params pparams = {"", "", "", ""}; static inline unsigned int dwmac_qcom_get_eth_type(unsigned char *buf) { @@ -208,6 +213,169 @@ 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) +{ + int ret = 1; + bool valid_mac = false; + + pparams.is_valid_mac_addr = false; + if (!mac_addr) + return ret; + + 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 ret; + +fail: + ETHQOSERR("Invalid Mac address programmed: %s\n", mac_addr); + return ret; +} + +__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); @@ -1270,6 +1438,37 @@ static void qcom_ethqos_phy_resume_clks(struct qcom_ethqos *ethqos) ETHQOSINFO("Exit\n"); } +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) < 0) + ETHQOSINFO("ERROR\n"); + + rtnl_unlock(); + + ETHQOSINFO("exit\n"); +} + void qcom_ethqos_request_phy_wol(struct plat_stmmacenet_data *plat) { struct qcom_ethqos *ethqos = plat->bsp_priv; @@ -1317,6 +1516,104 @@ void qcom_ethqos_request_phy_wol(struct plat_stmmacenet_data *plat) } } +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; @@ -1328,12 +1625,17 @@ static int qcom_ethqos_probe(struct platform_device *pdev) struct stmmac_priv *priv; int ret; +#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"); + ETHQOSINFO("Error creating logging context for emac\n"); else - ETHQOSDBG("IPC logging has been enabled for emac\n"); + ETHQOSINFO("IPC logging has been enabled for emac\n"); + if (of_device_is_compatible(pdev->dev.of_node, "qcom,emac-smmu-embedded")) return emac_emb_smmu_cb_probe(pdev); @@ -1358,7 +1660,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)) { @@ -1379,6 +1680,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 = 1; + 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); @@ -1460,11 +1777,22 @@ static int qcom_ethqos_probe(struct platform_device *pdev) 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); + } #ifdef CONFIG_ETH_IPA_OFFLOAD ethqos->ipa_enabled = true; priv->rx_queue[IPA_DMA_RX_CH].skip_sw = true; priv->tx_queue[IPA_DMA_TX_CH].skip_sw = true; ethqos_ipa_offload_event_handler(ethqos, EV_PROBE_INIT); + priv->hw->mac->map_mtl_to_dma(priv->hw, 0, 1); //change #endif return ret; @@ -1590,7 +1918,59 @@ 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); + + if (!ipc_emac_log_ctxt) + ipc_log_context_destroy(ipc_emac_log_ctxt); + + 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 Technologies, Inc. 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 7088f2e64af6..9fd2fcf42999 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h @@ -18,8 +18,20 @@ #include #include +#include +#include + +#include +#include +#include + +#include + extern void *ipc_emac_log_ctxt; +#define QCOM_ETH_QOS_MAC_ADDR_LEN 6 +#define QCOM_ETH_QOS_MAC_ADDR_STR_LEN 18 + #define IPCLOG_STATE_PAGES 50 #define MAX_QMP_MSG_SIZE 96 #define __FILENAME__ (strrchr(__FILE__, '/') ? \ @@ -397,12 +409,20 @@ struct qcom_ethqos { struct mbox_chan *qmp_mbox_chan; struct mbox_client *qmp_mbox_client; struct work_struct qmp_mailbox_work; + /* 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; + int disable_ctile_pc; u32 emac_mem_base; u32 emac_mem_size; bool ipa_enabled; + /* Key Performance Indicators */ + bool print_kpi; }; struct pps_cfg { @@ -430,6 +450,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/dwmac-qcom-ipa.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ipa.c index 3a803ae277d9..7f9a4a6ad277 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ipa.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ipa.c @@ -45,6 +45,15 @@ static struct ethqos_prv_ipa_data eth_ipa_ctx; static void __ipa_eth_free_msg(void *buff, u32 len, u32 type) {} +static inline void *ethqos_get_priv(struct qcom_ethqos *ethqos) +{ + struct platform_device *pdev = ethqos->pdev; + struct net_device *dev = platform_get_drvdata(pdev); + struct stmmac_priv *priv = netdev_priv(dev); + + return priv; +} + static int eth_ipa_send_msg(struct qcom_ethqos *ethqos, enum ipa_peripheral_event event) { @@ -981,10 +990,12 @@ static void ethqos_configure_ipa_rx_dma_channel(unsigned int QINX, static int ethqos_init_offload(struct qcom_ethqos *ethqos) { + struct stmmac_priv *priv = ethqos_get_priv(ethqos); + ETHQOSDBG("\n"); ethqos_configure_ipa_tx_dma_channel(IPA_DMA_TX_CH, ethqos); - MTL_RQDCM0R_RGWR(0x3020100); + priv->hw->mac->map_mtl_to_dma(priv->hw, 0, 0); ethqos_configure_ipa_rx_dma_channel(IPA_DMA_RX_CH, ethqos); ETHQOSDBG("\n"); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index d9f38cbd2240..10ce894dfa7e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -29,6 +29,10 @@ #include "common.h" #include #include +#ifdef CONFIG_MSM_BOOT_TIME_MARKER +#include +#endif +#include "dwmac-qcom-ipa-offload.h" struct stmmac_resources { void __iomem *addr; @@ -143,7 +147,8 @@ struct stmmac_priv { void __iomem *mmcaddr; void __iomem *ptpaddr; u32 mss; - + bool boot_kpi; + bool early_eth; #ifdef CONFIG_DEBUG_FS struct dentry *dbgfs_dir; struct dentry *dbgfs_rings_status; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index f1eee61493cc..cc0e234d47e8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -50,7 +50,7 @@ #include #include #include "dwmac1000.h" -#include "dwmac-qcom-ipa-offload.h" + #define STMMAC_ALIGN(x) __ALIGN_KERNEL(x, SMP_CACHE_BYTES) #define TSO_MAX_BUFF_SIZE (SZ_16K - 1) @@ -868,6 +868,12 @@ static void stmmac_adjust_link(struct net_device *dev) ethqos_ipa_offload_event_handler(priv, EV_PHY_LINK_DOWN); } +#ifdef CONFIG_MSM_BOOT_TIME_MARKER +if (phydev->link == 1 && !priv->boot_kpi) { + place_marker("M - Ethernet is Ready.Link is UP"); + priv->boot_kpi = true; +} +#endif if (phydev->is_pseudo_fixed_link) /* Stop PHY layer to call the hook to adjust the link in case @@ -906,6 +912,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 @@ -924,6 +954,7 @@ static int stmmac_init_phy(struct net_device *dev) int interface = priv->plat->interface; int max_speed = priv->plat->max_speed; priv->oldlink = false; + priv->boot_kpi = false; priv->speed = SPEED_UNKNOWN; priv->oldduplex = DUPLEX_UNKNOWN; @@ -950,6 +981,8 @@ static int stmmac_init_phy(struct net_device *dev) return PTR_ERR(phydev); } + pr_info(" qcom-ethqos: %s early eth setting stmmac init\n", + __func__); /* Stop Advertising 1000BASE Capability if interface is not GMII */ if ((interface == PHY_INTERFACE_MODE_MII) || @@ -967,6 +1000,29 @@ static int stmmac_init_phy(struct net_device *dev) SUPPORTED_100baseT_Half | SUPPORTED_10baseT_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) { + phydev->autoneg = AUTONEG_DISABLE; + phydev->speed = SPEED_100; + phydev->duplex = DUPLEX_FULL; + + phydev->supported = + SUPPORTED_100baseT_Full | SUPPORTED_TP | SUPPORTED_MII | + SUPPORTED_10baseT_Full; + phydev->supported &= ~SUPPORTED_Autoneg; + + phydev->advertising = phydev->supported; + phydev->advertising &= ~ADVERTISED_Autoneg; + + phy_set_max_speed(phydev, SPEED_100); + pr_info(" qcom-ethqos: %s early eth setting successful\n", + __func__); + + stmmac_set_speed100(phydev); + } + /* * Broken HW is sometimes missing the pull-up resistor on the * MDIO line, which results in reads to non-existent devices returning @@ -987,15 +1043,17 @@ static int stmmac_init_phy(struct net_device *dev) phydev->irq = PHY_POLL; if (phy_intr_en) { - phydev->irq = PHY_IGNORE_INTERRUPT; - phydev->interrupts = PHY_INTERRUPT_ENABLED; - if (phydev->drv->config_intr && !phydev->drv->config_intr(phydev)) { pr_debug(" qcom-ethqos: %s config_phy_intr successful\n", __func__); + qcom_ethqos_request_phy_wol(priv->plat); + phydev->irq = PHY_IGNORE_INTERRUPT; + phydev->interrupts = PHY_INTERRUPT_ENABLED; + } else { + pr_alert("Unable to register PHY IRQ\n"); + phydev->irq = PHY_POLL; } - qcom_ethqos_request_phy_wol(priv->plat); } phy_attached_info(phydev); return 0; @@ -1894,6 +1952,10 @@ static void stmmac_tx_clean(struct stmmac_priv *priv, 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); } @@ -3566,6 +3628,11 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) napi_gro_receive(&rx_q->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 += frame_len; } entry = next_entry; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index 4c4298f1ebee..992bb2e5f0f5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -139,6 +139,9 @@ int stmmac_mdio_reset(struct mii_bus *bus) unsigned int mii_address = priv->hw->mii.addr; struct stmmac_mdio_bus_data *data = priv->plat->mdio_bus_data; + if (priv->early_eth) + return 0; + #ifdef CONFIG_OF if (priv->device->of_node) { if (data->reset_gpio < 0) {