unm_nic_main.c revision 0dc2366f7b9f9f36e10909b1e95edbf2a261c2ac
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 NetXen, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/ethernet.h>
#include <sys/dditypes.h>
#include <sys/sysmacros.h>
#include <sys/ddi_impldefs.h>
#include "unm_nic_hw.h"
#include "unm_nic.h"
#include "nic_phan_reg.h"
#include "unm_nic_ioctl.h"
#include "nic_cmn.h"
#include "unm_version.h"
#include "unm_brdcfg.h"
#if defined(lint)
#endif /* lint */
#define UNM_ADAPTER_UP_MAGIC 777
#define VLAN_TAGSZ 0x4
/*
* Receive ISR processes NX_RX_MAXBUFS incoming packets at most, then posts
* as many buffers as packets processed. This loop repeats as required to
* process all incoming packets delivered in a single interrupt. Higher
* value of NX_RX_MAXBUFS improves performance by posting rx buffers less
* frequently, but at the cost of not posting quickly enough when card is
* running out of rx buffers.
*/
#define NX_RX_THRESHOLD 32
#define NX_RX_MAXBUFS 128
#define NX_MAX_TXCOMPS 256
static int unm_process_rcv_ring(unm_adapter *, int);
/* GLDv3 interface functions */
static int ntxn_m_start(void *);
static void ntxn_m_stop(void *);
static int ntxn_m_promisc(void *, boolean_t);
/*
* Allocates DMA handle, virtual memory and binds them
* returns size of actual memory binded and the physical address.
*/
int
{
int err;
*dma_handle = NULL;
if (size <= 0)
return (DDI_ENOMEM);
if (err != DDI_SUCCESS) {
return (DDI_ENOMEM);
}
handlep);
if (err != DDI_SUCCESS) {
"ret %d, request size: %d",
return (DDI_ENOMEM);
}
"memory :%d\n", unm_nic_driver_name,
return (DDI_FAILURE);
}
(ncookies != 1)) {
"!%s: %s: ddi_dma_addr_bind_handle FAILED: %d",
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* Unbinds the memory, frees the DMA handle and at the end, frees the memory
*/
void
{
int err;
if (err != DDI_SUCCESS) {
return;
}
}
static uint32_t msi_tgt_status[] = {
};
static void
{
__uint32_t temp = 0;
&temp, 4);
}
static inline int
{
/* check whether it's our interrupt */
if (!UNM_IS_MSI_FAMILY(adapter)) {
/* Legacy Interrupt case */
&status);
return (-1);
}
if (!ISR_IS_LEGACY_INTR_TRIGGERED(temp)) {
return (-1);
}
/* FIXME: Assumes pci_func is same as ctx */
if (our_int != 0) {
/* not our interrupt */
return (-1);
}
}
}
/* claim interrupt */
temp = 0xffffffff;
&mask);
/*
* Read again to make sure the legacy interrupt message got
* flushed out
*/
&mask);
/* clear interrupt */
temp = 0xffffffff;
}
return (0);
}
static void
{
&temp, 4);
if (!UNM_IS_MSI_FAMILY(adapter)) {
}
}
static void
{
adapter->context_alloced = 0;
}
}
}
}
}
}
}
}
static void
{
}
void
{
if (adapter->interrupt_crb) {
}
}
static int
{
/* Window 1 call */
if (state == PHAN_INITIALIZE_ACK)
return (0);
drv_usecwait(100);
/* Window 1 call */
loops++;
}
if (loops >= 200000) {
return (-EIO);
}
/* Window 1 call */
&tempout, 4);
&tempout, 4);
return (0);
}
/*
* Utility to synchronize with receive peg.
* Returns 0 on sucess
* -EIO on error
*/
int
{
/* Window 1 call */
drv_usecwait(100);
/* Window 1 call */
loops++;
}
if (loops >= 20000) {
state);
}
return (err);
}
/*
* check if the firmware has been downloaded and ready to run and
* setup the address for the descriptors in the adapter
*/
static int
{
void *addr;
int err;
int size;
return (err);
if (err != DDI_SUCCESS) {
return (err);
}
/*
* Allocate command descriptor ring.
*/
if (err != DDI_SUCCESS) {
return (err);
}
if (err != DDI_SUCCESS) {
goto free_cmd_desc;
}
/* rds rings */
if (err != DDI_SUCCESS) {
"rx desc ring %d\n", ring);
goto free_status_desc;
}
}
}
goto free_statusrx_desc;
return (DDI_SUCCESS);
return (err);
}
{
} else {
(void) ddi_dma_sync(handle, 0,
}
}
void
{
int data = crb_producer;
if (adapter->crb_addr_cmd_producer) {
}
}
static void
{
int data = crb_producer;
if (adapter->crb_addr_cmd_consumer)
}
/*
* Looks for type of packet and sets opcode accordingly
* so that checksum offload can be used.
*/
static void
{
return;
/*
* full checksum, even if stack has already done one or
* the other. Hardware will always get it correct even
* if stack has already done it.
*/
case IPPROTO_TCP:
break;
case IPPROTO_UDP:
break;
default:
/* Must be here with HCK_IPV4_HDRCKSUM */
return;
}
}
}
/*
* contiguous block ending at 8 byte aligned address as required by hardware.
* Caller assumes pktinfo->total_len will be updated by this function and
* if pktinfo->etype is set to 0, it will need to linearize the mblk and
* invoke unm_update_pkt_info() to determine ethertype, IP header len and
* protocol.
*/
static boolean_t
{
continue;
}
return (B_FALSE);
/*
* We just need non 1 byte aligned address, since ether_type is
* ushort.
*/
return (B_FALSE);
sizeof (ipha_t)))
return (B_FALSE);
type = ((struct ether_vlan_header *) \
} else {
}
/* IP header not aligned to quadward boundary? */
return (B_FALSE);
}
return (B_TRUE);
}
static void
{
} else {
}
}
}
static boolean_t
{
int no_of_desc = 1;
int MaxTxDescCount;
char *txb;
membar_enter();
MaxTxDescCount) <= 2) {
membar_exit();
return (B_FALSE);
}
continue;
}
/*
* Determine metadata if not previously done due to fragmented mblk.
*/
/* hwdesc->u1.s1.tcpHdrOffset = 0; */
/* hwdesc->mss = 0; */
sizeof (cmdDescType0_t),
return (B_TRUE);
}
/* Should be called with adapter->tx_lock held. */
static void
{
}
static unm_dmah_node_t *
{
membar_exit();
}
return (dmah);
}
static boolean_t
{
u32 saved_producer = 0;
int no_of_desc;
int k;
int MaxTxDescCount;
int ret, i;
uint32_t hdl_reserved = 0;
/* bind all the mblks of the packet first */
if (mblen == 0)
continue;
goto err_map;
}
if (ret != DDI_DMA_MAPPED)
goto err_map;
} else {
}
hdl_reserved++;
if (total_cookies > MAX_COOKIES_PER_CMD) {
goto err_map;
}
if (index == 0) {
/*
* there must be at least 16 bytes in the first
* descriptor.
*/
goto err_map;
}
} else {
if (hsize < 16) {
goto err_map;
}
}
}
index++;
ncookies--;
}
membar_enter();
/*
* If we are going to be trying the copy path, no point
* scheduling an upcall when Tx resources are freed.
*/
}
membar_exit();
goto err_alloc_desc;
}
/* Copy the descriptors into the hardware */
/* hwdesc->u1.s1.tcpHdrOffset = 0; */
/* hwdesc->mss = 0; */
for (i = k = 0; i < total_cookies; i++) {
if (k == 4) {
/* Move to the next descriptor */
k = 0;
}
switch (k) {
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
}
k++;
}
return (B_TRUE);
}
/*
* add the reserved but bind failed one to the list to be returned
*/
else {
}
hdl_reserved++;
}
return (B_FALSE);
}
static boolean_t
{
if (send_mapped == B_TRUE)
/* message too large */
}
return (status);
}
static int
{
int rv = 0;
return (0);
if (temp_state == NX_TEMP_PANIC) {
"maximum allowed, device has been shut down\n",
rv = 1;
} else if (temp_state == NX_TEMP_WARN) {
"operating range. Immediate action needed.\n",
}
} else {
"degrees C in normal range.\n",
}
}
return (rv);
}
static void
unm_watchdog(unsigned long v)
{
/*
* We return without turning on the netdev queue as there
* was an overheated device
*/
return;
}
/*
* This function schedules a call for itself.
*/
}
{
}
static void
{
int work_done, tx_complete;
loop:
goto loop;
}
/* ARGSUSED */
{
if (unm_nic_clear_int(adapter))
return (DDI_INTR_UNCLAIMED);
return (DDI_INTR_CLAIMED);
}
/*
* This is invoked from receive isr. Due to the single threaded nature
* of the invocation, pool_lock acquisition is not neccesary to protect
* pool_list.
*/
static void
{
/* mutex_enter(rcv_desc->pool_lock); */
rcv_desc->rx_buf_free++;
/* mutex_exit(rcv_desc->pool_lock); */
}
/*
* unm_process_rcv() send the received packet to the protocol stack.
*/
static mblk_t *
{
char *vaddr;
return (NULL);
}
return (NULL);
}
rcv_desc->rx_buf_card--;
/*
* Copy packet into new allocated message buffer, if pkt_length
* is below copy threshold.
*/
/*
* If card is running out of rx buffers, then attempt to allocate
* new mblk so we can feed this rx buffer back to card (we
* _could_ look at what's pending on free and recycle lists).
*/
docopy = 1;
}
if (docopy == 1) {
goto freebuf;
}
} else {
goto freebuf;
}
}
}
} else {
cksum_flags = 0;
}
return (mp);
return (NULL);
}
/* Process Receive status ring */
static int
{
break;
count++;
}
}
if (count) {
/* update the consumer index in phantom */
}
return (count);
}
/* Process Command status ring */
static int
{
int count = 0;
struct unm_cmd_buffer *buffer;
int done;
sizeof (uint32_t), DDI_DMA_SYNC_FORCPU);
while (last_consumer != consumer) {
free_hdls++;
}
} else {
}
}
}
if (++count > NX_MAX_TXCOMPS)
break;
}
if (count) {
int doresched;
membar_exit();
if (doresched)
adapter->resched_needed = 0;
if (doresched)
}
sizeof (uint32_t), DDI_DMA_SYNC_FORCPU);
return (done);
}
/*
* This is invoked from receive isr, and at initialization time when no
* rx buffers have been posted to card. Due to the single threaded nature
* of the invocation, pool_lock acquisition is not neccesary to protect
* pool_list.
*/
static unm_rx_buffer_t *
{
/* mutex_enter(rcv_desc->pool_lock); */
if (rcv_desc->rx_buf_free) {
rcv_desc->rx_buf_free--;
} else {
if (rcv_desc->rx_buf_recycle) {
rcv_desc->rx_buf_recycle = 0;
rcv_desc->rx_buf_free--;
}
}
/* mutex_exit(rcv_desc->pool_lock); */
return (rx_buffer);
}
static void
{
#define UNM_RCV_PEG_DB_ID 2
#define UNM_RCV_PRODUCER_OFFSET 0
/*
* Write a doorbell msg to tell phanmon of change in
* receive ring producer
*/
}
static int
{
int count;
}
else
return (DDI_FAILURE);
}
count--;
0, /* start */
count, /* count */
count, /* range */
sizeof (rcvDesc_t), /* unit_size */
DDI_DMA_SYNC_FORDEV); /* direction */
&count, 4);
return (DDI_SUCCESS);
}
static void
{
struct unm_rx_buffer *rx_buffer;
int last_producer = producer;
} else {
break;
}
}
/* if we did allocate buffers, then write the count to Phantom */
if (count) {
/* Sync rx ring, considering case for wrap around */
}
}
int
struct unm_statistics *unm_stats)
{
void *addr;
/* LINTED: E_FALSE_LOGICAL_EXPR */
/* LINTED: E_FALSE_LOGICAL_EXPR */
&(unm_stats->tx_packets));
/* LINTED: E_FALSE_LOGICAL_EXPR */
/* LINTED: E_FALSE_LOGICAL_EXPR */
&(unm_stats->rx_packets));
/* LINTED: E_FALSE_LOGICAL_EXPR */
/* LINTED: E_FALSE_LOGICAL_EXPR */
&(unm_stats->rx_CRC_errors));
/* LINTED: E_FALSE_LOGICAL_EXPR */
/* LINTED: E_FALSE_LOGICAL_EXPR */
/*
* For reading rx_MAC_error bit different procedure
* UNM_NIC_LOCKED_WRITE_REG(UNM_NIU_TEST_MUX_CTL, 0x15);
* UNM_NIC_LOCKED_READ_REG((UNM_CRB_NIU + 0xC0), &temp);
* unm_stats->rx_MAC_errors = temp & 0xff;
*/
} else {
unm_stats->rx_CRC_errors = 0;
unm_stats->rx_MAC_errors = 0;
}
return (0);
}
int
struct unm_statistics *unm_stats)
{
(void) unm_nic_hw_read_wx_2M(adapter,
(void) unm_nic_hw_read_wx_2M(adapter,
(void) unm_nic_hw_read_wx_2M(adapter,
} else {
unm_stats->rx_CRC_errors = 0;
unm_stats->rx_MAC_errors = 0;
}
return (0);
}
int
{
void *addr;
int data = 0;
/* LINTED: E_FALSE_LOGICAL_EXPR */
/* LINTED: E_FALSE_LOGICAL_EXPR */
/* LINTED: E_FALSE_LOGICAL_EXPR */
/* LINTED: E_FALSE_LOGICAL_EXPR */
/* LINTED: E_FALSE_LOGICAL_EXPR */
/* LINTED: E_FALSE_LOGICAL_EXPR */
/* LINTED: E_FALSE_LOGICAL_EXPR */
/* LINTED: E_FALSE_LOGICAL_EXPR */
return (0);
}
int
{
int data = 0;
&data, 4);
&data, 4);
&data, 4);
&data, 4);
&data, 4);
&data, 4);
&data, 4);
&data, 4);
return (0);
}
/*
* through these ioctls.
*/
static void
{
void *ptr;
switch (cmd) {
case UNM_NIC_CMD:
break;
case UNM_NIC_NAME:
/*
* Phanmon checks for "UNM-UNM" string
* Replace the hardcoded value with appropriate macro
*/
break;
default:
break;
}
}
int
{
(void *) adapter, 50000);
else
return (DDI_SUCCESS);
}
int
{
else
return (DDI_SUCCESS);
}
static int
{
struct unm_nic_ioctl_data *up_data;
int retval = 0;
uint64_t efuse_chip_id = 0;
char *ptr1;
short *ptr2;
int *ptr4;
sizeof (data));
/* Shouldn't access beyond legal limits of "char u[64];" member */
/* evil user tried to crash the kernel */
retval = GLD_BADARG;
goto error_out;
}
case unm_nic_cmd_pci_read:
goto error_out;
}
break;
case unm_nic_cmd_pci_write:
"returned %d\n", __FUNCTION__,
goto error_out;
}
break;
case unm_nic_cmd_pci_mem_read:
"returned %d\n", __FUNCTION__,
goto error_out;
}
break;
"%s(%d) unm_nic_cmd_pci_mem_write "
"returned %d\n",
goto error_out;
}
break;
&conf_handle)) != DDI_SUCCESS) {
goto error_out;
} else
case 1:
break;
case 2:
break;
case 4:
break;
}
break;
&conf_handle)) != DDI_SUCCESS) {
goto error_out;
} else {
}
case 1:
break;
case 2:
break;
case 4:
break;
}
break;
case unm_nic_cmd_get_stats:
(struct unm_statistics *)up_data);
break;
case unm_nic_cmd_clear_stats:
break;
case unm_nic_cmd_get_version:
sizeof (UNM_NIC_VERSIONID));
break;
case unm_nic_cmd_get_phy_type:
break;
efuse_chip_id <<= 32;
break;
default:
goto error_out;
}
return (DDI_SUCCESS);
return (retval);
}
/*
* Local datatype for defining tables of (Offset, Name) pairs
*/
typedef struct {
char *name;
static const unm_ksindex_t unm_kstat[] = {
{ 0, "freehdls" },
{ 1, "freecmds" },
{ 2, "tx_bcopy_threshold" },
{ 3, "rx_bcopy_threshold" },
{ 4, "xmitcalled" },
{ 5, "xmitedframes" },
{ 6, "xmitfinished" },
{ 7, "txbytes" },
{ 8, "txcopyed" },
{ 9, "txmapped" },
{ 10, "outoftxdmahdl" },
{ 11, "outofcmddesc" },
{ 12, "txdropped" },
{ 13, "polled" },
{ 14, "uphappy" },
{ 15, "updropped" },
{ 16, "csummed" },
{ 17, "no_rcv" },
{ 18, "rxbytes" },
{ 19, "rxcopyed" },
{ 20, "rxmapped" },
{ 21, "desballocfailed" },
{ 22, "outofrxbuf" },
{ 23, "promiscmode" },
{ 24, "rxbufshort" },
{ 25, "allocbfailed" },
{ -1, NULL }
};
static int
{
if (flag != KSTAT_READ)
return (EACCES);
return (0);
}
static kstat_t *
{
char *np;
int type;
int count = 0;
size /= sizeof (unm_ksindex_t);
return (NULL);
count++;
switch (*np) {
default:
break;
case '%':
np += 1;
break;
case '$':
np += 1;
break;
case '&':
np += 1;
break;
}
}
return (ksp);
}
void
{
sizeof (unm_kstat), unm_kstat_update);
}
void
{
}
}
static int
{
int ret = 0;
} else
return (ret);
}
/*
*/
static int
ntxn_m_start(void *arg)
{
int ring;
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
goto dest_rings;
}
if (unm_nic_hw_resources(adapter) != 0) {
goto dest_rings;
}
}
goto free_hw_res;
}
}
goto free_hw_res;
}
if (unm_nic_init_port(adapter) != 0) {
goto free_hw_res;
}
/*
* P2 and P3 should be handled similarly.
*/
if (unm_nic_set_promisc_mode(adapter) != 0) {
goto stop_and_free;
}
} else {
}
goto stop_and_free;
}
(void *)adapter, 0);
else
return (GLD_SUCCESS);
return (DDI_FAILURE);
}
/*
* This code is kept here for reference so as to
* see if something different is required to be done
* in GLDV3. This will be deleted later.
*/
/* ARGSUSED */
static void
ntxn_m_stop(void *arg)
{
}
/*ARGSUSED*/
static int
{
/*
* When we correctly implement this, invoke nx_p3_nic_set_multi()
* or nx_p2_nic_set_multi() here.
*/
return (GLD_SUCCESS);
}
/*ARGSUSED*/
static int
{
#if 0
int err = 0;
if (err)
return (GLD_FAILURE);
#endif
return (GLD_SUCCESS);
}
static int
{
switch (stat) {
case MAC_STAT_IFSPEED:
/* 10 Gigs */
*val = 10000000000ULL;
} else {
/* 1 Gig */
*val = 1000000000;
}
break;
case MAC_STAT_MULTIRCV:
*val = 0;
break;
case MAC_STAT_BRDCSTRCV:
case MAC_STAT_BRDCSTXMT:
*val = 0;
break;
case MAC_STAT_NORCVBUF:
break;
case MAC_STAT_NOXMTBUF:
break;
case MAC_STAT_RBYTES:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_OPACKETS:
break;
case MAC_STAT_IPACKETS:
break;
case MAC_STAT_OERRORS:
break;
case ETHER_STAT_LINK_DUPLEX:
*val = LINK_DUPLEX_FULL;
break;
default:
/*
* Shouldn't reach here...
*/
*val = 0;
"returned 1\n", stat));
}
return (0);
}
static int
{
return (EAGAIN);
return (0);
}
static mblk_t *
{
break;
}
}
return (mp);
}
static void
{
int cmd;
switch (status) {
default:
case IOC_INVAL:
break;
case IOC_DONE:
break;
case IOC_RESTART_ACK:
case IOC_ACK:
break;
case IOC_RESTART_REPLY:
case IOC_REPLY:
break;
}
return;
} else {
return;
}
}
/* ARGSUSED */
static boolean_t
{
switch (cap) {
case MAC_CAPAB_HCKSUM:
{
*txflags = (HCKSUM_ENABLE |
}
break;
#ifdef SOLARIS11
case MAC_CAPAB_ANCHOR_VNIC:
case MAC_CAPAB_MULTIFACTADDR:
#else
case MAC_CAPAB_POLL:
case MAC_CAPAB_MULTIADDRESS:
#endif
default:
return (B_FALSE);
}
return (B_TRUE);
}
static mac_callbacks_t ntxn_m_callbacks = {
NULL, /* mc_reserved */
NULL, /* mc_open */
NULL, /* mc_close */
NULL, /* mc_setprop */
NULL /* mc_getprop */
};
int
{
int ret;
return (DDI_FAILURE);
}
#ifdef SOLARIS11
#endif /* SOLARIS11 */
if (ret != 0) {
return (DDI_FAILURE);
}
/* Register NDD-tweakable parameters */
if (unm_nd_init(adapter)) {
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}