Skip to content
Snippets Groups Projects
be_main.c 92.4 KiB
Newer Older
Sathya Perla's avatar
Sathya Perla committed
/*
Sathya Perla's avatar
Sathya Perla committed
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.  The full GNU General
 * Public License is included in this distribution in the file called COPYING.
 *
 * Contact Information:
Sathya Perla's avatar
Sathya Perla committed
 *
 * Emulex
 * 3333 Susan Street
 * Costa Mesa, CA 92626
#include <linux/prefetch.h>
Sathya Perla's avatar
Sathya Perla committed
#include "be.h"
#include "be_cmds.h"
#include <asm/div64.h>
Sathya Perla's avatar
Sathya Perla committed

MODULE_VERSION(DRV_VER);
MODULE_DEVICE_TABLE(pci, be_dev_ids);
MODULE_DESCRIPTION(DRV_DESC " " DRV_VER);
MODULE_AUTHOR("ServerEngines Corporation");
MODULE_LICENSE("GPL");

static ushort rx_frag_size = 2048;
static unsigned int num_vfs;
module_param(rx_frag_size, ushort, S_IRUGO);
module_param(num_vfs, uint, S_IRUGO);
Sathya Perla's avatar
Sathya Perla committed
MODULE_PARM_DESC(rx_frag_size, "Size of a fragment that holds rcvd data.");
MODULE_PARM_DESC(num_vfs, "Number of PCI VFs to initialize");
Sathya Perla's avatar
Sathya Perla committed

static DEFINE_PCI_DEVICE_TABLE(be_dev_ids) = {
	{ PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) },
	{ PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID2) },
	{ PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) },
	{ PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) },
	{ PCI_DEVICE(EMULEX_VENDOR_ID, OC_DEVICE_ID3)},
	{ PCI_DEVICE(EMULEX_VENDOR_ID, OC_DEVICE_ID4)},
Sathya Perla's avatar
Sathya Perla committed
	{ 0 }
};
MODULE_DEVICE_TABLE(pci, be_dev_ids);
/* UE Status Low CSR */
static const char * const ue_status_low_desc[] = {
	"CEV",
	"CTX",
	"DBUF",
	"ERX",
	"Host",
	"MPU",
	"NDMA",
	"PTC ",
	"RDMA ",
	"RXF ",
	"RXIPS ",
	"RXULP0 ",
	"RXULP1 ",
	"RXULP2 ",
	"TIM ",
	"TPOST ",
	"TPRE ",
	"TXIPS ",
	"TXULP0 ",
	"TXULP1 ",
	"UC ",
	"WDMA ",
	"TXULP2 ",
	"HOST1 ",
	"P0_OB_LINK ",
	"P1_OB_LINK ",
	"HOST_GPIO ",
	"MBOX ",
	"AXGMAC0",
	"AXGMAC1",
	"JTAG",
	"MPU_INTPEND"
};
/* UE Status High CSR */
static const char * const ue_status_hi_desc[] = {
	"LPCMEMHOST",
	"MGMT_MAC",
	"PCS0ONLINE",
	"MPU_IRAM",
	"PCS1ONLINE",
	"PCTL0",
	"PCTL1",
	"PMEM",
	"RR",
	"TXPB",
	"RXPP",
	"XAUI",
	"TXP",
	"ARM",
	"IPC",
	"HOST2",
	"HOST3",
	"HOST4",
	"HOST5",
	"HOST6",
	"HOST7",
	"HOST8",
	"HOST9",
	"Unknown",
	"Unknown",
	"Unknown",
	"Unknown",
	"Unknown",
	"Unknown",
	"Unknown",
	"Unknown"
};
Sathya Perla's avatar
Sathya Perla committed

static void be_queue_free(struct be_adapter *adapter, struct be_queue_info *q)
{
	struct be_dma_mem *mem = &q->dma_mem;
	if (mem->va)
		dma_free_coherent(&adapter->pdev->dev, mem->size, mem->va,
				  mem->dma);
Sathya Perla's avatar
Sathya Perla committed
}

static int be_queue_alloc(struct be_adapter *adapter, struct be_queue_info *q,
		u16 len, u16 entry_size)
{
	struct be_dma_mem *mem = &q->dma_mem;

	memset(q, 0, sizeof(*q));
	q->len = len;
	q->entry_size = entry_size;
	mem->size = len * entry_size;
	mem->va = dma_alloc_coherent(&adapter->pdev->dev, mem->size, &mem->dma,
				     GFP_KERNEL);
Sathya Perla's avatar
Sathya Perla committed
	if (!mem->va)
		return -1;
	memset(mem->va, 0, mem->size);
	return 0;
}

static void be_intr_set(struct be_adapter *adapter, bool enable)
Sathya Perla's avatar
Sathya Perla committed
{
	u8 __iomem *addr = adapter->pcicfg + PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET;
Sathya Perla's avatar
Sathya Perla committed
	u32 reg = ioread32(addr);
	u32 enabled = reg & MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
	if (adapter->eeh_err)
		return;

	if (!enabled && enable)
Sathya Perla's avatar
Sathya Perla committed
		reg |= MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
	else if (enabled && !enable)
Sathya Perla's avatar
Sathya Perla committed
		reg &= ~MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
Sathya Perla's avatar
Sathya Perla committed
		return;
Sathya Perla's avatar
Sathya Perla committed
	iowrite32(reg, addr);
}

