/*
* 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 2012 Nexenta Systems, Inc. All rights reserved.
*/
/*
* rtls -- REALTEK 8139-serials PCI Fast Ethernet Driver, Depends on the
*
* This product is covered by one or more of the following patents:
* US5,307,459, US5,434,872, US5,732,094, US6,570,884, US6,115,776, and
* US6,327,625.
*
* Currently supports:
* RTL8139
*/
#include <sys/ethernet.h>
#include <sys/mac_provider.h>
#include <sys/mac_ether.h>
#include "rtls.h"
/*
* Declarations and Module Linkage
*/
/*
* This is the string displayed by modinfo, etc.
*/
#ifdef RTLS_DEBUG
int rtls_debug = 0;
#endif
/*
* Required system entry points
*/
static int rtls_quiesce(dev_info_t *);
/*
* Required driver entry points for MAC
*/
static int rtls_m_start(void *);
static void rtls_m_stop(void *);
static int rtls_m_unicst(void *, const uint8_t *);
static int rtls_m_promisc(void *, boolean_t);
void *);
const void *);
static void rtls_m_propinfo(void *, const char *, mac_prop_id_t,
/*
* MII entry points
*/
static void rtls_mii_notify(void *, link_state_t);
/*
* Internal functions used by the above entry points
*/
static void rtls_chip_init(rtls_t *);
static void rtls_receive(rtls_t *);
/*
* Buffer Management Routines
*/
static int rtls_alloc_bufs(rtls_t *);
static void rtls_free_bufs(rtls_t *);
uint_t, dma_area_t *);
static void rtls_free_dma_mem(dma_area_t *);
#ifdef RTLS_DEBUG
#endif
/*
* Used for buffers allocated by ddi_dma_mem_alloc()
*/
DMA_ATTR_V0, /* dma_attr version */
0, /* dma_attr_addr_lo */
0x7FFFFFFF, /* dma_attr_count_max */
4, /* dma_attr_align */
0x3F, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
1, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0, /* dma_attr_flags */
};
/*
* PIO access attributes for registers
*/
};
/*
* DMA access attributes for data
*/
};
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
NULL,
NULL, /* mc_ioctl */
NULL, /* mc_getcapab */
NULL, /* mc_open */
NULL, /* mc_close */
};
rtls_mii_notify, /* notify */
NULL, /* reset */
};
/*
* Standard module linkage initialization for a MAC driver
*/
&mod_driverops, /* type of module. This one is a driver */
rtls_ident, /* short description */
&rtls_dev_ops /* driver specific ops */
};
};
/*
* ========== RealTek chip register access Routines ==========
*/
#pragma inline(rtls_reg_get8)
static uint8_t
{
}
#pragma inline(rtls_reg_get16)
static uint16_t
{
}
#pragma inline(rtls_reg_get32)
static uint32_t
{
}
#pragma inline(rtls_reg_set8)
static void
{
}
#pragma inline(rtls_reg_set16)
static void
{
}
#pragma inline(rtls_reg_set32)
static void
{
}
/*
* ========== Module Loading Entry Points ==========
*/
int
_init(void)
{
int rv;
}
return (rv);
}
int
_fini(void)
{
int rv;
}
return (rv);
}
int
{
}
/*
* ========== DDI Entry Points ==========
*/
/*
* attach(9E) -- Attach a device to the system
*
* Called once for each board successfully probed.
*/
static int
{
int err;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_FAILURE);
}
/*
* Turn on Master Enable (DMA) and IO Enable bits.
* Enable PCI Memory Space accesses
* Disable Memory Write/Invalidate
*/
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/*
* we don't support high level interrupts in the driver
*/
if (ddi_intr_hilevel(devinfo, 0) != 0) {
return (DDI_FAILURE);
}
/*
* Get handle to access pci configuration space
*/
return (DDI_FAILURE);
}
/*
*/
/*
* See if we support this device
* We do not return for wrong device id. It's user risk.
*/
switch (device) {
default:
"RTLS doesn't support this device: "
"vendorID = 0x%x, deviceID = 0x%x",
break;
case RTLS_SUPPORT_DEVICE_1:
case RTLS_SUPPORT_DEVICE_2:
case RTLS_SUPPORT_DEVICE_3:
case RTLS_SUPPORT_DEVICE_4:
break;
}
/*
* Turn on Master Enable (DMA) and IO Enable bits.
* Enable PCI Memory Space accesses
* Disable Memory Write/Invalidate
*/
/*
* Free handle to access pci configuration space
*/
/*
* Map operating register
*/
if (err != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/*
* Allocate the TX and RX descriptors/buffers
*/
goto fail;
}
/*
* Reset the chip
*/
if (err != DDI_SUCCESS)
goto fail;
/*
* Init rtls_t structure
*/
/*
* Add the interrupt handler
*
* This will prevent receiving interrupts before device is ready, as
* we are initializing device after setting the interrupts. So we
* will not get our interrupt handler invoked by OS while our device
* is still coming up or timer routines will not start till we are
* all set to process...
*/
goto late_fail;
}
goto late_fail;
}
/*
* Note: Some models of 8139 can support pause, but we have
* not implemented support for it at this time. This might be
* an interesting feature to add later.
*/
goto late_fail;
}
/*
* Init mutex
*/
/*
* Initialize pointers to device specific functions which will be
* used by the generic layer.
*/
goto late_fail;
}
return (DDI_SUCCESS);
if (macp)
fail:
return (DDI_FAILURE);
}
/*
* detach(9E) -- Detach a device from the system
*/
static int
{
/*
* Get the driver private structure
*/
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/* device busy */
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* quiesce(9E) entry point.
*
* This function is called when the system is single-threaded at high
* PIL with preemption disabled. Therefore, this function must not be
* blocked.
*
* This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
* DDI_FAILURE indicates an error condition and should almost never happen.
*/
static int
{
/*
* Get the driver private structure
*/
return (DDI_FAILURE);
}
}
/*
* ========== MAC Entry Points ==========
*/
/*
* rtls_m_start() -- start the board receiving and allow transmits
*/
static int
{
if (!rtlsp->rtls_suspended)
drv_usecwait(100);
return (0);
}
/*
* rtls_m_stop() -- stop board receiving and transmits
*/
static void
{
if (!rtlsp->rtls_suspended)
}
/*
* rtls_m_unicst() -- set the physical network address
* on the board
*/
static int
{
if (!rtlsp->rtls_suspended)
return (0);
}
/*
* rtls_m_multicst() -- set(enable) or disable a multicast address
*
*/
static int
{
/* index value is between 0 and 63 */
if (enable) {
return (0);
}
} else {
return (0);
}
}
/*
* Set multicast register
*/
if (!rtlsp->rtls_suspended) {
}
return (0);
}
/*
* rtls_hash_index() -- a hashing function used for setting the
* node address or a multicast address
*/
static uint_t
{
int bytes;
int bit;
crc <<= 1;
crc |= 0x00000001;
}
currentbyte >>= 1;
}
}
return (index);
}
/*
* rtls_m_promisc() -- set or reset promiscuous mode on the board
*/
static int
{
if (!rtlsp->rtls_suspended) {
if (on) {
} else {
}
}
return (0);
}
/*
* rtls_m_stat() -- retrieve statistic
*
* MAC calls this routine just before it reads the driver's statistics
* structure. If your board maintains statistics, this is the time to
* read them in and update the values in the structure. If the driver
* maintains statistics continuously, this routine need do nothing.
*/
static int
{
return (0);
}
switch (stat) {
case MAC_STAT_IPACKETS:
break;
case MAC_STAT_RBYTES:
break;
case MAC_STAT_OPACKETS:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_IERRORS:
break;
case MAC_STAT_OERRORS:
break;
case MAC_STAT_MULTIRCV:
break;
case MAC_STAT_BRDCSTRCV:
break;
case MAC_STAT_MULTIXMT:
break;
case MAC_STAT_BRDCSTXMT:
break;
case MAC_STAT_UNDERFLOWS:
break;
case MAC_STAT_OVERFLOWS:
break;
case MAC_STAT_NORCVBUF:
break;
case MAC_STAT_COLLISIONS:
break;
case ETHER_STAT_FCS_ERRORS:
break;
case ETHER_STAT_ALIGN_ERRORS:
break;
case ETHER_STAT_DEFER_XMTS:
break;
break;
break;
break;
break;
break;
break;
default:
return (ENOTSUP);
}
/*
* RTL8139 don't support MII statistics,
* these values are maintained by the driver software.
*/
#ifdef RTLS_DEBUG
if (rtls_debug & RTLS_TRACE)
#endif
return (0);
}
int
void *val)
{
}
int
const void *val)
{
}
static void
{
}
/*
* rtls_send() -- send a packet
*
* Called when a packet is ready to be transmitted. A pointer to an
* M_DATA message that contains the packet is passed to this routine.
* The complete LLC header is contained in the message's first message
* block, and the remainder of the packet is contained within
* additional M_DATA message blocks linked to the first message block.
*
* Returns B_TRUE if the packet was properly disposed of, or B_FALSE if
* if the packet is being deferred and should be tried again later.
*/
static boolean_t
{
int totlen;
int ncc;
if (rtlsp->rtls_suspended) {
return (B_FALSE);
}
/*
* If chip error ...
*/
if (rtlsp->chip_error) {
#ifdef RTLS_DEBUG
"%s: send fail--CHIP ERROR!",
#endif
return (B_TRUE);
}
/*
* If chip link down ... Note that experimentation shows that
* the device seems not to care about whether or not we have
* this check, but if we don't add the check here, it might
* not be properly reported as a carrier error.
*/
#ifdef RTLS_DEBUG
"%s: send fail--LINK DOWN!",
#endif
return (B_TRUE);
}
/*
* Current transmit descriptor
*/
/*
* RealTek 8139 has 4 tx descriptor for transmit. In the first tx loop
* of transmit,we needn't judge transmit status.
*/
rtlsp->tx_first_loop++;
goto tx_ready;
}
/*
* If it's not the first tx loop, we need judge whether the chip is
* busy or not. Otherwise, we have to reschedule send and wait...
*/
/*
* H/W doesn't complete packet transmit
*/
if (!(tx_status & TX_COMPLETE_FLAG)) {
#ifdef RTLS_DEBUG
if (rtls_debug & RTLS_SEND) {
}
#endif
/*
* Through test, we find RTL8139 tx status might be
* not-completing all along. We have to reset chip
* to make RTL8139 tansmit re-work.
*/
/*
* Wait transmit h/w more time...
*/
RTLS_TX_WAIT_TIMEOUT; /* 100 ms */
/*
* Judge tx status again, if it remains not-completing,
* we can confirm RTL8139 is in chip error state
* and must reset it.
*/
if (!(tx_status & TX_COMPLETE_FLAG)) {
#ifdef RTLS_DEBUG
#endif
return (B_TRUE);
}
} else {
return (B_FALSE);
}
}
/*
* Transmit error?
*/
if (tx_status & TX_ERR_FLAG) {
#ifdef RTLS_DEBUG
if (rtls_debug & RTLS_SEND) {
}
#endif
if (tx_status & TX_STATUS_TX_UNDERRUN)
if (tx_status & TX_STATUS_CS_LOST)
if (tx_status & TX_STATUS_OWC)
}
if (ncc != 0) {
}
/*
* Initialize variable
*/
totlen = 0;
/*
* Copy packet to tx descriptor buffer
*/
"%s: rtls_send: try to send large %d packet",
return (B_TRUE);
}
/* this will free the mblk */
/* update stats */
if ((ptr[0] == 0xffff) &&
} else {
}
}
}
/* make sure caches are flushed */
/*
* Start transmit
* set transmit FIFO threshhold to 0x30*32 = 1536 bytes
* to avoid tx underrun.
*/
/*
* Update the value of current tx descriptor
*/
cur_desc++;
return (B_TRUE);
}
/*
* rtls_m_tx() -- send a chain of packets, linked by mp->b_next.
*/
static mblk_t *
{
break;
}
}
return (mp);
}
/*
* rtls_receive() -- receive packets
*
* Called when receive interrupts detected
*/
static void
{
int wrap_size;
if (rtlsp->rtls_suspended) {
return;
}
& RT_COMMAND_BUFF_EMPTY) == 0) {
/*
* Chip error state
*/
if (rtlsp->chip_error) {
#ifdef RTLS_DEBUG
"%s: receive fail--CHIP ERROR!",
#endif
break;
}
/*
* DMA still in progress
*/
if (packet_len == RX_STATUS_DMA_BUSY) {
break;
}
/*
* Check receive status
*/
if ((rx_status & RX_ERR_FLAGS) ||
(!(rx_status & RX_HEADER_STATUS_ROK)) ||
#ifdef RTLS_DEBUG
"%s: receive error, status = 0x%x, length = %d",
#endif
/*
* Rx error statistics
*/
if ((rx_status & RX_HEADER_STATUS_RUNT) ||
else if (rx_status & RX_HEADER_STATUS_CRC)
else if (rx_status & RX_HEADER_STATUS_FAE)
/*
* Set chip_error flag to reset chip:
* (suggested in RealTek programming guide.)
*/
return;
}
/*
* We need not up-send ETHERFCSL bytes of receive packet
*/
packet_len -= ETHERFCSL;
/*
* Allocate buffer to receive this good packet
*/
/*
* Copy the data found into the new cluster, we have (+4)
* to get us past the packet head data that the rtl chip
* places at the start of the message
*/
> RTLS_RX_BUF_RING) {
#ifdef RTLS_DEBUG
if (rtls_debug & RTLS_RECV) {
"%s: Rx: packet_len = %d, wrap_size = %d",
}
#endif
/* Flush caches */
0, wrap_size,
/*
* Copy in first section of message as stored
* at the end of the ring buffer
*/
if (rx_status & RX_HEADER_STATUS_BCAST)
if (rx_status & RX_HEADER_STATUS_MULTI)
} else {
}
/* 4-byte aligned */
} else {
/* Flush caches */
if (rx_status & RX_HEADER_STATUS_BCAST)
if (rx_status & RX_HEADER_STATUS_MULTI)
} else {
}
/* 4-byte aligned */
}
/*
* Update rx buffer ring read pointer:
* give us a little leeway to ensure no overflow
*/
cur_rx - READ_ADDR_GAP);
}
/*
* Upsend packet
*/
if (head) {
}
}
/*
* rtls_intr() -- interrupt from board to inform us that a receive or
* link change.
*/
static uint_t
{
if (rtlsp->rtls_suspended) {
return (DDI_INTR_UNCLAIMED);
}
/*
* Was this interrupt caused by our device...
*/
return (DDI_INTR_UNCLAIMED);
/* indicate it wasn't our interrupt */
}
/*
* Clear interrupt
*/
/*
* If chip error, restart chip...
*/
if (rtlsp->chip_error) {
return (DDI_INTR_CLAIMED);
/* no need to hand other interrupts */
}
/*
* Transmit error interrupt
*/
if (int_status & TX_ERR_INT) {
val32 |= TX_CLEAR_ABORT;
}
/*
* Trigger mac_tx_update
*/
if (rtlsp->need_sched) {
}
/*
* Receive interrupt
*/
if (int_status & RTLS_RX_INT) {
if (int_status & RX_OVERFLOW_INT) {
}
}
/*
* Link change interrupt.
*/
if (int_status & LINK_CHANGE_INT) {
}
if (resched) {
}
return (DDI_INTR_CLAIMED); /* indicate it was our interrupt */
}
/*
* ========== Buffer Management Routines ==========
*/
/*
* rtls_alloc_dma_mem() -- allocate an area of memory and a DMA handle
* for accessing it
*/
static int
{
int err;
/*
* Allocate handle
*/
if (err != DDI_SUCCESS) {
"%s: rtls_alloc_dma_mem: ddi_dma_alloc_handle failed: %d",
return (DDI_FAILURE);
}
/*
* Allocate memory
*/
if (err != DDI_SUCCESS) {
"%s: rtls_alloc_dma_mem: ddi_dma_mem_alloc failed: %d",
return (DDI_FAILURE);
}
/*
* Bind the two together
*/
"%s: rtls_alloc_dma_mem: "
"ddi_dma_addr_bind_handle failed: %d",
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* rtls_free_dma_mem() -- free one allocated area of DMAable memory
*/
static void
{
}
}
}
}
/*
* rtls_alloc_bufs() -- allocate descriptors/buffers for this device instance
*/
static int
{
int i;
int err;
/*
* Allocate memory & handle for Tx buffers
*/
for (i = 0; i < RTLS_MAX_TX_DESC; i++) {
&rtlsp->dma_area_tx[i]);
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
}
/*
* Allocate memory & handle for Rx buffers
*/
&rtlsp->dma_area_rx);
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* rtls_free_bufs() -- free descriptors/buffers allocated for this
* device instance.
*/
static void
{
int i;
for (i = 0; i < RTLS_MAX_TX_DESC; i++) {
}
}
/*
* ========== Chip H/W Operation Routines ==========
*/
/*
* rtls_chip_reset() -- reset chip
*/
static int
{
int i;
/*
* Chip should be in STOP state
*/
/*
* Disable interrupt
*/
/*
* Clear pended interrupt
*/
/*
* Reset chip
*/
/*
* Wait for reset success
*/
i = 0;
if (++i > RTLS_RESET_WAIT_NUM) {
/*
* At quiesce path we can't call cmn_err(), as
* it might block
*/
if (!quiesce)
"%s: chip reset fail.",
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
/*
* rtls_chip_init() -- initialize the specified network board short of
* actually starting the board. Call after rtls_chip_reset().
*/
static void
{
/*
* Initialize internal data structures
*/
rtlsp->tx_current_desc = 0;
rtlsp->tx_first_loop = 0;
/*
*/
/*
*/
/*
* Set transmit configuration register
*/
/*
* Set receive configuration register
*/
/*
* Set multicast register
*/
/*
* Set unicast address
*/
/*
* Set current address of packet read
*/
/*
* No early-rx interrupts
*/
val16 &= ~RT_MUL_INTSEL_BITS;
}
/*
* rtls_chip_start() -- start chip
*/
static void
{
/*
*/
/*
* Enable interrupt
*/
}
/*
* rtls_chip_restart() -- restart chip
*/
static void
{
}
/*
* rtls_chip_stop() -- stop board receiving
*/
static void
{
/*
* Disable interrupt
*/
/*
* Clear pended interrupt
*/
/*
*/
}
/*
* rtls_get_mac_addr() -- get the physical network address on the board
*/
static void
{
/*
* Read first 4-byte of mac address
*/
/*
* Read last 2-byte of mac address
*/
}
static void
{
/*
* Change to config register write enable mode
*/
/*
* Get first 4 bytes of mac address
*/
/*
* Set first 4 bytes of mac address
*/
/*
* Get last 2 bytes of mac address
*/
/*
* Set last 2 bytes of mac address
*/
/*
*/
val8 &= ~RT_93c46_MODE_CONFIG;
}
static uint16_t
{
if (phy != 1) {
return (0xffff);
}
switch (reg) {
case MII_CONTROL:
break;
case MII_STATUS:
break;
case MII_AN_ADVERT:
break;
case MII_AN_LPABLE:
break;
case MII_AN_EXPANSION:
break;
case MII_VENDOR(0):
/*
* We "simulate" a vendor private register so that the
* PHY layer can access it to determine detected link
*/
break;
case MII_PHYIDH:
case MII_PHYIDL:
default:
val = 0;
break;
}
return (val);
}
void
{
if (phy != 1) {
return;
}
switch (reg) {
case MII_CONTROL:
/* Enable writes to all bits of BMCR */
/* write out the value */
val8 &= ~RT_93c46_MODE_CONFIG;
return;
case MII_STATUS:
break;
case MII_AN_ADVERT:
break;
case MII_AN_LPABLE:
break;
case MII_AN_EXPANSION:
break;
case MII_PHYIDH:
case MII_PHYIDL:
default:
/* these are not writable */
break;
}
}
void
{
}
#ifdef RTLS_DEBUG
/*
* rtls_reg_print() -- print out reg value(for debug use only)
*/
static void
{
}
#endif