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/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/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c index 1e5e7dd3cc1b..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 @@ -22,6 +21,8 @@ #include #include #include +#include + #include "stmmac.h" #include "stmmac_platform.h" #include "dwmac-qcom-ethqos.h" @@ -113,10 +114,15 @@ #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; +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,293 @@ 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->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 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; @@ -1056,10 +1511,23 @@ 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 + + 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; @@ -1081,7 +1549,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 +1569,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 +1651,20 @@ 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); + + 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 +1773,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..b3ef4ff72b65 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.h @@ -5,12 +5,38 @@ #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 + +#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) #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 @@ -162,6 +188,15 @@ 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 dentry *debugfs_dir; }; struct pps_cfg { @@ -191,6 +226,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..f0bcd1c61d6f 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,9 @@ 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; + bool early_eth_config_set; #ifdef CONFIG_DEBUG_FS struct dentry *dbgfs_dir; #endif 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 b35585c7c165..097a6d84dff2 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 @@ -875,6 +877,31 @@ static void stmmac_validate(struct phylink_config *config, __ETHTOOL_LINK_MODE_MASK_NBITS); bitmap_andnot(state->advertising, state->advertising, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); + + /* 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, + __ETHTOOL_LINK_MODE_MASK_NBITS); + + pr_info(" qcom-ethqos: %s early eth setting successful\n", + __func__); + + stmmac_set_speed100(priv->phydev); + /* Validate method will also be called + * when we change speed using ethtool. + * Add check to avoid multiple calls + */ + priv->early_eth_config_set = 1; + } + } static int stmmac_mac_link_state(struct phylink_config *config, @@ -970,6 +997,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 +1039,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 +1077,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 +1120,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 +1999,11 @@ 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"); +#endif } stmmac_get_tx_hwtstamp(priv, p, skb); } @@ -3719,6 +3785,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++; } @@ -3733,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; } 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;