source "drivers/net/benet/Kconfig"
+source "drivers/net/hxge/Kconfig"
+
endif # NETDEV_10000
source "drivers/net/tokenring/Kconfig"
obj-$(CONFIG_BE2NET) += benet/
obj-$(CONFIG_VMXNET3) += vmxnet3/
obj-$(CONFIG_BNA) += bna/
-
+obj-$(CONFIG_HXGE) += hxge/
gianfar_driver-objs := gianfar.o \
gianfar_ethtool.o \
gianfar_sysfs.o
--- /dev/null
+config HXGE
+ tristate "Sun Microsystems 10Gb Ethernet Adapter "
+ depends on PCI
+ ---help---
+ This driver supports the Hydra 10Gb Ethernet driver.
+
+
+config HXGE_NAPI
+ bool "Use Rx Polling (NAPI)"
+ depends on HXGE
+ help
+ NAPI is a new driver API designed to reduce CPU and interrupt load
+ when the driver is receiving lots of packets from the card. It is
+ still somewhat experimental and thus not yet enabled by default.
+
+ If your estimated Rx load is 10kpps or more, or if the card will be
+ deployed on potentially unfriendly networks (e.g. in a firewall),
+ then say Y here.
+
+ See <file:Documentation/networking/NAPI_HOWTO.txt> for more
+ information.
+
+ If in doubt, say N.
+
--- /dev/null
+# Makefile for the Sun Microsystems 10Gb ethernet driver
+#
+
+#EXTRA_CFLAGS += -DCONFIG_HXGE_NAPI
+EXTRA_CFLAGS += -DCONFIG_SKB_SHARED -DUSE_PIO
+
+ifeq ($(ERRINJECT),1)
+EXTRA_CFLAGS += -DCONFIG_ERRINJECT
+endif
+
+
+obj-$(CONFIG_HXGE) += hxge.o
+hxge-objs := hxge_other.o hxge_ethtool.o hxge_main.o hxge_stats.o hxge_vmac.o hxge_param.o hxge_pfc.o hxge_txdma.o hxge_rxdma.o hxge_intr.o
+ifeq ($(ERRINJECT),1)
+hxge-objs += hxge_sysfs.o
+endif
+hxge-objs += hpi/hpi.o hpi/hpi_rxdma.o hpi/hpi_txdma.o hpi/hpi_pfc.o
+hxge-objs += hpi/hpi_vir.o hpi/hpi_vmac.o
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#include "linux/version.h"
+#include "hpi.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+#include <asm/semaphore.h>
+#else
+#include <linux/semaphore.h>
+#endif
+
+DECLARE_MUTEX(hpidebuglock);
+int hpi_debug_init = 0;
+uint64_t hpi_debug_level = 0;
+
+void
+hpi_debug_msg(uint64_t level, char *fmt, ...)
+{
+ char msg_buffer[1024];
+ va_list ap;
+
+ if ((level & hpi_debug_level) ||
+ (level & HPI_REG_CTL) || (level & HPI_ERR_CTL)) {
+
+ va_start(ap, fmt);
+ (void) vsprintf(msg_buffer, fmt, ap);
+ va_end(ap);
+
+ HPI_DEBUG("%s",msg_buffer);
+ }
+}
+
+void
+hpi_rtrace_buf_init(rtrace_t *rt)
+{
+ int i;
+
+ rt->next_idx = 0;
+ rt->last_idx = MAX_RTRACE_ENTRIES - 1;
+ rt->wrapped = FALSE;
+ for (i = 0; i < MAX_RTRACE_ENTRIES; i++) {
+ rt->buf[i].ctl_addr = TRACE_CTL_INVALID;
+ rt->buf[i].val_l32 = 0;
+ rt->buf[i].val_h32 = 0;
+ }
+}
+
+void
+hpi_rtrace_update(boolean_t wr, rtrace_t *rt,
+ uint32_t addr, uint64_t val)
+{
+}
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#ifndef _HPI_H
+#define _HPI_H
+
+#include "../hxge_defs.h"
+
+typedef uint32_t hpi_status_t;
+typedef void * hpi_handle_t;
+
+/* Common Block ID */
+#define VMAC_BLK_ID 0x1
+#define TXDMA_BLK_ID 0x2
+#define RXDMA_BLK_ID 0x3
+#define PFC_BLK_ID 0x4
+#define VIR_BLK_ID 0x5
+#define PEU_BLK_ID 0x6
+
+/* Common HW error code */
+/* HW unable to exit from reset state. */
+#define RESET_FAILED 0x81
+
+/* Write operation failed on indirect write. */
+#define WRITE_FAILED 0x82
+/* Read operation failed on indirect read. */
+#define READ_FAILED 0x83
+
+/* Error code boundary */
+
+#define COMMON_SW_ERR_START 0x40
+#define COMMON_SW_ERR_END 0x4f
+#define BLK_SPEC_SW_ERR_START 0x50
+#define BLK_SPEC_SW_ERR_END 0x7f
+#define COMMON_HW_ERR_START 0x80
+#define COMMON_HW_ERR_END 0x8f
+#define BLK_SPEC_HW_ERR_START 0x90
+#define BLK_SPEC_HW_ERR_END 0xbf
+
+#define IS_PORT 0x00100000
+#define IS_CHAN 0x00200000
+
+/* Common SW errors code */
+
+#define PORT_INVALID 0x41 /* Invalid port number */
+#define CHANNEL_INVALID 0x42 /* Invalid dma channel number */
+#define OPCODE_INVALID 0x43 /* Invalid opcode */
+#define REGISTER_INVALID 0x44 /* Invalid register number */
+#define COUNTER_INVALID 0x45 /* Invalid counter number */
+#define CONFIG_INVALID 0x46 /* Invalid config input */
+#define LOGICAL_PAGE_INVALID 0x47 /* Invalid logical page # */
+#define VLAN_INVALID 0x48 /* Invalid Vlan ID */
+#define RDC_TAB_INVALID 0x49 /* Invalid RDC Group Number */
+#define LOCATION_INVALID 0x4a /* Invalid Entry Location */
+
+#define HPI_SUCCESS 0 /* Operation succeed */
+#define HPI_FAILURE 0x80000000 /* Operation failed */
+
+#define HPI_CNT_CLR_VAL 0
+
+/*
+ * Block identifier starts at bit 8.
+ */
+#define HPI_BLOCK_ID_SHIFT 8
+
+/*
+ * Port, channel and misc. information starts at bit 12.
+ */
+#define HPI_PORT_CHAN_SHIFT 12
+
+/*
+ * Software Block specific error codes start at 0x50.
+ */
+#define HPI_BK_ERROR_START 0x50
+
+/*
+ * Hardware block specific error codes start at 0x90.
+ */
+#define HPI_BK_HW_ER_START 0x90
+
+/* Structures for register tracing */
+
+typedef struct _rt_buf {
+ uint32_t ctl_addr;
+ uint32_t val_l32;
+ uint32_t val_h32;
+} rt_buf_t;
+
+/*
+ * Control Address field format
+ *
+ * Bit 0 - 23: Address
+ * Bit 24 - 25: Function Number
+ * Bit 26 - 29: Instance Number
+ * Bit 30: Read/Write Direction bit
+ * Bit 31: Invalid bit
+ */
+
+#define MAX_RTRACE_ENTRIES 1024
+#define MAX_RTRACE_IOC_ENTRIES 64
+#define TRACE_ADDR_MASK 0x00FFFFFF
+#define TRACE_FUNC_MASK 0x03000000
+#define TRACE_INST_MASK 0x3C000000
+#define TRACE_CTL_WR 0x40000000
+#define TRACE_CTL_INVALID 0x80000000
+#define TRACE_FUNC_SHIFT 24
+#define TRACE_INST_SHIFT 26
+#define MSG_BUF_SIZE 1024
+
+
+typedef struct _rtrace {
+ uint16_t next_idx;
+ uint16_t last_idx;
+ boolean_t wrapped;
+ rt_buf_t buf[MAX_RTRACE_ENTRIES];
+} rtrace_t;
+
+typedef struct _err_inject {
+ uint8_t blk_id;
+ uint8_t chan;
+ uint32_t err_id;
+ uint32_t control;
+} err_inject_t;
+
+/* Configuration options */
+typedef enum config_op {
+ DISABLE = 0,
+ ENABLE,
+ INIT
+} config_op_t;
+
+/* I/O options */
+typedef enum io_op {
+ OP_SET = 0,
+ OP_GET,
+ OP_UPDATE,
+ OP_CLEAR
+} io_op_t;
+
+/* Counter options */
+typedef enum counter_op {
+ SNAP_STICKY = 0,
+ SNAP_ACCUMULATE,
+ CLEAR
+} counter_op_t;
+
+/* HPI attribute */
+typedef struct _hpi_attr_t {
+ uint32_t type;
+ uint32_t idata[16];
+ uint32_t odata[16];
+} hpi_attr_t;
+
+/* HPI Counter */
+typedef struct _hpi_counter_t {
+ uint32_t id;
+ char *name;
+ uint32_t val;
+} hpi_counter_t;
+
+/*
+ * Commmon definitions for HPI RXDMA and TXDMA functions.
+ */
+typedef struct _dma_log_page {
+ uint8_t page_num;
+ boolean_t valid;
+ uint8_t func_num;
+ uint64_t mask;
+ uint64_t value;
+ uint64_t reloc;
+} dma_log_page_t, *p_dma_log_page_t;
+
+extern rtrace_t hpi_rtracebuf;
+void hpi_rtrace_buf_init(rtrace_t *rt);
+void hpi_rtrace_update(boolean_t wr, rtrace_t *rt,
+ uint32_t addr, uint64_t val);
+void hpi_rtrace_buf_init(rtrace_t *rt);
+
+void hpi_debug_msg(uint64_t level, char *fmt, ...);
+
+#ifdef DBG
+#define HPI_DEBUG_MSG(params) hpi_debug_msg params
+#else
+#define HPI_DEBUG_MSG(params)
+#endif
+
+#define HPI_ERROR_MSG(params) hpi_debug_msg params
+#define HPI_REG_DUMP_MSG(params) hpi_debug_msg params
+
+#ifdef DBG
+#define HPI_DEBUG(args...) printk(KERN_DEBUG "hpi: " __FUNCTION__ , ##args)
+#else
+#define HPI_DEBUG(args...)
+#endif
+
+#define HPI_ERR(args...) printk(KERN_ERR "hpi: " __FUNCTION__ , ##args)
+
+#endif /* _HPI_H */
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include "hpi_vir.h"
+#include "hpi_pfc.h"
+
+#define TCAM_COMPLETION_TRY_COUNT 10
+
+uint64_t pfc_reg_offset[] = {
+ PFC_VLAN_TABLE, PFC_VLAN_CTRL,
+ PFC_MAC_ADDR, PFC_MAC_ADDR_MASK,
+ PFC_HASH_TABLE,
+ PFC_L2_CLASS_CONFIG, PFC_L3_CLASS_CONFIG,
+ PFC_TCAM_KEY0, PFC_TCAM_KEY1, PFC_TCAM_MASK0, PFC_TCAM_MASK1,
+ PFC_TCAM_CTRL,
+ PFC_CONFIG, TCP_CTRL_MASK, SRC_HASH_VAL,
+ PFC_INT_STATUS, PFC_DBG_INT_STATUS, PFC_INT_MASK,
+ PFC_DROP_LOG, PFC_DROP_LOG_MASK,
+ PFC_VLAN_PAR_ERR_LOG, PFC_TCAM_PAR_ERR_LOG,
+ PFC_BAD_CS_COUNTER, PFC_DROP_COUNTER, PFC_AUTO_INIT
+};
+
+const char *pfc_reg_name[] = {
+ "PFC_VLAN_TABLE", "PFC_VLAN_CTRL",
+ "PFC_MAC_ADDR", "PFC_MAC_ADDR_MASK",
+ "PFC_HASH_TABLE",
+ "PFC_L2_CLASS_CONFIG", "PFC_L3_CLASS_CONFIG",
+ "PFC_TCAM_KEY0", "PFC_TCAM_KEY1", "PFC_TCAM_MASK0", "PFC_TCAM_MASK1",
+ "PFC_TCAM_CTRL",
+ "PFC_CONFIG", "TCP_CTRL_MASK", "SRC_HASH_VAL",
+ "PFC_INT_STATUS", "PFC_DBG_INT_STATUS", "PFC_INT_MASK",
+ "PFC_DROP_LOG", "PFC_DROP_LOG_MASK",
+ "PFC_VLAN_PAR_ERR_LOG", "PFC_TCAM_PAR_ERR_LOG",
+ "PFC_BAD_CS_COUNTER", "PFC_DROP_COUNTER", "PFC_AUTO_INIT"
+};
+
+hpi_status_t
+hpi_pfc_dump_regs(hpi_handle_t handle)
+{
+ uint64_t value;
+ int num_regs, i;
+
+ num_regs = sizeof (pfc_reg_offset) / sizeof (uint64_t);
+ HPI_REG_DUMP_MSG(( HPI_REG_CTL,
+ "\nPFC Register Dump \n"));
+
+ for (i = 0; i < num_regs; i++) {
+ REG_PIO_READ64(handle, pfc_reg_offset[i], &value);
+ HPI_REG_DUMP_MSG(( HPI_REG_CTL,
+ " %08llx %s\t %016llx \n",
+ pfc_reg_offset[i], pfc_reg_name[i], value));
+ }
+
+ HPI_REG_DUMP_MSG(( HPI_REG_CTL,
+ "\n PFC Register Dump done\n"));
+
+ return (HPI_SUCCESS);
+}
+
+static uint64_t
+hpi_pfc_tcam_check_completion(hpi_handle_t handle, tcam_op_t op_type)
+{
+ uint32_t try_counter, tcam_delay = 10;
+ pfc_tcam_ctrl_t tctl;
+
+ try_counter = TCAM_COMPLETION_TRY_COUNT;
+
+ switch (op_type) {
+ case TCAM_RWC_STAT:
+ READ_TCAM_REG_CTL(handle, &tctl.value);
+ while ((try_counter) &&
+ (tctl.bits.status != TCAM_CTL_RWC_RWC_STAT)) {
+ try_counter--;
+ HXGE_DELAY(tcam_delay);
+ READ_TCAM_REG_CTL(handle, &tctl.value);
+ }
+
+ if (!try_counter) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " TCAM RWC_STAT operation"
+ " failed to complete \n"));
+ return (HPI_PFC_TCAM_HW_ERROR);
+ }
+
+ tctl.value = 0;
+ break;
+ case TCAM_RWC_MATCH:
+ READ_TCAM_REG_CTL(handle, &tctl.value);
+
+ while ((try_counter) &&
+ (tctl.bits.match != TCAM_CTL_RWC_RWC_MATCH)) {
+ try_counter--;
+ HXGE_DELAY(tcam_delay);
+ READ_TCAM_REG_CTL(handle, &tctl.value);
+ }
+
+ if (!try_counter) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " TCAM Match operationfailed to find match \n"));
+ }
+
+ break;
+ default:
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " Invalid TCAM completion Request \n"));
+ return (HPI_PFC_ERROR | HPI_TCAM_ERROR | OPCODE_INVALID);
+ }
+
+ return (tctl.value);
+}
+
+hpi_status_t
+hpi_pfc_tcam_entry_read(hpi_handle_t handle, uint32_t location,
+ hxge_tcam_entry_t *tcam_ptr)
+{
+ pfc_tcam_ctrl_t tctl;
+ pfc_tcam_ctrl_t tctl_rv;
+
+ /*
+ * Hydra doesn't allow to read TCAM entries. Use compare instead.
+ */
+ WRITE_TCAM_REG_MASK0(handle, tcam_ptr->mask0);
+ WRITE_TCAM_REG_MASK1(handle, tcam_ptr->mask1);
+
+ WRITE_TCAM_REG_KEY0(handle, tcam_ptr->key0);
+ WRITE_TCAM_REG_KEY1(handle, tcam_ptr->key1);
+
+ tctl.value = 0;
+ tctl.bits.addr = location;
+ tctl.bits.cmd = TCAM_CTL_RWC_TCAM_CMP;
+
+ WRITE_TCAM_REG_CTL(handle, tctl.value);
+
+ tctl_rv.value = hpi_pfc_tcam_check_completion(handle, TCAM_RWC_MATCH);
+
+ if (tctl_rv.bits.match)
+ return (HPI_SUCCESS);
+ else
+ return (HPI_FAILURE);
+}
+
+hpi_status_t
+hpi_pfc_tcam_asc_ram_entry_write(hpi_handle_t handle, uint32_t location,
+ uint64_t ram_data)
+{
+ uint64_t tcam_stat = 0;
+ pfc_tcam_ctrl_t tctl;
+
+ WRITE_TCAM_REG_KEY0(handle, ram_data);
+
+ tctl.value = 0;
+ tctl.bits.addr = location;
+ tctl.bits.cmd = TCAM_CTL_RWC_RAM_WR;
+
+ HPI_DEBUG_MSG(( HPI_PFC_CTL,
+ " tcam ascr write: location %x data %llx ctl value %llx \n",
+ location, ram_data, tctl.value));
+ WRITE_TCAM_REG_CTL(handle, tctl.value);
+ tcam_stat = hpi_pfc_tcam_check_completion(handle, TCAM_RWC_STAT);
+
+ if (tcam_stat & HPI_FAILURE) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ "TCAM RAM write failed loc %d \n", location));
+ return (HPI_PFC_ASC_RAM_WR_ERROR);
+ }
+
+ return (HPI_SUCCESS);
+}
+
+static hpi_status_t
+hpi_pfc_set_config(hpi_handle_t handle, pfc_config_t config)
+{
+ uint64_t offset;
+
+ offset = PFC_CONFIG;
+ REG_PIO_WRITE64(handle, offset, config.value);
+
+ return (HPI_SUCCESS);
+}
+
+static hpi_status_t
+hpi_pfc_get_config(hpi_handle_t handle, pfc_config_t *configp)
+{
+ uint64_t offset;
+
+ offset = PFC_CONFIG;
+ REG_PIO_READ64(handle, offset, &configp->value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_set_tcam_enable(hpi_handle_t handle, boolean_t tcam)
+{
+ pfc_config_t config;
+
+ /*
+ * Read the register first.
+ */
+ (void) hpi_pfc_get_config(handle, &config);
+
+ if (tcam)
+ config.bits.tcam_en = 1;
+ else
+ config.bits.tcam_en = 0;
+
+ return (hpi_pfc_set_config(handle, config));
+}
+
+hpi_status_t
+hpi_pfc_set_l2_hash(hpi_handle_t handle, boolean_t l2_hash)
+{
+ pfc_config_t config;
+
+ /*
+ * Read the register first.
+ */
+ (void) hpi_pfc_get_config(handle, &config);
+
+ if (l2_hash)
+ config.bits.l2_hash_en = 1;
+ else
+ config.bits.l2_hash_en = 0;
+
+ return (hpi_pfc_set_config(handle, config));
+}
+
+hpi_status_t
+hpi_pfc_set_tcp_cksum(hpi_handle_t handle, boolean_t cksum)
+{
+ pfc_config_t config;
+
+ /*
+ * Read the register first.
+ */
+ (void) hpi_pfc_get_config(handle, &config);
+
+ if (cksum)
+ config.bits.tcp_cs_en = 1;
+ else
+ config.bits.tcp_cs_en = 0;
+
+ return (hpi_pfc_set_config(handle, config));
+}
+
+hpi_status_t
+hpi_pfc_set_default_dma(hpi_handle_t handle, uint32_t dma_channel_no)
+{
+ pfc_config_t config;
+
+ (void) hpi_pfc_get_config(handle, &config);
+
+ if (dma_channel_no > PFC_MAX_DMA_CHANNELS)
+ return (HPI_FAILURE);
+
+ config.bits.default_dma = dma_channel_no;
+
+ return (hpi_pfc_set_config(handle, config));
+}
+
+hpi_status_t
+hpi_pfc_mac_addr_enable(hpi_handle_t handle, uint32_t slot)
+{
+ pfc_config_t config;
+ uint32_t bit;
+
+ if (slot >= PFC_N_MAC_ADDRESSES) {
+ return (HPI_FAILURE);
+ }
+
+ (void) hpi_pfc_get_config(handle, &config);
+
+ bit = 1 << slot;
+ config.bits.mac_addr_en = config.bits.mac_addr_en | bit;
+
+ return (hpi_pfc_set_config(handle, config));
+}
+
+hpi_status_t
+hpi_pfc_mac_addr_disable(hpi_handle_t handle, uint32_t slot)
+{
+ pfc_config_t config;
+ uint32_t bit;
+
+ if (slot >= PFC_N_MAC_ADDRESSES) {
+ return (HPI_FAILURE);
+ }
+
+ (void) hpi_pfc_get_config(handle, &config);
+
+ bit = 1 << slot;
+ config.bits.mac_addr_en = config.bits.mac_addr_en & ~bit;
+
+ return (hpi_pfc_set_config(handle, config));
+}
+
+hpi_status_t
+hpi_pfc_set_force_csum(hpi_handle_t handle, boolean_t force)
+{
+ pfc_config_t config;
+
+ (void) hpi_pfc_get_config(handle, &config);
+
+ if (force)
+ config.bits.force_cs_en = 1;
+ else
+ config.bits.force_cs_en = 0;
+
+ return (hpi_pfc_set_config(handle, config));
+}
+
+hpi_status_t
+hpi_pfc_cfg_vlan_table_dump(hpi_handle_t handle)
+{
+ int i;
+ int offset;
+ int step = 8;
+ pfc_vlan_table_t table_entry;
+
+ printk(KERN_DEBUG "Vlan table dump\n");
+ for (i = 0; i < 128; i++) {
+ table_entry.value = 0;
+ offset = PFC_VLAN_TABLE + i * step;
+ REG_PIO_READ64(handle, offset, &table_entry.value);
+
+ printk(KERN_DEBUG "%08x ", (unsigned int)table_entry.bits.member);
+ if ((i % 8) == 0)
+ printk(KERN_DEBUG "\n");
+ }
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_cfg_vlan_table_clear(hpi_handle_t handle)
+{
+ int i;
+ int offset;
+ int step = 8;
+ pfc_vlan_table_t table_entry;
+
+ table_entry.value = 0;
+ for (i = 0; i < 128; i++) {
+ table_entry.bits.member = 0;
+ offset = PFC_VLAN_TABLE + i * step;
+ REG_PIO_WRITE64(handle, offset, table_entry.value);
+ }
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_cfg_vlan_table_entry_clear(hpi_handle_t handle, vlan_id_t vlan_id)
+{
+ uint64_t offset;
+ pfc_vlan_table_t vlan_tbl_entry;
+ uint64_t bit;
+
+ /*
+ * Assumes that the hardware will generate the new parity
+ * data.
+ */
+ offset = PFC_VLAN_REG_OFFSET(vlan_id);
+ REG_PIO_READ64(handle, offset, (uint64_t *)&vlan_tbl_entry.value);
+
+ bit = PFC_VLAN_BIT_OFFSET(vlan_id);
+ bit = 1 << bit;
+ vlan_tbl_entry.bits.member = vlan_tbl_entry.bits.member & ~bit;
+
+ REG_PIO_WRITE64(handle, offset, vlan_tbl_entry.value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_cfg_vlan_table_entry_set(hpi_handle_t handle, vlan_id_t vlan_id)
+{
+ uint64_t offset;
+ pfc_vlan_table_t vlan_tbl_entry;
+ uint64_t bit;
+
+ /*
+ * Assumes that the hardware will generate the new parity
+ * data.
+ */
+ offset = PFC_VLAN_REG_OFFSET(vlan_id);
+ REG_PIO_READ64(handle, offset, (uint64_t *)&vlan_tbl_entry.value);
+
+ bit = PFC_VLAN_BIT_OFFSET(vlan_id);
+ bit = 1 << bit;
+ vlan_tbl_entry.bits.member = vlan_tbl_entry.bits.member | bit;
+
+ REG_PIO_WRITE64(handle, offset, vlan_tbl_entry.value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_cfg_vlan_control_set(hpi_handle_t handle, boolean_t parity,
+ boolean_t valid, vlan_id_t vlan_id)
+{
+ pfc_vlan_ctrl_t vlan_control;
+
+ vlan_control.value = 0;
+
+ if (parity)
+ vlan_control.bits.par_en = 1;
+ else
+ vlan_control.bits.par_en = 0;
+
+ if (valid)
+ vlan_control.bits.valid = 1;
+ else
+ vlan_control.bits.valid = 0;
+
+ vlan_control.bits.id = vlan_id;
+
+ REG_PIO_WRITE64(handle, PFC_VLAN_CTRL, vlan_control.value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_get_vlan_parity_log(hpi_handle_t handle, pfc_vlan_par_err_log_t *logp)
+{
+ uint64_t offset;
+
+ offset = PFC_VLAN_PAR_ERR_LOG;
+ REG_PIO_READ64(handle, offset, &logp->value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_set_mac_address(hpi_handle_t handle, uint32_t slot, uint64_t address)
+{
+ uint64_t offset;
+ uint64_t moffset;
+ pfc_mac_addr_mask_t mask;
+ pfc_mac_addr_t addr;
+
+ if (slot >= PFC_N_MAC_ADDRESSES)
+ return (HPI_FAILURE);
+
+ offset = PFC_MAC_ADDRESS(slot);
+ moffset = PFC_MAC_ADDRESS_MASK(slot);
+
+ addr.bits.addr = address;
+ mask.bits.mask = 0x0;
+
+ REG_PIO_WRITE64(handle, offset, addr.value);
+ REG_PIO_WRITE64(handle, moffset, mask.value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_get_mac_address(hpi_handle_t handle, int slot, uint8_t *data)
+{
+ pfc_mac_addr_t addr;
+ uint64_t offset;
+
+ if (slot >= PFC_N_MAC_ADDRESSES)
+ return (HPI_FAILURE);
+
+ offset = PFC_MAC_ADDRESS(slot);
+ REG_PIO_READ64(handle, offset, &addr.value);
+
+ data[0] = addr.byte[5];
+ data[1] = addr.byte[4];
+ data[2] = addr.byte[3];
+ data[3] = addr.byte[2];
+ data[4] = addr.byte[1];
+ data[5] = addr.byte[0];
+
+ return HPI_SUCCESS;
+}
+
+
+hpi_status_t
+hpi_pfc_clear_mac_address(hpi_handle_t handle, uint32_t slot)
+{
+ uint64_t offset, moffset;
+ uint64_t zaddr = 0x0ULL;
+ uint64_t zmask = 0x0ULL;
+
+ if (slot >= PFC_N_MAC_ADDRESSES)
+ return (HPI_FAILURE);
+
+ (void) hpi_pfc_mac_addr_disable(handle, slot);
+
+ offset = PFC_MAC_ADDRESS(slot);
+ moffset = PFC_MAC_ADDRESS_MASK(slot);
+
+ REG_PIO_WRITE64(handle, offset, zaddr);
+ REG_PIO_WRITE64(handle, moffset, zmask);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_clear_multicast_hash_table(hpi_handle_t handle, uint32_t slot)
+{
+ uint64_t offset;
+
+ offset = PFC_HASH_ADDR(slot);
+ REG_PIO_WRITE64(handle, offset, 0ULL);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_set_multicast_hash_table(hpi_handle_t handle, uint32_t slot,
+ uint64_t address)
+{
+ uint64_t offset;
+
+ offset = PFC_HASH_ADDR(slot);
+ REG_PIO_WRITE64(handle, offset, address);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_set_l2_class_slot(hpi_handle_t handle, uint16_t etype, boolean_t valid,
+ int slot)
+{
+ pfc_l2_class_config_t l2_config;
+ uint64_t offset;
+
+ l2_config.value = 0;
+
+ if (valid)
+ l2_config.bits.valid = 1;
+ else
+ l2_config.bits.valid = 0;
+
+ l2_config.bits.etype = etype;
+ l2_config.bits.rsrvd = 0;
+
+ offset = PFC_L2_CONFIG(slot);
+ REG_PIO_WRITE64(handle, offset, l2_config.value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_set_l3_class_config(hpi_handle_t handle, tcam_class_t slot,
+ tcam_key_cfg_t cfg)
+{
+ pfc_l3_class_config_t l3_config;
+ uint64_t offset;
+
+ l3_config.value = 0;
+
+ if (cfg.lookup_enable)
+ l3_config.bits.tsel = 1;
+ else
+ l3_config.bits.tsel = 0;
+
+ if (cfg.discard)
+ l3_config.bits.discard = 1;
+ else
+ l3_config.bits.discard = 0;
+
+ offset = PFC_L3_CONFIG(slot);
+ REG_PIO_WRITE64(handle, offset, l3_config.value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_get_l3_class_config(hpi_handle_t handle, tcam_class_t slot,
+ tcam_key_cfg_t *cfg)
+{
+ pfc_l3_class_config_t l3_config;
+ uint64_t offset;
+
+ offset = PFC_L3_CONFIG(slot);
+ REG_PIO_READ64(handle, offset, &l3_config.value);
+
+ if (l3_config.bits.tsel)
+ cfg->lookup_enable = 1;
+ else
+ cfg->lookup_enable = 0;
+
+ if (l3_config.bits.discard)
+ cfg->discard = 1;
+ else
+ cfg->discard = 0;
+
+ return (HPI_SUCCESS);
+}
+
+static hpi_status_t
+hpi_pfc_set_tcam_control(hpi_handle_t handle, pfc_tcam_ctrl_t *tcontrolp)
+{
+ uint64_t offset;
+
+ offset = PFC_TCAM_CTRL;
+ REG_PIO_WRITE64(handle, offset, tcontrolp->value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_tcam_entry_invalidate(hpi_handle_t handle, uint32_t location)
+{
+ hxge_tcam_entry_t tcam_ptr;
+
+ memset(&tcam_ptr, 0, sizeof (hxge_tcam_entry_t));
+ hpi_pfc_tcam_entry_write(handle, location, &tcam_ptr);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_tcam_invalidate_all(hpi_handle_t handle)
+{
+ int i;
+ pfc_tcam_ctrl_t tcontrol;
+
+ tcontrol.value = 0;
+ for (i = 0; i < PFC_N_TCAM_ENTRIES; i++) {
+ (void) hpi_pfc_set_tcam_control(handle, &tcontrol);
+ (void) hpi_pfc_tcam_entry_invalidate(handle, i);
+ }
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_tcam_entry_write(hpi_handle_t handle, uint32_t location,
+ hxge_tcam_entry_t *tcam_ptr)
+{
+ uint64_t tcam_stat;
+ pfc_tcam_ctrl_t tctl;
+
+ WRITE_TCAM_REG_MASK0(handle, tcam_ptr->mask0);
+ WRITE_TCAM_REG_MASK1(handle, tcam_ptr->mask1);
+
+ WRITE_TCAM_REG_KEY0(handle, tcam_ptr->key0);
+ WRITE_TCAM_REG_KEY1(handle, tcam_ptr->key1);
+
+ HPI_DEBUG_MSG(( HPI_PFC_CTL,
+ " tcam write: location %x\n key: %llx %llx\n mask: %llx %llx\n",
+ location, tcam_ptr->key0, tcam_ptr->key1,
+ tcam_ptr->mask0, tcam_ptr->mask1));
+
+ tctl.value = 0;
+ tctl.bits.addr = location;
+ tctl.bits.cmd = TCAM_CTL_RWC_TCAM_WR;
+
+ HPI_DEBUG_MSG(( HPI_PFC_CTL,
+ " tcam write: ctl value %llx \n", tctl.value));
+
+ WRITE_TCAM_REG_CTL(handle, tctl.value);
+
+ tcam_stat = hpi_pfc_tcam_check_completion(handle, TCAM_RWC_STAT);
+
+ if (tcam_stat & HPI_FAILURE) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ "TCAM Write failed loc %d \n", location));
+ return (HPI_PFC_TCAM_WR_ERROR);
+ }
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_get_tcam_parity_log(hpi_handle_t handle, pfc_tcam_par_err_log_t *logp)
+{
+ uint64_t offset;
+
+ offset = PFC_TCAM_PAR_ERR_LOG;
+ REG_PIO_READ64(handle, offset, &logp->value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_set_hash_seed_value(hpi_handle_t handle, uint32_t seed)
+{
+ uint64_t offset;
+ src_hash_val_t src_hash_seed;
+
+ src_hash_seed.value = 0;
+ src_hash_seed.bits.seed = seed;
+
+ offset = SRC_HASH_VAL;
+ REG_PIO_WRITE64(handle, offset, src_hash_seed.value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_get_interrupt_status(hpi_handle_t handle, pfc_int_status_t *statusp)
+{
+ uint64_t offset;
+
+ offset = PFC_INT_STATUS;
+ REG_PIO_READ64(handle, offset, &statusp->value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_clear_interrupt_status(hpi_handle_t handle)
+{
+ uint64_t offset;
+ uint64_t value = ~0ULL; /* force a match to occur */
+
+ offset = PFC_INT_STATUS;
+ REG_PIO_WRITE64(handle, offset, value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_set_interrupt_mask(hpi_handle_t handle, boolean_t drop,
+ boolean_t tcam_parity_error, boolean_t vlan_parity_error)
+{
+ pfc_int_mask_t mask;
+ uint64_t offset;
+
+ mask.value = 0;
+
+ if (drop)
+ mask.bits.pkt_drop_mask = 1;
+ else
+ mask.bits.pkt_drop_mask = 0;
+
+ if (tcam_parity_error)
+ mask.bits.tcam_parity_err_mask = 1;
+ else
+ mask.bits.tcam_parity_err_mask = 0;
+
+ if (vlan_parity_error)
+ mask.bits.vlan_parity_err_mask = 1;
+ else
+ mask.bits.vlan_parity_err_mask = 0;
+
+ offset = PFC_INT_MASK;
+ REG_PIO_WRITE64(handle, offset, mask.value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_get_drop_log(hpi_handle_t handle, pfc_drop_log_t *logp)
+{
+ uint64_t offset;
+
+ offset = PFC_DROP_LOG;
+ REG_PIO_READ64(handle, offset, &logp->value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_set_drop_log_mask(hpi_handle_t handle, boolean_t vlan_drop,
+ boolean_t tcam_drop, boolean_t class_code_drop, boolean_t l2_addr_drop,
+ boolean_t tcp_ctrl_drop)
+{
+ uint64_t offset;
+ pfc_drop_log_mask_t log;
+
+ log.value = 0;
+
+ if (vlan_drop)
+ log.bits.vlan_drop_mask = 1;
+ if (tcam_drop)
+ log.bits.tcam_drop_mask = 1;
+ if (class_code_drop)
+ log.bits.class_code_drop_mask = 1;
+ if (l2_addr_drop)
+ log.bits.l2_addr_drop_mask = 1;
+ if (tcp_ctrl_drop)
+ log.bits.tcp_ctrl_drop_mask = 1;
+
+ offset = PFC_DROP_LOG_MASK;
+ REG_PIO_WRITE64(handle, offset, log.value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_get_bad_csum_counter(hpi_handle_t handle, uint64_t *countp)
+{
+ uint64_t offset;
+
+ offset = PFC_BAD_CS_COUNTER;
+ REG_PIO_READ64(handle, offset, countp);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_pfc_get_drop_counter(hpi_handle_t handle, uint64_t *countp)
+{
+ uint64_t offset;
+
+ offset = PFC_DROP_COUNTER;
+ REG_PIO_READ64(handle, offset, countp);
+
+ return (HPI_SUCCESS);
+}
+
+/* This routine reads the MAC address from the SPROM programmed HCR
+ * region. This routine is used as part of programming the MAC addresses
+ * into the PFC for use
+ */
+hpi_status_t
+hpi_hcr_mac_addr_get(hpi_handle_t handle, int index, uint8_t *data)
+{
+ uint64_t step = sizeof (uint64_t);
+ uint32_t addr_hi = 0, addr_lo = 0;
+ peu_debug_training_vec_t blade;
+ uint32_t mac_offset;
+
+ HXGE_REG_RD32(handle, PEU_DEBUG_TRAINING_VEC, &blade.value);
+
+ printk(KERN_DEBUG "hpi_hcr_mac_addr_get: Blade ID %d\n", blade.bits.bld_num_upper);
+ if (!blade.bits.bld_num_upper) { /* for blade ID zero */
+ mac_offset = 0x8; /* CR 6687755 blade/port 0 is different */
+ } else {
+ mac_offset = 0xC;
+ }
+
+ /*
+ * Read the static MAC address out of the HCR. Note: these HCR
+ * MAC address(es) are initialized by NEM/SP from SPROM.
+ */
+ HXGE_REG_RD32(handle, HCR_REG + mac_offset + index * step, &addr_lo);
+ HXGE_REG_RD32(handle, HCR_REG + mac_offset+4 + index * step, &addr_hi);
+
+ /* Note that the MAC address in the SPROM is stored in big-endian
+ * format. So, do the transformation to little-endian for PFC
+ * and driver
+ */
+ data[0] = (addr_hi & 0x000000ff);
+ data[1] = (addr_hi & 0x0000ff00) >> 8;
+ data[2] = (addr_lo & 0xff000000) >> 24;
+ data[3] = (addr_lo & 0x00ff0000) >> 16;
+ data[4] = (addr_lo & 0x0000ff00) >> 8;
+ data[5] = addr_lo & 0x000000ff;
+
+ return (HPI_SUCCESS);
+}
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#ifndef _HPI_PFC_H
+#define _HPI_PFC_H
+
+#include "hpi.h"
+#include "../hxge_pfc_hw.h"
+#include "../hxge_pfc.h"
+
+typedef enum _tcam_op {
+ TCAM_RWC_STAT = 0x1,
+ TCAM_RWC_MATCH = 0x2
+} tcam_op_t;
+
+#define HPI_TCAM_COMP_NO_MATCH 0x8000000000000ULL
+
+/*
+ * HPI PFC ERROR Codes
+ */
+#define HPI_PFC_BLK_CODE PFC_BLK_ID << 8
+#define HPI_PFC_ERROR (HPI_FAILURE | HPI_PFC_BLK_CODE)
+#define HPI_TCAM_ERROR 0x10
+#define HPI_FCRAM_ERROR 0x20
+#define HPI_GEN_PFC 0x30
+#define HPI_PFC_SW_PARAM_ERROR 0x40
+#define HPI_PFC_HW_ERROR 0x80
+
+#define HPI_PFC_RESET_ERROR (HPI_PFC_ERROR | HPI_GEN_PFC | RESET_FAILED)
+#define HPI_PFC_RDC_TABLE_INVALID (HPI_PFC_ERROR | RDC_TAB_INVALID)
+#define HPI_PFC_VLAN_INVALID (HPI_PFC_ERROR | VLAN_INVALID)
+#define HPI_PFC_PORT_INVALID (HPI_PFC_ERROR | PORT_INVALID)
+#define HPI_PFC_TCAM_RD_ERROR \
+ (HPI_PFC_ERROR | HPI_TCAM_ERROR | READ_FAILED)
+#define HPI_PFC_TCAM_WR_ERROR \
+ (HPI_PFC_ERROR | HPI_TCAM_ERROR | WRITE_FAILED)
+#define HPI_PFC_TCAM_LOC_INVALID \
+ (HPI_PFC_ERROR | HPI_TCAM_ERROR | LOCATION_INVALID)
+#define HPI_PFC_ASC_RAM_RD_ERROR \
+ (HPI_PFC_ERROR | HPI_TCAM_ERROR | READ_FAILED)
+#define HPI_PFC_ASC_RAM_WR_ERROR \
+ (HPI_PFC_ERROR | HPI_TCAM_ERROR | WRITE_FAILED)
+#define HPI_PFC_FCRAM_READ_ERROR \
+ (HPI_PFC_ERROR | HPI_FCRAM_ERROR | READ_FAILED)
+#define HPI_PFC_FCRAM_WR_ERROR \
+ (HPI_PFC_ERROR | HPI_FCRAM_ERROR | WRITE_FAILED)
+#define HPI_PFC_FCRAM_PART_INVALID \
+ (HPI_PFC_ERROR | HPI_FCRAM_ERROR | RDC_TAB_INVALID)
+#define HPI_PFC_FCRAM_LOC_INVALID \
+ (HPI_PFC_ERROR | HPI_FCRAM_ERROR | LOCATION_INVALID)
+
+#define TCAM_CLASS_INVALID \
+ (HPI_PFC_SW_PARAM_ERROR | 0xb)
+/* have only 0xc, 0xd, 0xe and 0xf left for sw error codes */
+#define HPI_PFC_TCAM_CLASS_INVALID \
+ (HPI_PFC_ERROR | HPI_TCAM_ERROR | TCAM_CLASS_INVALID)
+#define HPI_PFC_TCAM_HW_ERROR \
+ (HPI_PFC_ERROR | HPI_PFC_HW_ERROR | HPI_TCAM_ERROR)
+#define HPI_PFC_FCRAM_HW_ERROR \
+ (HPI_PFC_ERROR | HPI_PFC_HW_ERROR | HPI_FCRAM_ERROR)
+
+#define PFC_N_VLAN_REGISTERS 0x80
+#define PFC_N_VLAN_MEMBERS 0x20
+
+#define PFC_N_MAC_ADDRESSES 16
+#define PFC_MAX_DMA_CHANNELS 4
+#define PFC_MAC_ADDR_STEP 8
+
+#define PFC_HASH_TABLE_SIZE 16
+#define PFC_HASH_STEP 0x08
+
+#define PFC_L2_CLASS_CONFIG_STEP 0x08
+
+#define PFC_L3_CLASS_SLOTS 0x08
+#define PFC_L3_CLASS_CONFIG_STEP 0x08
+
+#define PFC_N_TCAM_ENTRIES 42
+
+#define PFC_VLAN_REG_OFFSET(vlan_id) \
+ ((((vlan_id_t)(vlan_id / PFC_N_VLAN_MEMBERS)) * 8) + PFC_VLAN_TABLE)
+#define PFC_VLAN_BIT_OFFSET(vlan_id) \
+ (vlan_id % PFC_N_VLAN_MEMBERS)
+#define PFC_MAC_ADDRESS(slot) \
+ ((slot * PFC_MAC_ADDR_STEP) + PFC_MAC_ADDR)
+#define PFC_MAC_ADDRESS_MASK(slot) \
+ ((slot * PFC_MAC_ADDR_STEP) + PFC_MAC_ADDR_MASK)
+#define PFC_HASH_ADDR(slot) \
+ ((slot * PFC_HASH_STEP) + PFC_HASH_TABLE)
+
+#define PFC_L2_CONFIG(slot) \
+ ((slot * PFC_L2_CLASS_CONFIG_STEP) + PFC_L2_CLASS_CONFIG)
+#define PFC_L3_CONFIG(slot) \
+ (((slot - TCAM_CLASS_TCP_IPV4) * PFC_L3_CLASS_CONFIG_STEP) + \
+ PFC_L3_CLASS_CONFIG)
+
+typedef uint16_t vlan_id_t;
+
+hpi_status_t hpi_pfc_dump_regs(hpi_handle_t handle);
+
+/*
+ * PFC Control Register Functions
+ */
+hpi_status_t hpi_pfc_set_tcam_enable(hpi_handle_t, boolean_t);
+hpi_status_t hpi_pfc_set_l2_hash(hpi_handle_t, boolean_t);
+hpi_status_t hpi_pfc_set_tcp_cksum(hpi_handle_t, boolean_t);
+hpi_status_t hpi_pfc_set_default_dma(hpi_handle_t, uint32_t);
+hpi_status_t hpi_pfc_mac_addr_enable(hpi_handle_t, uint32_t);
+hpi_status_t hpi_pfc_mac_addr_disable(hpi_handle_t, uint32_t);
+hpi_status_t hpi_pfc_set_force_csum(hpi_handle_t, boolean_t);
+
+/*
+ * PFC vlan Functions
+ */
+hpi_status_t hpi_pfc_cfg_vlan_table_dump(hpi_handle_t handle);
+hpi_status_t hpi_pfc_cfg_vlan_table_clear(hpi_handle_t);
+hpi_status_t hpi_pfc_cfg_vlan_table_entry_clear(hpi_handle_t, vlan_id_t);
+hpi_status_t hpi_pfc_cfg_vlan_table_entry_set(hpi_handle_t, vlan_id_t);
+hpi_status_t hpi_pfc_cfg_vlan_control_set(hpi_handle_t, boolean_t,
+ boolean_t, vlan_id_t);
+hpi_status_t hpi_pfc_get_vlan_parity_log(hpi_handle_t,
+ pfc_vlan_par_err_log_t *);
+
+/*
+ * PFC Mac Address Functions
+ */
+hpi_status_t hpi_pfc_set_mac_address(hpi_handle_t, uint32_t, uint64_t);
+hpi_status_t hpi_pfc_clear_mac_address(hpi_handle_t, uint32_t);
+hpi_status_t hpi_pfc_clear_multicast_hash_table(hpi_handle_t, uint32_t);
+hpi_status_t hpi_pfc_set_multicast_hash_table(hpi_handle_t, uint32_t,
+ uint64_t);
+
+/*
+ * PFC L2 and L3 Config Functions.
+ */
+hpi_status_t hpi_pfc_set_l2_class_slot(hpi_handle_t, uint16_t, boolean_t,
+ int);
+hpi_status_t hpi_pfc_get_l3_class_config(hpi_handle_t handle, tcam_class_t slot,
+ tcam_key_cfg_t *cfg);
+hpi_status_t hpi_pfc_set_l3_class_config(hpi_handle_t handle, tcam_class_t slot,
+ tcam_key_cfg_t cfg);
+
+/*
+ * PFC TCAM Functions
+ */
+hpi_status_t hpi_pfc_tcam_invalidate_all(hpi_handle_t);
+hpi_status_t hpi_pfc_tcam_entry_invalidate(hpi_handle_t, uint32_t);
+hpi_status_t hpi_pfc_tcam_entry_write(hpi_handle_t, uint32_t,
+ hxge_tcam_entry_t *);
+hpi_status_t hpi_pfc_tcam_entry_read(hpi_handle_t, uint32_t,
+ hxge_tcam_entry_t *);
+hpi_status_t hpi_pfc_tcam_asc_ram_entry_read(hpi_handle_t handle,
+ uint32_t location, uint64_t *ram_data);
+hpi_status_t hpi_pfc_tcam_asc_ram_entry_write(hpi_handle_t handle,
+ uint32_t location, uint64_t ram_data);
+hpi_status_t hpi_pfc_get_tcam_parity_log(hpi_handle_t,
+ pfc_tcam_par_err_log_t *);
+hpi_status_t hpi_pfc_get_tcam_auto_init(hpi_handle_t,
+ pfc_auto_init_t *);
+
+/*
+ * PFC Hash Seed Value
+ */
+hpi_status_t hpi_pfc_set_hash_seed_value(hpi_handle_t, uint32_t);
+
+/*
+ * PFC Interrupt Management Functions
+ */
+hpi_status_t hpi_pfc_get_interrupt_status(hpi_handle_t, pfc_int_status_t *);
+hpi_status_t hpi_pfc_clear_interrupt_status(hpi_handle_t);
+hpi_status_t hpi_pfc_set_interrupt_mask(hpi_handle_t, boolean_t,
+ boolean_t, boolean_t);
+
+/*
+ * PFC Packet Logs
+ */
+hpi_status_t hpi_pfc_get_drop_log(hpi_handle_t, pfc_drop_log_t *);
+hpi_status_t hpi_pfc_set_drop_log_mask(hpi_handle_t, boolean_t,
+ boolean_t, boolean_t, boolean_t, boolean_t);
+hpi_status_t hpi_pfc_get_bad_csum_counter(hpi_handle_t, uint64_t *);
+hpi_status_t hpi_pfc_get_drop_counter(hpi_handle_t, uint64_t *);
+
+hpi_status_t hpi_pfc_get_mac_address(hpi_handle_t handle, int i, uint8_t *data);
+hpi_status_t hpi_hcr_mac_addr_get(hpi_handle_t handle, int i, uint8_t *data);
+hpi_status_t hpi_pfc_num_macs_get(hpi_handle_t handle, uint8_t *data);
+
+#endif /* !_HPI_PFC_H */
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#include "hpi_rxdma.h"
+
+/* The state bit indicates the value that you want the qst bit to be */
+hpi_status_t
+hpi_rxdma_cfg_rdc_wait_for_qst(hpi_handle_t handle, uint8_t rdc,
+ rdc_rx_cfg1_t *cfg, int state )
+{
+ uint32_t count = RXDMA_RESET_TRY_COUNT;
+ uint32_t delay_time = RXDMA_RESET_DELAY;
+
+ RXDMA_REG_READ64(handle, RDC_RX_CFG1, rdc, &cfg->value);
+ while ((count--) && (cfg->bits.qst == (1-state)))
+ {
+ HXGE_DELAY(delay_time);
+ RXDMA_REG_READ64(handle, RDC_RX_CFG1, rdc, &cfg->value);
+ }
+
+ if (!count) {
+ printk(KERN_DEBUG "hpi_rxdma_cfg_rdc_wait_for_qst not set to %d\n",state);
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ " hpi_rxdma_cfg_rdc_ctl"
+ " RXDMA_OP_DISABLE Failed for RDC %d \n",
+ rdc));
+ return (HPI_RXDMA_ERROR_ENCODE(HPI_RXDMA_RESET_ERR, rdc));
+ }
+
+ return (HPI_SUCCESS);
+}
+
+/* RX DMA functions */
+hpi_status_t
+hpi_rxdma_cfg_rdc_ctl(hpi_handle_t handle, uint8_t rdc, uint8_t op)
+{
+ rdc_rx_cfg1_t cfg;
+ uint32_t err = HPI_SUCCESS;
+
+ if (!RXDMA_CHANNEL_VALID(rdc)) {
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ "hpi_rxdma_cfg_rdc_ctl Illegal RDC number %d \n", rdc));
+ return (HPI_RXDMA_RDC_INVALID);
+ }
+
+ switch (op) {
+ case RXDMA_OP_ENABLE:
+ RXDMA_REG_READ64(handle, RDC_RX_CFG1, rdc, &cfg.value);
+ cfg.bits.enable = 1;
+ RXDMA_REG_WRITE64(handle, RDC_RX_CFG1, rdc, cfg.value);
+ err = hpi_rxdma_cfg_rdc_wait_for_qst(handle, rdc, &cfg, 0);
+ break;
+
+ case RXDMA_OP_DISABLE:
+ RXDMA_REG_READ64(handle, RDC_RX_CFG1, rdc, &cfg.value);
+ cfg.bits.enable = 0;
+ RXDMA_REG_WRITE64(handle, RDC_RX_CFG1, rdc, cfg.value);
+ err = hpi_rxdma_cfg_rdc_wait_for_qst(handle, rdc, &cfg, 1);
+ break;
+
+ case RXDMA_OP_RESET:
+ cfg.value = 0;
+ cfg.bits.reset = 1;
+ RXDMA_REG_WRITE64(handle, RDC_RX_CFG1, rdc, cfg.value);
+ err = hpi_rxdma_cfg_rdc_wait_for_qst(handle, rdc, &cfg,1);
+ break;
+
+ default:
+ err = HPI_RXDMA_SW_PARAM_ERROR;
+ break;
+ }
+
+ if (err != HPI_SUCCESS)
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ " hpi_rxdma_cfg_rdc_ctl"
+ " Reset Failed for RDC %d \n", rdc));
+ return (err);
+
+
+}
+
+hpi_status_t
+hpi_rxdma_cfg_rdc_rcr_ctl(hpi_handle_t handle, uint8_t rdc, uint8_t op,
+ uint16_t param)
+{
+ rdc_rcr_cfg_b_t rcr_cfgb;
+
+ if (!RXDMA_CHANNEL_VALID(rdc)) {
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ "rxdma_cfg_rdc_rcr_ctl Illegal RDC number %d \n", rdc));
+ return (HPI_RXDMA_RDC_INVALID);
+ }
+
+ RXDMA_REG_READ64(handle, RDC_RCR_CFG_B, rdc, &rcr_cfgb.value);
+
+ switch (op) {
+ case RCR_TIMEOUT_ENABLE:
+ rcr_cfgb.bits.timeout = (uint8_t)param;
+ rcr_cfgb.bits.entout = 1;
+ break;
+
+ case RCR_THRESHOLD:
+ rcr_cfgb.bits.pthres = param;
+ break;
+
+ case RCR_TIMEOUT_DISABLE:
+ rcr_cfgb.bits.entout = 0;
+ break;
+
+ default:
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ "rxdma_cfg_rdc_rcr_ctl Illegal opcode %x \n", op));
+ return (HPI_RXDMA_OPCODE_INVALID(rdc));
+ }
+
+ RXDMA_REG_WRITE64(handle, RDC_RCR_CFG_B, rdc, rcr_cfgb.value);
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_rxdma_cfg_rdc_rcr_timeout_disable(hpi_handle_t handle, uint8_t rdc)
+{
+ return (hpi_rxdma_cfg_rdc_rcr_ctl(handle, rdc, RCR_TIMEOUT_DISABLE, 0));
+}
+
+
+hpi_status_t
+hpi_rxdma_cfg_rdc_enable(hpi_handle_t handle, uint8_t rdc)
+{
+ return (hpi_rxdma_cfg_rdc_ctl(handle, rdc, RXDMA_OP_ENABLE));
+}
+
+hpi_status_t
+hpi_rxdma_cfg_rdc_disable(hpi_handle_t handle, uint8_t rdc)
+{
+ return (hpi_rxdma_cfg_rdc_ctl(handle, rdc, RXDMA_OP_DISABLE));
+}
+
+hpi_status_t
+hpi_rxdma_cfg_rdc_reset(hpi_handle_t handle, uint8_t rdc)
+{
+ return (hpi_rxdma_cfg_rdc_ctl(handle, rdc, RXDMA_OP_RESET));
+}
+
+
+hpi_status_t
+hpi_rxdma_cfg_rdc_rcr_timeout(hpi_handle_t handle, uint8_t rdc,
+ uint16_t rcr_timeout)
+{
+ return (hpi_rxdma_cfg_rdc_rcr_ctl(handle, rdc,
+ RCR_TIMEOUT_ENABLE, rcr_timeout));
+}
+
+/*
+ * hpi_rxdma_cfg_rdc_ring()
+ * Configure The RDC channel Rcv Buffer Ring
+ *
+ * Inputs:
+ * rdc: RX DMA Channel number
+ * rdc_params: RDC confiuration parameters
+ *
+ * Return:
+ * HPI_SUCCESS
+ * HPI_FAILURE
+ * HPI_SW_ERR
+ * HPI_HW_ERR
+ *
+ */
+hpi_status_t
+hpi_rxdma_cfg_rdc_ring(hpi_handle_t handle, uint8_t rdc,
+ rdc_desc_cfg_t *rdc_desc_cfg)
+{
+ rdc_rbr_cfg_a_t cfga;
+ rdc_rbr_cfg_b_t cfgb;
+ rdc_rx_cfg1_t cfg1;
+ rdc_rx_cfg2_t cfg2;
+ rdc_rcr_cfg_a_t rcr_cfga;
+ rdc_rcr_cfg_b_t rcr_cfgb;
+ rdc_page_handle_t page_handle;
+
+ if (!RXDMA_CHANNEL_VALID(rdc)) {
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ "rxdma_cfg_rdc_ring Illegal RDC number %d \n", rdc));
+ return (HPI_RXDMA_RDC_INVALID);
+ }
+
+ cfga.value = 0;
+ cfgb.value = 0;
+ cfg1.value = 0;
+ cfg2.value = 0;
+ page_handle.value = 0;
+
+ if (rdc_desc_cfg->mbox_enable == 1) {
+ cfg1.bits.mbaddr_h = (rdc_desc_cfg->mbox_addr >> 32) & 0xfff;
+ cfg2.bits.mbaddr_l = ((rdc_desc_cfg->mbox_addr &
+ RXDMA_CFIG2_MBADDR_L_MASK) >> RXDMA_CFIG2_MBADDR_L_SHIFT);
+
+ /*
+ * Only after all the configurations are set, then
+ * enable the RDC or else configuration fatal error
+ * will be returned (especially if the Hypervisor
+ * set up the logical pages with non-zero values.
+ * This HPI function only sets up the configuration.
+ * Call the enable function to enable the RDMC!
+ */
+ }
+
+ if (rdc_desc_cfg->full_hdr == 1)
+ cfg2.bits.full_hdr = 1;
+
+ if (RXDMA_BUFF_OFFSET_VALID(rdc_desc_cfg->offset)) {
+ cfg2.bits.offset = rdc_desc_cfg->offset;
+ } else {
+ cfg2.bits.offset = SW_OFFSET_NO_OFFSET;
+ }
+
+ /* rbr config */
+ cfga.value = (rdc_desc_cfg->rbr_addr &
+ (RBR_CFIG_A_STDADDR_MASK | RBR_CFIG_A_STDADDR_BASE_MASK));
+
+ /* The remaining 20 bits in the DMA address form the handle */
+ page_handle.bits.handle = (rdc_desc_cfg->rbr_addr >> 44) && 0xfffff;
+
+ /*
+ * Hydra:
+ * The RBR ring size must be multiple of 64.
+ */
+ if ((rdc_desc_cfg->rbr_len < RBR_DEFAULT_MIN_LEN) ||
+ (rdc_desc_cfg->rbr_len > RBR_DEFAULT_MAX_LEN) ||
+ (rdc_desc_cfg->rbr_len % 64)) {
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ "hpi_rxdma_cfg_rdc_ring Illegal RBR Queue Length %d \n",
+ rdc_desc_cfg->rbr_len));
+ return (HPI_RXDMA_ERROR_ENCODE(HPI_RXDMA_RBRSZIE_INVALID, rdc));
+ }
+
+ /*
+ * Hydra:
+ * The lower 6 bits are hardcoded to 0 and the higher 10 bits are
+ * stored in len.
+ */
+ cfga.bits.len = rdc_desc_cfg->rbr_len >> 6;
+ HPI_DEBUG_MSG((HPI_RDC_CTL,
+ "hpi_rxdma_cfg_rdc_ring CFGA 0x%llx len %d (RBR LEN %d)\n",
+ cfga.value, cfga.bits.len, rdc_desc_cfg->rbr_len));
+
+ /*
+ * Hydra: bksize is 1 bit, Neptune: bksize is 2 bits
+ * Buffer Block Size. b0 - 4K; b1 - 8K.
+ */
+ if (rdc_desc_cfg->page_size == SIZE_4KB)
+ cfgb.bits.bksize = RBR_BKSIZE_4K;
+ else if (rdc_desc_cfg->page_size == SIZE_8KB)
+ cfgb.bits.bksize = RBR_BKSIZE_8K;
+ else {
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ "rxdma_cfg_rdc_ring blksize: Illegal buffer size %d \n",
+ rdc_desc_cfg->page_size));
+ return (HPI_RXDMA_BUFSZIE_INVALID);
+ }
+
+ /*
+ * Hydra:
+ * Size 0 of packet buffer. b00 - 256; b01 - 512; b10 - 1K; b11 - resvd.
+ */
+ if (rdc_desc_cfg->valid0) {
+ if (rdc_desc_cfg->size0 == SIZE_256B)
+ cfgb.bits.bufsz0 = RBR_BUFSZ0_256B;
+ else if (rdc_desc_cfg->size0 == SIZE_512B)
+ cfgb.bits.bufsz0 = RBR_BUFSZ0_512B;
+ else if (rdc_desc_cfg->size0 == SIZE_1KB)
+ cfgb.bits.bufsz0 = RBR_BUFSZ0_1K;
+ else {
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ " rxdma_cfg_rdc_ring"
+ " blksize0: Illegal buffer size %x \n",
+ rdc_desc_cfg->size0));
+ return (HPI_RXDMA_BUFSZIE_INVALID);
+ }
+ cfgb.bits.vld0 = 1;
+ } else {
+ cfgb.bits.vld0 = 0;
+ }
+
+ /*
+ * Hydra:
+ * Size 1 of packet buffer. b0 - 1K; b1 - 2K.
+ */
+ if (rdc_desc_cfg->valid1) {
+ if (rdc_desc_cfg->size1 == SIZE_1KB)
+ cfgb.bits.bufsz1 = RBR_BUFSZ1_1K;
+ else if (rdc_desc_cfg->size1 == SIZE_2KB)
+ cfgb.bits.bufsz1 = RBR_BUFSZ1_2K;
+ else {
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ " rxdma_cfg_rdc_ring"
+ " blksize1: Illegal buffer size %x \n",
+ rdc_desc_cfg->size1));
+ return (HPI_RXDMA_BUFSZIE_INVALID);
+ }
+ cfgb.bits.vld1 = 1;
+ } else {
+ cfgb.bits.vld1 = 0;
+ }
+
+ /*
+ * Hydra:
+ * Size 2 of packet buffer. b0 - 2K; b1 - 4K.
+ */
+ if (rdc_desc_cfg->valid2) {
+ if (rdc_desc_cfg->size2 == SIZE_2KB)
+ cfgb.bits.bufsz2 = RBR_BUFSZ2_2K;
+ else if (rdc_desc_cfg->size2 == SIZE_4KB)
+ cfgb.bits.bufsz2 = RBR_BUFSZ2_4K;
+ else {
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ " rxdma_cfg_rdc_ring"
+ " blksize2: Illegal buffer size %x \n",
+ rdc_desc_cfg->size2));
+ return (HPI_RXDMA_BUFSZIE_INVALID);
+ }
+ cfgb.bits.vld2 = 1;
+ } else {
+ cfgb.bits.vld2 = 0;
+ }
+
+ rcr_cfga.value = (rdc_desc_cfg->rcr_addr &
+ (RCRCFIG_A_STADDR_MASK | RCRCFIG_A_STADDR_BASE_MASK));
+
+ /*
+ * Hydra:
+ * The rcr len must be multiple of 32.
+ */
+ if ((rdc_desc_cfg->rcr_len < RCR_DEFAULT_MIN_LEN) ||
+ (rdc_desc_cfg->rcr_len > HXGE_RCR_MAX) ||
+ (rdc_desc_cfg->rcr_len % 32)) {
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ " rxdma_cfg_rdc_ring Illegal RCR Queue Length %d \n",
+ rdc_desc_cfg->rcr_len));
+ return (HPI_RXDMA_ERROR_ENCODE(HPI_RXDMA_RCRSZIE_INVALID, rdc));
+ }
+
+ /*
+ * Hydra:
+ * Bits 15:5 of the maximum number of 8B entries in RCR. Bits 4:0 are
+ * hard-coded to zero. The maximum size is 2^16 - 32.
+ */
+ rcr_cfga.bits.len = rdc_desc_cfg->rcr_len >> 5;
+
+ rcr_cfgb.value = 0;
+ if (rdc_desc_cfg->rcr_timeout_enable == 1) {
+ /* check if the rcr timeout value is valid */
+
+ if (RXDMA_RCR_TO_VALID(rdc_desc_cfg->rcr_timeout)) {
+ rcr_cfgb.bits.timeout = rdc_desc_cfg->rcr_timeout;
+ rcr_cfgb.bits.entout = 1;
+ } else {
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ " rxdma_cfg_rdc_ring"
+ " Illegal RCR Timeout value %d \n",
+ rdc_desc_cfg->rcr_timeout));
+ rcr_cfgb.bits.entout = 0;
+ }
+ } else {
+ rcr_cfgb.bits.entout = 0;
+ }
+
+ rcr_cfgb.bits.pthres = rdc_desc_cfg->rcr_threshold;
+
+ /* now do the actual HW configuration */
+ RXDMA_REG_WRITE64(handle, RDC_RX_CFG1, rdc, cfg1.value);
+ RXDMA_REG_WRITE64(handle, RDC_RX_CFG2, rdc, cfg2.value);
+
+ RXDMA_REG_WRITE64(handle, RDC_RBR_CFG_A, rdc, cfga.value);
+ RXDMA_REG_WRITE64(handle, RDC_RBR_CFG_B, rdc, cfgb.value);
+
+ RXDMA_REG_WRITE64(handle, RDC_RCR_CFG_A, rdc, rcr_cfga.value);
+ RXDMA_REG_WRITE64(handle, RDC_RCR_CFG_B, rdc, rcr_cfgb.value);
+
+ RXDMA_REG_WRITE64(handle, RDC_PAGE_HANDLE, rdc, page_handle.value);
+
+ return (HPI_SUCCESS);
+}
+
+/* system wide conf functions */
+
+hpi_status_t
+hpi_rxdma_cfg_clock_div_set(hpi_handle_t handle, uint16_t count)
+{
+ uint64_t offset;
+ rdc_clock_div_t clk_div;
+
+ offset = RDC_CLOCK_DIV;
+
+ clk_div.value = 0;
+ clk_div.bits.count = count;
+ HPI_DEBUG_MSG((HPI_RDC_CTL,
+ " hpi_rxdma_cfg_clock_div_set: value 0x%llx",
+ clk_div.value));
+
+ HXGE_REG_WR64(handle, offset, clk_div.value);
+
+ return (HPI_SUCCESS);
+}
+
+
+hpi_status_t
+hpi_rxdma_rdc_rbr_qlen_get(hpi_handle_t handle, uint8_t rdc,
+ rdc_rbr_qlen_t *rbr_qlen)
+{
+ if (!RXDMA_CHANNEL_VALID(rdc)) {
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ " rxdma_rdc_rbr_qlen_get Illegal RDC Number %d \n", rdc));
+ return (HPI_RXDMA_RDC_INVALID);
+ }
+
+ RXDMA_REG_READ64(handle, RDC_RBR_QLEN, rdc, &rbr_qlen->value);
+ return (HPI_SUCCESS);
+}
+
+
+hpi_status_t
+hpi_rxdma_rdc_rcr_qlen_get(hpi_handle_t handle, uint8_t rdc,
+ uint16_t *rcr_qlen)
+{
+ rdc_rcr_qlen_t stats;
+
+ if (!RXDMA_CHANNEL_VALID(rdc)) {
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ " rxdma_rdc_rcr_qlen_get Illegal RDC Number %d \n", rdc));
+ return (HPI_RXDMA_RDC_INVALID);
+ }
+
+ RXDMA_REG_READ64(handle, RDC_RCR_QLEN, rdc, &stats.value);
+ *rcr_qlen = stats.bits.qlen;
+ HPI_DEBUG_MSG((HPI_RDC_CTL,
+ " rxdma_rdc_rcr_qlen_get RDC %d qlen %x qlen %x\n",
+ rdc, *rcr_qlen, stats.bits.qlen));
+ return (HPI_SUCCESS);
+}
+
+
+hpi_status_t
+hpi_rxdma_rdc_rcr_tail_get(hpi_handle_t handle, uint8_t rdc,
+ uint32_t *rcr_tail)
+{
+ rdc_rcr_tail_t tail;
+
+ if (!RXDMA_CHANNEL_VALID(rdc)) {
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ " rxdma_rdc_rcr_tail_get Illegal RDC Number %d \n", rdc));
+ return (HPI_RXDMA_RDC_INVALID);
+ }
+
+ RXDMA_REG_READ64(handle, RDC_RCR_TAIL, rdc, &tail.value);
+ *rcr_tail = tail.bits.tail;
+ HPI_DEBUG_MSG((HPI_RDC_CTL,
+ " rxdma_rdc_rcr_qlen_get RDC %d qlen %x \n",
+ rdc, *rcr_tail));
+ return (HPI_SUCCESS);
+}
+
+
+
+hpi_status_t
+hpi_rxdma_rdc_rcr_read_update(hpi_handle_t handle, uint8_t channel,
+ uint16_t pkts_read, uint16_t bufs_read)
+{
+ rdc_stat_t cs;
+
+ if (!RXDMA_CHANNEL_VALID(channel)) {
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ " hpi_rxdma_rdc_rcr_read_update ", " channel %d", channel));
+ return (HPI_FAILURE | HPI_RXDMA_CHANNEL_INVALID(channel));
+ }
+
+ HPI_DEBUG_MSG((HPI_RDC_CTL,
+ " hpi_rxdma_rdc_rcr_read_update bufs read %d pkt read %d",
+ bufs_read, pkts_read));
+
+ cs.value = 0; /* do not modify any other bits */
+ cs.bits.pktread = pkts_read;
+ cs.bits.ptrread = bufs_read;
+
+ RXDMA_REG_WRITE64(handle, RDC_STAT, channel, cs.value);
+
+ return (HPI_SUCCESS);
+}
+
+/*
+ * hpi_rxdma_channel_mex_set():
+ * This function is called to arm the DMA channel with
+ * mailbox updating capability. Software needs to rearm
+ * for each update by writing to the control and status register.
+ *
+ * Parameters:
+ * handle - HPI handle (virtualization flag must be defined).
+ * channel - logical RXDMA channel from 0 to 23.
+ * (If virtualization flag is not set, then
+ * logical channel is the same as the hardware
+ * channel number).
+ *
+ * Return:
+ * HPI_SUCCESS - If enable channel with mailbox update
+ * is complete successfully.
+ *
+ * Error:
+ * HPI_FAILURE -
+ * HPI_RXDMA_CHANNEL_INVALID -
+ */
+hpi_status_t
+hpi_rxdma_channel_mex_set(hpi_handle_t handle, uint8_t channel)
+{
+ return (hpi_rxdma_channel_control(handle, RXDMA_MEX_SET, channel));
+}
+
+hpi_status_t
+hpi_rxdma_channel_cs_clear_all(hpi_handle_t handle, uint8_t channel)
+{
+ return (hpi_rxdma_channel_control(handle, RXDMA_CS_CLEAR_ALL, channel));
+}
+
+/*
+ * hpi_rxdma_channel_control():
+ * This function is called to control a receive DMA channel
+ * for arming the channel with mailbox updates, resetting
+ * various event status bits (control and status register).
+ *
+ * Parameters:
+ * handle - HPI handle (virtualization flag must be defined).
+ * control - HPI defined control type supported:
+ * - RXDMA_MEX_SET
+ * - RXDMA_RCRTO_CLEAR
+ * - RXDMA_RCR_SFULL_CLEAR
+ * - RXDMA_RCR_FULL_CLEAR
+ * - RXDMA_RBR_PRE_EMPTY_CLEAR
+ * - RXDMA_RBR_EMPTY_CLEAR
+ * channel - logical RXDMA channel from 0 to 23.
+ * (If virtualization flag is not set, then
+ * logical channel is the same as the hardware.
+ * Return:
+ * HPI_SUCCESS
+ *
+ * Error:
+ * HPI_FAILURE -
+ * HPI_TXDMA_OPCODE_INVALID -
+ * HPI_TXDMA_CHANNEL_INVALID -
+ */
+hpi_status_t
+hpi_rxdma_channel_control(hpi_handle_t handle, rxdma_cs_cntl_t control,
+ uint8_t channel)
+{
+ rdc_stat_t cs;
+
+ if (!RXDMA_CHANNEL_VALID(channel)) {
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ " hpi_rxdma_channel_control", " channel", channel));
+ return (HPI_FAILURE | HPI_RXDMA_CHANNEL_INVALID(channel));
+ }
+
+ cs.value = 0; // do not modify other bits
+ switch (control) {
+ case RXDMA_MEX_SET:
+ cs.bits.mex = 1;
+ RXDMA_REG_WRITE64(handle, RDC_STAT, channel, cs.value);
+ break;
+
+ case RXDMA_RCRTO_CLEAR:
+ cs.bits.rcr_to = 1;
+ RXDMA_REG_WRITE64(handle, RDC_STAT, channel, cs.value);
+ break;
+
+ case RXDMA_RCR_SFULL_CLEAR:
+ cs.bits.rcr_shadow_full = 1;
+ RXDMA_REG_WRITE64(handle, RDC_STAT, channel, cs.value);
+ break;
+
+ case RXDMA_RCR_FULL_CLEAR:
+ cs.bits.rcr_full = 1;
+ RXDMA_REG_WRITE64(handle, RDC_STAT, channel, cs.value);
+ break;
+
+ case RXDMA_RBR_PRE_EMPTY_CLEAR:
+ cs.bits.rbr_pre_empty = 1;
+ RXDMA_REG_WRITE64(handle, RDC_STAT, channel, cs.value);
+ break;
+
+ case RXDMA_RBR_EMPTY_CLEAR:
+ cs.bits.rbr_empty = 1;
+ RXDMA_REG_WRITE64(handle, RDC_STAT, channel, cs.value);
+ break;
+
+ case RXDMA_CS_CLEAR_ALL:
+ cs.value = ~0ULL;
+ RXDMA_REG_WRITE64(handle, RDC_STAT, channel, cs.value);
+ break;
+
+ default:
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ "hpi_rxdma_channel_control", "control", control));
+ return (HPI_FAILURE | HPI_RXDMA_OPCODE_INVALID(channel));
+ }
+
+ return (HPI_SUCCESS);
+}
+
+/*
+ * hpi_rxdma_control_status():
+ * This function is called to operate on the control
+ * and status register.
+ *
+ * Parameters:
+ * handle - HPI handle
+ * op_mode - OP_GET: get hardware control and status
+ * OP_SET: set hardware control and status
+ * OP_UPDATE: update hardware control and status.
+ * OP_CLEAR: clear control and status register to 0s.
+ * channel - hardware RXDMA channel from 0 to 23.
+ * cs_p - pointer to hardware defined control and status
+ * structure.
+ * Return:
+ * HPI_SUCCESS
+ *
+ * Error:
+ * HPI_FAILURE -
+ * HPI_RXDMA_OPCODE_INVALID -
+ * HPI_RXDMA_CHANNEL_INVALID -
+ */
+hpi_status_t
+hpi_rxdma_control_status(hpi_handle_t handle, io_op_t op_mode, uint8_t channel,
+ rdc_stat_t *cs_p)
+{
+ int status = HPI_SUCCESS;
+ rdc_stat_t cs;
+
+ if (!RXDMA_CHANNEL_VALID(channel)) {
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ "hpi_rxdma_control_status", "channel", channel));
+ return (HPI_FAILURE | HPI_RXDMA_CHANNEL_INVALID(channel));
+ }
+
+ switch (op_mode) {
+ case OP_GET:
+ RXDMA_REG_READ64(handle, RDC_STAT, channel, &cs_p->value);
+ break;
+
+ case OP_SET:
+ RXDMA_REG_WRITE64(handle, RDC_STAT, channel, cs_p->value);
+ break;
+
+ case OP_UPDATE:
+ RXDMA_REG_READ64(handle, RDC_STAT, channel, &cs.value);
+ RXDMA_REG_WRITE64(handle, RDC_STAT, channel,
+ cs_p->value | cs.value);
+ break;
+
+ default:
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ "hpi_rxdma_control_status", "control", op_mode));
+ return (HPI_FAILURE | HPI_RXDMA_OPCODE_INVALID(channel));
+ }
+
+ return (status);
+}
+
+/*
+ * hpi_rxdma_event_mask():
+ * This function is called to operate on the event mask
+ * register which is used for generating interrupts.
+ *
+ * Parameters:
+ * handle - HPI handle
+ * op_mode - OP_GET: get hardware event mask
+ * OP_SET: set hardware interrupt event masks
+ * OP_CLEAR: clear control and status register to 0s.
+ * channel - hardware RXDMA channel from 0 to 23.
+ * mask_p - pointer to hardware defined event mask
+ * structure.
+ * Return:
+ * HPI_SUCCESS - If set is complete successfully.
+ *
+ * Error:
+ * HPI_FAILURE -
+ * HPI_RXDMA_OPCODE_INVALID -
+ * HPI_RXDMA_CHANNEL_INVALID -
+ */
+
+
+hpi_status_t
+hpi_rxdma_event_mask(hpi_handle_t handle, io_op_t op_mode, uint8_t channel,
+ rdc_int_mask_t *mask_p)
+{
+ int status = HPI_SUCCESS;
+ rdc_int_mask_t mask;
+
+ if (!RXDMA_CHANNEL_VALID(channel)) {
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ "hpi_rxdma_event_mask", "channel", channel));
+ return (HPI_FAILURE | HPI_RXDMA_CHANNEL_INVALID(channel));
+ }
+
+ switch (op_mode) {
+ case OP_GET:
+ RXDMA_REG_READ64(handle, RDC_INT_MASK, channel, &mask_p->value);
+ break;
+
+ case OP_SET:
+ RXDMA_REG_WRITE64(handle, RDC_INT_MASK, channel, mask_p->value);
+ break;
+
+ case OP_UPDATE:
+ RXDMA_REG_READ64(handle, RDC_INT_MASK, channel, &mask.value);
+ RXDMA_REG_WRITE64(handle, RDC_INT_MASK, channel,
+ mask_p->value | mask.value);
+ break;
+
+ default:
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ "hpi_rxdma_event_mask", "eventmask", op_mode));
+ return (HPI_FAILURE | HPI_RXDMA_OPCODE_INVALID(channel));
+ }
+
+ return (status);
+}
+
+/*
+ * hpi_rx_fifo_status():
+ * This function is called to operate on the RX Fifo Error Status
+ * register.
+ *
+ * Parameters:
+ * handle - HPI handle
+ * op_mode - OP_GET: get hardware control and status
+ * OP_SET: set hardware control and status
+ * cs_p - pointer to hardware defined fifo status structure.
+ *
+ * Return:
+ * HPI_SUCCESS
+ *
+ * Error:
+ * HPI_FAILURE -
+ * HPI_RXDMA_OPCODE_INVALID -
+ * HPI_RXDMA_CHANNEL_INVALID -
+ */
+hpi_status_t
+hpi_rx_fifo_status(hpi_handle_t handle, io_op_t op_mode,
+ rdc_fifo_err_stat_t *cs_p)
+{
+ int status = HPI_SUCCESS;
+
+ switch (op_mode) {
+ case OP_GET:
+ HXGE_REG_RD64(handle, RDC_FIFO_ERR_STAT, &cs_p->value);
+ break;
+
+ case OP_SET:
+ HXGE_REG_WR64(handle, RDC_FIFO_ERR_STAT, cs_p->value);
+ break;
+
+ default:
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ "hpi_rx_fifo_status", "control", op_mode));
+ return (HPI_FAILURE | HPI_RXDMA_OPCODE_INVALID(0));
+ }
+
+ return (status);
+}
+
+/*
+ * hpi_rx_fifo_mask():
+ * This function is called to operate on the fifo error mask
+ * register which is used for generating interrupts.
+ *
+ * Parameters:
+ * handle - HPI handle
+ * op_mode - OP_GET: get hardware event mask
+ * OP_SET: set hardware interrupt event masks
+ * mask_p - pointer to hardware defined event mask
+ * structure.
+ * Return:
+ * HPI_SUCCESS - If set is complete successfully.
+ *
+ * Error:
+ * HPI_FAILURE -
+ * HPI_RXDMA_OPCODE_INVALID -
+ * HPI_RXDMA_CHANNEL_INVALID -
+ */
+
+
+hpi_status_t
+hpi_rx_fifo_mask(hpi_handle_t handle, io_op_t op_mode,
+ rdc_fifo_err_mask_t *mask_p)
+{
+ int status = HPI_SUCCESS;
+
+ switch (op_mode) {
+ case OP_GET:
+ HXGE_REG_RD64(handle, RDC_FIFO_ERR_MASK, &mask_p->value);
+ break;
+
+ case OP_SET:
+ HXGE_REG_WR64(handle, RDC_FIFO_ERR_MASK, mask_p->value);
+ break;
+
+ default:
+ HPI_ERROR_MSG((HPI_ERR_CTL,
+ "hpi_rx_fifo_mask", "interrupt-mask", op_mode));
+ return (HPI_FAILURE | HPI_RXDMA_OPCODE_INVALID(0));
+ }
+
+ return (status);
+}
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#ifndef _HPI_RXDMA_H
+#define _HPI_RXDMA_H
+
+#include "hpi.h"
+#include "../hxge_defs.h"
+#include "../hxge_rdc_hw.h"
+
+#define RXDMA_CFIG2_MBADDR_L_SHIFT 6 /* bit 31:6 */
+#define RXDMA_CFIG2_MBADDR_L_MASK 0x00000000ffffffc0ULL
+
+#define RBR_CFIG_A_STDADDR_MASK 0x000000000003ffc0ULL
+#define RBR_CFIG_A_STDADDR_BASE_MASK 0x00000ffffffc0000ULL
+
+#define RCRCFIG_A_STADDR_SHIFT 6 /* bit 18:6 */
+#define RCRCFIG_A_STADDR_MASK 0x000000000007FFC0ULL
+#define RCRCFIG_A_STADDR_BASE_SHIF 19 /* bit 43:19 */
+#define RCRCFIG_A_STADDR_BASE_MASK 0x00000FFFFFF80000ULL
+#define RCRCFIG_A_LEN_SHIF 48 /* bit 63:48 */
+#define RCRCFIG_A_LEN_MASK 0xFFFF000000000000ULL
+
+#define RCR_FLSH_SHIFT 0 /* RW, bit 0:0 */
+#define RCR_FLSH_SET 0x0000000000000001ULL
+#define RCR_FLSH_MASK 0x0000000000000001ULL
+
+#define RBR_CFIG_A_LEN_SHIFT 48 /* bits 63:48 */
+#define RBR_CFIG_A_LEN_MASK 0xFFFF000000000000ULL
+
+#define RXDMA_RESET_TRY_COUNT 5
+#define RXDMA_RESET_DELAY 5
+
+#define RXDMA_OP_DISABLE 0
+#define RXDMA_OP_ENABLE 1
+#define RXDMA_OP_RESET 2
+
+#define RCR_TIMEOUT_ENABLE 1
+#define RCR_TIMEOUT_DISABLE 2
+#define RCR_THRESHOLD 4
+
+
+/*
+ * Buffer block descriptor
+ */
+typedef struct _rx_desc_t {
+ uint32_t block_addr;
+} rx_desc_t, *p_rx_desc_t;
+
+/*
+ * RXDMA HPI defined control types.
+ */
+typedef enum _rxdma_cs_cntl_e {
+ RXDMA_CS_CLEAR_ALL = 0x1,
+ RXDMA_MEX_SET = 0x2,
+ RXDMA_RCRTO_CLEAR = 0x4,
+ RXDMA_RCR_SFULL_CLEAR = 0x8,
+ RXDMA_RCR_FULL_CLEAR = 0x10,
+ RXDMA_RBR_PRE_EMPTY_CLEAR = 0x20,
+ RXDMA_RBR_EMPTY_CLEAR = 0x40
+} rxdma_cs_cntl_t;
+
+typedef union _rcr_addr44 {
+ uint64_t addr;
+ struct {
+#if defined(_BIG_ENDIAN)
+ uint32_t rsrvd:20;
+ uint32_t hdw:25;
+ uint32_t ldw:19;
+#else
+ uint32_t ldw:19;
+ uint32_t hdw:25;
+ uint32_t rsrvd:20;
+#endif
+ } bits;
+} rcr_addr44_t;
+
+typedef union _rbr_addr44 {
+ uint64_t addr;
+ struct {
+#if defined(_BIG_ENDIAN)
+ uint32_t rsrvd:20;
+ uint32_t hdw:26;
+ uint32_t ldw:18;
+#else
+ uint32_t ldw:18;
+ uint32_t hdw:26;
+ uint32_t rsrvd:20;
+#endif
+ } bits;
+} rbr_addr44_t;
+
+typedef enum _bsize {
+ SIZE_0B = 0x0,
+ SIZE_64B = 64,
+ SIZE_128B = 128,
+ SIZE_192B = 192,
+ SIZE_256B = 256,
+ SIZE_512B = 512,
+ SIZE_1KB = 1024,
+ SIZE_2KB = 2048,
+ SIZE_4KB = 4096,
+ SIZE_8KB = 8192,
+ SIZE_16KB = 16384,
+ SIZE_32KB = 32668
+} bsize_t;
+
+typedef struct _rdc_desc_cfg_t {
+ uint8_t mbox_enable; /* Enable full (18b) header */
+ uint8_t full_hdr; /* Enable full (18b) header */
+ uint8_t offset; /* 64 byte offsets */
+ uint8_t valid2; /* size 2 is valid */
+ bsize_t size2; /* Size 2 length */
+ uint8_t valid1; /* size 1 is valid */
+ bsize_t size1; /* Size 1 length */
+ uint8_t valid0; /* size 0 is valid */
+ bsize_t size0; /* Size 1 length */
+ bsize_t page_size; /* Page or buffer Size */
+ uint8_t rcr_timeout_enable;
+ uint8_t rcr_timeout;
+ uint16_t rcr_threshold;
+ uint16_t rcr_len; /* RBR Descriptor size (entries) */
+ uint16_t rbr_len; /* RBR Descriptor size (entries) */
+ uint64_t mbox_addr; /* Mailbox Address */
+ uint64_t rcr_addr; /* RCR Address */
+ uint64_t rbr_addr; /* RBB Address */
+} rdc_desc_cfg_t;
+
+
+/*
+ * Register offset (0x800 bytes for each channel) for receive ring registers.
+ */
+#define HXGE_RXDMA_OFFSET(x, v, channel) (x + \
+ (!v ? DMC_OFFSET(channel) : \
+ RDMC_PIOVADDR_OFFSET(channel)))
+
+#define RXDMA_REG_READ64(handle, reg, channel, data_p) \
+do {\
+ HXGE_REG_RD64(handle, (HXGE_RXDMA_OFFSET(reg, 0,\
+ channel)), (data_p));\
+} while(0)
+
+#define RXDMA_REG_READ32(handle, reg, channel, data_p) \
+ HXGE_REG_RD32(handle, (HXGE_RXDMA_OFFSET(reg, 0,\
+ channel)), (data_p))
+
+#define RXDMA_REG_WRITE64(handle, reg, channel, data)\
+do {\
+ HXGE_REG_WR64(handle, (HXGE_RXDMA_OFFSET(reg, 0,\
+ channel)), (data));\
+} while(0)
+
+/*
+ * RX HPI error codes
+ */
+#define RXDMA_ER_ST (RXDMA_BLK_ID << HPI_BLOCK_ID_SHIFT)
+#define RXDMA_ID_SHIFT(n) (n << HPI_PORT_CHAN_SHIFT)
+
+#define HPI_RXDMA_ERROR RXDMA_ER_ST
+
+#define HPI_RXDMA_SW_PARAM_ERROR (HPI_RXDMA_ERROR | 0x40)
+#define HPI_RXDMA_HW_ERROR (HPI_RXDMA_ERROR | 0x80)
+
+#define HPI_RXDMA_RDC_INVALID (HPI_RXDMA_ERROR | CHANNEL_INVALID)
+#define HPI_RXDMA_PAGE_INVALID (HPI_RXDMA_ERROR | LOGICAL_PAGE_INVALID)
+#define HPI_RXDMA_RESET_ERR (HPI_RXDMA_HW_ERROR | RESET_FAILED)
+#define HPI_RXDMA_DISABLE_ERR (HPI_RXDMA_HW_ERROR | 0x0000a)
+#define HPI_RXDMA_ENABLE_ERR (HPI_RXDMA_HW_ERROR | 0x0000b)
+#define HPI_RXDMA_FUNC_INVALID (HPI_RXDMA_SW_PARAM_ERROR | 0x0000a)
+#define HPI_RXDMA_BUFSZIE_INVALID (HPI_RXDMA_SW_PARAM_ERROR | 0x0000b)
+#define HPI_RXDMA_RBRSZIE_INVALID (HPI_RXDMA_SW_PARAM_ERROR | 0x0000c)
+#define HPI_RXDMA_RCRSZIE_INVALID (HPI_RXDMA_SW_PARAM_ERROR | 0x0000d)
+#define HPI_RXDMA_PORT_INVALID (HPI_RXDMA_ERROR | PORT_INVALID)
+#define HPI_RXDMA_TABLE_INVALID (HPI_RXDMA_ERROR | RDC_TAB_INVALID)
+
+#define HPI_RXDMA_CHANNEL_INVALID(n) (RXDMA_ID_SHIFT(n) | \
+ HPI_RXDMA_ERROR | CHANNEL_INVALID)
+#define HPI_RXDMA_OPCODE_INVALID(n) (RXDMA_ID_SHIFT(n) | \
+ HPI_RXDMA_ERROR | OPCODE_INVALID)
+
+#define HPI_RXDMA_ERROR_ENCODE(err, rdc) \
+ (RXDMA_ID_SHIFT(rdc) | RXDMA_ER_ST | err)
+
+#define RXDMA_CHANNEL_VALID(rdc) \
+ ((rdc < HXGE_MAX_RDCS))
+
+#define RXDMA_PAGE_VALID(page) \
+ ((page == 0) || (page == 1))
+
+#define RXDMA_BUFF_OFFSET_VALID(offset) \
+ ((offset == SW_OFFSET_NO_OFFSET) || \
+ (offset == SW_OFFSET_64) || \
+ (offset == SW_OFFSET_128))
+
+#define RXDMA_RCR_TO_VALID(tov) ((tov) && (tov < 64))
+#define RXDMA_RCR_THRESH_VALID(thresh) ((thresh < 65536))
+
+#define hpi_rxdma_rdc_rbr_kick(handle, rdc, num_buffers) \
+ RXDMA_REG_WRITE64(handle, RDC_RBR_KICK, rdc, num_buffers)
+
+hpi_status_t hpi_rxdma_cfg_rdc_ring(hpi_handle_t handle, uint8_t rdc,
+ rdc_desc_cfg_t *rdc_desc_params);
+hpi_status_t hpi_rxdma_cfg_clock_div_set(hpi_handle_t handle, uint16_t count);
+hpi_status_t hpi_rxdma_cfg_logical_page_handle(hpi_handle_t handle, uint8_t rdc,
+ uint64_t pg_handle);
+
+hpi_status_t hpi_rxdma_rdc_rcr_read_update(hpi_handle_t handle, uint8_t channel,
+ uint16_t num_pkts, uint16_t bufs_read);
+hpi_status_t hpi_rxdma_rdc_rbr_qlen_get(hpi_handle_t handle, uint8_t rdc,
+ rdc_rbr_qlen_t *rbr_stat);
+hpi_status_t hpi_rxdma_cfg_rdc_reset(hpi_handle_t handle, uint8_t rdc);
+hpi_status_t hpi_rxdma_cfg_rdc_enable(hpi_handle_t handle, uint8_t rdc);
+hpi_status_t hpi_rxdma_cfg_rdc_disable(hpi_handle_t handle, uint8_t rdc);
+hpi_status_t hpi_rxdma_cfg_rdc_rcr_timeout(hpi_handle_t handle, uint8_t rdc,
+ uint16_t rcr_timeout);
+
+hpi_status_t hpi_rxdma_cfg_rdc_rcr_threshold(hpi_handle_t handle, uint8_t rdc,
+ uint16_t rcr_threshold);
+hpi_status_t hpi_rxdma_cfg_rdc_rcr_timeout_disable(hpi_handle_t handle,
+ uint8_t rdc);
+hpi_status_t hpi_rxdma_cfg_rdc_rcr_ctl(hpi_handle_t handle, uint8_t rdc,
+ uint8_t op, uint16_t rcr_timeout);
+hpi_status_t hpi_rxdma_rdc_rcr_qlen_get(hpi_handle_t handle,
+ uint8_t rdc, uint16_t *qlen);
+hpi_status_t hpi_rxdma_channel_mex_set(hpi_handle_t handle, uint8_t channel);
+hpi_status_t hpi_rxdma_channel_control(hpi_handle_t handle,
+ rxdma_cs_cntl_t control, uint8_t channel);
+hpi_status_t hpi_rxdma_control_status(hpi_handle_t handle, io_op_t op_mode,
+ uint8_t channel, rdc_stat_t *cs_p);
+hpi_status_t hpi_rxdma_event_mask(hpi_handle_t handle, io_op_t op_mode,
+ uint8_t channel, rdc_int_mask_t *mask_p);
+hpi_status_t hpi_rxdma_channel_cs_clear_all(hpi_handle_t handle,
+ uint8_t channel);
+hpi_status_t hpi_rx_fifo_status(hpi_handle_t handle, io_op_t op_mode,
+ rdc_fifo_err_stat_t *stat);
+hpi_status_t hpi_rx_fifo_mask(hpi_handle_t handle, io_op_t op_mode,
+ rdc_fifo_err_mask_t *stat);
+hpi_status_t hpi_rxdma_rdc_rcr_tail_get(hpi_handle_t handle, uint8_t channel,
+ uint32_t *rcr_tail);
+hpi_status_t hpi_rxdma_cfg_rdc_wait_for_qst(hpi_handle_t handle, uint8_t rdc,
+ rdc_rx_cfg1_t *cfg, int state);
+
+
+#endif /* _HPI_RXDMA_H */
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#include "hpi_txdma.h"
+
+#define TXDMA_WAIT_LOOP 10000
+#define TXDMA_WAIT_USEC 5
+
+uint64_t tdc_dmc_offset[] = {
+ TDC_PAGE_HANDLE,
+ TDC_TDR_CFG,
+ TDC_TDR_HEAD,
+ TDC_TDR_PRE_HEAD,
+ TDC_TDR_KICK,
+ TDC_INT_MASK,
+ TDC_STAT,
+ TDC_MBH,
+ TDC_MBL,
+ TDC_BYTE_CNT,
+ TDC_TDR_QLEN,
+ TDC_DROP_CNT,
+ TDC_PREF_PAR_LOG,
+ TDC_STAT_INT_DBG,
+ TDC_PKT_REQ_TID_TAG,
+ TDC_SOP_PREF_DESC_LOG,
+ TDC_PREF_DESC_LOG,
+};
+
+const char *tdc_dmc_name[] = {
+ "TDC_PAGE_HANDLE",
+ "TDC_TDR_CFG",
+ "TDC_TDR_HEAD",
+ "TDC_TDR_PRE_HEAD",
+ "TDC_TDR_KICK",
+ "TDC_INT_MASK",
+ "TDC_STAT",
+ "TDC_MBH",
+ "TDC_MBL",
+ "TDC_BYTE_CNT",
+ "TDC_TDR_QLEN",
+ "TDC_DROP_CNT",
+ "TDC_PREF_PAR_LOG",
+ "TDC_STAT_INT_DBG",
+ "TDC_PKT_REQ_TID_TAG",
+ "TDC_SOP_PREF_DESC_LOG",
+ "TDC_PREF_DESC_LOG",
+};
+
+uint64_t tdc_reg_offset[] = {
+ TDC_RTAB_PTR,
+ TDC_LAST_PKT_RBUF_PTRS,
+ TDC_PREF_CMD,
+ TDC_PREF_DATA,
+ TDC_PREF_PAR_DATA,
+ TDC_REORD_BUF_CMD,
+ TDC_REORD_BUF_DATA,
+ TDC_REORD_BUF_ECC_DATA,
+ TDC_REORD_TBL_CMD,
+ TDC_REORD_TBL_DATA_LO,
+ TDC_REORD_TBL_DATA_HI,
+ TDC_REORD_BUF_ECC_LOG,
+ TDC_REORD_TBL_PAR_LOG,
+ TDC_FIFO_ERR_MASK,
+ TDC_FIFO_ERR_STAT,
+ TDC_FIFO_ERR_INT_DBG,
+ TDC_PEU_TXN_LOG,
+ TDC_DBG_TRAINING_VEC,
+ TDC_DBG_GRP_SEL,
+};
+
+const char *tdc_reg_name[] = {
+ "TDC_RTAB_PTR",
+ "TDC_LAST_PKT_RBUF_PTRS",
+ "TDC_PREF_CMD",
+ "TDC_PREF_DATA",
+ "TDC_PREF_PAR_DATA",
+ "TDC_REORD_BUF_CMD",
+ "TDC_REORD_BUF_DATA",
+ "TDC_REORD_BUF_ECC_DATA",
+ "TDC_REORD_TBL_CMD",
+ "TDC_REORD_TBL_DATA_LO",
+ "TDC_REORD_TBL_DATA_HI",
+ "TDC_REORD_BUF_ECC_LOG",
+ "TDC_REORD_TBL_PAR_LOG",
+ "TDC_FIFO_ERR_MASK",
+ "TDC_FIFO_ERR_STAT",
+ "TDC_FIFO_ERR_INT_DBG",
+ "TDC_PEU_TXN_LOG",
+ "TDC_DBG_TRAINING_VEC",
+ "TDC_DBG_GRP_SEL",
+};
+
+hpi_status_t
+hpi_txdma_dump_tdc_regs(hpi_handle_t handle, uint8_t tdc)
+{
+ uint64_t value, offset;
+ int num_regs, i;
+
+ if (!TXDMA_CHANNEL_VALID(tdc)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ "hpi_txdma_dump_tdc_regs Invalid TDC number %d \n", tdc));
+
+ return (HPI_FAILURE | HPI_TXDMA_CHANNEL_INVALID(tdc));
+ }
+
+ HPI_REG_DUMP_MSG(( HPI_REG_CTL,
+ "\nTXDMA Register Dump for Channel %d\n", tdc));
+
+ num_regs = sizeof (tdc_dmc_offset) / sizeof (uint64_t);
+ for (i = 0; i < num_regs; i++) {
+ TXDMA_REG_READ64(handle, tdc_dmc_offset[i], tdc, &value);
+ offset = HXGE_TXDMA_OFFSET(tdc_dmc_offset[i], 0,
+ tdc);
+ HPI_REG_DUMP_MSG(( HPI_REG_CTL, "0x%08llx "
+ "%s\t 0x%016llx \n", offset, tdc_dmc_name[i], value));
+ }
+
+ HPI_REG_DUMP_MSG(( HPI_REG_CTL,
+ "\nTXDMA Register Dump for Channel %d done\n", tdc));
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_txdma_dump_tdc_common_regs(hpi_handle_t handle)
+{
+ uint64_t value, offset;
+ int num_regs, i;
+
+ HPI_REG_DUMP_MSG(( HPI_REG_CTL,
+ "\nTXDMA Common Register Dump\n"));
+
+ num_regs = sizeof (tdc_reg_offset) / sizeof (uint64_t);
+ for (i = 0; i < num_regs; i++) {
+ offset = tdc_reg_offset[i];
+ HXGE_REG_RD64(handle, offset, &value);
+ HPI_REG_DUMP_MSG(( HPI_REG_CTL, "0x%08llx "
+ "%s\t %016llx \n", offset, tdc_reg_name[i], value));
+ }
+
+ HPI_REG_DUMP_MSG(( HPI_REG_CTL,
+ "\nTXDMA Common Register Dump done\n"));
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_txdma_log_page_handle_set(hpi_handle_t handle, uint8_t channel,
+ tdc_page_handle_t *hdl_p)
+{
+ int status = HPI_SUCCESS;
+
+ if (!TXDMA_CHANNEL_VALID(channel)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_log_page_handle_set"
+ " Invalid Input: channel <0x%x>", channel));
+ return (HPI_FAILURE | HPI_TXDMA_CHANNEL_INVALID(channel));
+ }
+
+ TXDMA_REG_WRITE64(handle, TDC_PAGE_HANDLE, channel, hdl_p->value);
+
+ return (status);
+}
+
+hpi_status_t
+hpi_txdma_channel_reset(hpi_handle_t handle, uint8_t channel)
+{
+ HPI_DEBUG_MSG(( HPI_TDC_CTL,
+ " hpi_txdma_channel_reset" " RESETTING", channel));
+ return (hpi_txdma_channel_control(handle, TXDMA_RESET, channel));
+}
+
+hpi_status_t
+hpi_txdma_channel_init_enable(hpi_handle_t handle, uint8_t channel)
+{
+ return (hpi_txdma_channel_control(handle, TXDMA_INIT_START, channel));
+}
+
+hpi_status_t
+hpi_txdma_channel_enable(hpi_handle_t handle, uint8_t channel)
+{
+ return (hpi_txdma_channel_control(handle, TXDMA_START, channel));
+}
+
+hpi_status_t
+hpi_txdma_channel_disable(hpi_handle_t handle, uint8_t channel)
+{
+ return (hpi_txdma_channel_control(handle, TXDMA_STOP, channel));
+}
+
+hpi_status_t
+hpi_txdma_channel_mbox_enable(hpi_handle_t handle, uint8_t channel)
+{
+ return (hpi_txdma_channel_control(handle, TXDMA_MBOX_ENABLE, channel));
+}
+
+hpi_status_t
+hpi_txdma_channel_control(hpi_handle_t handle, txdma_cs_cntl_t control,
+ uint8_t channel)
+{
+ hpi_status_t status = HPI_SUCCESS;
+ tdc_stat_t cs;
+ tdc_tdr_cfg_t cfg;
+
+ if (!TXDMA_CHANNEL_VALID(channel)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_channel_control"
+ " Invalid Input: channel <0x%x>", channel));
+ return (HPI_FAILURE | HPI_TXDMA_CHANNEL_INVALID(channel));
+ }
+
+ switch (control) {
+ case TXDMA_INIT_RESET:
+ cfg.value = 0;
+ TXDMA_REG_READ64(handle, TDC_TDR_CFG, channel, &cfg.value);
+ cfg.bits.reset = 1;
+ TXDMA_REG_WRITE64(handle, TDC_TDR_CFG, channel, cfg.value);
+ return (hpi_txdma_control_reset_wait(handle, channel));
+
+ case TXDMA_INIT_START:
+ cfg.value = 0;
+ TXDMA_REG_READ64(handle, TDC_TDR_CFG, channel, &cfg.value);
+ cfg.bits.enable = 1;
+ TXDMA_REG_WRITE64(handle, TDC_TDR_CFG, channel, cfg.value);
+ break;
+
+ case TXDMA_RESET:
+ /*
+ * Sets reset bit only (Hardware will reset all the RW bits but
+ * leave the RO bits alone.
+ */
+ cfg.value = 0;
+ cfg.bits.reset = 1;
+ TXDMA_REG_WRITE64(handle, TDC_TDR_CFG, channel, cfg.value);
+ return (hpi_txdma_control_reset_wait(handle, channel));
+
+ case TXDMA_START:
+ /* Enable the DMA channel */
+ TXDMA_REG_READ64(handle, TDC_TDR_CFG, channel, &cfg.value);
+ cfg.bits.enable = 1;
+ TXDMA_REG_WRITE64(handle, TDC_TDR_CFG, channel, cfg.value);
+ break;
+
+ case TXDMA_STOP:
+ /* Disable the DMA channel */
+ TXDMA_REG_READ64(handle, TDC_TDR_CFG, channel, &cfg.value);
+ cfg.bits.enable = 0;
+ TXDMA_REG_WRITE64(handle, TDC_TDR_CFG, channel, cfg.value);
+ status = hpi_txdma_control_stop_wait(handle, channel);
+ if (status) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ "Cannot stop channel %d (TXC hung!)", channel));
+ }
+ break;
+
+ case TXDMA_MBOX_ENABLE:
+ /*
+ * Write 1 to MB bit to enable mailbox update (cleared to 0 by
+ * hardware after update).
+ */
+ TXDMA_REG_READ64(handle, TDC_STAT, channel, &cs.value);
+ cs.bits.mb = 1;
+ TXDMA_REG_WRITE64(handle, TDC_STAT, channel, cs.value);
+ break;
+
+ default:
+ status = (HPI_FAILURE | HPI_TXDMA_OPCODE_INVALID(channel));
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_channel_control"
+ " Invalid Input: control <0x%x>", control));
+ }
+
+ return (status);
+}
+
+hpi_status_t
+hpi_txdma_control_status(hpi_handle_t handle, io_op_t op_mode, uint8_t channel,
+ tdc_stat_t *cs_p)
+{
+ int status = HPI_SUCCESS;
+ tdc_stat_t txcs;
+
+ if (!TXDMA_CHANNEL_VALID(channel)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_control_status"
+ " Invalid Input: channel <0x%x>", channel));
+ return (HPI_FAILURE | HPI_TXDMA_CHANNEL_INVALID(channel));
+ }
+ switch (op_mode) {
+ case OP_GET:
+ TXDMA_REG_READ64(handle, TDC_STAT, channel, &cs_p->value);
+ break;
+
+ case OP_SET:
+ TXDMA_REG_WRITE64(handle, TDC_STAT, channel, cs_p->value);
+ break;
+
+ case OP_UPDATE:
+ TXDMA_REG_READ64(handle, TDC_STAT, channel, &txcs.value);
+ TXDMA_REG_WRITE64(handle, TDC_STAT, channel,
+ cs_p->value | txcs.value);
+ break;
+
+ default:
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_control_status"
+ " Invalid Input: control <0x%x>", op_mode));
+ return (HPI_FAILURE | HPI_TXDMA_OPCODE_INVALID(channel));
+ }
+
+ return (status);
+}
+
+hpi_status_t
+hpi_txdma_event_mask(hpi_handle_t handle, io_op_t op_mode, uint8_t channel,
+ tdc_int_mask_t *mask_p)
+{
+ int status = HPI_SUCCESS;
+ tdc_int_mask_t mask;
+
+ if (!TXDMA_CHANNEL_VALID(channel)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_event_mask Invalid Input: channel <0x%x>",
+ channel));
+ return (HPI_FAILURE | HPI_TXDMA_CHANNEL_INVALID(channel));
+ }
+ switch (op_mode) {
+ case OP_GET:
+ TXDMA_REG_READ64(handle, TDC_INT_MASK, channel, &mask_p->value);
+ break;
+
+ case OP_SET:
+ TXDMA_REG_WRITE64(handle, TDC_INT_MASK, channel, mask_p->value);
+ break;
+
+ case OP_UPDATE:
+ TXDMA_REG_READ64(handle, TDC_INT_MASK, channel, &mask.value);
+ TXDMA_REG_WRITE64(handle, TDC_INT_MASK, channel,
+ mask_p->value | mask.value);
+ break;
+
+ default:
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_event_mask Invalid Input: eventmask <0x%x>",
+ op_mode));
+ return (HPI_FAILURE | HPI_TXDMA_OPCODE_INVALID(channel));
+ }
+
+ return (status);
+}
+
+hpi_status_t
+hpi_tx_fifo_status(hpi_handle_t handle, io_op_t op_mode,
+ tdc_fifo_err_stat_t *cs_p)
+{
+ int status = HPI_SUCCESS;
+
+ switch (op_mode) {
+ case OP_GET:
+ HXGE_REG_RD64(handle, TDC_FIFO_ERR_STAT, &cs_p->value);
+ break;
+
+ case OP_SET:
+ HXGE_REG_WR64(handle, TDC_FIFO_ERR_STAT, cs_p->value);
+ break;
+
+ default:
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_tx_fifo_status:"
+ " Invalid Input: control <0x%x>", op_mode));
+ return (HPI_FAILURE | HPI_TXDMA_OPCODE_INVALID(0));
+ }
+
+ return (status);
+}
+
+hpi_status_t
+hpi_tx_fifo_mask(hpi_handle_t handle, io_op_t op_mode,
+ tdc_fifo_err_mask_t *mask_p)
+{
+ int status = HPI_SUCCESS;
+
+ switch (op_mode) {
+ case OP_GET:
+ HXGE_REG_RD64(handle, TDC_FIFO_ERR_MASK, &mask_p->value);
+ break;
+
+ case OP_SET:
+ HXGE_REG_WR64(handle, TDC_FIFO_ERR_MASK, mask_p->value);
+ break;
+
+ default:
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_tx_fifo_mask: Invalid Input: eventmask <0x%x>",
+ op_mode));
+ return (HPI_FAILURE | HPI_TXDMA_OPCODE_INVALID(0));
+ }
+
+ return (status);
+}
+
+/*
+ * This function is called to mask out the packet transmit marked event.
+ */
+hpi_status_t
+hpi_txdma_event_mask_mk_out(hpi_handle_t handle, uint8_t channel)
+{
+ tdc_int_mask_t event_mask;
+ int status = HPI_SUCCESS;
+
+ if (!TXDMA_CHANNEL_VALID(channel)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_event_mask_mk_out"
+ " Invalid Input: channel <0x%x>", channel));
+ return (HPI_FAILURE | HPI_TXDMA_CHANNEL_INVALID(channel));
+ }
+ TXDMA_REG_READ64(handle, TDC_INT_MASK, channel, &event_mask.value);
+ TXDMA_REG_WRITE64(handle, TDC_INT_MASK, channel,
+ (event_mask.value & ~TDC_INT_MASK_MK_MASK));
+
+ return (status);
+}
+
+/*
+ * This function is called to set the mask for the the packet marked event.
+ */
+hpi_status_t
+hpi_txdma_event_mask_mk_in(hpi_handle_t handle, uint8_t channel)
+{
+ tdc_int_mask_t event_mask;
+ int status = HPI_SUCCESS;
+
+ if (!TXDMA_CHANNEL_VALID(channel)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_event_mask_mk_in"
+ " Invalid Input: channel <0x%x>", channel));
+ return (HPI_FAILURE | HPI_TXDMA_CHANNEL_INVALID(channel));
+ }
+ TXDMA_REG_READ64(handle, TDC_INT_MASK, channel, &event_mask.value);
+ TXDMA_REG_WRITE64(handle, TDC_INT_MASK, channel,
+ (event_mask.value | TDC_INT_MASK_MK_MASK));
+
+ return (status);
+}
+
+/*
+ * This function is called to configure the transmit descriptor
+ * ring address and its size.
+ */
+hpi_status_t
+hpi_txdma_ring_addr_set(hpi_handle_t handle, uint8_t channel,
+ uint64_t start_addr, uint32_t len)
+{
+ int status = HPI_SUCCESS;
+ tdc_tdr_cfg_t cfg;
+
+ if (!TXDMA_CHANNEL_VALID(channel)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_ring_addr_set"
+ " Invalid Input: channel <0x%x>", channel));
+ return (HPI_FAILURE | HPI_TXDMA_CHANNEL_INVALID(channel));
+ }
+ cfg.value = ((start_addr & TDC_TDR_CFG_ADDR_MASK) |
+ (((uint64_t)len) << TDC_TDR_CFG_LEN_SHIFT));
+ TXDMA_REG_WRITE64(handle, TDC_TDR_CFG, channel, cfg.value);
+
+ return (status);
+}
+
+hpi_status_t
+hpi_txdma_ring_config(hpi_handle_t handle, io_op_t op_mode,
+ uint8_t channel, uint64_t *reg_data)
+{
+ int status = HPI_SUCCESS;
+
+ if (!TXDMA_CHANNEL_VALID(channel)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_ring_config"
+ " Invalid Input: channel <0x%x>", channel));
+ return (HPI_FAILURE | HPI_TXDMA_CHANNEL_INVALID(channel));
+ }
+ switch (op_mode) {
+ case OP_GET:
+ TXDMA_REG_READ64(handle, TDC_TDR_CFG, channel, reg_data);
+ break;
+
+ case OP_SET:
+ TXDMA_REG_WRITE64(handle, TDC_TDR_CFG, channel, *reg_data);
+ break;
+
+ default:
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_ring_config"
+ " Invalid Input: ring_config <0x%x>", op_mode));
+ return (HPI_FAILURE | HPI_TXDMA_OPCODE_INVALID(channel));
+ }
+
+ return (status);
+}
+
+hpi_status_t
+hpi_txdma_mbox_config(hpi_handle_t handle, io_op_t op_mode,
+ uint8_t channel, uint64_t *mbox_addr)
+{
+ int status = HPI_SUCCESS;
+ tdc_mbh_t mh;
+ tdc_mbl_t ml;
+
+ if (!TXDMA_CHANNEL_VALID(channel)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_mbox_config Invalid Input: channel <0x%x>",
+ channel));
+ return (HPI_FAILURE | HPI_TXDMA_CHANNEL_INVALID(channel));
+ }
+
+ mh.value = ml.value = 0;
+
+ switch (op_mode) {
+ case OP_GET:
+ TXDMA_REG_READ64(handle, TDC_MBH, channel, &mh.value);
+ TXDMA_REG_READ64(handle, TDC_MBL, channel, &ml.value);
+ *mbox_addr = ml.value;
+ *mbox_addr |= (mh.value << TDC_MBH_ADDR_SHIFT);
+
+ break;
+
+ case OP_SET:
+ ml.bits.mbaddr = ((*mbox_addr & TDC_MBL_MASK) >> TDC_MBL_SHIFT);
+ TXDMA_REG_WRITE64(handle, TDC_MBL, channel, ml.value);
+ mh.bits.mbaddr = ((*mbox_addr >> TDC_MBH_ADDR_SHIFT) &
+ TDC_MBH_MASK);
+ TXDMA_REG_WRITE64(handle, TDC_MBH, channel, mh.value);
+ break;
+
+ default:
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_mbox_config Invalid Input: mbox <0x%x>",
+ op_mode));
+ return (HPI_FAILURE | HPI_TXDMA_OPCODE_INVALID(channel));
+ }
+
+ return (status);
+}
+
+/*
+ * This function is called to set up a transmit descriptor entry.
+ */
+hpi_status_t
+hpi_txdma_desc_gather_set(hpi_handle_t handle, p_tx_desc_t desc_p,
+ uint8_t gather_index, boolean_t mark, uint8_t ngathers,
+ uint64_t dma_ioaddr, uint32_t transfer_len)
+{
+ hpi_status_t status;
+
+ status = HPI_TXDMA_GATHER_INDEX(gather_index);
+ if (status) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_desc_gather_set"
+ " Invalid Input: gather_index <0x%x>", gather_index));
+ return (status);
+ }
+ if (transfer_len > TX_MAX_TRANSFER_LENGTH) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_desc_gather_set"
+ " Invalid Input: tr_len <0x%x>", transfer_len));
+ return (HPI_FAILURE | HPI_TXDMA_XFER_LEN_INVALID);
+ }
+ if (gather_index == 0) {
+ desc_p->bits.sop = 1;
+ desc_p->bits.mark = mark;
+ desc_p->bits.num_ptr = ngathers;
+ HPI_DEBUG_MSG(( HPI_TDC_CTL,
+ "hpi_txdma_gather_set: SOP len %d (%d)",
+ desc_p->bits.tr_len, transfer_len));
+ }
+ desc_p->bits.tr_len = transfer_len;
+ desc_p->bits.sad = dma_ioaddr;
+
+ HPI_DEBUG_MSG(( HPI_TDC_CTL,
+ "hpi_txdma_gather_set: xfer len %d to set (%d)",
+ desc_p->bits.tr_len, transfer_len));
+
+ HXGE_MEM_PIO_WRITE64(handle, desc_p->value);
+
+ return (status);
+}
+
+/*
+ * This function is called to set up the first gather entry.
+ */
+hpi_status_t
+hpi_txdma_desc_gather_sop_set(hpi_handle_t handle, p_tx_desc_t desc_p,
+ boolean_t mark_mode, uint8_t ngathers)
+{
+ hpi_status_t status = HPI_SUCCESS;
+
+ desc_p->bits.sop = 1;
+ desc_p->bits.mark = mark_mode;
+ desc_p->bits.num_ptr = ngathers;
+
+ HXGE_MEM_PIO_WRITE64(handle, desc_p->value);
+
+ return (status);
+}
+
+hpi_status_t
+hpi_txdma_desc_gather_sop_set_1(hpi_handle_t handle, p_tx_desc_t desc_p,
+ boolean_t mark_mode, uint8_t ngathers, uint32_t extra)
+{
+ int status = HPI_SUCCESS;
+
+ desc_p->bits.sop = 1;
+ desc_p->bits.mark = mark_mode;
+ desc_p->bits.num_ptr = ngathers;
+ desc_p->bits.tr_len += extra;
+
+ HXGE_MEM_PIO_WRITE64(handle, desc_p->value);
+
+ return (status);
+}
+
+hpi_status_t
+hpi_txdma_desc_set_xfer_len(hpi_handle_t handle, p_tx_desc_t desc_p,
+ uint32_t transfer_len)
+{
+ int status = HPI_SUCCESS;
+
+ desc_p->bits.tr_len = transfer_len;
+
+ HPI_DEBUG_MSG(( HPI_TDC_CTL,
+ "hpi_set_xfer_len: len %d (%d)",
+ desc_p->bits.tr_len, transfer_len));
+
+ HXGE_MEM_PIO_WRITE64(handle, desc_p->value);
+
+ return (status);
+}
+
+hpi_status_t
+hpi_txdma_desc_set_zero(hpi_handle_t handle, uint16_t entries)
+{
+ uint32_t offset;
+ int i;
+
+ /*
+ * Assume no wrapped around.
+ */
+ offset = 0;
+ for (i = 0; i < entries; i++) {
+ HXGE_REG_WR64(handle, offset, 0);
+ offset += (i * (sizeof (tx_desc_t)));
+ }
+
+ return (HPI_SUCCESS);
+}
+
+
+hpi_status_t
+hpi_txdma_desc_mem_get(hpi_handle_t handle, uint16_t index,
+ p_tx_desc_t desc_p)
+{
+ int status = HPI_SUCCESS;
+
+ hpi_txdma_dump_desc_one(handle, desc_p, index);
+
+ return (status);
+}
+
+/*
+ * This function is called to kick the transmit to start transmission.
+ */
+hpi_status_t
+hpi_txdma_desc_kick_reg_set(hpi_handle_t handle, uint8_t channel,
+ uint16_t tail_index, boolean_t wrap)
+{
+ int status = HPI_SUCCESS;
+ tdc_tdr_kick_t kick;
+
+ if (!TXDMA_CHANNEL_VALID(channel)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_desc_kick_reg_set"
+ " Invalid Input: channel <0x%x>", channel));
+ return (HPI_FAILURE | HPI_TXDMA_CHANNEL_INVALID(channel));
+ }
+ HPI_DEBUG_MSG(( HPI_TDC_CTL,
+ " hpi_txdma_desc_kick_reg_set: KICKING channel %d", channel));
+
+ /* Toggle the wrap around bit */
+ kick.value = 0;
+ kick.bits.wrap = wrap;
+ kick.bits.tail = tail_index;
+
+ /* Kick start the Transmit kick register */
+ TXDMA_REG_WRITE64(handle, TDC_TDR_KICK, channel, kick.value);
+
+ return (status);
+}
+
+/*
+ * This function is called to kick the transmit to start transmission.
+ */
+hpi_status_t
+hpi_txdma_desc_kick_reg_get(hpi_handle_t handle, uint8_t channel,
+ tdc_tdr_kick_t *kick_p)
+{
+ int status = HPI_SUCCESS;
+
+ if (!TXDMA_CHANNEL_VALID(channel)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_desc_kick_reg_get"
+ " Invalid Input: channel <0x%x>", channel));
+ return (HPI_FAILURE | HPI_TXDMA_CHANNEL_INVALID(channel));
+ }
+ TXDMA_REG_READ64(handle, TDC_TDR_KICK, channel, &kick_p->value);
+
+ return (status);
+}
+
+/*
+ * This function is called to get the transmit ring head index.
+ */
+hpi_status_t
+hpi_txdma_ring_head_get(hpi_handle_t handle, uint8_t channel,
+ tdc_tdr_head_t *hdl_p)
+{
+ int status = HPI_SUCCESS;
+
+ if (!TXDMA_CHANNEL_VALID(channel)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_ring_head_get"
+ " Invalid Input: channel <0x%x>", channel));
+ return (HPI_FAILURE | HPI_TXDMA_CHANNEL_INVALID(channel));
+ }
+ TXDMA_REG_READ64(handle, TDC_TDR_HEAD, channel, &hdl_p->value);
+
+ return (status);
+}
+
+/*ARGSUSED*/
+hpi_status_t
+hpi_txdma_channel_mbox_get(hpi_handle_t handle, uint8_t channel,
+ p_txdma_mailbox_t mbox_p)
+{
+ int status = HPI_SUCCESS;
+
+ return (status);
+}
+
+hpi_status_t
+hpi_txdma_channel_pre_state_get(hpi_handle_t handle, uint8_t channel,
+ tdc_tdr_pre_head_t *prep)
+{
+ int status = HPI_SUCCESS;
+
+ if (!TXDMA_CHANNEL_VALID(channel)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_txdma_channel_pre_state_get"
+ " Invalid Input: channel <0x%x>", channel));
+ return (HPI_FAILURE | HPI_TXDMA_CHANNEL_INVALID(channel));
+ }
+ TXDMA_REG_READ64(handle, TDC_TDR_PRE_HEAD, channel, &prep->value);
+
+ return (status);
+}
+
+/*
+ * Dumps the contents of transmit descriptors.
+ */
+/*ARGSUSED*/
+void
+hpi_txdma_dump_desc_one(hpi_handle_t handle, p_tx_desc_t desc_p, int desc_index)
+{
+ tx_desc_t desc, *desp;
+
+#ifdef HXGE_DEBUG
+ uint64_t sad;
+ int xfer_len;
+#endif
+
+ HPI_DEBUG_MSG(( HPI_TDC_CTL,
+ "\n==> hpi_txdma_dump_desc_one: dump "
+ " desc_p $%p descriptor entry %d\n", desc_p, desc_index));
+ desc.value = 0;
+ desp = ((desc_p != NULL) ? desc_p : (p_tx_desc_t)&desc);
+ desp->value = HXGE_MEM_PIO_READ64(handle);
+#ifdef HXGE_DEBUG
+ sad = desp->bits.sad;
+ xfer_len = desp->bits.tr_len;
+#endif
+ HPI_DEBUG_MSG(( HPI_TDC_CTL, "\n\t: value 0x%llx\n"
+ "\t\tsad $%p\ttr_len %d len %d\tnptrs %d\tmark %d sop %d\n",
+ desp->value, sad, desp->bits.hdw.tr_len, xfer_len,
+ desp->bits.hdw.num_ptr, desp->bits.hdw.mark, desp->bits.hdw.sop));
+
+ HPI_DEBUG_MSG(( HPI_TDC_CTL,
+ "\n<== hpi_txdma_dump_desc_one: Done \n"));
+}
+
+/*ARGSUSED*/
+void
+hpi_txdma_dump_hdr(hpi_handle_t handle, p_tx_pkt_header_t hdrp)
+{
+ HPI_DEBUG_MSG(( HPI_TDC_CTL,
+ "\n==> hpi_txdma_dump_hdr: dump\n"));
+ HPI_DEBUG_MSG(( HPI_TDC_CTL,
+ "\n\t: value 0x%llx\n"
+ "\t\tpkttype 0x%x\tip_ver %d\tllc %d\tvlan %d \tihl %d\n"
+ "\t\tl3start %d\tl4start %d\tl4stuff %d\n"
+ "\t\txferlen %d\tpad %d\n",
+ hdrp->value,
+ hdrp->bits.hdw.cksum_en_pkt_type,
+ hdrp->bits.hdw.ip_ver,
+ hdrp->bits.hdw.llc,
+ hdrp->bits.hdw.vlan,
+ hdrp->bits.hdw.ihl,
+ hdrp->bits.hdw.l3start,
+ hdrp->bits.hdw.l4start,
+ hdrp->bits.hdw.l4stuff,
+ hdrp->bits.ldw.tot_xfer_len,
+ hdrp->bits.ldw.pad));
+
+ HPI_DEBUG_MSG(( HPI_TDC_CTL,
+ "\n<== hpi_txdma_dump_hdr: Done \n"));
+}
+
+/*
+ * Static functions start here.
+ */
+hpi_status_t
+hpi_txdma_control_reset_wait(hpi_handle_t handle, uint8_t channel)
+{
+ tdc_tdr_cfg_t txcs;
+ int loop = 0;
+
+ txcs.value = 0;
+ do {
+ TXDMA_REG_READ64(handle, TDC_TDR_CFG, channel, &txcs.value);
+
+ /*
+ * Reset completes when this bit is set to 1 by hw
+ */
+ if (txcs.bits.qst) {
+ return (HPI_SUCCESS);
+ }
+ HXGE_DELAY(TXDMA_WAIT_USEC);
+ loop++;
+ } while (loop < TXDMA_WAIT_LOOP);
+
+ if (loop == TXDMA_WAIT_LOOP) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ "hpi_txdma_control_reset_wait: RST bit not "
+ "cleared to 0 txcs.bits 0x%llx", txcs.value));
+ return (HPI_FAILURE | HPI_TXDMA_RESET_FAILED);
+ }
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_txdma_control_stop_wait(hpi_handle_t handle, uint8_t channel)
+{
+ tdc_tdr_cfg_t txcs;
+ int loop = 0;
+
+ do {
+ txcs.value = 0;
+ TXDMA_REG_READ64(handle, TDC_TDR_CFG, channel, &txcs.value);
+ if (txcs.bits.qst) {
+ return (HPI_SUCCESS);
+ }
+ HXGE_DELAY(TXDMA_WAIT_USEC);
+ loop++;
+ } while (loop < TXDMA_WAIT_LOOP);
+
+ if (loop == TXDMA_WAIT_LOOP) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ "hpi_txdma_control_stop_wait: SNG_STATE not "
+ "set to 1 txcs.bits 0x%llx", txcs.value));
+ return (HPI_FAILURE | HPI_TXDMA_STOP_FAILED);
+ }
+ return (HPI_SUCCESS);
+}
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#ifndef _HPI_TXDMA_H
+#define _HPI_TXDMA_H
+
+#include "hpi.h"
+#include "../hxge_defs.h"
+#include "../hxge_txdma_hw.h"
+#include "../hxge_tdc_hw.h"
+
+typedef enum _txdma_cs_cntl_e {
+ TXDMA_INIT_RESET = 0x1,
+ TXDMA_INIT_START = 0x2,
+ TXDMA_START = 0x3,
+ TXDMA_RESET = 0x4,
+ TXDMA_STOP = 0x5,
+ TXDMA_MBOX_ENABLE = 0x6
+} txdma_cs_cntl_t;
+
+#define HXGE_TXDMA_OFFSET(x, v, channel) (x + \
+ (!v ? DMC_OFFSET(channel) : TDMC_PIOVADDR_OFFSET(channel)))
+/*
+ * PIO macros to read and write the transmit registers.
+ */
+#define TXDMA_REG_READ64(handle, reg, channel, val_p) \
+ HXGE_REG_RD64(handle, \
+ (HXGE_TXDMA_OFFSET(reg, 0, channel)), val_p)
+
+#define TXDMA_REG_WRITE64(handle, reg, channel, data) \
+ HXGE_REG_WR64(handle, \
+ HXGE_TXDMA_OFFSET(reg, 0, channel), data)
+
+#define HPI_TXDMA_GATHER_INDEX(index) \
+ ((index <= TX_MAX_GATHER_POINTERS)) ? HPI_SUCCESS : \
+ (HPI_TXDMA_GATHER_INVALID)
+
+/*
+ * Transmit HPI error codes
+ */
+#define TXDMA_ER_ST (TXDMA_BLK_ID << HPI_BLOCK_ID_SHIFT)
+#define TXDMA_ID_SHIFT(n) (n << HPI_PORT_CHAN_SHIFT)
+
+#define TXDMA_HW_STOP_FAILED (HPI_BK_HW_ER_START | 0x1)
+#define TXDMA_HW_RESUME_FAILED (HPI_BK_HW_ER_START | 0x2)
+
+#define TXDMA_GATHER_INVALID (HPI_BK_ERROR_START | 0x1)
+#define TXDMA_XFER_LEN_INVALID (HPI_BK_ERROR_START | 0x2)
+
+#define HPI_TXDMA_OPCODE_INVALID(n) (TXDMA_ID_SHIFT(n) | \
+ TXDMA_ER_ST | OPCODE_INVALID)
+
+#define HPI_TXDMA_FUNC_INVALID(n) (TXDMA_ID_SHIFT(n) | \
+ TXDMA_ER_ST | PORT_INVALID)
+#define HPI_TXDMA_CHANNEL_INVALID(n) (TXDMA_ID_SHIFT(n) | \
+ TXDMA_ER_ST | CHANNEL_INVALID)
+
+#define HPI_TXDMA_PAGE_INVALID(n) (TXDMA_ID_SHIFT(n) | \
+ TXDMA_ER_ST | LOGICAL_PAGE_INVALID)
+
+#define HPI_TXDMA_REGISTER_INVALID (TXDMA_ER_ST | REGISTER_INVALID)
+#define HPI_TXDMA_COUNTER_INVALID (TXDMA_ER_ST | COUNTER_INVALID)
+#define HPI_TXDMA_CONFIG_INVALID (TXDMA_ER_ST | CONFIG_INVALID)
+
+
+#define HPI_TXDMA_GATHER_INVALID (TXDMA_ER_ST | TXDMA_GATHER_INVALID)
+#define HPI_TXDMA_XFER_LEN_INVALID (TXDMA_ER_ST | TXDMA_XFER_LEN_INVALID)
+
+#define HPI_TXDMA_RESET_FAILED (TXDMA_ER_ST | RESET_FAILED)
+#define HPI_TXDMA_STOP_FAILED (TXDMA_ER_ST | TXDMA_HW_STOP_FAILED)
+#define HPI_TXDMA_RESUME_FAILED (TXDMA_ER_ST | TXDMA_HW_RESUME_FAILED)
+
+/*
+ * Transmit DMA Channel HPI Prototypes.
+ */
+hpi_status_t hpi_txdma_log_page_handle_set(hpi_handle_t handle,
+ uint8_t channel, tdc_page_handle_t *hdl_p);
+hpi_status_t hpi_txdma_channel_reset(hpi_handle_t handle, uint8_t channel);
+hpi_status_t hpi_txdma_channel_init_enable(hpi_handle_t handle,
+ uint8_t channel);
+hpi_status_t hpi_txdma_channel_enable(hpi_handle_t handle, uint8_t channel);
+hpi_status_t hpi_txdma_channel_disable(hpi_handle_t handle, uint8_t channel);
+hpi_status_t hpi_txdma_channel_mbox_enable(hpi_handle_t handle,
+ uint8_t channel);
+hpi_status_t hpi_txdma_channel_control(hpi_handle_t handle,
+ txdma_cs_cntl_t control, uint8_t channel);
+hpi_status_t hpi_txdma_control_status(hpi_handle_t handle, io_op_t op_mode,
+ uint8_t channel, tdc_stat_t *cs_p);
+
+hpi_status_t hpi_txdma_event_mask(hpi_handle_t handle, io_op_t op_mode,
+ uint8_t channel, tdc_int_mask_t *mask_p);
+hpi_status_t hpi_tx_fifo_status(hpi_handle_t handle, io_op_t op_mode,
+ tdc_fifo_err_stat_t *cs_p);
+hpi_status_t hpi_tx_fifo_mask(hpi_handle_t handle, io_op_t op_mode,
+ tdc_fifo_err_mask_t *mask_p);
+
+hpi_status_t hpi_txdma_event_mask_mk_out(hpi_handle_t handle, uint8_t channel);
+hpi_status_t hpi_txdma_event_mask_mk_in(hpi_handle_t handle, uint8_t channel);
+
+hpi_status_t hpi_txdma_ring_addr_set(hpi_handle_t handle, uint8_t channel,
+ uint64_t start_addr, uint32_t len);
+hpi_status_t hpi_txdma_ring_config(hpi_handle_t handle, io_op_t op_mode,
+ uint8_t channel, uint64_t *reg_data);
+hpi_status_t hpi_txdma_mbox_config(hpi_handle_t handle, io_op_t op_mode,
+ uint8_t channel, uint64_t *mbox_addr);
+hpi_status_t hpi_txdma_desc_gather_set(hpi_handle_t handle,
+ p_tx_desc_t desc_p, uint8_t gather_index,
+ boolean_t mark, uint8_t ngathers,
+ uint64_t dma_ioaddr, uint32_t transfer_len);
+
+hpi_status_t hpi_txdma_desc_gather_sop_set(hpi_handle_t handle,
+ p_tx_desc_t desc_p, boolean_t mark_mode, uint8_t ngathers);
+
+hpi_status_t hpi_txdma_desc_gather_sop_set_1(hpi_handle_t handle,
+ p_tx_desc_t desc_p, boolean_t mark_mode, uint8_t ngathers,
+ uint32_t transfer_len);
+
+hpi_status_t hpi_txdma_desc_set_xfer_len(hpi_handle_t handle,
+ p_tx_desc_t desc_p, uint32_t transfer_len);
+
+hpi_status_t hpi_txdma_desc_set_zero(hpi_handle_t handle, uint16_t entries);
+hpi_status_t hpi_txdma_desc_mem_get(hpi_handle_t handle, uint16_t index,
+ p_tx_desc_t desc_p);
+hpi_status_t hpi_txdma_desc_kick_reg_set(hpi_handle_t handle, uint8_t channel,
+ uint16_t tail_index, boolean_t wrap);
+hpi_status_t hpi_txdma_desc_kick_reg_get(hpi_handle_t handle, uint8_t channel,
+ tdc_tdr_kick_t *kick_p);
+hpi_status_t hpi_txdma_ring_head_get(hpi_handle_t handle, uint8_t channel,
+ tdc_tdr_head_t *hdl_p);
+hpi_status_t hpi_txdma_channel_mbox_get(hpi_handle_t handle, uint8_t channel,
+ p_txdma_mailbox_t mbox_p);
+hpi_status_t hpi_txdma_channel_pre_state_get(hpi_handle_t handle,
+ uint8_t channel, tdc_tdr_pre_head_t *prep);
+void hpi_txdma_dump_desc_one(hpi_handle_t handle, p_tx_desc_t desc_p,
+ int desc_index);
+hpi_status_t hpi_txdma_dump_tdc_regs(hpi_handle_t handle, uint8_t tdc);
+hpi_status_t hpi_txdma_dump_tdc_common_regs(hpi_handle_t handle);
+hpi_status_t hpi_txdma_control_reset_wait(hpi_handle_t handle,
+ uint8_t channel);
+hpi_status_t hpi_txdma_control_stop_wait(hpi_handle_t handle,
+ uint8_t channel);
+
+
+#endif /* _HPI_TXDMA_H */
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#include "hpi_vir.h"
+
+/* One register only */
+uint32_t fzc_pio_offset[] = {
+ LD_INTR_TIM_RES
+};
+
+const char *fzc_pio_name[] = {
+ "LD_INTR_TIM_RES"
+};
+
+/* 32 logical devices */
+uint32_t fzc_pio_ldgnum_offset[] = {
+ LD_GRP_CTRL
+};
+
+const char *fzc_pio_ldgnum_name[] = {
+ "LD_GRP_CTRL"
+};
+
+/* PIO_LDSV, 32 sets by 8192 bytes */
+uint32_t pio_ldsv_offset[] = {
+ LDSV0,
+ LDSV1,
+ LD_INTR_MGMT
+};
+const char *pio_ldsv_name[] = {
+ "LDSV0",
+ "LDSV1",
+ "LD_INTR_MGMT"
+};
+
+/* PIO_IMASK0: 32 by 8192 */
+uint32_t pio_imask0_offset[] = {
+ LD_INTR_MASK,
+};
+
+const char *pio_imask0_name[] = {
+ "LD_INTR_MASK",
+};
+
+/*
+ * Set up a logical group number that a logical device belongs to.
+ */
+hpi_status_t
+hpi_fzc_ldg_num_set(hpi_handle_t handle, uint8_t ld, uint8_t ldg)
+{
+ ld_grp_ctrl_t gnum;
+
+ if (!LD_VALID(ld)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_fzc_ldg_num_set ld <0x%x>", ld));
+ return (HPI_FAILURE | HPI_VIR_LD_INVALID(ld));
+ }
+
+ if (!LDG_VALID(ldg)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_fzc_ldg_num_set ldg <0x%x>", ldg));
+ return (HPI_FAILURE | HPI_VIR_LDG_INVALID(ld));
+ }
+
+ gnum.value = 0;
+ gnum.bits.num = ldg;
+
+ HXGE_REG_WR32(handle, LD_GRP_CTRL + LD_NUM_OFFSET(ld), gnum.value);
+
+ return (HPI_SUCCESS);
+}
+
+/*
+ * Get device state vectors.
+ */
+hpi_status_t
+hpi_ldsv_ldfs_get(hpi_handle_t handle, uint8_t ldg, uint32_t *vector0_p,
+ uint32_t *vector1_p)
+{
+ hpi_status_t status;
+
+ if ((status = hpi_ldsv_get(handle, ldg, VECTOR0, vector0_p))) {
+ return (status);
+ }
+ if ((status = hpi_ldsv_get(handle, ldg, VECTOR1, vector1_p))) {
+ return (status);
+ }
+
+ return (HPI_SUCCESS);
+}
+
+/*
+ * Get device state vectors.
+ */
+hpi_status_t
+hpi_ldsv_get(hpi_handle_t handle, uint8_t ldg, ldsv_type_t vector,
+ uint32_t *ldf_p)
+{
+ uint32_t offset;
+
+ if (!LDG_VALID(ldg)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_ldsv_get Invalid Input ldg <0x%x>", ldg));
+ return (HPI_FAILURE | HPI_VIR_LDG_INVALID(ldg));
+ }
+
+ switch (vector) {
+ case VECTOR0:
+ offset = LDSV0 + LDSV_OFFSET(ldg);
+ break;
+
+ case VECTOR1:
+ offset = LDSV1 + LDSV_OFFSET(ldg);
+ break;
+
+ default:
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_ldsv_get Invalid Input: ldsv type <0x%x>", vector));
+ return (HPI_FAILURE | HPI_VIR_LDSV_INVALID(vector));
+ }
+
+ HXGE_REG_RD32(handle, offset, ldf_p);
+
+ return (HPI_SUCCESS);
+}
+
+/*
+ * Set the mask bits for both ldf0 and ldf1.
+ */
+hpi_status_t
+hpi_intr_mask_set(hpi_handle_t handle, uint8_t ld, uint8_t ldf_mask)
+{
+ uint32_t offset;
+
+ if (!LD_VALID(ld)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_intr_mask_set ld", ld));
+ return (HPI_FAILURE | HPI_VIR_LD_INVALID(ld));
+ }
+
+ ldf_mask &= LD_IM_MASK;
+ offset = LDSV_OFFSET_MASK(ld);
+
+ HPI_DEBUG_MSG(( HPI_VIR_CTL,
+ "hpi_intr_mask_set: ld %d offset 0x%0x mask 0x%x",
+ ld, offset, ldf_mask));
+
+ HXGE_REG_WR32(handle, offset, (uint32_t)ldf_mask);
+
+ return (HPI_SUCCESS);
+}
+
+/*
+ * Set interrupt timer and arm bit.
+ */
+hpi_status_t
+hpi_intr_ldg_mgmt_set(hpi_handle_t handle, uint8_t ldg, boolean_t arm,
+ uint8_t timer)
+{
+ ld_intr_mgmt_t mgm;
+
+ if (!LDG_VALID(ldg)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_intr_ldg_mgmt_set Invalid Input: ldg <0x%x>", ldg));
+ return (HPI_FAILURE | HPI_VIR_LDG_INVALID(ldg));
+ }
+
+ if (!LD_INTTIMER_VALID(timer)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_intr_ldg_mgmt_set Invalid Input"
+ " timer <0x%x>", timer));
+ return (HPI_FAILURE | HPI_VIR_INTM_TM_INVALID(ldg));
+ }
+
+ if (arm) {
+ mgm.bits.arm = 1;
+ } else {
+ HXGE_REG_RD32(handle, LD_INTR_MGMT + LDSV_OFFSET(ldg),
+ &mgm.value);
+ }
+
+ mgm.bits.timer = timer;
+ HXGE_REG_WR32(handle, LD_INTR_MGMT + LDSV_OFFSET(ldg), mgm.value);
+
+ HPI_DEBUG_MSG(( HPI_VIR_CTL,
+ " hpi_intr_ldg_mgmt_set: ldg %d reg offset 0x%x",
+ ldg, LD_INTR_MGMT + LDSV_OFFSET(ldg)));
+
+ return (HPI_SUCCESS);
+}
+
+/*
+ * Arm the group.
+ */
+hpi_status_t
+hpi_intr_ldg_mgmt_arm(hpi_handle_t handle, uint8_t ldg)
+{
+ ld_intr_mgmt_t mgm;
+
+ if (!LDG_VALID(ldg)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_intr_ldg_mgmt_arm Invalid Input: ldg <0x%x>", ldg));
+ return (HPI_FAILURE | HPI_VIR_LDG_INVALID(ldg));
+ }
+
+ HXGE_REG_RD32(handle, (LD_INTR_MGMT + LDSV_OFFSET(ldg)), &mgm.value);
+ mgm.bits.arm = 1;
+
+ HXGE_REG_WR32(handle, LD_INTR_MGMT + LDSV_OFFSET(ldg), mgm.value);
+ HPI_DEBUG_MSG(( HPI_VIR_CTL,
+ " hpi_intr_ldg_mgmt_arm: ldg %d reg offset 0x%x",
+ ldg, LD_INTR_MGMT + LDSV_OFFSET(ldg)));
+
+ return (HPI_SUCCESS);
+}
+
+/*
+ * Set the timer resolution.
+ */
+hpi_status_t
+hpi_fzc_ldg_timer_res_set(hpi_handle_t handle, uint32_t res)
+{
+ ld_intr_tim_res_t tm;
+
+ if (res > LDGTITMRES_RES_MASK) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_fzc_ldg_timer_res_set Invalid Input: res <0x%x>",
+ res));
+ return (HPI_FAILURE | HPI_VIR_TM_RES_INVALID);
+ }
+
+ tm.value = 0;
+ tm.bits.res = res;
+
+ HXGE_REG_WR32(handle, LD_INTR_TIM_RES, tm.value);
+
+ return (HPI_SUCCESS);
+}
+
+/*
+ * Set the system interrupt data.
+ */
+hpi_status_t
+hpi_fzc_sid_set(hpi_handle_t handle, fzc_sid_t sid)
+{
+ sid_t sd;
+
+ if (!LDG_VALID(sid.ldg)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_fzc_sid_set Invalid Input: ldg <0x%x>", sid.ldg));
+ return (HPI_FAILURE | HPI_VIR_LDG_INVALID(sid.ldg));
+ }
+
+ if (!SID_VECTOR_VALID(sid.vector)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_fzc_sid_set Invalid Input: vector <0x%x>",
+ sid.vector));
+
+#if defined(SOLARIS) && defined(_KERNEL) && defined(HPI_DEBUG)
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " invalid VECTOR: hpi_fzc_sid_set(%d)", sid.vector));
+#endif
+ return (HPI_FAILURE | HPI_VIR_SID_VEC_INVALID(sid.vector));
+ }
+
+ sd.value = 0;
+ sd.bits.data = sid.vector;
+
+#if defined(SOLARIS) && defined(_KERNEL) && defined(HPI_DEBUG)
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_fzc_sid_set: group %d 0x%x", sid.ldg, sd.value));
+#endif
+
+ HXGE_REG_WR32(handle, SID + LDG_SID_OFFSET(sid.ldg), sd.value);
+
+ return (HPI_SUCCESS);
+}
+
+/*
+ * Get the system error stats.
+ */
+hpi_status_t
+hpi_fzc_sys_err_stat_get(hpi_handle_t handle, dev_err_stat_t *statp)
+{
+ HXGE_REG_RD32(handle, DEV_ERR_STAT, &statp->value);
+ return (HPI_SUCCESS);
+}
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#ifndef _HPI_VIR_H
+#define _HPI_VIR_H
+
+#include "hpi.h"
+#include "../hxge_peu_hw.h"
+
+/*
+ * Virtualization and Logical devices HPI error codes
+ */
+#define VIR_ERR_ST (VIR_BLK_ID << HPI_BLOCK_ID_SHIFT)
+#define VIR_ID_SHIFT(n) (n << HPI_PORT_CHAN_SHIFT)
+
+#define VIR_LD_INVALID (HPI_BK_ERROR_START | 0x30)
+#define VIR_LDG_INVALID (HPI_BK_ERROR_START | 0x31)
+#define VIR_LDSV_INVALID (HPI_BK_ERROR_START | 0x32)
+
+#define VIR_INTM_TM_INVALID (HPI_BK_ERROR_START | 0x33)
+#define VIR_TM_RES_INVALID (HPI_BK_ERROR_START | 0x34)
+#define VIR_SID_VEC_INVALID (HPI_BK_ERROR_START | 0x35)
+
+/*
+ * Error codes of logical devices and groups functions.
+ */
+#define HPI_VIR_LD_INVALID(n) (VIR_ID_SHIFT(n) | VIR_ERR_ST | VIR_LD_INVALID)
+#define HPI_VIR_LDG_INVALID(n) (VIR_ID_SHIFT(n) | VIR_ERR_ST | VIR_LDG_INVALID)
+#define HPI_VIR_LDSV_INVALID(n) (VIR_ID_SHIFT(n) | \
+ VIR_ERR_ST | VIR_LDSV_INVALID)
+#define HPI_VIR_INTM_TM_INVALID(n) (VIR_ID_SHIFT(n) | \
+ VIR_ERR_ST | VIR_INTM_TM_INVALID)
+#define HPI_VIR_TM_RES_INVALID (VIR_ERR_ST | VIR_TM_RES_INVALID)
+#define HPI_VIR_SID_VEC_INVALID(n) (VIR_ID_SHIFT(n) | \
+ VIR_ERR_ST | VIR_TM_RES_INVALID)
+
+/*
+ * Logical device definitions.
+ */
+#define LDG_NUM_STEP 4
+#define LD_NUM_OFFSET(ld) (ld * LDG_NUM_STEP)
+
+#define LDSV_STEP 8192
+#define LDSVG_OFFSET(ldg) (ldg * LDSV_STEP)
+#define LDSV_OFFSET(ldv) (ldv * LDSV_STEP)
+#define LDSV_OFFSET_MASK(ld) (LD_INTR_MASK + LDSV_OFFSET(ld))
+
+#define LDG_SID_STEP 8192
+#define LDG_SID_OFFSET(ldg) (ldg * LDG_SID_STEP)
+
+typedef enum {
+ VECTOR0,
+ VECTOR1,
+} ldsv_type_t;
+
+/*
+ * Definitions for the system interrupt data.
+ */
+typedef struct _fzc_sid {
+ uint8_t ldg;
+ uint8_t vector;
+} fzc_sid_t, *p_fzc_sid_t;
+
+/*
+ * Virtualization and Interrupt Prototypes.
+ */
+hpi_status_t hpi_fzc_ldg_num_set(hpi_handle_t handle, uint8_t ld, uint8_t ldg);
+hpi_status_t hpi_fzc_ldg_num_get(hpi_handle_t handle, uint8_t ld,
+ uint8_t *ldg_p);
+hpi_status_t hpi_ldsv_ldfs_get(hpi_handle_t handle, uint8_t ldg,
+ uint32_t *vector0_p, uint32_t *vecto1_p);
+hpi_status_t hpi_ldsv_get(hpi_handle_t handle, uint8_t ldg, ldsv_type_t vector,
+ uint32_t *ldf_p);
+hpi_status_t hpi_intr_mask_set(hpi_handle_t handle, uint8_t ld,
+ uint8_t ldf_mask);
+hpi_status_t hpi_intr_mask_get(hpi_handle_t handle, uint8_t ld,
+ uint8_t *ldf_mask_p);
+hpi_status_t hpi_intr_ldg_mgmt_set(hpi_handle_t handle, uint8_t ldg,
+ boolean_t arm, uint8_t timer);
+hpi_status_t hpi_intr_ldg_mgmt_timer_get(hpi_handle_t handle, uint8_t ldg,
+ uint8_t *timer_p);
+hpi_status_t hpi_intr_ldg_mgmt_arm(hpi_handle_t handle, uint8_t ldg);
+hpi_status_t hpi_fzc_ldg_timer_res_set(hpi_handle_t handle, uint32_t res);
+hpi_status_t hpi_fzc_ldg_timer_res_get(hpi_handle_t handle, uint8_t *res_p);
+hpi_status_t hpi_fzc_sid_set(hpi_handle_t handle, fzc_sid_t sid);
+hpi_status_t hpi_fzc_sid_get(hpi_handle_t handle, p_fzc_sid_t sid_p);
+hpi_status_t hpi_fzc_sys_err_mask_set(hpi_handle_t handle, uint32_t mask);
+hpi_status_t hpi_fzc_sys_err_stat_get(hpi_handle_t handle,
+ dev_err_stat_t *statp);
+hpi_status_t hpi_vir_dump_pio_fzc_regs_one(hpi_handle_t handle);
+hpi_status_t hpi_vir_dump_ldgnum(hpi_handle_t handle);
+hpi_status_t hpi_vir_dump_ldsv(hpi_handle_t handle);
+hpi_status_t hpi_vir_dump_imask0(hpi_handle_t handle);
+hpi_status_t hpi_vir_dump_sid(hpi_handle_t handle);
+
+#endif /* _HPI_VIR_H */
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#include "hpi_vmac.h"
+
+uint64_t vmac_offset[] = {
+ VMAC_RST,
+ VMAC_TX_CFG,
+ VMAC_RX_CFG,
+ VMAC_TX_STAT,
+ VMAC_TX_MSK,
+ VMAC_RX_STAT,
+ VMAC_RX_MSK,
+ VMAC_TX_STAT_MIRROR,
+ VMAC_RX_STAT_MIRROR,
+ VMAC_TX_FRAME_CNT,
+ VMAC_TX_BYTE_CNT,
+ VMAC_RX_FRAME_CNT,
+ VMAC_RX_BYTE_CNT,
+ VMAC_RX_DROP_FR_CNT,
+ VMAC_RX_DROP_BYTE_CNT,
+ VMAC_RX_CRC_CNT,
+ VMAC_RX_PAUSE_CNT,
+ VMAC_RX_BCAST_FR_CNT,
+ VMAC_RX_MCAST_FR_CNT
+};
+
+const char *vmac_name[] = {
+ "VMAC_RST",
+ "VMAC_TX_CFG",
+ "VMAC_RX_CFG",
+ "VMAC_TX_STAT",
+ "VMAC_TX_MSK",
+ "VMAC_RX_STAT",
+ "VMAC_RX_MSK",
+ "VMAC_TX_STAT_MIRROR",
+ "VMAC_RX_STAT_MIRROR",
+ "VMAC_TX_FRAME_CNT",
+ "VMAC_TX_BYTE_CNT",
+ "VMAC_RX_FRAME_CNT",
+ "VMAC_RX_BYTE_CNT",
+ "VMAC_RX_DROP_FR_CNT",
+ "VMAC_RX_DROP_BYTE_CNT",
+ "VMAC_RX_CRC_CNT",
+ "VMAC_RX_PAUSE_CNT",
+ "VMAC_RX_BCAST_FR_CNT",
+ "VMAC_RX_MCAST_FR_CNT"
+};
+
+
+hpi_status_t
+hpi_vmac_dump_regs(hpi_handle_t handle)
+{
+ uint64_t value;
+ int num_regs, i;
+
+ num_regs = sizeof (vmac_offset) / sizeof (uint64_t);
+ HPI_REG_DUMP_MSG(( HPI_REG_CTL,
+ "\nVMAC Register Dump\n"));
+
+ for (i = 0; i < num_regs; i++) {
+ HXGE_REG_RD64(handle, vmac_offset[i], &value);
+ HPI_REG_DUMP_MSG(( HPI_REG_CTL,
+ "%08llx %s\t %016llx \n",
+ vmac_offset[i], vmac_name[i], value));
+ }
+
+ HPI_REG_DUMP_MSG(( HPI_REG_CTL,
+ "\n VMAC Register Dump done\n"));
+
+ return (HPI_SUCCESS);
+}
+
+
+hpi_status_t
+hpi_tx_vmac_reset(hpi_handle_t handle)
+{
+ vmac_rst_t reset;
+
+ HXGE_REG_RD64(handle, VMAC_RST, &(reset.value));
+
+ reset.bits.tx_reset = 1;
+
+ HXGE_REG_WR64(handle, VMAC_RST, reset.value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_vmac_tx_ints(hpi_handle_t handle, int enable)
+{
+ uint64_t mask = 0;
+ if (!enable)
+ mask = ~0ULL;
+ HXGE_REG_WR64(handle, VMAC_TX_MSK, mask);
+ return (HPI_SUCCESS);
+}
+
+
+hpi_status_t
+hpi_tx_vmac_clear_regs(hpi_handle_t handle)
+{
+ uint64_t val;
+
+ HXGE_REG_WR64(handle, VMAC_TX_STAT, ~0ULL); /* RW1C */
+ HXGE_REG_WR64(handle, VMAC_TX_MSK, ~0ULL); /* disable everything */
+ HXGE_REG_RD64(handle, VMAC_TX_FRAME_CNT, &val);
+ HXGE_REG_RD64(handle, VMAC_TX_BYTE_CNT, &val);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_vmac_rx_ints(hpi_handle_t handle, int enable)
+{
+ uint64_t mask = 0;
+ if (!enable)
+ mask = ~0ULL;
+ HXGE_REG_WR64(handle, VMAC_RX_MSK, mask);
+ return (HPI_SUCCESS);
+}
+
+
+
+hpi_status_t
+hpi_rx_vmac_reset(hpi_handle_t handle)
+{
+ vmac_rst_t reset;
+
+ HXGE_REG_RD64(handle, VMAC_RST, &(reset.value));
+
+ reset.bits.rx_reset = 1;
+
+ HXGE_REG_WR64(handle, VMAC_RST, reset.value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_rx_vmac_clear_regs(hpi_handle_t handle)
+{
+ uint64_t val;
+
+ /* Clear off the Rx registers */
+ HXGE_REG_WR64(handle, VMAC_RX_STAT, ~0ULL); /* RW1C */
+ HXGE_REG_WR64(handle, VMAC_RX_MSK, ~0ULL); /* disable everything */
+ HXGE_REG_WR64(handle, VMAC_RX_STAT_MIRROR, 0);
+ HXGE_REG_RD64(handle, VMAC_RX_FRAME_CNT, &val); /* RORC */
+ HXGE_REG_RD64(handle, VMAC_RX_BYTE_CNT, &val); /* RORC */
+ HXGE_REG_RD64(handle, VMAC_RX_DROP_FR_CNT, &val); /* RORC */
+ HXGE_REG_RD64(handle, VMAC_RX_DROP_BYTE_CNT, &val); /* RORC */
+ HXGE_REG_RD64(handle, VMAC_RX_CRC_CNT, &val); /* RORC */
+ HXGE_REG_RD64(handle, VMAC_RX_PAUSE_CNT, &val); /* RORC */
+ HXGE_REG_RD64(handle, VMAC_RX_BCAST_FR_CNT, &val);/* RORC */
+ HXGE_REG_RD64(handle, VMAC_RX_MCAST_FR_CNT, &val); /* RORC */
+
+ return (HPI_SUCCESS);
+
+}
+
+
+hpi_status_t
+hpi_vmac_tx_config(hpi_handle_t handle, config_op_t op, uint64_t config,
+ uint16_t max_frame_length)
+{
+ vmac_tx_cfg_t cfg;
+ hpi_status_t err = HPI_SUCCESS;
+
+ if (config == 0) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_vmac_tx_config Invalid Input: config <0x%x>",
+ config));
+ return (HPI_FAILURE);
+ }
+
+ HXGE_REG_RD64(handle, VMAC_TX_CFG, &cfg.value);
+
+ switch (op) {
+ case ENABLE:
+ if (config & CFG_VMAC_TX_EN)
+ cfg.bits.tx_en = 1;
+ if (config & CFG_VMAC_TX_CRC_INSERT)
+ cfg.bits.crc_insert = 1;
+ if (config & CFG_VMAC_TX_PAD)
+ cfg.bits.tx_pad = 1;
+
+ /* If a bad MTU was passed, then leave the old value as is
+ * and return a failure so that "ifconfig mtu" can fail
+ */
+ if (max_frame_length > MAX_JUMBO_FRAME_SIZE) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_vmac_tx_config Invalid Input: max_frame_length <0x%x>",
+ max_frame_length));
+ err = HPI_FAILURE;
+ }
+ else if (max_frame_length)
+ cfg.bits.tx_max_frame_length = max_frame_length;
+ break;
+ case DISABLE:
+ if (config & CFG_VMAC_TX_EN)
+ cfg.bits.tx_en = 0;
+ if (config & CFG_VMAC_TX_CRC_INSERT)
+ cfg.bits.crc_insert = 0;
+ if (config & CFG_VMAC_TX_PAD)
+ cfg.bits.tx_pad = 0;
+ break;
+ case INIT:
+ if (config & CFG_VMAC_TX_EN)
+ cfg.bits.tx_en = 1;
+ else
+ cfg.bits.tx_en = 0;
+
+ if (config & CFG_VMAC_TX_CRC_INSERT)
+ cfg.bits.crc_insert = 1;
+ else
+ cfg.bits.crc_insert = 0;
+
+ if (config & CFG_VMAC_TX_PAD)
+ cfg.bits.tx_pad = 1;
+ else
+ cfg.bits.tx_pad = 0;
+
+ if (max_frame_length > MAX_JUMBO_FRAME_SIZE) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_vmac_tx_config Invalid Input: max_frame_length <0x%x>",
+ max_frame_length));
+ err = HPI_FAILURE;
+ }
+ else if (max_frame_length)
+ cfg.bits.tx_max_frame_length = max_frame_length;
+
+ break;
+ default:
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_vmac_tx_config Invalid Input: op <0x%x>", op));
+ return (HPI_FAILURE);
+ }
+
+ HXGE_REG_WR64(handle, VMAC_TX_CFG, cfg.value);
+
+ return (err);
+}
+
+hpi_status_t
+hpi_vmac_rx_config(hpi_handle_t handle, config_op_t op, uint64_t config,
+ uint16_t max_frame_length)
+{
+ vmac_rx_cfg_t cfg;
+
+ if (!config && (op != INIT)) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_vmac_rx_config Invalid Input: config <0x%x>",
+ config));
+ return (HPI_FAILURE);
+ }
+
+ HXGE_REG_RD64(handle, VMAC_RX_CFG, &cfg.value);
+
+ switch (op) {
+ case ENABLE:
+ if (config & CFG_VMAC_RX_EN)
+ cfg.bits.rx_en = 1;
+ if (config & CFG_VMAC_RX_CRC_CHECK_DISABLE)
+ cfg.bits.crc_check_disable = 1;
+ if (config & CFG_VMAC_RX_STRIP_CRC)
+ cfg.bits.strip_crc = 1;
+ if (config & CFG_VMAC_RX_PASS_FLOW_CTRL_FR)
+ cfg.bits.pass_flow_ctrl_fr = 1;
+ if (config & CFG_VMAC_RX_PROMISCUOUS_GROUP)
+ cfg.bits.promiscuous_group = 1;
+ if (config & CFG_VMAC_RX_PROMISCUOUS_MODE)
+ cfg.bits.promiscuous_mode = 1;
+ if (config & CFG_VMAC_RX_LOOP_BACK)
+ cfg.bits.loopback = 1;
+ break;
+ case DISABLE:
+ if (config & CFG_VMAC_RX_EN)
+ cfg.bits.rx_en = 0;
+ if (config & CFG_VMAC_RX_CRC_CHECK_DISABLE)
+ cfg.bits.crc_check_disable = 0;
+ if (config & CFG_VMAC_RX_STRIP_CRC)
+ cfg.bits.strip_crc = 0;
+ if (config & CFG_VMAC_RX_PASS_FLOW_CTRL_FR)
+ cfg.bits.pass_flow_ctrl_fr = 0;
+ if (config & CFG_VMAC_RX_PROMISCUOUS_GROUP)
+ cfg.bits.promiscuous_group = 0;
+ if (config & CFG_VMAC_RX_PROMISCUOUS_MODE)
+ cfg.bits.promiscuous_mode = 0;
+ if (config & CFG_VMAC_RX_LOOP_BACK)
+ cfg.bits.loopback = 0;
+ break;
+ case INIT:
+ if (config & CFG_VMAC_RX_EN)
+ cfg.bits.rx_en = 1;
+ else
+ cfg.bits.rx_en = 0;
+ if (config & CFG_VMAC_RX_CRC_CHECK_DISABLE)
+ cfg.bits.crc_check_disable = 1;
+ else
+ cfg.bits.crc_check_disable = 0;
+ if (config & CFG_VMAC_RX_STRIP_CRC)
+ cfg.bits.strip_crc = 1;
+ else
+ cfg.bits.strip_crc = 0;
+ if (config & CFG_VMAC_RX_PASS_FLOW_CTRL_FR)
+ cfg.bits.pass_flow_ctrl_fr = 1;
+ else
+ cfg.bits.pass_flow_ctrl_fr = 0;
+ if (config & CFG_VMAC_RX_PROMISCUOUS_GROUP)
+ cfg.bits.promiscuous_group = 1;
+ else
+ cfg.bits.promiscuous_group = 0;
+ if (config & CFG_VMAC_RX_PROMISCUOUS_MODE)
+ cfg.bits.promiscuous_mode = 1;
+ else
+ cfg.bits.promiscuous_mode = 0;
+ if (config & CFG_VMAC_RX_LOOP_BACK)
+ cfg.bits.loopback = 1;
+ else
+ cfg.bits.loopback = 0;
+
+ break;
+ default:
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_vmac_rx_config Invalid Input: op <0x%x>", op));
+ return (HPI_FAILURE);
+ }
+
+ if (max_frame_length > MAX_JUMBO_FRAME_SIZE) {
+ HPI_ERROR_MSG(( HPI_ERR_CTL,
+ " hpi_vmac_rx_config Invalid Input: max_frame_length <0x%x>",
+ max_frame_length));
+ return (HPI_FAILURE);
+ }
+
+ if (max_frame_length > 0)
+ cfg.bits.rx_max_frame_length = max_frame_length;
+
+ HXGE_REG_WR64(handle, VMAC_RX_CFG, cfg.value);
+
+ return (HPI_SUCCESS);
+}
+
+hpi_status_t
+hpi_vmac_rx_set_framesize(hpi_handle_t handle, uint16_t max_frame_length)
+{
+ return(hpi_vmac_rx_config(handle, ENABLE, 0, max_frame_length));
+}
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#ifndef _HPI_MAC_H
+#define _HPI_MAC_H
+
+#include "hpi.h"
+#include "../hxge_vmac_hw.h"
+
+hpi_status_t hpi_tx_vmac_reset(hpi_handle_t handle);
+hpi_status_t hpi_rx_vmac_reset(hpi_handle_t handle);
+hpi_status_t hpi_vmac_rx_ints(hpi_handle_t handle, int enable);
+hpi_status_t hpi_vmac_tx_ints(hpi_handle_t handle, int enable);
+hpi_status_t hpi_tx_vmac_clear_regs(hpi_handle_t handle);
+hpi_status_t hpi_rx_vmac_clear_regs(hpi_handle_t handle);
+hpi_status_t hpi_vmac_dump_regs(hpi_handle_t handle);
+hpi_status_t hpi_vmac_tx_config(hpi_handle_t handle, config_op_t op,
+ uint64_t config, uint16_t max_frame_length);
+hpi_status_t hpi_vmac_rx_config(hpi_handle_t handle, config_op_t op,
+ uint64_t config, uint16_t max_frame_length);
+hpi_status_t hpi_vmac_rx_set_framesize(hpi_handle_t handle,
+ uint16_t max_frame_length);
+
+
+#define CFG_VMAC_TX_EN 0x00000001
+#define CFG_VMAC_TX_CRC_INSERT 0x00000002
+#define CFG_VMAC_TX_PAD 0x00000004
+
+#define CFG_VMAC_RX_EN 0x00000001
+#define CFG_VMAC_RX_CRC_CHECK_DISABLE 0x00000002
+#define CFG_VMAC_RX_STRIP_CRC 0x00000004
+#define CFG_VMAC_RX_PASS_FLOW_CTRL_FR 0x00000008
+#define CFG_VMAC_RX_PROMISCUOUS_GROUP 0x00000010
+#define CFG_VMAC_RX_PROMISCUOUS_MODE 0x00000020
+#define CFG_VMAC_RX_LOOP_BACK 0x00000040
+
+#endif /* _HPI_MAC_H */
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+/* Linux Hydra 10GBe Driver main header file */
+
+#ifndef _HXGE_H_
+#define _HXGE_H_
+
+#include <linux/version.h>
+#include <linux/stddef.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <asm/byteorder.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/pagemap.h>
+#include <linux/dma-mapping.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <linux/capability.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <net/pkt_sched.h>
+#include <linux/list.h>
+#include <linux/reboot.h>
+#ifdef NETIF_F_TSO
+#include <net/checksum.h>
+#endif
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+#include <linux/version.h>
+
+#include "hxge_defs.h"
+#include "hxge_pfc_hw.h"
+#include "hxge_peu_hw.h"
+#include "hxge_rxdma.h"
+#include "hxge_txdma.h"
+#include "hxge_vmac.h"
+#include "hxge_pfc.h"
+#include "hxge_classify.h"
+
+#define BAR_0 0
+#define BAR_1 1
+#define BAR_5 5
+
+#define ETHERADDRL ETH_ALEN
+#define PCI_DEVICE_ID_SUN_HYDRA 0xAAAA
+
+#define SUN_ETHERNET_DEVICE(device_id) {\
+ PCI_DEVICE(PCI_VENDOR_ID_SUN, device_id)}
+
+#define MUTEX_INIT(lock, nm, tp, arg) spin_lock_init((lock))
+#define MUTEX_ENTER(lock) spin_lock((lock))
+#define MUTEX_TRY_ENTER(lock) spin_trylock((lock))
+#define MUTEX_EXIT(lock) spin_unlock((lock))
+#define MUTEX_ENTER_INT(lock, flags) spin_lock_irqsave(lock, flags)
+#define MUTEX_EXIT_INT(lock, flags) spin_unlock_irqrestore(lock, flags)
+#define MUTEX_DESTROY(lock)
+
+/* forward declaration of hxge_adapter structure */
+struct hxge_adapter;
+/* declarations required for debug */
+#define HXGE_DRIVER_NAME "hxge"
+#define PFX "hxge: "
+#define DPRINTK(adapter, nlevel, klevel, fmt, args...) \
+ do{\
+ (void)((adapter) &&\
+ (NETIF_MSG_##nlevel & ((struct hxge_adapter *)adapter)->msg_enable)&&\
+ printk(KERN_##klevel PFX "%s: %s: " fmt "\n",\
+ ((struct hxge_adapter *)adapter)->netdev->name, __FUNCTION__ , ##args));\
+ } while (0)
+/* Only two functions defined, can be extended */
+#define HXGE_ERR(adapter, fmt, args...) DPRINTK(adapter, HW, ERR, fmt, ##args)
+#define HXGE_DBG(adapter, fmt, args...) DPRINTK(adapter, DRV, DEBUG, fmt, ##args)
+
+/* This is for where the adapter is not defined in context */
+#define HPRINTK(klevel, fmt, args...) \
+ do{\
+ printk(KERN_##klevel PFX "%s: " fmt "\n", __FUNCTION__ , ##args);\
+ } while (0)
+#define HXGE_ERR_PRINT(fmt, args...) HPRINTK(ERR, fmt, ##args)
+#define HXGE_DBG_PRINT(fmt, args...) HPRINTK(DEBUG, fmt, ##args)
+
+/* Hydra can address up to 44-bits of DMA memory */
+#define HXGE_MAX_ADDRESS_BITS_MASK 0x00000FFFFFFFFFFFULL
+#define HXGE_MAX_ADDRESS_BITS 44
+
+/* Timeout for Transmit before declaring it hung */
+#define HXGE_TX_TIMEOUT (5*HZ)
+
+/* Periodic timeout for monitoring link state */
+#define HXGE_LINK_TIMEOUT (2*HZ)
+
+/* Device hardware error threshold/limits before taking hxge down
+ * THRESHOLD Initial count before invoking rate limit
+ * RATELIMIT Event count per day ("rate") before taking device "down".
+ *
+ * Example: THRESHOLD 4 & RATELIMIT 1 says allow 3 errors; on fourth
+ * error, impose the rate/limit of 1 per day. In other words,
+ * allow a burst of up to three errors "immediately", but if
+ * the long term average exceeds one per day (after any initial
+ * burst), take the hxge down; 300 errors would be OK if you've
+ * been up for a year.
+ */
+
+#define HARD_ERROR_THRESHOLD 4
+#define HARD_ERROR_RATELIMIT 1
+#define SOFT_ERROR_THRESHOLD 100
+#define SOFT_ERROR_RATELIMIT 20
+#define LINE_ERROR_THRESHOLD 1000
+#define LINE_ERROR_RATELIMIT 500
+
+typedef enum {
+ HXGE_DEVICE_TESTING = 0,
+ HXGE_DEVICE_RESETTING,
+ HXGE_DEVICE_INITIALIZED, /* Device available; hxge_probe() complete */
+ HXGE_DEVICE_OPENING, /* Opening ('UP'ing) device; hxge_open() */
+ HXGE_DEVICE_ALLOCATED, /* I/O Buffers allocated; hxge_open() */
+ HXGE_DEVICE_UP, /* In 'UP' state, active & running */
+ HXGE_DEVICE_CLOSING, /* Closing/shutting down; hxge_close() */
+ HXGE_DRIVER_REMOVING,
+ HXGE_DEVICE_SHUTTINGDOWN, /* Shutting down (on fatal error) */
+ HXGE_DEVICE_FATAL /* Fatal error in open, close & abort */
+} hxge_state_t;
+
+typedef enum {
+ LINK_MONITOR_DISABLED = 0,
+ LINK_MONITOR_ENABLED,
+}link_monitor_state_t;
+
+typedef enum {
+ LINK_MONITOR_START,
+ LINK_MONITOR_STOP
+} link_monitor_t;
+
+typedef enum {
+ LINK_MODE_INTR,
+ LINK_MODE_POLL
+} link_monitor_mode_t;
+
+
+struct hxge_hw {
+ uint8_t *hw_addr;
+};
+
+
+typedef struct _hxge_stats_t {
+ hxge_vmac_stats_t vmac_stats;
+ hxge_pfc_stats_t pfc_stats;
+ uint32_t link_monitor_cnt;
+ uint32_t hard_errors; /* Hard device errors */
+ uint32_t soft_errors; /* Soft device errors */
+ uint32_t line_errors; /* Line (non-device) errors */
+ uint32_t accum_hard_errors; /* Accumulated ... */
+ uint32_t accum_soft_errors; /* Accumulated ... */
+ uint32_t accum_line_errors; /* Accumulated ... */
+ /* Device Error status/statistics
+ * PEU_INTR_STAT Generic/other/high-level device errors */
+ uint32_t peu_errors; /* Accumulated non-"i/o" errors */
+ uint32_t peu_spc_acc_err; /* PEU_INTR_STAT[20] */
+ uint32_t peu_pioacc_err; /* PEU_INTR_STAT[19:16] */
+ uint32_t peu_pcie_parerr; /* PEU_INTR_STAT[9:2] */
+ uint32_t peu_hcr_msix_parerr; /* PEU_INTR_STAT[1:0] */
+ /* Device Error status/statistics
+ * RDC_FIFO_ERR_STAT Receive subsystem device errors */
+ uint32_t rx_ierrors; /* Generic/accumulated "i" errors */
+ uint32_t rx_ctrl_sec; /* RX Ctrl RAM SEC */
+ uint32_t rx_ctrl_ded; /* RX Ctrl RAM DED */
+ uint32_t rx_data_sec; /* RX Data RAM SEC */
+ uint32_t rx_data_ded; /* RX Data RAM DED */
+ /* Device Error status/statistics
+ * TDC_FIFO_ERR_STAT Transmit subsystem device errors */
+ uint32_t tx_oerrors; /* Generic/accumulated "o" errors */
+ uint32_t tx_timeout_cnt;
+ uint32_t tx_reorder_sec; /* TX Reorder buffer SEC */
+ uint32_t tx_reorder_ded; /* TX Reorder buffer DED */
+ uint32_t tx_rtab_parerr; /* TX Reorder table parity */
+} hxge_stats_t, *p_hxge_stats_t;
+
+#define LDV_RXDMA 1
+#define LDV_TXDMA 2
+#define LDV_VMAC 4
+#define LDV_PFC 8
+#define LDV_DEVERR 16
+#define LDV_ALL 0xFF
+
+#define INTx_TYPE 0
+#define MSI_TYPE 1
+#define MSIX_TYPE 2
+#define POLLING_TYPE 3
+
+struct ldv_array {
+ uint16_t type;
+ uint16_t dev_no;
+};
+struct hxge_ldv {
+ uint16_t ldv; /* logical device number */
+ uint16_t dev_type; /* rxdma,txdma,vmac,syserr,pfc */
+ boolean_t use_timer;
+ uint16_t ldv_flags;
+ uint8_t ldf_masks;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+ irqreturn_t (*intr_handler)(int, void *, struct pt_regs *);
+#else
+ irqreturn_t (*intr_handler)(int, void *);
+#endif
+
+ struct hxge_ldg *ldgp;
+ uint64_t data; /* device specific data */
+ struct list_head ldg_list;
+ struct list_head list;
+};
+
+#define HXGE_MAX_IRQNAME 16
+struct hxge_ldg {
+ uint16_t ldg;/* logical group number */
+ uint16_t vldg_index;
+ uint16_t vector;
+ uint16_t nldvs;
+ struct hxge_adapter *hxgep;
+ uint32_t timer;
+ boolean_t arm;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+ irqreturn_t (*intr_handler)(int, void *, struct pt_regs *);
+#else
+ irqreturn_t (*intr_handler)(int, void *);
+#endif
+
+ char irq_name[HXGE_MAX_IRQNAME]; /* dyn. allocated */
+ struct list_head ldv_list;
+ struct list_head list;
+};
+
+
+struct hxge_ldgv {
+ uint8_t nldvs;
+ uint8_t nldgs;
+ uint8_t max_ldgs;
+ uint8_t max_ldvs;
+ uint32_t tmres;
+ struct list_head ldgp;
+ struct list_head ldvp;
+};
+
+
+typedef enum {
+ RESET_TX_CHANNEL_0,
+ RESET_TX_CHANNEL_1,
+ RESET_TX_CHANNEL_2,
+ RESET_TX_CHANNEL_3,
+ RESET_RX_CHANNEL_0 = HXGE_MAX_TDCS,
+ RESET_RX_CHANNEL_1,
+ RESET_RX_CHANNEL_2,
+ RESET_RX_CHANNEL_3,
+ RESET_ADAPTER = HXGE_MAX_RDCS + HXGE_MAX_TDCS,
+ RESET_TDC,
+ RESET_RDC,
+ RESET_PFC,
+ RESET_VMAC,
+ SHUTDOWN_ADAPTER,
+ MAX_CMD
+} hxge_command_t;
+
+
+/*
+ * * VLAN table configuration
+ * */
+typedef struct hxge_mv_cfg {
+ uint8_t flag; /* 0:unconfigure 1:configured */
+} hxge_mv_cfg_t, *p_hxge_mv_cfg_t;
+
+
+
+/* classification configuration */
+typedef struct hxge_class_pt_cfg {
+ /* VLAN table */
+ hxge_mv_cfg_t vlan_tbl[VLAN_ID_MAX + 1];
+ /* class config value */
+ uint32_t init_hash;
+ uint32_t class_cfg[TCAM_CLASS_MAX];
+} hxge_class_pt_cfg_t, *p_hxge_class_pt_cfg_t;
+
+
+/* Adapter flags */
+#define HXGE_RX_CHKSUM_ENABLED 0x1
+#define HXGE_TX_CHKSUM_ENABLED 0x2
+#define HXGE_VLAN_ENABLED 0x4
+#define HXGE_TCAM_ENABLED 0x8
+
+#define HXGE_CHKSUM_ENABLED (HXGE_RX_CHKSUM_ENABLED | HXGE_TX_CHKSUM_ENABLED)
+
+/* board specific private data structure */
+
+struct hxge_adapter {
+#ifdef CONFIG_HXGE_NAPI
+ struct net_device *polling_netdev; /* One per active queue */
+#endif
+ /* OS defined structs */
+ struct net_device *netdev;
+ struct pci_dev *pdev;
+ struct net_device_stats net_stats;
+ unsigned long state;
+ int num_openers;
+ int rbrs_to_kick; /* workaround for CR 6671220 */
+ unsigned int tx_mark_ints;
+ unsigned long ifup_time; /* "ifconfig up" time */
+ /* Flags */
+ uint32_t flags;
+ unsigned long err_flags;
+
+ /* Used during suspend and resume to save and restore PCI configuration
+ space */
+ uint32_t *config_space;
+ uint32_t msg_enable;
+ uint32_t bd_number;
+ struct hxge_hw hw;
+ struct hxge_work_queue_t {
+ unsigned long command;
+ } work_q;
+ struct work_struct work_to_do;
+ uint32_t rx_buffer_len;
+
+ /* Locks */
+ spinlock_t lock;
+ spinlock_t stats_lock;
+ spinlock_t tcam_lock;
+ rwlock_t wtd_lock;
+
+ /* Interrupt */
+ unsigned int intr_type;
+#ifdef CONFIG_PCI_MSI
+ struct msix_entry *msix;
+#endif
+ atomic_t irq_sem;
+ struct hxge_ldgv *ldgvp;
+
+ /* link management */
+ link_monitor_t link_monitor_state;
+ link_monitor_mode_t link_mode;
+ int prev_link_status;
+ struct timer_list wd_timer;
+
+ /* Transmit and Receive */
+ uint32_t max_tdcs;
+ uint32_t max_rdcs;
+ uint32_t default_block_size;
+
+ /* threshold of packets when queued up will force an interrupt */
+ uint16_t rcr_threshold;
+ /* Max number of packets that are processed before giving the
+ interrupt handling a breather */
+ uint16_t max_rx_pkts;
+ /* Timeout value after which interrupt will be forced (if timeout is
+ enabled and interrupt is armed */
+ uint32_t rcr_timeout;
+ uint64_t rcr_cfgb_cpy;
+ /* Enable adaptive tuning of Rx interrupt rate */
+ uint32_t adaptive_rx;
+
+ /* Transmit */
+ struct tx_ring_t *tx_ring;
+
+ /* Receive */
+ struct rx_ring_t *rx_ring;
+
+ /* Statistics */
+ p_hxge_stats_t statsp;
+
+ /* Parameter array */
+ void *param;
+
+ /* VMAC block */
+ hxge_vmac_t vmac;
+
+ /* VLAN/TCAM/PFC */
+ hxge_classify_t classifier;
+ hxge_class_pt_cfg_t class_config;
+ pfc_vlan_table_t vlan_table[VLAN_MAX_ENTRIES];
+ struct vlan_group *vlangrp;
+ int vlan_id;
+
+ /* Multicast Filter Table */
+ uint16_t mcast_hash_tbl[MAC_MAX_HASH_ENTRY];
+};
+
+#define LB_IOC (SIOCDEVPRIVATE + 15)
+#define GET_INFO_SIZE 1
+#define GET_INFO 2
+#define GET_LB_MODE 3
+#define SET_LB_MODE 4
+
+struct lb_size_info {
+ int cmd;
+ uint32_t size;
+};
+
+typedef enum {
+ normal,
+ internal,
+ external
+} lb_type_t;
+
+typedef enum {
+ hxge_lb_normal,
+ hxge_lb_ext10g,
+} hxge_lb_t;
+
+
+typedef struct lb_property_s {
+ lb_type_t lb_type;
+ char key[16];
+ uint32_t value;
+} lb_property_t;
+
+/* Error injection flags */
+
+#define KMEM_FAILURE 0x1
+#define SKB_FAILURE 0x2
+#define ALLOC_PAGES_FAILURE 0x4
+#define CHKSUM_FAILURE 0x8
+#define PCIMAP_FAILURE 0x10
+
+/* hxge_ethtool.c */
+extern void hxge_set_ethtool_ops(struct net_device *netdev);
+
+/* hxge_param.c */
+extern void hxge_check_options(struct hxge_adapter *adapter);
+extern int hxge_get_option(const char *str, int *val);
+
+/* hxge_intr.c */
+extern void hxge_disable_rx_ints(struct hxge_adapter *hxgep, struct hxge_ldg *ldgp, int channel);
+extern void hxge_enable_rx_ints(struct hxge_adapter *hxgep, struct hxge_ldg *ldgp, int channel);
+extern void hxge_enable_tx_ints(struct hxge_adapter *hxgep, struct hxge_ldg *ldgp);
+extern void hxge_disable_tx_ints(struct hxge_adapter *hxgep);
+extern void get_ldf_flags(struct hxge_ldv *ldvp, int *ldf0, int *ldf1);
+extern int valid_alignment(uint64_t addr, uint64_t size, int);
+extern int hxge_debug;
+
+
+#endif /* _HXGE_H_ */
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#ifndef _HXGE_CLASSIFY_H
+#define _HXGE_CLASSIFY_H
+
+/*
+ * The following are the user configurable ether types. Refer to
+ * /usr/include/sys/ethernet.h
+ *
+ * ETHERTYPE_PUP (0x0200)
+ * ETHERTYPE_802_MIN (0x0600)
+ * ETHERTYPE_IP (0x0800)
+ * ETHERTYPE_ARP (0x0806)
+ * ETHERTYPE_REVARP (0x8035)
+ * ETHERTYPE_AT (0x809b)
+ * ETHERTYPE_AARP (0x80f3)
+ * ETHERTYPE_IPV6 (0x86dd)
+ * ETHERTYPE_SLOW (0x8809)
+ * ETHERTYPE_PPPOED (0x8863)
+ * ETHERTYPE_PPPOES (0x8864)
+ * ETHERTYPE_MAX (0xffff)
+ */
+
+/*
+ * Used for ip class tcam key config
+ */
+#define HXGE_CLASS_TCAM_LOOKUP 0x10000
+#define HXGE_CLASS_DISCARD 0x20000
+#define HXGE_CLASS_VALID 0x40000
+#define HXGE_CLASS_ETHER_TYPE_MASK 0x0FFFF
+
+typedef struct _tcam_flow_spec {
+ hxge_tcam_entry_t tce;
+ uint64_t flags;
+ uint64_t user_info;
+} tcam_flow_spec_t, *p_tcam_flow_spec_t;
+
+typedef struct {
+ uint16_t ether_type;
+ int count; /* How many TCAM entries using this class. */
+} hxge_class_usage_t;
+
+#define HXGE_PFC_HW_UNINIT 0x0
+#define HXGE_PFC_HW_RESET 0x1
+#define HXGE_PFC_HW_INIT 0x2
+#define HXGE_PFC_SW_INIT 0x4
+
+typedef struct _hxge_classify {
+ uint32_t tcam_size;
+ uint32_t n_used;
+ uint32_t state;
+ p_hxge_pfc_stats_t pfc_stats;
+
+ tcam_flow_spec_t *tcam_entries;
+ uint8_t tcam_location;
+ hxge_class_usage_t class_usage[TCAM_CLASS_MAX];
+} hxge_classify_t, *p_hxge_classify_t;
+
+#endif /* _HXGE_CLASSIFY_H */
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#ifndef _HXGE_HXGE_DEFS_H
+#define _HXGE_HXGE_DEFS_H
+
+#include <linux/delay.h>
+#include <asm/io.h>
+
+typedef enum {
+ FALSE = 0,
+ TRUE = 1
+} boolean_t;
+
+
+#define NO_DEBUG 0x0000000000000000ULL
+#define RX_CTL 0x0000000000000002ULL
+#define TX_CTL 0x0000000000000004ULL
+#define OBP_CTL 0x0000000000000008ULL
+
+#define VPD_CTL 0x0000000000000010ULL
+#define DDI_CTL 0x0000000000000020ULL
+#define MEM_CTL 0x0000000000000040ULL
+
+#define IOC_CTL 0x0000000000000100ULL
+#define MOD_CTL 0x0000000000000200ULL
+#define DMA_CTL 0x0000000000000400ULL
+#define STR_CTL 0x0000000000000800ULL
+
+#define INT_CTL 0x0000000000001000ULL
+#define SYSERR_CTL 0x0000000000002000ULL
+#define KST_CTL 0x0000000000004000ULL
+
+#define FCRAM_CTL 0x0000000000040000ULL
+#define MAC_CTL 0x0000000000080000ULL
+
+#define DMA2_CTL 0x0000000000200000ULL
+#define RX2_CTL 0x0000000000400000ULL
+#define TX2_CTL 0x0000000000800000ULL
+
+#define MEM2_CTL 0x0000000001000000ULL
+#define MEM3_CTL 0x0000000002000000ULL
+#define NEMO_CTL 0x0000000004000000ULL
+#define NDD_CTL 0x0000000008000000ULL
+#define NDD2_CTL 0x0000000010000000ULL
+
+#define TCAM_CTL 0x0000000020000000ULL
+#define CFG_CTL 0x0000000040000000ULL
+#define CFG2_CTL 0x0000000080000000ULL
+
+#define PFC_CTL TCAM_CTL
+
+#define VIR_CTL 0x0000000100000000ULL
+#define VIR2_CTL 0x0000000200000000ULL
+
+#define HXGE_NOTE 0x0000001000000000ULL
+#define HXGE_ERR_CTL 0x0000002000000000ULL
+
+#define DUMP_ALWAYS 0x2000000000000000ULL
+
+
+/************************************************************************
+ * Special Hydra handling for 32-bit access architecture
+ *
+ * Hydra CR xxxxxxxx
+ *
+ * If on a 32-bit architecture (e.g., i686 vs x86_64), we must perform
+ * two consecutive 32-bit PIOs to gain access to any 64-bit Hydra
+ * register. The Hydra PEU (PCI Execution Unit) will internally "buffer"
+ * the two separate 32-bit reads, do a SINGLE 64-bit (atomic) register
+ * read, and then return the two 32-bit values. The Hydra read does
+ * ***NOT*** occur until the second 32-bit PIO read arrives (the first
+ * 32-bit read is simply "held" or buffered pending arrival of the
+ * "other half of the read" operation). Similarly for write operations,
+ * the Hydra buffers and then coalesces two distinct 32-bit write PIOs
+ * into a single (atomic) 64-bit internal register write.
+ *
+ * Now, this is cool (gives us 64-bit "atomic/coherent") register access
+ * on a 32-bit machine.
+ *
+ * The Issue (there is debate over this being a "bug" or a "feature") is
+ * that ABSOLUTELY NO INTERVENING PCI PIO access can occur between the
+ * two consecutive 32-bit PIO accesses for a 64-bit register. If a
+ * PIO access comes in that is NOT consecutive (e.g., is NOT for address
+ * and address+4), the held/buffered PIO is discarded, and an entirely
+ * new register access is started, either a 32-bit register access that
+ * will run normally to completion, or a NEW 64-bit access, for which
+ * this is the "first half", and which will be held until the "second half"
+ * (address+4) arrives, triggering completion of the 64-bit access.
+ *
+ * As such, *ALL* Hydra PCI PIO read/writes must be locked with exclusive
+ * PCI bus access (guaranteed consecutive & sequential). Actually, the
+ * only thing we MUST guarantee is that the PIO *requests* are consecutive
+ * and sequential delivered to the Hydra; for reads we really don't care
+ * in which order Hydra sends the upper/lower halves back to us...
+ *
+ * Bear in mind we can have a dozen different CPUs concurrently executing
+ * Hydra driver code all trying to execute 64-bit PIO register access!
+ *
+ */
+
+#if BITS_PER_LONG == 32 /* Special Hydra handling for 32bit arch */
+
+struct hxge_adapter;
+
+extern spinlock_t hxge_lock;
+
+static inline u64 __hxge_readq(void __iomem *addr)
+{
+// /* Use 'MMX' 64-bit mov available on i386/i686 architecture */
+//
+// Unfortunately, needs to save FPU status, not just %%mm0
+// Deferred for future investigation...should be faster than spin_lock!
+//
+// u64 var = 0, tmp = 0;
+// __asm__ __volatile__ (
+// "movq %%mm0, %[t]\n\t"
+// "movl %[a], %%eax\n\t"
+// "movq (%%eax), %%mm0\n\t"
+// "movq %%mm0, %[r]\n\t"
+// "movq %[t], %%mm0\n\t"
+// :[r] "=m"(var), [t]"+m"(tmp)
+// :[a] "r"(addr)
+// :"%eax"
+// );
+// smp_rmb();
+// return var;
+
+ uint64_t val;
+
+ unsigned long flags;
+
+ spin_lock_irqsave (&hxge_lock, flags);
+
+#if defined(__BIG_ENDIAN)
+ val = (uint64_t)(readl(addr)) << 32;
+ val |= (uint64_t)(readl(addr+4) & 0xFFFFFFFF);
+#else
+ val = (uint64_t)(readl(addr) & 0xFFFFFFFF);
+ val |= (uint64_t)(readl(addr+4)) << 32;
+#endif
+
+ spin_unlock_irqrestore (&hxge_lock, flags);
+
+ return val;
+}
+
+static inline void __hxge_writeq(uint64_t val, void *addr)
+{
+// /* Use 'MMX' 64-bit mov available on i386/i686 architecture */
+//
+// Unfortunately, needs to save FPU status, not just %%mm0
+// Deferred for future investigation...should be faster than spin_lock!
+//
+// u64 tmp = 0;
+// __asm__ __volatile__ (
+// "movq %%mm0, %[t]\n\t"
+// "movq %[d], %%mm0\n\t"
+// "movl %[a], %%eax\n\t"
+// "movq %%mm0, (%%eax)\n\t"
+// "movq %[t], %%mm0\n\t"
+// :[t] "+m"(tmp)
+// :[a] "r"(addr), [d] "m"(val)
+// :"%eax"
+// );
+// smp_wmb();
+// return;
+
+ unsigned long flags;
+
+ spin_lock_irqsave (&hxge_lock, flags);
+
+#if defined(__BIG_ENDIAN)
+ writel ((uint32_t)(val >> 32), addr);
+ writel ((uint32_t)(val), addr+4);
+#else
+ writel ((uint32_t)(val), addr);
+ writel ((uint32_t)(val >> 32), addr+4);
+#endif /* defined (__BIG_ENDIAN) */
+
+ spin_unlock_irqrestore (&hxge_lock, flags);
+
+ return;
+}
+
+static inline u32 __hxge_readl(void __iomem *addr)
+{
+ uint32_t val;
+ unsigned long flags;
+
+ spin_lock_irqsave (&hxge_lock, flags);
+
+ val = readl(addr);
+
+ spin_unlock_irqrestore (&hxge_lock, flags);
+
+ return val;
+}
+
+static inline void __hxge_writel(u32 val, void *addr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave (&hxge_lock, flags);
+
+ writel (val, addr);
+
+ spin_unlock_irqrestore (&hxge_lock, flags);
+
+ return;
+}
+
+#define hxge_readq(addr)\
+({\
+ u64 v; v = __hxge_readq(addr); v;\
+})
+
+#define hxge_writeq(v, addr)\
+do{\
+ __hxge_writeq(v, addr);\
+} while(0)
+
+#define hxge_readl(addr)\
+({\
+ u32 v; v = __hxge_readl(addr); v;\
+})
+
+#define hxge_writel(v, addr)\
+do{\
+ __hxge_writel(v, addr);\
+} while(0)
+
+#else /* 64-bit BITS_PER_LONG -- the normal, easy case */
+
+#define hxge_readq(addr) readq(addr)
+#define hxge_writeq(val, addr) writeq(val, addr)
+
+#define hxge_readl(addr) readl(addr)
+#define hxge_writel(val, addr) writel(val, addr)
+
+#endif /* BITS_PER_LONG */
+
+
+/* HXGE specific definitions (uses the above ones) */
+#define HXGE_REG_RD64(handle, offset, val_p)\
+do{\
+ *(val_p) = hxge_readq((handle + offset));\
+} while (0)
+
+#define HXGE_REG_RD32(handle, offset, val_p)\
+do{\
+ *(val_p) = hxge_readl((handle + offset));\
+} while (0)
+
+#define HXGE_REG_WR64(handle, offset, val)\
+do{\
+ hxge_writeq( (val), (handle + (offset)));\
+} while (0)
+
+#define HXGE_REG_WR32(handle, offset, val)\
+do{\
+ hxge_writel((val), (handle +(offset)));\
+} while (0)
+
+#define HXGE_MEM_PIO_READ64(handle)\
+({\
+ u64 v;\
+ v = hxge_readq(handle);\
+ v;\
+})
+
+#define HXGE_MEM_PIO_WRITE32(handle, data)\
+do{\
+ hxge_writel((val), handle);\
+} while (0)
+
+#define HXGE_MEM_PIO_WRITE64(handle, data)\
+do{\
+ hxge_writeq((data), handle);\
+} while (0)
+
+
+/* RDC/TDC CSR size */
+#define DMA_CSR_SIZE 2048
+
+/*
+ * Define the Default RBR, RCR
+ */
+#define RBR_DEFAULT_MAX_BLKS 4096 /* each entry (16 blockaddr/64B) */
+#define RBR_NBLK_PER_LINE 16 /* 16 block addresses per 64 B line */
+#define RBR_DEFAULT_MAX_LEN 65472 /* 2^16 - 64 */
+#define RBR_DEFAULT_MIN_LEN 64 /* multiple of 64 */
+
+#define SW_OFFSET_NO_OFFSET 0
+#define SW_OFFSET_64 1 /* 64 bytes */
+#define SW_OFFSET_128 2 /* 128 bytes */
+#define SW_OFFSET_INVALID 3
+
+/*
+ * RBR block descriptor is 32 bits (bits [43:12]
+ */
+#define RBR_BKADDR_SHIFT 12
+#define RCR_DEFAULT_MAX_BLKS 4096 /* each entry (8 blockaddr/64B) */
+#define RCR_NBLK_PER_LINE 8 /* 8 block addresses per 64 B line */
+#define RCR_DEFAULT_MAX_LEN (RCR_DEFAULT_MAX_BLKS)
+#define RCR_DEFAULT_MIN_LEN 32
+
+/* DMA Channels. */
+#define HXGE_MAX_DMCS (HXGE_MAX_RDCS + HXGE_MAX_TDCS)
+#define HXGE_MIN_RDCS 1
+#define HXGE_MAX_RDCS 4
+#define HXGE_MIN_TDCS 1
+#define HXGE_MAX_TDCS 4
+
+#define VLAN_ETHERTYPE (0x8100)
+
+/* 256 total, each blade gets 42 */
+#define TCAM_HXGE_TCAM_MAX_ENTRY 42
+
+/*
+ * Locate the DMA channel start offset (PIO_VADDR)
+ * (DMA virtual address space of the PIO block)
+ */
+/* TX_RNG_CFIG is not used since we are not using VADDR. */
+#define TX_RNG_CFIG 0x1000000
+#define TDMC_PIOVADDR_OFFSET(channel) (2 * DMA_CSR_SIZE * channel)
+#define RDMC_PIOVADDR_OFFSET(channel) (TDMC_OFFSET(channel) + DMA_CSR_SIZE)
+
+/*
+ * PIO access using the DMC block directly (DMC)
+ */
+#define DMC_OFFSET(channel) (DMA_CSR_SIZE * channel)
+#define TDMC_OFFSET(channel) (TX_RNG_CFIG + DMA_CSR_SIZE * channel)
+
+/*
+ * The following macros expect unsigned input values.
+ */
+#define TXDMA_CHANNEL_VALID(cn) (cn < HXGE_MAX_TDCS)
+
+/*
+ * Logical device definitions.
+ */
+#define HXGE_INT_MAX_LD 32
+#define HXGE_INT_MAX_LDG 32
+
+#define HXGE_RDMA_LD_START 0 /* 0 - 3 with 4 - 7 reserved */
+#define HXGE_TDMA_LD_START 8 /* 8 - 11 with 12 - 15 reserved */
+#define HXGE_VMAC_LD 16
+#define HXGE_PFC_LD 17
+#define HXGE_NMAC_LD 18
+#define HXGE_MBOX_LD_START 20 /* 20 - 23 for SW Mbox */
+#define HXGE_SYS_ERROR_LD 31
+
+#define LDG_VALID(n) (n < HXGE_INT_MAX_LDG)
+#define LD_VALID(n) (n < HXGE_INT_MAX_LD)
+#define LD_RXDMA_LD_VALID(n) (n < HXGE_MAX_RDCS)
+#define LD_TXDMA_LD_VALID(n) (n >= HXGE_MAX_RDCS && \
+ ((n - HXGE_MAX_RDCS) < HXGE_MAX_TDCS)))
+
+#define LD_TIMER_MAX 0x3f
+#define LD_INTTIMER_VALID(n) (n <= LD_TIMER_MAX)
+
+/* System Interrupt Data */
+#define SID_VECTOR_MAX 0x1f
+#define SID_VECTOR_VALID(n) (n <= SID_VECTOR_MAX)
+
+#define LD_IM_MASK 0x00000003ULL
+#define LDGTITMRES_RES_MASK 0x000FFFFFULL
+
+#define STD_FRAME_SIZE 1522 /* 1518 + 4 = 5EE + 4 */
+
+#define HXGE_DMA_START B_TRUE
+#define HXGE_DMA_STOP B_FALSE
+
+/* The timer resolution is 4 microsec per tick (250MHz clock). So, we set it
+ to be 8 microsecs */
+#define HXGE_TIMER_RESO 8
+/* Number of ticks to count down before timer goes off. It is set to be
+ 16 microsecs */
+#define HXGE_TIMER_LDG 8
+
+/*
+ * Receive and Transmit DMA definitions
+ */
+#ifdef _DMA_USES_VIRTADDR
+#define HXGE_DMA_BLOCK 1
+#else
+#define HXGE_DMA_BLOCK (64 * 64)
+#endif
+
+#define HXGE_RBR_RBB_MIN (64)
+#define HXGE_RBR_RBB_MAX (65536-64)
+#define HXGE_RBR_RBB_DEFAULT (2048) /* CR 6779304 */
+#define HXGE_RBR_SPARE 0
+#define HXGE_RCR_MIN (HXGE_RBR_RBB_MIN * 2)
+#define HXGE_RCR_MAX (32768) /* 2^15 (CR 6779304) */
+
+#define HXGE_RCR_CLK_RESO 25000
+#define HXGE_RCR_TIMEOUT 1
+#define HXGE_RCR_TIMEOUT_MIN 0 /* 0 => disable timeout */
+#define HXGE_RCR_TIMEOUT_MAX 63
+
+#define HXGE_RCR_THRESHOLD 1
+#define HXGE_RCR_THRESHOLD_MIN 0
+#define HXGE_RCR_THRESHOLD_MAX 65535
+
+#define HXGE_MAX_RX_PKTS_MIN 10
+#define HXGE_MAX_RX_PKTS_MAX 65535
+/* Maximum number of Rx packets that can be processed before the interrupt
+ handler lets go and handles the rest later. The limit is imposed by the
+ quota and budget in the NAPI case but in the non-NAPI case, this is the
+ only way to limit processing at one time */
+#define HXGE_MAX_RX_PKTS 512
+
+/* Assume the smallest buffer size of 256B. So, we can have
+ * 16 256B packets in a 4K page
+ */
+#define HXGE_RCR_DEFAULT (HXGE_RBR_RBB_DEFAULT * 16)
+
+#define HXGE_TX_RING_DEFAULT (1024)
+#define HXGE_TX_RING_MAX (64 * 128 - 1)
+
+#define RBR_BKSIZE_4K 0
+#define RBR_BKSIZE_8K 1
+#define RBR_BKSIZE_4K_BYTES (4 * 1024)
+
+#define RBR_BUFSZ2_2K 0
+#define RBR_BUFSZ2_4K 1
+#define RBR_BUFSZ2_2K_BYTES (2 * 1024)
+#define RBR_BUFSZ2_4K_BYTES (4 * 1024)
+
+#define RBR_BUFSZ1_1K 0
+#define RBR_BUFSZ1_2K 1
+#define RBR_BUFSZ1_1K_BYTES 1024
+#define RBR_BUFSZ1_2K_BYTES (2 * 1024)
+
+#define RBR_BUFSZ0_256B 0
+#define RBR_BUFSZ0_512B 1
+#define RBR_BUFSZ0_1K 2
+#define RBR_BUFSZ0_256_BYTES 256
+#define RBR_BUFSZ0_512B_BYTES 512
+#define RBR_BUFSZ0_1K_BYTES (1024)
+
+#define HXGE_MAX_MAC_ADDRS 16
+
+/* HPI Debug and Error defines */
+#define HPI_RDC_CTL 0x0000000000000001ULL
+#define HPI_TDC_CTL 0x0000000000000002ULL
+
+#define HPI_XPCS_CTL 0x0000000000000010ULL
+#define HPI_PCS_CTL 0x0000000000000020ULL
+#define HPI_ESR_CTL 0x0000000000000040ULL
+#define HPI_BMAC_CTL 0x0000000000000080ULL
+#define HPI_XMAC_CTL 0x0000000000000100ULL
+#define HPI_MAC_CTL HPI_BMAC_CTL | HPI_XMAC_CTL
+
+#define HPI_TCAM_CTL 0x0000000000000400ULL
+#define HPI_FCRAM_CTL 0x0000000000000800ULL
+#define HPI_FFLP_CTL HPI_TCAM_CTL | HPI_FCRAM_CTL
+
+#define HPI_VIR_CTL 0x0000000000001000ULL
+#define HPI_PIO_CTL 0x0000000000002000ULL
+#define HPI_VIO_CTL 0x0000000000004000ULL
+
+#define HPI_REG_CTL 0x0000000040000000ULL
+#define HPI_CTL 0x0000000080000000ULL
+#define HPI_ERR_CTL 0x0000000080000000ULL
+
+#define HXGE_DELAY(microseconds) (udelay(microseconds))
+
+/* The sizes (in bytes) of a ethernet packet */
+#define ENET_HEADER_SIZE 14
+#define MAXIMUM_ETHERNET_FRAME_SIZE 1518 /* With checksum */
+#define MINIMUM_ETHERNET_FRAME_SIZE 64 /* With checksum */
+#define ETHERNET_CSUM_SIZE 4
+#define MAXIMUM_ETHERNET_PACKET_SIZE \
+ (MAXIMUM_ETHERNET_FRAME_SIZE - ETHERNET_CSUM_SIZE)
+#define MINIMUM_ETHERNET_PACKET_SIZE \
+ (MINIMUM_ETHERNET_FRAME_SIZE - ETHERNET_CSUM_SIZE)
+#define CRC_LENGTH ETHERNET_CSUM_SIZE
+#define MAX_JUMBO_FRAME_SIZE 9216 /* Standard Jumbo frame */
+#define MAXIMUM_ETHERNET_VLAN_SIZE MAXIMUM_ETHERNET_FRAME_SIZE+4 /* VLAN tagging included */
+
+#define HASH_TABLE_SIZE 1024
+
+typedef enum { /* Represents bit numbers */
+ RING_INIT = 1,
+ RING_ENABLED,
+ RING_RESET,
+ RING_RECLAIM
+} ring_state_t;
+
+
+#endif /* _HXGE_HXGE_DEFS_H */
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#include "hpi/hpi_vmac.h"
+#include "hpi/hpi_txdma.h"
+#include "hpi/hpi_rxdma.h"
+#include "hxge.h"
+
+/************************************************************************
+* How to add new diagnostic (loopback) tests
+*
+*
+* 1. Add a new entry to the loopback_params array with new test parameters.
+* 2. Add a corresponding new entry to hxge_gstrings_test array indicating
+* a default of "NOTRUN"
+*
+* Currently, the test will bail after the first failure. Also, the hydra
+* should be in loopback mode. I have been testing with the loopback module
+* plugged into the port. If there is no loopback module, then configure the
+* VMAC RX in loopback mode
+*
+*************************************************************************/
+
+
+
+extern const char hxge_driver_version[];
+extern int hxge_reset_tx_channel(struct hxge_adapter *hxgep, int i);
+extern int hxge_reset_rx_channel(struct hxge_adapter *hxgep, int i);
+extern hpi_status_t hpi_txdma_desc_mem_get(hpi_handle_t handle, uint16_t index,
+ p_tx_desc_t desc_p);
+extern hpi_status_t hpi_vmac_rx_config(hpi_handle_t handle, config_op_t op,
+ uint64_t config, uint16_t max_frame_length);
+extern hpi_status_t hpi_txdma_desc_kick_reg_set(hpi_handle_t handle,
+ uint8_t channel, uint16_t tail_index, boolean_t wrap);
+extern hpi_status_t hpi_rxdma_cfg_rdc_reset(hpi_handle_t handle, uint8_t rdc);
+extern hpi_status_t hpi_txdma_channel_reset(hpi_handle_t handle,
+ uint8_t channel);
+extern int hxge_process_packets(struct hxge_adapter *hxgep,
+#ifdef CONFIG_HXGE_NAPI
+ int work_to_do,
+#endif
+ int channel, struct sk_buff **skb);
+#define PATT_LEN 8
+uint8_t pattern[PATT_LEN] = {0xAA, 0xBB, 0xCC, 0xDD, 0xDE, 0xAD, 0xBE, 0xEF};
+
+extern int hxge_alloc_tx(struct hxge_adapter *hxgep);
+extern int hxge_alloc_rx(struct hxge_adapter *hxgep);
+extern void hxge_enable_adapter(struct hxge_adapter *hxgep);
+extern void hxge_disable_adapter(struct hxge_adapter *hxgep);
+extern int hxge_free_tx(struct hxge_adapter *hxgep);
+extern int hxge_free_rx(struct hxge_adapter *hxgep);
+extern int hxge_set_option(const char *parm, int val);
+extern int hxge_block_reset(struct hxge_adapter *hxgep, int device);
+extern int hxge_start_xmit(struct sk_buff *skb, struct net_device *netdev);
+extern int hxge_setup_interrupt(struct net_device *netdev);
+extern int hxge_teardown_interrupt(struct hxge_adapter *hxgep);
+extern int hxge_peu_get_link_status(struct hxge_adapter *hxgep);
+
+
+typedef struct hxge_loopback_params {
+ int intr_type; /* interrupt type - INTx, MSI, MSIX, polling */
+ int pkt_len; /* packet size in bytes */
+ int num_pkts; /* # of pkts to send */
+ int tx_channels; /* # of tx channels */
+ int rx_channels; /* # of rx channels */
+ int tx_descs; /* # of tx descriptors */
+ int rcr_entries; /* # of RCR entries */
+ int rbr_entries; /* # of RBR entries */
+ int rcr_threshold;
+ int rcr_timeout;
+ int bad_len; /* force a bad pkt_hdr formation */
+} loopback_params_t;
+
+
+loopback_params_t loopback_params[] =
+{
+ /* 1 Packet sent from 1 channel */
+ {
+ INTx_TYPE,
+ 64,
+ 32,
+ 1,
+ 1,
+ HXGE_TX_DESCS_MIN,
+ HXGE_RCR_MIN,
+ HXGE_RBR_RBB_MIN,
+ 1,
+ 0,
+ 1,
+ },
+ /* 1 Packet sent from 1 channel, using INTx */
+ {
+ INTx_TYPE,
+ 64,
+ 32,
+ 1,
+ 1,
+ HXGE_TX_DESCS_MIN,
+ HXGE_RCR_MIN,
+ HXGE_RBR_RBB_MIN,
+ 1,
+ 0,
+ 1
+ },
+ /* 1 Packet sent from 1 channel, threshold but no timeout. Sending
+ * 2 packets because 1 packet with threshold does not work */
+ {
+ INTx_TYPE,
+ 64,
+ 2,
+ 1,
+ 1,
+ HXGE_TX_DESCS_MIN,
+ HXGE_RCR_MIN,
+ HXGE_RBR_RBB_MIN,
+ 1,
+ 0,
+ 0
+ },
+ /* 1024-byte packet */
+ {
+ INTx_TYPE,
+ 1024,
+ 2,
+ 1,
+ 1,
+ HXGE_TX_DESCS_MIN,
+ HXGE_RCR_MIN,
+ HXGE_RBR_RBB_MIN,
+ 1,
+ 0,
+ 0
+ },
+ /* 1 Packet sent from 1 channel */
+ {
+ POLLING_TYPE,
+ 64,
+ 2,
+ 1,
+ 1,
+ HXGE_TX_DESCS_MIN,
+ HXGE_RCR_MIN,
+ HXGE_RBR_RBB_MIN,
+ 1,
+ 0,
+ 0
+ },
+ /* 2 Tx channels, 2 Tx channels, 1 packet each */
+ { POLLING_TYPE,
+ 64,
+ 1,
+ 2,
+ 2,
+ HXGE_TX_DESCS_MIN,
+ HXGE_RCR_MIN,
+ HXGE_RBR_RBB_MIN,
+ 1,
+ 0,
+ 0
+ },
+ /* 1 Tx channel, Tx descriptor number of packets */
+ { POLLING_TYPE,
+ 64,
+ -1,
+ 1,
+ 1,
+ HXGE_TX_DESCS_MIN,
+ HXGE_RCR_MIN,
+ HXGE_RBR_RBB_MIN,
+ 1,
+ 0,
+ 0
+ },
+};
+
+#define HXGE_TEST_LEN sizeof(hxge_gstrings_test) / ETH_GSTRING_LEN
+
+static char hxge_gstrings_test[][ETH_GSTRING_LEN] = {
+ "Loopback Test 1 = NOTRUN",
+ "Loopback Test 2 = NOTRUN",
+ "Loopback Test 3 = NOTRUN",
+ "Loopback Test 4 = NOTRUN",
+ "Loopback Test 5 = NOTRUN",
+ "Loopback Test 6 = NOTRUN",
+ "Loopback Test 7 = NOTRUN",
+};
+
+struct hxge_stats_struct {
+ char name[ETH_GSTRING_LEN];
+ uint64_t offset;
+};
+
+#define STAT_OFFSET(name) offsetof(hxge_stats_t, name)
+#define RX_STAT_OFFSET(name) offsetof(struct rx_ring_stats_t, name)
+
+static struct hxge_stats_struct hxge_rdc_stats[] = {
+ {"Rx Channel #", 0},
+ {" Rx Packets", RX_STAT_OFFSET(ipackets)},
+ {" Rx Bytes", RX_STAT_OFFSET(ibytes)},
+ {" Rx Errors", RX_STAT_OFFSET(ierrors)},
+ {" Jumbo Packets", RX_STAT_OFFSET(jumbo_pkts)},
+ {" ECC Errors", RX_STAT_OFFSET(ecc_errors)},
+ {" RBR Completion Timeout", RX_STAT_OFFSET(rbr_cpl_tmout)},
+ {" PEU Response Error", RX_STAT_OFFSET(peu_resp_err)},
+ {" RCR Shadow Parity", RX_STAT_OFFSET(rcr_shadow_parity)},
+ {" RCR Prefetch Parity", RX_STAT_OFFSET(rcr_prefetch_parity)},
+ {" RCR Shadow Full", RX_STAT_OFFSET(rcr_shadow_full)},
+ {" RCR Full", RX_STAT_OFFSET(rcr_full)},
+ {" RBR Empty", RX_STAT_OFFSET(rbr_empty)},
+ {" RBR Empty Handled", RX_STAT_OFFSET(rbr_empty_handled)},
+ {" RBR Empty Buffers Posted", RX_STAT_OFFSET(rbr_empty_posted)},
+ {" RBR Full", RX_STAT_OFFSET(rbr_full)},
+ {" RCR Timeouts", RX_STAT_OFFSET(rcr_to)},
+ {" RCR Thresholds", RX_STAT_OFFSET(rcr_thres)},
+ {" Packet Too Long Errors", RX_STAT_OFFSET(pkt_too_long)},
+ {" No RBR available", RX_STAT_OFFSET(no_rbr_avail)},
+ {" No Memory Drops", RX_STAT_OFFSET(nomem_drop)},
+ {" RVM Errors", RX_STAT_OFFSET(rvm_errors)},
+ {" Frame Errors", RX_STAT_OFFSET(frame_errors)},
+ {" RAM Errors", RX_STAT_OFFSET(ram_errors)},
+ {" CRC Errors", RX_STAT_OFFSET(crc_errors)}
+};
+#define HXGE_RDC_STATS_CNT (sizeof(hxge_rdc_stats)/sizeof(struct hxge_stats_struct))
+
+
+#define TX_STAT_OFFSET(name) offsetof(struct tx_ring_stats_t, name)
+static struct hxge_stats_struct hxge_tdc_stats[] = {
+ {"Tx Channel #", 0},
+ {" Tx Packets", TX_STAT_OFFSET(opackets)},
+ {" Tx Bytes", TX_STAT_OFFSET(obytes)},
+ {" Tx Errors", TX_STAT_OFFSET(oerrors)},
+ {" Tx Desc Used [0]", TX_STAT_OFFSET(descs_used[0])},
+ {" Tx Desc Used [1]", TX_STAT_OFFSET(descs_used[1])},
+ {" Tx Desc Used [2]", TX_STAT_OFFSET(descs_used[2])},
+ {" Tx Desc Used [3]", TX_STAT_OFFSET(descs_used[3])},
+ {" Tx Desc Used [4]", TX_STAT_OFFSET(descs_used[4])},
+ {" Tx Desc Used [5]", TX_STAT_OFFSET(descs_used[5])},
+ {" Tx Desc Used [6]", TX_STAT_OFFSET(descs_used[6])},
+ {" Tx Desc Used [7]", TX_STAT_OFFSET(descs_used[7])},
+ {" Tx Desc Used [8]", TX_STAT_OFFSET(descs_used[8])},
+ {" Tx Desc Used [9]", TX_STAT_OFFSET(descs_used[9])},
+ {" Tx Desc Used [10]", TX_STAT_OFFSET(descs_used[10])},
+ {" Tx Desc Used [11]", TX_STAT_OFFSET(descs_used[11])},
+ {" Tx Desc Used [12]", TX_STAT_OFFSET(descs_used[12])},
+ {" Tx Desc Used [13]", TX_STAT_OFFSET(descs_used[13])},
+ {" Tx Desc Used [14]", TX_STAT_OFFSET(descs_used[14])},
+ {" Tx Desc Used [15]", TX_STAT_OFFSET(descs_used[15])},
+ {" Tx Lock Failed", TX_STAT_OFFSET(txlock_acquire_failed)},
+ {" Marked Ints", TX_STAT_OFFSET(marked)},
+ {" PEU Response Error Ints", TX_STAT_OFFSET(peu_resp_err)},
+ {" Packet Hdr Size Err Ints", TX_STAT_OFFSET(pkt_size_hdr_err)},
+ {" Runt Packet Drop Ints", TX_STAT_OFFSET(runt_pkt_drop_err)},
+ {" Ring Overflow Ints", TX_STAT_OFFSET(tx_rng_oflow)},
+ {" Prefetch Parity Error Ints", TX_STAT_OFFSET(pref_par_err)},
+ {" Prefetch Compl Timeout Ints", TX_STAT_OFFSET(tdr_pref_cpl_to)},
+ {" Packet Completion Timeout Ints", TX_STAT_OFFSET(pkt_cpl_to)},
+ {" Invalid SOP Ints", TX_STAT_OFFSET(invalid_sop)},
+ {" Unexpected SOP Ints", TX_STAT_OFFSET(unexpected_sop)},
+ {" Header Error Count", TX_STAT_OFFSET(hdr_error_cnt)},
+ {" Abort Count", TX_STAT_OFFSET(abort_cnt)},
+ {" Runt Count", TX_STAT_OFFSET(runt_cnt)}
+};
+#define HXGE_TDC_STATS_CNT (sizeof(hxge_tdc_stats)/sizeof(struct hxge_stats_struct))
+
+#define PFC_STAT_OFFSET(name) offsetof(hxge_pfc_stats_t, name)
+static struct hxge_stats_struct hxge_pfc_stats[] = {
+ {"PFC ", 0},
+ {" Packets dropped", PFC_STAT_OFFSET(pkt_drop)},
+ {" TCAM Parity Errors", PFC_STAT_OFFSET(tcam_parity_err)},
+ {" VLAN Parity Errors", PFC_STAT_OFFSET(tcam_parity_err)},
+ {" Bad TCP/UDP Checksum Count ", PFC_STAT_OFFSET(bad_cs_count)},
+ {" Drop Counter ", PFC_STAT_OFFSET(drop_count)},
+ {" TCP Control Drop Cnt", PFC_STAT_OFFSET(errlog.tcp_ctrl_drop)},
+ {" L2 Addr Drop Cnt", PFC_STAT_OFFSET(errlog.l2_addr_drop)},
+ {" Class Code Drop Cnt", PFC_STAT_OFFSET(errlog.class_code_drop)},
+ {" TCAM Drop Cnt", PFC_STAT_OFFSET(errlog.tcam_drop)},
+ {" VLAN Drop Cnt", PFC_STAT_OFFSET(errlog.vlan_drop)},
+ {" VLAN Parity Error Address", PFC_STAT_OFFSET(errlog.vlan_par_err_log)},
+ {" TCAM Parity Error Address", PFC_STAT_OFFSET(errlog.tcam_par_err_log)},
+};
+#define HXGE_PFC_STATS_CNT (sizeof(hxge_pfc_stats)/sizeof(struct hxge_stats_struct))
+
+
+#define VMAC_STAT_OFFSET(name) offsetof(hxge_vmac_stats_t, name)
+static struct hxge_stats_struct hxge_vmac_stats[] = {
+ {"VMAC", 0},
+ {" Tx Byte Cnt Overflow Ints", VMAC_STAT_OFFSET(tx_byte_cnt_overflow)},
+ {" Tx Frame Count Overflow Ints", VMAC_STAT_OFFSET(tx_frame_cnt_overflow)},
+ {" Tx Frame Ints", VMAC_STAT_OFFSET(frame_tx)},
+ {" Broadcast Cnt Overflowed", VMAC_STAT_OFFSET(bcast_cnt_overflow)},
+ {" Multicast Cnt Overflowed", VMAC_STAT_OFFSET(mcast_cnt_overflow)},
+ {" Pause Cnt Overflowed", VMAC_STAT_OFFSET(pause_cnt_overflow)},
+ {" CRC Error Cnt Overflowed", VMAC_STAT_OFFSET(pause_cnt_overflow)},
+ {" Rx Drop Byte Cnt Overflowed", VMAC_STAT_OFFSET(rx_drop_byte_cnt_overflow)},
+ {" Rx Drop Frame Cnt Overflowed", VMAC_STAT_OFFSET(rx_drop_frame_cnt_overflow)},
+ {" Rx Frame Ints ", VMAC_STAT_OFFSET(frame_rx)},
+ {" # Frames Transmitted", VMAC_STAT_OFFSET(tx_frame_cnt)},
+ {" # Bytes Transmitted", VMAC_STAT_OFFSET(tx_byte_cnt)},
+ {" # Frames Received", VMAC_STAT_OFFSET(rx_frame_cnt)},
+ {" # Bytes Received", VMAC_STAT_OFFSET(rx_byte_cnt)},
+ {" # Rx Frames Dropped", VMAC_STAT_OFFSET(rx_drop_frame_cnt)},
+ {" # Rx Bytes Dropped", VMAC_STAT_OFFSET(rx_drop_byte_cnt)},
+ {" # Rx CRC Error Frames", VMAC_STAT_OFFSET(rx_crc_cnt)},
+ {" # Rx Pause Frames", VMAC_STAT_OFFSET(rx_pause_cnt)},
+ {" # Rx Broadcast Frames", VMAC_STAT_OFFSET(rx_bcast_fr_cnt)},
+ {" # Rx Multicast Frames", VMAC_STAT_OFFSET(rx_mcast_fr_cnt)}
+};
+#define HXGE_VMAC_STATS_CNT (sizeof(hxge_vmac_stats)/sizeof(struct hxge_stats_struct))
+
+wait_queue_head_t ethtool_evnt;
+volatile int ethtool_cond = 0;
+struct sk_buff *ethtool_skb;
+
+
+static int
+hxge_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
+{
+ cmd->supported = SUPPORTED_FIBRE;
+ cmd->advertising = ADVERTISED_FIBRE;
+ cmd->port = PORT_FIBRE;
+ cmd->transceiver = XCVR_EXTERNAL;
+ cmd->speed = SPEED_10000;
+ cmd->duplex = DUPLEX_FULL;
+
+ return 0;
+}
+
+
+static void
+hxge_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info)
+{
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+
+ strncpy(info->driver, HXGE_DRIVER_NAME, strlen(HXGE_DRIVER_NAME));
+ strncpy(info->version, hxge_driver_version, strlen(hxge_driver_version));
+ strncpy(info->fw_version, "N/A", strlen("N/A"));
+ strncpy(info->bus_info, pci_name(hxgep->pdev), strlen(pci_name(hxgep->pdev)));
+ info->testinfo_len = HXGE_TEST_LEN;
+}
+
+
+static struct sk_buff *create_lb_frame(loopback_params_t *p, int buffer_size)
+{
+ int i;
+ struct sk_buff *skb;
+ uint8_t *ptr;
+ int pkt_len = p->pkt_len;
+
+ skb = dev_alloc_skb(pkt_len);
+ if (!skb) {
+ HXGE_DBG_PRINT("Failed to allocate skb");
+ return NULL;
+ }
+
+ /* Abusing the priority field for my own devious ends.. */
+ skb->priority = p->bad_len;
+ memset(skb_put(skb, pkt_len), 0xFF, pkt_len);
+
+ ptr = skb->data;
+ for (i = 0; i < PATT_LEN; i++)
+ ptr[i] = pattern[i];
+
+ return skb;
+}
+
+static int good_pkt(struct sk_buff *skb, int pkt_len)
+{
+ uint8_t *data = (uint8_t *)skb->data;
+ int i;
+
+ for (i = 0; i < PATT_LEN; i++)
+ if (data[i] != pattern[i])
+ return 0;
+
+ while (i < pkt_len)
+ if (data[i++] != 0xFF) {
+ HXGE_DBG_PRINT("failed at byte %d",i);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int hxge_send_lb_pkts(struct hxge_adapter *hxgep,
+ loopback_params_t *param)
+{
+ struct tx_ring_t *tx_ring;
+ int i, j, buffer_size;
+ int num_pkts_sent = 0;
+ int pkts_to_send;
+ struct sk_buff *skb;
+
+ pkts_to_send = param->num_pkts;
+ for (i = 0; i < hxgep->max_tdcs; i++) {
+ tx_ring = &hxgep->tx_ring[i];
+ if (pkts_to_send <= 0)
+ pkts_to_send = tx_ring->num_tdrs;
+ buffer_size = tx_ring->tx_buffer_size;
+ for (j = 0; j < pkts_to_send; j++) {
+ skb = create_lb_frame(param, buffer_size);
+ if (!skb)
+ return -1;
+ hxge_start_xmit(skb, hxgep->netdev);
+ num_pkts_sent++;
+ }
+ }
+ HXGE_DBG(hxgep, "hxge_send_lb_pkts: %d Packets sent", num_pkts_sent);
+ return num_pkts_sent;
+
+}
+
+
+/* Process packets that are received. Instead of sending them to the linux
+ network stack, hxge_process_packets link up the skb's and sends it to us.
+ We free the skb's after validating the packet
+*/
+static int hxge_receive_and_validate_lb_pkts(struct hxge_adapter *hxgep, loopback_params_t *param)
+{
+ int i;
+ int pkts_rcvd, tot_pkts_rcvd = 0;
+ struct sk_buff *ptr, *skb;
+ int failed = 0;
+ int pkts_freed=0;
+ int retval;
+ int mismatch = 0;
+
+
+ /* If polling, then we have to explicity call the receive function
+ to collect the packets. In interrupt case, we will get an event
+ signalling packets have arrived */
+
+ if (param->intr_type != POLLING_TYPE) {
+ HXGE_DBG(hxgep, "Waiting to receive packet..%d", ethtool_cond);
+ retval = wait_event_interruptible_timeout(ethtool_evnt,
+ !ethtool_cond, 5000);
+ if (!retval) {
+ HXGE_DBG(hxgep, "Timeout out waiting for pkt");
+ }
+ else if (retval < 0) {
+ HXGE_DBG(hxgep, "Got interrupted - failing");
+ }
+ else {
+ HXGE_DBG(hxgep, "Received all packet");
+ }
+
+ if (ethtool_cond) {
+ HXGE_DBG(hxgep, "Did not get all the pkts");
+ failed = -1;
+ }
+ } else {
+ for (i = 0; i < hxgep->max_rdcs; i++) {
+ while ((pkts_rcvd = hxge_process_packets(hxgep,
+#ifdef CONFIG_HXGE_NAPI
+ param->tx_descs,
+#endif
+ i, ðtool_skb)) > 0)
+ tot_pkts_rcvd += pkts_rcvd;
+ if (pkts_rcvd < 0) {
+ HXGE_DBG(hxgep, "hxge_process_packets failed");
+ return -1;
+ }
+ else if (!tot_pkts_rcvd)
+ {
+ HXGE_DBG(hxgep, "No packets received. Problem with sending?");
+ return -1;
+ }
+ else
+ HXGE_DBG(hxgep, "%d packets received",tot_pkts_rcvd);
+ }
+ }
+
+ skb = ethtool_skb;
+ while (skb != NULL) {
+ if (!good_pkt(skb, param->pkt_len))
+ mismatch = 1;
+ ptr = skb;
+ skb = skb->next;
+ dev_kfree_skb_any(ptr);
+ pkts_freed++;
+ }
+
+ HXGE_DBG(hxgep, "%d Packets Freed",pkts_freed);
+ if (!param->bad_len && failed) {
+ if (mismatch) {
+ HXGE_DBG(hxgep, "Packet(s) did not match! Failing test");
+ } else {
+ HXGE_DBG(hxgep, "Receive failed");
+ }
+ } else if (param->bad_len)
+ failed = 0;
+
+ return failed;
+}
+
+static int hxge_setup_descs(struct hxge_adapter *hxgep, loopback_params_t *p)
+{
+
+ /* Allocate Tx and Rx descriptors */
+ if (hxge_alloc_tx(hxgep)) {
+ HXGE_DBG(hxgep, "hxge_setup_descs: Failed hxge_alloc_tx");
+ return -1;
+ }
+
+ if (hxge_alloc_rx(hxgep)) {
+ HXGE_DBG(hxgep, "hxge_setup_descs: Failed hxge_alloc_rx");
+ return -1;
+ }
+
+ /* Setup interrupts if needed */
+ if (hxge_setup_interrupt(hxgep->netdev)) {
+ HXGE_DBG(hxgep, "hxge_setup_interrupt failed");
+ return -1;
+ }
+
+ init_waitqueue_head(ðtool_evnt);
+
+ hxge_enable_adapter(hxgep);
+
+ return 0;
+}
+
+int hxge_free_descs(struct hxge_adapter *hxgep, loopback_params_t *p)
+{
+
+ hxge_disable_adapter(hxgep);
+
+ hxge_teardown_interrupt(hxgep);
+
+ hxge_free_tx(hxgep);
+ hxge_free_rx(hxgep);
+
+ return 0;
+}
+
+
+static int hxge_run_loopback_test(struct hxge_adapter *hxgep,
+ loopback_params_t *param)
+{
+ int pkts_sent;
+
+ ethtool_cond = param->num_pkts * param->tx_channels;
+ ethtool_skb = NULL;
+
+ /* Setup the Tx descriptor packets */
+ if ((pkts_sent = hxge_send_lb_pkts(hxgep, param)) <= 0) {
+ HXGE_DBG(hxgep, "hxge_send_lb_pkts failed. Packets not sent.");
+ return -1;
+ }
+
+ HXGE_DBG(hxgep, "Sleeping for 1 second before processing RX...");
+ msleep(1000); /* sleep for 2 ms before processing Rx */
+
+ /* Receive the lb packets and validate them */
+ if (hxge_receive_and_validate_lb_pkts(hxgep, param)) {
+ HXGE_DBG(hxgep, "hxge_receive_and_validate_lb_pkts failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* Reset the adapter without involving any OS structures */
+static void hxge_reset(struct hxge_adapter *hxgep)
+{
+ int i;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+
+
+ hxge_block_reset(hxgep, (LDV_RXDMA | LDV_TXDMA | LDV_VMAC));
+ for ( i = 0; i < hxgep->max_rdcs; i++)
+ hpi_rxdma_cfg_rdc_reset(handle, i);
+ for (i = 0; i < hxgep->max_tdcs; i++)
+ hpi_txdma_channel_reset(handle, i);
+ hpi_tx_vmac_reset(handle);
+ hpi_rx_vmac_reset(handle);
+}
+
+
+
+int configure_driver_and_card(struct hxge_adapter *hxgep,
+ loopback_params_t *param)
+{
+ uint64_t config = 0;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+
+ if ((hxge_set_option("intr_type", param->intr_type) < 0) ||
+ (hxge_set_option("num_tx_descs", param->tx_descs) < 0) ||
+ (hxge_set_option("tx_dma_channels", param->tx_channels) < 0) ||
+ (hxge_set_option("rx_dma_channels", param->rx_channels) < 0) ||
+ (hxge_set_option("rcr_entries", param->rcr_entries) < 0) ||
+ (hxge_set_option("rbr_entries", param->rbr_entries) < 0) ||
+ (hxge_set_option("rcr_threshold", param->rcr_threshold) < 0) ||
+ (hxge_set_option("rcr_timeout", param->rcr_timeout) < 0))
+ return -1;
+
+ hxge_reset(hxgep);
+
+ /* Set up descriptors. Going to poll for Rx packets; no interrupts
+ enabled here */
+ if (hxge_setup_descs(hxgep,param)) {
+ HXGE_DBG(hxgep, "configure_driver_and_card: Setting up descs failed");
+ return -1;
+ }
+
+ /* Set the adapter in loopback mode now. Make sure that the STRIP_CRC
+ is disabled due to a HW bug */
+ config = CFG_VMAC_RX_PROMISCUOUS_MODE | CFG_VMAC_RX_EN |
+ CFG_VMAC_RX_PROMISCUOUS_GROUP | CFG_VMAC_RX_LOOP_BACK;
+ if (hpi_vmac_rx_config(handle, INIT, config, 0) == HPI_FAILURE) {
+ HXGE_DBG(hxgep, "configure_driver_and_card: Could not configure VMAC Rx");
+ goto free_descs;
+ }
+
+ config = CFG_VMAC_TX_EN | CFG_VMAC_TX_CRC_INSERT;
+ if (hpi_vmac_tx_config(handle, INIT, config, 0) == HPI_FAILURE) {
+ HXGE_DBG(hxgep, "configure_driver_and_card: Could not configure VMAC Tx");
+ goto free_descs;
+ }
+
+ return 0;
+
+free_descs:
+ hxge_free_descs(hxgep,param);
+ return -1;
+}
+
+static int deconfigure_driver_and_card(struct hxge_adapter *hxgep,
+ loopback_params_t *p)
+{
+ uint64_t config = 0;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+
+ flush_scheduled_work();
+
+ config = 0;
+ hpi_vmac_rx_config(handle, INIT, config, 0);
+
+ hxge_free_descs(hxgep,p);
+
+ return 0;
+}
+
+static int hxge_loopback_test(struct hxge_adapter *hxgep, loopback_params_t *p)
+{
+ int failed = 0;
+
+ if (configure_driver_and_card(hxgep, p)) {
+ HXGE_DBG(hxgep, "hxge_loopback_test: failed to configure device"); return -1;
+ }
+
+ if (hxge_run_loopback_test(hxgep, p)) {
+ HXGE_DBG(hxgep, "hxge_loopback_test: Loopback Test failed");
+ failed = -1;
+ }
+
+ deconfigure_driver_and_card(hxgep, p);
+
+ return failed;
+}
+
+static void hxge_diag_test(struct net_device *netdev,
+ struct ethtool_test *eth_test, uint64_t *data)
+{
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+ boolean_t if_running = netif_running(netdev);
+ int link_up = hxge_peu_get_link_status(hxgep);
+ int i;
+ loopback_params_t *param;
+ loopback_params_t orig_params;
+ char *str;
+ int num_tests;
+
+ num_tests = sizeof(loopback_params)/sizeof(loopback_params_t);
+ for (i = 0, param = loopback_params; i < num_tests; i++)
+ {
+ str = strstr(hxge_gstrings_test[i], "=");
+ if (!str) {
+ HXGE_ERR(hxgep, "Error in test strings construct");
+ return;
+ }
+ str += 2; /* skip = and a space */
+ strncpy(str, "NOTRUN", strlen("NOTRUN"));
+ }
+
+ for (i = 0; i < num_tests; i++)
+ data[i] = 0;
+
+ /* These are offline tests */
+ if (eth_test->flags == ETH_TEST_FL_OFFLINE)
+ HXGE_DBG(hxgep, "hxge_diag_test: Offline test starting");
+
+ set_bit(HXGE_DEVICE_TESTING, &hxgep->state);
+
+ /* Close the device before running this offline test */
+ if (if_running) {
+ HXGE_ERR(hxgep, "hxge_diag_test: Cannot run offline test on a running interface. Bring interface down before attempting offline tests!");
+ eth_test->flags |= ETH_TEST_FL_FAILED;
+ return;
+ }
+
+ if (link_up) {
+ HXGE_ERR(hxgep, "hxge_diag_test: Link should be down for offline tests");
+ eth_test->flags |= ETH_TEST_FL_FAILED;
+ return;
+ }
+
+
+ if ((hxge_get_option("intr_type", &orig_params.intr_type) < 0) ||
+ (hxge_get_option("num_tx_descs",&orig_params.tx_descs) < 0) ||
+ (hxge_get_option("tx_dma_channels", &orig_params.tx_channels) < 0) ||
+ (hxge_get_option("rx_dma_channels", &orig_params.rx_channels) < 0) ||
+ (hxge_get_option("rcr_entries", &orig_params.rcr_entries) < 0) ||
+ (hxge_get_option("rbr_entries", &orig_params.rbr_entries) < 0) ||
+ (hxge_get_option("rcr_threshold", &orig_params.rcr_threshold) < 0) ||
+ (hxge_get_option("rcr_timeout", &orig_params.rcr_timeout) < 0))
+ {
+ eth_test->flags |= ETH_TEST_FL_FAILED;
+ return;
+ }
+
+
+
+ for (i = 0, param = loopback_params; i < num_tests; i++)
+ {
+ str = strstr(hxge_gstrings_test[i], "=");
+ if (!str) {
+ HXGE_ERR(hxgep, "Error in test strings construct");
+ return;
+ }
+ str += 2; /* skip = and a space */
+ HXGE_DBG(hxgep, "*** LOOPBACK TEST %d", i);
+ if (hxge_loopback_test(hxgep, ¶m[i])) {
+ eth_test->flags |= ETH_TEST_FL_FAILED;
+ strncpy(str, "FAILED", strlen("FAILED"));
+ break;
+ }
+ /* Replace FAILED with PASSED */
+ strncpy(str, "PASSED", strlen("PASSED"));
+ data[i] = 1;
+ }
+
+ /* restore parameters to original value */
+ hxge_set_option("rbr_entries", orig_params.rbr_entries);
+ hxge_set_option("rcr_entries", orig_params.rcr_entries);
+ hxge_set_option("rx_dma_channels", orig_params.rx_channels);
+ hxge_set_option("tx_dma_channels", orig_params.tx_channels);
+ hxge_set_option("num_tx_descs", orig_params.tx_descs);
+ hxge_set_option("intr_type", orig_params.intr_type);
+ hxge_set_option("rcr_threshold", orig_params.rcr_threshold);
+ hxge_set_option("rcr_timeout", orig_params.rcr_timeout);
+
+ clear_bit(HXGE_DEVICE_TESTING, &hxgep->state);
+}
+
+static void
+hxge_get_strings(struct net_device *netdev, uint32_t stringset, uint8_t *data)
+{
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+ int i, j, offset = 0;
+
+ switch (stringset) {
+ case ETH_SS_TEST:
+ memcpy(data, hxge_gstrings_test,
+ HXGE_TEST_LEN*ETH_GSTRING_LEN);
+ break;
+ case ETH_SS_STATS:
+ for (i = 0; i < hxgep->max_rdcs; i++)
+ for (j = 0; j < HXGE_RDC_STATS_CNT; j++) {
+ memcpy(&data[offset],
+ hxge_rdc_stats[j].name, ETH_GSTRING_LEN);
+ offset += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < hxgep->max_tdcs; i++)
+ for (j = 0; j < HXGE_TDC_STATS_CNT; j++) {
+ memcpy(&data[offset],
+ hxge_tdc_stats[j].name, ETH_GSTRING_LEN);
+ offset += ETH_GSTRING_LEN;
+ }
+ for (j = 0; j < HXGE_PFC_STATS_CNT; j++) {
+ memcpy(&data[offset],
+ hxge_pfc_stats[j].name, ETH_GSTRING_LEN);
+ offset += ETH_GSTRING_LEN;
+ }
+ for (j = 0; j < HXGE_VMAC_STATS_CNT; j++) {
+ memcpy(&data[offset],
+ hxge_vmac_stats[j].name, ETH_GSTRING_LEN);
+ offset += ETH_GSTRING_LEN;
+ }
+ break;
+ default: HXGE_ERR(hxgep, "hxge_get_strings: Unsupported type");
+ break;
+
+ }
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0)
+static int
+hxge_diag_test_count(struct net_device *netdev)
+{
+ return HXGE_TEST_LEN;
+}
+#endif
+
+static u32 hxge_get_tx_csum(struct net_device *netdev)
+{
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+ return ((hxgep->flags & HXGE_TX_CHKSUM_ENABLED) != 0);
+}
+
+static int hxge_set_tx_csum (struct net_device *netdev, u32 data)
+{
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+ int i;
+
+ if (data)
+ {
+ if (hxgep->flags & HXGE_TX_CHKSUM_ENABLED)
+ return 0;
+
+ hxgep->flags |= HXGE_TX_CHKSUM_ENABLED;
+ netdev->features |= NETIF_F_IP_CSUM;
+ }
+ else
+ {
+ if (!(hxgep->flags & HXGE_TX_CHKSUM_ENABLED))
+ return 0;
+
+ hxgep->flags &= ~HXGE_TX_CHKSUM_ENABLED;
+ /* Both chksum flags need to disabled for HW to be disabled */
+ if (!(hxgep->flags & HXGE_CHKSUM_ENABLED))
+ netdev->features &= ~NETIF_F_IP_CSUM;
+ }
+
+ for (i = 0; i < hxgep->max_tdcs; i++)
+ hxge_reset_tx_channel(hxgep, i);
+ return 0;
+}
+
+static u32 hxge_get_rx_csum(struct net_device *netdev)
+{
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+ return ((hxgep->flags & HXGE_RX_CHKSUM_ENABLED) != 0);
+}
+
+
+static int hxge_set_rx_csum(struct net_device *netdev, u32 data)
+{
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+ int i;
+
+ if (data)
+ {
+ if (hxgep->flags & HXGE_RX_CHKSUM_ENABLED)
+ return 0;
+
+ hxgep->flags |= HXGE_RX_CHKSUM_ENABLED;
+ netdev->features |= NETIF_F_IP_CSUM;
+ }
+ else
+ {
+ if (!(hxgep->flags & HXGE_RX_CHKSUM_ENABLED))
+ return 0;
+
+ hxgep->flags &= ~HXGE_RX_CHKSUM_ENABLED;
+ netdev->features &= ~NETIF_F_IP_CSUM;
+ }
+
+ for (i = 0; i < hxgep->max_rdcs; i++)
+ hxge_reset_rx_channel(hxgep, i);
+
+ return 0;
+}
+
+static int hxge_get_stats_count(struct net_device *netdev)
+{
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+ int stats_count;
+
+ stats_count = (hxgep->max_rdcs * HXGE_RDC_STATS_CNT);
+ stats_count += (hxgep->max_tdcs * HXGE_TDC_STATS_CNT);
+ stats_count += HXGE_PFC_STATS_CNT;
+ stats_count += HXGE_VMAC_STATS_CNT;
+ return (stats_count);
+}
+
+
+static void hxge_get_ethtool_stats(struct net_device *netdev,
+ struct ethtool_stats *stats, uint64_t *data)
+{
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+ struct rx_ring_t *rx_ring;
+ struct tx_ring_t *tx_ring;
+ int i, j, offset = 0;
+ uint32_t stat;
+ p_hxge_stats_t statsp = hxgep->statsp;
+ p_hxge_pfc_stats_t pfc_statsp;
+ p_hxge_vmac_stats_t vmac_statsp;
+
+ /* None of the data structures are allocated yet; so, nothing to
+ show yet */
+ if (!test_bit(HXGE_DEVICE_ALLOCATED, &hxgep->state)) {
+ memset(data, 0, hxge_get_stats_count(netdev)*sizeof(uint64_t));
+ return;
+ }
+
+ for (i = 0; i < hxgep->max_rdcs; i++) {
+ rx_ring = &hxgep->rx_ring[i];
+ data[offset++] = rx_ring->rdc;
+ for (j = 1; j < HXGE_RDC_STATS_CNT; j++)
+ data[offset++] = *(uint64_t *)((char *)&rx_ring->stats + hxge_rdc_stats[j].offset);
+ }
+
+ for (i = 0; i < hxgep->max_tdcs; i++) {
+ tx_ring = &hxgep->tx_ring[i];
+ data[offset++] = tx_ring->tdc;
+ for (j = 1; j < HXGE_TDC_STATS_CNT; j++)
+ data[offset++] = *(uint64_t *)((char *)&tx_ring->stats + hxge_tdc_stats[j].offset);
+ }
+
+ pfc_statsp = &statsp->pfc_stats;
+ data[offset++] = 0;
+ for (j = 1; j < HXGE_PFC_STATS_CNT; j++) {
+ stat = *(uint32_t *)((char *)pfc_statsp + hxge_pfc_stats[j].offset);
+ data[offset++] = (uint64_t)stat;
+ }
+
+ vmac_statsp = &statsp->vmac_stats;
+ data[offset++] = 0;
+ for (j = 1; j < HXGE_VMAC_STATS_CNT; j++)
+ data[offset++]= *(uint64_t *)((char *)vmac_statsp + hxge_vmac_stats[j].offset);
+}
+
+static uint32_t hxge_get_msglevel(struct net_device *netdev)
+{
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+ return (hxgep->msg_enable);
+}
+
+static void hxge_set_msglevel(struct net_device *netdev, uint32_t data)
+{
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+ hxgep->msg_enable = data;
+}
+
+static int
+hxge_get_coalesce_intr(struct net_device *dev,
+ struct ethtool_coalesce *intr_param)
+{
+ struct hxge_adapter *hxgep = netdev_priv(dev);
+
+ /* 250MHz clock that is divided down by HXGE_RCR_CLK_RESO value. So,
+ 1 tick = 0.004 usec */
+
+ intr_param->rx_coalesce_usecs = /* 32-bit safe integer expression */
+ hxgep->rcr_timeout * ((4 * HXGE_RCR_CLK_RESO)/1000);
+ intr_param->rx_max_coalesced_frames = hxgep->rcr_threshold;
+ intr_param->rx_max_coalesced_frames_irq = hxgep->max_rx_pkts;
+ intr_param->use_adaptive_rx_coalesce = hxgep->adaptive_rx;
+ return 0;
+}
+
+static int
+hxge_set_coalesce_intr(struct net_device *dev,
+ struct ethtool_coalesce *intr_param)
+{
+ struct hxge_adapter *hxgep = netdev_priv(dev);
+ int i;
+
+ /* Illegal to have both set to zero as this would disable both the
+ threshold and timeout mechanisms resulting in no Rx interrupt
+ generation */
+ if ((intr_param->rx_max_coalesced_frames == 0) &&
+ (intr_param->rx_coalesce_usecs == 0)) {
+ return (1);
+ }
+ if ((intr_param->rx_max_coalesced_frames_irq < HXGE_MAX_RX_PKTS_MIN) ||
+ (intr_param->rx_max_coalesced_frames_irq > HXGE_MAX_RX_PKTS_MAX))
+ return (1);
+
+ if (intr_param->rx_max_coalesced_frames > HXGE_RCR_THRESHOLD_MAX)
+ return (1);
+
+ spin_lock(&hxgep->lock);
+
+ hxgep->adaptive_rx = intr_param->use_adaptive_rx_coalesce;
+ if (intr_param->rx_coalesce_usecs) {
+ hxgep->rcr_timeout = /* 32-bit safe arithmetic */
+ (uint32_t)(intr_param->rx_coalesce_usecs
+ / ((4 * HXGE_RCR_CLK_RESO)/1000));
+ hxgep->rcr_cfgb_cpy = RCR_CFGB_ENABLE_TIMEOUT | hxgep->rcr_timeout;
+ } else {
+ hxgep->rcr_timeout= 0;
+ hxgep->rcr_cfgb_cpy = 0;
+ }
+
+ hxgep->rcr_threshold = intr_param->rx_max_coalesced_frames;
+ hxgep->max_rx_pkts = intr_param->rx_max_coalesced_frames_irq;
+
+ for (i = 0; i < hxgep->max_rdcs; i++) {
+ struct rx_ring_t *rx_ring = &hxgep->rx_ring[i];
+ if (test_bit(RING_ENABLED, &rx_ring->state))
+ RXDMA_REG_WRITE64(hxgep->hw.hw_addr, RDC_RCR_CFG_B, i, hxgep->rcr_threshold << 16 | hxgep->rcr_cfgb_cpy);
+ }
+
+ spin_unlock(&hxgep->lock);
+ return 0;
+}
+
+static struct ethtool_ops hxge_ethtool_ops = {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0)
+ .self_test_count = hxge_diag_test_count,
+ .get_stats_count = hxge_get_stats_count,
+#endif
+ .get_drvinfo = hxge_get_drvinfo,
+ .get_settings = hxge_get_settings,
+ .self_test = hxge_diag_test,
+ .get_strings = hxge_get_strings,
+ .get_tx_csum = hxge_get_tx_csum,
+ .set_tx_csum = hxge_set_tx_csum,
+ .get_rx_csum = hxge_get_rx_csum,
+ .set_rx_csum = hxge_set_rx_csum,
+ .get_ethtool_stats = hxge_get_ethtool_stats,
+ .get_msglevel = hxge_get_msglevel,
+ .set_msglevel = hxge_set_msglevel,
+ .get_tso = ethtool_op_get_tso,
+ .set_tso = ethtool_op_set_tso,
+ .get_coalesce = hxge_get_coalesce_intr,
+ .set_coalesce = hxge_set_coalesce_intr,
+ .get_link = ethtool_op_get_link
+};
+
+
+void hxge_set_ethtool_ops(struct net_device *netdev)
+{
+ SET_ETHTOOL_OPS(netdev, &hxge_ethtool_ops);
+
+}
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#include "hpi/hpi_vir.h"
+#include "hpi/hpi_rxdma.h"
+#include "hxge.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+extern irqreturn_t hxge_pfc_intr(int irq, void *data, struct pt_regs *regs);
+extern irqreturn_t hxge_vmac_intr(int irq, void *data, struct pt_regs *regs);
+extern irqreturn_t hxge_rx_intr(int irq, void *data, struct pt_regs *regs);
+extern irqreturn_t hxge_rx_deverr_intr(int irq, void *data, struct pt_regs *regs);
+extern irqreturn_t hxge_tx_intr(int irq, void *data, struct pt_regs *regs);
+extern irqreturn_t hxge_tx_deverr_intr(int irq, void *data, struct pt_regs *regs);
+extern irqreturn_t hxge_peu_deverr_intr(int irq, void *data, struct pt_regs *regs);
+#else
+extern irqreturn_t hxge_pfc_intr(int irq, void *data);
+extern irqreturn_t hxge_vmac_intr(int irq, void *data);
+extern irqreturn_t hxge_rx_intr(int irq, void *data);
+extern irqreturn_t hxge_rx_deverr_intr(int irq, void *data);
+extern irqreturn_t hxge_tx_intr(int irq, void *data);
+extern irqreturn_t hxge_tx_deverr_intr(int irq, void *data);
+extern irqreturn_t hxge_peu_deverr_intr(int irq, void *data);
+#endif
+
+extern int hxge_get_option(const char *str, int *val);
+
+static void hxge_dump_ints(struct hxge_adapter *hxgep);
+static void hxge_enable_ldg_ints(struct hxge_ldg *ldgp);
+void hxge_teardown_interrupt(struct hxge_adapter *hxgep);
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static irqreturn_t hxge_deverr_intr (int irq, void *data, struct pt_regs *regs)
+#else
+static irqreturn_t hxge_deverr_intr (int irq, void *data)
+#endif
+{
+ struct hxge_ldv *ldvp = (struct hxge_ldv *)data;
+ struct hxge_adapter *hxgep = ldvp->ldgp->hxgep;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ dev_err_stat_t dsts;
+ hpi_status_t hsts;
+
+ dsts.value = 0;
+ hsts = hpi_fzc_sys_err_stat_get(handle, &dsts);
+ if (hsts != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hxge_deverr_intr: Can't read DEV_ERR_STAT register");
+ /* Not clear what to do now...probably go down in flames...
+ * fake up a DEV_ERR_STAT with lotsa bits set, and see if
+ * the individual handlers have anything to report. We
+ * should probably down/reset the whole device.
+ */
+ dsts.value = 0xF; /* TDC/RDC/PEU/VMAC "errors" */
+ }
+
+ if (!dsts.value) {
+ HXGE_ERR(hxgep, "hxge_deverr_intr: DEV_ERR_STAT register empty:");
+ return (IRQ_NONE);
+ }
+
+ HXGE_DBG(hxgep, "hxge_deverr_intr: Device Error Interrupt! (0x%8.8x)",
+ dsts.value);
+
+ /* Look for TX, RX, or general (VMAC/PEU) and "dispatch" */
+
+ if (dsts.bits.tdc_err0 || dsts.bits.tdc_err1) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+ hxge_tx_deverr_intr(irq, ldvp, regs);
+#else
+ hxge_tx_deverr_intr(irq, ldvp);
+#endif
+ dsts.bits.tdc_err0 = 0;
+ dsts.bits.tdc_err1 = 0;
+ }
+
+ if (dsts.bits.rdc_err0 || dsts.bits.rdc_err1) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+ hxge_rx_deverr_intr(irq, ldvp, regs);
+#else
+ hxge_rx_deverr_intr(irq, ldvp);
+#endif
+ dsts.bits.rdc_err0 = 0;
+ dsts.bits.rdc_err1 = 0;
+ }
+
+ if (dsts.bits.vnm_pio_err1 || dsts.bits.peu_err1) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+ hxge_peu_deverr_intr(irq, ldvp, regs);
+#else
+ hxge_peu_deverr_intr(irq, ldvp);
+#endif
+ dsts.bits.vnm_pio_err1 = 0;
+ dsts.bits.peu_err1 = 0;
+ }
+
+ if (dsts.value) {
+ HXGE_ERR(hxgep, "hxge_deverr_intr: Unexpected/unknown DEV_ERR_STAT flags: %8.8x", dsts.value);
+ }
+
+ return (IRQ_HANDLED);
+}
+
+/**
+ * hxge_intr - Interrupt Handler
+ * @irq: interrupt number
+ * @data: pointer to a network interface device structure
+ * @pt_regs: CPU registers structure
+ **/
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static irqreturn_t
+hxge_intr(int irq, void *data, struct pt_regs *regs)
+#else
+static irqreturn_t
+hxge_intr(int irq, void *data)
+#endif
+{
+ struct hxge_ldg *ldgp = (struct hxge_ldg *)data;
+ struct hxge_ldv *ldvp;
+ irqreturn_t status = IRQ_NONE;
+ int ldf0, ldf1;
+
+
+ list_for_each_entry(ldvp, &ldgp->ldv_list, ldg_list) {
+
+ /* Check if there is an interrupt for this device */
+ get_ldf_flags(ldvp, &ldf0, &ldf1);
+ if (!ldf0 && !ldf1)
+ continue;
+
+ /* We're banking on the fact that IRQ_NONE is zero; otherwise
+ this neat trick won't work! */
+ switch (ldvp->dev_type) {
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+ case LDV_TXDMA :
+ status |= hxge_tx_intr(irq, ldvp, regs);
+ break;
+ case LDV_VMAC :
+ status |= hxge_vmac_intr(irq, ldvp, regs);
+ break;
+ case LDV_PFC :
+ status |= hxge_pfc_intr(irq, ldvp, regs);
+ break;
+ case LDV_DEVERR :
+ status |= hxge_deverr_intr(irq, ldvp, regs);
+ break;
+ case LDV_RXDMA :
+ status |= hxge_rx_intr(irq, ldvp, regs);
+ break;
+#else
+ case LDV_TXDMA :
+ status |= hxge_tx_intr(irq, ldvp);
+ break;
+ case LDV_VMAC :
+ status |= hxge_vmac_intr(irq, ldvp);
+ break;
+ case LDV_PFC :
+ status |= hxge_pfc_intr(irq, ldvp);
+ break;
+ case LDV_DEVERR :
+ status |= hxge_deverr_intr(irq, ldvp);
+ break;
+ case LDV_RXDMA :
+ status |= hxge_rx_intr(irq, ldvp);
+ break;
+#endif
+
+ default : HXGE_ERR_PRINT("hxge_intr: Unknown device %d", ldvp->ldv);
+ status = IRQ_HANDLED;
+ }
+ }
+
+ /* Enable interrutps for this logical device group */
+ if (status == IRQ_HANDLED)
+ hxge_enable_ldg_ints(ldgp);
+
+ return status;
+}
+
+
+
+/* Basic utility routine that enables or disables interrupts for the
+ devices (blocks) within hydra that are supported */
+static int hxge_interrupt_mgmt(struct hxge_adapter *hxgep, int enable_int,
+ int mask_int, int dev_type,
+ struct hxge_ldg *dev, int channel)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ struct hxge_ldv *ldvp;
+ struct hxge_ldg *ldgp;
+ int status;
+ int arm = FALSE;
+ uint8_t masks = (uint8_t)LD_IM_MASK;
+
+
+ if (hxgep->intr_type == POLLING_TYPE)
+ return 0;
+
+ /* If interrutps are enabled, then
+ a) arm the logical device groups
+ b) Enable the timer as well
+ */
+ if (enable_int)
+ arm = TRUE;
+
+ /* If mask_int is not true, then clear the LDF masks for the devices */
+ if (!mask_int)
+ masks = 0;
+
+ list_for_each_entry(ldgp, &hxgep->ldgvp->ldgp, list) {
+ if (dev && (dev != ldgp))
+ continue;
+ if (mask_int >= 0) {
+ list_for_each_entry(ldvp, &ldgp->ldv_list, ldg_list) {
+ if (ldvp->dev_type & dev_type) {
+ if (channel >= 0) {
+ if (dev_type == LDV_RXDMA)
+ channel += HXGE_RDMA_LD_START;
+ else if (dev_type == LDV_TXDMA)
+ channel += HXGE_TDMA_LD_START;
+ if (channel != ldvp->ldv)
+ continue;
+ }
+ ldvp->ldf_masks = masks;
+ status = hpi_intr_mask_set(handle,
+ ldvp->ldv, masks);
+ if (status != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_intr_mask_set failed");
+ return -1;
+ }
+ }
+ }
+ }
+ if (enable_int >= 0) {
+ ldgp->arm = arm;
+ status = hpi_intr_ldg_mgmt_set(handle,
+ ldgp->ldg, arm, ldgp->timer);
+ if (status != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_intr_ldg_mgmt_set failed");
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+void get_ldf_flags(struct hxge_ldv *ldvp, int *ldf0, int *ldf1)
+{
+ hpi_handle_t handle = ldvp->ldgp->hxgep->hw.hw_addr;
+ uint32_t vector0, vector1;
+
+ *ldf0 = 0;
+ *ldf1 = 0;
+
+ if (hpi_ldsv_ldfs_get(handle, ldvp->ldgp->ldg, &vector0, &vector1)
+ != HPI_SUCCESS) {
+ HXGE_ERR_PRINT("get_ldf_flags: hpi_ldsv_ldfs_get failed");
+ }
+
+ /* Only check for regular LDF0 interrupt. LDF1 implies error and
+ is handled in a separate context. Also, LDF0 implies that the
+ RCR Threshold and/or RCR Timeout bits in the RDC control/status
+ register is set */
+ *ldf0 = ((vector0 & (1 << ldvp->ldv)) != 0);
+
+ /* LDF1 indicates fatal error happened. Have to parse the RDC control
+ register for the exact error(s) */
+ *ldf1 = ((vector1 & (1 << ldvp->ldv)) != 0);
+}
+
+
+void hxge_enable_interrupts(struct hxge_adapter *hxgep)
+{
+ hxge_interrupt_mgmt(hxgep, 1, 0, LDV_ALL, NULL, -1);
+}
+
+void hxge_disable_interrupts(struct hxge_adapter *hxgep)
+{
+ hxge_interrupt_mgmt(hxgep, 0, 1, LDV_ALL, NULL, -1);
+}
+
+void hxge_enable_rx_ints(struct hxge_adapter *hxgep, struct hxge_ldg *dev,
+ int rdc)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ struct hxge_ldv *ldvp;
+ struct hxge_ldg *ldgp;
+ rdc_stat_t cs;
+ int channel;
+
+ if (hxgep->intr_type == POLLING_TYPE)
+ return;
+
+ /* For Rx devices, re-enable the mailbox interrupt
+ and couple of RCR bits. These are 1-shot and potentially need
+ to be reset */
+ list_for_each_entry(ldgp, &hxgep->ldgvp->ldgp, list) {
+ if (dev && (dev != ldgp))
+ continue;
+ list_for_each_entry(ldvp, &ldgp->ldv_list, ldg_list) {
+ if (ldvp->dev_type != LDV_RXDMA)
+ continue;
+ channel = ldvp->ldv-HXGE_RDMA_LD_START;
+ if ((rdc >= 0) && (channel != rdc))
+ continue;
+
+ /* set up the CS register. The rcr timeout and
+ * threshold were cleared in process_rx, when the
+ * cs register was read. If those bits are cleared
+ * here, we could wipe out a potential pending
+ * threshold and/or timeout interrupt inadvertantly
+ * (related to CR 6774415). So, clear all bits except
+ * ptrs and pkts read when processing interrupts
+ */
+
+ cs.value = (ldvp->data &
+ (RDC_STAT_PKTREAD_MASK | RDC_STAT_PTRREAD_MASK));
+ cs.bits.mex = 1;
+
+#ifdef DETECT_RCR_FULL
+ /* Temporary Check: To detect RCR Full conditions
+ * till we resolve the alignment requirement issues
+ * with RCR and RBRs
+ */
+ do {
+ rdc_stat_t curr_cs;
+ hpi_rxdma_control_status(handle, OP_GET,
+ ldvp->ldv-HXGE_RDMA_LD_START, &curr_cs);
+ if (curr_cs.bits.rcr_full) {
+ HXGE_ERR(hxgep, "hxge_enable_rx_ints: RCR Full caught!");
+ }
+ } while (0);
+#endif
+
+ /* read nothing; don't want to write
+ random old value in cs back! */
+ if (hpi_rxdma_control_status(handle, OP_SET,
+ ldvp->ldv-HXGE_RDMA_LD_START, &cs) !=
+ HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hxge_enable_rx_ints: Failed to read Rx channel %d",ldvp->ldv-HXGE_RDMA_LD_START);
+ }
+
+ }
+ }
+ hxge_interrupt_mgmt(hxgep, -1, 0, LDV_RXDMA, dev, rdc);
+}
+
+void hxge_disable_rx_ints(struct hxge_adapter *hxgep, struct hxge_ldg *ldgp,
+ int rdc)
+{
+ hxge_interrupt_mgmt(hxgep, -1, 1, LDV_RXDMA, ldgp, rdc);
+}
+
+void hxge_enable_tx_ints(struct hxge_adapter *hxgep, struct hxge_ldg *ldgp)
+{
+ hxge_interrupt_mgmt(hxgep, -1, 0, LDV_TXDMA, ldgp, -1);
+}
+
+static void hxge_enable_ldg_ints(struct hxge_ldg *ldgp)
+{
+ hxge_interrupt_mgmt(ldgp->hxgep , 1, -1, LDV_ALL, ldgp, -1);
+}
+
+void hxge_disable_ldg_ints(struct hxge_ldg *ldgp)
+{
+ hxge_interrupt_mgmt(ldgp->hxgep , 0, -1, LDV_ALL, ldgp, -1);
+}
+
+void hxge_disable_tx_ints(struct hxge_adapter *hxgep)
+{
+ hxge_interrupt_mgmt(hxgep, -1, 1, LDV_TXDMA, NULL, -1);
+}
+
+
+
+/* Set up the hydra registers related to interrupt management. However,
+ interrupts are only enabled when the interaface is brought up i.e in
+ hxge_up
+*/
+int hxge_set_hw_interrupt_regs (struct hxge_adapter *hxgep)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ hpi_status_t status = HPI_SUCCESS;
+ fzc_sid_t sid;
+ int i;
+ struct hxge_ldv *ldvp;
+ struct hxge_ldg *ldgp;
+
+ /* Configure the initial timer resolution */
+ if (hpi_fzc_ldg_timer_res_set (handle, hxgep->ldgvp->tmres)
+ != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_fzc_ldg_timer_res_set failed");
+ return -1;
+ }
+
+
+ /* Set up the logical device groups and relate them to the logical
+ devices. Also program the sid values */
+
+ i = 0;
+ list_for_each_entry(ldgp, &hxgep->ldgvp->ldgp, list) {
+ list_for_each_entry(ldvp, &ldgp->ldv_list, ldg_list) {
+ HXGE_DBG(hxgep, "Setting LDV %d->LDG %d",ldvp->ldv,ldgp->ldg);
+ status = hpi_fzc_ldg_num_set(handle, ldvp->ldv,
+ ldgp->ldg);
+ if (status != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_fzc_ldg_num_set failed");
+ return -1;
+ }
+ }
+ sid.vector = i++; /* just has to be unique for each entry */
+ sid.ldg = ldgp->ldg;
+ if (hpi_fzc_sid_set(handle, sid) != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_fzc_sid_set failed");
+ return -1;
+ }
+ }
+
+ return 0;
+
+}
+
+/* Return the number of logical device groups that we want to support. This
+ is purely programmatic. The assumption in picking the number of groups
+ is the best potential combination that would minimize interrupt latency
+ and maximize throughput */
+
+static int get_num_ldgs(struct hxge_adapter *hxgep, struct ldv_array *ldv)
+{
+ int nldgs = 0;
+ int i;
+
+ /* Each Tx channel has its own group */
+ nldgs += hxgep->max_tdcs;
+ for (i = 0; i < hxgep->max_tdcs; i++) {
+ ldv->type = LDV_TXDMA;
+ ldv->dev_no = HXGE_TDMA_LD_START+i;
+ ldv++;
+ }
+
+ /* Each Rx channel */
+ nldgs += hxgep->max_rdcs;
+ for (i = 0; i < hxgep->max_rdcs; i++) {
+ ldv->type = LDV_RXDMA;
+ ldv->dev_no = HXGE_RDMA_LD_START+i;
+ ldv++;
+ }
+
+ /* VMAC */
+ nldgs++;
+ ldv->type = LDV_VMAC;
+ ldv->dev_no = HXGE_VMAC_LD;
+ ldv++;
+
+ /* PFC */
+ nldgs++;
+ ldv->type = LDV_PFC;
+ ldv->dev_no = HXGE_PFC_LD;
+ ldv++;
+
+ /* Device Errors */
+ nldgs++;
+ ldv->type = LDV_DEVERR;
+ ldv->dev_no = HXGE_SYS_ERROR_LD;
+
+ return nldgs;
+}
+
+
+static void
+hxge_ldg_uninit(struct hxge_adapter *hxgep)
+{
+ struct hxge_ldv *ldvp, *loc_ldvp;
+ struct hxge_ldg *ldgp, *loc_ldgp;
+
+ list_for_each_entry_safe(ldvp, loc_ldvp, &hxgep->ldgvp->ldvp, list)
+ kfree(ldvp);
+
+ list_for_each_entry_safe(ldgp, loc_ldgp, &hxgep->ldgvp->ldgp, list)
+ kfree(ldgp);
+
+ kfree(hxgep->ldgvp);
+
+}
+
+static void hxge_dump_ints(struct hxge_adapter *hxgep)
+{
+ struct hxge_ldgv *ldgvp;
+ struct hxge_ldv *ldvp;
+ struct hxge_ldg *ldgp;
+
+ HXGE_DBG(hxgep, "Hydra Interrupt Structure =>");
+ ldgvp = hxgep->ldgvp;
+ HXGE_DBG(hxgep, " Timer resolution = 0x%x",ldgvp->tmres);
+ HXGE_DBG(hxgep, " Max groups = 0x%x",ldgvp->max_ldgs);
+ HXGE_DBG(hxgep, " Max devices = 0x%x",ldgvp->max_ldvs);
+ HXGE_DBG(hxgep, " No. of groups = 0x%x",ldgvp->nldgs);
+ HXGE_DBG(hxgep, " No. of devices = 0x%x",ldgvp->nldvs);
+
+ list_for_each_entry(ldgp, &hxgep->ldgvp->ldgp, list) {
+ HXGE_DBG(hxgep, "");
+ HXGE_DBG(hxgep, " Logical Group %d =>",ldgp->ldg);
+ HXGE_DBG(hxgep, " Vector = %d",ldgp->vector);
+ HXGE_DBG(hxgep, " No. of devices= %d",ldgp->nldvs);
+ HXGE_DBG(hxgep, " arm = %d",ldgp->arm);
+ list_for_each_entry(ldvp, &ldgp->ldv_list, ldg_list) {
+ HXGE_DBG(hxgep, "");
+ HXGE_DBG(hxgep, " Logical Device %d =>",ldvp->ldv);
+ HXGE_DBG(hxgep, " Dev type = %d",ldvp->dev_type);
+ HXGE_DBG(hxgep, " use_timer = %d",ldvp->use_timer);
+ HXGE_DBG(hxgep, " ldv_flags = 0x%x",ldvp->ldv_flags);
+ HXGE_DBG(hxgep, " ldf_mask = 0x%x",ldvp->ldf_masks);
+ }
+ }
+
+}
+
+/* Set up the Hydra interrupt structures - LDV and LDG */
+static int
+hxge_ldg_init(struct hxge_adapter *hxgep, int num_ints_required,
+ int num_ints_available, struct ldv_array *ldv_arr)
+{
+ struct hxge_ldgv *ldgvp;
+ struct hxge_ldv *ldvp;
+ struct hxge_ldg *ldgp = NULL;
+ int ldg_assigned = -1;
+ int i;
+
+
+ ldgvp = kzalloc(sizeof(struct hxge_ldgv), GFP_KERNEL);
+ if (!ldgvp)
+ {
+ HXGE_ERR(hxgep, "Could not allocate ldgv structure");
+ return -1;
+ }
+ hxgep->ldgvp = ldgvp;
+
+ HXGE_DBG(hxgep, "hxge_ldg_init: num_ints_avail=%d, num_ints_reqd=%d",num_ints_available,num_ints_required);
+
+ /* num_ints_required is what we want for the number of LDVs. However,
+ number of interrupts available defines how many LDGs we can have */
+ ldgvp->max_ldgs = num_ints_available;
+ ldgvp->max_ldvs = num_ints_required;
+ if (num_ints_required > HXGE_INT_MAX_LDG) {
+ HXGE_ERR(hxgep, "hxge_ldg_init: bad interrupt request");
+ return -1;
+ }
+
+ INIT_LIST_HEAD(&ldgvp->ldvp);
+ INIT_LIST_HEAD(&ldgvp->ldgp);
+ ldgvp->tmres = HXGE_TIMER_RESO;
+
+ /* Allocate the bins and fill then later. If we have fewer LDGs than
+ LDVs, then after we have reached the last LDG, lump the remaining
+ LDVs into that LDG */
+ for (i = 0; i < ldgvp->max_ldvs; i++) {
+ ldvp = kzalloc(sizeof(struct hxge_ldv), GFP_KERNEL);
+ INIT_LIST_HEAD(&ldvp->ldg_list);
+ INIT_LIST_HEAD(&ldvp->list);
+ ldgvp->nldvs++;
+ list_add_tail(&ldvp->list, &ldgvp->ldvp);
+ if (i < ldgvp->max_ldgs) { /* not the last LDG */
+ ldgp = kzalloc(sizeof(struct hxge_ldg), GFP_KERNEL);
+ if (!ldgp) {
+ HXGE_ERR(hxgep, "Alloc failed for ldg structure");
+ hxge_teardown_interrupt(hxgep);
+ return -1;
+ }
+ ldgp->vector = -1;
+ INIT_LIST_HEAD(&ldgp->ldv_list);
+ INIT_LIST_HEAD(&ldgp->list);
+ list_add_tail(&ldgp->list, &ldgvp->ldgp);
+ ldgp->hxgep = hxgep;
+ ldgp->intr_handler = hxge_intr;
+ ldgvp->nldgs++;
+ ++ldg_assigned;
+ ldgp->ldg = ldg_assigned;
+ }
+ /* add LDV to the LDG list */
+ list_add_tail(&ldvp->ldg_list, &ldgp->ldv_list);
+ ldgp->nldvs++;
+ ldvp->ldgp = ldgp;
+ ldvp->dev_type = ldv_arr[i].type;
+ ldvp->ldv = ldv_arr[i].dev_no;
+ /* mask interrupts for all devices for starters */
+ ldvp->ldf_masks = (uint8_t)LD_IM_MASK;
+ }
+
+ /* Go through the devices we care about and assign interrupt handlers
+ to them. Also enable timers for those devices we want it for */
+ list_for_each_entry(ldvp, &ldgvp->ldvp, list) {
+ switch (ldvp->dev_type) {
+ case LDV_RXDMA :
+ ldvp->intr_handler = hxge_rx_intr;
+ ldgp->timer = HXGE_TIMER_LDG;
+ break;
+ case LDV_TXDMA :
+ ldvp->intr_handler = hxge_tx_intr;
+ ldgp->timer = HXGE_TIMER_LDG;
+ break;
+ case LDV_VMAC :
+ ldvp->intr_handler = hxge_vmac_intr;
+ ldgp->timer = HXGE_TIMER_LDG;
+ break;
+ case LDV_PFC :
+ ldvp->intr_handler = hxge_pfc_intr;
+ ldgp->timer = HXGE_TIMER_LDG;
+ break;
+ case LDV_DEVERR :
+ ldvp->intr_handler = hxge_deverr_intr;
+ break;
+ default:
+ HXGE_ERR(hxgep, "hxge_ldg_init: Unsupported device type, %d",ldvp->dev_type);
+ hxge_ldg_uninit(hxgep);
+ return -1;
+ }
+ }
+
+ hxge_dump_ints(hxgep);
+
+ return 0;
+}
+
+/* Tear down the interrupt infrastructure. Typically, this is called when
+ an interface is taken down so that the precious interrupt resources are
+ freed for use by others */
+void hxge_teardown_interrupt(struct hxge_adapter *hxgep)
+{
+ struct hxge_ldv *ldvp;
+ struct hxge_ldg *ldgp;
+
+ /* Nothing to do if polling */
+ if (hxgep->intr_type == POLLING_TYPE)
+ return;
+
+
+ list_for_each_entry(ldgp, &hxgep->ldgvp->ldgp, list) {
+ ldvp = list_entry(ldgp->ldv_list.next, struct hxge_ldv,
+ ldg_list);
+ if (ldgp->vector > 0)
+ free_irq(ldgp->vector, ldgp);
+ }
+
+ switch (hxgep->intr_type) {
+ case MSIX_TYPE :
+ pci_disable_msix(hxgep->pdev);
+ break;
+ case MSI_TYPE:
+ pci_disable_msi(hxgep->pdev);
+ break;
+ }
+
+#ifdef CONFIG_PCI_MSI
+ if (hxgep->intr_type == MSIX_TYPE)
+ kfree(hxgep->msix);
+#endif
+ hxge_ldg_uninit(hxgep);
+}
+
+static int hxge_request_irqs(struct net_device *netdev)
+{
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+ struct hxge_ldv *ldvp;
+ struct hxge_ldg *ldgp;
+ int i = 0, status;
+
+ list_for_each_entry(ldgp, &hxgep->ldgvp->ldgp, list) {
+#ifdef CONFIG_PCI_MSI
+ if ((hxgep->intr_type == MSI_TYPE) ||
+ (hxgep->intr_type == INTx_TYPE))
+ ldgp->vector = hxgep->pdev->irq;
+ else
+ ldgp->vector = hxgep->msix[i].vector;
+#else
+ ldgp->vector = hxgep->pdev->irq;
+#endif
+ ldvp = list_entry(ldgp->ldv_list.next,
+ struct hxge_ldv, ldg_list);
+ snprintf(ldgp->irq_name, HXGE_MAX_IRQNAME, "%s_int%d",netdev->name, i);
+ HXGE_DBG(hxgep, "Allocating interrupt: irq=%d, dev=%d, intr_name=%s",ldgp->vector,ldvp->ldv, ldgp->irq_name);
+ /* If multiple blocks belong to the same group,
+ then pass in the LDG pointer; otherwise, pass in LDV
+ (this saves one indirection on interrupt side) */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+ status = request_irq(ldgp->vector, ldgp->intr_handler,
+ SA_SHIRQ, ldgp->irq_name, ldgp);
+#else
+ status = request_irq(ldgp->vector, ldgp->intr_handler,
+ IRQF_SHARED, ldgp->irq_name, ldgp);
+#endif
+ if (status) {
+ HXGE_ERR(hxgep, "request_irq() failed, returned %d",
+ status);
+ /* no irq allocation done for ldvp. So, reset vector */
+ ldgp->vector = -1;
+ return status;
+ }
+ i++;
+ }
+
+ return 0;
+}
+
+/* This routine sets up the interupt infrastructure for Hydra such as the
+ LDV and LDG for the various functional blocks and allocates IRQs (either
+ legacy or MSIX) by requesting it from the kernel. Finally, it sets the
+ appropriate hardware state indicating LDGs and enabling the appropriate
+ interrupts for the required blocks */
+int hxge_setup_interrupt (struct net_device *netdev)
+{
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+ int num_ints_required,num_ints_available, i;
+#ifdef CONFIG_PCI_MSI
+ struct msix_entry *ptr, *msix_ent = NULL;
+#endif
+ struct ldv_array ldv_arr[HXGE_INT_MAX_LD];
+ int intr_type, status;
+
+ if (hxge_get_option("intr_type", &intr_type)) {
+ HXGE_ERR(hxgep, "hxge_setup_interrupt: intry_type invalid");
+ return -1;
+ }
+
+ hxgep->intr_type = intr_type;
+ if (hxgep->intr_type == POLLING_TYPE)
+ {
+ HXGE_DBG(hxgep, "hxge_setup_interrupt: intr_type = polling, do nothing");
+ return 0;
+ }
+
+
+ /* Determine the number of logical device groups needed */
+ memset(ldv_arr, 0xff, sizeof(ldv_arr));
+ num_ints_required = get_num_ldgs(hxgep, ldv_arr);
+
+#ifdef CONFIG_PCI_MSI
+ switch (intr_type) {
+ case MSIX_TYPE :
+ hxgep->intr_type = MSIX_TYPE;
+ msix_ent = kzalloc( num_ints_required *
+ sizeof(struct msix_entry), GFP_KERNEL);
+ if (!msix_ent) {
+ HXGE_ERR(hxgep, "hxge_setup_interrupt: Could not allocate msix entries");
+ return -1;
+ }
+ hxgep->msix = msix_ent;
+ for (i = 0, ptr = msix_ent; i < num_ints_required; i++,ptr++)
+ ptr->entry = i;
+
+ /* Keep trying till we get available vectors */
+ num_ints_available = num_ints_required;
+ while ((status = pci_enable_msix(hxgep->pdev, msix_ent,
+ num_ints_available)) > 0)
+ {
+ num_ints_available = status;
+ HXGE_ERR(hxgep, "pci_enable_msix: status=%d",status);
+ }
+
+ if (status < 0) {
+ HXGE_ERR(hxgep, "hxge_setup_interrupt: pci_enable_msix failed, status=%d",status);
+ kfree(msix_ent);
+ hxgep->msix = NULL;
+ return -1;
+ }
+ HXGE_DBG(hxgep, "hxge_setup_interrupt: Interrupt type is MSIX_TYPE; %d interrupts available", num_ints_available);
+ break;
+ case MSI_TYPE :
+ num_ints_available = 1;
+ if (!pci_enable_msi(hxgep->pdev)) {
+ hxgep->intr_type = MSI_TYPE;
+ HXGE_DBG(hxgep, "hxge_setup_interrupt: Interrupt type is MSI");
+ break;
+ }
+ /* fall through; reverting to INTx */
+ HXGE_DBG(hxgep, "hxge_setup_interrupt: No MSI, using INTx");
+ case INTx_TYPE :
+ num_ints_available = 1;
+ hxgep->intr_type = INTx_TYPE;
+ break;
+ default :
+ HXGE_ERR(hxgep, "hxge_setup_interrupt: Bad type");
+ return -1;
+ }
+
+#else
+ num_ints_available = 1;
+ hxgep->intr_type = INTx_TYPE;
+#endif
+
+ if (num_ints_available < 1) {
+ HXGE_ERR(hxgep, "num_ints_available should be atleast 1");
+ return -1;
+ }
+
+
+
+ /* Initialize Hydra interrupt data structures */
+ status = hxge_ldg_init(hxgep, num_ints_required, num_ints_available,
+ ldv_arr);
+ if (status) {
+ HXGE_ERR(hxgep, "hxge_setup_interrupt: hxge_ldv_interrupt_init failed");
+#ifdef CONFIG_PCI_MSI
+ if (msix_ent) /* implies MSIX_TYPE */
+ kfree(msix_ent);
+#endif
+ return -1;
+ }
+
+ /* Request for IRQs (in the case of msix, for each vector assigned) */
+ if (hxge_request_irqs(netdev)) {
+ HXGE_ERR(hxgep, "hxge_setup_interrupt: request_irq failed");
+ hxge_teardown_interrupt(hxgep);
+ return -1;
+ }
+
+ /* Enable the hardware interrupt registers. Setup the LDG and LDV
+ registers for the respective blocks */
+ if (hxge_set_hw_interrupt_regs(hxgep)) {
+ HXGE_ERR(hxgep, "hxge_setup_interrupt: hxge_set_hw_interrupt failed");
+ hxge_teardown_interrupt(hxgep);
+ return -1;
+ }
+
+ return 0;
+}
+
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#include "hxge.h"
+
+static const char hxge_driver_string[] = "Sun Microsystems(R) 10 Gigabit Network Driver";
+#ifndef CONFIG_HXGE_NAPI
+#define DRIVERNAPI
+#else
+#define DRIVERNAPI "-NAPI"
+#endif
+#define DRV_VERSION "1.3.3"
+const char hxge_driver_version[] = DRV_VERSION;
+static const char hxge_copyright[] = "Copyright (c) 2009, 2011 Oracle America.";
+
+
+lb_property_t lb_properties[] = {
+ {normal, "normal", hxge_lb_normal},
+ {external, "external10g", hxge_lb_ext10g}
+};
+
+
+/* hxge_pci_tbl - PCI Device ID Table
+ *
+ * Last entry must be all 0s
+ *
+ * Macro expands to...
+ * {PCI_DEVICE(PCI_VENDOR_ID_INTEL, device_id)}
+ */
+static struct pci_device_id hxge_pci_tbl[] = {
+ SUN_ETHERNET_DEVICE(PCI_DEVICE_ID_SUN_HYDRA),
+ /* required last entry */
+ {0,}
+};
+MODULE_DEVICE_TABLE(pci, hxge_pci_tbl);
+
+/* External Functions */
+extern int hxge_pfc_init_mac_addrs(struct hxge_adapter *hxgep);
+extern int hxge_pfc_hw_reset(struct hxge_adapter *hxgep);
+extern int hxge_init_param(struct hxge_adapter *hxgep);
+extern void hxge_init_stats(struct hxge_adapter *hxgep);
+extern void hxge_free_stats(struct hxge_adapter *hxgep);
+extern int hxge_set_mac_address(struct net_device *netdev, void *p);
+extern int hxge_vmac_init(struct hxge_adapter *hxgep);
+extern void hxge_vmac_uninit(struct hxge_adapter *hxgep);
+extern int hxge_link_intr(struct hxge_adapter *hxgep, int cmd);
+extern int hxge_get_option(const char *str, int *val);
+extern void hxge_enable_interrupts(struct hxge_adapter *hxgep);
+extern int hxge_enable_rx(struct hxge_adapter *hxgep);
+extern int hxge_enable_tx(struct hxge_adapter *hxgep);
+extern int hxge_disable_rx(struct hxge_adapter *hxgep);
+extern int hxge_disable_tx(struct hxge_adapter *hxgep);
+extern void hxge_disable_interrupts(struct hxge_adapter *hxgep);
+extern int hxge_alloc_rx(struct hxge_adapter *hxgep);
+extern int hxge_alloc_tx(struct hxge_adapter *hxgep);
+extern int hxge_setup_interrupt (struct net_device *netdev);
+extern void hxge_teardown_interrupt(struct hxge_adapter *hxgep);
+extern int hxge_peu_get_link_status(struct hxge_adapter *hxgep);
+extern int hxge_free_rx(struct hxge_adapter *hxgep);
+extern int hxge_ok_to_continue(struct hxge_adapter *hxgep);
+extern int hxge_block_reset(struct hxge_adapter *hxgep, int device);
+extern int hxge_peu_deverr_init(struct hxge_adapter *hxgep);
+extern void hxge_free_tx(struct hxge_adapter *hxgep);
+extern void hxge_reset_tx_channel(struct hxge_adapter *hxgep, int channel);
+extern void hxge_reset_rx_channel(struct hxge_adapter *hxgep, int channel);
+extern int hxge_reset_tdc(struct hxge_adapter *hxgep);
+extern int hxge_reset_rdc(struct hxge_adapter *hxgep);
+#ifdef CONFIG_HXGE_NAPI
+extern int hxge_poll(struct net_device *poll_dev, int *budget);
+#endif
+extern int hxge_start_xmit(struct sk_buff *skb, struct net_device *netdev);
+extern struct net_device_stats *hxge_get_stats(struct net_device *netdev);
+extern int hxge_classify_init(struct hxge_adapter *hxgep);
+extern int hxge_classify_uninit(struct hxge_adapter *hxgep);
+extern void hxge_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp);
+extern void hxge_vlan_rx_add_vid(struct net_device *netdev, uint16_t vid);
+extern void hxge_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid);
+extern void hxge_set_multi(struct net_device *netdev);
+extern int hxge_get_tcam_properties(struct hxge_adapter *hxgep);
+extern int hxge_set_loopback(struct hxge_adapter *hxgep, boolean_t enable);
+
+#ifdef CONFIG_ERRINJECT
+extern int hxge_create_sysfs(struct net_device *netdev);
+extern void hxge_remove_sysfs(struct net_device *netdev);
+#endif
+
+
+/* Local Function Prototypes */
+
+static int hxge_init_module(void);
+static void hxge_exit_module(void);
+static int hxge_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
+static void __devexit hxge_remove(struct pci_dev *pdev);
+static int hxge_open(struct net_device *netdev);
+static int hxge_close(struct net_device *netdev);
+static int hxge_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd);
+static int hxge_change_mtu(struct net_device *netdev, int new_mtu);
+static int hxge_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd);
+static void hxge_tx_timeout(struct net_device *dev);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static void hxge_work_to_do(struct hxge_adapter *hxgep);
+#else
+static void hxge_work_to_do(struct work_struct *work);
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
+#define DMA_MASK DMA_BIT_MASK(32)
+#else
+#define DMA_MASK DMA_32BIT_MASK
+#endif
+
+
+static int hxge_sw_init(struct hxge_adapter *adapter);
+static int hxge_link_monitor(struct hxge_adapter *hxgep, int cmd);
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/* for netdump / net console */
+static void hxge_netpoll (struct net_device *netdev);
+#endif
+
+static struct pci_driver hxge_driver = {
+ .name = HXGE_DRIVER_NAME,
+ .id_table = hxge_pci_tbl,
+ .probe = hxge_probe,
+ .remove = __devexit_p(hxge_remove),
+};
+
+MODULE_AUTHOR("Oracle Corporation, <james.puthukattukaran@oracle.com>");
+MODULE_DESCRIPTION("Oracle Corporation(R) 10 Gigabit Network Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+static int debug = NETIF_MSG_HW | NETIF_MSG_PROBE;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0)
+spinlock_t hxge_lock = SPIN_LOCK_UNLOCKED; /* HXGE PIO global lock */
+#else
+DEFINE_SPINLOCK(hxge_lock);
+#endif
+
+/**
+ * hxge_init_module - Driver Registration Routine
+ *
+ * hxge_init_module is called when the driver is loaded. It registers
+ * the driver with the PCI subsystem
+ **/
+
+static int __init
+hxge_init_module(void)
+{
+ int ret;
+ printk(KERN_INFO "%s - version %s\n",
+ hxge_driver_string, hxge_driver_version);
+
+ printk(KERN_INFO "%s\n", hxge_copyright);
+
+ ret = pci_register_driver(&hxge_driver);
+
+ return ret;
+}
+module_init(hxge_init_module);
+
+/**
+ * hxge_exit_module - Driver Exit Routine
+ *
+ * hxge_exit_module is called when driver is unloaded via rmmod. It
+ * unregisters itself from the PCI subsystem
+ *
+ **/
+
+static void __exit
+hxge_exit_module(void)
+{
+ pci_unregister_driver(&hxge_driver);
+}
+module_exit(hxge_exit_module);
+
+/**
+ * hxge_irq_disable - Mask off interrupt generation on the NIC
+ * @adapter: board private structure
+ **/
+
+static void
+hxge_irq_disable(struct hxge_adapter *adapter)
+{
+ atomic_inc(&adapter->irq_sem);
+ hxge_disable_interrupts(adapter);
+ synchronize_irq(adapter->pdev->irq);
+}
+
+/**
+ * hxge_irq_enable - Enable default interrupt generation settings
+ * @adapter: board private structure
+ **/
+
+static void
+hxge_irq_enable(struct hxge_adapter *adapter)
+{
+ if (likely(atomic_dec_and_test(&adapter->irq_sem))) {
+ hxge_enable_interrupts(adapter);
+ }
+}
+
+/* Ignores linux network stack niceties and brings down the adapter */
+void
+hxge_disable_adapter(struct hxge_adapter *hxgep)
+{
+ /* Disable interrupts */
+ hxge_disable_interrupts(hxgep);
+
+ /* Disable TCAM */
+ hxge_classify_uninit(hxgep);
+
+ /* Disable VMAC interface */
+ hxge_vmac_uninit(hxgep);
+
+ /* Disable all Tx channels */
+ hxge_disable_tx(hxgep);
+
+ /* Disable all Rx channels */
+ hxge_disable_rx(hxgep);
+
+ /* Do a PEU reset to take care of CR 6668282 in the event that
+ * the PCI links are not left in the proper state
+ */
+ hxge_block_reset(hxgep, (LDV_RXDMA | LDV_TXDMA | LDV_VMAC));
+}
+
+int
+hxge_enable_adapter(struct hxge_adapter *hxgep)
+{
+ /* Do reset of all the major blocks, especially those that
+ are shared across channels and potentially blades. This is done
+ via the PEU space */
+ hxge_block_reset(hxgep, (LDV_RXDMA | LDV_TXDMA | LDV_VMAC));
+
+ /* Enable Tx DMA channels */
+ if (hxge_enable_tx(hxgep))
+ {
+ if (!test_bit(HXGE_DEVICE_UP, &hxgep->state)) {
+ HXGE_ERR(hxgep, "hxge_enable_adapter: hxge_enable_tx failed");
+ hxge_disable_adapter(hxgep);
+ return -1;
+ }
+ }
+
+ /* Enable the Rx DMA channels */
+ hxge_enable_rx(hxgep);
+
+ /* Allocate and enable TCAM */
+ hxge_classify_init(hxgep);
+
+ hxge_vmac_init(hxgep);
+
+ /* Now that all the feeds into Device Error (PEU, TDC, etc.)
+ * have been cleared/initialized, enable the Device Error
+ * [logical] device/function */
+
+ if (hxge_peu_deverr_init(hxgep))
+ return -1;
+
+ /* Enable interrupts for all devices */
+ hxge_enable_interrupts(hxgep);
+
+ return 0;
+}
+
+static int
+hxge_lif_up(struct hxge_adapter *hxgep)
+{
+ struct net_device *netdev = hxgep->netdev;
+
+ /* Start link monitoring */
+ hxgep->prev_link_status = -1;
+ hxge_link_monitor(hxgep, LINK_MONITOR_START);
+
+#ifdef CONFIG_HXGE_NAPI
+ netif_poll_enable(netdev);
+#endif
+
+ if (hxge_enable_adapter(hxgep))
+ return -1;
+
+ hxge_irq_enable(hxgep);
+
+ /* Enable Linux network stack */
+ netif_carrier_on(netdev);
+ netif_start_queue(netdev);
+
+ set_bit(HXGE_DEVICE_UP, &hxgep->state);
+ return 0;
+}
+
+static int
+hxge_up(struct hxge_adapter *hxgep)
+{
+ /* If we were in error shutdown state, this constitutes a
+ * manual intervention to bring it back up again. */
+
+ clear_bit(HXGE_DEVICE_SHUTTINGDOWN, &hxgep->state);
+
+ hxgep->statsp->accum_hard_errors += hxgep->statsp->hard_errors;
+ hxgep->statsp->accum_soft_errors += hxgep->statsp->soft_errors;
+ hxgep->statsp->accum_line_errors += hxgep->statsp->line_errors;
+ hxgep->statsp->hard_errors = 0; /* Reset all error rate counters */
+ hxgep->statsp->soft_errors = 0;
+ hxgep->statsp->line_errors = 0;
+ hxgep->ifup_time = jiffies; /* "t = 0" for error rate calc */
+
+ return(hxge_lif_up (hxgep)); /* local interface "up" work */
+}
+
+static void
+hxge_down(struct hxge_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+
+ clear_bit(HXGE_DEVICE_UP, &adapter->state);
+
+ hxge_link_monitor(adapter, LINK_MONITOR_STOP);
+ netif_carrier_off(netdev); /* avoids Tx timeouts */
+ netif_stop_queue(netdev);
+
+#ifdef CONFIG_HXGE_NAPI
+ netif_poll_disable(netdev);
+#endif
+ hxge_irq_disable(adapter);
+
+ /* Reset the adapter */
+ hxge_disable_adapter(adapter);
+}
+
+
+/* This routine memory maps the Hydra register (PIO/BAR0) */
+static int hxge_map_registers(struct net_device *netdev)
+{
+ unsigned long pio_base, pio_len;
+ struct hxge_adapter *adapter= netdev_priv(netdev);
+ struct pci_dev *pdev = adapter->pdev;
+ int err;
+ u32 vendorid;
+
+ pio_base = pci_resource_start(pdev, BAR_0);
+ pio_len = pci_resource_len(pdev, BAR_0);
+
+ err = -EIO;
+ adapter->hw.hw_addr = ioremap(pio_base, pio_len);
+ if (!adapter->hw.hw_addr)
+ return err;
+
+ /* There is a bug in the HW (we think) where the link retrain, which is
+ as part of the ASPM common clock configuration code, fails.
+ Consequently, the device is left in a completely initialized state
+ i.e. the BARs are reset and inaccessible by the time the driver is
+ loaded. We have verified that bypassing the common clock
+ configuration code works around the problem (pcie_aspm=off). This
+ is a warning/informative message to the customer to check if the
+ boot argument is passed
+ */
+ vendorid= readl(adapter->hw.hw_addr + PCI_VENDOR_ID);
+ if (vendorid== 0xffffffff) {
+ HXGE_ERR(adapter,"Device probe failed. The PCI BAR space is inaccessible! You may want to set the boot argument pcie_aspm=off");
+ }
+
+ netdev->mem_start = pio_base;
+ netdev->mem_end = pio_base + pio_len;
+
+ return 0;
+}
+
+/* This function runs periodically trigger off the one-shot timer and
+ periodically monitors the link status (if monitoring is enabled).
+ It notifies the kernel (linux network stack) of change in link status
+ so that the traffic control subsystem can take appropriate action */
+
+static void hxge_watchdog_timer(unsigned long data)
+{
+ struct hxge_adapter *hxgep = (struct hxge_adapter *)data;
+ int link_up = hxge_peu_get_link_status(hxgep);
+
+ /* Calling either netif_carrier_on if link is already up does nothing.
+ Similarly, calling netif_carrier_down if link is down does nothing
+ either */
+
+ if (link_up)
+ netif_carrier_on(hxgep->netdev);
+ else
+ netif_carrier_off(hxgep->netdev);
+
+ /* Log a change in link status */
+ if (hxgep->prev_link_status != link_up) {
+ HXGE_ERR(hxgep, "hxge_watchdog_timer: link is %s", (link_up) ? "up":"down");
+ hxgep->prev_link_status = link_up;
+ }
+ mod_timer (&hxgep->wd_timer, jiffies + HXGE_LINK_TIMEOUT);
+}
+
+
+/* Initialize link management */
+static void hxge_init_link(struct hxge_adapter *hxgep)
+{
+ hxgep->link_monitor_state = LINK_MONITOR_DISABLED;
+ hxgep->link_mode = LINK_MODE_POLL;
+ setup_timer(&hxgep->wd_timer,hxge_watchdog_timer,(unsigned long)hxgep);
+}
+
+/* Start or stop link monitoring. If we want to be interrupted for a link
+ state change, then we call this routine just once when turning on
+ interrupt monitoring. For polling, this routine is called periodically
+ every HX_LINK_TIMEOUT seconds and the one-shot timer is set for the next
+ period. */
+static int hxge_link_monitor(struct hxge_adapter *hxgep, int cmd)
+{
+ if (netif_msg_link(hxgep)) {
+ HXGE_DBG(hxgep, "hxge_link_monitor: cmd = %d",cmd);
+ if (hxgep->link_monitor_state == LINK_MONITOR_DISABLED) {
+ HXGE_DBG(hxgep, "hxge_link_monitor: Link monitoring disabled");
+ }
+ else {
+ HXGE_DBG(hxgep, "hxge_link_monitor: Link monitoring enabled");
+ }
+ }
+
+ hxgep->statsp->link_monitor_cnt++;
+ switch (cmd) {
+ case LINK_MONITOR_START:
+ /* Assert an interrupt when link state changes. */
+ if (hxgep->link_mode == LINK_MODE_INTR) {
+ if (hxge_link_intr(hxgep, cmd))
+ goto fail;
+ /* Periodically poll for for state change */
+ } else if (hxgep->link_monitor_state == LINK_MONITOR_DISABLED) {
+ hxgep->link_monitor_state = LINK_MONITOR_ENABLED;
+ mod_timer (&hxgep->wd_timer, jiffies + HXGE_LINK_TIMEOUT);
+ }
+ HXGE_DBG(hxgep, "hxge_link_monitor: Link monitoring started");
+ break;
+ case LINK_MONITOR_STOP:
+ if (hxgep->link_mode == LINK_MODE_INTR) {
+ if (hxge_link_intr(hxgep, cmd))
+ goto fail;
+ } else if (hxgep->link_monitor_state == LINK_MONITOR_ENABLED)
+ {
+ hxgep->link_monitor_state = LINK_MONITOR_DISABLED;
+ del_timer_sync(&hxgep->wd_timer);
+ }
+ HXGE_DBG(hxgep, "hxge_link_monitor: Link monitoring stopped");
+ break;
+ default:
+ HXGE_ERR(hxgep, "hxge_link_monitor: Unknown command");
+ break;
+ }
+ return 0;
+
+fail:
+ HXGE_ERR(hxgep, "hxge_link_monitor: failed");
+ return -1;
+}
+
+
+
+static void hxge_init_locks(struct hxge_adapter *hxgep)
+{
+ spin_lock_init(&hxgep->lock);
+ spin_lock_init(&hxgep->stats_lock);
+ spin_lock_init(&hxgep->tcam_lock);
+ rwlock_init(&hxgep->wtd_lock);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
+static const struct net_device_ops hxge_netdev_ops = {
+ .ndo_open = hxge_open,
+ .ndo_stop = hxge_close,
+ .ndo_start_xmit = hxge_start_xmit,
+ .ndo_get_stats = hxge_get_stats,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0)
+ .ndo_set_multicast_list = hxge_set_multi,
+#else
+ .ndo_set_rx_mode = hxge_set_multi,
+#endif
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = hxge_set_mac_address,
+ .ndo_do_ioctl = hxge_ioctl,
+ .ndo_tx_timeout = hxge_tx_timeout,
+ .ndo_change_mtu = hxge_change_mtu,
+ .ndo_vlan_rx_register = hxge_vlan_rx_register,
+ .ndo_vlan_rx_add_vid = hxge_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = hxge_vlan_rx_kill_vid,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = hxge_netpoll,
+#endif
+};
+#endif
+
+
+
+/**
+ * hxge_probe - Device Initialization Routine
+ * @pdev: PCI device information struct
+ * @ent: entry in hxge_pci_tbl
+ *
+ * Returns 0 on success, negative on failure
+ *
+ * hxge_probe initializes an adapter identified by a pci_dev structure.
+ * The OS initialization, configuring of the adapter private structure,
+ * and a hardware reset occur.
+ **/
+
+static int __devinit
+hxge_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *netdev;
+ struct hxge_adapter *adapter;
+ static int cards_found = 0;
+ int err, pci_using_dac;
+
+ if ((err = pci_enable_device(pdev)))
+ {
+ HXGE_ERR_PRINT("hxge_probe: Failed to (PCI) enable device");
+ return err;
+ }
+
+ /* Hydra can address up to 44-bits of physical memory. Let the
+ kernel know */
+ if (!(err = pci_set_dma_mask(pdev, HXGE_MAX_ADDRESS_BITS_MASK)) &&
+ !(err = pci_set_consistent_dma_mask(pdev, HXGE_MAX_ADDRESS_BITS_MASK))) {
+ pci_using_dac = 1;
+ } else {
+ if ((err = pci_set_dma_mask(pdev, DMA_MASK)) &&
+ (err = pci_set_consistent_dma_mask(pdev, DMA_MASK))) {
+ HXGE_ERR_PRINT("No usable DMA configuration, aborting");
+ goto err_dma;
+ }
+ pci_using_dac = 0;
+ }
+
+ if ((err = pci_request_regions(pdev, HXGE_DRIVER_NAME)))
+ goto err_pci_reg;
+
+ pci_set_master(pdev);
+
+ err = -ENOMEM;
+ netdev = alloc_etherdev(sizeof(struct hxge_adapter));
+ if (!netdev)
+ goto err_alloc_etherdev;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+ SET_MODULE_OWNER(netdev);
+#endif
+
+ SET_NETDEV_DEV(netdev, &pdev->dev);
+
+ pci_set_drvdata(pdev, netdev);
+ adapter = netdev_priv(netdev);
+ adapter->netdev = netdev;
+ adapter->pdev = pdev;
+ adapter->msg_enable = debug;
+
+ if (hxge_map_registers(netdev) < 0)
+ goto err_mapfailed;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+ netdev->open = &hxge_open;
+ netdev->stop = &hxge_close;
+ netdev->hard_start_xmit = &hxge_start_xmit;
+ netdev->get_stats = &hxge_get_stats;
+ netdev->set_multicast_list = &hxge_set_multi;
+ netdev->set_mac_address = &hxge_set_mac_address;
+ netdev->change_mtu = &hxge_change_mtu;
+ netdev->do_ioctl = &hxge_ioctl;
+ netdev->tx_timeout = &hxge_tx_timeout;
+ netdev->vlan_rx_register = hxge_vlan_rx_register;
+ netdev->vlan_rx_add_vid = hxge_vlan_rx_add_vid;
+ netdev->vlan_rx_kill_vid = hxge_vlan_rx_kill_vid;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ netdev->poll_controller = hxge_netpoll;
+#endif
+#else
+ netdev->netdev_ops = &hxge_netdev_ops;
+#endif
+
+ hxge_set_ethtool_ops(netdev);
+#ifdef CONFIG_HXGE_NAPI
+ netdev->poll = &hxge_poll;
+ netdev->weight = 64;
+#endif
+ netdev->watchdog_timeo = HXGE_TX_TIMEOUT;
+ strncpy(netdev->name, pci_name(pdev), strlen(pci_name(pdev)));
+
+ adapter->bd_number = cards_found;
+
+ HXGE_DBG(adapter, "Allocated adapter");
+
+ /* Initialize locks */
+ hxge_init_locks(adapter);
+ HXGE_DBG(adapter, "Got Locks");
+
+ /* Get the driver parameters */
+ if (hxge_init_param(adapter))
+ goto err_register;
+ HXGE_DBG(adapter, "Initialized parameter list");
+
+ /* setup the private structure */
+ if ((err = hxge_sw_init(adapter)))
+ goto err_sw_init;
+ HXGE_DBG(adapter, "Initialized hxgep with parameters");
+
+ if (hxge_pfc_hw_reset(adapter))
+ {
+ HXGE_ERR(adapter, "hxge_probe: Failed hxge_pfc_hw_reset");
+ goto err_register;
+ }
+ HXGE_DBG(adapter, "Reset the HW");
+
+ /* Initialize link management */
+ hxge_init_link(adapter);
+ HXGE_DBG(adapter, "Initialized the link");
+
+ /* Initialize and set up statistics for the device */
+ hxge_init_stats(adapter);
+ HXGE_DBG(adapter, "Initialized stats");
+
+ err = -EIO;
+
+ if (pci_using_dac)
+ netdev->features |= NETIF_F_HIGHDMA;
+
+ /* We have to provide our own locking for transmit */
+ netdev->features |= NETIF_F_LLTX;
+ netdev->features |= NETIF_F_SG;
+
+
+ if (adapter->flags & HXGE_TX_CHKSUM_ENABLED)
+ netdev->features |= (NETIF_F_IP_CSUM
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+ | NETIF_F_IPV6_CSUM
+#endif
+ );
+
+
+ if (adapter->flags & HXGE_VLAN_ENABLED)
+ netdev->features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER;
+
+
+ /* copy the MAC address from the PFC block; all 16 of them */
+ if (hxge_pfc_init_mac_addrs(adapter)) {
+ HXGE_ERR(adapter, "EEPROM Read Error");
+ goto err_sw_init;
+ }
+ HXGE_DBG(adapter, "Initialized MAC addresses");
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+ INIT_WORK(&adapter->work_to_do,
+ (void (*)(void *))hxge_work_to_do, adapter);
+#else
+ INIT_WORK(&adapter->work_to_do, hxge_work_to_do);
+#endif
+
+ /* we're going to reset the device because we have no clue what
+ state we find it in */
+ netif_carrier_off(netdev);
+ netif_stop_queue(netdev);
+
+ strcpy(netdev->name, "eth%d");
+ if ((err = register_netdev(netdev)))
+ goto err_register;
+
+ HXGE_DBG(adapter, "hxge_probe: SMI(R) 10 Gb Ethernet Network Connection");
+
+#ifdef CONFIG_ERRINJECT
+ hxge_create_sysfs(netdev);
+#endif
+ cards_found++;
+ set_bit (HXGE_DEVICE_INITIALIZED, &adapter->state);
+ return 0;
+
+err_register:
+err_sw_init:
+ iounmap(adapter->hw.hw_addr);
+err_mapfailed:
+ free_netdev(netdev);
+err_alloc_etherdev:
+ pci_release_regions(pdev);
+err_pci_reg:
+err_dma:
+ pci_disable_device(pdev);
+ return err;
+}
+
+/**
+ * hxge_remove - Device Removal Routine
+ * @pdev: PCI device information struct
+ *
+ * hxge_remove is called by the PCI subsystem to alert the driver
+ * that it should release a PCI device. The could be caused by a
+ * Hot-Plug event, or because the driver is going to be removed from
+ * memory.
+ **/
+
+static void __devexit
+hxge_remove(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct hxge_adapter *adapter = netdev_priv(netdev);
+
+ if (!netdev)
+ return;
+
+ set_bit(HXGE_DRIVER_REMOVING, &adapter->state);
+#ifdef CONFIG_ERRINJECT
+ hxge_remove_sysfs(netdev);
+#endif
+
+ /*
+ * grab the wtd lock before calling flush_scheduled work. Also called
+ * in hxge_down. However, if we are calling hxge_down via hxge_close
+ * from hxge_remove (when we do a rmmod without ifconfig down), we
+ * could have a nasty deadlock situation. See hxge_down for details.
+ *
+ */
+ write_lock(&adapter->wtd_lock);
+
+ flush_scheduled_work();
+ unregister_netdev(netdev);
+
+ write_unlock(&adapter->wtd_lock);
+
+ hxge_free_stats(adapter);
+
+ iounmap(adapter->hw.hw_addr);
+ pci_release_regions(pdev);
+
+ free_netdev(netdev);
+
+ pci_disable_device(pdev);
+}
+
+/**
+ * hxge_sw_init - Initialize general software structures (struct hxge_adapter)
+ * @adapter: board private structure to initialize
+ *
+ * hxge_sw_init initializes the Adapter private data structure.
+ * Fields are initialized based on PCI device information and
+ * OS network device settings (MTU size).
+ **/
+
+static int __devinit
+hxge_sw_init(struct hxge_adapter *adapter)
+{
+ int flags;
+ int tcam;
+
+ adapter->max_tdcs = HXGE_MAX_TDCS; /* max Tx DMA channels per blade */
+ adapter->max_rdcs = HXGE_MAX_RDCS; /* max Rx DMA channels per blade */
+
+ adapter->default_block_size = PAGE_SIZE;
+ adapter->num_openers = 0;
+ if (hxge_get_option("intr_type", &adapter->intr_type)) {
+ HXGE_ERR(adapter, "hxge_sw_init: intr_type failed");
+ return -1;
+ }
+
+ /* Hydra supports IP (L4) checksumming in hardware */
+ if (hxge_get_option("chksum", &flags)) {
+ HXGE_ERR(adapter, "hxge_probe: chksum invalid");
+ return -1;
+ }
+
+ /* When Hydra enables HW TCP/UDP checksumming, it's for both Rx and
+ * Tx. There is no finer knob than that. So, when one of them is
+ * disabled via the sw flag, it's still done in HW but ignored and
+ * redone by Linux
+ */
+
+ if (flags) {
+ adapter->flags |= flags;
+ if (adapter->flags & HXGE_TX_CHKSUM_ENABLED)
+ HXGE_DBG(adapter, "hxge_sw_init: HW TCP/UDP TX Chksum Enabled");
+ if (adapter->flags & HXGE_RX_CHKSUM_ENABLED)
+ HXGE_DBG(adapter, "hxge_sw_init: HW TCP/UDP RX Chksum Enabled");
+ }
+
+ /* Get number of Rx dma channels */
+ if (hxge_get_option("rx_dma_channels", &adapter->max_rdcs)) {
+ HXGE_ERR(adapter, "Invalid rx_dma_channels option");
+ return -1;
+ }
+
+
+ /* Check for TCAM enablement */
+ if (hxge_get_option("tcam", &tcam)) {
+ HXGE_ERR(adapter, "tcam failed");
+ return -1;
+ }
+
+ if (!tcam && (adapter->max_rdcs > 1))
+ {
+ HXGE_ERR(adapter, "running with only 1 rx channel; other channels unused if tcam is disabled");
+ adapter->max_rdcs = 1;
+ }
+
+ if (tcam && (adapter->max_rdcs < HXGE_MAX_RDCS)) {
+ HXGE_ERR(adapter,"cannot have tcam enabled and have less than four channels; tcam needs all four channels enabled to work!");
+ return -1;
+ }
+
+ if (tcam) {
+ HXGE_DBG(adapter, "hxge_sw_init: TCAM enabled");
+ adapter->flags |= HXGE_TCAM_ENABLED;
+ }
+
+ if (hxge_get_option("vlan_id", &adapter->vlan_id)) {
+ HXGE_ERR(adapter, "hxge_sw_init: vlan_id failed");
+ return -1;
+ }
+
+ if (hxge_get_option("vlan", &flags)) {
+ HXGE_ERR(adapter, "hxge_sw_init: vlan failed");
+ return -1;
+ }
+
+ if (flags) {
+ HXGE_DBG(adapter, "hxge_sw_init: VLAN enabled");
+ adapter->flags |= HXGE_VLAN_ENABLED;
+ }
+
+ if (hxge_get_option("tx_mark_ints", &adapter->tx_mark_ints)) {
+ HXGE_ERR(adapter, "hxge_sw_init: tx_mark_ints invalid");
+ return -1;
+ }
+
+ hxge_get_tcam_properties(adapter);
+
+ /* Throughput/latency tuning parameters. Can be changed via eththool */
+
+ if (hxge_get_option("rcr_timeout", &adapter->rcr_timeout)) {
+ HXGE_ERR(adapter, "rcr_timeout invalid");
+ return -1;
+ }
+
+ if (hxge_get_option("rcr_threshold", &flags)) {
+ HXGE_ERR(adapter, "rcr_threshold invalid");
+ return -1;
+ }
+ adapter->rcr_threshold = (uint16_t)flags;
+
+ if (hxge_get_option("max_rx_pkts", &flags)) {
+ HXGE_ERR(adapter, "max_rx_pkts invalid");
+ return -1;
+ }
+ adapter->max_rx_pkts = (uint16_t)flags;
+
+ return 0;
+}
+
+
+/**
+ * hxge_open - Called when a network interface is made active
+ * @netdev: network interface device structure
+ *
+ * Returns 0 on success, negative value on failure
+ *
+ * The open entry point is called when a network interface is made
+ * active by the system (IFF_UP). At this point all resources needed
+ * for transmit and receive operations are allocated, the interrupt
+ * handler is registered with the OS, the watchdog timer is started,
+ * and the stack is notified that the interface is ready.
+ **/
+
+static int
+hxge_open (struct net_device *netdev)
+{
+ struct hxge_adapter *hxgep= netdev_priv(netdev);
+ int retval;
+
+ set_bit(HXGE_DEVICE_OPENING, &hxgep->state);
+ clear_bit(HXGE_DEVICE_FATAL, &hxgep->state);
+
+ /* Allocate hxge data structures only if first opener */
+ if (!hxgep->num_openers) {
+ HXGE_DBG(hxgep, "hxge: Allocating I/O buffers and resources");
+
+ if (hxge_alloc_rx (hxgep)) {
+ HXGE_ERR(hxgep, "hxge_open: hxge_alloc_rx failed");
+ return -1;
+ }
+
+ if (hxge_alloc_tx (hxgep)) {
+ HXGE_ERR(hxgep, "hxge_open: hxge_alloc_tx failed");
+ return -1;
+ }
+
+ if (hxge_setup_interrupt(netdev)) {
+ HXGE_ERR(hxgep, "hxge_open: hxge_setup_interrupt failed");
+ return -1;
+ }
+
+ set_bit(HXGE_DEVICE_ALLOCATED, &hxgep->state);
+ }
+
+ /* Bring up the interface */
+ write_lock(&hxgep->wtd_lock);
+ spin_lock(&hxgep->lock);
+ retval = hxge_up(hxgep);
+ spin_unlock(&hxgep->lock);
+ write_unlock(&hxgep->wtd_lock);
+
+ hxgep->num_openers++;
+ if (retval || test_bit(HXGE_DEVICE_FATAL, &hxgep->state))
+ {
+ HXGE_ERR(hxgep, "hxge_open: Fatal error bringing hxge up");
+ hxge_close(netdev);
+ retval = -1;
+ }
+
+ /* We're either 'UP' or quiescent (dead) now */
+
+ clear_bit (HXGE_DEVICE_OPENING, &hxgep->state);
+
+ return retval;
+}
+
+
+/**
+ * hxge_close - Disables a network interface
+ * @netdev: network interface device structure
+ *
+ * Returns 0, this is not allowed to fail
+ *
+ * The close entry point is called when an interface is de-activated
+ * by the OS. The hardware is still under the drivers control, but
+ * needs to be disabled. A global MAC reset is issued to stop the
+ * hardware, and all transmit and receive resources are freed.
+ **/
+
+static int
+hxge_close(struct net_device *netdev)
+{
+ struct hxge_adapter *adapter = netdev_priv(netdev);
+ int already_locked = test_bit(HXGE_DRIVER_REMOVING, &adapter->state);
+
+ set_bit (HXGE_DEVICE_CLOSING, &adapter->state);
+ clear_bit (HXGE_DEVICE_OPENING, &adapter->state);
+
+ /* Bring down the interface */
+ if (!already_locked)
+ write_lock(&adapter->wtd_lock);
+
+ /* Force any work queue functions to just run. It will do nothing,
+ * of course, because the wtd_lock is held by this routine. We just
+ * want to make sure that nothing is outstanding which could run
+ * after the driver is removed. That would be very bad. Also, by
+ * this point, nothing new ever gets scheduled. Need "removing" to
+ * avoid nasty race with linkwatch_event under workqueue thread
+ * attempting to get the rtnl_lock (this is really a bug in Linux
+ * network layer)
+ */
+ if (already_locked)
+ flush_scheduled_work();
+
+
+ spin_lock(&adapter->lock);
+ hxge_down(adapter);
+ spin_unlock(&adapter->lock);
+
+ if (!already_locked)
+ write_unlock(&adapter->wtd_lock);
+
+ /* Free hxge data structures only if last closer */
+
+ adapter->num_openers--;
+ if (!adapter->num_openers) {
+ HXGE_DBG(adapter, "hxge: Freeing I/O buffers and resources");
+
+ /* Free up allocated resouces */
+ hxge_teardown_interrupt(adapter);
+
+ hxge_free_rx(adapter);
+
+ /* Free all Tx data structures */
+ hxge_free_tx(adapter);
+
+ clear_bit(HXGE_DEVICE_ALLOCATED, &adapter->state);
+ }
+
+ /* Back to quiescent (not UP) state */
+
+ clear_bit (HXGE_DEVICE_CLOSING, &adapter->state);
+
+ return 0;
+}
+
+
+/* Reinitialize the device and driver structures. This is a big hammer
+ that will bring down the interface and bring it back up */
+static void hxge_reset_adapter(struct hxge_adapter *hxgep)
+{
+ HXGE_DBG(hxgep, "hxge_reset_adapter called");
+ WARN_ON(in_interrupt());
+
+ /* We are currently being reset. This could happen if there is a
+ separate thread of execution, say, from a user command line like
+ ethtool. Wait till that completes */
+ if (test_and_set_bit(HXGE_DEVICE_RESETTING, &hxgep->state)) {
+ while (test_bit(HXGE_DEVICE_RESETTING, &hxgep->state))
+ msleep(1);
+ return;
+ }
+
+ /* Shutting down adapter trumps resetting the adapter */
+ if (!(test_bit(HXGE_DEVICE_SHUTTINGDOWN, &hxgep->state))) {
+ spin_lock(&hxgep->lock);
+ hxge_down(hxgep);
+ hxge_lif_up (hxgep); /* Maintain hard_errors/etc. counters */
+ spin_unlock(&hxgep->lock);
+ }
+
+ clear_bit(HXGE_DEVICE_RESETTING, &hxgep->state);
+ HXGE_DBG(hxgep, "hxge_reset_adapter done");
+}
+
+
+/* Take down the hxge device. This is a big hammer that will bring down
+ the interface and leave it down until manually brought back up. */
+static void hxge_shutdown_adapter(struct hxge_adapter *hxgep)
+{
+ HXGE_DBG(hxgep, "hxge_shutdown_adapter called");
+ WARN_ON(in_interrupt());
+ set_bit(HXGE_DEVICE_SHUTTINGDOWN, &hxgep->state);
+
+ /* If an hxge_reset_adapter() is in progress, wait for it to
+ complete. This could happen if there is a separate thread
+ of execution, say, from a user command line like ethtool. */
+ while (test_bit(HXGE_DEVICE_RESETTING, &hxgep->state))
+ msleep(1);
+
+ spin_lock(&hxgep->lock);
+ hxge_down(hxgep);
+ spin_unlock(&hxgep->lock);
+
+ /* HXGE_DEVICE_SHUTTINGDOWN only cleared in up/open */
+
+ HXGE_DBG(hxgep, "hxge_shutdown_adapter done");
+}
+
+
+/**
+ * hxge_tx_timeout - Respond to a Tx Hang
+ * @netdev: network interface device structure
+ **/
+
+static void
+hxge_tx_timeout(struct net_device *netdev)
+{
+ struct hxge_adapter *hxgep= netdev_priv(netdev);
+
+ /* Do the reset outside of interrupt context */
+ hxgep->statsp->tx_timeout_cnt++;
+
+ if (netif_queue_stopped(netdev))
+ netif_wake_queue(netdev);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static void
+hxge_work_to_do(struct hxge_adapter *hxgep)
+#else
+static void
+hxge_work_to_do(struct work_struct *work)
+#endif
+{
+ int i;
+ int channel;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)
+ struct hxge_adapter *hxgep = container_of(work, struct hxge_adapter, work_to_do);
+#endif
+
+ HXGE_ERR(hxgep, "hxge_work_to_do called on cpu %d",smp_processor_id());
+ if (!read_trylock(&hxgep->wtd_lock)) {
+ return;
+ }
+
+ for (i = 0; i < MAX_CMD; i++)
+ if (test_and_clear_bit(i, &hxgep->work_q.command)) {
+ switch (i) {
+ case RESET_TX_CHANNEL_0 :
+ case RESET_TX_CHANNEL_1 :
+ case RESET_TX_CHANNEL_2 :
+ case RESET_TX_CHANNEL_3 :
+ channel = i - RESET_TX_CHANNEL_0;
+ HXGE_ERR(hxgep, "hxge_work_to_do: Resetting Tx channel %d", channel);
+ hxge_reset_tx_channel(hxgep, channel);
+ break;
+ case RESET_RX_CHANNEL_0 :
+ case RESET_RX_CHANNEL_1 :
+ case RESET_RX_CHANNEL_2 :
+ case RESET_RX_CHANNEL_3 :
+ channel = i - RESET_RX_CHANNEL_0;
+ HXGE_ERR(hxgep, "hxge_work_to_do: Resetting Rx channel %d", channel);
+ hxge_reset_rx_channel(hxgep, channel);
+ break;
+ case RESET_ADAPTER:
+ HXGE_ERR(hxgep, "hxge_work_to_do: Resetting adapter");
+ hxge_reset_adapter(hxgep);
+ break;
+ case RESET_TDC:
+ HXGE_ERR(hxgep, "hxge_work_to_do: Resetting TDC core");
+ hxge_reset_tdc(hxgep);
+ break;
+ case RESET_RDC:
+ HXGE_ERR(hxgep, "hxge_work_to_do: Resetting RDC core");
+ hxge_reset_rdc(hxgep);
+ break;
+ case RESET_PFC:
+ /* For now, just reset the whole hxge */
+ HXGE_ERR(hxgep, "hxge_work_to_do: Resetting PFC; resetting whole hxge");
+ hxge_reset_adapter(hxgep);
+ break;
+ case RESET_VMAC:
+ /* For now, just reset the whole hxge */
+ HXGE_ERR(hxgep, "hxge_work_to_do: Resetting VMAC; resetting whole hxge");
+ hxge_reset_adapter(hxgep);
+ break;
+ case SHUTDOWN_ADAPTER:
+ HXGE_ERR(hxgep, "hxge_work_to_do: Shutting down adapter");
+ hxge_shutdown_adapter(hxgep);
+ break;
+ default:
+ HXGE_ERR(hxgep, "hxge_work_to_do: Uh? unknown command");
+ break;
+ }
+ }
+ read_unlock(&hxgep->wtd_lock);
+}
+
+
+/* Bypasses the OS structures and linux network stack niceties and directly
+ wacks the adapter. Currently used by ethtool for diagnostic tests where
+ the card might not be "up" but could be in an unknown state */
+void hxge_reset (struct hxge_adapter *hxgep)
+{
+ spin_lock(&hxgep->lock);
+ hxge_disable_adapter(hxgep);
+ hxge_enable_adapter(hxgep);
+ spin_unlock(&hxgep->lock);
+}
+
+/**
+ * hxge_change_mtu - Change the Maximum Transfer Unit
+ * @netdev: network interface device structure
+ * @new_mtu: new value for maximum frame size
+ *
+ * Returns 0 on success, negative on failure
+ **/
+
+static int
+hxge_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct hxge_adapter *hxgep= netdev_priv(dev);
+ int old_mtu = dev->mtu;
+ int is_jumbo;
+ int err = 0;
+
+ HXGE_DBG(hxgep, "hxge_change_mtu : old_mtu=%d, new mtu = %d",old_mtu,new_mtu);
+ if (hxge_get_option("enable_jumbo", &is_jumbo)) {
+ HXGE_ERR(hxgep, "hxge_change_mtu: Could not read enable_jumbo");
+ return -1;
+ }
+
+ if ((new_mtu < ETH_ZLEN) || (new_mtu > MAX_JUMBO_FRAME_SIZE))
+ return -EINVAL;
+ if ((new_mtu > ETH_DATA_LEN) && !is_jumbo)
+ return -EINVAL;
+
+ dev->mtu = new_mtu;
+
+ /* The device is not up or the device is not present. In
+ either case, we set the MTU and just return. The MTU
+ for the hardware interface will be set when the
+ VMAC is initialized */
+
+ if (!netif_running(dev) || !netif_device_present(dev)) {
+ HXGE_DBG(hxgep, "hxge_change_mtu: interface not up");
+ return 0;
+ }
+
+
+ /* It is now a jumbo packet. So, we have to reset the adapter. We
+ * really want to only reset the Tx but we also change the size of the
+ * Rx frame size. So, reset the whole adapter for safety */
+ if (old_mtu > new_mtu) {
+ hxge_reset_adapter(hxgep);
+ }
+ else {
+ netif_stop_queue(dev);
+ hxge_vmac_uninit(hxgep);
+ err = hxge_vmac_init(hxgep);
+ /* If new mtu failed, then revert to old mtu and reinit vmac.
+ * Return failure to the caller
+ */
+ if (err) {
+ HXGE_DBG(hxgep, "hxge_change_mtu: Bad MTU size, returning %d",err);
+ dev->mtu = old_mtu;
+ hxge_vmac_init(hxgep);
+ }
+ netif_wake_queue(dev);
+ }
+
+ if (!err) {
+ if (new_mtu > ETH_DATA_LEN)
+ hxgep->vmac.is_jumbo = TRUE;
+ else
+ hxgep->vmac.is_jumbo = FALSE;
+ }
+
+ return err;
+}
+
+
+static int
+hxge_lb_ioctl(struct hxge_adapter *hxgep, void *data)
+{
+ struct lb_size_info *lb_size = (struct lb_size_info *)data;
+ int cmd = *(int *)data;
+
+ switch (cmd)
+ {
+ case GET_INFO_SIZE:
+ lb_size->size = sizeof(lb_properties);
+ HXGE_ERR(hxgep, "hxge_lb_ioctl: lb_size is %d",lb_size->size);
+ break;
+ case GET_INFO:
+ memcpy((char *)data+sizeof(int),lb_properties,
+ sizeof(lb_properties));
+ break;
+ case GET_LB_MODE:
+ lb_size->size = (uint32_t)hxgep->vmac.loopback;
+ break;
+ case SET_LB_MODE:
+ hxge_set_loopback(hxgep, (lb_size->size) ?TRUE : FALSE);
+ default:
+ HXGE_ERR(hxgep, "hxge_lb_ioctl: Unsupported ioctl 0x%x",cmd);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * hxge_ioctl-
+ * @netdev:
+ * @ifreq:
+ * @cmd:
+ **/
+
+static int
+hxge_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+
+ switch (cmd) {
+ case LB_IOC:
+ if (!test_bit(HXGE_DEVICE_UP, &hxgep->state)) {
+ HXGE_ERR(hxgep, "hxge_ioctl: interface is not up");
+ return -1;
+ }
+ hxge_lb_ioctl(hxgep, ifr->ifr_data);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Polling 'interrupt' - used by things like netconsole to send skbs
+ * without having to re-enable interrupts. It's not called while
+ * the interrupt routine is executing.
+ */
+static void
+hxge_netpoll(struct net_device *netdev)
+{
+
+}
+#endif
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#include "hpi/hpi_vir.h"
+#include "hpi/hpi_rxdma.h"
+#include "hxge.h"
+#include "hxge_peu_hw.h"
+
+extern void hxge_disable_interrupts(struct hxge_adapter *hxgep);
+
+int hxge_read_mac_addr (struct hxge_hw *hw)
+{
+ return 0;
+}
+
+void hxge_check_options(struct hxge_adapter *adapter)
+{
+
+}
+
+/* Get the status of the link */
+int
+hxge_peu_get_link_status(struct hxge_adapter *hxgep)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ cip_link_stat_t link_stat;
+
+ HXGE_REG_RD32(handle, CIP_LINK_STAT, &link_stat.value);
+
+ /* XPCS0 is the XAUI connector, the other two are part of IHL */
+ if (link_stat.bits.xpcs0_link_up)
+ return 1;
+ else
+ return 0;
+}
+
+
+/* See if device error rate exceeded, or if it's ok to continue using
+ * the hxge device. This is sort of a first-order approximation to
+ * an intelligent/heuristical running health check.
+ *
+ * We want to allow for some RealWorld "Stuff Happens" (and it does,
+ * especially in a network environment), but still not let a kroaked
+ * hxge swamp the system with error handling/reporting (and, at 10Gb,
+ * errors can accumulate at a prodigous rate).
+ *
+ * Returns TRUE (non-zero) if ok to continue (e.g., recover/reset the
+ * device/subsystem) using hxge; FALSE (zero) to give up.
+ */
+
+int
+hxge_ok_to_continue(struct hxge_adapter *hxgep)
+{
+ /* Check hard device error rate */
+ if (hxgep->statsp->hard_errors >= HARD_ERROR_THRESHOLD) {
+ if (((hxgep->statsp->hard_errors * HZ * 86400)
+ / (jiffies + 1 - hxgep->ifup_time)) /* +1 no div-by-0 */
+ > HARD_ERROR_RATELIMIT) {
+ return 0;
+ }
+ }
+
+ /* Check soft (generally, "recoverable") device error rate */
+ if (hxgep->statsp->soft_errors >= SOFT_ERROR_THRESHOLD) {
+ if (((hxgep->statsp->soft_errors * HZ * 86400)
+ / (jiffies + 1 - hxgep->ifup_time)) /* +1 no div-by-0 */
+ > SOFT_ERROR_RATELIMIT) {
+ return 0;
+ }
+ }
+
+ /* For now, ignore "line" errors, even though this could
+ * conceivably impose a huge interrupt load on host CPU(s).
+ */
+
+ return (TRUE); /* Error rates within limits, keep going */
+}
+
+int
+hxge_block_reset(struct hxge_adapter *hxgep, int device)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ block_reset_t reset_reg;
+ int count = 5; /* # msecs to wait */
+
+ HXGE_REG_RD32(handle, BLOCK_RESET, &reset_reg.value);
+
+ if (device & LDV_TXDMA) {
+ reset_reg.bits.tdc_rst = 1;
+ }
+ if (device & LDV_RXDMA) {
+ reset_reg.bits.rdc_rst = 1;
+ }
+ if (device & LDV_VMAC) {
+ reset_reg.bits.vmac_rst = 1;
+ }
+ if (device & LDV_PFC) {
+ reset_reg.bits.pfc_rst = 1;
+ }
+
+ HXGE_REG_WR32(handle, BLOCK_RESET, reset_reg.value);
+ while (--count && (reset_reg.bits.tdc_rst || reset_reg.bits.rdc_rst ||
+ reset_reg.bits.vmac_rst || reset_reg.bits.pfc_rst)) {
+ HXGE_REG_RD32(handle, BLOCK_RESET, &reset_reg.value);
+ msleep(1);
+ }
+
+ if (!count) {
+ HXGE_ERR(hxgep, "hxge_block_reset: Reset of PEU blocks did not complete: 0x%8.8x", reset_reg.value);
+ return -1;
+ }
+ return 0;
+}
+
+
+
+
+/* hxge_peu_deverr_init -- Initialize PEU & Device Error interrupt mask
+ *
+ * put here for lack of any other likely place to put it...
+ */
+
+int
+hxge_peu_deverr_init(struct hxge_adapter *hxgep)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ int regv, status=0;
+
+ /* Initialize global PEU-related error handling */
+
+ HXGE_REG_RD32(handle, PEU_INTR_STAT, ®v);
+ if (regv) { /* We're likely doomed if any set! */
+ /* While an interesting case (error flags should probably
+ * not be set), do not count against hxgep->hard_errors */
+ if (regv & ~2) { /* Ignore MSIX_PARERR here */
+ HXGE_ERR(hxgep, "hxge_peu_deverr_init: Error! PEU_INTR_STAT 0x%8.8x hardware error flags set",
+ (unsigned int)regv);
+ status = regv; /* Return error */
+ }
+ }
+
+ /* MSIX_PARERR sometimes erroneously asserted on poweron. If set
+ * here, assume that's the case, and mask it out. We risk missing
+ * a real MSIX_PARERR, but subsequent system reset/reboot should
+ * clear this condition, and re-enable trap on MSIX_PARERR */
+
+ regv = regv & 2; /* Ignore MSIX_PARERR if initially asserted */
+ if (regv) { /* Other than to note that detail in syslog */
+ HXGE_ERR(hxgep, "hxge_peu_deverr_init: MSIX workaround applied");
+ }
+ HXGE_REG_WR32(handle, PEU_INTR_MASK, regv); /* 0 = no disables */
+
+ /* Initialize Device Error interrupt */
+
+ HXGE_REG_RD32(handle, DEV_ERR_STAT, ®v);
+ if (regv) { /* We're in trouble if any set! */
+ /* While an interesting case (error flags should probably
+ * not be set), do not count against hxgep->hard_errors */
+ if (regv & ~1) { /* Ignore MSIX_PARERR->PEU_ERR1 here */
+ HXGE_ERR(hxgep, "hxge_deverr_init: Error! DEV_ERR_STAT 0x%8.8x hardware error flags set",
+ regv);
+ status = regv; /* Return error */
+ }
+ }
+ HXGE_REG_WR32(handle, DEV_ERR_MASK, 0); /* 0 = no disables */
+
+ return status;
+}
+
+
+
+/* General Hydra error interrupt handler
+ *
+ * Called from Device Error Interrupt (ldv 31) service, not RX DMA
+ *
+ * NB: *data is Device Error ldv 31, not an RX DMA channel ldv!
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+irqreturn_t hxge_peu_deverr_intr(int irq, void *data, struct pt_regs *regs)
+#else
+irqreturn_t hxge_peu_deverr_intr(int irq, void *data)
+#endif
+{
+ struct hxge_ldv *ldvp = (struct hxge_ldv *)data; /* Device Error ldv */
+ struct hxge_adapter *hxgep = ldvp->ldgp->hxgep;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ peu_intr_stat_t psts, pclr;
+ int peu_fatal_flag = 0;
+ int regv32; /* Temp/holding 32-bit register value */
+
+ HXGE_REG_RD32(handle, PEU_INTR_STAT, &psts.value);
+
+ HXGE_ERR(hxgep, "hxge_peu_deverr_intr: PEU hardware error interrupt (0x%8.8x)!",
+ (unsigned int)psts.value);
+
+ pclr.value = psts.value;
+
+ /* All "PEU" errors are hard device errors. One might quibble
+ * over tdc_pioacc_err and rdc_pioacc_err being more specifically
+ * accountable under "RX" or "TX" errors (e.g., for ifconfig).
+ */
+
+ hxgep->statsp->hard_errors++;
+
+ if (psts.bits.spc_acc_err) {
+ HXGE_ERR(hxgep, "hxge_peu_errors: spc_acc_err");
+ hxgep->statsp->peu_spc_acc_err++;
+ hxgep->statsp->peu_errors++; /* PEU/generic summary count */
+ pclr.bits.spc_acc_err = 0;
+ }
+
+ if (psts.bits.tdc_pioacc_err) {
+ HXGE_ERR(hxgep, "hxge_peu_errors: tdc_pioacc_err");
+ hxgep->statsp->peu_pioacc_err++;
+ hxgep->statsp->tx_oerrors++; /* Tx summary count */
+ pclr.bits.tdc_pioacc_err = 0;
+ }
+
+ if (psts.bits.rdc_pioacc_err) {
+ HXGE_ERR(hxgep, "hxge_peu_errors: rdc_pioacc_err");
+ hxgep->statsp->peu_pioacc_err++;
+ hxgep->statsp->rx_ierrors++; /* Rx summary count */
+ pclr.bits.rdc_pioacc_err = 0;
+ }
+
+ if (psts.bits.pfc_pioacc_err) {
+ HXGE_ERR(hxgep, "hxge_peu_errors: pfc_pioacc_err");
+ hxgep->statsp->peu_pioacc_err++;
+ hxgep->statsp->peu_errors++; /* PEU/generic summary count */
+ pclr.bits.pfc_pioacc_err = 0;
+ }
+
+ if (psts.bits.vmac_pioacc_err) {
+ HXGE_ERR(hxgep, "hxge_peu_errors: vmac_pioacc_err");
+ hxgep->statsp->peu_pioacc_err++;
+ hxgep->statsp->peu_errors++; /* PEU/generic summary count */
+ pclr.bits.vmac_pioacc_err = 0;
+ }
+
+ if (psts.bits.cpl_hdrq_parerr) {
+ HXGE_ERR(hxgep, "hxge_peu_errors: cpl_hdrq_parerr");
+ hxgep->statsp->peu_pcie_parerr++;
+ hxgep->statsp->peu_errors++; /* PEU/generic summary count */
+ peu_fatal_flag = TRUE; /* PEU unrecoverable error */
+ pclr.bits.cpl_hdrq_parerr = 0;
+ }
+
+ if (psts.bits.cpl_dataq_parerr) {
+ HXGE_ERR(hxgep, "hxge_peu_errors: cpl_dataq_parerr");
+ hxgep->statsp->peu_pcie_parerr++;
+ hxgep->statsp->peu_errors++; /* PEU/generic summary count */
+ peu_fatal_flag = TRUE; /* PEU unrecoverable error */
+ pclr.bits.cpl_dataq_parerr = 0;
+ }
+
+ if (psts.bits.retryram_xdlh_parerr) {
+ HXGE_ERR(hxgep, "hxge_peu_errors: retryram_xdlh_parerr");
+ hxgep->statsp->peu_pcie_parerr++;
+ hxgep->statsp->peu_errors++; /* PEU/generic summary count */
+ peu_fatal_flag = TRUE; /* PEU unrecoverable error */
+ pclr.bits.retryram_xdlh_parerr = 0;
+ }
+
+ if (psts.bits.retrysotram_xdlh_parerr) {
+ HXGE_ERR(hxgep, "hxge_peu_errors: retrysotram_xdlh_parerr");
+ hxgep->statsp->peu_pcie_parerr++;
+ hxgep->statsp->peu_errors++; /* PEU/generic summary count */
+ peu_fatal_flag = TRUE; /* PEU unrecoverable error */
+ pclr.bits.retrysotram_xdlh_parerr = 0;
+ }
+
+ if (psts.bits.p_hdrq_parerr) {
+ HXGE_ERR(hxgep, "hxge_peu_errors: p_hdrq_parerr");
+ hxgep->statsp->peu_pcie_parerr++;
+ hxgep->statsp->peu_errors++; /* PEU/generic summary count */
+ peu_fatal_flag = TRUE; /* PEU unrecoverable error */
+ pclr.bits.p_hdrq_parerr = 0;
+ }
+
+ if (psts.bits.p_dataq_parerr) {
+ HXGE_ERR(hxgep, "hxge_peu_errors: p_dataq_parerr");
+ hxgep->statsp->peu_pcie_parerr++;
+ hxgep->statsp->peu_errors++; /* PEU/generic summary count */
+ peu_fatal_flag = TRUE; /* PEU unrecoverable error */
+ pclr.bits.p_dataq_parerr = 0;
+ }
+
+ if (psts.bits.np_hdrq_parerr) {
+ HXGE_ERR(hxgep, "hxge_peu_errors: np_hdrq_parerr");
+ hxgep->statsp->peu_pcie_parerr++;
+ hxgep->statsp->peu_errors++; /* PEU/generic summary count */
+ peu_fatal_flag = TRUE; /* PEU unrecoverable error */
+ pclr.bits.np_hdrq_parerr = 0;
+ }
+
+ if (psts.bits.np_dataq_parerr) {
+ HXGE_ERR(hxgep, "hxge_peu_errors: np_dataq_parerr");
+ hxgep->statsp->peu_pcie_parerr++;
+ hxgep->statsp->peu_errors++; /* PEU/generic summary count */
+ peu_fatal_flag = TRUE; /* PEU unrecoverable error */
+ pclr.bits.np_dataq_parerr = 0;
+ }
+
+ if (psts.bits.eic_msix_parerr) {
+ HXGE_REG_RD32(handle, MSIX_PERR_LOC, ®v32);
+ HXGE_ERR(hxgep, "hxge_peu_errors: eic_msix_parerr: 0x%8.8x", regv32);
+ hxgep->statsp->peu_hcr_msix_parerr++;
+ hxgep->statsp->peu_errors++; /* PEU/generic summary count */
+ peu_fatal_flag = TRUE; /* PEU unrecoverable error */
+ pclr.bits.eic_msix_parerr = 0;
+ }
+
+ if (psts.bits.hcr_parerr) {
+ HXGE_REG_RD32(handle, HCR_PERR_LOC, ®v32);
+ HXGE_ERR(hxgep, "hxge_peu_errors: hcr_parerr: 0x%8.8x", regv32);
+ hxgep->statsp->peu_hcr_msix_parerr++;
+ hxgep->statsp->peu_errors++; /* PEU/generic summary count */
+ peu_fatal_flag = TRUE; /* PEU unrecoverable error */
+ pclr.bits.hcr_parerr = 0;
+ }
+
+ if (pclr.value) {
+ HXGE_ERR(hxgep, "hxge_peu_deverr_intr: Unknown/unexpected/reserved PEU_INTR_STAT bits 0x%8.8x", pclr.value);
+ }
+
+ /* Now that we have "logged" the errors, try to recover from
+ * whatever happened.
+ *
+ * Unlike lesser errors, PEU_INTR_STAT errors are NOT RW1C bits.
+ * Rather, one must externally (to PEU_INTR_STAT register) clear
+ * the underlying fault conditions.
+ *
+ * Errors in the PEU block are irrecoverable from program con-
+ * trol, the Hydra needs a PCI bus reset (aka, reset/reboot the
+ * host OS). Other errors are -- in theory -- recoverable by
+ * resetting (and reinitializing) the hardware subsystem.
+ */
+
+ /* Check hxge viability */
+
+ if (peu_fatal_flag) {
+ /* Irrecoverable ("FATAL") PEU-class error, time to die. */
+ HXGE_ERR(hxgep, "hxge_peu_deverr_intr: PEU FATAL error");
+ HXGE_ERR(hxgep, " Taking hxge device down");
+ hxge_disable_interrupts(hxgep);
+ set_bit(SHUTDOWN_ADAPTER, &hxgep->work_q.command);
+ schedule_work(&hxgep->work_to_do);
+ } else if (hxge_ok_to_continue(hxgep)) {
+
+ /* Error should/could be recoverable by resetting
+ * subsystem involved; reset and restart Hydra
+ * logic subsystem (resume operation) */
+
+ if (psts.bits.tdc_pioacc_err) {
+ hxge_disable_tx_ints(hxgep);
+ set_bit(RESET_TDC, &hxgep->work_q.command);
+ schedule_work(&hxgep->work_to_do);
+ }
+ if (psts.bits.rdc_pioacc_err) {
+ hxge_disable_rx_ints(hxgep, NULL, -1);
+ set_bit(RESET_RDC, &hxgep->work_q.command);
+ schedule_work(&hxgep->work_to_do);
+ }
+ if (psts.bits.pfc_pioacc_err) {
+ set_bit(RESET_PFC, &hxgep->work_q.command);
+ schedule_work(&hxgep->work_to_do);
+ }
+ if (psts.bits.vmac_pioacc_err) {
+ set_bit(RESET_VMAC, &hxgep->work_q.command);
+ schedule_work(&hxgep->work_to_do);
+ }
+ } else {
+ /* Too many errors, "hxge_shutdown" the hxge device */
+ HXGE_ERR(hxgep, "hxge_peu_deverr_intr: Excessive hardware error rate");
+ HXGE_ERR(hxgep, " Taking hxge device down");
+ hxge_disable_interrupts(hxgep);
+ set_bit(SHUTDOWN_ADAPTER, &hxgep->work_q.command);
+ schedule_work(&hxgep->work_to_do);
+ }
+
+ return (IRQ_HANDLED);
+}
+
+int hxge_link_intr (struct hxge_adapter *hxgep, int cmd)
+{
+ return -1;
+
+}
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+
+#include "hxge.h"
+
+struct hxge_option {
+ enum {range, boolean, range_mod} type;
+ char *name;
+ int val;
+ int def;
+ int min;
+ int max;
+ int mod;
+};
+
+typedef enum {
+ param_enable_jumbo = 0,
+ param_intr_type,
+ param_rbr_entries,
+ param_rcr_entries,
+ param_rcr_timeout,
+ param_rcr_threshold,
+ param_rx_dma_channels,
+ param_tx_dma_channels,
+ param_num_tx_descs,
+ param_tx_buffer_size,
+ param_tx_mark_ints,
+ param_max_rx_pkts,
+ param_vlan_id,
+ param_strip_crc,
+ param_enable_vmac_ints,
+ param_promiscuous,
+ param_chksum,
+ param_vlan,
+ param_tcam,
+ param_tcam_seed,
+ param_tcam_ether_usr1,
+ param_tcam_ether_usr2,
+ param_tcam_tcp_ipv4,
+ param_tcam_udp_ipv4,
+ param_tcam_ipsec_ipv4,
+ param_tcam_stcp_ipv4,
+ param_tcam_tcp_ipv6,
+ param_tcam_udp_ipv6,
+ param_tcam_ipsec_ipv6,
+ param_tcam_stcp_ipv6,
+} hxge_param_t;
+
+
+
+
+#define HXGE_PARAM(X, desc, default_val_addr) \
+ module_param_named(X, default_val_addr, int, 0); \
+ MODULE_PARM_DESC(X, desc);
+
+#define PARAM(x) hxge_arr[x].val
+
+struct hxge_option hxge_arr[] = {
+{ boolean, "enable_jumbo", 1, 0},
+{ range, "intr_type", MSIX_TYPE, MSI_TYPE, INTx_TYPE, POLLING_TYPE},
+{ range_mod, "rbr_entries", HXGE_RBR_RBB_DEFAULT, HXGE_RBR_RBB_DEFAULT, HXGE_RBR_RBB_MIN, HXGE_RBR_RBB_MAX, 64},
+{ range_mod, "rcr_entries", HXGE_RCR_DEFAULT, HXGE_RCR_DEFAULT, HXGE_RCR_MIN, HXGE_RCR_MAX, 32},
+{ range, "rcr_timeout", HXGE_RCR_TIMEOUT, HXGE_RCR_TIMEOUT, HXGE_RCR_TIMEOUT_MIN, HXGE_RCR_TIMEOUT_MAX},
+{ range, "rcr_threshold", HXGE_RCR_THRESHOLD, HXGE_RCR_THRESHOLD, HXGE_RCR_THRESHOLD_MIN, HXGE_RCR_THRESHOLD_MAX},
+{ range, "rx_dma_channels", HXGE_MAX_RDCS, HXGE_MAX_RDCS, HXGE_MIN_RDCS, HXGE_MAX_RDCS},
+{ range, "tx_dma_channels", HXGE_MAX_TDCS, HXGE_MAX_TDCS, HXGE_MIN_TDCS, HXGE_MAX_TDCS},
+{ range_mod, "num_tx_descs", HXGE_TX_DESCS_DEFAULT, HXGE_TX_DESCS_DEFAULT, HXGE_TX_DESCS_MIN, HXGE_TX_DESCS_MAX, 32},
+{ range, "tx_buffer_size", HXGE_TX_BUF_SZ_MIN, HXGE_TX_BUF_SZ_MIN, HXGE_TX_BUF_SZ_MIN, HXGE_TX_BUF_SZ_MAX},
+{ range, "tx_mark_ints", 32, 16, 1, HXGE_TX_DESCS_DEFAULT/4},
+{ range, "max_rx_pkts", HXGE_MAX_RX_PKTS, HXGE_MAX_RX_PKTS, 0, HXGE_MAX_RX_PKTS_MAX},
+{ range, "vlan_id", VLAN_ID_MIN, VLAN_ID_IMPLICIT, VLAN_ID_MIN, VLAN_ID_MAX},
+{ boolean, "strip_crc", 0, 0},
+{ boolean, "enable_vmac_ints", 0, 0},
+{ boolean, "promiscuous", 0, 0},
+{ range, "chksum", HXGE_CHKSUM_ENABLED, HXGE_CHKSUM_ENABLED, 0, HXGE_CHKSUM_ENABLED },
+{ boolean, "vlan", 1, 1},
+{ boolean, "tcam", 1, 1},
+{ range, "tcam_seed", 0x2323433, 0, 0, 0x7fffffff},
+{ range, "tcam_ether_usr1", 0, 0x40000, 0, 0x4ffff},
+{ range, "tcam_ether_usr2", 0, 0x40000, 0, 0x4ffff},
+{ range, "tcam_tcp_ipv4", 1, 1, 0, 2},
+{ range, "tcam_udp_ipv4", 1, 1, 0, 2},
+{ range, "tcam_ipsec_ipv4", 0, 1, 0, 2},
+{ range, "tcam_stcp_ipv4", 0, 1, 0, 2},
+{ range, "tcam_tcp_ipv6", 0, 1, 0, 2},
+{ range, "tcam_udp_ipv6", 0, 1, 0, 2},
+{ range, "tcam_ipsec_ipv6", 0, 1, 0, 2},
+{ range, "tcam_stcp_ipv6", 0, 1, 0, 2}
+};
+
+HXGE_PARAM(enable_jumbo, "enable jumbo packets", PARAM(param_enable_jumbo));
+HXGE_PARAM(intr_type, "Interrupt type (INTx=0, MSI=1, MSIx=2, Polling=3)", PARAM(param_intr_type));
+HXGE_PARAM(rbr_entries, "No. of RBR Entries", PARAM(param_rbr_entries));
+HXGE_PARAM(rcr_entries, "No. of RCR Entries", PARAM(param_rcr_entries));
+HXGE_PARAM(rcr_timeout, "RCR Timeout", PARAM(param_rcr_timeout));
+HXGE_PARAM(rcr_threshold, "RCR Threshold", PARAM(param_rcr_threshold));
+HXGE_PARAM(rx_dma_channels, "No. of Rx DMA Channels", PARAM(param_rx_dma_channels));
+HXGE_PARAM(tx_dma_channels, "No. of Tx DMA Channels", PARAM(param_tx_dma_channels));
+HXGE_PARAM(num_tx_descs, "No. of Tx Descriptors", PARAM(param_num_tx_descs));
+HXGE_PARAM(tx_buffer_size, "No. of Tx Buffers", PARAM(param_tx_buffer_size));
+HXGE_PARAM(tx_mark_ints, "Tx packets before getting marked interrupt", PARAM(param_tx_mark_ints));
+HXGE_PARAM(max_rx_pkts, "Max Rx Packets", PARAM(param_max_rx_pkts));
+HXGE_PARAM(vlan_id, "Implicit VLAN ID", PARAM(param_vlan_id));
+HXGE_PARAM(strip_crc, "Strip CRC at VMAC (0=disable, 1=enable)", PARAM(param_strip_crc));
+HXGE_PARAM(enable_vmac_ints, "Enable VMAC Interrupt Processing(0=disable, 1=enable)", PARAM(param_enable_vmac_ints));
+HXGE_PARAM(promiscuous, "Enable promiscuous mode (0=disable, 1=enable)",PARAM(param_promiscuous));
+HXGE_PARAM(chksum, "Enable HW Checksum(0=disable, 1=enable)",PARAM(param_chksum));
+HXGE_PARAM(vlan, "Enable VLAN(0=disable, 1=enable)",PARAM(param_vlan));
+HXGE_PARAM(tcam, "Enable TCAM(0=disable, 1=enable)",PARAM(param_tcam));
+HXGE_PARAM(tcam_seed, "Source hash seed",PARAM(param_tcam_seed));
+HXGE_PARAM(tcam_ether_usr1, "EtherType Class usr1",PARAM(param_tcam_ether_usr1));
+HXGE_PARAM(tcam_ether_usr2, "EtherType Class usr2",PARAM(param_tcam_ether_usr2));
+HXGE_PARAM(tcam_tcp_ipv4, "TCP over IPv4 class",PARAM(param_tcam_tcp_ipv4));
+HXGE_PARAM(tcam_udp_ipv4, "UDP over IPv4 class",PARAM(param_tcam_udp_ipv4));
+HXGE_PARAM(tcam_ipsec_ipv4, "IPSec over IPv4 class",PARAM(param_tcam_ipsec_ipv4));
+HXGE_PARAM(tcam_stcp_ipv4, "STCP over IPv4 class",PARAM(param_tcam_stcp_ipv4));
+HXGE_PARAM(tcam_tcp_ipv6, "TCP over IPv6 class",PARAM(param_tcam_tcp_ipv6));
+HXGE_PARAM(tcam_udp_ipv6, "UDP over IPv6 class",PARAM(param_tcam_udp_ipv6));
+HXGE_PARAM(tcam_ipsec_ipv6, "IPsec over IPv6 class",PARAM(param_tcam_ipsec_ipv6));
+HXGE_PARAM(tcam_stcp_ipv6, "STCP over IPv6 class",PARAM(param_tcam_stcp_ipv6));
+
+/* Find the value corresponding to the option name. Return -1 for
+ * failure */
+
+static struct hxge_option *find_option(const char *option_name)
+{
+ int i;
+ for (i = 0; i < (sizeof(hxge_arr)/sizeof(struct hxge_option)); i++)
+ if (!strcmp(hxge_arr[i].name, option_name))
+ return &hxge_arr[i];
+ return NULL;
+}
+
+int hxge_get_option(const char *option_name, int *value)
+{
+ struct hxge_option *option = find_option(option_name);
+
+ if (option) {
+ *value = option->val;
+ return 0;
+ }
+ return -1;
+}
+
+int hxge_set_option(const char *option_name, int value)
+{
+ struct hxge_option *option = find_option(option_name);
+ int orig_value;
+
+ if (!option) {
+ HXGE_ERR_PRINT("Illegal option name");
+ return -1;
+ }
+
+ orig_value = option->val;
+ HXGE_DBG_PRINT("Setting %s to %d",option_name, value);
+ switch (option->type) {
+ case boolean :
+ option->val = (value) ? 1 : 0;
+ break;
+ case range_mod:
+ if (value % option->mod) {
+ HXGE_ERR_PRINT("value %d for %s is not multiple of %d", value, option_name, option->mod);
+ return -1;
+ }
+ /* fall through */
+ case range :
+ if ((value < option->min) || (value > option->max)) {
+ HXGE_ERR_PRINT("value %d for %s out of range; min=%d, max=%d",value,option->name,option->min, option->max);
+ return -1;
+ }
+ option->val = value;
+ break;
+ default:
+ HXGE_ERR_PRINT("Illegal option type");
+ return -1;
+ }
+
+ return orig_value;
+}
+
+
+int hxge_init_param (struct hxge_adapter *hxgep)
+{
+ int i;
+ int entries = (sizeof(hxge_arr)/sizeof(struct hxge_option));
+ int val;
+
+ hxgep->param = hxge_arr;
+ /* validate options; leverage set function to do validation */
+ HXGE_DBG(hxgep, "hxge_init_param: %d parameters", entries);
+ for (i = 0; i < entries; i++) {
+ if (hxge_get_option(hxge_arr[i].name, &val)) {
+ HXGE_ERR(hxgep, "hxge_init_param: failed param validation");
+ return -1;
+ }
+ if (hxge_set_option(hxge_arr[i].name, val) < 0)
+ return -1;
+ }
+
+
+ return 0;
+}
+
+/* Restore default values to all driver parameters */
+void hxge_restore_defaults(struct hxge_adapter *hxgep)
+{
+ struct hxge_option *hxge_optp;
+ int len = (sizeof(hxge_arr)/sizeof(struct hxge_option));
+ int i;
+
+ for (hxge_optp = hxgep->param, i = 0; i < len; i++, hxge_optp++)
+ hxge_optp->val = hxge_optp->def;
+}
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#ifndef _HXGE_PEU_HW_H
+#define _HXGE_PEU_HW_H
+
+#define PIO_BASE_ADDR 0X000000
+#define PIO_LDSV_BASE_ADDR 0X800000
+#define PIO_LDMASK_BASE_ADDR 0XA00000
+
+#define HCR_REG (PIO_BASE_ADDR + 0x2000)
+#define BLOCK_RESET (PIO_BASE_ADDR + 0x8000)
+#define PEU_INTR_STAT (PIO_BASE_ADDR + 0x8148)
+#define PEU_INTR_MASK (PIO_BASE_ADDR + 0x814C)
+#define MSIX_PERR_LOC (PIO_BASE_ADDR + 0x8174)
+#define HCR_PERR_LOC (PIO_BASE_ADDR + 0x8178)
+#define PHY_DEBUG_TRAINING_VEC (PIO_BASE_ADDR + 0x80F4)
+#define PEU_DEBUG_TRAINING_VEC (PIO_BASE_ADDR + 0x80F8)
+#define DEV_ERR_STAT (PIO_BASE_ADDR + 0x8380)
+#define DEV_ERR_MASK (PIO_BASE_ADDR + 0x8384)
+#define CIP_LINK_STAT (PIO_BASE_ADDR + 0x801C)
+#define LD_GRP_CTRL (PIO_BASE_ADDR + 0x8300)
+#define LD_INTR_TIM_RES (PIO_BASE_ADDR + 0x8390)
+#define LDSV0 (PIO_LDSV_BASE_ADDR + 0x0)
+#define LDSV1 (PIO_LDSV_BASE_ADDR + 0x4)
+#define LD_INTR_MASK (PIO_LDMASK_BASE_ADDR + 0x0)
+#define LD_INTR_MGMT (PIO_LDMASK_BASE_ADDR + 0x4)
+#define SID (PIO_LDMASK_BASE_ADDR + 0x8)
+
+/*
+ * Register: DevErrStat DEV_ERR_STAT (PIO_BASE_ADDR + 0x8380)
+ * also: DevErrMask DEV_ERR_MASK (PIO_BASE_ADDR + 0x8384)
+ * Device Error Status / Mask
+ * Description: Device Error Status logs errors that cannot be
+ * attributed to a given dma channel. It does not duplicate errors
+ * already observable via specific block logical device groups.
+ * Device Error Status bits [31:16] feed LDSV0.devErr0 Device Error
+ * Status bits [15:0] feed LDSV1.devErr1
+ * Fields:
+ * Set to 1 if Reorder Buffer/Reorder Table has a single bit
+ * ecc/parity error. This error condition is asserted by TDC to
+ * PEU.
+ * Set to 1 if RX Ctrl or Data FIFO has a single bit ecc error.
+ * This error condition is asserted by RDC to PEU.
+ * Set to 1 if any of the external block accesses have resulted
+ * in error or if a parity error was detected in the SPROM
+ * internal ram. Refer to peuIntrStat for the errors that
+ * contribute to this bit.
+ * Set to 1 if Reorder Buffer/Reorder Table has a double bit
+ * ecc/parity error. This error condition is asserted by TDC to
+ * PEU.
+ * Set to 1 if RX Ctrl or Data FIFO has a double bit ecc error.
+ * This error condition is asserted by RDC to PEU.
+ * Set to 1 if any PEU ram (MSI-X, retrybuf/sot, p/np/cpl queues)
+ * has a parity error Refer to peuIntrStat for the errors that
+ * contribute to this bit.
+ */
+
+typedef union {
+ uint32_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint32_t rsrvd:13;
+ uint32_t tdc_err0:1;
+ uint32_t rdc_err0:1;
+ uint32_t rsrvd1:1;
+ uint32_t rsrvd2:12;
+ uint32_t vnm_pio_err1:1;
+ uint32_t tdc_err1:1;
+ uint32_t rdc_err1:1;
+ uint32_t peu_err1:1;
+#else
+ uint32_t peu_err1:1;
+ uint32_t rdc_err1:1;
+ uint32_t tdc_err1:1;
+ uint32_t vnm_pio_err1:1;
+ uint32_t rsrvd2:12;
+ uint32_t rsrvd1:1;
+ uint32_t rdc_err0:1;
+ uint32_t tdc_err0:1;
+ uint32_t rsrvd:13;
+#endif
+ } bits;
+} dev_err_stat_t;
+
+
+
+/*
+ * Register: BlockReset BLOCK_RESET (PIO_BASE_ADDR + 0x8000)
+ * Block Reset
+ * Description: Soft resets to modules. Blade domain modules are
+ * reset by setting the corresponding block reset to 1. Shared domain
+ * resets are sent to SPI for processing and corresponding action by
+ * SPI. Shared domains are reset only if all the blades have
+ * requested a reset for that block. Below is an example scenario :
+ * s/w initiates the reset by writing '1' to the dpmRst bit dpmRst
+ * bit remains '1' until dpmRstStat is detected to be 1. Once
+ * dpmRstStat is detected to be 1, even if s/w writes 1 to this bit
+ * again no new reset will be initiated to the shared domain, ie,
+ * DPM. dpmRstStat is driven by external i/f (shared domain status
+ * provided by SPI) dpmRstStat bit will show '1' as long as the input
+ * stays at 1 or until s/w reads the status and is cleared only after
+ * s/w reads it and if dpmRstStat is 0 by then.
+ * If Host wants to reset entire Hydra it should do so through the
+ * mailbox. In this case, the message interprettation is upto the
+ * software. Writing a '1' to any of these bits generates a single
+ * pulse to the SP module which then controls the reset of the
+ * respective block.
+ *
+ * Fields:
+ * 1 : indicates that an active reset has been applied to the SP
+ * based on the request from all of the blades. Clears on Read
+ * provided the reset to SP has been deasserted by then by SPI.
+ * Setting to 1 allows this blade to request Service Processor
+ * (Shared) reset. However, SP reset can only occur if all blades
+ * agree. The success of reset request is indicated by spRstStat
+ * = 1 which is wired-AND of request from all the blades. Current
+ * request can be removed by writing a '0' to this bit. This bit
+ * clears automatically on detecting spRstStat = 1.
+ * Enable blade to service processor (Shared) reset voter
+ * registration = 1, disabled = 0
+ * Issue power reset to the EP Core Clears to 0, writing 0 has no
+ * effect.
+ * Issue core reset to the EP Core Clears to 0, writing 0 has no
+ * effect.
+ * Issue system reset (sysPor) to the PIPE Core This issues reset
+ * to the EP core, PCIe domains of Tdc, Rdc, and CIP. This shuts
+ * down the PCIe clock until Pipe core comes out of reset. The
+ * status of the Pipe core can be read by reading out the
+ * cipLinkStat register's pipe core status and pcie reset status
+ * bits. Clears to 0, writing 0 has no effect.
+ * 1 : indicates that an active reset has been applied to the
+ * NMAC based on the request from all of the blades. Clears on
+ * Read provided the reset to NMAC has been deasserted by then by
+ * SPI.
+ * 1 : indicates that an active reset has been applied to the TDP
+ * based on the request from all of the blades. Clears on Read
+ * provided the reset to TDP has been deasserted by then by SPI.
+ * 1 : indicates that an active reset has been applied to the DPM
+ * based on the request from all of the blades. Clears on Read
+ * provided the reset to DPM has been deasserted by then by SPI.
+ * This bit is effective only if sharedVoterEn (bit 24 of this
+ * reg) has been enabled. Writing '1' sends a request to SP to
+ * reset NMAC if sharedVoterEn=1. Intended for backdoor access.
+ * The success of reset request is indicated by nmacRstStat = 1
+ * which is wired-AND of request from all the blades. This also
+ * means that the reset request is successful only if all the
+ * blades requested for reset of this block. Current request can
+ * be removed by writing a '0' to this bit. This bit clears
+ * automatically on detecting nmacRstStat = 1.
+ * This bit is effective only if sharedVoterEn (bit 24 of this
+ * reg) has been enabled. Writing '1' sends a request to SP to
+ * reset TDP if sharedVoterEn=1. Intended for backdoor access.
+ * Intended for backdoor access. The success of reset request is
+ * indicated by tdpRstStat = 1 which is wired-AND of request from
+ * all the blades. This also means that the reset request is
+ * successful only if all the blades requested for reset of this
+ * block. Current request can be removed by writing a '0' to this
+ * bit. This bit clears automatically on detecting tdpRstStat =
+ * 1.
+ * This bit is effective only if sharedVoterEn (bit 24 of this
+ * reg) has been enabled. Writing '1' sends a request to SP to
+ * reset DPM if sharedVoterEn=1. Intended for backdoor access.
+ * Intended for backdoor access. The success of reset request is
+ * indicated by dpmRstStat = 1 which is wired-AND of request from
+ * all the blades. This also means that the reset request is
+ * successful only if all the blades requested for reset of this
+ * block. Current request can be removed by writing a '0' to this
+ * bit. This bit clears automatically on detecting dpmRstStat =
+ * 1.
+ * Setting to 1 generates tdcCoreReset and tdcPcieReset to the
+ * TDC block. The reset will stay asserted for atleast 4 clock
+ * cycles. Clears to 0, writing 0 has no effect.
+ * Setting to 1 generates rdcCoreReset and rdcPcieReset to the
+ * RDC block. The reset will stay asserted for atleast 4 clock
+ * cycles. Clears to 0, writing 0 has no effect.
+ * Setting to 1 generates reset to the PFC block. The reset will
+ * stay asserted for atleast 4 clock cycles. Clears to 0, writing
+ * 0 has no effect.
+ * Setting to 1 generates reset to the VMAC block. The reset will
+ * stay asserted for atleast 4 clock cycles. Clears to 0, writing
+ * 0 has no effect.
+ */
+typedef union {
+ uint32_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint32_t rsrvd:13;
+ uint32_t sp_rst_stat:1;
+ uint32_t sp_rst:1;
+ uint32_t shared_voter_en:1;
+ uint32_t epcore_pwr_rst:1;
+ uint32_t epcore_core_rst:1;
+ uint32_t pipe_sys_rst:1;
+ uint32_t nmac_rst_stat:1;
+ uint32_t tdp_rst_stat:1;
+ uint32_t dpm_rst_stat:1;
+ uint32_t rsrvd1:1;
+ uint32_t nmac_rst:1;
+ uint32_t tdp_rst:1;
+ uint32_t dpm_rst:1;
+ uint32_t rsrvd2:1;
+ uint32_t tdc_rst:1;
+ uint32_t rdc_rst:1;
+ uint32_t pfc_rst:1;
+ uint32_t vmac_rst:1;
+ uint32_t rsrvd3:1;
+#else
+ uint32_t rsrvd3:1;
+ uint32_t vmac_rst:1;
+ uint32_t pfc_rst:1;
+ uint32_t rdc_rst:1;
+ uint32_t tdc_rst:1;
+ uint32_t rsrvd2:1;
+ uint32_t dpm_rst:1;
+ uint32_t tdp_rst:1;
+ uint32_t nmac_rst:1;
+ uint32_t rsrvd1:1;
+ uint32_t dpm_rst_stat:1;
+ uint32_t tdp_rst_stat:1;
+ uint32_t nmac_rst_stat:1;
+ uint32_t pipe_sys_rst:1;
+ uint32_t epcore_core_rst:1;
+ uint32_t epcore_pwr_rst:1;
+ uint32_t shared_voter_en:1;
+ uint32_t sp_rst:1;
+ uint32_t sp_rst_stat:1;
+ uint32_t rsrvd:13;
+#endif
+ } bits;
+} block_reset_t;
+
+
+/*
+ * Register: CipLinkStat CIP_LINK_STAT (PIO_BASE_ADDR + 0x801C)
+ * Link Status Register
+ * Description: This register returns the Link status
+ * Fields:
+ * NMAC XPCS-2 Link Status
+ * NMAC XPCS-1 Link Status
+ * NMAC XPCS-0 Link Status
+ * '1' indicates that pipe core went down suddenly when its reset
+ * sources are at deactivated level. When this happens, the PCIe
+ * domain logics are reset including the EP core, TDC/RDC PCIe
+ * domains. All these logics, EP Core, and the pipe core are held
+ * at reset until s/w writes 1 to this bit to clear status which
+ * will also bring the PCIe domain out of reset
+ * pipe core clock & reset status 1: core is up & running, ie,
+ * PIPE core is out of reset and clock is ON
+ * PCIe domain reset status 1: PCIe domain logics including EP
+ * core are out of reset; This also implies that PCIe clock is up
+ * and running
+ * EP Core XDM Link State
+ * EP Core RDM Link State
+ * EP Core LTSSM State
+ */
+typedef union {
+ uint32_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint32_t rsrvd:13;
+ uint32_t xpcs2_link_up:1;
+ uint32_t xpcs1_link_up:1;
+ uint32_t xpcs0_link_up:1;
+ uint32_t rsrvd1:6;
+ uint32_t surprise_pipedn:1;
+ uint32_t pipe_core_stable:1;
+ uint32_t pcie_domain_stable:1;
+ uint32_t xmlh_link_up:1;
+ uint32_t rdlh_link_up:1;
+ uint32_t xmlh_ltssm_state:5;
+#else
+ uint32_t xmlh_ltssm_state:5;
+ uint32_t rdlh_link_up:1;
+ uint32_t xmlh_link_up:1;
+ uint32_t pcie_domain_stable:1;
+ uint32_t pipe_core_stable:1;
+ uint32_t surprise_pipedn:1;
+ uint32_t rsrvd1:6;
+ uint32_t xpcs0_link_up:1;
+ uint32_t xpcs1_link_up:1;
+ uint32_t xpcs2_link_up:1;
+ uint32_t rsrvd:13;
+#endif
+ } bits;
+} cip_link_stat_t;
+
+
+/*
+ * Register: PeuIntrStat PEU_INTR_STAT (PIO_BASE_ADDR + 0x8148)
+ * also: PeuIntrMask PEU_INTR_MASK (PIO_BASE_ADDR + 0x814C)
+ * PEU Interrupt Status / Mask
+ * Description: Returns the parity error status of all of the PEU
+ * RAMs, and external (to peu) block pio access errors. External
+ * block pio access errors could be due to either host or SPI
+ * initiated accesses. These fields are RO and can be cleared only
+ * through a cip reset All these errors feed to devErrStat.peuErr1
+ * which in turn feed to LDSV1.devErr1
+ * Partity Error bits: These bits log the very first parity error
+ * detected in a particular memory. The corresponding memory location
+ * is logged in respective perrLoc registers. External Block PIO
+ * Access Error bits: These bits log the very first error that
+ * resulted in access error. The corresponding address is logged in
+ * respective accErrLog registers.
+ * These bits can be set by writing a '1' to the corresponding
+ * mirror bit in the peuIntrStatMirror register.
+ * Note: PEU RAM Parity Errors and their corresponding interrupt:
+ * When these bits are set and the device error status interrupt is
+ * not masked, the PEU attempts to send the corresponding interrupt
+ * back to the RC. Depending on which ram is impacted and the
+ * corresponding logic impacted in the EP core, a coherent interrupt
+ * message may not be sent in all cases. For the times when the EP
+ * core is unable to send an interrupt, the SPI interface is to be
+ * used for error diagnosis as the PEU interrupt status is logged
+ * regardless of whether the interrupt is sent to the RC. The
+ * following data was collected via simulation: -Parity error
+ * impacted rams that likely will be able to send an interrupt:
+ * npDataq, pDataq, cplDataq, hcr. -Parity error impacted rams that
+ * may not be able to send an interrupt: npHdrq, pHdrq, cplHdrq, MSIx
+ * table, retryram, retrysot.
+ *
+ * Fields:
+ * Error indication from SPROM Controller for Sprom Download
+ * access This error indicates that a parity error was detected
+ * from SRAM. For more details, please refer to SPROM Controller
+ * PRM.
+ * Error indication from TDC for PIO access The error location
+ * and type are logged in tdcPioaccErrLog
+ * Error indication from RDC for PIO access The error location
+ * and type are logged in rdcPioaccErrLog
+ * Error indication from PFC for PIO access The error location
+ * and type are logged in pfcPioaccErrLog
+ * Error indication from VMAC for PIO access The error location
+ * and type are logged in vmacPioaccErrLog
+ * memory in PCIe data path and value unknown until packet flow
+ * starts.
+ * memory in PCIe data path and value unknown until packet flow
+ * starts.
+ * memory in PCIe data path and value unknown until packet flow
+ * starts.
+ * memory in PCIe data path and value unknown until packet flow
+ * starts.
+ * memory in PCIe data path and value unknown until packet flow
+ * starts.
+ * memory in PCIe data path and value unknown until packet flow
+ * starts.
+ * memory in PCIe data path and value unknown until packet flow
+ * starts.
+ * memory in PCIe data path and value unknown until packet flow
+ * starts.
+ */
+typedef union {
+ uint32_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint32_t rsrvd:11;
+ uint32_t spc_acc_err:1;
+ uint32_t tdc_pioacc_err:1;
+ uint32_t rdc_pioacc_err:1;
+ uint32_t pfc_pioacc_err:1;
+ uint32_t vmac_pioacc_err:1;
+ uint32_t rsrvd1:6;
+ uint32_t cpl_hdrq_parerr:1;
+ uint32_t cpl_dataq_parerr:1;
+ uint32_t retryram_xdlh_parerr:1;
+ uint32_t retrysotram_xdlh_parerr:1;
+ uint32_t p_hdrq_parerr:1;
+ uint32_t p_dataq_parerr:1;
+ uint32_t np_hdrq_parerr:1;
+ uint32_t np_dataq_parerr:1;
+ uint32_t eic_msix_parerr:1;
+ uint32_t hcr_parerr:1;
+#else
+ uint32_t hcr_parerr:1;
+ uint32_t eic_msix_parerr:1;
+ uint32_t np_dataq_parerr:1;
+ uint32_t np_hdrq_parerr:1;
+ uint32_t p_dataq_parerr:1;
+ uint32_t p_hdrq_parerr:1;
+ uint32_t retrysotram_xdlh_parerr:1;
+ uint32_t retryram_xdlh_parerr:1;
+ uint32_t cpl_dataq_parerr:1;
+ uint32_t cpl_hdrq_parerr:1;
+ uint32_t rsrvd1:6;
+ uint32_t vmac_pioacc_err:1;
+ uint32_t pfc_pioacc_err:1;
+ uint32_t rdc_pioacc_err:1;
+ uint32_t tdc_pioacc_err:1;
+ uint32_t spc_acc_err:1;
+ uint32_t rsrvd:11;
+#endif
+ } bits;
+} peu_intr_stat_t;
+
+/*
+ * Register: PhyDebugTrainingVec
+ * peuPhy Debug Training Vector
+ * Description: peuPhy Debug Training Vector register.
+ * Fields:
+ * Hard-coded value for peuPhy wrt global debug training block
+ * signatures.
+ * Blade Number, the value read depends on the blade this block
+ * resides
+ * debug training vector the sub-group select value of 0 selects
+ * this vector
+ */
+typedef union {
+ uint32_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint32_t dbg_msb:1;
+ uint32_t bld_num:3;
+ uint32_t phydbg_training_vec:28;
+#else
+ uint32_t phydbg_training_vec:28;
+ uint32_t bld_num:3;
+ uint32_t dbg_msb:1;
+#endif
+ } bits;
+} phy_debug_training_vec_t;
+
+/*
+ * Register: PeuDebugTrainingVec
+ * PEU Debug Training Vector
+ * Description: PEU Debug Training Vector register.
+ * Fields:
+ * Hard-coded value for PEU (VNMy - core clk domain) wrt global
+ * debug training block signatures.
+ * Blade Number, the value read depends on the blade this block
+ * resides
+ * debug training vector the sub-group select value of 0 selects
+ * this vector
+ * Hard-coded value for PEU (VNMy - core clk domain) wrt global
+ * debug training block signatures.
+ * Blade Number, the value read depends on the blade this block
+ * resides
+ * debug training vector the sub-group select value of 0 selects
+ * this vector
+ */
+typedef union {
+ uint32_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint32_t dbgmsb_upper:1;
+ uint32_t bld_num_upper:3;
+ uint32_t peudbg_upper_training_vec:12;
+ uint32_t dbgmsb_lower:1;
+ uint32_t bld_num_lower:3;
+ uint32_t peudbg_lower_training_vec:12;
+#else
+ uint32_t peudbg_lower_training_vec:12;
+ uint32_t bld_num_lower:3;
+ uint32_t dbgmsb_lower:1;
+ uint32_t peudbg_upper_training_vec:12;
+ uint32_t bld_num_upper:3;
+ uint32_t dbgmsb_upper:1;
+#endif
+ } bits;
+} peu_debug_training_vec_t;
+
+/*
+ * Register: SID
+ * System Interrupt Data
+ * Description: System Interrupt Data (MSI Vectors)
+ * Fields:
+ * Data sent along with the interrupt
+ */
+typedef union {
+ uint32_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint32_t rsrvd:27;
+ uint32_t data:5;
+#else
+ uint32_t data:5;
+ uint32_t rsrvd:27;
+#endif
+ } bits;
+} sid_t;
+
+/*
+ * Register: LdIntrTimRes
+ * Logical Device Interrupt Timer Resolution
+ * Description: Logical Device Interrupt Timer Resolution
+ * Fields:
+ * Timer resolution in 250 MHz cycles
+ */
+typedef union {
+ uint32_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint32_t rsrvd:12;
+ uint32_t res:20;
+#else
+ uint32_t res:20;
+ uint32_t rsrvd:12;
+#endif
+ } bits;
+} ld_intr_tim_res_t;
+
+/*
+ * Register: LdIntrMask
+ * Logical Device Interrupt Mask
+ * Description: Logical Device Interrupt Mask
+ * Fields:
+ * Flag1 mask for logical device N (0-31)
+ * Flag0 mask for logical device N (0-31)
+ */
+typedef union {
+ uint32_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint32_t rsrvd:30;
+ uint32_t ldf1_mask:1;
+ uint32_t ldf0_mask:1;
+#else
+ uint32_t ldf0_mask:1;
+ uint32_t ldf1_mask:1;
+ uint32_t rsrvd:30;
+#endif
+ } bits;
+} ld_intr_mask_t;
+
+
+/*
+ * Register: LdIntrMgmt
+ * Logical Device Interrupt Management
+ * Description: Logical Device Interrupt Management
+ * Fields:
+ * SW arms the logical device for interrupt. Cleared by HW after
+ * interrupt issued. (1 = arm)
+ * Timer set by SW. Hardware counts down.
+ */
+typedef union {
+ uint32_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint32_t arm:1;
+ uint32_t rsrvd:25;
+ uint32_t timer:6;
+#else
+ uint32_t timer:6;
+ uint32_t rsrvd:25;
+ uint32_t arm:1;
+#endif
+ } bits;
+} ld_intr_mgmt_t;
+
+
+/*
+ * Register: LdGrpCtrl
+ * Logical Device Group Control
+ * Description: LD Group assignment
+ * Fields:
+ * Logical device group number of this logical device
+ */
+typedef union {
+ uint32_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint32_t rsrvd:27;
+ uint32_t num:5;
+#else
+ uint32_t num:5;
+ uint32_t rsrvd:27;
+#endif
+ } bits;
+} ld_grp_ctrl_t;
+
+
+
+#define HCR_ADDR_LO 0xC
+#define HCR_ADDR_HI 0x10
+
+
+
+
+#endif /* _HXGE_PEU_HW_H */
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#include "hxge.h"
+#include "hpi/hpi_pfc.h"
+
+
+/* Static prototypes */
+static int hxge_pfc_set_mac_address(struct hxge_adapter *, uint32_t,
+ pfc_mac_addr_t ether_addr);
+int hxge_vmac_promisc(struct hxge_adapter *, int);
+
+
+int
+hxge_get_tcam_properties(struct hxge_adapter *hxgep)
+{
+ tcam_class_t class;
+ p_hxge_class_pt_cfg_t class_config = &hxgep->class_config;
+
+ if (hxge_get_option("tcam_seed", &class_config->init_hash)) {
+ HXGE_ERR(hxgep, "tcam_seed failed");
+ return -1;
+ }
+
+ if (hxge_get_option("tcam_ether_usr1", &class_config->class_cfg[TCAM_CLASS_ETYPE_1]))
+ {
+ HXGE_ERR(hxgep, "tcam_ether_usr1 failed");
+ return -1;
+ }
+
+ if (hxge_get_option("tcam_ether_usr2", &class_config->class_cfg[TCAM_CLASS_ETYPE_2]))
+ {
+ HXGE_ERR(hxgep, "tcam_ether_usr2 failed");
+ return -1;
+ }
+
+ if (hxge_get_option("tcam_tcp_ipv4",&class_config->class_cfg[TCAM_CLASS_TCP_IPV4])) {
+
+ HXGE_ERR(hxgep, "tcam_tcp_ipv4 failed");
+ return -1;
+ }
+
+ if (hxge_get_option("tcam_udp_ipv4",&class_config->class_cfg[TCAM_CLASS_UDP_IPV4])) {
+
+ HXGE_ERR(hxgep, "tcam_udp_ipv4 failed");
+ return -1;
+ }
+
+ if (hxge_get_option("tcam_ipsec_ipv4",&class_config->class_cfg[TCAM_CLASS_AH_ESP_IPV4])) {
+
+ HXGE_ERR(hxgep, "tcam_ipsec_ipv4 failed");
+ return -1;
+ }
+
+ if (hxge_get_option("tcam_stcp_ipv4",&class_config->class_cfg[TCAM_CLASS_SCTP_IPV4])) {
+
+ HXGE_ERR(hxgep, "tcam_stcp_ipv4 failed");
+ return -1;
+ }
+
+ if (hxge_get_option("tcam_tcp_ipv6",&class_config->class_cfg[TCAM_CLASS_TCP_IPV6])) {
+
+ HXGE_ERR(hxgep, "tcam_tcp_ipv6 failed");
+ return -1;
+ }
+
+ if (hxge_get_option("tcam_udp_ipv6",&class_config->class_cfg[TCAM_CLASS_UDP_IPV6])) {
+
+ HXGE_ERR(hxgep, "tcam_udp_ipv6 failed");
+ return -1;
+ }
+
+ if (hxge_get_option("tcam_ipsec_ipv6",&class_config->class_cfg[TCAM_CLASS_AH_ESP_IPV6])) {
+
+ HXGE_ERR(hxgep, "tcam_ipsec_ipv6 failed");
+ return -1;
+ }
+
+ if (hxge_get_option("tcam_stcp_ipv6",&class_config->class_cfg[TCAM_CLASS_SCTP_IPV6])) {
+
+ HXGE_ERR(hxgep, "tcam_stcp_ipv6 failed");
+ return -1;
+ }
+
+ for (class = TCAM_CLASS_TCP_IPV4; class < TCAM_CLASS_SCTP_IPV6; class++)
+ {
+ switch (class_config->class_cfg[class]) {
+ case 0 : /* do nothing */
+ break;
+ case 1 : class_config->class_cfg[class] = HXGE_CLASS_TCAM_LOOKUP;
+ break;
+ case 2 : class_config->class_cfg[class] = HXGE_CLASS_DISCARD;
+ break;
+ default: HXGE_ERR(hxgep, "Bad class value");
+ return -1;
+ }
+ }
+
+ return 0;
+
+}
+int
+hxge_pfc_config_tcam_enable(struct hxge_adapter * hxgep)
+{
+ void *handle;
+ boolean_t enable = TRUE;
+ hpi_status_t hpi_status;
+
+ if (!(hxgep->flags & HXGE_TCAM_ENABLED)) {
+ HXGE_DBG(hxgep, "TCAM not enabled");
+ return 0;
+ }
+
+ handle = hxgep->hw.hw_addr;
+ hpi_status = hpi_pfc_set_tcam_enable(handle, enable);
+ if (hpi_status != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "enable tcam failed");
+ return -1;
+ }
+
+ HXGE_DBG(hxgep, "TCAM enabled");
+
+ return 0;
+}
+
+int
+hxge_pfc_config_tcam_disable(struct hxge_adapter * hxgep)
+{
+ void * handle;
+ boolean_t enable = FALSE;
+ hpi_status_t hpi_status;
+
+ handle = hxgep->hw.hw_addr;
+ hpi_status = hpi_pfc_set_tcam_enable(handle, enable);
+ if (hpi_status != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "disable tcam failed");
+ return -1;
+ }
+
+ return 0;
+}
+int
+hxge_pfc_set_hash(struct hxge_adapter *hxgep, uint32_t seed)
+{
+ hpi_status_t rs = HPI_SUCCESS;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ p_hxge_class_pt_cfg_t p_class_cfgp;
+
+ HXGE_DBG(hxgep, " ==> hxge_pfc_set_hash");
+
+ p_class_cfgp = (p_hxge_class_pt_cfg_t)&hxgep->class_config;
+ p_class_cfgp->init_hash = seed;
+
+ rs = hpi_pfc_set_hash_seed_value(handle, seed);
+ if (rs & HPI_PFC_ERROR) {
+ HXGE_ERR(hxgep, " hxge_pfc_set_hash %x failed ", seed);
+ return -1;
+ }
+
+ HXGE_DBG(hxgep, " <== hxge_pfc_set_hash");
+
+ return 0;
+}
+
+static int
+hxge_cfg_tcam_ip_class_get(struct hxge_adapter *hxgep, tcam_class_t class,
+ uint32_t *class_config)
+{
+ hpi_status_t rs = HPI_SUCCESS;
+ tcam_key_cfg_t cfg;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ uint32_t ccfg = 0;
+
+ HXGE_DBG(hxgep, "==> hxge_cfg_tcam_ip_class_get");
+
+ memset(&cfg, 0, sizeof (tcam_key_cfg_t));
+
+ rs = hpi_pfc_get_l3_class_config(handle, class, &cfg);
+ if (rs & HPI_PFC_ERROR) {
+ HXGE_ERR(hxgep, "hxge_cfg_tcam_ip_class opt %x for class %d failed ", *class_config, class);
+ return -1;
+ }
+ if (cfg.discard)
+ ccfg |= HXGE_CLASS_DISCARD;
+
+ if (cfg.lookup_enable)
+ ccfg |= HXGE_CLASS_TCAM_LOOKUP;
+
+ *class_config = ccfg;
+
+ HXGE_DBG(hxgep, " ==> hxge_cfg_tcam_ip_class_get %x", ccfg);
+
+ return 0;
+}
+
+
+int
+hxge_pfc_ip_class_config_get(struct hxge_adapter *hxgep, tcam_class_t class,
+ uint32_t *config)
+{
+ uint32_t t_class_config;
+
+ HXGE_DBG(hxgep, " ==> hxge_pfc_ip_class_config_get");
+ t_class_config = 0;
+ if (hxge_cfg_tcam_ip_class_get(hxgep, class, &t_class_config)) {
+ HXGE_ERR(hxgep, " hxge_pfc_ip_class_config_get for class %d tcam failed", class);
+ return -1;
+ }
+
+ HXGE_DBG(hxgep, " hxge_pfc_ip_class_config tcam %x", t_class_config);
+
+ *config = t_class_config;
+
+ HXGE_DBG(hxgep, "<== hxge_pfc_ip_class_config_get");
+ return 0;
+}
+
+static int
+hxge_pfc_ip_class_config(struct hxge_adapter *hxgep, tcam_class_t class,
+ uint32_t config)
+{
+ uint32_t class_config;
+ p_hxge_class_pt_cfg_t p_class_cfgp;
+ tcam_key_cfg_t cfg;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ hpi_status_t rs = HPI_SUCCESS;
+
+ HXGE_DBG(hxgep, " ==> hxge_pfc_ip_class_config");
+ p_class_cfgp = (p_hxge_class_pt_cfg_t)&hxgep->class_config;
+ class_config = p_class_cfgp->class_cfg[class];
+
+ if (class_config != config) {
+ p_class_cfgp->class_cfg[class] = config;
+ class_config = config;
+ }
+
+ if (class == TCAM_CLASS_ETYPE_1 || class == TCAM_CLASS_ETYPE_2) {
+ rs = hpi_pfc_set_l2_class_slot(handle,
+ class_config & HXGE_CLASS_ETHER_TYPE_MASK,
+ class_config & HXGE_CLASS_VALID,
+ class - TCAM_CLASS_ETYPE_1);
+ } else {
+ if (class_config & HXGE_CLASS_DISCARD)
+ cfg.discard = 1;
+ else
+ cfg.discard = 0;
+ if (class_config & HXGE_CLASS_TCAM_LOOKUP)
+ cfg.lookup_enable = 1;
+ else
+ cfg.lookup_enable = 0;
+
+ rs = hpi_pfc_set_l3_class_config(handle, class, cfg);
+ }
+
+ if (rs & HPI_PFC_ERROR) {
+ HXGE_DBG(hxgep, "hxge_pfc_ip_class_config %x for class %d tcam failed", config, class);
+ return -1;
+ }
+
+ HXGE_DBG(hxgep, "<== hxge_pfc_ip_class_config");
+ return 0;
+}
+
+static int
+hxge_pfc_ip_class_config_all(struct hxge_adapter *hxgep)
+{
+ uint32_t class_config;
+ tcam_class_t cl;
+
+ HXGE_DBG(hxgep, "==> hxge_pfc_ip_class_config_all");
+
+ for (cl = TCAM_CLASS_ETYPE_1; cl <= TCAM_CLASS_SCTP_IPV6; cl++) {
+ if (cl == TCAM_CLASS_RESERVED_4 ||
+ cl == TCAM_CLASS_RESERVED_5 ||
+ cl == TCAM_CLASS_RESERVED_6 ||
+ cl == TCAM_CLASS_RESERVED_7)
+ continue;
+
+ class_config = hxgep->class_config.class_cfg[cl];
+ if (hxge_pfc_ip_class_config(hxgep, cl, class_config)) {
+ HXGE_ERR(hxgep, "hxge_pfc_ip_class_config failed, class %d config %x ", cl, class_config);
+ return -1;
+ }
+ }
+
+ HXGE_DBG(hxgep, "<== hxge_pfc_ip_class_config_all");
+ return 0;
+}
+
+static int
+hxge_pfc_update_hw(struct hxge_adapter *hxgep)
+{
+ p_hxge_class_pt_cfg_t p_class_cfgp;
+
+ HXGE_DBG(hxgep, "==> hxge_pfc_update_hw");
+ p_class_cfgp = (p_hxge_class_pt_cfg_t)&hxgep->class_config;
+
+ if (hxge_pfc_set_hash(hxgep, p_class_cfgp->init_hash)) {
+ HXGE_DBG(hxgep, "hxge_pfc_set_hash Failed");
+ return -1;
+ }
+
+ /*TODO: Setup VLAN */
+
+ /* Configure hash value and classes */
+ if (hxge_pfc_ip_class_config_all(hxgep)) {
+ HXGE_ERR(hxgep, "hxge_pfc_ip_class_config_all Failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+static uint32_t
+hxge_get_blade_id(struct hxge_adapter *hxgep)
+{
+ phy_debug_training_vec_t blade_id;
+
+ HXGE_DBG(hxgep, "==> hxge_get_blade_id");
+ HXGE_REG_RD32(hxgep->hw.hw_addr, PHY_DEBUG_TRAINING_VEC,
+ &blade_id.value);
+ HXGE_DBG(hxgep, "<== hxge_get_blade_id: id = %d",blade_id.bits.bld_num);
+
+ return (blade_id.bits.bld_num);
+}
+
+
+
+static int
+hxge_tcam_default_add_entry(struct hxge_adapter *hxgep, tcam_class_t class)
+{
+ hpi_status_t rs = HPI_SUCCESS;
+ uint32_t location;
+ hxge_tcam_entry_t entry;
+ hxge_tcam_spread_t *key = NULL;
+ hxge_tcam_spread_t *mask = NULL;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+
+ memset(&entry, 0, sizeof (hxge_tcam_entry_t));
+
+ /*
+ * The class id and blade id are common for all classes
+ * Only use the blade id for matching and the rest are wild cards.
+ * This will allow one TCAM entry to match all traffic in order
+ * to spread the traffic using source hash.
+ */
+ key = &entry.key.spread;
+ mask = &entry.mask.spread;
+
+ key->blade_id = hxge_get_blade_id(hxgep);
+
+ mask->class_code = 0xf;
+ mask->class_code_l = 0x1;
+ mask->blade_id = 0;
+ mask->wild1 = 0x7ffffff;
+ mask->wild = 0xffffffff;
+ mask->wild_l = 0xffffffff;
+
+ location = class;
+
+ spin_lock(&hxgep->tcam_lock);
+ rs = hpi_pfc_tcam_entry_write(handle, location, &entry);
+ if (rs & HPI_PFC_ERROR) {
+ spin_unlock(&hxgep->tcam_lock);
+ HXGE_ERR(hxgep, " hxge_tcam_default_add_entry tcam entry write failed for location %d", location);
+ return -1;
+ }
+
+ /* Add the associative portion */
+ entry.match_action.value = 0;
+
+ /* Use source hash to spread traffic */
+ entry.match_action.bits.channel_d = 0;
+ entry.match_action.bits.channel_c = 1;
+ entry.match_action.bits.channel_b = 2;
+ entry.match_action.bits.channel_a = 3;
+ entry.match_action.bits.source_hash = 1;
+ entry.match_action.bits.discard = 0;
+
+ rs = hpi_pfc_tcam_asc_ram_entry_write(handle,
+ location, entry.match_action.value);
+ if (rs & HPI_PFC_ERROR) {
+ spin_lock(&hxgep->tcam_lock);
+ HXGE_DBG(hxgep, " hxge_tcam_default_add_entry tcam entry write failed for ASC RAM location %d", location);
+ return -1;
+ }
+
+ memcpy((void *) &entry,
+ (void *) &hxgep->classifier.tcam_entries[location].tce,
+ sizeof (hxge_tcam_entry_t));
+
+ spin_unlock(&hxgep->tcam_lock);
+ return 0;
+}
+
+
+
+/*
+ * Configure one TCAM entry for each class and make it match
+ * everything within the class in order to spread the traffic
+ * among the DMA channels based on the source hash.
+ *
+ * This is the default for now. This may change when Crossbow is
+ * available for configuring TCAM.
+ */
+static int
+hxge_tcam_default_config(struct hxge_adapter *hxgep)
+{
+ uint8_t class;
+ uint32_t class_config;
+
+ HXGE_DBG(hxgep, "==> hxge_tcam_default_config");
+
+ /*
+ * Add TCAM and its associative ram entries
+ * A wild card will be used for the class code in order to match
+ * any classes.
+ */
+ class = 0;
+ if (hxge_tcam_default_add_entry(hxgep, class)) {
+ HXGE_ERR(hxgep, "hxge_tcam_default_add_entry failed class %d ", class);
+ return -1;
+ }
+
+ /* Enable the classes */
+ for (class = TCAM_CLASS_TCP_IPV4;
+ class <= TCAM_CLASS_SCTP_IPV6; class++) {
+ /*
+ * By default, it is set to HXGE_CLASS_TCAM_LOOKUP in
+ * hxge_ndd.c. It may be overwritten in hxge.conf.
+ */
+ class_config = hxgep->class_config.class_cfg[class];
+
+ if (hxge_pfc_ip_class_config(hxgep, class, class_config)) {
+ HXGE_ERR(hxgep, "hxge_pfc_ip_class_config failed. class %d config %x ", class, class_config);
+ return -1;
+ }
+ }
+
+ if (hxge_pfc_config_tcam_enable(hxgep)) {
+ HXGE_ERR(hxgep, "hxge_pfc_config_tcam_enable failed");
+ return -1;
+ }
+
+ HXGE_DBG(hxgep, "hxge_tcam_default_config done");
+
+ return 0;
+}
+
+
+int
+hxge_classify_init_sw(struct hxge_adapter *hxgep)
+{
+ int alloc_size;
+ hxge_classify_t *classify_ptr;
+
+ HXGE_DBG(hxgep, "==> hxge_classify_init_sw");
+ classify_ptr = &hxgep->classifier;
+
+ if (classify_ptr->state & HXGE_PFC_SW_INIT) {
+ HXGE_DBG(hxgep, "hxge_classify_init_sw already init");
+ return 0;
+ }
+
+ /* Init SW structures */
+ classify_ptr->tcam_size = TCAM_HXGE_TCAM_MAX_ENTRY;
+
+ alloc_size = sizeof (tcam_flow_spec_t) * classify_ptr->tcam_size;
+ classify_ptr->tcam_entries = kzalloc(alloc_size, GFP_KERNEL);
+ memset(classify_ptr->class_usage, 0, sizeof (classify_ptr->class_usage));
+
+ /* Start from the beginning of TCAM */
+ hxgep->classifier.tcam_location = 0;
+ classify_ptr->state |= HXGE_PFC_SW_INIT;
+
+ HXGE_DBG(hxgep, "<== hxge_classify_init_sw");
+
+ return 0;
+}
+
+
+int
+hxge_classify_init_hw(struct hxge_adapter *hxgep)
+{
+ HXGE_DBG(hxgep, "==> hxge_classify_init_hw");
+
+ if (hxgep->classifier.state & HXGE_PFC_HW_INIT) {
+ HXGE_DBG(hxgep, "hxge_classify_init_hw already init");
+ return 0;
+ }
+
+ /* Now do a real configuration */
+ if (hxge_pfc_update_hw(hxgep)) {
+ HXGE_ERR(hxgep, "hxge_pfc_update_hw failed");
+ return -1;
+ }
+
+ if (hxge_tcam_default_config(hxgep)) {
+ HXGE_ERR(hxgep, "hxge_tcam_default_config failed");
+ return -1;
+ }
+
+ hxgep->classifier.state |= HXGE_PFC_HW_INIT;
+
+ HXGE_DBG(hxgep, "<== hxge_classify_init_hw");
+ return 0;
+}
+
+int
+hxge_classify_exit_sw(struct hxge_adapter *hxgep)
+{
+ hxge_classify_t *classify_ptr;
+
+ HXGE_DBG(hxgep, "==> hxge_classify_exit_sw");
+ classify_ptr = &hxgep->classifier;
+
+ if (classify_ptr->tcam_entries) {
+ kfree(classify_ptr->tcam_entries);
+ }
+ hxgep->classifier.state = HXGE_PFC_HW_UNINIT;
+
+ HXGE_DBG(hxgep, "<== hxge_classify_exit_sw");
+
+ return 0;
+}
+
+
+int
+hxge_classify_init(struct hxge_adapter *hxgep)
+{
+ HXGE_DBG(hxgep, "==> hxge_classify_init");
+
+ if (hxge_classify_init_sw(hxgep)) {
+ HXGE_ERR(hxgep, "SW init failed");
+ return -1;
+ }
+
+ if (hxge_classify_init_hw(hxgep)) {
+ HXGE_ERR(hxgep, "hxge_classify_init_hw failed");
+ hxge_classify_exit_sw(hxgep);
+ return -1;
+ }
+
+ HXGE_DBG(hxgep, "<== hxge_classify_init");
+ return 0;
+}
+
+int
+hxge_classify_uninit(struct hxge_adapter *hxgep)
+{
+ return (hxge_classify_exit_sw(hxgep));
+}
+
+void
+hxge_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp)
+{
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ hpi_status_t status;
+ int enable_vlan_id;
+
+ enable_vlan_id = (grp ? (hxgep->vlan_id > 0) : 0);
+ status = hpi_pfc_cfg_vlan_control_set(handle, 0, enable_vlan_id,
+ hxgep->vlan_id);
+ HXGE_DBG(hxgep, "Implicit VLAN ID %d",hxgep->vlan_id);
+ if (status != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_pfc_cfg_vlan_control_set failed to enable VLAN");
+ return;
+ }
+ if (grp) {
+ HXGE_DBG(hxgep, "Adding vlan group");
+ /* Initialize the entire vlan table to avoid parity err */
+ hpi_pfc_cfg_vlan_table_clear(handle);
+ }
+ else {
+ HXGE_DBG(hxgep, "Removing vlan group");
+ }
+ hxgep->vlangrp = grp;
+}
+
+
+void
+hxge_vlan_rx_add_vid(struct net_device *netdev, uint16_t vid)
+{
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ hpi_status_t status;
+
+ HXGE_DBG(hxgep, "Adding VID %d", vid);
+ status = hpi_pfc_cfg_vlan_table_entry_set(handle, vid);
+ if (status != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_pfc_cfg_vlan_table_entry_set failed, status = %d", status);
+ return;
+ }
+}
+
+void
+hxge_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid)
+{
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ hpi_status_t status;
+
+ HXGE_DBG(hxgep, "Removing VID %d", vid);
+ status = hpi_pfc_cfg_vlan_table_entry_clear(handle, vid);
+ if (status != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_pfc_cfg_vlan_table_entry_clear failed");
+ return;
+ }
+}
+
+
+int
+hxge_set_mac_address(struct net_device *netdev, void *p)
+{
+ struct sockaddr *addr = (struct sockaddr *)p;
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+ pfc_mac_addr_t mac_addr;
+ int status = 0;
+ int i;
+
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ MUTEX_ENTER(&hxgep->lock);
+
+ /* Hydra expects the MAC address to be in little endian (PCI device).
+ * So, we byte swap from network order (big endian) to little endian
+ */
+
+ for (i = 0; i < ETH_ALEN; i++)
+ mac_addr.byte[i] = addr->sa_data[ETH_ALEN-1-i];
+
+ status = hxge_pfc_set_mac_address(hxgep, HXGE_MAC_DEFAULT_ADDR_SLOT,
+ mac_addr);
+
+ memcpy(netdev->dev_addr, addr->sa_data, ETH_ALEN);
+ memcpy(&mac_addr.value, addr->sa_data, ETH_ALEN);
+
+ MUTEX_EXIT(&hxgep->lock);
+
+ if (status)
+ HXGE_ERR(hxgep, "Unable to set mac address");
+
+ return (status);
+}
+
+int
+hxge_add_mcast_addr(struct hxge_adapter * hxgep, uint8_t *addrp)
+{
+ return 0;
+}
+
+int
+hxge_del_mcast_addr(struct hxge_adapter * hxgep, uint8_t *addrp)
+{
+ return 0;
+}
+
+static int
+hxge_pfc_set_mac_address(struct hxge_adapter * hxgep, uint32_t slot,
+ pfc_mac_addr_t address)
+{
+ void * handle = hxgep->hw.hw_addr;
+ hpi_status_t hpi_status;
+
+ hpi_status = hpi_pfc_set_mac_address(handle, slot, address.value);
+ if (hpi_status != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "failed to set PFC slot %d to MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+ slot, address.byte[5], address.byte[4],
+ address.byte[3], address.byte[2],
+ address.byte[1], address.byte[0]);
+ hpi_pfc_mac_addr_disable(handle, slot); /* Might as well try */
+ return -1;
+ }
+
+ /* A MAC address of 00:00:00:00:00:00 is a call to disable
+ * that PFC MAC slot; all other addresses assumed valid and
+ * the PFC is enabled to match incoming traffic against that
+ * MAC address slot.
+ */
+
+ if (address.bits.addr) { /* Enable PFC MAC addr match */
+ hpi_status = hpi_pfc_mac_addr_enable(handle, slot);
+ if (hpi_status != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "Failed to enable PFC slot %d for MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+ slot, address.byte[5], address.byte[4],
+ address.byte[3], address.byte[2],
+ address.byte[1], address.byte[0]);
+ return -1;
+ }
+ } else { /* Disable PFC MAC==0 slot */
+ hpi_status = hpi_pfc_mac_addr_disable(handle, slot);
+ if (hpi_status != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "Failed to disable PFC slot %d for MAC address 00:00:00:00:00:00",
+ slot);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int
+hxge_pfc_num_macs_get(struct hxge_adapter * hxgep, uint32_t *nmacs)
+{
+ *nmacs = PFC_N_MAC_ADDRESSES;
+ return 0;
+}
+
+
+static int
+hxge_pfc_config_init(struct hxge_adapter * hxgep)
+{
+ void * handle;
+ handle = hxgep->hw.hw_addr;
+
+ MUTEX_ENTER(&hxgep->lock);
+
+ (void) hpi_pfc_set_tcam_enable(handle, FALSE);
+ (void) hpi_pfc_set_l2_hash(handle, TRUE);
+
+ if (hxgep->flags & (HXGE_RX_CHKSUM_ENABLED | HXGE_TX_CHKSUM_ENABLED))
+ (void) hpi_pfc_set_tcp_cksum(handle, TRUE);
+ else
+ (void) hpi_pfc_set_tcp_cksum(handle, FALSE);
+
+ (void) hpi_pfc_set_default_dma(handle, 0);
+ (void) hpi_pfc_mac_addr_enable(handle, 0);
+ (void) hpi_pfc_set_force_csum(handle, FALSE);
+
+ /* Clear the interrupt masks */
+ hpi_pfc_set_interrupt_mask(handle, 0, 0, 0);
+ hpi_pfc_set_drop_log_mask(handle, 1, 1, 1, 1, 1);
+
+ MUTEX_EXIT(&hxgep->lock);
+ return 0;
+}
+
+int
+hxge_pfc_hw_reset(struct hxge_adapter * hxgep)
+{
+ int status = 0;
+
+ HXGE_DBG(hxgep, "==> hxge_pfc_hw_reset");
+
+ status = hxge_pfc_config_init(hxgep);
+ if (status != 0) {
+ HXGE_ERR(hxgep, "failed PFC config init.");
+ return (status);
+ }
+
+ return 0;
+}
+
+
+static int hxge_pfc_load_hash_table(struct hxge_adapter *hxgep)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ int i;
+
+
+ hpi_pfc_set_l2_hash(handle, FALSE);
+
+ for (i = 0; i < MAC_MAX_HASH_ENTRY; i++)
+ if (hpi_pfc_set_multicast_hash_table(handle, i,
+ hxgep->mcast_hash_tbl[i])) {
+ HXGE_ERR(hxgep, "hpi_pfc_set_multicast_hash_table failed");
+ return -1;
+ }
+
+ hpi_pfc_set_l2_hash(handle, TRUE);
+ return 0;
+
+}
+
+/* Took this routine from ether_crc_le in linux/crc32.h and modified the
+ * way that the ethernet address is passed. Hydra computes the
+ * address backwards i.e pass the highest octet first and work backwards
+ * to the lowest octet
+ */
+static
+uint32_t crc32_le(unsigned char const *addr, size_t len)
+{
+ int i;
+ uint32_t crc = 0xffffffff;
+
+ while (len--) {
+ crc ^= addr[len];
+ for (i = 0; i < 8; i++)
+ crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0);
+ }
+ return ((~crc) >> 24);
+}
+
+void hxge_set_multi (struct net_device *dev)
+{
+ struct hxge_adapter *hxgep = netdev_priv(dev);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0)
+ struct dev_mc_list *dmi = dev->mc_list;
+#else
+ struct netdev_hw_addr *dmi;
+#endif
+ uint16_t *filter_tbl= &hxgep->mcast_hash_tbl[0];
+ uint32_t crc;
+ uint8_t byte = 0;
+
+ spin_lock(&hxgep->lock);
+
+ if (dev->flags & IFF_PROMISC)
+ {
+ HXGE_DBG(hxgep, "PROMISC enabled");
+ hxge_vmac_promisc(hxgep, 1);
+ }
+ else {
+ HXGE_DBG(hxgep, "PROMSC disabled");
+ hxge_vmac_promisc(hxgep, 0);
+ }
+
+ if (dev->flags & IFF_ALLMULTI)
+ {
+ HXGE_DBG(hxgep, "Setting allmulti");
+ byte = 0xff;
+ }
+
+ memset(filter_tbl, byte, sizeof(uint16_t)*MAC_MAX_HASH_ENTRY);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0)
+ while (dmi) {
+ HXGE_DBG(hxgep, "Adding %x:%x:%x:%x:%x:%x", dmi->dmi_addr[0],dmi->dmi_addr[1],dmi->dmi_addr[2],dmi->dmi_addr[3],dmi->dmi_addr[4],dmi->dmi_addr[5]);
+ crc = crc32_le(dmi->dmi_addr, ETH_ALEN);
+ filter_tbl[crc/MAC_MAX_HASH_ENTRY] |=
+ 1 << (crc%MAC_MAX_HASH_ENTRY);
+ dmi = dmi->next;
+ }
+#else
+ netdev_for_each_mc_addr(dmi, dev) {
+ crc = crc32_le(dmi->addr, ETH_ALEN);
+ filter_tbl[crc/MAC_MAX_HASH_ENTRY] |=
+ 1 << (crc%MAC_MAX_HASH_ENTRY);
+ }
+#endif
+ hxge_pfc_load_hash_table(hxgep);
+ spin_unlock(&hxgep->lock);
+}
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+irqreturn_t
+hxge_pfc_intr(int irq, void *data, struct pt_regs *regs)
+#else
+irqreturn_t
+hxge_pfc_intr(int irq, void *data)
+#endif
+{
+ struct hxge_ldv *ldvp = (struct hxge_ldv *)data;
+ struct hxge_ldg *ldgp = ldvp->ldgp;
+ struct hxge_adapter *hxgep = ldgp->hxgep;
+ void * handle;
+ p_hxge_pfc_stats_t statsp;
+ pfc_int_status_t int_status;
+ pfc_bad_cs_counter_t bad_cs_count;
+ pfc_drop_counter_t drop_count;
+ pfc_drop_log_t drop_log;
+ pfc_vlan_par_err_log_t vlan_par_err_log;
+ pfc_tcam_par_err_log_t tcam_par_err_log;
+
+ handle = hxgep->hw.hw_addr;
+ statsp = (p_hxge_pfc_stats_t)&hxgep->statsp->pfc_stats;
+
+ /*
+ * need to read the pfc interrupt status register to figure out
+ * what is happenning
+ */
+ hpi_pfc_get_interrupt_status(handle, &int_status);
+
+ if (int_status.bits.pkt_drop) {
+ statsp->pkt_drop++;
+
+ /* Collect each individual drops */
+ hpi_pfc_get_drop_log(handle, &drop_log);
+
+ if (drop_log.bits.l2_addr_drop) {
+ statsp->errlog.l2_addr_drop++;
+ HXGE_DBG(hxgep, "dropped => l2_addr");
+ }
+ if (drop_log.bits.class_code_drop) {
+ statsp->errlog.class_code_drop++;
+ HXGE_DBG(hxgep, "dropped => class_code");
+ }
+ if (drop_log.bits.tcam_drop) {
+ statsp->errlog.tcam_drop++;
+ HXGE_DBG(hxgep, "dropped => tcam_drop");
+ }
+ if (drop_log.bits.tcp_ctrl_drop) {
+ statsp->errlog.tcp_ctrl_drop++;
+ HXGE_DBG(hxgep, "dropped => tcp_ctrl");
+ }
+
+ /* Collect the total drops for all kinds */
+ (void) hpi_pfc_get_drop_counter(handle, &drop_count.value);
+ statsp->drop_count += drop_count.bits.drop_count;
+ }
+
+ if (int_status.bits.tcam_parity_err) {
+ statsp->tcam_parity_err++;
+
+ (void) hpi_pfc_get_tcam_parity_log(handle, &tcam_par_err_log);
+ statsp->errlog.tcam_par_err_log = tcam_par_err_log.bits.addr;
+ HXGE_DBG(hxgep, "hxge_pfc_intr: TCAM parity error addr: 0x%x",tcam_par_err_log.bits.addr);
+ }
+
+ if (int_status.bits.vlan_parity_err) {
+ statsp->vlan_parity_err++;
+
+ (void) hpi_pfc_get_vlan_parity_log(handle, &vlan_par_err_log);
+ statsp->errlog.vlan_par_err_log = vlan_par_err_log.bits.addr;
+
+ HXGE_DBG(hxgep, "hxge_pfc_intr: vlan table parity error addr: 0x%x", vlan_par_err_log.bits.addr);
+ }
+
+ (void) hpi_pfc_get_bad_csum_counter(handle, &bad_cs_count.value);
+ statsp->bad_cs_count += bad_cs_count.bits.bad_cs_count;
+
+ (void) hpi_pfc_clear_interrupt_status(handle);
+ return (IRQ_HANDLED);
+}
+
+int
+hxge_pfc_mac_addr_get(struct hxge_adapter * hxgep, int slot, uint8_t *mac_addr)
+{
+ hpi_status_t hpi_status = HPI_SUCCESS;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+
+ /* MAC is in big-endian format now because callers require it that
+ * way
+ */
+ hpi_status = hpi_pfc_get_mac_address(handle, slot, mac_addr);
+ if (hpi_status != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "slot %d failed", slot);
+ return (-1 | hpi_status);
+ }
+
+ if (!is_valid_ether_addr(mac_addr)) {
+ return -1;
+ }
+
+ return (0);
+}
+
+
+/* Look for MAC address from the 16 that are available. We program the
+ net_device with the first valid mac address that we find. This routine
+ only fails if we have not found any valid mac addressess.
+*/
+
+int
+hxge_pfc_mac_addrs_get(struct hxge_adapter *hxgep)
+{
+ int i, num_found = 0, nmacs;
+ uint8_t mac_addr[ETH_ALEN];
+
+ hxge_pfc_num_macs_get(hxgep, &nmacs);
+ for (i = 0; i < nmacs; i++) {
+ if (hxge_pfc_mac_addr_get(hxgep, i, mac_addr)) {
+ HXGE_DBG(hxgep, "Slot %d No valid MAC address",i);
+ continue;
+ } else {
+ if (num_found==0) { /* Primary MAC */
+// HXGE_ERR(hxgep,
+// "Setting Net Device MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+// mac_addr[0], mac_addr[1], mac_addr[2],
+// mac_addr[3], mac_addr[4], mac_addr[5]);
+
+ /* Copy the MAC address into net_device struct */
+ memcpy(hxgep->netdev->dev_addr, mac_addr, ETH_ALEN);
+ /* Used by ethtool */
+ memcpy(hxgep->netdev->perm_addr, mac_addr, ETH_ALEN);
+ num_found++;
+ }
+ }
+
+// memcpy(&hxgep->vmac.mac_addr[i], mac_addr, ETH_ALEN);
+ }
+
+ if (!num_found)
+ return -1; /* FAIL if not at least one valid MAC address */
+
+ return 0;
+}
+
+/* This routine is called from the probe function. It reads the MAC addresses
+ * from the HCR register space and writes them to the PFC MAC address
+ * registers which are used by the interface to filter MAC addresses
+ */
+int
+hxge_pfc_init_mac_addrs(struct hxge_adapter *hxgep)
+{
+ int nmacs,i;
+ pfc_mac_addr_t mac_addr;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+
+ uint32_t addr_hi, addr_lo;
+ peu_debug_training_vec_t blade;
+ int offset;
+ int hcr_mac_count;
+ peu_intr_stat_t peusts;
+
+ hxge_pfc_num_macs_get(hxgep, &nmacs);
+
+ /* CR 6687755 Skewed SPROM vs HCR offsets */
+
+ HXGE_REG_RD32(handle, PEU_DEBUG_TRAINING_VEC, &blade.value);
+ HXGE_DBG(hxgep, "Base address 0x%p", handle);
+ HXGE_DBG(hxgep, "... PEU_DEBUG_TRAINING_VEC 0x%8.8x", blade.value);
+ HXGE_DBG(hxgep, "... Blade number (U/L) %d/%d",
+ blade.bits.bld_num_upper, blade.bits.bld_num_lower);
+
+ if (blade.bits.bld_num_upper) {
+ offset = 0x08; /* Hydra blade/ports 1-5 MAC count at 8 */
+ } else {
+ offset = 0x04; /* Hydra blade/port 0 MAC count at 4 */
+ }
+
+ /* Get count of SPROM/HCR-resident static MAC addresses */
+
+ HXGE_REG_RD32(handle, HCR_REG + offset, &addr_lo);
+ hcr_mac_count = (addr_lo >> 8) & 0xffffff; /* MAC count in [31:08] */
+ if (hcr_mac_count > 4) {
+ HXGE_ERR(hxgep, "HCR MAC count %d too large",
+ hcr_mac_count);
+ hcr_mac_count = 1; /* Only use first entry */
+ }
+
+ HXGE_DBG(hxgep, "... HCR_REG_CNT 0x%8.8x (= %d)", addr_lo, hcr_mac_count);
+
+ offset += 4; /* Step to first HCR MAC address */
+
+ /* Initialize ("fill") PFC with MAC addresses; copy all static
+ * MAC addresses from SPROM (HCR registers) into PFC, zero-filling
+ * the rest of the PFC
+ */
+
+ for (i = 0; i < nmacs; i++, hcr_mac_count--) {
+ if (hcr_mac_count > 0) {
+ HXGE_REG_RD32(handle, HCR_REG + offset, &addr_lo);
+ HXGE_REG_RD32(handle, HCR_REG + offset+4, &addr_hi);
+ offset += 8;
+ /* Device error interrupt not yet enabled so must
+ * poll PEU_INTR_STAT to get a timely HCR parity
+ * error report
+ */
+ HXGE_REG_RD32(handle, PEU_INTR_STAT, &peusts.value);
+ if (peusts.bits.hcr_parerr) {
+ HXGE_ERR(hxgep, "HCR Parity Error (PEU_INTR_STAT=0x%8.8x) reading MAC %d",
+ peusts.value, i);
+ return (-1); /* Init failed */
+ }
+ } else {
+ addr_lo = 0; /* No more SPROM MAC addresses, so */
+ addr_hi = 0; /* fill rest of PFC with zeros */
+ }
+
+ mac_addr.byte[5] = ((addr_lo)) & 0xff; /* First 4 MAC octets */
+ mac_addr.byte[4] = ((addr_lo) >> 8) & 0xff;
+ mac_addr.byte[3] = ((addr_lo) >> 16) & 0xff;
+ mac_addr.byte[2] = ((addr_lo) >> 24) & 0xff;
+ mac_addr.byte[1] = ((addr_hi)) & 0xff; /* Final 2 MAC octets */
+ mac_addr.byte[0] = ((addr_hi) >> 8) & 0xff;
+
+ if (hcr_mac_count > 0) {
+ HXGE_ERR(hxgep, "Initializing static MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+ mac_addr.byte[5], mac_addr.byte[4],
+ mac_addr.byte[3], mac_addr.byte[2],
+ mac_addr.byte[1], mac_addr.byte[0]);
+ }
+
+ hxge_pfc_set_mac_address(hxgep, i, mac_addr);
+ }
+
+ if (hxge_pfc_mac_addrs_get(hxgep))
+ return -1;
+
+ return 0;
+}
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#ifndef _HXGE_PFC_H
+#define _HXGE_PFC_H
+
+/* 0 and 4095 are reserved */
+#define VLAN_ID_MIN 0
+#define VLAN_ID_MAX 4094
+#define VLAN_ID_IMPLICIT 4094
+#define VLAN_MAX_ENTRIES 128
+
+#define HXGE_MAC_DEFAULT_ADDR_SLOT 0
+
+#define REG_PIO_WRITE64(handle, offset, value) \
+ HXGE_REG_WR64((handle), (offset), (value))
+#define REG_PIO_READ64(handle, offset, val_p) \
+ HXGE_REG_RD64((handle), (offset), (val_p))
+
+#define TCAM_CTL_RWC_TCAM_WR 0x0
+#define TCAM_CTL_RWC_TCAM_CMP 0x2
+#define TCAM_CTL_RWC_RAM_WR 0x4
+#define TCAM_CTL_RWC_RAM_RD 0x5
+#define TCAM_CTL_RWC_RWC_STAT 0x1
+#define TCAM_CTL_RWC_RWC_MATCH 0x1
+
+#define MAC_MAX_HASH_ENTRY 16
+
+#define WRITE_TCAM_REG_CTL(handle, ctl) \
+ REG_PIO_WRITE64(handle, PFC_TCAM_CTRL, ctl)
+
+#define READ_TCAM_REG_CTL(handle, val_p) \
+ REG_PIO_READ64(handle, PFC_TCAM_CTRL, val_p)
+
+#define WRITE_TCAM_REG_KEY0(handle, key) \
+ REG_PIO_WRITE64(handle, PFC_TCAM_KEY0, key)
+#define WRITE_TCAM_REG_KEY1(handle, key) \
+ REG_PIO_WRITE64(handle, PFC_TCAM_KEY1, key)
+#define WRITE_TCAM_REG_MASK0(handle, mask) \
+ REG_PIO_WRITE64(handle, PFC_TCAM_MASK0, mask)
+#define WRITE_TCAM_REG_MASK1(handle, mask) \
+ REG_PIO_WRITE64(handle, PFC_TCAM_MASK1, mask)
+
+#define READ_TCAM_REG_KEY0(handle, val_p) \
+ REG_PIO_READ64(handle, PFC_TCAM_KEY0, val_p)
+#define READ_TCAM_REG_KEY1(handle, val_p) \
+ REG_PIO_READ64(handle, PFC_TCAM_KEY1, val_p)
+#define READ_TCAM_REG_MASK0(handle, val_p) \
+ REG_PIO_READ64(handle, PFC_TCAM_MASK0, val_p)
+#define READ_TCAM_REG_MASK1(handle, val_p) \
+ REG_PIO_READ64(handle, PFC_TCAM_MASK1, val_p)
+
+typedef union _hxge_tcam_res_t {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t padding:34;
+ uint64_t reserved:15;
+ uint64_t parity:1;
+ uint64_t hit_count:4;
+ uint64_t channel_d:2;
+ uint64_t channel_c:2;
+ uint64_t channel_b:2;
+ uint64_t channel_a:2;
+ uint64_t source_hash:1;
+ uint64_t discard:1;
+#else
+ uint64_t discard:1;
+ uint64_t source_hash:1;
+ uint64_t channel_a:2;
+ uint64_t channel_b:2;
+ uint64_t channel_c:2;
+ uint64_t channel_d:2;
+ uint64_t hit_count:4;
+ uint64_t parity:1;
+ uint64_t reserved:15;
+ uint64_t padding:34;
+#endif
+ } bits;
+} hxge_tcam_res_t, *p_hxge_tcam_res_t;
+
+typedef struct tcam_reg {
+#if defined(__BIG_ENDIAN)
+ uint64_t reg1; /* 99:64 */
+ uint64_t reg0; /* 63:0 */
+#else
+ uint64_t reg0; /* 63:0 */
+ uint64_t reg1; /* 99:64 */
+#endif
+} hxge_tcam_reg_t;
+
+typedef struct hxge_tcam_ipv4_S {
+#if defined(__BIG_ENDIAN)
+ uint32_t class_code:4; /* 99:95 */
+ uint32_t blade_id:3; /* 94:91 */
+ uint32_t rsrvd2:2; /* 90:89 */
+ uint32_t noport:1; /* 88 */
+ uint32_t protocol:8; /* 87:80 */
+ uint32_t l4_hdr; /* 79:48 */
+ uint32_t rsrvd:16; /* 47:32 */
+ uint32_t ip_daddr; /* 31:0 */
+#else
+ uint32_t ip_daddr; /* 31:0 */
+ uint32_t rsrvd:16; /* 47:32 */
+ uint32_t l4_hdr; /* 79:48 */
+ uint32_t protocol:8; /* 87:80 */
+ uint32_t noport:1; /* 88 */
+ uint32_t rsrvd2:2; /* 90:89 */
+ uint32_t blade_id:3; /* 94:91 */
+ uint32_t class_code:4; /* 99:95 */
+#endif
+} hxge_tcam_ipv4_t;
+
+typedef struct hxge_tcam_ipv6_S {
+#if defined(__BIG_ENDIAN)
+ uint32_t class_code:4; /* 99:95 */
+ uint32_t blade_id:3; /* 94:91 */
+ uint64_t rsrvd2:3; /* 90:88 */
+ uint64_t protocol:8; /* 87:80 */
+ uint64_t l4_hdr:32; /* 79:48 */
+ uint64_t rsrvd:48; /* 47:0 */
+#else
+ uint64_t rsrvd:48; /* 47:0 */
+ uint64_t l4_hdr:32; /* 79:48 */
+ uint64_t protocol:8; /* 87:80 */
+ uint64_t rsrvd2:3; /* 90:88 */
+ uint32_t blade_id:3; /* 94:91 */
+ uint32_t class_code:4; /* 99:95 */
+#endif
+} hxge_tcam_ipv6_t;
+
+typedef struct hxge_tcam_enet_S {
+#if defined(__BIG_ENDIAN)
+ uint8_t class_code:4; /* 99:95 */
+ uint8_t blade_id:3; /* 94:91 */
+ uint8_t rsrvd:3; /* 90:88 */
+ uint8_t eframe[11]; /* 87:0 */
+#else
+ uint8_t eframe[11]; /* 87:0 */
+ uint8_t rsrvd:3; /* 90:88 */
+ uint8_t blade_id:3; /* 94:91 */
+ uint8_t class_code:4; /* 99:95 */
+#endif
+} hxge_tcam_ether_t;
+
+typedef struct hxge_tcam_spread_S {
+#if defined(_BIG_ENDIAN)
+ uint32_t unused:28; /* 127:100 */
+ uint32_t class_code:4; /* 99:96 */
+ uint32_t class_code_l:1; /* 95:95 */
+ uint32_t blade_id:4; /* 94:91 */
+ uint32_t wild1:27; /* 90:64 */
+ uint32_t wild; /* 63:32 */
+ uint32_t wild_l; /* 31:0 */
+#else
+ uint32_t wild_l; /* 31:0 */
+ uint32_t wild; /* 63:32 */
+ uint32_t wild1:27; /* 90:64 */
+ uint32_t blade_id:4; /* 94:91 */
+ uint32_t class_code_l:1; /* 95:95 */
+ uint32_t class_code:4; /* 99:96 */
+ uint32_t unused:28; /* 127:100 */
+#endif
+} hxge_tcam_spread_t;
+
+
+typedef struct hxge_tcam_entry_S {
+ union _hxge_tcam_entry {
+ hxge_tcam_ipv4_t ipv4;
+ hxge_tcam_ipv6_t ipv6;
+ hxge_tcam_ether_t enet;
+ hxge_tcam_reg_t regs;
+ hxge_tcam_spread_t spread;
+ } key, mask;
+ hxge_tcam_res_t match_action;
+ uint16_t ether_type;
+} hxge_tcam_entry_t;
+
+#define key_reg0 key.regs.reg0
+#define key_reg1 key.regs.reg1
+#define mask_reg0 mask.regs.reg0
+#define mask_reg1 mask.regs.reg1
+
+#define key0 key.regs.reg0
+#define key1 key.regs.reg1
+#define mask0 mask.regs.reg0
+#define mask1 mask.regs.reg1
+
+#define ip4_class_key key.ipv4.class_code
+#define ip4_blade_id_key key.ipv4.blade_id
+#define ip4_noport_key key.ipv4.noport
+#define ip4_proto_key key.ipv4.protocol
+#define ip4_l4_hdr_key key.ipv4.l4_hdr
+#define ip4_dest_key key.ipv4.ip_daddr
+
+#define ip4_class_mask mask.ipv4.class_code
+#define ip4_blade_id_mask mask.ipv4.blade_id
+#define ip4_noport_mask mask.ipv4.noport
+#define ip4_proto_mask mask.ipv4.protocol
+#define ip4_l4_hdr_mask mask.ipv4.l4_hdr
+#define ip4_dest_mask mask.ipv4.ip_daddr
+
+#define ip6_class_key key.ipv6.class_code
+#define ip6_blade_id_key key.ipv6.blade_id
+#define ip6_proto_key key.ipv6.protocol
+#define ip6_l4_hdr_key key.ipv6.l4_hdr
+
+#define ip6_class_mask mask.ipv6.class_code
+#define ip6_blade_id_mask mask.ipv6.blade_id
+#define ip6_proto_mask mask.ipv6.protocol
+#define ip6_l4_hdr_mask mask.ipv6.l4_hdr
+
+#define ether_class_key key.ether.class_code
+#define ether_blade_id_key key.ether.blade_id
+#define ether_ethframe_key key.ether.eframe
+
+#define ether_class_mask mask.ether.class_code
+#define ether_blade_id_mask mask.ether.blade_id
+#define ether_ethframe_mask mask.ether.eframe
+
+typedef struct _pfc_errlog {
+ uint32_t tcp_ctrl_drop; /* pfc_drop_log */
+ uint32_t l2_addr_drop;
+ uint32_t class_code_drop;
+ uint32_t tcam_drop;
+ uint32_t vlan_drop;
+
+ uint32_t vlan_par_err_log; /* pfc_vlan_par_err_log */
+ uint32_t tcam_par_err_log; /* pfc_tcam_par_err_log */
+} pfc_errlog_t, *p_pfc_errlog_t;
+
+typedef struct _pfc_stats {
+ uint32_t pkt_drop; /* pfc_int_status */
+ uint32_t tcam_parity_err;
+ uint32_t vlan_parity_err;
+
+ uint32_t bad_cs_count; /* pfc_bad_cs_counter */
+ uint32_t drop_count; /* pfc_drop_counter */
+ pfc_errlog_t errlog;
+} hxge_pfc_stats_t, *p_hxge_pfc_stats_t;
+
+typedef enum pfc_tcam_class {
+ TCAM_CLASS_INVALID = 0,
+ TCAM_CLASS_DUMMY = 1,
+ TCAM_CLASS_ETYPE_1 = 2,
+ TCAM_CLASS_ETYPE_2,
+ TCAM_CLASS_RESERVED_4,
+ TCAM_CLASS_RESERVED_5,
+ TCAM_CLASS_RESERVED_6,
+ TCAM_CLASS_RESERVED_7,
+ TCAM_CLASS_TCP_IPV4,
+ TCAM_CLASS_UDP_IPV4,
+ TCAM_CLASS_AH_ESP_IPV4,
+ TCAM_CLASS_SCTP_IPV4,
+ TCAM_CLASS_TCP_IPV6,
+ TCAM_CLASS_UDP_IPV6,
+ TCAM_CLASS_AH_ESP_IPV6,
+ TCAM_CLASS_SCTP_IPV6,
+ TCAM_CLASS_ARP,
+ TCAM_CLASS_RARP,
+ TCAM_CLASS_DUMMY_12,
+ TCAM_CLASS_DUMMY_13,
+ TCAM_CLASS_DUMMY_14,
+ TCAM_CLASS_DUMMY_15,
+ TCAM_CLASS_MAX
+} tcam_class_t;
+
+typedef struct _tcam_key_cfg_t {
+ boolean_t lookup_enable;
+ boolean_t discard;
+} tcam_key_cfg_t;
+
+#define HXGE_ETHER_FLOWS (FLOW_ETHER_DHOST | FLOW_ETHER_SHOST | \
+ FLOW_ETHER_TYPE)
+#define HXGE_VLAN_FLOWS (FLOW_ETHER_TPID | FLOW_ETHER_TCI)
+#define HXGE_ETHERNET_FLOWS (HXGE_ETHER_FLOWS | HXGE_VLAN_FLOWS)
+#define HXGE_PORT_FLOWS (FLOW_ULP_PORT_REMOTE | FLOW_ULP_PORT_LOCAL)
+#define HXGE_ADDR_FLOWS (FLOW_IP_REMOTE | FLOW_IP_LOCAL)
+#define HXGE_IP_FLOWS (FLOW_IP_VERSION | FLOW_IP_PROTOCOL | \
+ HXGE_PORT_FLOWS | HXGE_ADDR_FLOWS)
+#define HXGE_SUPPORTED_FLOWS (HXGE_ETHERNET_FLOWS | HXGE_IP_FLOWS)
+
+#define VID_MASK ((1 << 12) - 1)
+#define L2RDC_TBL_NUM_MASK ((1 << 2) - 1)
+#define CLS_CODE_MASK ((1 << 5) - 1)
+#define PID_MASK ((1 << 8) - 1)
+#define IP_PORT_MASK ((1 << 16) - 1)
+
+#define IP_ADDR_SA_MASK 0xFFFFFFFF
+#define IP_ADDR_DA_MASK IP_ADDR_SA_MASK
+#define L4PT_SPI_MASK IP_ADDR_SA_MASK
+
+#endif /* !_HXGE_PFC_H */
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#ifndef _HXGE_PFC_HW_H
+#define _HXGE_PFC_HW_H
+
+#define PFC_BASE_ADDR 0X0200000
+
+#define PFC_VLAN_TABLE (PFC_BASE_ADDR + 0x0)
+#define PFC_VLAN_CTRL (PFC_BASE_ADDR + 0x9000)
+#define PFC_MAC_ADDR (PFC_BASE_ADDR + 0x10000)
+#define PFC_MAC_ADDR_MASK (PFC_BASE_ADDR + 0x10080)
+#define PFC_HASH_TABLE (PFC_BASE_ADDR + 0x10100)
+#define PFC_L2_CLASS_CONFIG (PFC_BASE_ADDR + 0x20000)
+#define PFC_L3_CLASS_CONFIG (PFC_BASE_ADDR + 0x20030)
+#define PFC_TCAM_KEY0 (PFC_BASE_ADDR + 0x20090)
+#define PFC_TCAM_KEY1 (PFC_BASE_ADDR + 0x20098)
+#define PFC_TCAM_MASK0 (PFC_BASE_ADDR + 0x200B0)
+#define PFC_TCAM_MASK1 (PFC_BASE_ADDR + 0x200B8)
+#define PFC_TCAM_CTRL (PFC_BASE_ADDR + 0x200D0)
+#define PFC_CONFIG (PFC_BASE_ADDR + 0x20100)
+#define TCP_CTRL_MASK (PFC_BASE_ADDR + 0x20108)
+#define SRC_HASH_VAL (PFC_BASE_ADDR + 0x20110)
+#define PFC_INT_STATUS (PFC_BASE_ADDR + 0x30000)
+#define PFC_DBG_INT_STATUS (PFC_BASE_ADDR + 0x30008)
+#define PFC_INT_MASK (PFC_BASE_ADDR + 0x30100)
+#define PFC_DROP_LOG (PFC_BASE_ADDR + 0x30200)
+#define PFC_DROP_LOG_MASK (PFC_BASE_ADDR + 0x30208)
+#define PFC_VLAN_PAR_ERR_LOG (PFC_BASE_ADDR + 0x30210)
+#define PFC_TCAM_PAR_ERR_LOG (PFC_BASE_ADDR + 0x30218)
+#define PFC_BAD_CS_COUNTER (PFC_BASE_ADDR + 0x30220)
+#define PFC_DROP_COUNTER (PFC_BASE_ADDR + 0x30228)
+#define PFC_AUTO_INIT (PFC_BASE_ADDR + 0x30300)
+
+
+/*
+ * Register: PfcVlanTable
+ * VLAN Table Registers
+ * Description: VLAN membership table. CPU programs in the VLANs that
+ * it wants to belong to. A blade may be a member of multiple VLANs.
+ * Bits [31:0] of the first entry corresponds to vlan members [31:0],
+ * bits [31:0] of the second entry corresponds to vlan members
+ * [63:32] and so on.
+ * Fields:
+ * Odd parities of member[31:24], member[23:16], member[17:8],
+ * member[7:0]. These parity bits are ignored when parEn in the
+ * VLAN Control register is set to '0'.
+ * Set to 1 to indicate that blade is a member of the VLAN IDs
+ * (32 to 0) * entry number
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:28;
+ uint64_t parity:4;
+ uint64_t member:32;
+#else
+ uint64_t member:32;
+ uint64_t parity:4;
+ uint64_t rsrvd:28;
+#endif
+ } bits;
+} pfc_vlan_table_t;
+
+
+/*
+ * Register: PfcVlanCtrl
+ * VLAN Control Register
+ * Description: VLAN control register. Controls VLAN table properties
+ * and implicit VLAN properties for non-VLAN tagged packets.
+ * Fields:
+ * VLAN table parity debug write enable. When set to 1, software
+ * writes the parity bits together with the data during a VLAN
+ * table write. Otherwise, hardware automatically generates the
+ * parity bits from the data.
+ * Set to 1 to indicate the implicit VLAN ID is valid for use in
+ * non-VLAN tagged packets filtering
+ * Implicit VLAN ID for non-VLAN tagged packets
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:50;
+ uint64_t par_en:1;
+ uint64_t valid:1;
+ uint64_t id:12;
+#else
+ uint64_t id:12;
+ uint64_t valid:1;
+ uint64_t par_en:1;
+ uint64_t rsrvd:50;
+#endif
+ } bits;
+} pfc_vlan_ctrl_t;
+
+
+/*
+ * Register: PfcMacAddr
+ * MAC Address
+ * Description: MAC Address - Contains a station's 48 bit MAC
+ * address. The first register corresponds to MAC address 0, the
+ * second register corresponds to MAC address 1 and so on. For a MAC
+ * address of format aa-bb-cc-dd-ee-ff, addr[47:0] corresponds to
+ * "aabbccddeeff". When used in conjunction with the MAC address
+ * filter mask registers, these registers can be used to construct
+ * either a unicast or multicast address. An address is considered
+ * matched if (DA & ~mask) == (MAC address & ~mask)
+ * Fields:
+ * 48 bits of stations's MAC address
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:16;
+ uint64_t addr:48;
+#else
+ uint64_t addr:48;
+ uint64_t rsrvd:16;
+#endif
+ } bits;
+ uint8_t byte[8];
+} pfc_mac_addr_t;
+
+
+/*
+ * Register: PfcMacAddrMask
+ * MAC Address Filter
+ * Description: MAC Address Filter Mask - Contains the station's 48
+ * bit MAC address filter mask. The first register corresponds to MAC
+ * address 0 filter mask, the second register corresponds to MAC
+ * address 1 filter mask and so on. These filter masks cover MAC
+ * address bits 47:0 in the same order as the address registers
+ * Fields:
+ * 48 bits of stations's MAC address filter mask
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:16;
+ uint64_t mask:48;
+#else
+ uint64_t mask:48;
+ uint64_t rsrvd:16;
+#endif
+ } bits;
+ uint8_t byte[8];
+} pfc_mac_addr_mask_t;
+
+
+/*
+ * Register: PfcHashTable
+ * MAC Multicast Hash Filter
+ * Description: MAC multicast hash table filter. The multicast
+ * destination address is used to perform Ethernet CRC-32 hashing
+ * with seed value 0xffffFfff. Bits 47:40 of the hash result are used
+ * to index one bit of this multicast hash table. If the bit is '1',
+ * the multicast hash matches.
+ * Fields:
+ * 16 bits of 256 bit hash table. First entry contains bits
+ * [15:0], last entry contains bits [255:240]
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:48;
+ uint64_t hash_val:16;
+#else
+ uint64_t hash_val:16;
+ uint64_t rsrvd:48;
+#endif
+ } bits;
+} pfc_hash_table_t;
+
+
+/*
+ * Register: PfcL2ClassConfig
+ * L2 Class Configuration
+ * Description: Programmable EtherType for class codes 2 and 3. The
+ * first register is class 2, and the second class 3
+ * Fields:
+ * Set to 1 to indicate that the entry is valid for use in
+ * classification
+ * EtherType value
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:47;
+ uint64_t valid:1;
+ uint64_t etype:16;
+#else
+ uint64_t etype:16;
+ uint64_t valid:1;
+ uint64_t rsrvd:47;
+#endif
+ } bits;
+} pfc_l2_class_config_t;
+
+
+/*
+ * Register: PfcL3ClassConfig
+ * L3 Class Configuration
+ * Description: Configuration for class codes 0x8-0xF. PFC can be set
+ * to discard certain classes of traffic, or to not initiate a TCAM
+ * match for that class
+ * Fields:
+ * Set to 1 to discard all packets of this class code
+ * Set to 1 to indicate that packets of this class should be sent
+ * to the TCAM for perfect match
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:60;
+ uint64_t discard:1;
+ uint64_t tsel:1;
+ uint64_t rsrvd1:2;
+#else
+ uint64_t rsrvd1:2;
+ uint64_t tsel:1;
+ uint64_t discard:1;
+ uint64_t rsrvd:60;
+#endif
+ } bits;
+} pfc_l3_class_config_t;
+
+
+/*
+ * Register: PfcTcamKey0
+ * TCAM Key 0
+ * Description: TCAM key value. Holds bit 63:0 of the TCAM key
+ * Fields:
+ * bits 63:0 of tcam key
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t key:64;
+#else
+ uint64_t key:64;
+#endif
+ } bits;
+} pfc_tcam_key0_t;
+
+
+/*
+ * Register: PfcTcamKey1
+ * TCAM Key 1
+ * Description: TCAM key value. Holds bit 99:64 of the TCAM key
+ * Fields:
+ * bits 99:64 of tcam key
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:28;
+ uint64_t key:36;
+#else
+ uint64_t key:36;
+ uint64_t rsrvd:28;
+#endif
+ } bits;
+} pfc_tcam_key1_t;
+
+
+/*
+ * Register: PfcTcamMask0
+ * TCAM Mask 0
+ * Description: TCAM mask value. Holds bit 63:0 of the TCAM mask
+ * Fields:
+ * bits 63:0 of tcam mask
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t mask:64;
+#else
+ uint64_t mask:64;
+#endif
+ } bits;
+} pfc_tcam_mask0_t;
+
+
+/*
+ * Register: PfcTcamMask1
+ * TCAM Mask 1
+ * Description: TCAM mask value. Holds bit 99:64 of the TCAM mask
+ * Fields:
+ * bits 99:64 of tcam mask
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:28;
+ uint64_t mask:36;
+#else
+ uint64_t mask:36;
+ uint64_t rsrvd:28;
+#endif
+ } bits;
+} pfc_tcam_mask1_t;
+
+
+/*
+ * Register: PfcTcamCtrl
+ * TCAM Control
+ * Description: TCAM and TCAM lookup memory access control register.
+ * Controls how TCAM and result lookup table are accessed by blade
+ * CPU. For a TCAM write, the data in the TCAM key and mask registers
+ * will be written to the TCAM. A compare will initiate a TCAM match
+ * with the data stored in the TCAM key register. The match bit is
+ * toggled, and the matching address is reported in the addr field.
+ * For an access to the TCAM result lookup memory, the TCAM 0 key
+ * register is used for the read/write data.
+ * Fields:
+ * TCAM lookup table debug parity bit write enable. When a '1' is
+ * written, software writes the parity bit together with the data
+ * during a TCAM result lookup write. Otherwise, hardware
+ * automatically generates the parity bit from the data.
+ * 3'b000 = TCAM write 3'b001 = reserved 3'b010 = TCAM compare
+ * 3'b011 = reserved 3'b100 = TCAM result lookup write 3'b101 =
+ * TCAM result lookup read 3'b110 = reserved 3'b111 = reserved
+ * Status of read/write/compare operation. When a zero is
+ * written, hardware initiates access. Hardware writes a '1' to
+ * the bit when it completes
+ * Set to 1 if there is a TCAM match for compare command. Zero
+ * otherwise
+ * Address location for access of TCAM or RAM (valid values
+ * 0-42). For a compare, the location of the match is written
+ * here by hardware.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:45;
+ uint64_t par_en:1;
+ uint64_t cmd:3;
+ uint64_t status:1;
+ uint64_t match:1;
+ uint64_t rsrvd1:5;
+ uint64_t addr:8;
+#else
+ uint64_t addr:8;
+ uint64_t rsrvd1:5;
+ uint64_t match:1;
+ uint64_t status:1;
+ uint64_t cmd:3;
+ uint64_t par_en:1;
+ uint64_t rsrvd:45;
+#endif
+ } bits;
+} pfc_tcam_ctrl_t;
+
+
+/*
+ * Register: PfcConfig
+ * PFC General Configuration
+ * Description: PFC configuration options that are under the control
+ * of a blade CPU
+ * Fields:
+ * MAC address enable mask. Each bit corresponds to one MAC
+ * adress (lsb = addr0). With 16 MAC addresses, only the lower 16
+ * bits are valid.
+ * default DMA channel number
+ * force TCP/UDP checksum result to always match
+ * Enable for TCP/UDP checksum. If not enabled, the result will
+ * never match.
+ * Enable TCAM matching. If TCAM matching is not enabled, traffic
+ * will be sent to the default DMA channel.
+ * Enable L2 Multicast hash
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:24;
+ uint64_t mac_addr_en:32;
+ uint64_t default_dma:4;
+ uint64_t force_cs_en:1;
+ uint64_t tcp_cs_en:1;
+ uint64_t tcam_en:1;
+ uint64_t l2_hash_en:1;
+#else
+ uint64_t l2_hash_en:1;
+ uint64_t tcam_en:1;
+ uint64_t tcp_cs_en:1;
+ uint64_t force_cs_en:1;
+ uint64_t default_dma:4;
+ uint64_t mac_addr_en:32;
+ uint64_t rsrvd:24;
+#endif
+ } bits;
+} pfc_config_t;
+
+
+/*
+ * Register: TcpCtrlMask
+ * TCP control bits mask
+ * Description: Mask of TCP control bits to forward onto downstream
+ * blocks The TCP packet's control bits are masked, and then bitwise
+ * OR'd to produce a signal to the Rx DMA. Normally, all bits are
+ * masked off except the TCP SYN bit. The Rx DMA uses this bitwise OR
+ * for statistics. When discard = 1, the packet will be dropped if
+ * the bitwise OR = 1.
+ * Fields:
+ * Drop the packet if bitwise OR of the TCP control bits masked
+ * on = 1
+ * TCP end of data flag
+ * TCP SYN flag
+ * TCP reset flag
+ * TCP push flag
+ * TCP ack flag
+ * TCP urgent flag
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:57;
+ uint64_t discard:1;
+ uint64_t fin:1;
+ uint64_t syn:1;
+ uint64_t rst:1;
+ uint64_t psh:1;
+ uint64_t ack:1;
+ uint64_t urg:1;
+#else
+ uint64_t urg:1;
+ uint64_t ack:1;
+ uint64_t psh:1;
+ uint64_t rst:1;
+ uint64_t syn:1;
+ uint64_t fin:1;
+ uint64_t discard:1;
+ uint64_t rsrvd:57;
+#endif
+ } bits;
+} tcp_ctrl_mask_t;
+
+
+/*
+ * Register: SrcHashVal
+ * Source hash Seed Value
+ * Hash CRC seed value
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t seed:32;
+#else
+ uint64_t seed:32;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} src_hash_val_t;
+
+
+/*
+ * Register: PfcIntStatus
+ * PFC Interrupt Status
+ * Description: PFC interrupt status register
+ * Fields:
+ * triggered when packet drop log captured a drop. Part of LDF 0.
+ * Write 1 to clear.
+ * TCAM result lookup table parity error. Part of LDF 0. Write 1
+ * to clear.
+ * VLAN table parity error. Part of LDF 0. Write 1 to clear.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:61;
+ uint64_t pkt_drop:1;
+ uint64_t tcam_parity_err:1;
+ uint64_t vlan_parity_err:1;
+#else
+ uint64_t vlan_parity_err:1;
+ uint64_t tcam_parity_err:1;
+ uint64_t pkt_drop:1;
+ uint64_t rsrvd:61;
+#endif
+ } bits;
+} pfc_int_status_t;
+
+
+/*
+ * Register: PfcDbgIntStatus
+ * PFC Debug Interrupt Status
+ * Description: PFC debug interrupt status mirror register. This
+ * debug register triggers the same interrupts as those in the PFC
+ * Interrupt Status register. Interrupts in this mirror register are
+ * subject to the filtering of the PFC Interrupt Mask register.
+ * Fields:
+ * Packet drop. Part of LDF 0.
+ * TCAM result lookup table parity error. Part of LDF 0.
+ * VLAN table parity error. Part of LDF 0.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:61;
+ uint64_t pkt_drop:1;
+ uint64_t tcam_parity_err:1;
+ uint64_t vlan_parity_err:1;
+#else
+ uint64_t vlan_parity_err:1;
+ uint64_t tcam_parity_err:1;
+ uint64_t pkt_drop:1;
+ uint64_t rsrvd:61;
+#endif
+ } bits;
+} pfc_dbg_int_status_t;
+
+
+/*
+ * Register: PfcIntMask
+ * PFC Interrupt Mask
+ * Description: PFC interrupt status mask register
+ * Fields:
+ * mask for pktDrop capture;
+ * TCAM result lookup table parity error mask;
+ * VLAN table parity error mask;
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:61;
+ uint64_t pkt_drop_mask:1;
+ uint64_t tcam_parity_err_mask:1;
+ uint64_t vlan_parity_err_mask:1;
+#else
+ uint64_t vlan_parity_err_mask:1;
+ uint64_t tcam_parity_err_mask:1;
+ uint64_t pkt_drop_mask:1;
+ uint64_t rsrvd:61;
+#endif
+ } bits;
+} pfc_int_mask_t;
+
+
+/*
+ * Register: PfcDropLog
+ * Packet Drop Log
+ * Description: Packet drop log. Log for capturing packet drops. Log
+ * is re-armed when associated interrupt bit is cleared.
+ * Fields:
+ * drop because bitwise OR of the tcp control bits masked on = 1
+ * drop because L2 address did not match
+ * drop because class code indicated drop
+ * drop because TCAM result indicated drop
+ * drop because blade was not a member of VLAN
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:59;
+ uint64_t tcp_ctrl_drop:1;
+ uint64_t l2_addr_drop:1;
+ uint64_t class_code_drop:1;
+ uint64_t tcam_drop:1;
+ uint64_t vlan_drop:1;
+#else
+ uint64_t vlan_drop:1;
+ uint64_t tcam_drop:1;
+ uint64_t class_code_drop:1;
+ uint64_t l2_addr_drop:1;
+ uint64_t tcp_ctrl_drop:1;
+ uint64_t rsrvd:59;
+#endif
+ } bits;
+} pfc_drop_log_t;
+
+
+/*
+ * Register: PfcDropLogMask
+ * Packet Drop Log Mask
+ * Description: Mask for logging packet drop. If the drop type is
+ * masked off, it will not trigger the drop log to capture the packet
+ * drop
+ * Fields:
+ * mask drop because bitwise OR of the tcp control bits masked on
+ * = 1
+ * mask drop because L2 address did not match
+ * mask drop because class code indicated
+ * mask drop because TCAM result indicated drop
+ * mask drop because blade was not a member of VLAN
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:59;
+ uint64_t tcp_ctrl_drop_mask:1;
+ uint64_t l2_addr_drop_mask:1;
+ uint64_t class_code_drop_mask:1;
+ uint64_t tcam_drop_mask:1;
+ uint64_t vlan_drop_mask:1;
+#else
+ uint64_t vlan_drop_mask:1;
+ uint64_t tcam_drop_mask:1;
+ uint64_t class_code_drop_mask:1;
+ uint64_t l2_addr_drop_mask:1;
+ uint64_t tcp_ctrl_drop_mask:1;
+ uint64_t rsrvd:59;
+#endif
+ } bits;
+} pfc_drop_log_mask_t;
+
+
+/*
+ * Register: PfcVlanParErrLog
+ * VLAN Parity Error Log
+ * Description: Log of parity errors in VLAN table.
+ * Fields:
+ * address of parity error. Log is cleared when corresponding
+ * interrupt bit is cleared by writing '1'.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:52;
+ uint64_t addr:12;
+#else
+ uint64_t addr:12;
+ uint64_t rsrvd:52;
+#endif
+ } bits;
+} pfc_vlan_par_err_log_t;
+
+
+/*
+ * Register: PfcTcamParErrLog
+ * TCAM Parity Error Log
+ * Description: Log of parity errors in TCAM result lookup table.
+ * Fields:
+ * address of parity error. Log is cleared when corresponding
+ * interrupt bit is cleared by writing '1'.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:56;
+ uint64_t addr:8;
+#else
+ uint64_t addr:8;
+ uint64_t rsrvd:56;
+#endif
+ } bits;
+} pfc_tcam_par_err_log_t;
+
+
+/*
+ * Register: PfcBadCsCounter
+ * PFC Bad Checksum Counter
+ * Description: Count number of bad TCP/UDP checksum. Only counted if
+ * L2 adddress matched
+ * Fields:
+ * count of number of bad TCP/UDP checksums received. Clear on
+ * read
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t bad_cs_count:32;
+#else
+ uint64_t bad_cs_count:32;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} pfc_bad_cs_counter_t;
+
+
+/*
+ * Register: PfcDropCounter
+ * PFC Drop Counter
+ * Description: Count number of packets dropped due to VLAN
+ * membership, class code, TCP control bits, or TCAM results Only
+ * counted if L2 address matched.
+ * Fields:
+ * Count of number of packets dropped due to VLAN, TCAM results.
+ * Clear on read
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t drop_count:32;
+#else
+ uint64_t drop_count:32;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} pfc_drop_counter_t;
+
+
+/*
+ * Register: PfcAutoInit
+ * PFC Auto Init
+ * Description: PFC Auto Initialization. Writing to this register
+ * triggers the auto initialization of the blade's TCAM entries with
+ * 100 bits of '0' for both key and mask. TCAM lookup is disabled
+ * during auto initialization.
+ * Fields:
+ * TCAM auto initialization status. 0=busy, 1=done.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:63;
+ uint64_t auto_init_status:1;
+#else
+ uint64_t auto_init_status:1;
+ uint64_t rsrvd:63;
+#endif
+ } bits;
+} pfc_auto_init_t;
+
+
+#endif /* _HXGE_PFC_HW_H */
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#ifndef _HXGE_RDC_HW_H
+#define _HXGE_RDC_HW_H
+
+#define RDC_BASE_ADDR 0X00300000
+
+#define RDC_PAGE_HANDLE (RDC_BASE_ADDR + 0x8)
+#define RDC_RX_CFG1 (RDC_BASE_ADDR + 0x20)
+#define RDC_RX_CFG2 (RDC_BASE_ADDR + 0x28)
+#define RDC_RBR_CFG_A (RDC_BASE_ADDR + 0x40)
+#define RDC_RBR_CFG_B (RDC_BASE_ADDR + 0x48)
+#define RDC_RBR_KICK (RDC_BASE_ADDR + 0x50)
+#define RDC_RBR_QLEN (RDC_BASE_ADDR + 0x58)
+#define RDC_RBR_HEAD (RDC_BASE_ADDR + 0x68)
+#define RDC_RCR_CFG_A (RDC_BASE_ADDR + 0x80)
+#define RDC_RCR_CFG_B (RDC_BASE_ADDR + 0x88)
+#define RDC_RCR_QLEN (RDC_BASE_ADDR + 0x90)
+#define RDC_RCR_TAIL (RDC_BASE_ADDR + 0xA0)
+#define RDC_RCR_FLUSH (RDC_BASE_ADDR + 0xA8)
+#define RDC_CLOCK_DIV (RDC_BASE_ADDR + 0xB0)
+#define RDC_INT_MASK (RDC_BASE_ADDR + 0xB8)
+#define RDC_STAT (RDC_BASE_ADDR + 0xC0)
+#define RDC_PKT_COUNT (RDC_BASE_ADDR + 0xD0)
+#define RDC_DROP_COUNT (RDC_BASE_ADDR + 0xD8)
+#define RDC_BYTE_COUNT (RDC_BASE_ADDR + 0xE0)
+#define RDC_PREF_CMD (RDC_BASE_ADDR + 0x100)
+#define RDC_PREF_DATA (RDC_BASE_ADDR + 0x108)
+#define RDC_SHADOW_CMD (RDC_BASE_ADDR + 0x110)
+#define RDC_SHADOW_DATA (RDC_BASE_ADDR + 0x118)
+#define RDC_SHADOW_PAR_DATA (RDC_BASE_ADDR + 0x120)
+#define RDC_CTRL_FIFO_CMD (RDC_BASE_ADDR + 0x128)
+#define RDC_CTRL_FIFO_DATA_LO (RDC_BASE_ADDR + 0x130)
+#define RDC_CTRL_FIFO_DATA_HI (RDC_BASE_ADDR + 0x138)
+#define RDC_CTRL_FIFO_DATA_ECC (RDC_BASE_ADDR + 0x140)
+#define RDC_DATA_FIFO_CMD (RDC_BASE_ADDR + 0x148)
+#define RDC_DATA_FIFO_DATA_LO (RDC_BASE_ADDR + 0x150)
+#define RDC_DATA_FIFO_DATA_HI (RDC_BASE_ADDR + 0x158)
+#define RDC_DATA_FIFO_DATA_ECC (RDC_BASE_ADDR + 0x160)
+#define RDC_STAT_INT_DBG (RDC_BASE_ADDR + 0x200)
+#define RDC_PREF_PAR_LOG (RDC_BASE_ADDR + 0x210)
+#define RDC_SHADOW_PAR_LOG (RDC_BASE_ADDR + 0x218)
+#define RDC_CTRL_FIFO_ECC_LOG (RDC_BASE_ADDR + 0x220)
+#define RDC_DATA_FIFO_ECC_LOG (RDC_BASE_ADDR + 0x228)
+#define RDC_FIFO_ERR_MASK (RDC_BASE_ADDR + 0x230)
+#define RDC_FIFO_ERR_STAT (RDC_BASE_ADDR + 0x238)
+#define RDC_FIFO_ERR_INT_DBG (RDC_BASE_ADDR + 0x240)
+#define RDC_PEU_TXN_LOG (RDC_BASE_ADDR + 0x250)
+#define RDC_DBG_TRAINING_VEC (RDC_BASE_ADDR + 0x300)
+#define RDC_DBG_GRP_SEL (RDC_BASE_ADDR + 0x308)
+
+
+/*
+ * Register: RdcPageHandle
+ * Logical Page Handle
+ * Description: Logical page handle specifying upper bits of 64-bit
+ * PCIE addresses. Fields in this register are part of the dma
+ * configuration and cannot be changed once the dma is enabled.
+ * Fields:
+ * Bits [63:44] of a 64-bit address, used to concatenate to a
+ * 44-bit address when generating 64-bit addresses on the PCIE
+ * bus.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:44;
+ uint64_t handle:20;
+#else
+ uint64_t handle:20;
+ uint64_t rsrvd:44;
+#endif
+ } bits;
+} rdc_page_handle_t;
+
+
+/*
+ * Register: RdcRxCfg1
+ * DMA Configuration 1
+ * Description: Configuration parameters for receive DMA block.
+ * Fields in this register are part of the dma configuration and
+ * cannot be changed once the dma is enabled.
+ * The usage of enable, reset, and qst is as follows. Software
+ * should use the following sequence to reset a DMA channel. First,
+ * set DMA.enable to 0, wait for DMA.qst=1 and then, set DMA.reset to
+ * 1. After DMA.reset is cleared by hardware and the DMA.qst is set
+ * to 1, software may then start configuring the DMA channel. The
+ * DMA.enable can be set or cleared while the DMA is in operation.
+ * The state machines of the DMA may not have returned to its initial
+ * states yet after the DMA.enable bit is cleared. This condition is
+ * indicated by the value of the DMA.qst. An example of DMA.enable
+ * being cleared during operation is when a fatal error occurs.
+ * Fields:
+ * Set to 1 to enable the Receive DMA. If set to 0, packets
+ * selecting this DMA will be discarded. On fatal errors, this
+ * bit will be cleared by hardware. This bit cannot be set if sw
+ * has not resolved any pending fatal error condition: i.e. any
+ * RdcStat ldf1 error bits remain set.
+ * Set to 1 to reset the DMA. Hardware will clear this bit after
+ * reset is completed. A reset will bring the sepecific DMA back
+ * to the power on state (including the DMA.en in this register).
+ * When set to 1, it indicates all state associated with the DMA
+ * are in its initial state following either dma reset or
+ * disable. Thus, once this is set to 1, sw could start to
+ * configure the DMA if needed.
+ * Bits [43:32] of the Mailbox address.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t enable:1;
+ uint64_t reset:1;
+ uint64_t qst:1;
+ uint64_t rsrvd1:17;
+ uint64_t mbaddr_h:12;
+#else
+ uint64_t mbaddr_h:12;
+ uint64_t rsrvd1:17;
+ uint64_t qst:1;
+ uint64_t reset:1;
+ uint64_t enable:1;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} rdc_rx_cfg1_t;
+
+
+/*
+ * Register: RdcRxCfg2
+ * DMA Configuration 2
+ * Description: Configuration parameters for receive DMA block.
+ * Fields in this register are part of the dma configuration and
+ * cannot be changed once the dma is enabled.
+ * Fields:
+ * Bits [31:6] of the Mailbox address. Bits [5:0] are assumed to
+ * be zero, or 64B aligned.
+ * Multiple of 64Bs, 0 means no offset, b01 means 64B, b10 means
+ * 128B. b11 is invalid, hardware behavior not specified.
+ * Set to 1 to select the entire header of 6B.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t mbaddr_l:26;
+ uint64_t rsrvd1:3;
+ uint64_t offset:2;
+ uint64_t full_hdr:1;
+#else
+ uint64_t full_hdr:1;
+ uint64_t offset:2;
+ uint64_t rsrvd1:3;
+ uint64_t mbaddr_l:26;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} rdc_rx_cfg2_t;
+
+
+/*
+ * Register: RdcRbrCfgA
+ * RBR Configuration A
+ * Description: The following registers are used to configure and
+ * manage the RBR. Note that the entire RBR must stay within the
+ * 'page' defined by staddrBase. The behavior of the hardware is
+ * undefined if the last entry is outside of the page (if bits 43:18
+ * of the address of the last entry are different from bits 43:18 of
+ * the base address). Hardware will support wrapping around at the
+ * end of the ring buffer defined by LEN. LEN must be a multiple of
+ * 64. Fields in this register are part of the dma configuration and
+ * cannot be changed once the dma is enabled.
+ * HW does not check for all configuration errors across different
+ * fields.
+ *
+ * Fields:
+ * Bits 15:6 of the maximum number of RBBs in the buffer ring.
+ * Bits 5:0 are hardcoded to zero. The maximum is (2^16 - 64) and
+ * is limited by the staddr value. (len + staddr) should not
+ * exceed (2^16 - 64).
+ * Bits [43:18] of the address for the RBR. This value remains
+ * fixed, and is used as the base address of the ring. All
+ * entries in the ring have this as their upper address bits.
+ * Bits [17:6] of the address of the RBR. staddrBase concatinated
+ * with staddr is the starting address of the RBR. (len + staddr)
+ * should not exceed (2^16 - 64).
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t len:10;
+ uint64_t len_lo:6;
+ uint64_t rsrvd:4;
+ uint64_t staddr_base:26;
+ uint64_t staddr:12;
+ uint64_t rsrvd1:6;
+#else
+ uint64_t rsrvd1:6;
+ uint64_t staddr:12;
+ uint64_t staddr_base:26;
+ uint64_t rsrvd:4;
+ uint64_t len_lo:6;
+ uint64_t len:10;
+#endif
+ } bits;
+} rdc_rbr_cfg_a_t;
+
+
+/*
+ * Register: RdcRbrCfgB
+ * RBR Configuration B
+ * Description: This register configures the block size, and the
+ * individual packet buffer sizes. The VLD bits of the three block
+ * sizes have to be set to 1 in normal operations. These bits may be
+ * turned off for debug purpose only. Fields in this register are
+ * part of the dma configuration and cannot be changed once the dma
+ * is enabled.
+ * Fields:
+ * Buffer Block Size. b0 - 4K; b1 - 8K.
+ * Set to 1 to indicate SIZE2 is valid, and enable hardware to
+ * allocate buffers of size 2. Always set to 1 in normal
+ * operation.
+ * Size 2 of packet buffer. b0 - 2K; b1 - 4K.
+ * Set to 1 to indicate SIZE1 is valid, and enable hardware to
+ * allocate buffers of size 1. Always set to 1 in normal
+ * operation.
+ * Size 1 of packet buffer. b0 - 1K; b1 - 2K.
+ * Set to 1 to indicate SIZE0 is valid, and enable hardware to
+ * allocate buffers of size 0. Always set to 1 in normal
+ * operation.
+ * Size 0 of packet buffer. b00 - 256; b01 - 512; b10 - 1K; b11 -
+ * reserved.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:39;
+ uint64_t bksize:1;
+ uint64_t vld2:1;
+ uint64_t rsrvd1:6;
+ uint64_t bufsz2:1;
+ uint64_t vld1:1;
+ uint64_t rsrvd2:6;
+ uint64_t bufsz1:1;
+ uint64_t vld0:1;
+ uint64_t rsrvd3:5;
+ uint64_t bufsz0:2;
+#else
+ uint64_t bufsz0:2;
+ uint64_t rsrvd3:5;
+ uint64_t vld0:1;
+ uint64_t bufsz1:1;
+ uint64_t rsrvd2:6;
+ uint64_t vld1:1;
+ uint64_t bufsz2:1;
+ uint64_t rsrvd1:6;
+ uint64_t vld2:1;
+ uint64_t bksize:1;
+ uint64_t rsrvd:39;
+#endif
+ } bits;
+} rdc_rbr_cfg_b_t;
+
+
+/*
+ * Register: RdcRbrKick
+ * RBR Kick
+ * Description: Block buffer addresses are added to the ring buffer
+ * by software. When software writes to the Kick register, indicating
+ * the number of descriptors added, hardware will update the internal
+ * state of the corresponding buffer pool.
+ * HW does not check for all configuration errors across different
+ * fields.
+ *
+ * Fields:
+ * Number of Block Buffers added by software. Hardware effect
+ * will be triggered when the register is written to.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:48;
+ uint64_t bkadd:16;
+#else
+ uint64_t bkadd:16;
+ uint64_t rsrvd:48;
+#endif
+ } bits;
+} rdc_rbr_kick_t;
+
+
+/*
+ * Register: RdcRbrQlen
+ * RBR Queue Length
+ * Description: The current number of entries in the RBR.
+ * Fields:
+ * Number of block addresses in the ring buffer.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:48;
+ uint64_t qlen:16;
+#else
+ uint64_t qlen:16;
+ uint64_t rsrvd:48;
+#endif
+ } bits;
+} rdc_rbr_qlen_t;
+
+
+/*
+ * Register: RdcRbrHead
+ * RBR Head
+ * Description: Lower bits of the RBR head pointer. Software programs
+ * the upper bits, specified in rdcRbrConfigA.staddrBase.
+ * Fields:
+ * Bits [17:2] of the software posted address, 4B aligned. This
+ * pointer is updated by hardware after each block buffer is
+ * consumed.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:46;
+ uint64_t head:16;
+ uint64_t rsrvd1:2;
+#else
+ uint64_t rsrvd1:2;
+ uint64_t head:16;
+ uint64_t rsrvd:46;
+#endif
+ } bits;
+} rdc_rbr_head_t;
+
+
+/*
+ * Register: RdcRcrCfgA
+ * RCR Configuration A
+ * Description: The RCR should be within the 'page' defined by the
+ * staddrBase, i.e. staddrBase concatenate with STADDR plus 8 x LEN
+ * should be within the last address of the 'page' defined by
+ * staddrBase. The length must be a multiple of 32. Fields in this
+ * register are part of the dma configuration and cannot be changed
+ * once the dma is enabled.
+ * HW does not check for all configuration errors across different
+ * fields.
+ *
+ * Fields:
+ * Bits 15:5 of the maximum number of 8B entries in RCR. Bits 4:0
+ * are hard-coded to zero. The maximum size is (2^16 - 32) and is
+ * limited by staddr value. (len + staddr) should not exceed
+ * (2^16 - 32).
+ * Bits [43:19] of the Start address for the RCR.
+ * Bits [18:6] of start address for the RCR. (len + staddr)
+ * should not exceed (2^16 - 32).
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t len:11;
+ uint64_t len_lo:5;
+ uint64_t rsrvd:4;
+ uint64_t staddr_base:25;
+ uint64_t staddr:13;
+ uint64_t rsrvd1:6;
+#else
+ uint64_t rsrvd1:6;
+ uint64_t staddr:13;
+ uint64_t staddr_base:25;
+ uint64_t rsrvd:4;
+ uint64_t len_lo:5;
+ uint64_t len:11;
+#endif
+ } bits;
+} rdc_rcr_cfg_a_t;
+
+
+/*
+ * Register: RdcRcrCfgB
+ * RCR Configuration B
+ * Description: RCR configuration settings.
+ * Fields:
+ * Packet Threshold; when the number of packets enqueued in RCR
+ * is strictly larger than PTHRES, the DMA MAY issue an interrupt
+ * if enabled.
+ * Enable timeout. If set to one, enable the timeout. A timeout
+ * will initiate an update of the software visible states. If
+ * interrupt is armed, in addition to the update, an interrupt to
+ * CPU will be generated, and the interrupt disarmed.
+ * Time out value. The system clock is divided down by the value
+ * programmed in the Receive DMA Clock Divider register.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t pthres:16;
+ uint64_t entout:1;
+ uint64_t rsrvd1:9;
+ uint64_t timeout:6;
+#else
+ uint64_t timeout:6;
+ uint64_t rsrvd1:9;
+ uint64_t entout:1;
+ uint64_t pthres:16;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} rdc_rcr_cfg_b_t;
+
+
+/*
+ * Register: RdcRcrQlen
+ * RCR Queue Length
+ * Description: The number of entries in the RCR.
+ * Fields:
+ * Number of packets queued. Initialize to zero after the RCR
+ * Configuration A register is written to.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:48;
+ uint64_t qlen:16;
+#else
+ uint64_t qlen:16;
+ uint64_t rsrvd:48;
+#endif
+ } bits;
+} rdc_rcr_qlen_t;
+
+
+/*
+ * Register: RdcRcrTail
+ * RCR Tail
+ * Description: Lower bits of the RCR tail pointer. Software programs
+ * the upper bits, specified in rdcRcrConfigA.staddrBase.
+ * Fields:
+ * Address of the RCR Tail Pointer [18:3] (points to the next
+ * available location.) Initialized after the RCR Configuration A
+ * register is written to.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:45;
+ uint64_t tail:16;
+ uint64_t rsrvd1:3;
+#else
+ uint64_t rsrvd1:3;
+ uint64_t tail:16;
+ uint64_t rsrvd:45;
+#endif
+ } bits;
+} rdc_rcr_tail_t;
+
+
+/*
+ * Register: RdcRcrFlush
+ * RCR Flush
+ * Description: This register will force an update to the RCR in
+ * system memory.
+ * Fields:
+ * Set to 1 to force the hardware to store the shadow tail block
+ * to DRAM if the hardware state (queue length and pointers) is
+ * different from the software visible state. Reset to 0 by
+ * hardware when done.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:63;
+ uint64_t flush:1;
+#else
+ uint64_t flush:1;
+ uint64_t rsrvd:63;
+#endif
+ } bits;
+} rdc_rcr_flush_t;
+
+
+/*
+ * Register: RdcClockDiv
+ * Receive DMA Clock Divider
+ * Description: The granularity of the DMA timers is determined by
+ * the following counter. This is used to drive the DMA timeout
+ * counters. For a 250MHz system clock, a value of 25000 (decimal)
+ * will yield a granularity of 100 usec.
+ * Fields:
+ * System clock divider, determines the granularity of the DMA
+ * timeout count-down. The hardware count down is count+1.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:48;
+ uint64_t count:16;
+#else
+ uint64_t count:16;
+ uint64_t rsrvd:48;
+#endif
+ } bits;
+} rdc_clock_div_t;
+
+
+/*
+ * Register: RdcIntMask
+ * RDC Interrupt Mask
+ * Description: RDC interrupt status register. RCRTHRES and RCRTO
+ * bits are used to keep track of normal DMA operations, while the
+ * remaining bits are primarily used to detect error conditions.
+ * Fields:
+ * Set to 0 to enable flagging when rdc receives a response
+ * completion timeout from peu. Part of LDF 1.
+ * Set to 1 to enable flagging when rdc receives a poisoned
+ * completion or non-zero (unsuccessful) completion status
+ * received from PEU. Part of LDF 1.
+ * Set to 0 to enable flagging when RCR threshold crossed. Part
+ * of LDF 0.
+ * Set to 0 to enable flagging when RCR timeout. Part of LDF 0.
+ * Set to 0 to enable flagging when read from rcr shadow ram
+ * generates a parity error Part of LDF 1.
+ * Set to 0 to enable flagging when read from rbr prefetch ram
+ * generates a parity error Part of LDF 1.
+ * Set to 0 to enable flagging when Receive Block Ring prefetch
+ * is empty (not enough buffer blocks available depending on
+ * incoming pkt size) when hardware tries to queue a packet.
+ * Incoming packets will be discarded. Non-fatal error. Part of
+ * LDF 1.
+ * Set to 0 to enable flagging when packet discard because of RCR
+ * shadow full.
+ * Set to 0 to enable flagging when Receive Completion Ring full
+ * when hardware tries to enqueue the completion status of a
+ * packet. Part of LDF 1.
+ * Set to 0 to enable flagging when RBR empty when hardware
+ * attempts to prefetch. Part of LDF 1.
+ * Set to 0 to enable flagging when Receive Block Ring full when
+ * software tries to post more blocks. Part of LDF 1.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:10;
+ uint64_t rbr_cpl_to:1;
+ uint64_t peu_resp_err:1;
+ uint64_t rsrvd1:5;
+ uint64_t rcr_thres:1;
+ uint64_t rcr_to:1;
+ uint64_t rcr_shadow_par_err:1;
+ uint64_t rbr_prefetch_par_err:1;
+ uint64_t rsrvd2:2;
+ uint64_t rbr_pre_empty:1;
+ uint64_t rcr_shadow_full:1;
+ uint64_t rsrvd3:2;
+ uint64_t rcr_full:1;
+ uint64_t rbr_empty:1;
+ uint64_t rbr_full:1;
+ uint64_t rsrvd4:2;
+ uint64_t rsrvd5:32;
+#else
+ uint64_t rsrvd5:32;
+ uint64_t rsrvd4:2;
+ uint64_t rbr_full:1;
+ uint64_t rbr_empty:1;
+ uint64_t rcr_full:1;
+ uint64_t rsrvd3:2;
+ uint64_t rcr_shadow_full:1;
+ uint64_t rbr_pre_empty:1;
+ uint64_t rsrvd2:2;
+ uint64_t rbr_prefetch_par_err:1;
+ uint64_t rcr_shadow_par_err:1;
+ uint64_t rcr_to:1;
+ uint64_t rcr_thres:1;
+ uint64_t rsrvd1:5;
+ uint64_t peu_resp_err:1;
+ uint64_t rbr_cpl_to:1;
+ uint64_t rsrvd:10;
+#endif
+ } bits;
+} rdc_int_mask_t;
+
+
+/*
+ * Register: RdcStat
+ * RDC Control And Status
+ * Description: The DMA channels are controlled using this register.
+ * Fields:
+ * Set to 1 to indicate rdc received a response completion
+ * timeout from peu. Fatal error. Part of LDF 1.
+ * Set to 1 to indicate poisoned completion or non-zero
+ * (unsuccessful) completion status received from PEU. Part of
+ * LDF 1.
+ * Set to 1 to enable mailbox update. Hardware will reset to 0
+ * after one update. Software needs to set to 1 for each update.
+ * Write 0 has no effect. Note that once set by software, only
+ * hardware can reset the value. This bit is also used to keep
+ * track of the exclusivity between threshold triggered or
+ * timeout triggered interrupt. If this bit is not set, there
+ * will be no timer based interrupt, and threshold based
+ * interrupt will not issue a mailbox update. It is recommended
+ * that software should set this bit to one when arming the
+ * device for interrupt.
+ * Set to 1 to indicate RCR threshold crossed. This is a level
+ * event. Part of LDF 0.
+ * Set to 1 to indicate RCR time-outed if MEX bit is set and the
+ * queue length is non-zero when timeout occurs. When software
+ * writes 1 to this bit, RCRTO will be reset to 0. Part of LDF 0.
+ * Set to 1 to indicate read from rcr shadow ram generates a
+ * parity error Writing a 1 to this register also clears the
+ * rdcshadowParLog register Fatal error. Part of LDF 1.
+ * Set to 1 to indicate read from rbr prefetch ram generates
+ * parity error Writing a 1 to this register also clears the
+ * rdcPrefParLog register Fatal error. Part of LDF 1.
+ * Set to 1 to indicate Receive Block Ring prefetch is empty (not
+ * enough buffer blocks available depending on incoming pkt size)
+ * when hardware tries to queue a packet. Incoming packets will
+ * be discarded. Non-fatal error. Part of LDF 1.
+ * Set to 1 to indicate packet discard because of RCR shadow
+ * full. RCR Shadow full cannot be set to 1 in a normal
+ * operation. When set to 1, it indicates a fatal error. Part of
+ * LDF 1.
+ * Set to 1 to indicate Receive Completion Ring full when
+ * hardware tries to enqueue the completion status of a packet.
+ * Incoming packets will be discarded. No buffer consumed. Fatal
+ * error. Part of LDF 1.
+ * Set to 1 to indicate RBR empty when hardware attempts to
+ * prefetch. Part of LDF 1.
+ * Set to 1 to indicate Receive Buffer Ring full when software
+ * writes the kick register with a value greater than the length
+ * of the RBR length. Incoming packets will be discarded. Fatal
+ * error. Part of LDF 1.
+ * Number of buffer pointers read. Used to advance the RCR head
+ * pointer.
+ * Number of packets read; when written to, decrement the QLEN
+ * counter by PKTREAD. QLEN is lower bounded to zero.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:10;
+ uint64_t rbr_cpl_to:1;
+ uint64_t peu_resp_err:1;
+ uint64_t rsrvd1:4;
+ uint64_t mex:1;
+ uint64_t rcr_thres:1;
+ uint64_t rcr_to:1;
+ uint64_t rcr_shadow_par_err:1;
+ uint64_t rbr_prefetch_par_err:1;
+ uint64_t rsrvd2:2;
+ uint64_t rbr_pre_empty:1;
+ uint64_t rcr_shadow_full:1;
+ uint64_t rsrvd3:2;
+ uint64_t rcr_full:1;
+ uint64_t rbr_empty:1;
+ uint64_t rbr_full:1;
+ uint64_t rsrvd4:2;
+ uint64_t ptrread:16;
+ uint64_t pktread:16;
+#else
+ uint64_t pktread:16;
+ uint64_t ptrread:16;
+ uint64_t rsrvd4:2;
+ uint64_t rbr_full:1;
+ uint64_t rbr_empty:1;
+ uint64_t rcr_full:1;
+ uint64_t rsrvd3:2;
+ uint64_t rcr_shadow_full:1;
+ uint64_t rbr_pre_empty:1;
+ uint64_t rsrvd2:2;
+ uint64_t rbr_prefetch_par_err:1;
+ uint64_t rcr_shadow_par_err:1;
+ uint64_t rcr_to:1;
+ uint64_t rcr_thres:1;
+ uint64_t mex:1;
+ uint64_t rsrvd1:4;
+ uint64_t peu_resp_err:1;
+ uint64_t rbr_cpl_to:1;
+ uint64_t rsrvd:10;
+#endif
+ } bits;
+} rdc_stat_t;
+
+
+/*
+ * Register: RdcPktCount
+ * Rx DMA Packet Counter
+ * Description: Counts the number of packets received from the Rx
+ * Virtual MAC for this DMA channel.
+ * Fields:
+ * Count of SYN packets received from RVM. This counter
+ * saturates.
+ * Count of packets received from RVM. This counter saturates.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t syn_pkt_count:32;
+ uint64_t pkt_count:32;
+#else
+ uint64_t pkt_count:32;
+ uint64_t syn_pkt_count:32;
+#endif
+ } bits;
+} rdc_pkt_count_t;
+
+
+/*
+ * Register: RdcDropCount
+ * Rx DMA Dropped Packet Counters
+ * Description: Counts the number of packets dropped due to different
+ * types of errors.
+ * Fields:
+ * Count of packets dropped because they were longer than the
+ * maximum length. This counter saturates.
+ * Count of packets dropped because there was no block available
+ * in the RBR Prefetch Buffer. This counter saturates.
+ * Count of packets dropped because the RVM marked the packet as
+ * errored. This counter saturates.
+ * Count of packets dropped because there was a framing error
+ * from the RVM. This counter saturates.
+ * Count of packets dropped because the packet did not fit in the
+ * rx ram. This counter saturates.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:16;
+ uint64_t too_long:8;
+ uint64_t no_rbr_avail:8;
+ uint64_t rvm_error:8;
+ uint64_t frame_error:8;
+ uint64_t rxram_error:8;
+ uint64_t rsrvd1:8;
+#else
+ uint64_t rsrvd1:8;
+ uint64_t rxram_error:8;
+ uint64_t frame_error:8;
+ uint64_t rvm_error:8;
+ uint64_t no_rbr_avail:8;
+ uint64_t too_long:8;
+ uint64_t rsrvd:16;
+#endif
+ } bits;
+} rdc_drop_count_t;
+
+
+/*
+ * Register: RdcByteCount
+ * Rx DMA Byte Counter
+ * Description: Counts the number of bytes transferred by dma for all
+ * channels.
+ * Fields:
+ * Count of bytes transferred by dma. This counter saturates.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t count:32;
+#else
+ uint64_t count:32;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} rdc_byte_count_t;
+
+
+/*
+ * Register: RdcPrefCmd
+ * Rx DMA Prefetch Buffer Command
+ * Description: Allows debug access to the entire prefetch buffer,
+ * along with the rdcPrefData register. Writing the rdcPrefCmd
+ * triggers the access. For writes, software writes the 32 bits of
+ * data to the rdcPrefData register before writing the write command
+ * to this register. For reads, software first writes the the read
+ * command to this register, then reads the 32-bit value from the
+ * rdcPrefData register. The status field should be polled by
+ * software until it goes low, indicating the read or write has
+ * completed.
+ * Fields:
+ * status of indirect access 0=busy 1=done
+ * Command type. 1 indicates a read command, 0 a write command.
+ * enable writing of parity bits 1=enabled, 0=disabled
+ * DMA channel of entry to read or write
+ * Entry in the prefetch buffer to read or write
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t status:1;
+ uint64_t cmd:1;
+ uint64_t par_en:1;
+ uint64_t rsrvd1:22;
+ uint64_t dmc:2;
+ uint64_t entry:5;
+#else
+ uint64_t entry:5;
+ uint64_t dmc:2;
+ uint64_t rsrvd1:22;
+ uint64_t par_en:1;
+ uint64_t cmd:1;
+ uint64_t status:1;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} rdc_pref_cmd_t;
+
+
+/*
+ * Register: RdcPrefData
+ * Rx DMA Prefetch Buffer Data
+ * Description: See rdcPrefCmd register.
+ * Fields:
+ * For writes, parity bits is written into prefetch buffer. For
+ * reads, parity bits read from the prefetch buffer.
+ * For writes, data which is written into prefetch buffer. For
+ * reads, data read from the prefetch buffer.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:28;
+ uint64_t par:4;
+ uint64_t data:32;
+#else
+ uint64_t data:32;
+ uint64_t par:4;
+ uint64_t rsrvd:28;
+#endif
+ } bits;
+} rdc_pref_data_t;
+
+
+/*
+ * Register: RdcShadowCmd
+ * Rx DMA Shadow Tail Command
+ * Description: Allows debug access to the entire shadow tail, along
+ * with the rdcShadowData register. Writing the rdcShadowCmd triggers
+ * the access. For writes, software writes the 64 bits of data to the
+ * rdcShadowData register before writing the write command to this
+ * register. For reads, software first writes the the read command to
+ * this register, then reads the 64-bit value from the rdcShadowData
+ * register. The valid field should be polled by software until it
+ * goes low, indicating the read or write has completed.
+ * Fields:
+ * status of indirect access 0=busy 1=done
+ * Command type. 1 indicates a read command, 0 a write command.
+ * enable writing of parity bits 1=enabled, 0=disabled
+ * DMA channel of entry to read or write
+ * Entry in the shadow tail to read or write
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t status:1;
+ uint64_t cmd:1;
+ uint64_t par_en:1;
+ uint64_t rsrvd1:23;
+ uint64_t dmc:2;
+ uint64_t entry:4;
+#else
+ uint64_t entry:4;
+ uint64_t dmc:2;
+ uint64_t rsrvd1:23;
+ uint64_t par_en:1;
+ uint64_t cmd:1;
+ uint64_t status:1;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} rdc_shadow_cmd_t;
+
+
+/*
+ * Register: RdcShadowData
+ * Rx DMA Shadow Tail Data
+ * Description: See rdcShadowCmd register.
+ * Fields:
+ * For writes, data which is written into shadow tail. For reads,
+ * data read from the shadow tail.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t data:64;
+#else
+ uint64_t data:64;
+#endif
+ } bits;
+} rdc_shadow_data_t;
+
+
+/*
+ * Register: RdcShadowParData
+ * Rx DMA Shadow Tail Parity Data
+ * Description: See rdcShadowCmd register.
+ * Fields:
+ * For writes, parity data is written into shadow tail. For
+ * reads, parity data read from the shadow tail.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rsrvd1:24;
+ uint64_t parity_data:8;
+#else
+ uint64_t parity_data:8;
+ uint64_t rsrvd1:24;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} rdc_shadow_par_data_t;
+
+
+/*
+ * Register: RdcCtrlFifoCmd
+ * Rx DMA Control Fifo Command
+ * Description: Allows debug access to the entire Rx Ctl FIFO, along
+ * with the rdcCtrlFifoData register. Writing the rdcCtrlFifoCmd
+ * triggers the access. For writes, software writes the 128 bits of
+ * data to the rdcCtrlFifoData registers before writing the write
+ * command to this register. For reads, software first writes the the
+ * read command to this register, then reads the 128-bit value from
+ * the rdcCtrlFifoData registers. The valid field should be polled by
+ * software until it goes low, indicating the read or write has
+ * completed.
+ * Fields:
+ * status of indirect access 0=busy 1=done
+ * Command type. 1 indicates a read command, 0 a write command.
+ * enable writing of ECC bits 1=enabled, 0=disabled
+ * Entry in the rx control ram to read or write
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t status:1;
+ uint64_t cmd:1;
+ uint64_t ecc_en:1;
+ uint64_t rsrvd1:20;
+ uint64_t entry:9;
+#else
+ uint64_t entry:9;
+ uint64_t rsrvd1:20;
+ uint64_t ecc_en:1;
+ uint64_t cmd:1;
+ uint64_t status:1;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} rdc_ctrl_fifo_cmd_t;
+
+
+/*
+ * Register: RdcCtrlFifoDataLo
+ * Rx DMA Control Fifo Data Lo
+ * Description: Lower 64 bits read or written to the Rx Ctl FIFO. See
+ * rdcCtrlFifoCmd register.
+ * Fields:
+ * For writes, data which is written into rx control ram. For
+ * reads, data read from the rx control ram.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t data:64;
+#else
+ uint64_t data:64;
+#endif
+ } bits;
+} rdc_ctrl_fifo_data_lo_t;
+
+
+/*
+ * Register: RdcCtrlFifoDataHi
+ * Rx DMA Control Fifo Data Hi
+ * Description: Upper 64 bits read or written to the Rx Ctl FIFO. See
+ * rdcCtrlFifoCmd register.
+ * Fields:
+ * For writes, data which is written into rx control ram. For
+ * reads, data read from the rx control ram.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t data:64;
+#else
+ uint64_t data:64;
+#endif
+ } bits;
+} rdc_ctrl_fifo_data_hi_t;
+
+
+/*
+ * Register: RdcCtrlFifoDataEcc
+ * Rx DMA Control Fifo Data ECC
+ * Description: 16 bits ECC data read or written to the Rx Ctl FIFO.
+ * See rdcCtrlFifoCmd register.
+ * Fields:
+ * For writes, data which is written into rx control ram. For
+ * reads, data read from the rx control ram.
+ * For writes, data which is written into rx control ram. For
+ * reads, data read from the rx control ram.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rsrvd1:16;
+ uint64_t ecc_data_hi:8;
+ uint64_t ecc_data_lo:8;
+#else
+ uint64_t ecc_data_lo:8;
+ uint64_t ecc_data_hi:8;
+ uint64_t rsrvd1:16;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} rdc_ctrl_fifo_data_ecc_t;
+
+
+/*
+ * Register: RdcDataFifoCmd
+ * Rx DMA Data Fifo Command
+ * Description: Allows debug access to the entire Rx Data FIFO, along
+ * with the rdcDataFifoData register. Writing the rdcCtrlFifoCmd
+ * triggers the access. For writes, software writes the 128 bits of
+ * data to the rdcDataFifoData registers before writing the write
+ * command to this register. For reads, software first writes the the
+ * read command to this register, then reads the 128-bit value from
+ * the rdcDataFifoData registers. The valid field should be polled by
+ * software until it goes low, indicating the read or write has
+ * completed.
+ * Fields:
+ * status of indirect access 0=busy 1=done
+ * Command type. 1 indicates a read command, 0 a write command.
+ * enable writing of ECC bits 1=enabled, 0=disabled
+ * Entry in the rx data ram to read or write
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t status:1;
+ uint64_t cmd:1;
+ uint64_t ecc_en:1;
+ uint64_t rsrvd1:18;
+ uint64_t entry:11;
+#else
+ uint64_t entry:11;
+ uint64_t rsrvd1:18;
+ uint64_t ecc_en:1;
+ uint64_t cmd:1;
+ uint64_t status:1;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} rdc_data_fifo_cmd_t;
+
+
+/*
+ * Register: RdcDataFifoDataLo
+ * Rx DMA Data Fifo Data Lo
+ * Description: Lower 64 bits read or written to the Rx Data FIFO.
+ * See rdcDataFifoCmd register.
+ * Fields:
+ * For writes, data which is written into rx data ram. For reads,
+ * data read from the rx data ram.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t data:64;
+#else
+ uint64_t data:64;
+#endif
+ } bits;
+} rdc_data_fifo_data_lo_t;
+
+
+/*
+ * Register: RdcDataFifoDataHi
+ * Rx DMA Data Fifo Data Hi
+ * Description: Upper 64 bits read or written to the Rx Data FIFO.
+ * See rdcDataFifoCmd register.
+ * Fields:
+ * For writes, data which is written into rx data ram. For reads,
+ * data read from the rx data ram.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t data:64;
+#else
+ uint64_t data:64;
+#endif
+ } bits;
+} rdc_data_fifo_data_hi_t;
+
+
+/*
+ * Register: RdcDataFifoDataEcc
+ * Rx DMA Data Fifo ECC Data
+ * Description: 16 bits ECC data read or written to the Rx Data FIFO.
+ * See rdcDataFifoCmd register.
+ * Fields:
+ * For writes, data which is written into rx data ram. For reads,
+ * data read from the rx data ram.
+ * For writes, data which is written into rx data ram. For reads,
+ * data read from the rx data ram.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rsrvd1:16;
+ uint64_t ecc_data_hi:8;
+ uint64_t ecc_data_lo:8;
+#else
+ uint64_t ecc_data_lo:8;
+ uint64_t ecc_data_hi:8;
+ uint64_t rsrvd1:16;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} rdc_data_fifo_data_ecc_t;
+
+
+/*
+ * Register: RdcStatIntDbg
+ * RDC Debug Control and Status Interrupt
+ * Description: RDC debug control and status interrupt register.
+ * Debug RDC control and status register bits to check if interrupt
+ * is asserted used to detect error conditions.
+ * Fields:
+ * Set to 1 to enable interrupt Part of LDF 1.
+ * Set to 1 to enable interrupt Part of LDF 1.
+ * Set to 1 to enable interrupt Part of LDF 0.
+ * Set to 1 to enable interrupt Part of LDF 0.
+ * Set to 1 to enable interrupt Part of LDF 1.
+ * Set to 1 to enable interrupt Part of LDF 1.
+ * Set to 1 to enable interrupt Part of LDF 1.
+ * Set to 1 to enable interrupt
+ * Set to 1 to enable interrupt Part of LDF 1.
+ * Set to 1 to enable interrupt Part of LDF 1.
+ * Set to 1 to enable interrupt Part of LDF 1.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:10;
+ uint64_t rbr_cpl_to:1;
+ uint64_t peu_resp_err:1;
+ uint64_t rsrvd1:5;
+ uint64_t rcr_thres:1;
+ uint64_t rcr_to:1;
+ uint64_t rcr_shadow_par_err:1;
+ uint64_t rbr_prefetch_par_err:1;
+ uint64_t rsrvd2:2;
+ uint64_t rbr_pre_empty:1;
+ uint64_t rcr_shadow_full:1;
+ uint64_t rsrvd3:2;
+ uint64_t rcr_full:1;
+ uint64_t rbr_empty:1;
+ uint64_t rbr_full:1;
+ uint64_t rsrvd4:2;
+ uint64_t rsrvd5:32;
+#else
+ uint64_t rsrvd5:32;
+ uint64_t rsrvd4:2;
+ uint64_t rbr_full:1;
+ uint64_t rbr_empty:1;
+ uint64_t rcr_full:1;
+ uint64_t rsrvd3:2;
+ uint64_t rcr_shadow_full:1;
+ uint64_t rbr_pre_empty:1;
+ uint64_t rsrvd2:2;
+ uint64_t rbr_prefetch_par_err:1;
+ uint64_t rcr_shadow_par_err:1;
+ uint64_t rcr_to:1;
+ uint64_t rcr_thres:1;
+ uint64_t rsrvd1:5;
+ uint64_t peu_resp_err:1;
+ uint64_t rbr_cpl_to:1;
+ uint64_t rsrvd:10;
+#endif
+ } bits;
+} rdc_stat_int_dbg_t;
+
+
+/*
+ * Register: RdcPrefParLog
+ * Rx DMA Prefetch Buffer Parity Log
+ * Description: RDC DMA Prefetch Buffer parity log register This
+ * register logs the first parity error that is encountered. Writing
+ * a 1 to RdcStat::rbrPrefetchParErr clears this register
+ * Fields:
+ * Address of parity error
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:57;
+ uint64_t address:7;
+#else
+ uint64_t address:7;
+ uint64_t rsrvd:57;
+#endif
+ } bits;
+} rdc_pref_par_log_t;
+
+
+/*
+ * Register: RdcShadowParLog
+ * Rx DMA Shadow Tail Parity Log
+ * Description: RDC DMA Shadow Tail parity log register This register
+ * logs the first parity error that is encountered. Writing a 1 to
+ * RdcStat::rcrShadowParErr clears this register
+ * Fields:
+ * Address of parity error
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rsrvd1:26;
+ uint64_t address:6;
+#else
+ uint64_t address:6;
+ uint64_t rsrvd1:26;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} rdc_shadow_par_log_t;
+
+
+/*
+ * Register: RdcCtrlFifoEccLog
+ * Rx DMA Control Fifo ECC Log
+ * Description: RDC DMA Control FIFO ECC log register This register
+ * logs the first ECC error that is encountered. A double-bit ecc
+ * error over writes any single-bit ecc error previously logged
+ * Fields:
+ * Address of ECC error for upper 64 bits Writing a 1 to
+ * RdcFifoErrStat::rxCtrlFifoDed[1] or
+ * RdcFifoErrStat::rxCtrlFifoSec[1] clears this register
+ * Address of ECC error for lower 64 bits Writing a 1 to
+ * RdcFifoErrStat::rxCtrlFifoDed[0] or
+ * RdcFifoErrStat::rxCtrlFifoSec[0] clears this register
+ * ECC syndrome for upper 64 bits Writing a 1 to
+ * RdcFifoErrStat::rxCtrlFifoDed[1] or
+ * RdcFifoErrStat::rxCtrlFifoSec[1] clears this register
+ * ECC syndrome for lower 64 bits Writing a 1 to
+ * RdcFifoErrStat::rxCtrlFifoDed[0] or
+ * RdcFifoErrStat::rxCtrlFifoSec[0] clears this register
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:7;
+ uint64_t address_hi:9;
+ uint64_t rsrvd1:7;
+ uint64_t address_lo:9;
+ uint64_t rsrvd2:8;
+ uint64_t syndrome_hi:8;
+ uint64_t rsrvd3:8;
+ uint64_t syndrome_lo:8;
+#else
+ uint64_t syndrome_lo:8;
+ uint64_t rsrvd3:8;
+ uint64_t syndrome_hi:8;
+ uint64_t rsrvd2:8;
+ uint64_t address_lo:9;
+ uint64_t rsrvd1:7;
+ uint64_t address_hi:9;
+ uint64_t rsrvd:7;
+#endif
+ } bits;
+} rdc_ctrl_fifo_ecc_log_t;
+
+
+/*
+ * Register: RdcDataFifoEccLog
+ * Rx DMA Data Fifo ECC Log
+ * Description: RDC DMA data FIFO ECC log register This register logs
+ * the first ECC error that is encountered. A double-bit ecc error
+ * over writes any single-bit ecc error previously logged
+ * Fields:
+ * Address of ECC error for upper 64 bits Writing a 1 to
+ * RdcFifoErrStat::rxDataFifoDed[1] or
+ * RdcFifoErrStat::rxDataFifoSec[1] clears this register
+ * Address of ECC error for lower 64 bits Writing a 1 to
+ * RdcFifoErrStat::rxDataFifoDed[0] or
+ * RdcFifoErrStat::rxDataFifoSec[0] clears this register
+ * ECC syndrome for upper 64 bits Writing a 1 to
+ * RdcFifoErrStat::rxDataFifoDed[1] or
+ * RdcFifoErrStat::rxDataFifoSec[1] clears this register
+ * ECC syndrome for lower 64 bits Writing a 1 to
+ * RdcFifoErrStat::rxDataFifoDed[0] or
+ * RdcFifoErrStat::rxDataFifoSec[0] clears this register
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:5;
+ uint64_t address_hi:11;
+ uint64_t rsrvd1:5;
+ uint64_t address_lo:11;
+ uint64_t rsrvd2:8;
+ uint64_t syndrome_hi:8;
+ uint64_t rsrvd3:8;
+ uint64_t syndrome_lo:8;
+#else
+ uint64_t syndrome_lo:8;
+ uint64_t rsrvd3:8;
+ uint64_t syndrome_hi:8;
+ uint64_t rsrvd2:8;
+ uint64_t address_lo:11;
+ uint64_t rsrvd1:5;
+ uint64_t address_hi:11;
+ uint64_t rsrvd:5;
+#endif
+ } bits;
+} rdc_data_fifo_ecc_log_t;
+
+
+/*
+ * Register: RdcFifoErrMask
+ * FIFO Error Interrupt Mask
+ * Description: FIFO Error interrupt mask register. Control the
+ * interrupt assertion of FIFO Errors. see FIFO Error Status register
+ * for more description
+ * Fields:
+ * Set to 0 to enable flagging when rx ctrl ram logs ecc single
+ * bit error Part of Device Error 0.
+ * Set to 0 to enable flagging when rx ctrl ram logs ecc double
+ * bit error Part of Device Error 1.
+ * Set to 0 to enable flagging when rx data ram logs ecc single
+ * bit error Part of Device Error 0.
+ * Set to 0 to enable flagging when rx data ram logs ecc double
+ * bit error Part of Device Error 1.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rsrvd1:24;
+ uint64_t rx_ctrl_fifo_sec:2;
+ uint64_t rx_ctrl_fifo_ded:2;
+ uint64_t rx_data_fifo_sec:2;
+ uint64_t rx_data_fifo_ded:2;
+#else
+ uint64_t rx_data_fifo_ded:2;
+ uint64_t rx_data_fifo_sec:2;
+ uint64_t rx_ctrl_fifo_ded:2;
+ uint64_t rx_ctrl_fifo_sec:2;
+ uint64_t rsrvd1:24;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} rdc_fifo_err_mask_t;
+
+
+/*
+ * Register: RdcFifoErrStat
+ * FIFO Error Status
+ * Description: FIFO Error Status register. Log status of FIFO
+ * Errors. Rx Data buffer is physically two seperate memory, each of
+ * the two error bits point to one of the memory. Each entry in the
+ * rx ctrl point to 2 buffer locations and they are read seperatly.
+ * The two error bits point to each half of the entry.
+ * Fields:
+ * Set to 1 by HW to indicate rx control ram received a ecc
+ * single bit error Writing a 1 to either bit clears the
+ * RdcCtrlFifoEccLog register Non-Fatal error. Part of Device
+ * Error 0
+ * Set to 1 by HW to indicate rx control ram received a ecc
+ * double bit error Writing a 1 to either bit clears the
+ * RdcCtrlFifoEccLog register Fatal error. Part of Device Error 1
+ * Set to 1 by HW to indicate rx data ram received a ecc single
+ * bit error Writing a 1 to either bit clears the
+ * RdcDataFifoEccLog register Non-Fatal error. Part of Device
+ * Error 0
+ * Set to 1 by HW to indicate rx data ram received a ecc double
+ * bit error Writing a 1 to either bit clears the
+ * RdcDataFifoEccLog register Fatal error. Part of Device Error 1
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:56;
+ uint64_t rx_ctrl_fifo_sec:2;
+ uint64_t rx_ctrl_fifo_ded:2;
+ uint64_t rx_data_fifo_sec:2;
+ uint64_t rx_data_fifo_ded:2;
+#else
+ uint64_t rx_data_fifo_ded:2;
+ uint64_t rx_data_fifo_sec:2;
+ uint64_t rx_ctrl_fifo_ded:2;
+ uint64_t rx_ctrl_fifo_sec:2;
+ uint64_t rsrvd:56;
+#endif
+ } bits;
+} rdc_fifo_err_stat_t;
+
+
+/*
+ * Register: RdcFifoErrIntDbg
+ * FIFO Error Interrupt Debug
+ * Description: FIFO Error interrupt Debug register. Debug Control
+ * the interrupt assertion of FIFO Errors.
+ * Fields:
+ * Set to 1 to enable interrupt Part of Device Error 0.
+ * Set to 1 to enable interrupt Part of Device Error 1.
+ * Set to 1 to enable interrupt Part of Device Error 0.
+ * Set to 1 to enable interrupt Part of Device Error 1.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rsrvd1:24;
+ uint64_t rx_ctrl_fifo_sec:2;
+ uint64_t rx_ctrl_fifo_ded:2;
+ uint64_t rx_data_fifo_sec:2;
+ uint64_t rx_data_fifo_ded:2;
+#else
+ uint64_t rx_data_fifo_ded:2;
+ uint64_t rx_data_fifo_sec:2;
+ uint64_t rx_ctrl_fifo_ded:2;
+ uint64_t rx_ctrl_fifo_sec:2;
+ uint64_t rsrvd1:24;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} rdc_fifo_err_int_dbg_t;
+
+
+/*
+ * Register: RdcPeuTxnLog
+ * PEU Transaction Log
+ * Description: PEU Transaction Log register. Counts the memory read
+ * and write requests sent to peu block. For debug only.
+ * Fields:
+ * Counts the memory write transactions sent to peu block. This
+ * counter saturates. This counter increments when vnmDbg is on
+ * Counts the memory read transactions sent to peu block. This
+ * counter saturates. This counter increments when vnmDbg is on
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rsrvd1:16;
+ uint64_t peu_mem_wr_count:8;
+ uint64_t peu_mem_rd_count:8;
+#else
+ uint64_t peu_mem_rd_count:8;
+ uint64_t peu_mem_wr_count:8;
+ uint64_t rsrvd1:16;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} rdc_peu_txn_log_t;
+
+
+/*
+ * Register: RdcDbgTrainingVec
+ * Debug Training Vector
+ * Description: Debug Training Vector register Debug Training Vector
+ * for the coreClk domain. For the pcieClk domain, the dbgxMsb and
+ * dbgyMsb values are flipped on the debug bus.
+ * Fields:
+ * Blade Number, the value read depends on the blade this block
+ * resides
+ * debug training vector the sub-group select value of 0 selects
+ * this vector
+ * Blade Number, the value read depends on the blade this block
+ * resides
+ * debug training vector the sub-group select value of 0 selects
+ * this vector
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t dbgx_msb:1;
+ uint64_t dbgx_bld_num:3;
+ uint64_t dbgx_training_vec:12;
+ uint64_t dbgy_msb:1;
+ uint64_t dbgy_bld_num:3;
+ uint64_t dbgy_training_vec:12;
+#else
+ uint64_t dbgy_training_vec:12;
+ uint64_t dbgy_bld_num:3;
+ uint64_t dbgy_msb:1;
+ uint64_t dbgx_training_vec:12;
+ uint64_t dbgx_bld_num:3;
+ uint64_t dbgx_msb:1;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} rdc_dbg_training_vec_t;
+
+
+/*
+ * Register: RdcDbgGrpSel
+ * Debug Group Select
+ * Description: Debug Group Select register. Debug Group Select
+ * register selects the group of signals brought out on the debug
+ * port
+ * Fields:
+ * high 32b sub-group select
+ * low 32b sub-group select
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:48;
+ uint64_t dbg_h32_sub_sel:8;
+ uint64_t dbg_l32_sub_sel:8;
+#else
+ uint64_t dbg_l32_sub_sel:8;
+ uint64_t dbg_h32_sub_sel:8;
+ uint64_t rsrvd:48;
+#endif
+ } bits;
+} rdc_dbg_grp_sel_t;
+
+
+#endif /* _HXGE_RDC_HW_H */
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#include "hpi/hpi_rxdma.h"
+#include "hpi/hpi_vir.h"
+#include "hxge.h"
+
+extern wait_queue_head_t ethtool_evnt;
+extern volatile int ethtool_cond;
+extern struct sk_buff *ethtool_skb;
+extern int hxge_get_option(const char *str, int *val);
+extern int hxge_ok_to_continue(struct hxge_adapter *hxgep);
+extern int hxge_block_reset(struct hxge_adapter *hxgep, int device);
+extern void hxge_disable_interrupts(struct hxge_adapter *hxgep);
+static int strip_crc_bytes = 0;
+extern int hxge_vmac_rx_set_framesize(struct hxge_adapter *hxgep, uint16_t size);
+
+#ifndef CONFIG_ERRINJECT
+
+#define HXGE_KMALLOC kmalloc
+#define ALLOC_SKB dev_alloc_skb
+#define ALLOC_PAGES alloc_pages
+
+#else
+
+#define HXGE_KMALLOC hxge_kmalloc
+#define ALLOC_SKB hxge_alloc_skb
+#define ALLOC_PAGES hxge_alloc_pages
+
+static void *hxge_kmalloc(struct hxge_adapter *hxgep, size_t size, gfp_t gfp)
+{
+
+ if (hxgep->err_flags & KMEM_FAILURE)
+ return NULL;
+ else
+ return (kmalloc(size, gfp));
+}
+
+static struct sk_buff *hxge_alloc_skb(struct hxge_adapter *hxgep,
+ unsigned int size)
+{
+ if (hxgep->err_flags & SKB_FAILURE)
+ return NULL;
+ else
+ return (dev_alloc_skb(size));
+}
+
+static inline struct page *
+hxge_alloc_pages(struct hxge_adapter *hxgep, gfp_t gfp_mask, unsigned int order)
+{
+ if (hxgep->err_flags & ALLOC_PAGES_FAILURE)
+ return NULL;
+ else
+ return (alloc_pages(gfp_mask, order));
+}
+#endif
+
+static void parse_rdc_stat(const char *prefix, rdc_stat_t cs)
+{
+ char rdc_str[400];
+ char *str;
+
+ rdc_str[0] = '\0';
+ str = rdc_str;
+
+ if (cs.bits.mex)
+ str = strcat(str, "mex ");
+
+ if (cs.bits.rcr_thres)
+ str = strcat(str, "rcr_thres ");
+
+ if (cs.bits.rcr_to)
+ str = strcat(str , "rcr_to ");
+
+ if (cs.bits.rbr_cpl_to)
+ str = strcat(str , "rbr_cpl ");
+
+ if (cs.bits.peu_resp_err)
+ str = strcat(str , "peu_resp ");
+
+ if (cs.bits.rcr_shadow_par_err)
+ str = strcat(str , "rcr_shad ");
+
+ if (cs.bits.rbr_prefetch_par_err)
+ str = strcat(str , "rbr_pre_par ");
+
+ if (cs.bits.rbr_pre_empty)
+ str = strcat(str , "rbr_preempty ");
+
+ if (cs.bits.rcr_shadow_full)
+ str = strcat(str , "rcr_shad_full ");
+
+ if (cs.bits.rcr_full)
+ str = strcat(str , "rcr_full ");
+
+ if (cs.bits.rbr_empty)
+ str = strcat(str , "rbr_empty ");
+
+ if (cs.bits.rbr_full)
+ str = strcat(str , "rbr_full ");
+
+ HXGE_ERR_PRINT(" %s => %s",prefix,str);
+}
+
+
+/* Creating a hash entry based on the DMA address value that is assigned to
+ a particular RBR block. This is the way that the receive code is able to
+ find the RBR block from a given DMA address. Note that the DMA address is a
+ page-aligned address. So, the compare takes place at the page level only */
+static int add_to_hash_table(
+#ifdef CONFIG_ERRINJECT
+ struct hxge_adapter *hxgep,
+#endif
+ rx_rbr_ring_t *rbr_ring, int index)
+{
+ struct rx_hash_entry *hash_entry;
+ struct rbr_desc_addr_t desc_addr = rbr_ring->buf_blocks[index].addr;
+
+ int bucket = (desc_addr.dma_addr>>PAGE_SHIFT) & (HASH_TABLE_SIZE-1);
+
+ hash_entry = HXGE_KMALLOC(
+#ifdef CONFIG_ERRINJECT
+ hxgep,
+#endif
+ sizeof(struct rx_hash_entry), GFP_ATOMIC);
+ if (!hash_entry) {
+ HXGE_ERR_PRINT("Failed to get memory");
+ return RX_FAILURE;
+ }
+
+ hash_entry->dma_addr = desc_addr.dma_addr;
+ hash_entry->index = index;
+ if (rbr_ring->hash_table[bucket] == NULL)
+ hash_entry->next = NULL;
+ else
+ hash_entry->next = rbr_ring->hash_table[bucket];
+
+ rbr_ring->hash_table[bucket] = hash_entry;
+
+ return RX_NO_ERR;
+}
+
+static void free_hash_table(rx_rbr_ring_t *rbr_ring)
+{
+ struct rx_hash_entry *ptr, *next;
+ int i;
+
+ for (i = 0; i < HASH_TABLE_SIZE; i++) {
+ next = ptr = rbr_ring->hash_table[i];
+ while (next != NULL) {
+ next = ptr->next;
+ kfree(ptr);
+ ptr = next;
+ }
+ rbr_ring->hash_table[i] = NULL;
+ }
+}
+
+static int get_index_from_hash(rx_rbr_ring_t *rbr_ring, uint64_t pkt_addr)
+{
+ int bucket = (pkt_addr >> PAGE_SHIFT) & (HASH_TABLE_SIZE-1);
+ struct rx_hash_entry *ptr;
+
+ /* Look in the hash table for a match */
+ for (ptr = rbr_ring->hash_table[bucket]; ptr != NULL; ptr=ptr->next)
+ if (ptr->dma_addr == (pkt_addr & PAGE_MASK))
+ break;
+
+ /* did not find it in the hash table. So, add it */
+ if (!ptr) {
+ HXGE_ERR_PRINT("Did not find mapping. pkt_addr : 0x%llx",pkt_addr);
+ return RX_FAILURE;
+ }
+
+ return(ptr->index);
+}
+
+#ifdef CONFIG_SKB_SHARED
+static int remove_hash_entry(rx_rbr_ring_t *rbr_ring, unsigned long pkt_addr)
+{
+ int bucket = (pkt_addr >> PAGE_SHIFT) & (HASH_TABLE_SIZE-1);
+ struct rx_hash_entry *ptr, *prev_ptr = NULL;
+
+ /* Look in the hash table for a match */
+ for (ptr = rbr_ring->hash_table[bucket]; ptr != NULL;
+ prev_ptr=ptr, ptr=ptr->next)
+ if (ptr->dma_addr == (pkt_addr & PAGE_MASK))
+ break;
+
+ /* Did not find it in the hash table; something wrong.. */
+ if (!ptr) {
+ HXGE_ERR_PRINT("Did not find mapping. pkt_addr : %lu",pkt_addr);
+ return RX_FAILURE;
+ }
+
+ if (ptr == rbr_ring->hash_table[bucket])
+ rbr_ring->hash_table[bucket] = ptr->next;
+ else
+ prev_ptr->next = ptr->next;
+ kfree(ptr);
+ return RX_NO_ERR;
+}
+#endif
+
+static void free_dma_buffer(struct hxge_adapter *hxgep,
+ struct rx_rbr_entry_t *buf_block, boolean_t free_page)
+{
+ int order = (PAGE_SHIFT-12);
+ if (buf_block->addr.dma_addr)
+ pci_unmap_page(hxgep->pdev, buf_block->addr.dma_addr,
+ PAGE_SIZE, PCI_DMA_FROMDEVICE);
+ if ((free_page) && (buf_block->page))
+ __free_pages(buf_block->page, order);
+
+ buf_block->addr.dma_addr = 0;
+ buf_block->addr.vaddr = NULL;
+ buf_block->page = NULL;
+}
+
+
+static void hxge_free_rx_channel(struct hxge_adapter *hxgep, uint16_t rdc)
+{
+ int i;
+ struct rx_ring_t *rx_ring = &hxgep->rx_ring[rdc];
+ struct rx_rbr_entry_t *buf_blocks = rx_ring->rbr.buf_blocks;
+
+ /* Assumption: Entries filled from top down. The first null entry
+ vaddr indicates end of list */
+ if (buf_blocks) {
+ for (i = 0; buf_blocks[i].addr.vaddr &&
+ (i < rx_ring->rbr.num_rbr_entries); i++)
+ free_dma_buffer(hxgep, &buf_blocks[i], TRUE);
+ }
+ HXGE_DBG(hxgep, "Channel %d :",rdc);
+ HXGE_DBG(hxgep, " Freed buf blocks");
+
+ if (rx_ring->rbr.rbr_addr.vaddr)
+ pci_free_consistent(hxgep->pdev,
+ rx_ring->rbr.num_rbr_entries*sizeof(rbr_desc_entry_t),
+ rx_ring->rbr.rbr_addr.vaddr,
+ rx_ring->rbr.rbr_addr.dma_addr);
+ HXGE_DBG(hxgep, " Freed RBR Descriptor Ring");
+
+ if (rx_ring->rcr.rcr_addr.vaddr)
+ pci_free_consistent(hxgep->pdev,
+ rx_ring->rcr.num_rcr_entries*sizeof(rcr_entry_t),
+ rx_ring->rcr.rcr_addr.vaddr,
+ rx_ring->rcr.rcr_addr.dma_addr);
+ HXGE_DBG(hxgep, " Freed RCR Descriptor Ring");
+
+ if (rx_ring->mbox.vaddr)
+ pci_free_consistent(hxgep->pdev, sizeof(rxdma_mailbox_t),
+ rx_ring->mbox.vaddr,
+ rx_ring->mbox.dma_addr);
+ HXGE_DBG(hxgep, " Freed Mailbox");
+
+ if (rx_ring->rbr.buf_blocks)
+ free_pages((unsigned long)rx_ring->rbr.buf_blocks,
+ rx_ring->rbr.buf_blocks_order);
+ HXGE_DBG(hxgep, " Freed Buf block Pages");
+
+ free_hash_table(&rx_ring->rbr);
+ HXGE_DBG(hxgep, " Freed Hash Table");
+}
+
+void hxge_free_rx (struct hxge_adapter *hxgep)
+{
+ int i;
+
+ for (i = 0; i < hxgep->max_rdcs; i++)
+ hxge_free_rx_channel(hxgep, i);
+
+ if (hxgep->rx_ring)
+ kfree(hxgep->rx_ring);
+ hxgep->rx_ring = NULL;
+}
+
+/* Allocate buffers (if alloc is TRUE), initialize the buf_block meta data
+ * structure and update the hydra RBR kick registers. Also, update the
+ * free location in the RBR descriptor ring where the new free buffer will
+ * go
+ */
+static int setup_dma_buffers(struct hxge_adapter *hxgep, int channel,
+ struct rx_rbr_entry_t *buf_block, int entries,
+ boolean_t alloc, boolean_t init)
+{
+ struct page *page;
+ int order,i;
+ struct rx_rbr_entry_t *ptr;
+ rx_rbr_ring_t *rbr_ring = &hxgep->rx_ring[channel].rbr;
+ int rbr_loc = rbr_ring->rbr_free_loc;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ int retval = RX_NO_ERR;
+
+ order = (PAGE_SHIFT-12);
+ for (i = 0, ptr = buf_block; i < entries; i++, ptr++) {
+ if (likely(alloc)) {
+ BUG_ON(ptr->page);
+ ptr->page = page = ALLOC_PAGES(
+#ifdef CONFIG_ERRINJECT
+ hxgep,
+#endif
+ GFP_ATOMIC, order);
+ if (!page) {
+ HXGE_ERR(hxgep, "could not allocate buffer");
+ retval = RX_DROP_PKT;
+ goto failed;
+ }
+ ptr->addr.vaddr = (rbr_desc_entry_t *)page_address(page);
+ ptr->addr.dma_addr = pci_map_page(hxgep->pdev,
+ ptr->page, 0, PAGE_SIZE, PCI_DMA_FROMDEVICE);
+
+ if (!ptr->addr.dma_addr) {
+ HXGE_ERR(hxgep, "pci_map_page failed");
+ retval = RX_FAILURE;
+ goto failed;
+ }
+ retval = add_to_hash_table(
+#ifdef CONFIG_ERRINJECT
+ hxgep,
+#endif
+ rbr_ring, ptr->index);
+ if (retval < 0) {
+ HXGE_ERR(hxgep, "add_to_hash_table failed");
+ goto failed;
+ }
+ }
+ ptr->in_use = 0;
+ rbr_ring->rbr_addr.vaddr[rbr_loc] =
+ ptr->addr.dma_addr >> PAGE_SHIFT;
+ rbr_loc = (rbr_loc+1) % rbr_ring->num_rbr_entries;
+ }
+
+ /* Only done when the channel is initialized */
+ if (init == TRUE) {
+ rbr_ring->rbr_free_loc = 0;
+ rbr_ring->pages_to_post = 0;
+ hpi_rxdma_rdc_rbr_kick(handle, channel, entries);
+ HXGE_ERR(hxgep, "Initially kicked %d",entries);
+ return RX_NO_ERR;
+ }
+
+ rbr_ring->rbr_free_loc = rbr_loc;
+ rbr_ring->pages_to_post = entries;
+
+ if (rbr_ring->rbr_empty_flag)
+ rbr_ring->rbr_empty_threshold++;
+ else
+ hpi_rxdma_rdc_rbr_kick(handle, channel, rbr_ring->pages_to_post);
+
+ return RX_NO_ERR;
+
+failed:
+ for (; alloc && i && (ptr >= buf_block); i--, ptr--)
+ free_dma_buffer(hxgep, ptr, TRUE);
+
+ return retval;
+}
+
+
+static void print_regs(struct hxge_adapter *hxgep, int type, int channel)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ rdc_rx_cfg1_t cfg_1;
+ rdc_rx_cfg2_t cfg_2;
+ rdc_rbr_cfg_a_t cfga;
+ rdc_rbr_cfg_b_t cfgb;
+ rdc_rcr_cfg_a_t rcr_cfga;
+ rdc_rcr_cfg_b_t rcr_cfgb;
+
+ switch (type) {
+ case LDV_RXDMA:
+ HXGE_DBG(hxgep, "Rx REGISTERS :");
+ RXDMA_REG_READ64(handle, RDC_RX_CFG1, channel, &cfg_1.value);
+ HXGE_DBG(hxgep, " DMA Configuration 1 : 0x%llx",cfg_1.value);
+ RXDMA_REG_READ64(handle, RDC_RX_CFG2, channel, &cfg_2.value);
+ HXGE_DBG(hxgep, " DMA Configuration 2 : 0x%llx", cfg_2.value);
+ RXDMA_REG_READ64(handle, RDC_RBR_CFG_A, channel, &cfga.value);
+ HXGE_DBG(hxgep, " RBR Configuration A : 0x%llx",cfga.value);
+ RXDMA_REG_READ64(handle, RDC_RBR_CFG_B, channel, &cfgb.value);
+ HXGE_DBG(hxgep, " RBR Configuration B : 0x%llx", cfgb.value);
+ RXDMA_REG_READ64(handle, RDC_RCR_CFG_A, channel, &rcr_cfga.value);
+ HXGE_DBG(hxgep, " RCR Configuration A : 0x%llx", rcr_cfga.value);
+ RXDMA_REG_READ64(handle, RDC_RCR_CFG_B, channel, &rcr_cfgb.value);
+ HXGE_DBG(hxgep, " RCR Configuration B : 0x%llx", rcr_cfgb.value);
+ break;
+ default:
+ HXGE_ERR(hxgep, "Invalid type to print_regs : %d", type);
+ break;
+ }
+}
+
+
+static int hxge_map_rx_to_hw(struct hxge_adapter *hxgep, int rdc)
+{
+ rdc_desc_cfg_t cfg_desc;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ struct rx_ring_t *rx_ring = &hxgep->rx_ring[rdc];
+
+ /* Setup the mailbox location */
+ cfg_desc.mbox_enable = 1;
+ cfg_desc.mbox_addr = rx_ring->mbox.dma_addr;
+
+ rx_ring->full_hdr = FALSE; /* 2 bytes of header */
+ rx_ring->offset = 0; /* no SW offset */
+ cfg_desc.full_hdr = rx_ring->full_hdr;
+ cfg_desc.offset = rx_ring->offset;
+
+ cfg_desc.rbr_addr = rx_ring->rbr.rbr_addr.dma_addr;
+ cfg_desc.rbr_len = rx_ring->rbr.num_rbr_entries;
+ cfg_desc.page_size = hxgep->default_block_size;
+ cfg_desc.valid0 = 1;
+ cfg_desc.size0 = rx_ring->rbr.pkt_buf_size_bytes[0];
+ cfg_desc.valid1 = 1;
+ cfg_desc.size1 = rx_ring->rbr.pkt_buf_size_bytes[1];
+ cfg_desc.valid2 = 1;
+ cfg_desc.size2 = rx_ring->rbr.pkt_buf_size_bytes[2];
+
+ /* RCR stuff */
+ cfg_desc.rcr_addr = rx_ring->rcr.rcr_addr.dma_addr;
+ cfg_desc.rcr_len = rx_ring->rcr.num_rcr_entries;
+ cfg_desc.rcr_threshold = hxgep->rcr_threshold;
+
+ /* Set the registers */
+ if (hpi_rxdma_cfg_rdc_ring(handle, rx_ring->rdc, &cfg_desc) !=
+ HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "Configuration of Rx failed!");
+ return RX_FAILURE;
+ }
+
+ if (hpi_rxdma_cfg_clock_div_set(handle, HXGE_RCR_CLK_RESO) !=
+ HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_rxdma_cfg_clock_div_set failed!");
+ return RX_FAILURE;
+ }
+
+ /* Needed when processing RCE buffers */
+ RXDMA_REG_READ64(handle, RDC_PAGE_HANDLE, rdc, &rx_ring->page_hdl.value);
+ print_regs(hxgep, LDV_RXDMA, rdc);
+ return RX_NO_ERR;
+}
+
+/* Initialize the Rx channel. This is called at driver initialzation time when
+ channel is created and also anytime that the channel is reset (on error
+ situations, for example) */
+
+static int hxge_init_rx_channel(struct hxge_adapter *hxgep, int channel)
+{
+ struct rx_ring_t *rx_ring = &hxgep->rx_ring[channel];
+ struct rx_rbr_entry_t *buf_blocks = rx_ring->rbr.buf_blocks;
+ int i, alloc;
+
+ rx_ring->first_time = 1; /* CR 6769038 */
+ rx_ring->rbr.pkt_buf_size[0] = RBR_BUFSZ0_256B;
+ rx_ring->rbr.pkt_buf_size_bytes[0] = RBR_BUFSZ0_256_BYTES;
+ rx_ring->rbr.pkt_buf_size[1] = RBR_BUFSZ0_1K;
+ rx_ring->rbr.pkt_buf_size_bytes[1] = RBR_BUFSZ1_1K_BYTES;
+ rx_ring->rbr.pkt_buf_size[2] = RBR_BUFSZ2_2K;
+ rx_ring->rbr.pkt_buf_size_bytes[2] = RBR_BUFSZ2_2K_BYTES;
+ rx_ring->rbr.pkt_buf_size[3] = -1; /* special case; block size */
+ rx_ring->rbr.pkt_buf_size_bytes[3] = hxgep->default_block_size;
+
+ /* Initialize the RCR entries. Hardware can now use it */
+ memset(rx_ring->rcr.rcr_addr.vaddr, 0,
+ rx_ring->rcr.num_rcr_entries*sizeof(rcr_entry_t));
+ for (i = 0; i < rx_ring->rcr.num_rcr_entries; i++)
+ rx_ring->rcr.rcr_addr.vaddr[i] = (rcr_entry_t)RCR_INIT_PATTERN;
+ rx_ring->rcr.rcr_curr_loc = 0;
+
+ /* Initialize the mailbox as well */
+ memset(rx_ring->mbox.vaddr, 0, sizeof(rxdma_mailbox_t));
+
+ /* Initialize statistics */
+ memset(&rx_ring->stats, 0, sizeof(struct rx_ring_stats_t));
+
+ /* Program the hydra registers with the information about the Rx
+ channels and buffers */
+ if (hxge_map_rx_to_hw(hxgep, channel))
+ return RX_FAILURE;
+
+ /* hxge_init_rx_channel can be called from hxge_work_to_do during
+ * a channel reset, in which case we do not want to allocate
+ * new buffers (memory leak); just reuse the existing ones
+ */
+ alloc = test_bit(HXGE_DEVICE_OPENING, &hxgep->state) ? TRUE : FALSE;
+ if (setup_dma_buffers(hxgep, channel, buf_blocks,
+ rx_ring->rbr.num_rbr_entries, alloc, TRUE) < 0)
+ {
+ HXGE_ERR(hxgep, "setup_dma_buffers failed");
+ return RX_FAILURE;
+ }
+ set_bit(RING_INIT, &rx_ring->state);
+ return RX_NO_ERR;
+}
+
+static void hxge_scrub_rx_mem(struct hxge_adapter *hxgep)
+{
+ int i;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+
+ /*
+ * Scrub the RDC Rx DMA Prefetch Buffer Command.
+ */
+ for (i = 0; i < 128; i++) {
+ HXGE_REG_WR64(handle, RDC_PREF_CMD, i);
+ }
+
+ /*
+ * Scrub Rx DMA Shadow Tail Command.
+ */
+ for (i = 0; i < 64; i++) {
+ HXGE_REG_WR64(handle, RDC_SHADOW_CMD, i);
+ }
+
+ /*
+ * Scrub Rx DMA Control Fifo Command.
+ */
+ for (i = 0; i < 512; i++) {
+ HXGE_REG_WR64(handle, RDC_CTRL_FIFO_CMD, i);
+ }
+
+ /*
+ * Scrub Rx DMA Data Fifo Command.
+ */
+ for (i = 0; i < 1536; i++) {
+ HXGE_REG_WR64(handle, RDC_DATA_FIFO_CMD, i);
+ }
+}
+
+static int hxge_enable_rx_channel (struct hxge_adapter *hxgep, int rdc)
+{
+ struct rx_ring_t *rx_ring = &hxgep->rx_ring[rdc];
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ rdc_int_mask_t rdc_mask;
+ hpi_status_t status;
+
+
+ /* Reset the Rx DMA channel */
+ if (hpi_rxdma_cfg_rdc_reset(handle, rdc) != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_rxdma_cfg_rdc_reset failed");
+ }
+
+ /* Initialize Rx data structures and some of the HW registers */
+ hxge_init_rx_channel(hxgep, rdc);
+
+ /* Enable the mbox update */
+ if (hpi_rxdma_channel_mex_set(handle, rdc) != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_rxdma_channel_mex_set failed");
+ }
+
+ /* Enable the RCR timeout, if needed; otherwise explicitly disable */
+ hxgep->rcr_cfgb_cpy = 0;
+ if (hxgep->rcr_timeout > 0) {
+ status = hpi_rxdma_cfg_rdc_rcr_timeout(handle, rdc,
+ hxgep->rcr_timeout);
+ hxgep->rcr_cfgb_cpy = RCR_CFGB_ENABLE_TIMEOUT | hxgep->rcr_timeout;
+ }
+ else
+ status = hpi_rxdma_cfg_rdc_rcr_timeout_disable(handle, rdc);
+
+ if (status != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_rxdma_cfg_rdc_rcr_timeout failed");
+ }
+
+
+ /* Clear all the bits in the RDC Stat and Control */
+ if (hpi_rxdma_channel_cs_clear_all(handle, rdc) != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_rxdma_channel_cs_clear_all failed");
+ }
+
+
+ /* Clear all the event masks */
+ rdc_mask.value = 0;
+ hpi_rxdma_event_mask(handle, OP_SET, rdc, &rdc_mask);
+
+ /* Unmask the appropriate LDV for this Rx channel */
+ hxge_enable_rx_ints(hxgep, NULL, rdc);
+
+
+ /* Enable the Rx DMA channel */
+ if (hpi_rxdma_cfg_rdc_enable(handle, rdc) != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_rxdma_cfg_rdc_enable failed");
+ }
+
+ set_bit(RING_ENABLED, &rx_ring->state);
+ HXGE_ERR(hxgep, "Channel %d enabled", rdc);
+
+
+ return RX_NO_ERR;
+}
+
+static int hxge_disable_rx_channel(struct hxge_adapter *hxgep, int rdc)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ struct rx_ring_t *rx_ring = &hxgep->rx_ring[rdc];
+
+ if (!test_and_clear_bit(RING_ENABLED, &rx_ring->state))
+ return RX_NO_ERR;
+
+ /* Disable RCR timeout */
+ hxgep->rcr_cfgb_cpy = 0;
+ if (hpi_rxdma_cfg_rdc_rcr_timeout_disable(handle, rdc) != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_rxdma_cfg_rdc_rcr_timeout_disable failed");
+ }
+
+ /* Disable the Rx DMA channel */
+ if (hpi_rxdma_cfg_rdc_disable(handle, rdc) != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_rxdma_cfg_rdc_disable failed");
+ }
+ return RX_NO_ERR;
+}
+
+
+int valid_alignment(uint64_t addr, uint64_t size, int shift)
+{
+ uint64_t max_addr = (1 << shift);
+ uint64_t mask = max_addr - 1;
+
+ if (((addr & mask) & (0x3 << (shift-1))) != (((addr & mask) + (size-8)) & (0x3 << (shift-1))))
+ return 0;
+
+ return 1;
+}
+
+
+/* This routine is called once per Rx DMA channel. It allocates the requisite
+ data structures for the receive ring and sets up the hardware registers
+ to point to them. For example, the RBR, RCR and mailbox structures */
+
+static int hxge_alloc_rx_channel (struct hxge_adapter *hxgep, uint16_t rdc)
+{
+ struct rx_ring_t *rx_ring = &hxgep->rx_ring[rdc];
+ int rbr_entries, rcr_entries;
+ int i;
+ unsigned int size;
+ struct rx_rbr_entry_t *buf_blocks, *buf_block;
+
+ if (hxge_get_option("rbr_entries",&rbr_entries)) {
+ HXGE_ERR(hxgep, "rbr_entries invalid");
+ return RX_FAILURE;
+ }
+
+ if(hxge_get_option("rcr_entries", &rcr_entries)) {
+ HXGE_ERR(hxgep, "rcr_entries invalid");
+ return RX_FAILURE;
+ }
+
+ rx_ring->rdc = rdc;
+ rx_ring->rbr.num_rbr_entries = rbr_entries;
+ rx_ring->rcr.num_rcr_entries = rcr_entries;
+
+ /* Allocate metadata to keep track of buffer blocks */
+ size = (rbr_entries*sizeof(struct rx_rbr_entry_t));
+ rx_ring->rbr.buf_blocks_order = get_order(size);
+ buf_blocks = rx_ring->rbr.buf_blocks =
+ (struct rx_rbr_entry_t *)__get_free_pages(GFP_KERNEL,
+ rx_ring->rbr.buf_blocks_order);
+
+ if (!buf_blocks) {
+ HXGE_ERR(hxgep, "Failed to get buf blocks, %lu bytes", rbr_entries*sizeof(struct rx_rbr_entry_t));
+ return RX_FAILURE;
+ }
+ for (i = 0, buf_block=&buf_blocks[0]; i < rbr_entries;
+ i++, buf_block++) {
+ memset(buf_block, 0, sizeof(struct rx_rbr_entry_t));
+ buf_block->index = i;
+ }
+
+ rx_ring->rbr.pkt_buf_size[0] = RBR_BUFSZ0_256B;
+ rx_ring->rbr.pkt_buf_size_bytes[0] = RBR_BUFSZ0_256_BYTES;
+ rx_ring->rbr.pkt_buf_size[1] = RBR_BUFSZ0_1K;
+ rx_ring->rbr.pkt_buf_size_bytes[1] = RBR_BUFSZ1_1K_BYTES;
+ rx_ring->rbr.pkt_buf_size[2] = RBR_BUFSZ2_2K;
+ rx_ring->rbr.pkt_buf_size_bytes[2] = RBR_BUFSZ2_2K_BYTES;
+ rx_ring->rbr.pkt_buf_size[3] = -1; /* special case; block size */
+ rx_ring->rbr.pkt_buf_size_bytes[3] = hxgep->default_block_size;
+
+ /* The PRM mandates a formula to compute the RCRs based on the
+ * smallest possible buffer size. This guarantees the we will
+ * see a rbr empty and not an rcr full (see CR 6779304 why this is
+ * important).
+ */
+ do {
+ int compute_rcrs;
+ compute_rcrs = rbr_entries *
+ (hxgep->default_block_size/rx_ring->rbr.pkt_buf_size_bytes[0]);
+ if (compute_rcrs > rx_ring->rcr.num_rcr_entries) {
+ HXGE_ERR(hxgep, "%d rcr entries not sufficient for driver to function. You need at least %d rcr entries.",rcr_entries, compute_rcrs);
+ return RX_FAILURE;
+ }
+ else
+ rx_ring->rcr.num_rcr_entries = compute_rcrs;
+ } while (0);
+ HXGE_DBG(hxgep, "RBR = %d, RCR = %d",rbr_entries, rx_ring->rcr.num_rcr_entries);
+
+ /* Allocate the RBR descriptor ring. We allocate a power of two
+ * larger than we really need to assure that we meet the alignment
+ * restriction of the PRM. We do the same thing for the RCR further
+ * down
+ */
+ rx_ring->rbr.rbr_addr.vaddr = pci_alloc_consistent( hxgep->pdev,
+ rx_ring->rbr.num_rbr_entries*sizeof(rbr_desc_entry_t),
+ &rx_ring->rbr.rbr_addr.dma_addr);
+ if (!rx_ring->rbr.rbr_addr.vaddr) {
+ HXGE_ERR(hxgep, "Could not get DMA for RBR, channel %d",rdc);
+ }
+
+ /* Now validate the alignment */
+ if (!valid_alignment(rx_ring->rbr.rbr_addr.dma_addr,
+ rbr_entries*sizeof(rbr_desc_entry_t), 18)) {
+ HXGE_ERR(hxgep, "RBR Desc @ 0x%lx not aligned properly, channel %d",
+ (long unsigned int)rx_ring->rbr.rbr_addr.dma_addr, rdc);
+ return RX_FAILURE;
+ }
+
+ rx_ring->rcr.rcr_addr.vaddr = pci_alloc_consistent(hxgep->pdev,
+ rx_ring->rcr.num_rcr_entries*sizeof(rcr_entry_t),
+ &rx_ring->rcr.rcr_addr.dma_addr);
+ if (!rx_ring->rcr.rcr_addr.vaddr) {
+ HXGE_ERR(hxgep, "Could not get DMA for RCR, channel %d",rdc);
+ return RX_FAILURE;
+ }
+
+ if (!valid_alignment(rx_ring->rcr.rcr_addr.dma_addr,
+ rx_ring->rcr.num_rcr_entries*sizeof(rcr_entry_t), 19)) {
+ HXGE_ERR(hxgep, "RCR Desc @ 0x%lx aligned properly, channel %d",
+ (long unsigned int)rx_ring->rcr.rcr_addr.dma_addr, rdc);
+ return RX_FAILURE;
+ }
+ memset(rx_ring->rcr.rcr_addr.vaddr, 0,
+ rx_ring->rcr.num_rcr_entries*sizeof(rcr_entry_t));
+
+ HXGE_DBG(hxgep, "Allocated RBR at 0x%p (0x%llx) of size %d",rx_ring->rbr.rbr_addr.vaddr, rx_ring->rbr.rbr_addr.dma_addr, (int)(rbr_entries*sizeof(rbr_desc_entry_t)));
+ HXGE_DBG(hxgep, "Allocated RCR at 0x%p (0x%llx) of size %d",rx_ring->rcr.rcr_addr.vaddr, rx_ring->rcr.rcr_addr.dma_addr, (int)(rx_ring->rcr.num_rcr_entries*sizeof(rcr_entry_t)));
+
+ rx_ring->mbox.vaddr = pci_alloc_consistent(hxgep->pdev,
+ sizeof(rxdma_mailbox_t),
+ &rx_ring->mbox.dma_addr);
+ if (!rx_ring->mbox.vaddr) {
+ HXGE_ERR(hxgep, "Could not get DMA for mailbox, channel %d",rdc);
+ }
+
+ return RX_NO_ERR;
+}
+
+
+int hxge_alloc_rx(struct hxge_adapter *hxgep)
+{
+ int i;
+ int stripcrc = 0;
+
+ if (hxge_get_option("strip_crc", &stripcrc)) {
+ HXGE_ERR(hxgep, "Cannot get strip_crc value");
+ return RX_FAILURE;
+ }
+ if (stripcrc)
+ strip_crc_bytes = 0;
+ else
+ strip_crc_bytes = 4;
+
+ hxgep->rx_ring = kzalloc(sizeof(struct rx_ring_t)*hxgep->max_rdcs,
+ GFP_KERNEL);
+ if (!hxgep->rx_ring) {
+ HXGE_ERR(hxgep, "Could not alloc rx_ring");
+ return RX_FAILURE;
+ }
+
+ for (i = 0; i < hxgep->max_rdcs; i++) {
+ if (hxge_alloc_rx_channel(hxgep, i)) {
+ HXGE_ERR(hxgep, "Could not alloc rx for channel");
+ hxge_free_rx(hxgep);
+ return RX_FAILURE;
+ }
+ }
+
+ return RX_NO_ERR;
+}
+
+int hxge_enable_rx(struct hxge_adapter *hxgep)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ rdc_fifo_err_mask_t fifo_mask;
+ unsigned long long reg64 = 0xfeedfacedeadbeefULL;
+ int i;
+
+ /* Scrub/initialize RDC memory */
+ hxge_scrub_rx_mem(hxgep);
+
+ /* Reset the FIFO Error Status */
+ HXGE_REG_RD64(handle, RDC_FIFO_ERR_STAT, ®64);
+ if (reg64) {
+ /* While an interesting case (error flags should probably
+ * not be set), do not count against hxgep->hard_errors */
+ HXGE_ERR(hxgep, "RDC_FIFO_ERR_STAT 0x%16.16x hardware error flags set",
+ (unsigned int)reg64);
+ }
+ HXGE_REG_WR64(handle, RDC_FIFO_ERR_STAT, reg64); /* RW1C err bits */
+
+ /* Set the error mask to receive interrupts */
+ fifo_mask.value = 0;
+ if (hpi_rx_fifo_mask(handle, OP_SET, &fifo_mask) != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_rx_fifo_mask failed");
+ }
+
+ /* Enable all appropriate RX DMA channels */
+
+ for (i = 0; i < hxgep->max_rdcs; i++) {
+ if (hxge_enable_rx_channel(hxgep, i)) {
+ HXGE_ERR(hxgep, "Could not enable Rx chan %i",i);
+ return RX_FAILURE;
+ }
+ }
+
+ hxge_enable_rx_ints(hxgep, NULL, -1);
+
+ return RX_NO_ERR;
+}
+
+int hxge_disable_rx(struct hxge_adapter *hxgep)
+{
+ int i;
+
+ /* Disable Rx interrupts */
+ hxge_disable_rx_ints(hxgep,NULL, -1);
+
+ /* Disable all channels. Print warning message but don't exit until
+ attempting to disable all of them */
+ for (i = 0; i < hxgep->max_rdcs; i++) {
+ if (hxge_disable_rx_channel(hxgep, i)) {
+ HXGE_ERR(hxgep, "Could not disable Rx chan %i",i);
+ return RX_FAILURE;
+ }
+ }
+ return RX_NO_ERR;
+}
+
+
+
+/* Reset entire Rx ("RDC") subsystem */
+int hxge_reset_rdc(struct hxge_adapter *hxgep)
+{
+ /* Shutdown all receive traffic first */
+
+ hxge_disable_rx(hxgep);
+
+ /* Generate rdc_core logic reset */
+
+ hxge_block_reset(hxgep, LDV_RXDMA);
+
+ /* Bring up all the receive channels now */
+
+ hxge_enable_rx(hxgep);
+
+ return RX_NO_ERR;
+}
+
+
+/* This routines takes a DMA pkt_addr provided in an RCR descriptor entry and
+ finds out the buffer block (CPU addr) in the RBR that matches it */
+static struct rx_rbr_entry_t *get_buf_block(struct rx_ring_t *rx_ring,
+ rcr_entry_t *rcr_entry, uint32_t blk_size, uint64_t *offset, int first_rcr)
+{
+ rx_rbr_ring_t *rbr_ring = &rx_ring->rbr;
+ int index;
+ struct rx_rbr_entry_t *buf_block;
+ uint64_t pkt_addr;
+ int pktsz;
+
+ /* Get 64-bit packet address */
+ pkt_addr = rcr_entry->bits.pkt_buf_addr << RCR_PKT_BUF_ADDR_SHIFT_FULL;
+ pkt_addr |= ((rx_ring->page_hdl.value & 0xfffff)
+ << HXGE_MAX_ADDRESS_BITS);
+
+ *offset = pkt_addr & (blk_size - 1);
+ pktsz = ((first_rcr == 0) ? blk_size :
+ rbr_ring->pkt_buf_size_bytes[rcr_entry->bits.pktbufsz]);
+
+
+ /* Hit CR 6698258. The workaround is to ignore this entry and call it
+ * a success. The entry will be populated on the next interrupt */
+ if ((rcr_entry->value == 0x0) || (rcr_entry->value == RCR_INIT_PATTERN)
+ || ((index = get_index_from_hash(rbr_ring, pkt_addr)) < 0))
+ {
+ HXGE_ERR_PRINT("bad hash entry for pkt address 0x%llx",(unsigned long long)pkt_addr);
+ goto fail;
+ }
+
+ buf_block = &rbr_ring->buf_blocks[index];
+ buf_block->pkt_size = pktsz;
+ return buf_block;
+
+fail:
+ HXGE_ERR_PRINT("rcr_entry 0x%p: 0x%lx, pkt_addr: 0x%lx",rcr_entry, (unsigned long)rcr_entry->value,(unsigned long)rcr_entry->bits.pkt_buf_addr);
+
+ HXGE_ERR_PRINT(" multi=%d",rcr_entry->bits.multi);
+ HXGE_ERR_PRINT(" pkt_type=%d",rcr_entry->bits.pkt_type);
+ HXGE_ERR_PRINT(" error=%d",rcr_entry->bits.error);
+ HXGE_ERR_PRINT(" l2_len=%d",rcr_entry->bits.l2_len);
+ HXGE_ERR_PRINT(" pktbufsz=%d",rcr_entry->bits.pktbufsz);
+ HXGE_ERR_PRINT(" pkt_addr=0x%lux",(unsigned long)rcr_entry->bits.pkt_buf_addr);
+ return NULL;
+}
+
+static void update_buf_block(struct rx_rbr_entry_t *buf_block,
+ uint32_t blk_size)
+{
+ if (!buf_block->in_use) /* not in use; virgin block */ {
+ buf_block->in_use = 1;
+ buf_block->max_pkts = blk_size / buf_block->pkt_size;
+ } else /* already in use */
+ buf_block->in_use++;
+
+ /* Check if the buffer block is full. If so, mark it as "can be
+ freed" once the packet is copied out of it */
+ if (buf_block->in_use == buf_block->max_pkts)
+ buf_block->in_use = -1;
+#ifdef DBG
+ HXGE_DBG_PRINT("BUF_BLOCK [%d] => %p",index,buf_block);
+ HXGE_DBG_PRINT(" in_use = %d", buf_block->in_use);
+ HXGE_DBG_PRINT(" pkt_size = %d", buf_block->pkt_size);
+ HXGE_DBG_PRINT(" max_pkts = %d", buf_block->max_pkts);
+ HXGE_DBG_PRINT("PKT_ADDR : 0x%lx, offset=0x%lx",(unsigned long)pkt_addr,(unsigned long)*offset);
+#endif
+
+}
+
+#ifdef CONFIG_SKB_SHARED
+static void unmap_dma_buffer(struct hxge_adapter *hxgep, int channel,
+ struct rx_rbr_entry_t *buf_block)
+{
+ struct rx_ring_t *rx_ring = &hxgep->rx_ring[channel];
+ remove_hash_entry(&rx_ring->rbr, buf_block->addr.dma_addr);
+ free_dma_buffer(hxgep, buf_block, FALSE);
+}
+
+/* This routine is called for jumbo packets where a packet spans more than one
+ * buffer block. The routine sets up skb fragments to avoid having to do
+ * expensive copies of large buffers. */
+
+static int setup_skb_frag(struct sk_buff *skb, struct hxge_adapter *hxgep,
+ int channel, struct rx_rbr_entry_t *buf_block, uint32_t offset,
+ uint32_t len)
+{
+ int free = skb_shinfo(skb)->nr_frags;
+ skb_frag_t *fragp;
+
+ if (free >= MAX_SKB_FRAGS) {
+ HXGE_ERR(hxgep, "Too many skb fragments attempted!");
+ return RX_FAILURE;
+ }
+
+ /* setup the old page to a skb fragment */
+ fragp = &skb_shinfo(skb)->frags[free];
+ fragp->page = buf_block->page;
+ fragp->page_offset = offset;
+ fragp->size = len;
+
+ /* Unmap the DMA view from the old page (do not free it!), allocate
+ a new page and have the buf block meta data point to it */
+
+ unmap_dma_buffer(hxgep, channel, buf_block); /* unmap and remove hash */
+ if (setup_dma_buffers(hxgep, channel, buf_block, 1, TRUE, FALSE) < 0) {
+ HXGE_ERR(hxgep, "setup_dma_buffer failed");
+ buf_block->in_use = 0; /* can be re-used again */
+ return RX_DROP_PKT;
+ }
+
+ /* No errors. So, we can now account for this entry */
+ skb_shinfo(skb)->nr_frags++;
+ return RX_NO_ERR;
+
+}
+#endif
+
+
+static inline void process_pkt_hdr (pkt_hdr_twobyte_t *pkt_hdr)
+{
+ HXGE_DBG_PRINT("PKT_HDR =>");
+ HXGE_DBG_PRINT(" drop_code = %d",pkt_hdr->bits.drop_code);
+ HXGE_DBG_PRINT(" tcamhit = %d",pkt_hdr->bits.tcamhit);
+ HXGE_DBG_PRINT(" badip = %d",pkt_hdr->bits.badip);
+ HXGE_DBG_PRINT(" noport = %d",pkt_hdr->bits.noport);
+ HXGE_DBG_PRINT(" bcast_frame = %d",pkt_hdr->bits.bcast_frame);
+ HXGE_DBG_PRINT(" vlan = %d",pkt_hdr->bits.vlan);
+ HXGE_DBG_PRINT(" class= %d",pkt_hdr->bits.class);
+ HXGE_DBG_PRINT(" maccheck= %d",pkt_hdr->bits.maccheck);
+ HXGE_DBG_PRINT(" l4_cs_eq= %d",pkt_hdr->bits.l4_cs_eq);
+}
+
+/* This routine processes the first RCR entry for a packet. This could be the
+ * only packet (packet size <= block size) or could be the first entry in a
+ * multi entry RCR packet (jumbo frames). In either case, it sets up the skb
+ * buffer and initializes it appropriately based on packet size
+ */
+struct sk_buff *process_first_rcr_entry(struct hxge_adapter *hxgep,
+ struct rx_ring_t *rx_ring, int *bytes_remaining)
+{
+ rcr_entry_t *rcr_entry;
+ rx_rcr_ring_t *rcr_ring = &rx_ring->rcr;
+ struct rx_rbr_entry_t *buf_block;
+ struct sk_buff *skb;
+ uint32_t l2_len, hdr_len, len, data_size;
+ unsigned char *cpu_addr;
+ pkt_hdr_twobyte_t *pkt_hdr;
+ uint64_t offset;
+ uint32_t data_offset;
+
+ *bytes_remaining = RX_NO_ERR;
+ rcr_entry = GET_RCR_ENTRY(rcr_ring);
+
+ HXGE_DBG(hxgep, "rcr_ring loc : 0x%p", rcr_ring->rcr_addr.vaddr);
+ HXGE_DBG(hxgep, "rcr_curr_loc : %d",rcr_ring->rcr_curr_loc);
+ HXGE_DBG(hxgep, "rcr entry: 0x%lux",(unsigned long)rcr_ring->rcr_addr.vaddr[rcr_ring->rcr_curr_loc].value);
+ /* Get index into buf_blocks meta data so that we can get to the
+ CPU address from the DMA address, among other things */
+ if (!(buf_block = get_buf_block(rx_ring, rcr_entry, hxgep->default_block_size, &offset, 1))) {
+ HXGE_ERR(hxgep, "Bad pkt_addr");
+ HXGE_ERR(hxgep, "rcr_curr_loc : %d",rcr_ring->rcr_curr_loc);
+ return NULL;
+ }
+
+ update_buf_block(buf_block, hxgep->default_block_size);
+ cpu_addr = (char *)buf_block->addr.vaddr + offset;
+ pkt_hdr = (pkt_hdr_twobyte_t *)cpu_addr;
+
+#if 0
+ process_pkt_hdr(pkt_hdr);
+#endif
+
+ /* Get to the actual data part of the packet received */
+ data_offset = rx_ring->offset + (rx_ring->full_hdr ? 6 : 2);
+ data_size = buf_block->pkt_size - data_offset;
+ cpu_addr += data_offset;
+ hdr_len = ETH_HLEN;
+
+ len = l2_len = rcr_entry->bits.l2_len;
+ *bytes_remaining = l2_len;
+
+ rx_ring->stats.ipackets++;
+ rx_ring->stats.ibytes += l2_len;
+ if (l2_len > ETH_FRAME_LEN)
+ rx_ring->stats.jumbo_pkts++;
+
+ /* Create pkt_hdr structure and check for vlan */
+ if (pkt_hdr->bits.vlan)
+ hdr_len += 4;
+
+ /* TODO: There is the case where we could get small packets with
+ taking up an entire buffer block mostly wasted. This happens
+ if we the packet size options programmed into the config
+ registers are limited. For example, if only a packet size of
+ 256B is set, then anything >256B will take up an entire
+ 4KB block! Need to copy out the small packet in this case */
+
+#ifdef CONFIG_SKB_SHARED
+ if (rcr_entry->bits.multi)
+ len = hdr_len;
+#endif
+ skb = ALLOC_SKB(
+#ifdef CONFIG_ERRINJECT
+ hxgep,
+#endif
+ len + NET_IP_ALIGN);
+ if (!skb) {
+ HXGE_ERR(hxgep, "Could not allocate skb!");
+ *bytes_remaining = RX_DROP_PKT;
+ return NULL;
+ }
+
+ skb_reserve(skb, NET_IP_ALIGN);
+ skb->dev = hxgep->netdev;
+
+ /* Let Linux know the status of IP checksumming. If we have HW (IP)
+ * chksum available, then it's already done. Otherwise, we let Linux
+ * know that no checksum has been done.
+ */
+
+ if ((hxgep->flags & HXGE_RX_CHKSUM_ENABLED) &&
+ !pkt_hdr->bits.noport && !rcr_entry->bits.error &&
+ ((rcr_entry->bits.pkt_type == RCR_PKT_TCP) ||
+ (rcr_entry->bits.pkt_type == RCR_PKT_UDP)) &&
+ pkt_hdr->bits.l4_cs_eq)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else {
+ if (((rcr_entry->bits.pkt_type == RCR_PKT_TCP) ||
+ (rcr_entry->bits.pkt_type == RCR_PKT_UDP)) &&
+ (!pkt_hdr->bits.l4_cs_eq) && (!pkt_hdr->bits.noport))
+ HXGE_DBG(hxgep, "Checksum failed");
+ skb->ip_summed = CHECKSUM_NONE;
+ }
+
+#ifndef CONFIG_SKB_SHARED
+ if (rcr_entry->bits.multi)
+ len = data_size;
+#endif
+
+ memcpy(skb_put(skb, len), cpu_addr, len);
+#ifdef CONFIG_SKB_SHARED
+ if (rcr_entry->bits.multi) /* Copy packet data into fragment */ {
+ int retval;
+ skb->len += (l2_len - len);
+ skb->data_len = l2_len - len;
+ skb->truesize += (l2_len - len);
+ skb_shinfo(skb)->nr_frags = 0;
+ retval = setup_skb_frag(skb, hxgep, rx_ring->rdc, buf_block,
+ data_offset + len, data_size-len);
+ if (retval < 0)
+ *bytes_remaining = retval;
+ else
+ *bytes_remaining -= data_size;
+ } else
+#endif
+ { /* in single packet case, check if buf block can be freed */
+ if ((buf_block->in_use < 0) && (setup_dma_buffers(hxgep,
+ rx_ring->rdc, buf_block, 1, FALSE, FALSE) < 0))
+ *bytes_remaining = RX_FAILURE;
+ else
+ *bytes_remaining -= len;
+ }
+
+ /* Increment to next location in the RCR descriptor table */
+ INCREMENT_RCR_ENTRY_TO_NEXT(rcr_ring);
+ return skb;
+}
+
+
+/* This routine is called only for packets that span multiple buffer blocks
+ * and is not called for single packet cases. It processes the multiple RCR
+ * entries for large packets and sets up the skb fragments for each buffer
+ * block (parts of the packet data)
+ */
+static int process_rcr_entries(struct hxge_adapter *hxgep,
+ struct rx_ring_t *rx_ring, struct sk_buff *skb, int bytes_remaining)
+{
+ struct rx_rbr_entry_t *buf_block;
+ rx_rcr_ring_t *rcr_ring = &rx_ring->rcr;
+ rcr_entry_t *rcr_entry;
+ int len;
+ uint64_t offset;
+ int retval = RX_NO_ERR;
+
+ rcr_entry = GET_RCR_ENTRY(rcr_ring);
+
+ /* Get index into buf_blocks meta data so that we can get to the
+ CPU address from the DMA address, among other things */
+ if (!(buf_block = get_buf_block(rx_ring, rcr_entry,
+ hxgep->default_block_size, &offset, 0)))
+ {
+ HXGE_ERR(hxgep, "Bad pkt_addr");
+ HXGE_ERR(hxgep, "rcr_curr_loc : %d",rcr_ring->rcr_curr_loc);
+ return RX_FAILURE;
+ }
+
+ update_buf_block(buf_block, hxgep->default_block_size);
+ len = min(buf_block->pkt_size, (uint32_t)bytes_remaining);
+#ifdef CONFIG_SKB_SHARED
+ if ((retval = setup_skb_frag(skb, hxgep, rx_ring->rdc,
+ buf_block, 0, len)) < 0) {
+ HXGE_ERR(hxgep, "could not get skb fragment");
+ return retval;
+ }
+#else
+ memcpy(skb_put(skb, len), (char *)buf_block->addr.vaddr + offset, len);
+ if (buf_block->in_use < 0)
+ if ((retval = setup_dma_buffers(hxgep, rx_ring->rdc,
+ buf_block, 1,
+ FALSE, FALSE)) < 0) {
+ HXGE_ERR(hxgep,"setup_dma_buffers failed");
+ return retval;
+ }
+
+#endif
+
+ INCREMENT_RCR_ENTRY_TO_NEXT(rcr_ring);
+ return (bytes_remaining - len);
+}
+
+static void free_skb(struct sk_buff *skb)
+{
+ if (!skb)
+ return;
+
+ dev_kfree_skb_any(skb);
+}
+
+
+/* This routine is called by the packet processing routine in the event of
+ * a failure that is related (currently) to memory allocation failure. In that
+ * case, we opt to drop the packet rather than declare this a catastrophic
+ * failure i.e. requiring a reset of the channel, which will flush out valid
+ * packets that are awaiting processing. Instead, we just drop the current
+ * packet and move ahead with processing as usual.
+ *
+ * NOTE: The implicit assumption in this routine is that update_buf_block has
+ * already been called for the first rcr_entry that we are processing, always.
+ */
+
+int drop_packet(struct hxge_adapter *hxgep, struct rx_ring_t *rx_ring,
+ struct sk_buff *skb)
+{
+ rx_rcr_ring_t *rcr_ring = &rx_ring->rcr;
+ int rcr_processed = 0;
+
+ rx_ring->stats.nomem_drop++;
+ if (skb)
+ free_skb(skb);
+
+ /* Go through and reassign the buffer to the hardware */
+ do {
+ uint64_t ignore;
+ struct rx_rbr_entry_t *buf_block;
+ rcr_entry_t *rcr = GET_RCR_ENTRY(rcr_ring);
+
+ buf_block = get_buf_block(rx_ring, rcr,
+ hxgep->default_block_size, &ignore, 1);
+ if (!buf_block) {
+
+ /* we've already cleaned up the first multi entry
+ * in setup_skb_frag by clearing the in_use bit for
+ * re-use. So, just skip and go to the next one
+ */
+ if (!rcr_processed && rcr->bits.multi)
+ continue;
+
+ /* Something wrong; the buf_block should be in the
+ * hash list for non-multi case
+ */
+ HXGE_ERR(hxgep, "get_buf_block failed");
+ return RX_FAILURE;
+ }
+
+ /* Use rcr_processed as "first time through this loop"
+ * in_use count. Please see "NOTE" above
+ */
+ if (rcr_processed)
+ update_buf_block(buf_block, hxgep->default_block_size);
+
+ BUG_ON(buf_block->in_use < -1);
+ if (buf_block->in_use < 0)
+ setup_dma_buffers(hxgep, rx_ring->rdc, buf_block,
+ 1, FALSE, FALSE);
+ rcr_processed++;
+ INCREMENT_RCR_ENTRY_TO_NEXT(rcr_ring);
+ if (!rcr->bits.multi)
+ break;
+ } while (TRUE);
+
+
+ //HXGE_ERR(hxgep, "Dropped %d packets",rcr_processed);
+ return rcr_processed;
+}
+
+/* Process one packet. This could be either a single or a multiple RCR entry
+ case.
+ ASSUMPTION: The code assumes that a multi-buffer scenario implies that all
+ except the last is a full buffer block. While it does not need to be, it
+ makes coding simpler */
+
+static int hxge_process_one_pkt (struct hxge_adapter *hxgep,
+ struct rx_ring_t *rx_ring, struct sk_buff **skb_list)
+{
+ struct sk_buff *skb = NULL;
+ int bytes_remaining;
+ int rcr_entries_read = 0;
+
+ /* This routine processes the first entry of the RCR for this packet,
+ including allocating a skbuff and copying the header */
+ skb = process_first_rcr_entry(hxgep, rx_ring, &bytes_remaining);
+ if (!skb || (bytes_remaining < 0))
+ goto failed;
+
+ rcr_entries_read++;
+
+ /* This can only be true for multi-packet cases. For single packet,
+ everything processed in first rcr entry */
+ while (bytes_remaining > 0) {
+ bytes_remaining = process_rcr_entries(hxgep, rx_ring, skb,
+ bytes_remaining);
+ if (bytes_remaining < 0)
+ goto failed;
+ rcr_entries_read++;
+ }
+
+ /* Remove the ethernet crc from the packet before passing to the
+ network stack */
+ pskb_trim(skb, skb->len - strip_crc_bytes);
+
+ if (!test_bit(HXGE_DEVICE_TESTING, &hxgep->state))
+ skb->protocol = eth_type_trans(skb, hxgep->netdev);
+
+ /* If we are running diagnostic tests, then don't pass the skb to
+ the OS. Instead, the diag test routine will manage the skb's
+ itself */
+ if (unlikely(test_bit(HXGE_DEVICE_TESTING, &hxgep->state))) {
+ if (*skb_list == NULL) {
+ *skb_list = skb;
+ skb->next = NULL;
+ }
+ else {
+ skb->next = (*skb_list)->next;
+ (*skb_list)->next = skb;
+ }
+
+ } else {
+
+ /* Pass skb up the linux network stack for processing */
+#ifdef CONFIG_HXGE_NAPI
+ netif_receive_skb(skb);
+#else
+ netif_rx(skb);
+#endif
+ hxgep->netdev->last_rx = jiffies;
+ }
+
+ return rcr_entries_read;
+
+failed:
+ if (bytes_remaining == RX_DROP_PKT) {
+ int rcrs = drop_packet(hxgep, rx_ring, skb);
+ if (rcrs < 0)
+ return RX_FAILURE;
+ else
+ return (rcrs + rcr_entries_read);
+ }
+ HXGE_ERR(hxgep, "Bad pkt_addr on channel %d, bytes_remaining: %d",rx_ring->rdc, bytes_remaining);
+ return RX_FAILURE;
+}
+
+
+/* Workaroud for CR 6698258. Scan through the rcr entries that the HW
+ has fully populated. The assumption in the code is that there is
+ a complete set of packets in this list. If not, the processing
+ code will catch it manifested in various ways. The routine
+ converts RCR entries into number of packets, which is then used
+ as a limit on the packets to process.
+*/
+uint32_t scan_for_last_eop(struct rx_ring_t *rx_ring, int num_rcrs)
+{
+ rx_rcr_ring_t *rcr_ring = &rx_ring->rcr;
+ rcr_entry_t *rcr_entry = GET_RCR_ENTRY(rcr_ring);
+ uint32_t loc = rcr_ring->rcr_curr_loc;
+ int rcrs = 0;
+ uint32_t pkts = 0;
+
+ while ((rcrs < num_rcrs) && (rcr_entry->value != 0) &&
+ (rcr_entry->value != RCR_INIT_PATTERN))
+ {
+ if (!rcr_entry->bits.multi)
+ pkts++;
+ loc = (loc + 1) % rcr_ring->num_rcr_entries;
+ rcr_entry = &rcr_ring->rcr_addr.vaddr[loc];
+ rcrs++;
+ }
+ return pkts;
+}
+
+
+
+/* This routine processes the Rx channels. For each network device, all
+ the Rx channels are processed for packets even though each individual
+ channel has its own interrupt handler called (NAPI case only). This is
+ because the higher network layers are unaware of channels and only work
+ with a network device. For non-NAPI case, channel processing happens in
+ parallel */
+#ifdef CONFIG_HXGE_NAPI
+int hxge_process_packets(struct hxge_adapter *hxgep, int work_to_do,
+ struct hxge_ldv *ldvp, struct sk_buff **skb_list)
+#else
+int hxge_process_packets(struct hxge_adapter *hxgep, struct hxge_ldv *ldvp,
+ struct sk_buff **skb_list)
+#endif
+{
+ int channel = ldvp->ldv-HXGE_RDMA_LD_START;
+ struct rx_ring_t *rx_ring = &hxgep->rx_ring[channel];
+ uint32_t max_pkts, scan_pkts;
+ int pkts_processed = 0, rcr_entries_read=0, rcr_entries_processed=0;
+ rdc_stat_t *cs = (rdc_stat_t *)&ldvp->data;
+#ifdef USE_MBOX
+ rxdma_mailbox_t *mboxp;
+ uint32_t mbox_rcrtail;
+#endif
+ uint32_t num_entries;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+#ifdef USE_PIO
+ uint32_t rcr_tail, rcrtail_index;
+#endif
+
+ if (channel != rx_ring->rdc) { /* should match */
+ HXGE_ERR(hxgep, "channels do not match!");
+ return RX_FAILURE;
+ }
+
+ /* not enabled; don't return error since no harm done */
+ if (!test_bit(RING_ENABLED, &rx_ring->state)) {
+ HXGE_ERR(hxgep, "Channel %d not enabled",channel);
+ return RX_NO_ERR;
+ }
+
+
+ /* Determine the max number of packets that can be processed */
+ max_pkts = hxgep->max_rx_pkts;
+#ifdef CONFIG_HXGE_NAPI
+ max_pkts = min((uint32_t)work_to_do, max_pkts);
+#endif
+#ifdef USE_PIO
+ if (hpi_rxdma_rdc_rcr_tail_get(handle, channel, &rcr_tail)
+ != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_rxdma_rdc_rcr_tail_get failed");
+ }
+
+ rcrtail_index = rcr_tail - (uint32_t)(((uint64_t)rx_ring->rcr.rcr_addr.dma_addr & 0x7ffffULL) >> 3);
+
+ if (rcrtail_index >= rx_ring->rcr.rcr_curr_loc)
+ num_entries = rcrtail_index - rx_ring->rcr.rcr_curr_loc;
+ else
+ num_entries = rx_ring->rcr.num_rcr_entries -
+ (rx_ring->rcr.rcr_curr_loc - rcrtail_index);
+#endif
+
+#ifdef USE_MBOX
+
+ mboxp = (rxdma_mailbox_t *)rx_ring->mbox.vaddr;
+ mbox_rcrtail = mboxp->rcr_tail.bits.tail;
+ mbox_rcrtail = mbox_rcrtail - (uint32_t)(((uint64_t)rx_ring->rcr.rcr_addr.dma_addr & 0x7ffffULL) >> 3);
+ if (mbox_rcrtail >= rx_ring->rcr.rcr_curr_loc)
+ num_entries = mbox_rcrtail - rx_ring->rcr.rcr_curr_loc;
+ else
+ num_entries = rx_ring->rcr.num_rcr_entries -
+ (rx_ring->rcr.rcr_curr_loc - mbox_rcrtail);
+
+ if (rx_ring->rbr.rbr_empty_flag) {
+ uint16_t qlen;
+ if (hpi_rxdma_rdc_rcr_qlen_get(handle, channel, &qlen)
+ != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "qlen read failed for channel %i",channel);
+ return RX_FAILURE;
+ }
+ HXGE_DBG(hxgep, "channel %d , qlen = %d",channel,qlen);
+ num_entries = qlen;
+ }
+#endif
+
+ max_pkts = min(max_pkts, num_entries);
+ scan_pkts = scan_for_last_eop(rx_ring, num_entries);
+ max_pkts = min(max_pkts, scan_pkts);
+
+ if (!max_pkts)
+ return RX_NO_ERR;
+
+ do {
+ rcr_entries_read = hxge_process_one_pkt(hxgep, rx_ring, skb_list);
+ HXGE_DBG(hxgep, "%d rcr entries read",rcr_entries_read);
+ if (rcr_entries_read < 0)
+ break;
+
+ rcr_entries_processed += rcr_entries_read;
+ HXGE_DBG(hxgep, "%d rcr entries processed",rcr_entries_processed);
+ } while ((rcr_entries_read > 0) && (++pkts_processed < max_pkts));
+
+ HXGE_DBG(hxgep, "%d pkts processed",pkts_processed);
+
+ if (rcr_entries_read <= 0) {
+ HXGE_ERR(hxgep, "Channel %d => ",channel);
+#ifdef USE_MBOX
+ HXGE_ERR(hxgep, " MBOX Info");
+ HXGE_ERR(hxgep, " RCR Tail(index) = %d",mbox_rcrtail);
+ HXGE_ERR(hxgep, " RCR Qlen = %d",mboxp->rcr_qlen.bits.qlen);
+ parse_rdc_stat("MBOX RDC ", mboxp->rxdma_ctl_stat);
+#endif
+ parse_rdc_stat("PIO RDC ", (rdc_stat_t)ldvp->data);
+ HXGE_ERR(hxgep, " SW RCR Head = %d",rx_ring->rcr.rcr_curr_loc);
+ HXGE_ERR(hxgep, " Num RCR Entries = %d",num_entries);
+ HXGE_ERR(hxgep, " Packets found scan_eop = %d",scan_pkts);
+ HXGE_ERR(hxgep, " RCR Entries Processed = %d",rcr_entries_processed);
+ HXGE_ERR(hxgep, " Num Packets Processed = %d",pkts_processed);
+ HXGE_ERR(hxgep, " RCR Entries Read (curr pkt) = %d",rcr_entries_read);
+ return RX_FAILURE;
+ }
+
+ if (hxgep->adaptive_rx)
+ RXDMA_REG_WRITE64(handle, RDC_RCR_CFG_B, channel, max_pkts << 16 | hxgep->rcr_cfgb_cpy);
+
+ /* CR 6769038 Workaround */
+
+ if (rx_ring->first_time && pkts_processed) {
+ pkts_processed--;
+ rx_ring->first_time--;
+ }
+
+
+
+ /* Update the RDC Status/Control with packets and rcr entries read */
+ cs->bits.ptrread = rcr_entries_processed;
+ cs->bits.pktread = pkts_processed;
+ return pkts_processed;
+}
+
+void hxge_reset_rx_channel(struct hxge_adapter *hxgep, int rdc)
+{
+ struct rx_ring_t *rx_ring = &hxgep->rx_ring[rdc];
+
+ HXGE_ERR(hxgep, "Entering routine");
+ if (test_and_set_bit(RING_RESET, &rx_ring->state))
+ return;
+
+ spin_lock(&hxgep->lock);
+ hxge_disable_rx_channel(hxgep, rdc);
+ hxge_enable_rx_channel(hxgep, rdc);
+ spin_unlock(&hxgep->lock);
+
+ clear_bit(RING_RESET, &rx_ring->state);
+ HXGE_ERR(hxgep, "Exiting routine");
+}
+
+/* Called to update the cumulative Rx stats structure. This is later used
+ to fill in the net_device_stats structure for higher level client such as
+ ifconfig */
+static void update_rx_err_stats(hpi_handle_t handle, struct rx_ring_t *rx_ring)
+{
+ struct rx_ring_stats_t *stats = &rx_ring->stats;
+ rdc_drop_count_t drop_cnt;
+
+ RXDMA_REG_READ64(handle, RDC_DROP_COUNT, rx_ring->rdc, &drop_cnt.value);
+
+ stats->pkt_too_long += drop_cnt.bits.too_long;
+ stats->no_rbr_avail += drop_cnt.bits.no_rbr_avail;
+ stats->rvm_errors += drop_cnt.bits.rvm_error;
+ stats->ram_errors += drop_cnt.bits.rxram_error;
+ stats->frame_errors += drop_cnt.bits.frame_error;
+
+ stats->ierrors += drop_cnt.bits.too_long
+ + drop_cnt.bits.no_rbr_avail
+ + drop_cnt.bits.rvm_error
+ + drop_cnt.bits.rxram_error
+ + drop_cnt.bits.frame_error;
+
+}
+
+
+/* Error handler, specifically for fatal errors. Report the error to the
+ system log. */
+static rdc_stat_t process_rx(struct hxge_ldv *ldvp, int *got_ldf1)
+{
+ struct hxge_adapter *hxgep = ldvp->ldgp->hxgep;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ rdc_stat_t cs;
+ int channel = ldvp->ldv-HXGE_RDMA_LD_START;
+ struct rx_ring_t *rx_ring = &hxgep->rx_ring[channel];
+ struct rx_ring_stats_t *stats = &rx_ring->stats;
+ rdc_rbr_qlen_t rbr_qlen;
+
+ hpi_rxdma_control_status(handle, OP_GET, channel, &cs);
+ cs.bits.ptrread = 0;
+ cs.bits.pktread = 0;
+ hpi_rxdma_control_status(handle, OP_SET, channel, &cs);
+ ldvp->data = cs.value; /* Used to store npkts and nptrs later */
+
+ if (cs.bits.rcr_to)
+ stats->rcr_to++;
+
+ if (cs.bits.rcr_thres)
+ stats->rcr_thres++;
+
+ if (cs.value & RDC_LDF1) {
+ *got_ldf1 = 1;
+ if (cs.bits.rbr_cpl_to) {
+ HXGE_ERR(hxgep, "Fatal Error: Response completion timeout from PEU");
+ stats->rbr_cpl_tmout++;
+ stats->ierrors++;
+ }
+
+ if (cs.bits.peu_resp_err) {
+ HXGE_ERR(hxgep, "Fatal Error: Poisoned completion from PEU");
+ stats->peu_resp_err++;
+ stats->ierrors++;
+ }
+
+ if (cs.bits.rcr_shadow_par_err) {
+ HXGE_ERR(hxgep, "Fatal Error: RCR shadow ram parity error");
+ stats->rcr_shadow_parity++;
+ stats->ierrors++;
+ }
+
+ if (cs.bits.rbr_prefetch_par_err) {
+ HXGE_ERR(hxgep, "Fatal Error: RBR prefetch parity error");
+ stats->rcr_prefetch_parity++;
+ stats->ierrors++;
+ }
+
+ if (cs.bits.rbr_pre_empty) {
+ HXGE_ERR(hxgep, "Fatal Error: Not enough RBR buffers to prefetch!");
+ stats->rbr_prefetch_empty++;
+ stats->ierrors++;
+ }
+
+ if (cs.bits.rcr_shadow_full) {
+ HXGE_ERR(hxgep, "Fatal Error: RCR Shadow Full");
+ stats->rcr_shadow_full++;
+ stats->ierrors++;
+ }
+
+ if (cs.bits.rcr_full) {
+ HXGE_ERR(hxgep, "Fatal Error: No space in RCR descriptor ring");
+ stats->rcr_full++;
+ stats->ierrors++;
+ }
+
+ /* Re-enable the DMA channel. But, before returing from the
+ interrupt, process as many packets from the RCR as possible
+ to minimize the number of these interrupts we will receive
+ */
+ if (cs.bits.rbr_empty) {
+ rdc_rx_cfg1_t cfg;
+ hpi_rxdma_rdc_rbr_qlen_get(handle, channel, &rbr_qlen);
+ HXGE_DBG(hxgep, "Fatal Error: No more RBR buffers available for use, chan = %d, qlen = %d",channel, rbr_qlen.bits.qlen);
+ stats->rbr_empty++;
+ stats->ierrors++;
+ if (hpi_rxdma_cfg_rdc_wait_for_qst(handle, channel, &cfg,1)
+ != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "qst bit did not quiet down");
+ }
+ else
+ rx_ring->rbr.rbr_empty_flag++;
+ }
+
+ if (cs.bits.rbr_full) {
+ HXGE_ERR(hxgep, "Fatal Error: No space for more RBR buffers in hardware!");
+ stats->rbr_full++;
+ stats->ierrors++;
+ }
+
+ /* Update dropped-packets counts from Hydra's counters */
+ update_rx_err_stats(handle, rx_ring);
+ }
+
+ return cs;
+}
+
+#ifdef CONFIG_HXGE_NAPI
+/**
+ * hxge_poll - NAPI Rx polling callback. This is called once for each
+ * device. So, this routine will have to go through and process the Rx
+ * packets from all the channels for the device. So, Rx interrupts for all
+ * channels must be disabled at this point.
+ *
+ * work_to_do - Represents total work on this call of hxge_poll
+ * work_per_channel - divide the total work allowed equally between channels
+ * to avoid starvation
+ * work_in_last_iteration - used as an idicator that no more packets available
+ * across all channels. So, we can stop
+ * @adapter: board private structure
+ **/
+int
+hxge_poll(struct net_device *netdev, int *budget)
+{
+ struct hxge_adapter *hxgep = netdev->priv;
+ int work_to_do = min(*budget, netdev->quota);
+ int work_done = 0;
+ int work_per_channel;
+ struct hxge_ldgv *ldgvp = hxgep->ldgvp;
+ struct hxge_ldv *ldvp;
+ int got_ldf0, got_ldf1, pkt_cnt = 0, work_in_last_iteration;
+
+
+ /* Go through each Rx channel and process packets received. If there
+ is an error, then reset the channel and continue with other channels
+ and then exit after all channels are processed. To avoid starving
+ a specific channel, we will equally budget the work_to_do across all
+ channels. If we have any left over, we will go through a second
+ round till we hit the budget limit */
+
+ work_per_channel = work_to_do / hxgep->max_rdcs;
+ do {
+ rdc_stat_t cs;
+
+ work_in_last_iteration = 0;
+ list_for_each_entry (ldvp, &ldgvp->ldvp, list) {
+ if (ldvp->dev_type != LDV_RXDMA)
+ continue;
+ cs = process_rx(ldvp, &got_ldf1);
+ pkt_cnt = hxge_process_packets(hxgep,
+ work_per_channel,
+ ldvp->ldv-HXGE_RDMA_LD_START,NULL);
+ if (pkt_cnt < 0) {
+ HXGE_ERR("hxge_process_packets failed");
+ got_ldf1 = 1;
+ } else /* keep count of packets processed */ {
+ HXGE_DBG("Channel %d = Processed %d pkts",ldvp->ldv-HXGE_RDMA_LD_START,pkt_cnt);
+ work_done += pkt_cnt;
+ work_in_last_iteration += pkt_cnt;
+ work_to_do -= pkt_cnt;
+ }
+ if (got_ldf1) {
+ set_bit(RESET_RX_CHANNEL_0+ldvp->ldv-HXGE_RDMA_LD_START, &hxgep->work_q.command);
+ schedule_work(&hxgep->work_to_do);
+ }
+
+ }
+ } while ((work_in_last_iteration > 0) && (work_to_do > 0));
+
+
+ HXGE_DBG(hxgep, "Processed %d packets for all channels",work_done);
+ if ((work_to_do <= 0) || (work_in_last_iteration <= 0)) {
+ netif_rx_complete(netdev);
+ hxge_enable_rx_ints(hxgep, NULL, -1);
+ return 0;
+ }
+
+ /* still more Rx processing to do */
+ *budget -= work_done;
+ netdev->quota -= work_done;
+ return 1;
+}
+#endif
+
+/* Rx Interrupt handling. It can be called for each channel instance if they
+ assigned to separate LDGs. However, given the nature of NAPI handling
+ (a device can only be queued once to a given CPU; multiple instances of
+ the same device cannot be queued, even to different CPUs), only one
+ CPU at any given time handles a given device Rx channels. However, for
+ the non-NAPI case, multiple CPUs can be processing different channels
+ Rx buffers at the same time. However, careful locking will be required
+ when the skb is created and queued from different CPUs for the same
+ device */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+irqreturn_t hxge_rx_intr(int irq, void *data, struct pt_regs *regs)
+#else
+irqreturn_t hxge_rx_intr(int irq, void *data)
+#endif
+{
+ struct hxge_ldv *ldvp = (struct hxge_ldv *)data;
+ struct hxge_ldg *ldgp = ldvp->ldgp;
+ struct hxge_adapter *hxgep = ldgp->hxgep;
+#ifdef CONFIG_HXGE_NAPI
+ struct net_device *netdev = hxgep->netdev;
+#endif
+ int got_ldf0, got_ldf1;
+ int pkts_rcvd;
+ int channel = ldvp->ldv-HXGE_RDMA_LD_START;
+ rdc_stat_t cs;
+
+ get_ldf_flags(ldvp, &got_ldf0, &got_ldf1);
+ /* Check if this is our interrupt. If not, probably shared. So just
+ return and let other shared handlers check and take care of it */
+ if (!got_ldf0 && !got_ldf1)
+ return IRQ_NONE;
+
+#ifdef CONFIG_HXGE_NAPI
+ /* Go ahead and queue device for processing later. However, ensure
+ that all Rx channel device interrupts are masked since the packets
+ that have arrived at all channels are processed at the same time
+ by one CPU */
+ if (likely(netif_rx_schedule_prep(netdev)))
+ {
+ HXGE_DBG(hxgep, "Disable Rx Ints and schedule poll");
+ hxge_disable_rx_ints(hxgep, NULL, -1);
+ __netif_rx_schedule(netdev);
+ }
+#else
+ /* Process errors first. If fatal, then we reset the channel
+ instead of attempting to process packets */
+ cs = process_rx(ldvp, &got_ldf1);
+
+ /* If not fatal error (taken care of by the error routine),
+ then go ahead and see if there are packets to be received.
+ Note that Rx processing can be done by different CPUs for
+ different channels. hxge_process_packets must take care of
+ synchronization in required areas */
+
+ if (unlikely(test_bit(HXGE_DEVICE_TESTING, &hxgep->state))) {
+ pkts_rcvd = hxge_process_packets(hxgep, ldvp, ðtool_skb);
+ if (pkts_rcvd > 0) {
+ HXGE_DBG(hxgep, "%d pkts rcvd",pkts_rcvd);
+ ethtool_cond -= pkts_rcvd;
+ }
+ if (!ethtool_cond || (pkts_rcvd < 0))
+ wake_up_interruptible(ðtool_evnt);
+ } else
+ if (hxge_process_packets(hxgep, ldvp, NULL) < 0) {
+ HXGE_ERR(hxgep, "hxge_process_packets failed");
+ hxge_disable_rx_ints(hxgep, ldgp, channel);
+ got_ldf1 = 1; /* fall through */
+ }
+
+ if (got_ldf1) {
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ struct rx_ring_t *rx_ring = &hxgep->rx_ring[channel];
+
+ if (cs.bits.rbr_empty && rx_ring->rbr.rbr_empty_flag) {
+ int i;
+ HXGE_DBG(hxgep, "Posting %d buffers",rx_ring->rbr.rbr_empty_threshold);
+ hpi_rxdma_rdc_rbr_kick(handle, channel,
+ rx_ring->rbr.rbr_empty_threshold);
+ rx_ring->stats.rbr_empty_posted += rx_ring->rbr.rbr_empty_threshold;
+ rx_ring->rbr.rbr_empty_flag--;
+ rx_ring->rbr.rbr_empty_threshold = 0;
+ hxge_vmac_rx_set_framesize(hxgep, 1);
+ HXGE_DBG(hxgep, "Disabled VMAC");
+ if (hpi_rxdma_cfg_rdc_enable(handle, channel)
+ != HPI_SUCCESS) {
+ hxge_vmac_rx_set_framesize(hxgep, hxgep->vmac.rx_max_framesize);
+ HXGE_ERR(hxgep, "hpi_rxdma_cfg_rdc_enable failed");
+ set_bit(RESET_RX_CHANNEL_0+channel,
+ &hxgep->work_q.command);
+ schedule_work(&hxgep->work_to_do);
+ goto failed;
+ } else {
+ HXGE_DBG(hxgep, "Re-enabled RDC Channel %d",channel);
+ }
+
+ HXGE_DBG(hxgep, "Enabled the RDC Channel");
+ /* a delay loop; forcing PIO reads to cause any
+ * preceding PIO writes to complete??
+ */
+ for (i = 0; i < 5; i++)
+ udelay(100);
+
+ HXGE_DBG(hxgep, "Completed wait cycle");
+
+ hxge_vmac_rx_set_framesize(hxgep, hxgep->vmac.rx_max_framesize);
+ rx_ring->stats.rbr_empty_handled++;
+ HXGE_DBG(hxgep, "Enabled VMAC");
+ } else {
+ HXGE_ERR(hxgep, "Resetting on LDF1");
+ /* Reset the channel. We do this for all fatal (LDF1)
+ * errors. We assume that the reset will clear out the
+ * error bits as well */
+ set_bit(RESET_RX_CHANNEL_0+channel,
+ &hxgep->work_q.command);
+ schedule_work(&hxgep->work_to_do);
+ goto failed;
+ }
+ }
+ hxge_enable_rx_ints(hxgep, ldgp, channel);
+failed:
+#endif
+ return (IRQ_HANDLED);
+}
+
+
+
+/* Receive error interrupt handler
+ *
+ * Called from Device Error Interrupt (ldv 31) service, not RX DMA
+ *
+ * NB: *data is Device Error ldv 31, not an RX DMA channel ldv!
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+irqreturn_t hxge_rx_deverr_intr(int irq, void *data, struct pt_regs *regs)
+#else
+irqreturn_t hxge_rx_deverr_intr(int irq, void *data)
+#endif
+{
+ struct hxge_ldv *ldvp = (struct hxge_ldv *)data; /* Device Error ldv */
+ struct hxge_adapter *hxgep = ldvp->ldgp->hxgep;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ rdc_fifo_err_stat_t sts, clr;
+ int hard_error = 0;
+
+ if (hpi_rx_fifo_status(handle, OP_GET, &sts)
+ != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_rx_fifo_status failed");
+ }
+
+ HXGE_ERR(hxgep, "RX hardware error interrupt (0x%16.16x)!",
+ (unsigned int)sts.value);
+
+ clr.value = sts.value;
+
+ if (sts.bits.rx_ctrl_fifo_sec) {
+ HXGE_ERR(hxgep, "rx_ctrl_fifo_sec");
+ hxgep->statsp->rx_ctrl_sec++;
+ hxgep->statsp->soft_errors++; /* Soft (recovered) dev error */
+ clr.bits.rx_ctrl_fifo_sec = 0;
+ }
+
+ if (sts.bits.rx_ctrl_fifo_ded) {
+ HXGE_ERR(hxgep, "rx_ctrl_fifo_ded");
+ hxgep->statsp->rx_ctrl_ded++;
+ hxgep->statsp->rx_ierrors++; /* Rx summary count */
+ hxgep->statsp->hard_errors++; /* Hard device error */
+ hard_error = TRUE;
+ clr.bits.rx_ctrl_fifo_ded = 0;
+ }
+
+ if (sts.bits.rx_data_fifo_sec) {
+ HXGE_ERR(hxgep, "rx_data_fifo_sec");
+ hxgep->statsp->rx_data_sec++;
+ hxgep->statsp->soft_errors++; /* Soft (recovered) dev error */
+ clr.bits.rx_data_fifo_sec = 0;
+ }
+
+ if (sts.bits.rx_data_fifo_ded) {
+ HXGE_ERR(hxgep, "rx_data_fifo_ded");
+ hxgep->statsp->rx_data_ded++;
+ hxgep->statsp->rx_ierrors++; /* Rx summary count */
+ hxgep->statsp->hard_errors++; /* Hard device error */
+ hard_error = TRUE;
+ clr.bits.rx_data_fifo_ded = 0;
+ }
+
+ if (clr.value) {
+ HXGE_ERR(hxgep, "Unknown/unexpected/reserved RDC_FIFO_ERR_STAT bits 0x%16.16x", (unsigned int)clr.value);
+ hxgep->statsp->hard_errors++; /* Unknown hard device error */
+ hard_error = TRUE; /* Whatever, it's bad; hammer something */
+ }
+
+ /* Now that we have "logged" the errors, try to recover from
+ * whatever happened. Note that "SEC" (Single Bit ECC) is
+ * recovered by hardware, and needs no further action here.
+ */
+
+ /* Acknowledge (and clear) error status bits */
+
+ hpi_rx_fifo_status(handle, OP_SET, &sts);
+
+ /* Resume processing unless too many errors (hard or soft) */
+
+ if (hxge_ok_to_continue(hxgep)) {
+ if (hard_error) {
+ /* Double-bit error, integrity lost, reset Rx */
+ hxge_disable_rx_ints(hxgep, NULL, -1);
+ set_bit(RESET_RDC, &hxgep->work_q.command);
+ schedule_work(&hxgep->work_to_do);
+ } /* Else single-bit error, just dismiss and continue */
+ } else {
+ HXGE_ERR(hxgep, "Excessive hardware error rate");
+ HXGE_ERR(hxgep, " Taking hxge device down");
+ hxge_disable_interrupts(hxgep);
+ set_bit(SHUTDOWN_ADAPTER, &hxgep->work_q.command);
+ schedule_work(&hxgep->work_to_do);
+ }
+
+ return (IRQ_HANDLED);
+}
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#ifndef _HXGE_HXGE_RXDMA_H
+#define _HXGE_HXGE_RXDMA_H
+
+#include "hxge_rdc_hw.h"
+
+#define RXDMA_CK_DIV_DEFAULT 7500 /* 25 usec */
+#define RXDMA_RCR_PTHRES_DEFAULT 0x20
+#define RXDMA_RCR_TO_DEFAULT 0x8
+#define RXDMA_HDR_SIZE_DEFAULT 2
+#define RXDMA_HDR_SIZE_FULL 6 /* entire header of 6B */
+
+/*
+ * Receive Completion Ring (RCR)
+ */
+#define RCR_PKT_BUF_ADDR_SHIFT 0 /* bit 37:0 */
+#define RCR_PKT_BUF_ADDR_SHIFT_FULL 6 /* fulll buffer address */
+#define RCR_PKT_BUF_ADDR_MASK 0x0000003FFFFFFFFFULL
+#define RCR_PKTBUFSZ_SHIFT 38 /* bit 39:38 */
+#define RCR_PKTBUFSZ_MASK 0x000000C000000000ULL
+#define RCR_L2_LEN_SHIFT 40 /* bit 39:38 */
+#define RCR_L2_LEN_MASK 0x003fff0000000000ULL
+#define RCR_ERROR_SHIFT 54 /* bit 57:55 */
+#define RCR_ERROR_MASK 0x03C0000000000000ULL
+#define RCR_PKT_TYPE_SHIFT 61 /* bit 62:61 */
+#define RCR_PKT_TYPE_MASK 0x6000000000000000ULL
+#define RCR_MULTI_SHIFT 63 /* bit 63 */
+#define RCR_MULTI_MASK 0x8000000000000000ULL
+
+#define RCR_PKTBUFSZ_0 0x00
+#define RCR_PKTBUFSZ_1 0x01
+#define RCR_PKTBUFSZ_2 0x02
+#define RCR_SINGLE_BLOCK 0x03
+
+#define RCR_NO_ERROR 0x0
+#define RCR_CTRL_FIFO_DED 0x1
+#define RCR_DATA_FIFO_DED 0x2
+#define RCR_ERROR_RESERVE 0x4
+
+#define RCR_PKT_TCP 0x1
+#define RCR_PKT_UDP 0x2
+#define RCR_PKT_IS_TCP 0x2000000000000000ULL
+#define RCR_PKT_IS_UDP 0x4000000000000000ULL
+#define RCR_PKT_IS_SCTP 0x6000000000000000ULL
+
+#define RDC_INT_MASK_RBRFULL_SHIFT 34 /* bit 2: 0 to flag */
+#define RDC_INT_MASK_RBRFULL_MASK 0x0000000400000000ULL
+#define RDC_INT_MASK_RBREMPTY_SHIFT 35 /* bit 3: 0 to flag */
+#define RDC_INT_MASK_RBREMPTY_MASK 0x0000000800000000ULL
+#define RDC_INT_MASK_RCRFULL_SHIFT 36 /* bit 4: 0 to flag */
+#define RDC_INT_MASK_RCRFULL_MASK 0x0000001000000000ULL
+#define RDC_INT_MASK_RCRSH_FULL_SHIFT 39 /* bit 7: 0 to flag */
+#define RDC_INT_MASK_RCRSH_FULL_MASK 0x0000008000000000ULL
+#define RDC_INT_MASK_RBR_PRE_EMPTY_SHIFT 40 /* bit 8: 0 to flag */
+#define RDC_INT_MASK_RBR_PRE_EMPTY_MASK 0x0000010000000000ULL
+#define RDC_INT_MASK_RCR_SHA_PAR_SHIFT 43 /* bit 12: 0 to flag */
+#define RDC_INT_MASK_RCR_SHA_PAR_MASK 0x0000080000000000ULL
+#define RDC_INT_MASK_RBR_PRE_PAR_SHIFT 44 /* bit 11: 0 to flag */
+#define RDC_INT_MASK_RBR_PRE_PAR_MASK 0x0000100000000000ULL
+#define RDC_INT_MASK_RCRTO_SHIFT 45 /* bit 13: 0 to flag */
+#define RDC_INT_MASK_RCRTO_MASK 0x0000200000000000ULL
+#define RDC_INT_MASK_THRES_SHIFT 46 /* bit 14: 0 to flag */
+#define RDC_INT_MASK_THRES_MASK 0x0000400000000000ULL
+#define RDC_INT_MASK_RBR_CPL_SHIFT 53 /* bit 21: 0 to flag */
+#define RDC_INT_MASK_RBR_CPL_MASK 0x0020000000000000ULL
+#define RDC_INT_MASK_ALL (RDC_INT_MASK_RBRFULL_MASK | \
+ RDC_INT_MASK_RBREMPTY_MASK | \
+ RDC_INT_MASK_RCRFULL_MASK | \
+ RDC_INT_MASK_RCRSH_FULL_MASK | \
+ RDC_INT_MASK_RBR_PRE_EMPTY_MASK | \
+ RDC_INT_MASK_RCR_SHA_PAR_MASK | \
+ RDC_INT_MASK_RBR_PRE_PAR_MASK | \
+ RDC_INT_MASK_RCRTO_MASK | \
+ RDC_INT_MASK_THRES_MASK | \
+ RDC_INT_MASK_RBR_CPL_MASK)
+
+#define RDC_LDF1 (RDC_INT_MASK_RBRFULL_MASK | \
+ RDC_INT_MASK_RBREMPTY_MASK | \
+ RDC_INT_MASK_RCRFULL_MASK | \
+ RDC_INT_MASK_RCRSH_FULL_MASK | \
+ RDC_INT_MASK_RBR_PRE_EMPTY_MASK | \
+ RDC_INT_MASK_RCR_SHA_PAR_MASK | \
+ RDC_INT_MASK_RBR_PRE_PAR_MASK | \
+ RDC_INT_MASK_RBR_CPL_MASK)
+
+
+#define RDC_STAT_PKTREAD_SHIFT 0 /* WO, bit 15:0 */
+#define RDC_STAT_PKTREAD_MASK 0x000000000000ffffULL
+#define RDC_STAT_PTRREAD_SHIFT 16 /* WO, bit 31:16 */
+#define RDC_STAT_PTRREAD_MASK 0x00000000FFFF0000ULL
+
+#define RDC_STAT_RBRFULL_SHIFT 34 /* RO, bit 34 */
+#define RDC_STAT_RBRFULL 0x0000000400000000ULL
+#define RDC_STAT_RBRFULL_MASK 0x0000000400000000ULL
+#define RDC_STAT_RBREMPTY_SHIFT 35 /* RW1C, bit 35 */
+#define RDC_STAT_RBREMPTY 0x0000000800000000ULL
+#define RDC_STAT_RBREMPTY_MASK 0x0000000800000000ULL
+#define RDC_STAT_RCR_FULL_SHIFT 36 /* RW1C, bit 36 */
+#define RDC_STAT_RCR_FULL 0x0000001000000000ULL
+#define RDC_STAT_RCR_FULL_MASK 0x0000001000000000ULL
+
+#define RDC_STAT_RCR_SHDW_FULL_SHIFT 39 /* RO, bit 39 */
+#define RDC_STAT_RCR_SHDW_FULL 0x0000008000000000ULL
+#define RDC_STAT_RCR_SHDW_FULL_MASK 0x0000008000000000ULL
+#define RDC_STAT_RBR_PRE_EMPTY_SHIFT 40 /* RO, bit 40 */
+#define RDC_STAT_RBR_PRE_EMPTY 0x0000010000000000ULL
+#define RDC_STAT_RBR_PRE_EMPTY_MASK 0x0000010000000000ULL
+
+#define RDC_STAT_RCR_SHA_PAR_SHIFT 43 /* RO, bit 43 */
+#define RDC_STAT_RCR_SHA_PAR 0x0000080000000000ULL
+#define RDC_STAT_RCR_SHA_PAR_MASK 0x0000080000000000ULL
+#define RDC_STAT_RBR_PRE_PAR_SHIFT 44 /* RO, bit 44 */
+#define RDC_STAT_RBR_PRE_PAR 0x0000100000000000ULL
+#define RDC_STAT_RBR_PRE_PAR_MASK 0x0000100000000000ULL
+
+#define RDC_STAT_RCR_TO_SHIFT 45 /* RW1C, bit 45 */
+#define RDC_STAT_RCR_TO 0x0000200000000000ULL
+#define RDC_STAT_RCR_TO_MASK 0x0000200000000000ULL
+#define RDC_STAT_RCR_THRES_SHIFT 46 /* RO, bit 46 */
+#define RDC_STAT_RCR_THRES 0x0000400000000000ULL
+#define RDC_STAT_RCR_THRES_MASK 0x0000400000000000ULL
+#define RDC_STAT_RCR_MEX_SHIFT 47 /* RW, bit 47 */
+#define RDC_STAT_RCR_MEX 0x0000800000000000ULL
+#define RDC_STAT_RCR_MEX_MASK 0x0000800000000000ULL
+
+#define RDC_STAT_RBR_CPL_SHIFT 53 /* RO, bit 53 */
+#define RDC_STAT_RBR_CPL 0x0020000000000000ULL
+#define RDC_STAT_RBR_CPL_MASK 0x0020000000000000ULL
+#define RX_DMA_CTRL_STAT_ENT_MASK_SHIFT 32
+
+#define RDC_STAT_ERROR (RDC_INT_MASK_ALL << \
+ RX_DMA_CTRL_STAT_ENT_MASK_SHIFT)
+
+/* the following are write 1 to clear bits */
+#define RDC_STAT_WR1C (RDC_STAT_RBREMPTY | \
+ RDC_STAT_RCR_SHDW_FULL | \
+ RDC_STAT_RBR_PRE_EMPTY | \
+ RDC_STAT_RCR_TO | \
+ RDC_STAT_RCR_THRES)
+
+#define RCR_CFGB_ENABLE_TIMEOUT 0x8000
+
+typedef union _rcr_entry_t {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t multi:1;
+ uint64_t pkt_type:2;
+ uint64_t reserved1:3;
+ uint64_t error:3;
+ uint64_t reserved2:1;
+ uint64_t l2_len:14;
+ uint64_t pktbufsz:2;
+ uint64_t pkt_buf_addr:38;
+#else
+ uint64_t pkt_buf_addr:38;
+ uint64_t pktbufsz:2;
+ uint64_t l2_len:14;
+ uint64_t reserved2:1;
+ uint64_t error:3;
+ uint64_t reserved1:3;
+ uint64_t pkt_type:2;
+ uint64_t multi:1;
+#endif
+ } bits;
+} rcr_entry_t, *p_rcr_entry_t;
+
+typedef union _pkt_hdr_twobyte_t {
+ uint16_t value;
+ struct {
+#if defined (_BIG_ENDIAN)
+ uint16_t rsvd:1;
+ uint16_t l4_cs_eq:1;
+ uint16_t maccheck:1;
+ uint16_t class:5;
+ uint16_t vlan:1;
+ uint16_t bcast_frame:1;
+ uint16_t noport:1;
+ uint16_t badip:1;
+ uint16_t tcamhit:1;
+ uint16_t drop_code:3;
+#else
+ uint16_t drop_code:3;
+ uint16_t tcamhit:1;
+ uint16_t badip:1;
+ uint16_t noport:1;
+ uint16_t bcast_frame:1;
+ uint16_t vlan:1;
+ uint16_t class:5;
+ uint16_t maccheck:1;
+ uint16_t l4_cs_eq:1;
+ uint16_t rsvd:1;
+#endif
+ } bits;
+} pkt_hdr_twobyte_t, *p_pkt_hdr_twobyte_t;
+
+
+#define RX_DMA_MAILBOX_BYTE_LENGTH 64
+#define RX_DMA_MBOX_UNUSED_1 8
+#define RX_DMA_MBOX_UNUSED_2 16
+
+typedef struct _rxdma_mailbox_t {
+ rdc_stat_t rxdma_ctl_stat; /* 8 bytes */
+ rdc_rbr_qlen_t rbr_qlen; /* 8 bytes */
+ rdc_rbr_head_t rbr_hdh; /* 8 bytes */
+ uint8_t resv_1[RX_DMA_MBOX_UNUSED_1];
+ rdc_rcr_tail_t rcr_tail; /* 8 bytes */
+ uint8_t resv_2[RX_DMA_MBOX_UNUSED_1];
+ rdc_rcr_qlen_t rcr_qlen; /* 8 bytes */
+ uint8_t resv_3[RX_DMA_MBOX_UNUSED_1];
+} rxdma_mailbox_t, *p_rxdma_mailbox_t;
+
+/*
+ * hardware workarounds: kick 16 (was 8 before)
+ */
+#define HXGE_RXDMA_POST_BATCH 16
+
+#define RXBUF_START_ADDR(a, index, bsize) ((a & (index * bsize))
+#define RXBUF_OFFSET_FROM_START(a, start) (start - a)
+#define RXBUF_64B_ALIGNED 64
+
+#define HXGE_RXBUF_EXTRA 34
+
+/*
+ * Receive buffer thresholds and buffer types
+ */
+#define HXGE_RX_BCOPY_SCALE 8 /* use 1/8 as lowest granularity */
+
+typedef enum {
+ HXGE_RX_COPY_ALL = 0, /* do bcopy on every packet */
+ HXGE_RX_COPY_1, /* bcopy on 1/8 of buffer posted */
+ HXGE_RX_COPY_2, /* bcopy on 2/8 of buffer posted */
+ HXGE_RX_COPY_3, /* bcopy on 3/8 of buffer posted */
+ HXGE_RX_COPY_4, /* bcopy on 4/8 of buffer posted */
+ HXGE_RX_COPY_5, /* bcopy on 5/8 of buffer posted */
+ HXGE_RX_COPY_6, /* bcopy on 6/8 of buffer posted */
+ HXGE_RX_COPY_7, /* bcopy on 7/8 of buffer posted */
+ HXGE_RX_COPY_NONE /* don't do bcopy at all */
+} hxge_rxbuf_threshold_t;
+
+typedef enum {
+ HXGE_RBR_TYPE0 = RCR_PKTBUFSZ_0, /* bcopy buffer size 0 (small) */
+ HXGE_RBR_TYPE1 = RCR_PKTBUFSZ_1, /* bcopy buffer size 1 (medium) */
+ HXGE_RBR_TYPE2 = RCR_PKTBUFSZ_2 /* bcopy buffer size 2 (large) */
+} hxge_rxbuf_type_t;
+
+typedef struct _rdc_errlog {
+ rdc_pref_par_log_t pre_par;
+ rdc_pref_par_log_t sha_par;
+ uint8_t compl_err_type;
+} rdc_errlog_t;
+
+
+
+/* RBR Descriptor entries are 4-bytes long */
+typedef uint32_t rbr_desc_entry_t;
+
+struct rx_desc_t {
+ caddr_t vaddr;
+ dma_addr_t dma_addr;
+};
+
+struct rx_hash_entry {
+ unsigned long dma_addr;
+ int index;
+ struct rx_hash_entry *next;
+};
+
+
+/* Receive Completion Ring */
+typedef struct _rx_rcr_ring_t {
+
+ uint32_t num_rcr_entries;
+ struct rcr_desc_t {
+ rcr_entry_t *vaddr;
+ dma_addr_t dma_addr;
+ } rcr_addr;
+
+ /* Current location where processing is to take place next */
+ uint32_t rcr_curr_loc;
+
+ /* Copies of HW registers */
+ rdc_rcr_cfg_a_t rcr_cfga;
+ rdc_rcr_cfg_b_t rcr_cfgb;
+ boolean_t cfg_set;
+} rx_rcr_ring_t, *p_rx_rcr_ring_t;
+
+
+struct rx_rbr_entry_t {
+ struct rbr_desc_addr_t {
+ rbr_desc_entry_t *vaddr;
+ dma_addr_t dma_addr;
+ } addr;
+ int index;
+ struct page *page;
+ uint32_t max_pkts;
+ uint32_t pkt_size;
+ int in_use;
+};
+
+
+struct rx_ring_stats_t {
+ /* Maintained by software */
+
+ uint64_t ipackets;
+ uint64_t ibytes;
+ uint64_t ierrors;
+ uint64_t jumbo_pkts;
+ uint64_t nomem_drop;
+
+ uint64_t ecc_errors;
+
+ uint64_t rbr_cpl_tmout;
+ uint64_t peu_resp_err;
+ uint64_t rcr_shadow_parity;
+ uint64_t rcr_prefetch_parity;
+ uint64_t rbr_prefetch_empty;
+ uint64_t rcr_shadow_full;
+ uint64_t rcr_full;
+ uint64_t rbr_empty;
+ uint64_t rbr_empty_handled;
+ uint64_t rbr_empty_posted;
+ uint64_t rbr_full;
+ uint64_t rcr_to;
+ uint64_t rcr_thres;
+
+ /* Hardware counters */
+ uint64_t pkt_cnt;
+ uint64_t pkt_too_long;
+ uint64_t no_rbr_avail;
+ uint64_t rvm_errors;
+ uint64_t frame_errors;
+ uint64_t ram_errors;
+
+ uint64_t crc_errors;
+
+};
+
+
+typedef struct _rx_rbr_ring_t {
+
+ struct rx_rbr_entry_t *buf_blocks;
+ struct rbr_desc_t {
+ rbr_desc_entry_t *vaddr;
+ dma_addr_t dma_addr;
+ } rbr_addr;
+ unsigned int buf_blocks_order;
+ uint32_t num_rbr_entries;
+ uint32_t rbr_free_loc;
+ uint32_t pages_to_post;
+ uint16_t rbr_empty_threshold;
+ uint16_t rbr_empty_flag; /* we are in empty procesing */
+ struct rx_hash_entry *hash_table[HASH_TABLE_SIZE];
+
+
+ /* Copies of what is in the HW registers */
+ rdc_rbr_cfg_a_t rbr_cfga;
+ rdc_rbr_cfg_b_t rbr_cfgb;
+ rdc_rbr_kick_t rbr_kick;
+ boolean_t cfg_set;
+
+ /* what goes into RBR Configuration B register */
+ uint16_t pkt_buf_size[4];
+ uint16_t pkt_buf_size_bytes[4];
+} rx_rbr_ring_t, *p_rx_rbr_ring_t;
+
+struct rx_ring_t {
+ uint16_t rdc;
+
+ /* Copies of some HW register */
+ rdc_page_handle_t page_hdl;
+ uint16_t offset;
+ boolean_t full_hdr;
+ unsigned long state;
+
+ rx_rbr_ring_t rbr;
+ rx_rcr_ring_t rcr;
+ struct rx_desc_t mbox;
+ uint16_t dma_clk_res;
+ int first_time; /* CR 6769038 */
+ struct rx_ring_stats_t stats;
+
+};
+
+#define RX_NO_ERR 0
+#define RX_DROP_PKT -1
+#define RX_FAILURE -2
+
+#define RCR_INIT_PATTERN 0x5a5a6b6b7c7c8d8dULL
+
+#define GET_RCR_ENTRY(entry) \
+ &entry->rcr_addr.vaddr[entry->rcr_curr_loc]
+
+/* SW workaround for CR 6698258: One of the side effects of this bug is to
+ * push rcr entries that have already been processed, causing the packet
+ * processing routines to complain about "bad packets". Hydra could flush
+ * 64B cache line into memory at particular junctures (see bug for details).
+ * In order to avoid getting old packet addresses, we should initialize
+ * the entire cache line once we have processed the last entry in a cache
+ * line (rcr_ring is aligned to 64B)
+ */
+#define INCREMENT_RCR_ENTRY_TO_NEXT(rcr_ring) \
+ { \
+ if ((rcr_ring->rcr_curr_loc & 0x7) == 0x7) { \
+ uint32_t tmp_loc = rcr_ring->rcr_curr_loc- 7; \
+ int i; \
+ for (i = 0; i < 8; i++) \
+ rcr_ring->rcr_addr.vaddr[tmp_loc+i].value = RCR_INIT_PATTERN; \
+ } \
+ rcr_ring->rcr_curr_loc = (rcr_ring->rcr_curr_loc+1) % rcr_ring->num_rcr_entries; \
+ }
+#endif
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#include "hxge.h"
+
+void
+hxge_init_stats(struct hxge_adapter *hxgep)
+{
+ HXGE_DBG(hxgep, "Initializing statistics");
+ hxgep->statsp = kzalloc(sizeof(hxge_stats_t), GFP_KERNEL);
+}
+
+void
+hxge_free_stats(struct hxge_adapter *hxgep)
+{
+ HXGE_DBG(hxgep, "Free statistics");
+ if (hxgep->statsp)
+ kfree(hxgep->statsp);
+ hxgep->statsp = NULL;
+}
+
+
+/* Get the Hydra network statistics and package the information in a format
+ * that can displayed by the OS network layer */
+struct net_device_stats *
+hxge_get_stats (struct net_device *netdev)
+{
+ struct hxge_adapter *hxgep= netdev_priv(netdev);
+ struct net_device_stats *stats = &hxgep->net_stats;
+ struct rx_ring_t *rx_ring;
+ struct tx_ring_t *tx_ring;
+ int i;
+ hxge_vmac_stats_t *vmac_stats = &hxgep->statsp->vmac_stats;
+
+ spin_lock(&hxgep->stats_lock);
+
+ memset(stats, 0, sizeof(struct net_device_stats));
+
+ for (i = 0, rx_ring=&hxgep->rx_ring[i];
+ rx_ring && i < hxgep->max_rdcs;
+ i++, rx_ring=&hxgep->rx_ring[i]) {
+ stats->rx_packets += rx_ring->stats.ipackets;
+ stats->rx_bytes += rx_ring->stats.ibytes;
+ stats->rx_errors += rx_ring->stats.ierrors;
+
+ /* Various reasons for dropped packets */
+ stats->rx_dropped += rx_ring->stats.pkt_too_long
+ + rx_ring->stats.no_rbr_avail
+ + rx_ring->stats.rvm_errors
+ + rx_ring->stats.frame_errors
+ + rx_ring->stats.ram_errors
+ + rx_ring->stats.nomem_drop
+ + vmac_stats->rx_drop_frame_cnt;
+
+ stats->rx_frame_errors += rx_ring->stats.frame_errors;
+ stats->rx_crc_errors += rx_ring->stats.crc_errors;
+ stats->rx_over_errors += rx_ring->stats.no_rbr_avail;
+ stats->rx_length_errors += rx_ring->stats.pkt_too_long;
+ }
+
+ /* Account for non-channel-specific RX errors */
+ stats->rx_errors += hxgep->statsp->rx_ierrors
+ + hxgep->statsp->peu_errors; /* Count PEU as "RX" for now */
+
+ for (i = 0, tx_ring=&hxgep->tx_ring[i];
+ tx_ring && i < hxgep->max_tdcs;
+ i++, tx_ring=&hxgep->tx_ring[i]) {
+ stats->tx_packets += tx_ring->stats.opackets;
+ stats->tx_bytes += tx_ring->stats.obytes;
+ stats->tx_errors += tx_ring->stats.oerrors;
+ stats->tx_dropped += tx_ring->stats.hdr_error_cnt
+ + tx_ring->stats.abort_cnt
+ + tx_ring->stats.runt_cnt;
+ }
+
+ /* Account for non-channel-specific TX errors */
+ stats->tx_errors += hxgep->statsp->tx_oerrors;
+
+ spin_unlock(&hxgep->stats_lock);
+
+ return stats;
+}
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#include "hxge.h"
+#include <linux/sysfs.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+#define DEV_TYPE class_device
+#define to_net_dev(class) container_of(class, struct net_device, class_dev)
+static ssize_t errinject_show(struct class_device *, char *);
+static ssize_t errinject_store(struct class_device *, const char *, size_t);
+static CLASS_DEVICE_ATTR(errinject, S_IWUSR | S_IRUGO, errinject_show, errinject_store);
+#else
+#define DEV_TYPE device
+static ssize_t errinject_show(struct device *, struct device_attribute *,
+ char *);
+static ssize_t errinject_store(struct device *, struct device_attribute *,
+ const char *, size_t);
+DEVICE_ATTR(errinject, S_IWUSR | S_IRUGO, errinject_show, errinject_store);
+#endif
+
+
+static ssize_t errinject_show (struct DEV_TYPE *dev,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
+ struct device_attribute *attr,
+#endif
+ char *buf)
+{
+ struct hxge_adapter *hxgep = netdev_priv(to_net_dev(dev));
+
+ return (sprintf(buf, "%#lx\n", hxgep->err_flags));
+}
+
+static ssize_t errinject_store (struct DEV_TYPE *dev,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
+ struct device_attribute *attr,
+#endif
+ const char *buf,
+ size_t len)
+{
+ unsigned long val;
+ char *endp;
+ struct hxge_adapter *hxgep = netdev_priv(to_net_dev(dev));
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ val = simple_strtoul(buf, &endp, 16);
+ if (endp == buf)
+ return -EINVAL;
+
+ HXGE_ERR(hxgep, "val is 0x%lx, len = %d", val, (int)len);
+ spin_lock(&hxgep->lock);
+ hxgep->err_flags = val;
+ spin_unlock(&hxgep->lock);
+ HXGE_ERR(hxgep, "Setting err_flags to 0x%lx", hxgep->err_flags);
+ return len;
+
+}
+
+
+int hxge_create_sysfs(struct net_device *netdev)
+{
+ struct DEV_TYPE *dev;
+ int ret;
+
+ printk(KERN_DEBUG "Creating errinject device file.. \n");
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+ dev = &netdev->class_dev;
+ ret = class_device_create_file(dev, &class_device_attr_errinject);
+#else
+ dev = &netdev->dev;
+ ret = device_create_file(dev, &dev_attr_errinject);
+#endif
+ return ret;
+}
+
+void hxge_remove_sysfs(struct net_device *netdev)
+{
+ struct DEV_TYPE *dev;
+
+ printk(KERN_DEBUG "Removing errinject device.. \n");
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+ dev = &netdev->class_dev;
+ class_device_remove_file(dev, &class_device_attr_errinject);
+#else
+ dev = &netdev->dev;
+ device_remove_file(dev, &dev_attr_errinject);
+#endif
+}
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#ifndef _HXGE_TDC_HW_H
+#define _HXGE_TDC_HW_H
+
+#define TDC_BASE_ADDR 0X00400000
+
+#define TDC_PAGE_HANDLE (TDC_BASE_ADDR + 0x8)
+#define TDC_TDR_CFG (TDC_BASE_ADDR + 0x20)
+#define TDC_TDR_HEAD (TDC_BASE_ADDR + 0x28)
+#define TDC_TDR_PRE_HEAD (TDC_BASE_ADDR + 0x30)
+#define TDC_TDR_KICK (TDC_BASE_ADDR + 0x38)
+#define TDC_INT_MASK (TDC_BASE_ADDR + 0x40)
+#define TDC_STAT (TDC_BASE_ADDR + 0x48)
+#define TDC_MBH (TDC_BASE_ADDR + 0x50)
+#define TDC_MBL (TDC_BASE_ADDR + 0x58)
+#define TDC_BYTE_CNT (TDC_BASE_ADDR + 0x80)
+#define TDC_TDR_QLEN (TDC_BASE_ADDR + 0x88)
+#define TDC_RTAB_PTR (TDC_BASE_ADDR + 0x90)
+#define TDC_DROP_CNT (TDC_BASE_ADDR + 0x98)
+#define TDC_LAST_PKT_RBUF_PTRS (TDC_BASE_ADDR + 0xA8)
+#define TDC_PREF_CMD (TDC_BASE_ADDR + 0x100)
+#define TDC_PREF_DATA (TDC_BASE_ADDR + 0x108)
+#define TDC_PREF_PAR_DATA (TDC_BASE_ADDR + 0x110)
+#define TDC_REORD_BUF_CMD (TDC_BASE_ADDR + 0x120)
+#define TDC_REORD_BUF_DATA (TDC_BASE_ADDR + 0x128)
+#define TDC_REORD_BUF_ECC_DATA (TDC_BASE_ADDR + 0x130)
+#define TDC_REORD_TBL_CMD (TDC_BASE_ADDR + 0x140)
+#define TDC_REORD_TBL_DATA_LO (TDC_BASE_ADDR + 0x148)
+#define TDC_REORD_TBL_DATA_HI (TDC_BASE_ADDR + 0x150)
+#define TDC_PREF_PAR_LOG (TDC_BASE_ADDR + 0x200)
+#define TDC_REORD_BUF_ECC_LOG (TDC_BASE_ADDR + 0x208)
+#define TDC_REORD_TBL_PAR_LOG (TDC_BASE_ADDR + 0x210)
+#define TDC_FIFO_ERR_MASK (TDC_BASE_ADDR + 0x220)
+#define TDC_FIFO_ERR_STAT (TDC_BASE_ADDR + 0x228)
+#define TDC_FIFO_ERR_INT_DBG (TDC_BASE_ADDR + 0x230)
+#define TDC_STAT_INT_DBG (TDC_BASE_ADDR + 0x240)
+#define TDC_PKT_REQ_TID_TAG (TDC_BASE_ADDR + 0x250)
+#define TDC_SOP_PREF_DESC_LOG (TDC_BASE_ADDR + 0x260)
+#define TDC_PREF_DESC_LOG (TDC_BASE_ADDR + 0x268)
+#define TDC_PEU_TXN_LOG (TDC_BASE_ADDR + 0x270)
+#define TDC_DBG_TRAINING_VEC (TDC_BASE_ADDR + 0x300)
+#define TDC_DBG_GRP_SEL (TDC_BASE_ADDR + 0x308)
+
+
+/*
+ * Register: TdcPageHandle
+ * Logical Page Handle
+ * Description: Upper 20 bits [63:44] to use for all accesses over
+ * the PCI-E bus. Fields in this register are part of the dma
+ * configuration and cannot be changed once the dma is enabled.
+ * Fields:
+ * Page handle, bits [63:44] of all PCI-E transactions for this
+ * channel.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:44;
+ uint64_t page_handle:20;
+#else
+ uint64_t page_handle:20;
+ uint64_t rsrvd:44;
+#endif
+ } bits;
+} tdc_page_handle_t;
+
+
+/*
+ * Register: TdcTdrCfg
+ * Transmit Ring Configuration
+ * Description: Configuration parameters for transmit DMA block.
+ * Software configures the location of the transmit ring in host
+ * memory, as well as its maximum size. Fields in this register are
+ * part of the dma configuration and cannot be changed once the dma
+ * is enabled.
+ * HW does not check for all configuration errors across different
+ * fields.
+ * The usage of enable, reset, and qst is as follows. Software
+ * should use the following sequence to reset a DMA channel. First,
+ * set DMA.enable to 0, wait for DMA.qst=1 and then, set DMA.reset to
+ * 1. After DMA.reset is cleared by hardware and the DMA.qst is set
+ * to 1, software may then start configuring the DMA channel. The
+ * DMA.enable can be set or cleared while the DMA is in operation.
+ * The state machines of the DMA may not have returned to its initial
+ * states yet after the DMA.enable bit is cleared. This condition is
+ * indicated by the value of the DMA.qst. An example of DMA.enable
+ * being cleared during operation is when a fatal error occurs.
+ * Fields:
+ * Bits [15:5] of the maximum number of entries in the Transmit
+ * Queue ring buffer. Bits [4:0] are always 0. Maximum number of
+ * entries is (2^16 - 32) and is limited by the staddr value.
+ * (len + staddr) should not exceed (2^16 - 32).
+ * Set to 1 to enable the Transmit DMA. On fatal errors, this bit
+ * will be cleared by hardware. This bit cannot be set if sw has
+ * not resolved any pending fatal error condition: i.e. any
+ * TdcStat ldf1 error bits remain set.
+ * Set to 1 to reset the DMA. Hardware will clear this bit after
+ * reset is completed. A reset will bring the sepecific DMA back
+ * to the power on state (including the DMA.en in this register).
+ * When set to 1, it indicates all state associated with the DMA
+ * are in its initial state following either dma reset or
+ * disable. Thus, once this is set to 1, sw could start to
+ * configure the DMA if needed. In an extreme case such as if a
+ * parity error on an EOP descriptor prevents recognition of the
+ * EOP, it is possible that the qst bit will not be set even
+ * though the dma engine has been disabled.
+ * Address bits [43:19] of the start address for the transmit
+ * ring buffer. The value in this field is dependent on len
+ * field. (len + staddr) should not exceed (2^16 - 32).
+ * Bits [18:6] of the start address for the transmit ring buffer.
+ * Bits [5:0] are assumed to be zero, or 64B aligned.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t len:11;
+ uint64_t rsrvd:5;
+ uint64_t enable:1;
+ uint64_t reset:1;
+ uint64_t qst:1;
+ uint64_t rsrvd1:1;
+ uint64_t staddr_base:25;
+ uint64_t staddr:13;
+ uint64_t rsrvd2:6;
+#else
+ uint64_t rsrvd2:6;
+ uint64_t staddr:13;
+ uint64_t staddr_base:25;
+ uint64_t rsrvd1:1;
+ uint64_t qst:1;
+ uint64_t reset:1;
+ uint64_t enable:1;
+ uint64_t rsrvd:5;
+ uint64_t len:11;
+#endif
+ } bits;
+} tdc_tdr_cfg_t;
+
+
+/*
+ * Register: TdcTdrHead
+ * Transmit Ring Head
+ * Description: Read-only register software call poll to determine
+ * the current head of the transmit ring, from the tdcTxPkt block.
+ * Software uses this to know which Tdr entries have had their
+ * descriptors transmitted. These entries and their descriptors may
+ * then be reused by software.
+ * Fields:
+ * Hardware will toggle this bit every time the head is wrapped
+ * around the configured ring buffer.
+ * Entry in transmit ring which will be the next descriptor
+ * transmitted. Software should consider the Tdr full if head ==
+ * TdcTdrKick::tail and wrap != TdcTdrKick::wrap. The ring is
+ * empty of head == TdcTdrKick::tail and wrap ==
+ * TdcTdrKick::wrap.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:47;
+ uint64_t wrap:1;
+ uint64_t head:16;
+#else
+ uint64_t head:16;
+ uint64_t wrap:1;
+ uint64_t rsrvd:47;
+#endif
+ } bits;
+} tdc_tdr_head_t;
+
+
+/*
+ * Register: TdcTdrPreHead
+ * Transmit Ring Prefetch Head
+ * Description: Read-only register software call poll to determine
+ * the current prefetch head of the transmit ring, from the tdcPktReq
+ * block. Transmit descriptors are prefetched into chip memory.
+ * Indicates next descriptor to be read from host memory. For debug
+ * use only.
+ * Fields:
+ * Hardware will toggle this bit every time the prefetch head is
+ * wrapped around the configured ring buffer.
+ * Entry in transmit ring which will be fetched next from host
+ * memory.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:47;
+ uint64_t wrap:1;
+ uint64_t head:16;
+#else
+ uint64_t head:16;
+ uint64_t wrap:1;
+ uint64_t rsrvd:47;
+#endif
+ } bits;
+} tdc_tdr_pre_head_t;
+
+
+/*
+ * Register: TdcTdrKick
+ * Transmit Ring Kick
+ * Description: After posting transmit descriptors to the Transmit
+ * Ring, software updates the tail pointer to inform Hydra of the new
+ * descriptors. Software can only post descriptors through this
+ * register when the entire packet is in the ring. Otherwise,
+ * hardware dead-lock can occur. If an overflow kick occurs when the
+ * channel is disabled, tdcStat.txRngOflow (Transmit Ring Overflow)
+ * status is not set.
+ * Fields:
+ * Software needs to toggle this bit every time the tail is
+ * wrapped around the configured ring buffer.
+ * Entry where the next valid descriptor will be added (one entry
+ * past the last valid descriptor.)
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:47;
+ uint64_t wrap:1;
+ uint64_t tail:16;
+#else
+ uint64_t tail:16;
+ uint64_t wrap:1;
+ uint64_t rsrvd:47;
+#endif
+ } bits;
+} tdc_tdr_kick_t;
+
+
+/*
+ * Register: TdcIntMask
+ * Transmit Event Mask
+ * Description: The Tx DMA can generate a number of LDF events. The
+ * events can be enabled by software by setting the corresponding bit
+ * to 0. The default value of 1 means the event is masked and no LDF
+ * event is generated.
+ * Fields:
+ * Set to 0 to select the event to raise the LDF for packets
+ * marked. An LDF 0 event.
+ * Set to 0 to select the event to raise the LDF when poisoned
+ * completion or non-zero (unsuccessful) completion status
+ * received from PEU. An LDF 1 event.
+ * Set to 0 to select the event to raise the LDF when total bytes
+ * transmitted compared against pkt internal header bytes
+ * transmitted mismatch. An LDF 1 event.
+ * Set to 0 to select the event to raise the LDF when a runt
+ * packet is dropped (when VMAC does not allow runt packets to be
+ * padded). An LDF 1 event.
+ * Set to 0 to select the event to raise the LDF when the packet
+ * size exceeds hardware limit. An LDF 1 event.
+ * Set to 0 to select the event to raise the LDF to indicate
+ * Transmit Ring Overflow An LDF 1 event.
+ * Set to 0 to select the event to raise the LDF to indicate
+ * parity error on the tdr prefetch buffer occurred. An LDF 1
+ * event.
+ * Set to 0 to select the event to raise the LDF to indicate tdc
+ * received a response completion timeout from peu for tdr
+ * descriptor prefetch An LDF 1 event.
+ * Set to 0 to select the event to raise the LDF to indicate tdc
+ * received a response completion timeout from peu for packet
+ * data request An LDF 1 event.
+ * Set to 0 to select the event to raise the LDF to indicate tdc
+ * did not receive an SOP in the 1st descriptor as was expected
+ * or the numPtr in the 1st descriptor was set to 0. An LDF 1
+ * event.
+ * Set to 0 to select the event to raise the LDF to indicate tdc
+ * received an unexpected SOP descriptor error. An LDF 1 event.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:48;
+ uint64_t marked:1;
+ uint64_t rsrvd1:5;
+ uint64_t peu_resp_err:1;
+ uint64_t pkt_size_hdr_err:1;
+ uint64_t runt_pkt_drop_err:1;
+ uint64_t pkt_size_err:1;
+ uint64_t tx_rng_oflow:1;
+ uint64_t pref_par_err:1;
+ uint64_t tdr_pref_cpl_to:1;
+ uint64_t pkt_cpl_to:1;
+ uint64_t invalid_sop:1;
+ uint64_t unexpected_sop:1;
+#else
+ uint64_t unexpected_sop:1;
+ uint64_t invalid_sop:1;
+ uint64_t pkt_cpl_to:1;
+ uint64_t tdr_pref_cpl_to:1;
+ uint64_t pref_par_err:1;
+ uint64_t tx_rng_oflow:1;
+ uint64_t pkt_size_err:1;
+ uint64_t runt_pkt_drop_err:1;
+ uint64_t pkt_size_hdr_err:1;
+ uint64_t peu_resp_err:1;
+ uint64_t rsrvd1:5;
+ uint64_t marked:1;
+ uint64_t rsrvd:48;
+#endif
+ } bits;
+} tdc_int_mask_t;
+
+
+/*
+ * Register: TdcStat
+ * Transmit Control and Status
+ * Description: Combined control and status register. When writing to
+ * this register, any bit that software wishes not to change should
+ * be written to 0. The TdcStat register may be read or written only
+ * when no mailbox updates are pending. Accordingly, the expected
+ * algorithm for software to use in tracking marked packets and
+ * mailbox updates is one of the following only: 1) enable
+ * interrupts, enable mb, send a single marked packet, wait for Ldf0,
+ * clear marked, repeat or 2) disable interrupts, never enable mb,
+ * send one or more marked packets, poll TdcStat for marked/mMarked
+ * state, clear marked/mMarked bits, repeat. If interrupts are
+ * enabled, upon receiving an Ldf1 interrupt for a given channel
+ * software must wait until a channel's Qst bit has asserted before
+ * reading TdcStat for corresponding error information and before
+ * writing to TdcStat to clear error state.
+ * Fields:
+ * A wrap-around counter to keep track of packets transmitted.
+ * Reset to zero when the DMA is reset
+ * The pktCnt corresponds to the last packet with the MARK bit
+ * set. Reset to zero when the DMA is reset.
+ * Set to 1 to cause HW to update the mailbox when the next
+ * packet with the marked bit set is transmitted. HW clears this
+ * bit to zero after the mailbox update has completed. Note that,
+ * correspondingly, the TdcStat data for the Tx mailbox write
+ * will reflect the state of mb prior to the mb bit's update for
+ * the marked packet being sent. Software should send only one
+ * marked packet per assertion of the mb bit. Multiple marked
+ * packets after setting the mb bit and before receiving the
+ * corresponding mailbox update is not supported. Precautionary
+ * note: Emphasize HW is responsible for clearing this bit. If
+ * software clears this bit, the behavior is undefined.
+ * Set to 1 when a packet with the mark bit set is transmitted.
+ * If mb is set at the time of the marked packet transmission,
+ * marked will not be set until the corresponding mailbox write
+ * has completed. Note that, correspondingly, the TdcStat data
+ * for the Tx mailbox write will reflect the state of marked
+ * prior to the marked bit's update for the marked packet being
+ * sent. Software may read the register to clear the bit.
+ * Alternatively, software may write a 1 to clear the MARKED bit
+ * (Write 0 has no effect). In the case of write 1, if mMarked
+ * bit is set, MARKED bit will NOT be cleared. This bit is used
+ * to generate LDF 0 consistent with settings in TdcIntMask.
+ * Overflow bit for MARKED register bit. Indicates that multiple
+ * marked packets have been transmitted since the last clear of
+ * the marked bit. If hardware is waiting to update MARKED until
+ * a mailbox write has completed, when another marked packet is
+ * transmitted, mMarked will also not be set until the mailbox
+ * write completes. Note that, correspondingly, the TdcStat data
+ * for the Tx mailbox write will reflect the state of mMarked
+ * prior to the mMarked bit's update for the marked packet being
+ * sent. Software reads to clear. A write 1 to MARKED bit will
+ * also clear the mMarked bit. A write 0 has no effect.
+ * Set to 1 to indicate poisoned completion or non-zero
+ * (unsuccessful) completion status received from PEU. Part of
+ * LDF 1.
+ * Set to 1 to indicate tdc descriptor error: total bytes
+ * transmitted compared against pkt internal header bytes
+ * transmitted mismatch. Fatal error. Part of LDF 1.
+ * Set to 1 when a runt packet is dropped (when VMAC does not
+ * allow runt packets to be padded. Fatal error. Part of LDF1.
+ * Set to 1 when the packet size exceeds hardware limit: the sum
+ * of gathers exceeds the maximum transmit length (specified in
+ * the Tx VMAC Configuration register txMaxFrameLength) or any
+ * descriptor attempts to transmit more than 4K. Writing a 1
+ * clears the value to 0. Writing a 0 has no effect. Part of LDF
+ * 1. Note that packet size for the purpose of this error is
+ * determined by the actual transfer size from the Tdc to the Tdp
+ * and not from the totXferSize field of the internal header.
+ * Set to 1 to indicate Transmit Ring Overflow: Tail > Ringlength
+ * or if the relative position of the shadow tail to the ring
+ * tail is not correct with respect to the wrap bit. Transmit
+ * Ring Overflow status is not set, if the dma is disabled. Fatal
+ * error. Part of LDF1.
+ * Set to 1 by HW to indicate parity error on the tdr prefetch
+ * buffer occurred. Writing a 1 clears the parity error log
+ * register Part of LDF 1.
+ * Set to 1 to indicate tdc received a response completion
+ * timeout from peu for tdr descriptor prefetch Fatal error. Part
+ * of LDF 1.
+ * Set to 1 to indicate tdc received a response completion
+ * timeout from peu for packet data request Fatal error. Part of
+ * LDF 1.
+ * Set to 1 to indicate tdc did not receive an SOP in the 1st
+ * descriptor as was expected or the numPtr in the 1st descriptor
+ * was set to 0. Fatal error. Part of LDF 1.
+ * Set to 1 to indicate tdc received an unexpected SOP descriptor
+ * error. Fatal error. Part of LDF 1.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:4;
+ uint64_t pkt_cnt:12;
+ uint64_t rsrvd1:4;
+ uint64_t lastmark:12;
+ uint64_t rsrvd2:2;
+ uint64_t mb:1;
+ uint64_t rsrvd3:13;
+ uint64_t marked:1;
+ uint64_t m_marked:1;
+ uint64_t rsrvd4:4;
+ uint64_t peu_resp_err:1;
+ uint64_t pkt_size_hdr_err:1;
+ uint64_t runt_pkt_drop_err:1;
+ uint64_t pkt_size_err:1;
+ uint64_t tx_rng_oflow:1;
+ uint64_t pref_par_err:1;
+ uint64_t tdr_pref_cpl_to:1;
+ uint64_t pkt_cpl_to:1;
+ uint64_t invalid_sop:1;
+ uint64_t unexpected_sop:1;
+#else
+ uint64_t unexpected_sop:1;
+ uint64_t invalid_sop:1;
+ uint64_t pkt_cpl_to:1;
+ uint64_t tdr_pref_cpl_to:1;
+ uint64_t pref_par_err:1;
+ uint64_t tx_rng_oflow:1;
+ uint64_t pkt_size_err:1;
+ uint64_t runt_pkt_drop_err:1;
+ uint64_t pkt_size_hdr_err:1;
+ uint64_t peu_resp_err:1;
+ uint64_t rsrvd4:4;
+ uint64_t m_marked:1;
+ uint64_t marked:1;
+ uint64_t rsrvd3:13;
+ uint64_t mb:1;
+ uint64_t rsrvd2:2;
+ uint64_t lastmark:12;
+ uint64_t rsrvd1:4;
+ uint64_t pkt_cnt:12;
+ uint64_t rsrvd:4;
+#endif
+ } bits;
+} tdc_stat_t;
+
+
+/*
+ * Register: TdcMbh
+ * Tx DMA Mailbox High
+ * Description: Upper bits of Tx DMA mailbox address in host memory.
+ * Fields in this register are part of the dma configuration and
+ * cannot be changed once the dma is enabled.
+ * Fields:
+ * Bits [43:32] of the Mailbox address.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:52;
+ uint64_t mbaddr:12;
+#else
+ uint64_t mbaddr:12;
+ uint64_t rsrvd:52;
+#endif
+ } bits;
+} tdc_mbh_t;
+
+
+/*
+ * Register: TdcMbl
+ * Tx DMA Mailbox Low
+ * Description: Lower bits of Tx DMA mailbox address in host memory.
+ * Fields in this register are part of the dma configuration and
+ * cannot be changed once the dma is enabled.
+ * Fields:
+ * Bits [31:6] of the Mailbox address. Bits [5:0] are assumed to
+ * be zero, or 64B aligned.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t mbaddr:26;
+ uint64_t rsrvd1:6;
+#else
+ uint64_t rsrvd1:6;
+ uint64_t mbaddr:26;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} tdc_mbl_t;
+
+
+/*
+ * Register: TdcByteCnt
+ * Tx DMA Byte Count
+ * Description: Counts the number of bytes transmitted to the tx
+ * datapath block. This count may increment in advance of
+ * corresponding updates to TdcStat for the bytes transmitted.
+ * Fields:
+ * Number of bytes transmitted from transmit ring. This counter
+ * will saturate. This register is cleared on read.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t byte_count:32;
+#else
+ uint64_t byte_count:32;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} tdc_byte_cnt_t;
+
+
+/*
+ * Register: TdcTdrQlen
+ * Tdr Queue Length
+ * Description: Number of descriptors in Tdr For debug only. Note:
+ * Not analogous to either rdc.rbrQlen or tdc.tdcKick -
+ * tdc.tdcTdrHead. Indicates depth of the two intermediate descriptor
+ * usage points rather than end-to-end descriptor availability.
+ * Fields:
+ * Current number of descriptors in Tdr, unprefetched
+ * Current number of descriptors in Tdr in prefetch buffer, i.e.
+ * those which have been prefetched but have not yet been
+ * allocated to the RTab.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t tdr_qlen:16;
+ uint64_t tdr_pref_qlen:16;
+#else
+ uint64_t tdr_pref_qlen:16;
+ uint64_t tdr_qlen:16;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} tdc_tdr_qlen_t;
+
+
+/*
+ * Register: TdcRtabPtr
+ * RTAB pointers
+ * Description: Status of the reorder table pointers Writing to this
+ * register is for debug purposes only and is enabled when vnmDbgOn
+ * is set to 1
+ * Fields:
+ * Current rtab head pointer, used in the txPkt block This
+ * register is used to dequeue entries in the reorder table when
+ * packets are sent out
+ * Current rtab head pointer, used in the pktResp block This
+ * register is used to scan entries in the reorder table when
+ * packet data response completions arrive
+ * Current rtab tail pointer, used in the pktReq block This
+ * register is used to allocate entries in the reorder table when
+ * packet data requests are made
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:24;
+ uint64_t pkt_rtab_head:8;
+ uint64_t rsrvd1:7;
+ uint64_t rtab_head:9;
+ uint64_t rsrvd2:7;
+ uint64_t rtab_tail:9;
+#else
+ uint64_t rtab_tail:9;
+ uint64_t rsrvd2:7;
+ uint64_t rtab_head:9;
+ uint64_t rsrvd1:7;
+ uint64_t pkt_rtab_head:8;
+ uint64_t rsrvd:24;
+#endif
+ } bits;
+} tdc_rtab_ptr_t;
+
+
+/*
+ * Register: TdcDropCnt
+ * Packet Drop Counter
+ * Description: Counts the number of runt, aborted and size
+ * mismatched packets dropped by the tx datapath block.
+ * Fields:
+ * Number of dropped due to pktSizeHdrErr. This counter will
+ * saturate. This counter is cleared on read.
+ * Number of dropped due to packet abort bit being set. Many
+ * different error events could be the source of packet abort
+ * drop. Descriptor-related error events include those errors
+ * encountered while in the middle of processing a packet
+ * request: 1. unexpectedSop; 2. non-SOP descriptor parity error
+ * (prefParErr); 3. ran out of non-SOP descriptors due to peu
+ * response errors (tdrPrefCplTo or peuRespErr) or the channel
+ * being disabled before the TDR request can be made. Packet
+ * response errors encountered while in the middle of processing
+ * a packet request also can trigger the packet abort: 4. packet
+ * response did not return due to peu response errors ( pktCplTo
+ * or peuRespErr); 5. Rtab parity error (reordTblParErr). This
+ * counter will saturate. This counter is cleared on read. Note
+ * that packet aborts are not counted until the packet is cleared
+ * from the RTab, which may be an arbitrary amount of time after
+ * the corresponding error is logged in TdcStat. In most cases,
+ * this will occur before the channel is quiesced following
+ * channel disable. In an extreme case such as if a parity error
+ * on an EOP descriptor prevents recognition of the EOP, it is
+ * possible that the quiescent bit itself will not be set
+ * although the packet drop counter will be incremented.
+ * Number of dropped due to runt packet size error. This counter
+ * will saturate. This counter is cleared on read.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:40;
+ uint64_t hdr_size_error_count:8;
+ uint64_t abort_count:8;
+ uint64_t runt_count:8;
+#else
+ uint64_t runt_count:8;
+ uint64_t abort_count:8;
+ uint64_t hdr_size_error_count:8;
+ uint64_t rsrvd:40;
+#endif
+ } bits;
+} tdc_drop_cnt_t;
+
+
+/*
+ * Register: TdcLastPktRbufPtrs
+ * Last Packet RBUF Pointers
+ * Description: Logs the RBUF head and tail pointer of the last
+ * packet sent by the tx datapath block.
+ * Fields:
+ * Logs the RBUF tail pointer of the last packet sent
+ * Logs the RBUF head pointer of the last packet sent
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:36;
+ uint64_t rbuf_tail_ptr:12;
+ uint64_t rsrvd1:4;
+ uint64_t rbuf_head_ptr:12;
+#else
+ uint64_t rbuf_head_ptr:12;
+ uint64_t rsrvd1:4;
+ uint64_t rbuf_tail_ptr:12;
+ uint64_t rsrvd:36;
+#endif
+ } bits;
+} tdc_last_pkt_rbuf_ptrs_t;
+
+
+/*
+ * Register: TdcPrefCmd
+ * Tx DMA Prefetch Buffer Command
+ * Description: Allows debug access to the entire prefetch buffer.
+ * For writes, software writes the tdcPrefData and tdcPrefParData
+ * registers, before writing the tdcPrefCmd register. For reads,
+ * software writes the tdcPrefCmd register, then reads the
+ * tdcPrefData and tdcPrefParData registers. The valid field should
+ * be polled by software until it goes low, indicating the read or
+ * write has completed. Writing the tdcPrefCmd triggers the access.
+ * Fields:
+ * status of indirect access 0=busy 1=done
+ * Command type. 1 indicates a read command, 0 a write command.
+ * enable writing of parity bits 1=enabled, 0=disabled
+ * DMA channel of entry to read or write
+ * Entry in the prefetch buffer to read or write
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t status:1;
+ uint64_t cmd:1;
+ uint64_t par_en:1;
+ uint64_t rsrvd1:23;
+ uint64_t dmc:2;
+ uint64_t entry:4;
+#else
+ uint64_t entry:4;
+ uint64_t dmc:2;
+ uint64_t rsrvd1:23;
+ uint64_t par_en:1;
+ uint64_t cmd:1;
+ uint64_t status:1;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} tdc_pref_cmd_t;
+
+
+/*
+ * Register: TdcPrefData
+ * Tx DMA Prefetch Buffer Data
+ * Description: See tdcPrefCmd register.
+ * Fields:
+ * For writes, data which is written into prefetch buffer. For
+ * reads, data read from the prefetch buffer.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t data:64;
+#else
+ uint64_t data:64;
+#endif
+ } bits;
+} tdc_pref_data_t;
+
+
+/*
+ * Register: TdcPrefParData
+ * Tx DMA Prefetch Buffer Parity Data
+ * Description: See tdcPrefCmd register.
+ * Fields:
+ * For writes, parity data which is written into prefetch buffer.
+ * For reads, parity data read from the prefetch buffer.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:56;
+ uint64_t par_data:8;
+#else
+ uint64_t par_data:8;
+ uint64_t rsrvd:56;
+#endif
+ } bits;
+} tdc_pref_par_data_t;
+
+
+/*
+ * Register: TdcReordBufCmd
+ * Tx DMA Reorder Buffer Command
+ * Description: Allows debug access to the entire Reorder buffer. For
+ * writes, software writes the tdcReordBufData and tdcReordBufEccData
+ * before writing the tdcReordBufCmd register. For reads, software
+ * writes the tdcReordBufCmd register, then reads the tdcReordBufData
+ * and tdcReordBufEccData registers. The valid field should be polled
+ * by software until it goes low, indicating the read or write has
+ * completed. Writing the tdcReordBufCmd triggers the access.
+ * Fields:
+ * status of indirect access 0=busy 1=done
+ * Command type. 1 indicates a read command, 0 a write command.
+ * enable writing of ecc bits 1=enabled, 0=disabled
+ * Entry in the reorder buffer to read or write
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t status:1;
+ uint64_t cmd:1;
+ uint64_t ecc_en:1;
+ uint64_t rsrvd1:17;
+ uint64_t entry:12;
+#else
+ uint64_t entry:12;
+ uint64_t rsrvd1:17;
+ uint64_t ecc_en:1;
+ uint64_t cmd:1;
+ uint64_t status:1;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} tdc_reord_buf_cmd_t;
+
+
+/*
+ * Register: TdcReordBufData
+ * Tx DMA Reorder Buffer Data
+ * Description: See tdcReordBufCmd register.
+ * Fields:
+ * For writes, data which is written into reorder buffer. For
+ * reads, data read from the reorder buffer.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t data:64;
+#else
+ uint64_t data:64;
+#endif
+ } bits;
+} tdc_reord_buf_data_t;
+
+
+/*
+ * Register: TdcReordBufEccData
+ * Tx DMA Reorder Buffer ECC Data
+ * Description: See tdcReordBufCmd register.
+ * Fields:
+ * For writes, ecc data which is written into reorder buffer. For
+ * reads, ecc data read from the reorder buffer.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:56;
+ uint64_t ecc_data:8;
+#else
+ uint64_t ecc_data:8;
+ uint64_t rsrvd:56;
+#endif
+ } bits;
+} tdc_reord_buf_ecc_data_t;
+
+
+/*
+ * Register: TdcReordTblCmd
+ * Tx DMA Reorder Table Command
+ * Description: Allows debug access to the entire Reorder Table. For
+ * writes, software writes the tdcReordTblData and tdcReordTblParData
+ * before writing the tdcReordTblCmd register. For reads, software
+ * writes the tdcReordTblCmd register, then reads the tdcReordTblData
+ * and tdcReordTblParData registers. The valid field should be polled
+ * by software until it goes low, indicating the read or write has
+ * completed. Writing the tdcReordTblCmd triggers the access.
+ * Fields:
+ * status of indirect access 0=busy 1=done
+ * Command type. 1 indicates a read command, 0 a write command.
+ * enable writing of par bits 1=enabled, 0=disabled
+ * Address in the reorder table to read from or write to
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t status:1;
+ uint64_t cmd:1;
+ uint64_t par_en:1;
+ uint64_t rsrvd1:21;
+ uint64_t entry:8;
+#else
+ uint64_t entry:8;
+ uint64_t rsrvd1:21;
+ uint64_t par_en:1;
+ uint64_t cmd:1;
+ uint64_t status:1;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} tdc_reord_tbl_cmd_t;
+
+
+/*
+ * Register: TdcReordTblDataLo
+ * Tx DMA Reorder Table Data Lo
+ * Description: See tdcReordTblCmd register.
+ * Fields:
+ * For writes, data which is written into reorder table. For
+ * reads, data read from the reorder table.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t data:64;
+#else
+ uint64_t data:64;
+#endif
+ } bits;
+} tdc_reord_tbl_data_lo_t;
+
+
+/*
+ * Register: TdcReordTblDataHi
+ * Tx DMA Reorder Table Data Hi
+ * Description: See tdcReordTblCmd register.
+ * Fields:
+ * For writes, parity data which is written into reorder table.
+ * For reads, parity data read from the reorder table.
+ * For writes, data which is written into reorder table. For
+ * reads, data read from the reorder table.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:47;
+ uint64_t par_data:9;
+ uint64_t hi_data:8;
+#else
+ uint64_t hi_data:8;
+ uint64_t par_data:9;
+ uint64_t rsrvd:47;
+#endif
+ } bits;
+} tdc_reord_tbl_data_hi_t;
+
+
+/*
+ * Register: TdcPrefParLog
+ * Tx DMA Prefetch Buffer Parity Log
+ * Description: TDC DMA Prefetch Buffer parity log register This
+ * register logs the first parity error encountered. Writing a 1 to
+ * TdcStat::prefParErr clears this register and re-arms for logging
+ * the next error
+ * Fields:
+ * Address of parity error read data
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rsrvd1:26;
+ uint64_t address:6;
+#else
+ uint64_t address:6;
+ uint64_t rsrvd1:26;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} tdc_pref_par_log_t;
+
+
+/*
+ * Register: TdcReordBufEccLog
+ * Tx Reorder Buffer ECC Log
+ * Description: TDC Reorder Buffer ECC log register This register
+ * logs the first ECC error encountered. Writing a 1 to
+ * tdcFifoErrStat::reordBufDedErr or tdcFifoErrStat::reordBufSecErr
+ * clears this register and re-arms for logging
+ * Fields:
+ * Address of ECC error
+ * Syndrome of ECC error
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rsrvd1:4;
+ uint64_t address:12;
+ uint64_t rsrvd2:8;
+ uint64_t syndrome:8;
+#else
+ uint64_t syndrome:8;
+ uint64_t rsrvd2:8;
+ uint64_t address:12;
+ uint64_t rsrvd1:4;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} tdc_reord_buf_ecc_log_t;
+
+
+/*
+ * Register: TdcReordTblParLog
+ * Tx Reorder Table Parity Log
+ * Description: TDC Reorder Table parity log register This register
+ * logs the first parity error encountered. Writing a 1 to
+ * tdcFifoErrStat::reordTblParErr clears this register and re-arms
+ * for logging
+ * Fields:
+ * Address of parity error
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rsrvd1:24;
+ uint64_t address:8;
+#else
+ uint64_t address:8;
+ uint64_t rsrvd1:24;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} tdc_reord_tbl_par_log_t;
+
+
+/*
+ * Register: TdcFifoErrMask
+ * FIFO Error Mask
+ * Description: FIFO Error Mask register. Mask status of Reorder
+ * Buffer and Reorder Table Buffer Errors.
+ * Fields:
+ * Set to 0 to select the event to raise the LDF to indicate
+ * reorder table ram received a parity error An Device Error 1
+ * event.
+ * Set to 0 to select the event to raise the LDF to indicate
+ * reorder buffer ram received a ecc double bit error An Device
+ * Error 1 event.
+ * Set to 0 to select the event to raise the LDF to indicate
+ * reorder buffer ram received a ecc single bit error An Device
+ * Error 0 event.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:61;
+ uint64_t reord_tbl_par_err:1;
+ uint64_t reord_buf_ded_err:1;
+ uint64_t reord_buf_sec_err:1;
+#else
+ uint64_t reord_buf_sec_err:1;
+ uint64_t reord_buf_ded_err:1;
+ uint64_t reord_tbl_par_err:1;
+ uint64_t rsrvd:61;
+#endif
+ } bits;
+} tdc_fifo_err_mask_t;
+
+
+/*
+ * Register: TdcFifoErrStat
+ * FIFO Error Status
+ * Description: FIFO Error Status register. Log status of Reorder
+ * Buffer and Reorder Table Buffer Errors.
+ * Fields:
+ * Set to 1 by HW to indicate reorder table ram received a parity
+ * error Writing a 1 clears this bit and also clears the
+ * TdcReordTblParLog register Fatal error. Part of Device Error
+ * 1.
+ * Set to 1 by HW to indicate reorder buffer ram received a
+ * double bit ecc error Writing a 1 clears this bit and also
+ * clears the tdcReordBufEccLog register Fatal error. Part of
+ * Device Error 1.
+ * Set to 1 by HW to indicate reorder buffer ram received a
+ * single bit ecc error Writing a 1 clears this bit and also
+ * clears the tdcReordBufEccLog register Non-Fatal error. Part of
+ * Device Error 0.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:61;
+ uint64_t reord_tbl_par_err:1;
+ uint64_t reord_buf_ded_err:1;
+ uint64_t reord_buf_sec_err:1;
+#else
+ uint64_t reord_buf_sec_err:1;
+ uint64_t reord_buf_ded_err:1;
+ uint64_t reord_tbl_par_err:1;
+ uint64_t rsrvd:61;
+#endif
+ } bits;
+} tdc_fifo_err_stat_t;
+
+
+/*
+ * Register: TdcFifoErrIntDbg
+ * FIFO Error Interrupt Debug
+ * Description: FIFO Error Interrupt Debug register. Write this
+ * regsiter to set bits in TdcFifoErrStat, allowing debug creation of
+ * interrupts without needing to create the actual events. This
+ * register holds no state. Reading this register gives the Tdc Fifo
+ * Err Status data. Clear interrupt state by clearing TdcFifoErrStat.
+ * For Debug only
+ * Fields:
+ * Set to 1 to select the event to raise the LDF to indicate
+ * reorder table ram received a parity error An Device Error 1
+ * event.
+ * Set to 1 to select the event to raise the LDF to indicate
+ * reorder buffer ram received a ecc double bit error An Device
+ * Error 1 event.
+ * Set to 1 to select the event to raise the LDF to indicate
+ * reorder buffer ram received a ecc single bit error An Device
+ * Error 0 event.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:61;
+ uint64_t reord_tbl_par_err:1;
+ uint64_t reord_buf_ded_err:1;
+ uint64_t reord_buf_sec_err:1;
+#else
+ uint64_t reord_buf_sec_err:1;
+ uint64_t reord_buf_ded_err:1;
+ uint64_t reord_tbl_par_err:1;
+ uint64_t rsrvd:61;
+#endif
+ } bits;
+} tdc_fifo_err_int_dbg_t;
+
+
+/*
+ * Register: TdcStatIntDbg
+ * Transmit Status Interrupt Debug
+ * Description: Write this regsiter to set bits in TdcStat, allowing
+ * debug creation of interrupts without needing to create the actual
+ * events. This register holds no state. Reading this register gives
+ * the Transmit Control and Status data. Clear interrupt state by
+ * clearing TdcStat. For Debug only
+ * Fields:
+ * Set to 1 to select the event to raise the LDF for packets
+ * marked. An LDF 0 event.
+ * Set to 1 to select the event to raise the LDF when poisoned
+ * completion or non-zero (unsuccessful) completion status
+ * received from PEU. An LDF 1 event.
+ * Set to 1 to select the event to raise the LDF when total bytes
+ * transmitted compared against pkt internal header bytes
+ * transmitted mismatch. An LDF 1 event.
+ * Set to 1 to select the event to raise the LDF when a runt
+ * packet is dropped (when VMAC does not allow runt packets to be
+ * padded). An LDF 1 event.
+ * Set to 1 to select the event to raise the LDF when the packet
+ * size exceeds hardware limit. An LDF 1 event.
+ * Set to 1 to select the event to raise the LDF to indicate
+ * Transmit Ring Overflow An LDF 1 event.
+ * Set to 1 to select the event to raise the LDF to indicate
+ * parity error on the tdr prefetch buffer occurred. An LDF 1
+ * event.
+ * Set to 1 to select the event to raise the LDF to indicate tdc
+ * received a response completion timeout from peu for tdr
+ * descriptor prefetch An LDF 1 event.
+ * Set to 1 to select the event to raise the LDF to indicate tdc
+ * received a response completion timeout from peu for packet
+ * data request An LDF 1 event.
+ * Set to 1 to select the event to raise the LDF to indicate tdc
+ * did not receive an SOP in the 1st descriptor as was expected
+ * or the numPtr in the 1st descriptor was set to 0. An LDF 1
+ * event.
+ * Set to 1 to select the event to raise the LDF to indicate tdc
+ * received an unexpected SOP descriptor error. An LDF 1 event.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:48;
+ uint64_t marked:1;
+ uint64_t rsrvd1:5;
+ uint64_t peu_resp_err:1;
+ uint64_t pkt_size_hdr_err:1;
+ uint64_t runt_pkt_drop_err:1;
+ uint64_t pkt_size_err:1;
+ uint64_t tx_rng_oflow:1;
+ uint64_t pref_par_err:1;
+ uint64_t tdr_pref_cpl_to:1;
+ uint64_t pkt_cpl_to:1;
+ uint64_t invalid_sop:1;
+ uint64_t unexpected_sop:1;
+#else
+ uint64_t unexpected_sop:1;
+ uint64_t invalid_sop:1;
+ uint64_t pkt_cpl_to:1;
+ uint64_t tdr_pref_cpl_to:1;
+ uint64_t pref_par_err:1;
+ uint64_t tx_rng_oflow:1;
+ uint64_t pkt_size_err:1;
+ uint64_t runt_pkt_drop_err:1;
+ uint64_t pkt_size_hdr_err:1;
+ uint64_t peu_resp_err:1;
+ uint64_t rsrvd1:5;
+ uint64_t marked:1;
+ uint64_t rsrvd:48;
+#endif
+ } bits;
+} tdc_stat_int_dbg_t;
+
+
+/*
+ * Register: TdcPktReqTidTag
+ * Packet Request TID Tag
+ * Description: Packet Request TID Tag register Track the packet
+ * request TID currently used
+ * Fields:
+ * When set to 1, it indicates the TID is currently being used
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t pkt_req_tid_tag:24;
+ uint64_t rsrvd1:8;
+#else
+ uint64_t rsrvd1:8;
+ uint64_t pkt_req_tid_tag:24;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} tdc_pkt_req_tid_tag_t;
+
+
+/*
+ * Register: TdcSopPrefDescLog
+ * SOP Prefetch Descriptor Log
+ * Description: SOP Descriptor Log register Logs the last SOP
+ * prefetch descriptor processed by the packet request block. This
+ * log could represent the current SOP prefetch descriptor if the
+ * packet request block did not complete issuing the data requests
+ * from this descriptor. Descriptors are logged to this register when
+ * the packet request block is expecting an SOP descriptor, and it
+ * receives it.
+ * Fields:
+ * Represents the last or current SOP descriptor being processed
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t sop_pref_desc_log:64;
+#else
+ uint64_t sop_pref_desc_log:64;
+#endif
+ } bits;
+} tdc_sop_pref_desc_log_t;
+
+
+/*
+ * Register: TdcPrefDescLog
+ * Prefetch Descriptor Log
+ * Description: SOP Descriptor Log register Logs the last prefetch
+ * descriptor processed by the packet request block. This log could
+ * represent the current prefetch descriptor if the packet request
+ * block did not complete issuing the data requests from this
+ * descriptor. The contents in this register could differ from the
+ * SOP Prefetch Descriptor Log register if a particular packet
+ * requires usage of more than 1 descriptor. Descriptors are logged
+ * to this register when the packet request block is expecting a
+ * descriptor after the SOP descriptor.
+ * Fields:
+ * Represents the last or current descriptor being processed
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t pref_desc_log:64;
+#else
+ uint64_t pref_desc_log:64;
+#endif
+ } bits;
+} tdc_pref_desc_log_t;
+
+
+/*
+ * Register: TdcPeuTxnLog
+ * PEU Transaction Log
+ * Description: PEU Transaction Log register. Counts the memory read
+ * and write requests sent to peu block. For debug only.
+ * Fields:
+ * Counts the memory write transactions sent to peu block. This
+ * counter saturates. This counter increments when vnmDbg is on
+ * Counts the memory read transactions sent to peu block. This
+ * counter saturates. This counter increments when vnmDbg is on
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rsrvd1:16;
+ uint64_t peu_mem_wr_count:8;
+ uint64_t peu_mem_rd_count:8;
+#else
+ uint64_t peu_mem_rd_count:8;
+ uint64_t peu_mem_wr_count:8;
+ uint64_t rsrvd1:16;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} tdc_peu_txn_log_t;
+
+
+/*
+ * Register: TdcDbgTrainingVec
+ * Debug Training Vector
+ * Description: Debug Training Vector register. Debug Training Vector
+ * for the coreClk domain. For the pcieClk domain, the dbgxMsb and
+ * dbgyMsb values are flipped on the debug bus.
+ * Fields:
+ * Blade Number, the value read depends on the blade this block
+ * resides
+ * debug training vector the sub-group select value of 0 selects
+ * this vector
+ * Blade Number, the value read depends on the blade this block
+ * resides
+ * debug training vector the sub-group select value of 0 selects
+ * this vector
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t dbgx_msb:1;
+ uint64_t dbgx_bld_num:3;
+ uint64_t dbgx_training_vec:12;
+ uint64_t dbgy_msb:1;
+ uint64_t dbgy_bld_num:3;
+ uint64_t dbgy_training_vec:12;
+#else
+ uint64_t dbgy_training_vec:12;
+ uint64_t dbgy_bld_num:3;
+ uint64_t dbgy_msb:1;
+ uint64_t dbgx_training_vec:12;
+ uint64_t dbgx_bld_num:3;
+ uint64_t dbgx_msb:1;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} tdc_dbg_training_vec_t;
+
+
+/*
+ * Register: TdcDbgGrpSel
+ * Debug Group Select
+ * Description: Debug Group Select register. Debug Group Select
+ * register selects the group of signals brought out on the debug
+ * port
+ * Fields:
+ * high 32b sub-group select
+ * low 32b sub-group select
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:48;
+ uint64_t rsrvd1:1;
+ uint64_t dbg_h32_sub_sel:7;
+ uint64_t rsrvd2:1;
+ uint64_t dbg_l32_sub_sel:7;
+#else
+ uint64_t dbg_l32_sub_sel:7;
+ uint64_t rsrvd2:1;
+ uint64_t dbg_h32_sub_sel:7;
+ uint64_t rsrvd1:1;
+ uint64_t rsrvd:48;
+#endif
+ } bits;
+} tdc_dbg_grp_sel_t;
+
+
+#endif /* _HXGE_TDC_HW_H */
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#include "hpi/hpi_txdma.h"
+#include "hxge.h"
+
+/* failure return codes for map_skbdata_to_descs and copy_skbdata_to_descs
+ * respectively */
+#define MAP_DESC_FAILED -1
+#define COPY_DESC_FAILED -2
+
+extern int hxge_ok_to_continue(struct hxge_adapter *hxgep);
+extern int hxge_block_reset(struct hxge_adapter *hxgep, int device);
+extern void hxge_disable_interrupts(struct hxge_adapter *hxgep);
+
+
+static int start_reclaim_thread(struct tx_ring_t *tx_ring);
+static int stop_reclaim_thread(struct tx_ring_t *tx_ring);
+
+#ifdef CONFIG_ERRINJECT
+
+#define FREE_SKB hxge_free_skb
+
+static atomic_t skb_count = ATOMIC_INIT(0);
+void hxge_free_skb(struct sk_buff *skb)
+{
+ atomic_dec(&skb_count);
+ dev_kfree_skb_any(skb);
+}
+
+
+#else
+#define FREE_SKB dev_kfree_skb_any
+#endif
+
+
+/* Program the Tx registers in hardware to get it ready for transmit. The
+ * enabling of the channel is done later
+ */
+static int hxge_map_tx_to_hw(struct hxge_adapter *hxgep, int channel)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ struct tx_ring_t *tx_ring = &hxgep->tx_ring[channel];
+ tdc_tdr_cfg_t tdr_cfg;
+ tdc_int_mask_t ev_mask;
+
+ /* Reset already completed, so don't need to set that bit. Set the
+ DMA address of the Tx descriptor ring. Also the entries in the
+ ring */
+ tdr_cfg.value = 0;
+ tdr_cfg.value = tx_ring->desc_ring.dma_addr & TDC_TDR_CFG_ADDR_MASK;
+ tdr_cfg.bits.len = tx_ring->num_tdrs >> 5;
+ if (hpi_txdma_ring_config(handle, OP_SET, channel, &tdr_cfg.value) !=
+ HPI_SUCCESS)
+ {
+ HXGE_ERR(hxgep, "hpi_txdma_ring_config failed");
+ return -1;
+ }
+
+ /* Write the mailbox register */
+ if (hpi_txdma_mbox_config(handle, OP_SET, channel,
+ &tx_ring->mbox.dma_addr) != HPI_SUCCESS)
+ {
+ HXGE_ERR(hxgep, "hpi_txdma_mbox_config failed");
+ return -1;
+ }
+
+ /* Setup the transmit event mask */
+ ev_mask.value = 0;
+
+ /* CR 6678180 workaround - Mask Tx ring overflow to avoid getting
+ * false overflow interrupts
+ */
+ ev_mask.bits.tx_rng_oflow = 1;
+ if (hpi_txdma_event_mask(handle, OP_SET, channel, &ev_mask) !=
+ HPI_SUCCESS)
+ {
+ HXGE_ERR(hxgep, "hpi_txdma_event_mask failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* Assumes that the skb->csum is prep'ed for hw checksumming. This routine
+ * just completes what the hardware would have done. There is here as a
+ * workaround for a hw checksum bug
+*/
+static int fixup_checksum(struct sk_buff *skb
+#ifdef CONFIG_ERRINJECT
+ , struct hxge_adapter *hxgep
+#endif
+)
+
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+ unsigned int csum;
+ int ret = 0;
+ int offset = skb->h.raw - skb->data;
+
+ if (skb_cloned(skb)) {
+ ret = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+ if (ret)
+ goto out;
+ }
+
+ BUG_ON(offset > (int)skb->len);
+ csum = skb_checksum(skb, offset, skb->len-offset, 0);
+
+ offset = skb->tail - skb->h.raw;
+ BUG_ON(offset <= 0);
+ BUG_ON(skb->csum + 2 > offset);
+
+ *(u16*)(skb->h.raw + skb->csum) = csum_fold(csum);
+#ifdef CONFIG_ERRINJECT
+ if (hxgep->err_flags & CHKSUM_FAILURE) {
+ HXGE_ERR(hxgep, "Injecting checksum error");
+ *(u16*)(skb->h.raw + skb->csum) = 0;
+ }
+#endif
+#else
+ __wsum csum;
+ int ret = 0, offset;
+
+ offset = skb->csum_start - skb_headroom(skb);
+ BUG_ON(offset >= skb_headlen(skb));
+ csum = skb_checksum(skb, offset, skb->len - offset, 0);
+
+ offset += skb->csum_offset;
+ BUG_ON(offset + sizeof(__sum16) > skb_headlen(skb));
+
+ if (skb_cloned(skb) &&
+ !skb_clone_writable(skb, offset + sizeof(__sum16))) {
+ ret = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+ if (ret)
+ goto out;
+ }
+ *(__sum16 *)(skb->data + offset) = csum_fold(csum);
+#ifdef CONFIG_ERRINJECT
+ if (hxgep->err_flags & CHKSUM_FAILURE) {
+ HXGE_ERR(hxgep, "Injecting checksum error");
+ *(__sum16 *)(skb->data + offset) = 0;
+ }
+#endif
+#endif
+out:
+ return ret;
+}
+
+
+/* This routine initializes the data structures specific to a channel.
+ * Additionally, it initializes the tx channel related registers in the
+ * hydra hardware
+ */
+
+static int hxge_init_tx_channel(struct hxge_adapter *hxgep, int channel)
+{
+ struct tx_ring_t *tx_ring = &hxgep->tx_ring[channel];
+ int num_tdrs = tx_ring->num_tdrs;
+ int buffer_size = tx_ring->tx_buffer_size, i;
+ struct tx_buf_t *tx_buf;
+ dma_addr_t dma_addr;
+ caddr_t vaddr;
+
+ HXGE_DBG(hxgep, "Calling hxge_init_tx_channel for channel %d",channel);
+
+ /* initialize the Tx descriptor ring */
+ memset(tx_ring->desc_ring.vaddr, 0, sizeof(tx_desc_t)*num_tdrs);
+ HXGE_DBG(hxgep, "Initializing Tx Descriptor ring at 0x%p",tx_ring->desc_ring.vaddr);
+
+ /* Initialize the Tx buffers */
+ //memset(tx_ring->data_buf.vaddr, 0, sizeof(struct tx_buf_t)*num_tdrs);
+
+ HXGE_DBG(hxgep, "Initializing Tx Buffers for headers");
+ dma_addr = tx_ring->data_buf.dma_addr;
+ vaddr = tx_ring->data_buf.vaddr;
+ for (i = 0, tx_buf=tx_ring->tx_buf; i < num_tdrs;
+ i++, tx_buf++, dma_addr += buffer_size, vaddr += buffer_size) {
+ tx_buf->map.dma_addr = dma_addr;
+ tx_buf->map.vaddr = vaddr;
+ tx_buf->flags = TX_FLAGS_UNUSED;
+ }
+
+ /* Entire ring available at start */
+ atomic_set(&tx_ring->descs_avail, num_tdrs);
+ tx_ring->tail = 0;
+ tx_ring->wrap = FALSE; /* really does not matter since it's a toggle */
+ tx_ring->reclaim_head = 0;
+ tx_ring->reclaim_wrap = FALSE;
+ tx_ring->hxgep = hxgep;
+ tx_ring->mark_ints = 0;
+
+ /* Initialize the mailbox */
+ memset(tx_ring->mbox.vaddr, 0, sizeof(txdma_mailbox_t));
+
+ hxge_map_tx_to_hw(hxgep, channel);
+
+ /* Start the reclaim thread */
+ tx_ring->thread_pid = -1;
+ start_reclaim_thread(tx_ring);
+ return 0;
+}
+
+/* Caller must have the Tx ring lock */
+static int reclaim_tx_ring(struct hxge_adapter *hxgep,
+ struct tx_ring_t *tx_ring, tdc_tdr_head_t head_reg)
+{
+ int descs_processed = 0;
+ boolean_t head_wrap;
+ int head_index;
+ struct tx_buf_t *tx_buf;
+ int wrapped;
+ unsigned long start_time = jiffies;
+ int full;
+
+ head_wrap = head_reg.bits.wrap;
+ head_index = head_reg.bits.head;
+ wrapped = (head_wrap != tx_ring->reclaim_wrap);
+ full = ((head_index == tx_ring->reclaim_head) && wrapped);
+
+
+ if (head_index >= tx_ring->num_tdrs) {
+ HXGE_ERR(hxgep, "head_index exceeds ring size! (head_index = 0x%x, Tx ring size = 0x%x",head_index, tx_ring->num_tdrs);
+ BUG();
+ }
+
+ if (full && atomic_read(&tx_ring->descs_avail)) {
+ HXGE_ERR(hxgep, "discrepancy in buffer mgmt: hd=0x%x,rhd=0x%x, hwrp=%d, rwrp=%d, descs_avail=%d",head_index, head_wrap, tx_ring->reclaim_head, tx_ring->reclaim_wrap, atomic_read(&tx_ring->descs_avail));
+ BUG();
+ }
+
+ while (!time_after(jiffies, start_time+800) &&
+ ((tx_ring->reclaim_head != head_index) || wrapped))
+ {
+ wrapped = 0;
+ tx_buf = &tx_ring->tx_buf[tx_ring->reclaim_head];
+ if (tx_buf->flags & TX_FLAGS_UNMAP) /* mapped data */ {
+ int len;
+ HXGE_DBG(hxgep, "Unmapping @(%p,%d)",(char *)tx_buf->map.dma_addr,tx_buf->map.len);
+ len = (tx_buf->flags & TX_FLAGS_ALLOC) ? PAGE_SIZE :
+ tx_buf->map.len;
+ pci_unmap_page(hxgep->pdev, tx_buf->map.dma_addr,
+ len, PCI_DMA_TODEVICE);
+ }
+
+ if (tx_buf->flags & TX_FLAGS_ALLOC) {
+ HXGE_DBG(hxgep, "Freeing tx_buf->map.vaddr@%p of len %d (channel %d)",tx_buf->map.vaddr, (int)PAGE_SIZE, tx_ring->tdc);
+ free_page((unsigned long)tx_buf->map.vaddr);
+ }
+
+ tx_buf->map.vaddr = 0;
+ tx_buf->map.dma_addr = 0;
+ tx_buf->map.len = 0;
+
+ if (tx_buf->skb) {
+ /* free the header array for gso */
+ if (SKB_IS_GSO(tx_buf->skb)) {
+ int i;
+ struct skb_hdr_info_t *skb_hdr =
+ (struct skb_hdr_info_t *)tx_buf->skb->cb;
+ struct pci_dma_map_t *pci_map = &skb_hdr->pci_map;
+ pci_free_consistent(hxgep->pdev,
+ skb_hdr->hdr_array.len,
+ skb_hdr->hdr_array.vaddr,
+ skb_hdr->hdr_array.dma_addr);
+
+
+ if (pci_map->num_dma_mappings) {
+ for (i = 0;
+ i < pci_map->num_dma_mappings;
+ i++)
+ pci_unmap_page(hxgep->pdev,
+ pci_map->dma_map[i].dma_addr,
+ pci_map->dma_map[i].len,
+ PCI_DMA_TODEVICE);
+ kfree(pci_map->dma_map);
+ }
+ }
+ FREE_SKB(tx_buf->skb);
+ tx_buf->skb = NULL;
+ }
+ tx_buf->flags = TX_FLAGS_UNUSED;
+ tx_ring->reclaim_head = (tx_ring->reclaim_head + 1) %
+ tx_ring->num_tdrs;
+ if (!tx_ring->reclaim_head) {
+ if (tx_ring->reclaim_wrap == TRUE)
+ tx_ring->reclaim_wrap = FALSE;
+ else
+ tx_ring->reclaim_wrap = TRUE;
+ }
+ descs_processed++;
+ }
+
+ atomic_add(descs_processed, &tx_ring->descs_avail);
+ if (netif_queue_stopped(hxgep->netdev))
+ netif_wake_queue(hxgep->netdev);
+
+ return descs_processed;
+}
+
+static int entries_to_process(struct hxge_adapter *hxgep,
+ struct tx_ring_t *tx_ring, tdc_tdr_head_t *head_reg)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ int head_index;
+ boolean_t head_wrap;
+
+ if (hpi_txdma_ring_head_get(handle, tx_ring->tdc, head_reg)
+ != HPI_SUCCESS)
+ {
+ HXGE_ERR(hxgep, "hpi_txdma_ring_head_get_failed for channel %d",tx_ring->tdc);
+ return -1;
+ }
+ head_index = head_reg->bits.head;
+ head_wrap = (head_reg->bits.wrap) ? TRUE : FALSE;
+ if (((head_index != tx_ring->reclaim_head) ||
+ (head_wrap != tx_ring->reclaim_wrap)))
+ return 1;
+
+ return 0;
+}
+
+/* When killing the reclaim thread (either due to interface bringdown or
+ * driver unload), make sure that all pending Tx descriptors have been sent
+ * before stopping the Tx channel
+ */
+int wait_till_empty(struct hxge_adapter *hxgep, struct tx_ring_t *tx_ring)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ int hindex;
+ boolean_t hwrap;
+ tdc_tdr_head_t head_reg;
+ int count = 5;
+
+ do {
+ msleep(1);
+ hpi_txdma_ring_head_get(handle, tx_ring->tdc, &head_reg);
+ hindex = head_reg.bits.head;
+ hwrap = head_reg.bits.wrap;
+ } while(!TXDMA_RING_EMPTY(hindex, hwrap, tx_ring->tail,tx_ring->wrap)
+ && --count);
+
+ if (!count) {
+ HXGE_ERR(hxgep, "Pending Tx Descriptors not all sent out!");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+/* There is one kernel thread per Tx channel to reclaim unused Tx descriptor
+ * rings for use by the Tx channel. The thread periodically wakes up to
+ * do reclaim work. Otherwise, it is woken up immediately when a Tx completes
+ * (hxge_tx_intr) when there is reclaim work for sure. It is also woken up
+ * if there is high transmit traffic and an immediate need to free up
+ * space (hxge_start_xmit)
+ */
+
+static int reclaim_thread(void *data) {
+ struct tx_ring_t *tx_ring = data;
+ struct hxge_adapter *hxgep = tx_ring->hxgep;
+ int ret_val;
+ tdc_tdr_head_t head_reg;
+
+
+ daemonize("tx_reclaim_%d", tx_ring->tdc);
+ while (1) {
+
+ wait_event_interruptible_timeout(tx_ring->reclaim_event,
+ (ret_val = entries_to_process(hxgep, tx_ring,&head_reg)),
+ RECLAIM_TIMEOUT);
+
+ if (ret_val < 0)
+ break;
+
+ /* Ready to get killed. make sure that any outstanding
+ * descriptors are freed before leaving; otherwise we
+ * can be leaking skbs
+ */
+ if (tx_ring->kill_reclaim == TRUE) {
+ wait_till_empty(hxgep, tx_ring);
+ reclaim_tx_ring(hxgep, tx_ring, head_reg);
+ break;
+ }
+
+ /* Try to get the lock. If already taken, probably initializing
+ the channel due to some error. Return and try later */
+ if (test_and_set_bit(RING_RECLAIM, &tx_ring->state))
+ continue;
+
+ reclaim_tx_ring(hxgep, tx_ring, head_reg);
+ clear_bit(RING_RECLAIM, &tx_ring->state);
+ }
+
+ while (tx_ring->kill_reclaim == FALSE);
+ complete(&tx_ring->reclaim_complete);
+ return 0;
+
+}
+
+static int start_reclaim_thread(struct tx_ring_t *tx_ring)
+{
+ tx_ring->kill_reclaim = FALSE;
+ init_completion(&tx_ring->reclaim_complete);
+ init_waitqueue_head(&tx_ring->reclaim_event);
+ tx_ring->thread_pid = kernel_thread(reclaim_thread, tx_ring, CLONE_FS|CLONE_FILES);
+ if (tx_ring->thread_pid < 0) {
+ HXGE_ERR_PRINT("Failed to start kernel thread");
+ return -1;
+ }
+ HXGE_DBG_PRINT("reclaim thread %d started", tx_ring->tdc);
+ return 0;
+}
+
+static int stop_reclaim_thread(struct tx_ring_t *tx_ring)
+{
+ tx_ring->kill_reclaim = TRUE;
+ wake_up_interruptible(&tx_ring->reclaim_event);
+ wait_for_completion(&tx_ring->reclaim_complete);
+ tx_ring->thread_pid = -1;
+ HXGE_DBG_PRINT("reclaim thread %d killed", tx_ring->tdc);
+ return 0;
+}
+
+
+
+/* Allocate data structures to manage the transmit channel including calling
+ * the routine that intializes the HW registers specific to this transmit
+ * channel
+ */
+static int hxge_alloc_tx_channel(struct hxge_adapter *hxgep, int channel)
+{
+ struct tx_ring_t *tx_ring = &hxgep->tx_ring[channel];
+ uint32_t size;
+ int num_tdrs;
+
+ spin_lock_init(&tx_ring->lock);
+ HXGE_DBG(hxgep, "Calling hxge_alloc_tx_channel for channel %d", channel);
+ if (hxge_get_option("num_tx_descs", &num_tdrs)) {
+ HXGE_ERR(hxgep, "invalid value for num_tx_descriptors");
+ return -1;
+ }
+
+ tx_ring->num_tdrs = num_tdrs;
+ tx_ring->hxgep = hxgep;
+
+ if (hxge_get_option("tx_buffer_size", &tx_ring->tx_buffer_size)) {
+ HXGE_ERR(hxgep, "invalid tx_buffer_size");
+ return -1;
+ }
+
+ tx_ring->tdc = channel;
+ tx_ring->desc_ring.vaddr = pci_alloc_consistent(hxgep->pdev,
+ sizeof(tx_desc_t)*tx_ring->num_tdrs,
+ &tx_ring->desc_ring.dma_addr);
+ if (!tx_ring->desc_ring.vaddr) {
+ HXGE_ERR(hxgep, "Could not alloate descriptor ring");
+ return -1;
+ }
+ HXGE_DBG(hxgep, "Allocated Tx Descriptor Ring at %p, %llx", tx_ring->desc_ring.vaddr, tx_ring->desc_ring.dma_addr);
+
+ if (!valid_alignment(tx_ring->data_buf.dma_addr,
+ sizeof(tx_desc_t)*tx_ring->num_tdrs, 19)) {
+ HXGE_ERR(hxgep, "Tx Descriptor Ring not aligned");
+ return -1;
+ }
+
+ /* TODO: Allocate tx buffers. All packets need to have the packet header
+ prepended to them. The skbs coming in might not have head room
+ for the packet header. So, these tx buffers are used to allocate
+ the header and then copy the skb header+data into them */
+
+ tx_ring->tx_buf = kzalloc(num_tdrs*sizeof(struct tx_buf_t), GFP_KERNEL);
+
+ if (!tx_ring->tx_buf) {
+ HXGE_ERR(hxgep, "could not allocate Tx Buffers");
+ return -1;
+ }
+ HXGE_DBG(hxgep, "Allocated Tx Buffer Array at %p", tx_ring->tx_buf);
+
+ size = tx_ring->tx_buffer_size * num_tdrs; /* in bytes */
+ tx_ring->data_buf.vaddr = pci_alloc_consistent(hxgep->pdev, size,
+ &tx_ring->data_buf.dma_addr);
+ if (!tx_ring->data_buf.vaddr) {
+ HXGE_ERR(hxgep, "could not allocate tx buffers");
+ return -1;
+ }
+ HXGE_DBG(hxgep, "Allocated Tx Data Buffers at %p", tx_ring->data_buf.vaddr);
+
+
+
+ /* Allocate mailbox */
+ tx_ring->mbox.vaddr = pci_alloc_consistent(hxgep->pdev,
+ sizeof(txdma_mailbox_t), &tx_ring->mbox.dma_addr);
+ if (!tx_ring->mbox.vaddr) {
+ HXGE_ERR(hxgep, "could not allocate mbox");
+ return -1;
+ }
+
+ return 0;
+
+}
+
+static void hxge_free_tx_channel(struct hxge_adapter *hxgep, int channel)
+{
+ struct tx_ring_t *tx_ring = &hxgep->tx_ring[channel];
+ int size;
+
+
+ if (tx_ring->desc_ring.vaddr) {
+ size = sizeof(tx_desc_t) * tx_ring->num_tdrs;
+ pci_free_consistent(hxgep->pdev, size,
+ tx_ring->desc_ring.vaddr, tx_ring->desc_ring.dma_addr);
+ }
+
+ if (tx_ring->data_buf.vaddr) {
+ size = tx_ring->tx_buffer_size * tx_ring->num_tdrs;
+ pci_free_consistent(hxgep->pdev, size,
+ tx_ring->data_buf.vaddr, tx_ring->data_buf.dma_addr);
+ }
+
+ if (tx_ring->mbox.vaddr)
+ pci_free_consistent(hxgep->pdev, sizeof(txdma_mailbox_t),
+ tx_ring->mbox.vaddr, tx_ring->mbox.dma_addr);
+
+ if (tx_ring->tx_buf)
+ kfree(tx_ring->tx_buf);
+}
+
+void hxge_free_tx(struct hxge_adapter *hxgep)
+{
+ int i;
+
+ for (i = 0; i < hxgep->max_tdcs; i++)
+ hxge_free_tx_channel(hxgep, i);
+
+ if (hxgep->tx_ring)
+ kfree(hxgep->tx_ring);
+
+ hxgep->tx_ring = NULL;
+
+#ifdef CONFIG_ERRINJECT
+ if (atomic_read(&skb_count)) {
+ HXGE_ERR(hxgep,"All SKBs have not been freed! Memory leak, skb pending = %d", atomic_read(&skb_count));
+ }
+#endif
+}
+
+int hxge_alloc_tx(struct hxge_adapter *hxgep)
+{
+ int i;
+
+ if (hxge_get_option("tx_dma_channels", &hxgep->max_tdcs)) {
+ HXGE_ERR(hxgep, "invalid value for tx_dma_channels option");
+ return -1;
+ }
+
+ hxgep->tx_ring = kzalloc(hxgep->max_tdcs*sizeof(struct tx_ring_t),
+ GFP_KERNEL);
+ if (!hxgep->tx_ring) {
+ HXGE_ERR(hxgep, "Could not allocate tx_ring");
+ return -1;
+ }
+
+ for (i = 0; i < hxgep->max_tdcs; i++)
+ if (hxge_alloc_tx_channel(hxgep, i)) {
+ HXGE_ERR(hxgep, "could not alloc tx for channel");
+ hxge_free_tx(hxgep);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int hxge_disable_tx_channel(struct hxge_adapter *hxgep, int channel)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ struct tx_ring_t *tx_ring = &hxgep->tx_ring[channel];
+
+ clear_bit(RING_ENABLED, &tx_ring->state);
+
+ /* Stop the reclaim thread */
+ if (tx_ring->thread_pid > 0)
+ stop_reclaim_thread(tx_ring);
+ else
+ HXGE_DBG(hxgep, "Reclaim thread for Tx channel %d already stopped?",channel);
+
+ /* Disable transmits through this channel */
+ if (hpi_txdma_channel_disable(handle, channel) != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_txdma_channel_disable failed");
+ }
+ HXGE_DBG(hxgep, "Channel %d disabled",channel);
+ return 0;
+}
+
+int hxge_disable_tx(struct hxge_adapter *hxgep)
+{
+ int i;
+ struct tx_ring_t *tx_ring;
+
+ hxge_disable_tx_ints(hxgep);
+
+ for (i = 0; i < hxgep->max_tdcs; i++) {
+ tx_ring = &hxgep->tx_ring[i];
+ spin_lock(&tx_ring->lock);
+ if (hxge_disable_tx_channel(hxgep, i)) {
+ HXGE_ERR(hxgep, "Could not disable channel %i",i);
+ }
+ spin_unlock(&tx_ring->lock);
+ /* Free any pending skbs that were queued up for transmission
+ but did not get a chance to be sent */
+ if (tx_ring->tx_buf) {
+ int j;
+ for (j = 0; j < tx_ring->num_tdrs; j++) {
+ struct tx_buf_t *tx_buf = &tx_ring->tx_buf[j];
+ if (tx_buf->skb) {
+ FREE_SKB(tx_buf->skb);
+ tx_buf->skb = NULL;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int hxge_enable_tx_channel(struct hxge_adapter *hxgep, int channel)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ tdc_stat_t cs;
+ struct tx_ring_t *tx_ring = &hxgep->tx_ring[channel];
+
+ /* Reset the channel */
+ hpi_txdma_channel_reset(handle, channel);
+
+ /* Explicitly set the kick register to zero because reset does not
+ do it. Set tail and wrap to zero */
+ hpi_txdma_desc_kick_reg_set(handle, channel, 0, 0);
+
+ /* Explicitly clear out the Tx Stat register; some of them are
+ RW1C types. The channel reset does not seem to do it */
+ cs.value = (u64)~0ULL;
+ hpi_txdma_control_status(handle, OP_SET, 0, &cs);
+
+ hxge_init_tx_channel(hxgep, channel);
+
+
+ /* Make sure that reclaim thread is not running when we reinitialize
+ this Tx channel. We make the assumption that the tx_ring lock
+ WILL be locked by caller except in initialiation case. So, this
+ statement means the following :
+ 1. In the initialiation case, it is doing exactly that i.e
+ initializing the lock
+ 2. In the reset channel case (or any other case for that
+ matter), it serves a unlocking the lock */
+
+ spin_lock_init(&tx_ring->lock);
+
+ /* Set the enable bit for the channel to start transmits */
+ hpi_txdma_channel_enable(handle, channel);
+ set_bit(RING_ENABLED, &tx_ring->state);
+
+ HXGE_ERR(hxgep, "Channel %d enabled",channel);
+
+ return 0;
+}
+
+/* Re-enable Tx channel (e.g., in response to LDF1 channel disable) */
+static int hxge_reenable_tx_channel(struct hxge_adapter *hxgep, int channel)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ struct tx_ring_t *tx_ring = &hxgep->tx_ring[channel];
+
+ /* The various TdcStat, kick, head, etc. registers should have
+ remained valid and should NOT need to be re-initialized.
+ We should just "resume" from whence we were "paused". */
+
+ if ((!test_bit(RING_ENABLED, &tx_ring->state))
+ || (test_bit(RING_RESET, &tx_ring->state)))
+ return 0; /* Explicitly disabled; leave un-enabled */
+
+ /* Set the enable bit for the channel to re-start Tx operations */
+ hpi_txdma_channel_enable(handle, channel);
+
+ HXGE_DBG(hxgep, "Channel %d re-enabled",channel);
+
+ return 0;
+}
+
+/* Wait for Tx channel idle (wait for QST to set) */
+static int hxge_qstwait_tx_channel(struct hxge_adapter *hxgep, int channel)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ hpi_status_t hsts;
+
+ /* Wait for Tx operations to quiesce (QST to be set) */
+
+ hsts = hpi_txdma_control_reset_wait(handle, channel);
+ if (hsts != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "Channel %d failed to idle (QST not set)", channel);
+ }
+
+ return 0;
+}
+
+void hxge_reset_tx_channel(struct hxge_adapter *hxgep, int channel)
+{
+ struct tx_ring_t *tx_ring = &hxgep->tx_ring[channel];
+
+ HXGE_ERR(hxgep, "Starting tx reset");
+ /* If already in this code, then return */
+ if (test_and_set_bit(RING_RESET, &tx_ring->state))
+ return;
+
+ /* We don't want the reclaim thread to run while we resetting. If
+ already in reclaim, then wait for it to finish */
+ while (test_and_set_bit(RING_RECLAIM, &tx_ring->state))
+ mdelay(1);
+
+ /* Not calling unlock; hxge_init_tx_channel will initialize it
+ * unlocked */
+ spin_lock(&tx_ring->lock);
+
+ hxge_disable_tx_channel(hxgep, channel);
+ hxge_enable_tx_channel(hxgep, channel);
+
+ clear_bit(RING_RECLAIM, &tx_ring->state);
+ clear_bit(RING_RESET, &tx_ring->state);
+ HXGE_ERR(hxgep, "Tx channel reset complete");
+}
+
+/* Enable Transmits. Also, enable Tx interrupts */
+int hxge_enable_tx(struct hxge_adapter *hxgep)
+{
+ int i;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ uint64_t reg64 = 0xfeedfacedeadbeefULL;
+ uint64_t data;
+
+ /* Initialize global TX-related error handling */
+
+ HXGE_REG_RD64(handle, TDC_FIFO_ERR_STAT, ®64);
+ if (reg64) {
+ /* While an interesting case (error flags should probably
+ * not be set), do not count against hxgep->hard_errors */
+ HXGE_ERR(hxgep, "TDC_FIFO_ERR_STAT 0x%16.16x hardware error flags set",
+ (unsigned int)reg64);
+ }
+ HXGE_REG_WR64(handle, TDC_FIFO_ERR_STAT, reg64); /* RW1C err bits */
+ HXGE_REG_WR64(handle, TDC_FIFO_ERR_MASK, 0); /* 0 = no disables */
+
+
+ /* Scrub rtab memory */
+ HXGE_REG_WR64(handle, TDC_REORD_TBL_DATA_HI, 0);
+ HXGE_REG_WR64(handle, TDC_REORD_TBL_DATA_LO, 0);
+ for (i = 0; i < 256; i++) {
+ HXGE_REG_WR64(handle, TDC_REORD_TBL_CMD, i);
+ do {
+ HXGE_REG_RD64(handle, TDC_REORD_TBL_CMD, ®64);
+ } while (!(reg64 & 0x80000000ULL));
+
+ HXGE_REG_WR64(handle, TDC_REORD_TBL_CMD, (1ULL << 30) | i);
+ do {
+ HXGE_REG_RD64(handle, TDC_REORD_TBL_CMD, ®64);
+ } while (!(reg64 & 0x80000000ULL));
+
+
+ HXGE_REG_RD64(handle, TDC_REORD_TBL_DATA_LO, ®64);
+ HXGE_REG_RD64(handle, TDC_REORD_TBL_DATA_HI, &data);
+ data &= 0xffULL; /* save only data bits, not parity */
+
+ if (reg64 | data) {
+ HXGE_ERR(hxgep, "Error reading RTAB data regs, entry: 0x%x, data lo: 0x%llx, data hi: 0x%llx",i, reg64, data);
+ HXGE_REG_RD64(handle, TDC_FIFO_ERR_STAT, ®64);
+ if (reg64) {
+ HXGE_ERR(hxgep, "ReordTbl parity error, entry: %x, fifo error stat: 0x%llx", i, reg64);
+ }
+ return -1;
+ }
+ }
+
+ /* Now enable each of the TDC DMA channels */
+
+ for (i = 0; i < hxgep->max_tdcs; i++)
+ hxge_enable_tx_channel(hxgep, i);
+
+ /* Enable Tx interrupts */
+ hxge_enable_tx_ints(hxgep, NULL);
+
+ return 0;
+}
+
+
+/* Reset entire Tx ("TDC") subsystem */
+int hxge_reset_tdc(struct hxge_adapter *hxgep)
+{
+ /* Quiesce ("shutdown") all transmit activity first */
+
+ hxge_disable_tx(hxgep);
+
+ /* Generate tdc_core logic reset */
+
+ hxge_block_reset(hxgep, LDV_TXDMA);
+
+ /* Finally, bring all the Tx channels back up */
+
+ hxge_enable_tx(hxgep);
+
+ return 0;
+}
+
+
+/* Re-enable Transmits/Tx interrupts (e.g., after LDF1 auto-disable) */
+int hxge_reenable_tx(struct hxge_adapter *hxgep)
+{
+ int i;
+
+ for (i = 0; i < hxgep->max_tdcs; i++)
+ hxge_reenable_tx_channel(hxgep, i);
+
+ /* We assume the Tx interrupts "enablement" was unchanged by
+ * whatever course of events led us here. */
+
+ return 0;
+}
+
+
+/* Wait for Transmits/Tx to go idle (wait for Tx QST to set) */
+int hxge_qstwait_tx(struct hxge_adapter *hxgep)
+{
+ int i;
+
+ for (i = 0; i < hxgep->max_tdcs; i++)
+ hxge_qstwait_tx_channel(hxgep, i);
+
+ return 0;
+}
+
+
+static int get_desc_required(struct tx_ring_t *tx_ring, struct sk_buff *skb)
+{
+ int skb_len = skb->len;
+ int num_descs = 0, len, tx_len;
+
+ if ((skb_len + TX_PKT_HEADER_SIZE < tx_ring->tx_buffer_size) &&
+ !skb_shinfo(skb)->nr_frags)
+ return 1;
+
+ len = skb_headlen(skb);
+ num_descs++; /* one for internal header */
+ while (len) {
+ tx_len = len < TX_MAX_TRANSFER_LENGTH ?
+ len : TX_MAX_TRANSFER_LENGTH;
+ len -= tx_len;
+ num_descs++;
+ }
+
+ /* The max size of an SKB fragment is one page size i.e 4096 bytes.
+ * However, due to a bug, the max size that one Tx descriptor buffer
+ * can transfer is 4076 bytes. So, worst case, we might need at most
+ * two descriptors to send one fragment
+ */
+ if (skb_shinfo(skb)->nr_frags) {
+ num_descs += (2*skb_shinfo(skb)->nr_frags);
+ return num_descs;
+ }
+
+ return num_descs;
+}
+
+
+/* Pick the next channel to transmit on. We pick the CPU that the transmit
+ * thread is running on (hoping cpu affinity helps). If that particular
+ * channel does not have sufficient descriptors for this skb, then do a
+ * round robin from that point till we find one that has space; better to
+ * find one than just return attempting a retransmit from higher layers
+ */
+static struct tx_ring_t *get_channel_to_transmit(struct hxge_adapter *hxgep,
+ struct sk_buff *skb, int *state, int *desc_required)
+{
+ uint8_t tx_channel_state = 0;
+ struct tx_ring_t *tx_ring = NULL;
+ int channel, start_channel, channels_tried;
+ int attempt = 1; /* 1 ms */
+
+ *state = NETDEV_TX_OK;
+
+ channel = start_channel = smp_processor_id() % hxgep->max_tdcs;
+ for (channels_tried = 0; channels_tried < hxgep->max_tdcs;
+ channels_tried++, channel = (channel+1) % hxgep->max_tdcs)
+ {
+ tx_ring = &hxgep->tx_ring[channel];
+
+ if (!test_bit(RING_ENABLED, &tx_ring->state)) {
+ tx_channel_state |= (1 << channel);
+ continue;
+ }
+ /* Grab the Tx channel lock to avoid race between multiple Tx
+ * threads using this channel. Also, synchronizes with the
+ * reset code since we don't want to be in here while a reset
+ * is happening and visa versa.
+ */
+ while (!(spin_trylock(&tx_ring->lock))) {
+ mdelay(1);
+ if (!attempt) {
+ HXGE_ERR(hxgep, "Could not get tx lock!");
+ tx_ring->stats.txlock_acquire_failed++;
+
+ dev_kfree_skb_any(skb);
+ *state = NETDEV_TX_OK;
+ return NULL;
+ }
+ attempt--;
+ }
+ if (*desc_required < 0)
+ *desc_required = get_desc_required(tx_ring, skb);
+ if (atomic_read(&tx_ring->descs_avail) < *desc_required) {
+ wake_up_interruptible(&tx_ring->reclaim_event);
+ spin_unlock(&tx_ring->lock);
+ continue;
+ }
+ else break;
+ }
+
+ if (tx_channel_state == (1 << HXGE_MAX_TDCS) -1) {
+ HXGE_ERR(hxgep, "All channels disabled!");
+ dev_kfree_skb_any(skb);
+ *state = NETDEV_TX_OK;
+ return NULL;
+ }
+
+ if (channels_tried == hxgep->max_tdcs) {
+ *state = NETDEV_TX_BUSY;
+ return NULL;
+ }
+
+ return tx_ring;
+}
+
+static inline int valid_packet(struct hxge_adapter *hxgep,
+ struct tx_ring_t *tx_ring, struct sk_buff *skb)
+{
+ int skb_len = skb->len;
+
+ /* Badly constructed skb */
+ if (unlikely(!skb || (skb_len <= 0))) {
+ HXGE_ERR(hxgep, "Badly formed skb!");
+ return 0;
+ }
+
+ /* More skb fragments than we have Tx descriptor ring entries */
+ if (unlikely(skb_shinfo(skb)->nr_frags + 1 > tx_ring->num_tdrs)) {
+ HXGE_ERR(hxgep, "Too many skb fragments than space allows");
+ return 0;
+ }
+
+ /* packet larger than MTU size. Really shoud not happen since higher
+ layer protocol probably needs to fragment the packet before sending
+ to us */
+ if (skb_len > hxgep->vmac.maxframesize) {
+ HXGE_ERR(hxgep, "skb_len is %d, Packet size MTU (max frame size) supported",skb_len);
+ return 0;
+ }
+
+ return 1;
+
+}
+
+/* A utility routine to allocate a new data buffer for transmit if the
+ * current one is full. The caller must ensure that the curr_index is properly
+ * updated
+ */
+static inline struct tx_buf_t *get_txbuf(struct hxge_adapter *hxgep,
+ struct tx_ring_t *tx_ring, int *desc_required)
+{
+ struct tx_buf_t *tx_buf;
+ tx_buf = &tx_ring->tx_buf[tx_ring->curr_index];
+
+ if (!tx_buf->flags) /* unused location; allocate new buffer */
+ {
+
+#ifdef CONFIG_ERRINJECT
+ if (hxgep->err_flags & ALLOC_PAGES_FAILURE) {
+ HXGE_ERR(hxgep, "no page alloc'ed (errinj)");
+ return NULL;
+ }
+#endif
+
+ /* allocate new page */
+ tx_buf->map.vaddr = (caddr_t) __get_free_page(GFP_DMA);
+
+ if (!tx_buf->map.vaddr) {
+ HXGE_ERR(hxgep, "no page alloc'ed");
+ return NULL;
+ }
+
+#ifdef CONFIG_ERRINJECT
+ if (hxgep->err_flags & PCIMAP_FAILURE) {
+ HXGE_ERR(hxgep, "pci_map_page failed (errinj)");
+ free_page((unsigned long)tx_buf->map.vaddr);
+ return NULL;
+ }
+#endif
+
+ /* grab a DMA-map for the page */
+ tx_buf->map.dma_addr = pci_map_page(hxgep->pdev,
+ virt_to_page(tx_buf->map.vaddr),
+ offset_in_page(tx_buf->map.vaddr),
+ PAGE_SIZE, PCI_DMA_TODEVICE);
+
+ if (!tx_buf->map.dma_addr)
+ {
+ HXGE_ERR(hxgep, "pc_map_page failed");
+ free_page((unsigned long)tx_buf->map.vaddr);
+ return NULL;
+ }
+
+ tx_buf->map.len = 0;
+ tx_buf->flags = TX_FLAGS_DATA | TX_FLAGS_ALLOC | TX_FLAGS_UNMAP;
+ tx_buf->skb = NULL; /* should be set in hdr desc */
+ ++*desc_required;
+ }
+
+
+ return tx_buf;
+}
+
+/* Utility routine to write the transmit descriptors into the Tx descriptor
+ * ring for the HW to process. Only the non-SOP entries are written using
+ * this routine.
+ */
+static int write_tx_descs(struct hxge_adapter *hxgep, struct tx_ring_t *tx_ring,
+ int start, int num_descs)
+{
+ int i;
+ tx_desc_t desc;
+ desc.value = 0;
+
+
+ for (i = start; i < (start+num_descs); i++) {
+ struct tx_buf_t *tx_buf;
+ hpi_handle_t handle;
+ tx_desc_t *descp;
+
+ handle = &tx_ring->desc_ring.vaddr[i % tx_ring->num_tdrs];
+ tx_buf = &tx_ring->tx_buf[i % tx_ring->num_tdrs];
+ if (hpi_txdma_desc_gather_set(handle, &desc, 1, 0, 0,
+ tx_buf->map.dma_addr, tx_buf->map.len) != HPI_SUCCESS)
+ {
+ HXGE_ERR(hxgep, "hpi_txdma_desc_gather_set failed");
+ return -1;
+ }
+ descp = (tx_desc_t *)handle;
+ HXGE_DBG(hxgep, "TX_BUF");
+ HXGE_DBG(hxgep, " tx_buf->len : %d",tx_buf->map.len);
+ HXGE_DBG(hxgep, " tx_buf->vaddr : %p",tx_buf->map.vaddr);
+ HXGE_DBG(hxgep, " tx_buf->dma_addr: %p",(void *)tx_buf->map.dma_addr);
+ HXGE_DBG(hxgep, " DESC =>");
+ HXGE_DBG(hxgep, " SOP:%d, mark:%d, num_ptr:%d, len:%d, sad:0x%llx",
+ descp->bits.sop, descp->bits.mark,
+ descp->bits.num_ptr, descp->bits.tr_len,
+ (unsigned long long)descp->bits.sad);
+ }
+ return 0;
+}
+
+/* This routine copies data from the SKB to staging buffers instead of
+ * using scatter gather lists like map_skbdata_to_descs. This routine is
+ * called when the number of skb fragments approach or exceed the 15
+ * descriptors per packet limit
+ */
+static int copy_skbdata_to_descs(struct hxge_adapter *hxgep,
+ struct tx_ring_t *tx_ring, char *data_ptr, uint32_t len,
+ int *desc_required)
+{
+ struct tx_buf_t *tx_buf = NULL;
+
+ while (len) {
+ uint32_t max_len;
+ uint32_t tx_len;
+
+ tx_buf = get_txbuf(hxgep, tx_ring, desc_required);
+
+ if (!tx_buf || !(tx_buf->flags & TX_FLAGS_ALLOC)) {
+ HXGE_ERR(hxgep, "tx_buf (%p) alloc failed",tx_buf);
+ return COPY_DESC_FAILED;
+ }
+
+ max_len = TX_MAX_TRANSFER_LENGTH - tx_buf->map.len;
+ tx_len = len < max_len ? len : max_len;
+
+ memcpy(tx_buf->map.vaddr + tx_buf->map.len, data_ptr, tx_len);
+ tx_buf->map.len += tx_len;
+ data_ptr += tx_len;
+ len -= tx_len;
+
+ /* the buffer is full; start new buffer next time around */
+ if (tx_buf->map.len == TX_MAX_TRANSFER_LENGTH)
+ TXDMA_GET_NEXT_INDEX(tx_ring, 1);
+ }
+
+ /* corner case: When we are the last skb fragment calling in and
+ * we happened to just fill this data buffer, we don't want to bump
+ * the index in this case because we did it at the tail end of the
+ * while loop above. However, we do want the caller of this routine
+ * to bump to the next index a new packet
+ */
+ if (tx_buf && (tx_buf->map.len < TX_MAX_TRANSFER_LENGTH))
+ return 1;
+
+ return 0;
+}
+
+/* To map skb data into Tx descriptor ring entries. The case for the internal
+ * header is done separately (not here). Also, this does not include the
+ * case where the skb is copied in to the tx buffer
+ */
+static int map_skbdata_to_descs(struct hxge_adapter *hxgep,
+ struct tx_ring_t *tx_ring, char *data_ptr, uint32_t len,
+ int *desc_required)
+{
+ uint32_t tx_len;
+ struct tx_buf_t *tx_buf;
+
+ while (len) {
+ tx_buf = &tx_ring->tx_buf[tx_ring->curr_index];
+ tx_len = len < TX_MAX_TRANSFER_LENGTH ?
+ len : TX_MAX_TRANSFER_LENGTH;
+
+#ifdef CONFIG_ERRINJECT
+ if (hxgep->err_flags & PCIMAP_FAILURE) {
+ HXGE_ERR(hxgep, "pcimap err injected");
+ return MAP_DESC_FAILED;
+ }
+#endif
+
+ tx_buf->map.dma_addr = pci_map_page(hxgep->pdev,
+ virt_to_page(data_ptr),
+ offset_in_page(data_ptr), tx_len,
+ PCI_DMA_TODEVICE);
+
+ if (!tx_buf->map.dma_addr) {
+ HXGE_ERR(hxgep, "pci_map_page failed");
+ return MAP_DESC_FAILED;
+ }
+
+ tx_buf->map.len = tx_len;
+ tx_buf->map.vaddr = data_ptr;
+ tx_buf->flags = TX_FLAGS_DATA | TX_FLAGS_UNMAP;
+ tx_buf->skb = NULL; /* already set in hdr */
+ data_ptr += tx_len;
+ len -= tx_len;
+ TXDMA_GET_NEXT_INDEX(tx_ring, 1);
+ ++*desc_required;
+
+ }
+
+ return 0;
+}
+
+/* Cleanup up in the event of a failure either in map_skbdata_to_descs
+ * or copy_skbdata_to_descs. Free up allocated pages and unmap dma
+ * mappings as appropriate. Also move the curr_index back for next packet
+ */
+
+static void cleanup_descs(struct hxge_adapter *hxgep,
+ struct tx_ring_t *tx_ring, int descs)
+{
+ struct tx_buf_t *tx_buf;
+
+ while (descs) {
+ TXDMA_DEC_INDEX(tx_ring);
+ tx_buf = &tx_ring->tx_buf[tx_ring->curr_index];
+ if (tx_buf->flags & TX_FLAGS_UNMAP) {
+ int len = (tx_buf->flags & TX_FLAGS_ALLOC) ? PAGE_SIZE :
+ tx_buf->map.len;
+ pci_unmap_page(hxgep->pdev, tx_buf->map.dma_addr,
+ len, PCI_DMA_TODEVICE);
+ }
+ if (tx_buf->flags & TX_FLAGS_ALLOC)
+ free_page((unsigned long)tx_buf->map.vaddr);
+
+ memset(tx_buf, 0, sizeof(struct tx_buf_t));
+ descs--;
+ }
+}
+
+
+
+/* Fill in the internal header set up in a Tx buffer by the transmit code. This
+ * requires tunneling through the ethernet payload and getting inforamtion from
+ * the L2, L3 and L4 layers to fill in information required.
+ * Arguments:
+ * pkt_hdr - Pointer to where the header information
+ * skb - Contains the data payload
+ * len - The total length of the data payload excluding internal header
+ */
+
+static void fill_tx_hdr(tx_pkt_header_t *pkt_hdr, struct sk_buff *skb, int len
+#ifdef CONFIG_ERRINJECT
+ , struct hxge_adapter *hxgep
+#endif
+)
+{
+ uint8_t *ip_p = NULL;
+ uint16_t eth_type;
+ char *proto_hdr = NULL;
+ struct udphdr *udp_hdr = NULL;
+ struct tcphdr *tcp_hdr = NULL;
+ struct iphdr *ip_hdr = NULL;
+ uint8_t ip_proto = 0;
+ struct ipv6hdr *ipv6_hdr = NULL;
+ struct skb_hdr_info_t *skb_hdr_info;
+
+ skb_hdr_info = (struct skb_hdr_info_t *)skb->cb;
+ memset(pkt_hdr, 0, sizeof(tx_pkt_header_t));
+ pkt_hdr->bits.tot_xfer_len = len;
+
+ /* Read the type field from the ethernet frame */
+ eth_type = ntohs(skb->protocol);
+
+
+ /* This is when type < 1500 i.e. 802.3 type and not Ethernet III */
+ if (eth_type < ETH_DATA_LEN) {
+ if (*(skb->data + ETH_HLEN) == LLC_SAP_SNAP) {
+ eth_type = ntohs(*((uint16_t*)(skb->data + ETH_HLEN + 6)));
+ if (eth_type == ETH_P_IP || eth_type == ETH_P_IPV6)
+ ip_p = (uint8_t*)(skb->data + ETH_HLEN + 8);
+ } else return;
+ } else if (eth_type == ETH_P_8021Q) { /* VLAN support */
+
+ pkt_hdr->bits.vlan = 1;
+ eth_type = ntohs(((struct vlan_ethhdr *)skb->data)->h_vlan_encapsulated_proto);
+ if (eth_type == ETH_P_IP || eth_type == ETH_P_IPV6)
+ ip_p = (uint8_t*)(skb->data + VLAN_ETH_HLEN);
+ } else /* Ethernet III type */
+ ip_p = (uint8_t*)(skb->data + ETH_HLEN);
+
+
+ /* Now we have got the "real" type value. Tunnel through the IP
+ payload to get L3 and L4 information needed in the header */
+ switch (eth_type) {
+ case ETH_P_IP : /* IPv4 */
+ ip_hdr = (struct iphdr *)ip_p;
+ pkt_hdr->bits.ip_ver = 0;
+ pkt_hdr->bits.ihl = ip_hdr->ihl;
+ pkt_hdr->bits.l3start = ((ulong)ip_hdr - (ulong)skb->data) >> 1;
+ ip_proto = ip_hdr->protocol;
+ proto_hdr = (char *)ip_hdr + (ip_hdr->ihl<<2);
+ break;
+ case ETH_P_IPV6: /* IPv6 */
+ ipv6_hdr = (struct ipv6hdr *)ip_p;
+ pkt_hdr->bits.ip_ver = 1;
+ pkt_hdr->bits.ihl = 40 >> 2; /* hard-coded */
+ pkt_hdr->bits.l3start = ((ulong)ipv6_hdr - (ulong)skb->data)>>1;
+ ip_proto = ipv6_hdr->nexthdr;
+ proto_hdr = (char *)ipv6_hdr + 40;
+ break;
+ default :
+ return;
+ break;
+ }
+
+ /* Checksumming is done only if Linux has marked for it to be done.
+ The driver has notified Linux that it does L4 checksumming as part
+ of initialization but there are scenarios where Linux will have to
+ do checksumming itself (IP fragments). So, check the ip_summed field
+ to see if Linux has requested for it */
+
+ /* TODO: Is a zero value in l4stuff a valid offset? How does one turn
+ off checksumming on a per-packet basis on Hydra? */
+ switch (ip_proto) {
+ case IPPROTO_TCP :
+ tcp_hdr = (struct tcphdr *)proto_hdr;
+ pkt_hdr->bits.l4start = (ulong)tcp_hdr - (ulong)skb->data;
+ skb_hdr_info->l4_payload_offset = pkt_hdr->bits.l4start +
+ (tcp_hdr->doff << 2);
+ pkt_hdr->bits.l4start >>=1;
+ if (skb->ip_summed == HXGE_CHECKSUM) {/* hardware do checksum */
+ pkt_hdr->bits.cksum_en_pkt_type = 1;
+ pkt_hdr->bits.l4stuff = pkt_hdr->bits.l4start +
+ (SKB_CKSUM_OFFSET(skb) >> 1);
+#ifdef CONFIG_ERRINJECT
+ if (hxgep->err_flags & CHKSUM_FAILURE) {
+ HXGE_ERR(hxgep, "Injecting checksum error");
+ pkt_hdr->bits.l4stuff--;
+ }
+#endif
+ }
+ break;
+ case IPPROTO_UDP :
+ udp_hdr = (struct udphdr *)proto_hdr;
+ pkt_hdr->bits.l4start = (ulong)udp_hdr - (ulong)skb->data;
+ skb_hdr_info->l4_payload_offset = pkt_hdr->bits.l4start +
+ sizeof(struct udphdr);
+ pkt_hdr->bits.l4start >>= 1;
+
+
+
+ if (skb->ip_summed == HXGE_CHECKSUM) {
+ /* workaround a hw checksum offload for udp/ipv6.
+ Complete the partial checksum that was already
+ setup in skb->csum just like the hw would have done
+ it */
+
+ if (eth_type == ETH_P_IPV6) {
+ fixup_checksum(skb
+#ifdef CONFIG_ERRINJECT
+ ,hxgep
+#endif
+ );
+ break;
+ }
+ pkt_hdr->bits.cksum_en_pkt_type = 2;
+ pkt_hdr->bits.l4stuff = pkt_hdr->bits.l4start +
+ (SKB_CKSUM_OFFSET(skb) >> 1);
+#ifdef CONFIG_ERRINJECT
+ if (hxgep->err_flags & CHKSUM_FAILURE) {
+ HXGE_ERR(hxgep, "Injecting checksum error");
+ pkt_hdr->bits.l4stuff--;
+ }
+#endif
+
+ }
+ break;
+ default :
+ break;
+ }
+}
+
+
+/* The main routine for transmitting a packet. It is called directly by the
+ * Linux network stack via the hard_xmit_frame network device function pointer.
+ * It determines which channel to pick for transmit, maps the packet data to
+ * Tx descriptor ring entries, creates the internal packet header and informs
+ * hydra of the new descriptor entries via the kick register
+ */
+
+int hxge_tx_ring(struct sk_buff *skb, struct net_device *netdev)
+
+{
+ int channel, desc_required=-1, hdr_index, i;
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+ struct tx_ring_t *tx_ring;
+ int skb_len = skb->len, tot_len;
+ int len;
+ struct tx_buf_t *tx_buf;
+ tx_desc_t desc;
+ tx_desc_t *descp;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ tx_pkt_header_t *pkt_hdr;
+ int state;
+
+ /*
+ * Get channel to transmit on. It returns with the tx_ring->lock locked
+ * if successful
+ */
+
+ tx_ring = get_channel_to_transmit(hxgep, skb, &state, &desc_required);
+ if (!tx_ring) {
+ if (state == NETDEV_TX_BUSY) {
+ if (!netif_queue_stopped(netdev))
+ netif_stop_queue(netdev);
+ }
+ return (state);
+ }
+
+ /* Validate the packet */
+ if (!valid_packet(hxgep, tx_ring, skb)) {
+ HXGE_ERR(hxgep, "Freeing skb due to invalid packet");
+ dev_kfree_skb_any(skb);
+ spin_unlock(&tx_ring->lock);
+ return (NETDEV_TX_OK);
+ }
+
+
+ channel = tx_ring->tdc;
+
+ /* There is space for at least the current packet */
+
+ TXDMA_GET_CURR_INDEX(tx_ring);
+ hdr_index= tx_ring->curr_index;
+ tx_buf = &tx_ring->tx_buf[hdr_index];
+ tx_buf->map.vaddr = tx_ring->data_buf.vaddr +
+ (hdr_index*tx_ring->tx_buffer_size);
+
+ tx_buf->map.dma_addr = tx_ring->data_buf.dma_addr +
+ (hdr_index*tx_ring->tx_buffer_size);
+ tx_buf->flags = TX_FLAGS_HDR;
+ tx_buf->map.len = TX_PKT_HEADER_SIZE; /* assume just header; no data content */
+ tx_buf->skb = skb;
+ skb_orphan(skb);
+#ifdef CONFIG_ERRINJECT
+ atomic_inc(&skb_count);
+#endif
+
+ tot_len = TX_PKT_HEADER_SIZE;
+ /* Increment to next free entry */
+ TXDMA_GET_NEXT_INDEX(tx_ring, 1);
+
+#if 0
+ HXGE_DBG(hxgep, "Have %d descs", desc_required);
+ HXGE_DBG(hxgep, "SKB => ");
+ HXGE_DBG(hxgep, " skb->len = %d",skb->len);
+ HXGE_DBG(hxgep, " skb->priority = %d", skb->priority);
+ HXGE_DBG(hxgep, " skb->data_len= %d", skb->data_len);
+ HXGE_DBG(hxgep, " skb->nr_frags= %d", skb_shinfo(skb)->nr_frags);
+#endif
+
+ if (desc_required == 1) /* small packet */ {
+ len = skb_headlen(skb);
+ memcpy((char *)tx_buf->map.vaddr + TX_PKT_HEADER_SIZE, skb->data,
+ len);
+ tx_buf->flags = TX_FLAGS_ALL;
+ tx_buf->map.len += len; /* data len in addition to internal hdr */
+ tot_len += len;
+ } else {
+ int ret = 0;
+ desc_required = 1; /* for header */
+
+ if (map_skbdata_to_descs(hxgep, tx_ring,
+ (char *)skb->data, skb_headlen(skb),&desc_required) < 0)
+ {
+ HXGE_ERR(hxgep, "map_skbdata_to_descs failed");
+ FREE_SKB(skb);
+ cleanup_descs(hxgep, tx_ring, desc_required);
+ spin_unlock(&tx_ring->lock);
+ return (NETDEV_TX_OK);
+ }
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ skb_frag_t *fragp = &skb_shinfo(skb)->frags[i];
+ char *dat = (char *)page_address(fragp->page)+fragp->page_offset;
+
+ HXGE_DBG(hxgep, "Frag %d @ (%p,%d), len %d",i,(char *)fragp->page, fragp->page_offset,fragp->size);
+
+
+ /* CR 7104801: If the number of skb fragments are more
+ * the number of tx descriptors available, then we
+ * have to copy and consolidate the remaining fragments
+ * into newly allocated buffers. We need to make
+ * sure that at least 3 descriptor entries are reserved
+ * for the copy giving us a total of 12KB (4KB page).
+ * This should be sufficient on Hydra where the largest
+ * packet cannot exceed 9KB
+ */
+ if (desc_required > HXGE_TX_DESCS_PER_PACKET-4)
+ ret = copy_skbdata_to_descs(hxgep, tx_ring,
+ dat, fragp->size, &desc_required);
+ else
+ ret = map_skbdata_to_descs(hxgep, tx_ring, dat,
+ fragp->size, &desc_required);
+
+ if (ret < 0)
+ {
+ char fn[25];
+ if (ret == COPY_DESC_FAILED)
+ strcpy(fn, "copy_skbdata_to_descs");
+ else
+ strcpy(fn, "map_skbdata_to_descs");
+
+ HXGE_ERR(hxgep, "%s failed", fn);
+ FREE_SKB(skb);
+ cleanup_descs(hxgep, tx_ring, desc_required);
+ spin_unlock(&tx_ring->lock);
+ return (NETDEV_TX_OK);
+ }
+
+ }
+ tot_len += skb_len;
+
+ /* copy_skbdata_to_descs ends in the middle of a tx_buf; need
+ * to point to new one for next packet
+ */
+ if (ret > 0)
+ TXDMA_GET_NEXT_INDEX(tx_ring, 1);
+
+ }
+
+ /* write out the non-SOP descriptors now */
+ write_tx_descs(hxgep, tx_ring, hdr_index+1, desc_required-1);
+
+ /* Create internal header. This requires tunneling into packet
+ data. When doing ethtool testing, just fill total len and ignore
+ other fields in the skb header */
+ if (unlikely(test_bit(HXGE_DEVICE_TESTING, &hxgep->state))) {
+ pkt_hdr = (tx_pkt_header_t *)tx_buf->map.vaddr;
+ pkt_hdr->value = 0;
+ pkt_hdr->bits.tot_xfer_len = tot_len+skb->priority;
+ HXGE_DBG(hxgep, "Internal header size is %d", pkt_hdr->bits.tot_xfer_len);
+ }
+ else
+ fill_tx_hdr((tx_pkt_header_t *)tx_buf->map.vaddr, skb, tot_len
+#ifdef CONFIG_ERRINJECT
+ , hxgep
+#endif
+);
+
+
+ /* Set up and write the SOP descriptor. The length does not include
+ * internal packet length; just the data packet only */
+
+ desc.value = 0;
+ tx_ring->mark_ints = (tx_ring->mark_ints+1) % hxgep->tx_mark_ints;
+
+
+ hpi_txdma_desc_gather_set(&tx_ring->desc_ring.vaddr[hdr_index], &desc,
+ 0, (tx_ring->mark_ints == 0), desc_required,
+ tx_buf->map.dma_addr, tx_buf->map.len);
+
+
+ tx_ring->stats.descs_used[desc_required]++;
+
+ /* Sanity Test: descriptors used should never exceed HW limit of 15 */
+ if (desc_required > HXGE_TX_DESCS_PER_PACKET) {
+ HXGE_ERR(hxgep, "BUG: desc_required > %d!! Will cause tx_reset",HXGE_TX_DESCS_PER_PACKET);
+ BUG();
+ }
+
+ HXGE_DBG(hxgep, "Sending through channel %d. Packet Info =>",channel);
+ for (i = 0, descp=&tx_ring->desc_ring.vaddr[hdr_index]; i < desc_required; i++)
+ HXGE_DBG(hxgep, " SOP:%d, mark:%d, num_ptr:%d, len:%d, sad:0x%llx",
+ descp->bits.sop, descp->bits.mark,
+ descp->bits.num_ptr, descp->bits.tr_len,
+ (unsigned long long)descp->bits.sad);
+
+
+ /* Update the descriptors available for use. Note that update to this
+ variable races with the reclaim thread, hence an atomic operation */
+ atomic_sub(desc_required, &tx_ring->descs_avail);
+
+ if (atomic_read(&tx_ring->descs_avail) < 0) {
+ HXGE_ERR(hxgep, "Descriptors available less than zero!!");
+ }
+ /* Update descriptors available. Write the SOP descriptor entry and
+ kick the number of new entries added */
+ TXDMA_UPDATE_INDEX(tx_ring);
+ hpi_txdma_desc_kick_reg_set(handle, channel, tx_ring->tail,
+ tx_ring->wrap);
+
+ /* Successfully queued one network packet of <skb_len> bytes */
+
+ tx_ring->stats.opackets++;
+ tx_ring->stats.obytes += skb_len;
+
+ spin_unlock(&tx_ring->lock);
+
+ return NETDEV_TX_OK;
+}
+
+static inline int compute_tot_hdrlen(struct skb_hdr_info_t *skb_hdr,
+ struct staging_info_t *si)
+{
+ int l4_hdr_len, l3_hdr_len;
+ tx_pkt_header_t *pkt_hdr = &si->pkthdr;
+
+ si->l3_offset = pkt_hdr->bits.l3start << 1;
+ si->l4_offset = pkt_hdr->bits.l4start << 1;
+ l4_hdr_len = skb_hdr->l4_payload_offset -
+ (pkt_hdr->bits.l4start<<1);
+ si->l4_hdr_len = l4_hdr_len;
+ l3_hdr_len = (pkt_hdr->bits.l4start-pkt_hdr->bits.l3start)<<1;
+ return ((pkt_hdr->bits.l3start<<1) + l3_hdr_len + l4_hdr_len);
+}
+
+static void free_staging_info(struct hxge_adapter *hxgep,
+ struct staging_info_t *si, int failure)
+{
+ if (failure) {
+ int i;
+ struct pci_dma_map_t *pci_map = &si->pci_map;
+ if (si->hdr_array.vaddr)
+ pci_free_consistent(hxgep->pdev, si->hdr_array.len,
+ si->hdr_array.vaddr, si->hdr_array.dma_addr);
+
+ if (pci_map->num_dma_mappings) {
+ for (i = 0; i < pci_map->num_dma_mappings; i++)
+ pci_unmap_page(hxgep->pdev,
+ pci_map->dma_map[i].dma_addr,
+ pci_map->dma_map[i].len,
+ PCI_DMA_TODEVICE);
+ kfree(pci_map->dma_map);
+ }
+ }
+
+ if (si->desc)
+ kfree(si->desc);
+
+ if (si)
+ kfree(si);
+}
+
+static struct staging_info_t *setup_staging_info(struct hxge_adapter *hxgep,
+ struct sk_buff *skb)
+{
+ struct staging_info_t *si;
+ struct skb_hdr_info_t *skb_hdr = (struct skb_hdr_info_t *)skb->cb;
+ int frame_cnt = SKB_GSO_SEGS(skb);
+
+ si = (struct staging_info_t *)kzalloc(sizeof(struct staging_info_t), GFP_KERNEL);
+ if (!si) {
+ HXGE_ERR(hxgep,"No space for staging structure");
+ return NULL;
+ }
+
+ memset(skb_hdr, 0, sizeof(struct skb_hdr_info_t));
+ fill_tx_hdr(&si->pkthdr, skb, 0
+#ifdef CONFIG_ERRINJECT
+ , hxgep
+#endif
+ );
+
+ si->tcpip_hdr_len = compute_tot_hdrlen(skb_hdr,
+ si);
+ si->hdr_array.len = frame_cnt* sizeof(si->hdr_template);
+ si->max_frames = frame_cnt;
+ si->frame_size = SKB_IS_GSO(skb);
+ si->hdr_array.vaddr = pci_alloc_consistent(hxgep->pdev,
+ si->hdr_array.len,
+ &si->hdr_array.dma_addr);
+ if (!si->hdr_array.vaddr)
+ goto fail;
+
+ /* do not use until we are done setting up the entire packet */
+ si->desc = kmalloc(frame_cnt*DESCS_PER_FRAME*
+ sizeof(struct staging_desc_t), GFP_KERNEL);
+ if (!si->desc) {
+ HXGE_ERR(hxgep,"No space for staging desc");
+ goto fail;
+ }
+
+ /* This is stored so that we can free the staging structure but
+ * keep a pointer to the hdr array which needs to be around till
+ * the skb is done i.e entire packet has been transmitted
+ */
+ skb_hdr->hdr_array = si->hdr_array;
+
+ /* setup the hdr template with standard values. This contains the
+ values for l3stuff, l4stuff, l3start, l4start, etc all set up
+ by the call to fill_tx_hdr and saved to si->pkthdr */
+ memcpy(si->hdr_template, (char *)&si->pkthdr,
+ sizeof(tx_pkt_header_t));
+ memcpy(si->hdr_template+TX_PKT_HEADER_SIZE, skb->data,
+ si->tcpip_hdr_len);
+
+ /* Initialize some of the fields within the template with initial
+ values from the skb */
+ do {
+ char *hdrp = si->hdr_template;
+ struct tcphdr *tcphdrp = (struct tcphdr *)
+ (hdrp + TX_PKT_HEADER_SIZE + si->l4_offset);
+ struct iphdr *iphdrp = (struct iphdr *)
+ (hdrp + TX_PKT_HEADER_SIZE + si->l3_offset);
+ si->tcp_sequence = ntohl(tcphdrp->seq);
+ si->ip_id = ntohs(iphdrp->id);
+ /* Save the initial skb state of the flags and then clear it
+ out in the template. fixup_tcpip_hdr takes care of setting
+ the vaues in the appropriate frame */
+ if (tcphdrp->urg) si->tcp_flags = TCP_FLAGS_URG;
+ if (tcphdrp->psh) si->tcp_flags |= TCP_FLAGS_PSH;
+ if (tcphdrp->rst) si->tcp_flags |= TCP_FLAGS_RST;
+ if (tcphdrp->fin) si->tcp_flags |= TCP_FLAGS_FIN;
+ tcphdrp->urg = tcphdrp->psh = tcphdrp->rst = 0;
+ tcphdrp->fin = 0;
+
+ } while (0);
+
+ return si;
+fail:
+ free_staging_info(hxgep, si, 1);
+ return NULL;
+}
+
+static inline void fixup_tcpip_hdr(struct staging_info_t *sp, int frame, int len,
+ struct tcphdr *tcphdrp, struct iphdr *iphdrp)
+{
+ tcphdrp->seq = htonl(sp->tcp_sequence);
+ iphdrp->id = htons(sp->ip_id++);
+ sp->tcp_sequence += len;
+ iphdrp->tot_len = htons(len + sp->l4_hdr_len + sp->l4_offset -
+ sp->l3_offset);
+
+ /* Fix up the TCP flags; only the first and last matter. The rest of
+ the frames have zeros for these flags */
+ if (!frame) // first frame
+ tcphdrp->urg = (sp->tcp_flags & TCP_FLAGS_URG);
+ else if (frame == sp->max_frames-1) {
+ tcphdrp->psh = (sp->tcp_flags & TCP_FLAGS_PSH);
+ tcphdrp->rst = (sp->tcp_flags & TCP_FLAGS_RST);
+ tcphdrp->fin = (sp->tcp_flags & TCP_FLAGS_FIN);
+ }
+ /* do checksumming last after all the fields in tcp and ip
+ * headers have been fixed up properly
+ */
+ iphdrp->check = 0;
+ iphdrp->check = ip_fast_csum((uint8_t *)iphdrp, iphdrp->ihl);
+ tcphdrp->check = ~csum_tcpudp_magic(iphdrp->saddr, iphdrp->daddr,
+ sp->l4_hdr_len+len, IPPROTO_TCP, 0);
+}
+
+static int setup_frame(struct hxge_adapter *hxgep, struct staging_info_t *si,
+ int len, int frame)
+{
+ int ngathers = 1; // alwauys one descriptor
+ char *hdrp = si->hdr_array.vaddr+(frame*HDR_TEMPLATE_SIZE);
+ struct staging_desc_t *descp = &si->desc[si->desc_idx];
+ struct staging_desc_t *sop_descp = descp++;
+ struct tcphdr *tcphdrp;
+ struct iphdr *iphdrp;
+ tx_pkt_header_t *pkt_hdr;
+ int loc_len = len;
+ int idx = si->dma_map_idx;
+ int off = si->dma_map_off;
+ struct dma_map_t *dma_map = si->pci_map.dma_map;
+
+
+ memcpy(hdrp, si->hdr_template, sizeof(si->hdr_template));
+ tcphdrp = (struct tcphdr *)(hdrp + TX_PKT_HEADER_SIZE +
+ si->l4_offset);
+ iphdrp = (struct iphdr *)(hdrp + TX_PKT_HEADER_SIZE +
+ si->l3_offset);
+ fixup_tcpip_hdr(si, frame, len, tcphdrp, iphdrp);
+
+ while (loc_len) {
+ dma_addr_t dma_addr = dma_map[idx].dma_addr+off;
+ int desc_len = min(loc_len, dma_map[idx].len-off);
+ if (idx >= si->pci_map.num_dma_mappings) {
+ HXGE_ERR(hxgep,"idx (%d) > num_mappings (%d), dma_map = %p, si = %p, len=%d",idx,si->pci_map.num_dma_mappings,dma_map, si, len);
+ BUG();
+ }
+ desc_len = min(desc_len, TX_MAX_TRANSFER_LENGTH);
+ memset(descp, 0, sizeof(struct staging_desc_t));
+ descp->entry.bits.sad = (uint64_t)dma_addr;
+ descp->entry.bits.tr_len = desc_len;
+ loc_len-= desc_len;
+ off += desc_len;
+ if (off == dma_map[idx].len) {
+ off = 0;
+ idx++;
+ }
+ descp++;
+ ngathers++;
+ }
+
+ si->dma_map_idx = idx;
+ si->dma_map_off = off;
+
+ /* setup the SOP descriptor; it contains the internal packet header
+ * and the TCP/IP header information
+ */
+ sop_descp->entry.bits.sop = 1;
+ sop_descp->entry.bits.num_ptr = ngathers;
+ sop_descp->entry.bits.tr_len = TX_PKT_HEADER_SIZE+si->tcpip_hdr_len;
+ sop_descp->entry.bits.sad = si->hdr_array.dma_addr+frame*HDR_TEMPLATE_SIZE;
+ pkt_hdr = (tx_pkt_header_t *)hdrp;
+ pkt_hdr->bits.tot_xfer_len = len + sop_descp->entry.bits.tr_len;
+ si->desc_idx += ngathers;
+ HXGE_DBG(hxgep, "Frame %d : %d len, %d descriptors required",frame,len,ngathers);
+ return 0;
+}
+
+static int setup_dma_mapping(struct hxge_adapter *hxgep,
+ struct sk_buff *skb, struct staging_info_t *si)
+{
+ /* setup the buffer in the main skb structure */
+ int num_mappings = skb_shinfo(skb)->nr_frags;
+ skb_frag_t *fragp;
+ struct dma_map_t *dma_map;
+ struct skb_hdr_info_t *skb_hdr = (struct skb_hdr_info_t *)skb->cb;
+ int i = 0; /* at least one mapping */
+ int main_buf_len;
+
+ /* allocate for the main buffer + the fragments */
+ main_buf_len = skb_headlen(skb) - si->tcpip_hdr_len;
+ if (main_buf_len)
+ num_mappings++;
+
+ dma_map = kmalloc(num_mappings*sizeof(struct dma_map_t), GFP_KERNEL);
+ if (!dma_map) {
+ HXGE_ERR(hxgep, "failed to alloc dma_map");
+ return MAP_DESC_FAILED;
+ }
+
+ /* First map the main buffer in skb; odd one out */
+ if (main_buf_len) {
+ char *data = skb->data+si->tcpip_hdr_len;
+ dma_map[i].len = main_buf_len;
+ dma_map[i].vaddr = data;
+ dma_map[i].dma_addr = pci_map_page(hxgep->pdev,
+ virt_to_page(data),
+ offset_in_page(data),
+ dma_map[0].len,
+ PCI_DMA_TODEVICE);
+ ++i;
+ }
+
+ for (fragp = &skb_shinfo(skb)->frags[0]; i < num_mappings;
+ i++, fragp++) {
+ char *data = page_address(fragp->page);
+ dma_map[i].len = fragp->size;
+ dma_map[i].vaddr = data + fragp->page_offset;
+ dma_map[i].dma_addr = pci_map_page(hxgep->pdev,
+ virt_to_page(data), fragp->page_offset,
+ fragp->size, PCI_DMA_TODEVICE);
+ }
+ si->pci_map.num_dma_mappings = i;
+ si->pci_map.dma_map = dma_map;
+ skb_hdr->pci_map = si->pci_map;
+ return 0;
+}
+
+
+static int hxge_tx_gso(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct hxge_adapter *hxgep = netdev_priv(netdev);
+ int desc_required = 0;
+ struct staging_info_t *si;
+ int i, state;
+ struct tx_ring_t *tx_ring;
+ struct staging_desc_t *desc;
+ struct tx_buf_t *tx_buf;
+ int tot_len, len;
+
+ si = setup_staging_info(hxgep, skb);
+ if (!si)
+ {
+ HXGE_ERR(hxgep, "setup_staging_info failed");
+ dev_kfree_skb_any(skb);
+ return (NETDEV_TX_OK);
+ }
+
+ if (setup_dma_mapping(hxgep, skb, si)) {
+ HXGE_ERR(hxgep, "setup_dma_mapping failed");
+ free_staging_info(hxgep, si, 1);
+ dev_kfree_skb_any(skb);
+ return (NETDEV_TX_OK);
+ }
+
+ tot_len = skb->len - si->tcpip_hdr_len;
+ for (i = 0, len = min(tot_len,si->frame_size); i < si->max_frames;
+ i++, len = min(tot_len, si->frame_size)) {
+ if (setup_frame(hxgep, si, len, i)) {
+ HXGE_ERR(hxgep, "setup_frame for main buffer failed");
+ free_staging_info(hxgep, si, 1);
+ dev_kfree_skb_any(skb);
+ return (NETDEV_TX_OK);
+ }
+ tot_len -= len;
+ }
+
+ /* Pass in known desc_required. Success implies we have the channel
+ * lock
+ */
+ desc_required = si->desc_idx;
+
+ if (desc_required > (DESCS_PER_FRAME*si->max_frames)) {
+ HXGE_ERR(hxgep,"BUG: Not enough space allocated for temporary descrpitors; only have %d, should have %d!",4*si->max_frames,desc_required);
+ BUG();
+ }
+
+ tx_ring = get_channel_to_transmit(hxgep, skb, &state, &desc_required);
+ if (!tx_ring) {
+ if (state == NETDEV_TX_BUSY) {
+ if (!netif_queue_stopped(netdev))
+ netif_stop_queue(netdev);
+ }
+ free_staging_info(hxgep,si,1);
+ return (state);
+ }
+
+ TXDMA_GET_CURR_INDEX(tx_ring);
+ desc = si->desc;
+ tx_buf = &tx_ring->tx_buf[tx_ring->curr_index];
+ for (i = 0; i < desc_required; i++, desc++) {
+ tx_desc_t *real_desc;
+ tx_buf = &tx_ring->tx_buf[tx_ring->curr_index];
+ real_desc = &tx_ring->desc_ring.vaddr[tx_ring->curr_index];
+ tx_buf->flags = TX_FLAGS_DATA;
+ tx_buf->map.dma_addr = desc->addr;
+ tx_buf->map.len = desc->len;
+ tx_buf->skb = NULL;
+ HXGE_MEM_PIO_WRITE64(real_desc, desc->entry.value);
+ TXDMA_GET_NEXT_INDEX(tx_ring, 1);
+ }
+
+ /* We assume there is at least one descriptor. Make sure that
+ * last tx_buf has all the requisite pointers for the reclaim thread
+ * to be able to free the mapped header array. It *has* to be the
+ * last Tx descriptor to ensure that all descriptors relevant to
+ * this skb have been sent out by the HW. No need to set the
+ * TX_FLAGS_UNMAP; the reclaim thread knows that if the skb is
+ * valid and gso is enabled, this is the last descriptor and
+ * consequently, there must be a header array to be unmapped and
+ * freed. Setting the unmap flag will confuse the non-gso code in
+ * reclaim_tx_thread and cause problems.
+ */
+ tx_buf->skb = skb;
+ skb_orphan(skb);
+#ifdef CONFIG_ERRINJECT
+ atomic_inc(&skb_count);
+#endif
+
+ /* Update the descriptors available for use. Note that update to this
+ variable races with the reclaim thread, hence an atomic operation */
+ atomic_sub(desc_required, &tx_ring->descs_avail);
+
+ if (atomic_read(&tx_ring->descs_avail) < 0) {
+ HXGE_ERR(hxgep, "Descriptors available less than zero!!");
+ }
+ /* Update descriptors available. Write the SOP descriptor entry and
+ kick the number of new entries added */
+ TXDMA_UPDATE_INDEX(tx_ring);
+ hpi_txdma_desc_kick_reg_set(hxgep->hw.hw_addr, tx_ring->tdc,
+ tx_ring->tail, tx_ring->wrap);
+
+ /* Successfully queued one network packet of <skb_len> bytes */
+
+ tx_ring->stats.opackets += si->max_frames;
+ tx_ring->stats.obytes += skb->len;
+ spin_unlock(&tx_ring->lock);
+
+ free_staging_info(hxgep, si, 0);
+ return NETDEV_TX_OK;
+}
+
+
+int hxge_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ int status;
+ if (SKB_IS_GSO(skb)) {
+ status = hxge_tx_gso(skb, netdev);
+ } else
+ status = hxge_tx_ring(skb, netdev);
+
+ return status;
+}
+
+static void update_tx_err_stats(hpi_handle_t handle, struct tx_ring_t *tx_ring)
+{
+ struct tx_ring_stats_t *stats = &tx_ring->stats;
+ tdc_drop_cnt_t drop_cnt;
+
+ TXDMA_REG_READ64(handle, TDC_DROP_CNT, tx_ring->tdc, &drop_cnt.value);
+
+ stats->hdr_error_cnt += drop_cnt.bits.hdr_size_error_count;
+ stats->abort_cnt += drop_cnt.bits.abort_count;
+ stats->runt_cnt += drop_cnt.bits.runt_count;
+
+ stats->oerrors += drop_cnt.bits.hdr_size_error_count
+ + drop_cnt.bits.abort_count
+ + drop_cnt.bits.runt_count;
+
+}
+
+static tdc_stat_t process_tx_status(struct hxge_ldv *ldvp, int ldf0, int ldf1)
+{
+ struct hxge_adapter *hxgep = ldvp->ldgp->hxgep;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ tdc_stat_t cs;
+ int channel = ldvp->ldv-HXGE_TDMA_LD_START;
+ struct tx_ring_t *tx_ring = &hxgep->tx_ring[channel];
+ struct tx_ring_stats_t *stats = &tx_ring->stats;
+ tdc_tdr_head_t head_reg;
+
+
+ /* If an LDF1 error, then wait till the qst bit is asserted before
+ * reading the TdcStat register. Otherwise, all the error information
+ * for that channel may not have been updated yet
+ */
+ if (ldf1)
+ hpi_txdma_control_reset_wait(handle, channel);
+
+ if (hpi_txdma_control_status(handle, OP_GET, channel, &cs)
+ != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_txdma_control_status failed");
+ }
+
+
+ if (cs.bits.marked) {
+ stats->marked++;
+ HXGE_DBG(hxgep, "Sent marked packet - kick reclaim thread");
+ wake_up_interruptible(&tx_ring->reclaim_event);
+ }
+
+ if (ldf1) {
+ HXGE_ERR(hxgep, "LDF1 on channel %d, cs 0x%llx",channel,cs.value);
+
+ if (cs.bits.peu_resp_err) {
+ HXGE_ERR(hxgep, "peu_resp_error");
+ stats->peu_resp_err++;
+ stats->oerrors++;
+ }
+
+ if (cs.bits.pkt_size_hdr_err) {
+ HXGE_ERR(hxgep, "pkt_size_hdr_err");
+ stats->pkt_size_hdr_err++;
+ stats->oerrors++;
+ }
+
+ if (cs.bits.runt_pkt_drop_err) {
+ HXGE_ERR(hxgep, "runt_pkt_drop_err");
+ stats->runt_pkt_drop_err++;
+ stats->oerrors++;
+ }
+
+ if (cs.bits.pkt_size_err) {
+ HXGE_ERR(hxgep, "pkt_size_err");
+ stats->pkt_size_err++;
+ stats->oerrors++;
+ }
+
+ if (cs.bits.tx_rng_oflow) {
+ HXGE_ERR(hxgep, "tx_rng_oflow");
+ stats->tx_rng_oflow++;
+ stats->oerrors++;
+ if (hpi_txdma_ring_head_get(handle, channel,
+ &head_reg) != HPI_SUCCESS)
+ {
+ HXGE_ERR(hxgep, "hpi_txdma_ring_head_get_failed for channel %d",tx_ring->tdc);
+ }
+ HXGE_ERR(hxgep, "head: 0x%x, tail : 0x%x, hwrap: %d, twrap: %d",head_reg.bits.head, tx_ring->tail, tx_ring->wrap, head_reg.bits.wrap);
+ if (!((head_reg.bits.head == tx_ring->tail) && (
+ tx_ring->wrap != head_reg.bits.wrap))) {
+ HXGE_ERR(hxgep, "False overflow!");
+ }
+ }
+
+ if (cs.bits.pref_par_err) {
+ HXGE_ERR(hxgep, "pref_par_err");
+ stats->pref_par_err++;
+ stats->oerrors++;
+ }
+
+ if (cs.bits.tdr_pref_cpl_to) {
+ HXGE_ERR(hxgep, "tdr_pref_cpl_to");
+ stats->tdr_pref_cpl_to++;
+ stats->oerrors++;
+ }
+
+ if (cs.bits.pkt_cpl_to) {
+ HXGE_ERR(hxgep, "pkt_cpl_to");
+ stats->pkt_cpl_to++;
+ stats->oerrors++;
+ }
+
+ if (cs.bits.invalid_sop) {
+ HXGE_ERR(hxgep, "invalid_sop");
+ stats->invalid_sop++;
+ stats->oerrors++;
+ }
+
+ if (cs.bits.unexpected_sop) {
+ HXGE_ERR(hxgep, "unexpected_sop");
+ stats->unexpected_sop++;
+ stats->oerrors++;
+ }
+
+ BUG();
+ /* Update discarded-packets counts from Hydra's counters */
+ update_tx_err_stats(handle, tx_ring);
+
+ }
+
+ /* Clear out the bits that require W1C */
+ hpi_txdma_control_status(handle, OP_SET, channel, &cs);
+
+ /* Do a reset of the channel (on call side) */
+ if (ldf1) {
+ set_bit(RESET_TX_CHANNEL_0+channel, &hxgep->work_q.command);
+ schedule_work(&hxgep->work_to_do);
+ }
+
+ return cs;
+}
+
+/* Transmit interrupt handler. The fact that we get here imples that this is
+ * a LDG with just one LDV. Otherwise, we would end up in hxge_intr.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+irqreturn_t hxge_tx_intr(int irq, void *data, struct pt_regs *regs)
+#else
+irqreturn_t hxge_tx_intr(int irq, void *data)
+#endif
+{
+ struct hxge_ldv *ldvp = (struct hxge_ldv *)data;
+ int got_ldf0, got_ldf1;
+
+
+ /* Determine if we have a LDF0 or LDF1 */
+ get_ldf_flags(ldvp, &got_ldf0, &got_ldf1);
+
+ /* Neither one. So, probably a shared interrupt? Return but don't
+ dimiss the interrupt but let another handler have a shot at it */
+ if (!got_ldf0 && !got_ldf1)
+ return IRQ_NONE;
+
+ /* If LDF0, then this is probably an interrupt indicating a
+ transmit completed ("marked" bit was set and triggered an
+ interrupt */
+ process_tx_status(ldvp, got_ldf0, got_ldf1);
+
+ return (IRQ_HANDLED);
+
+}
+
+
+/* Transmit error interrupt handler
+ *
+ * Called from Device Error Interrupt (ldv 31) service, not TX DMA
+ *
+ * NB: *data is Device Error ldv 31, not a TX DMA channel ldv!
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+irqreturn_t hxge_tx_deverr_intr(int irq, void *data, struct pt_regs *regs)
+#else
+irqreturn_t hxge_tx_deverr_intr(int irq, void *data)
+#endif
+{
+ struct hxge_ldv *ldvp = (struct hxge_ldv *)data; /* Device Error ldv */
+ struct hxge_adapter *hxgep = ldvp->ldgp->hxgep;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ tdc_fifo_err_stat_t sts, clr;
+ int hard_error = 0;
+
+ if (hpi_tx_fifo_status(handle, OP_GET, &sts)
+ != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hpi_tx_fifo_status failed");
+ }
+
+ HXGE_ERR(hxgep, "TX hardware error interrupt (0x%16.16x)!",
+ (unsigned int)sts.value);
+
+ clr.value = sts.value;
+
+ if (sts.bits.reord_buf_sec_err) {
+ HXGE_ERR(hxgep, "reord_buf_sec_err");
+ hxgep->statsp->tx_reorder_sec++;
+ hxgep->statsp->soft_errors++; /* Soft (recovered) dev error */
+ clr.bits.reord_buf_sec_err = 0;
+ }
+
+ if (sts.bits.reord_buf_ded_err) {
+ HXGE_ERR(hxgep, "reord_buf_ded_err");
+ hxgep->statsp->tx_reorder_ded++;
+ hxgep->statsp->tx_oerrors++; /* Tx summary count */
+ hxgep->statsp->hard_errors++; /* Hard device error */
+ hard_error = TRUE;
+ clr.bits.reord_buf_ded_err = 0;
+ }
+
+ if (sts.bits.reord_tbl_par_err) {
+ HXGE_ERR(hxgep, "reord_tbl_par_err");
+ hxgep->statsp->tx_rtab_parerr++;
+ hxgep->statsp->tx_oerrors++; /* Tx summary count */
+ hxgep->statsp->hard_errors++; /* Hard device error */
+ hard_error = TRUE;
+ clr.bits.reord_tbl_par_err = 0;
+ }
+
+ if (clr.value) {
+ HXGE_ERR(hxgep, "Unknown/unexpected/reserved TDC_FIFO_ERR_STAT bits 0x%16.16x", (unsigned int)clr.value);
+ hxgep->statsp->hard_errors++; /* Hard device error */
+ hard_error = TRUE; /* Unknown, hope TDC reset nails it */
+ }
+
+ /* Now that we have "logged" the errors, try to recover from
+ * whatever happened. Note that "SEC" (Single Bit ECC) is
+ * recovered by hardware, and needs no further action here.
+ */
+
+ /* Acknowledge error status, resume processing */
+
+ hpi_tx_fifo_status(handle, OP_SET, &sts);
+
+ /* We're probably going to hang now... */
+
+ if (hxge_ok_to_continue(hxgep)) {
+ if (hard_error) {
+ /* Hard error, data corrupt, need TDC reset */
+ hxge_disable_tx_ints(hxgep);
+ set_bit(RESET_TDC, &hxgep->work_q.command);
+ schedule_work(&hxgep->work_to_do);
+ } /* Else Corrected (single-bit) error, OK to resume */
+ } else {
+ HXGE_ERR(hxgep, "Excessive hardware error rate");
+ HXGE_ERR(hxgep, " Taking hxge device down");
+ hxge_disable_interrupts(hxgep);
+ set_bit(SHUTDOWN_ADAPTER, &hxgep->work_q.command);
+ schedule_work(&hxgep->work_to_do);
+ }
+
+ return (IRQ_HANDLED);
+}
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#ifndef _HXGE_HXGE_TXDMA_H
+#define _HXGE_HXGE_TXDMA_H
+
+#include "hxge_txdma_hw.h"
+#include <linux/llc.h>
+
+#define TXDMA_RECLAIM_PENDING_DEFAULT 64
+#define TX_FULL_MARK 3
+
+/*
+ * Descriptor ring empty:
+ * (1) head index is equal to tail index.
+ * (2) wrapped around bits are the same.
+ * Descriptor ring full:
+ * (1) head index is equal to tail index.
+ * (2) wrapped around bits are different.
+ *
+ */
+#define TXDMA_RING_EMPTY(head, head_wrap, tail, tail_wrap) \
+ ((head == tail && head_wrap == tail_wrap) ? TRUE : FALSE)
+
+#define TXDMA_RING_FULL(head, head_wrap, tail, tail_wrap) \
+ ((head == tail && head_wrap != tail_wrap) ? TRUE: FALSE)
+
+#define TXDMA_DESC_NEXT_INDEX(index, entries, wrap_mask) \
+ ((index + entries) & wrap_mask)
+
+#define TXDMA_GET_CURR_INDEX(tx_ring) \
+ tx_ring->curr_index = tx_ring->tail
+
+#define TXDMA_GET_NEXT_INDEX(tx_ring, entries) \
+ tx_ring->curr_index = (tx_ring->curr_index+entries) % tx_ring->num_tdrs;
+
+#define TXDMA_DEC_INDEX(tx_ring) \
+ tx_ring->curr_index = (tx_ring->curr_index-1); \
+ if (!tx_ring->curr_index) \
+ tx_ring->curr_index = tx_ring->num_tdrs-1;
+
+#define TXDMA_UPDATE_INDEX(tx_ring) \
+ if (tx_ring->curr_index < tx_ring->tail) \
+ tx_ring->wrap = (tx_ring->wrap == TRUE) ? FALSE : TRUE; \
+ tx_ring->tail = tx_ring->curr_index;
+
+
+#define RECLAIM_TIMEOUT 5 /* 5 ms (in jiffies) */
+
+#define HXGE_TX_DESCS_MIN 32 /* 32 entries */
+#define HXGE_TX_DESCS_DEFAULT 1024 /* 1024 entries */
+#define HXGE_TX_DESCS_MAX 5120 /* 10 4K pages worth */
+
+#define HXGE_TX_DESCS_PER_PACKET 15
+
+#define HXGE_TX_BUF_SZ_MIN 256 /* in bytes */
+#define HXGE_TX_BUF_SZ_MAX 4096 /* 4KB */
+
+#define TX_FLAGS_UNUSED 0x0
+#define TX_FLAGS_HDR 0x1 /* descriptor contains only internal header */
+#define TX_FLAGS_ALL 0x2 /* small packet; internal header + skb */
+#define TX_FLAGS_DATA 0x4 /* packet only contains data */
+#define TX_FLAGS_UNMAP 0x8 /* unmap the page when skb is freed */
+#define TX_FLAGS_ALLOC 0x10 /* allocated data page dynamically */
+
+
+#define SKB_IS_GSO(skb) skb_shinfo(skb)->gso_size
+#define SKB_GSO_SEGS(skb) skb_shinfo(skb)->gso_segs
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 19)
+#define SKB_CKSUM_OFFSET(skb) skb->csum_offset
+#define HXGE_CHECKSUM CHECKSUM_PARTIAL
+#else
+#define SKB_CKSUM_OFFSET(skb) skb->csum
+#define HXGE_CHECKSUM CHECKSUM_HW
+#endif
+
+/* When we are in GSO mode, the maximum possible number of Tx descriptors
+ * required is 7. The max possible frame size is ~9KB because this is the
+ * largest MTU possible for Hydra. Then, the math breaks down as follows
+ * 1 - for the L2/L3/L4 header (have 256 bytes of space available)
+ * 1 - fragment starting at some random offset
+ * 2 - 4096 byte fragment (Tx descriptor can only take 4076 bytes)
+ * 2 - 4096 byte fragment ( " )
+ * 1 - (potential) remnant left-over fragment
+ */
+#define DESCS_PER_FRAME 7
+
+
+/* Keep a copy of the Tx descriptor and information required to unmap the dma
+ * address once we are done with it
+ */
+struct staging_desc_t {
+ tx_desc_t entry;
+ dma_addr_t addr;
+ char *vaddr;
+ int len;
+};
+
+struct dma_map_t
+{
+ dma_addr_t dma_addr;
+ caddr_t vaddr;
+ int len;
+};
+
+/* structure that tracks the dma mappings created for the various SKB
+ fragments and the main buffer
+*/
+struct pci_dma_map_t {
+ int num_dma_mappings;
+ struct dma_map_t *dma_map;
+};
+
+#define HDR_TEMPLATE_SIZE 256
+#define TCP_FLAGS_URG 1
+#define TCP_FLAGS_PSH 2
+#define TCP_FLAGS_RST 4
+#define TCP_FLAGS_FIN 8
+struct staging_info_t {
+ int desc_idx;
+ tx_pkt_header_t pkthdr;
+ struct staging_desc_t *desc;
+ struct dma_map_t hdr_array;
+ int l3_offset;
+ int l4_offset;
+ int l4_hdr_len;
+ int tcpip_hdr_len;
+ int max_frames;
+ int frame_size;
+ int dma_map_idx;
+ int dma_map_off;
+ struct pci_dma_map_t pci_map;
+ uint32_t tcp_sequence;
+ uint16_t ip_id;
+ uint8_t tcp_flags; /* urg=0, psh=1, rst=4, fin=9 */
+ char hdr_template[HDR_TEMPLATE_SIZE];
+};
+
+struct skb_hdr_info_t {
+ struct dma_map_t hdr_array;
+ struct pci_dma_map_t pci_map;
+ uint8_t l4_payload_offset;
+};
+
+
+struct tx_buf_t {
+ struct sk_buff *skb;
+ struct dma_map_t map;
+ uint32_t flags;
+};
+
+
+struct tx_desc_buf_t {
+ dma_addr_t dma_addr;
+ tx_desc_t *vaddr; /* Tx descriptor ring */
+};
+
+struct tx_mbox_t {
+ dma_addr_t dma_addr;
+ txdma_mailbox_t *vaddr;
+};
+
+struct tx_data_buf_t {
+ dma_addr_t dma_addr;
+ caddr_t vaddr;
+};
+
+struct reclaim_data_t {
+ struct hxge_adapter *hxgep;
+ int channel;
+};
+
+struct tx_ring_stats_t {
+ uint64_t opackets;
+ uint64_t obytes;
+ uint64_t oerrors;
+ uint64_t txlock_acquire_failed;
+
+ /* Hydra specific from control/status */
+ uint64_t marked;
+ uint64_t peu_resp_err;
+ uint64_t pkt_size_hdr_err;
+ uint64_t runt_pkt_drop_err;
+ uint64_t pkt_size_err;
+ uint64_t tx_rng_oflow;
+ uint64_t pref_par_err;
+ uint64_t tdr_pref_cpl_to;
+ uint64_t pkt_cpl_to;
+ uint64_t invalid_sop;
+ uint64_t unexpected_sop;
+ uint64_t hdr_error_cnt;
+ uint64_t abort_cnt;
+ uint64_t runt_cnt;
+ uint64_t descs_used[16];
+};
+
+struct tx_ring_t {
+ int tdc; /* channel no */
+ int num_tdrs; /* number of transmit desc entries */
+ int num_tx_buffers;
+ int tx_buffer_size;
+ struct hxge_adapter *hxgep;
+ struct tx_desc_buf_t desc_ring;
+ spinlock_t lock; /* per-channel lock */
+ struct tx_data_buf_t data_buf; /* pointer to packet buffers */
+ struct tx_buf_t *tx_buf;
+ struct tx_mbox_t mbox; /* Tx mailbox */
+ int curr_index; /* running index for free desc location */
+ int tail; /* entry to write next packet in ring */
+ int wrap; /* toggle every time tail wraps around */
+ atomic_t descs_avail; /* # descs available for use */
+ unsigned long state;
+ unsigned int mark_ints;
+
+
+
+ /* reclaim unused tx buffers **/
+ wait_queue_head_t reclaim_event;
+ struct completion reclaim_complete;
+ volatile boolean_t kill_reclaim;
+ pid_t thread_pid;
+ int reclaim_head;
+ int reclaim_wrap;
+
+ /* stats */
+ struct tx_ring_stats_t stats;
+};
+
+#endif /* _HXGE_HXGE_TXDMA_H */
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#ifndef _HXGE_HXGE_TXDMA_HW_H
+#define _HXGE_HXGE_TXDMA_HW_H
+
+#include "hxge_defs.h"
+#include "hxge_tdc_hw.h"
+
+/*
+ * Transmit Packet Descriptor Structure
+ * See Hydra PRM (Chapter 8, Section 8.1.1)
+ */
+typedef union _tx_desc_t {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t sop:1;
+ uint64_t mark:1;
+ uint64_t num_ptr:4;
+ uint64_t rsvd:1;
+ uint64_t tr_len:13;
+ uint64_t sad:44;
+#else
+ uint64_t sad:44;
+ uint64_t tr_len:13;
+ uint64_t rsvd:1;
+ uint64_t num_ptr:4;
+ uint64_t mark:1;
+ uint64_t sop:1;
+#endif
+ } bits;
+} tx_desc_t, *p_tx_desc_t;
+
+/*
+ * TDC Ring Configuration
+ */
+#define TDC_TDR_CFG_STADDR_SHIFT 6 /* bits 18:6 */
+#define TDC_TDR_CFG_STADDR_MASK 0x000000000007FFC0ULL
+#define TDC_TDR_CFG_ADDR_MASK 0x00000FFFFFFFFFC0ULL
+#define TDC_TDR_CFG_STADDR_BASE_SHIFT 19 /* bits 43:19 */
+#define TDC_TDR_CFG_STADDR_BASE_MASK 0x00000FFFFFF80000ULL
+#define TDC_TDR_CFG_LEN_SHIFT 53 /* bits 63:53 */
+#define TDC_TDR_CFG_LEN_MASK 0xFFE0000000000000ULL
+#define TDC_TDR_RST_SHIFT 46
+#define TDC_TDR_RST_MASK 0x0000400000000000ULL
+
+/*
+ * Transmit Event Mask
+ */
+#define TDC_INT_MASK_MK_MASK 0x0000000000008000ULL
+
+/*
+ * Trasnmit Mailbox High
+ */
+#define TDC_MBH_SHIFT 0 /* bit 11:0 */
+#define TDC_MBH_ADDR_SHIFT 32 /* bit 43:32 */
+#define TDC_MBH_MASK 0x0000000000000FFFULL
+
+/*
+ * Trasnmit Mailbox Low
+ */
+#define TDC_MBL_SHIFT 6 /* bit 31:6 */
+#define TDC_MBL_MASK 0x00000000FFFFFFC0ULL
+
+#define TXDMA_MAILBOX_BYTE_LENGTH 64
+#define TXDMA_MAILBOX_UNUSED 24
+
+typedef struct _txdma_mailbox_t {
+ tdc_stat_t tx_cs; /* 8 bytes */
+ tdc_tdr_pre_head_t tx_dma_pre_st; /* 8 bytes */
+ tdc_tdr_head_t tx_ring_hdl; /* 8 bytes */
+ tdc_tdr_kick_t tx_ring_kick; /* 8 bytes */
+ uint32_t tx_rng_err_logh; /* 4 bytes */
+ uint32_t tx_rng_err_logl; /* 4 bytes */
+ uint8_t resv[TXDMA_MAILBOX_UNUSED];
+} txdma_mailbox_t, *p_txdma_mailbox_t;
+
+/*
+ * Internal Transmit Packet Format (16 bytes)
+ */
+#define TX_PKT_HEADER_SIZE 16
+
+
+/* A packet can be composed of no more than 15 blocks; no block can be
+ * larger than 4K in size
+ */
+
+#define TX_MAX_GATHER_POINTERS 15
+#define TX_GATHER_POINTERS_THRESHOLD 8
+/*
+ * There is bugs in the hardware
+ * and max sfter len is changed from 4096 to 4076.
+ *
+ * Jumbo from 9500 to 9216
+ */
+#define TX_MAX_TRANSFER_LENGTH 4076
+#define TX_JUMBO_MTU 9216
+
+#define TX_PKT_HEADER_PAD_SHIFT 0 /* bit 2:0 */
+#define TX_PKT_HEADER_PAD_MASK 0x0000000000000007ULL
+#define TX_PKT_HEADER_TOT_XFER_LEN_SHIFT 16 /* bit 16:29 */
+#define TX_PKT_HEADER_TOT_XFER_LEN_MASK 0x000000000000FFF8ULL
+#define TX_PKT_HEADER_L4STUFF_SHIFT 32 /* bit 37:32 */
+#define TX_PKT_HEADER_L4STUFF_MASK 0x0000003F00000000ULL
+#define TX_PKT_HEADER_L4START_SHIFT 40 /* bit 45:40 */
+#define TX_PKT_HEADER_L4START_MASK 0x00003F0000000000ULL
+#define TX_PKT_HEADER_L3START_SHIFT 48 /* bit 45:40 */
+#define TX_PKT_HEADER_IHL_SHIFT 52 /* bit 52 */
+#define TX_PKT_HEADER_VLAN__SHIFT 56 /* bit 56 */
+#define TX_PKT_HEADER_TCP_UDP_CRC32C_SHIFT 57 /* bit 57 */
+#define TX_PKT_HEADER_LLC_SHIFT 57 /* bit 57 */
+#define TX_PKT_HEADER_TCP_UDP_CRC32C_SET 0x0200000000000000ULL
+#define TX_PKT_HEADER_TCP_UDP_CRC32C_MASK 0x0200000000000000ULL
+#define TX_PKT_HEADER_L4_PROTO_OP_SHIFT 2 /* bit 59:58 */
+#define TX_PKT_HEADER_L4_PROTO_OP_MASK 0x0C00000000000000ULL
+#define TX_PKT_HEADER_V4_HDR_CS_SHIFT 60 /* bit 60 */
+#define TX_PKT_HEADER_V4_HDR_CS_SET 0x1000000000000000ULL
+#define TX_PKT_HEADER_V4_HDR_CS_MASK 0x1000000000000000ULL
+#define TX_PKT_HEADER_IP_VER_SHIFT 61 /* bit 61 */
+#define TX_PKT_HEADER_IP_VER_MASK 0x2000000000000000ULL
+#define TX_PKT_HEADER_PKT_TYPE_SHIFT 62 /* bit 62 */
+#define TX_PKT_HEADER_PKT_TYPE_MASK 0x6000000000000000ULL
+
+/* L4 Prototol Operations */
+#define TX_PKT_L4_PROTO_OP_NOP 0x00
+#define TX_PKT_L4_PROTO_OP_FULL_L4_CSUM 0x01
+#define TX_PKT_L4_PROTO_OP_L4_PAYLOAD_CSUM 0x02
+#define TX_PKT_L4_PROTO_OP_SCTP_CRC32 0x04
+
+/* Transmit Packet Types */
+#define TX_PKT_PKT_TYPE_NOP 0x00
+#define TX_PKT_PKT_TYPE_TCP 0x01
+#define TX_PKT_PKT_TYPE_UDP 0x02
+#define TX_PKT_PKT_TYPE_SCTP 0x03
+
+typedef union _tx_pkt_header_t {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t cksum_en_pkt_type:2;
+ uint64_t ip_ver:1;
+ uint64_t rsrvd:4;
+ uint64_t vlan:1;
+ uint64_t ihl:4;
+ uint64_t l3start:4;
+ uint64_t rsvrvd1:2;
+ uint64_t l4start:6;
+ uint64_t rsvrvd2:2;
+ uint64_t l4stuff:6;
+ uint64_t rsvrvd3:2;
+ uint64_t tot_xfer_len:14;
+ uint64_t rsrrvd4:13;
+ uint64_t pad:3;
+#else
+ uint64_t pad:3;
+ uint64_t rsrrvd4:13;
+ uint64_t tot_xfer_len:14;
+ uint64_t rsvrvd3:2;
+ uint64_t l4stuff:6;
+ uint64_t rsvrvd2:2;
+ uint64_t l4start:6;
+ uint64_t rsvrvd1:2;
+ uint64_t l3start:4;
+ uint64_t ihl:4;
+ uint64_t vlan:1;
+ uint64_t rsrvd:4;
+ uint64_t ip_ver:1;
+ uint64_t cksum_en_pkt_type:2;
+#endif
+ } bits;
+} tx_pkt_header_t, *p_tx_pkt_header_t;
+
+
+#define INCREMENT_DESC_INDEX(tx_ring) \
+ tx_ring->tail = tx_ring->tail + 1; \
+ if (tx_ring->tail == tx_ring->num_tdrs) { \
+ tx_ring->tail = 0; \
+ tx_ring->wrap ^= 0x1; \
+ } \
+
+
+
+#endif /* _HXGE_HXGE_TXDMA_HW_H */
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+
+#include "hpi/hpi_vmac.h"
+#include "hxge_vmac.h"
+#include "hxge.h"
+
+
+int hxge_vmac_init(struct hxge_adapter * hxgep);
+int hxge_tx_vmac_init(struct hxge_adapter * hxgep);
+int hxge_rx_vmac_init(struct hxge_adapter * hxgep);
+int hxge_tx_vmac_enable(struct hxge_adapter * hxgep);
+int hxge_tx_vmac_disable(struct hxge_adapter * hxgep);
+int hxge_rx_vmac_enable(struct hxge_adapter * hxgep);
+int hxge_rx_vmac_disable(struct hxge_adapter * hxgep);
+int hxge_tx_vmac_reset(struct hxge_adapter * hxgep);
+int hxge_rx_vmac_reset(struct hxge_adapter * hxgep);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+irqreturn_t hxge_vmac_intr(int irq, void *data, struct pt_regs *regs);
+#else
+irqreturn_t hxge_vmac_intr(int irq, void *data);
+#endif
+
+int hxge_set_promisc(struct hxge_adapter * hxgep, boolean_t on);
+
+extern int hxge_get_option (const char *option_name, int *value);
+
+
+int
+hxge_vmac_init(struct hxge_adapter * hxgep)
+{
+ if (hxge_tx_vmac_reset(hxgep))
+ goto fail;
+
+ if (hxge_rx_vmac_reset(hxgep))
+ goto fail;
+
+
+ if (hxge_tx_vmac_enable(hxgep))
+ goto fail;
+
+ if (hxge_rx_vmac_enable(hxgep))
+ goto fail;
+
+ return 0;
+fail:
+ HXGE_ERR(hxgep, "hxge_vmac_init failed");
+ return -1;
+}
+
+
+int
+hxge_vmac_uninit(struct hxge_adapter * hxgep)
+{
+ if (hxge_tx_vmac_disable(hxgep))
+ goto fail;
+
+ if (hxge_rx_vmac_disable(hxgep))
+ goto fail;
+
+ return 0;
+fail:
+ HXGE_ERR(hxgep, "hxge_vmac_uninit failed");
+ return -1;
+}
+
+
+
+/* Initialize the TxVMAC sub-block */
+
+int
+hxge_tx_vmac_init(struct hxge_adapter * hxgep)
+{
+ int rs;
+ uint64_t config;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ struct net_device *netdev = hxgep->netdev;
+
+
+ /* When setting the size of the Tx, one must account for the
+ CRC and the internal header of 16 bytes. There are no pad bytes
+ in the internal header because we guarantee internal header is
+ 16-byte aligned and so, payload that follows has the right 2-byte
+ alignment as well */
+
+ hxgep->vmac.maxframesize = netdev->mtu + ETH_HLEN + VLAN_HLEN +
+ CRC_LENGTH + TX_PKT_HEADER_SIZE;
+ hxgep->vmac.minframesize = MINIMUM_ETHERNET_FRAME_SIZE;
+
+ /* CFG_VMAC_TX_EN is done separately */
+ config = CFG_VMAC_TX_CRC_INSERT | CFG_VMAC_TX_PAD;
+
+ if ((rs = hpi_vmac_tx_config(handle, INIT, config,
+ hxgep->vmac.maxframesize)) != HPI_SUCCESS)
+ {
+ HXGE_ERR(hxgep, "hxge_tx_vmac_init: hpi_vmac_tx_config failed");
+ return -1;
+ }
+
+ hxgep->vmac.tx_config = config;
+
+ if (hxgep->vmac.is_jumbo == TRUE) {
+ HXGE_DBG(hxgep, "hxge_tx_vmac_init: Jumbo enabled, MTU %d", hxgep->vmac.maxframesize);
+ } else {
+ HXGE_DBG(hxgep, "hxge_tx_vmac_init: Jumbo disabled, MTU %d",hxgep->vmac.maxframesize);
+ }
+
+ return 0;
+}
+
+/* Initialize the RxVMAC sub-block */
+
+int
+hxge_rx_vmac_init(struct hxge_adapter * hxgep)
+{
+ int rs;
+ uint64_t xconfig;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ int stripcrc;
+ int promisc;
+ struct net_device *netdev = hxgep->netdev;
+
+
+ hxgep->vmac.rx_max_framesize = netdev->mtu + ETH_HLEN + VLAN_HLEN +
+ CRC_LENGTH;
+
+ /* CFG_VMAC_RX_EN is done separately */
+ xconfig = CFG_VMAC_RX_PASS_FLOW_CTRL_FR;
+ hxge_get_option("strip_crc", &stripcrc);
+ if (stripcrc)
+ xconfig |= CFG_VMAC_RX_STRIP_CRC;
+
+ if (hxge_get_option("promiscuous",&promisc) < 0) {
+ HXGE_ERR(hxgep, "hxge_rx_vmac_init: promiscuous invalid");
+ return -1;
+ }
+
+ if (promisc || (netdev->flags & IFF_PROMISC)) {
+ HXGE_DBG(hxgep, "hxge_rx_vmac_init: Set to promiscuous mode");
+ xconfig |= CFG_VMAC_RX_PROMISCUOUS_MODE;
+ }
+
+ if ((rs = hpi_vmac_rx_config(handle, INIT, xconfig, hxgep->vmac.rx_max_framesize))
+ != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hxge_rx_vmac_init: hpi_vmac_rx_config failed");
+ return -1;
+ }
+
+ hxgep->vmac.rx_config = xconfig;
+
+ return 0;
+}
+
+int
+hxge_vmac_rx_set_framesize(struct hxge_adapter *hxgep, uint16_t frame_size)
+{
+ if (hpi_vmac_rx_set_framesize(hxgep->hw.hw_addr, frame_size) !=
+ HPI_SUCCESS)
+ return -1;
+ return 0;
+}
+
+int
+hxge_vmac_promisc(struct hxge_adapter *hxgep, int enable)
+{
+ uint64_t xconfig;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ config_op_t cmd = DISABLE;
+
+ xconfig = CFG_VMAC_RX_PROMISCUOUS_MODE;
+ if (enable)
+ cmd = ENABLE;
+
+ if (hpi_vmac_rx_config(handle, cmd, xconfig, 0) != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hxge_vmac_promisc: hpi_vmac_rx_config failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* Enable TxVMAC */
+
+int
+hxge_tx_vmac_enable(struct hxge_adapter * hxgep)
+{
+ hpi_status_t rv;
+ int status = 0;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ int enable;
+
+
+ rv = hxge_tx_vmac_init(hxgep);
+ if (rv != 0)
+ return (rv);
+
+ rv = hpi_vmac_tx_config(handle, ENABLE, CFG_VMAC_TX_EN, 0);
+ if (rv == HPI_SUCCESS) {
+ if (hxge_get_option("enable_vmac_ints", &enable))
+ return -1;
+ rv = hpi_vmac_tx_ints(handle, enable);
+ }
+
+ status = (rv == HPI_SUCCESS) ? 0: -1;
+
+
+
+ return (status);
+}
+
+/* Disable TxVMAC */
+
+int
+hxge_tx_vmac_disable(struct hxge_adapter * hxgep)
+{
+ hpi_status_t rv;
+ int status = 0;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+
+
+ rv = hpi_vmac_tx_config(handle, DISABLE, CFG_VMAC_TX_EN, 0);
+
+ status = (rv == HPI_SUCCESS) ? 0 : -1;
+
+
+ return (status);
+}
+
+/* Enable RxVMAC */
+
+int
+hxge_rx_vmac_enable(struct hxge_adapter * hxgep)
+{
+ hpi_status_t rv;
+ int status = 0;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ int enable;
+
+
+ rv = hxge_rx_vmac_init(hxgep);
+ if (rv != 0)
+ return (rv);
+
+ rv = hpi_vmac_rx_config(handle, ENABLE, CFG_VMAC_RX_EN, 0);
+ if (rv == HPI_SUCCESS) {
+ if (hxge_get_option("enable_vmac_ints", &enable))
+ return -1;
+ rv = hpi_vmac_rx_ints(handle, enable);
+ }
+
+ status = (rv == HPI_SUCCESS) ? 0 : -1;
+
+
+ return (status);
+}
+
+/* Disable RxVMAC */
+
+int
+hxge_rx_vmac_disable(struct hxge_adapter * hxgep)
+{
+ hpi_status_t rv;
+ int status = 0;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ uint64_t xconfig;
+
+ xconfig = CFG_VMAC_RX_EN;
+ rv = hpi_vmac_rx_config(handle, DISABLE, xconfig, 0);
+
+ status = (rv == HPI_SUCCESS) ? 0 : -1;
+
+
+ return (status);
+}
+
+/* Reset TxVMAC */
+
+int
+hxge_tx_vmac_reset(struct hxge_adapter * hxgep)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+
+ hpi_tx_vmac_reset(handle);
+ hpi_tx_vmac_clear_regs(handle);
+
+ return 0;
+}
+
+/* Reset RxVMAC */
+
+int
+hxge_rx_vmac_reset(struct hxge_adapter * hxgep)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+
+ hpi_rx_vmac_reset(handle);
+ hpi_rx_vmac_clear_regs(handle);
+
+ return 0;
+}
+
+/*ARGSUSED*/
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+irqreturn_t
+hxge_vmac_intr(int irq, void *data, struct pt_regs *regs)
+#else
+irqreturn_t
+hxge_vmac_intr(int irq, void *data)
+#endif
+{
+ struct hxge_ldv *ldvp = (struct hxge_ldv *)data;
+ struct hxge_ldg *ldgp = ldvp->ldgp;
+ struct hxge_adapter * hxgep = ldgp->hxgep;
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ p_hxge_stats_t statsp;
+
+ vmac_tx_stat_t tx_stat;
+ vmac_rx_stat_t rx_stat;
+ vmac_tx_frame_cnt_t tx_frame_cnt;
+ vmac_tx_byte_cnt_t tx_byte_cnt;
+ vmac_rx_frame_cnt_t rx_frame_cnt;
+ vmac_rx_byte_cnt_t rx_byte_cnt;
+ vmac_rx_drop_fr_cnt_t rx_drop_fr_cnt;
+ vmac_rx_drop_byte_cnt_t rx_drop_byte_cnt;
+ vmac_rx_crc_cnt_t rx_crc_cnt;
+ vmac_rx_pause_cnt_t rx_pause_cnt;
+ vmac_rx_bcast_fr_cnt_t rx_bcast_fr_cnt;
+ vmac_rx_mcast_fr_cnt_t rx_mcast_fr_cnt;
+
+ int got_ldf0, got_ldf1;
+
+ get_ldf_flags(ldvp, &got_ldf0, &got_ldf1);
+ if (!got_ldf0 && !got_ldf1)
+ return IRQ_NONE;
+
+
+ /*
+ * This interrupt handler is for a specific mac port.
+ */
+ statsp = (p_hxge_stats_t)hxgep->statsp;
+
+ HXGE_REG_RD64(handle, VMAC_TX_STAT, &tx_stat.value);
+ HXGE_REG_RD64(handle, VMAC_TX_FRAME_CNT, &tx_frame_cnt.value);
+ HXGE_REG_RD64(handle, VMAC_TX_BYTE_CNT, &tx_byte_cnt.value);
+
+ HXGE_REG_RD64(handle, VMAC_RX_STAT, &rx_stat.value);
+ HXGE_REG_RD64(handle, VMAC_RX_FRAME_CNT, &rx_frame_cnt.value);
+ HXGE_REG_RD64(handle, VMAC_RX_BYTE_CNT, &rx_byte_cnt.value);
+ HXGE_REG_RD64(handle, VMAC_RX_DROP_FR_CNT, &rx_drop_fr_cnt.value);
+ HXGE_REG_RD64(handle, VMAC_RX_DROP_BYTE_CNT, &rx_drop_byte_cnt.value);
+ HXGE_REG_RD64(handle, VMAC_RX_CRC_CNT, &rx_crc_cnt.value);
+ HXGE_REG_RD64(handle, VMAC_RX_PAUSE_CNT, &rx_pause_cnt.value);
+ HXGE_REG_RD64(handle, VMAC_RX_BCAST_FR_CNT, &rx_bcast_fr_cnt.value);
+ HXGE_REG_RD64(handle, VMAC_RX_MCAST_FR_CNT, &rx_mcast_fr_cnt.value);
+
+ if (tx_stat.bits.tx_byte_cnt_overflow)
+ statsp->vmac_stats.tx_byte_cnt_overflow++;
+ if (tx_stat.bits.tx_frame_cnt_overflow)
+ statsp->vmac_stats.tx_frame_cnt_overflow++;
+ if (tx_stat.bits.frame_tx)
+ statsp->vmac_stats.frame_tx++;
+
+ if (rx_stat.bits.bcast_cnt_overflow)
+ statsp->vmac_stats.bcast_cnt_overflow++;
+ if (rx_stat.bits.mcast_cnt_overflow)
+ statsp->vmac_stats.mcast_cnt_overflow++;
+ if (rx_stat.bits.pause_cnt_overflow)
+ statsp->vmac_stats.pause_cnt_overflow++;
+ if (rx_stat.bits.crc_err_cnt_overflow)
+ statsp->vmac_stats.crc_err_cnt_overflow++;
+ if (rx_stat.bits.rx_drop_byte_cnt_overflow)
+ statsp->vmac_stats.rx_drop_byte_cnt_overflow++;
+ if (rx_stat.bits.rx_drop_frame_cnt_overflow)
+ statsp->vmac_stats.rx_drop_frame_cnt_overflow++;
+ if (rx_stat.bits.rx_byte_cnt_overflow)
+ statsp->vmac_stats.rx_byte_cnt_overflow++;
+ if (rx_stat.bits.rx_frame_cnt_overflow)
+ statsp->vmac_stats.rx_frame_cnt_overflow++;
+ if (rx_stat.bits.frame_rx)
+ statsp->vmac_stats.frame_rx++;
+
+ statsp->vmac_stats.tx_frame_cnt += tx_frame_cnt.bits.tx_frame_cnt;
+ statsp->vmac_stats.tx_byte_cnt += tx_byte_cnt.bits.tx_byte_cnt;
+
+ statsp->vmac_stats.rx_frame_cnt += rx_frame_cnt.bits.rx_frame_cnt;
+ statsp->vmac_stats.rx_byte_cnt += rx_byte_cnt.bits.rx_byte_cnt;
+ statsp->vmac_stats.rx_drop_frame_cnt +=
+ rx_drop_fr_cnt.bits.rx_drop_frame_cnt;
+ statsp->vmac_stats.rx_drop_byte_cnt +=
+ rx_drop_byte_cnt.bits.rx_drop_byte_cnt;
+ statsp->vmac_stats.rx_crc_cnt += rx_crc_cnt.bits.rx_crc_cnt;
+ statsp->vmac_stats.rx_pause_cnt += rx_pause_cnt.bits.rx_pause_cnt;
+ statsp->vmac_stats.rx_bcast_fr_cnt +=
+ rx_bcast_fr_cnt.bits.rx_bcast_fr_cnt;
+ statsp->vmac_stats.rx_mcast_fr_cnt +=
+ rx_mcast_fr_cnt.bits.rx_mcast_fr_cnt;
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Set promiscous mode
+ */
+
+int
+hxge_set_promisc(struct hxge_adapter * hxgep, boolean_t on)
+{
+ int status = 0;
+
+ spin_lock(&hxgep->lock);
+ if ((status = hxge_rx_vmac_disable(hxgep)) != 0)
+ goto fail;
+ if ((status = hxge_rx_vmac_enable(hxgep)) != 0)
+ goto fail;
+
+ if (on)
+ hxgep->vmac.promisc = TRUE;
+ else
+ hxgep->vmac.promisc = FALSE;
+
+fail:
+ spin_unlock(&hxgep->lock);
+
+ return (status);
+}
+
+
+/*
+ * Set in loopback mode
+ */
+
+int
+hxge_set_loopback(struct hxge_adapter *hxgep, boolean_t enable)
+{
+ hpi_handle_t handle = hxgep->hw.hw_addr;
+ config_op_t cmd;
+
+ spin_lock(&hxgep->lock);
+
+
+ if (enable)
+ cmd = ENABLE;
+ else
+ cmd = DISABLE;
+
+ if (hpi_vmac_rx_config(handle, CFG_VMAC_RX_LOOP_BACK, cmd, 0) != HPI_SUCCESS) {
+ HXGE_ERR(hxgep, "hxge_set_loopback: hpi_vmac_rx_config failed");
+ return -1;
+ }
+
+ hxgep->vmac.loopback = enable;
+ spin_unlock(&hxgep->lock);
+
+ return 0;
+}
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#ifndef _HXGE_HXGE_VMAC_H
+#define _HXGE_HXGE_VMAC_H
+
+#include "hxge_vmac_hw.h"
+
+/* VMAC statistics */
+
+typedef struct _hxge_vmac_stats {
+ /* vmac_tx_stat_t */
+ uint64_t tx_byte_cnt_overflow;
+ uint64_t tx_frame_cnt_overflow;
+ uint64_t frame_tx;
+
+ /* vmac_rx_stat_t */
+ uint64_t bcast_cnt_overflow;
+ uint64_t mcast_cnt_overflow;
+ uint64_t pause_cnt_overflow;
+ uint64_t crc_err_cnt_overflow;
+ uint64_t rx_drop_byte_cnt_overflow;
+ uint64_t rx_drop_frame_cnt_overflow;
+ uint64_t rx_byte_cnt_overflow;
+ uint64_t rx_frame_cnt_overflow;
+ uint64_t frame_rx;
+
+ uint64_t tx_frame_cnt; /* vmac_tx_frame_cnt_t */
+ uint64_t tx_byte_cnt; /* vmac_tx_byte_cnt_t */
+
+ uint64_t rx_frame_cnt; /* vmac_rx_frame_cnt_t */
+ uint64_t rx_byte_cnt; /* vmac_rx_byte_cnt_t */
+ uint64_t rx_drop_frame_cnt; /* vmac_rx_drop_fr_cnt_t */
+ uint64_t rx_drop_byte_cnt; /* vmac_rx_drop_byte_cnt_t */
+ uint64_t rx_crc_cnt; /* vmac_rx_crc_cnt_t */
+ uint64_t rx_pause_cnt; /* vmac_rx_pause_cnt_t */
+ uint64_t rx_bcast_fr_cnt; /* vmac_rx_bcast_fr_cnt_t */
+ uint64_t rx_mcast_fr_cnt; /* vmac_rx_mcast_fr_cnt_t */
+} hxge_vmac_stats_t, *p_hxge_vmac_stats_t;
+
+
+typedef struct _hxge_vmac {
+ boolean_t is_jumbo;
+ boolean_t promisc;
+ boolean_t loopback;
+ uint64_t tx_config;
+ uint64_t rx_config;
+ uint16_t minframesize;
+ uint16_t maxframesize;
+ uint16_t maxburstsize;
+ uint16_t rx_max_framesize;
+// uint8_t mac_addr[HXGE_MAX_MAC_ADDRS];
+} hxge_vmac_t;
+
+
+#endif /* _HXGE_HXGE_VMAC_H */
--- /dev/null
+/*****************************************************************************
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+*
+* Copyright 2009, 2011 Oracle America, Inc. 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 only, as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for
+* more details (a copy is included in the LICENSE file that accompanied this
+* code).
+*
+* You should have received a copy of the GNU General Public License version 2
+* along with this program; If not,
+* see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 or
+* visit www.oracle.com if you need additional information or have any
+* questions.
+*
+******************************************************************************/
+
+#ifndef _HXGE_VMAC_HW_H
+#define _HXGE_VMAC_HW_H
+
+#define VMAC_BASE_ADDR 0X00100000
+
+#define VMAC_RST (VMAC_BASE_ADDR + 0x0)
+#define VMAC_TX_CFG (VMAC_BASE_ADDR + 0x8)
+#define VMAC_RX_CFG (VMAC_BASE_ADDR + 0x10)
+#define VMAC_TX_STAT (VMAC_BASE_ADDR + 0x20)
+#define VMAC_TX_MSK (VMAC_BASE_ADDR + 0x28)
+#define VMAC_RX_STAT (VMAC_BASE_ADDR + 0x30)
+#define VMAC_RX_MSK (VMAC_BASE_ADDR + 0x38)
+#define VMAC_TX_STAT_MIRROR (VMAC_BASE_ADDR + 0x40)
+#define VMAC_RX_STAT_MIRROR (VMAC_BASE_ADDR + 0x48)
+#define VMAC_TX_FRAME_CNT (VMAC_BASE_ADDR + 0x100)
+#define VMAC_TX_BYTE_CNT (VMAC_BASE_ADDR + 0x108)
+#define VMAC_RX_FRAME_CNT (VMAC_BASE_ADDR + 0x120)
+#define VMAC_RX_BYTE_CNT (VMAC_BASE_ADDR + 0x128)
+#define VMAC_RX_DROP_FR_CNT (VMAC_BASE_ADDR + 0x130)
+#define VMAC_RX_DROP_BYTE_CNT (VMAC_BASE_ADDR + 0x138)
+#define VMAC_RX_CRC_CNT (VMAC_BASE_ADDR + 0x140)
+#define VMAC_RX_PAUSE_CNT (VMAC_BASE_ADDR + 0x148)
+#define VMAC_RX_BCAST_FR_CNT (VMAC_BASE_ADDR + 0x150)
+#define VMAC_RX_MCAST_FR_CNT (VMAC_BASE_ADDR + 0x158)
+
+
+/*
+ * Register: VmacRst
+ * VMAC Software Reset Command
+ * Description:
+ * Fields:
+ * Write a '1' to reset Rx VMAC; auto clears. This brings rx vmac
+ * to power on reset state.
+ * Write a '1' to reset Tx VMAC; auto clears. This brings tx vmac
+ * to power on reset state.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:55;
+ uint64_t rx_reset:1;
+ uint64_t rsrvd1:7;
+ uint64_t tx_reset:1;
+#else
+ uint64_t tx_reset:1;
+ uint64_t rsrvd1:7;
+ uint64_t rx_reset:1;
+ uint64_t rsrvd:55;
+#endif
+ } bits;
+} vmac_rst_t;
+
+
+/*
+ * Register: VmacTxCfg
+ * Tx VMAC Configuration
+ * Description:
+ * Fields:
+ * Maximum length of any total transfer gathered by Tx VMAC,
+ * including packet data, header, crc, transmit header and any
+ * pad bytes. Default value of 0x2422 represents 9220 bytes of
+ * packet data, ethernet header, and crc, 14 bytes maximum pad,
+ * and 16 bytes transmit header = 9250 (0x2422).
+ * Enable padding of short packet to meet minimum frame length of
+ * 64 bytes. Software should note that if txPad functionality is
+ * used to pad runt packets to minimum length, that crcInsert
+ * functionality (below) must also be used to provide the packet
+ * with correct L2 crc.
+ * 1: Enable generation and appending of FCS to the packets. 0:
+ * Disable generation and appending of FCS to the packets.
+ * Enable Tx VMAC. Write a '1' to enable Tx VMAC; write a '0' to
+ * disable it. This bit also propagates as vmacTdcEn to the TDC
+ * block. In TDC, the vmacTdcEn bit disables the RTab state
+ * machine. Hence, the transmission from that blade would be
+ * stopped and be queued, but no packets would be dropped. Thus,
+ * the VMAC can only be enabled/disabled at packet boundary. The
+ * VMAC will not send out portion of a packet. The currently
+ * processed packet will continue to be sent out when Tx VMAC is
+ * disabled.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t tx_max_frame_length:14;
+ uint64_t rsrvd1:15;
+ uint64_t tx_pad:1;
+ uint64_t crc_insert:1;
+ uint64_t tx_en:1;
+#else
+ uint64_t tx_en:1;
+ uint64_t crc_insert:1;
+ uint64_t tx_pad:1;
+ uint64_t rsrvd1:15;
+ uint64_t tx_max_frame_length:14;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} vmac_tx_cfg_t;
+
+
+/*
+ * Register: VmacRxCfg
+ * Rx VMAC Configuration
+ * Description: MAC address and length in Type/Length field are
+ * checked in PFC.
+ * Fields:
+ * Maximum length of a frame accepted by Rx/Tx VMAC. Only packets
+ * with length between 64 bytes and maxFrameLength will be
+ * accepted by Rx/Tx VMAC. This length indicates just the packet
+ * length excluding the packet header, crc, and any pad bytes.
+ * Maximum value is 9K (9*1024)
+ * enable packets from the same blade to loopback
+ * Enable acceptance of all Unicast packets for L2 destination
+ * address, ie, allow all Unicast packets to pass the L2
+ * filtering.
+ * Enable acceptance of all multi-cast packets, ie, allow all
+ * multi-cast packets to pass the L2 filtering.
+ * Enable the passing through of flow control frames.
+ * Enable the stripping of FCS field in the packets.
+ * Disable of FCS checking. When enable, packets with incorrect
+ * FCS value are dropped by Rx VMAC.
+ * Enable rx VMAC. Write a '1' to enable rx VMAC; write a '0' to
+ * disable it. The VMAC will begin to accept packet at the
+ * detection of the SOP (start of packet). When disable, the
+ * currently processed packet will continue to be accepted.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rx_max_frame_length:14;
+ uint64_t reserved:11;
+ uint64_t loopback:1;
+ uint64_t promiscuous_mode:1;
+ uint64_t promiscuous_group:1;
+ uint64_t pass_flow_ctrl_fr:1;
+ uint64_t strip_crc:1;
+ uint64_t crc_check_disable:1;
+ uint64_t rx_en:1;
+#else
+ uint64_t rx_en:1;
+ uint64_t crc_check_disable:1;
+ uint64_t strip_crc:1;
+ uint64_t pass_flow_ctrl_fr:1;
+ uint64_t promiscuous_group:1;
+ uint64_t promiscuous_mode:1;
+ uint64_t loopback:1;
+ uint64_t reserved:11;
+ uint64_t rx_max_frame_length:14;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} vmac_rx_cfg_t;
+
+
+/*
+ * Register: VmacTxStat
+ * Tx VMAC Status Register
+ * Description: A new interrupt will be generated only if Tx VMAC is
+ * enabled by vmacTxCfg::txEn=1. Disabling Tx VMAC does not affect
+ * currently-existing Ldf state. Writing this register affects
+ * vmacTxStatMirror register bits also the same way.
+ * Fields:
+ * Indicates that counter of byte transmitted has exceeded the
+ * max value.
+ * Indicates that counter of frame transmitted has exceeded the
+ * max value.
+ * A frame has been successfully transmitted.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:61;
+ uint64_t tx_byte_cnt_overflow:1;
+ uint64_t tx_frame_cnt_overflow:1;
+ uint64_t frame_tx:1;
+#else
+ uint64_t frame_tx:1;
+ uint64_t tx_frame_cnt_overflow:1;
+ uint64_t tx_byte_cnt_overflow:1;
+ uint64_t rsrvd:61;
+#endif
+ } bits;
+} vmac_tx_stat_t;
+
+
+/*
+ * Register: VmacTxMsk
+ * Tx VMAC Status Mask
+ * Description: masking vmacTxStat from interrupt.
+ * Fields:
+ * 1: mask interrupt due to overflow of counter of byte
+ * transmitted
+ * 1: mask interrupt due to overflow of counter of frame
+ * transmitted
+ * 1: mask interrupt due to successful transmition of frame.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:61;
+ uint64_t tx_byte_cnt_overflow_msk:1;
+ uint64_t tx_frame_cnt_overflow_msk:1;
+ uint64_t frame_tx_msk:1;
+#else
+ uint64_t frame_tx_msk:1;
+ uint64_t tx_frame_cnt_overflow_msk:1;
+ uint64_t tx_byte_cnt_overflow_msk:1;
+ uint64_t rsrvd:61;
+#endif
+ } bits;
+} vmac_tx_msk_t;
+
+
+/*
+ * Register: VmacRxStat
+ * Rx VMAC Status Register
+ * Description: Overflow indicators are read-only registers; Read off
+ * the counters to clear. A new interrupt will be generated only if
+ * Rx VMAC is enabled by vmacRxCfg::rxEn=1. Disabling Rx VMAC does
+ * not affect currently-existing Ldf state. Writing this register
+ * affects vmacRxStatMirror register bits also the same way.
+ * Fields:
+ * Indicates that the counter for broadcast packets has exceeded
+ * the max value.
+ * Indicates that the counter for multicast packets has exceeded
+ * the max value.
+ * Indicates that the counter for pause packets has exceeded the
+ * max value.
+ * Indicates that the counter for packets with mismatched FCS has
+ * exceeded the max value.
+ * Indicates that counter of dropped byte has exceeded the max
+ * value.
+ * Indicates that counter of dropped frame has exceeded the max
+ * value.
+ * Indicates that counter of byte received has exceeded the max
+ * value.
+ * Indicates that counter of frame received has exceeded the max
+ * value.
+ * A valid frame has been successfully received.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:55;
+ uint64_t bcast_cnt_overflow:1;
+ uint64_t mcast_cnt_overflow:1;
+ uint64_t pause_cnt_overflow:1;
+ uint64_t crc_err_cnt_overflow:1;
+ uint64_t rx_drop_byte_cnt_overflow:1;
+ uint64_t rx_drop_frame_cnt_overflow:1;
+ uint64_t rx_byte_cnt_overflow:1;
+ uint64_t rx_frame_cnt_overflow:1;
+ uint64_t frame_rx:1;
+#else
+ uint64_t frame_rx:1;
+ uint64_t rx_frame_cnt_overflow:1;
+ uint64_t rx_byte_cnt_overflow:1;
+ uint64_t rx_drop_frame_cnt_overflow:1;
+ uint64_t rx_drop_byte_cnt_overflow:1;
+ uint64_t crc_err_cnt_overflow:1;
+ uint64_t pause_cnt_overflow:1;
+ uint64_t mcast_cnt_overflow:1;
+ uint64_t bcast_cnt_overflow:1;
+ uint64_t rsrvd:55;
+#endif
+ } bits;
+} vmac_rx_stat_t;
+
+
+/*
+ * Register: VmacRxMsk
+ * Rx VMAC Status Mask
+ * Description:
+ * Fields:
+ * 1: mask interrupt due to overflow of the counter for broadcast
+ * packets
+ * 1: mask interrupt due to overflow of the counter for multicast
+ * packets
+ * 1: mask interrupt due to overflow of the counter for pause
+ * packets
+ * 1: mask interrupt due to overflow of the counter for packets
+ * with mismatched FCS the max value.
+ * 1: mask interrupt due to overflow of dropped byte counter
+ * 1: mask interrupt due to overflow of dropped frame counter
+ * 1: mask interrupt due to overflow of received byte counter
+ * 1: mask interrupt due to overflow of received frame counter
+ * 1: mask interrupt due to a valid frame has been successfully
+ * received.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:55;
+ uint64_t bcast_cnt_overflow_msk:1;
+ uint64_t mcast_cnt_overflow_msk:1;
+ uint64_t pause_cnt_overflow_msk:1;
+ uint64_t crc_err_cnt_overflow_msk:1;
+ uint64_t rx_drop_byte_cnt_overflow_msk:1;
+ uint64_t rx_drop_frame_cnt_overflow_msk:1;
+ uint64_t rx_byte_cnt_overflow_msk:1;
+ uint64_t rx_frame_cnt_overflow_msk:1;
+ uint64_t frame_rx_msk:1;
+#else
+ uint64_t frame_rx_msk:1;
+ uint64_t rx_frame_cnt_overflow_msk:1;
+ uint64_t rx_byte_cnt_overflow_msk:1;
+ uint64_t rx_drop_frame_cnt_overflow_msk:1;
+ uint64_t rx_drop_byte_cnt_overflow_msk:1;
+ uint64_t crc_err_cnt_overflow_msk:1;
+ uint64_t pause_cnt_overflow_msk:1;
+ uint64_t mcast_cnt_overflow_msk:1;
+ uint64_t bcast_cnt_overflow_msk:1;
+ uint64_t rsrvd:55;
+#endif
+ } bits;
+} vmac_rx_msk_t;
+
+
+/*
+ * Register: VmacTxStatMirror
+ * Tx VMAC Status Mirror Register
+ * Description: Write a 1 to this register to force the corresponding
+ * interrupt. Reading this register returns the current Tx interrupt
+ * status which would be the same as reading the vmacTxStat register.
+ * The bits are cleared by writing 1 to the corresponding register
+ * bit in the vmacTxStat register. ie, bit 0 of this register is
+ * cleared by writing 1 to bit 0 in the vmacTxStat register.
+ *
+ * Fields:
+ * 1 : Force tx byte counter overflow interrupt generation
+ * 1 : Force tx frame counter overflow interrupt generation
+ * 1 : Force frame transmitted interrupt generation
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:61;
+ uint64_t force_tx_byte_cnt_overflow:1;
+ uint64_t force_tx_frame_cnt_overflow:1;
+ uint64_t force_frame_tx:1;
+#else
+ uint64_t force_frame_tx:1;
+ uint64_t force_tx_frame_cnt_overflow:1;
+ uint64_t force_tx_byte_cnt_overflow:1;
+ uint64_t rsrvd:61;
+#endif
+ } bits;
+} vmac_tx_stat_mirror_t;
+
+
+/*
+ * Register: VmacRxStatMirror
+ * Rx VMAC Status Mirror Register
+ * Description: Write a 1 to this register to force the corresponding
+ * interrupt. Reading this register returns the current Rx interrupt
+ * status which would be the same as reading the vmacRxStat register.
+ * The bits are cleared by writing 1 to the corresponding register
+ * bit in the vmacRxStat register. ie, bit 0 of this register is
+ * cleared by writing 1 to bit 0 in the vmacRxStat register.
+ * Fields:
+ * 1 : Force broadcast frame counter overflow interrupt
+ * generation
+ * 1 : Force multicast frame counter overflow interrupt
+ * generation
+ * 1 : Force pause frame counter overflow interrupt generation
+ * 1 : Force crc error counter overflow interrupt generation
+ * 1 : Force dropped frames byte counter overflow interrupt
+ * generation
+ * 1 : Force dropped frame counter overflow interrupt generation
+ * 1 : Force rx byte counter overflow interrupt generation
+ * 1 : Force rx frame counter overflow interrupt generation
+ * 1 : Force frame received interrupt generation
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:55;
+ uint64_t force_bcast_cnt_overflow:1;
+ uint64_t force_mcast_cnt_overflow:1;
+ uint64_t force_pause_cnt_overflow:1;
+ uint64_t force_crc_err_cnt_overflow:1;
+ uint64_t force_rx_drop_byte_cnt_overflow:1;
+ uint64_t force_rx_drop_frame_cnt_overflow:1;
+ uint64_t force_rx_byte_cnt_overflow:1;
+ uint64_t force_rx_frame_cnt_overflow:1;
+ uint64_t force_frame_rx:1;
+#else
+ uint64_t force_frame_rx:1;
+ uint64_t force_rx_frame_cnt_overflow:1;
+ uint64_t force_rx_byte_cnt_overflow:1;
+ uint64_t force_rx_drop_frame_cnt_overflow:1;
+ uint64_t force_rx_drop_byte_cnt_overflow:1;
+ uint64_t force_crc_err_cnt_overflow:1;
+ uint64_t force_pause_cnt_overflow:1;
+ uint64_t force_mcast_cnt_overflow:1;
+ uint64_t force_bcast_cnt_overflow:1;
+ uint64_t rsrvd:55;
+#endif
+ } bits;
+} vmac_rx_stat_mirror_t;
+
+
+/*
+ * Register: VmacTxFrameCnt
+ * VMAC transmitted frame counter
+ * Description:
+ * Fields:
+ * Indicates the number of frames transmitted by Tx VMAC. The
+ * counter will saturate at max value. The counter is stalled
+ * when Tx VMAC is disabled by vmacTxCfg::txEn=0
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t tx_frame_cnt:32;
+#else
+ uint64_t tx_frame_cnt:32;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} vmac_tx_frame_cnt_t;
+
+
+/*
+ * Register: VmacTxByteCnt
+ * VMAC transmitted byte counter
+ * Description:
+ * Fields:
+ * Indicates the number of byte (octet) of data transmitted by Tx
+ * VMAC. This counter counts all the bytes of the incoming data
+ * including packet header, packet data, crc, and pad bytes. The
+ * counter will saturate at max value. The counter is stalled
+ * when Tx VMAC is disabled by vmacTxCfg::txEn=0
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t tx_byte_cnt:32;
+#else
+ uint64_t tx_byte_cnt:32;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} vmac_tx_byte_cnt_t;
+
+
+/*
+ * Register: VmacRxFrameCnt
+ * VMAC received frame counter
+ * Description:
+ * Fields:
+ * Indicates the number of frame received by Rx VMAC. The counter
+ * will saturate at max value. The counter is stalled when Rx
+ * VMAC is disabled by vmacRxCfg::rxEn=0.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rx_frame_cnt:32;
+#else
+ uint64_t rx_frame_cnt:32;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} vmac_rx_frame_cnt_t;
+
+
+/*
+ * Register: VmacRxByteCnt
+ * VMAC received byte counter
+ * Description:
+ * Fields:
+ * Indicates the number of bytes (octet) of data received by Rx
+ * VMAC including any error frames. The counter will saturate at
+ * max value. The counter is stalled when Rx VMAC is disabled by
+ * vmacRxCfg::rxEn=0.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rx_byte_cnt:32;
+#else
+ uint64_t rx_byte_cnt:32;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} vmac_rx_byte_cnt_t;
+
+
+/*
+ * Register: VmacRxDropFrCnt
+ * VMAC dropped frame counter
+ * Description:
+ * Fields:
+ * Indicates the number of frame dropped by Rx VMAC. The counter
+ * will This counter increments for every frame dropped for the
+ * following: - crc mismatch & crc check is enabled - failed the
+ * L2 address match & Vmac is not in promiscuous mode - pause
+ * packet & Vmac is not programmed to pass these frames The
+ * counter will saturate at max value. The counter is stalled
+ * when Rx VMAC is disabled by vmacRxCfg::rxEn=0.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rx_drop_frame_cnt:32;
+#else
+ uint64_t rx_drop_frame_cnt:32;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} vmac_rx_drop_fr_cnt_t;
+
+
+/*
+ * Register: VmacRxDropByteCnt
+ * VMAC dropped byte counter
+ * Description:
+ * Fields:
+ * Indicates the number of byte of data dropped by Rx VMAC.
+ * Frames are dropped for one of the follg conditions : - crc
+ * mismatch & crc check is enabled - failed the L2 address match
+ * & Vmac is not in promiscuous mode - pause packet & Vmac is not
+ * programmed to pass these frames The counter will saturate at
+ * max value. The counter is stalled when Rx VMAC is disabled by
+ * vmacRxCfg::rxEn=0.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rx_drop_byte_cnt:32;
+#else
+ uint64_t rx_drop_byte_cnt:32;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} vmac_rx_drop_byte_cnt_t;
+
+
+/*
+ * Register: VmacRxCrcCnt
+ * VMAC received CRC error frame counter
+ * Description:
+ * Fields:
+ * Indicates the number of frames with invalid CRC. When NMAC
+ * truncates a packet, it asserts crcError indication to VMAC
+ * which then counts it towards CRC error. Thus the VMAC crc
+ * error counter reflects the CRC mismatches on all the packets
+ * going out of RxMAC while the NMAC crc error counter reflects
+ * the CRC mismatches on all the packets coming into RxMAC. The
+ * counter will saturate at max value The counter is stalled when
+ * Rx VMAC is disabled by vmacRxCfg::rxEn=0.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rx_crc_cnt:32;
+#else
+ uint64_t rx_crc_cnt:32;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} vmac_rx_crc_cnt_t;
+
+
+/*
+ * Register: VmacRxPauseCnt
+ * VMAC received pause frame counter
+ * Description:
+ * Fields:
+ * Count the number of pause frames received by Rx VMAC. The
+ * counter is stalled when Rx VMAC is disabled by
+ * vmacRxCfg::rxEn=0.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rx_pause_cnt:32;
+#else
+ uint64_t rx_pause_cnt:32;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} vmac_rx_pause_cnt_t;
+
+
+/*
+ * Register: VmacRxBcastFrCnt
+ * VMAC received broadcast frame counter
+ * Description:
+ * Fields:
+ * Indicates the number of broadcast frames received The counter
+ * is stalled when Rx VMAC is disabled by vmacRxCfg::rxEn=0.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rx_bcast_fr_cnt:32;
+#else
+ uint64_t rx_bcast_fr_cnt:32;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} vmac_rx_bcast_fr_cnt_t;
+
+
+/*
+ * Register: VmacRxMcastFrCnt
+ * VMAC received multicast frame counter
+ * Description:
+ * Fields:
+ * Indicates the number of multicast frames received The counter
+ * is stalled when Rx VMAC is disabled by vmacRxCfg::rxEn=0.
+ */
+typedef union {
+ uint64_t value;
+ struct {
+#if defined(__BIG_ENDIAN)
+ uint64_t rsrvd:32;
+ uint64_t rx_mcast_fr_cnt:32;
+#else
+ uint64_t rx_mcast_fr_cnt:32;
+ uint64_t rsrvd:32;
+#endif
+ } bits;
+} vmac_rx_mcast_fr_cnt_t;
+
+
+#endif /* _HXGE_VMAC_HW_H */
CONFIG_QLCNIC=m
CONFIG_QLGE=m
CONFIG_BNA=m
+CONFIG_HXGE=m
CONFIG_SFC=m
CONFIG_SFC_MTD=y
CONFIG_BE2NET=m