#include <sys/types.h>
#include <sys/debug.h>
#include <sys/errno.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/kmem.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/devops.h>
#include <sys/ksynch.h>
#include <sys/stat.h>
#include <sys/conf.h>
#include <sys/modctl.h>
#include <sys/dlpi.h>
#include <sys/ethernet.h>
#include <sys/vlan.h>
#include <sys/strsun.h>
#include <sys/pci.h>
#include <sys/sunddi.h>
#include <sys/mii.h>
#include <sys/miiregs.h>
#include <sys/mac_provider.h>
#include <sys/mac_ether.h>
#include "rtls.h"
static char rtls_ident[] = "RealTek 8139 Ethernet driver";
#ifdef RTLS_DEBUG
int rtls_debug = 0;
#endif
static int rtls_attach(dev_info_t *, ddi_attach_cmd_t);
static int rtls_detach(dev_info_t *, ddi_detach_cmd_t);
static int rtls_quiesce(dev_info_t *);
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_multicst(void *, boolean_t, const uint8_t *);
static int rtls_m_promisc(void *, boolean_t);
static mblk_t *rtls_m_tx(void *, mblk_t *);
static int rtls_m_getprop(void *, const char *, mac_prop_id_t, uint_t,
void *);
static int rtls_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
const void *);
static void rtls_m_propinfo(void *, const char *, mac_prop_id_t,
mac_prop_info_handle_t);
static int rtls_m_stat(void *, uint_t, uint64_t *);
static uint_t rtls_intr(caddr_t);
static uint16_t rtls_mii_read(void *, uint8_t, uint8_t);
static void rtls_mii_write(void *, uint8_t, uint8_t, uint16_t);
static void rtls_mii_notify(void *, link_state_t);
static int rtls_chip_reset(rtls_t *, boolean_t);
static void rtls_chip_init(rtls_t *);
static void rtls_chip_stop(rtls_t *rtlsp);
static void rtls_chip_start(rtls_t *rtlsp);
static void rtls_chip_restart(rtls_t *rtlsp);
static void rtls_get_mac_addr(rtls_t *, uint8_t *);
static void rtls_set_mac_addr(rtls_t *, const uint8_t *);
static uint_t rtls_hash_index(const uint8_t *);
static boolean_t rtls_send(rtls_t *, mblk_t *);
static void rtls_receive(rtls_t *);
static int rtls_alloc_bufs(rtls_t *);
static void rtls_free_bufs(rtls_t *);
static int rtls_alloc_dma_mem(rtls_t *, size_t, ddi_device_acc_attr_t *,
uint_t, dma_area_t *);
static void rtls_free_dma_mem(dma_area_t *);
#ifdef RTLS_DEBUG
static void rtls_reg_print(rtls_t *);
#endif
#define RTLS_DRIVER_NAME "rtls"
static ddi_dma_attr_t dma_attr = {
DMA_ATTR_V0,
0,
(uint_t)0xFFFFFFFF,
0x7FFFFFFF,
4,
0x3F,
1,
(uint_t)0xFFFFFFFF,
(uint_t)0xFFFFFFFF,
1,
1,
0,
};
static ddi_device_acc_attr_t rtls_reg_accattr = {
DDI_DEVICE_ATTR_V0,
DDI_STRUCTURE_LE_ACC,
DDI_STRICTORDER_ACC
};
static ddi_device_acc_attr_t rtls_buf_accattr = {
DDI_DEVICE_ATTR_V0,
DDI_NEVERSWAP_ACC,
DDI_STRICTORDER_ACC
};
uchar_t rtls_broadcastaddr[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
static mac_callbacks_t rtls_m_callbacks = {
MC_PROPERTIES,
rtls_m_stat,
rtls_m_start,
rtls_m_stop,
rtls_m_promisc,
rtls_m_multicst,
rtls_m_unicst,
rtls_m_tx,
NULL,
NULL,
NULL,
NULL,
NULL,
rtls_m_setprop,
rtls_m_getprop,
rtls_m_propinfo
};
static mii_ops_t rtls_mii_ops = {
MII_OPS_VERSION,
rtls_mii_read,
rtls_mii_write,
rtls_mii_notify,
NULL,
};
DDI_DEFINE_STREAM_OPS(rtls_dev_ops, nulldev, nulldev, rtls_attach, rtls_detach,
nodev, NULL, D_MP, NULL, rtls_quiesce);
static struct modldrv rtls_modldrv = {
&mod_driverops,
rtls_ident,
&rtls_dev_ops
};
static struct modlinkage modlinkage = {
MODREV_1, { (void *)&rtls_modldrv, NULL }
};
static uint8_t
rtls_reg_get8(rtls_t *rtlsp, uint32_t reg)
{
uint8_t *addr;
addr = REG8(rtlsp->io_reg, reg);
return (ddi_get8(rtlsp->io_handle, addr));
}
static uint16_t
rtls_reg_get16(rtls_t *rtlsp, uint32_t reg)
{
uint16_t *addr;
addr = REG16(rtlsp->io_reg, reg);
return (ddi_get16(rtlsp->io_handle, addr));
}
static uint32_t
rtls_reg_get32(rtls_t *rtlsp, uint32_t reg)
{
uint32_t *addr;
addr = REG32(rtlsp->io_reg, reg);
return (ddi_get32(rtlsp->io_handle, addr));
}
static void
rtls_reg_set8(rtls_t *rtlsp, uint32_t reg, uint8_t value)
{
uint8_t *addr;
addr = REG8(rtlsp->io_reg, reg);
ddi_put8(rtlsp->io_handle, addr, value);
}
static void
rtls_reg_set16(rtls_t *rtlsp, uint32_t reg, uint16_t value)
{
uint16_t *addr;
addr = REG16(rtlsp->io_reg, reg);
ddi_put16(rtlsp->io_handle, addr, value);
}
static void
rtls_reg_set32(rtls_t *rtlsp, uint32_t reg, uint32_t value)
{
uint32_t *addr;
addr = REG32(rtlsp->io_reg, reg);
ddi_put32(rtlsp->io_handle, addr, value);
}
int
_init(void)
{
int rv;
mac_init_ops(&rtls_dev_ops, RTLS_DRIVER_NAME);
if ((rv = mod_install(&modlinkage)) != DDI_SUCCESS) {
mac_fini_ops(&rtls_dev_ops);
}
return (rv);
}
int
_fini(void)
{
int rv;
if ((rv = mod_remove(&modlinkage)) == DDI_SUCCESS) {
mac_fini_ops(&rtls_dev_ops);
}
return (rv);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
rtls_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
{
rtls_t *rtlsp;
ddi_acc_handle_t pci_handle;
uint16_t pci_commond;
uint16_t vendorid;
uint16_t deviceid;
uint32_t device;
mac_register_t *macp;
int err;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
if ((rtlsp = ddi_get_driver_private(devinfo)) == NULL) {
return (DDI_FAILURE);
}
mutex_enter(&rtlsp->rtls_io_lock);
mutex_enter(&rtlsp->rtls_rx_lock);
mutex_enter(&rtlsp->rtls_tx_lock);
if (pci_config_setup(devinfo, &pci_handle) != DDI_SUCCESS) {
mutex_exit(&rtlsp->rtls_tx_lock);
mutex_exit(&rtlsp->rtls_rx_lock);
mutex_exit(&rtlsp->rtls_io_lock);
return (DDI_FAILURE);
}
pci_commond = pci_config_get16(pci_handle, PCI_CONF_COMM);
pci_commond &= ~PCI_COMM_MEMWR_INVAL;
pci_commond |= PCI_COMM_ME | PCI_COMM_MAE | PCI_COMM_IO;
pci_config_put32(pci_handle, PCI_CONF_COMM, pci_commond);
pci_config_teardown(&pci_handle);
rtls_chip_restart(rtlsp);
rtlsp->chip_error = B_FALSE;
rtlsp->tx_retry = 0;
rtlsp->rtls_suspended = B_FALSE;
mutex_exit(&rtlsp->rtls_tx_lock);
mutex_exit(&rtlsp->rtls_rx_lock);
mutex_exit(&rtlsp->rtls_io_lock);
mii_resume(rtlsp->mii);
mac_tx_update(rtlsp->mh);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (ddi_intr_hilevel(devinfo, 0) != 0) {
cmn_err(CE_WARN, "unsupported high level interrupt");
return (DDI_FAILURE);
}
if (pci_config_setup(devinfo, &pci_handle) != DDI_SUCCESS) {
cmn_err(CE_WARN, "pci_config_setup fail.");
return (DDI_FAILURE);
}
vendorid = pci_config_get16(pci_handle, PCI_CONF_VENID);
deviceid = pci_config_get16(pci_handle, PCI_CONF_DEVID);
device = vendorid;
device = (device << 16) | deviceid;
switch (device) {
default:
cmn_err(CE_WARN,
"RTLS doesn't support this device: "
"vendorID = 0x%x, deviceID = 0x%x",
vendorid, deviceid);
break;
case RTLS_SUPPORT_DEVICE_1:
case RTLS_SUPPORT_DEVICE_2:
case RTLS_SUPPORT_DEVICE_3:
case RTLS_SUPPORT_DEVICE_4:
break;
}
pci_commond = pci_config_get16(pci_handle, PCI_CONF_COMM);
pci_commond &= ~PCI_COMM_MEMWR_INVAL;
pci_commond |= PCI_COMM_ME | PCI_COMM_MAE | PCI_COMM_IO;
pci_config_put32(pci_handle, PCI_CONF_COMM, pci_commond);
pci_config_teardown(&pci_handle);
rtlsp = kmem_zalloc(sizeof (rtls_t), KM_SLEEP);
ddi_set_driver_private(devinfo, rtlsp);
rtlsp->devinfo = devinfo;
rtlsp->instance = ddi_get_instance(devinfo);
err = ddi_regs_map_setup(devinfo, 1, &rtlsp->io_reg,
(offset_t)0, 0, &rtls_reg_accattr, &rtlsp->io_handle);
if (err != DDI_SUCCESS) {
kmem_free((caddr_t)rtlsp, sizeof (rtls_t));
cmn_err(CE_WARN, "ddi_regs_map_setup fail.");
return (DDI_FAILURE);
}
if (rtls_alloc_bufs(rtlsp) == DDI_FAILURE) {
cmn_err(CE_WARN, "DMA buffer allocation fail.");
goto fail;
}
err = rtls_chip_reset(rtlsp, B_FALSE);
if (err != DDI_SUCCESS)
goto fail;
rtls_get_mac_addr(rtlsp, rtlsp->netaddr);
if (ddi_add_intr(devinfo, 0, &rtlsp->iblk, NULL, rtls_intr,
(caddr_t)rtlsp) != DDI_SUCCESS) {
cmn_err(CE_WARN, "ddi_add_intr fail.");
goto late_fail;
}
if ((rtlsp->mii = mii_alloc(rtlsp, devinfo, &rtls_mii_ops)) == NULL) {
ddi_remove_intr(devinfo, 0, rtlsp->iblk);
goto late_fail;
}
mii_set_pauseable(rtlsp->mii, B_FALSE, B_FALSE);
if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
cmn_err(CE_WARN, "mac_alloc fail.");
ddi_remove_intr(devinfo, 0, rtlsp->iblk);
goto late_fail;
}
mutex_init(&rtlsp->rtls_io_lock, NULL, MUTEX_DRIVER, rtlsp->iblk);
mutex_init(&rtlsp->rtls_tx_lock, NULL, MUTEX_DRIVER, rtlsp->iblk);
mutex_init(&rtlsp->rtls_rx_lock, NULL, MUTEX_DRIVER, rtlsp->iblk);
macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
macp->m_driver = rtlsp;
macp->m_dip = devinfo;
macp->m_src_addr = rtlsp->netaddr;
macp->m_callbacks = &rtls_m_callbacks;
macp->m_min_sdu = 0;
macp->m_max_sdu = ETHERMTU;
macp->m_margin = VLAN_TAGSZ;
if (mac_register(macp, &rtlsp->mh) != 0) {
ddi_remove_intr(devinfo, 0, rtlsp->iblk);
mutex_destroy(&rtlsp->rtls_io_lock);
mutex_destroy(&rtlsp->rtls_tx_lock);
mutex_destroy(&rtlsp->rtls_rx_lock);
goto late_fail;
}
mac_free(macp);
return (DDI_SUCCESS);
late_fail:
if (macp)
mac_free(macp);
if (rtlsp->mii)
mii_free(rtlsp->mii);
fail:
ddi_regs_map_free(&rtlsp->io_handle);
rtls_free_bufs(rtlsp);
kmem_free(rtlsp, sizeof (rtls_t));
return (DDI_FAILURE);
}
static int
rtls_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
{
rtls_t *rtlsp;
if ((rtlsp = ddi_get_driver_private(devinfo)) == NULL) {
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
mii_suspend(rtlsp->mii);
mutex_enter(&rtlsp->rtls_io_lock);
mutex_enter(&rtlsp->rtls_rx_lock);
mutex_enter(&rtlsp->rtls_tx_lock);
rtlsp->rtls_suspended = B_TRUE;
rtls_chip_stop(rtlsp);
mutex_exit(&rtlsp->rtls_tx_lock);
mutex_exit(&rtlsp->rtls_rx_lock);
mutex_exit(&rtlsp->rtls_io_lock);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (mac_unregister(rtlsp->mh) != 0) {
return (DDI_FAILURE);
}
ddi_remove_intr(devinfo, 0, rtlsp->iblk);
mii_free(rtlsp->mii);
mutex_destroy(&rtlsp->rtls_io_lock);
mutex_destroy(&rtlsp->rtls_tx_lock);
mutex_destroy(&rtlsp->rtls_rx_lock);
ddi_regs_map_free(&rtlsp->io_handle);
rtls_free_bufs(rtlsp);
kmem_free(rtlsp, sizeof (rtls_t));
return (DDI_SUCCESS);
}
static int
rtls_quiesce(dev_info_t *devinfo)
{
rtls_t *rtlsp;
if ((rtlsp = ddi_get_driver_private(devinfo)) == NULL) {
return (DDI_FAILURE);
}
return (rtls_chip_reset(rtlsp, B_TRUE));
}
static int
rtls_m_start(void *arg)
{
rtls_t *rtlsp = (rtls_t *)arg;
mutex_enter(&rtlsp->rtls_io_lock);
mutex_enter(&rtlsp->rtls_rx_lock);
mutex_enter(&rtlsp->rtls_tx_lock);
if (!rtlsp->rtls_suspended)
rtls_chip_restart(rtlsp);
rtlsp->rtls_running = B_TRUE;
mutex_exit(&rtlsp->rtls_tx_lock);
mutex_exit(&rtlsp->rtls_rx_lock);
mutex_exit(&rtlsp->rtls_io_lock);
drv_usecwait(100);
mii_start(rtlsp->mii);
return (0);
}
static void
rtls_m_stop(void *arg)
{
rtls_t *rtlsp = (rtls_t *)arg;
mii_stop(rtlsp->mii);
mutex_enter(&rtlsp->rtls_io_lock);
if (!rtlsp->rtls_suspended)
rtls_chip_stop(rtlsp);
rtlsp->rtls_running = B_FALSE;
mutex_exit(&rtlsp->rtls_io_lock);
}
static int
rtls_m_unicst(void *arg, const uint8_t *macaddr)
{
rtls_t *rtlsp = arg;
mutex_enter(&rtlsp->rtls_io_lock);
bcopy(macaddr, rtlsp->netaddr, ETHERADDRL);
if (!rtlsp->rtls_suspended)
rtls_set_mac_addr(rtlsp, rtlsp->netaddr);
mutex_exit(&rtlsp->rtls_io_lock);
return (0);
}
static int
rtls_m_multicst(void *arg, boolean_t enable, const uint8_t *mcast)
{
rtls_t *rtlsp = (rtls_t *)arg;
uint_t index;
uint32_t *hashp;
mutex_enter(&rtlsp->rtls_io_lock);
hashp = rtlsp->multi_hash;
index = rtls_hash_index(mcast);
if (enable) {
if (rtlsp->multicast_cnt[index]++) {
mutex_exit(&rtlsp->rtls_io_lock);
return (0);
}
hashp[index/32] |= 1<< (index % 32);
} else {
if (--rtlsp->multicast_cnt[index]) {
mutex_exit(&rtlsp->rtls_io_lock);
return (0);
}
hashp[index/32] &= ~(1<< (index % 32));
}
if (!rtlsp->rtls_suspended) {
rtls_reg_set32(rtlsp, MULTICAST_0_REG, hashp[0]);
rtls_reg_set32(rtlsp, MULTICAST_4_REG, hashp[1]);
}
mutex_exit(&rtlsp->rtls_io_lock);
return (0);
}
static uint_t
rtls_hash_index(const uint8_t *address)
{
uint32_t crc = (ulong_t)RTLS_HASH_CRC;
uint32_t const POLY = RTLS_HASH_POLY;
uint32_t msb;
int bytes;
uchar_t currentbyte;
uint_t index;
int bit;
for (bytes = 0; bytes < ETHERADDRL; bytes++) {
currentbyte = address[bytes];
for (bit = 0; bit < 8; bit++) {
msb = crc >> 31;
crc <<= 1;
if (msb ^ (currentbyte & 1)) {
crc ^= POLY;
crc |= 0x00000001;
}
currentbyte >>= 1;
}
}
index = crc >> 26;
return (index);
}
static int
rtls_m_promisc(void *arg, boolean_t on)
{
rtls_t *rtlsp = arg;
mutex_enter(&rtlsp->rtls_io_lock);
rtlsp->promisc = on;
if (!rtlsp->rtls_suspended) {
uint32_t val32 = rtls_reg_get32(rtlsp, RX_CONFIG_REG);
if (on) {
val32 |= RX_ACCEPT_ALL_PACKET;
} else {
val32 &= ~RX_ACCEPT_ALL_PACKET;
}
rtls_reg_set32(rtlsp, RX_CONFIG_REG, val32);
}
mutex_exit(&rtlsp->rtls_io_lock);
return (0);
}
static int
rtls_m_stat(void *arg, uint_t stat, uint64_t *val)
{
rtls_t *rtlsp = arg;
if (mii_m_getstat(rtlsp->mii, stat, val) == 0) {
return (0);
}
switch (stat) {
case MAC_STAT_IPACKETS:
*val = rtlsp->stats.ipackets;
break;
case MAC_STAT_RBYTES:
*val = rtlsp->stats.rbytes;
break;
case MAC_STAT_OPACKETS:
*val = rtlsp->stats.opackets;
break;
case MAC_STAT_OBYTES:
*val = rtlsp->stats.obytes;
break;
case MAC_STAT_IERRORS:
*val = rtlsp->stats.rcv_err;
break;
case MAC_STAT_OERRORS:
*val = rtlsp->stats.xmt_err;
break;
case MAC_STAT_MULTIRCV:
*val = rtlsp->stats.multi_rcv;
break;
case MAC_STAT_BRDCSTRCV:
*val = rtlsp->stats.brdcst_rcv;
break;
case MAC_STAT_MULTIXMT:
*val = rtlsp->stats.multi_xmt;
break;
case MAC_STAT_BRDCSTXMT:
*val = rtlsp->stats.brdcst_xmt;
break;
case MAC_STAT_UNDERFLOWS:
*val = rtlsp->stats.underflow;
break;
case MAC_STAT_OVERFLOWS:
*val = rtlsp->stats.overflow;
break;
case MAC_STAT_NORCVBUF:
*val = rtlsp->stats.no_rcvbuf;
break;
case MAC_STAT_COLLISIONS:
*val = rtlsp->stats.collisions;
break;
case ETHER_STAT_FCS_ERRORS:
*val = rtlsp->stats.crc_err;
break;
case ETHER_STAT_ALIGN_ERRORS:
*val = rtlsp->stats.frame_err;
break;
case ETHER_STAT_DEFER_XMTS:
*val = rtlsp->stats.defer;
break;
case ETHER_STAT_TX_LATE_COLLISIONS:
*val = rtlsp->stats.xmt_latecoll;
break;
case ETHER_STAT_TOOLONG_ERRORS:
*val = rtlsp->stats.too_long;
break;
case ETHER_STAT_TOOSHORT_ERRORS:
*val = rtlsp->stats.in_short;
break;
case ETHER_STAT_CARRIER_ERRORS:
*val = rtlsp->stats.no_carrier;
break;
case ETHER_STAT_FIRST_COLLISIONS:
*val = rtlsp->stats.firstcol;
break;
case ETHER_STAT_MULTI_COLLISIONS:
*val = rtlsp->stats.multicol;
break;
default:
return (ENOTSUP);
}
#ifdef RTLS_DEBUG
if (rtls_debug & RTLS_TRACE)
rtls_reg_print(rtlsp);
#endif
return (0);
}
int
rtls_m_getprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
void *val)
{
rtls_t *rtlsp = arg;
return (mii_m_getprop(rtlsp->mii, name, num, sz, val));
}
int
rtls_m_setprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
const void *val)
{
rtls_t *rtlsp = arg;
return (mii_m_setprop(rtlsp->mii, name, num, sz, val));
}
static void
rtls_m_propinfo(void *arg, const char *name, mac_prop_id_t num,
mac_prop_info_handle_t prh)
{
rtls_t *rtlsp = arg;
mii_m_propinfo(rtlsp->mii, name, num, prh);
}
static boolean_t
rtls_send(rtls_t *rtlsp, mblk_t *mp)
{
int totlen;
int ncc;
uint16_t cur_desc;
uint32_t tx_status;
ASSERT(mp != NULL);
ASSERT(rtlsp->rtls_running);
mutex_enter(&rtlsp->rtls_tx_lock);
if (rtlsp->rtls_suspended) {
mutex_exit(&rtlsp->rtls_tx_lock);
return (B_FALSE);
}
if (rtlsp->chip_error) {
#ifdef RTLS_DEBUG
cmn_err(CE_WARN,
"%s: send fail--CHIP ERROR!",
mac_name(rtlsp->mh));
#endif
mutex_exit(&rtlsp->rtls_tx_lock);
freemsg(mp);
return (B_TRUE);
}
if (rtls_reg_get8(rtlsp, MEDIA_STATUS_REG) & MEDIA_STATUS_LINK) {
#ifdef RTLS_DEBUG
cmn_err(CE_WARN,
"%s: send fail--LINK DOWN!",
mac_name(rtlsp->mh));
#endif
rtlsp->stats.no_carrier++;
mutex_exit(&rtlsp->rtls_tx_lock);
freemsg(mp);
return (B_TRUE);
}
cur_desc = rtlsp->tx_current_desc;
ASSERT(cur_desc < RTLS_MAX_TX_DESC);
if (rtlsp->tx_first_loop < RTLS_MAX_TX_DESC) {
rtlsp->tx_first_loop++;
goto tx_ready;
}
tx_status = rtls_reg_get32(rtlsp, TX_STATUS_DESC0_REG + 4 * cur_desc);
if (!(tx_status & TX_COMPLETE_FLAG)) {
#ifdef RTLS_DEBUG
if (rtls_debug & RTLS_SEND) {
cmn_err(CE_NOTE,
"%s: rtls_send: need_sched", mac_name(rtlsp->mh));
}
#endif
if (rtlsp->tx_retry++ > RTLS_TX_RETRY_NUM) {
RTLS_TX_WAIT_TIMEOUT;
tx_status = rtls_reg_get32(rtlsp,
TX_STATUS_DESC0_REG + 4 * cur_desc);
if (!(tx_status & TX_COMPLETE_FLAG)) {
#ifdef RTLS_DEBUG
cmn_err(CE_NOTE, "%s: tx chip_error = 0x%x",
mac_name(rtlsp->mh), tx_status);
#endif
rtlsp->tx_retry = 0;
rtlsp->chip_error = B_TRUE;
rtlsp->stats.xmt_err++;
rtlsp->stats.mac_xmt_err++;
mutex_exit(&rtlsp->rtls_tx_lock);
freemsg(mp);
return (B_TRUE);
}
} else {
rtlsp->stats.defer++;
rtlsp->need_sched = B_TRUE;
mutex_exit(&rtlsp->rtls_tx_lock);
return (B_FALSE);
}
}
if (tx_status & TX_ERR_FLAG) {
#ifdef RTLS_DEBUG
if (rtls_debug & RTLS_SEND) {
cmn_err(CE_NOTE, "%s: transmit error, status = 0x%x",
mac_name(rtlsp->mh), tx_status);
}
#endif
rtlsp->stats.xmt_err++;
if (tx_status & TX_STATUS_TX_UNDERRUN)
rtlsp->stats.underflow++;
if (tx_status & TX_STATUS_CS_LOST)
rtlsp->stats.no_carrier++;
if (tx_status & TX_STATUS_OWC)
rtlsp->stats.xmt_latecoll++;
}
ncc = ((tx_status & TX_STATUS_NCC) >> TX_STATUS_NCC_SHIFT);
if (ncc != 0) {
rtlsp->stats.collisions += ncc;
rtlsp->stats.firstcol++;
rtlsp->stats.multicol += ncc - 1;
}
tx_ready:
rtlsp->tx_retry = 0;
totlen = 0;
totlen = msgsize(mp);
if (totlen > (ETHERMAX + 4)) {
cmn_err(CE_NOTE,
"%s: rtls_send: try to send large %d packet",
mac_name(rtlsp->mh), totlen);
rtlsp->stats.mac_xmt_err++;
rtlsp->stats.xmt_err++;
freemsg(mp);
mutex_exit(&rtlsp->rtls_tx_lock);
return (B_TRUE);
}
mcopymsg(mp, rtlsp->tx_buf[cur_desc]);
if (*rtlsp->tx_buf[cur_desc] & 0x1) {
uint16_t *ptr = (void *)rtlsp->tx_buf[cur_desc];
if ((ptr[0] == 0xffff) &&
(ptr[1] == 0xffff) &&
(ptr[2] == 0xffff)) {
rtlsp->stats.brdcst_xmt++;
} else {
rtlsp->stats.multi_xmt++;
}
}
rtlsp->stats.opackets++;
rtlsp->stats.obytes += totlen;
if (totlen < ETHERMIN) {
bzero(rtlsp->tx_buf[cur_desc] + totlen, ETHERMIN - totlen);
totlen = ETHERMIN;
}
(void) ddi_dma_sync(rtlsp->dma_area_tx[cur_desc].dma_hdl, 0, totlen,
DDI_DMA_SYNC_FORDEV);
rtls_reg_set32(rtlsp, TX_STATUS_DESC0_REG + 4 * cur_desc,
totlen | (0x30 << TX_STATUS_TX_THRESHOLD_SHIFT));
cur_desc++;
cur_desc %= RTLS_MAX_TX_DESC;
rtlsp->tx_current_desc = cur_desc;
mutex_exit(&rtlsp->rtls_tx_lock);
return (B_TRUE);
}
static mblk_t *
rtls_m_tx(void *arg, mblk_t *mp)
{
rtls_t *rtlsp = arg;
mblk_t *next;
while (mp != NULL) {
next = mp->b_next;
mp->b_next = NULL;
if (!rtls_send(rtlsp, mp)) {
mp->b_next = next;
break;
}
mp = next;
}
return (mp);
}
static void
rtls_receive(rtls_t *rtlsp)
{
mblk_t *head = NULL;
mblk_t **mpp;
mblk_t *mp;
uint16_t rx_status;
uint16_t packet_len;
int wrap_size;
uint32_t cur_rx;
uint8_t *rx_ptr;
mpp = &head;
mutex_enter(&rtlsp->rtls_rx_lock);
if (rtlsp->rtls_suspended) {
mutex_exit(&rtlsp->rtls_rx_lock);
return;
}
while ((rtls_reg_get8(rtlsp, RT_COMMAND_REG)
& RT_COMMAND_BUFF_EMPTY) == 0) {
if (rtlsp->chip_error) {
#ifdef RTLS_DEBUG
cmn_err(CE_WARN,
"%s: receive fail--CHIP ERROR!",
mac_name(rtlsp->mh));
#endif
break;
}
cur_rx = rtlsp->cur_rx;
rx_ptr = rtlsp->rx_ring + cur_rx;
packet_len = (rx_ptr[3] << 8) | (rx_ptr[2]);
rx_status = rx_ptr[0];
if (packet_len == RX_STATUS_DMA_BUSY) {
cmn_err(CE_NOTE, "%s: Rx DMA still in progress",
mac_name(rtlsp->mh));
break;
}
if ((rx_status & RX_ERR_FLAGS) ||
(!(rx_status & RX_HEADER_STATUS_ROK)) ||
(packet_len < (ETHERMIN + ETHERFCSL)) ||
(packet_len > (ETHERMAX + ETHERFCSL + 4))) {
#ifdef RTLS_DEBUG
cmn_err(CE_NOTE,
"%s: receive error, status = 0x%x, length = %d",
mac_name(rtlsp->mh), rx_status, packet_len);
#endif
if ((rx_status & RX_HEADER_STATUS_RUNT) ||
(packet_len < (ETHERMIN + ETHERFCSL)))
rtlsp->stats.in_short++;
else if (packet_len > (ETHERMAX + ETHERFCSL + 4))
rtlsp->stats.too_long++;
else if (rx_status & RX_HEADER_STATUS_CRC)
rtlsp->stats.crc_err++;
else if (rx_status & RX_HEADER_STATUS_FAE)
rtlsp->stats.frame_err++;
rtlsp->chip_error = B_TRUE;
mutex_exit(&rtlsp->rtls_rx_lock);
return;
}
packet_len -= ETHERFCSL;
mp = allocb(packet_len, 0);
if ((cur_rx + packet_len + RX_HEADER_SIZE)
> RTLS_RX_BUF_RING) {
wrap_size = cur_rx + packet_len + RX_HEADER_SIZE
- RTLS_RX_BUF_RING;
#ifdef RTLS_DEBUG
if (rtls_debug & RTLS_RECV) {
cmn_err(CE_NOTE,
"%s: Rx: packet_len = %d, wrap_size = %d",
mac_name(rtlsp->mh), packet_len, wrap_size);
}
#endif
if (mp != NULL) {
(void) ddi_dma_sync(rtlsp->dma_area_rx.dma_hdl,
cur_rx + RX_HEADER_SIZE,
packet_len - wrap_size,
DDI_DMA_SYNC_FORKERNEL);
(void) ddi_dma_sync(rtlsp->dma_area_rx.dma_hdl,
0, wrap_size,
DDI_DMA_SYNC_FORKERNEL);
bcopy(rx_ptr + RX_HEADER_SIZE,
mp->b_wptr, packet_len - wrap_size);
mp->b_wptr += packet_len - wrap_size;
bcopy(rtlsp->rx_ring, mp->b_wptr, wrap_size);
mp->b_wptr += wrap_size;
*mpp = mp;
mpp = &mp->b_next;
rtlsp->stats.ipackets++;
if (rx_status & RX_HEADER_STATUS_BCAST)
rtlsp->stats.brdcst_rcv++;
if (rx_status & RX_HEADER_STATUS_MULTI)
rtlsp->stats.multi_rcv++;
rtlsp->stats.rbytes += packet_len;
} else {
rtlsp->stats.no_rcvbuf++;
}
cur_rx = RTLS_RX_ADDR_ALIGNED(wrap_size + ETHERFCSL);
} else {
if (mp != NULL) {
(void) ddi_dma_sync(rtlsp->dma_area_rx.dma_hdl,
cur_rx + RX_HEADER_SIZE, packet_len,
DDI_DMA_SYNC_FORKERNEL);
bcopy(rx_ptr + RX_HEADER_SIZE, mp->b_wptr,
packet_len);
mp->b_wptr += packet_len;
*mpp = mp;
mpp = &mp->b_next;
rtlsp->stats.ipackets++;
if (rx_status & RX_HEADER_STATUS_BCAST)
rtlsp->stats.brdcst_rcv++;
if (rx_status & RX_HEADER_STATUS_MULTI)
rtlsp->stats.multi_rcv++;
rtlsp->stats.rbytes += packet_len;
} else {
rtlsp->stats.no_rcvbuf++;
}
cur_rx += packet_len + RX_HEADER_SIZE + ETHERFCSL;
cur_rx = RTLS_RX_ADDR_ALIGNED(cur_rx);
}
rtlsp->cur_rx = cur_rx;
rtls_reg_set16(rtlsp, RX_CURRENT_READ_ADDR_REG,
cur_rx - READ_ADDR_GAP);
}
mutex_exit(&rtlsp->rtls_rx_lock);
if (head) {
mac_rx(rtlsp->mh, NULL, head);
}
}
static uint_t
rtls_intr(caddr_t arg)
{
rtls_t *rtlsp = (void *)arg;
uint32_t int_status;
uint32_t val32;
boolean_t resched = B_FALSE;
mutex_enter(&rtlsp->rtls_io_lock);
if (rtlsp->rtls_suspended) {
mutex_exit(&rtlsp->rtls_io_lock);
return (DDI_INTR_UNCLAIMED);
}
int_status = rtls_reg_get16(rtlsp, RT_INT_STATUS_REG);
if (!(int_status & rtlsp->int_mask)) {
mutex_exit(&rtlsp->rtls_io_lock);
return (DDI_INTR_UNCLAIMED);
}
rtls_reg_set16(rtlsp, RT_INT_STATUS_REG, int_status);
if (rtlsp->chip_error) {
mutex_enter(&rtlsp->rtls_rx_lock);
mutex_enter(&rtlsp->rtls_tx_lock);
rtls_chip_restart(rtlsp);
rtlsp->chip_error = B_FALSE;
rtlsp->tx_retry = 0;
mutex_exit(&rtlsp->rtls_tx_lock);
mutex_exit(&rtlsp->rtls_rx_lock);
mutex_exit(&rtlsp->rtls_io_lock);
return (DDI_INTR_CLAIMED);
}
if (int_status & TX_ERR_INT) {
val32 = rtls_reg_get32(rtlsp, TX_CONFIG_REG);
val32 |= TX_CLEAR_ABORT;
rtls_reg_set32(rtlsp, TX_CONFIG_REG, val32);
cmn_err(CE_WARN, "%s: transmit abort!!!", mac_name(rtlsp->mh));
}
if (rtlsp->need_sched) {
rtlsp->need_sched = B_FALSE;
resched = B_TRUE;
}
mutex_exit(&rtlsp->rtls_io_lock);
if (int_status & RTLS_RX_INT) {
if (int_status & RX_OVERFLOW_INT) {
rtlsp->stats.overflow++;
rtlsp->stats.rcv_err++;
}
rtls_receive(rtlsp);
}
if (int_status & LINK_CHANGE_INT) {
mii_check(rtlsp->mii);
}
if (resched) {
mac_tx_update(rtlsp->mh);
}
return (DDI_INTR_CLAIMED);
}
static int
rtls_alloc_dma_mem(rtls_t *rtlsp, size_t memsize,
ddi_device_acc_attr_t *attr_p, uint_t dma_flags, dma_area_t *dma_p)
{
caddr_t vaddr;
int err;
err = ddi_dma_alloc_handle(rtlsp->devinfo, &dma_attr,
DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl);
if (err != DDI_SUCCESS) {
cmn_err(CE_WARN,
"%s: rtls_alloc_dma_mem: ddi_dma_alloc_handle failed: %d",
mac_name(rtlsp->mh), err);
dma_p->dma_hdl = NULL;
return (DDI_FAILURE);
}
err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, attr_p,
dma_flags & (DDI_DMA_CONSISTENT | DDI_DMA_STREAMING),
DDI_DMA_SLEEP, NULL, &vaddr, &dma_p->alength, &dma_p->acc_hdl);
if (err != DDI_SUCCESS) {
cmn_err(CE_WARN,
"%s: rtls_alloc_dma_mem: ddi_dma_mem_alloc failed: %d",
mac_name(rtlsp->mh), err);
ddi_dma_free_handle(&dma_p->dma_hdl);
dma_p->dma_hdl = NULL;
dma_p->acc_hdl = NULL;
return (DDI_FAILURE);
}
dma_p->mem_va = vaddr;
err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL,
vaddr, dma_p->alength, dma_flags, DDI_DMA_SLEEP, NULL,
&dma_p->cookie, &dma_p->ncookies);
if (err != DDI_DMA_MAPPED || dma_p->ncookies != 1) {
cmn_err(CE_WARN,
"%s: rtls_alloc_dma_mem: "
"ddi_dma_addr_bind_handle failed: %d",
mac_name(rtlsp->mh), err);
ddi_dma_mem_free(&dma_p->acc_hdl);
ddi_dma_free_handle(&dma_p->dma_hdl);
dma_p->acc_hdl = NULL;
dma_p->dma_hdl = NULL;
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
rtls_free_dma_mem(dma_area_t *dma_p)
{
if (dma_p->dma_hdl != NULL) {
if (dma_p->ncookies) {
(void) ddi_dma_unbind_handle(dma_p->dma_hdl);
dma_p->ncookies = 0;
}
ddi_dma_free_handle(&dma_p->dma_hdl);
dma_p->dma_hdl = NULL;
}
if (dma_p->acc_hdl != NULL) {
ddi_dma_mem_free(&dma_p->acc_hdl);
dma_p->acc_hdl = NULL;
}
}
static int
rtls_alloc_bufs(rtls_t *rtlsp)
{
int i;
int err;
for (i = 0; i < RTLS_MAX_TX_DESC; i++) {
err = rtls_alloc_dma_mem(rtlsp,
RTLS_TX_BUF_SIZE,
&rtls_buf_accattr,
DDI_DMA_WRITE | DDI_DMA_STREAMING,
&rtlsp->dma_area_tx[i]);
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
rtlsp->tx_buf[i] = (uint8_t *)rtlsp->dma_area_tx[i].mem_va;
}
err = rtls_alloc_dma_mem(rtlsp,
RTLS_RX_BUF_SIZE,
&rtls_buf_accattr,
DDI_DMA_READ | DDI_DMA_STREAMING,
&rtlsp->dma_area_rx);
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
rtlsp->rx_ring = (uint8_t *)rtlsp->dma_area_rx.mem_va;
return (DDI_SUCCESS);
}
static void
rtls_free_bufs(rtls_t *rtlsp)
{
int i;
for (i = 0; i < RTLS_MAX_TX_DESC; i++) {
rtls_free_dma_mem(&rtlsp->dma_area_tx[i]);
rtlsp->tx_buf[i] = NULL;
}
rtls_free_dma_mem(&rtlsp->dma_area_rx);
rtlsp->rx_ring = NULL;
}
static int
rtls_chip_reset(rtls_t *rtlsp, boolean_t quiesce)
{
int i;
uint16_t val16;
uint8_t val8;
val8 = rtls_reg_get8(rtlsp, RT_COMMAND_REG);
val8 &= ~(RT_COMMAND_RX_ENABLE | RT_COMMAND_TX_ENABLE);
rtls_reg_set8(rtlsp, RT_COMMAND_REG, val8);
val16 = rtls_reg_get16(rtlsp, RT_INT_MASK_REG);
rtls_reg_set16(rtlsp, RT_INT_MASK_REG, val16 & (~RTLS_INT_MASK_ALL));
rtlsp->int_mask = RTLS_INT_MASK_NONE;
val16 = rtls_reg_get16(rtlsp, RT_INT_STATUS_REG);
rtls_reg_set16(rtlsp, RT_INT_STATUS_REG, val16);
val8 = rtls_reg_get8(rtlsp, RT_COMMAND_REG);
rtls_reg_set8(rtlsp, RT_COMMAND_REG, val8 | RT_COMMAND_RESET);
i = 0;
while (rtls_reg_get8(rtlsp, RT_COMMAND_REG) & RT_COMMAND_RESET) {
if (++i > RTLS_RESET_WAIT_NUM) {
if (!quiesce)
cmn_err(CE_WARN,
"%s: chip reset fail.",
mac_name(rtlsp->mh));
return (DDI_FAILURE);
}
RTLS_RESET_WAIT_INTERVAL;
}
return (DDI_SUCCESS);
}
static void
rtls_chip_init(rtls_t *rtlsp)
{
uint32_t val32;
uint16_t val16;
uint8_t val8;
rtlsp->cur_rx = 0;
rtlsp->tx_current_desc = 0;
rtlsp->tx_first_loop = 0;
rtls_reg_set32(rtlsp, RX_BUFF_ADDR_REG,
(ulong_t)rtlsp->dma_area_rx.cookie.dmac_address);
rtls_reg_set32(rtlsp, TX_ADDR_DESC0_REG,
(ulong_t)rtlsp->dma_area_tx[0].cookie.dmac_address);
rtls_reg_set32(rtlsp, TX_ADDR_DESC1_REG,
(ulong_t)rtlsp->dma_area_tx[1].cookie.dmac_address);
rtls_reg_set32(rtlsp, TX_ADDR_DESC2_REG,
(ulong_t)rtlsp->dma_area_tx[2].cookie.dmac_address);
rtls_reg_set32(rtlsp, TX_ADDR_DESC3_REG,
(ulong_t)rtlsp->dma_area_tx[3].cookie.dmac_address);
val8 = rtls_reg_get8(rtlsp, RT_COMMAND_REG);
rtls_reg_set8(rtlsp, RT_COMMAND_REG,
val8 | RT_COMMAND_RX_ENABLE | RT_COMMAND_TX_ENABLE);
val32 = rtls_reg_get32(rtlsp, TX_CONFIG_REG);
val32 &= TX_CONSIG_REG_RESERVE;
rtls_reg_set32(rtlsp, TX_CONFIG_REG, val32 | TX_CONFIG_DEFAULT);
val32 = rtls_reg_get32(rtlsp, RX_CONFIG_REG);
val32 &= RX_CONSIG_REG_RESERVE;
if (rtlsp->promisc)
val32 |= RX_ACCEPT_ALL_PACKET;
rtls_reg_set32(rtlsp, RX_CONFIG_REG, val32 | RX_CONFIG_DEFAULT);
rtls_reg_set32(rtlsp, MULTICAST_0_REG, rtlsp->multi_hash[0]);
rtls_reg_set32(rtlsp, MULTICAST_4_REG, rtlsp->multi_hash[1]);
rtls_set_mac_addr(rtlsp, rtlsp->netaddr);
rtls_reg_set16(rtlsp, RX_CURRENT_READ_ADDR_REG, RX_READ_RESET_VAL);
val16 = rtls_reg_get16(rtlsp, RT_MUL_INTSEL_REG);
val16 &= ~RT_MUL_INTSEL_BITS;
rtls_reg_set16(rtlsp, RT_MUL_INTSEL_REG, val16);
}
static void
rtls_chip_start(rtls_t *rtlsp)
{
uint16_t val16;
uint8_t val8;
val8 = rtls_reg_get8(rtlsp, RT_COMMAND_REG);
rtls_reg_set8(rtlsp, RT_COMMAND_REG,
val8 | RT_COMMAND_RX_ENABLE | RT_COMMAND_TX_ENABLE);
val16 = rtls_reg_get16(rtlsp, RT_INT_MASK_REG);
rtls_reg_set16(rtlsp, RT_INT_MASK_REG, val16 | RTLS_INT_MASK);
rtlsp->int_mask = RTLS_INT_MASK;
}
static void
rtls_chip_restart(rtls_t *rtlsp)
{
(void) rtls_chip_reset(rtlsp, B_FALSE);
rtls_chip_init(rtlsp);
rtls_chip_start(rtlsp);
}
static void
rtls_chip_stop(rtls_t *rtlsp)
{
uint16_t val16;
uint8_t val8;
val16 = rtls_reg_get16(rtlsp, RT_INT_MASK_REG);
rtls_reg_set16(rtlsp, RT_INT_MASK_REG, val16 & (~RTLS_INT_MASK_ALL));
rtlsp->int_mask = RTLS_INT_MASK_NONE;
val16 = rtls_reg_get16(rtlsp, RT_INT_STATUS_REG);
rtls_reg_set16(rtlsp, RT_INT_STATUS_REG, val16);
val8 = rtls_reg_get8(rtlsp, RT_COMMAND_REG);
val8 &= ~(RT_COMMAND_RX_ENABLE | RT_COMMAND_TX_ENABLE);
rtls_reg_set8(rtlsp, RT_COMMAND_REG, val8);
}
static void
rtls_get_mac_addr(rtls_t *rtlsp, uint8_t *macaddr)
{
uint32_t val32;
val32 = rtls_reg_get32(rtlsp, ID_0_REG);
macaddr[0] = val32 & 0xff;
val32 = val32 >> 8;
macaddr[1] = val32 & 0xff;
val32 = val32 >> 8;
macaddr[2] = val32 & 0xff;
val32 = val32 >> 8;
macaddr[3] = val32 & 0xff;
val32 = rtls_reg_get32(rtlsp, ID_4_REG);
macaddr[4] = val32 & 0xff;
val32 = val32 >> 8;
macaddr[5] = val32 & 0xff;
}
static void
rtls_set_mac_addr(rtls_t *rtlsp, const uint8_t *macaddr)
{
uint32_t val32;
uint8_t val8;
val8 = rtls_reg_get8(rtlsp, RT_93c46_COMMAND_REG);
val8 |= RT_93c46_MODE_CONFIG;
rtls_reg_set8(rtlsp, RT_93c46_COMMAND_REG, val8);
val32 = macaddr[3];
val32 = val32 << 8;
val32 |= macaddr[2];
val32 = val32 << 8;
val32 |= macaddr[1];
val32 = val32 << 8;
val32 |= macaddr[0];
rtls_reg_set32(rtlsp, ID_0_REG, val32);
val32 = macaddr[5];
val32 = val32 << 8;
val32 |= macaddr[4];
val32 |= rtls_reg_get32(rtlsp, ID_4_REG) & ~0xffff;
rtls_reg_set32(rtlsp, ID_4_REG, val32);
val8 &= ~RT_93c46_MODE_CONFIG;
rtls_reg_set8(rtlsp, RT_93c46_COMMAND_REG, val8);
}
static uint16_t
rtls_mii_read(void *arg, uint8_t phy, uint8_t reg)
{
rtls_t *rtlsp = arg;
uint16_t val;
if (phy != 1) {
return (0xffff);
}
switch (reg) {
case MII_CONTROL:
val = rtls_reg_get16(rtlsp, BASIC_MODE_CONTROL_REG);
break;
case MII_STATUS:
val = rtls_reg_get16(rtlsp, BASIC_MODE_STATUS_REG);
break;
case MII_AN_ADVERT:
val = rtls_reg_get16(rtlsp, AUTO_NEGO_AD_REG);
break;
case MII_AN_LPABLE:
val = rtls_reg_get16(rtlsp, AUTO_NEGO_LP_REG);
break;
case MII_AN_EXPANSION:
val = rtls_reg_get16(rtlsp, AUTO_NEGO_EXP_REG);
break;
case MII_VENDOR(0):
val = rtls_reg_get8(rtlsp, MEDIA_STATUS_REG);
break;
case MII_PHYIDH:
case MII_PHYIDL:
default:
val = 0;
break;
}
return (val);
}
void
rtls_mii_write(void *arg, uint8_t phy, uint8_t reg, uint16_t val)
{
rtls_t *rtlsp = arg;
uint8_t val8;
if (phy != 1) {
return;
}
switch (reg) {
case MII_CONTROL:
val8 = rtls_reg_get8(rtlsp, RT_93c46_COMMAND_REG);
val8 |= RT_93c46_MODE_CONFIG;
rtls_reg_set8(rtlsp, RT_93c46_COMMAND_REG, val8);
rtls_reg_set16(rtlsp, BASIC_MODE_CONTROL_REG, val);
val8 &= ~RT_93c46_MODE_CONFIG;
rtls_reg_set8(rtlsp, RT_93c46_COMMAND_REG, val8);
return;
case MII_STATUS:
rtls_reg_set16(rtlsp, BASIC_MODE_STATUS_REG, val);
break;
case MII_AN_ADVERT:
rtls_reg_set16(rtlsp, AUTO_NEGO_AD_REG, val);
break;
case MII_AN_LPABLE:
rtls_reg_set16(rtlsp, AUTO_NEGO_LP_REG, val);
break;
case MII_AN_EXPANSION:
rtls_reg_set16(rtlsp, AUTO_NEGO_EXP_REG, val);
break;
case MII_PHYIDH:
case MII_PHYIDL:
default:
break;
}
}
void
rtls_mii_notify(void *arg, link_state_t link)
{
rtls_t *rtlsp = arg;
mac_link_update(rtlsp->mh, link);
}
#ifdef RTLS_DEBUG
static void
rtls_reg_print(rtls_t *rtlsp)
{
uint8_t val8;
uint16_t val16;
uint32_t val32;
val8 = rtls_reg_get8(rtlsp, RT_COMMAND_REG);
cmn_err(CE_NOTE, "%s: RT_COMMAND_REG = 0x%x",
mac_name(rtlsp->mh), val8);
delay(drv_usectohz(1000));
val16 = rtls_reg_get16(rtlsp, RT_INT_STATUS_REG);
cmn_err(CE_NOTE, "%s: RT_INT_STATUS_REG = 0x%x",
mac_name(rtlsp->mh), val16);
delay(drv_usectohz(1000));
val16 = rtls_reg_get16(rtlsp, RT_INT_MASK_REG);
cmn_err(CE_NOTE, "%s: RT_INT_MASK_REG = 0x%x",
mac_name(rtlsp->mh), val16);
delay(drv_usectohz(1000));
val32 = rtls_reg_get32(rtlsp, RX_CONFIG_REG);
cmn_err(CE_NOTE, "%s: RX_CONFIG_REG = 0x%x",
mac_name(rtlsp->mh), val32);
delay(drv_usectohz(1000));
val16 = rtls_reg_get16(rtlsp, TX_DESC_STAUS_REG);
cmn_err(CE_NOTE, "%s: TX_DESC_STAUS_REG = 0x%x, cur_desc = %d",
mac_name(rtlsp->mh), val16, rtlsp->tx_current_desc);
delay(drv_usectohz(1000));
val32 = rtls_reg_get32(rtlsp, TX_STATUS_DESC0_REG);
cmn_err(CE_NOTE, "%s: TX_STATUS_DESC0_REG = 0x%x",
mac_name(rtlsp->mh), val32);
delay(drv_usectohz(1000));
val32 = rtls_reg_get32(rtlsp, TX_STATUS_DESC1_REG);
cmn_err(CE_NOTE, "%s: TX_STATUS_DESC1_REG = 0x%x",
mac_name(rtlsp->mh), val32);
delay(drv_usectohz(1000));
val32 = rtls_reg_get32(rtlsp, TX_STATUS_DESC2_REG);
cmn_err(CE_NOTE, "%s: TX_STATUS_DESC2_REG = 0x%x",
mac_name(rtlsp->mh), val32);
delay(drv_usectohz(1000));
val32 = rtls_reg_get32(rtlsp, TX_STATUS_DESC3_REG);
cmn_err(CE_NOTE, "%s: TX_STATUS_DESC3_REG = 0x%x",
mac_name(rtlsp->mh), val32);
delay(drv_usectohz(1000));
cmn_err(CE_NOTE, "%s: in = %llu, multicast = %llu, broadcast = %llu",
mac_name(rtlsp->mh),
(unsigned long long)rtlsp->stats.ipackets,
(unsigned long long)rtlsp->stats.multi_rcv,
(unsigned long long)rtlsp->stats.brdcst_rcv);
delay(drv_usectohz(1000));
}
#endif