nge_main.c revision 2d58516de420814c8fd871526b0aaa3cd9fb9443
/*
* 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 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "nge.h"
/*
* Describes the chip's DMA engine
*/
static ddi_dma_attr_t hot_dma_attr = {
DMA_ATTR_V0, /* dma_attr version */
0x0000000000000000ull, /* dma_attr_addr_lo */
0x000000FFFFFFFFFFull, /* dma_attr_addr_hi */
0x000000007FFFFFFFull, /* dma_attr_count_max */
0x0000000000000010ull, /* dma_attr_align */
0x00000FFF, /* dma_attr_burstsizes */
0x00000001, /* dma_attr_minxfer */
0x000000000000FFFFull, /* dma_attr_maxxfer */
0x000000FFFFFFFFFFull, /* dma_attr_seg */
1, /* dma_attr_sgllen */
0x00000001, /* dma_attr_granular */
0
};
static ddi_dma_attr_t hot_tx_dma_attr = {
DMA_ATTR_V0, /* dma_attr version */
0x0000000000000000ull, /* dma_attr_addr_lo */
0x000000FFFFFFFFFFull, /* dma_attr_addr_hi */
0x0000000000003FFFull, /* dma_attr_count_max */
0x0000000000000010ull, /* dma_attr_align */
0x00000FFF, /* dma_attr_burstsizes */
0x00000001, /* dma_attr_minxfer */
0x0000000000003FFFull, /* dma_attr_maxxfer */
0x000000FFFFFFFFFFull, /* dma_attr_seg */
NGE_MAX_COOKIES, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0
};
static ddi_dma_attr_t sum_dma_attr = {
DMA_ATTR_V0, /* dma_attr version */
0x0000000000000000ull, /* dma_attr_addr_lo */
0x00000000FFFFFFFFull, /* dma_attr_addr_hi */
0x000000007FFFFFFFull, /* dma_attr_count_max */
0x0000000000000010ull, /* dma_attr_align */
0x00000FFF, /* dma_attr_burstsizes */
0x00000001, /* dma_attr_minxfer */
0x000000000000FFFFull, /* dma_attr_maxxfer */
0x00000000FFFFFFFFull, /* dma_attr_seg */
1, /* dma_attr_sgllen */
0x00000001, /* dma_attr_granular */
0
};
static ddi_dma_attr_t sum_tx_dma_attr = {
DMA_ATTR_V0, /* dma_attr version */
0x0000000000000000ull, /* dma_attr_addr_lo */
0x00000000FFFFFFFFull, /* dma_attr_addr_hi */
0x0000000000003FFFull, /* dma_attr_count_max */
0x0000000000000010ull, /* dma_attr_align */
0x00000FFF, /* dma_attr_burstsizes */
0x00000001, /* dma_attr_minxfer */
0x0000000000003FFFull, /* dma_attr_maxxfer */
0x00000000FFFFFFFFull, /* dma_attr_seg */
NGE_MAX_COOKIES, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0
};
/*
* DMA access attributes for data.
*/
};
/*
* DMA access attributes for descriptors.
*/
static ddi_device_acc_attr_t nge_desc_accattr = {
};
/*
* PIO access attributes for registers
*/
static ddi_device_acc_attr_t nge_reg_accattr = {
};
/*
* NIC DESC MODE 2
*/
static const nge_desc_attr_t nge_sum_desc = {
sizeof (sum_rx_bd),
sizeof (sum_tx_bd),
};
/*
* NIC DESC MODE 3
*/
static const nge_desc_attr_t nge_hot_desc = {
sizeof (hot_rx_bd),
sizeof (hot_tx_bd),
};
static char nge_ident[] = "nVidia 1Gb Ethernet %I%";
static char clsize_propname[] = "cache-line-size";
static char latency_propname[] = "latency-timer";
static char debug_propname[] = "nge-debug-flags";
static char intr_moderation[] = "intr-moderation";
static char rx_data_hw[] = "rx-data-hw";
static char rx_prd_lw[] = "rx-prd-lw";
static char rx_prd_hw[] = "rx-prd-hw";
static char sw_intr_intv[] = "sw-intr-intvl";
static char nge_desc_mode[] = "desc-mode";
static char default_mtu[] = "default_mtu";
static char low_memory_mode[] = "minimal-memory-usage";
static int nge_m_start(void *);
static void nge_m_stop(void *);
static int nge_m_promisc(void *, boolean_t);
static int nge_m_unicst(void *, const uint8_t *);
static mac_callbacks_t nge_m_callbacks = {
NULL,
};
static int nge_add_intrs(nge_t *, int);
static void nge_rem_intrs(nge_t *);
static int nge_register_intrs_and_init_locks(nge_t *);
/*
* NGE MSI tunable:
*/
static enum ioc_reply
{
/*
* If the mode isn't being changed, there's nothing to do ...
*/
return (IOC_ACK);
/*
* Validate the requested mode and prepare a suitable message
* probably induce ...
*/
switch (mode) {
default:
return (IOC_INVAL);
case NGE_LOOP_NONE:
case NGE_LOOP_EXTERNAL_100:
case NGE_LOOP_EXTERNAL_10:
case NGE_LOOP_INTERNAL_PHY:
break;
}
/*
* All OK; tell the caller to reprogram
*/
return (IOC_RESTART_ACK);
}
#define NGE_DBG NGE_DBG_INIT
/*
* Utility routine to carve a slice off a chunk of allocated memory,
* updating the chunk descriptor accordingly. The size of the slice
* is given by the product of the <qty> and <size> parameters.
*/
void
{
}
/*
* Allocate an area of memory and a DMA handle for accessing it
*/
int
{
int err;
NGE_TRACE(("nge_alloc_dma_mem($%p, %ld, $%p, 0x%x, $%p)",
/*
* Allocate handle
*/
if (err != DDI_SUCCESS)
goto fail;
/*
* Allocate memory
*/
if (err != DDI_SUCCESS)
goto fail;
/*
* Bind the two together
*/
goto fail;
return (DDI_SUCCESS);
fail:
NGE_DEBUG(("nge_alloc_dma_mem: fail to alloc dma memory!"));
return (DDI_FAILURE);
}
/*
* Free one allocated area of DMAable memory
*/
void
{
}
}
}
}
}
#define ALLOC_TX_BUF 0x1
#define ALLOC_TX_DESC 0x2
#define ALLOC_RX_DESC 0x4
int
{
int err;
int split;
int progress;
progress = 0;
/*
* Allocate memory & handles for TX buffers
*/
if (err != DDI_SUCCESS)
goto fail;
}
progress |= ALLOC_TX_BUF;
/*
* Allocate memory & handles for receive return rings and
* buffer (producer) descriptor rings
*/
if (err != DDI_SUCCESS)
goto fail;
/*
* Allocate memory & handles for TX descriptor rings,
*/
if (err != DDI_SUCCESS)
goto fail;
return (DDI_SUCCESS);
fail:
if (progress & ALLOC_RX_DESC)
if (progress & ALLOC_TX_BUF) {
}
return (DDI_FAILURE);
}
/*
* This routine frees the transmit and receive buffers and descriptors.
* Make sure the chip is stopped before calling it!
*/
void
{
int split;
}
/*
* Clean up initialisation done above before the memory is freed
*/
static void
{
}
}
}
/*
* Initialise the specified Send Ring, using the information in the
* <dma_area> descriptors that it contains to set up all the other
* fields. This routine should be called only once for each ring.
*/
static int
{
/*
* Other one-off initialisation of per-ring data
*/
/*
* Allocate the array of s/w Send Buffer Descriptors
*/
/*
* Now initialise each array element once and for all
*/
}
}
/* preallocate dma handles for tx buffer */
if (err != DDI_SUCCESS) {
"nge_init_send_ring: alloc dma handle fails");
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
/*
* Intialize the tx recycle pointer and tx sending pointer of tx ring
* and set the type of tx's data descriptor by default.
*/
static void
{
/*
* Reinitialise control variables ...
*/
/*
* Zero and sync all the h/w Send Buffer Descriptors
*/
}
}
/*
* Initialize the slot number of rx's ring
*/
static void
{
}
/*
* Intialize the rx recycle pointer and rx sending pointer of rx ring
*/
static void
{
/*
* Reinitialise control variables ...
*/
rrp->prod_index = 0;
/*
* Zero and sync all the h/w Send Buffer Descriptors
*/
}
/*
* Clean up initialisation done above before the memory is freed
*/
static void
{
uint32_t i;
}
}
}
}
}
/*
* Intialize the Rx's data ring and free ring
*/
static int
{
/*
* Allocate the array of s/w Recv Buffer Descriptors
*/
+ NGE_HEADROOM),
if (err != DDI_SUCCESS) {
return (DDI_FAILURE);
}
0, &bufp->rx_recycle);
return (DDI_FAILURE);
}
}
/*
* Now initialise each array element once and for all
*/
}
return (DDI_SUCCESS);
}
/*
* Fill the host address of data in rx' descriptor
* and initialize free pointers of rx free ring
*/
static int
{
void *hw_bd_p;
/*
* There is a scenario: When the traffic of small tcp
* packet is heavy, suspending the tcp traffic will
* cause the preallocated buffers for rx not to be
* released in time by tcp taffic and cause rx's buffer
* pointers not to be refilled in time.
*
* At this point, if we reinitialize the driver, the bufp
* pointer for rx's traffic will be NULL.
* So the result of the reinitializion fails.
*/
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
{
/* Init the locks for send ring */
/* Init parameters of buffer ring */
/* Init recycle list lock */
}
int
{
if (err != DDI_SUCCESS) {
return (err);
}
if (err != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (err);
}
static int
{
int err;
return (err);
}
void
{
/*
* For receive ring, nothing need to be finished.
* So only finish buffer ring and send ring here.
*/
}
/*
* Loopback ioctl code
*/
static lb_property_t loopmodes[] = {
};
enum ioc_reply
{
int cmd;
/*
* Validate format of ioctl
*/
return (IOC_INVAL);
switch (cmd) {
default:
return (IOC_INVAL);
case LB_GET_INFO_SIZE:
return (IOC_INVAL);
return (IOC_REPLY);
case LB_GET_INFO:
return (IOC_INVAL);
return (IOC_REPLY);
case LB_GET_MODE:
return (IOC_INVAL);
return (IOC_REPLY);
case LB_SET_MODE:
return (IOC_INVAL);
}
}
#define NGE_DBG NGE_DBG_NEMO
static void
{
}
}
/*
* nge_get_props -- get the parameters to tune the driver
*/
static void
{
if (dev_param_p->jumbo) {
} else
} else if (ngep->lowmem_mode != 0) {
} else {
}
}
static int
{
int err;
if (err == DDI_FAILURE) {
return (err);
}
/*
* Clear the Multicast mac address table
*/
if (err == DDI_FAILURE)
return (err);
ngep->factotum_flag = 0;
ngep->resched_needed = 0;
/* Clear the software statistics */
sw_stp->recv_count = 0;
sw_stp->xmit_count = 0;
return (DDI_SUCCESS);
}
static void
nge_m_stop(void *arg)
{
/*
* Just stop processing, then record new MAC state
*/
/* If suspended, the adapter is already stopped, just return. */
return;
}
/* Try to wait all the buffer post to upper layer be released */
/* Recycle all the TX BD */
}
static int
nge_m_start(void *arg)
{
int err;
/*
* Start processing and record new MAC state
*/
/*
* If suspended, don't start, as the resume processing
* will recall this function with the suspended flag off.
*/
return (DDI_FAILURE);
}
if (err != DDI_SUCCESS) {
goto finish;
}
if (err != DDI_SUCCESS) {
goto finish;
}
return (err);
}
static int
{
/*
* Remember the new current address in the driver state
* Sync the chip's idea of the address too ...
*/
/*
* If we are suspended, we want to quit now, and not update
* the chip. Doing so might put it in a bad state, but the
* resume will get the unicast address installed.
*/
return (DDI_SUCCESS);
}
return (0);
}
static int
{
/*
* Store specified mode and pass to chip layer to update h/w
*/
/*
* If suspended, there is no need to do anything, even
* recording the promiscuious mode is not neccessary, as
* it won't be properly set on resume. Just return failing.
*/
return (DDI_FAILURE);
}
return (0);
}
return (0);
}
{
}
}
}
}
}
static int
{
if (add) {
if (b_eq) {
break;
}
plist_prev = plist;
}
}
if (plist_prev == NULL)
else
}
} else {
if (b_eq) {
break;
}
plist_prev = plist;
}
if (update) {
if ((plist_prev == NULL) &&
else if ((plist_prev == NULL) &&
else
}
}
}
}
return (0);
}
static void
{
int err;
int cmd;
/*
* If suspended, we might actually be able to do some of
* these ioctls, but it is harder to make sure they occur
* without actually putting the hardware in an undesireable
* state. So just NAK it.
*/
return;
}
/*
* Validate the command before bothering with the mutex ...
*/
switch (cmd) {
default:
("nge_m_ioctl: unknown cmd 0x%x", cmd));
return;
case NGE_MII_READ:
case NGE_MII_WRITE:
case NGE_SEE_READ:
case NGE_SEE_WRITE:
case NGE_DIAG:
case NGE_PEEK:
case NGE_POKE:
case NGE_PHY_RESET:
case NGE_SOFT_RESET:
case NGE_HARD_RESET:
break;
case LB_GET_INFO_SIZE:
case LB_GET_INFO:
case LB_GET_MODE:
break;
case LB_SET_MODE:
break;
case ND_GET:
break;
case ND_SET:
break;
}
if (need_privilege) {
/*
* Check for specific net_config privilege.
*/
if (err != 0) {
NGE_DEBUG(("nge_m_ioctl: rejected cmd 0x%x, err %d",
return;
}
}
switch (cmd) {
default:
break;
case NGE_MII_READ:
case NGE_MII_WRITE:
case NGE_SEE_READ:
case NGE_SEE_WRITE:
case NGE_DIAG:
case NGE_PEEK:
case NGE_POKE:
case NGE_PHY_RESET:
case NGE_SOFT_RESET:
case NGE_HARD_RESET:
break;
case LB_GET_INFO_SIZE:
case LB_GET_INFO:
case LB_GET_MODE:
case LB_SET_MODE:
break;
case ND_GET:
case ND_SET:
break;
}
/*
* Do it now, while we still have the mutex.
*
* Note: update the PHY first, 'cos it controls the
*/
switch (status) {
case IOC_RESTART_REPLY:
case IOC_RESTART_ACK:
break;
default:
break;
}
/*
* Finally, decide how to reply
*/
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;
}
}
/* ARGSUSED */
static boolean_t
{
switch (cap) {
case MAC_CAPAB_HCKSUM: {
if (dev_param_p->tx_hw_checksum) {
} else
return (B_FALSE);
break;
}
case MAC_CAPAB_POLL:
/*
* There's nothing for us to fill in, simply returning
* B_TRUE, stating that we support polling is sufficient.
*/
break;
default:
return (B_FALSE);
}
return (B_TRUE);
}
int
{
int err = 0;
if (!err)
if (err) {
return (DDI_FAILURE);
} else {
return (DDI_SUCCESS);
}
}
void
{
if (ngep->factotum_flag == 0) {
}
}
/*
* High-level cyclic handler
*
* This routine schedules a (low-level) softint callback to the
* factotum.
*/
static void
nge_chip_cyclic(void *arg)
{
switch (ngep->nge_chip_state) {
default:
return;
case NGE_CHIP_RUNNING:
break;
case NGE_CHIP_FAULT:
case NGE_CHIP_ERROR:
break;
}
}
static void
{
/*
* Flag that no more activity may be initiated
*/
/*
* Quiesce the PHY and MAC (leave it reset but still powered).
* Clean up and free all NGE data structures
*/
}
}
}
}
static int
{
int err;
err = 0;
/*
* If there are state inconsistancies, this is bad. Returning
* DDI_FAILURE here will eventually cause the machine to panic,
* so it is best done here so that there is a possibility of
* debugging the problem.
*/
"nge: ngep returned from ddi_get_driver_private was NULL");
"nge: passed devinfo not the same as saved devinfo");
/*
* Fetch the config space. Even though we have most of it cached,
*/
/*
* Only in one case, this conditional branch can be executed: the port
* hasn't been plumbed.
*/
return (DDI_SUCCESS);
}
if (!err) {
if (!err)
}
if (err) {
/*
* We note the failure, but return success, as the
* system is still usable without this controller.
*/
} else {
}
return (DDI_SUCCESS);
}
/*
* attach(9E) -- Attach a device to the system
*
* Called once for each board successfully probed.
*/
static int
{
int err;
int i;
int instance;
switch (cmd) {
default:
return (DDI_FAILURE);
case DDI_RESUME:
return (nge_resume(devinfo));
case DDI_ATTACH:
break;
}
if (err != DDI_SUCCESS) {
goto attach_fail;
}
if (err != DDI_SUCCESS) {
goto attach_fail;
}
if (err != DDI_SUCCESS) {
" register intrs and init locks failed");
goto attach_fail;
}
/*
* Initialise link state variables
* Stop, reset & reinitialise the chip.
* Initialise the (internal) PHY.
*/
if (err != DDI_SUCCESS) {
goto attach_fail;
}
/*
* Now that mutex locks are initialized, enable interrupts.
*/
/* Call ddi_intr_block_enable() for MSI interrupts */
} else {
/* Call ddi_intr_enable for MSI or FIXED interrupts */
for (i = 0; i < ngep->intr_actual_cnt; i++) {
}
}
/*
* Register NDD-tweakable parameters
*/
if (nge_nd_init(ngep)) {
goto attach_fail;
}
/*
* Create & initialise named kstats
*/
goto attach_fail;
/*
* Finally, we're ready to register ourselves with the mac
* interface; if this succeeds, we're all ready to start()
*/
if (err != 0)
goto attach_fail;
/*
* Register a periodical handler.
* nge_chip_cyclic() is invoked in kernel context.
*/
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
static int
{
/* if the port hasn't been plumbed, just return */
return (DDI_SUCCESS);
}
return (DDI_SUCCESS);
}
/*
* detach(9E) -- Detach a device from the system
*/
static int
{
int i;
switch (cmd) {
default:
return (DDI_FAILURE);
case DDI_SUSPEND:
/*
* Stop the NIC
* Note: This driver doesn't currently support WOL, but
* should it in the future, it is important to
* make sure the PHY remains powered so that the
* wakeup packet can actually be recieved.
*/
return (nge_suspend(ngep));
case DDI_DETACH:
break;
}
/* Try to wait all the buffer post to upper layer be released */
for (i = 0; i < 1000; i++) {
break;
drv_usecwait(1000);
}
/* If there is any posted buffer, reject to detach */
return (DDI_FAILURE);
/* Recycle the multicast table */
}
/*
* Unregister from the GLD subsystem. This can fail, in
* particular if there are DLPI style-2 streams still open -
* in which case we just return failure without shutting
* down chip operations.
*/
return (DDI_FAILURE);
/*
* All activity stopped, so we can clean up & exit
*/
return (DDI_SUCCESS);
}
/*
* ========== Module Loading Data & Entry Points ==========
*/
static struct modldrv nge_modldrv = {
&mod_driverops, /* Type of module. This one is a driver */
nge_ident, /* short description */
&nge_dev_ops /* driver specific ops */
};
static struct modlinkage modlinkage = {
};
int
{
}
int
_init(void)
{
int status;
if (status != DDI_SUCCESS)
else
return (status);
}
int
_fini(void)
{
int status;
if (status == DDI_SUCCESS) {
}
return (status);
}
/*
* ============ Init MSI/Fixed/SoftInterrupt routines ==============
*/
/*
* Register interrupts and initialize each mutex and condition variables
*/
static int
{
int err;
int intr_types;
/*
* Add the softint handlers:
*
* Both of these handlers are used to avoid restrictions on the
* particular, the hardware interrupt handler and its subfunctions
* can detect a number of conditions that we don't want to handle
* in that context or with that set of mutexes held. So, these
* softints are triggered instead:
*
* the <resched> softint is triggered if if we have previously
* had to refuse to send a packet because of resource shortage
* (we've run out of transmit buffers), but the send completion
* interrupt handler has now detected that more buffers have
* become available. Its only purpose is to call gld_sched()
* to retry the pending transmits (we're not allowed to hold
* driver-defined mutexes across gld_sched()).
*
* the <factotum> is triggered if the h/w interrupt handler
* sees the <link state changed> or <error> bits in the status
* block. It's also triggered periodically to poll the link
* state, just in case we aren't getting link status change
* interrupts ...
*/
if (err != DDI_SUCCESS) {
"nge_attach: add nge_reschedule softintr failed");
return (DDI_FAILURE);
}
if (err != DDI_SUCCESS) {
"nge_attach: add nge_chip_factotum softintr failed!");
return (DDI_FAILURE);
}
!= DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* Get supported interrupt types */
!= DDI_SUCCESS) {
return (DDI_FAILURE);
}
NGE_DEBUG(("ddi_intr_get_supported_types() returned: %x",
intr_types));
/* MSI Configurations for mcp55 chipset */
/* Enable the 8 vectors */
/*
* Remapping the MSI MAP0 and MAP1. MCP55
* is default mapping all the interrupt to 0 vector.
* Software needs to remapping this.
* This mapping is same as CK804.
*/
}
NGE_DEBUG(("MSI registration failed, "
"trying FIXED interrupt type\n"));
} else {
}
}
(intr_types & DDI_INTR_TYPE_FIXED)) {
"registration failed\n");
return (DDI_FAILURE);
}
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* nge_add_intrs:
*
* Register FIXED or MSI interrupts.
*/
static int
{
/* Get number of interrupts */
return (DDI_FAILURE);
}
/* Get number of available interrupts */
return (DDI_FAILURE);
}
NGE_DEBUG(("nitrs() returned %d, navail returned %d\n",
}
/* Allocate an array of interrupt handles */
/* Call ddi_intr_alloc() */
return (DDI_FAILURE);
}
NGE_DEBUG(("Requested: %d, Received: %d\n",
}
/*
* Get priority for first msi, assume remaining are all the same
*/
DDI_SUCCESS) {
/* Free already allocated intr */
for (i = 0; i < actual; i++) {
}
return (DDI_FAILURE);
}
/* Test for high level mutex */
"Hi level interrupt not supported");
for (i = 0; i < actual; i++)
return (DDI_FAILURE);
}
/* Call ddi_intr_add_handler() */
for (i = 0; i < actual; i++) {
"failed %d\n", ret);
/* Free already allocated intr */
for (i = 0; i < actual; i++) {
}
return (DDI_FAILURE);
}
}
!= DDI_SUCCESS) {
for (i = 0; i < actual; i++) {
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* nge_rem_intrs:
*
* Unregister FIXED or MSI interrupts
*/
static void
{
int i;
NGE_DEBUG(("nge_rem_intrs\n"));
/* Disable all interrupts */
/* Call ddi_intr_block_disable() */
} else {
for (i = 0; i < ngep->intr_actual_cnt; i++) {
}
}
/* Call ddi_intr_remove_handler() */
for (i = 0; i < ngep->intr_actual_cnt; i++) {
}
}