diff options
Diffstat (limited to 'drivers/emac-dwc-eqos/DWC_ETH_QOS_platform.c')
-rw-r--r-- | drivers/emac-dwc-eqos/DWC_ETH_QOS_platform.c | 494 |
1 files changed, 449 insertions, 45 deletions
diff --git a/drivers/emac-dwc-eqos/DWC_ETH_QOS_platform.c b/drivers/emac-dwc-eqos/DWC_ETH_QOS_platform.c index 323c3a2..4d57d7a 100644 --- a/drivers/emac-dwc-eqos/DWC_ETH_QOS_platform.c +++ b/drivers/emac-dwc-eqos/DWC_ETH_QOS_platform.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -49,6 +49,8 @@ #include "DWC_ETH_QOS_yheader.h" #include "DWC_ETH_QOS_ipa.h" +void *ipc_emac_log_ctxt; + static UCHAR dev_addr[6] = {0, 0x55, 0x7b, 0xb5, 0x7d, 0xf7}; struct DWC_ETH_QOS_res_data dwc_eth_qos_res_data = {0, }; static struct msm_bus_scale_pdata *emac_bus_scale_vec = NULL; @@ -62,7 +64,7 @@ struct emac_emb_smmu_cb_ctx emac_emb_smmu_ctx = {0}; static struct qmp_pkt pkt; static char qmp_buf[MAX_QMP_MSG_SIZE + 1] = {0}; extern int create_pps_interrupt_info_device_node(dev_t *pps_dev_t, - struct cdev* pps_cdev, struct class* pps_class, + struct cdev** pps_cdev, struct class** pps_class, char *pps_dev_node_name); extern int remove_pps_interrupt_info_device_node(struct DWC_ETH_QOS_prv_data *pdata); @@ -85,6 +87,105 @@ module_param(phy_interrupt_en, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); MODULE_PARM_DESC(phy_interrupt_en, "Enable PHY interrupt [0-DISABLE, 1-ENABLE]"); +struct ip_params pparams = {}; +#ifdef DWC_ETH_QOS_BUILTIN +/*! + * \brief API to extract MAC Address from given string + * + * \param[in] pointer to MAC Address string + * + * \return None + */ +void DWC_ETH_QOS_extract_macid(char *mac_addr) +{ + char *input = NULL; + int i = 0; + UCHAR mac_id = 0; + + if (!mac_addr) + return; + + /* Extract MAC ID byte by byte */ + input = strsep(&mac_addr, ":"); + while(input != NULL && i < DWC_ETH_QOS_MAC_ADDR_LEN) { + sscanf(input, "%x", &mac_id); + pparams.mac_addr[i++] = mac_id; + input = strsep(&mac_addr, ":"); + } + if (!is_valid_ether_addr(pparams.mac_addr)) { + EMACERR("Invalid Mac address programmed: %s\n", mac_addr); + return; + } else + pparams.is_valid_mac_addr = true; + + return; +} + +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)); + EMACDBG("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) { + EMACERR("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)); + EMACDBG("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 == 0) { + EMACERR("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; + char temp_mac_addr[DWC_ETH_QOS_MAC_ADDR_STR_LEN]; + pparams.is_valid_mac_addr = false; + + if(!mac_addr) + return ret; + + strlcpy(temp_mac_addr, mac_addr, sizeof(temp_mac_addr)); + EMACDBG("Early ethernet MAC address assigned: %s\n", temp_mac_addr); + temp_mac_addr[DWC_ETH_QOS_MAC_ADDR_STR_LEN-1] = '\0'; + + DWC_ETH_QOS_extract_macid(temp_mac_addr); + return ret; +} +__setup("ermac=", set_early_ethernet_mac); +#endif + static ssize_t read_phy_reg_dump(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -425,8 +526,10 @@ static void DWC_ETH_QOS_configure_gpio_pins(struct platform_device *pdev) return; } EMACDBG("get pinctrl succeed\n"); + dwc_eth_qos_res_data.pinctrl = pinctrl; if (dwc_eth_qos_res_data.emac_hw_version_type == EMAC_HW_v2_2_0 || + dwc_eth_qos_res_data.emac_hw_version_type == EMAC_HW_v2_1_2 || dwc_eth_qos_res_data.emac_hw_version_type == EMAC_HW_v2_3_1) { /* PPS0 pin */ emac_pps_0 = pinctrl_lookup_state(pinctrl, EMAC_PIN_PPS0); @@ -618,6 +721,28 @@ static void DWC_ETH_QOS_configure_gpio_pins(struct platform_device *pdev) else EMACDBG("Set rgmii_rxc_state succeed\n"); + dwc_eth_qos_res_data.rgmii_rxc_suspend_state = + pinctrl_lookup_state(pinctrl, EMAC_RGMII_RXC_SUSPEND); + if (IS_ERR_OR_NULL(dwc_eth_qos_res_data.rgmii_rxc_suspend_state)) { + ret = PTR_ERR(dwc_eth_qos_res_data.rgmii_rxc_suspend_state); + EMACERR("Failed to get rgmii_rxc_suspend_state, err = %d\n", ret); + dwc_eth_qos_res_data.rgmii_rxc_suspend_state = NULL; + } + else { + EMACDBG("Get rgmii_rxc_suspend_state succeed\n"); + } + + dwc_eth_qos_res_data.rgmii_rxc_resume_state = + pinctrl_lookup_state(pinctrl, EMAC_RGMII_RXC_RESUME); + if (IS_ERR_OR_NULL(dwc_eth_qos_res_data.rgmii_rxc_resume_state)) { + ret = PTR_ERR(dwc_eth_qos_res_data.rgmii_rxc_resume_state); + EMACERR("Failed to get rgmii_rxc_resume_state, err = %d\n", ret); + dwc_eth_qos_res_data.rgmii_rxc_resume_state = NULL; + } + else { + EMACDBG("Get rgmii_rxc_resume_state succeed\n"); + } + rgmii_rx_ctl_state = pinctrl_lookup_state(pinctrl, EMAC_RGMII_RX_CTL); if (IS_ERR_OR_NULL(rgmii_rx_ctl_state)) { ret = PTR_ERR(rgmii_rx_ctl_state); @@ -723,6 +848,10 @@ static int DWC_ETH_QOS_get_dts_config(struct platform_device *pdev) } EMACDBG(": emac_core_version = %d\n", dwc_eth_qos_res_data.emac_hw_version_type); + if (dwc_eth_qos_res_data.emac_hw_version_type == EMAC_HW_v2_3_1 || + dwc_eth_qos_res_data.emac_hw_version_type == EMAC_HW_v2_1_2) + dwc_eth_qos_res_data.pps_lpass_conn_en = true; + if (dwc_eth_qos_res_data.emac_hw_version_type == EMAC_HW_v2_3_1) { resource = platform_get_resource_byname(pdev, IORESOURCE_IRQ, @@ -748,6 +877,17 @@ static int DWC_ETH_QOS_get_dts_config(struct platform_device *pdev) } + dwc_eth_qos_res_data.early_eth_en = 0; + 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 */ + dwc_eth_qos_res_data.early_eth_en = 1; + EMACINFO("Early ethernet is enabled\n"); + } + ret = DWC_ETH_QOS_get_io_macro_config(pdev); if (ret) goto err_out; @@ -829,7 +969,7 @@ int DWC_ETH_QOS_qmp_mailbox_init(struct DWC_ETH_QOS_prv_data *pdata) pdata->qmp_mbox_client = devm_kzalloc( &pdata->pdev->dev, sizeof(*pdata->qmp_mbox_client), GFP_KERNEL); - if (IS_ERR(pdata->qmp_mbox_client)){ + if (pdata->qmp_mbox_client == NULL || IS_ERR(pdata->qmp_mbox_client)){ EMACERR("qmp alloc client failed\n"); return -1; } @@ -968,8 +1108,15 @@ void DWC_ETH_QOS_resume_clks(struct DWC_ETH_QOS_prv_data *pdata) else DWC_ETH_QOS_set_clk_and_bus_config(pdata, SPEED_10); +#ifdef DWC_ETH_QOS_CONFIG_PTP + if (dwc_eth_qos_res_data.ptp_clk) + clk_prepare_enable(dwc_eth_qos_res_data.ptp_clk); +#endif + pdata->clks_suspended = 0; - complete_all(&pdata->clk_enable_done); + + if (pdata->phy_intr_en) + complete_all(&pdata->clk_enable_done); EMACDBG("Exit\n"); } @@ -978,7 +1125,9 @@ void DWC_ETH_QOS_suspend_clks(struct DWC_ETH_QOS_prv_data *pdata) { EMACDBG("Enter\n"); - reinit_completion(&pdata->clk_enable_done); + if (pdata->phy_intr_en) + reinit_completion(&pdata->clk_enable_done); + pdata->clks_suspended = 1; DWC_ETH_QOS_set_clk_and_bus_config(pdata, 0); @@ -992,6 +1141,11 @@ void DWC_ETH_QOS_suspend_clks(struct DWC_ETH_QOS_prv_data *pdata) if (dwc_eth_qos_res_data.rgmii_clk) clk_disable_unprepare(dwc_eth_qos_res_data.rgmii_clk); +#ifdef DWC_ETH_QOS_CONFIG_PTP + if (dwc_eth_qos_res_data.ptp_clk) + clk_disable_unprepare(dwc_eth_qos_res_data.ptp_clk); +#endif + EMACDBG("Exit\n"); } @@ -1320,7 +1474,8 @@ static int DWC_ETH_QOS_init_gpios(struct device *dev) } } - if (dwc_eth_qos_res_data.is_gpio_phy_reset) { + if (dwc_eth_qos_res_data.is_gpio_phy_reset && + !dwc_eth_qos_res_data.early_eth_en) { ret = setup_gpio_output_common( dev, EMAC_GPIO_PHY_RESET_NAME, &dwc_eth_qos_res_data.gpio_phy_reset, PHY_RESET_GPIO_LOW); @@ -1349,6 +1504,104 @@ static struct of_device_id DWC_ETH_QOS_plat_drv_match[] = { {} }; +void is_ipv6_NW_stack_ready(struct work_struct *work) +{ + struct delayed_work *dwork; + struct DWC_ETH_QOS_prv_data *pdata; + int ret; + + EMACDBG("\n"); + dwork = container_of(work, struct delayed_work, work); + pdata = container_of(dwork, struct DWC_ETH_QOS_prv_data, ipv6_addr_assign_wq); + + ret = DWC_ETH_QOS_add_ipv6addr(pdata); + if (ret) + return; + + cancel_delayed_work_sync(&pdata->ipv6_addr_assign_wq); + flush_delayed_work(&pdata->ipv6_addr_assign_wq); + return; +} + +int DWC_ETH_QOS_add_ipv6addr(struct DWC_ETH_QOS_prv_data *pdata) +{ + int ret; +#ifdef DWC_ETH_QOS_BUILTIN + struct in6_ifreq ir6; + char* prefix; + struct ip_params *ip_info = &pparams; + struct net *net = dev_net(pdata->dev); + + EMACDBG("\n"); + if (!net || !net->genl_sock || !net->genl_sock->sk_socket) + EMACERR("Sock is null, unable to assign ipv6 address\n"); + + if (!net->ipv6.devconf_dflt) { + EMACDBG("ipv6.devconf_dflt is null, schedule wq\n"); + schedule_delayed_work(&pdata->ipv6_addr_assign_wq, msecs_to_jiffies(1000)); + return -EFAULT; + } + + /*For valid IPv6 address*/ + memset(&ir6, 0, sizeof(ir6)); + memcpy(&ir6, &ip_info->ipv6_addr, sizeof(struct in6_ifreq)); + ir6.ifr6_ifindex = pdata->dev->ifindex; + + if ((prefix = strchr(ip_info->ipv6_addr_str, '/')) == NULL) + ir6.ifr6_prefixlen = 0; + else { + ir6.ifr6_prefixlen = simple_strtoul(prefix + 1, NULL, 0); + if (ir6.ifr6_prefixlen > 128) + ir6.ifr6_prefixlen = 0; + } + + ret = inet6_ioctl(net->genl_sock->sk_socket, SIOCSIFADDR, (unsigned long)(void *)&ir6); + if (ret) + EMACERR("Can't setup IPv6 address!\r\n"); + else + EMACDBG("Assigned IPv6 address: %s\r\n", ip_info->ipv6_addr_str); +#else + ret = -EFAULT; +#endif + return ret; +} + +int DWC_ETH_QOS_add_ipaddr(struct DWC_ETH_QOS_prv_data *pdata) +{ + int ret=0; +#ifdef DWC_ETH_QOS_BUILTIN + struct ip_params *ip_info = &pparams; + struct ifreq ir; + struct sockaddr_in *sin = (void *) &ir.ifr_ifru.ifru_addr; + struct net *net = dev_net(pdata->dev); + + if (!net || !net->genl_sock || !net->genl_sock->sk_socket) + EMACERR("Sock is null, unable to assign ipv4 address\n"); + + /*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, pdata->dev->name, sizeof(ir.ifr_ifrn.ifrn_name) + 1); + sin->sin_family = AF_INET; + sin->sin_port = 0; + + ret = inet_ioctl(net->genl_sock->sk_socket, SIOCSIFADDR, (unsigned long)(void *)&ir); + if (ret) + EMACERR( "Can't setup IPv4 address!: %d\r\n", ret); + else + EMACDBG("Assigned IPv4 address: %s\r\n", ip_info->ipv4_addr_str); +#endif + return ret; +} + +u32 l3mdev_fib_table1 (const struct net_device *dev) +{ + return RT_TABLE_LOCAL; +} + +const struct l3mdev_ops l3mdev_op1 = {.l3mdev_fib_table = l3mdev_fib_table1}; + static int DWC_ETH_QOS_configure_netdevice(struct platform_device *pdev) { struct DWC_ETH_QOS_prv_data *pdata = NULL; @@ -1371,6 +1624,10 @@ static int DWC_ETH_QOS_configure_netdevice(struct platform_device *pdev) ret = -ENOMEM; goto err_out_dev_failed; } + + if (pparams.is_valid_mac_addr == true) + ether_addr_copy(dev_addr, pparams.mac_addr); + dev->dev_addr[0] = dev_addr[0]; dev->dev_addr[1] = dev_addr[1]; dev->dev_addr[2] = dev_addr[2]; @@ -1420,9 +1677,24 @@ static int DWC_ETH_QOS_configure_netdevice(struct platform_device *pdev) /* store emac hw version in pdata*/ pdata->emac_hw_version_type = dwc_eth_qos_res_data.emac_hw_version_type; +#ifdef CONFIG_NET_L3_MASTER_DEV + if (pdata->res_data->early_eth_en && pdata->emac_hw_version_type == EMAC_HW_v2_3_1) { + EMACDBG("l3mdev_op1 set \n"); + dev->priv_flags = IFF_L3MDEV_MASTER; + dev->l3mdev_ops = &l3mdev_op1; + } +#endif + + /* Scale the clocks to 10Mbps speed */ - pdata->speed = SPEED_10; - DWC_ETH_QOS_set_clk_and_bus_config(pdata, SPEED_10); + if (pdata->res_data->early_eth_en) { + pdata->speed = SPEED_100; + DWC_ETH_QOS_set_clk_and_bus_config(pdata, SPEED_100); + } + else { + pdata->speed = SPEED_10; + DWC_ETH_QOS_set_clk_and_bus_config(pdata, SPEED_10); + } DWC_ETH_QOS_set_rgmii_func_clk_en(); @@ -1440,7 +1712,7 @@ static int DWC_ETH_QOS_configure_netdevice(struct platform_device *pdev) pdata->ipa_enabled = 0; #endif - EMACINFO("EMAC IPA enabled: %d\n", pdata->ipa_enabled); + EMACDBG("EMAC IPA enabled: %d\n", pdata->ipa_enabled); if (pdata->ipa_enabled) { pdata->prv_ipa.ipa_ver = ipa_get_hw_type(); device_init_wakeup(&pdev->dev, 1); @@ -1580,10 +1852,10 @@ static int DWC_ETH_QOS_configure_netdevice(struct platform_device *pdev) if (pdata->emac_hw_version_type == EMAC_HW_v2_3_1) { create_pps_interrupt_info_device_node(&pdata->avb_class_a_dev_t, - pdata->avb_class_a_cdev, pdata->avb_class_a_class, AVB_CLASS_A_POLL_DEV_NODE_NAME); + &pdata->avb_class_a_cdev, &pdata->avb_class_a_class, AVB_CLASS_A_POLL_DEV_NODE_NAME); create_pps_interrupt_info_device_node(&pdata->avb_class_b_dev_t, - pdata->avb_class_b_cdev ,pdata->avb_class_b_class, AVB_CLASS_B_POLL_DEV_NODE_NAME); + &pdata->avb_class_b_cdev ,&pdata->avb_class_b_class, AVB_CLASS_B_POLL_DEV_NODE_NAME); } DWC_ETH_QOS_create_debugfs(pdata); @@ -1596,6 +1868,19 @@ static int DWC_ETH_QOS_configure_netdevice(struct platform_device *pdev) queue_work(system_wq, &pdata->qmp_mailbox_work); } + if (pdata->res_data->early_eth_en) { + if (pparams.is_valid_ipv4_addr) + ret = DWC_ETH_QOS_add_ipaddr(pdata); + + if (pparams.is_valid_ipv6_addr) { + INIT_DELAYED_WORK(&pdata->ipv6_addr_assign_wq, is_ipv6_NW_stack_ready); + ret = DWC_ETH_QOS_add_ipv6addr(pdata); + if (ret) + schedule_delayed_work(&pdata->ipv6_addr_assign_wq, msecs_to_jiffies(1000)); + } + + } + EMACDBG("<-- DWC_ETH_QOS_configure_netdevice\n"); return 0; @@ -1773,7 +2058,9 @@ static int DWC_ETH_QOS_probe(struct platform_device *pdev) int ret = 0; EMACDBG("--> DWC_ETH_QOS_probe\n"); - +#ifdef CONFIG_MSM_BOOT_TIME_MARKER + place_marker("M - Ethernet probe start"); +#endif if (of_device_is_compatible(pdev->dev.of_node, "qcom,emac-smmu-embedded")) return emac_emb_smmu_cb_probe(pdev); @@ -2001,10 +2288,10 @@ static void DWC_ETH_QOS_shutdown(struct platform_device *pdev) * \retval 0 */ -static INT DWC_ETH_QOS_suspend(struct platform_device *pdev, pm_message_t state) +static INT DWC_ETH_QOS_suspend(struct device *dev) { - struct net_device *dev = platform_get_drvdata(pdev); - struct DWC_ETH_QOS_prv_data *pdata = netdev_priv(dev); + struct DWC_ETH_QOS_prv_data *pdata = gDWC_ETH_QOS_prv_data; + struct net_device *net_dev = pdata->dev; struct hw_if_struct *hw_if = &pdata->hw_if; INT ret, pmt_flags = 0; unsigned int rwk_filter_values[] = { @@ -2040,7 +2327,7 @@ static INT DWC_ETH_QOS_suspend(struct platform_device *pdev, pm_message_t state) EMACDBG("-->DWC_ETH_QOS_suspend\n"); - if (of_device_is_compatible(pdev->dev.of_node, "qcom,emac-smmu-embedded")) { + if (of_device_is_compatible(dev->of_node, "qcom,emac-smmu-embedded")) { EMACDBG("<--DWC_ETH_QOS_suspend smmu return\n"); return 0; } @@ -2049,16 +2336,10 @@ static INT DWC_ETH_QOS_suspend(struct platform_device *pdev, pm_message_t state) pdata->power_down_type |= DWC_ETH_QOS_EMAC_INTR_WAKEUP; enable_irq_wake(pdata->irq_number); - /* Set PHY intr as wakeup-capable to handle change in PHY link status after suspend */ - if (pdata->phy_intr_en && pdata->phy_irq && pdata->phy_wol_wolopts) { - pmt_flags |= DWC_ETH_QOS_PHY_INTR_WAKEUP; - enable_irq_wake(pdata->phy_irq); - } - return 0; } - if (!dev || !netif_running(dev)) { + if (!net_dev || !netif_running(net_dev)) { return -EINVAL; } @@ -2070,15 +2351,26 @@ static INT DWC_ETH_QOS_suspend(struct platform_device *pdev, pm_message_t state) if (pdata->hw_feat.mgk_sel && (pdata->wolopts & WAKE_MAGIC)) pmt_flags |= DWC_ETH_QOS_MAGIC_WAKEUP; - if (pdata->phy_intr_en && pdata->phy_irq && pdata->phy_wol_wolopts) - pmt_flags |= DWC_ETH_QOS_PHY_INTR_WAKEUP; - - ret = DWC_ETH_QOS_powerdown(dev, pmt_flags, DWC_ETH_QOS_DRIVER_CONTEXT); + ret = DWC_ETH_QOS_powerdown(net_dev, pmt_flags, DWC_ETH_QOS_DRIVER_CONTEXT); DWC_ETH_QOS_suspend_clks(pdata); - EMACDBG("<--DWC_ETH_QOS_suspend ret = %d\n", ret); + /* Suspend the PHY RXC clock. */ + if (dwc_eth_qos_res_data.is_pinctrl_names && + (dwc_eth_qos_res_data.rgmii_rxc_suspend_state != NULL)) { + /* Remove RXC clock source from Phy.*/ + ret = pinctrl_select_state(dwc_eth_qos_res_data.pinctrl, + dwc_eth_qos_res_data.rgmii_rxc_suspend_state); + if (ret) + EMACERR("Unable to set rgmii_rxc_suspend_state state, err = %d\n", ret); + else + EMACDBG("Set rgmii_rxc_suspend_state succeed\n"); + } + EMACDBG("<--DWC_ETH_QOS_suspend ret = %d\n", ret); +#ifdef CONFIG_MSM_BOOT_TIME_MARKER + pdata->print_kpi = 0; +#endif return ret; } @@ -2104,18 +2396,18 @@ static INT DWC_ETH_QOS_suspend(struct platform_device *pdev, pm_message_t state) * \retval 0 */ -static INT DWC_ETH_QOS_resume(struct platform_device *pdev) +static INT DWC_ETH_QOS_resume(struct device *dev) { - struct net_device *dev = platform_get_drvdata(pdev); - struct DWC_ETH_QOS_prv_data *pdata = netdev_priv(dev); + struct DWC_ETH_QOS_prv_data *pdata = gDWC_ETH_QOS_prv_data; + struct net_device *net_dev = pdata->dev; INT ret; - DBGPR("-->DWC_ETH_QOS_resume\n"); - if (of_device_is_compatible(pdev->dev.of_node, "qcom,emac-smmu-embedded")) + EMACDBG("-->DWC_ETH_QOS_resume\n"); + if (of_device_is_compatible(dev->of_node, "qcom,emac-smmu-embedded")) return 0; - if (!dev || !netif_running(dev)) { - DBGPR("<--DWC_ETH_QOS_dev_resume\n"); + if (!net_dev || !netif_running(net_dev)) { + EMACERR("<--DWC_ETH_QOS_dev_resume\n"); return -EINVAL; } @@ -2132,40 +2424,143 @@ static INT DWC_ETH_QOS_resume(struct platform_device *pdev) /* Wakeup reason can be PHY link event or a RX packet */ /* Set a wakeup event to ensure enough time for processing */ - pm_wakeup_event(&pdev->dev, 5000); + pm_wakeup_event(dev, 5000); return 0; } + /* Resume the PhY RXC clock. */ + if (dwc_eth_qos_res_data.is_pinctrl_names && + (dwc_eth_qos_res_data.rgmii_rxc_resume_state != NULL)) { + + /* Enable RXC clock source from Phy.*/ + ret = pinctrl_select_state(dwc_eth_qos_res_data.pinctrl, + dwc_eth_qos_res_data.rgmii_rxc_resume_state); + if (ret) + EMACERR("Unable to set rgmii_rxc_resume_state state, err = %d\n", ret); + else + EMACDBG("Set rgmii_rxc_resume_state succeed\n"); + } + DWC_ETH_QOS_resume_clks(pdata); - ret = DWC_ETH_QOS_powerup(dev, DWC_ETH_QOS_DRIVER_CONTEXT); + ret = DWC_ETH_QOS_powerup(net_dev, DWC_ETH_QOS_DRIVER_CONTEXT); if (pdata->ipa_enabled) DWC_ETH_QOS_ipa_offload_event_handler(pdata, EV_DPM_RESUME); /* Wakeup reason can be PHY link event or a RX packet */ /* Set a wakeup event to ensure enough time for processing */ - pm_wakeup_event(&pdev->dev, 5000); + pm_wakeup_event(dev, 5000); - DBGPR("<--DWC_ETH_QOS_resume\n"); + EMACDBG("<--DWC_ETH_QOS_resume\n"); return ret; } #endif /* CONFIG_PM */ -static struct platform_driver DWC_ETH_QOS_plat_drv = { - .probe = DWC_ETH_QOS_probe, - .remove = DWC_ETH_QOS_remove, - .shutdown = DWC_ETH_QOS_shutdown, +static int DWC_ETH_QOS_hib_restore(struct device *dev) { + struct DWC_ETH_QOS_prv_data *pdata = gDWC_ETH_QOS_prv_data; + int ret = 0; + + if (of_device_is_compatible(dev->of_node, "qcom,emac-smmu-embedded")) + return 0; + + EMACINFO(" start\n"); + + ret = DWC_ETH_QOS_init_regulators(dev); + if (ret) + return ret; + + ret = DWC_ETH_QOS_init_gpios(dev); + if (ret) + return ret; + + ret = DWC_ETH_QOS_get_clks(dev); + if (ret) + return ret; + + DWC_ETH_QOS_set_clk_and_bus_config(pdata, pdata->speed); + + DWC_ETH_QOS_set_rgmii_func_clk_en(); + +#ifdef DWC_ETH_QOS_CONFIG_PTP + DWC_ETH_QOS_ptp_init(pdata); +#endif /* end of DWC_ETH_QOS_CONFIG_PTP */ + + /* issue software reset to device */ + pdata->hw_if.exit(); + + /* Bypass PHYLIB for TBI, RTBI and SGMII interface */ + if (pdata->hw_feat.sma_sel == 1) { + ret = DWC_ETH_QOS_mdio_register(pdata->dev); + if (ret < 0) { + EMACERR("MDIO bus (id %d) registration failed\n", + pdata->bus_id); + return ret; + } + } + + if (!(pdata->dev->flags & IFF_UP)) { + pdata->dev->netdev_ops->ndo_open(pdata->dev); + pdata->dev->flags |= IFF_UP; + } + + EMACINFO("end\n"); + + return ret; +} + +static int DWC_ETH_QOS_hib_freeze(struct device *dev) { + struct DWC_ETH_QOS_prv_data *pdata = gDWC_ETH_QOS_prv_data; + int ret = 0; + + if (of_device_is_compatible(dev->of_node, "qcom,emac-smmu-embedded")) + return 0; + + EMACINFO(" start\n"); + if (pdata->dev->flags & IFF_UP) { + pdata->dev->netdev_ops->ndo_stop(pdata->dev); + pdata->dev->flags &= ~IFF_UP; + } + + if (pdata->hw_feat.sma_sel == 1) + DWC_ETH_QOS_mdio_unregister(pdata->dev); + +#ifdef DWC_ETH_QOS_CONFIG_PTP + DWC_ETH_QOS_ptp_remove(pdata); +#endif /* end of DWC_ETH_QOS_CONFIG_PTP */ + + DWC_ETH_QOS_disable_clks(dev); + + DWC_ETH_QOS_disable_regulators(); + + DWC_ETH_QOS_free_gpios(); + + EMACINFO("end\n"); + + return ret; +} + +static const struct dev_pm_ops DWC_ETH_QOS_pm_ops = { + .freeze = DWC_ETH_QOS_hib_freeze, + .restore = DWC_ETH_QOS_hib_restore, + .thaw = DWC_ETH_QOS_hib_restore, #ifdef CONFIG_PM .suspend = DWC_ETH_QOS_suspend, .resume = DWC_ETH_QOS_resume, #endif +}; + +static struct platform_driver DWC_ETH_QOS_plat_drv = { + .probe = DWC_ETH_QOS_probe, + .remove = DWC_ETH_QOS_remove, + .shutdown = DWC_ETH_QOS_shutdown, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, .of_match_table = DWC_ETH_QOS_plat_drv_match, + .pm = &DWC_ETH_QOS_pm_ops, }, }; @@ -2182,7 +2577,7 @@ static int DWC_ETH_QOS_init_module(void) { INT ret = 0; - DBGPR("-->DWC_ETH_QOS_init_module\n"); + EMACDBG("-->DWC_ETH_QOS_init_module\n"); ret = platform_driver_register(&DWC_ETH_QOS_plat_drv); if (ret < 0) { @@ -2190,11 +2585,17 @@ static int DWC_ETH_QOS_init_module(void) return ret; } + ipc_emac_log_ctxt = ipc_log_context_create(IPCLOG_STATE_PAGES,"emac", 0); + if (!ipc_emac_log_ctxt) + EMACERR("Error creating logging context for emac\n"); + else + EMACDBG("IPC logging has been enabled for emac\n"); + #ifdef DWC_ETH_QOS_CONFIG_DEBUGFS create_debug_files(); #endif - DBGPR("<--DWC_ETH_QOS_init_module\n"); + EMACDBG("<--DWC_ETH_QOS_init_module\n"); return ret; } @@ -2218,6 +2619,9 @@ static void __exit DWC_ETH_QOS_exit_module(void) platform_driver_unregister(&DWC_ETH_QOS_plat_drv); + if (ipc_emac_log_ctxt != NULL) + ipc_log_context_destroy(ipc_emac_log_ctxt); + DBGPR("<--DWC_ETH_QOS_exit_module\n"); } |