static void be_rxq_notify(struct be_adapter *adapter, u16 qid, u16 posted)
Sathya Perla's avatar
Sathya Perla committed
{
	u32 val = 0;
	val |= qid & DB_RQ_RING_ID_MASK;
	val |= posted << DB_RQ_NUM_POSTED_SHIFT;
	iowrite32(val, adapter->db + DB_RQ_OFFSET);
static void be_txq_notify(struct be_adapter *adapter, u16 qid, u16 posted)
Sathya Perla's avatar
Sathya Perla committed
{
	u32 val = 0;
	val |= qid & DB_TXULP_RING_ID_MASK;
	val |= (posted & DB_TXULP_NUM_POSTED_MASK) << DB_TXULP_NUM_POSTED_SHIFT;
	iowrite32(val, adapter->db + DB_TXULP1_OFFSET);
static void be_eq_notify(struct be_adapter *adapter, u16 qid,
Sathya Perla's avatar
Sathya Perla committed
		bool arm, bool clear_int, u16 num_popped)
{
	u32 val = 0;
	val |= qid & DB_EQ_RING_ID_MASK;
	val |= ((qid & DB_EQ_RING_ID_EXT_MASK) <<
			DB_EQ_RING_ID_EXT_MASK_SHIFT);
Sathya Perla's avatar
Sathya Perla committed
	if (arm)
		val |= 1 << DB_EQ_REARM_SHIFT;
	if (clear_int)
		val |= 1 << DB_EQ_CLR_SHIFT;
	val |= 1 << DB_EQ_EVNT_SHIFT;
	val |= num_popped << DB_EQ_NUM_POPPED_SHIFT;
	iowrite32(val, adapter->db + DB_EQ_OFFSET);
void be_cq_notify(struct be_adapter *adapter, u16 qid, bool arm, u16 num_popped)
Sathya Perla's avatar
Sathya Perla committed
{
	u32 val = 0;
	val |= qid & DB_CQ_RING_ID_MASK;
	val |= ((qid & DB_CQ_RING_ID_EXT_MASK) <<
			DB_CQ_RING_ID_EXT_MASK_SHIFT);
Sathya Perla's avatar
Sathya Perla committed
	if (arm)
		val |= 1 << DB_CQ_REARM_SHIFT;
	val |= num_popped << DB_CQ_NUM_POPPED_SHIFT;
	iowrite32(val, adapter->db + DB_CQ_OFFSET);
Sathya Perla's avatar
Sathya Perla committed
}

static int be_mac_addr_set(struct net_device *netdev, void *p)
{
	struct be_adapter *adapter = netdev_priv(netdev);
	struct sockaddr *addr = p;
	int status = 0;

	if (!is_valid_ether_addr(addr->sa_data))
		return -EADDRNOTAVAIL;

	/* MAC addr configuration will be done in hardware for VFs
	 * by their corresponding PFs. Just copy to netdev addr here
	 */
	if (!be_physfn(adapter))
		goto netdev_addr;

	status = be_cmd_pmac_del(adapter, adapter->if_handle,
				adapter->pmac_id, 0);
	status = be_cmd_pmac_add(adapter, (u8 *)addr->sa_data,
				adapter->if_handle, &adapter->pmac_id, 0);
netdev_addr:
Sathya Perla's avatar
Sathya Perla committed
	if (!status)
		memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);

	return status;
}

static void populate_be2_stats(struct be_adapter *adapter)
{

	struct be_drv_stats *drvs = &adapter->drv_stats;
	struct be_pmem_stats *pmem_sts = be_pmem_stats_from_cmd(adapter);
	struct be_port_rxf_stats_v0 *port_stats =
		be_port_rxf_stats_from_cmd(adapter);
	struct be_rxf_stats_v0 *rxf_stats =
		be_rxf_stats_from_cmd(adapter);

	drvs->rx_pause_frames = port_stats->rx_pause_frames;
	drvs->rx_crc_errors = port_stats->rx_crc_errors;
	drvs->rx_control_frames = port_stats->rx_control_frames;
	drvs->rx_in_range_errors = port_stats->rx_in_range_errors;
	drvs->rx_frame_too_long = port_stats->rx_frame_too_long;
	drvs->rx_dropped_runt = port_stats->rx_dropped_runt;
	drvs->rx_ip_checksum_errs = port_stats->rx_ip_checksum_errs;
	drvs->rx_tcp_checksum_errs = port_stats->rx_tcp_checksum_errs;
	drvs->rx_udp_checksum_errs = port_stats->rx_udp_checksum_errs;
	drvs->rxpp_fifo_overflow_drop = port_stats->rx_fifo_overflow;
	drvs->rx_dropped_tcp_length = port_stats->rx_dropped_tcp_length;
	drvs->rx_dropped_too_small = port_stats->rx_dropped_too_small;
	drvs->rx_dropped_too_short = port_stats->rx_dropped_too_short;
	drvs->rx_out_range_errors = port_stats->rx_out_range_errors;
	drvs->rx_input_fifo_overflow_drop =
		port_stats->rx_input_fifo_overflow;
	drvs->rx_dropped_header_too_small =
		port_stats->rx_dropped_header_too_small;
	drvs->rx_address_match_errors =
		port_stats->rx_address_match_errors;
	drvs->rx_alignment_symbol_errors =
		port_stats->rx_alignment_symbol_errors;

	drvs->tx_pauseframes = port_stats->tx_pauseframes;
	drvs->tx_controlframes = port_stats->tx_controlframes;

	if (adapter->port_num)
		drvs->jabber_events =
			rxf_stats->port1_jabber_events;
	else
		drvs->jabber_events =
			rxf_stats->port0_jabber_events;
	drvs->rx_drops_no_pbuf = rxf_stats->rx_drops_no_pbuf;
	drvs->rx_drops_no_txpb = rxf_stats->rx_drops_no_txpb;
	drvs->rx_drops_no_erx_descr = rxf_stats->rx_drops_no_erx_descr;
	drvs->rx_drops_invalid_ring = rxf_stats->rx_drops_invalid_ring;
	drvs->forwarded_packets = rxf_stats->forwarded_packets;
	drvs->rx_drops_mtu = rxf_stats->rx_drops_mtu;
	drvs->rx_drops_no_tpre_descr =
		rxf_stats->rx_drops_no_tpre_descr;
	drvs->rx_drops_too_many_frags =
		rxf_stats->rx_drops_too_many_frags;
	adapter->drv_stats.eth_red_drops = pmem_sts->eth_red_drops;
}

static void populate_be3_stats(struct be_adapter *adapter)
{
	struct be_drv_stats *drvs = &adapter->drv_stats;
	struct be_pmem_stats *pmem_sts = be_pmem_stats_from_cmd(adapter);

	struct be_rxf_stats_v1 *rxf_stats =
		be_rxf_stats_from_cmd(adapter);
	struct be_port_rxf_stats_v1 *port_stats =
		be_port_rxf_stats_from_cmd(adapter);

	drvs->rx_priority_pause_frames = 0;
	drvs->pmem_fifo_overflow_drop = 0;
	drvs->rx_pause_frames = port_stats->rx_pause_frames;
	drvs->rx_crc_errors = port_stats->rx_crc_errors;
	drvs->rx_control_frames = port_stats->rx_control_frames;
	drvs->rx_in_range_errors = port_stats->rx_in_range_errors;
	drvs->rx_frame_too_long = port_stats->rx_frame_too_long;
	drvs->rx_dropped_runt = port_stats->rx_dropped_runt;
	drvs->rx_ip_checksum_errs = port_stats->rx_ip_checksum_errs;
	drvs->rx_tcp_checksum_errs = port_stats->rx_tcp_checksum_errs;
	drvs->rx_udp_checksum_errs = port_stats->rx_udp_checksum_errs;
	drvs->rx_dropped_tcp_length = port_stats->rx_dropped_tcp_length;
	drvs->rx_dropped_too_small = port_stats->rx_dropped_too_small;
	drvs->rx_dropped_too_short = port_stats->rx_dropped_too_short;
	drvs->rx_out_range_errors = port_stats->rx_out_range_errors;
	drvs->rx_dropped_header_too_small =
		port_stats->rx_dropped_header_too_small;
	drvs->rx_input_fifo_overflow_drop =
		port_stats->rx_input_fifo_overflow_drop;
	drvs->rx_address_match_errors =
		port_stats->rx_address_match_errors;
	drvs->rx_alignment_symbol_errors =
		port_stats->rx_alignment_symbol_errors;
	drvs->rxpp_fifo_overflow_drop =
		port_stats->rxpp_fifo_overflow_drop;
	drvs->tx_pauseframes = port_stats->tx_pauseframes;
	drvs->tx_controlframes = port_stats->tx_controlframes;
	drvs->jabber_events = port_stats->jabber_events;
	drvs->rx_drops_no_pbuf = rxf_stats->rx_drops_no_pbuf;
	drvs->rx_drops_no_txpb = rxf_stats->rx_drops_no_txpb;
	drvs->rx_drops_no_erx_descr = rxf_stats->rx_drops_no_erx_descr;
	drvs->rx_drops_invalid_ring = rxf_stats->rx_drops_invalid_ring;
	drvs->forwarded_packets = rxf_stats->forwarded_packets;
	drvs->rx_drops_mtu = rxf_stats->rx_drops_mtu;
	drvs->rx_drops_no_tpre_descr =
		rxf_stats->rx_drops_no_tpre_descr;
	drvs->rx_drops_too_many_frags =
		rxf_stats->rx_drops_too_many_frags;
	adapter->drv_stats.eth_red_drops = pmem_sts->eth_red_drops;
}

Selvin Xavier's avatar
Selvin Xavier committed
static void populate_lancer_stats(struct be_adapter *adapter)
{
Selvin Xavier's avatar
Selvin Xavier committed
	struct be_drv_stats *drvs = &adapter->drv_stats;
	struct lancer_cmd_pport_stats *pport_stats = pport_stats_from_cmd
						(adapter);
	drvs->rx_priority_pause_frames = 0;
	drvs->pmem_fifo_overflow_drop = 0;
	drvs->rx_pause_frames =
		make_64bit_val(pport_stats->rx_pause_frames_hi,
				 pport_stats->rx_pause_frames_lo);
Selvin Xavier's avatar
Selvin Xavier committed
	drvs->rx_crc_errors = make_64bit_val(pport_stats->rx_crc_errors_hi,
						pport_stats->rx_crc_errors_lo);
	drvs->rx_control_frames =
			make_64bit_val(pport_stats->rx_control_frames_hi,
			pport_stats->rx_control_frames_lo);
	drvs->rx_in_range_errors = pport_stats->rx_in_range_errors;
	drvs->rx_frame_too_long =
		make_64bit_val(pport_stats->rx_internal_mac_errors_hi,
					pport_stats->rx_frames_too_long_lo);
	drvs->rx_dropped_runt = pport_stats->rx_dropped_runt;
	drvs->rx_ip_checksum_errs = pport_stats->rx_ip_checksum_errors;
	drvs->rx_tcp_checksum_errs = pport_stats->rx_tcp_checksum_errors;
	drvs->rx_udp_checksum_errs = pport_stats->rx_udp_checksum_errors;
	drvs->rx_dropped_tcp_length =
				pport_stats->rx_dropped_invalid_tcp_length;
	drvs->rx_dropped_too_small = pport_stats->rx_dropped_too_small;
	drvs->rx_dropped_too_short = pport_stats->rx_dropped_too_short;
	drvs->rx_out_range_errors = pport_stats->rx_out_of_range_errors;
	drvs->rx_dropped_header_too_small =
				pport_stats->rx_dropped_header_too_small;
	drvs->rx_input_fifo_overflow_drop = pport_stats->rx_fifo_overflow;
	drvs->rx_address_match_errors = pport_stats->rx_address_match_errors;
	drvs->rx_alignment_symbol_errors =
		make_64bit_val(pport_stats->rx_symbol_errors_hi,
				pport_stats->rx_symbol_errors_lo);
	drvs->rxpp_fifo_overflow_drop = pport_stats->rx_fifo_overflow;
	drvs->tx_pauseframes = make_64bit_val(pport_stats->tx_pause_frames_hi,
					pport_stats->tx_pause_frames_lo);
	drvs->tx_controlframes =
		make_64bit_val(pport_stats->tx_control_frames_hi,
				pport_stats->tx_control_frames_lo);
	drvs->jabber_events = pport_stats->rx_jabbers;
	drvs->rx_drops_no_pbuf = 0;
	drvs->rx_drops_no_txpb = 0;
	drvs->rx_drops_no_erx_descr = 0;
	drvs->rx_drops_invalid_ring = pport_stats->rx_drops_invalid_queue;
	drvs->forwarded_packets = make_64bit_val(pport_stats->num_forwards_hi,
						pport_stats->num_forwards_lo);
	drvs->rx_drops_mtu = make_64bit_val(pport_stats->rx_drops_mtu_hi,
						pport_stats->rx_drops_mtu_lo);
	drvs->rx_drops_no_tpre_descr = 0;
	drvs->rx_drops_too_many_frags =
		make_64bit_val(pport_stats->rx_drops_too_many_frags_hi,
				pport_stats->rx_drops_too_many_frags_lo);
}

void be_parse_stats(struct be_adapter *adapter)
{
Selvin Xavier's avatar
Selvin Xavier committed
	if (adapter->generation == BE_GEN3) {
		if (lancer_chip(adapter))
			populate_lancer_stats(adapter);
		 else
			populate_be3_stats(adapter);
	} else {
		populate_be2_stats(adapter);
void netdev_stats_update(struct be_adapter *adapter)
Sathya Perla's avatar
Sathya Perla committed
{
	struct be_drv_stats *drvs = &adapter->drv_stats;
	struct net_device_stats *dev_stats = &adapter->netdev->stats;
	struct be_rx_obj *rxo;
	struct be_tx_obj *txo;
	unsigned long pkts = 0, bytes = 0, mcast = 0, drops = 0;
	for_all_rx_queues(adapter, rxo, i) {
		pkts += rx_stats(rxo)->rx_pkts;
		bytes += rx_stats(rxo)->rx_bytes;
		mcast += rx_stats(rxo)->rx_mcast_pkts;
		drops += rx_stats(rxo)->rx_dropped;
		/*  no space in linux buffers: best possible approximation */
		if (adapter->generation == BE_GEN3) {
Selvin Xavier's avatar
Selvin Xavier committed
			if (!(lancer_chip(adapter))) {
				struct be_erx_stats_v1 *erx =
					be_erx_stats_from_cmd(adapter);
				drops += erx->rx_drops_no_fragments[rxo->q.id];
			struct be_erx_stats_v0 *erx =
					be_erx_stats_from_cmd(adapter);
			drops += erx->rx_drops_no_fragments[rxo->q.id];
	dev_stats->rx_packets = pkts;
	dev_stats->rx_bytes = bytes;
	dev_stats->multicast = mcast;
	dev_stats->rx_dropped = drops;
	pkts = bytes = 0;
	for_all_tx_queues(adapter, txo, i) {
		pkts += tx_stats(txo)->be_tx_pkts;
		bytes += tx_stats(txo)->be_tx_bytes;
	dev_stats->tx_packets = pkts;
	dev_stats->tx_bytes = bytes;
Sathya Perla's avatar
Sathya Perla committed

	/* bad pkts received */
	dev_stats->rx_errors = drvs->rx_crc_errors +
		drvs->rx_alignment_symbol_errors +
		drvs->rx_in_range_errors +
		drvs->rx_out_range_errors +
		drvs->rx_frame_too_long +
		drvs->rx_dropped_too_small +
		drvs->rx_dropped_too_short +
		drvs->rx_dropped_header_too_small +
		drvs->rx_dropped_tcp_length +
		drvs->rx_dropped_runt +
		drvs->rx_tcp_checksum_errs +
		drvs->rx_ip_checksum_errs +
		drvs->rx_udp_checksum_errs;
Sathya Perla's avatar
Sathya Perla committed
	/* detailed rx errors */
	dev_stats->rx_length_errors = drvs->rx_in_range_errors +
		drvs->rx_out_range_errors +
		drvs->rx_frame_too_long;
	dev_stats->rx_crc_errors = drvs->rx_crc_errors;
Sathya Perla's avatar
Sathya Perla committed

	/* frame alignment errors */
	dev_stats->rx_frame_errors = drvs->rx_alignment_symbol_errors;
Sathya Perla's avatar
Sathya Perla committed
	/* receiver fifo overrun */
	/* drops_no_pbuf is no per i/f, it's per BE card */
	dev_stats->rx_fifo_errors = drvs->rxpp_fifo_overflow_drop +
				drvs->rx_input_fifo_overflow_drop +
				drvs->rx_drops_no_pbuf;
void be_link_status_update(struct be_adapter *adapter, bool link_up)
Sathya Perla's avatar
Sathya Perla committed
{
	struct net_device *netdev = adapter->netdev;

	/* If link came up or went down */
	if (adapter->link_up != link_up) {
Sathya Perla's avatar
Sathya Perla committed
			netif_carrier_on(netdev);
			printk(KERN_INFO "%s: Link up\n", netdev->name);
		} else {
			netif_carrier_off(netdev);
			printk(KERN_INFO "%s: Link down\n", netdev->name);
Sathya Perla's avatar
Sathya Perla committed
		}
		adapter->link_up = link_up;
Sathya Perla's avatar
Sathya Perla committed
	}
}

/* Update the EQ delay n BE based on the RX frags consumed / sec */
static void be_rx_eqd_update(struct be_adapter *adapter, struct be_rx_obj *rxo)
Sathya Perla's avatar
Sathya Perla committed
{
	struct be_eq_obj *rx_eq = &rxo->rx_eq;
	struct be_rx_stats *stats = &rxo->stats;
	ulong now = jiffies;
	u32 eqd;

	if (!rx_eq->enable_aic)
		return;

	/* Wrapped around */
	if (time_before(now, stats->rx_fps_jiffies)) {
		stats->rx_fps_jiffies = now;
		return;
	}
Sathya Perla's avatar
Sathya Perla committed

	/* Update once a second */
	if ((now - stats->rx_fps_jiffies) < HZ)
Sathya Perla's avatar
Sathya Perla committed
		return;

	stats->rx_fps = (stats->rx_frags - stats->prev_rx_frags) /
			((now - stats->rx_fps_jiffies) / HZ);
	stats->rx_fps_jiffies = now;
	stats->prev_rx_frags = stats->rx_frags;
	eqd = stats->rx_fps / 110000;
Sathya Perla's avatar
Sathya Perla committed
	eqd = eqd << 3;
	if (eqd > rx_eq->max_eqd)
		eqd = rx_eq->max_eqd;
	if (eqd < rx_eq->min_eqd)
		eqd = rx_eq->min_eqd;
	if (eqd < 10)
		eqd = 0;
	if (eqd != rx_eq->cur_eqd)
		be_cmd_modify_eqd(adapter, rx_eq->q.id, eqd);
Sathya Perla's avatar
Sathya Perla committed

	rx_eq->cur_eqd = eqd;
}

static u32 be_calc_rate(u64 bytes, unsigned long ticks)
{
	u64 rate = bytes;

	do_div(rate, ticks / HZ);
	rate <<= 3;			/* bytes/sec -> bits/sec */
	do_div(rate, 1000000ul);	/* MB/Sec */

	return rate;
}

static void be_tx_rate_update(struct be_tx_obj *txo)
	struct be_tx_stats *stats = tx_stats(txo);
	ulong now = jiffies;

	/* Wrapped around? */
	if (time_before(now, stats->be_tx_jiffies)) {
		stats->be_tx_jiffies = now;
		return;
	}

	/* Update tx rate once in two seconds */
	if ((now - stats->be_tx_jiffies) > 2 * HZ) {
		stats->be_tx_rate = be_calc_rate(stats->be_tx_bytes
						  - stats->be_tx_bytes_prev,
						 now - stats->be_tx_jiffies);
		stats->be_tx_jiffies = now;
		stats->be_tx_bytes_prev = stats->be_tx_bytes;
	}
}

static void be_tx_stats_update(struct be_tx_obj *txo,
			u32 wrb_cnt, u32 copied, u32 gso_segs, bool stopped)
Sathya Perla's avatar
Sathya Perla committed
{
	struct be_tx_stats *stats = tx_stats(txo);

Sathya Perla's avatar
Sathya Perla committed
	stats->be_tx_reqs++;
	stats->be_tx_wrbs += wrb_cnt;
	stats->be_tx_bytes += copied;
	stats->be_tx_pkts += (gso_segs ? gso_segs : 1);
Sathya Perla's avatar
Sathya Perla committed
	if (stopped)
		stats->be_tx_stops++;
}

/* Determine number of WRB entries needed to xmit data in an skb */
static u32 wrb_cnt_for_skb(struct be_adapter *adapter, struct sk_buff *skb,
								bool *dummy)
Sathya Perla's avatar
Sathya Perla committed
{
	int cnt = (skb->len > skb->data_len);

	cnt += skb_shinfo(skb)->nr_frags;

Sathya Perla's avatar
Sathya Perla committed
	/* to account for hdr wrb */
	cnt++;
	if (lancer_chip(adapter) || !(cnt & 1)) {
		*dummy = false;
	} else {
Sathya Perla's avatar
Sathya Perla committed
		/* add a dummy to make it an even num */
		cnt++;
		*dummy = true;
Sathya Perla's avatar
Sathya Perla committed
	BUG_ON(cnt > BE_MAX_TX_FRAG_COUNT);
	return cnt;
}

static inline void wrb_fill(struct be_eth_wrb *wrb, u64 addr, int len)
{
	wrb->frag_pa_hi = upper_32_bits(addr);
	wrb->frag_pa_lo = addr & 0xFFFFFFFF;
	wrb->frag_len = len & ETH_WRB_FRAG_LEN_MASK;
}

static void wrb_fill_hdr(struct be_adapter *adapter, struct be_eth_hdr_wrb *hdr,
		struct sk_buff *skb, u32 wrb_cnt, u32 len)
Sathya Perla's avatar
Sathya Perla committed
{
	u8 vlan_prio = 0;
	u16 vlan_tag = 0;

Sathya Perla's avatar
Sathya Perla committed
	memset(hdr, 0, sizeof(*hdr));

	AMAP_SET_BITS(struct amap_eth_hdr_wrb, crc, hdr, 1);

	if (skb_is_gso(skb)) {
Sathya Perla's avatar
Sathya Perla committed
		AMAP_SET_BITS(struct amap_eth_hdr_wrb, lso, hdr, 1);
		AMAP_SET_BITS(struct amap_eth_hdr_wrb, lso_mss,
			hdr, skb_shinfo(skb)->gso_size);
		if (skb_is_gso_v6(skb) && !lancer_chip(adapter))
			AMAP_SET_BITS(struct amap_eth_hdr_wrb, lso6, hdr, 1);
		if (lancer_chip(adapter) && adapter->sli_family  ==
							LANCER_A0_SLI_FAMILY) {
			AMAP_SET_BITS(struct amap_eth_hdr_wrb, ipcs, hdr, 1);
			if (is_tcp_pkt(skb))
				AMAP_SET_BITS(struct amap_eth_hdr_wrb,
								tcpcs, hdr, 1);
			else if (is_udp_pkt(skb))
				AMAP_SET_BITS(struct amap_eth_hdr_wrb,
								udpcs, hdr, 1);
		}
Sathya Perla's avatar
Sathya Perla committed
	} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
		if (is_tcp_pkt(skb))
			AMAP_SET_BITS(struct amap_eth_hdr_wrb, tcpcs, hdr, 1);
		else if (is_udp_pkt(skb))
			AMAP_SET_BITS(struct amap_eth_hdr_wrb, udpcs, hdr, 1);
	}

	if (vlan_tx_tag_present(skb)) {
Sathya Perla's avatar
Sathya Perla committed
		AMAP_SET_BITS(struct amap_eth_hdr_wrb, vlan, hdr, 1);
		vlan_tag = vlan_tx_tag_get(skb);
		vlan_prio = (vlan_tag & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
		/* If vlan priority provided by OS is NOT in available bmap */
		if (!(adapter->vlan_prio_bmap & (1 << vlan_prio)))
			vlan_tag = (vlan_tag & ~VLAN_PRIO_MASK) |
					adapter->recommended_prio;
		AMAP_SET_BITS(struct amap_eth_hdr_wrb, vlan_tag, hdr, vlan_tag);
Sathya Perla's avatar
Sathya Perla committed
	}

	AMAP_SET_BITS(struct amap_eth_hdr_wrb, event, hdr, 1);
	AMAP_SET_BITS(struct amap_eth_hdr_wrb, complete, hdr, 1);
	AMAP_SET_BITS(struct amap_eth_hdr_wrb, num_wrb, hdr, wrb_cnt);
	AMAP_SET_BITS(struct amap_eth_hdr_wrb, len, hdr, len);
}

static void unmap_tx_frag(struct device *dev, struct be_eth_wrb *wrb,
		bool unmap_single)
{
	dma_addr_t dma;

	be_dws_le_to_cpu(wrb, sizeof(*wrb));

	dma = (u64)wrb->frag_pa_hi << 32 | (u64)wrb->frag_pa_lo;
	if (wrb->frag_len) {
		if (unmap_single)
			dma_unmap_single(dev, dma, wrb->frag_len,
					 DMA_TO_DEVICE);
			dma_unmap_page(dev, dma, wrb->frag_len, DMA_TO_DEVICE);
static int make_tx_wrbs(struct be_adapter *adapter, struct be_queue_info *txq,
Sathya Perla's avatar
Sathya Perla committed
		struct sk_buff *skb, u32 wrb_cnt, bool dummy_wrb)
{
	dma_addr_t busaddr;
	int i, copied = 0;
	struct device *dev = &adapter->pdev->dev;
Sathya Perla's avatar
Sathya Perla committed
	struct sk_buff *first_skb = skb;
	struct be_eth_wrb *wrb;
	struct be_eth_hdr_wrb *hdr;
	bool map_single = false;
	u16 map_head;
Sathya Perla's avatar
Sathya Perla committed

	hdr = queue_head_node(txq);
	queue_head_inc(txq);
	map_head = txq->head;
	if (skb->len > skb->data_len) {
		int len = skb_headlen(skb);
		busaddr = dma_map_single(dev, skb->data, len, DMA_TO_DEVICE);
		if (dma_mapping_error(dev, busaddr))
			goto dma_err;
		map_single = true;
		wrb = queue_head_node(txq);
		wrb_fill(wrb, busaddr, len);
		be_dws_cpu_to_le(wrb, sizeof(*wrb));
		queue_head_inc(txq);
		copied += len;
	}
	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
		struct skb_frag_struct *frag =
			&skb_shinfo(skb)->frags[i];
		busaddr = dma_map_page(dev, frag->page, frag->page_offset,
				       frag->size, DMA_TO_DEVICE);
		if (dma_mapping_error(dev, busaddr))
		wrb = queue_head_node(txq);
		wrb_fill(wrb, busaddr, frag->size);
		be_dws_cpu_to_le(wrb, sizeof(*wrb));
		queue_head_inc(txq);
		copied += frag->size;
Sathya Perla's avatar
Sathya Perla committed
	}

	if (dummy_wrb) {
		wrb = queue_head_node(txq);
		wrb_fill(wrb, 0, 0);
		be_dws_cpu_to_le(wrb, sizeof(*wrb));
		queue_head_inc(txq);
	}

	wrb_fill_hdr(adapter, hdr, first_skb, wrb_cnt, copied);
Sathya Perla's avatar
Sathya Perla committed
	be_dws_cpu_to_le(hdr, sizeof(*hdr));

	return copied;
dma_err:
	txq->head = map_head;
	while (copied) {
		wrb = queue_head_node(txq);
		unmap_tx_frag(dev, wrb, map_single);
		map_single = false;
		copied -= wrb->frag_len;
		queue_head_inc(txq);
	}
	return 0;
static netdev_tx_t be_xmit(struct sk_buff *skb,
			struct net_device *netdev)
Sathya Perla's avatar
Sathya Perla committed
{
	struct be_adapter *adapter = netdev_priv(netdev);
	struct be_tx_obj *txo = &adapter->tx_obj[skb_get_queue_mapping(skb)];
	struct be_queue_info *txq = &txo->q;
Sathya Perla's avatar
Sathya Perla committed
	u32 wrb_cnt = 0, copied = 0;
	u32 start = txq->head;
	bool dummy_wrb, stopped = false;

	wrb_cnt = wrb_cnt_for_skb(adapter, skb, &dummy_wrb);
	copied = make_tx_wrbs(adapter, txq, skb, wrb_cnt, dummy_wrb);
	if (copied) {
		/* record the sent skb in the sent_skb table */
		BUG_ON(txo->sent_skb_list[start]);
		txo->sent_skb_list[start] = skb;

		/* Ensure txq has space for the next skb; Else stop the queue
		 * *BEFORE* ringing the tx doorbell, so that we serialze the
		 * tx compls of the current transmit which'll wake up the queue
		 */
		atomic_add(wrb_cnt, &txq->used);
		if ((BE_MAX_TX_FRAG_COUNT + atomic_read(&txq->used)) >=
								txq->len) {
			netif_stop_subqueue(netdev, skb_get_queue_mapping(skb));
		be_txq_notify(adapter, txq->id, wrb_cnt);
		be_tx_stats_update(txo, wrb_cnt, copied,
				skb_shinfo(skb)->gso_segs, stopped);
	} else {
		txq->head = start;
		dev_kfree_skb_any(skb);
Sathya Perla's avatar
Sathya Perla committed
	}
	return NETDEV_TX_OK;
}

static int be_change_mtu(struct net_device *netdev, int new_mtu)
{
	struct be_adapter *adapter = netdev_priv(netdev);
	if (new_mtu < BE_MIN_MTU ||
			new_mtu > (BE_MAX_JUMBO_FRAME_SIZE -
					(ETH_HLEN + ETH_FCS_LEN))) {
Sathya Perla's avatar
Sathya Perla committed
		dev_info(&adapter->pdev->dev,
			"MTU must be between %d and %d bytes\n",
			BE_MIN_MTU,
			(BE_MAX_JUMBO_FRAME_SIZE - (ETH_HLEN + ETH_FCS_LEN)));
Sathya Perla's avatar
Sathya Perla committed
		return -EINVAL;
	}
	dev_info(&adapter->pdev->dev, "MTU changed from %d to %d bytes\n",
			netdev->mtu, new_mtu);
	netdev->mtu = new_mtu;
	return 0;
}

/*
 * A max of 64 (BE_NUM_VLANS_SUPPORTED) vlans can be configured in BE.
 * If the user configures more, place BE in vlan promiscuous mode.
Sathya Perla's avatar
Sathya Perla committed
 */
static int be_vid_config(struct be_adapter *adapter, bool vf, u32 vf_num)
Sathya Perla's avatar
Sathya Perla committed
{
	u16 vtag[BE_NUM_VLANS_SUPPORTED];
	u16 ntags = 0, i;
	u32 if_handle;

	if (vf) {
		if_handle = adapter->vf_cfg[vf_num].vf_if_handle;
		vtag[0] = cpu_to_le16(adapter->vf_cfg[vf_num].vf_vlan_tag);
		status = be_cmd_vlan_config(adapter, if_handle, vtag, 1, 1, 0);
	}
	if (adapter->vlans_added <= adapter->max_vlans)  {
Sathya Perla's avatar
Sathya Perla committed
		/* Construct VLAN Table to give to HW */
		for (i = 0; i < VLAN_N_VID; i++) {
Sathya Perla's avatar
Sathya Perla committed
			if (adapter->vlan_tag[i]) {
				vtag[ntags] = cpu_to_le16(i);
				ntags++;
			}
		}
		status = be_cmd_vlan_config(adapter, adapter->if_handle,
					vtag, ntags, 1, 0);
Sathya Perla's avatar
Sathya Perla committed
	} else {
		status = be_cmd_vlan_config(adapter, adapter->if_handle,
					NULL, 0, 1, 1);
Sathya Perla's avatar
Sathya Perla committed
	}
Sathya Perla's avatar
Sathya Perla committed
}

static void be_vlan_add_vid(struct net_device *netdev, u16 vid)
{
	struct be_adapter *adapter = netdev_priv(netdev);

	adapter->vlans_added++;
	if (!be_physfn(adapter))
		return;

Sathya Perla's avatar
Sathya Perla committed
	adapter->vlan_tag[vid] = 1;
	if (adapter->vlans_added <= (adapter->max_vlans + 1))
		be_vid_config(adapter, false, 0);
Sathya Perla's avatar
Sathya Perla committed
}

static void be_vlan_rem_vid(struct net_device *netdev, u16 vid)
{
	struct be_adapter *adapter = netdev_priv(netdev);

	if (!be_physfn(adapter))
		return;

Sathya Perla's avatar
Sathya Perla committed
	adapter->vlan_tag[vid] = 0;
	if (adapter->vlans_added <= adapter->max_vlans)
		be_vid_config(adapter, false, 0);
static void be_set_multicast_list(struct net_device *netdev)
Sathya Perla's avatar
Sathya Perla committed
{
	struct be_adapter *adapter = netdev_priv(netdev);

	if (netdev->flags & IFF_PROMISC) {
		be_cmd_promiscuous_config(adapter, true);
		adapter->promiscuous = true;
		goto done;
Lucas De Marchi's avatar
Lucas De Marchi committed
	/* BE was previously in promiscuous mode; disable it */
	if (adapter->promiscuous) {
		adapter->promiscuous = false;
		be_cmd_promiscuous_config(adapter, false);
	/* Enable multicast promisc if num configured exceeds what we support */
	if (netdev->flags & IFF_ALLMULTI ||
	    netdev_mc_count(netdev) > BE_MAX_MC) {
		be_cmd_multicast_set(adapter, adapter->if_handle, NULL,
	be_cmd_multicast_set(adapter, adapter->if_handle, netdev,
		&adapter->mc_cmd_mem);
static int be_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
{
	struct be_adapter *adapter = netdev_priv(netdev);
	int status;

	if (!adapter->sriov_enabled)
		return -EPERM;

	if (!is_valid_ether_addr(mac) || (vf >= num_vfs))
		return -EINVAL;

	if (adapter->vf_cfg[vf].vf_pmac_id != BE_INVALID_PMAC_ID)
		status = be_cmd_pmac_del(adapter,
					adapter->vf_cfg[vf].vf_if_handle,
					adapter->vf_cfg[vf].vf_pmac_id, vf + 1);
	status = be_cmd_pmac_add(adapter, mac,
				adapter->vf_cfg[vf].vf_if_handle,
				&adapter->vf_cfg[vf].vf_pmac_id, vf + 1);
		dev_err(&adapter->pdev->dev, "MAC %pM set on VF %d Failed\n",
				mac, vf);
	else
		memcpy(adapter->vf_cfg[vf].vf_mac_addr, mac, ETH_ALEN);

static int be_get_vf_config(struct net_device *netdev, int vf,
			struct ifla_vf_info *vi)
{
	struct be_adapter *adapter = netdev_priv(netdev);

	if (!adapter->sriov_enabled)
		return -EPERM;

	if (vf >= num_vfs)
		return -EINVAL;

	vi->vf = vf;
	vi->tx_rate = adapter->vf_cfg[vf].vf_tx_rate;
	vi->vlan = adapter->vf_cfg[vf].vf_vlan_tag;
	vi->qos = 0;
	memcpy(&vi->mac, adapter->vf_cfg[vf].vf_mac_addr, ETH_ALEN);

	return 0;
}

static int be_set_vf_vlan(struct net_device *netdev,
			int vf, u16 vlan, u8 qos)
{
	struct be_adapter *adapter = netdev_priv(netdev);
	int status = 0;

	if (!adapter->sriov_enabled)
		return -EPERM;

	if ((vf >= num_vfs) || (vlan > 4095))
		return -EINVAL;

	if (vlan) {
		adapter->vf_cfg[vf].vf_vlan_tag = vlan;
		adapter->vlans_added++;
	} else {
		adapter->vf_cfg[vf].vf_vlan_tag = 0;
		adapter->vlans_added--;
	}

	status = be_vid_config(adapter, true, vf);

	if (status)
		dev_info(&adapter->pdev->dev,
				"VLAN %d config on VF %d failed\n", vlan, vf);
	return status;
}

static int be_set_vf_tx_rate(struct net_device *netdev,
			int vf, int rate)
{
	struct be_adapter *adapter = netdev_priv(netdev);
	int status = 0;

	if (!adapter->sriov_enabled)
		return -EPERM;

	if ((vf >= num_vfs) || (rate < 0))
		return -EINVAL;

	if (rate > 10000)
		rate = 10000;

	adapter->vf_cfg[vf].vf_tx_rate = rate;
	status = be_cmd_set_qos(adapter, rate / 10, vf + 1);