/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. */ /* * Copyright (c) 2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "alx.h" #include "alx_hwcom.h" #include #ifdef MDM_PLATFORM #include #include #endif char alx_drv_name[] = "alx"; static const char alx_drv_description[] = "Qualcomm Atheros(R) " "AR813x/AR815x/AR816x PCI-E Ethernet Network Driver"; // ProjE change u32 mac_addr_hi16=0xFFFFFFFFUL; module_param(mac_addr_hi16, uint, 0); MODULE_PARM_DESC(mac_addr_hi16,"Specify the high 16 bits (most significant) of the mac address"); u32 mac_addr_lo32=0xFFFFFFFFUL; module_param(mac_addr_lo32, uint, 0); MODULE_PARM_DESC(mac_addr_lo32,"Specify the low 32 bits of the mac address"); // END of ProjE change /* alx_pci_tbl - PCI Device ID Table * * Wildcard entries (PCI_ANY_ID) should come last * Last entry must be all 0s * * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, * Class, Class Mask, private data (not used) } */ #define ALX_ETHER_DEVICE(device_id) {\ PCI_DEVICE(ALX_VENDOR_ID, device_id)} static DEFINE_PCI_DEVICE_TABLE(alx_pci_tbl) = { ALX_ETHER_DEVICE(ALX_DEV_ID_AR8131), ALX_ETHER_DEVICE(ALX_DEV_ID_AR8132), ALX_ETHER_DEVICE(ALX_DEV_ID_AR8151_V1), ALX_ETHER_DEVICE(ALX_DEV_ID_AR8151_V2), ALX_ETHER_DEVICE(ALX_DEV_ID_AR8152_V1), ALX_ETHER_DEVICE(ALX_DEV_ID_AR8152_V2), ALX_ETHER_DEVICE(ALX_DEV_ID_AR8161), ALX_ETHER_DEVICE(ALX_DEV_ID_AR8162), {0,} }; MODULE_DEVICE_TABLE(pci, alx_pci_tbl); MODULE_AUTHOR("Qualcomm Corporation, "); MODULE_DESCRIPTION("Qualcomm Atheros Gigabit Ethernet Driver"); MODULE_LICENSE("Dual BSD/GPL"); static int alx_open_internal(struct alx_adapter *adpt, u32 ctrl); static void alx_stop_internal(struct alx_adapter *adpt, u32 ctrl); static void alx_init_ring_ptrs(struct alx_adapter *adpt); static int alx_ipa_rm_try_release(struct alx_adapter *adpt); static int alx_ipa_setup_rm(struct alx_adapter *adpt); #ifdef MDM_PLATFORM /* Global CTX PTR which can be used for debugging */ static struct alx_adapter *galx_adapter_ptr = NULL; static s8 debugfs_ipa_enable = 1; const mode_t read_write_mode = S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP; static inline char *alx_ipa_rm_state_to_str(enum alx_ipa_rm_state state) { switch (state) { case ALX_IPA_RM_RELEASED: return "RELEASED"; case ALX_IPA_RM_REQUESTED: return "REQUESTED"; case ALX_IPA_RM_GRANT_PENDING: return "GRANT PENDING"; case ALX_IPA_RM_GRANTED: return "GRANTED"; } return "UNKNOWN"; } #ifdef ALX_IPA_DEBUG /** * alx_dump_buff() - dumps buffer for debug purposes * @base: buffer base address * @phy_base: buffer physical base address * @size: size of the buffer */ static void alx_dump_buff(void *base, dma_addr_t phy_base, u32 size) { int i; u32 *cur = (u32 *)base; u8 *byt; pr_info("system phys addr=%pa len=%u\n", &phy_base, size); for (i = 0; i < size / 4; i++) { byt = (u8 *)(cur + i); pr_info("%2d %08x %02x %02x %02x %02x\n", i, *(cur + i), byt[0], byt[1], byt[2], byt[3]); } pr_info("END\n"); } #define ALX_DUMP_BUFF(base, phy_base, size) alx_dump_buff(base, phy_base, size) static bool alx_is_packet_dhcp(struct sk_buff *skb) { uint16_t sport = 0; uint16_t dport = 0; sport = be16_to_cpu((uint16_t)(*(uint16_t *)(skb->data + ALX_IP_OFFSET + ALX_IP_HEADER_SIZE))); dport = be16_to_cpu((uint16_t)(*(uint16_t *)(skb->data + ALX_IP_OFFSET + ALX_IP_HEADER_SIZE + sizeof(uint16_t)))); if (((sport == ALX_DHCP_SRV_PORT) && (dport == ALX_DHCP_CLI_PORT)) || ((dport == ALX_DHCP_SRV_PORT) && (sport == ALX_DHCP_CLI_PORT))) { return true; } return false; } #endif static int alx_ipa_rm_request(struct alx_adapter *adpt); #endif int alx_cfg_r16(const struct alx_hw *hw, int reg, u16 *pval) { if (!(hw && hw->adpt && hw->adpt->pdev)) return -EINVAL; return pci_read_config_word(hw->adpt->pdev, reg, pval); } int alx_cfg_w16(const struct alx_hw *hw, int reg, u16 val) { if (!(hw && hw->adpt && hw->adpt->pdev)) return -EINVAL; return pci_write_config_word(hw->adpt->pdev, reg, val); } void alx_mem_flush(const struct alx_hw *hw) { readl(hw->hw_addr); } void alx_mem_r32(const struct alx_hw *hw, int reg, u32 *val) { if (unlikely(!hw->link_up)) readl(hw->hw_addr + reg); *(u32 *)val = readl(hw->hw_addr + reg); } void alx_mem_w32(const struct alx_hw *hw, int reg, u32 val) { if (hw->mac_type == alx_mac_l2cb_v20 && reg < 0x1400) readl(hw->hw_addr + reg); writel(val, hw->hw_addr + reg); } static void alx_mem_r16(const struct alx_hw *hw, int reg, u16 *val) { if (unlikely(!hw->link_up)) readl(hw->hw_addr + reg); *(u16 *)val = readw(hw->hw_addr + reg); } static void alx_mem_w16(const struct alx_hw *hw, int reg, u16 val) { if (hw->mac_type == alx_mac_l2cb_v20 && reg < 0x1400) readl(hw->hw_addr + reg); writew(val, hw->hw_addr + reg); } void alx_mem_w8(const struct alx_hw *hw, int reg, u8 val) { if (hw->mac_type == alx_mac_l2cb_v20 && reg < 0x1400) readl(hw->hw_addr + reg); writeb(val, hw->hw_addr + reg); } /* * alx_hw_printk */ void alx_hw_printk(const char *level, const struct alx_hw *hw, const char *fmt, ...) { struct va_format vaf; va_list args; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; /* warning : __netdev_printk if no compat.o if (hw && hw->adpt && hw->adpt->netdev) __netdev_printk(level, hw->adpt->netdev, &vaf); else */ printk("%salx_hw: %pV", level, &vaf); va_end(args); } /* * alx_validate_mac_addr - Validate MAC address */ static int alx_validate_mac_addr(u8 *mac_addr) { int retval = 0; if (mac_addr[0] & 0x01) { printk(KERN_DEBUG "MAC address is multicast\n"); retval = -EADDRNOTAVAIL; } else if (mac_addr[0] == 0xff && mac_addr[1] == 0xff) { printk(KERN_DEBUG "MAC address is broadcast\n"); retval = -EADDRNOTAVAIL; } else if (mac_addr[0] == 0 && mac_addr[1] == 0 && mac_addr[2] == 0 && mac_addr[3] == 0 && mac_addr[4] == 0 && mac_addr[5] == 0) { printk(KERN_DEBUG "MAC address is all zeros\n"); retval = -EADDRNOTAVAIL; } return retval; } /* * alx_set_mac_type - Sets MAC type */ static int alx_set_mac_type(struct alx_adapter *adpt) { struct alx_hw *hw = &adpt->hw; int retval = 0; if (hw->pci_venid == ALX_VENDOR_ID) { switch (hw->pci_devid) { case ALX_DEV_ID_AR8131: hw->mac_type = alx_mac_l1c; break; case ALX_DEV_ID_AR8132: hw->mac_type = alx_mac_l2c; break; case ALX_DEV_ID_AR8151_V1: hw->mac_type = alx_mac_l1d_v1; break; case ALX_DEV_ID_AR8151_V2: /* just use l1d configure */ hw->mac_type = alx_mac_l1d_v2; break; case ALX_DEV_ID_AR8152_V1: hw->mac_type = alx_mac_l2cb_v1; break; case ALX_DEV_ID_AR8152_V2: if (hw->pci_revid == ALX_REV_ID_AR8152_V2_0) hw->mac_type = alx_mac_l2cb_v20; else hw->mac_type = alx_mac_l2cb_v21; break; case ALX_DEV_ID_AR8161: hw->mac_type = alx_mac_l1f; break; case ALX_DEV_ID_AR8162: hw->mac_type = alx_mac_l2f; break; default: retval = -EINVAL; break; } } else { retval = -EINVAL; } netif_dbg(adpt, hw, adpt->netdev, "found mac: %d, returns: %d\n", hw->mac_type, retval); return retval; } /* * alx_init_hw_callbacks */ static int alx_init_hw_callbacks(struct alx_adapter *adpt) { struct alx_hw *hw = &adpt->hw; int retval = 0; alx_set_mac_type(adpt); switch (hw->mac_type) { case alx_mac_l1f: case alx_mac_l2f: retval = alf_init_hw_callbacks(hw); break; case alx_mac_l1c: case alx_mac_l2c: case alx_mac_l2cb_v1: case alx_mac_l2cb_v20: case alx_mac_l2cb_v21: case alx_mac_l1d_v1: case alx_mac_l1d_v2: retval = alc_init_hw_callbacks(hw); break; default: retval = -EINVAL; break; } return retval; } void alx_reinit_locked(struct alx_adapter *adpt) { WARN_ON(in_interrupt()); while (CHK_ADPT_FLAG(1, STATE_RESETTING)) msleep(20); SET_ADPT_FLAG(1, STATE_RESETTING); alx_stop_internal(adpt, ALX_OPEN_CTRL_RESET_MAC); alx_open_internal(adpt, ALX_OPEN_CTRL_RESET_MAC); CLI_ADPT_FLAG(1, STATE_RESETTING); } static void alx_task_schedule(struct alx_adapter *adpt) { if (!CHK_ADPT_FLAG(1, STATE_DOWN) && !CHK_ADPT_FLAG(1, STATE_WATCH_DOG)) { SET_ADPT_FLAG(1, STATE_WATCH_DOG); schedule_work(&adpt->alx_task); } } static void alx_check_lsc(struct alx_adapter *adpt) { SET_ADPT_FLAG(0, TASK_LSC_REQ); adpt->link_jiffies = jiffies + ALX_TRY_LINK_TIMEOUT; if (!CHK_ADPT_FLAG(1, STATE_DOWN)) alx_task_schedule(adpt); } /* * alx_tx_timeout - Respond to a Tx Hang */ static void alx_tx_timeout(struct net_device *netdev) { struct alx_adapter *adpt = netdev_priv(netdev); /* Do the reset outside of interrupt context */ if (!CHK_ADPT_FLAG(1, STATE_DOWN)) { SET_ADPT_FLAG(0, TASK_REINIT_REQ); alx_task_schedule(adpt); } } /* * alx_set_multicase_list - Multicast and Promiscuous mode set */ static void alx_set_multicase_list(struct net_device *netdev) { struct alx_adapter *adpt = netdev_priv(netdev); struct alx_hw *hw = &adpt->hw; struct netdev_hw_addr *ha; /* Check for Promiscuous and All Multicast modes */ if (netdev->flags & IFF_PROMISC) { SET_HW_FLAG(PROMISC_EN); } else if (netdev->flags & IFF_ALLMULTI) { SET_HW_FLAG(MULTIALL_EN); CLI_HW_FLAG(PROMISC_EN); } else { CLI_HW_FLAG(MULTIALL_EN); CLI_HW_FLAG(PROMISC_EN); } hw->cbs.config_mac_ctrl(hw); /* clear the old settings from the multicast hash table */ hw->cbs.clear_mc_addr(hw); /* comoute mc addresses' hash value ,and put it into hash table */ netdev_for_each_mc_addr(ha, netdev) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) hw->cbs.set_mc_addr(hw, ha->addr); #else hw->cbs.set_mc_addr(hw, ha->dmi_addr); #endif } /* * alx_set_mac - Change the Ethernet Address of the NIC */ static int alx_set_mac_address(struct net_device *netdev, void *data) { struct alx_adapter *adpt = netdev_priv(netdev); struct alx_hw *hw = &adpt->hw; struct sockaddr *addr = data; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; if (netif_running(netdev)) return -EBUSY; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) if (netdev->addr_assign_type & NET_ADDR_RANDOM) netdev->addr_assign_type ^= NET_ADDR_RANDOM; #endif memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); memcpy(hw->mac_addr, addr->sa_data, netdev->addr_len); if (hw->cbs.set_mac_addr) hw->cbs.set_mac_addr(hw, hw->mac_addr); return 0; } /* * Read / Write Ptr Initialize: */ static void alx_init_ring_ptrs(struct alx_adapter *adpt) { int i, j; for (i = 0; i < adpt->num_txques; i++) { struct alx_tx_queue *txque = adpt->tx_queue[i]; struct alx_buffer *tpbuf = txque->tpq.tpbuff; txque->tpq.produce_idx = 0; txque->tpq.consume_idx = 0; for (j = 0; j < txque->tpq.count; j++) tpbuf[j].dma = 0; } for (i = 0; i < adpt->num_hw_rxques; i++) { struct alx_rx_queue *rxque = adpt->rx_queue[i]; struct alx_buffer *rfbuf = rxque->rfq.rfbuff; rxque->rrq.produce_idx = 0; rxque->rrq.consume_idx = 0; rxque->rfq.produce_idx = 0; rxque->rfq.consume_idx = 0; for (j = 0; j < rxque->rfq.count; j++) rfbuf[j].dma = 0; } if (CHK_ADPT_FLAG(0, SRSS_EN)) goto srrs_enable; return; srrs_enable: for (i = 0; i < adpt->num_sw_rxques; i++) { struct alx_rx_queue *rxque = adpt->rx_queue[i]; rxque->swq.produce_idx = 0; rxque->swq.consume_idx = 0; } } static void alx_config_rss(struct alx_adapter *adpt) { static const u8 key[40] = { 0xE2, 0x91, 0xD7, 0x3D, 0x18, 0x05, 0xEC, 0x6C, 0x2A, 0x94, 0xB3, 0x0D, 0xA5, 0x4F, 0x2B, 0xEC, 0xEA, 0x49, 0xAF, 0x7C, 0xE2, 0x14, 0xAD, 0x3D, 0xB8, 0x55, 0xAA, 0xBE, 0x6A, 0x3E, 0x67, 0xEA, 0x14, 0x36, 0x4D, 0x17, 0x3B, 0xED, 0x20, 0x0D}; struct alx_hw *hw = &adpt->hw; u32 reta = 0; int i, j; /* initialize rss hash type and idt table size */ hw->rss_hstype = ALX_RSS_HSTYP_ALL_EN; hw->rss_idt_size = 0x100; /* Fill out redirection table */ memcpy(hw->rss_key, key, sizeof(hw->rss_key)); /* Fill out redirection table */ memset(hw->rss_idt, 0x0, sizeof(hw->rss_idt)); for (i = 0, j = 0; i < 256; i++, j++) { if (j == adpt->max_rxques) j = 0; reta |= (j << ((i & 7) * 4)); if ((i & 7) == 7) { hw->rss_idt[i>>3] = reta; reta = 0; } } if (hw->cbs.config_rss) hw->cbs.config_rss(hw, CHK_ADPT_FLAG(0, SRSS_EN)); } #ifdef MDM_PLATFORM static bool alx_ipa_is_non_ip_pkt(uint8_t proto) { if ((proto == RRD_PROTO_NON_IP) || (proto == RRD_PROTO_LLDP)) return true; return false; } static void alx_ipa_remove_padding(struct sk_buff *skb, bool ipv4, bool ipv6) { if (ipv4) { struct iphdr *ip_hdr = NULL; ip_hdr = (struct iphdr *)(skb_mac_header(skb) + ETH_HLEN); skb_trim(skb, ntohs(ip_hdr->tot_len) + ETH_HLEN); } else if (ipv6) { struct ipv6hdr *ip6_hdr = NULL; ip6_hdr = (struct ipv6hdr *)(skb_mac_header(skb) + ETH_HLEN); skb_trim(skb, ntohs(ip6_hdr->payload_len) + sizeof(struct ipv6hdr) + ETH_HLEN); } } static bool alx_ipa_is_ip_frag_pkt(struct sk_buff *skb) { struct iphdr *ip_hdr = NULL; ip_hdr = (struct iphdr *)(skb_mac_header(skb) + ETH_HLEN); /* Return true if: 'More_Frag bit is set' OR 'Fragmentation Offset is set' */ if (ip_hdr->frag_off & htons(IP_MF | IP_OFFSET)) return true; return false; } static bool alx_ipa_is_ipv4_pkt(uint8_t proto) { if ((proto == RRD_PROTO_IPv4) || (proto == RRD_PROTO_IPv4_TCP) || (proto == RRD_PROTO_IPv4_UDP)) return true; else return false; } static bool alx_ipa_is_ipv6_pkt(uint8_t proto) { if ((proto == RRD_PROTO_IPv6) || (proto == RRD_PROTO_IPv6_TCP) || (proto == RRD_PROTO_IPv6_UDP)) return true; else return false; } #endif /* * alx_receive_skb */ static void alx_receive_skb(struct alx_adapter *adpt, struct sk_buff *skb, u16 vlan_tag, bool vlan_flag) { if (vlan_flag) { u16 vlan; ALX_TAG_TO_VLAN(vlan_tag, vlan); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)) __vlan_hwaccel_put_tag(skb, skb->vlan_proto, vlan); #else __vlan_hwaccel_put_tag(skb, vlan); #endif } netif_receive_skb(skb); } #ifdef MDM_PLATFORM /* * alx_receive_skb_ipa */ static void alx_receive_skb_ipa(struct alx_adapter *adpt, struct sk_buff *skb, u16 vlan_tag, bool vlan_flag, uint8_t proto) { struct ipa_tx_meta ipa_meta = {0x0}; int ret =0; struct alx_ipa_rx_desc_node *node = NULL; bool schedule_ipa_work = false; bool is_pkt_ipv4 = alx_ipa_is_ipv4_pkt(proto); bool is_pkt_ipv6 = alx_ipa_is_ipv6_pkt(proto); skb_reset_mac_header(skb); if (vlan_flag) { u16 vlan; ALX_TAG_TO_VLAN(vlan_tag, vlan); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)) __vlan_hwaccel_put_tag(skb, skb->vlan_proto, vlan); #else __vlan_hwaccel_put_tag(skb, vlan); #endif } /* If Packet is an IP Packet and non-fragmented then Send it to IPA/ODU Bridge Driver*/ if (alx_ipa_is_non_ip_pkt(proto) || (is_pkt_ipv4 && alx_ipa_is_ip_frag_pkt(skb)) || (!CHK_ADPT_FLAG(2, ODU_CONNECT))) { /* Send packet to network stack */ skb->protocol = eth_type_trans(skb, adpt->netdev); adpt->palx_ipa->stats.non_ip_frag_pkt++; /* Prevent device to goto suspend for 200 msec; to provide enough time for packet to be processed by network stack */ pm_wakeup_event(&adpt->pdev->dev, 200); netif_receive_skb(skb); return; } else { /* Send Packet to ODU bridge Driver */ spin_lock(&adpt->flow_ctrl_lock); /* Drop Packets if we cannot handle them */ if (adpt->freeq_cnt == 0) { adpt->palx_ipa->stats.flow_control_pkt_drop++; pr_debug("ALX-IPA Flow Control - Drop Pkt" " IPA pending packets = %d", adpt->ipa_free_desc_cnt); dev_kfree_skb(skb); /* Schedule ipa_work task if IPA has desc left. This is done since priority of softirq > IPA_WRITE_DONE Event. Which implies ipa_send_task will not be scheduled in a timely manner */ if ((adpt->ipa_free_desc_cnt > 0) && !CHK_ADPT_FLAG(2, WQ_SCHED)) schedule_ipa_work = true; goto unlock_and_schedule; } /* Remove extra padding if the rcv_pkt_len == 64 */ if (skb->len == ETH_ZLEN) alx_ipa_remove_padding(skb, is_pkt_ipv4, is_pkt_ipv6); /* Send Packet to IPA; if there are no pending packets and ipa has available descriptors */ if ((adpt->pendq_cnt == 0) && (adpt->ipa_free_desc_cnt > 0) && (alx_ipa_rm_request(adpt) == 0)) { ipa_meta.dma_address_valid = false; /* Send Packet to ODU bridge Driver */ ret = odu_bridge_tx_dp(skb, &ipa_meta); if (ret) { pr_err("odu_bridge_tx_dp() Failed!!" " ret %d--Free SKB\n", ret); kfree(skb); adpt->palx_ipa->stats.rx_ipa_send_fail++; } else { adpt->ipa_free_desc_cnt--; adpt->palx_ipa->stats.rx_ipa_send++; /* Increment the ipa_rx_completion Counter */ spin_lock(&alx_ipa->rm_ipa_lock); if (alx_ipa->acquire_wake_src == false) { __pm_stay_awake(&alx_ipa->rm_ipa_wait); alx_ipa->acquire_wake_src = true; } alx_ipa->ipa_rx_completion++; spin_unlock(&alx_ipa->rm_ipa_lock); } } else if (adpt->pendq_cnt <= ALX_IPA_SYS_PIPE_DNE_PKTS) { /* If we have pending packets to send, add the current packet to the end of the queue. */ node = list_first_entry(&adpt->free_queue_head, struct alx_ipa_rx_desc_node, link); list_del(&node->link); node->skb_ptr = skb; list_add_tail(&node->link, &adpt->pend_queue_head); adpt->freeq_cnt--; adpt->pendq_cnt++; if (!CHK_ADPT_FLAG(2, WQ_SCHED)) schedule_ipa_work = true; } unlock_and_schedule: spin_unlock(&adpt->flow_ctrl_lock); if (schedule_ipa_work) { SET_ADPT_FLAG(2, WQ_SCHED); schedule_work(&adpt->ipa_send_task); } } } #endif static bool alx_get_rrdesc(struct alx_adapter *adpt, struct alx_rx_queue *rxque, union alx_sw_rrdesc *srrd) { u32 cnt = 0; union alx_hw_rrdesc *hrrd = ALX_RRD(rxque, rxque->rrq.consume_idx); srrd->dfmt.dw0 = le32_to_cpu(hrrd->dfmt.dw0); srrd->dfmt.dw1 = le32_to_cpu(hrrd->dfmt.dw1); srrd->dfmt.dw2 = le32_to_cpu(hrrd->dfmt.dw2); srrd->dfmt.dw3 = le32_to_cpu(hrrd->dfmt.dw3); if (!srrd->genr.update) return false; /* workaround for the PCIe DMA write issue */ /* Please make sure hrrd->dmt.dw0 is set to 0 after handling, please refer to the later line in the same function alx_get_rrdesc: hrrd->dfmt.dw0 = 0;*/ if (srrd->dfmt.dw0 == 0) { volatile u32 *flag = (volatile u32 *)&hrrd->dfmt.dw0; while (*flag == 0) { /* while the dword0 of the rrd is still 0 which means it's not updated yet, read it again */ if (++cnt >= 10) { /* 10 more times should be enough, actually should NOT get here */ alx_err(adpt, "ERROR, RRD update timeout\n"); return false; } } /* read the rrd descriptor from hardware again after all four dwords of the rrd are synced by DMA engine */ srrd->dfmt.dw0 = le32_to_cpu(hrrd->dfmt.dw0); srrd->dfmt.dw1 = le32_to_cpu(hrrd->dfmt.dw1); srrd->dfmt.dw2 = le32_to_cpu(hrrd->dfmt.dw2); srrd->dfmt.dw3 = le32_to_cpu(hrrd->dfmt.dw3); } if (unlikely(srrd->genr.nor != 1)) { /* TODO support mul rfd*/ printk(KERN_EMERG "Multi rfd not support yet!\n"); alx_err(adpt, "Please make sure PCIe DMA Write workaroud has been applied\n"); alx_err(adpt, "SRRD 0/1/2/3:0x%08x/0x%08x/0x%08x/0x%08x\n", srrd->dfmt.dw0, srrd->dfmt.dw1, srrd->dfmt.dw2, srrd->dfmt.dw3); alx_err(adpt, "HRRD 0/1/2/3:0x%08x/0x%08x/0x%08x/0x%08x\n", hrrd->dfmt.dw0, hrrd->dfmt.dw1, hrrd->dfmt.dw2, hrrd->dfmt.dw3); } srrd->genr.update = 0; hrrd->dfmt.dw3 = cpu_to_le32(srrd->dfmt.dw3); /* clear the update bit in hardware */ /* Workaround for the PCIe DMA write issue */ /* Note: for intensive verification to set it 0 */ hrrd->dfmt.dw0 = 0; if (++rxque->rrq.consume_idx == rxque->rrq.count) rxque->rrq.consume_idx = 0; return true; } static bool alx_set_rfdesc(struct alx_rx_queue *rxque, union alx_sw_rfdesc *srfd) { union alx_hw_rfdesc *hrfd = ALX_RFD(rxque, rxque->rfq.produce_idx); hrfd->qfmt.qw0 = cpu_to_le64(srfd->qfmt.qw0); if (++rxque->rfq.produce_idx == rxque->rfq.count) rxque->rfq.produce_idx = 0; return true; } static bool alx_set_tpdesc(struct alx_tx_queue *txque, union alx_sw_tpdesc *stpd) { union alx_hw_tpdesc *htpd; txque->tpq.last_produce_idx = txque->tpq.produce_idx; htpd = ALX_TPD(txque, txque->tpq.produce_idx); if (++txque->tpq.produce_idx == txque->tpq.count) txque->tpq.produce_idx = 0; htpd->dfmt.dw0 = cpu_to_le32(stpd->dfmt.dw0); htpd->dfmt.dw1 = cpu_to_le32(stpd->dfmt.dw1); htpd->qfmt.qw1 = cpu_to_le64(stpd->qfmt.qw1); return true; } static void alx_set_tpdesc_lastfrag(struct alx_tx_queue *txque) { union alx_hw_tpdesc *htpd; #define ALX_TPD_LAST_FLAGMENT 0x80000000 htpd = ALX_TPD(txque, txque->tpq.last_produce_idx); htpd->dfmt.dw1 |= cpu_to_le32(ALX_TPD_LAST_FLAGMENT); } static int alx_refresh_rx_buffer(struct alx_rx_queue *rxque) { struct alx_adapter *adpt = netdev_priv(rxque->netdev); struct alx_hw *hw = &adpt->hw; struct alx_buffer *curr_rxbuf; struct alx_buffer *next_rxbuf; union alx_sw_rfdesc srfd; struct sk_buff *skb; void *skb_data = NULL; u16 count = 0; u16 next_produce_idx; next_produce_idx = rxque->rfq.produce_idx; if (++next_produce_idx == rxque->rfq.count) next_produce_idx = 0; curr_rxbuf = GET_RF_BUFFER(rxque, rxque->rfq.produce_idx); next_rxbuf = GET_RF_BUFFER(rxque, next_produce_idx); /* this always has a blank rx_buffer*/ while (next_rxbuf->dma == 0) { skb = dev_alloc_skb(adpt->rxbuf_size); if (unlikely(!skb)) { alx_err(adpt, "alloc rx buffer failed\n"); break; } /* * Make buffer alignment 2 beyond a 16 byte boundary * this will result in a 16 byte aligned IP header after * the 14 byte MAC header is removed */ skb_data = skb->data; /*skb_reserve(skb, NET_IP_ALIGN);*/ curr_rxbuf->skb = skb; curr_rxbuf->length = adpt->rxbuf_size; curr_rxbuf->dma = dma_map_single(rxque->dev, skb_data, curr_rxbuf->length, DMA_FROM_DEVICE); srfd.genr.addr = curr_rxbuf->dma; alx_set_rfdesc(rxque, &srfd); /*alx_err(adpt, "rx-buffer-addr=%llx\n", (u64)curr_rxbuf->dma);*/ next_produce_idx = rxque->rfq.produce_idx; if (++next_produce_idx == rxque->rfq.count) next_produce_idx = 0; curr_rxbuf = GET_RF_BUFFER(rxque, rxque->rfq.produce_idx); next_rxbuf = GET_RF_BUFFER(rxque, next_produce_idx); count++; } if (count) { wmb(); alx_mem_w16(hw, rxque->produce_reg, rxque->rfq.produce_idx); netif_dbg(adpt, rx_err, adpt->netdev, "RX[%d]: prod_reg[%x] = 0x%x, rfq.prod_idx = 0x%x\n", rxque->que_idx, rxque->produce_reg, rxque->rfq.produce_idx, rxque->rfq.produce_idx); } return count; } static void alx_clean_rfdesc(struct alx_rx_queue *rxque, union alx_sw_rrdesc *srrd) { struct alx_buffer *rfbuf = rxque->rfq.rfbuff; u32 consume_idx = srrd->genr.si; u32 i; for (i = 0; i < srrd->genr.nor; i++) { rfbuf[consume_idx].skb = NULL; if (++consume_idx == rxque->rfq.count) consume_idx = 0; } rxque->rfq.consume_idx = consume_idx; } #if 0 #define ROLL_BK_NUM 16 static void alx_dump_rrd(struct alx_adapter *adpt, struct alx_rx_queue *rxque) { union alx_sw_rrdesc srrd; union alx_hw_rrdesc *hrrd; u16 begin, end; alx_err(adpt, "PATCH v5, dumpping RRD .... consumer idx=%x\n", rxque->rrq.consume_idx); begin = (rxque->rrq.consume_idx - ROLL_BK_NUM) % rxque->rrq.count; end = (rxque->rrq.consume_idx + ROLL_BK_NUM) % rxque->rrq.count; while (begin != end) { hrrd = ALX_RRD(rxque, begin); srrd.dfmt.dw0 = le32_to_cpu(hrrd->dfmt.dw0); srrd.dfmt.dw1 = le32_to_cpu(hrrd->dfmt.dw1); srrd.dfmt.dw2 = le32_to_cpu(hrrd->dfmt.dw2); srrd.dfmt.dw3 = le32_to_cpu(hrrd->dfmt.dw3); alx_err(adpt, "Index:%x\n", begin); alx_err(adpt, "rrd->word0/1/2/3:0x%08x/0x%08x/0x%08x/0x%08x\n", srrd.dfmt.dw0, srrd.dfmt.dw1, srrd.dfmt.dw2, srrd.dfmt.dw3); if (++begin == rxque->rrq.count) begin = 0; } } static void alx_dump_rfd(struct alx_adapter *adpt, struct alx_rx_queue *rxque, u16 idx) { struct alx_buffer *rfbuf; u16 begin, end; alx_err(adpt, "RFD:%x\n", idx); //begin = (idx - 3) % rxque->rfq.count; //end = (idx + 3) % rxque->rfq.count; begin = 0; end = rxque->rfq.count - 1; while (begin != end) { rfbuf = GET_RF_BUFFER(rxque, begin); alx_err(adpt, "IDX(%x): addr=0x%llx\n", begin, (u64)rfbuf->dma); if (++begin == rxque->rfq.count) begin = 0; } } static void alx_dump_register(struct alx_adapter *adpt) { struct alx_hw *hw = &adpt->hw; u16 reg, count; u32 val; for (reg = 0x40, count = 0; count < 0x20; count++, reg++) { val = alx_read_dbg_reg(hw, reg); alx_err(adpt, "DBG-reg(%x)=%08X\n", reg, val); } } #endif u32 alx_read_dbg_reg(struct alx_hw *hw, u16 reg) { u32 val; alx_mem_w32(hw, 0x1900, reg); alx_mem_r32(hw, 0x1904, &val); return val; } static bool alx_dispatch_rx_irq(struct alx_msix_param *msix, struct alx_rx_queue *rxque) { struct alx_adapter *adpt = msix->adpt; struct pci_dev *pdev = adpt->pdev; struct net_device *netdev = adpt->netdev; union alx_sw_rrdesc srrd; struct alx_buffer *rfbuf; struct sk_buff *skb; struct alx_rx_queue *swque; struct alx_sw_buffer *curr_swbuf; struct alx_sw_buffer *next_swbuf; u16 next_produce_idx; u16 count = 0; while (1) { if (!alx_get_rrdesc(adpt, rxque, &srrd)) break; if (srrd.genr.res || srrd.genr.lene) { alx_clean_rfdesc(rxque, &srrd); netif_warn(adpt, rx_err, adpt->netdev, "wrong packet! rrd->word3 is 0x%08x\n", srrd.dfmt.dw3); continue; } /* Good Receive */ if (likely(srrd.genr.nor == 1)) { rfbuf = GET_RF_BUFFER(rxque, srrd.genr.si); pci_unmap_single(pdev, rfbuf->dma, rfbuf->length, DMA_FROM_DEVICE); rfbuf->dma = 0; skb = rfbuf->skb; netif_dbg(adpt, rx_err, adpt->netdev, "skb addr = %p, rxbuf_len = %x\n", skb->data, rfbuf->length); } else { /* TODO */ alx_err(adpt, "alx_dispatch_rx_irq: Multi rfd not support yet!\n"); break; } alx_clean_rfdesc(rxque, &srrd); skb_put(skb, srrd.genr.pkt_len - ETH_FCS_LEN); skb->protocol = eth_type_trans(skb, netdev); skb_checksum_none_assert(skb); /* start to dispatch */ swque = adpt->rx_queue[srrd.genr.rss_cpu]; next_produce_idx = swque->swq.produce_idx; if (++next_produce_idx == swque->swq.count) next_produce_idx = 0; curr_swbuf = GET_SW_BUFFER(swque, swque->swq.produce_idx); next_swbuf = GET_SW_BUFFER(swque, next_produce_idx); /* * if full, will discard the packet, * and at lease has a blank sw_buffer. */ if (!next_swbuf->skb) { curr_swbuf->skb = skb; curr_swbuf->vlan_tag = srrd.genr.vlan_tag; curr_swbuf->vlan_flag = srrd.genr.vlan_flag; if (++swque->swq.produce_idx == swque->swq.count) swque->swq.produce_idx = 0; } count++; if (count == 32) break; } if (count) alx_refresh_rx_buffer(rxque); return true; } static bool alx_handle_srx_irq(struct alx_msix_param *msix, struct alx_rx_queue *rxque, int *num_pkts, int max_pkts) { struct alx_adapter *adpt = msix->adpt; struct alx_sw_buffer *swbuf; bool retval = true; while (rxque->swq.consume_idx != rxque->swq.produce_idx) { swbuf = GET_SW_BUFFER(rxque, rxque->swq.consume_idx); alx_receive_skb(adpt, swbuf->skb, (u16)swbuf->vlan_tag, (bool)swbuf->vlan_flag); swbuf->skb = NULL; if (++rxque->swq.consume_idx == rxque->swq.count) rxque->swq.consume_idx = 0; (*num_pkts)++; if (*num_pkts >= max_pkts) { retval = false; break; } } return retval; } static bool alx_handle_rx_irq(struct alx_msix_param *msix, struct alx_rx_queue *rxque, int *num_pkts, int max_pkts) { struct alx_adapter *adpt = msix->adpt; struct alx_hw *hw = &adpt->hw; struct pci_dev *pdev = adpt->pdev; union alx_sw_rrdesc srrd; struct alx_buffer *rfbuf; struct sk_buff *skb; u16 hw_consume_idx, num_consume_pkts; u16 count = 0; alx_mem_r16(hw, rxque->consume_reg, &hw_consume_idx); num_consume_pkts = (hw_consume_idx > rxque->rrq.consume_idx) ? (hw_consume_idx - rxque->rrq.consume_idx) : (hw_consume_idx + rxque->rrq.count - rxque->rrq.consume_idx); while (1) { if (!num_consume_pkts) break; if (!alx_get_rrdesc(adpt, rxque, &srrd)) break; if (srrd.genr.res || srrd.genr.lene) { alx_clean_rfdesc(rxque, &srrd); netif_warn(adpt, rx_err, adpt->netdev, "wrong packet! rrd->word3 is 0x%08x\n", srrd.dfmt.dw3); continue; } /* TODO: Good Receive */ if (likely(srrd.genr.nor == 1)) { rfbuf = GET_RF_BUFFER(rxque, srrd.genr.si); pci_unmap_single(pdev, rfbuf->dma, rfbuf->length, DMA_FROM_DEVICE); rfbuf->dma = 0; skb = rfbuf->skb; } else { /* TODO */ alx_err(adpt, "alx_hande_rx_irq: Multi rfd not support yet!\n"); break; } alx_clean_rfdesc(rxque, &srrd); skb_put(skb, srrd.genr.pkt_len - ETH_FCS_LEN); #ifndef MDM_PLATFORM skb->protocol = eth_type_trans(skb, adpt->netdev); #endif skb_checksum_none_assert(skb); #ifdef MDM_PLATFORM alx_receive_skb_ipa(adpt, skb, (u16)srrd.genr.vlan_tag, (bool)srrd.genr.vlan_flag, (uint8_t)srrd.genr.proto); #else alx_receive_skb(adpt, skb, (u16)srrd.genr.vlan_tag, (bool)srrd.genr.vlan_flag); #endif num_consume_pkts--; count++; (*num_pkts)++; if (*num_pkts >= max_pkts) break; } if (count) alx_refresh_rx_buffer(rxque); return true; } static bool alx_handle_tx_irq(struct alx_msix_param *msix, struct alx_tx_queue *txque) { struct alx_adapter *adpt = msix->adpt; struct alx_hw *hw = &adpt->hw; struct alx_buffer *tpbuf; u16 consume_data; #ifdef MDM_PLATFORM struct alx_ipa_ctx *alx_ipa = adpt->palx_ipa; int num_tx_comp = 0; #endif alx_mem_r16(hw, txque->consume_reg, &consume_data); netif_dbg(adpt, tx_err, adpt->netdev, "TX[%d]: consume_reg[0x%x] = 0x%x, tpq.consume_idx = 0x%x\n", txque->que_idx, txque->consume_reg, consume_data, txque->tpq.consume_idx); while (txque->tpq.consume_idx != consume_data) { tpbuf = GET_TP_BUFFER(txque, txque->tpq.consume_idx); if (tpbuf->dma) { pci_unmap_page(adpt->pdev, tpbuf->dma, tpbuf->length, DMA_TO_DEVICE); tpbuf->dma = 0; } if (tpbuf->skb) { /* Since its called by NAPI; we are already in bh context; so its safe to free the skb here*/ dev_kfree_skb(tpbuf->skb); tpbuf->skb = NULL; } if (++txque->tpq.consume_idx == txque->tpq.count) txque->tpq.consume_idx = 0; #ifdef MDM_PLATFORM /* Update TX Completetion Recieved */ num_tx_comp++; #endif } #ifdef MDM_PLATFORM /* Release Wakelock if all TX Completion is done */ spin_lock_bh(&alx_ipa->rm_ipa_lock); alx_ipa->alx_tx_completion -= num_tx_comp; if (!alx_ipa->ipa_rx_completion && !alx_ipa->alx_tx_completion && (alx_ipa->acquire_wake_src == true)) { __pm_relax(&alx_ipa->rm_ipa_wait); alx_ipa->acquire_wake_src = false; } spin_unlock_bh(&alx_ipa->rm_ipa_lock); #endif if (netif_queue_stopped(adpt->netdev) && netif_carrier_ok(adpt->netdev)) { netif_wake_queue(adpt->netdev); } return true; } static irqreturn_t alx_msix_timer(int irq, void *data) { struct alx_msix_param *msix = data; struct alx_adapter *adpt = msix->adpt; struct alx_hw *hw = &adpt->hw; u32 isr; hw->cbs.disable_msix_intr(hw, msix->vec_idx); alx_mem_r32(hw, ALX_ISR, &isr); isr = isr & (ALX_ISR_TIMER | ALX_ISR_MANU); if (isr == 0) { hw->cbs.enable_msix_intr(hw, msix->vec_idx); return IRQ_NONE; } /* Ack ISR */ alx_mem_w32(hw, ALX_ISR, isr); if (isr & ALX_ISR_MANU) { adpt->net_stats.tx_carrier_errors++; alx_check_lsc(adpt); } hw->cbs.enable_msix_intr(hw, msix->vec_idx); return IRQ_HANDLED; } static irqreturn_t alx_msix_alert(int irq, void *data) { struct alx_msix_param *msix = data; struct alx_adapter *adpt = msix->adpt; struct alx_hw *hw = &adpt->hw; u32 isr; hw->cbs.disable_msix_intr(hw, msix->vec_idx); alx_mem_r32(hw, ALX_ISR, &isr); isr = isr & ALX_ISR_ALERT_MASK; if (isr == 0) { hw->cbs.enable_msix_intr(hw, msix->vec_idx); return IRQ_NONE; } alx_mem_w32(hw, ALX_ISR, isr); hw->cbs.enable_msix_intr(hw, msix->vec_idx); return IRQ_HANDLED; } static irqreturn_t alx_msix_smb(int irq, void *data) { struct alx_msix_param *msix = data; struct alx_adapter *adpt = msix->adpt; struct alx_hw *hw = &adpt->hw; hw->cbs.disable_msix_intr(hw, msix->vec_idx); hw->cbs.enable_msix_intr(hw, msix->vec_idx); return IRQ_HANDLED; } static irqreturn_t alx_msix_phy(int irq, void *data) { struct alx_msix_param *msix = data; struct alx_adapter *adpt = msix->adpt; struct alx_hw *hw = &adpt->hw; hw->cbs.disable_msix_intr(hw, msix->vec_idx); if (hw->cbs.ack_phy_intr) hw->cbs.ack_phy_intr(hw); adpt->net_stats.tx_carrier_errors++; alx_check_lsc(adpt); hw->cbs.enable_msix_intr(hw, msix->vec_idx); return IRQ_HANDLED; } /* * alx_msix_rtx */ static irqreturn_t alx_msix_rtx(int irq, void *data) { struct alx_msix_param *msix = data; struct alx_adapter *adpt = msix->adpt; struct alx_hw *hw = &adpt->hw; netif_dbg(adpt, intr, adpt->netdev, "msix vec_idx = %d\n", msix->vec_idx); hw->cbs.disable_msix_intr(hw, msix->vec_idx); if (!msix->rx_count && !msix->tx_count) { hw->cbs.enable_msix_intr(hw, msix->vec_idx); return IRQ_HANDLED; } napi_schedule(&msix->napi); return IRQ_HANDLED; } /* * alx_napi_msix_rtx */ static int alx_napi_msix_rtx(struct napi_struct *napi, int max_pkts) { struct alx_msix_param *msix = container_of(napi, struct alx_msix_param, napi); struct alx_adapter *adpt = msix->adpt; struct alx_hw *hw = &adpt->hw; struct alx_rx_queue *rxque; struct alx_rx_queue *swque; struct alx_tx_queue *txque; unsigned long flags = 0; bool complete = true; int num_pkts = 0; int rque_idx, tque_idx; int i, j; netif_dbg(adpt, intr, adpt->netdev, "NAPI: msix vec_idx = %d\n", msix->vec_idx); pr_info("NAPI: msix vec_idx = %d\n", msix->vec_idx); /* RX */ for (i = 0; i < msix->rx_count; i++) { rque_idx = msix->rx_map[i]; num_pkts = 0; if (CHK_ADPT_FLAG(0, SRSS_EN)) { if (!spin_trylock_irqsave(&adpt->rx_lock, flags)) goto clean_sw_irq; for (j = 0; j < adpt->num_hw_rxques; j++) alx_dispatch_rx_irq(msix, adpt->rx_queue[j]); spin_unlock_irqrestore(&adpt->rx_lock, flags); clean_sw_irq: swque = adpt->rx_queue[rque_idx]; complete &= alx_handle_srx_irq(msix, swque, &num_pkts, max_pkts); } else { rxque = adpt->rx_queue[rque_idx]; complete &= alx_handle_rx_irq(msix, rxque, &num_pkts, max_pkts); } } /* Handle TX */ for (i = 0; i < msix->tx_count; i++) { tque_idx = msix->tx_map[i]; txque = adpt->tx_queue[tque_idx]; complete &= alx_handle_tx_irq(msix, txque); } if (!complete) { netif_dbg(adpt, intr, adpt->netdev, "Some packets in the queue are not handled!\n"); num_pkts = max_pkts; } netif_dbg(adpt, intr, adpt->netdev, "num_pkts = %d, max_pkts = %d\n", num_pkts, max_pkts); /* If all work done, exit the polling mode */ if (num_pkts < max_pkts) { napi_complete(napi); if (!CHK_ADPT_FLAG(1, STATE_DOWN)) hw->cbs.enable_msix_intr(hw, msix->vec_idx); } return num_pkts; } /* * alx_napi_legacy_rtx - NAPI Rx polling callback */ static int alx_napi_legacy_rtx(struct napi_struct *napi, int max_pkts) { struct alx_msix_param *msix = container_of(napi, struct alx_msix_param, napi); struct alx_adapter *adpt = msix->adpt; struct alx_hw *hw = &adpt->hw; int complete = true; int num_pkts = 0; int que_idx; netif_dbg(adpt, intr, adpt->netdev, "NAPI: msix vec_idx = %d\n", msix->vec_idx); /* Keep link state information with original netdev */ if (!netif_carrier_ok(adpt->netdev)) goto enable_rtx_irq; for (que_idx = 0; que_idx < adpt->num_txques; que_idx++) complete &= alx_handle_tx_irq(msix, adpt->tx_queue[que_idx]); for (que_idx = 0; que_idx < adpt->num_hw_rxques; que_idx++) { num_pkts = 0; complete &= alx_handle_rx_irq(msix, adpt->rx_queue[que_idx], &num_pkts, max_pkts); } if (!complete) num_pkts = max_pkts; if (num_pkts < max_pkts) { enable_rtx_irq: napi_complete(napi); hw->intr_mask |= (ALX_ISR_RXQ | ALX_ISR_TXQ); alx_mem_w32(hw, ALX_IMR, hw->intr_mask); } return num_pkts; } static void alx_set_msix_flags(struct alx_msix_param *msix, enum alx_msix_type type, int index) { if (type == alx_msix_type_rx) { switch (index) { case 0: SET_MSIX_FLAG(RX0); break; case 1: SET_MSIX_FLAG(RX1); break; case 2: SET_MSIX_FLAG(RX2); break; case 3: SET_MSIX_FLAG(RX3); break; case 4: SET_MSIX_FLAG(RX4); break; case 5: SET_MSIX_FLAG(RX5); break; case 6: SET_MSIX_FLAG(RX6); break; case 7: SET_MSIX_FLAG(RX7); break; default: printk(KERN_ERR "alx_set_msix_flags: rx error."); break; } } else if (type == alx_msix_type_tx) { switch (index) { case 0: SET_MSIX_FLAG(TX0); break; case 1: SET_MSIX_FLAG(TX1); break; case 2: SET_MSIX_FLAG(TX2); break; case 3: SET_MSIX_FLAG(TX3); break; default: printk(KERN_ERR "alx_set_msix_flags: tx error."); break; } } else if (type == alx_msix_type_other) { switch (index) { case ALX_MSIX_TYPE_OTH_TIMER: SET_MSIX_FLAG(TIMER); break; case ALX_MSIX_TYPE_OTH_ALERT: SET_MSIX_FLAG(ALERT); break; case ALX_MSIX_TYPE_OTH_SMB: SET_MSIX_FLAG(SMB); break; case ALX_MSIX_TYPE_OTH_PHY: SET_MSIX_FLAG(PHY); break; default: printk(KERN_ERR "alx_set_msix_flags: other error."); break; } } } /* alx_setup_msix_maps */ static int alx_setup_msix_maps(struct alx_adapter *adpt) { int msix_idx = 0; int que_idx = 0; int num_rxques = adpt->num_rxques; int num_txques = adpt->num_txques; int num_msix_rxques = adpt->num_msix_rxques; int num_msix_txques = adpt->num_msix_txques; int num_msix_noques = adpt->num_msix_noques; if (CHK_ADPT_FLAG(0, FIXED_MSIX)) goto fixed_msix_map; netif_warn(adpt, ifup, adpt->netdev, "don't support non-fixed msix map\n"); return -EINVAL; fixed_msix_map: /* * For RX queue msix map */ msix_idx = 0; for (que_idx = 0; que_idx < num_msix_rxques; que_idx++, msix_idx++) { struct alx_msix_param *msix = adpt->msix[msix_idx]; if (que_idx < num_rxques) { adpt->rx_queue[que_idx]->msix = msix; msix->rx_map[msix->rx_count] = que_idx; msix->rx_count++; alx_set_msix_flags(msix, alx_msix_type_rx, que_idx); } } if (msix_idx != num_msix_rxques) netif_warn(adpt, ifup, adpt->netdev, "msix_idx is wrong\n"); /* * For TX queue msix map */ for (que_idx = 0; que_idx < num_msix_txques; que_idx++, msix_idx++) { struct alx_msix_param *msix = adpt->msix[msix_idx]; if (que_idx < num_txques) { adpt->tx_queue[que_idx]->msix = msix; msix->tx_map[msix->tx_count] = que_idx; msix->tx_count++; alx_set_msix_flags(msix, alx_msix_type_tx, que_idx); } } if (msix_idx != (num_msix_rxques + num_msix_txques)) netif_warn(adpt, ifup, adpt->netdev, "msix_idx is wrong\n"); /* * For NON queue msix map */ for (que_idx = 0; que_idx < num_msix_noques; que_idx++, msix_idx++) { struct alx_msix_param *msix = adpt->msix[msix_idx]; alx_set_msix_flags(msix, alx_msix_type_other, que_idx); } return 0; } static inline void alx_reset_msix_maps(struct alx_adapter *adpt) { int que_idx, msix_idx; for (que_idx = 0; que_idx < adpt->num_rxques; que_idx++) adpt->rx_queue[que_idx]->msix = NULL; for (que_idx = 0; que_idx < adpt->num_txques; que_idx++) adpt->tx_queue[que_idx]->msix = NULL; for (msix_idx = 0; msix_idx < adpt->num_msix_intrs; msix_idx++) { struct alx_msix_param *msix = adpt->msix[msix_idx]; memset(msix->rx_map, 0, sizeof(msix->rx_map)); memset(msix->tx_map, 0, sizeof(msix->tx_map)); msix->rx_count = 0; msix->tx_count = 0; CLI_MSIX_FLAG(ALL); } } /* * alx_enable_intr - Enable default interrupt generation settings */ static inline void alx_enable_intr(struct alx_adapter *adpt) { struct alx_hw *hw = &adpt->hw; int i; if (!atomic_dec_and_test(&adpt->irq_sem)) return; if (hw->cbs.enable_legacy_intr) hw->cbs.enable_legacy_intr(hw); /* enable all MSIX IRQs */ for (i = 0; i < adpt->num_msix_intrs; i++) { if (hw->cbs.disable_msix_intr) hw->cbs.disable_msix_intr(hw, i); if (hw->cbs.enable_msix_intr) hw->cbs.enable_msix_intr(hw, i); } } /* alx_disable_intr - Mask off interrupt generation on the NIC */ static inline void alx_disable_intr(struct alx_adapter *adpt) { struct alx_hw *hw = &adpt->hw; atomic_inc(&adpt->irq_sem); if (hw->cbs.disable_legacy_intr) hw->cbs.disable_legacy_intr(hw); if (CHK_ADPT_FLAG(0, MSIX_EN)) { int i; for (i = 0; i < adpt->num_msix_intrs; i++) { synchronize_irq(adpt->msix_entries[i].vector); hw->cbs.disable_msix_intr(hw, i); } } else { synchronize_irq(adpt->pdev->irq); } } /* * alx_interrupt - Interrupt Handler */ static irqreturn_t alx_interrupt(int irq, void *data) { struct net_device *netdev = data; struct alx_adapter *adpt = netdev_priv(netdev); struct alx_hw *hw = &adpt->hw; struct alx_msix_param *msix = adpt->msix[0]; int max_intrs = ALX_MAX_HANDLED_INTRS; u32 isr, status; do { alx_mem_r32(hw, ALX_ISR, &isr); status = isr & hw->intr_mask; if (status == 0) { alx_mem_w32(hw, ALX_ISR, 0); if (max_intrs != ALX_MAX_HANDLED_INTRS) return IRQ_HANDLED; return IRQ_NONE; } /* GPHY_INT is received when cabled is plugged in */ /* Connect to ODU Bridge Driver */ /* ack ISR to PHY register */ if (status & ALX_ISR_PHY) { hw->cbs.ack_phy_intr(hw); } /* ack ISR to MAC register */ alx_mem_w32(hw, ALX_ISR, status | ALX_ISR_DIS); if (status & ALX_ISR_ERROR) { netif_warn(adpt, intr, adpt->netdev, "isr error (status = 0x%x)\n", status & ALX_ISR_ERROR); if (status & ALX_ISR_PCIE_FERR) { alx_mem_w16(hw, ALX_DEV_STAT, ALX_DEV_STAT_FERR | ALX_DEV_STAT_NFERR | ALX_DEV_STAT_CERR); } /* reset MAC */ SET_ADPT_FLAG(0, TASK_REINIT_REQ); alx_task_schedule(adpt); return IRQ_HANDLED; } if (status & (ALX_ISR_RXQ | ALX_ISR_TXQ)) { if (napi_schedule_prep(&(msix->napi))) { hw->intr_mask &= ~(ALX_ISR_RXQ | ALX_ISR_TXQ); alx_mem_w32(hw, ALX_IMR, hw->intr_mask); __napi_schedule(&(msix->napi)); } } if (status & ALX_ISR_OVER) { netif_warn(adpt, intr, adpt->netdev, "TX/RX overflow (status = 0x%x)\n", status & ALX_ISR_OVER); } /* link event */ if (status & (ALX_ISR_PHY | ALX_ISR_MANU)) { netdev->stats.tx_carrier_errors++; alx_check_lsc(adpt); break; } } while (--max_intrs > 0); /* re-enable Interrupt*/ alx_mem_w32(hw, ALX_ISR, 0); return IRQ_HANDLED; } /* * alx_request_msix_irqs - Initialize MSI-X interrupts */ static int alx_request_msix_irq(struct alx_adapter *adpt) { struct net_device *netdev = adpt->netdev; irqreturn_t (*handler)(int, void *); int msix_idx; int num_msix_intrs = adpt->num_msix_intrs; int rx_idx = 0, tx_idx = 0; int i; int retval; retval = alx_setup_msix_maps(adpt); if (retval) return retval; for (msix_idx = 0; msix_idx < num_msix_intrs; msix_idx++) { struct alx_msix_param *msix = adpt->msix[msix_idx]; if (CHK_MSIX_FLAG(RXS) && CHK_MSIX_FLAG(TXS)) { handler = alx_msix_rtx; snprintf(msix->name, sizeof(msix->name), "%s:%s%d", netdev->name, "rtx", rx_idx); rx_idx++; tx_idx++; } else if (CHK_MSIX_FLAG(RXS)) { handler = alx_msix_rtx; snprintf(msix->name, sizeof(msix->name), "%s:%s%d", netdev->name, "rx", rx_idx); rx_idx++; } else if (CHK_MSIX_FLAG(TXS)) { handler = alx_msix_rtx; snprintf(msix->name, sizeof(msix->name), "%s:%s%d", netdev->name, "tx", tx_idx); tx_idx++; } else if (CHK_MSIX_FLAG(TIMER)) { handler = alx_msix_timer; snprintf(msix->name, sizeof(msix->name), "%s:%s", netdev->name, "timer"); } else if (CHK_MSIX_FLAG(ALERT)) { handler = alx_msix_alert; snprintf(msix->name, sizeof(msix->name), "%s:%s", netdev->name, "alert"); } else if (CHK_MSIX_FLAG(SMB)) { handler = alx_msix_smb; snprintf(msix->name, sizeof(msix->name), "%s:%s", netdev->name, "smb"); } else if (CHK_MSIX_FLAG(PHY)) { handler = alx_msix_phy; snprintf(msix->name, sizeof(msix->name), "%s:%s", netdev->name, "phy"); } else { netif_dbg(adpt, ifup, adpt->netdev, "MSIX entry [%d] is blank\n", msix->vec_idx); continue; } netif_dbg(adpt, ifup, adpt->netdev, "MSIX entry [%d] is %s\n", msix->vec_idx, msix->name); retval = request_irq(adpt->msix_entries[msix_idx].vector, handler, 0, msix->name, msix); if (retval) goto free_msix_irq; /* assign the mask for this irq */ irq_set_affinity_hint(adpt->msix_entries[msix_idx].vector, msix->affinity_mask); } return retval; free_msix_irq: for (i = 0; i < msix_idx; i++) { irq_set_affinity_hint(adpt->msix_entries[i].vector, NULL); free_irq(adpt->msix_entries[i].vector, adpt->msix[i]); } CLI_ADPT_FLAG(0, MSIX_EN); pci_disable_msix(adpt->pdev); kfree(adpt->msix_entries); adpt->msix_entries = NULL; return retval; } /* * alx_request_irq - initialize interrupts */ static int alx_request_irq(struct alx_adapter *adpt) { struct net_device *netdev = adpt->netdev; int retval; /* request MSIX irq */ if (CHK_ADPT_FLAG(0, MSIX_EN)) { retval = alx_request_msix_irq(adpt); if (retval) { alx_err(adpt, "request msix irq failed, error = %d\n", retval); } goto out; } /* request MSI irq */ if (CHK_ADPT_FLAG(0, MSI_EN)) { retval = request_irq(adpt->pdev->irq, alx_interrupt, 0, netdev->name, netdev); if (retval) { alx_err(adpt, "request msix irq failed, error = %d\n", retval); } pr_info("%s -- request_irq() suceeded for irq %d\n", __func__, adpt->pdev->irq); goto out; } /* request shared irq */ retval = request_irq(adpt->pdev->irq, alx_interrupt, IRQF_SHARED, netdev->name, netdev); if (retval) { alx_err(adpt, "request shared irq failed, error = %d\n", retval); } out: return retval; } static void alx_free_irq(struct alx_adapter *adpt) { struct net_device *netdev = adpt->netdev; int i; if (CHK_ADPT_FLAG(0, MSIX_EN)) { for (i = 0; i < adpt->num_msix_intrs; i++) { struct alx_msix_param *msix = adpt->msix[i]; netif_dbg(adpt, ifdown, adpt->netdev, "msix entry = %d\n", i); if (!CHK_MSIX_FLAG(ALL)) continue; if (CHK_MSIX_FLAG(RXS) || CHK_MSIX_FLAG(TXS)) { irq_set_affinity_hint( adpt->msix_entries[i].vector, NULL); } free_irq(adpt->msix_entries[i].vector, msix); } alx_reset_msix_maps(adpt); } else { free_irq(adpt->pdev->irq, netdev); } } static void alx_vlan_mode(struct net_device *netdev, netdev_features_t features) { struct alx_adapter *adpt = netdev_priv(netdev); struct alx_hw *hw = &adpt->hw; if (!CHK_ADPT_FLAG(1, STATE_DOWN)) alx_disable_intr(adpt); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) if (features & NETIF_F_HW_VLAN_CTAG_RX) { #else if (features & NETIF_F_HW_VLAN_RX) { #endif /* enable VLAN tag insert/strip */ SET_HW_FLAG(VLANSTRIP_EN); } else { /* disable VLAN tag insert/strip */ CLI_HW_FLAG(VLANSTRIP_EN); } hw->cbs.config_mac_ctrl(hw); if (!CHK_ADPT_FLAG(1, STATE_DOWN)) alx_enable_intr(adpt); } static void alx_restore_vlan(struct alx_adapter *adpt) { alx_vlan_mode(adpt->netdev, adpt->netdev->features); } static void alx_napi_enable_all(struct alx_adapter *adpt) { struct alx_msix_param *msix; int num_msix_intrs = adpt->num_msix_intrs; int msix_idx; if (!CHK_ADPT_FLAG(0, MSIX_EN)) num_msix_intrs = 1; for (msix_idx = 0; msix_idx < num_msix_intrs; msix_idx++) { struct napi_struct *napi; msix = adpt->msix[msix_idx]; napi = &msix->napi; napi_enable(napi); } } static void alx_napi_disable_all(struct alx_adapter *adpt) { struct alx_msix_param *msix; int num_msix_intrs = adpt->num_msix_intrs; int msix_idx; if (!CHK_ADPT_FLAG(0, MSIX_EN)) num_msix_intrs = 1; for (msix_idx = 0; msix_idx < num_msix_intrs; msix_idx++) { msix = adpt->msix[msix_idx]; napi_disable(&msix->napi); } } static void alx_clean_tx_queue(struct alx_tx_queue *txque) { struct device *dev = txque->dev; unsigned long size; u16 i; /* ring already cleared, nothing to do */ if (!txque->tpq.tpbuff) return; for (i = 0; i < txque->tpq.count; i++) { struct alx_buffer *tpbuf; tpbuf = GET_TP_BUFFER(txque, i); if (tpbuf->dma) { pci_unmap_single(to_pci_dev(dev), tpbuf->dma, tpbuf->length, DMA_TO_DEVICE); tpbuf->dma = 0; } if (tpbuf->skb) { dev_kfree_skb_any(tpbuf->skb); tpbuf->skb = NULL; } } size = sizeof(struct alx_buffer) * txque->tpq.count; memset(txque->tpq.tpbuff, 0, size); /* Zero out Tx-buffers */ memset(txque->tpq.tpdesc, 0, txque->tpq.size); txque->tpq.consume_idx = 0; txque->tpq.produce_idx = 0; } /* * alx_clean_all_tx_queues */ static void alx_clean_all_tx_queues(struct alx_adapter *adpt) { int i; for (i = 0; i < adpt->num_txques; i++) alx_clean_tx_queue(adpt->tx_queue[i]); } static void alx_clean_rx_queue(struct alx_rx_queue *rxque) { struct device *dev = rxque->dev; unsigned long size; int i; if (CHK_RX_FLAG(HW_QUE)) { /* ring already cleared, nothing to do */ if (!rxque->rfq.rfbuff) goto clean_sw_queue; for (i = 0; i < rxque->rfq.count; i++) { struct alx_buffer *rfbuf; rfbuf = GET_RF_BUFFER(rxque, i); if (rfbuf->dma) { pci_unmap_single(to_pci_dev(dev), rfbuf->dma, rfbuf->length, DMA_FROM_DEVICE); rfbuf->dma = 0; } if (rfbuf->skb) { dev_kfree_skb(rfbuf->skb); rfbuf->skb = NULL; } } size = sizeof(struct alx_buffer) * rxque->rfq.count; memset(rxque->rfq.rfbuff, 0, size); /* zero out the descriptor ring */ memset(rxque->rrq.rrdesc, 0, rxque->rrq.size); rxque->rrq.produce_idx = 0; rxque->rrq.consume_idx = 0; memset(rxque->rfq.rfdesc, 0, rxque->rfq.size); rxque->rfq.produce_idx = 0; rxque->rfq.consume_idx = 0; } clean_sw_queue: if (CHK_RX_FLAG(SW_QUE)) { /* ring already cleared, nothing to do */ if (!rxque->swq.swbuff) return; for (i = 0; i < rxque->swq.count; i++) { struct alx_sw_buffer *swbuf; swbuf = GET_SW_BUFFER(rxque, i); /* swq doesn't map DMA*/ if (swbuf->skb) { dev_kfree_skb(swbuf->skb); swbuf->skb = NULL; } } size = sizeof(struct alx_buffer) * rxque->swq.count; memset(rxque->swq.swbuff, 0, size); /* swq doesn't have any descripter rings */ rxque->swq.produce_idx = 0; rxque->swq.consume_idx = 0; } } /* * alx_clean_all_rx_queues */ static void alx_clean_all_rx_queues(struct alx_adapter *adpt) { int i; for (i = 0; i < adpt->num_rxques; i++) alx_clean_rx_queue(adpt->rx_queue[i]); } /* * alx_set_rss_queues: Allocate queues for RSS */ static inline void alx_set_num_txques(struct alx_adapter *adpt) { struct alx_hw *hw = &adpt->hw; if (hw->mac_type == alx_mac_l1f || hw->mac_type == alx_mac_l2f) adpt->num_txques = 4; else adpt->num_txques = 2; } /* * alx_set_rss_queues: Allocate queues for RSS */ static inline void alx_set_num_rxques(struct alx_adapter *adpt) { if (CHK_ADPT_FLAG(0, SRSS_CAP)) { adpt->num_hw_rxques = 1; adpt->num_sw_rxques = adpt->max_rxques; adpt->num_rxques = max_t(u16, adpt->num_hw_rxques, adpt->num_sw_rxques); } } /* * alx_set_num_queues: Allocate queues for device, feature dependant */ static void alx_set_num_queues(struct alx_adapter *adpt) { /* Start with default case */ adpt->num_txques = 1; adpt->num_rxques = 1; adpt->num_hw_rxques = 1; adpt->num_sw_rxques = 0; alx_set_num_rxques(adpt); alx_set_num_txques(adpt); } /* alx_alloc_all_rtx_queue - allocate all queues */ static int alx_alloc_all_rtx_queue(struct alx_adapter *adpt) { int que_idx; for (que_idx = 0; que_idx < adpt->num_txques; que_idx++) { struct alx_tx_queue *txque = adpt->tx_queue[que_idx]; txque = kzalloc(sizeof(struct alx_tx_queue), GFP_KERNEL); if (!txque) goto err_alloc_tx_queue; txque->tpq.count = adpt->num_txdescs; txque->que_idx = que_idx; txque->dev = &adpt->pdev->dev; txque->netdev = adpt->netdev; adpt->tx_queue[que_idx] = txque; } for (que_idx = 0; que_idx < adpt->num_rxques; que_idx++) { struct alx_rx_queue *rxque = adpt->rx_queue[que_idx]; rxque = kzalloc(sizeof(struct alx_rx_queue), GFP_KERNEL); if (!rxque) goto err_alloc_rx_queue; rxque->rrq.count = adpt->num_rxdescs; rxque->rfq.count = adpt->num_rxdescs; rxque->swq.count = adpt->num_rxdescs; rxque->que_idx = que_idx; rxque->dev = &adpt->pdev->dev; rxque->netdev = adpt->netdev; if (CHK_ADPT_FLAG(0, SRSS_EN)) { if (que_idx < adpt->num_hw_rxques) SET_RX_FLAG(HW_QUE); if (que_idx < adpt->num_sw_rxques) SET_RX_FLAG(SW_QUE); } else { SET_RX_FLAG(HW_QUE); } adpt->rx_queue[que_idx] = rxque; } netif_dbg(adpt, probe, adpt->netdev, "num_tx_descs = %d, num_rx_descs = %d\n", adpt->num_txdescs, adpt->num_rxdescs); return 0; err_alloc_rx_queue: alx_err(adpt, "goto err_alloc_rx_queue"); for (que_idx = 0; que_idx < adpt->num_rxques; que_idx++) kfree(adpt->rx_queue[que_idx]); err_alloc_tx_queue: alx_err(adpt, "goto err_alloc_tx_queue"); for (que_idx = 0; que_idx < adpt->num_txques; que_idx++) kfree(adpt->tx_queue[que_idx]); return -ENOMEM; } /* alx_free_all_rtx_queue */ static void alx_free_all_rtx_queue(struct alx_adapter *adpt) { int que_idx; for (que_idx = 0; que_idx < adpt->num_txques; que_idx++) { kfree(adpt->tx_queue[que_idx]); adpt->tx_queue[que_idx] = NULL; } for (que_idx = 0; que_idx < adpt->num_rxques; que_idx++) { kfree(adpt->rx_queue[que_idx]); adpt->rx_queue[que_idx] = NULL; } } /* alx_set_interrupt_param - set interrupt parameter */ static int alx_set_interrupt_param(struct alx_adapter *adpt) { struct alx_msix_param *msix; int (*poll)(struct napi_struct *, int); int msix_idx; if (CHK_ADPT_FLAG(0, MSIX_EN)) { poll = &alx_napi_msix_rtx; } else { adpt->num_msix_intrs = 1; poll = &alx_napi_legacy_rtx; } for (msix_idx = 0; msix_idx < adpt->num_msix_intrs; msix_idx++) { msix = kzalloc(sizeof(struct alx_msix_param), GFP_KERNEL); if (!msix) goto err_alloc_msix; msix->adpt = adpt; msix->vec_idx = msix_idx; /* Allocate the affinity_hint cpumask, configure the mask */ if (!alloc_cpumask_var(&msix->affinity_mask, GFP_KERNEL)) goto err_alloc_cpumask; cpumask_set_cpu((msix_idx % num_online_cpus()), msix->affinity_mask); netif_napi_add(adpt->netdev, &msix->napi, (*poll), 64); adpt->msix[msix_idx] = msix; } return 0; err_alloc_cpumask: kfree(msix); adpt->msix[msix_idx] = NULL; err_alloc_msix: for (msix_idx--; msix_idx >= 0; msix_idx--) { msix = adpt->msix[msix_idx]; netif_napi_del(&msix->napi); free_cpumask_var(msix->affinity_mask); kfree(msix); adpt->msix[msix_idx] = NULL; } alx_err(adpt, "can't allocate memory\n"); return -ENOMEM; } /* * alx_reset_interrupt_param - Free memory allocated for interrupt vectors */ static void alx_reset_interrupt_param(struct alx_adapter *adpt) { int msix_idx; for (msix_idx = 0; msix_idx < adpt->num_msix_intrs; msix_idx++) { struct alx_msix_param *msix = adpt->msix[msix_idx]; netif_napi_del(&msix->napi); free_cpumask_var(msix->affinity_mask); kfree(msix); adpt->msix[msix_idx] = NULL; } } /* set msix interrupt mode */ static int alx_set_msix_interrupt_mode(struct alx_adapter *adpt) { int msix_intrs, msix_idx; int retval = 0; adpt->msix_entries = kcalloc(adpt->max_msix_intrs, sizeof(struct msix_entry), GFP_KERNEL); if (!adpt->msix_entries) { netif_dbg(adpt, probe, adpt->netdev, "can't allocate msix entry\n"); CLI_ADPT_FLAG(0, MSIX_EN); goto try_msi_mode; } for (msix_idx = 0; msix_idx < adpt->max_msix_intrs; msix_idx++) adpt->msix_entries[msix_idx].entry = msix_idx; msix_intrs = adpt->max_msix_intrs; while (msix_intrs >= adpt->min_msix_intrs) { retval = pci_enable_msix(adpt->pdev, adpt->msix_entries, msix_intrs); if (!retval) /* Success in acquiring all requested vectors. */ break; else if (retval < 0) msix_intrs = 0; /* Nasty failure, quit now */ else /* error == number of vectors we should try again with */ msix_intrs = retval; } if (msix_intrs < adpt->min_msix_intrs) { netif_dbg(adpt, probe, adpt->netdev, "can't enable MSI-X interrupts\n"); CLI_ADPT_FLAG(0, MSIX_EN); kfree(adpt->msix_entries); adpt->msix_entries = NULL; goto try_msi_mode; } netif_dbg(adpt, probe, adpt->netdev, "enable MSI-X interrupts, num_msix_intrs = %d\n", msix_intrs); SET_ADPT_FLAG(0, MSIX_EN); if (CHK_ADPT_FLAG(0, SRSS_CAP)) SET_ADPT_FLAG(0, SRSS_EN); adpt->num_msix_intrs = min_t(int, msix_intrs, adpt->max_msix_intrs); retval = 0; return retval; try_msi_mode: CLI_ADPT_FLAG(0, SRSS_CAP); CLI_ADPT_FLAG(0, SRSS_EN); alx_set_num_queues(adpt); retval = -1; return retval; } /* set msi interrupt mode */ static int alx_set_msi_interrupt_mode(struct alx_adapter *adpt) { int retval; retval = pci_enable_msi(adpt->pdev); adpt->netdev->irq = adpt->pdev->irq; if (retval) { netif_dbg(adpt, probe, adpt->netdev, "can't enable MSI interrupt, error = %d\n", retval); return retval; } SET_ADPT_FLAG(0, MSI_EN); return retval; } /* set interrupt mode */ static int alx_set_interrupt_mode(struct alx_adapter *adpt) { int retval = 0; if (CHK_ADPT_FLAG(0, MSIX_CAP)) { netif_dbg(adpt, probe, adpt->netdev, "try to set MSIX interrupt\n"); retval = alx_set_msix_interrupt_mode(adpt); if (!retval) return retval; } if (CHK_ADPT_FLAG(0, MSI_CAP)) { netif_dbg(adpt, probe, adpt->netdev, "try to set MSI interrupt\n"); retval = alx_set_msi_interrupt_mode(adpt); if (!retval) return retval; } netif_dbg(adpt, probe, adpt->netdev, "can't enable MSIX and MSI, will enable shared interrupt\n"); retval = 0; return retval; } static void alx_reset_interrupt_mode(struct alx_adapter *adpt) { if (CHK_ADPT_FLAG(0, MSIX_EN)) { CLI_ADPT_FLAG(0, MSIX_EN); pci_disable_msix(adpt->pdev); kfree(adpt->msix_entries); adpt->msix_entries = NULL; } else if (CHK_ADPT_FLAG(0, MSI_EN)) { CLI_ADPT_FLAG(0, MSI_EN); pci_disable_msi(adpt->pdev); } } static int __devinit alx_init_adapter_special(struct alx_adapter *adpt) { switch (adpt->hw.mac_type) { case alx_mac_l1f: case alx_mac_l2f: goto init_alf_adapter; break; case alx_mac_l1c: case alx_mac_l2c: goto init_alc_adapter; break; case alx_mac_l1d_v1: case alx_mac_l1d_v2: case alx_mac_l2cb_v1: case alx_mac_l2cb_v20: case alx_mac_l2cb_v21: adpt->hw.bHibBug = true; goto init_alc_adapter; break; default: break; } return -1; init_alc_adapter: if (CHK_ADPT_FLAG(0, MSIX_CAP)) alx_err(adpt, "ALC doesn't support MSIX\n"); /* msi for tx, rx and none queues */ adpt->num_msix_txques = 0; adpt->num_msix_rxques = 0; adpt->num_msix_noques = 0; return 0; init_alf_adapter: if (CHK_ADPT_FLAG(0, MSIX_CAP)) { /* msix for tx, rx and none queues */ adpt->num_msix_txques = 4; adpt->num_msix_rxques = 8; adpt->num_msix_noques = ALF_MAX_MSIX_NOQUE_INTRS; /* msix vector range */ adpt->max_msix_intrs = ALF_MAX_MSIX_INTRS; adpt->min_msix_intrs = ALF_MIN_MSIX_INTRS; } else { /* msi for tx, rx and none queues */ adpt->num_msix_txques = 0; adpt->num_msix_rxques = 0; adpt->num_msix_noques = 0; } return 0; } /* * alx_init_adapter */ static int __devinit alx_init_adapter(struct alx_adapter *adpt) { struct alx_hw *hw = &adpt->hw; struct pci_dev *pdev = adpt->pdev; u16 revision; int max_frame; /* PCI config space info */ hw->pci_venid = pdev->vendor; hw->pci_devid = pdev->device; alx_cfg_r16(hw, PCI_CLASS_REVISION, &revision); hw->pci_revid = revision & 0xFF; hw->pci_sub_venid = pdev->subsystem_vendor; hw->pci_sub_devid = pdev->subsystem_device; if (alx_init_hw_callbacks(adpt) != 0) { alx_err(adpt, "set HW function pointers failed\n"); return -1; } if (hw->cbs.identify_nic(hw) != 0) { alx_err(adpt, "HW is disabled\n"); return -1; } /* Set adapter flags */ switch (hw->mac_type) { case alx_mac_l1f: case alx_mac_l2f: #ifdef CONFIG_ALX_MSI SET_ADPT_FLAG(0, MSI_CAP); #endif #ifdef CONFIG_ALX_MSIX SET_ADPT_FLAG(0, MSIX_CAP); #endif if (CHK_ADPT_FLAG(0, MSIX_CAP)) { SET_ADPT_FLAG(0, FIXED_MSIX); SET_ADPT_FLAG(0, MRQ_CAP); #ifdef CONFIG_ALX_RSS SET_ADPT_FLAG(0, SRSS_CAP); #endif } pdev->dev_flags |= PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG; break; case alx_mac_l1c: case alx_mac_l1d_v1: case alx_mac_l1d_v2: case alx_mac_l2c: case alx_mac_l2cb_v1: case alx_mac_l2cb_v20: case alx_mac_l2cb_v21: #ifdef CONFIG_ALX_MSI SET_ADPT_FLAG(0, MSI_CAP); #endif break; default: break; } /* set default for alx_adapter */ adpt->max_msix_intrs = 1; adpt->min_msix_intrs = 1; max_frame = adpt->netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; adpt->rxbuf_size = adpt->netdev->mtu > ALX_DEF_RX_BUF_SIZE ? ALIGN(max_frame, 8) : ALX_DEF_RX_BUF_SIZE; adpt->wol = 0; device_set_wakeup_enable(&pdev->dev, false); /* set default for alx_hw */ hw->link_up = false; hw->link_speed = ALX_LINK_SPEED_UNKNOWN; hw->preamble = 7; hw->intr_mask = ALX_IMR_NORMAL_MASK; hw->smb_timer = 400; /* 400ms */ hw->mtu = adpt->netdev->mtu; #ifdef MDM_PLATFORM hw->imt = 500; /* For MDM set it to 1ms */ #else hw->imt = 100; /* set to 200us */ #endif /* set default for wrr */ hw->wrr_prio0 = 4; hw->wrr_prio1 = 4; hw->wrr_prio2 = 4; hw->wrr_prio3 = 4; hw->wrr_mode = alx_wrr_mode_none; /* set default flow control settings */ hw->req_fc_mode = alx_fc_full; hw->cur_fc_mode = alx_fc_full; /* init for ethtool output */ hw->disable_fc_autoneg = false; hw->fc_was_autonegged = false; hw->fc_single_pause = true; /* set default for rss info*/ hw->rss_hstype = 0; hw->rss_mode = alx_rss_mode_disable; hw->rss_idt_size = 0; hw->rss_base_cpu = 0; memset(hw->rss_idt, 0x0, sizeof(hw->rss_idt)); memset(hw->rss_key, 0x0, sizeof(hw->rss_key)); atomic_set(&adpt->irq_sem, 1); spin_lock_init(&adpt->tx_lock); spin_lock_init(&adpt->rx_lock); alx_init_adapter_special(adpt); if (hw->cbs.init_phy) { if (hw->cbs.init_phy(hw)) return -EINVAL; } SET_ADPT_FLAG(1, STATE_DOWN); return 0; } static int alx_set_register_info_special(struct alx_adapter *adpt) { struct alx_hw *hw = &adpt->hw; int num_txques = adpt->num_txques; switch (adpt->hw.mac_type) { case alx_mac_l1f: case alx_mac_l2f: goto cache_alf_register; break; case alx_mac_l1c: case alx_mac_l1d_v1: case alx_mac_l1d_v2: case alx_mac_l2c: case alx_mac_l2cb_v1: case alx_mac_l2cb_v20: case alx_mac_l2cb_v21: goto cache_alc_register; break; default: break; } return -1; cache_alc_register: /* setting for Produce Index and Consume Index */ adpt->rx_queue[0]->produce_reg = hw->rx_prod_reg[0]; adpt->rx_queue[0]->consume_reg = hw->rx_cons_reg[0]; switch (num_txques) { case 2: adpt->tx_queue[1]->produce_reg = hw->tx_prod_reg[1]; adpt->tx_queue[1]->consume_reg = hw->tx_cons_reg[1]; case 1: adpt->tx_queue[0]->produce_reg = hw->tx_prod_reg[0]; adpt->tx_queue[0]->consume_reg = hw->tx_cons_reg[0]; break; } return 0; cache_alf_register: /* setting for Produce Index and Consume Index */ adpt->rx_queue[0]->produce_reg = hw->rx_prod_reg[0]; adpt->rx_queue[0]->consume_reg = hw->rx_cons_reg[0]; switch (num_txques) { case 4: adpt->tx_queue[3]->produce_reg = hw->tx_prod_reg[3]; adpt->tx_queue[3]->consume_reg = hw->tx_cons_reg[3]; case 3: adpt->tx_queue[2]->produce_reg = hw->tx_prod_reg[2]; adpt->tx_queue[2]->consume_reg = hw->tx_cons_reg[2]; case 2: adpt->tx_queue[1]->produce_reg = hw->tx_prod_reg[1]; adpt->tx_queue[1]->consume_reg = hw->tx_cons_reg[1]; case 1: adpt->tx_queue[0]->produce_reg = hw->tx_prod_reg[0]; adpt->tx_queue[0]->consume_reg = hw->tx_cons_reg[0]; } return 0; } /* alx_alloc_tx_descriptor - allocate Tx Descriptors */ static int alx_alloc_tx_descriptor(struct alx_adapter *adpt, struct alx_tx_queue *txque) { struct alx_ring_header *ring_header = &adpt->ring_header; int size; netif_dbg(adpt, ifup, adpt->netdev, "tpq.count = %d\n", txque->tpq.count); size = sizeof(struct alx_buffer) * txque->tpq.count; txque->tpq.tpbuff = kzalloc(size, GFP_KERNEL); if (!txque->tpq.tpbuff) goto err_alloc_tpq_buffer; /* round up to nearest 4K */ txque->tpq.size = txque->tpq.count * sizeof(union alx_hw_tpdesc); txque->tpq.tpdma = ring_header->dma + ring_header->used; txque->tpq.tpdesc = ring_header->desc + ring_header->used; adpt->hw.tpdma[txque->que_idx] = (u64)txque->tpq.tpdma; ring_header->used += ALIGN(txque->tpq.size, 8); txque->tpq.produce_idx = 0; txque->tpq.consume_idx = 0; txque->max_packets = txque->tpq.count; return 0; err_alloc_tpq_buffer: alx_err(adpt, "Unable to allocate memory for the Tx descriptor\n"); return -ENOMEM; } /* alx_alloc_all_tx_descriptor - allocate all Tx Descriptors */ static int alx_alloc_all_tx_descriptor(struct alx_adapter *adpt) { int i, retval = 0; netif_dbg(adpt, ifup, adpt->netdev, "num_tques = %d\n", adpt->num_txques); for (i = 0; i < adpt->num_txques; i++) { retval = alx_alloc_tx_descriptor(adpt, adpt->tx_queue[i]); if (!retval) continue; alx_err(adpt, "Allocation for Tx Queue %u failed\n", i); break; } return retval; } /* alx_alloc_rx_descriptor - allocate Rx Descriptors */ static int alx_alloc_rx_descriptor(struct alx_adapter *adpt, struct alx_rx_queue *rxque) { struct alx_ring_header *ring_header = &adpt->ring_header; int size; netif_dbg(adpt, ifup, adpt->netdev, "RRD.count = %d, RFD.count = %d, SWD.count = %d\n", rxque->rrq.count, rxque->rfq.count, rxque->swq.count); if (CHK_RX_FLAG(HW_QUE)) { /* alloc buffer info */ size = sizeof(struct alx_buffer) * rxque->rfq.count; rxque->rfq.rfbuff = kzalloc(size, GFP_KERNEL); if (!rxque->rfq.rfbuff) goto err_alloc_rfq_buffer; /* * set dma's point of rrq and rfq */ /* Round up to nearest 4K */ rxque->rrq.size = rxque->rrq.count * sizeof(union alx_hw_rrdesc); rxque->rfq.size = rxque->rfq.count * sizeof(union alx_hw_rfdesc); rxque->rrq.rrdma = ring_header->dma + ring_header->used; rxque->rrq.rrdesc = ring_header->desc + ring_header->used; adpt->hw.rrdma[rxque->que_idx] = (u64)rxque->rrq.rrdma; ring_header->used += ALIGN(rxque->rrq.size, 8); rxque->rfq.rfdma = ring_header->dma + ring_header->used; rxque->rfq.rfdesc = ring_header->desc + ring_header->used; adpt->hw.rfdma[rxque->que_idx] = (u64)rxque->rfq.rfdma; ring_header->used += ALIGN(rxque->rfq.size, 8); /* clean all counts within rxque */ rxque->rrq.produce_idx = 0; rxque->rrq.consume_idx = 0; rxque->rfq.produce_idx = 0; rxque->rfq.consume_idx = 0; } if (CHK_RX_FLAG(SW_QUE)) { size = sizeof(struct alx_sw_buffer) * rxque->swq.count; rxque->swq.swbuff = kzalloc(size, GFP_KERNEL); if (!rxque->swq.swbuff) goto err_alloc_swq_buffer; rxque->swq.consume_idx = 0; rxque->swq.produce_idx = 0; } rxque->max_packets = rxque->rrq.count / 2; return 0; err_alloc_swq_buffer: kfree(rxque->rfq.rfbuff); rxque->rfq.rfbuff = NULL; err_alloc_rfq_buffer: alx_err(adpt, "Unable to allocate memory for the Rx descriptor\n"); return -ENOMEM; } /* alx_alloc_all_rx_descriptor - allocate all Rx Descriptors */ static int alx_alloc_all_rx_descriptor(struct alx_adapter *adpt) { int i, error = 0; for (i = 0; i < adpt->num_rxques; i++) { error = alx_alloc_rx_descriptor(adpt, adpt->rx_queue[i]); if (!error) continue; alx_err(adpt, "Allocation for Rx Queue %u failed\n", i); break; } return error; } /* alx_free_tx_descriptor - Free Tx Descriptor */ static void alx_free_tx_descriptor(struct alx_tx_queue *txque) { alx_clean_tx_queue(txque); kfree(txque->tpq.tpbuff); txque->tpq.tpbuff = NULL; /* if not set, then don't free */ if (!txque->tpq.tpdesc) return; txque->tpq.tpdesc = NULL; } /* alx_free_all_tx_descriptor - Free all Tx Descriptor */ static void alx_free_all_tx_descriptor(struct alx_adapter *adpt) { int i; for (i = 0; i < adpt->num_txques; i++) alx_free_tx_descriptor(adpt->tx_queue[i]); } /* alx_free_all_rx_descriptor - Free all Rx Descriptor */ static void alx_free_rx_descriptor(struct alx_rx_queue *rxque) { alx_clean_rx_queue(rxque); if (CHK_RX_FLAG(HW_QUE)) { kfree(rxque->rfq.rfbuff); rxque->rfq.rfbuff = NULL; /* if not set, then don't free */ if (!rxque->rrq.rrdesc) return; rxque->rrq.rrdesc = NULL; if (!rxque->rfq.rfdesc) return; rxque->rfq.rfdesc = NULL; } if (CHK_RX_FLAG(SW_QUE)) { kfree(rxque->swq.swbuff); rxque->swq.swbuff = NULL; } } #ifdef MDM_PLATFORM static int alx_alloc_flow_ctrl_desc(struct alx_adapter *adpt) { int i; struct alx_ipa_rx_desc_node *node = NULL; for (i = 0; i < ALX_IPA_SYS_PIPE_DNE_PKTS; i++) { node = (struct alx_ipa_rx_desc_node *) kzalloc(sizeof(struct alx_ipa_rx_desc_node), GFP_KERNEL); if (!node) { pr_err("%s -- Only able to allocate %d nodes \n" ,__func__, adpt->freeq_cnt); return -ENOMEM; } spin_lock(&adpt->flow_ctrl_lock); adpt->freeq_cnt++; list_add_tail(&node->link, &adpt->free_queue_head); spin_unlock(&adpt->flow_ctrl_lock); } return 0; } static void alx_free_flow_ctrl_desc(struct alx_adapter *adpt) { struct alx_ipa_rx_desc_node *node, *tmp; spin_lock_bh(&adpt->flow_ctrl_lock); list_for_each_entry_safe(node, tmp, &adpt->free_queue_head, link) { list_del(&node->link); kfree(node); adpt->freeq_cnt--; } spin_unlock_bh(&adpt->flow_ctrl_lock); if (adpt->freeq_cnt != 0) { pr_err("%s - Memory Leak Detected \n",__func__); BUG(); } } #endif /* alx_free_all_rx_descriptor - Free all Rx Descriptor */ static void alx_free_all_rx_descriptor(struct alx_adapter *adpt) { int i; for (i = 0; i < adpt->num_rxques; i++) alx_free_rx_descriptor(adpt->rx_queue[i]); } /* * alx_alloc_all_rtx_descriptor - allocate Tx / RX descriptor queues */ static int alx_alloc_all_rtx_descriptor(struct alx_adapter *adpt) { struct device *dev = &adpt->pdev->dev; struct alx_ring_header *ring_header = &adpt->ring_header; int num_tques = adpt->num_txques; int num_rques = adpt->num_hw_rxques; unsigned int num_tx_descs = adpt->num_txdescs; unsigned int num_rx_descs = adpt->num_rxdescs; int retval; /* * real ring DMA buffer * each ring/block may need up to 8 bytes for alignment, hence the * additional bytes tacked onto the end. */ ring_header->size = num_tques * num_tx_descs * sizeof(union alx_hw_tpdesc) + num_rques * num_rx_descs * sizeof(union alx_hw_rfdesc) + num_rques * num_rx_descs * sizeof(union alx_hw_rrdesc) + sizeof(struct coals_msg_block) + sizeof(struct alx_hw_stats) + num_tques * 8 + num_rques * 2 * 8 + 8 * 2; netif_dbg(adpt, ifup, adpt->netdev, "num_tques = %d, num_tx_descs = %d\n", num_tques, num_tx_descs); netif_dbg(adpt, ifup, adpt->netdev, "num_rques = %d, num_rx_descs = %d\n", num_rques, num_rx_descs); ring_header->used = 0; ring_header->desc = dma_alloc_coherent(dev, ring_header->size, &ring_header->dma, GFP_KERNEL); if (!ring_header->desc) { alx_err(adpt, "dma_alloc_coherent failed\n"); retval = -ENOMEM; goto err_alloc_dma; } memset(ring_header->desc, 0, ring_header->size); ring_header->used = ALIGN(ring_header->dma, 8) - ring_header->dma; netif_dbg(adpt, ifup, adpt->netdev, "ring header: size = %d, used= %d\n", ring_header->size, ring_header->used); /* allocate receive descriptors */ retval = alx_alloc_all_tx_descriptor(adpt); if (retval) goto err_alloc_tx; /* allocate receive descriptors */ retval = alx_alloc_all_rx_descriptor(adpt); if (retval) goto err_alloc_rx; /* Init CMB dma address */ adpt->cmb.dma = ring_header->dma + ring_header->used; adpt->cmb.cmb = (u8 *) ring_header->desc + ring_header->used; ring_header->used += ALIGN(sizeof(struct coals_msg_block), 8); adpt->smb.dma = ring_header->dma + ring_header->used; adpt->smb.smb = (u8 *)ring_header->desc + ring_header->used; ring_header->used += ALIGN(sizeof(struct alx_hw_stats), 8); return 0; err_alloc_rx: alx_free_all_rx_descriptor(adpt); err_alloc_tx: alx_free_all_tx_descriptor(adpt); err_alloc_dma: return retval; } /* * alx_alloc_all_rtx_descriptor - allocate Tx / RX descriptor queues */ static void alx_free_all_rtx_descriptor(struct alx_adapter *adpt) { struct pci_dev *pdev = adpt->pdev; struct alx_ring_header *ring_header = &adpt->ring_header; alx_free_all_tx_descriptor(adpt); alx_free_all_rx_descriptor(adpt); adpt->cmb.dma = 0; adpt->cmb.cmb = NULL; adpt->smb.dma = 0; adpt->smb.smb = NULL; if (ring_header->desc) { pci_free_consistent(pdev, ring_header->size, ring_header->desc, ring_header->dma); ring_header->desc = NULL; } ring_header->size = ring_header->used = 0; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) static netdev_features_t alx_fix_features(struct net_device *netdev, netdev_features_t features) { struct alx_adapter *adpt = netdev_priv(netdev); /* * Since there is no support for separate rx/tx vlan accel * enable/disable make sure tx flag is always in same state as rx. */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) if (features & NETIF_F_HW_VLAN_CTAG_RX) features |= NETIF_F_HW_VLAN_CTAG_TX; else features &= ~NETIF_F_HW_VLAN_CTAG_TX; #else if (features & NETIF_F_HW_VLAN_RX) features |= NETIF_F_HW_VLAN_TX; else features &= ~NETIF_F_HW_VLAN_TX; #endif /*(LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))*/ if (netdev->mtu > ALX_MAX_TSO_PKT_SIZE || adpt->hw.mac_type == alx_mac_l1c || adpt->hw.mac_type == alx_mac_l2c) features &= ~(NETIF_F_TSO | NETIF_F_TSO6); return features; } static int alx_set_features(struct net_device *netdev, netdev_features_t features) { netdev_features_t changed = netdev->features ^ features; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) if (changed & NETIF_F_HW_VLAN_CTAG_RX) #else if (changed & NETIF_F_HW_VLAN_RX) #endif /*(LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))*/ alx_vlan_mode(netdev, features); return 0; } #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ /* * alx_change_mtu - Change the Maximum Transfer Unit */ static int alx_change_mtu(struct net_device *netdev, int new_mtu) { struct alx_adapter *adpt = netdev_priv(netdev); int old_mtu = netdev->mtu; int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; if ((max_frame < ALX_MIN_ETH_FRAME_SIZE) || (max_frame > ALX_MAX_ETH_FRAME_SIZE)) { alx_err(adpt, "invalid MTU setting\n"); return -EINVAL; } /* set MTU */ if (old_mtu != new_mtu && netif_running(netdev)) { netif_dbg(adpt, hw, adpt->netdev, "changing MTU from %d to %d\n", netdev->mtu, new_mtu); netdev->mtu = new_mtu; adpt->hw.mtu = new_mtu; adpt->rxbuf_size = new_mtu > ALX_DEF_RX_BUF_SIZE ? ALIGN(max_frame, 8) : ALX_DEF_RX_BUF_SIZE; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) if (new_mtu > (7*1024)) { netdev->features &= ~NETIF_F_TSO; netdev->features &= ~NETIF_F_TSO6; } else { netdev->features |= NETIF_F_TSO; netdev->features |= NETIF_F_TSO6; } #else netdev_update_features(netdev); #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)) */ alx_reinit_locked(adpt); } return 0; } static int alx_open_internal(struct alx_adapter *adpt, u32 ctrl) { struct alx_hw *hw = &adpt->hw; int retval = 0; int i; alx_init_ring_ptrs(adpt); alx_set_multicase_list(adpt->netdev); alx_restore_vlan(adpt); if (hw->cbs.config_mac) retval = hw->cbs.config_mac(hw, adpt->rxbuf_size, adpt->num_hw_rxques, adpt->num_rxdescs, adpt->num_txques, adpt->num_txdescs); if (hw->cbs.config_tx) retval = hw->cbs.config_tx(hw); if (hw->cbs.config_rx) retval = hw->cbs.config_rx(hw); alx_config_rss(adpt); for (i = 0; i < adpt->num_hw_rxques; i++) alx_refresh_rx_buffer(adpt->rx_queue[i]); /* configure HW regsiters of MSIX */ if (hw->cbs.config_msix) retval = hw->cbs.config_msix(hw, adpt->num_msix_intrs, CHK_ADPT_FLAG(0, MSIX_EN), CHK_ADPT_FLAG(0, MSI_EN)); if (ctrl & ALX_OPEN_CTRL_IRQ_EN) { retval = alx_request_irq(adpt); if (retval) goto err_request_irq; } /* enable NAPI, INTR and TX */ alx_napi_enable_all(adpt); alx_enable_intr(adpt); netif_tx_start_all_queues(adpt->netdev); CLI_ADPT_FLAG(1, STATE_DOWN); /* check link status */ SET_ADPT_FLAG(0, TASK_LSC_REQ); adpt->link_jiffies = jiffies + ALX_TRY_LINK_TIMEOUT; #ifdef ALX_HIB_TIMER_CONFIG mod_timer(&adpt->alx_timer, jiffies); #endif return retval; err_request_irq: alx_clean_all_rx_queues(adpt); return retval; } static void alx_stop_internal(struct alx_adapter *adpt, u32 ctrl) { struct net_device *netdev = adpt->netdev; struct alx_hw *hw = &adpt->hw; SET_ADPT_FLAG(1, STATE_DOWN); netif_tx_stop_all_queues(netdev); /* call carrier off first to avoid false dev_watchdog timeouts */ netif_carrier_off(netdev); netif_tx_disable(netdev); alx_disable_intr(adpt); alx_napi_disable_all(adpt); if (ctrl & ALX_OPEN_CTRL_IRQ_EN) alx_free_irq(adpt); CLI_ADPT_FLAG(0, TASK_LSC_REQ); CLI_ADPT_FLAG(0, TASK_REINIT_REQ); #ifdef ALX_HIB_TIMER_CONFIG del_timer_sync(&adpt->alx_timer); #endif if (ctrl & ALX_OPEN_CTRL_RESET_PHY) hw->cbs.reset_phy(hw); if (ctrl & ALX_OPEN_CTRL_RESET_MAC) hw->cbs.reset_mac(hw); adpt->hw.link_speed = ALX_LINK_SPEED_UNKNOWN; alx_clean_all_tx_queues(adpt); alx_clean_all_rx_queues(adpt); } /* * alx_open - Called when a network interface is made active */ static int alx_open(struct net_device *netdev) { struct alx_adapter *adpt = netdev_priv(netdev); struct alx_hw *hw = &adpt->hw; int retval; /* disallow open during test */ if (CHK_ADPT_FLAG(1, STATE_TESTING) || CHK_ADPT_FLAG(1, STATE_DIAG_RUNNING)) return -EBUSY; netif_carrier_off(netdev); /* allocate rx/tx dma buffer & descriptors */ retval = alx_alloc_all_rtx_descriptor(adpt); if (retval) { alx_err(adpt, "error in alx_alloc_all_rtx_descriptor\n"); goto err_alloc_rtx; } #ifdef MDM_PLATFORM /* Allocate Nodes and List for storing flow control packets*/ retval = alx_alloc_flow_ctrl_desc(adpt); if (retval) { alx_err(adpt, "Error in allocating Flow Control Buffers \n"); goto err_alloc_flow_ctrl; } pr_info("%s -- %d Flow Control Buffer Allocated \n", __func__, adpt->freeq_cnt); #endif retval = alx_open_internal(adpt, ALX_OPEN_CTRL_IRQ_EN); if (retval) goto err_open_internal; return retval; err_open_internal: alx_stop_internal(adpt, ALX_OPEN_CTRL_IRQ_EN); #ifdef MDM_PLATFORM err_alloc_flow_ctrl: alx_free_flow_ctrl_desc(adpt); #endif err_alloc_rtx: alx_free_all_rtx_descriptor(adpt); hw->cbs.reset_mac(hw); return retval; } /* * alx_stop - Disables a network interface */ static int alx_stop(struct net_device *netdev) { struct alx_adapter *adpt = netdev_priv(netdev); struct alx_ipa_rx_desc_node *node = NULL; if (CHK_ADPT_FLAG(1, STATE_RESETTING)) netif_warn(adpt, ifdown, adpt->netdev, "flag STATE_RESETTING has already set\n"); alx_stop_internal(adpt, ALX_OPEN_CTRL_IRQ_EN | ALX_OPEN_CTRL_RESET_MAC); alx_free_all_rtx_descriptor(adpt); #ifdef MDM_PLATFORM /* Flush any pending packets */ pr_info("ALX - Flush %d Pending Packets \n", adpt->pendq_cnt); spin_lock_bh(&adpt->flow_ctrl_lock); while (adpt->pendq_cnt) { node = list_first_entry(&adpt->pend_queue_head, struct alx_ipa_rx_desc_node, link); list_del(&node->link); list_add_tail(&node->link, &adpt->free_queue_head); adpt->pendq_cnt--; adpt->freeq_cnt++; } spin_unlock_bh(&adpt->flow_ctrl_lock); if ((adpt->freeq_cnt != ALX_IPA_SYS_PIPE_DNE_PKTS) || (adpt->pendq_cnt != 0)) { pr_err("%s -- Memory leak detected freeq_cnt %d, pendq_cnt %d", __func__, adpt->freeq_cnt, adpt->pendq_cnt); BUG(); } alx_free_flow_ctrl_desc(adpt); #endif return 0; } static int alx_shutdown_internal(struct pci_dev *pdev, bool *wakeup) { struct alx_adapter *adpt = pci_get_drvdata(pdev); struct net_device *netdev = adpt->netdev; struct alx_hw *hw = &adpt->hw; u32 wufc = adpt->wol; u16 lpa; u32 speed, adv_speed, misc; bool link_up; int i; int retval = 0; hw->cbs.config_aspm(hw, false, false); netif_device_detach(netdev); if (netif_running(netdev)) alx_stop_internal(adpt, 0); #ifdef CONFIG_PM_SLEEP retval = pci_save_state(pdev); if (retval) return retval; #endif hw->cbs.check_phy_link(hw, &speed, &link_up); if (link_up) { if (hw->mac_type == alx_mac_l1f || hw->mac_type == alx_mac_l2f) { alx_mem_r32(hw, ALX_MISC, &misc); misc |= ALX_MISC_INTNLOSC_OPEN; alx_mem_w32(hw, ALX_MISC, misc); } retval = hw->cbs.read_phy_reg(hw, MII_LPA, &lpa); if (retval) return retval; adv_speed = ALX_LINK_SPEED_10_HALF; if (lpa & LPA_10FULL) adv_speed = ALX_LINK_SPEED_10_FULL; else if (lpa & LPA_10HALF) adv_speed = ALX_LINK_SPEED_10_HALF; else if (lpa & LPA_100FULL) adv_speed = ALX_LINK_SPEED_100_FULL; else if (lpa & LPA_100HALF) adv_speed = ALX_LINK_SPEED_100_HALF; retval = hw->cbs.setup_phy_link(hw, adv_speed, true, !hw->disable_fc_autoneg); if (retval) return retval; for (i = 0; i < ALX_MAX_SETUP_LNK_CYCLE; i++) { mdelay(100); retval = hw->cbs.check_phy_link(hw, &speed, &link_up); if (retval) continue; if (link_up) break; } } else { speed = ALX_LINK_SPEED_10_HALF; link_up = false; /* When link is disabled and PHY/MAC not programmed at all * Due to a suspected bug in HW, we dont get PHY UP interrupt * As a solution, program the MAC/PHY with 10Mbps HD link speed * even the there is no LINK Detected */ retval = hw->cbs.setup_phy_link(hw, speed, true, !hw->disable_fc_autoneg); if (retval) return retval; } hw->link_speed = speed; hw->link_up = link_up; retval = hw->cbs.config_wol(hw, wufc); if (retval) return retval; /* clear phy interrupt */ retval = hw->cbs.ack_phy_intr(hw); if (retval) return retval; if (wufc) { /* pcie patch */ device_set_wakeup_enable(&pdev->dev, 1); } retval = hw->cbs.config_pow_save(hw, adpt->hw.link_speed, (wufc ? true : false), false, (wufc & ALX_WOL_MAGIC ? true : false), true); if (retval) return retval; *wakeup = wufc ? true : false; pci_disable_device(pdev); return 0; } static void alx_shutdown(struct pci_dev *pdev) { bool wakeup; alx_shutdown_internal(pdev, &wakeup); pci_wake_from_d3(pdev, wakeup); pci_set_power_state(pdev, PCI_D3hot); } #ifdef CONFIG_PM_SLEEP static int alx_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct alx_adapter *adpt = pci_get_drvdata(pdev); int retval; bool wakeup; if (!pdev || !adpt) { pr_err("%s NULL Pointers pdev %p adpt %p",__func__,pdev,adpt); return -1; } retval = alx_shutdown_internal(pdev, &wakeup); if (retval) return retval; if (alx_ipa_rm_try_release(adpt)) pr_err("%s -- ODU PROD Release unsuccessful \n",__func__); else adpt->palx_ipa->ipa_prod_rm_state = ALX_IPA_RM_RELEASED; if (wakeup) { pci_prepare_to_sleep(pdev); } else { pci_wake_from_d3(pdev, false); pci_set_power_state(pdev, PCI_D3hot); } return 0; } static int alx_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct alx_adapter *adpt = pci_get_drvdata(pdev); struct net_device *netdev = adpt->netdev; struct alx_hw *hw = &adpt->hw; u32 retval; pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); /* * pci_restore_state clears dev->state_saved so call * pci_save_state to restore it. */ pci_save_state(pdev); pci_enable_pcie_error_reporting(pdev); pci_set_master(pdev); pci_enable_wake(pdev, PCI_D3hot, 0); pci_enable_wake(pdev, PCI_D3cold, 0); retval = hw->cbs.reset_pcie(hw, true, true); retval = hw->cbs.reset_phy(hw); retval = hw->cbs.reset_mac(hw); retval = hw->cbs.setup_phy_link(hw, hw->autoneg_advertised, true, !hw->disable_fc_autoneg); retval = hw->cbs.config_wol(hw, 0); if (netif_running(netdev)) { retval = alx_open_internal(adpt, 0); if (retval) return retval; } netif_device_attach(netdev); /* Hold the wake lock for 5sec to ensure any traffic*/ pm_wakeup_event(dev, 5000); return 0; } #endif /* * alx_update_hw_stats - Update the board statistics counters. */ void alx_update_hw_stats(struct alx_adapter *adpt) { struct net_device_stats *net_stats; struct alx_hw *hw = &adpt->hw; struct alx_hw_stats *hwstats = &adpt->hw_stats; unsigned long *hwstat_item = NULL; u32 hwstat_reg; u32 hwstat_data; if (CHK_ADPT_FLAG(1, STATE_DOWN) || CHK_ADPT_FLAG(1, STATE_RESETTING)) return; /* update RX status */ hwstat_reg = hw->rxstat_reg; hwstat_item = &hwstats->rx_ok; while (hwstat_reg < hw->rxstat_reg + hw->rxstat_sz) { alx_mem_r32(hw, hwstat_reg, &hwstat_data); *hwstat_item += hwstat_data; hwstat_reg += 4; hwstat_item++; } /* update TX status */ hwstat_reg = hw->txstat_reg; hwstat_item = &hwstats->tx_ok; while (hwstat_reg < hw->txstat_reg + hw->txstat_sz) { alx_mem_r32(hw, hwstat_reg, &hwstat_data); *hwstat_item += hwstat_data; hwstat_reg += 4; hwstat_item++; } net_stats = &adpt->netdev->stats; net_stats->rx_packets = hwstats->rx_ok; net_stats->tx_packets = hwstats->tx_ok; net_stats->rx_bytes = hwstats->rx_byte_cnt; net_stats->tx_bytes = hwstats->tx_byte_cnt; net_stats->multicast = hwstats->rx_mcast; net_stats->collisions = hwstats->tx_single_col + hwstats->tx_multi_col * 2 + hwstats->tx_late_col + hwstats->tx_abort_col; net_stats->rx_errors = hwstats->rx_frag + hwstats->rx_fcs_err + hwstats->rx_len_err + hwstats->rx_ov_sz + hwstats->rx_ov_rrd + hwstats->rx_align_err; net_stats->rx_fifo_errors = hwstats->rx_ov_rxf; net_stats->rx_length_errors = hwstats->rx_len_err; net_stats->rx_crc_errors = hwstats->rx_fcs_err; net_stats->rx_frame_errors = hwstats->rx_align_err; net_stats->rx_over_errors = hwstats->rx_ov_rrd + hwstats->rx_ov_rxf; net_stats->rx_missed_errors = hwstats->rx_ov_rrd + hwstats->rx_ov_rxf; net_stats->tx_errors = hwstats->tx_late_col + hwstats->tx_abort_col + hwstats->tx_underrun + hwstats->tx_trunc; net_stats->tx_fifo_errors = hwstats->tx_underrun; net_stats->tx_aborted_errors = hwstats->tx_abort_col; net_stats->tx_window_errors = hwstats->tx_late_col; } /* * alx_get_stats - Get System Network Statistics * * Returns the address of the device statistics structure. * The statistics are actually updated from the timer callback. */ static struct net_device_stats *alx_get_stats(struct net_device *netdev) { struct alx_adapter *adpt = netdev_priv(netdev); alx_update_hw_stats(adpt); return &netdev->stats; } #ifdef ALX_LINK_DOWN_CONFIG static int alx_link_mac_restore(struct alx_adapter *adpt) { struct alx_hw *hw = &adpt->hw; int retval = 0; int i; printk(KERN_INFO "alx: into NEW alx_link_mac_restore\n"); alx_init_ring_ptrs(adpt); alx_set_multicase_list(adpt->netdev); alx_restore_vlan(adpt); if (hw->cbs.config_mac) retval = hw->cbs.config_mac(hw, adpt->rxbuf_size, adpt->num_hw_rxques, adpt->num_rxdescs, adpt->num_txques, adpt->num_txdescs); if (hw->cbs.config_tx) retval = hw->cbs.config_tx(hw); if (hw->cbs.config_rx) retval = hw->cbs.config_rx(hw); alx_config_rss(adpt); for (i = 0; i < adpt->num_hw_rxques; i++) alx_refresh_rx_buffer(adpt->rx_queue[i]); /* configure HW regsiters of MSIX */ if (hw->cbs.config_msix) retval = hw->cbs.config_msix(hw, adpt->num_msix_intrs, CHK_ADPT_FLAG(0, MSIX_EN), CHK_ADPT_FLAG(0, MSI_EN)); return retval; } #endif static int alx_ipa_set_perf_level(void) { struct ipa_rm_perf_profile profile; struct alx_ipa_ctx *alx_ipa = galx_adapter_ptr->palx_ipa; int ret = 0; if (!alx_ipa) { pr_err("%s alx_ipa cts NULL ctx:%p\n",__func__,alx_ipa); return -1; } memset(&profile, 0, sizeof(profile)); profile.max_supported_bandwidth_mbps = MAX_AR8151_BW; ret = ipa_rm_set_perf_profile (IPA_RM_RESOURCE_ODU_ADAPT_PROD, &profile); if (ret) { pr_err("Err to set BW: IPA_RM_RESOURCE_ODU_ADAPT_PROD err:%d\n", ret); return ret; } ret = ipa_rm_set_perf_profile (IPA_RM_RESOURCE_ODU_ADAPT_CONS, &profile); if (ret) { pr_err("Err to set BW: IPA_RM_RESOURCE_ODU_ADAPT_CONS err:%d\n", ret); return ret; } alx_ipa->alx_ipa_perf_requested = true; return ret; } static void alx_link_task_routine(struct alx_adapter *adpt) { struct net_device *netdev = adpt->netdev; struct alx_hw *hw = &adpt->hw; char *link_desc; int ret = 0; struct alx_ipa_ctx *alx_ipa = adpt->palx_ipa; if (!CHK_ADPT_FLAG(0, TASK_LSC_REQ)) return; CLI_ADPT_FLAG(0, TASK_LSC_REQ); if (CHK_ADPT_FLAG(1, STATE_DOWN)) return; if (hw->cbs.check_phy_link) { hw->cbs.check_phy_link(hw, &hw->link_speed, &hw->link_up); } else { /* always assume link is up, if no check link function */ hw->link_speed = ALX_LINK_SPEED_1GB_FULL; hw->link_up = true; } netif_dbg(adpt, timer, adpt->netdev, "link_speed = %d, link_up = %d\n", hw->link_speed, hw->link_up); if (!hw->link_up && time_after(adpt->link_jiffies, jiffies)) SET_ADPT_FLAG(0, TASK_LSC_REQ); if (hw->link_up) { if (netif_carrier_ok(netdev)) return; link_desc = (hw->link_speed == ALX_LINK_SPEED_1GB_FULL) ? "1 Gbps Duplex Full" : (hw->link_speed == ALX_LINK_SPEED_100_FULL ? "100 Mbps Duplex Full" : (hw->link_speed == ALX_LINK_SPEED_100_HALF ? "100 Mbps Duplex Half" : (hw->link_speed == ALX_LINK_SPEED_10_FULL ? "10 Mbps Duplex Full" : (hw->link_speed == ALX_LINK_SPEED_10_HALF ? "10 Mbps Duplex HALF" : "unknown speed")))); netif_dbg(adpt, timer, adpt->netdev, "NIC Link is Up %s\n", link_desc); hw->cbs.config_aspm(hw, true, true); hw->cbs.start_mac(hw); netif_carrier_on(netdev); netif_tx_wake_all_queues(netdev); #ifdef MDM_PLATFORM /* Enable ODU Bridge */ if (alx_ipa->ipa_ready == true && CHK_ADPT_FLAG(2, ODU_INIT)) { ret = odu_bridge_connect(); if (ret) pr_err("Could not connect to ODU bridge %d \n", ret); else SET_ADPT_FLAG(2, ODU_CONNECT); /* Request for IPA Resources */ spin_lock_bh(&alx_ipa->ipa_rm_state_lock); if (alx_ipa->ipa_prod_rm_state == ALX_IPA_RM_RELEASED) { spin_unlock_bh(&alx_ipa->ipa_rm_state_lock); alx_ipa_rm_request(adpt); } else { spin_unlock_bh(&alx_ipa->ipa_rm_state_lock); } } #endif } else { /* only continue if link was up previously */ if (!netif_carrier_ok(netdev)) return; hw->link_speed = 0; netif_dbg(adpt, timer, adpt->netdev, "NIC Link is Down\n"); netif_carrier_off(netdev); netif_tx_stop_all_queues(netdev); #ifdef ALX_LINK_DOWN_CONFIG hw->cbs.reset_mac(hw); #else hw->cbs.stop_mac(hw); #endif hw->cbs.config_aspm(hw, false, true); hw->cbs.setup_phy_link(hw, hw->autoneg_advertised, true, !hw->disable_fc_autoneg); #ifdef ALX_LINK_DOWN_CONFIG alx_link_mac_restore(adpt); #endif #ifdef MDM_PLATFORM /* Disable ODU Bridge */ ret = odu_bridge_disconnect(); if (ret) { pr_err("Could not connect to ODU bridge %d \n", ret); } else { CLI_ADPT_FLAG(2, ODU_CONNECT); adpt->palx_ipa->alx_ipa_perf_requested = false; if (alx_ipa_rm_try_release(adpt)) pr_err("%s -- ODU PROD Release unsuccessful \n",__func__); } #endif } } static void alx_reinit_task_routine(struct alx_adapter *adpt) { if (!CHK_ADPT_FLAG(0, TASK_REINIT_REQ)) return; CLI_ADPT_FLAG(0, TASK_REINIT_REQ); if (CHK_ADPT_FLAG(1, STATE_DOWN) || CHK_ADPT_FLAG(1, STATE_RESETTING)) return; alx_reinit_locked(adpt); } /* * alx_timer_routine - Timer Call-back */ static void alx_timer_routine(unsigned long data) { struct alx_adapter *adpt = (struct alx_adapter *)data; unsigned long delay; #ifdef ALX_HIB_TASK_CONFIG struct alx_hw *hw = &adpt->hw; if (hw->bHibBug) hw->cbs.apply_phy_hib_patch(hw); #endif /* poll faster when waiting for link */ if (CHK_ADPT_FLAG(0, TASK_LSC_REQ)) delay = HZ / 10; else delay = HZ * 2; /* Reset the timer */ mod_timer(&adpt->alx_timer, delay + jiffies); alx_task_schedule(adpt); } /* * alx_task_routine - manages and runs subtasks */ static void alx_task_routine(struct work_struct *work) { struct alx_adapter *adpt = container_of(work, struct alx_adapter, alx_task); /* test state of adapter */ if (!CHK_ADPT_FLAG(1, STATE_WATCH_DOG)) netif_warn(adpt, timer, adpt->netdev, "flag STATE_WATCH_DOG doesn't set\n"); /* reinit task */ alx_reinit_task_routine(adpt); /* link task */ alx_link_task_routine(adpt); /* flush memory to make sure state is correct before next watchog */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) smp_mb(); #else smp_mb__before_clear_bit(); #endif CLI_ADPT_FLAG(1, STATE_WATCH_DOG); } /* * alx_ipa_send_routine - Sends packets to IPA/ODU bridge Driver * Scheduled on RX of IPA_WRITE_DONE Event */ static void alx_ipa_send_routine(struct work_struct *work) { struct alx_adapter *adpt = container_of(work, struct alx_adapter, ipa_send_task); struct alx_ipa_rx_desc_node *node = NULL; struct ipa_tx_meta ipa_meta = {0x0}; int ret =0; struct alx_ipa_ctx *alx_ipa = adpt->palx_ipa; /* Set the Perf level when the Request is granted */ if (!alx_ipa->alx_ipa_perf_requested) { spin_lock_bh(&alx_ipa->ipa_rm_state_lock); if (alx_ipa->ipa_prod_rm_state == ALX_IPA_RM_GRANTED) { spin_unlock_bh(&alx_ipa->ipa_rm_state_lock); alx_ipa_set_perf_level(); alx_ipa->alx_ipa_perf_requested = true; } else { spin_unlock_bh(&alx_ipa->ipa_rm_state_lock); } } /* Send all pending packets to IPA. Compute the number of desc left for HW and send packets accordingly*/ spin_lock_bh(&adpt->flow_ctrl_lock); CLI_ADPT_FLAG(2, WQ_SCHED); if (unlikely(!adpt->pendq_cnt)) { pr_err("%s - Error no pending packets in Queue %d\n", __func__, adpt->pendq_cnt); spin_unlock_bh(&adpt->flow_ctrl_lock); return; } if (adpt->ipa_free_desc_cnt < adpt->ipa_low_watermark) { adpt->palx_ipa->stats.ipa_low_watermark_cnt++; spin_unlock_bh(&adpt->flow_ctrl_lock); return; } while (adpt->ipa_free_desc_cnt && adpt->pendq_cnt) { node = list_first_entry(&adpt->pend_queue_head, struct alx_ipa_rx_desc_node, link); list_del(&node->link); list_add_tail(&node->link, &adpt->free_queue_head); adpt->freeq_cnt++; adpt->pendq_cnt--; ipa_meta.dma_address_valid = false; /* Send Packet to ODU bridge Driver */ ret = odu_bridge_tx_dp(node->skb_ptr, &ipa_meta); if (ret) { pr_err("odu_bridge_tx_dp() Failed in %s!!" " ret %d--Free SKB\n", __func__, ret); kfree(node->skb_ptr); adpt->palx_ipa->stats.rx_ipa_send_fail++; } else { adpt->palx_ipa->stats.rx_ipa_send++; adpt->ipa_free_desc_cnt--; /* Increment the ipa_rx_completion Counter */ spin_lock(&alx_ipa->rm_ipa_lock); if (alx_ipa->acquire_wake_src == false) { __pm_stay_awake(&alx_ipa->rm_ipa_wait); alx_ipa->acquire_wake_src = true; } alx_ipa->ipa_rx_completion++; spin_unlock(&alx_ipa->rm_ipa_lock); } } /* Release PROD if we dont have any more data to send*/ if (adpt->pendq_cnt == 0) alx_ipa_rm_try_release(adpt); spin_unlock_bh(&adpt->flow_ctrl_lock); } /* Calculate the transmit packet descript needed*/ static bool alx_check_num_tpdescs(struct alx_tx_queue *txque, const struct sk_buff *skb) { u16 num_required = 1; u16 num_available = 0; u16 produce_idx = txque->tpq.produce_idx; u16 consume_idx = txque->tpq.consume_idx; int i = 0; u16 proto_hdr_len = 0; if (skb_is_gso(skb)) { proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); if (proto_hdr_len < skb_headlen(skb)) num_required++; if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) num_required++; } for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) num_required++; num_available = (u16)(consume_idx > produce_idx) ? (consume_idx - produce_idx - 1) : (txque->tpq.count + consume_idx - produce_idx - 1); return num_required < num_available; } static int alx_tso_csum(struct alx_adapter *adpt, struct alx_tx_queue *txque, struct sk_buff *skb, union alx_sw_tpdesc *stpd) { struct pci_dev *pdev = adpt->pdev; u8 hdr_len; int retval; if (skb_is_gso(skb)) { if (skb_header_cloned(skb)) { retval = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); if (unlikely(retval)) return retval; } if (skb->protocol == htons(ETH_P_IP)) { u32 pkt_len = ((unsigned char *)ip_hdr(skb) - skb->data) + ntohs(ip_hdr(skb)->tot_len); if (skb->len > pkt_len) pskb_trim(skb, pkt_len); } hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); if (unlikely(skb->len == hdr_len)) { /* we only need to do csum */ dev_warn(&pdev->dev, "tso doesn't need, if packet with 0 data\n"); goto do_csum; } if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) { ip_hdr(skb)->check = 0; tcp_hdr(skb)->check = ~csum_tcpudp_magic( ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, 0, IPPROTO_TCP, 0); stpd->genr.ipv4 = 1; } if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) { /* ipv6 tso need an extra tpd */ union alx_sw_tpdesc extra_tpd; memset(stpd, 0, sizeof(union alx_sw_tpdesc)); memset(&extra_tpd, 0, sizeof(union alx_sw_tpdesc)); ipv6_hdr(skb)->payload_len = 0; tcp_hdr(skb)->check = ~csum_ipv6_magic( &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, 0, IPPROTO_TCP, 0); extra_tpd.tso.pkt_len = skb->len; extra_tpd.tso.lso = 0x1; extra_tpd.tso.lso_v2 = 0x1; alx_set_tpdesc(txque, &extra_tpd); stpd->tso.lso_v2 = 0x1; } stpd->tso.lso = 0x1; stpd->tso.tcphdr_offset = skb_transport_offset(skb); stpd->tso.mss = skb_shinfo(skb)->gso_size; return 0; } do_csum: if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { u8 css, cso; cso = skb_checksum_start_offset(skb); if (unlikely(cso & 0x1)) { dev_err(&pdev->dev, "pay load offset should not be an " "event number\n"); return -1; } else { css = cso + skb->csum_offset; stpd->csum.payld_offset = cso >> 1; stpd->csum.cxsum_offset = css >> 1; stpd->csum.c_csum = 0x1; } } return 0; } static void alx_tx_map(struct alx_adapter *adpt, struct alx_tx_queue *txque, struct sk_buff *skb, union alx_sw_tpdesc *stpd) { struct alx_buffer *tpbuf = NULL; unsigned int nr_frags = skb_shinfo(skb)->nr_frags; unsigned int len = skb_headlen(skb); u16 map_len = 0; u16 mapped_len = 0; u16 hdr_len = 0; u16 f; u32 tso = stpd->tso.lso; if (tso) { /* TSO */ map_len = hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); tpbuf = GET_TP_BUFFER(txque, txque->tpq.produce_idx); tpbuf->length = map_len; tpbuf->dma = dma_map_single(txque->dev, skb->data, hdr_len, DMA_TO_DEVICE); mapped_len += map_len; stpd->genr.addr = tpbuf->dma; stpd->genr.buffer_len = tpbuf->length; alx_set_tpdesc(txque, stpd); } if (mapped_len < len) { tpbuf = GET_TP_BUFFER(txque, txque->tpq.produce_idx); tpbuf->length = len - mapped_len; tpbuf->dma = dma_map_single(txque->dev, skb->data + mapped_len, tpbuf->length, DMA_TO_DEVICE); stpd->genr.addr = tpbuf->dma; stpd->genr.buffer_len = tpbuf->length; alx_set_tpdesc(txque, stpd); } for (f = 0; f < nr_frags; f++) { struct skb_frag_struct *frag; frag = &skb_shinfo(skb)->frags[f]; tpbuf = GET_TP_BUFFER(txque, txque->tpq.produce_idx); tpbuf->length = skb_frag_size(frag); tpbuf->dma = skb_frag_dma_map(txque->dev, frag, 0, tpbuf->length, DMA_TO_DEVICE); stpd->genr.addr = tpbuf->dma; stpd->genr.buffer_len = tpbuf->length; alx_set_tpdesc(txque, stpd); } /* The last tpd */ alx_set_tpdesc_lastfrag(txque); /* * The last buffer info contain the skb address, * so it will be free after unmap */ if (tpbuf) tpbuf->skb = skb; } static netdev_tx_t alx_start_xmit_frame(struct alx_adapter *adpt, struct alx_tx_queue *txque, struct sk_buff *skb) { struct alx_hw *hw = &adpt->hw; unsigned long flags = 0; union alx_sw_tpdesc stpd; /* normal*/ #ifdef MDM_PLATFORM struct alx_ipa_ctx *alx_ipa = adpt->palx_ipa; #endif if (CHK_ADPT_FLAG(1, STATE_DOWN) || CHK_ADPT_FLAG(1, STATE_DIAG_RUNNING)) { dev_kfree_skb_any(skb); return NETDEV_TX_OK; } if (!spin_trylock_irqsave(&adpt->tx_lock, flags)) { alx_err(adpt, "tx locked!\n"); return NETDEV_TX_LOCKED; } if (!alx_check_num_tpdescs(txque, skb)) { /* no enough descriptor, just stop queue */ netif_stop_queue(adpt->netdev); spin_unlock_irqrestore(&adpt->tx_lock, flags); alx_err(adpt, "No TX Desc to send packet\n"); return NETDEV_TX_BUSY; } memset(&stpd, 0, sizeof(union alx_sw_tpdesc)); /* do TSO and check sum */ if (alx_tso_csum(adpt, txque, skb, &stpd) != 0) { spin_unlock_irqrestore(&adpt->tx_lock, flags); dev_kfree_skb_any(skb); return NETDEV_TX_OK; } if (unlikely(vlan_tx_tag_present(skb))) { u16 vlan = vlan_tx_tag_get(skb); u16 tag; ALX_VLAN_TO_TAG(vlan, tag); stpd.genr.vlan_tag = tag; stpd.genr.instag = 0x1; } if (skb_network_offset(skb) != ETH_HLEN) stpd.genr.type = 0x1; /* Ethernet frame */ alx_tx_map(adpt, txque, skb, &stpd); /* update produce idx */ wmb(); alx_mem_w16(hw, txque->produce_reg, txque->tpq.produce_idx); netif_dbg(adpt, tx_err, adpt->netdev, "TX[%d]: tpq.consume_idx = 0x%x, tpq.produce_idx = 0x%x\n", txque->que_idx, txque->tpq.consume_idx, txque->tpq.produce_idx); netif_dbg(adpt, tx_err, adpt->netdev, "TX[%d]: Produce Reg[%x] = 0x%x\n", txque->que_idx, txque->produce_reg, txque->tpq.produce_idx); spin_unlock_irqrestore(&adpt->tx_lock, flags); #ifdef MDM_PLATFORM /* Hold on to the wake lock for TX Completion Event */ spin_lock_bh(&alx_ipa->rm_ipa_lock); if (alx_ipa->acquire_wake_src == false) { __pm_stay_awake(&alx_ipa->rm_ipa_wait); alx_ipa->acquire_wake_src = true; } alx_ipa->alx_tx_completion++; spin_unlock_bh(&alx_ipa->rm_ipa_lock); #endif return NETDEV_TX_OK; } static netdev_tx_t alx_start_xmit(struct sk_buff *skb, struct net_device *netdev) { struct alx_adapter *adpt = netdev_priv(netdev); struct alx_tx_queue *txque; txque = adpt->tx_queue[0]; return alx_start_xmit_frame(adpt, txque, skb); } /* * alx_mii_ioctl */ static int alx_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { struct alx_adapter *adpt = netdev_priv(netdev); struct alx_hw *hw = &adpt->hw; struct mii_ioctl_data *data = if_mii(ifr); int retval = 0; if (!netif_running(netdev)) return -EINVAL; switch (cmd) { case SIOCGMIIPHY: data->phy_id = 0; break; case SIOCGMIIREG: if (data->reg_num & ~(0x1F)) { retval = -EFAULT; goto out; } retval = hw->cbs.read_phy_reg(hw, data->reg_num, &data->val_out); netif_dbg(adpt, hw, adpt->netdev, "read phy %02x %04x\n", data->reg_num, data->val_out); if (retval) { retval = -EIO; goto out; } break; case SIOCSMIIREG: if (data->reg_num & ~(0x1F)) { retval = -EFAULT; goto out; } retval = hw->cbs.write_phy_reg(hw, data->reg_num, data->val_in); netif_dbg(adpt, hw, adpt->netdev, "write phy %02x %04x\n", data->reg_num, data->val_in); if (retval) { retval = -EIO; goto out; } break; default: retval = -EOPNOTSUPP; break; } out: return retval; } static int alx_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { switch (cmd) { case SIOCGMIIPHY: case SIOCGMIIREG: case SIOCSMIIREG: return alx_mii_ioctl(netdev, ifr, cmd); default: return -EOPNOTSUPP; } } #ifdef CONFIG_NET_POLL_CONTROLLER static void alx_poll_controller(struct net_device *netdev) { struct alx_adapter *adpt = netdev_priv(netdev); int num_msix_intrs = adpt->num_msix_intrs; int msix_idx; /* if interface is down do nothing */ if (CHK_ADPT_FLAG(1, STATE_DOWN)) return; if (CHK_ADPT_FLAG(0, MSIX_EN)) { for (msix_idx = 0; msix_idx < num_msix_intrs; msix_idx++) { struct alx_msix_param *msix = adpt->msix[msix_idx]; if (CHK_MSIX_FLAG(RXS) || CHK_MSIX_FLAG(TXS)) alx_msix_rtx(0, msix); else if (CHK_MSIX_FLAG(TIMER)) alx_msix_timer(0, msix); else if (CHK_MSIX_FLAG(ALERT)) alx_msix_alert(0, msix); else if (CHK_MSIX_FLAG(SMB)) alx_msix_smb(0, msix); else if (CHK_MSIX_FLAG(PHY)) alx_msix_phy(0, msix); } } else { alx_interrupt(adpt->pdev->irq, netdev); } } #endif static const struct net_device_ops alx_netdev_ops = { .ndo_open = alx_open, .ndo_stop = alx_stop, .ndo_start_xmit = alx_start_xmit, .ndo_get_stats = alx_get_stats, .ndo_set_rx_mode = alx_set_multicase_list, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = alx_set_mac_address, .ndo_change_mtu = alx_change_mtu, .ndo_do_ioctl = alx_ioctl, .ndo_tx_timeout = alx_tx_timeout, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) .ndo_fix_features = alx_fix_features, .ndo_set_features = alx_set_features, #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = alx_poll_controller, #endif }; #ifdef MDM_PLATFORM static void alx_ipa_tx_dp_cb(void *priv, enum ipa_dp_evt_type evt, unsigned long data) { struct alx_adapter *adpt = priv; struct alx_ipa_ctx *alx_ipa = adpt->palx_ipa; struct sk_buff *skb = (struct sk_buff *)data; bool schedule_ipa_work = false; if (!CHK_ADPT_FLAG(2, ODU_CONNECT)) { pr_err("%s called before ODU_CONNECT was called with evt %d \n", __func__, evt); return; } pr_debug("%s %d EVT Rcvd %d \n", __func__, __LINE__, evt); if (evt == IPA_RECEIVE) { /* Deliver SKB to network adapter */ alx_ipa->stats.rx_ipa_excep++; skb->dev = adpt->netdev; skb->protocol = eth_type_trans(skb, skb->dev); /* Prevent device to goto suspend for 200 msec; to provide enough time for packet to be processed by network stack */ pm_wakeup_event(&adpt->pdev->dev, 200); netif_rx_ni(skb); } else if (evt == IPA_WRITE_DONE) { /* SKB send to IPA, safe to free */ alx_ipa->stats.rx_ipa_write_done++; dev_kfree_skb(skb); spin_lock_bh(&adpt->flow_ctrl_lock); adpt->ipa_free_desc_cnt++; /* Decrement the ipa_rx_completion Counter */ spin_lock_bh(&alx_ipa->rm_ipa_lock); alx_ipa->ipa_rx_completion--; if (!alx_ipa->ipa_rx_completion && !alx_ipa->alx_tx_completion && (alx_ipa->acquire_wake_src == true)) { __pm_relax(&alx_ipa->rm_ipa_wait); alx_ipa->acquire_wake_src = false; } spin_unlock_bh(&alx_ipa->rm_ipa_lock); if ((adpt->pendq_cnt > 0) && (adpt->ipa_free_desc_cnt < adpt->ipa_low_watermark)) { alx_ipa->stats.ipa_low_watermark_cnt++; } else if ((adpt->pendq_cnt > 0) && (adpt->ipa_free_desc_cnt >= adpt->ipa_low_watermark) && !CHK_ADPT_FLAG(2, WQ_SCHED)) { schedule_ipa_work = true; } spin_unlock_bh(&adpt->flow_ctrl_lock); if (schedule_ipa_work) { SET_ADPT_FLAG(2, WQ_SCHED); schedule_work(&adpt->ipa_send_task); } } } static void alx_ipa_tx_dl(void *priv, struct sk_buff *skb) { struct alx_adapter *adpt = priv; struct alx_ipa_ctx *alx_ipa = adpt->palx_ipa; netdev_tx_t ret = __NETDEV_TX_MIN; if (!CHK_ADPT_FLAG(2, ODU_CONNECT)) { pr_err("%s called before ODU_CONNECT was called! \n",__func__); return; } pr_debug("%s %d SKB Send to line \n",__func__,__LINE__); if ((ret = alx_start_xmit(skb, adpt->netdev)) != NETDEV_TX_OK) { pr_err("%s alx_ipa_tx_dl() failed xmit returned %d \n", __func__, ret); alx_ipa->stats.tx_ipa_send_err++; dev_kfree_skb_any(skb); } else { /* Deliver SKB to HW */ alx_ipa->stats.tx_ipa_send++; } } static void alx_ipa_ready_cb(struct alx_adapter *adpt) { struct alx_ipa_ctx *alx_ipa = adpt->palx_ipa; struct odu_bridge_params *params_ptr, params; int retval = 0; struct alx_hw *hw = &adpt->hw; params_ptr = ¶ms; pr_info("%s:%d --- IPA is ready --- \n",__func__,__LINE__); alx_ipa->ipa_ready = true; /* Init IPA Resources */ if (alx_ipa_setup_rm(adpt)) { pr_err("ALX: IPA Setup RM Failed \n"); return; } else { SET_ADPT_FLAG(2, IPA_RM); } /* Initialize the ODU bridge driver now: odu_bridge_init()*/ params_ptr->netdev_name = adpt->netdev->name; params_ptr->priv = adpt; params.tx_dp_notify = alx_ipa_tx_dp_cb; params_ptr->send_dl_skb = (void *)&alx_ipa_tx_dl; memcpy(params_ptr->device_ethaddr, adpt->netdev->dev_addr, ETH_ALEN); /* The maximum number of descriptors that can be provided to a BAM at * once is one less than the total number of descriptors that the buffer * can contain. */ params_ptr->ipa_desc_size = (adpt->ipa_high_watermark + 1) * sizeof(struct sps_iovec); retval = odu_bridge_init(params_ptr); if (retval) { pr_err("Couldnt initialize ODU_Bridge Driver \n"); return; } else { SET_ADPT_FLAG(2, ODU_INIT); } /* Check for link phy state */ if (hw->link_up) { /* Enable ODU Bridge */ if (CHK_ADPT_FLAG(2, ODU_INIT)) { retval = odu_bridge_connect(); if (retval) { pr_err("Could not connect to ODU bridge %d \n", retval); return; } else { SET_ADPT_FLAG(2, ODU_CONNECT); } spin_lock_bh(&alx_ipa->ipa_rm_state_lock); if (alx_ipa->ipa_prod_rm_state == ALX_IPA_RM_RELEASED) { spin_unlock_bh(&alx_ipa->ipa_rm_state_lock); alx_ipa_rm_request(adpt); } else { spin_unlock_bh(&alx_ipa->ipa_rm_state_lock); } } } } static ssize_t alx_ipa_debugfs_read_ipa_stats(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct alx_adapter *adpt = file->private_data; struct alx_ipa_ctx *alx_ipa = adpt->palx_ipa; char *buf; unsigned int len = 0, buf_len = 2000; ssize_t ret_cnt; u16 pendq_cnt, freeq_cnt, ipa_free_desc_cnt; u16 max_pkts_allowed, min_pkts_allowed; if (unlikely(!alx_ipa)) { pr_err(" %s NULL Pointer \n",__func__); return -EINVAL; } if (!CHK_ADPT_FLAG(2, DEBUGFS_INIT)) return 0; buf = kzalloc(buf_len, GFP_KERNEL); if (!buf) return -ENOMEM; len += scnprintf(buf + len, buf_len - len, "\n \n"); len += scnprintf(buf + len, buf_len - len, "%25s\n", "ALX IPA stats"); len += scnprintf(buf + len, buf_len - len, "%25s\n\n", "=================================================="); len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n", "IPA RX Pkt Send: ", alx_ipa->stats.rx_ipa_send); len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n", "IPA RX IPA Send Fail: ", alx_ipa->stats.rx_ipa_send_fail); len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n", "IPA RX Write done: ", alx_ipa->stats.rx_ipa_write_done); len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n", "IPA RX Exception: ", alx_ipa->stats.rx_ipa_excep); len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n", "IPA TX Send: ", alx_ipa->stats.tx_ipa_send); len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n", "IPA TX Send Err: ", alx_ipa->stats.tx_ipa_send_err); len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n", "Non-IP or Frag RX Pkt: ", alx_ipa->stats.non_ip_frag_pkt); spin_lock_bh(&adpt->flow_ctrl_lock); pendq_cnt = adpt->pendq_cnt; freeq_cnt = adpt->freeq_cnt; ipa_free_desc_cnt = adpt->ipa_free_desc_cnt; max_pkts_allowed = adpt->ipa_high_watermark; min_pkts_allowed = adpt->ipa_low_watermark; spin_unlock_bh(&adpt->flow_ctrl_lock); len += scnprintf(buf + len, buf_len - len, "%25s %10u\n", "ALX Pending Queue Count: ", pendq_cnt); len += scnprintf(buf + len, buf_len - len, "%25s %10u\n", "ALX Free Queue Count: ", freeq_cnt); len += scnprintf(buf + len, buf_len - len, "%25s %10u\n", "IPA Free Queue Count: ", ipa_free_desc_cnt); len += scnprintf(buf + len, buf_len - len, "%25s %10u\n", "IPA High Watermark: ", max_pkts_allowed); len += scnprintf(buf + len, buf_len - len, "%25s %10u\n", "IPA Low Watermark: ", min_pkts_allowed); len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n", "IPA Low Watermark Count: ", alx_ipa->stats.ipa_low_watermark_cnt); len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n", "IPA Flow Ctrl Pkt Drop: ", alx_ipa->stats.flow_control_pkt_drop); len += scnprintf(buf + len, buf_len - len, "\n \n"); len += scnprintf(buf + len, buf_len - len, "%25s %s\n", "Data Path IPA Enabled: ",CHK_ADPT_FLAG(2, ODU_CONNECT)?"True":"False"); len += scnprintf(buf + len, buf_len - len, "<------------------ ALX RM STATS ------------------>\n"); len += scnprintf(buf + len, buf_len - len, "%25s %s\n", "IPA PROD RM State: ", alx_ipa_rm_state_to_str(alx_ipa->ipa_prod_rm_state)); len += scnprintf(buf + len, buf_len - len, "%25s %s\n", "IPA CONS RM State: ", alx_ipa_rm_state_to_str(alx_ipa->ipa_cons_rm_state)); len += scnprintf(buf + len, buf_len - len, "%25s %s\n", "IPA Perf Requested:",alx_ipa->alx_ipa_perf_requested?"True":"False"); len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n", "IPA RX Completions Events Remaining: ", alx_ipa->ipa_rx_completion); len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n", "ALX TX Completions Events Remaining: ", alx_ipa->alx_tx_completion); if (len > buf_len) len = buf_len; ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return ret_cnt; } static ssize_t alx_ipa_debugfs_disable_ipa(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct alx_adapter *adpt = galx_adapter_ptr; struct alx_ipa_ctx *alx_ipa = adpt->palx_ipa; int ret = 0; char *buf; unsigned int buf_len = 100; unsigned long missing; s8 value; if (unlikely(!alx_ipa)) { pr_err(" %s NULL Pointer \n",__func__); return -EINVAL; } if (!CHK_ADPT_FLAG(2, DEBUGFS_INIT)) return 0; buf = kzalloc(buf_len, GFP_KERNEL); if (!buf) return -ENOMEM; missing = copy_from_user(buf, user_buf, count); if (missing) return -EFAULT; buf[count] = '\0'; if (kstrtos8(buf, 0, &value)) return -EFAULT; /* Only Booloean values allowed */ if (value != 0 && value != 1) return -EFAULT; if ((value == 0) && CHK_ADPT_FLAG(2, ODU_CONNECT)) { /* Disable ODU Bridge */ ret = odu_bridge_disconnect(); pr_info("ALX: Disabling IPA Data Path \n"); if (ret) pr_err("Could not connect to ODU bridge %d \n", ret); else CLI_ADPT_FLAG(2, ODU_CONNECT); } if ((value == 1) && !CHK_ADPT_FLAG(2, ODU_CONNECT)) { /* Enable ODU Bridge */ pr_info("ALX: Enabling IPA Data Path \n"); ret = odu_bridge_connect(); if (ret) pr_err("Could not connect to ODU bridge %d \n", ret); else SET_ADPT_FLAG(2, ODU_CONNECT); /* Request for IPA Resources */ alx_ipa_rm_request(galx_adapter_ptr); } debugfs_ipa_enable = value; return count; } static ssize_t alx_ipa_debugfs_disable_ipa_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct alx_adapter *adpt = file->private_data; ssize_t ret_cnt; char* buf = NULL; unsigned int buf_len = 100; if (unlikely(!adpt)) { pr_err(" %s NULL Pointer \n",__func__); return -EINVAL; } if (!CHK_ADPT_FLAG(2, DEBUGFS_INIT)) return 0; buf = kzalloc(buf_len, GFP_KERNEL); if ( !buf ) return -ENOMEM; ret_cnt = scnprintf(buf, buf_len, "%d\n",debugfs_ipa_enable); return simple_read_from_buffer(user_buf, count, ppos, buf, ret_cnt); } static const struct file_operations fops_ipa_stats = { .read = alx_ipa_debugfs_read_ipa_stats, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static const struct file_operations fops_ipa_disable = { .write = alx_ipa_debugfs_disable_ipa, .read = alx_ipa_debugfs_disable_ipa_read, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; static int alx_debugfs_init(struct alx_adapter *adpt) { adpt->palx_ipa->debugfs_dir = debugfs_create_dir("alx", 0); if (!adpt->palx_ipa->debugfs_dir) return -ENOMEM; debugfs_create_file("stats", S_IRUSR, adpt->palx_ipa->debugfs_dir, adpt, &fops_ipa_stats); debugfs_create_file("ipa_enable", read_write_mode, adpt->palx_ipa->debugfs_dir, adpt, &fops_ipa_disable); return 0; } void alx_debugfs_exit(struct alx_adapter *adpt) { if (adpt->palx_ipa->debugfs_dir) debugfs_remove_recursive(adpt->palx_ipa->debugfs_dir); } /* static void alx_ipa_process_evt(int evt, void *priv) { *** When the RM Request is granted then send packet to IPA *** But we might not use this since we send the packet to odu_bridge *** and may be ODU bridge will request for IPA_RM_GRANT *** Need to confirm with ady } */ static void alx_ipa_rm_notify(void *user_data, enum ipa_rm_event event, unsigned long data) { struct alx_adapter *adpt = user_data; struct alx_ipa_ctx *alx_ipa = adpt->palx_ipa; pr_debug(" %s IPA RM Evt: %d alx_ipa->ipa_prod_rm_state %s\n",__func__, event, alx_ipa_rm_state_to_str(alx_ipa->ipa_prod_rm_state)); switch(event) { case IPA_RM_RESOURCE_GRANTED: pr_debug("%s:%d IPA_RM_RESOURCE_GRANTED \n",__func__,__LINE__); spin_lock_bh(&alx_ipa->ipa_rm_state_lock); if (alx_ipa->ipa_prod_rm_state == ALX_IPA_RM_GRANTED) { spin_unlock_bh(&alx_ipa->ipa_rm_state_lock); alx_err(adpt,"%s ERR:RM_GRANTED RCVD but rm_state already Granted\n", __func__); break; } alx_ipa->ipa_prod_rm_state = ALX_IPA_RM_GRANTED; spin_unlock_bh(&alx_ipa->ipa_rm_state_lock); /* Use Send task as a deffered way to request for IPA RM */ if (!alx_ipa->alx_ipa_perf_requested) { schedule_work(&adpt->ipa_send_task); break; } spin_lock_bh(&adpt->flow_ctrl_lock); if (adpt->pendq_cnt && !CHK_ADPT_FLAG(2, WQ_SCHED)) { SET_ADPT_FLAG(2, WQ_SCHED); spin_unlock_bh(&adpt->flow_ctrl_lock); schedule_work(&adpt->ipa_send_task); } else { spin_unlock_bh(&adpt->flow_ctrl_lock); alx_err(adpt,"%s -- ERR RM_GRANTED RCVD but pendq_cnt %d, WQ_SCHED:%s\n", __func__,adpt->pendq_cnt,CHK_ADPT_FLAG(2, WQ_SCHED)?"true":"false"); } break; case IPA_RM_RESOURCE_RELEASED: spin_lock_bh(&alx_ipa->ipa_rm_state_lock); alx_ipa->ipa_prod_rm_state = ALX_IPA_RM_RELEASED; spin_unlock_bh(&alx_ipa->ipa_rm_state_lock); pr_debug("%s -- IPA RM Release \n",__func__); break; default: pr_err("Unknown RM Evt: %d", event); break; } } static int alx_ipa_rm_cons_request(void) { struct alx_adapter *adpt = galx_adapter_ptr; struct alx_ipa_ctx *alx_ipa = NULL; pr_info("-- %s:%d -- \n",__func__,__LINE__); if (!adpt) { pr_err("%s --- adpt NULL pointer\n",__func__); return 0; } alx_ipa = adpt->palx_ipa; spin_lock_bh(&alx_ipa->ipa_rm_state_lock); if (alx_ipa->ipa_cons_rm_state != ALX_IPA_RM_REQUESTED) { alx_ipa->ipa_cons_rm_state = ALX_IPA_RM_REQUESTED; spin_unlock_bh(&alx_ipa->ipa_rm_state_lock); } else { spin_unlock_bh(&alx_ipa->ipa_rm_state_lock); pr_err("%s -- IPA RM CONS state = Requested %d." "Missed a release request\n" ,__func__, alx_ipa->ipa_cons_rm_state); } /* Prevent device to goto suspend for 200 msec; to provide enough time for IPA to send packets to us */ pm_wakeup_event(&adpt->pdev->dev, 200); return 0; } static int alx_ipa_rm_cons_release(void) { struct alx_adapter *adpt = galx_adapter_ptr; struct alx_ipa_ctx *alx_ipa = NULL; pr_info("-- %s:%d -- \n",__func__,__LINE__); if (!adpt) { pr_err("%s --- adpt NULL pointer\n",__func__); return 0; } alx_ipa = adpt->palx_ipa; spin_lock_bh(&alx_ipa->ipa_rm_state_lock); if (alx_ipa->ipa_cons_rm_state != ALX_IPA_RM_RELEASED) { alx_ipa->ipa_cons_rm_state = ALX_IPA_RM_RELEASED; spin_unlock_bh(&alx_ipa->ipa_rm_state_lock); } else { spin_unlock_bh(&alx_ipa->ipa_rm_state_lock); pr_err("%s -- IPA RM CONS state = Requested %d." "Missed a release request\n" ,__func__, alx_ipa->ipa_cons_rm_state); } return 0; } static int alx_ipa_setup_rm(struct alx_adapter *adpt) { struct ipa_rm_create_params create_params = {0}; int ret; struct alx_ipa_ctx *alx_ipa = adpt->palx_ipa; memset(&create_params, 0, sizeof(create_params)); create_params.name = IPA_RM_RESOURCE_ODU_ADAPT_PROD; create_params.reg_params.user_data = adpt; create_params.reg_params.notify_cb = alx_ipa_rm_notify; create_params.floor_voltage = IPA_VOLTAGE_SVS; ret = ipa_rm_create_resource(&create_params); if (ret) { pr_err("Create ODU PROD RM resource failed: %d \n", ret); goto prod_fail; } ipa_rm_add_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD, IPA_RM_RESOURCE_APPS_CONS); ret = ipa_rm_inactivity_timer_init(IPA_RM_RESOURCE_ODU_ADAPT_PROD, ALX_IPA_INACTIVITY_DELAY_MS); if (ret) { pr_err("Create ODU PROD RM inactivity timer failed: %d \n", ret); goto delete_prod; } memset(&create_params, 0, sizeof(create_params)); create_params.name = IPA_RM_RESOURCE_ODU_ADAPT_CONS; create_params.request_resource= alx_ipa_rm_cons_request; create_params.release_resource= alx_ipa_rm_cons_release; create_params.floor_voltage = IPA_VOLTAGE_SVS; ret = ipa_rm_create_resource(&create_params); if (ret) { pr_err("Create ODU CONS RM resource failed: %d \n", ret); goto delete_prod; } alx_ipa_set_perf_level(); /* Initialize IPA RM State variables */ spin_lock_init(&alx_ipa->ipa_rm_state_lock); spin_lock_init(&alx_ipa->rm_ipa_lock); alx_ipa->ipa_rx_completion = 0; alx_ipa->alx_tx_completion = 0; alx_ipa->acquire_wake_src = 0; alx_ipa->ipa_prod_rm_state = ALX_IPA_RM_RELEASED; alx_ipa->ipa_cons_rm_state = ALX_IPA_RM_RELEASED; wakeup_source_init(&alx_ipa->rm_ipa_wait, "alx_ipa_completion_wake_source"); return ret; delete_prod: ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD); prod_fail: return ret; } static void alx_ipa_cleanup_rm(struct alx_adapter *adpt) { int ret; struct alx_ipa_ctx *alx_ipa = adpt->palx_ipa; ret = ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD); if (ret) pr_err("Resource:IPA_RM_RESOURCE_ODU_ADAPT_PROD del fail %d\n", ret); ret = ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS); if (ret) pr_err("Resource:IPA_RM_RESOURCE_ODU_ADAPT_CONS del fail %d\n", ret); if (!ret) { /* ReInitialize IPA RM State to be Released */ alx_ipa->ipa_prod_rm_state = ALX_IPA_RM_RELEASED; alx_ipa->ipa_cons_rm_state = ALX_IPA_RM_RELEASED; wakeup_source_trash(&alx_ipa->rm_ipa_wait); alx_ipa->ipa_rx_completion = 0; alx_ipa->alx_tx_completion = 0; } } /* Request IPA RM Resource for ODU_PROD */ static int alx_ipa_rm_request(struct alx_adapter *adpt) { int ret = 0; struct alx_ipa_ctx *alx_ipa = adpt->palx_ipa; pr_debug("%s - IPA RM PROD State state %s\n",__func__, alx_ipa_rm_state_to_str(alx_ipa->ipa_prod_rm_state)); spin_lock_bh(&alx_ipa->ipa_rm_state_lock); switch (alx_ipa->ipa_prod_rm_state) { case ALX_IPA_RM_GRANTED: spin_unlock_bh(&alx_ipa->ipa_rm_state_lock); return 0; case ALX_IPA_RM_GRANT_PENDING: spin_unlock_bh(&alx_ipa->ipa_rm_state_lock); return -EINPROGRESS; case ALX_IPA_RM_RELEASED: alx_ipa->ipa_prod_rm_state = ALX_IPA_RM_GRANT_PENDING; break; default: spin_unlock_bh(&alx_ipa->ipa_rm_state_lock); pr_err("%s - IPA RM PROD State state %s\n",__func__, alx_ipa_rm_state_to_str(alx_ipa->ipa_prod_rm_state)); return -EINVAL; } spin_unlock_bh(&alx_ipa->ipa_rm_state_lock); ret = ipa_rm_inactivity_timer_request_resource( IPA_RM_RESOURCE_ODU_ADAPT_PROD); if (ret == -EINPROGRESS) { pr_debug("%s - IPA RM PROD State state %s, wake %d \n",__func__, alx_ipa_rm_state_to_str(alx_ipa->ipa_prod_rm_state), alx_ipa->acquire_wake_src); /* Acquire the IPA TX Complete wait lock; Since we will only request for grant if we want to send packets*/ spin_lock_bh(&alx_ipa->rm_ipa_lock); if (alx_ipa->acquire_wake_src == false) { __pm_stay_awake(&alx_ipa->rm_ipa_wait); alx_ipa->acquire_wake_src = true; } spin_unlock_bh(&alx_ipa->rm_ipa_lock); ret = 0; } else if (ret == 0) { spin_lock_bh(&alx_ipa->ipa_rm_state_lock); alx_ipa->ipa_prod_rm_state = ALX_IPA_RM_GRANTED; spin_unlock_bh(&alx_ipa->ipa_rm_state_lock); pr_debug("%s - IPA RM PROD State state %s\n",__func__, alx_ipa_rm_state_to_str(alx_ipa->ipa_prod_rm_state)); } else { pr_err("%s -- IPA RM Request failed ret=%d\n",__func__, ret); } return ret; } /* Release IPA RM Resource for ODU_PROD if not needed*/ static int alx_ipa_rm_try_release(struct alx_adapter *adpt) { pr_debug("%s:%d \n",__func__,__LINE__); return ipa_rm_inactivity_timer_release_resource( IPA_RM_RESOURCE_ODU_ADAPT_PROD); } #endif /* * alx_init - Device Initialization Routine */ static int __devinit alx_init(struct pci_dev *pdev, const struct pci_device_id *ent) { struct net_device *netdev; struct alx_adapter *adpt = NULL; struct alx_hw *hw = NULL; #ifdef MDM_PLATFORM struct alx_ipa_ctx *alx_ipa = NULL; #endif static int cards_found; int retval; struct odu_bridge_params *params_ptr, params; params_ptr = ¶ms; #ifdef MDM_PLATFORM retval = msm_pcie_pm_control(MSM_PCIE_RESUME, pdev->bus->number, pdev, NULL, 0); if (retval) { pr_err("Couldnt perform PCIe MSM Link resume %d\n", retval); return retval; } #endif /* enable device (incl. PCI PM wakeup and hotplug setup) */ retval = pci_enable_device_mem(pdev); if (retval) { dev_err(&pdev->dev, "cannot enable PCI device\n"); goto err_alloc_device; } /* * The alx chip can DMA to 64-bit addresses, but it uses a single * shared register for the high 32 bits, so only a single, aligned, * 4 GB physical address range can be used at a time. */ if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)) && !dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64))) { dev_info(&pdev->dev, "DMA to 64-BIT addresses\n"); } else { retval = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); if (retval) { retval = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); if (retval) { dev_err(&pdev->dev, "No usable DMA config, aborting\n"); goto err_alloc_pci_res_mem; } } } retval = pci_request_selected_regions(pdev, pci_select_bars(pdev, IORESOURCE_MEM), alx_drv_name); if (retval) { dev_err(&pdev->dev, "pci_request_selected_regions failed 0x%x\n", retval); goto err_alloc_pci_res_mem; } pci_enable_pcie_error_reporting(pdev); pci_set_master(pdev); netdev = alloc_etherdev(sizeof(struct alx_adapter)); if (netdev == NULL) { dev_err(&pdev->dev, "etherdev alloc failed\n"); retval = -ENOMEM; goto err_alloc_netdev; } SET_NETDEV_DEV(netdev, &pdev->dev); netdev->irq = pdev->irq; adpt = netdev_priv(netdev); #ifdef MDM_PLATFORM /* Init IPA Context */ alx_ipa = kzalloc(sizeof(struct alx_ipa_ctx), GFP_KERNEL); if (!alx_ipa) { pr_err("kzalloc err.\n"); return -ENOMEM; } else { adpt->palx_ipa = alx_ipa; } /* Reset all the flags */ CLI_ADPT_FLAG(2, ODU_CONNECT); CLI_ADPT_FLAG(2, ODU_INIT); CLI_ADPT_FLAG(2, IPA_RM); CLI_ADPT_FLAG(2, DEBUGFS_INIT); CLI_ADPT_FLAG(2, WQ_SCHED); galx_adapter_ptr = adpt; /* Initialize all the flow control variables */ adpt->pendq_cnt = 0; adpt->freeq_cnt = 0; adpt->ipa_free_desc_cnt = ALX_IPA_SYS_PIPE_MAX_PKTS_DESC; adpt->ipa_high_watermark = ALX_IPA_SYS_PIPE_MAX_PKTS_DESC; adpt->ipa_low_watermark = ALX_IPA_SYS_PIPE_MIN_PKTS_DESC; alx_ipa->ipa_ready = false; spin_lock_init(&adpt->flow_ctrl_lock); INIT_LIST_HEAD(&adpt->pend_queue_head); INIT_LIST_HEAD(&adpt->free_queue_head); #endif pci_set_drvdata(pdev, adpt); adpt->netdev = netdev; adpt->pdev = pdev; hw = &adpt->hw; hw->adpt = adpt; adpt->msg_enable = ALX_MSG_DEFAULT; #ifdef MDM_PLATFORM retval = ipa_register_ipa_ready_cb(alx_ipa_ready_cb, (void *)adpt); if (retval < 0) { if (retval == -EEXIST) { pr_info("%s:%d -- IPA is Ready retval %d \n", __func__,__LINE__,retval); alx_ipa->ipa_ready = true; } else { pr_info("%s:%d -- IPA is Not Ready retval %d \n", __func__,__LINE__,retval); alx_ipa->ipa_ready = false; } } if (alx_ipa->ipa_ready == true) { if (alx_ipa_setup_rm(adpt)) { pr_err("ALX: IPA Setup RM Failed \n"); goto err_ipa_rm; } else { SET_ADPT_FLAG(2, IPA_RM); } } if (alx_debugfs_init(adpt)) { pr_err("ALX: Debugfs Init failed \n"); } else { SET_ADPT_FLAG(2, DEBUGFS_INIT); } alx_ipa->alx_ipa_perf_requested = false; #endif adpt->hw.hw_addr = ioremap(pci_resource_start(pdev, BAR_0), pci_resource_len(pdev, BAR_0)); if (!adpt->hw.hw_addr) { alx_err(adpt, "cannot map device registers\n"); retval = -EIO; goto err_iomap; } netdev->base_addr = (unsigned long)adpt->hw.hw_addr; /* set cb member of netdev structure*/ #ifdef MDM_PLATFORM netdev->netdev_ops = &alx_netdev_ops; #else netdev_attach_ops(netdev, &alx_netdev_ops); #endif alx_set_ethtool_ops(netdev); netdev->watchdog_timeo = ALX_WATCHDOG_TIME; strlcpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1); /* init alx_adapte structure */ retval = alx_init_adapter(adpt); if (retval) { alx_err(adpt, "net device private data init failed\n"); goto err_init_adapter; } /* reset pcie */ retval = hw->cbs.reset_pcie(hw, true, true); if (retval) { alx_err(adpt, "PCIE Reset failed, error = %d\n", retval); retval = -EIO; goto err_init_adapter; } /* Init GPHY as early as possible due to power saving issue */ retval = hw->cbs.reset_phy(hw); if (retval) { alx_err(adpt, "PHY Reset failed, error = %d\n", retval); retval = -EIO; goto err_init_adapter; } /* reset mac */ retval = hw->cbs.reset_mac(hw); if (retval) { alx_err(adpt, "MAC Reset failed, error = %d\n", retval); retval = -EIO; goto err_init_adapter; } /* setup link to put it in a known good starting state */ retval = hw->cbs.setup_phy_link(hw, hw->autoneg_advertised, true, !hw->disable_fc_autoneg); /* get user settings */ adpt->num_txdescs = 1024; adpt->num_rxdescs = 512; adpt->max_rxques = min_t(int, ALX_MAX_RX_QUEUES, num_online_cpus()); adpt->max_txques = min_t(int, ALX_MAX_TX_QUEUES, num_online_cpus()); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) netdev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) NETIF_F_HW_VLAN_CTAG_RX; #else NETIF_F_HW_VLAN_RX; #endif /*(LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))*/ if (adpt->hw.mac_type != alx_mac_l1c && adpt->hw.mac_type != alx_mac_l2c) { netdev->hw_features = netdev->hw_features | NETIF_F_TSO | NETIF_F_TSO6; } netdev->features = netdev->hw_features | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) NETIF_F_HW_VLAN_CTAG_TX; #else NETIF_F_HW_VLAN_TX; #endif /*(LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))*/ #else netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) NETIF_F_HW_VLAN_CTAG_RX; #else NETIF_F_HW_VLAN_RX; #endif /*(LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))*/ if (adpt->hw.mac_type != alx_mac_l1c && adpt->hw.mac_type != alx_mac_l2c) { netdev->features = netdev->features | NETIF_F_TSO | NETIF_F_TSO6; } netdev->features = netdev->features | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) NETIF_F_HW_VLAN_CTAG_TX; #else NETIF_F_HW_VLAN_TX; #endif /*(LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))*/ #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)) */ /* get mac addr and perm mac addr, set to register */ if (hw->cbs.get_mac_addr) retval = hw->cbs.get_mac_addr(hw, hw->mac_perm_addr); else { retval = -EINVAL; } /* original QC code */ #if 0 if (retval) { eth_hw_addr_random(netdev); memcpy(hw->mac_perm_addr, netdev->dev_addr, netdev->addr_len); } #endif /* ProjE change */ /* Fill the mac address from input parameters * Always use the mac address from the input parameters */ netdev->dev_addr[0] = ((mac_addr_hi16 >> 8) & 0xFF); netdev->dev_addr[1] = ((mac_addr_hi16) & 0xFF); netdev->dev_addr[2] = ((mac_addr_lo32 >> 24) & 0xFF); netdev->dev_addr[3] = ((mac_addr_lo32 >> 16) & 0xFF); netdev->dev_addr[4] = ((mac_addr_lo32 >> 8) & 0xFF); netdev->dev_addr[5] = ((mac_addr_lo32) & 0xFF); printk(KERN_INFO "alx: Input mac: %X:%X:%X:%X:%X:%X\n", netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2], netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]); if ((mac_addr_hi16 == 0xFFFFFFFF) || (mac_addr_lo32 == 0xFFFFFFFF)) { printk(KERN_INFO "alx: Use random generate the mac address \n"); eth_hw_addr_random(netdev); } printk(KERN_INFO "alx: Use mac addr: %X:%X:%X:%X:%X:%X\n", netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2], netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]); memcpy(hw->mac_perm_addr, netdev->dev_addr, netdev->addr_len); // END ProjE change memcpy(hw->mac_addr, hw->mac_perm_addr, netdev->addr_len); if (hw->cbs.set_mac_addr) hw->cbs.set_mac_addr(hw, hw->mac_addr); memcpy(netdev->dev_addr, hw->mac_perm_addr, netdev->addr_len); memcpy(netdev->perm_addr, hw->mac_perm_addr, netdev->addr_len); retval = alx_validate_mac_addr(netdev->perm_addr); if (retval) { alx_err(adpt, "invalid MAC address\n"); goto err_init_adapter; } setup_timer(&adpt->alx_timer, &alx_timer_routine, (unsigned long)adpt); INIT_WORK(&adpt->alx_task, alx_task_routine); /* Number of supported queues */ alx_set_num_queues(adpt); retval = alx_set_interrupt_mode(adpt); if (retval) { alx_err(adpt, "can't set interrupt mode\n"); goto err_set_interrupt_mode; } retval = alx_set_interrupt_param(adpt); if (retval) { alx_err(adpt, "can't set interrupt parameter\n"); goto err_set_interrupt_param; } retval = alx_alloc_all_rtx_queue(adpt); if (retval) { alx_err(adpt, "can't allocate memory for queues\n"); goto err_alloc_rtx_queue; } alx_set_register_info_special(adpt); netif_dbg(adpt, probe, adpt->netdev, "num_msix_noque_intrs = %d, num_msix_rxque_intrs = %d, " "num_msix_txque_intrs = %d\n", adpt->num_msix_noques, adpt->num_msix_rxques, adpt->num_msix_txques); netif_dbg(adpt, probe, adpt->netdev, "num_msix_all_intrs = %d\n", adpt->num_msix_intrs); netif_dbg(adpt, probe, adpt->netdev, "RX Queue Count = %u, HRX Queue Count = %u, " "SRX Queue Count = %u, TX Queue Count = %u\n", adpt->num_rxques, adpt->num_hw_rxques, adpt->num_sw_rxques, adpt->num_txques); /* WOL not supported for all but the following */ switch (hw->pci_devid) { case ALX_DEV_ID_AR8131: case ALX_DEV_ID_AR8132: case ALX_DEV_ID_AR8151_V1: case ALX_DEV_ID_AR8151_V2: case ALX_DEV_ID_AR8152_V1: case ALX_DEV_ID_AR8152_V2: adpt->wol = (ALX_WOL_MAGIC | ALX_WOL_PHY); break; case ALX_DEV_ID_AR8161: case ALX_DEV_ID_AR8162: adpt->wol = (ALX_WOL_MAGIC | ALX_WOL_PHY); break; default: adpt->wol = 0; break; } device_set_wakeup_enable(&adpt->pdev->dev, adpt->wol); SET_ADPT_FLAG(1, STATE_DOWN); strlcpy(netdev->name, "eth%d", sizeof(netdev->name) - 1); retval = register_netdev(netdev); if (retval) { alx_err(adpt, "register netdevice failed\n"); goto err_register_netdev; } adpt->netdev_registered = true; #ifdef MDM_PLATFORM /* Initialize the ODU bridge driver now: odu_bridge_init()*/ if (alx_ipa->ipa_ready == true) { params_ptr->netdev_name = netdev->name; params_ptr->priv = adpt; params.tx_dp_notify = alx_ipa_tx_dp_cb; params_ptr->send_dl_skb = (void *)&alx_ipa_tx_dl; memcpy(params_ptr->device_ethaddr, netdev->dev_addr, ETH_ALEN); /* The maximum number of descriptors that can be provided to a * BAM at once is one less than the total number of descriptors * that the buffer can contain. */ params_ptr->ipa_desc_size = (adpt->ipa_high_watermark + 1) * sizeof(struct sps_iovec); retval = odu_bridge_init(params_ptr); if (retval) { pr_err("Couldnt initialize ODU_Bridge Driver \n"); goto err_init_odu_bridge; } else { SET_ADPT_FLAG(2, ODU_INIT); } } /* Initialize IPA Flow Control Work Task */ INIT_WORK(&adpt->ipa_send_task, alx_ipa_send_routine); /* Register with MSM PCIe PM Framework */ adpt->msm_pcie_event.events = MSM_PCIE_EVENT_LINKDOWN; adpt->msm_pcie_event.user = pdev; adpt->msm_pcie_event.mode = MSM_PCIE_TRIGGER_CALLBACK; adpt->msm_pcie_event.callback = NULL; adpt->msm_pcie_event.options = MSM_PCIE_CONFIG_NO_RECOVERY; retval = msm_pcie_register_event(&adpt->msm_pcie_event); if (retval) { pr_err("%s: PCI link down detect register failed %d\n", __func__, retval); goto msm_pcie_register_fail; } #endif /* carrier off reporting is important to ethtool even BEFORE open */ netif_carrier_off(netdev); /* keep stopping all the transmit queues for older kernels */ netif_tx_stop_all_queues(netdev); /* print the MAC address */ netif_dbg(adpt, probe, adpt->netdev, "%pM\n", netdev->dev_addr); /* print the adapter capability */ if (CHK_ADPT_FLAG(0, MSI_CAP)) { netif_dbg(adpt, probe, adpt->netdev, "MSI Capable: %s\n", CHK_ADPT_FLAG(0, MSI_EN) ? "Enable" : "Disable"); } if (CHK_ADPT_FLAG(0, MSIX_CAP)) { netif_dbg(adpt, probe, adpt->netdev, "MSIX Capable: %s\n", CHK_ADPT_FLAG(0, MSIX_EN) ? "Enable" : "Disable"); } if (CHK_ADPT_FLAG(0, MRQ_CAP)) { netif_dbg(adpt, probe, adpt->netdev, "MRQ Capable: %s\n", CHK_ADPT_FLAG(0, MRQ_EN) ? "Enable" : "Disable"); } if (CHK_ADPT_FLAG(0, MRQ_CAP)) { netif_dbg(adpt, probe, adpt->netdev, "MTQ Capable: %s\n", CHK_ADPT_FLAG(0, MTQ_EN) ? "Enable" : "Disable"); } if (CHK_ADPT_FLAG(0, SRSS_CAP)) { netif_dbg(adpt, probe, adpt->netdev, "RSS(SW) Capable: %s\n", CHK_ADPT_FLAG(0, SRSS_EN) ? "Enable" : "Disable"); } #ifdef ALX_HIB_TIMER_CONFIG mod_timer(&adpt->alx_timer, jiffies); #endif printk(KERN_INFO "alx: Atheros Gigabit Network Connection\n"); cards_found++; return 0; msm_pcie_register_fail: err_init_odu_bridge: unregister_netdev(netdev); adpt->netdev_registered = false; err_register_netdev: alx_free_all_rtx_queue(adpt); err_alloc_rtx_queue: alx_reset_interrupt_param(adpt); err_set_interrupt_param: alx_reset_interrupt_mode(adpt); err_ipa_rm: err_set_interrupt_mode: err_init_adapter: iounmap(adpt->hw.hw_addr); err_iomap: free_netdev(netdev); err_alloc_netdev: pci_release_selected_regions(pdev, pci_select_bars(pdev, IORESOURCE_MEM)); err_alloc_pci_res_mem: pci_disable_device(pdev); err_alloc_device: dev_err(&pdev->dev, "error when probe device, error = %d\n", retval); return retval; } /* * alx_remove - Device Removal Routine */ static void __devexit alx_remove(struct pci_dev *pdev) { struct alx_adapter *adpt = pci_get_drvdata(pdev); struct alx_hw *hw = &adpt->hw; struct net_device *netdev = adpt->netdev; int retval = 0; #ifdef ALX_HIB_TIMER_CONFIG del_timer_sync(&adpt->alx_timer); #endif SET_ADPT_FLAG(1, STATE_DOWN); cancel_work_sync(&adpt->alx_task); hw->cbs.config_pow_save(hw, ALX_LINK_SPEED_UNKNOWN, false, false, false, false); /* resume permanent mac address */ hw->cbs.set_mac_addr(hw, hw->mac_perm_addr); if (adpt->netdev_registered) { unregister_netdev(netdev); adpt->netdev_registered = false; } alx_free_all_rtx_queue(adpt); alx_reset_interrupt_param(adpt); alx_reset_interrupt_mode(adpt); iounmap(adpt->hw.hw_addr); pci_release_selected_regions(pdev, pci_select_bars(pdev, IORESOURCE_MEM)); netif_dbg(adpt, probe, adpt->netdev, "complete\n"); free_netdev(netdev); #ifdef MDM_PLATFORM if (CHK_ADPT_FLAG(2, ODU_CONNECT)) { retval = odu_bridge_disconnect(); if (retval) pr_err("Could not Disconnect to ODU bridge" "or ODU Bridge already disconnected %d \n", retval); } if (CHK_ADPT_FLAG(2, ODU_INIT)) { retval = odu_bridge_cleanup(); if (retval) pr_err("Couldnt cleanup ODU_Bridge Driver %d \n", retval); } /* ALX IPA Specific Cleanup */ if (CHK_ADPT_FLAG(2, IPA_RM)) alx_ipa_cleanup_rm(adpt); if (CHK_ADPT_FLAG(2, DEBUGFS_INIT)) alx_debugfs_exit(adpt); /* Reset all the flags */ CLI_ADPT_FLAG(2, ODU_CONNECT); CLI_ADPT_FLAG(2, ODU_INIT); CLI_ADPT_FLAG(2, IPA_RM); CLI_ADPT_FLAG(2, DEBUGFS_INIT); CLI_ADPT_FLAG(2, WQ_SCHED); kfree(adpt->palx_ipa); /* Cancel ALX IPA Flow Control Work */ cancel_work_sync(&adpt->ipa_send_task); #endif /* Release wakelock to ensure that system can goto power collapse*/ pm_relax(&pdev->dev); pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); #ifdef MDM_PLATFORM /* De-register with MSM PCIe PM framework */ msm_pcie_deregister_event(&adpt->msm_pcie_event); retval = msm_pcie_pm_control(MSM_PCIE_SUSPEND, pdev->bus->number, pdev, NULL, 0); if (retval) pr_err("Couldnt Suspend PCIe MSM Link %d \n", retval); #endif } /* * alx_pci_error_detected */ static pci_ers_result_t alx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { struct alx_adapter *adpt = pci_get_drvdata(pdev); struct net_device *netdev = adpt->netdev; pci_ers_result_t retval = PCI_ERS_RESULT_NEED_RESET; netif_device_detach(netdev); if (state == pci_channel_io_perm_failure) { retval = PCI_ERS_RESULT_DISCONNECT; goto out; } if (netif_running(netdev)) alx_stop_internal(adpt, ALX_OPEN_CTRL_RESET_MAC); pci_disable_device(pdev); out: return retval; } /* * alx_pci_error_slot_reset */ static pci_ers_result_t alx_pci_error_slot_reset(struct pci_dev *pdev) { struct alx_adapter *adpt = pci_get_drvdata(pdev); pci_ers_result_t retval = PCI_ERS_RESULT_DISCONNECT; if (pci_enable_device(pdev)) { alx_err(adpt, "cannot re-enable PCI device after reset\n"); goto out; } pci_set_master(pdev); pci_enable_wake(pdev, PCI_D3hot, 0); pci_enable_wake(pdev, PCI_D3cold, 0); adpt->hw.cbs.reset_mac(&adpt->hw); retval = PCI_ERS_RESULT_RECOVERED; out: pci_cleanup_aer_uncorrect_error_status(pdev); return retval; } /* * alx_pci_error_resume */ static void alx_pci_error_resume(struct pci_dev *pdev) { struct alx_adapter *adpt = pci_get_drvdata(pdev); struct net_device *netdev = adpt->netdev; if (netif_running(netdev)) { if (alx_open_internal(adpt, 0)) return; } netif_device_attach(netdev); } static struct pci_error_handlers alx_err_handler = { .error_detected = alx_pci_error_detected, .slot_reset = alx_pci_error_slot_reset, .resume = alx_pci_error_resume, }; #ifdef CONFIG_PM_SLEEP static SIMPLE_DEV_PM_OPS(alx_pm_ops, alx_suspend, alx_resume); #define ALX_PM_OPS (&alx_pm_ops) #ifndef MDM_PLATFORM compat_pci_suspend(alx_suspend) compat_pci_resume(alx_resume) #endif #else #define ALX_PM_OPS NULL #endif static struct pci_driver alx_driver = { .name = alx_drv_name, .id_table = alx_pci_tbl, .probe = alx_init, .remove = __devexit_p(alx_remove), .shutdown = alx_shutdown, .err_handler = &alx_err_handler, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) .driver.pm = ALX_PM_OPS, #elif defined(CONFIG_PM_SLEEP) .suspend = alx_suspend_compat, .resume = alx_resume_compat, #endif }; static int __init alx_init_module(void) { int retval; printk(KERN_INFO "%s\n", alx_drv_description); /* printk(KERN_INFO "%s\n", "-----ALX_V1.0.0.2-----"); */ retval = pci_register_driver(&alx_driver); return retval; } module_init(alx_init_module); static void __exit alx_exit_module(void) { pci_unregister_driver(&alx_driver); } module_exit(alx_exit_module);