From: Maxim Uvarov Date: Tue, 27 Mar 2012 22:07:54 +0000 (-0700) Subject: add hxge-1.3.3 driver X-Git-Tag: v2.6.39-400.9.0~565^2~1 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=b2b7f06270c61fcd193fe69607ff1990da47fa08;p=users%2Fjedix%2Flinux-maple.git add hxge-1.3.3 driver Signed-off-by: Maxim Uvarov --- diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 93359fab240e..003116b8bb2f 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2927,6 +2927,8 @@ source "drivers/net/sfc/Kconfig" source "drivers/net/benet/Kconfig" +source "drivers/net/hxge/Kconfig" + endif # NETDEV_10000 source "drivers/net/tokenring/Kconfig" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index d5ce0115e065..5add04207dd1 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -38,7 +38,7 @@ obj-$(CONFIG_JME) += jme.o 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 diff --git a/drivers/net/hxge/Kconfig b/drivers/net/hxge/Kconfig new file mode 100644 index 000000000000..1b77e561acdc --- /dev/null +++ b/drivers/net/hxge/Kconfig @@ -0,0 +1,24 @@ +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 for more + information. + + If in doubt, say N. + diff --git a/drivers/net/hxge/Makefile b/drivers/net/hxge/Makefile new file mode 100644 index 000000000000..6bed51d11b7c --- /dev/null +++ b/drivers/net/hxge/Makefile @@ -0,0 +1,18 @@ +# 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 diff --git a/drivers/net/hxge/hpi/hpi.c b/drivers/net/hxge/hpi/hpi.c new file mode 100644 index 000000000000..6b53a730efdc --- /dev/null +++ b/drivers/net/hxge/hpi/hpi.c @@ -0,0 +1,75 @@ +/***************************************************************************** +* 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 +#else +#include +#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) +{ +} diff --git a/drivers/net/hxge/hpi/hpi.h b/drivers/net/hxge/hpi/hpi.h new file mode 100644 index 000000000000..92090b8b3c09 --- /dev/null +++ b/drivers/net/hxge/hpi/hpi.h @@ -0,0 +1,220 @@ +/***************************************************************************** +* 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 */ diff --git a/drivers/net/hxge/hpi/hpi_pfc.c b/drivers/net/hxge/hpi/hpi_pfc.c new file mode 100644 index 000000000000..ed7bcc69005b --- /dev/null +++ b/drivers/net/hxge/hpi/hpi_pfc.c @@ -0,0 +1,873 @@ +/***************************************************************************** +* 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 +#include +#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); +} diff --git a/drivers/net/hxge/hpi/hpi_pfc.h b/drivers/net/hxge/hpi/hpi_pfc.h new file mode 100644 index 000000000000..2fe363890734 --- /dev/null +++ b/drivers/net/hxge/hpi/hpi_pfc.h @@ -0,0 +1,208 @@ +/***************************************************************************** +* 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 */ diff --git a/drivers/net/hxge/hpi/hpi_rxdma.c b/drivers/net/hxge/hpi/hpi_rxdma.c new file mode 100644 index 000000000000..92fa0a54dcfa --- /dev/null +++ b/drivers/net/hxge/hpi/hpi_rxdma.c @@ -0,0 +1,834 @@ +/***************************************************************************** +* 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); +} diff --git a/drivers/net/hxge/hpi/hpi_rxdma.h b/drivers/net/hxge/hpi/hpi_rxdma.h new file mode 100644 index 000000000000..f1bfc844e20c --- /dev/null +++ b/drivers/net/hxge/hpi/hpi_rxdma.h @@ -0,0 +1,266 @@ +/***************************************************************************** +* 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 */ diff --git a/drivers/net/hxge/hpi/hpi_txdma.c b/drivers/net/hxge/hpi/hpi_txdma.c new file mode 100644 index 000000000000..4952333382af --- /dev/null +++ b/drivers/net/hxge/hpi/hpi_txdma.c @@ -0,0 +1,903 @@ +/***************************************************************************** +* 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); +} diff --git a/drivers/net/hxge/hpi/hpi_txdma.h b/drivers/net/hxge/hpi/hpi_txdma.h new file mode 100644 index 000000000000..c08fbaa8bbc4 --- /dev/null +++ b/drivers/net/hxge/hpi/hpi_txdma.h @@ -0,0 +1,166 @@ +/***************************************************************************** +* 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 */ diff --git a/drivers/net/hxge/hpi/hpi_vir.c b/drivers/net/hxge/hpi/hpi_vir.c new file mode 100644 index 000000000000..a3c61a1e0296 --- /dev/null +++ b/drivers/net/hxge/hpi/hpi_vir.c @@ -0,0 +1,309 @@ +/***************************************************************************** +* 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); +} diff --git a/drivers/net/hxge/hpi/hpi_vir.h b/drivers/net/hxge/hpi/hpi_vir.h new file mode 100644 index 000000000000..e45d2bffc828 --- /dev/null +++ b/drivers/net/hxge/hpi/hpi_vir.h @@ -0,0 +1,118 @@ +/***************************************************************************** +* 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 */ diff --git a/drivers/net/hxge/hpi/hpi_vmac.c b/drivers/net/hxge/hpi/hpi_vmac.c new file mode 100644 index 000000000000..2447b8f7c5ac --- /dev/null +++ b/drivers/net/hxge/hpi/hpi_vmac.c @@ -0,0 +1,370 @@ +/***************************************************************************** +* 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)); +} diff --git a/drivers/net/hxge/hpi/hpi_vmac.h b/drivers/net/hxge/hpi/hpi_vmac.h new file mode 100644 index 000000000000..41a2aa96cd2b --- /dev/null +++ b/drivers/net/hxge/hpi/hpi_vmac.h @@ -0,0 +1,59 @@ +/***************************************************************************** +* 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 */ diff --git a/drivers/net/hxge/hxge.h b/drivers/net/hxge/hxge.h new file mode 100644 index 000000000000..0d8efa9ec722 --- /dev/null +++ b/drivers/net/hxge/hxge.h @@ -0,0 +1,482 @@ +/***************************************************************************** +* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef NETIF_F_TSO +#include +#endif +#include +#include +#include +#include + +#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_ */ diff --git a/drivers/net/hxge/hxge_classify.h b/drivers/net/hxge/hxge_classify.h new file mode 100644 index 000000000000..71e4fcab11fe --- /dev/null +++ b/drivers/net/hxge/hxge_classify.h @@ -0,0 +1,82 @@ +/***************************************************************************** +* 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 */ diff --git a/drivers/net/hxge/hxge_defs.h b/drivers/net/hxge/hxge_defs.h new file mode 100644 index 000000000000..5c99bf82126a --- /dev/null +++ b/drivers/net/hxge/hxge_defs.h @@ -0,0 +1,516 @@ +/***************************************************************************** +* 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 +#include + +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 */ diff --git a/drivers/net/hxge/hxge_ethtool.c b/drivers/net/hxge/hxge_ethtool.c new file mode 100644 index 000000000000..f08d838467e1 --- /dev/null +++ b/drivers/net/hxge/hxge_ethtool.c @@ -0,0 +1,1060 @@ +/***************************************************************************** +* 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); + +} diff --git a/drivers/net/hxge/hxge_intr.c b/drivers/net/hxge/hxge_intr.c new file mode 100644 index 000000000000..29e0c86b32b1 --- /dev/null +++ b/drivers/net/hxge/hxge_intr.c @@ -0,0 +1,851 @@ +/***************************************************************************** +* 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; +} + diff --git a/drivers/net/hxge/hxge_main.c b/drivers/net/hxge/hxge_main.c new file mode 100644 index 000000000000..65a58ddab754 --- /dev/null +++ b/drivers/net/hxge/hxge_main.c @@ -0,0 +1,1315 @@ +/***************************************************************************** +* 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, "); +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 diff --git a/drivers/net/hxge/hxge_other.c b/drivers/net/hxge/hxge_other.c new file mode 100644 index 000000000000..4a743a3d4124 --- /dev/null +++ b/drivers/net/hxge/hxge_other.c @@ -0,0 +1,411 @@ +/***************************************************************************** +* 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; + +} diff --git a/drivers/net/hxge/hxge_param.c b/drivers/net/hxge/hxge_param.c new file mode 100644 index 000000000000..39c7ab7e23d7 --- /dev/null +++ b/drivers/net/hxge/hxge_param.c @@ -0,0 +1,237 @@ +/***************************************************************************** +* 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; +} diff --git a/drivers/net/hxge/hxge_peu_hw.h b/drivers/net/hxge/hxge_peu_hw.h new file mode 100644 index 000000000000..afbe973ef139 --- /dev/null +++ b/drivers/net/hxge/hxge_peu_hw.h @@ -0,0 +1,600 @@ +/***************************************************************************** +* 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 */ diff --git a/drivers/net/hxge/hxge_pfc.c b/drivers/net/hxge/hxge_pfc.c new file mode 100644 index 000000000000..b0521132c4be --- /dev/null +++ b/drivers/net/hxge/hxge_pfc.c @@ -0,0 +1,1108 @@ +/***************************************************************************** +* 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; +} diff --git a/drivers/net/hxge/hxge_pfc.h b/drivers/net/hxge/hxge_pfc.h new file mode 100644 index 000000000000..801f0f57ea81 --- /dev/null +++ b/drivers/net/hxge/hxge_pfc.h @@ -0,0 +1,315 @@ +/***************************************************************************** +* 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 */ diff --git a/drivers/net/hxge/hxge_pfc_hw.h b/drivers/net/hxge/hxge_pfc_hw.h new file mode 100644 index 000000000000..f620c5f2322a --- /dev/null +++ b/drivers/net/hxge/hxge_pfc_hw.h @@ -0,0 +1,765 @@ +/***************************************************************************** +* 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 */ diff --git a/drivers/net/hxge/hxge_rdc_hw.h b/drivers/net/hxge/hxge_rdc_hw.h new file mode 100644 index 000000000000..b376f109aee5 --- /dev/null +++ b/drivers/net/hxge/hxge_rdc_hw.h @@ -0,0 +1,1601 @@ +/***************************************************************************** +* 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 */ diff --git a/drivers/net/hxge/hxge_rxdma.c b/drivers/net/hxge/hxge_rxdma.c new file mode 100644 index 000000000000..e9f4bf029900 --- /dev/null +++ b/drivers/net/hxge/hxge_rxdma.c @@ -0,0 +1,1959 @@ +/***************************************************************************** +* 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); +} diff --git a/drivers/net/hxge/hxge_rxdma.h b/drivers/net/hxge/hxge_rxdma.h new file mode 100644 index 000000000000..8776e0fe380f --- /dev/null +++ b/drivers/net/hxge/hxge_rxdma.h @@ -0,0 +1,436 @@ +/***************************************************************************** +* 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 diff --git a/drivers/net/hxge/hxge_stats.c b/drivers/net/hxge/hxge_stats.c new file mode 100644 index 000000000000..0701f973aae2 --- /dev/null +++ b/drivers/net/hxge/hxge_stats.c @@ -0,0 +1,104 @@ +/***************************************************************************** +* 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; +} diff --git a/drivers/net/hxge/hxge_sysfs.c b/drivers/net/hxge/hxge_sysfs.c new file mode 100644 index 000000000000..36ed6797bc97 --- /dev/null +++ b/drivers/net/hxge/hxge_sysfs.c @@ -0,0 +1,112 @@ +/***************************************************************************** +* 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 + +#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 +} diff --git a/drivers/net/hxge/hxge_tdc_hw.h b/drivers/net/hxge/hxge_tdc_hw.h new file mode 100644 index 000000000000..f2425f3eb66b --- /dev/null +++ b/drivers/net/hxge/hxge_tdc_hw.h @@ -0,0 +1,1384 @@ +/***************************************************************************** +* 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 */ diff --git a/drivers/net/hxge/hxge_txdma.c b/drivers/net/hxge/hxge_txdma.c new file mode 100644 index 000000000000..7070f013db5e --- /dev/null +++ b/drivers/net/hxge/hxge_txdma.c @@ -0,0 +1,2218 @@ +/***************************************************************************** +* 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 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 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); +} diff --git a/drivers/net/hxge/hxge_txdma.h b/drivers/net/hxge/hxge_txdma.h new file mode 100644 index 000000000000..3b119df47ec6 --- /dev/null +++ b/drivers/net/hxge/hxge_txdma.h @@ -0,0 +1,252 @@ +/***************************************************************************** +* 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 + +#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 */ diff --git a/drivers/net/hxge/hxge_txdma_hw.h b/drivers/net/hxge/hxge_txdma_hw.h new file mode 100644 index 000000000000..d5d8aedbbbe6 --- /dev/null +++ b/drivers/net/hxge/hxge_txdma_hw.h @@ -0,0 +1,206 @@ +/***************************************************************************** +* 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 */ diff --git a/drivers/net/hxge/hxge_vmac.c b/drivers/net/hxge/hxge_vmac.c new file mode 100644 index 000000000000..cd0b0068fafe --- /dev/null +++ b/drivers/net/hxge/hxge_vmac.c @@ -0,0 +1,481 @@ +/***************************************************************************** +* 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; +} diff --git a/drivers/net/hxge/hxge_vmac.h b/drivers/net/hxge/hxge_vmac.h new file mode 100644 index 000000000000..1c2ca1f10b92 --- /dev/null +++ b/drivers/net/hxge/hxge_vmac.h @@ -0,0 +1,78 @@ +/***************************************************************************** +* 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 */ diff --git a/drivers/net/hxge/hxge_vmac_hw.h b/drivers/net/hxge/hxge_vmac_hw.h new file mode 100644 index 000000000000..dd40164cebca --- /dev/null +++ b/drivers/net/hxge/hxge_vmac_hw.h @@ -0,0 +1,683 @@ +/***************************************************************************** +* 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 */ diff --git a/uek-rpm/ol6/config-generic b/uek-rpm/ol6/config-generic index b67b1887e174..3f62b409d6c3 100644 --- a/uek-rpm/ol6/config-generic +++ b/uek-rpm/ol6/config-generic @@ -1897,6 +1897,7 @@ CONFIG_BNX2X=m CONFIG_QLCNIC=m CONFIG_QLGE=m CONFIG_BNA=m +CONFIG_HXGE=m CONFIG_SFC=m CONFIG_SFC_MTD=y CONFIG_BE2NET=m