vr.c revision 2ca5b6595b95478e6568b0e77c6c83c8a870867a
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/ethernet.h>
#include <sys/mac_provider.h>
#include <sys/mac_ether.h>
#include <sys/sysmacros.h>
#include <sys/dditypes.h>
#include <sys/byteorder.h>
#include "vr.h"
#include "vr_impl.h"
/*
* VR in a nutshell
* The card uses two rings of data structures to communicate with the host.
* These are referred to as "descriptor rings" and there is one for transmit
* (TX) and one for receive (RX).
*
* The driver uses a "DMA buffer" data type for mapping to those descriptor
* rings. This is a structure with handles and a DMA'able buffer attached to it.
*
* Receive
* The receive ring is filled with DMA buffers. Received packets are copied into
* a newly allocated mblk's and passed upstream.
*
* Transmit
* Each transmit descriptor has a DMA buffer attached to it. The data of TX
* packets is copied into the DMA buffer which is then enqueued for
* transmission.
*
* Reclaim of transmitted packets is done as a result of a transmit completion
* interrupt which is generated 3 times per ring at minimum.
*/
#if defined(DEBUG)
if (vrdebug > 0) \
} while (0)
#else
#endif
static char vr_ident[] = "VIA Rhine Ethernet v1.42";
/*
* Attributes for accessing registers and memory descriptors for this device.
*/
static ddi_device_acc_attr_t vr_dev_dma_accattr = {
};
/*
* Attributes for accessing data.
*/
static ddi_device_acc_attr_t vr_data_dma_accattr = {
};
/*
* DMA attributes for descriptors for communication with the device
* This driver assumes that all descriptors of one ring fit in one consequitive
* memory area of max 4K (256 descriptors) that does not cross a page boundary.
* Therefore, we request 4K alignement.
*/
static ddi_dma_attr_t vr_dev_dma_attr = {
DMA_ATTR_V0, /* version number */
0, /* low DMA address range */
0xFFFFFFFF, /* high DMA address range */
0x7FFFFFFF, /* DMA counter register */
0x1000, /* DMA address alignment */
0x7F, /* DMA burstsizes */
1, /* min effective DMA size */
0xFFFFFFFF, /* max DMA xfer size */
0xFFFFFFFF, /* segment boundary */
1, /* s/g list length */
1, /* granularity of device */
0 /* DMA transfer flags */
};
/*
* Note that the alignement is set to 2K so hat a 1500 byte packet never
* crosses a page boundary and thus that a DMA transfer is not split up in
* multiple cookies with a 4K/8K pagesize
*/
static ddi_dma_attr_t vr_data_dma_attr = {
DMA_ATTR_V0, /* version number */
0, /* low DMA address range */
0xFFFFFFFF, /* high DMA address range */
0x7FFFFFFF, /* DMA counter register */
0x800, /* DMA address alignment */
0xfff, /* DMA burstsizes */
1, /* min effective DMA size */
0xFFFFFFFF, /* max DMA xfer size */
0xFFFFFFFF, /* segment boundary */
1, /* s/g list length */
1, /* granularity of device */
0 /* DMA transfer flags */
};
static mac_callbacks_t vr_mac_callbacks = {
vr_mac_getstat, /* Get the value of a statistic */
vr_mac_start, /* Start the device */
vr_mac_stop, /* Stop the device */
vr_mac_set_promisc, /* Enable or disable promiscuous mode */
vr_mac_set_multicast, /* Enable or disable a multicast addr */
vr_mac_set_ether_addr, /* Set the unicast MAC address */
vr_mac_tx_enqueue_list, /* Transmit a packet */
NULL, /* Process an unknown ioctl */
NULL, /* Get capability information */
NULL, /* Open the device */
NULL, /* Close the device */
vr_mac_setprop, /* Set properties of the device */
vr_mac_getprop /* Get properties of the device */
};
/*
* Table with bugs and features for each incarnation of the card.
*/
static const chip_info_t vr_chip_info [] = {
{
0x0, 0x0,
"VIA Rhine Fast Ethernet",
},
{
0x04, 0x21,
"VIA VT86C100A Fast Ethernet",
},
{
0x40, 0x41,
"VIA VT6102-A Rhine II Fast Ethernet",
},
{
0x42, 0x7f,
"VIA VT6102-C Rhine II Fast Ethernet",
},
{
0x80, 0x82,
"VIA VT6105-A Rhine III Fast Ethernet",
(VR_BUG_NONE),
},
{
0x83, 0x89,
"VIA VT6105-B Rhine III Fast Ethernet",
(VR_BUG_NONE),
},
{
0x8a, 0x8b,
"VIA VT6105-LOM Rhine III Fast Ethernet",
(VR_BUG_NONE),
},
{
0x8c, 0x8c,
"VIA VT6107-A0 Rhine III Fast Ethernet",
(VR_BUG_NONE),
},
{
0x8d, 0x8f,
"VIA VT6107-A1 Rhine III Fast Ethernet",
(VR_BUG_NONE),
},
{
0x90, 0x93,
"VIA VT6105M-A0 Rhine III Fast Ethernet Management Adapter",
(VR_BUG_NONE),
},
{
0x94, 0xff,
"VIA VT6105M-B1 Rhine III Fast Ethernet Management Adapter",
(VR_BUG_NONE),
}
};
/*
* Function prototypes
*/
static void vr_periodic(void *p);
static int
{
if (cmd == DDI_RESUME)
else if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
/*
* Attach.
*/
/*
* Store the name+instance of the module.
*/
/*
* Bus initialization.
*/
goto fail0;
}
/*
* Initialize default parameters.
*/
/*
* Setup the descriptor rings.
*/
goto fail1;
}
/*
* Initialize kstats.
*/
/*
* Add interrupt to the OS.
*/
goto fail3;
}
/*
* Add mutexes.
*/
/*
* Enable interrupt.
*/
goto fail5;
}
/*
* Register with parent, mac.
*/
goto fail6;
}
goto fail7;
}
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
static int
{
if (cmd == DDI_SUSPEND)
return (vr_suspend(devinfo));
else if (cmd != DDI_DETACH)
return (DDI_FAILURE);
return (DDI_FAILURE);
/*
* Try to un-register from the MAC layer.
*/
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* quiesce the card for fast reboot.
*/
int
{
/*
* Stop interrupts.
*/
/*
* Stop DMA.
*/
return (DDI_SUCCESS);
}
/*
* Add an interrupt for our device to the OS.
*/
static vr_result_t
{
int nintrs;
int rc;
DDI_INTR_TYPE_FIXED, /* type */
0, /* number */
1, /* count */
&nintrs, /* actualp */
if (rc != DDI_SUCCESS) {
return (VR_FAILURE);
}
if (rc != DDI_SUCCESS) {
return (VR_FAILURE);
}
if (rc != DDI_SUCCESS) {
return (VR_FAILURE);
}
return (VR_SUCCESS);
}
/*
* Remove our interrupt from the OS.
*/
static void
{
}
/*
* Resume operation after suspend.
*/
static int
{
return (DDI_SUCCESS);
}
/*
* Suspend operation.
*/
static int
{
}
return (DDI_SUCCESS);
}
/*
* Initial bus- and device configuration during attach(9E).
*/
static vr_result_t
{
/*
* Get the reg property which describes the various access methods.
*/
return (VR_FAILURE);
}
/*
* Setup access to all available sets.
*/
for (n = 0; n < nsets; n++) {
if (rc != DDI_SUCCESS) {
"Setup of register set %d failed", n);
while (--n >= 0)
return (VR_FAILURE);
}
}
/*
* Assign type-named pointers to the register sets.
*/
for (n = 0; n < nsets; n++) {
}
/*
* Assure there is one of each type.
*/
for (n = 0; n < nsets; n++)
"Config-, I/O- and memory sets not available");
return (VR_FAILURE);
}
/*
*/
/*
* Copy the matching chip_info_t structure.
*/
for (n = 0; n < elem; n++) {
bcopy((void*)&vr_chip_info[n],
sizeof (chip_info_t));
break;
}
}
/*
* If we didn't find a chip_info_t for this card, copy the first
* entry of the info structures. This is a generic Rhine whith no
* bugs and no features.
*/
bcopy((void*)&vr_chip_info[0],
sizeof (chip_info_t));
}
/*
* Tell what is found.
*/
/*
* Assure that the device is prepared for memory space accesses
* This should be the default as the device advertises memory
* access in it's BAR's. However, my VT6102 on a EPIA CL board doesn't
* and thus we explicetely enable it.
*/
/*
* Setup a handle for regular usage, prefer memory space accesses.
*/
else
/*
* Store the vendor's MAC address.
*/
for (n = 0; n < ETHERADDRL; n++) {
VR_ETHERADDR + n);
}
return (VR_SUCCESS);
}
static void
{
uint_t n;
/*
* Free the register access handles.
*/
}
/*
* Initialize parameter structures.
*/
static void
{
/*
* Initialize default link configuration parameters.
*/
/* Not a PHY ability, but advertised on behalf of MAC */
/*
* Store the PHY identity.
*/
/*
* Clear incapabilities imposed by PHY in phymask.
*/
/*
* Clear incapabilities imposed by MAC in macmask
* Note that flowcontrol (FCS?) is never masked. All of our adapters
* have the ability to honor incoming pause frames. Only the newer can
* transmit pause frames. Since there's no asym flowcontrol in 100Mbit
* Ethernet, we always advertise (symmetric) pause.
*/
/*
* Advertised capabilities is enabled minus incapable.
*/
/*
* Ensure that autoneg of the PHY matches our default.
*/
else
}
/*
* Setup the descriptor rings.
*/
static vr_result_t
{
/*
* Create a ring for receive.
*/
return (VR_FAILURE);
/*
* Create a ring for transmit.
*/
return (VR_FAILURE);
}
return (VR_SUCCESS);
}
static void
{
}
/*
* Allocate a descriptor ring
* The number of descriptor entries must fit in a single page so that the
* whole ring fits in one consequtive space.
* i386: 4K page / 16 byte descriptor = 256 entries
* sparc: 8K page / 16 byte descriptor = 512 entries
*/
static vr_result_t
{
int i, rc;
/*
* Allocate a DMA handle for the chip descriptors.
*/
NULL,
if (rc != DDI_SUCCESS) {
"ddi_dma_alloc_handle in vr_alloc_ring failed.");
return (VR_FAILURE);
}
/*
* Allocate memory for the chip descriptors.
*/
n * sizeof (vr_chip_desc_t),
NULL,
&rbytes,
if (rc != DDI_SUCCESS) {
"ddi_dma_mem_alloc in vr_alloc_ring failed.");
return (VR_FAILURE);
}
/*
* Map the descriptor memory.
*/
NULL,
NULL,
"ddi_dma_addr_bind_handle in vr_alloc_ring failed: "
return (VR_FAILURE);
}
/*
* Allocate memory for the host descriptor ring.
*/
/*
* Interlink the descriptors and connect host- to chip descriptors.
*/
for (i = 0; i < n; i++) {
/*
* Connect the host descriptor to a chip descriptor.
*/
/*
* Store the DMA address and offset in the descriptor
* Offset is for ddi_dma_sync() and paddr is for ddi_get/-put().
*/
/*
* Link the previous descriptor to this one.
*/
if (i > 0) {
/* Host */
/* Chip */
}
}
/*
* Make rings out of this list by pointing last to first.
*/
i = n - 1;
return (VR_SUCCESS);
}
/*
* Free the memory allocated for a ring.
*/
static void
{
/*
* Unmap and free the chip descriptors.
*/
(void) ddi_dma_unbind_handle(r->handle);
ddi_dma_mem_free(&r->acchdl);
ddi_dma_free_handle(&r->handle);
/*
* Free the memory for storing host descriptors
*/
}
/*
* Initialize the receive ring.
*/
static vr_result_t
{
int i, rc;
/*
* Set the read pointer at the start of the ring.
*/
/*
* Assign a DMA buffer to each receive descriptor.
*/
if (rc != VR_SUCCESS) {
while (--i >= 0)
return (VR_FAILURE);
}
/*
* Store the address of the dma buffer in the chip descriptor
*/
/*
* Put the buffer length in the chip descriptor. Ensure that
* length fits in the 11 bits of stat1 (2047/0x7FF)
*/
/*
* Set descriptor ownership to the card
*/
/*
* Sync the descriptor with main memory
*/
sizeof (vr_chip_desc_t), DDI_DMA_SYNC_FORDEV);
}
return (VR_SUCCESS);
}
/*
* Free the DMA buffers assigned to the receive ring.
*/
static void
{
int i;
}
static vr_result_t
{
int i, rc;
/*
* Set the write- and claim pointer.
*/
/*
* (Re)set the TX bookkeeping.
*/
/*
* Every transmit decreases nfree. Every reclaim increases nfree.
*/
/*
* Attach a DMA buffer to each transmit descriptor.
*/
if (rc != VR_SUCCESS) {
while (--i >= 0)
return (VR_FAILURE);
}
}
/*
* Init & sync the TX descriptors so the device sees a valid ring.
*/
sizeof (vr_chip_desc_t),
}
return (VR_SUCCESS);
}
/*
* Free the DMA buffers attached to the TX ring.
*/
static void
{
int i;
/*
* Free the DMA buffers attached to the TX ring
*/
}
/*
* Allocate a DMA buffer.
*/
static vr_result_t
{
int rc;
/*
* Allocate a DMA handle for the buffer
*/
if (rc != DDI_SUCCESS) {
"ddi_dma_alloc_handle failed in vr_alloc_dmabuf");
return (VR_FAILURE);
}
/*
* Allocate the buffer
* The allocated buffer is aligned on 2K boundary. This ensures that
* a 1500 byte frame never cross a page boundary and thus that the DMA
* mapping can be established in 1 fragment.
*/
if (rc != DDI_SUCCESS) {
"ddi_dma_mem_alloc failed in vr_alloc_dmabuf");
return (VR_FAILURE);
}
/*
* Map the memory
*/
NULL,
NULL,
&cookiecnt);
/*
* The cookiecount should never > 1 because we requested 2K alignment
*/
"dma_addr_bind_handle failed in vr_alloc_dmabuf: "
return (VR_FAILURE);
}
return (VR_SUCCESS);
}
/*
* Destroy a DMA buffer.
*/
static void
{
}
/*
* Interrupt service routine
* When our vector is shared with another device, av_dispatch_autovect calls
* all service routines for the vector until *none* of them return claimed
* That means that, when sharing vectors, this routine is called at least
* twice for each interrupt.
*/
{
tx_resched = 0;
link_change = 0;
/*
* Read the status register to see if the interrupt is from our device
* This read also ensures that posted writes are brought to main memory.
*/
if (status == 0) {
/*
* Status contains no configured interrupts
* The interrupt was not generated by our device.
*/
return (DDI_INTR_UNCLAIMED);
}
/*
* Acknowledge the event(s) that caused interruption.
*/
/*
* Receive completion.
*/
/*
* Received some packets.
*/
/*
* DMA stops after a conflict in the FIFO.
*/
if ((status & VR_ISR_RX_ERR_BITS) != 0)
}
/*
* Transmit completion.
*/
/*
* Card done with transmitting some packets
* TX_DONE is generated 3 times per ring but it appears
* more often because it is also set when an RX_DONE
* interrupt is generated.
*/
}
/*
* Link status change.
*/
if ((status & VR_ICR0_LINKSTATUS) != 0) {
/*
* Get new link state and inform the mac layer.
*/
status &= ~VR_ICR0_LINKSTATUS;
link_change = 1;
}
/*
* Bus error.
*/
if ((status & VR_ISR0_BUSERR) != 0) {
status &= ~VR_ISR0_BUSERR;
}
/*
* We must have handled all things here.
*/
/*
* Reset the device if requested
* The request can come from the periodic tx check or from the interrupt
* status.
*/
}
/*
* Pass up the list with received packets.
*/
/*
* Inform the upper layer on the linkstatus if there was a change.
*/
if (link_change != 0)
/*
* Restart transmissions if we were waiting for tx descriptors.
*/
if (tx_resched == 1)
/*
* Read something from the card to ensure that all of our configuration
* writes are delivered to the device before the interrupt is ended.
*/
return (DDI_INTR_CLAIMED);
}
/*
* Respond to an unforseen situation by resetting the card and our bookkeeping.
*/
static void
{
}
/*
* Collect received packets in a list.
*/
static mblk_t *
{
uint32_t n;
n = 0;
/*
* Sync the descriptor before looking at it.
*/
sizeof (vr_chip_desc_t), DDI_DMA_SYNC_FORKERNEL);
/*
* Get the status from the descriptor.
*/
/*
* We're done if the descriptor is owned by the card.
*/
if ((rxstat0 & VR_RDES0_OWN) != 0)
break;
else if ((rxstat0 & VR_RDES0_RXOK) != 0) {
/*
* Received a good packet
*/
/*
* Sync the data.
*/
/*
* Send a new copied message upstream.
*/
if ((rxstat0 & VR_RDES0_BAR) != 0)
else if ((rxstat0 & VR_RDES0_MAR) != 0)
/*
* Link this packet in the list.
*/
else {
}
} else {
}
} else {
/*
* Received with errors.
*/
if ((rxstat0 & VR_RDES0_FAE) != 0)
if ((rxstat0 & VR_RDES0_CRCERR) != 0)
if ((rxstat0 & VR_RDES0_LONG) != 0)
if ((rxstat0 & VR_RDES0_RUNT) != 0)
if ((rxstat0 & VR_RDES0_FOV) != 0)
}
/*
* Reset descriptor ownership to the MAC.
*/
sizeof (vr_chip_desc_t),
}
/*
* If we do flowcontrol and if the card can transmit pause frames,
* increment the "available receive descriptors" register.
*/
/*
* Whenever the card moves a fragment to host memory it
* decrements the RXBUFCOUNT register. If the value in the
* register reaches a low watermark, the card transmits a pause
* frame. If the value in this register reaches a high
* watermark, the card sends a "cancel pause" frame
*
* Non-zero values written to this byte register are added
* by the chip to the register's contents, so we must write
* the number of descriptors free'd.
*/
}
return (lp);
}
/*
* Enqueue a list of packets for transmission
* Return the packets not transmitted.
*/
mblk_t *
{
do {
break;
}
/*
* Tell the chip to poll the TX ring.
*/
return (mp);
}
/*
* Enqueue a message for transmission.
*/
static void
{
int padlen;
/*
* Copy the message into the pre-mapped buffer and free mp
*/
/*
* Clean padlen bytes of short packet.
*/
if (padlen > 0) {
}
/*
* Most of the statistics are updated on reclaim, after the actual
* transmit. obytes is maintained here because the length is cleared
* after transmission
*/
/*
* Sync the data so the device sees the new content too.
*/
/*
* If we have reached the TX interrupt distance, enable a TX interrupt
* for this packet. The Interrupt Control (IC) bit in the transmit
* descriptor doesn't have any effect on the interrupt generation
* despite the vague statements in the datasheet. Thus, we use the
* more obscure interrupt suppress bit which is probably part of the
* MAC's bookkeeping for TX interrupts and fragmented packets.
*/
/*
* Don't suppress the interrupt for this packet.
*/
nextp &= (~VR_TDES3_SUPPRESS_INTR);
} else {
/*
* Suppress the interrupt for this packet.
*/
}
/*
* Write and sync the chip's descriptor
*/
sizeof (vr_chip_desc_t), DDI_DMA_SYNC_FORDEV);
/*
* The ticks counter is cleared by reclaim when it reclaimed some
* descriptors and incremented by the periodic TX stall check.
*/
}
/*
* Free transmitted descriptors.
*/
static void
{
freed = 0;
/*
* Sync & get descriptor status.
*/
sizeof (vr_chip_desc_t),
if ((stat0 & VR_TDES0_OWN) != 0)
break;
/*
* Do stats for the first descriptor in a chain.
*/
if ((stat1 & VR_TDES1_STP) != 0) {
if ((stat0 & VR_TDES0_TERR) != 0) {
if ((stat0 & VR_TDES0_UDF) != 0)
if ((stat0 & VR_TDES0_ABT) != 0)
/*
* Abort and FIFO underflow stop the MAC.
* Packet queueing must be disabled with HD
* links because otherwise the MAC is also lost
* after a few of these events.
*/
} else
if ((stat0 & VR_TDES0_COL) != 0) {
} else {
}
(stat0 & VR_TDES0_NCR);
}
if ((stat0 & VR_TDES0_CRS) != 0)
if ((stat0 & VR_TDES0_OWC) != 0)
}
freed += 1;
dirty -= 1;
}
if (freed > 0) {
} else
}
/*
* Check TX health every 2 seconds.
*/
static void
vr_periodic(void *p)
{
/*
* No succesful reclaim in the last n
* intervals. Reset the MAC.
*/
"TX stalled, resetting MAC");
} else {
/*
* Increase until we find that we've
* waited long enough.
*/
}
}
}
}
}
/*
* Bring the device to our desired initial state.
*/
static void
{
/*
* Reset the MAC
* If we don't wait long enough for the forced reset to complete,
* MAC looses sync with PHY. Result link up, no link change interrupt
* and no data transfer.
*/
time = 0;
do {
drv_usecwait(100);
time += 100;
if (time >= 100000) {
}
/*
* Load the PROM contents into the MAC again.
*/
/*
* Tell the MAC via IO space that we like to use memory space for
* accessing registers.
*/
}
/*
* Prepare and enable the card (MAC + PHY + PCI).
*/
static int
{
/*
* Allocate DMA buffers for RX.
*/
return (ENOMEM);
}
/*
* Allocate DMA buffers for TX.
*/
return (ENOMEM);
}
/*
* Changes of the chip specific registers as done in VIA's fet driver
* These bits are not in the datasheet and controlled by vr_chip_info.
*/
/*
* RX: Accept broadcast packets.
*/
/*
* RX: Start DMA when there are 256 bytes in the FIFO.
*/
/*
* TX: Start transmit when there are 256 bytes in the FIFO.
*/
/*
* Burst transfers up to 256 bytes.
*/
/*
* Disable TX autopolling as it is bad for RX performance
* I assume this is because the RX process finds the bus often occupied
* by the polling process.
*/
/*
* Honor the PCI latency timer if it is reasonable.
*/
else
/*
* Ensure that VLAN filtering is off, because this strips the tag.
*/
}
/*
* Clear the CAM filter.
*/
drv_usecwait(2);
}
/*
* Give the start addresses of the descriptor rings to the DMA
* controller on the MAC.
*/
/*
* We don't use the additionally invented interrupt ICR1 register,
* so make sure these are disabled.
*/
/*
* Enable interrupts.
*/
/*
* Enable the DMA controller.
*/
/*
* Configure the link. Rely on the link change interrupt for getting
* the link state into the driver.
*/
/*
* Set the software view on the state to 'running'.
*/
return (0);
}
/*
* Stop DMA and interrupts.
*/
static int
{
/*
* Stop interrupts.
*/
/*
* Stop DMA.
*/
/*
* Set the software view on the state to stopped.
*/
/*
* Remove DMA buffers from the rings.
*/
return (0);
}
int
vr_mac_start(void *p)
{
int rc;
/*
* Reset the card.
*/
/*
* Prepare and enable the card.
*/
/*
* Configure a cyclic function to keep the card & driver from diverting.
*/
vrp->periodic_id =
return (rc);
}
void
vr_mac_stop(void *p)
{
/*
* Stop the device.
*/
/*
* Remove the cyclic from the system.
*/
}
/*
*
* From the 21143 manual:
* The 21143 can store 512 bits serving as hash bucket heads, and one physical
* 48-bit Ethernet address. Incoming frames with multicast destination
* addresses are subjected to imperfect filtering. Frames with physical
* destination addresses are checked against the single physical address.
* For any incoming frame with a multicast destination address, the 21143
* applies the standard Ethernet cyclic redundancy check (CRC) function to the
* first 6 bytes containing the destination address, then it uses the most
* significant 9 bits of the result as a bit index into the table. If the
* indexed bit is set, the frame is accepted. If the bit is cleared, the frame
* is rejected. This filtering mode is called imperfect because multicast
* frames not addressed to this station may slip through, but it still
* decreases the number of frames that the host can receive.
* I assume the above is also the way the VIA chips work. There's not a single
* word about the multicast filter in the datasheet.
*
* Another word on the CAM filter on VT6105M controllers:
* The VT6105M has content addressable memory which can be used for perfect
* filtering of 32 multicast addresses and a few VLAN id's
*
* I think it works like this: When the controller receives a multicast
* address, it looks up the address using CAM. When it is found, it takes the
* matching cell address (index) and compares this to the bit position in the
* cam mask. If the bit is set, the packet is passed up. If CAM lookup does not
* result in a match, the packet is filtered using the hash based filter,
* if that matches, the packet is passed up and dropped otherwise
* Also, there's not a single word in the datasheet on how this cam is supposed
* to work ...
*/
int
{
uint32_t a;
/*
* Program the perfect filter.
*/
/*
* Get index of first empty slot.
*/
if (cam_index != -1) {
/*
* Add address at cam_index.
*/
for (a = 0; a < ETHERADDRL; a++) {
}
drv_usecwait(2);
} else {
/*
* No free CAM slots available
* Add mca to the imperfect filter.
*/
}
} else {
/*
* Find the index of the entry to remove
* If the entry was not found (-1), the addition was
* probably done when the table was full.
*/
if (cam_index != -1) {
/*
* Disable the corresponding mask bit.
*/
} else {
/*
* The entry to be removed was not found
* The likely cause is that the CAM was full
* during addition. The entry is added to the
* hash filter in that case and needs to be
* removed there too.
*/
}
}
} else {
/*
* No CAM in the MAC, thus we need the hash filter.
*/
}
if (use_hash_filter == B_TRUE) {
/*
* Get the CRC-32 of the multicast address
* The card uses the "MSB first" direction when calculating the
* the CRC. This is odd because ethernet is "LSB first"
* We have to use that "big endian" approach as well.
*/
/*
* Turn bit[crc_index] on.
*/
if (crc_index < 32)
else
} else {
/*
* Turn bit[crc_index] off.
*/
if (crc_index < 32)
else
}
/*
* When not promiscuous write the filter now. When promiscuous,
* the filter is open and will be written when promiscuous ends.
*/
}
}
/*
*/
else
return (0);
}
/*
* Calculate the CRC32 for 6 bytes of multicast address in MSB(it) first order.
* The MSB first order is a bit odd because Ethernet standard is LSB first
*/
static uint32_t
{
uint8_t c;
crc <<= 1;
c >>= 1;
if (carry)
}
}
return (crc);
}
/*
* Return the CAM index (base 0) of maddr or -1 if maddr is not found
* If maddr is 0, return the index of an empty slot in CAM or -1 when no free
* slots available.
*/
static int32_t
{
uint32_t a;
/*
* Read the CAM mask from the controller.
*/
/*
* If maddr is 0, return the first unused slot or -1 for no unused.
*/
/*
* Look for the first unused position in mask.
*/
return (index);
}
return (-1);
} else {
/*
* Look for maddr in CAM.
*/
/* Look at enabled entries only */
continue;
drv_usecwait(2);
for (a = 0; a < ETHERADDRL; a++)
return (index);
}
}
return (-1);
}
/*
* Set promiscuous mode on or off.
*/
int
{
/*
* Get current receive configuration.
*/
if (promiscflag == B_TRUE) {
/*
* Enable promiscuous mode and open the multicast filter.
*/
} else {
/*
* Restore the multicast filter and disable promiscuous mode.
*/
rxcfg &= ~VR_RXCFG_PROMISC;
}
return (0);
}
int
{
uint64_t v;
switch (stat) {
default:
return (ENOTSUP);
case ETHER_STAT_ADV_CAP_100T4:
break;
break;
break;
case ETHER_STAT_ADV_CAP_10FDX:
break;
case ETHER_STAT_ADV_CAP_10HDX:
break;
v = 0;
break;
break;
case ETHER_STAT_ADV_CAP_PAUSE:
break;
case ETHER_STAT_ADV_REMFAULT:
break;
case ETHER_STAT_ALIGN_ERRORS:
break;
case ETHER_STAT_CAP_100T4:
break;
case ETHER_STAT_CAP_100FDX:
break;
case ETHER_STAT_CAP_100HDX:
break;
case ETHER_STAT_CAP_10FDX:
break;
case ETHER_STAT_CAP_10HDX:
break;
case ETHER_STAT_CAP_ASMPAUSE:
v = 0;
break;
case ETHER_STAT_CAP_AUTONEG:
break;
case ETHER_STAT_CAP_PAUSE:
v = 1;
break;
case ETHER_STAT_CAP_REMFAULT:
break;
/*
* Number of times carrier was lost or never detected on a
* transmission attempt.
*/
break;
case ETHER_STAT_JABBER_ERRORS:
return (ENOTSUP);
case ETHER_STAT_DEFER_XMTS:
/*
* Packets without collisions where first transmit attempt was
* delayed because the medium was busy.
*/
break;
case ETHER_STAT_EX_COLLISIONS:
/*
* Frames where excess collisions occurred on transmit, causing
* transmit failure.
*/
break;
case ETHER_STAT_FCS_ERRORS:
/*
* Packets received with CRC errors.
*/
break;
/*
* Packets successfully transmitted with exactly one collision.
*/
break;
case ETHER_STAT_LINK_ASMPAUSE:
v = 0;
break;
case ETHER_STAT_LINK_AUTONEG:
break;
case ETHER_STAT_LINK_DUPLEX:
break;
case ETHER_STAT_LINK_PAUSE:
break;
case ETHER_STAT_LP_CAP_100T4:
break;
v = 0;
break;
v = 0;
break;
case ETHER_STAT_LP_CAP_100FDX:
break;
case ETHER_STAT_LP_CAP_100HDX:
break;
case ETHER_STAT_LP_CAP_10FDX:
break;
case ETHER_STAT_LP_CAP_10HDX:
break;
v = 0;
break;
break;
case ETHER_STAT_LP_CAP_PAUSE:
break;
case ETHER_STAT_LP_REMFAULT:
break;
case ETHER_STAT_MACRCV_ERRORS:
/*
* Packets received with MAC errors, except align_errors,
* fcs_errors, and toolong_errors.
*/
break;
case ETHER_STAT_MACXMT_ERRORS:
/*
* Packets encountering transmit MAC failures, except carrier
* and collision failures.
*/
break;
/*
* Packets successfully transmitted with multiple collisions.
*/
break;
case ETHER_STAT_SQE_ERRORS:
/*
* Number of times signal quality error was reported
* This one is reported by the PHY.
*/
return (ENOTSUP);
/*
* Packets received larger than the maximum permitted length.
*/
break;
break;
/*
* Number of times a transmit collision occurred late
* (after 512 bit times).
*/
break;
case ETHER_STAT_XCVR_ADDR:
/*
* MII address in the 0 to 31 range of the physical layer
* device in use for a given Ethernet device.
*/
break;
case ETHER_STAT_XCVR_ID:
/*
* MII transceiver manufacturer and device ID.
*/
break;
case ETHER_STAT_XCVR_INUSE:
break;
case MAC_STAT_BRDCSTRCV:
break;
case MAC_STAT_BRDCSTXMT:
break;
case MAC_STAT_MULTIXMT:
break;
case MAC_STAT_COLLISIONS:
break;
case MAC_STAT_IERRORS:
break;
case MAC_STAT_IFSPEED:
v = 100 * 1000 * 1000;
v = 10 * 1000 * 1000;
else
v = 0;
break;
case MAC_STAT_IPACKETS:
break;
case MAC_STAT_MULTIRCV:
break;
case MAC_STAT_NORCVBUF:
break;
case MAC_STAT_NOXMTBUF:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_OERRORS:
break;
case MAC_STAT_OPACKETS:
break;
case MAC_STAT_RBYTES:
break;
case MAC_STAT_UNKNOWNS:
/*
* Isn't this something for the MAC layer to maintain?
*/
return (ENOTSUP);
case MAC_STAT_UNDERFLOWS:
break;
case MAC_STAT_OVERFLOWS:
break;
}
*val = v;
return (0);
}
int
{
int i;
/*
* Set a new station address.
*/
for (i = 0; i < ETHERADDRL; i++)
return (0);
}
/*
* Configure the ethernet link according to param and chip.mii.
*/
static void
{
/*
* If we do autoneg, ensure restart autoneg is ON.
*/
/*
* The advertisements are prepared by param_init.
*/
} else {
/*
* If we don't autoneg, we need speed, duplex and flowcontrol
* to configure the link. However, dladm doesn't allow changes
* to speed and duplex (readonly). The way this is solved
* (ahem) is to select the highest enabled combination
* Speed and duplex should be r/w when autoneg is off.
*/
MII_ABILITY_100BASE_TX_FD) != 0) {
MII_ABILITY_100BASE_TX) != 0) {
MII_ABILITY_10BASE_T_FD) != 0) {
} else {
}
}
/*
* Write the control register.
*/
/*
* With autoneg off we cannot rely on the link_change interrupt for
* for getting the status into the driver.
*/
}
}
/*
* Get link state in the driver and configure the MAC accordingly.
*/
static void
{
/*
* highest common denominator.
*/
if ((mask & MII_ABILITY_100BASE_TX_FD) != 0) {
} else if ((mask & MII_ABILITY_100BASE_T4) != 0) {
} else if ((mask & MII_ABILITY_100BASE_TX) != 0) {
} else if ((mask & MII_ABILITY_10BASE_T_FD) != 0) {
} else if ((mask & MII_ABILITY_10BASE_T) != 0) {
} else {
}
/*
* Did we negotiate pause?
*/
if ((mask & MII_AN_ADVERT_FCS) != 0 &&
else
/*
* Did either one detect a AN fault?
*/
"AN remote fault reported by LP.");
} else {
/*
* We didn't autoneg
* The link type is defined by the control register.
*/
} else {
}
else {
/*
* No pause on HDX links.
*/
}
}
/*
* Set the duplex mode on the MAC according to that of the PHY.
*/
/*
* Enable packet queueing on FDX links.
*/
} else {
/*
* Disable packet queueing on HDX links. With queueing enabled,
* this MAC get's lost after a TX abort (too many colisions).
*/
}
/*
* Set pause options on the MAC.
*/
/*
* All of our MAC's can receive pause frames.
*/
/*
* VT6105 and above can transmit pause frames.
*/
/*
* Set the number of available receive descriptors
* Non-zero values written to this register are added
* to the register's contents. Careful: Writing zero
* clears the register and thus causes a (long) pause
* request.
*/
/*
* Request pause when we have 4 descs left.
*/
/*
* Cancel the pause when there are 24 descriptors again.
*/
/*
* Request a pause of FFFF bit-times. This long pause
* is cancelled when the high watermark is reached.
*/
/*
* Enable flow control on the MAC.
*/
}
} else {
/*
* Turn flow control OFF.
*/
}
}
/*
* Set link state.
*/
else
}
/*
* The PHY is automatically polled by the MAC once per 1024 MD clock cycles
* MD is clocked once per 960ns so polling happens about every 1M ns, some
* 1000 times per second
* This polling process is required for the functionality of the link change
* interrupt. Polling process must be disabled in order to access PHY registers
* using MDIO
*
* Turn off PHY polling so that the PHY registers can be accessed.
*/
static void
{
/*
* Special procedure to stop the autopolling.
*/
/*
* If polling is enabled.
*/
if ((miicmd & VR_MIICMD_MD_AUTO) != 0) {
/*
* Wait for the end of a cycle (mdone set).
*/
time = 0;
do {
drv_usecwait(10);
if (time >= VR_MMI_WAITMAX) {
"Timeout in "
"disable MII polling");
break;
}
time += VR_MMI_WAITINCR;
} while ((miiaddr & VR_MIIADDR_MDONE) == 0);
}
/*
* Once paused, we can disable autopolling.
*/
} else {
/*
* Turn off MII polling.
*/
/*
* Wait for MIDLE in MII address register.
*/
time = 0;
do {
if (time >= VR_MMI_WAITMAX) {
"Timeout in disable MII polling");
break;
}
time += VR_MMI_WAITINCR;
} while ((miiaddr & VR_MIIADDR_MIDLE) == 0);
}
}
/*
* Turn on PHY polling. PHY's registers cannot be accessed.
*/
static void
{
/*
* Wait for the polling process to finish.
*/
time = 0;
do {
if (time >= VR_MMI_WAITMAX) {
break;
}
time += VR_MMI_WAITINCR;
/*
* Initiate a polling.
*/
}
/*
* Read a register from the PHY using MDIO.
*/
static void
{
/*
* Write the register number to the lower 5 bits of the MII address
* register.
*/
/*
* Write a READ command to the MII control register
* This bit will be cleared when the read is finished.
*/
/*
* Wait until the read is done.
*/
time = 0;
do {
if (time >= VR_MMI_WAITMAX) {
break;
}
time += VR_MMI_WAITINCR;
}
/*
* Write to a PHY's register.
*/
static void
{
/*
* Write the register number to the MII address register.
*/
/*
* Write the value to the data register.
*/
/*
* Issue the WRITE command to the command register.
* This bit will be cleared when the write is finished.
*/
time = 0;
do {
if (time >= VR_MMI_WAITMAX) {
break;
}
time += VR_MMI_WAITINCR;
}
/*
* Initialize and install some private kstats.
*/
typedef struct {
char *name;
} vr_kstat_t;
static const vr_kstat_t vr_driver_stats [] = {
{"allocbfail", KSTAT_DATA_INT32},
{"intr_claimed", KSTAT_DATA_INT64},
{"intr_unclaimed", KSTAT_DATA_INT64},
{"linkchanges", KSTAT_DATA_INT64},
{"txnfree", KSTAT_DATA_INT32},
{"txstalls", KSTAT_DATA_INT32},
{"resets", KSTAT_DATA_INT32},
{"txreclaims", KSTAT_DATA_INT64},
{"txreclaim0", KSTAT_DATA_INT64},
{"cyclics", KSTAT_DATA_INT64},
{"txchecks", KSTAT_DATA_INT64},
};
static void
{
struct kstat_named *knp;
int i;
int nstats;
vr_driver_stats[i].type);
}
}
static int
{
struct kstat_named *knp;
if (access != KSTAT_READ)
return (EACCES);
return (0);
}
/*
* Remove 'private' kstats.
*/
static void
{
}
/*
* Remarks:
* - pr_val is always an integer of size pr_valsize
* - ENABLED (EN) is what is configured via dladm
* - DEFAULT are driver- and hardware defaults (DEFAULT is implemented as a
* flag in pr_flags instead of MAC_PROP_DEFAULT_)
* - perm is the permission printed on ndd -get /.. \?
*/
int
{
/* Since we have no private properties */
err = 0;
if ((pr_flags & MAC_PROP_DEFAULT) != 0) {
/*
* with enotsup ....
*/
*perm = MAC_PROP_PERM_RW;
switch (pr_num) {
case MAC_PROP_ADV_1000FDX_CAP:
case MAC_PROP_EN_1000FDX_CAP:
case MAC_PROP_ADV_1000HDX_CAP:
case MAC_PROP_EN_1000HDX_CAP:
val = 0;
break;
case MAC_PROP_ADV_100FDX_CAP:
case MAC_PROP_EN_100FDX_CAP:
MII_STATUS_100_BASEX_FD) != 0;
break;
case MAC_PROP_ADV_100HDX_CAP:
case MAC_PROP_EN_100HDX_CAP:
MII_STATUS_100_BASEX) != 0;
break;
case MAC_PROP_ADV_100T4_CAP:
case MAC_PROP_EN_100T4_CAP:
MII_STATUS_100_BASE_T4) != 0;
break;
case MAC_PROP_ADV_10FDX_CAP:
case MAC_PROP_EN_10FDX_CAP:
MII_STATUS_10_FD) != 0;
break;
case MAC_PROP_ADV_10HDX_CAP:
case MAC_PROP_EN_10HDX_CAP:
MII_STATUS_10) != 0;
break;
case MAC_PROP_AUTONEG:
case MAC_PROP_EN_AUTONEG:
MII_STATUS_CANAUTONEG) != 0;
break;
case MAC_PROP_DUPLEX:
break;
case MAC_PROP_FLOWCTRL:
break;
case MAC_PROP_MTU:
break;
case MAC_PROP_SPEED:
break;
case MAC_PROP_STATUS:
break;
default:
return (ENOTSUP);
}
} else {
switch (pr_num) {
case MAC_PROP_ADV_1000FDX_CAP:
case MAC_PROP_ADV_1000HDX_CAP:
val = 0;
break;
case MAC_PROP_EN_1000FDX_CAP:
case MAC_PROP_EN_1000HDX_CAP:
val = 0;
break;
case MAC_PROP_ADV_100FDX_CAP:
MII_ABILITY_100BASE_TX_FD) != 0;
break;
case MAC_PROP_ADV_100HDX_CAP:
MII_ABILITY_100BASE_TX) != 0;
break;
case MAC_PROP_ADV_100T4_CAP:
MII_ABILITY_100BASE_T4) != 0;
break;
case MAC_PROP_ADV_10FDX_CAP:
MII_ABILITY_10BASE_T_FD) != 0;
break;
case MAC_PROP_ADV_10HDX_CAP:
MII_ABILITY_10BASE_T) != 0;
break;
case MAC_PROP_AUTONEG:
*perm = MAC_PROP_PERM_RW;
MII_CONTROL_ANE) != 0;
break;
case MAC_PROP_DUPLEX:
/*
* Writability depends on autoneg.
*/
MII_CONTROL_ANE) == 0)
*perm = MAC_PROP_PERM_RW;
else
break;
case MAC_PROP_EN_100FDX_CAP:
*perm = MAC_PROP_PERM_RW;
MII_ABILITY_100BASE_TX_FD) != 0;
break;
case MAC_PROP_EN_100HDX_CAP:
*perm = MAC_PROP_PERM_RW;
MII_ABILITY_100BASE_TX) != 0;
break;
case MAC_PROP_EN_100T4_CAP:
MII_ABILITY_100BASE_T4) != 0;
break;
case MAC_PROP_EN_10FDX_CAP:
*perm = MAC_PROP_PERM_RW;
MII_ABILITY_10BASE_T_FD) != 0;
break;
case MAC_PROP_EN_10HDX_CAP:
*perm = MAC_PROP_PERM_RW;
MII_ABILITY_10BASE_T) != 0;
break;
case MAC_PROP_EN_AUTONEG:
*perm = MAC_PROP_PERM_RW;
break;
case MAC_PROP_FLOWCTRL:
*perm = MAC_PROP_PERM_RW;
break;
case MAC_PROP_MTU:
*perm = MAC_PROP_PERM_RW;
break;
case MAC_PROP_SPEED:
/*
* Writability depends on autoneg.
*/
MII_CONTROL_ANE) == 0)
*perm = MAC_PROP_PERM_RW;
else
else
val = 0;
break;
case MAC_PROP_STATUS:
break;
default:
break;
}
}
if (pr_valsize == sizeof (uint64_t))
else if (pr_valsize == sizeof (uint32_t))
else if (pr_valsize == sizeof (uint16_t))
else if (pr_valsize == sizeof (uint8_t))
else
}
return (err);
}
/*
* Set a property of the device.
*/
int
{
/* Since we have no private properties */
err = 0;
/*
* The current set of public property values are passed as integers
* Private properties are passed as strings in pr_val length pr_valsize.
*/
if (pr_num != MAC_PROP_PRIVATE) {
if (pr_valsize == sizeof (uint64_t))
else if (pr_valsize == sizeof (uint32_t))
else if (pr_valsize == sizeof (uint16_t))
else if (pr_valsize == sizeof (uint8_t))
else {
return (EINVAL);
}
}
switch (pr_num) {
case MAC_PROP_DUPLEX:
if (val == LINK_DUPLEX_FULL)
else if (val == LINK_DUPLEX_HALF)
else
} else
break;
case MAC_PROP_EN_100FDX_CAP:
if (val == 0)
else
break;
case MAC_PROP_EN_100HDX_CAP:
if (val == 0)
else
break;
case MAC_PROP_EN_100T4_CAP:
if (val == 0)
else
break;
case MAC_PROP_EN_10FDX_CAP:
if (val == 0)
else
break;
case MAC_PROP_EN_10HDX_CAP:
if (val == 0)
else
break;
case MAC_PROP_AUTONEG:
case MAC_PROP_EN_AUTONEG:
if (val == 0) {
} else {
MII_STATUS_CANAUTONEG) != 0)
else
}
break;
case MAC_PROP_FLOWCTRL:
if (val == LINK_FLOWCTRL_NONE)
else if (val == LINK_FLOWCTRL_BI)
else
break;
case MAC_PROP_MTU:
else
break;
case MAC_PROP_SPEED:
else
break;
default:
break;
}
}
return (err);
}
/*
* Logging and debug functions.
*/
static struct {
const char *ifname;
const char *fmt;
int level;
} prtdata;
static void
{
char buf[512];
}
static void
{
}
#if defined(DEBUG)
static void
{
}
void
{
return (vr_prt);
}
#endif /* DEBUG */
static struct modldrv vr_modldrv = {
&mod_driverops, /* Type of module. This one is a driver */
vr_ident, /* short description */
&vr_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 == 0) {
}
return (status);
}