5525 lines
142 KiB
C
Raw Normal View History

2024-09-09 08:57:42 +00:00
/* 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 <linux/moduleparam.h>
#ifdef MDM_PLATFORM
#include <linux/debugfs.h>
#include <linux/pm_wakeup.h>
#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, <nic-devel@qualcomm.com>");
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 = &params;
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 = &params;
#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);