From 5625559c3d7b334a508f09ba9bd2effaaba04bee Mon Sep 17 00:00:00 2001 From: Suraj Jaiswal Date: Thu, 7 Mar 2024 15:49:55 +0530 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 Signed-off-by: Suraj Jaiswal --- .../admin-guide/kernel-parameters.rst | 5 + .../admin-guide/kernel-parameters.txt | 6 + .../stmicro/stmmac/dwmac-qcom-ethqos.c | 420 +++++++++++++++++- .../stmicro/stmmac/dwmac-qcom-ethqos.h | 33 ++ .../ethernet/stmicro/stmmac/dwmac-qcom-gpio.c | 4 +- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 2 + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 74 ++- .../net/ethernet/stmicro/stmmac/stmmac_mdio.c | 3 + include/linux/stmmac.h | 1 + 9 files changed, 541 insertions(+), 7 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.rst b/Documentation/admin-guide/kernel-parameters.rst index 959f73a32712..38a8231700f1 100644 --- a/Documentation/admin-guide/kernel-parameters.rst +++ b/Documentation/admin-guide/kernel-parameters.rst @@ -222,3 +222,8 @@ 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. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 8ac0b91b4d85..9e771bd59684 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -7053,3 +7053,9 @@ management firmware translates the requests into actual hardware states (core frequency, data fabric and memory clocks etc.) + + 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 153820231f2e..65cba1a9f9bc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "stmmac.h" #include "stmmac_platform.h" @@ -167,6 +168,21 @@ struct emac_emb_smmu_cb_ctx emac_emb_smmu_ctx = {0}; struct plat_stmmacenet_data *plat_dat; +struct qcom_ethqos *pethqos; + +#ifdef MODULE +static char *eipv4; +module_param(eipv4, charp, 0660); +MODULE_PARM_DESC(eipv4, "ipv4 value from ethernet partition"); + +static char *eipv6; +module_param(eipv6, charp, 0660); +MODULE_PARM_DESC(eipv6, "ipv6 value from ethernet partition"); + +static char *ermac; +module_param(ermac, charp, 0660); +MODULE_PARM_DESC(ermac, "mac address from ethernet partition"); +#endif inline void *qcom_ethqos_get_priv(struct qcom_ethqos *ethqos) { @@ -177,6 +193,196 @@ inline void *qcom_ethqos_get_priv(struct qcom_ethqos *ethqos) return priv; } +static unsigned char dev_addr[ETH_ALEN] = { + 0, 0x55, 0x7b, 0xb5, 0x7d, 0xf7}; +static struct ip_params pparams = {"", "", "", ""}; + +static int set_early_ethernet_ipv4(char *ipv4_addr_in) +{ + int ret = 1; + + pparams.is_valid_ipv4_addr = false; + + if (!ipv4_addr_in) + return ret; + + strscpy(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; +} + +static int set_early_ethernet_ipv6(char *ipv6_addr_in) +{ + int ret = 1; + + pparams.is_valid_ipv6_addr = false; + + if (!ipv6_addr_in) + return ret; + + strscpy(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; +} + +static int 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; +} + +#ifndef MODULE +static int __init set_early_ethernet_ipv4_static(char *ipv4_addr_in) +{ + int ret = 1; + + ret = set_early_ethernet_ipv4(ipv4_addr_in); + return ret; +} + +__setup("eipv4=", set_early_ethernet_ipv4_static); + +static int __init set_early_ethernet_ipv6_static(char *ipv6_addr_in) +{ + int ret = 1; + + ret = set_early_ethernet_ipv6(ipv6_addr_in); + return ret; +} + +__setup("eipv6=", set_early_ethernet_ipv6_static); + +static int __init set_early_ethernet_mac_static(char *mac_addr) +{ + int ret = 1; + + ret = set_early_ethernet_mac(mac_addr); + return ret; +} + +__setup("ermac=", set_early_ethernet_mac_static); +#endif + +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)); + + strscpy(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); + + ETHQOSINFO("M - Etherent Assigned IPv4 address\n"); + } + 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 { + ret = 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); + + ETHQOSINFO("M - Ethernet Assigned IPv6 address\n"); + } + return ret; +} + static int rgmii_readl(struct qcom_ethqos *ethqos, unsigned int offset) { return readl(ethqos->rgmii_base + offset); @@ -1438,17 +1644,147 @@ static void qcom_ethqos_phy_resume_clks(struct qcom_ethqos *ethqos) ETHQOSDBG("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, 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) +{ + 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; + + if (pparams.is_valid_ipv4_addr) { + INIT_DELAYED_WORK(ðqos->ipv4_addr_assign_wq, + ethqos_is_ipv4_NW_stack_ready); + schedule_delayed_work(ðqos->ipv4_addr_assign_wq, + 0); + } + + if (pparams.is_valid_ipv6_addr) { + INIT_DELAYED_WORK(ðqos->ipv6_addr_assign_wq, + ethqos_is_ipv6_NW_stack_ready); + 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); + eth_hw_addr_set(priv->dev, dev_addr); + } + + return 0; +} + static int qcom_ethqos_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct stmmac_resources stmmac_res; struct qcom_ethqos *ethqos = NULL; + struct net_device *ndev; + struct stmmac_priv *priv; int ret; if (of_device_is_compatible(pdev->dev.of_node, "qcom,emac-smmu-embedded")) return emac_emb_smmu_cb_probe(pdev, plat_dat); + + ETHQOSINFO("M - Ethernet probe start\n"); + +#ifdef MODULE + if (eipv4) + ret = set_early_ethernet_ipv4(eipv4); + + if (eipv6) + ret = set_early_ethernet_ipv6(eipv6); + + if (ermac) + ret = set_early_ethernet_mac(ermac); +#endif + ret = stmmac_get_platform_resources(pdev, &stmmac_res); if (ret) return ret; @@ -1493,6 +1829,22 @@ static int qcom_ethqos_probe(struct platform_device *pdev) plat_dat->interface == PHY_INTERFACE_MODE_USXGMII) qcom_ethqos_serdes_configure_dt(ethqos); + /*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(ethqos, SPEED_10); ethqos_set_func_clk_en(ethqos); @@ -1502,6 +1854,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev) plat_dat->serdes_loopback_v3_1 = qcom_serdes_loopback_v3_1; plat_dat->dump_debug_regs = rgmii_dump; plat_dat->has_gmac4 = 1; + plat_dat->early_eth = ethqos->early_eth_enabled; if (plat_dat->interface == PHY_INTERFACE_MODE_SGMII || plat_dat->interface == PHY_INTERFACE_MODE_USXGMII) plat_dat->serdes_powerup = ethqos_serdes_power_up; @@ -1564,6 +1917,8 @@ static int qcom_ethqos_probe(struct platform_device *pdev) if (ret) goto err_clk; + pethqos = ethqos; + if (of_property_read_bool(np, "pcs-v3")) { plat_dat->pcs_v3 = true; } else { @@ -1578,6 +1933,20 @@ static int qcom_ethqos_probe(struct platform_device *pdev) ETHQOSERR("Phy interrupt configuration failed"); } rgmii_dump(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); + } + ETHQOSINFO("M - Ethernet probe end\n"); return ret; err_clk: @@ -1897,7 +2266,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; + + ETHQOSDBG("\n"); + + ret = platform_driver_register(&qcom_ethqos_driver); + if (ret < 0) { + ETHQOSINFO("qcom-ethqos: Driver registration failed"); + return ret; + } + + ETHQOSDBG("\n"); + + return ret; +} + +static void __exit qcom_ethqos_exit_module(void) +{ + ETHQOSDBG("\n"); + + platform_driver_unregister(&qcom_ethqos_driver); + + ETHQOSDBG("\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 7c0e696c685e..10569bd0b1fd 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h @@ -3,6 +3,18 @@ #ifndef _DWMAC_QCOM_ETHQOS_H #define _DWMAC_QCOM_ETHQOS_H +#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) @@ -86,6 +98,27 @@ struct qcom_ethqos { /* Boolean flag for turning off GDSC during suspend */ bool gdsc_off_on_suspend; + /* 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 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_regulators(struct qcom_ethqos *ethqos); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-gpio.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-gpio.c index cd74d36ff457..91ae40a8dea5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-gpio.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-gpio.c @@ -214,7 +214,7 @@ int ethqos_init_pinctrl(struct device *dev) return ret; } - ETHQOSINFO("pinctrl_lookup_state %s succeeded\n", name); + ETHQOSDBG("pinctrl_lookup_state %s succeeded\n", name); ret = pinctrl_select_state(pinctrl, pinctrl_state); if (ret) { @@ -222,7 +222,7 @@ int ethqos_init_pinctrl(struct device *dev) return ret; } - ETHQOSINFO("pinctrl_select_state %s succeeded\n", name); + ETHQOSDBG("pinctrl_select_state %s succeeded\n", name); } return ret; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 573588328d2a..55455f240a85 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -326,6 +326,8 @@ struct stmmac_priv { struct bpf_prog *xdp_prog; bool phy_irq_enabled; + bool boot_kpi; + bool early_eth; }; enum stmmac_state { diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 31050d6d7c23..f32dfa696a6e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -183,6 +183,8 @@ int stmmac_bus_clks_config(struct stmmac_priv *priv, bool enabled) } EXPORT_SYMBOL_GPL(stmmac_bus_clks_config); +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 @@ -1038,12 +1040,38 @@ 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); linkmode_and(supported, supported, mac_supported); linkmode_andnot(supported, supported, mask); - linkmode_and(state->advertising, - state->advertising, mac_supported); - linkmode_andnot(state->advertising, - state->advertising, mask); + + phylink_clear(mac_supported, Autoneg); + linkmode_and(state->advertising, state->advertising, mac_supported); + linkmode_andnot(state->advertising, state->advertising, mask); + + pr_info(" qcom-ethqos: %s early eth setting successful\n", + __func__); + + stmmac_set_speed100(priv->phydev); + } else { + linkmode_and(supported, supported, mac_supported); + linkmode_andnot(supported, supported, mask); + linkmode_and(state->advertising, + state->advertising, mac_supported); + linkmode_andnot(state->advertising, + state->advertising, mask); + } } static void stmmac_mac_config(struct phylink_config *config, unsigned int mode, @@ -1195,6 +1223,11 @@ static void stmmac_mac_link_up(struct phylink_config *config, if (priv->dma_cap.fpesel) stmmac_fpe_link_state_handle(priv, true); + + if (phy->link == 1 && !priv->boot_kpi) { + pr_info("M - Ethernet is Ready.Link is UP\n"); + priv->boot_kpi = true; + } } static const struct phylink_mac_ops stmmac_phylink_mac_ops = { @@ -1230,6 +1263,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 @@ -1245,6 +1302,8 @@ static int stmmac_init_phy(struct net_device *dev) struct fwnode_handle *fwnode; int ret = 0; + priv->boot_kpi = false; + if (!phylink_expects_phy(priv->phylink)) return 0; @@ -2706,6 +2765,9 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue) priv->dev->stats.tx_packets++; priv->xstats.tx_pkt_n++; priv->xstats.txq_stats[queue].tx_pkt_n++; + + if (priv->dev->stats.tx_packets == 1) + pr_info("M - Ethernet first packet transmitted\n"); } if (skb) stmmac_get_tx_hwtstamp(priv, p, skb); @@ -5548,6 +5610,10 @@ drain_data: skb = NULL; priv->dev->stats.rx_packets++; + + if (priv->dev->stats.rx_packets == 1) + pr_info_once("M - Ethernet first packet received\n"); + 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 40f946e62201..15f22a184c84 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -351,6 +351,9 @@ int stmmac_mdio_reset(struct mii_bus *bus) unsigned int mii_address = priv->hw->mii.addr; bool active_high = false; + if (priv->plat->early_eth) + return 0; + #ifdef CONFIG_OF if (priv->device->of_node) { struct gpio_desc *reset_gpio; diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index b08d402f822c..3f73e8d437c3 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -296,5 +296,6 @@ struct plat_stmmacenet_data { bool pcs_v3; void (*phy_irq_enable)(void *priv); void (*phy_irq_disable)(void *priv); + bool early_eth; }; #endif