diff options
Diffstat (limited to 'qemu/roms/ipxe/src/drivers/net/phantom')
-rw-r--r-- | qemu/roms/ipxe/src/drivers/net/phantom/nx_bitops.h | 195 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/drivers/net/phantom/nxhal_nic_interface.h | 501 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/drivers/net/phantom/phantom.c | 2177 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/drivers/net/phantom/phantom.h | 213 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/drivers/net/phantom/phantom_hw.h | 185 |
5 files changed, 3271 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/drivers/net/phantom/nx_bitops.h b/qemu/roms/ipxe/src/drivers/net/phantom/nx_bitops.h new file mode 100644 index 000000000..15f3d3767 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/phantom/nx_bitops.h @@ -0,0 +1,195 @@ +#ifndef _NX_BITOPS_H +#define _NX_BITOPS_H + +/* + * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * @file + * + * NetXen bit operations + * + */ + +/** Datatype used to represent a bit in the pseudo-structures */ +typedef unsigned char pseudo_bit_t; + +/** + * Wrapper structure for pseudo_bit_t structures + * + * This structure provides a wrapper around pseudo_bit_t structures. + * It has the correct size, and also encapsulates type information + * about the underlying pseudo_bit_t-based structure, which allows the + * NX_FILL etc. macros to work without requiring explicit type + * information. + */ +#define NX_PSEUDO_BIT_STRUCT( _structure ) \ + union { \ + uint8_t bytes[ sizeof ( _structure ) / 8 ]; \ + uint64_t qwords[ sizeof ( _structure ) / 64 ]; \ + _structure *dummy[0]; \ + } u; + +/** Get pseudo_bit_t structure type from wrapper structure pointer */ +#define NX_PSEUDO_STRUCT( _ptr ) \ + typeof ( *((_ptr)->u.dummy[0]) ) + +/** Bit offset of a field within a pseudo_bit_t structure */ +#define NX_BIT_OFFSET( _ptr, _field ) \ + offsetof ( NX_PSEUDO_STRUCT ( _ptr ), _field ) + +/** Bit width of a field within a pseudo_bit_t structure */ +#define NX_BIT_WIDTH( _ptr, _field ) \ + sizeof ( ( ( NX_PSEUDO_STRUCT ( _ptr ) * ) NULL )->_field ) + +/** Qword offset of a field within a pseudo_bit_t structure */ +#define NX_QWORD_OFFSET( _ptr, _field ) \ + ( NX_BIT_OFFSET ( _ptr, _field ) / 64 ) + +/** Qword bit offset of a field within a pseudo_bit_t structure + * + * Yes, using mod-64 would work, but would lose the check for the + * error of specifying a mismatched field name and qword index. + */ +#define NX_QWORD_BIT_OFFSET( _ptr, _index, _field ) \ + ( NX_BIT_OFFSET ( _ptr, _field ) - ( 64 * (_index) ) ) + +/** Bit mask for a field within a pseudo_bit_t structure */ +#define NX_BIT_MASK( _ptr, _field ) \ + ( ( ~( ( uint64_t ) 0 ) ) >> \ + ( 64 - NX_BIT_WIDTH ( _ptr, _field ) ) ) + +/* + * Assemble native-endian qword from named fields and values + * + */ + +#define NX_ASSEMBLE_1( _ptr, _index, _field, _value ) \ + ( ( ( uint64_t) (_value) ) << \ + NX_QWORD_BIT_OFFSET ( _ptr, _index, _field ) ) + +#define NX_ASSEMBLE_2( _ptr, _index, _field, _value, ... ) \ + ( NX_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + NX_ASSEMBLE_1 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_ASSEMBLE_3( _ptr, _index, _field, _value, ... ) \ + ( NX_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + NX_ASSEMBLE_2 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_ASSEMBLE_4( _ptr, _index, _field, _value, ... ) \ + ( NX_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + NX_ASSEMBLE_3 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_ASSEMBLE_5( _ptr, _index, _field, _value, ... ) \ + ( NX_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + NX_ASSEMBLE_4 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_ASSEMBLE_6( _ptr, _index, _field, _value, ... ) \ + ( NX_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + NX_ASSEMBLE_5 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_ASSEMBLE_7( _ptr, _index, _field, _value, ... ) \ + ( NX_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + NX_ASSEMBLE_6 ( _ptr, _index, __VA_ARGS__ ) ) + +/* + * Build native-endian (positive) qword bitmasks from named fields + * + */ + +#define NX_MASK_1( _ptr, _index, _field ) \ + ( NX_BIT_MASK ( _ptr, _field ) << \ + NX_QWORD_BIT_OFFSET ( _ptr, _index, _field ) ) + +#define NX_MASK_2( _ptr, _index, _field, ... ) \ + ( NX_MASK_1 ( _ptr, _index, _field ) | \ + NX_MASK_1 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_MASK_3( _ptr, _index, _field, ... ) \ + ( NX_MASK_1 ( _ptr, _index, _field ) | \ + NX_MASK_2 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_MASK_4( _ptr, _index, _field, ... ) \ + ( NX_MASK_1 ( _ptr, _index, _field ) | \ + NX_MASK_3 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_MASK_5( _ptr, _index, _field, ... ) \ + ( NX_MASK_1 ( _ptr, _index, _field ) | \ + NX_MASK_4 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_MASK_6( _ptr, _index, _field, ... ) \ + ( NX_MASK_1 ( _ptr, _index, _field ) | \ + NX_MASK_5 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_MASK_7( _ptr, _index, _field, ... ) \ + ( NX_MASK_1 ( _ptr, _index, _field ) | \ + NX_MASK_6 ( _ptr, _index, __VA_ARGS__ ) ) + +/* + * Populate big-endian qwords from named fields and values + * + */ + +#define NX_FILL( _ptr, _index, _assembled ) \ + do { \ + uint64_t *__ptr = &(_ptr)->u.qwords[(_index)]; \ + uint64_t __assembled = (_assembled); \ + *__ptr = cpu_to_le64 ( __assembled ); \ + } while ( 0 ) + +#define NX_FILL_1( _ptr, _index, ... ) \ + NX_FILL ( _ptr, _index, NX_ASSEMBLE_1 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_FILL_2( _ptr, _index, ... ) \ + NX_FILL ( _ptr, _index, NX_ASSEMBLE_2 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_FILL_3( _ptr, _index, ... ) \ + NX_FILL ( _ptr, _index, NX_ASSEMBLE_3 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_FILL_4( _ptr, _index, ... ) \ + NX_FILL ( _ptr, _index, NX_ASSEMBLE_4 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_FILL_5( _ptr, _index, ... ) \ + NX_FILL ( _ptr, _index, NX_ASSEMBLE_5 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_FILL_6( _ptr, _index, ... ) \ + NX_FILL ( _ptr, _index, NX_ASSEMBLE_6 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_FILL_7( _ptr, _index, ... ) \ + NX_FILL ( _ptr, _index, NX_ASSEMBLE_7 ( _ptr, _index, __VA_ARGS__ ) ) + +/** Extract value of named field */ +#define NX_GET64( _ptr, _field ) \ + ( { \ + unsigned int __index = NX_QWORD_OFFSET ( _ptr, _field ); \ + uint64_t *__ptr = &(_ptr)->u.qwords[__index]; \ + uint64_t __value = le64_to_cpu ( *__ptr ); \ + __value >>= \ + NX_QWORD_BIT_OFFSET ( _ptr, __index, _field ); \ + __value &= NX_BIT_MASK ( _ptr, _field ); \ + __value; \ + } ) + +/** Extract value of named field (for fields up to the size of a long) */ +#define NX_GET( _ptr, _field ) \ + ( ( unsigned long ) NX_GET64 ( _ptr, _field ) ) + +#endif /* _NX_BITOPS_H */ diff --git a/qemu/roms/ipxe/src/drivers/net/phantom/nxhal_nic_interface.h b/qemu/roms/ipxe/src/drivers/net/phantom/nxhal_nic_interface.h new file mode 100644 index 000000000..f487624bf --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/phantom/nxhal_nic_interface.h @@ -0,0 +1,501 @@ +FILE_LICENCE ( GPL2_ONLY ); + +/* + * Data types and structure for HAL - NIC interface. + * + */ + +#ifndef _NXHAL_NIC_INTERFACE_H_ +#define _NXHAL_NIC_INTERFACE_H_ + +/***************************************************************************** + * Simple Types + *****************************************************************************/ + +typedef U32 nx_reg_addr_t; + +/***************************************************************************** + * Root crb-based firmware commands + *****************************************************************************/ + +/* CRB Root Command + + A single set of crbs is used across all physical/virtual + functions for capability queries, initialization, and + context creation/destruction. + + There are 4 CRBS: + Command/Response CRB + Argument1 CRB + Argument2 CRB + Argument3 CRB + Signature CRB + + The cmd/rsp crb is always intiated by the host via + a command code and always responded by the card with + a response code. The cmd and rsp codes are disjoint. + The sequence of use is always CMD, RSP, CLEAR CMD. + + The arguments are for passing in command specific + and response specific parameters/data. + + The signature is composed of a magic value, the + pci function id, and a command sequence id: + [7:0] = pci function + [15:8] = version + [31:16] = magic of 0xcafe + + The pci function allows the card to take correct + action for the given particular commands. + The firmware will attempt to detect + an errant driver that has died while holding + the root crb hardware lock. Such an error condition + shows up as the cmd/rsp crb stuck in a non-clear state. + + Interface Sequence: + Host always makes requests and firmware always responds. + Note that data field is always set prior to command field. + + [READ] CMD/RSP CRB ARGUMENT FIELD + Host grab lock + Host -> CMD optional parameter + FW <- (Good) RSP-OK DATA + FW <- (Fail) RSP-FAIL optional failure code + Host -> CLEAR + Host release lock + + [WRITE] CMD/RSP CRB ARGUMENT FIELD + Host grab lock + Host -> CMD DATA + FW <- (Good) RSP-OK optional write status + FW <- (Write) RSP-FAIL optional failure code + Host -> CLEAR + Host release lock + +*/ + + +/***************************************************************************** + * CMD/RSP + *****************************************************************************/ + +#define NX_CDRP_SIGNATURE_TO_PCIFN(sign) ((sign) & 0xff) +#define NX_CDRP_SIGNATURE_TO_VERSION(sign) (((sign)>>8) & 0xff) +#define NX_CDRP_SIGNATURE_TO_MAGIC(sign) (((sign)>>16) & 0xffff) +#define NX_CDRP_SIGNATURE_VALID(sign) \ + ( NX_CDRP_SIGNATURE_TO_MAGIC(sign) == 0xcafe && \ + NX_CDRP_SIGNATURE_TO_PCIFN(sign) < 8) +#define NX_CDRP_SIGNATURE_MAKE(pcifn,version) \ + ( ((pcifn) & 0xff) | \ + (((version) & 0xff) << 8) | \ + (0xcafe << 16) ) + +#define NX_CDRP_CLEAR 0x00000000 +#define NX_CDRP_CMD_BIT 0x80000000 + +/* All responses must have the NX_CDRP_CMD_BIT cleared + * in the crb NX_CDRP_CRB_OFFSET. */ +#define NX_CDRP_FORM_RSP(rsp) (rsp) +#define NX_CDRP_IS_RSP(rsp) (((rsp) & NX_CDRP_CMD_BIT) == 0) + +#define NX_CDRP_RSP_OK 0x00000001 +#define NX_CDRP_RSP_FAIL 0x00000002 +#define NX_CDRP_RSP_TIMEOUT 0x00000003 + +/* All commands must have the NX_CDRP_CMD_BIT set in + * the crb NX_CDRP_CRB_OFFSET. + * The macros below do not have it explicitly set to + * allow their use in lookup tables */ +#define NX_CDRP_FORM_CMD(cmd) (NX_CDRP_CMD_BIT | (cmd)) +#define NX_CDRP_IS_CMD(cmd) (((cmd) & NX_CDRP_CMD_BIT) != 0) + +/* [CMD] Capability Vector [RSP] Capability Vector */ +#define NX_CDRP_CMD_SUBMIT_CAPABILITIES 0x00000001 + +/* [CMD] - [RSP] Query Value */ +#define NX_CDRP_CMD_READ_MAX_RDS_PER_CTX 0x00000002 + +/* [CMD] - [RSP] Query Value */ +#define NX_CDRP_CMD_READ_MAX_SDS_PER_CTX 0x00000003 + +/* [CMD] - [RSP] Query Value */ +#define NX_CDRP_CMD_READ_MAX_RULES_PER_CTX 0x00000004 + +/* [CMD] - [RSP] Query Value */ +#define NX_CDRP_CMD_READ_MAX_RX_CTX 0x00000005 + +/* [CMD] - [RSP] Query Value */ +#define NX_CDRP_CMD_READ_MAX_TX_CTX 0x00000006 + +/* [CMD] Rx Config DMA Addr [RSP] rcode */ +#define NX_CDRP_CMD_CREATE_RX_CTX 0x00000007 + +/* [CMD] Rx Context Handle, Reset Kind [RSP] rcode */ +#define NX_CDRP_CMD_DESTROY_RX_CTX 0x00000008 + +/* [CMD] Tx Config DMA Addr [RSP] rcode */ +#define NX_CDRP_CMD_CREATE_TX_CTX 0x00000009 + +/* [CMD] Tx Context Handle, Reset Kind [RSP] rcode */ +#define NX_CDRP_CMD_DESTROY_TX_CTX 0x0000000a + +/* [CMD] Stat setup dma addr - [RSP] Handle, rcode */ +#define NX_CDRP_CMD_SETUP_STATISTICS 0x0000000e + +/* [CMD] Handle - [RSP] rcode */ +#define NX_CDRP_CMD_GET_STATISTICS 0x0000000f + +/* [CMD] Handle - [RSP] rcode */ +#define NX_CDRP_CMD_DELETE_STATISTICS 0x00000010 + +#define NX_CDRP_CMD_MAX 0x00000011 + +/***************************************************************************** + * Capabilities + *****************************************************************************/ + +#define NX_CAP_BIT(class, bit) (1 << bit) + +/* Class 0 (i.e. ARGS 1) + */ +#define NX_CAP0_LEGACY_CONTEXT NX_CAP_BIT(0, 0) +#define NX_CAP0_MULTI_CONTEXT NX_CAP_BIT(0, 1) +#define NX_CAP0_LEGACY_MN NX_CAP_BIT(0, 2) +#define NX_CAP0_LEGACY_MS NX_CAP_BIT(0, 3) +#define NX_CAP0_CUT_THROUGH NX_CAP_BIT(0, 4) +#define NX_CAP0_LRO NX_CAP_BIT(0, 5) +#define NX_CAP0_LSO NX_CAP_BIT(0, 6) + +/* Class 1 (i.e. ARGS 2) + */ +#define NX_CAP1_NIC NX_CAP_BIT(1, 0) +#define NX_CAP1_PXE NX_CAP_BIT(1, 1) +#define NX_CAP1_CHIMNEY NX_CAP_BIT(1, 2) +#define NX_CAP1_LSA NX_CAP_BIT(1, 3) +#define NX_CAP1_RDMA NX_CAP_BIT(1, 4) +#define NX_CAP1_ISCSI NX_CAP_BIT(1, 5) +#define NX_CAP1_FCOE NX_CAP_BIT(1, 6) + +/* Class 2 (i.e. ARGS 3) + */ + +/***************************************************************************** + * Rules + *****************************************************************************/ + +typedef U32 nx_rx_rule_type_t; + +#define NX_RX_RULETYPE_DEFAULT 0 +#define NX_RX_RULETYPE_MAC 1 +#define NX_RX_RULETYPE_MAC_VLAN 2 +#define NX_RX_RULETYPE_MAC_RSS 3 +#define NX_RX_RULETYPE_MAC_VLAN_RSS 4 +#define NX_RX_RULETYPE_MAX 5 + +typedef U32 nx_rx_rule_cmd_t; + +#define NX_RX_RULECMD_ADD 0 +#define NX_RX_RULECMD_REMOVE 1 +#define NX_RX_RULECMD_MAX 2 + +typedef struct nx_rx_rule_arg_s { + union { + struct { + char mac[6]; + } m; + struct { + char mac[6]; + char vlan; + } mv; + struct { + char mac[6]; + } mr; + struct { + char mac[6]; + char vlan; + } mvr; + }; + /* will be union of all the different args for rules */ + U64 data; +} nx_rx_rule_arg_t; + +typedef struct nx_rx_rule_s { + U32 id; + U32 active; + nx_rx_rule_arg_t arg; + nx_rx_rule_type_t type; +} nx_rx_rule_t; + +/* MSG - REQUIRES TX CONTEXT */ + +/* The rules can be added/deleted from both the + * host and card sides so rq/rsp are similar. + */ +typedef struct nx_hostmsg_rx_rule_s { + nx_rx_rule_cmd_t cmd; + nx_rx_rule_t rule; +} nx_hostmsg_rx_rule_t; + +typedef struct nx_cardmsg_rx_rule_s { + nx_rcode_t rcode; + nx_rx_rule_cmd_t cmd; + nx_rx_rule_t rule; +} nx_cardmsg_rx_rule_t; + + +/***************************************************************************** + * Common to Rx/Tx contexts + *****************************************************************************/ + +/* + * Context states + */ + +typedef U32 nx_host_ctx_state_t; + +#define NX_HOST_CTX_STATE_FREED 0 /* Invalid state */ +#define NX_HOST_CTX_STATE_ALLOCATED 1 /* Not committed */ +/* The following states imply FW is aware of context */ +#define NX_HOST_CTX_STATE_ACTIVE 2 +#define NX_HOST_CTX_STATE_DISABLED 3 +#define NX_HOST_CTX_STATE_QUIESCED 4 +#define NX_HOST_CTX_STATE_MAX 5 + +/* + * Interrupt mask crb use must be set identically on the Tx + * and Rx context configs across a pci function + */ + +/* Rx and Tx have unique interrupt/crb */ +#define NX_HOST_INT_CRB_MODE_UNIQUE 0 +/* Rx and Tx share a common interrupt/crb */ +#define NX_HOST_INT_CRB_MODE_SHARED 1 /* <= LEGACY */ +/* Rx does not use a crb */ +#define NX_HOST_INT_CRB_MODE_NORX 2 +/* Tx does not use a crb */ +#define NX_HOST_INT_CRB_MODE_NOTX 3 +/* Neither Rx nor Tx use a crb */ +#define NX_HOST_INT_CRB_MODE_NORXTX 4 + +/* + * Destroy Rx/Tx + */ + +#define NX_DESTROY_CTX_RESET 0 +#define NX_DESTROY_CTX_D3_RESET 1 +#define NX_DESTROY_CTX_MAX 2 + + +/***************************************************************************** + * Tx + *****************************************************************************/ + +/* + * Components of the host-request for Tx context creation. + * CRB - DOES NOT REQUIRE Rx/TX CONTEXT + */ + +typedef struct nx_hostrq_cds_ring_s { + U64 host_phys_addr; /* Ring base addr */ + U32 ring_size; /* Ring entries */ + U32 rsvd; /* Padding */ +} nx_hostrq_cds_ring_t; + +typedef struct nx_hostrq_tx_ctx_s { + U64 host_rsp_dma_addr; /* Response dma'd here */ + U64 cmd_cons_dma_addr; /* */ + U64 dummy_dma_addr; /* */ + U32 capabilities[4]; /* Flag bit vector */ + U32 host_int_crb_mode; /* Interrupt crb usage */ + U32 rsvd1; /* Padding */ + U16 rsvd2; /* Padding */ + U16 interrupt_ctl; + U16 msi_index; + U16 rsvd3; /* Padding */ + nx_hostrq_cds_ring_t cds_ring; /* Desc of cds ring */ + U8 reserved[128]; /* future expansion */ +} nx_hostrq_tx_ctx_t; + +typedef struct nx_cardrsp_cds_ring_s { + U32 host_producer_crb; /* Crb to use */ + U32 interrupt_crb; /* Crb to use */ +} nx_cardrsp_cds_ring_t; + +typedef struct nx_cardrsp_tx_ctx_s { + U32 host_ctx_state; /* Starting state */ + U16 context_id; /* Handle for context */ + U8 phys_port; /* Physical id of port */ + U8 virt_port; /* Virtual/Logical id of port */ + nx_cardrsp_cds_ring_t cds_ring; /* Card cds settings */ + U8 reserved[128]; /* future expansion */ +} nx_cardrsp_tx_ctx_t; + +#define SIZEOF_HOSTRQ_TX(HOSTRQ_TX) \ + ( sizeof(HOSTRQ_TX)) + +#define SIZEOF_CARDRSP_TX(CARDRSP_TX) \ + ( sizeof(CARDRSP_TX)) + +/***************************************************************************** + * Rx + *****************************************************************************/ + +/* + * RDS ring mapping to producer crbs + */ + +/* Each ring has a unique crb */ +#define NX_HOST_RDS_CRB_MODE_UNIQUE 0 /* <= LEGACY */ + +/* All configured RDS Rings share common crb: + 1 Ring - same as unique + 2 Rings - 16, 16 + 3 Rings - 10, 10, 10 */ +#define NX_HOST_RDS_CRB_MODE_SHARED 1 + +/* Bit usage is specified per-ring using the + ring's size. Sum of bit lengths must be <= 32. + Packing is [Ring N] ... [Ring 1][Ring 0] */ +#define NX_HOST_RDS_CRB_MODE_CUSTOM 2 +#define NX_HOST_RDS_CRB_MODE_MAX 3 + + +/* + * RDS Ting Types + */ + +#define NX_RDS_RING_TYPE_NORMAL 0 +#define NX_RDS_RING_TYPE_JUMBO 1 +#define NX_RDS_RING_TYPE_LRO 2 +#define NX_RDS_RING_TYPE_MAX 3 + +/* + * Components of the host-request for Rx context creation. + * CRB - DOES NOT REQUIRE Rx/TX CONTEXT + */ + +typedef struct nx_hostrq_sds_ring_s { + U64 host_phys_addr; /* Ring base addr */ + U32 ring_size; /* Ring entries */ + U16 msi_index; + U16 rsvd; /* Padding */ +} nx_hostrq_sds_ring_t; + +typedef struct nx_hostrq_rds_ring_s { + U64 host_phys_addr; /* Ring base addr */ + U64 buff_size; /* Packet buffer size */ + U32 ring_size; /* Ring entries */ + U32 ring_kind; /* Class of ring */ +} nx_hostrq_rds_ring_t; + +typedef struct nx_hostrq_rx_ctx_s { + U64 host_rsp_dma_addr; /* Response dma'd here */ + U32 capabilities[4]; /* Flag bit vector */ + U32 host_int_crb_mode; /* Interrupt crb usage */ + U32 host_rds_crb_mode; /* RDS crb usage */ + /* These ring offsets are relative to data[0] below */ + U32 rds_ring_offset; /* Offset to RDS config */ + U32 sds_ring_offset; /* Offset to SDS config */ + U16 num_rds_rings; /* Count of RDS rings */ + U16 num_sds_rings; /* Count of SDS rings */ + U16 rsvd1; /* Padding */ + U16 rsvd2; /* Padding */ + U8 reserved[128]; /* reserve space for future expansion*/ + /* MUST BE 64-bit aligned. + The following is packed: + - N hostrq_rds_rings + - N hostrq_sds_rings */ + char data[0]; +} nx_hostrq_rx_ctx_t; + +typedef struct nx_cardrsp_rds_ring_s { + U32 host_producer_crb; /* Crb to use */ + U32 rsvd1; /* Padding */ +} nx_cardrsp_rds_ring_t; + +typedef struct nx_cardrsp_sds_ring_s { + U32 host_consumer_crb; /* Crb to use */ + U32 interrupt_crb; /* Crb to use */ +} nx_cardrsp_sds_ring_t; + +typedef struct nx_cardrsp_rx_ctx_s { + /* These ring offsets are relative to data[0] below */ + U32 rds_ring_offset; /* Offset to RDS config */ + U32 sds_ring_offset; /* Offset to SDS config */ + U32 host_ctx_state; /* Starting State */ + U32 num_fn_per_port; /* How many PCI fn share the port */ + U16 num_rds_rings; /* Count of RDS rings */ + U16 num_sds_rings; /* Count of SDS rings */ + U16 context_id; /* Handle for context */ + U8 phys_port; /* Physical id of port */ + U8 virt_port; /* Virtual/Logical id of port */ + U8 reserved[128]; /* save space for future expansion */ + /* MUST BE 64-bit aligned. + The following is packed: + - N cardrsp_rds_rings + - N cardrs_sds_rings */ + char data[0]; +} nx_cardrsp_rx_ctx_t; + +#define SIZEOF_HOSTRQ_RX(HOSTRQ_RX, rds_rings, sds_rings) \ + ( sizeof(HOSTRQ_RX) + \ + (rds_rings)*(sizeof (nx_hostrq_rds_ring_t)) + \ + (sds_rings)*(sizeof (nx_hostrq_sds_ring_t)) ) + +#define SIZEOF_CARDRSP_RX(CARDRSP_RX, rds_rings, sds_rings) \ + ( sizeof(CARDRSP_RX) + \ + (rds_rings)*(sizeof (nx_cardrsp_rds_ring_t)) + \ + (sds_rings)*(sizeof (nx_cardrsp_sds_ring_t)) ) + + +/***************************************************************************** + * Statistics + *****************************************************************************/ + +/* + * The model of statistics update to use + */ + +#define NX_STATISTICS_MODE_INVALID 0 + +/* Permanent setup; Updates are only sent on explicit request + (NX_CDRP_CMD_GET_STATISTICS) */ +#define NX_STATISTICS_MODE_PULL 1 + +/* Permanent setup; Updates are sent automatically and on + explicit request (NX_CDRP_CMD_GET_STATISTICS) */ +#define NX_STATISTICS_MODE_PUSH 2 + +/* One time stat update. */ +#define NX_STATISTICS_MODE_SINGLE_SHOT 3 + +#define NX_STATISTICS_MODE_MAX 4 + +/* + * What set of stats + */ +#define NX_STATISTICS_TYPE_INVALID 0 +#define NX_STATISTICS_TYPE_NIC_RX_CORE 1 +#define NX_STATISTICS_TYPE_NIC_TX_CORE 2 +#define NX_STATISTICS_TYPE_NIC_RX_ALL 3 +#define NX_STATISTICS_TYPE_NIC_TX_ALL 4 +#define NX_STATISTICS_TYPE_MAX 5 + + +/* + * Request to setup statistics gathering. + * CRB - DOES NOT REQUIRE Rx/TX CONTEXT + */ + +typedef struct nx_hostrq_stat_setup_s { + U64 host_stat_buffer; /* Where to dma stats */ + U32 host_stat_size; /* Size of stat buffer */ + U16 context_id; /* Which context */ + U16 stat_type; /* What class of stats */ + U16 stat_mode; /* When to update */ + U16 stat_interval; /* Frequency of update */ +} nx_hostrq_stat_setup_t; + + + +#endif /* _NXHAL_NIC_INTERFACE_H_ */ diff --git a/qemu/roms/ipxe/src/drivers/net/phantom/phantom.c b/qemu/roms/ipxe/src/drivers/net/phantom/phantom.c new file mode 100644 index 000000000..e70ded08c --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/phantom/phantom.c @@ -0,0 +1,2177 @@ +/* + * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. + * Copyright (C) 2008 NetXen, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <assert.h> +#include <byteswap.h> +#include <ipxe/pci.h> +#include <ipxe/io.h> +#include <ipxe/malloc.h> +#include <ipxe/iobuf.h> +#include <ipxe/netdevice.h> +#include <ipxe/if_ether.h> +#include <ipxe/ethernet.h> +#include <ipxe/spi.h> +#include <ipxe/settings.h> +#include "phantom.h" + +/** + * @file + * + * NetXen Phantom NICs + * + */ + +/** Maximum number of ports */ +#define PHN_MAX_NUM_PORTS 8 + +/** Maximum time to wait for command PEG to initialise + * + * BUGxxxx + * + * The command PEG will currently report initialisation complete only + * when at least one PHY has detected a link (so that the global PHY + * clock can be set to 10G/1G as appropriate). This can take a very, + * very long time. + * + * A future firmware revision should decouple PHY initialisation from + * firmware initialisation, at which point the command PEG will report + * initialisation complete much earlier, and this timeout can be + * reduced. + */ +#define PHN_CMDPEG_INIT_TIMEOUT_SEC 50 + +/** Maximum time to wait for receive PEG to initialise */ +#define PHN_RCVPEG_INIT_TIMEOUT_SEC 2 + +/** Maximum time to wait for firmware to accept a command */ +#define PHN_ISSUE_CMD_TIMEOUT_MS 2000 + +/** Maximum time to wait for test memory */ +#define PHN_TEST_MEM_TIMEOUT_MS 100 + +/** Maximum time to wait for CLP command to be issued */ +#define PHN_CLP_CMD_TIMEOUT_MS 500 + +/** Link state poll frequency + * + * The link state will be checked once in every N calls to poll(). + */ +#define PHN_LINK_POLL_FREQUENCY 4096 + +/** Number of RX descriptors */ +#define PHN_NUM_RDS 32 + +/** RX maximum fill level. Must be strictly less than PHN_NUM_RDS. */ +#define PHN_RDS_MAX_FILL 16 + +/** RX buffer size */ +#define PHN_RX_BUFSIZE ( 32 /* max LL padding added by card */ + \ + ETH_FRAME_LEN ) + +/** Number of RX status descriptors */ +#define PHN_NUM_SDS 32 + +/** Number of TX descriptors */ +#define PHN_NUM_CDS 8 + +/** A Phantom descriptor ring set */ +struct phantom_descriptor_rings { + /** RX descriptors */ + struct phantom_rds rds[PHN_NUM_RDS]; + /** RX status descriptors */ + struct phantom_sds sds[PHN_NUM_SDS]; + /** TX descriptors */ + union phantom_cds cds[PHN_NUM_CDS]; + /** TX consumer index */ + volatile uint32_t cmd_cons; +}; + +/** RX context creation request and response buffers */ +struct phantom_create_rx_ctx_rqrsp { + struct { + struct nx_hostrq_rx_ctx_s rx_ctx; + struct nx_hostrq_rds_ring_s rds; + struct nx_hostrq_sds_ring_s sds; + } __unm_dma_aligned hostrq; + struct { + struct nx_cardrsp_rx_ctx_s rx_ctx; + struct nx_cardrsp_rds_ring_s rds; + struct nx_cardrsp_sds_ring_s sds; + } __unm_dma_aligned cardrsp; +}; + +/** TX context creation request and response buffers */ +struct phantom_create_tx_ctx_rqrsp { + struct { + struct nx_hostrq_tx_ctx_s tx_ctx; + } __unm_dma_aligned hostrq; + struct { + struct nx_cardrsp_tx_ctx_s tx_ctx; + } __unm_dma_aligned cardrsp; +}; + +/** A Phantom NIC */ +struct phantom_nic { + /** BAR 0 */ + void *bar0; + /** Current CRB window */ + unsigned long crb_window; + /** CRB window access method */ + unsigned long ( *crb_access ) ( struct phantom_nic *phantom, + unsigned long reg ); + + + /** Port number */ + unsigned int port; + + + /** RX context ID */ + uint16_t rx_context_id; + /** RX descriptor producer CRB offset */ + unsigned long rds_producer_crb; + /** RX status descriptor consumer CRB offset */ + unsigned long sds_consumer_crb; + /** RX interrupt mask CRB offset */ + unsigned long sds_irq_mask_crb; + /** RX interrupts enabled */ + unsigned int sds_irq_enabled; + + /** RX producer index */ + unsigned int rds_producer_idx; + /** RX consumer index */ + unsigned int rds_consumer_idx; + /** RX status consumer index */ + unsigned int sds_consumer_idx; + /** RX I/O buffers */ + struct io_buffer *rds_iobuf[PHN_RDS_MAX_FILL]; + + + /** TX context ID */ + uint16_t tx_context_id; + /** TX descriptor producer CRB offset */ + unsigned long cds_producer_crb; + + /** TX producer index */ + unsigned int cds_producer_idx; + /** TX consumer index */ + unsigned int cds_consumer_idx; + /** TX I/O buffers */ + struct io_buffer *cds_iobuf[PHN_NUM_CDS]; + + + /** Descriptor rings */ + struct phantom_descriptor_rings *desc; + + + /** Last known link state */ + uint32_t link_state; + /** Link state poll timer */ + unsigned long link_poll_timer; + + + /** Non-volatile settings */ + struct settings settings; +}; + +/** Interrupt mask registers */ +static const unsigned long phantom_irq_mask_reg[PHN_MAX_NUM_PORTS] = { + UNM_PCIE_IRQ_MASK_F0, + UNM_PCIE_IRQ_MASK_F1, + UNM_PCIE_IRQ_MASK_F2, + UNM_PCIE_IRQ_MASK_F3, + UNM_PCIE_IRQ_MASK_F4, + UNM_PCIE_IRQ_MASK_F5, + UNM_PCIE_IRQ_MASK_F6, + UNM_PCIE_IRQ_MASK_F7, +}; + +/** Interrupt status registers */ +static const unsigned long phantom_irq_status_reg[PHN_MAX_NUM_PORTS] = { + UNM_PCIE_IRQ_STATUS_F0, + UNM_PCIE_IRQ_STATUS_F1, + UNM_PCIE_IRQ_STATUS_F2, + UNM_PCIE_IRQ_STATUS_F3, + UNM_PCIE_IRQ_STATUS_F4, + UNM_PCIE_IRQ_STATUS_F5, + UNM_PCIE_IRQ_STATUS_F6, + UNM_PCIE_IRQ_STATUS_F7, +}; + +/*************************************************************************** + * + * CRB register access + * + */ + +/** + * Prepare for access to CRB register via 128MB BAR + * + * @v phantom Phantom NIC + * @v reg Register offset within abstract address space + * @ret offset Register offset within PCI BAR0 + */ +static unsigned long phantom_crb_access_128m ( struct phantom_nic *phantom, + unsigned long reg ) { + unsigned long offset = ( 0x6000000 + ( reg & 0x1ffffff ) ); + uint32_t window = ( reg & 0x2000000 ); + uint32_t verify_window; + + if ( phantom->crb_window != window ) { + + /* Write to the CRB window register */ + writel ( window, phantom->bar0 + UNM_128M_CRB_WINDOW ); + + /* Ensure that the write has reached the card */ + verify_window = readl ( phantom->bar0 + UNM_128M_CRB_WINDOW ); + assert ( verify_window == window ); + + /* Record new window */ + phantom->crb_window = window; + } + + return offset; +} + +/** + * Prepare for access to CRB register via 32MB BAR + * + * @v phantom Phantom NIC + * @v reg Register offset within abstract address space + * @ret offset Register offset within PCI BAR0 + */ +static unsigned long phantom_crb_access_32m ( struct phantom_nic *phantom, + unsigned long reg ) { + unsigned long offset = ( reg & 0x1ffffff ); + uint32_t window = ( reg & 0x2000000 ); + uint32_t verify_window; + + if ( phantom->crb_window != window ) { + + /* Write to the CRB window register */ + writel ( window, phantom->bar0 + UNM_32M_CRB_WINDOW ); + + /* Ensure that the write has reached the card */ + verify_window = readl ( phantom->bar0 + UNM_32M_CRB_WINDOW ); + assert ( verify_window == window ); + + /* Record new window */ + phantom->crb_window = window; + } + + return offset; +} + +/** + * Prepare for access to CRB register via 2MB BAR + * + * @v phantom Phantom NIC + * @v reg Register offset within abstract address space + * @ret offset Register offset within PCI BAR0 + */ +static unsigned long phantom_crb_access_2m ( struct phantom_nic *phantom, + unsigned long reg ) { + static const struct { + uint8_t block; + uint16_t window_hi; + } reg_window_hi[] = { + { UNM_CRB_BLK_PCIE, 0x773 }, + { UNM_CRB_BLK_CAM, 0x416 }, + { UNM_CRB_BLK_ROMUSB, 0x421 }, + { UNM_CRB_BLK_TEST, 0x295 }, + { UNM_CRB_BLK_PEG_0, 0x340 }, + { UNM_CRB_BLK_PEG_1, 0x341 }, + { UNM_CRB_BLK_PEG_2, 0x342 }, + { UNM_CRB_BLK_PEG_3, 0x343 }, + { UNM_CRB_BLK_PEG_4, 0x34b }, + }; + unsigned int block = UNM_CRB_BLK ( reg ); + unsigned long offset = UNM_CRB_OFFSET ( reg ); + uint32_t window; + uint32_t verify_window; + unsigned int i; + + for ( i = 0 ; i < ( sizeof ( reg_window_hi ) / + sizeof ( reg_window_hi[0] ) ) ; i++ ) { + + if ( reg_window_hi[i].block != block ) + continue; + + window = ( ( reg_window_hi[i].window_hi << 20 ) | + ( offset & 0x000f0000 ) ); + + if ( phantom->crb_window != window ) { + + /* Write to the CRB window register */ + writel ( window, phantom->bar0 + UNM_2M_CRB_WINDOW ); + + /* Ensure that the write has reached the card */ + verify_window = readl ( phantom->bar0 + + UNM_2M_CRB_WINDOW ); + assert ( verify_window == window ); + + /* Record new window */ + phantom->crb_window = window; + } + + return ( 0x1e0000 + ( offset & 0xffff ) ); + } + + assert ( 0 ); + return 0; +} + +/** + * Read from Phantom CRB register + * + * @v phantom Phantom NIC + * @v reg Register offset within abstract address space + * @ret value Register value + */ +static uint32_t phantom_readl ( struct phantom_nic *phantom, + unsigned long reg ) { + unsigned long offset; + + offset = phantom->crb_access ( phantom, reg ); + return readl ( phantom->bar0 + offset ); +} + +/** + * Write to Phantom CRB register + * + * @v phantom Phantom NIC + * @v value Register value + * @v reg Register offset within abstract address space + */ +static void phantom_writel ( struct phantom_nic *phantom, uint32_t value, + unsigned long reg ) { + unsigned long offset; + + offset = phantom->crb_access ( phantom, reg ); + writel ( value, phantom->bar0 + offset ); +} + +/** + * Write to Phantom CRB HI/LO register pair + * + * @v phantom Phantom NIC + * @v value Register value + * @v lo_offset LO register offset within CRB + * @v hi_offset HI register offset within CRB + */ +static inline void phantom_write_hilo ( struct phantom_nic *phantom, + uint64_t value, + unsigned long lo_offset, + unsigned long hi_offset ) { + uint32_t lo = ( value & 0xffffffffUL ); + uint32_t hi = ( value >> 32 ); + + phantom_writel ( phantom, lo, lo_offset ); + phantom_writel ( phantom, hi, hi_offset ); +} + +/*************************************************************************** + * + * Firmware message buffer access (for debug) + * + */ + +/** + * Read from Phantom test memory + * + * @v phantom Phantom NIC + * @v offset Offset within test memory + * @v buf 8-byte buffer to fill + * @ret rc Return status code + */ +static int phantom_read_test_mem_block ( struct phantom_nic *phantom, + unsigned long offset, + uint32_t buf[2] ) { + unsigned int retries; + uint32_t test_control; + + phantom_write_hilo ( phantom, offset, UNM_TEST_ADDR_LO, + UNM_TEST_ADDR_HI ); + phantom_writel ( phantom, UNM_TEST_CONTROL_ENABLE, UNM_TEST_CONTROL ); + phantom_writel ( phantom, + ( UNM_TEST_CONTROL_ENABLE | UNM_TEST_CONTROL_START ), + UNM_TEST_CONTROL ); + + for ( retries = 0 ; retries < PHN_TEST_MEM_TIMEOUT_MS ; retries++ ) { + test_control = phantom_readl ( phantom, UNM_TEST_CONTROL ); + if ( ( test_control & UNM_TEST_CONTROL_BUSY ) == 0 ) { + buf[0] = phantom_readl ( phantom, UNM_TEST_RDDATA_LO ); + buf[1] = phantom_readl ( phantom, UNM_TEST_RDDATA_HI ); + return 0; + } + mdelay ( 1 ); + } + + DBGC ( phantom, "Phantom %p timed out waiting for test memory\n", + phantom ); + return -ETIMEDOUT; +} + +/** + * Read single byte from Phantom test memory + * + * @v phantom Phantom NIC + * @v offset Offset within test memory + * @ret byte Byte read, or negative error + */ +static int phantom_read_test_mem ( struct phantom_nic *phantom, + unsigned long offset ) { + static union { + uint8_t bytes[8]; + uint32_t dwords[2]; + } cache; + static unsigned long cache_offset = -1UL; + unsigned long sub_offset; + int rc; + + sub_offset = ( offset & ( sizeof ( cache ) - 1 ) ); + offset = ( offset & ~( sizeof ( cache ) - 1 ) ); + + if ( cache_offset != offset ) { + if ( ( rc = phantom_read_test_mem_block ( phantom, offset, + cache.dwords )) !=0 ) + return rc; + cache_offset = offset; + } + + return cache.bytes[sub_offset]; +} + +/** + * Dump Phantom firmware dmesg log + * + * @v phantom Phantom NIC + * @v log Log number + * @v max_lines Maximum number of lines to show, or -1 to show all + * @ret rc Return status code + */ +static int phantom_dmesg ( struct phantom_nic *phantom, unsigned int log, + unsigned int max_lines ) { + uint32_t head; + uint32_t tail; + uint32_t sig; + uint32_t offset; + int byte; + + /* Optimise out for non-debug builds */ + if ( ! DBG_LOG ) + return 0; + + /* Locate log */ + head = phantom_readl ( phantom, UNM_CAM_RAM_DMESG_HEAD ( log ) ); + tail = phantom_readl ( phantom, UNM_CAM_RAM_DMESG_TAIL ( log ) ); + sig = phantom_readl ( phantom, UNM_CAM_RAM_DMESG_SIG ( log ) ); + DBGC ( phantom, "Phantom %p firmware dmesg buffer %d (%08x-%08x)\n", + phantom, log, head, tail ); + assert ( ( head & 0x07 ) == 0 ); + if ( sig != UNM_CAM_RAM_DMESG_SIG_MAGIC ) { + DBGC ( phantom, "Warning: bad signature %08x (want %08lx)\n", + sig, UNM_CAM_RAM_DMESG_SIG_MAGIC ); + } + + /* Locate start of last (max_lines) lines */ + for ( offset = tail ; offset > head ; offset-- ) { + if ( ( byte = phantom_read_test_mem ( phantom, + ( offset - 1 ) ) ) < 0 ) + return byte; + if ( ( byte == '\n' ) && ( max_lines-- == 0 ) ) + break; + } + + /* Print lines */ + for ( ; offset < tail ; offset++ ) { + if ( ( byte = phantom_read_test_mem ( phantom, offset ) ) < 0 ) + return byte; + DBG ( "%c", byte ); + } + DBG ( "\n" ); + return 0; +} + +/** + * Dump Phantom firmware dmesg logs + * + * @v phantom Phantom NIC + * @v max_lines Maximum number of lines to show, or -1 to show all + */ +static void __attribute__ (( unused )) +phantom_dmesg_all ( struct phantom_nic *phantom, unsigned int max_lines ) { + unsigned int i; + + for ( i = 0 ; i < UNM_CAM_RAM_NUM_DMESG_BUFFERS ; i++ ) + phantom_dmesg ( phantom, i, max_lines ); +} + +/*************************************************************************** + * + * Firmware interface + * + */ + +/** + * Wait for firmware to accept command + * + * @v phantom Phantom NIC + * @ret rc Return status code + */ +static int phantom_wait_for_cmd ( struct phantom_nic *phantom ) { + unsigned int retries; + uint32_t cdrp; + + for ( retries = 0 ; retries < PHN_ISSUE_CMD_TIMEOUT_MS ; retries++ ) { + mdelay ( 1 ); + cdrp = phantom_readl ( phantom, UNM_NIC_REG_NX_CDRP ); + if ( NX_CDRP_IS_RSP ( cdrp ) ) { + switch ( NX_CDRP_FORM_RSP ( cdrp ) ) { + case NX_CDRP_RSP_OK: + return 0; + case NX_CDRP_RSP_FAIL: + return -EIO; + case NX_CDRP_RSP_TIMEOUT: + return -ETIMEDOUT; + default: + return -EPROTO; + } + } + } + + DBGC ( phantom, "Phantom %p timed out waiting for firmware to accept " + "command\n", phantom ); + return -ETIMEDOUT; +} + +/** + * Issue command to firmware + * + * @v phantom Phantom NIC + * @v command Firmware command + * @v arg1 Argument 1 + * @v arg2 Argument 2 + * @v arg3 Argument 3 + * @ret rc Return status code + */ +static int phantom_issue_cmd ( struct phantom_nic *phantom, + uint32_t command, uint32_t arg1, uint32_t arg2, + uint32_t arg3 ) { + uint32_t signature; + int rc; + + /* Issue command */ + signature = NX_CDRP_SIGNATURE_MAKE ( phantom->port, + NXHAL_VERSION ); + DBGC2 ( phantom, "Phantom %p issuing command %08x (%08x, %08x, " + "%08x)\n", phantom, command, arg1, arg2, arg3 ); + phantom_writel ( phantom, signature, UNM_NIC_REG_NX_SIGN ); + phantom_writel ( phantom, arg1, UNM_NIC_REG_NX_ARG1 ); + phantom_writel ( phantom, arg2, UNM_NIC_REG_NX_ARG2 ); + phantom_writel ( phantom, arg3, UNM_NIC_REG_NX_ARG3 ); + phantom_writel ( phantom, NX_CDRP_FORM_CMD ( command ), + UNM_NIC_REG_NX_CDRP ); + + /* Wait for command to be accepted */ + if ( ( rc = phantom_wait_for_cmd ( phantom ) ) != 0 ) { + DBGC ( phantom, "Phantom %p could not issue command: %s\n", + phantom, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Issue buffer-format command to firmware + * + * @v phantom Phantom NIC + * @v command Firmware command + * @v buffer Buffer to pass to firmware + * @v len Length of buffer + * @ret rc Return status code + */ +static int phantom_issue_buf_cmd ( struct phantom_nic *phantom, + uint32_t command, void *buffer, + size_t len ) { + uint64_t physaddr; + + physaddr = virt_to_bus ( buffer ); + return phantom_issue_cmd ( phantom, command, ( physaddr >> 32 ), + ( physaddr & 0xffffffffUL ), len ); +} + +/** + * Create Phantom RX context + * + * @v phantom Phantom NIC + * @ret rc Return status code + */ +static int phantom_create_rx_ctx ( struct phantom_nic *phantom ) { + struct phantom_create_rx_ctx_rqrsp *buf; + int rc; + + /* Allocate context creation buffer */ + buf = malloc_dma ( sizeof ( *buf ), UNM_DMA_BUFFER_ALIGN ); + if ( ! buf ) { + rc = -ENOMEM; + goto out; + } + memset ( buf, 0, sizeof ( *buf ) ); + + /* Prepare request */ + buf->hostrq.rx_ctx.host_rsp_dma_addr = + cpu_to_le64 ( virt_to_bus ( &buf->cardrsp ) ); + buf->hostrq.rx_ctx.capabilities[0] = + cpu_to_le32 ( NX_CAP0_LEGACY_CONTEXT | NX_CAP0_LEGACY_MN ); + buf->hostrq.rx_ctx.host_int_crb_mode = + cpu_to_le32 ( NX_HOST_INT_CRB_MODE_SHARED ); + buf->hostrq.rx_ctx.host_rds_crb_mode = + cpu_to_le32 ( NX_HOST_RDS_CRB_MODE_UNIQUE ); + buf->hostrq.rx_ctx.rds_ring_offset = cpu_to_le32 ( 0 ); + buf->hostrq.rx_ctx.sds_ring_offset = + cpu_to_le32 ( sizeof ( buf->hostrq.rds ) ); + buf->hostrq.rx_ctx.num_rds_rings = cpu_to_le16 ( 1 ); + buf->hostrq.rx_ctx.num_sds_rings = cpu_to_le16 ( 1 ); + buf->hostrq.rds.host_phys_addr = + cpu_to_le64 ( virt_to_bus ( phantom->desc->rds ) ); + buf->hostrq.rds.buff_size = cpu_to_le64 ( PHN_RX_BUFSIZE ); + buf->hostrq.rds.ring_size = cpu_to_le32 ( PHN_NUM_RDS ); + buf->hostrq.rds.ring_kind = cpu_to_le32 ( NX_RDS_RING_TYPE_NORMAL ); + buf->hostrq.sds.host_phys_addr = + cpu_to_le64 ( virt_to_bus ( phantom->desc->sds ) ); + buf->hostrq.sds.ring_size = cpu_to_le32 ( PHN_NUM_SDS ); + + DBGC ( phantom, "Phantom %p creating RX context\n", phantom ); + DBGC2_HDA ( phantom, virt_to_bus ( &buf->hostrq ), + &buf->hostrq, sizeof ( buf->hostrq ) ); + + /* Issue request */ + if ( ( rc = phantom_issue_buf_cmd ( phantom, + NX_CDRP_CMD_CREATE_RX_CTX, + &buf->hostrq, + sizeof ( buf->hostrq ) ) ) != 0 ) { + DBGC ( phantom, "Phantom %p could not create RX context: " + "%s\n", phantom, strerror ( rc ) ); + DBGC ( phantom, "Request:\n" ); + DBGC_HDA ( phantom, virt_to_bus ( &buf->hostrq ), + &buf->hostrq, sizeof ( buf->hostrq ) ); + DBGC ( phantom, "Response:\n" ); + DBGC_HDA ( phantom, virt_to_bus ( &buf->cardrsp ), + &buf->cardrsp, sizeof ( buf->cardrsp ) ); + goto out; + } + + /* Retrieve context parameters */ + phantom->rx_context_id = + le16_to_cpu ( buf->cardrsp.rx_ctx.context_id ); + phantom->rds_producer_crb = + ( UNM_CAM_RAM + + le32_to_cpu ( buf->cardrsp.rds.host_producer_crb ) ); + phantom->sds_consumer_crb = + ( UNM_CAM_RAM + + le32_to_cpu ( buf->cardrsp.sds.host_consumer_crb ) ); + phantom->sds_irq_mask_crb = + ( UNM_CAM_RAM + + le32_to_cpu ( buf->cardrsp.sds.interrupt_crb ) ); + + DBGC ( phantom, "Phantom %p created RX context (id %04x, port phys " + "%02x virt %02x)\n", phantom, phantom->rx_context_id, + buf->cardrsp.rx_ctx.phys_port, buf->cardrsp.rx_ctx.virt_port ); + DBGC2_HDA ( phantom, virt_to_bus ( &buf->cardrsp ), + &buf->cardrsp, sizeof ( buf->cardrsp ) ); + DBGC ( phantom, "Phantom %p RDS producer CRB is %08lx\n", + phantom, phantom->rds_producer_crb ); + DBGC ( phantom, "Phantom %p SDS consumer CRB is %08lx\n", + phantom, phantom->sds_consumer_crb ); + DBGC ( phantom, "Phantom %p SDS interrupt mask CRB is %08lx\n", + phantom, phantom->sds_irq_mask_crb ); + + out: + free_dma ( buf, sizeof ( *buf ) ); + return rc; +} + +/** + * Destroy Phantom RX context + * + * @v phantom Phantom NIC + * @ret rc Return status code + */ +static void phantom_destroy_rx_ctx ( struct phantom_nic *phantom ) { + int rc; + + DBGC ( phantom, "Phantom %p destroying RX context (id %04x)\n", + phantom, phantom->rx_context_id ); + + /* Issue request */ + if ( ( rc = phantom_issue_cmd ( phantom, + NX_CDRP_CMD_DESTROY_RX_CTX, + phantom->rx_context_id, + NX_DESTROY_CTX_RESET, 0 ) ) != 0 ) { + DBGC ( phantom, "Phantom %p could not destroy RX context: " + "%s\n", phantom, strerror ( rc ) ); + /* We're probably screwed */ + return; + } + + /* Clear context parameters */ + phantom->rx_context_id = 0; + phantom->rds_producer_crb = 0; + phantom->sds_consumer_crb = 0; + + /* Reset software counters */ + phantom->rds_producer_idx = 0; + phantom->rds_consumer_idx = 0; + phantom->sds_consumer_idx = 0; +} + +/** + * Create Phantom TX context + * + * @v phantom Phantom NIC + * @ret rc Return status code + */ +static int phantom_create_tx_ctx ( struct phantom_nic *phantom ) { + struct phantom_create_tx_ctx_rqrsp *buf; + int rc; + + /* Allocate context creation buffer */ + buf = malloc_dma ( sizeof ( *buf ), UNM_DMA_BUFFER_ALIGN ); + if ( ! buf ) { + rc = -ENOMEM; + goto out; + } + memset ( buf, 0, sizeof ( *buf ) ); + + /* Prepare request */ + buf->hostrq.tx_ctx.host_rsp_dma_addr = + cpu_to_le64 ( virt_to_bus ( &buf->cardrsp ) ); + buf->hostrq.tx_ctx.cmd_cons_dma_addr = + cpu_to_le64 ( virt_to_bus ( &phantom->desc->cmd_cons ) ); + buf->hostrq.tx_ctx.capabilities[0] = + cpu_to_le32 ( NX_CAP0_LEGACY_CONTEXT | NX_CAP0_LEGACY_MN ); + buf->hostrq.tx_ctx.host_int_crb_mode = + cpu_to_le32 ( NX_HOST_INT_CRB_MODE_SHARED ); + buf->hostrq.tx_ctx.cds_ring.host_phys_addr = + cpu_to_le64 ( virt_to_bus ( phantom->desc->cds ) ); + buf->hostrq.tx_ctx.cds_ring.ring_size = cpu_to_le32 ( PHN_NUM_CDS ); + + DBGC ( phantom, "Phantom %p creating TX context\n", phantom ); + DBGC2_HDA ( phantom, virt_to_bus ( &buf->hostrq ), + &buf->hostrq, sizeof ( buf->hostrq ) ); + + /* Issue request */ + if ( ( rc = phantom_issue_buf_cmd ( phantom, + NX_CDRP_CMD_CREATE_TX_CTX, + &buf->hostrq, + sizeof ( buf->hostrq ) ) ) != 0 ) { + DBGC ( phantom, "Phantom %p could not create TX context: " + "%s\n", phantom, strerror ( rc ) ); + DBGC ( phantom, "Request:\n" ); + DBGC_HDA ( phantom, virt_to_bus ( &buf->hostrq ), + &buf->hostrq, sizeof ( buf->hostrq ) ); + DBGC ( phantom, "Response:\n" ); + DBGC_HDA ( phantom, virt_to_bus ( &buf->cardrsp ), + &buf->cardrsp, sizeof ( buf->cardrsp ) ); + goto out; + } + + /* Retrieve context parameters */ + phantom->tx_context_id = + le16_to_cpu ( buf->cardrsp.tx_ctx.context_id ); + phantom->cds_producer_crb = + ( UNM_CAM_RAM + + le32_to_cpu(buf->cardrsp.tx_ctx.cds_ring.host_producer_crb)); + + DBGC ( phantom, "Phantom %p created TX context (id %04x, port phys " + "%02x virt %02x)\n", phantom, phantom->tx_context_id, + buf->cardrsp.tx_ctx.phys_port, buf->cardrsp.tx_ctx.virt_port ); + DBGC2_HDA ( phantom, virt_to_bus ( &buf->cardrsp ), + &buf->cardrsp, sizeof ( buf->cardrsp ) ); + DBGC ( phantom, "Phantom %p CDS producer CRB is %08lx\n", + phantom, phantom->cds_producer_crb ); + + out: + free_dma ( buf, sizeof ( *buf ) ); + return rc; +} + +/** + * Destroy Phantom TX context + * + * @v phantom Phantom NIC + * @ret rc Return status code + */ +static void phantom_destroy_tx_ctx ( struct phantom_nic *phantom ) { + int rc; + + DBGC ( phantom, "Phantom %p destroying TX context (id %04x)\n", + phantom, phantom->tx_context_id ); + + /* Issue request */ + if ( ( rc = phantom_issue_cmd ( phantom, + NX_CDRP_CMD_DESTROY_TX_CTX, + phantom->tx_context_id, + NX_DESTROY_CTX_RESET, 0 ) ) != 0 ) { + DBGC ( phantom, "Phantom %p could not destroy TX context: " + "%s\n", phantom, strerror ( rc ) ); + /* We're probably screwed */ + return; + } + + /* Clear context parameters */ + phantom->tx_context_id = 0; + phantom->cds_producer_crb = 0; + + /* Reset software counters */ + phantom->cds_producer_idx = 0; + phantom->cds_consumer_idx = 0; +} + +/*************************************************************************** + * + * Descriptor ring management + * + */ + +/** + * Allocate Phantom RX descriptor + * + * @v phantom Phantom NIC + * @ret index RX descriptor index, or negative error + */ +static int phantom_alloc_rds ( struct phantom_nic *phantom ) { + unsigned int rds_producer_idx; + unsigned int next_rds_producer_idx; + + /* Check for space in the ring. RX descriptors are consumed + * out of order, but they are *read* by the hardware in strict + * order. We maintain a pessimistic consumer index, which is + * guaranteed never to be an overestimate of the number of + * descriptors read by the hardware. + */ + rds_producer_idx = phantom->rds_producer_idx; + next_rds_producer_idx = ( ( rds_producer_idx + 1 ) % PHN_NUM_RDS ); + if ( next_rds_producer_idx == phantom->rds_consumer_idx ) { + DBGC ( phantom, "Phantom %p RDS ring full (index %d not " + "consumed)\n", phantom, next_rds_producer_idx ); + return -ENOBUFS; + } + + return rds_producer_idx; +} + +/** + * Post Phantom RX descriptor + * + * @v phantom Phantom NIC + * @v rds RX descriptor + */ +static void phantom_post_rds ( struct phantom_nic *phantom, + struct phantom_rds *rds ) { + unsigned int rds_producer_idx; + unsigned int next_rds_producer_idx; + struct phantom_rds *entry; + + /* Copy descriptor to ring */ + rds_producer_idx = phantom->rds_producer_idx; + entry = &phantom->desc->rds[rds_producer_idx]; + memcpy ( entry, rds, sizeof ( *entry ) ); + DBGC2 ( phantom, "Phantom %p posting RDS %ld (slot %d):\n", + phantom, NX_GET ( rds, handle ), rds_producer_idx ); + DBGC2_HDA ( phantom, virt_to_bus ( entry ), entry, sizeof ( *entry ) ); + + /* Update producer index */ + next_rds_producer_idx = ( ( rds_producer_idx + 1 ) % PHN_NUM_RDS ); + phantom->rds_producer_idx = next_rds_producer_idx; + wmb(); + phantom_writel ( phantom, phantom->rds_producer_idx, + phantom->rds_producer_crb ); +} + +/** + * Allocate Phantom TX descriptor + * + * @v phantom Phantom NIC + * @ret index TX descriptor index, or negative error + */ +static int phantom_alloc_cds ( struct phantom_nic *phantom ) { + unsigned int cds_producer_idx; + unsigned int next_cds_producer_idx; + + /* Check for space in the ring. TX descriptors are consumed + * in strict order, so we just check for a collision against + * the consumer index. + */ + cds_producer_idx = phantom->cds_producer_idx; + next_cds_producer_idx = ( ( cds_producer_idx + 1 ) % PHN_NUM_CDS ); + if ( next_cds_producer_idx == phantom->cds_consumer_idx ) { + DBGC ( phantom, "Phantom %p CDS ring full (index %d not " + "consumed)\n", phantom, next_cds_producer_idx ); + return -ENOBUFS; + } + + return cds_producer_idx; +} + +/** + * Post Phantom TX descriptor + * + * @v phantom Phantom NIC + * @v cds TX descriptor + */ +static void phantom_post_cds ( struct phantom_nic *phantom, + union phantom_cds *cds ) { + unsigned int cds_producer_idx; + unsigned int next_cds_producer_idx; + union phantom_cds *entry; + + /* Copy descriptor to ring */ + cds_producer_idx = phantom->cds_producer_idx; + entry = &phantom->desc->cds[cds_producer_idx]; + memcpy ( entry, cds, sizeof ( *entry ) ); + DBGC2 ( phantom, "Phantom %p posting CDS %d:\n", + phantom, cds_producer_idx ); + DBGC2_HDA ( phantom, virt_to_bus ( entry ), entry, sizeof ( *entry ) ); + + /* Update producer index */ + next_cds_producer_idx = ( ( cds_producer_idx + 1 ) % PHN_NUM_CDS ); + phantom->cds_producer_idx = next_cds_producer_idx; + wmb(); + phantom_writel ( phantom, phantom->cds_producer_idx, + phantom->cds_producer_crb ); +} + +/*************************************************************************** + * + * MAC address management + * + */ + +/** + * Add/remove MAC address + * + * @v phantom Phantom NIC + * @v ll_addr MAC address to add or remove + * @v opcode MAC request opcode + * @ret rc Return status code + */ +static int phantom_update_macaddr ( struct phantom_nic *phantom, + const uint8_t *ll_addr, + unsigned int opcode ) { + union phantom_cds cds; + int index; + + /* Get descriptor ring entry */ + index = phantom_alloc_cds ( phantom ); + if ( index < 0 ) + return index; + + /* Fill descriptor ring entry */ + memset ( &cds, 0, sizeof ( cds ) ); + NX_FILL_1 ( &cds, 0, + nic_request.common.opcode, UNM_NIC_REQUEST ); + NX_FILL_2 ( &cds, 1, + nic_request.header.opcode, UNM_MAC_EVENT, + nic_request.header.context_id, phantom->port ); + NX_FILL_7 ( &cds, 2, + nic_request.body.mac_request.opcode, opcode, + nic_request.body.mac_request.mac_addr_0, ll_addr[0], + nic_request.body.mac_request.mac_addr_1, ll_addr[1], + nic_request.body.mac_request.mac_addr_2, ll_addr[2], + nic_request.body.mac_request.mac_addr_3, ll_addr[3], + nic_request.body.mac_request.mac_addr_4, ll_addr[4], + nic_request.body.mac_request.mac_addr_5, ll_addr[5] ); + + /* Post descriptor */ + phantom_post_cds ( phantom, &cds ); + + return 0; +} + +/** + * Add MAC address + * + * @v phantom Phantom NIC + * @v ll_addr MAC address to add or remove + * @ret rc Return status code + */ +static inline int phantom_add_macaddr ( struct phantom_nic *phantom, + const uint8_t *ll_addr ) { + + DBGC ( phantom, "Phantom %p adding MAC address %s\n", + phantom, eth_ntoa ( ll_addr ) ); + + return phantom_update_macaddr ( phantom, ll_addr, UNM_MAC_ADD ); +} + +/** + * Remove MAC address + * + * @v phantom Phantom NIC + * @v ll_addr MAC address to add or remove + * @ret rc Return status code + */ +static inline int phantom_del_macaddr ( struct phantom_nic *phantom, + const uint8_t *ll_addr ) { + + DBGC ( phantom, "Phantom %p removing MAC address %s\n", + phantom, eth_ntoa ( ll_addr ) ); + + return phantom_update_macaddr ( phantom, ll_addr, UNM_MAC_DEL ); +} + +/*************************************************************************** + * + * Link state detection + * + */ + +/** + * Poll link state + * + * @v netdev Network device + */ +static void phantom_poll_link_state ( struct net_device *netdev ) { + struct phantom_nic *phantom = netdev_priv ( netdev ); + uint32_t xg_state_p3; + unsigned int link; + + /* Read link state */ + xg_state_p3 = phantom_readl ( phantom, UNM_NIC_REG_XG_STATE_P3 ); + + /* If there is no change, do nothing */ + if ( phantom->link_state == xg_state_p3 ) + return; + + /* Record new link state */ + DBGC ( phantom, "Phantom %p new link state %08x (was %08x)\n", + phantom, xg_state_p3, phantom->link_state ); + phantom->link_state = xg_state_p3; + + /* Indicate link state to iPXE */ + link = UNM_NIC_REG_XG_STATE_P3_LINK ( phantom->port, + phantom->link_state ); + switch ( link ) { + case UNM_NIC_REG_XG_STATE_P3_LINK_UP: + DBGC ( phantom, "Phantom %p link is up\n", phantom ); + netdev_link_up ( netdev ); + break; + case UNM_NIC_REG_XG_STATE_P3_LINK_DOWN: + DBGC ( phantom, "Phantom %p link is down\n", phantom ); + netdev_link_down ( netdev ); + break; + default: + DBGC ( phantom, "Phantom %p bad link state %d\n", + phantom, link ); + break; + } +} + +/*************************************************************************** + * + * Main driver body + * + */ + +/** + * Refill descriptor ring + * + * @v netdev Net device + */ +static void phantom_refill_rx_ring ( struct net_device *netdev ) { + struct phantom_nic *phantom = netdev_priv ( netdev ); + struct io_buffer *iobuf; + struct phantom_rds rds; + unsigned int handle; + int index; + + for ( handle = 0 ; handle < PHN_RDS_MAX_FILL ; handle++ ) { + + /* Skip this index if the descriptor has not yet been + * consumed. + */ + if ( phantom->rds_iobuf[handle] != NULL ) + continue; + + /* Allocate descriptor ring entry */ + index = phantom_alloc_rds ( phantom ); + assert ( PHN_RDS_MAX_FILL < PHN_NUM_RDS ); + assert ( index >= 0 ); /* Guaranteed by MAX_FILL < NUM_RDS ) */ + + /* Try to allocate an I/O buffer */ + iobuf = alloc_iob ( PHN_RX_BUFSIZE ); + if ( ! iobuf ) { + /* Failure is non-fatal; we will retry later */ + netdev_rx_err ( netdev, NULL, -ENOMEM ); + break; + } + + /* Fill descriptor ring entry */ + memset ( &rds, 0, sizeof ( rds ) ); + NX_FILL_2 ( &rds, 0, + handle, handle, + length, iob_len ( iobuf ) ); + NX_FILL_1 ( &rds, 1, + dma_addr, virt_to_bus ( iobuf->data ) ); + + /* Record I/O buffer */ + assert ( phantom->rds_iobuf[handle] == NULL ); + phantom->rds_iobuf[handle] = iobuf; + + /* Post descriptor */ + phantom_post_rds ( phantom, &rds ); + } +} + +/** + * Open NIC + * + * @v netdev Net device + * @ret rc Return status code + */ +static int phantom_open ( struct net_device *netdev ) { + struct phantom_nic *phantom = netdev_priv ( netdev ); + int rc; + + /* Allocate and zero descriptor rings */ + phantom->desc = malloc_dma ( sizeof ( *(phantom->desc) ), + UNM_DMA_BUFFER_ALIGN ); + if ( ! phantom->desc ) { + rc = -ENOMEM; + goto err_alloc_desc; + } + memset ( phantom->desc, 0, sizeof ( *(phantom->desc) ) ); + + /* Create RX context */ + if ( ( rc = phantom_create_rx_ctx ( phantom ) ) != 0 ) + goto err_create_rx_ctx; + + /* Create TX context */ + if ( ( rc = phantom_create_tx_ctx ( phantom ) ) != 0 ) + goto err_create_tx_ctx; + + /* Fill the RX descriptor ring */ + phantom_refill_rx_ring ( netdev ); + + /* Add MAC addresses + * + * BUG5583 + * + * We would like to be able to enable receiving all multicast + * packets (or, failing that, promiscuous mode), but the + * firmware doesn't currently support this. + */ + if ( ( rc = phantom_add_macaddr ( phantom, + netdev->ll_broadcast ) ) != 0 ) + goto err_add_macaddr_broadcast; + if ( ( rc = phantom_add_macaddr ( phantom, + netdev->ll_addr ) ) != 0 ) + goto err_add_macaddr_unicast; + + return 0; + + phantom_del_macaddr ( phantom, netdev->ll_addr ); + err_add_macaddr_unicast: + phantom_del_macaddr ( phantom, netdev->ll_broadcast ); + err_add_macaddr_broadcast: + phantom_destroy_tx_ctx ( phantom ); + err_create_tx_ctx: + phantom_destroy_rx_ctx ( phantom ); + err_create_rx_ctx: + free_dma ( phantom->desc, sizeof ( *(phantom->desc) ) ); + phantom->desc = NULL; + err_alloc_desc: + return rc; +} + +/** + * Close NIC + * + * @v netdev Net device + */ +static void phantom_close ( struct net_device *netdev ) { + struct phantom_nic *phantom = netdev_priv ( netdev ); + struct io_buffer *iobuf; + unsigned int i; + + /* Shut down the port */ + phantom_del_macaddr ( phantom, netdev->ll_addr ); + phantom_del_macaddr ( phantom, netdev->ll_broadcast ); + phantom_destroy_tx_ctx ( phantom ); + phantom_destroy_rx_ctx ( phantom ); + free_dma ( phantom->desc, sizeof ( *(phantom->desc) ) ); + phantom->desc = NULL; + + /* Flush any uncompleted descriptors */ + for ( i = 0 ; i < PHN_RDS_MAX_FILL ; i++ ) { + iobuf = phantom->rds_iobuf[i]; + if ( iobuf ) { + free_iob ( iobuf ); + phantom->rds_iobuf[i] = NULL; + } + } + for ( i = 0 ; i < PHN_NUM_CDS ; i++ ) { + iobuf = phantom->cds_iobuf[i]; + if ( iobuf ) { + netdev_tx_complete_err ( netdev, iobuf, -ECANCELED ); + phantom->cds_iobuf[i] = NULL; + } + } +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int phantom_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct phantom_nic *phantom = netdev_priv ( netdev ); + union phantom_cds cds; + int index; + + /* Get descriptor ring entry */ + index = phantom_alloc_cds ( phantom ); + if ( index < 0 ) + return index; + + /* Fill descriptor ring entry */ + memset ( &cds, 0, sizeof ( cds ) ); + NX_FILL_3 ( &cds, 0, + tx.opcode, UNM_TX_ETHER_PKT, + tx.num_buffers, 1, + tx.length, iob_len ( iobuf ) ); + NX_FILL_2 ( &cds, 2, + tx.port, phantom->port, + tx.context_id, phantom->port ); + NX_FILL_1 ( &cds, 4, + tx.buffer1_dma_addr, virt_to_bus ( iobuf->data ) ); + NX_FILL_1 ( &cds, 5, + tx.buffer1_length, iob_len ( iobuf ) ); + + /* Record I/O buffer */ + assert ( phantom->cds_iobuf[index] == NULL ); + phantom->cds_iobuf[index] = iobuf; + + /* Post descriptor */ + phantom_post_cds ( phantom, &cds ); + + return 0; +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void phantom_poll ( struct net_device *netdev ) { + struct phantom_nic *phantom = netdev_priv ( netdev ); + struct io_buffer *iobuf; + unsigned int irq_vector; + unsigned int irq_state; + unsigned int cds_consumer_idx; + unsigned int raw_new_cds_consumer_idx; + unsigned int new_cds_consumer_idx; + unsigned int rds_consumer_idx; + unsigned int sds_consumer_idx; + struct phantom_sds *sds; + unsigned int sds_handle; + unsigned int sds_opcode; + + /* Occasionally poll the link state */ + if ( phantom->link_poll_timer-- == 0 ) { + phantom_poll_link_state ( netdev ); + /* Reset the link poll timer */ + phantom->link_poll_timer = PHN_LINK_POLL_FREQUENCY; + } + + /* Check for interrupts */ + if ( phantom->sds_irq_enabled ) { + + /* Do nothing unless an interrupt is asserted */ + irq_vector = phantom_readl ( phantom, UNM_PCIE_IRQ_VECTOR ); + if ( ! ( irq_vector & UNM_PCIE_IRQ_VECTOR_BIT( phantom->port ))) + return; + + /* Do nothing unless interrupt state machine has stabilised */ + irq_state = phantom_readl ( phantom, UNM_PCIE_IRQ_STATE ); + if ( ! UNM_PCIE_IRQ_STATE_TRIGGERED ( irq_state ) ) + return; + + /* Acknowledge interrupt */ + phantom_writel ( phantom, UNM_PCIE_IRQ_STATUS_MAGIC, + phantom_irq_status_reg[phantom->port] ); + phantom_readl ( phantom, UNM_PCIE_IRQ_VECTOR ); + } + + /* Check for TX completions */ + cds_consumer_idx = phantom->cds_consumer_idx; + raw_new_cds_consumer_idx = phantom->desc->cmd_cons; + new_cds_consumer_idx = le32_to_cpu ( raw_new_cds_consumer_idx ); + while ( cds_consumer_idx != new_cds_consumer_idx ) { + DBGC2 ( phantom, "Phantom %p CDS %d complete\n", + phantom, cds_consumer_idx ); + /* Completions may be for commands other than TX, so + * there may not always be an associated I/O buffer. + */ + if ( ( iobuf = phantom->cds_iobuf[cds_consumer_idx] ) ) { + netdev_tx_complete ( netdev, iobuf ); + phantom->cds_iobuf[cds_consumer_idx] = NULL; + } + cds_consumer_idx = ( ( cds_consumer_idx + 1 ) % PHN_NUM_CDS ); + phantom->cds_consumer_idx = cds_consumer_idx; + } + + /* Check for received packets */ + rds_consumer_idx = phantom->rds_consumer_idx; + sds_consumer_idx = phantom->sds_consumer_idx; + while ( 1 ) { + sds = &phantom->desc->sds[sds_consumer_idx]; + if ( NX_GET ( sds, owner ) == 0 ) + break; + + DBGC2 ( phantom, "Phantom %p SDS %d status:\n", + phantom, sds_consumer_idx ); + DBGC2_HDA ( phantom, virt_to_bus ( sds ), sds, sizeof (*sds) ); + + /* Check received opcode */ + sds_opcode = NX_GET ( sds, opcode ); + if ( ( sds_opcode == UNM_RXPKT_DESC ) || + ( sds_opcode == UNM_SYN_OFFLOAD ) ) { + + /* Sanity check: ensure that all of the SDS + * descriptor has been written. + */ + if ( NX_GET ( sds, total_length ) == 0 ) { + DBGC ( phantom, "Phantom %p SDS %d " + "incomplete; deferring\n", + phantom, sds_consumer_idx ); + /* Leave for next poll() */ + break; + } + + /* Process received packet */ + sds_handle = NX_GET ( sds, handle ); + iobuf = phantom->rds_iobuf[sds_handle]; + assert ( iobuf != NULL ); + iob_put ( iobuf, NX_GET ( sds, total_length ) ); + iob_pull ( iobuf, NX_GET ( sds, pkt_offset ) ); + DBGC2 ( phantom, "Phantom %p RDS %d complete\n", + phantom, sds_handle ); + netdev_rx ( netdev, iobuf ); + phantom->rds_iobuf[sds_handle] = NULL; + + /* Update RDS consumer counter. This is a + * lower bound for the number of descriptors + * that have been read by the hardware, since + * the hardware must have read at least one + * descriptor for each completion that we + * receive. + */ + rds_consumer_idx = + ( ( rds_consumer_idx + 1 ) % PHN_NUM_RDS ); + phantom->rds_consumer_idx = rds_consumer_idx; + + } else { + + DBGC ( phantom, "Phantom %p unexpected SDS opcode " + "%02x\n", phantom, sds_opcode ); + DBGC_HDA ( phantom, virt_to_bus ( sds ), + sds, sizeof ( *sds ) ); + } + + /* Clear status descriptor */ + memset ( sds, 0, sizeof ( *sds ) ); + + /* Update SDS consumer index */ + sds_consumer_idx = ( ( sds_consumer_idx + 1 ) % PHN_NUM_SDS ); + phantom->sds_consumer_idx = sds_consumer_idx; + wmb(); + phantom_writel ( phantom, phantom->sds_consumer_idx, + phantom->sds_consumer_crb ); + } + + /* Refill the RX descriptor ring */ + phantom_refill_rx_ring ( netdev ); +} + +/** + * Enable/disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void phantom_irq ( struct net_device *netdev, int enable ) { + struct phantom_nic *phantom = netdev_priv ( netdev ); + + phantom_writel ( phantom, ( enable ? 1 : 0 ), + phantom->sds_irq_mask_crb ); + phantom_writel ( phantom, UNM_PCIE_IRQ_MASK_MAGIC, + phantom_irq_mask_reg[phantom->port] ); + phantom->sds_irq_enabled = enable; +} + +/** Phantom net device operations */ +static struct net_device_operations phantom_operations = { + .open = phantom_open, + .close = phantom_close, + .transmit = phantom_transmit, + .poll = phantom_poll, + .irq = phantom_irq, +}; + +/*************************************************************************** + * + * CLP settings + * + */ + +/** Phantom CLP settings scope */ +static const struct settings_scope phantom_settings_scope; + +/** Phantom CLP data + * + */ +union phantom_clp_data { + /** Data bytes + * + * This field is right-aligned; if only N bytes are present + * then bytes[0]..bytes[7-N] should be zero, and the data + * should be in bytes[7-N+1] to bytes[7]; + */ + uint8_t bytes[8]; + /** Dwords for the CLP interface */ + struct { + /** High dword, in network byte order */ + uint32_t hi; + /** Low dword, in network byte order */ + uint32_t lo; + } dwords; +}; +#define PHN_CLP_BLKSIZE ( sizeof ( union phantom_clp_data ) ) + +/** + * Wait for Phantom CLP command to complete + * + * @v phantom Phantom NIC + * @ret rc Return status code + */ +static int phantom_clp_wait ( struct phantom_nic *phantom ) { + unsigned int retries; + uint32_t status; + + for ( retries = 0 ; retries < PHN_CLP_CMD_TIMEOUT_MS ; retries++ ) { + status = phantom_readl ( phantom, UNM_CAM_RAM_CLP_STATUS ); + if ( status & UNM_CAM_RAM_CLP_STATUS_DONE ) + return 0; + mdelay ( 1 ); + } + + DBGC ( phantom, "Phantom %p timed out waiting for CLP command\n", + phantom ); + return -ETIMEDOUT; +} + +/** + * Issue Phantom CLP command + * + * @v phantom Phantom NIC + * @v port Virtual port number + * @v opcode Opcode + * @v data_in Data in, or NULL + * @v data_out Data out, or NULL + * @v offset Offset within data + * @v len Data buffer length + * @ret len Total transfer length (for reads), or negative error + */ +static int phantom_clp_cmd ( struct phantom_nic *phantom, unsigned int port, + unsigned int opcode, const void *data_in, + void *data_out, size_t offset, size_t len ) { + union phantom_clp_data data; + unsigned int index = ( offset / sizeof ( data ) ); + unsigned int last = 0; + size_t in_frag_len; + uint8_t *in_frag; + uint32_t command; + uint32_t status; + size_t read_len; + unsigned int error; + size_t out_frag_len; + uint8_t *out_frag; + int rc; + + /* Sanity checks */ + assert ( ( offset % sizeof ( data ) ) == 0 ); + if ( len > 255 ) { + DBGC ( phantom, "Phantom %p invalid CLP length %zd\n", + phantom, len ); + return -EINVAL; + } + + /* Check that CLP interface is ready */ + if ( ( rc = phantom_clp_wait ( phantom ) ) != 0 ) + return rc; + + /* Copy data in */ + memset ( &data, 0, sizeof ( data ) ); + if ( data_in ) { + assert ( offset < len ); + in_frag_len = ( len - offset ); + if ( in_frag_len > sizeof ( data ) ) { + in_frag_len = sizeof ( data ); + } else { + last = 1; + } + in_frag = &data.bytes[ sizeof ( data ) - in_frag_len ]; + memcpy ( in_frag, ( data_in + offset ), in_frag_len ); + phantom_writel ( phantom, be32_to_cpu ( data.dwords.lo ), + UNM_CAM_RAM_CLP_DATA_LO ); + phantom_writel ( phantom, be32_to_cpu ( data.dwords.hi ), + UNM_CAM_RAM_CLP_DATA_HI ); + } + + /* Issue CLP command */ + command = ( ( index << 24 ) | ( ( data_in ? len : 0 ) << 16 ) | + ( port << 8 ) | ( last << 7 ) | ( opcode << 0 ) ); + phantom_writel ( phantom, command, UNM_CAM_RAM_CLP_COMMAND ); + mb(); + phantom_writel ( phantom, UNM_CAM_RAM_CLP_STATUS_START, + UNM_CAM_RAM_CLP_STATUS ); + + /* Wait for command to complete */ + if ( ( rc = phantom_clp_wait ( phantom ) ) != 0 ) + return rc; + + /* Get command status */ + status = phantom_readl ( phantom, UNM_CAM_RAM_CLP_STATUS ); + read_len = ( ( status >> 16 ) & 0xff ); + error = ( ( status >> 8 ) & 0xff ); + if ( error ) { + DBGC ( phantom, "Phantom %p CLP command error %02x\n", + phantom, error ); + return -EIO; + } + + /* Copy data out */ + if ( data_out ) { + data.dwords.lo = cpu_to_be32 ( phantom_readl ( phantom, + UNM_CAM_RAM_CLP_DATA_LO ) ); + data.dwords.hi = cpu_to_be32 ( phantom_readl ( phantom, + UNM_CAM_RAM_CLP_DATA_HI ) ); + out_frag_len = ( read_len - offset ); + if ( out_frag_len > sizeof ( data ) ) + out_frag_len = sizeof ( data ); + out_frag = &data.bytes[ sizeof ( data ) - out_frag_len ]; + if ( out_frag_len > ( len - offset ) ) + out_frag_len = ( len - offset ); + memcpy ( ( data_out + offset ), out_frag, out_frag_len ); + } + + return read_len; +} + +/** + * Store Phantom CLP setting + * + * @v phantom Phantom NIC + * @v port Virtual port number + * @v setting Setting number + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +static int phantom_clp_store ( struct phantom_nic *phantom, unsigned int port, + unsigned int setting, const void *data, + size_t len ) { + unsigned int opcode = setting; + size_t offset; + int rc; + + for ( offset = 0 ; offset < len ; offset += PHN_CLP_BLKSIZE ) { + if ( ( rc = phantom_clp_cmd ( phantom, port, opcode, data, + NULL, offset, len ) ) < 0 ) + return rc; + } + return 0; +} + +/** + * Fetch Phantom CLP setting + * + * @v phantom Phantom NIC + * @v port Virtual port number + * @v setting Setting number + * @v data Data buffer + * @v len Length of data buffer + * @ret len Length of setting, or negative error + */ +static int phantom_clp_fetch ( struct phantom_nic *phantom, unsigned int port, + unsigned int setting, void *data, size_t len ) { + unsigned int opcode = ( setting + 1 ); + size_t offset = 0; + int read_len; + + while ( 1 ) { + read_len = phantom_clp_cmd ( phantom, port, opcode, NULL, + data, offset, len ); + if ( read_len < 0 ) + return read_len; + offset += PHN_CLP_BLKSIZE; + if ( offset >= ( unsigned ) read_len ) + break; + if ( offset >= len ) + break; + } + return read_len; +} + +/** A Phantom CLP setting */ +struct phantom_clp_setting { + /** iPXE setting */ + const struct setting *setting; + /** Setting number */ + unsigned int clp_setting; +}; + +/** Phantom CLP settings */ +static struct phantom_clp_setting clp_settings[] = { + { &mac_setting, 0x01 }, +}; + +/** + * Find Phantom CLP setting + * + * @v setting iPXE setting + * @v clp_setting Setting number, or 0 if not found + */ +static unsigned int +phantom_clp_setting ( struct phantom_nic *phantom, + const struct setting *setting ) { + struct phantom_clp_setting *clp_setting; + unsigned int i; + + /* Search the list of explicitly-defined settings */ + for ( i = 0 ; i < ( sizeof ( clp_settings ) / + sizeof ( clp_settings[0] ) ) ; i++ ) { + clp_setting = &clp_settings[i]; + if ( setting_cmp ( setting, clp_setting->setting ) == 0 ) + return clp_setting->clp_setting; + } + + /* Allow for use of numbered settings */ + if ( setting->scope == &phantom_settings_scope ) + return setting->tag; + + DBGC2 ( phantom, "Phantom %p has no \"%s\" setting\n", + phantom, setting->name ); + + return 0; +} + +/** + * Check applicability of Phantom CLP setting + * + * @v settings Settings block + * @v setting Setting + * @ret applies Setting applies within this settings block + */ +static int phantom_setting_applies ( struct settings *settings, + const struct setting *setting ) { + struct phantom_nic *phantom = + container_of ( settings, struct phantom_nic, settings ); + unsigned int clp_setting; + + /* Find Phantom setting equivalent to iPXE setting */ + clp_setting = phantom_clp_setting ( phantom, setting ); + return ( clp_setting != 0 ); +} + +/** + * Store Phantom CLP setting + * + * @v settings Settings block + * @v setting Setting to store + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ +static int phantom_store_setting ( struct settings *settings, + const struct setting *setting, + const void *data, size_t len ) { + struct phantom_nic *phantom = + container_of ( settings, struct phantom_nic, settings ); + unsigned int clp_setting; + int rc; + + /* Find Phantom setting equivalent to iPXE setting */ + clp_setting = phantom_clp_setting ( phantom, setting ); + assert ( clp_setting != 0 ); + + /* Store setting */ + if ( ( rc = phantom_clp_store ( phantom, phantom->port, + clp_setting, data, len ) ) != 0 ) { + DBGC ( phantom, "Phantom %p could not store setting \"%s\": " + "%s\n", phantom, setting->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Fetch Phantom CLP setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int phantom_fetch_setting ( struct settings *settings, + struct setting *setting, + void *data, size_t len ) { + struct phantom_nic *phantom = + container_of ( settings, struct phantom_nic, settings ); + unsigned int clp_setting; + int read_len; + int rc; + + /* Find Phantom setting equivalent to iPXE setting */ + clp_setting = phantom_clp_setting ( phantom, setting ); + assert ( clp_setting != 0 ); + + /* Fetch setting */ + if ( ( read_len = phantom_clp_fetch ( phantom, phantom->port, + clp_setting, data, len ) ) < 0 ){ + rc = read_len; + DBGC ( phantom, "Phantom %p could not fetch setting \"%s\": " + "%s\n", phantom, setting->name, strerror ( rc ) ); + return rc; + } + + return read_len; +} + +/** Phantom CLP settings operations */ +static struct settings_operations phantom_settings_operations = { + .applies = phantom_setting_applies, + .store = phantom_store_setting, + .fetch = phantom_fetch_setting, +}; + +/*************************************************************************** + * + * Initialisation + * + */ + +/** + * Map Phantom CRB window + * + * @v phantom Phantom NIC + * @ret rc Return status code + */ +static int phantom_map_crb ( struct phantom_nic *phantom, + struct pci_device *pci ) { + unsigned long bar0_start; + unsigned long bar0_size; + + bar0_start = pci_bar_start ( pci, PCI_BASE_ADDRESS_0 ); + bar0_size = pci_bar_size ( pci, PCI_BASE_ADDRESS_0 ); + DBGC ( phantom, "Phantom %p is " PCI_FMT " with BAR0 at %08lx+%lx\n", + phantom, PCI_ARGS ( pci ), bar0_start, bar0_size ); + + if ( ! bar0_start ) { + DBGC ( phantom, "Phantom %p BAR not assigned; ignoring\n", + phantom ); + return -EINVAL; + } + + switch ( bar0_size ) { + case ( 128 * 1024 * 1024 ) : + DBGC ( phantom, "Phantom %p has 128MB BAR\n", phantom ); + phantom->crb_access = phantom_crb_access_128m; + break; + case ( 32 * 1024 * 1024 ) : + DBGC ( phantom, "Phantom %p has 32MB BAR\n", phantom ); + phantom->crb_access = phantom_crb_access_32m; + break; + case ( 2 * 1024 * 1024 ) : + DBGC ( phantom, "Phantom %p has 2MB BAR\n", phantom ); + phantom->crb_access = phantom_crb_access_2m; + break; + default: + DBGC ( phantom, "Phantom %p has bad BAR size\n", phantom ); + return -EINVAL; + } + + phantom->bar0 = ioremap ( bar0_start, bar0_size ); + if ( ! phantom->bar0 ) { + DBGC ( phantom, "Phantom %p could not map BAR0\n", phantom ); + return -EIO; + } + + /* Mark current CRB window as invalid, so that the first + * read/write will set the current window. + */ + phantom->crb_window = -1UL; + + return 0; +} + +/** + * Unhalt all PEGs + * + * @v phantom Phantom NIC + */ +static void phantom_unhalt_pegs ( struct phantom_nic *phantom ) { + uint32_t halt_status; + + halt_status = phantom_readl ( phantom, UNM_PEG_0_HALT_STATUS ); + phantom_writel ( phantom, halt_status, UNM_PEG_0_HALT_STATUS ); + halt_status = phantom_readl ( phantom, UNM_PEG_1_HALT_STATUS ); + phantom_writel ( phantom, halt_status, UNM_PEG_1_HALT_STATUS ); + halt_status = phantom_readl ( phantom, UNM_PEG_2_HALT_STATUS ); + phantom_writel ( phantom, halt_status, UNM_PEG_2_HALT_STATUS ); + halt_status = phantom_readl ( phantom, UNM_PEG_3_HALT_STATUS ); + phantom_writel ( phantom, halt_status, UNM_PEG_3_HALT_STATUS ); + halt_status = phantom_readl ( phantom, UNM_PEG_4_HALT_STATUS ); + phantom_writel ( phantom, halt_status, UNM_PEG_4_HALT_STATUS ); +} + +/** + * Initialise the Phantom command PEG + * + * @v phantom Phantom NIC + * @ret rc Return status code + */ +static int phantom_init_cmdpeg ( struct phantom_nic *phantom ) { + uint32_t cold_boot; + uint32_t sw_reset; + unsigned int retries; + uint32_t cmdpeg_state; + uint32_t last_cmdpeg_state = 0; + + /* Check for a previous initialisation. This could have + * happened if, for example, the BIOS used the UNDI API to + * drive the NIC prior to a full PXE boot. + */ + cmdpeg_state = phantom_readl ( phantom, UNM_NIC_REG_CMDPEG_STATE ); + if ( cmdpeg_state == UNM_NIC_REG_CMDPEG_STATE_INITIALIZE_ACK ) { + DBGC ( phantom, "Phantom %p command PEG already initialized\n", + phantom ); + /* Unhalt the PEGs. Previous firmware (e.g. BOFM) may + * have halted the PEGs to prevent internal bus + * collisions when the BIOS re-reads the expansion ROM. + */ + phantom_unhalt_pegs ( phantom ); + return 0; + } + + /* If this was a cold boot, check that the hardware came up ok */ + cold_boot = phantom_readl ( phantom, UNM_CAM_RAM_COLD_BOOT ); + if ( cold_boot == UNM_CAM_RAM_COLD_BOOT_MAGIC ) { + DBGC ( phantom, "Phantom %p coming up from cold boot\n", + phantom ); + sw_reset = phantom_readl ( phantom, UNM_ROMUSB_GLB_SW_RESET ); + if ( sw_reset != UNM_ROMUSB_GLB_SW_RESET_MAGIC ) { + DBGC ( phantom, "Phantom %p reset failed: %08x\n", + phantom, sw_reset ); + return -EIO; + } + } else { + DBGC ( phantom, "Phantom %p coming up from warm boot " + "(%08x)\n", phantom, cold_boot ); + } + /* Clear cold-boot flag */ + phantom_writel ( phantom, 0, UNM_CAM_RAM_COLD_BOOT ); + + /* Set port modes */ + phantom_writel ( phantom, UNM_CAM_RAM_PORT_MODE_AUTO_NEG_1G, + UNM_CAM_RAM_WOL_PORT_MODE ); + + /* Pass dummy DMA area to card */ + phantom_write_hilo ( phantom, 0, + UNM_NIC_REG_DUMMY_BUF_ADDR_LO, + UNM_NIC_REG_DUMMY_BUF_ADDR_HI ); + phantom_writel ( phantom, UNM_NIC_REG_DUMMY_BUF_INIT, + UNM_NIC_REG_DUMMY_BUF ); + + /* Tell the hardware that tuning is complete */ + phantom_writel ( phantom, UNM_ROMUSB_GLB_PEGTUNE_DONE_MAGIC, + UNM_ROMUSB_GLB_PEGTUNE_DONE ); + + /* Wait for command PEG to finish initialising */ + DBGC ( phantom, "Phantom %p initialising command PEG (will take up to " + "%d seconds)...\n", phantom, PHN_CMDPEG_INIT_TIMEOUT_SEC ); + for ( retries = 0; retries < PHN_CMDPEG_INIT_TIMEOUT_SEC; retries++ ) { + cmdpeg_state = phantom_readl ( phantom, + UNM_NIC_REG_CMDPEG_STATE ); + if ( cmdpeg_state != last_cmdpeg_state ) { + DBGC ( phantom, "Phantom %p command PEG state is " + "%08x after %d seconds...\n", + phantom, cmdpeg_state, retries ); + last_cmdpeg_state = cmdpeg_state; + } + if ( cmdpeg_state == UNM_NIC_REG_CMDPEG_STATE_INITIALIZED ) { + /* Acknowledge the PEG initialisation */ + phantom_writel ( phantom, + UNM_NIC_REG_CMDPEG_STATE_INITIALIZE_ACK, + UNM_NIC_REG_CMDPEG_STATE ); + return 0; + } + mdelay ( 1000 ); + } + + DBGC ( phantom, "Phantom %p timed out waiting for command PEG to " + "initialise (status %08x)\n", phantom, cmdpeg_state ); + return -ETIMEDOUT; +} + +/** + * Read Phantom MAC address + * + * @v phanton_port Phantom NIC + * @v hw_addr Buffer to fill with MAC address + */ +static void phantom_get_macaddr ( struct phantom_nic *phantom, + uint8_t *hw_addr ) { + union { + uint8_t mac_addr[2][ETH_ALEN]; + uint32_t dwords[3]; + } u; + unsigned long offset; + int i; + + /* Read the three dwords that include this MAC address and one other */ + offset = ( UNM_CAM_RAM_MAC_ADDRS + + ( 12 * ( phantom->port / 2 ) ) ); + for ( i = 0 ; i < 3 ; i++, offset += 4 ) { + u.dwords[i] = phantom_readl ( phantom, offset ); + } + + /* Copy out the relevant MAC address */ + for ( i = 0 ; i < ETH_ALEN ; i++ ) { + hw_addr[ ETH_ALEN - i - 1 ] = + u.mac_addr[ phantom->port & 1 ][i]; + } + DBGC ( phantom, "Phantom %p MAC address is %s\n", + phantom, eth_ntoa ( hw_addr ) ); +} + +/** + * Check Phantom is enabled for boot + * + * @v phanton_port Phantom NIC + * @ret rc Return status code + * + * This is something of an ugly hack to accommodate an OEM + * requirement. The NIC has only one expansion ROM BAR, rather than + * one per port. To allow individual ports to be selectively + * enabled/disabled for PXE boot (as required), we must therefore + * leave the expansion ROM always enabled, and place the per-port + * enable/disable logic within the iPXE driver. + */ +static int phantom_check_boot_enable ( struct phantom_nic *phantom ) { + unsigned long boot_enable; + + boot_enable = phantom_readl ( phantom, UNM_CAM_RAM_BOOT_ENABLE ); + if ( ! ( boot_enable & ( 1 << phantom->port ) ) ) { + DBGC ( phantom, "Phantom %p PXE boot is disabled\n", + phantom ); + return -ENOTSUP; + } + + return 0; +} + +/** + * Initialise Phantom receive PEG + * + * @v phantom Phantom NIC + * @ret rc Return status code + */ +static int phantom_init_rcvpeg ( struct phantom_nic *phantom ) { + unsigned int retries; + uint32_t rcvpeg_state; + uint32_t last_rcvpeg_state = 0; + + DBGC ( phantom, "Phantom %p initialising receive PEG (will take up to " + "%d seconds)...\n", phantom, PHN_RCVPEG_INIT_TIMEOUT_SEC ); + for ( retries = 0; retries < PHN_RCVPEG_INIT_TIMEOUT_SEC; retries++ ) { + rcvpeg_state = phantom_readl ( phantom, + UNM_NIC_REG_RCVPEG_STATE ); + if ( rcvpeg_state != last_rcvpeg_state ) { + DBGC ( phantom, "Phantom %p receive PEG state is " + "%08x after %d seconds...\n", + phantom, rcvpeg_state, retries ); + last_rcvpeg_state = rcvpeg_state; + } + if ( rcvpeg_state == UNM_NIC_REG_RCVPEG_STATE_INITIALIZED ) + return 0; + mdelay ( 1000 ); + } + + DBGC ( phantom, "Phantom %p timed out waiting for receive PEG to " + "initialise (status %08x)\n", phantom, rcvpeg_state ); + return -ETIMEDOUT; +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @v id PCI ID + * @ret rc Return status code + */ +static int phantom_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct phantom_nic *phantom; + struct settings *parent_settings; + int rc; + + /* Allocate Phantom device */ + netdev = alloc_etherdev ( sizeof ( *phantom ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc_etherdev; + } + netdev_init ( netdev, &phantom_operations ); + phantom = netdev_priv ( netdev ); + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( phantom, 0, sizeof ( *phantom ) ); + phantom->port = PCI_FUNC ( pci->busdevfn ); + assert ( phantom->port < PHN_MAX_NUM_PORTS ); + settings_init ( &phantom->settings, + &phantom_settings_operations, + &netdev->refcnt, &phantom_settings_scope ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map CRB */ + if ( ( rc = phantom_map_crb ( phantom, pci ) ) != 0 ) + goto err_map_crb; + + /* BUG5945 - need to hack PCI config space on P3 B1 silicon. + * B2 will have this fixed; remove this hack when B1 is no + * longer in use. + */ + if ( PCI_FUNC ( pci->busdevfn ) == 0 ) { + unsigned int i; + for ( i = 0 ; i < 8 ; i++ ) { + uint32_t temp; + pci->busdevfn = + PCI_BUSDEVFN ( PCI_BUS ( pci->busdevfn ), + PCI_SLOT ( pci->busdevfn ), i ); + pci_read_config_dword ( pci, 0xc8, &temp ); + pci_read_config_dword ( pci, 0xc8, &temp ); + pci_write_config_dword ( pci, 0xc8, 0xf1000 ); + } + pci->busdevfn = PCI_BUSDEVFN ( PCI_BUS ( pci->busdevfn ), + PCI_SLOT ( pci->busdevfn ), 0 ); + } + + /* Initialise the command PEG */ + if ( ( rc = phantom_init_cmdpeg ( phantom ) ) != 0 ) + goto err_init_cmdpeg; + + /* Initialise the receive PEG */ + if ( ( rc = phantom_init_rcvpeg ( phantom ) ) != 0 ) + goto err_init_rcvpeg; + + /* Read MAC addresses */ + phantom_get_macaddr ( phantom, netdev->hw_addr ); + + /* Skip if boot disabled on NIC */ + if ( ( rc = phantom_check_boot_enable ( phantom ) ) != 0 ) + goto err_check_boot_enable; + + /* Register network devices */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) { + DBGC ( phantom, "Phantom %p could not register net device: " + "%s\n", phantom, strerror ( rc ) ); + goto err_register_netdev; + } + + /* Register settings blocks */ + parent_settings = netdev_settings ( netdev ); + if ( ( rc = register_settings ( &phantom->settings, + parent_settings, "clp" ) ) != 0 ) { + DBGC ( phantom, "Phantom %p could not register settings: " + "%s\n", phantom, strerror ( rc ) ); + goto err_register_settings; + } + + return 0; + + unregister_settings ( &phantom->settings ); + err_register_settings: + unregister_netdev ( netdev ); + err_register_netdev: + err_check_boot_enable: + err_init_rcvpeg: + err_init_cmdpeg: + err_map_crb: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc_etherdev: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void phantom_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct phantom_nic *phantom = netdev_priv ( netdev ); + + unregister_settings ( &phantom->settings ); + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** Phantom PCI IDs */ +static struct pci_device_id phantom_nics[] = { + PCI_ROM ( 0x4040, 0x0100, "nx", "NX", 0 ), +}; + +/** Phantom PCI driver */ +struct pci_driver phantom_driver __pci_driver = { + .ids = phantom_nics, + .id_count = ( sizeof ( phantom_nics ) / sizeof ( phantom_nics[0] ) ), + .probe = phantom_probe, + .remove = phantom_remove, +}; diff --git a/qemu/roms/ipxe/src/drivers/net/phantom/phantom.h b/qemu/roms/ipxe/src/drivers/net/phantom/phantom.h new file mode 100644 index 000000000..1647168ba --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/phantom/phantom.h @@ -0,0 +1,213 @@ +#ifndef _PHANTOM_H +#define _PHANTOM_H + +/* + * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. + * Copyright (C) 2008 NetXen, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * @file + * + * NetXen Phantom NICs + * + */ + +#include <stdint.h> + +/* Drag in hardware definitions */ +#include "nx_bitops.h" +#include "phantom_hw.h" +struct phantom_rds { NX_PSEUDO_BIT_STRUCT ( struct phantom_rds_pb ) }; +struct phantom_sds { NX_PSEUDO_BIT_STRUCT ( struct phantom_sds_pb ) }; +union phantom_cds { NX_PSEUDO_BIT_STRUCT ( union phantom_cds_pb ) }; + +/* Drag in firmware interface definitions */ +typedef uint8_t U8; +typedef uint16_t U16; +typedef uint32_t U32; +typedef uint64_t U64; +typedef uint32_t nx_rcode_t; +#define NXHAL_VERSION 1 +#include "nxhal_nic_interface.h" + +/** DMA buffer alignment */ +#define UNM_DMA_BUFFER_ALIGN 16 + +/** Mark structure as DMA-aligned */ +#define __unm_dma_aligned __attribute__ (( aligned ( UNM_DMA_BUFFER_ALIGN ) )) + +/****************************************************************************** + * + * Register definitions + * + */ + +#define UNM_128M_CRB_WINDOW 0x6110210UL +#define UNM_32M_CRB_WINDOW 0x0110210UL +#define UNM_2M_CRB_WINDOW 0x0130060UL + +/** + * Phantom register blocks + * + * The upper address bits vary between cards. We define an abstract + * address space in which the upper 8 bits of the 32-bit register + * address encode the register block. This gets translated to a bus + * address by the phantom_crb_access_xxx() methods. + */ +enum unm_reg_blocks { + UNM_CRB_BLK_PCIE = 0x01, + UNM_CRB_BLK_CAM = 0x22, + UNM_CRB_BLK_ROMUSB = 0x33, + UNM_CRB_BLK_TEST = 0x02, + UNM_CRB_BLK_PEG_0 = 0x11, + UNM_CRB_BLK_PEG_1 = 0x12, + UNM_CRB_BLK_PEG_2 = 0x13, + UNM_CRB_BLK_PEG_3 = 0x14, + UNM_CRB_BLK_PEG_4 = 0x0f, +}; +#define UNM_CRB_BASE(blk) ( (blk) << 20 ) +#define UNM_CRB_BLK(reg) ( (reg) >> 20 ) +#define UNM_CRB_OFFSET(reg) ( (reg) & 0x000fffff ) + +#define UNM_CRB_PCIE UNM_CRB_BASE ( UNM_CRB_BLK_PCIE ) +#define UNM_PCIE_SEM2_LOCK ( UNM_CRB_PCIE + 0x1c010 ) +#define UNM_PCIE_SEM2_UNLOCK ( UNM_CRB_PCIE + 0x1c014 ) +#define UNM_PCIE_IRQ_VECTOR ( UNM_CRB_PCIE + 0x10100 ) +#define UNM_PCIE_IRQ_VECTOR_BIT(n) ( 1 << ( (n) + 7 ) ) +#define UNM_PCIE_IRQ_STATE ( UNM_CRB_PCIE + 0x1206c ) +#define UNM_PCIE_IRQ_STATE_TRIGGERED(state) (( (state) & 0x300 ) == 0x200 ) +#define UNM_PCIE_IRQ_MASK_F0 ( UNM_CRB_PCIE + 0x10128 ) +#define UNM_PCIE_IRQ_MASK_F1 ( UNM_CRB_PCIE + 0x10170 ) +#define UNM_PCIE_IRQ_MASK_F2 ( UNM_CRB_PCIE + 0x10174 ) +#define UNM_PCIE_IRQ_MASK_F3 ( UNM_CRB_PCIE + 0x10178 ) +#define UNM_PCIE_IRQ_MASK_F4 ( UNM_CRB_PCIE + 0x10370 ) +#define UNM_PCIE_IRQ_MASK_F5 ( UNM_CRB_PCIE + 0x10374 ) +#define UNM_PCIE_IRQ_MASK_F6 ( UNM_CRB_PCIE + 0x10378 ) +#define UNM_PCIE_IRQ_MASK_F7 ( UNM_CRB_PCIE + 0x1037c ) +#define UNM_PCIE_IRQ_MASK_MAGIC 0x0000fbffUL +#define UNM_PCIE_IRQ_STATUS_F0 ( UNM_CRB_PCIE + 0x10118 ) +#define UNM_PCIE_IRQ_STATUS_F1 ( UNM_CRB_PCIE + 0x10160 ) +#define UNM_PCIE_IRQ_STATUS_F2 ( UNM_CRB_PCIE + 0x10164 ) +#define UNM_PCIE_IRQ_STATUS_F3 ( UNM_CRB_PCIE + 0x10168 ) +#define UNM_PCIE_IRQ_STATUS_F4 ( UNM_CRB_PCIE + 0x10360 ) +#define UNM_PCIE_IRQ_STATUS_F5 ( UNM_CRB_PCIE + 0x10364 ) +#define UNM_PCIE_IRQ_STATUS_F6 ( UNM_CRB_PCIE + 0x10368 ) +#define UNM_PCIE_IRQ_STATUS_F7 ( UNM_CRB_PCIE + 0x1036c ) +#define UNM_PCIE_IRQ_STATUS_MAGIC 0xffffffffUL + +#define UNM_CRB_CAM UNM_CRB_BASE ( UNM_CRB_BLK_CAM ) + +#define UNM_CAM_RAM ( UNM_CRB_CAM + 0x02000 ) +#define UNM_CAM_RAM_PORT_MODE ( UNM_CAM_RAM + 0x00024 ) +#define UNM_CAM_RAM_PORT_MODE_AUTO_NEG 4 +#define UNM_CAM_RAM_PORT_MODE_AUTO_NEG_1G 5 +#define UNM_CAM_RAM_DMESG_HEAD(n) ( UNM_CAM_RAM + 0x00030 + (n) * 0x10 ) +#define UNM_CAM_RAM_DMESG_LEN(n) ( UNM_CAM_RAM + 0x00034 + (n) * 0x10 ) +#define UNM_CAM_RAM_DMESG_TAIL(n) ( UNM_CAM_RAM + 0x00038 + (n) * 0x10 ) +#define UNM_CAM_RAM_DMESG_SIG(n) ( UNM_CAM_RAM + 0x0003c + (n) * 0x10 ) +#define UNM_CAM_RAM_DMESG_SIG_MAGIC 0xcafebabeUL +#define UNM_CAM_RAM_NUM_DMESG_BUFFERS 5 +#define UNM_CAM_RAM_CLP_COMMAND ( UNM_CAM_RAM + 0x000c0 ) +#define UNM_CAM_RAM_CLP_COMMAND_LAST 0x00000080UL +#define UNM_CAM_RAM_CLP_DATA_LO ( UNM_CAM_RAM + 0x000c4 ) +#define UNM_CAM_RAM_CLP_DATA_HI ( UNM_CAM_RAM + 0x000c8 ) +#define UNM_CAM_RAM_CLP_STATUS ( UNM_CAM_RAM + 0x000cc ) +#define UNM_CAM_RAM_CLP_STATUS_START 0x00000001UL +#define UNM_CAM_RAM_CLP_STATUS_DONE 0x00000002UL +#define UNM_CAM_RAM_CLP_STATUS_ERROR 0x0000ff00UL +#define UNM_CAM_RAM_CLP_STATUS_UNINITIALISED 0xffffffffUL +#define UNM_CAM_RAM_BOOT_ENABLE ( UNM_CAM_RAM + 0x000fc ) +#define UNM_CAM_RAM_WOL_PORT_MODE ( UNM_CAM_RAM + 0x00198 ) +#define UNM_CAM_RAM_MAC_ADDRS ( UNM_CAM_RAM + 0x001c0 ) +#define UNM_CAM_RAM_COLD_BOOT ( UNM_CAM_RAM + 0x001fc ) +#define UNM_CAM_RAM_COLD_BOOT_MAGIC 0x55555555UL + +#define UNM_NIC_REG ( UNM_CRB_CAM + 0x02200 ) +#define UNM_NIC_REG_NX_CDRP ( UNM_NIC_REG + 0x00018 ) +#define UNM_NIC_REG_NX_ARG1 ( UNM_NIC_REG + 0x0001c ) +#define UNM_NIC_REG_NX_ARG2 ( UNM_NIC_REG + 0x00020 ) +#define UNM_NIC_REG_NX_ARG3 ( UNM_NIC_REG + 0x00024 ) +#define UNM_NIC_REG_NX_SIGN ( UNM_NIC_REG + 0x00028 ) +#define UNM_NIC_REG_DUMMY_BUF_ADDR_HI ( UNM_NIC_REG + 0x0003c ) +#define UNM_NIC_REG_DUMMY_BUF_ADDR_LO ( UNM_NIC_REG + 0x00040 ) +#define UNM_NIC_REG_CMDPEG_STATE ( UNM_NIC_REG + 0x00050 ) +#define UNM_NIC_REG_CMDPEG_STATE_INITIALIZED 0xff01 +#define UNM_NIC_REG_CMDPEG_STATE_INITIALIZE_ACK 0xf00f +#define UNM_NIC_REG_DUMMY_BUF ( UNM_NIC_REG + 0x000fc ) +#define UNM_NIC_REG_DUMMY_BUF_INIT 0 +#define UNM_NIC_REG_XG_STATE_P3 ( UNM_NIC_REG + 0x00098 ) +#define UNM_NIC_REG_XG_STATE_P3_LINK( port, state_p3 ) \ + ( ( (state_p3) >> ( (port) * 4 ) ) & 0x0f ) +#define UNM_NIC_REG_XG_STATE_P3_LINK_UP 0x01 +#define UNM_NIC_REG_XG_STATE_P3_LINK_DOWN 0x02 +#define UNM_NIC_REG_RCVPEG_STATE ( UNM_NIC_REG + 0x0013c ) +#define UNM_NIC_REG_RCVPEG_STATE_INITIALIZED 0xff01 + +#define UNM_CRB_ROMUSB UNM_CRB_BASE ( UNM_CRB_BLK_ROMUSB ) + +#define UNM_ROMUSB_GLB ( UNM_CRB_ROMUSB + 0x00000 ) +#define UNM_ROMUSB_GLB_STATUS ( UNM_ROMUSB_GLB + 0x00004 ) +#define UNM_ROMUSB_GLB_STATUS_ROM_DONE ( 1 << 1 ) +#define UNM_ROMUSB_GLB_SW_RESET ( UNM_ROMUSB_GLB + 0x00008 ) +#define UNM_ROMUSB_GLB_SW_RESET_MAGIC 0x0080000fUL +#define UNM_ROMUSB_GLB_PEGTUNE_DONE ( UNM_ROMUSB_GLB + 0x0005c ) +#define UNM_ROMUSB_GLB_PEGTUNE_DONE_MAGIC 0x31 + +#define UNM_ROMUSB_ROM ( UNM_CRB_ROMUSB + 0x10000 ) +#define UNM_ROMUSB_ROM_INSTR_OPCODE ( UNM_ROMUSB_ROM + 0x00004 ) +#define UNM_ROMUSB_ROM_ADDRESS ( UNM_ROMUSB_ROM + 0x00008 ) +#define UNM_ROMUSB_ROM_WDATA ( UNM_ROMUSB_ROM + 0x0000c ) +#define UNM_ROMUSB_ROM_ABYTE_CNT ( UNM_ROMUSB_ROM + 0x00010 ) +#define UNM_ROMUSB_ROM_DUMMY_BYTE_CNT ( UNM_ROMUSB_ROM + 0x00014 ) +#define UNM_ROMUSB_ROM_RDATA ( UNM_ROMUSB_ROM + 0x00018 ) + +#define UNM_CRB_TEST UNM_CRB_BASE ( UNM_CRB_BLK_TEST ) + +#define UNM_TEST_CONTROL ( UNM_CRB_TEST + 0x00090 ) +#define UNM_TEST_CONTROL_START 0x01 +#define UNM_TEST_CONTROL_ENABLE 0x02 +#define UNM_TEST_CONTROL_BUSY 0x08 +#define UNM_TEST_ADDR_LO ( UNM_CRB_TEST + 0x00094 ) +#define UNM_TEST_ADDR_HI ( UNM_CRB_TEST + 0x00098 ) +#define UNM_TEST_RDDATA_LO ( UNM_CRB_TEST + 0x000a8 ) +#define UNM_TEST_RDDATA_HI ( UNM_CRB_TEST + 0x000ac ) + +#define UNM_CRB_PEG_0 UNM_CRB_BASE ( UNM_CRB_BLK_PEG_0 ) +#define UNM_PEG_0_HALT_STATUS ( UNM_CRB_PEG_0 + 0x00030 ) +#define UNM_PEG_0_HALT ( UNM_CRB_PEG_0 + 0x0003c ) + +#define UNM_CRB_PEG_1 UNM_CRB_BASE ( UNM_CRB_BLK_PEG_1 ) +#define UNM_PEG_1_HALT_STATUS ( UNM_CRB_PEG_1 + 0x00030 ) +#define UNM_PEG_1_HALT ( UNM_CRB_PEG_1 + 0x0003c ) + +#define UNM_CRB_PEG_2 UNM_CRB_BASE ( UNM_CRB_BLK_PEG_2 ) +#define UNM_PEG_2_HALT_STATUS ( UNM_CRB_PEG_2 + 0x00030 ) +#define UNM_PEG_2_HALT ( UNM_CRB_PEG_2 + 0x0003c ) + +#define UNM_CRB_PEG_3 UNM_CRB_BASE ( UNM_CRB_BLK_PEG_3 ) +#define UNM_PEG_3_HALT_STATUS ( UNM_CRB_PEG_3 + 0x00030 ) +#define UNM_PEG_3_HALT ( UNM_CRB_PEG_3 + 0x0003c ) + +#define UNM_CRB_PEG_4 UNM_CRB_BASE ( UNM_CRB_BLK_PEG_4 ) +#define UNM_PEG_4_HALT_STATUS ( UNM_CRB_PEG_4 + 0x00030 ) +#define UNM_PEG_4_HALT ( UNM_CRB_PEG_4 + 0x0003c ) + +#endif /* _PHANTOM_H */ diff --git a/qemu/roms/ipxe/src/drivers/net/phantom/phantom_hw.h b/qemu/roms/ipxe/src/drivers/net/phantom/phantom_hw.h new file mode 100644 index 000000000..7dfff52b2 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/net/phantom/phantom_hw.h @@ -0,0 +1,185 @@ +#ifndef _PHANTOM_HW_H +#define _PHANTOM_HW_H + +/* + * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. + * Copyright (C) 2008 NetXen, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * @file + * + * Phantom hardware definitions + * + */ + +/** A Phantom RX descriptor */ +struct phantom_rds_pb { + pseudo_bit_t handle[16]; /**< Reference handle */ + pseudo_bit_t flags[16]; /**< Flags */ + pseudo_bit_t length[32]; /**< Buffer length */ + + /* --------------------------------------------------------------- */ + + pseudo_bit_t dma_addr[64]; /**< Buffer DMA address */ + +}; + +/** A Phantom RX status descriptor */ +struct phantom_sds_pb { + pseudo_bit_t port[4]; /**< Port number */ + pseudo_bit_t status[4]; /**< Checksum status */ + pseudo_bit_t type[4]; /**< Type */ + pseudo_bit_t total_length[16]; /**< Total packet length */ + pseudo_bit_t handle[16]; /**< Reference handle */ + pseudo_bit_t protocol[4]; /**< Protocol */ + pseudo_bit_t pkt_offset[5]; /**< Offset to packet start */ + pseudo_bit_t desc_cnt[3]; /**< Descriptor count */ + pseudo_bit_t owner[2]; /**< Owner */ + pseudo_bit_t opcode[6]; /**< Opcode */ + + /* --------------------------------------------------------------- */ + + pseudo_bit_t hash_value[32]; /**< RSS hash value */ + pseudo_bit_t hash_type[8]; /**< RSS hash type */ + pseudo_bit_t lro[8]; /**< LRO data */ +}; + +/** Phantom RX status opcodes */ +enum phantom_sds_opcode { + UNM_SYN_OFFLOAD = 0x03, + UNM_RXPKT_DESC = 0x04, +}; + +/** A Phantom TX descriptor */ +struct phantom_tx_cds_pb { + pseudo_bit_t tcp_hdr_offset[8]; /**< TCP header offset (LSO) */ + pseudo_bit_t ip_hdr_offset[8]; /**< IP header offset (LSO) */ + pseudo_bit_t flags[7]; /**< Flags */ + pseudo_bit_t opcode[6]; /**< Opcode */ + pseudo_bit_t hw_rsvd_0[3]; /**< (Reserved) */ + pseudo_bit_t num_buffers[8]; /**< Total number of buffers */ + pseudo_bit_t length[24]; /**< Total length */ + + /* --------------------------------------------------------------- */ + + pseudo_bit_t buffer2_dma_addr[64]; /**< Buffer 2 DMA address */ + + /* --------------------------------------------------------------- */ + + pseudo_bit_t handle[16]; /**< Reference handle (n/a) */ + pseudo_bit_t port_mss[16]; /**< TCP MSS (LSO) */ + pseudo_bit_t port[4]; /**< Port */ + pseudo_bit_t context_id[4]; /**< Context ID */ + pseudo_bit_t total_hdr_length[8]; /**< MAC+IP+TCP header (LSO) */ + pseudo_bit_t conn_id[16]; /**< IPSec connection ID */ + + /* --------------------------------------------------------------- */ + + pseudo_bit_t buffer3_dma_addr[64]; /**< Buffer 3 DMA address */ + + /* --------------------------------------------------------------- */ + + pseudo_bit_t buffer1_dma_addr[64]; /**< Buffer 1 DMA address */ + + /* --------------------------------------------------------------- */ + + pseudo_bit_t buffer1_length[16]; /**< Buffer 1 length */ + pseudo_bit_t buffer2_length[16]; /**< Buffer 2 length */ + pseudo_bit_t buffer3_length[16]; /**< Buffer 3 length */ + pseudo_bit_t buffer4_length[16]; /**< Buffer 4 length */ + + /* --------------------------------------------------------------- */ + + pseudo_bit_t buffer4_dma_addr[64]; /**< Buffer 4 DMA address */ + + /* --------------------------------------------------------------- */ + + pseudo_bit_t hw_rsvd_1[64]; /**< (Reserved) */ +}; + +/** A Phantom MAC address request body */ +struct phantom_nic_request_body_mac_request_pb { + pseudo_bit_t opcode[8]; /**< Opcode */ + pseudo_bit_t tag[8]; /**< Tag */ + pseudo_bit_t mac_addr_0[8]; /**< MAC address byte 0 */ + pseudo_bit_t mac_addr_1[8]; /**< MAC address byte 1 */ + pseudo_bit_t mac_addr_2[8]; /**< MAC address byte 2 */ + pseudo_bit_t mac_addr_3[8]; /**< MAC address byte 3 */ + pseudo_bit_t mac_addr_4[8]; /**< MAC address byte 4 */ + pseudo_bit_t mac_addr_5[8]; /**< MAC address byte 5 */ +}; + +/** Phantom MAC request opcodes */ +enum phantom_mac_request_opcode { + UNM_MAC_ADD = 0x01, /**< Add MAC address */ + UNM_MAC_DEL = 0x02, /**< Delete MAC address */ +}; + +/** A Phantom NIC request command descriptor */ +struct phantom_nic_request_cds_pb { + struct { + pseudo_bit_t dst_minor[18]; + pseudo_bit_t dst_subq[1]; + pseudo_bit_t dst_major[4]; + pseudo_bit_t opcode[6]; + pseudo_bit_t hw_rsvd_0[3]; + pseudo_bit_t msginfo[24]; + pseudo_bit_t hw_rsvd_1[2]; + pseudo_bit_t qmsg_type[6]; + } common; + + /* --------------------------------------------------------------- */ + + struct { + pseudo_bit_t opcode[8]; + pseudo_bit_t comp_id [8]; + pseudo_bit_t context_id[16]; + pseudo_bit_t need_completion[1]; + pseudo_bit_t hw_rsvd_0[23]; + pseudo_bit_t sub_opcode[8]; + } header; + + /* --------------------------------------------------------------- */ + + union { + struct phantom_nic_request_body_mac_request_pb mac_request; + pseudo_bit_t padding[384]; + } body; +}; + +/** Phantom NIC request opcodes */ +enum phantom_nic_request_opcode { + UNM_MAC_EVENT = 0x01, /**< Add/delete MAC address */ +}; + +/** A Phantom command descriptor */ +union phantom_cds_pb { + struct phantom_tx_cds_pb tx; + struct phantom_nic_request_cds_pb nic_request; +}; + +/** Phantom command descriptor opcodes */ +enum phantom_cds_opcode { + UNM_TX_ETHER_PKT = 0x01, /**< Transmit raw Ethernet */ + UNM_NIC_REQUEST = 0x14, /**< NIC request */ +}; + +#endif /* _PHANTOM_HW_H */ |