rge_main.c revision 27dcfa4c56aa595f49377fbd0272cd1752b94ad8
/*
* 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.
*/
#include "rge.h"
/*
* This is the string displayed by modinfo, etc.
* Make sure you keep the version ID up to date!
*/
static char rge_ident[] = "Realtek 1Gb Ethernet v1.11";
/*
* Used for buffers allocated by ddi_dma_mem_alloc()
*/
static ddi_dma_attr_t dma_attr_buf = {
DMA_ATTR_V0, /* dma_attr version */
(uint32_t)0, /* dma_attr_addr_lo */
0xFFFFFFFF, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
1, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0, /* dma_attr_flags */
};
/*
* Used for BDs allocated by ddi_dma_mem_alloc()
*/
static ddi_dma_attr_t dma_attr_desc = {
DMA_ATTR_V0, /* dma_attr version */
(uint32_t)0, /* dma_attr_addr_lo */
0xFFFFFFFF, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
1, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0, /* dma_attr_flags */
};
/*
* PIO access attributes for registers
*/
static ddi_device_acc_attr_t rge_reg_accattr = {
};
/*
* DMA access attributes for descriptors
*/
static ddi_device_acc_attr_t rge_desc_accattr = {
};
/*
* DMA access attributes for data
*/
static ddi_device_acc_attr_t rge_buf_accattr = {
};
/*
* Property names
*/
static char debug_propname[] = "rge_debug_flags";
static char mtu_propname[] = "default_mtu";
static char msi_propname[] = "msi_enable";
static int rge_m_start(void *);
static void rge_m_stop(void *);
static int rge_m_promisc(void *, boolean_t);
static int rge_m_unicst(void *, const uint8_t *);
static void rge_m_resources(void *);
static mac_callbacks_t rge_m_callbacks = {
};
/*
* Allocate an area of memory and a DMA handle for accessing it
*/
static int
{
int err;
/*
* Allocate handle
*/
if (err != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/*
* Allocate memory
*/
if (err != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/*
* Bind the two together
*/
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* Free one allocated area of DMAable memory
*/
static void
{
}
}
}
}
/*
* 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.
*/
static void
{
}
static int
{
int err;
/*
* Allocate memory & handle for packet statistics
*/
&rgep->dma_area_stats);
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
/*
* Allocate memory & handle for Tx descriptor ring
*/
&rgep->dma_area_txdesc);
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
/*
* Allocate memory & handle for Rx descriptor ring
*/
&rgep->dma_area_rxdesc);
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* rge_free_bufs() -- free descriptors/buffers allocated for this
* device instance.
*/
static void
{
}
/*
* ========== Transmit and receive ring reinitialisation ==========
*/
/*
* state, assuming that the corresponding <init> routine has already
* been called exactly once.
*/
static void
{
/*
* re-init send ring
*/
bdp->host_buf_addr =
/* last BD in Tx ring */
ssbdp++;
bdp++;
}
}
static void
{
/*
* re-init receive ring
*/
bdp->host_buf_addr =
/* last BD in Tx ring */
srbdp++;
bdp++;
}
}
static void
{
return;
/*
* If all the up-sending buffers haven't been returned to driver,
* use bcopy() only in rx process.
*/
}
static void
{
}
static void
{
ssbdp++;
}
}
static void
{
}
}
}
}
static void
{
return;
}
}
}
}
static void
{
}
static int
{
int err;
/*
* Allocate the array of s/w Tx Buffer Descriptors
*/
/*
* Init send ring
*/
/*
* Allocate memory & handle for Tx buffers
*/
if (err != DDI_SUCCESS) {
"rge_init_send_ring: alloc tx buffer failed");
return (DDI_FAILURE);
}
ssbdp++;
}
return (DDI_SUCCESS);
}
static int
{
int err;
/*
* Allocate the array of s/w Rx Buffer Descriptors
*/
/*
* Init receive ring
*/
/*
* Allocate memory & handle for Rx buffers
*/
if (err != DDI_SUCCESS) {
"rge_init_recv_ring: alloc rx buffer failed");
return (DDI_FAILURE);
}
"rge_init_recv_ring: desballoc() failed");
return (DDI_FAILURE);
}
}
srbdp++;
}
return (DDI_SUCCESS);
}
static int
{
int err;
return (DDI_SUCCESS);
}
/*
* Allocate the array of s/w free Buffer Descriptors
*/
/*
* Init free buffer ring
*/
/*
* Allocate memory & handle for free Rx buffers
*/
if (err != DDI_SUCCESS) {
"rge_init_buf_ring: alloc rx free buffer failed");
return (DDI_FAILURE);
}
"rge_init_buf_ring: desballoc() failed");
return (DDI_FAILURE);
}
free_srbdp++;
}
return (DDI_SUCCESS);
}
static int
{
int err;
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
if (err != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if (err != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* ========== Internal state management entry points ==========
*/
/*
* These routines provide all the functionality required by the
* corresponding MAC layer entry points, but don't update the
* MAC state so they can be called internally without disturbing
* our record of what NEMO thinks we should be doing ...
*/
/*
* rge_reset() -- reset h/w & rings to initial state
*/
static void
{
/*
* Grab all the other mutexes in the world (this should
* ensure no other threads are manipulating driver state)
*/
(void) rge_chip_reset(rgep);
/*
* Free the world ...
*/
}
/*
* rge_stop() -- stop processing, don't reset h/w or rings
*/
static void
{
}
/*
* rge_start() -- start transmitting/receiving
*/
static void
{
/*
* Start chip processing, including enabling interrupts
*/
}
/*
* rge_restart - restart transmitting/receiving after error or suspend
*/
void
{
uint32_t i;
/*
* Wait for posted buffer to be freed...
*/
for (i = 0; i < RXBUFF_FREE_LOOP; i++) {
break;
drv_usecwait(1000);
RGE_DEBUG(("rge_restart: waiting for rx buf free..."));
}
}
}
}
/*
* ========== Nemo-required management entry points ==========
*/
/*
* rge_m_stop() -- stop transmitting/receiving
*/
static void
rge_m_stop(void *arg)
{
uint32_t i;
/*
* Just stop processing, then record new MAC state
*/
return;
}
/*
* Wait for posted buffer to be freed...
*/
for (i = 0; i < RXBUFF_FREE_LOOP; i++) {
break;
drv_usecwait(1000);
RGE_DEBUG(("rge_m_stop: waiting for rx buf free..."));
}
}
}
/*
* rge_m_start() -- start transmitting/receiving
*/
static int
rge_m_start(void *arg)
{
return (DDI_FAILURE);
}
/*
*/
/*
* Start processing and record new MAC state
*/
return (0);
}
/*
* rge_m_unicst_set() -- set the physical network address
*/
static int
{
/*
* Remember the new current address in the driver state
* Sync the chip's idea of the address too ...
*/
return (DDI_SUCCESS);
}
return (0);
}
/*
* Compute the index of the required bit in the multicast hash map.
* This must mirror the way the hardware actually does it!
*/
static uint32_t
{
int bytes;
int bit;
crc <<= 1;
currentbyte >>= 1;
}
}
/* the index value is between 0 and 63(0x3f) */
return (index);
}
/*
*/
static int
{
struct ether_addr *addr;
/*
* Calculate the Multicast address hash index value
* Normally, the position of MAR0-MAR7 is
* MAR0: offset 0x08, ..., MAR7: offset 0x0F.
*
* For pcie chipset, the position of MAR0-MAR7 is
* different from others:
* MAR0: offset 0x0F, ..., MAR7: offset 0x08.
*/
else
if (add) {
return (0);
}
} else {
return (0);
}
}
return (DDI_SUCCESS);
}
/*
* Set multicast register
*/
return (0);
}
/*
* rge_m_promisc() -- set or reset promiscuous mode on the board
*
* receive-all-multicast modes.
*/
static int
{
/*
* Store MAC layer specified mode and pass to chip layer to update h/w
*/
return (0);
}
return (DDI_SUCCESS);
}
return (0);
}
/*
* Loopback ioctl code
*/
static lb_property_t loopmodes[] = {
};
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 RGE_LOOP_NONE:
case RGE_LOOP_INTERNAL_PHY:
case RGE_LOOP_INTERNAL_MAC:
break;
}
/*
* All OK; tell the caller to reprogram
*/
return (IOC_RESTART_ACK);
}
static enum ioc_reply
{
int cmd;
/*
* Validate format of ioctl
*/
return (IOC_INVAL);
switch (cmd) {
default:
/* NOTREACHED */
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);
}
}
/*
* Specific rge IOCTLs, the MAC layer handles the generic ones.
*/
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:
return;
case RGE_MII_READ:
case RGE_MII_WRITE:
case RGE_DIAG:
case RGE_PEEK:
case RGE_POKE:
case RGE_PHY_RESET:
case RGE_SOFT_RESET:
case RGE_HARD_RESET:
break;
case LB_GET_INFO_SIZE:
case LB_GET_INFO:
case LB_GET_MODE:
/* FALLTHRU */
case LB_SET_MODE:
break;
case ND_GET:
/* FALLTHRU */
case ND_SET:
break;
}
if (need_privilege) {
/*
* Check for specific net_config privilege
*/
if (err != 0) {
return;
}
}
switch (cmd) {
default:
break;
case RGE_MII_READ:
case RGE_MII_WRITE:
case RGE_DIAG:
case RGE_PEEK:
case RGE_POKE:
case RGE_PHY_RESET:
case RGE_SOFT_RESET:
case RGE_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;
}
/*
* Finally, decide how to reply
*/
switch (status) {
default:
case IOC_INVAL:
/*
* Error, reply with a NAK and EINVAL or the specified error
*/
break;
case IOC_DONE:
/*
* OK, reply already sent
*/
break;
case IOC_RESTART_ACK:
case IOC_ACK:
/*
* OK, reply with an ACK
*/
break;
case IOC_RESTART_REPLY:
case IOC_REPLY:
/*
* OK, send prepared reply as ACK or NAK
*/
break;
}
}
static void
rge_m_resources(void *arg)
{
/*
* Register Rx rings as resources and save mac
* resource id for future reference
*/
}
/* ARGSUSED */
static boolean_t
{
switch (cap) {
case MAC_CAPAB_HCKSUM: {
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);
}
/*
*/
/*
* rge_add_intrs:
*
* Register FIXED or MSI interrupts.
*/
static int
{
int avail;
int actual;
int intr_size;
int count;
int i, j;
int ret;
/* Get number of interrupts */
return (DDI_FAILURE);
}
/* Get number of available interrupts */
return (DDI_FAILURE);
}
/* Allocate an array of interrupt handles */
/* Call ddi_intr_alloc() */
return (DDI_FAILURE);
}
}
/*
* 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);
/* Remove already added intr */
for (j = 0; j < i; j++)
/* 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);
}
/*
* rge_rem_intrs:
*
* Unregister FIXED or MSI interrupts
*/
static void
{
int i;
/* Disable all interrupts */
/* Call ddi_intr_block_disable() */
} else {
}
}
/* Call ddi_intr_remove_handler() */
}
}
/*
*/
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 RGE data structures
*/
}
(void) rge_phy_reset(rgep);
(void) rge_chip_reset(rgep);
}
}
}
static int
{
/*
* 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.
*/
"rge: ngep returned from ddi_get_driver_private was NULL");
/*
* Refuse to resume if the data structures aren't consistent
*/
"rge: passed devinfo not the same as saved devinfo");
/*
* Read chip ID & set up config space command register(s)
* Refuse to resume if the chip has changed its identity!
*/
return (DDI_FAILURE);
return (DDI_FAILURE);
return (DDI_FAILURE);
/*
* Only in one case, this conditional branch can be executed: the port
* hasn't been plumbed.
*/
return (DDI_SUCCESS);
}
/*
* All OK, reinitialise h/w & kick off NEMO scheduling
*/
return (DDI_SUCCESS);
}
/*
* attach(9E) -- Attach a device to the system
*
* Called once for each board successfully probed.
*/
static int
{
int intr_types;
int instance;
int i;
int err;
/*
* we don't support high level interrupts in the driver
*/
if (ddi_intr_hilevel(devinfo, 0) != 0) {
"rge_attach -- unsupported high level interrupt");
return (DDI_FAILURE);
}
RGE_GTRACE(("rge_attach($%p, %d) instance %d",
switch (cmd) {
default:
return (DDI_FAILURE);
case DDI_RESUME:
return (rge_resume(devinfo));
case DDI_ATTACH:
break;
}
/*
* Initialize more fields in RGE private data
*/
/*
* Map config space registers
* Read chip ID & set up config space command register(s)
*
* Note: this leaves the chip accessible by Memory Space
* accesses, but with interrupts and Bus Mastering off.
* This should ensure that nothing untoward will happen
* if it has been left active by the (net-)bootloader.
* We'll re-enable Bus Mastering once we've reset the chip,
* and allow interrupts only when everything else is set up.
*/
if (err != DDI_SUCCESS) {
goto attach_fail;
}
/*
* Map operating registers
*/
if (err != DDI_SUCCESS) {
goto attach_fail;
}
/*
* Characterise the device, so we know its requirements.
* Then allocate the appropriate TX and RX descriptors & buffers.
*/
if (err != DDI_SUCCESS) {
goto attach_fail;
}
/*
* Register NDD-tweakable parameters
*/
if (rge_nd_init(rgep)) {
goto attach_fail;
}
/*
* 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.
*
* 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) {
goto attach_fail;
}
if (err != DDI_SUCCESS) {
goto attach_fail;
}
/*
* Get supported interrupt types
*/
!= DDI_SUCCESS) {
goto attach_fail;
}
/*
* Add the h/w interrupt handler and initialise mutexes
* RTL8101E is observed to have MSI invalidation issue after S/R.
* So the FIXED interrupt is used instead.
*/
"trying FIXED interrupt type\n");
} else {
}
}
(intr_types & DDI_INTR_TYPE_FIXED)) {
"registration failed\n");
goto attach_fail;
}
}
goto attach_fail;
}
/*
* Initialize rings
*/
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 */
}
}
/*
* Initialise link state variables
* Stop, reset & reinitialise the chip.
* Initialise the (internal) PHY.
*/
/*
* Reset chip & rings to initial state; also reset address
* filtering, promiscuity, loopback mode.
*/
(void) rge_chip_reset(rgep);
/*
* Create & initialise named kstats
*/
goto attach_fail;
/*
* Finally, we're ready to register ourselves with the MAC layer
* interface; if this succeeds, we're all ready to start()
*/
if (err != 0)
goto attach_fail;
/*
* Register a periodical handler.
* reg_chip_cyclic() is invoked in kernel context.
*/
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*
*/
static int
{
/*
* Stop processing and idle (powerdown) the PHY ...
*/
return (DDI_SUCCESS);
}
return (DDI_SUCCESS);
}
/*
* detach(9E) -- Detach a device from the system
*/
static int
{
switch (cmd) {
default:
return (DDI_FAILURE);
case DDI_SUSPEND:
return (rge_suspend(rgep));
case DDI_DETACH:
break;
}
/*
* If there is any posted buffer, the driver should reject to be
* detached. Need notice upper layer to release them.
*/
return (DDI_FAILURE);
/*
* Unregister from the MAC layer 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 rge_modldrv = {
&mod_driverops, /* Type of module. This one is a driver */
rge_ident, /* short description */
&rge_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);
}