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