#include <sys/types.h>
#include <sys/sunddi.h>
#include <sys/policy.h>
#include <sys/sdt.h>
#include "dmfe_impl.h"
static char dmfe_ident[] = "Davicom DM9102 Ethernet";
#define DMFE_PCI_RNUMBER 2
#define DMFE_SLOP (8*sizeof (uint32_t))
#define DMFE_BUF_SIZE 2048
#define DMFE_BUF_SIZE_1 2000
#define DMFE_DMA_MODE DDI_DMA_STREAMING
#define DMFE_HEADROOM 34
static uint32_t dmfe_bus_modes = TX_POLL_INTVL | CACHE_ALIGN;
static uint32_t dmfe_setup_desc1 = TX_SETUP_PACKET | SETUPBUF_SIZE |
TX_FILTER_TYPE0;
#define DMFE_TX_DESC 128
#define DMFE_RX_DESC 128
static uint32_t dmfe_rx_desc = DMFE_RX_DESC;
static uint32_t dmfe_tx_desc = DMFE_TX_DESC;
static uint32_t dmfe_tx_min_free = 1;
static uint32_t dmfe_tx_reclaim_level = 1;
static uint32_t dmfe_tx_int_factor = (DMFE_TX_DESC / 2) - 1;
static boolean_t dmfe_reclaim_on_done = B_FALSE;
#define DMFE_TICK 25000
#define TX_STALL_TIME_100 50000
#define TX_STALL_TIME_10 200000
static uint32_t dmfe_tick_us = DMFE_TICK;
static uint32_t dmfe_tx100_stall_us = TX_STALL_TIME_100;
static uint32_t dmfe_tx10_stall_us = TX_STALL_TIME_10;
static uint32_t stall_100_tix[TX_PROCESS_MAX_STATE+1];
static uint32_t stall_10_tix[TX_PROCESS_MAX_STATE+1];
static char localmac_propname[] = "local-mac-address";
static char opmode_propname[] = "opmode-reg-value";
static int dmfe_m_start(void *);
static void dmfe_m_stop(void *);
static int dmfe_m_promisc(void *, boolean_t);
static int dmfe_m_multicst(void *, boolean_t, const uint8_t *);
static int dmfe_m_unicst(void *, const uint8_t *);
static void dmfe_m_ioctl(void *, queue_t *, mblk_t *);
static mblk_t *dmfe_m_tx(void *, mblk_t *);
static int dmfe_m_stat(void *, uint_t, uint64_t *);
static int dmfe_m_getprop(void *, const char *, mac_prop_id_t,
uint_t, void *);
static int dmfe_m_setprop(void *, const char *, mac_prop_id_t,
uint_t, const void *);
static void dmfe_m_propinfo(void *, const char *, mac_prop_id_t,
mac_prop_info_handle_t);
static mac_callbacks_t dmfe_m_callbacks = {
MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
dmfe_m_stat,
dmfe_m_start,
dmfe_m_stop,
dmfe_m_promisc,
dmfe_m_multicst,
dmfe_m_unicst,
dmfe_m_tx,
NULL,
dmfe_m_ioctl,
NULL,
NULL,
NULL,
dmfe_m_setprop,
dmfe_m_getprop,
dmfe_m_propinfo
};
static ddi_dma_attr_t dma_attr = {
DMA_ATTR_V0,
0,
(uint32_t)0xFFFFFFFF,
0x0FFFFFF,
0x20,
0x7F,
1,
(uint32_t)0xFFFFFFFF,
(uint32_t)0xFFFFFFFF,
1,
1,
0
};
static ddi_device_acc_attr_t dmfe_reg_accattr = {
DDI_DEVICE_ATTR_V0,
DDI_STRUCTURE_LE_ACC,
DDI_STRICTORDER_ACC
};
static ddi_device_acc_attr_t dmfe_data_accattr = {
DDI_DEVICE_ATTR_V0,
DDI_NEVERSWAP_ACC,
DDI_STRICTORDER_ACC
};
static uchar_t dmfe_broadcast_addr[ETHERADDRL] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
uint32_t
dmfe_chip_get32(dmfe_t *dmfep, off_t offset)
{
uint32_t *addr;
addr = (void *)(dmfep->io_reg + offset);
return (ddi_get32(dmfep->io_handle, addr));
}
void
dmfe_chip_put32(dmfe_t *dmfep, off_t offset, uint32_t value)
{
uint32_t *addr;
addr = (void *)(dmfep->io_reg + offset);
ddi_put32(dmfep->io_handle, addr, value);
}
static uint32_t
dmfe_ring_get32(dma_area_t *dma_p, uint_t index, uint_t offset)
{
uint32_t *addr;
addr = (void *)dma_p->mem_va;
return (ddi_get32(dma_p->acc_hdl, addr + index*DESC_SIZE + offset));
}
static void
dmfe_ring_put32(dma_area_t *dma_p, uint_t index, uint_t offset, uint32_t value)
{
uint32_t *addr;
addr = (void *)dma_p->mem_va;
ddi_put32(dma_p->acc_hdl, addr + index*DESC_SIZE + offset, value);
}
static uint32_t
dmfe_setup_get32(dma_area_t *dma_p, uint_t index)
{
uint32_t *addr;
addr = (void *)dma_p->setup_va;
return (ddi_get32(dma_p->acc_hdl, addr + index));
}
static void
dmfe_setup_put32(dma_area_t *dma_p, uint_t index, uint32_t value)
{
uint32_t *addr;
addr = (void *)dma_p->setup_va;
ddi_put32(dma_p->acc_hdl, addr + index, value);
}
static void
dmfe_set_opmode(dmfe_t *dmfep)
{
ASSERT(mutex_owned(dmfep->oplock));
dmfe_chip_put32(dmfep, OPN_MODE_REG, dmfep->opmode);
drv_usecwait(10);
}
static void
dmfe_stop_chip(dmfe_t *dmfep, enum chip_state newstate)
{
ASSERT(mutex_owned(dmfep->oplock));
dmfe_chip_put32(dmfep, INT_MASK_REG, 0);
dmfep->opmode &= ~(START_TRANSMIT | START_RECEIVE);
dmfe_set_opmode(dmfep);
dmfe_chip_put32(dmfep, STATUS_REG, TX_STOPPED_INT | RX_STOPPED_INT);
switch (newstate) {
default:
ASSERT(!"can't get here");
return;
case CHIP_STOPPED:
case CHIP_ERROR:
break;
case CHIP_RESET:
dmfe_chip_put32(dmfep, BUS_MODE_REG, SW_RESET);
drv_usecwait(10);
dmfe_chip_put32(dmfep, BUS_MODE_REG, 0);
drv_usecwait(10);
dmfe_chip_put32(dmfep, BUS_MODE_REG, dmfe_bus_modes);
break;
}
dmfep->chip_state = newstate;
}
static void
dmfe_init_rings(dmfe_t *dmfep)
{
dma_area_t *descp;
uint32_t pstart;
uint32_t pnext;
uint32_t pbuff;
uint32_t desc1;
int i;
ASSERT(mutex_owned(dmfep->oplock));
ASSERT(mutex_owned(dmfep->rxlock));
ASSERT(mutex_owned(dmfep->txlock));
descp = &dmfep->rx_desc;
pstart = descp->mem_dvma;
pnext = pstart + sizeof (struct rx_desc_type);
pbuff = dmfep->rx_buff.mem_dvma;
desc1 = RX_CHAINING | DMFE_BUF_SIZE_1;
for (i = 0; i < dmfep->rx.n_desc; ++i) {
dmfe_ring_put32(descp, i, RD_NEXT, pnext);
dmfe_ring_put32(descp, i, BUFFER1, pbuff);
dmfe_ring_put32(descp, i, DESC1, desc1);
dmfe_ring_put32(descp, i, DESC0, RX_OWN);
pnext += sizeof (struct rx_desc_type);
pbuff += DMFE_BUF_SIZE;
}
dmfe_ring_put32(descp, --i, RD_NEXT, pstart);
DMA_SYNC(descp, DDI_DMA_SYNC_FORDEV);
dmfep->rx.next_free = 0;
dmfe_chip_put32(dmfep, RX_BASE_ADDR_REG, descp->mem_dvma);
descp = &dmfep->tx_desc;
pstart = descp->mem_dvma;
pnext = pstart + sizeof (struct tx_desc_type);
pbuff = dmfep->tx_buff.mem_dvma;
desc1 = TX_CHAINING;
for (i = 0; i < dmfep->tx.n_desc; ++i) {
dmfe_ring_put32(descp, i, TD_NEXT, pnext);
dmfe_ring_put32(descp, i, BUFFER1, pbuff);
dmfe_ring_put32(descp, i, DESC1, desc1);
dmfe_ring_put32(descp, i, DESC0, 0);
pnext += sizeof (struct tx_desc_type);
pbuff += DMFE_BUF_SIZE;
}
dmfe_ring_put32(descp, --i, TD_NEXT, pstart);
DMA_SYNC(descp, DDI_DMA_SYNC_FORDEV);
dmfep->tx.n_free = dmfep->tx.n_desc;
dmfep->tx.next_free = dmfep->tx.next_busy = 0;
dmfe_chip_put32(dmfep, TX_BASE_ADDR_REG, descp->mem_dvma);
}
static void
dmfe_start_chip(dmfe_t *dmfep, int mode)
{
ASSERT(mutex_owned(dmfep->oplock));
dmfep->opmode |= mode;
dmfe_set_opmode(dmfep);
dmfe_chip_put32(dmfep, W_J_TIMER_REG, 0);
dmfe_chip_put32(dmfep, W_J_TIMER_REG, VLAN_ENABLE);
dmfe_chip_put32(dmfep, STATUS_REG, TX_STOPPED_INT | RX_STOPPED_INT);
dmfep->chip_state = mode & START_RECEIVE ? CHIP_TX_RX :
mode & START_TRANSMIT ? CHIP_TX_ONLY : CHIP_STOPPED;
}
static void
dmfe_enable_interrupts(dmfe_t *dmfep)
{
ASSERT(mutex_owned(dmfep->oplock));
dmfep->imask = RX_PKTDONE_INT | TX_PKTDONE_INT |
RX_STOPPED_INT | TX_STOPPED_INT | RX_UNAVAIL_INT | SYSTEM_ERR_INT;
dmfe_chip_put32(dmfep, INT_MASK_REG,
NORMAL_SUMMARY_INT | ABNORMAL_SUMMARY_INT | dmfep->imask);
dmfep->chip_state = CHIP_RUNNING;
}
static void
dmfe_update_rx_stats(dmfe_t *dmfep, uint32_t desc0)
{
ASSERT(mutex_owned(dmfep->rxlock));
if (((RX_LAST_DESC | RX_ERR_SUMMARY) & ~desc0) == 0) {
dmfep->rx_stats_ierrors += 1;
if (desc0 & RX_OVERFLOW) {
dmfep->rx_stats_overflow += 1;
} else if (desc0 & RX_RUNT_FRAME)
dmfep->rx_stats_short += 1;
if (desc0 & RX_CRC)
dmfep->rx_stats_fcs += 1;
if (desc0 & RX_FRAME2LONG)
dmfep->rx_stats_toolong += 1;
}
if (desc0 & RX_RCV_WD_TO)
dmfep->rx_stats_macrcv_errors += 1;
if (desc0 & RX_DRIBBLING)
dmfep->rx_stats_align += 1;
if (desc0 & RX_MII_ERR)
dmfep->rx_stats_macrcv_errors += 1;
}
static mblk_t *
dmfe_getp(dmfe_t *dmfep)
{
dma_area_t *descp;
mblk_t **tail;
mblk_t *head;
mblk_t *mp;
char *rxb;
uchar_t *dp;
uint32_t desc0;
uint32_t misses;
int packet_length;
int index;
mutex_enter(dmfep->rxlock);
misses = dmfe_chip_get32(dmfep, MISSED_FRAME_REG);
dmfep->rx_stats_norcvbuf += (misses & MISSED_FRAME_MASK);
descp = &dmfep->rx_desc;
DMA_SYNC(descp, DDI_DMA_SYNC_FORKERNEL);
index = dmfep->rx.next_free;
desc0 = dmfe_ring_get32(descp, index, DESC0);
DTRACE_PROBE1(rx__start, uint32_t, desc0);
for (head = NULL, tail = &head; (desc0 & RX_OWN) == 0; ) {
dmfe_update_rx_stats(dmfep, desc0);
if (((RX_FIRST_DESC | RX_LAST_DESC) & ~desc0) != 0) {
DTRACE_PROBE1(rx__frag, uint32_t, desc0);
goto skip;
}
if (desc0 & RX_ERR_SUMMARY) {
DTRACE_PROBE1(rx__err, uint32_t, desc0);
goto skip;
}
packet_length = (desc0 >> 16) & 0x3fff;
if (packet_length > DMFE_MAX_PKT_SIZE) {
DTRACE_PROBE1(rx__toobig, int, packet_length);
goto skip;
} else if (packet_length < ETHERMIN) {
DTRACE_PROBE1(rx__runt, int, packet_length);
goto skip;
}
(void) ddi_dma_sync(dmfep->rx_buff.dma_hdl,
index * DMFE_BUF_SIZE, DMFE_BUF_SIZE,
DDI_DMA_SYNC_FORKERNEL);
rxb = &dmfep->rx_buff.mem_va[index*DMFE_BUF_SIZE];
mp = allocb(DMFE_HEADROOM + packet_length, 0);
if (mp == NULL) {
DTRACE_PROBE(rx__no__buf);
dmfep->rx_stats_norcvbuf += 1;
goto skip;
}
dmfep->rx_stats_ipackets += 1;
dmfep->rx_stats_rbytes += packet_length;
if (desc0 & RX_MULTI_FRAME) {
if (bcmp(rxb, dmfe_broadcast_addr, ETHERADDRL)) {
dmfep->rx_stats_multi += 1;
} else {
dmfep->rx_stats_bcast += 1;
}
}
dp = mp->b_rptr += DMFE_HEADROOM;
mp->b_cont = mp->b_next = NULL;
bcopy(rxb, dp, packet_length);
mp->b_wptr = mp->b_rptr + packet_length - ETHERFCSL;
*tail = mp;
tail = &mp->b_next;
skip:
dmfe_ring_put32(descp, index, DESC0, RX_OWN);
index = NEXT(index, dmfep->rx.n_desc);
desc0 = dmfe_ring_get32(descp, index, DESC0);
}
dmfep->rx.next_free = index;
DMA_SYNC(descp, DDI_DMA_SYNC_FORDEV);
dmfe_chip_put32(dmfep, RX_POLL_REG, 0);
mutex_exit(dmfep->rxlock);
return (head);
}
static void
dmfe_update_tx_stats(dmfe_t *dmfep, int index, uint32_t desc0, uint32_t desc1)
{
uint32_t collisions;
uint32_t errbits;
uint32_t errsum;
ASSERT(mutex_owned(dmfep->txlock));
collisions = ((desc0 >> 3) & 0x0f);
errsum = desc0 & TX_ERR_SUMMARY;
errbits = desc0 & (TX_UNDERFLOW | TX_LATE_COLL | TX_CARRIER_LOSS |
TX_NO_CARRIER | TX_EXCESS_COLL | TX_JABBER_TO);
if ((errsum == 0) != (errbits == 0)) {
dmfe_log(dmfep, "dubious TX error status 0x%x", desc0);
desc0 |= TX_ERR_SUMMARY;
}
if (desc0 & TX_ERR_SUMMARY) {
dmfep->tx_stats_oerrors += 1;
if (desc0 & TX_JABBER_TO) {
dmfep->tx_stats_macxmt_errors += 1;
dmfep->tx_stats_jabber += 1;
}
if (desc0 & TX_UNDERFLOW)
dmfep->tx_stats_underflow += 1;
else if (desc0 & TX_LATE_COLL)
dmfep->tx_stats_xmtlatecoll += 1;
if (desc0 & (TX_CARRIER_LOSS | TX_NO_CARRIER))
dmfep->tx_stats_nocarrier += 1;
if (desc0 & TX_EXCESS_COLL) {
dmfep->tx_stats_excoll += 1;
collisions = 16;
}
} else {
int bit = index % NBBY;
int byt = index / NBBY;
if (dmfep->tx_mcast[byt] & bit) {
dmfep->tx_mcast[byt] &= ~bit;
dmfep->tx_stats_multi += 1;
} else if (dmfep->tx_bcast[byt] & bit) {
dmfep->tx_bcast[byt] &= ~bit;
dmfep->tx_stats_bcast += 1;
}
dmfep->tx_stats_opackets += 1;
dmfep->tx_stats_obytes += desc1 & TX_BUFFER_SIZE1;
}
if (collisions == 1)
dmfep->tx_stats_first_coll += 1;
else if (collisions != 0)
dmfep->tx_stats_multi_coll += 1;
dmfep->tx_stats_collisions += collisions;
if (desc0 & TX_DEFERRED)
dmfep->tx_stats_defer += 1;
}
static boolean_t
dmfe_reclaim_tx_desc(dmfe_t *dmfep)
{
dma_area_t *descp;
uint32_t desc0;
uint32_t desc1;
int i;
ASSERT(mutex_owned(dmfep->txlock));
descp = &dmfep->tx_desc;
DMA_SYNC(descp, DDI_DMA_SYNC_FORKERNEL);
i = dmfep->tx.next_busy;
if (i == dmfep->tx.next_free)
return (B_FALSE);
desc0 = dmfe_ring_get32(descp, i, DESC0);
if (desc0 & TX_OWN)
return (B_FALSE);
for (;;) {
desc1 = dmfe_ring_get32(descp, i, DESC1);
ASSERT((desc1 & (TX_SETUP_PACKET | TX_LAST_DESC)) != 0);
if ((desc1 & TX_SETUP_PACKET) == 0) {
dmfe_update_tx_stats(dmfep, i, desc0, desc1);
}
dmfep->tx.n_free += 1;
i = NEXT(i, dmfep->tx.n_desc);
if (i == dmfep->tx.next_free)
break;
desc0 = dmfe_ring_get32(descp, i, DESC0);
if (desc0 & TX_OWN)
break;
}
dmfep->tx.next_busy = i;
dmfep->tx_pending_tix = 0;
return (B_TRUE);
}
static boolean_t
dmfe_send_msg(dmfe_t *dmfep, mblk_t *mp)
{
dma_area_t *descp;
mblk_t *bp;
char *txb;
uint32_t desc1;
uint32_t index;
size_t totlen;
size_t mblen;
uint32_t paddr;
mutex_enter(dmfep->txlock);
if (dmfep->suspended)
return (B_FALSE);
if (dmfep->tx.n_free <= dmfe_tx_reclaim_level &&
dmfe_reclaim_tx_desc(dmfep) == B_FALSE &&
dmfep->tx.n_free <= dmfe_tx_min_free) {
mutex_exit(dmfep->txlock);
DTRACE_PROBE(tx__no__desc);
return (B_FALSE);
}
index = dmfep->tx.next_free;
dmfep->tx.next_free = NEXT(index, dmfep->tx.n_desc);
ASSERT(dmfep->tx.next_free != dmfep->tx.next_busy);
if (dmfep->tx.n_free-- == dmfep->tx.n_desc)
dmfep->tx_pending_tix = 0;
ASSERT(dmfep->tx.n_free >= 1);
mutex_exit(dmfep->txlock);
descp = &dmfep->tx_desc;
ASSERT((dmfe_ring_get32(descp, index, DESC0) & TX_OWN) == 0);
if (mp == NULL) {
desc1 = dmfe_setup_desc1;
paddr = descp->setup_dvma;
} else {
txb = &dmfep->tx_buff.mem_va[index*DMFE_BUF_SIZE];
totlen = 0;
bp = mp;
for (; bp != NULL; bp = bp->b_cont) {
mblen = MBLKL(bp);
if ((totlen += mblen) <= DMFE_MAX_PKT_SIZE) {
bcopy(bp->b_rptr, txb, mblen);
txb += mblen;
}
}
txb = &dmfep->tx_buff.mem_va[index*DMFE_BUF_SIZE];
if (txb[0] & 0x1) {
if (bcmp(txb, dmfe_broadcast_addr, ETHERADDRL) == 0) {
dmfep->tx_bcast[index / NBBY] |=
(1 << (index % NBBY));
} else {
dmfep->tx_mcast[index / NBBY] |=
(1 << (index % NBBY));
}
}
ASSERT(bp == NULL);
ASSERT(totlen <= DMFE_MAX_PKT_SIZE);
totlen &= TX_BUFFER_SIZE1;
desc1 = TX_FIRST_DESC | TX_LAST_DESC | totlen;
paddr = dmfep->tx_buff.mem_dvma + index*DMFE_BUF_SIZE;
(void) ddi_dma_sync(dmfep->tx_buff.dma_hdl,
index * DMFE_BUF_SIZE, DMFE_BUF_SIZE, DDI_DMA_SYNC_FORDEV);
}
if ((index & dmfe_tx_int_factor) == 0)
desc1 |= TX_INT_ON_COMP;
desc1 |= TX_CHAINING;
dmfe_ring_put32(descp, index, BUFFER1, paddr);
dmfe_ring_put32(descp, index, DESC1, desc1);
dmfe_ring_put32(descp, index, DESC0, TX_OWN);
DMA_SYNC(descp, DDI_DMA_SYNC_FORDEV);
dmfe_chip_put32(dmfep, TX_POLL_REG, 0);
if (mp)
freemsg(mp);
return (B_TRUE);
}
static mblk_t *
dmfe_m_tx(void *arg, mblk_t *mp)
{
dmfe_t *dmfep = arg;
mblk_t *next;
ASSERT(mp != NULL);
ASSERT(dmfep->mac_state == DMFE_MAC_STARTED);
if (dmfep->chip_state != CHIP_RUNNING)
return (mp);
while (mp != NULL) {
next = mp->b_next;
mp->b_next = NULL;
if (!dmfe_send_msg(dmfep, mp)) {
mp->b_next = next;
break;
}
mp = next;
}
return (mp);
}
static uint32_t
dmfe_hash_index(const uint8_t *address)
{
uint32_t const POLY = HASH_POLY;
uint32_t crc = HASH_CRC;
uint32_t index;
uint32_t msb;
uchar_t currentbyte;
int byteslength;
int shift;
int bit;
for (byteslength = 0; byteslength < ETHERADDRL; ++byteslength) {
currentbyte = address[byteslength];
for (bit = 0; bit < 8; ++bit) {
msb = crc >> 31;
crc <<= 1;
if (msb ^ (currentbyte & 1)) {
crc ^= POLY;
crc |= 0x00000001;
}
currentbyte >>= 1;
}
}
for (index = 0, bit = 23, shift = 8; shift >= 0; ++bit, --shift)
index |= (((crc >> bit) & 1) << shift);
return (index);
}
static void
dmfe_update_hash(dmfe_t *dmfep, uint32_t index, boolean_t val)
{
dma_area_t *descp;
uint32_t tmp;
ASSERT(mutex_owned(dmfep->oplock));
descp = &dmfep->tx_desc;
tmp = dmfe_setup_get32(descp, index/16);
if (val)
tmp |= 1 << (index%16);
else
tmp &= ~(1 << (index%16));
dmfe_setup_put32(descp, index/16, tmp);
}
static boolean_t
dmfe_update_mcast(dmfe_t *dmfep, const uint8_t *mca, boolean_t val)
{
uint32_t index;
uint8_t *refp;
boolean_t change;
index = dmfe_hash_index(mca);
refp = &dmfep->mcast_refs[index];
change = (val ? (*refp)++ : --(*refp)) == 0;
if (change)
dmfe_update_hash(dmfep, index, val);
return (change);
}
static int
dmfe_send_setup(dmfe_t *dmfep)
{
int status;
ASSERT(mutex_owned(dmfep->oplock));
if (dmfep->suspended)
return (0);
if ((dmfep->opmode & START_TRANSMIT) == 0)
return (0);
status = dmfe_send_msg(dmfep, NULL);
dmfep->need_setup = status ? B_FALSE : B_TRUE;
return (0);
}
static int
dmfe_m_unicst(void *arg, const uint8_t *macaddr)
{
dmfe_t *dmfep = arg;
int status;
int index;
mutex_enter(dmfep->oplock);
if (dmfep->addr_set && dmfe_setup_desc1 & TX_FILTER_TYPE1)
(void) dmfe_update_mcast(dmfep, dmfep->curr_addr, B_FALSE);
if (dmfe_setup_desc1 & TX_FILTER_TYPE1)
(void) dmfe_update_mcast(dmfep, macaddr, B_TRUE);
if (!dmfep->addr_set)
(void) dmfe_update_mcast(dmfep, dmfe_broadcast_addr, B_TRUE);
ethaddr_copy(macaddr, dmfep->curr_addr);
dmfep->addr_set = B_TRUE;
for (index = 0; index < ETHERADDRL; index += 2)
dmfe_setup_put32(&dmfep->tx_desc, SETUPBUF_PHYS+index/2,
(macaddr[index+1] << 8) | macaddr[index]);
status = dmfe_send_setup(dmfep);
mutex_exit(dmfep->oplock);
return (status);
}
static int
dmfe_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
{
dmfe_t *dmfep = arg;
int status = 0;
mutex_enter(dmfep->oplock);
if (dmfe_update_mcast(dmfep, mca, add))
status = dmfe_send_setup(dmfep);
mutex_exit(dmfep->oplock);
return (status);
}
static void
dmfe_stop(dmfe_t *dmfep)
{
ASSERT(mutex_owned(dmfep->oplock));
dmfe_stop_chip(dmfep, CHIP_STOPPED);
}
static void
dmfe_reset(dmfe_t *dmfep)
{
ASSERT(mutex_owned(dmfep->oplock));
ASSERT(mutex_owned(dmfep->rxlock));
ASSERT(mutex_owned(dmfep->txlock));
dmfe_stop_chip(dmfep, CHIP_RESET);
dmfe_init_rings(dmfep);
}
static void
dmfe_start(dmfe_t *dmfep)
{
uint32_t gpsr;
ASSERT(mutex_owned(dmfep->oplock));
ASSERT(dmfep->chip_state == CHIP_RESET ||
dmfep->chip_state == CHIP_STOPPED);
gpsr = dmfe_chip_get32(dmfep, PHY_STATUS_REG);
if (gpsr & GPS_FULL_DUPLEX)
dmfep->opmode |= FULL_DUPLEX;
else
dmfep->opmode &= ~FULL_DUPLEX;
dmfe_start_chip(dmfep, START_TRANSMIT);
(void) dmfe_send_setup(dmfep);
drv_usecwait(10);
dmfe_start_chip(dmfep, START_RECEIVE);
dmfe_enable_interrupts(dmfep);
}
static void
dmfe_restart(dmfe_t *dmfep)
{
ASSERT(mutex_owned(dmfep->oplock));
mutex_enter(dmfep->rxlock);
mutex_enter(dmfep->txlock);
dmfe_reset(dmfep);
mutex_exit(dmfep->txlock);
mutex_exit(dmfep->rxlock);
if (dmfep->mac_state == DMFE_MAC_STARTED) {
dmfe_start(dmfep);
}
}
static void
dmfe_m_stop(void *arg)
{
dmfe_t *dmfep = arg;
mii_stop(dmfep->mii);
mutex_enter(dmfep->oplock);
if (!dmfep->suspended)
dmfe_stop(dmfep);
dmfep->mac_state = DMFE_MAC_STOPPED;
mutex_exit(dmfep->oplock);
}
static int
dmfe_m_start(void *arg)
{
dmfe_t *dmfep = arg;
mutex_enter(dmfep->oplock);
if (!dmfep->suspended)
dmfe_start(dmfep);
dmfep->mac_state = DMFE_MAC_STARTED;
mutex_exit(dmfep->oplock);
mii_start(dmfep->mii);
return (0);
}
static int
dmfe_m_promisc(void *arg, boolean_t on)
{
dmfe_t *dmfep = arg;
mutex_enter(dmfep->oplock);
dmfep->opmode &= ~(PROMISC_MODE | PASS_MULTICAST);
if (on)
dmfep->opmode |= PROMISC_MODE;
if (!dmfep->suspended)
dmfe_set_opmode(dmfep);
mutex_exit(dmfep->oplock);
return (0);
}
static uint_t
dmfe_factotum(caddr_t arg)
{
dmfe_t *dmfep;
dmfep = (void *)arg;
ASSERT(dmfep->dmfe_guard == DMFE_GUARD);
mutex_enter(dmfep->oplock);
if (dmfep->suspended) {
mutex_exit(dmfep->oplock);
return (DDI_INTR_CLAIMED);
}
dmfep->factotum_flag = 0;
DRV_KS_INC(dmfep, KS_FACTOTUM_RUN);
if (dmfep->chip_state == CHIP_ERROR) {
DRV_KS_INC(dmfep, KS_RECOVERY);
dmfe_restart(dmfep);
mutex_exit(dmfep->oplock);
mii_reset(dmfep->mii);
} else if (dmfep->need_setup) {
(void) dmfe_send_setup(dmfep);
mutex_exit(dmfep->oplock);
}
return (DDI_INTR_CLAIMED);
}
static void
dmfe_wake_factotum(dmfe_t *dmfep, int ks_id, const char *why)
{
_NOTE(ARGUNUSED(why));
ASSERT(mutex_owned(dmfep->oplock));
DRV_KS_INC(dmfep, ks_id);
if (dmfep->factotum_flag++ == 0)
ddi_trigger_softintr(dmfep->factotum_id);
}
static void
dmfe_tick_stall_check(dmfe_t *dmfep, uint32_t gpsr, uint32_t istat)
{
boolean_t tx_stall;
uint32_t tx_state;
uint32_t limit;
ASSERT(mutex_owned(dmfep->oplock));
tx_stall = B_FALSE;
if (mutex_tryenter(dmfep->txlock)) {
if (dmfep->tx.n_free < dmfep->tx.n_desc) {
tx_state = TX_PROCESS_STATE(istat);
if (gpsr & GPS_LINK_100)
limit = stall_100_tix[tx_state];
else
limit = stall_10_tix[tx_state];
if (++dmfep->tx_pending_tix >= limit &&
dmfe_reclaim_tx_desc(dmfep) == B_FALSE) {
dmfe_log(dmfep, "TX stall detected "
"after %d ticks in state %d; "
"automatic recovery initiated",
dmfep->tx_pending_tix, tx_state);
tx_stall = B_TRUE;
}
}
mutex_exit(dmfep->txlock);
}
if (tx_stall) {
dmfe_stop_chip(dmfep, CHIP_ERROR);
dmfe_wake_factotum(dmfep, KS_TX_STALL, "tick (TX stall)");
}
}
static void
dmfe_cyclic(void *arg)
{
dmfe_t *dmfep = arg;
uint32_t istat;
uint32_t gpsr;
if (mutex_tryenter(dmfep->oplock) == 0)
return;
if ((dmfep->suspended) || (dmfep->chip_state != CHIP_RUNNING)) {
mutex_exit(dmfep->oplock);
return;
}
if (dmfep->chip_state == CHIP_RUNNING) {
istat = dmfe_chip_get32(dmfep, STATUS_REG);
gpsr = dmfe_chip_get32(dmfep, PHY_STATUS_REG);
dmfe_tick_stall_check(dmfep, gpsr, istat);
}
DRV_KS_INC(dmfep, KS_CYCLIC_RUN);
mutex_exit(dmfep->oplock);
}
static uint_t
dmfe_interrupt(caddr_t arg)
{
dmfe_t *dmfep;
uint32_t interrupts;
uint32_t istat;
const char *msg;
mblk_t *mp;
boolean_t warning_msg = B_TRUE;
dmfep = (void *)arg;
mutex_enter(dmfep->oplock);
if (dmfep->suspended) {
mutex_exit(dmfep->oplock);
return (DDI_INTR_UNCLAIMED);
}
istat = dmfe_chip_get32(dmfep, STATUS_REG);
if ((istat & (NORMAL_SUMMARY_INT | ABNORMAL_SUMMARY_INT)) == 0) {
mutex_exit(dmfep->oplock);
return (DDI_INTR_UNCLAIMED);
}
DRV_KS_INC(dmfep, KS_INTERRUPT);
istat |= dmfe_chip_get32(dmfep, STATUS_REG);
interrupts = istat & dmfep->imask;
ASSERT(interrupts != 0);
DTRACE_PROBE1(intr, uint32_t, istat);
if (interrupts & ~(RX_PKTDONE_INT | TX_PKTDONE_INT)) {
if (istat & ABNORMAL_SUMMARY_INT) {
if (interrupts & SYSTEM_ERR_INT) {
switch (istat & SYSTEM_ERR_BITS) {
case SYSTEM_ERR_M_ABORT:
msg = "Bus Master Abort";
break;
case SYSTEM_ERR_T_ABORT:
msg = "Bus Target Abort";
break;
case SYSTEM_ERR_PARITY:
msg = "Parity Error";
break;
default:
msg = "Unknown System Bus Error";
break;
}
} else if (interrupts & RX_STOPPED_INT) {
msg = "RX process stopped";
} else if (interrupts & RX_UNAVAIL_INT) {
msg = "RX buffer unavailable";
warning_msg = B_FALSE;
} else if (interrupts & RX_WATCHDOG_INT) {
msg = "RX watchdog timeout?";
} else if (interrupts & RX_EARLY_INT) {
msg = "RX early interrupt?";
} else if (interrupts & TX_STOPPED_INT) {
msg = "TX process stopped";
} else if (interrupts & TX_JABBER_INT) {
msg = "TX jabber timeout";
} else if (interrupts & TX_UNDERFLOW_INT) {
msg = "TX underflow?";
} else if (interrupts & TX_EARLY_INT) {
msg = "TX early interrupt?";
} else if (interrupts & LINK_STATUS_INT) {
msg = "Link status change?";
} else if (interrupts & GP_TIMER_INT) {
msg = "Timer expired?";
}
if (warning_msg)
dmfe_warning(dmfep, "abnormal interrupt, "
"status 0x%x: %s", istat, msg);
dmfe_stop_chip(dmfep, CHIP_ERROR);
dmfe_wake_factotum(dmfep, KS_CHIP_ERROR,
"interrupt (error)");
} else {
DTRACE_PROBE1(intr__unexpected, uint32_t, istat);
}
}
if (dmfep->chip_state != CHIP_ERROR) {
(void) dmfe_chip_put32(dmfep, STATUS_REG, istat);
(void) dmfe_chip_get32(dmfep, STATUS_REG);
}
mutex_exit(dmfep->oplock);
if (interrupts & RX_PKTDONE_INT)
if ((mp = dmfe_getp(dmfep)) != NULL)
mac_rx(dmfep->mh, NULL, mp);
if (interrupts & TX_PKTDONE_INT) {
if (dmfe_reclaim_on_done && mutex_tryenter(dmfep->txlock)) {
(void) dmfe_reclaim_tx_desc(dmfep);
mutex_exit(dmfep->txlock);
}
mac_tx_update(dmfep->mh);
}
return (DDI_INTR_CLAIMED);
}
static int
dmfe_m_stat(void *arg, uint_t stat, uint64_t *val)
{
dmfe_t *dmfep = arg;
int rv = 0;
if (mii_m_getstat(dmfep->mii, stat, val) == 0) {
return (0);
}
mutex_enter(dmfep->oplock);
mutex_enter(dmfep->rxlock);
mutex_enter(dmfep->txlock);
(void) dmfe_reclaim_tx_desc(dmfep);
switch (stat) {
case MAC_STAT_IPACKETS:
*val = dmfep->rx_stats_ipackets;
break;
case MAC_STAT_MULTIRCV:
*val = dmfep->rx_stats_multi;
break;
case MAC_STAT_BRDCSTRCV:
*val = dmfep->rx_stats_bcast;
break;
case MAC_STAT_RBYTES:
*val = dmfep->rx_stats_rbytes;
break;
case MAC_STAT_IERRORS:
*val = dmfep->rx_stats_ierrors;
break;
case MAC_STAT_NORCVBUF:
*val = dmfep->rx_stats_norcvbuf;
break;
case MAC_STAT_COLLISIONS:
*val = dmfep->tx_stats_collisions;
break;
case MAC_STAT_OERRORS:
*val = dmfep->tx_stats_oerrors;
break;
case MAC_STAT_OPACKETS:
*val = dmfep->tx_stats_opackets;
break;
case MAC_STAT_MULTIXMT:
*val = dmfep->tx_stats_multi;
break;
case MAC_STAT_BRDCSTXMT:
*val = dmfep->tx_stats_bcast;
break;
case MAC_STAT_OBYTES:
*val = dmfep->tx_stats_obytes;
break;
case MAC_STAT_OVERFLOWS:
*val = dmfep->rx_stats_overflow;
break;
case MAC_STAT_UNDERFLOWS:
*val = dmfep->tx_stats_underflow;
break;
case ETHER_STAT_ALIGN_ERRORS:
*val = dmfep->rx_stats_align;
break;
case ETHER_STAT_FCS_ERRORS:
*val = dmfep->rx_stats_fcs;
break;
case ETHER_STAT_TOOLONG_ERRORS:
*val = dmfep->rx_stats_toolong;
break;
case ETHER_STAT_TOOSHORT_ERRORS:
*val = dmfep->rx_stats_short;
break;
case ETHER_STAT_MACRCV_ERRORS:
*val = dmfep->rx_stats_macrcv_errors;
break;
case ETHER_STAT_MACXMT_ERRORS:
*val = dmfep->tx_stats_macxmt_errors;
break;
case ETHER_STAT_JABBER_ERRORS:
*val = dmfep->tx_stats_jabber;
break;
case ETHER_STAT_CARRIER_ERRORS:
*val = dmfep->tx_stats_nocarrier;
break;
case ETHER_STAT_TX_LATE_COLLISIONS:
*val = dmfep->tx_stats_xmtlatecoll;
break;
case ETHER_STAT_EX_COLLISIONS:
*val = dmfep->tx_stats_excoll;
break;
case ETHER_STAT_DEFER_XMTS:
*val = dmfep->tx_stats_defer;
break;
case ETHER_STAT_FIRST_COLLISIONS:
*val = dmfep->tx_stats_first_coll;
break;
case ETHER_STAT_MULTI_COLLISIONS:
*val = dmfep->tx_stats_multi_coll;
break;
default:
rv = ENOTSUP;
}
mutex_exit(dmfep->txlock);
mutex_exit(dmfep->rxlock);
mutex_exit(dmfep->oplock);
return (rv);
}
static lb_property_t dmfe_loopmodes[] = {
{ normal, "normal", 0 },
{ internal, "Internal", 1 },
{ external, "External", 2 },
};
static void
dmfe_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
{
dmfe_t *dmfep = arg;
struct iocblk *iocp;
int rv = 0;
lb_info_sz_t sz;
int cmd;
uint32_t mode;
iocp = (void *)mp->b_rptr;
cmd = iocp->ioc_cmd;
if (mp->b_cont == NULL) {
miocnak(wq, mp, 0, EINVAL);
return;
}
switch (cmd) {
case LB_GET_INFO_SIZE:
if (iocp->ioc_count != sizeof (sz)) {
rv = EINVAL;
} else {
sz = sizeof (dmfe_loopmodes);
bcopy(&sz, mp->b_cont->b_rptr, sizeof (sz));
}
break;
case LB_GET_INFO:
if (iocp->ioc_count != sizeof (dmfe_loopmodes)) {
rv = EINVAL;
} else {
bcopy(dmfe_loopmodes, mp->b_cont->b_rptr,
iocp->ioc_count);
}
break;
case LB_GET_MODE:
if (iocp->ioc_count != sizeof (mode)) {
rv = EINVAL;
} else {
mutex_enter(dmfep->oplock);
switch (dmfep->opmode & LOOPBACK_MODE_MASK) {
case LOOPBACK_OFF:
mode = 0;
break;
case LOOPBACK_INTERNAL:
mode = 1;
break;
default:
mode = 2;
break;
}
mutex_exit(dmfep->oplock);
bcopy(&mode, mp->b_cont->b_rptr, sizeof (mode));
}
break;
case LB_SET_MODE:
rv = secpolicy_net_config(iocp->ioc_cr, B_FALSE);
if (rv != 0)
break;
if (iocp->ioc_count != sizeof (mode)) {
rv = EINVAL;
break;
}
bcopy(mp->b_cont->b_rptr, &mode, sizeof (mode));
mutex_enter(dmfep->oplock);
dmfep->opmode &= ~LOOPBACK_MODE_MASK;
switch (mode) {
case 2:
dmfep->opmode |= LOOPBACK_PHY_D;
break;
case 1:
dmfep->opmode |= LOOPBACK_INTERNAL;
break;
default:
break;
}
if (!dmfep->suspended) {
dmfe_restart(dmfep);
}
mutex_exit(dmfep->oplock);
break;
default:
rv = EINVAL;
break;
}
if (rv == 0) {
miocack(wq, mp, iocp->ioc_count, 0);
} else {
miocnak(wq, mp, 0, rv);
}
}
int
dmfe_m_getprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
void *val)
{
dmfe_t *dmfep = arg;
return (mii_m_getprop(dmfep->mii, name, num, sz, val));
}
int
dmfe_m_setprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
const void *val)
{
dmfe_t *dmfep = arg;
return (mii_m_setprop(dmfep->mii, name, num, sz, val));
}
static void
dmfe_m_propinfo(void *arg, const char *name, mac_prop_id_t num,
mac_prop_info_handle_t mph)
{
dmfe_t *dmfep = arg;
mii_m_propinfo(dmfep->mii, name, num, mph);
}
static void
dmfe_find_mac_address(dmfe_t *dmfep)
{
uchar_t *prop;
uint_t propsize;
int err;
bzero(dmfep->curr_addr, sizeof (dmfep->curr_addr));
err = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dmfep->devinfo,
DDI_PROP_DONTPASS, localmac_propname, &prop, &propsize);
if (err == DDI_PROP_SUCCESS) {
if (propsize == ETHERADDRL)
ethaddr_copy(prop, dmfep->curr_addr);
ddi_prop_free(prop);
} else {
dmfe_read_eeprom(dmfep, EEPROM_EN_ADDR, dmfep->curr_addr,
ETHERADDRL);
}
}
static int
dmfe_alloc_dma_mem(dmfe_t *dmfep, size_t memsize,
size_t setup, size_t slop, ddi_device_acc_attr_t *attr_p,
uint_t dma_flags, dma_area_t *dma_p)
{
ddi_dma_cookie_t dma_cookie;
uint_t ncookies;
int err;
err = ddi_dma_alloc_handle(dmfep->devinfo, &dma_attr,
DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl);
if (err != DDI_SUCCESS) {
dmfe_error(dmfep, "DMA handle allocation failed");
return (DDI_FAILURE);
}
err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize + setup + slop,
attr_p, dma_flags & (DDI_DMA_CONSISTENT | DDI_DMA_STREAMING),
DDI_DMA_SLEEP, NULL,
&dma_p->mem_va, &dma_p->alength, &dma_p->acc_hdl);
if (err != DDI_SUCCESS) {
dmfe_error(dmfep, "DMA memory allocation failed: %d", err);
return (DDI_FAILURE);
}
err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL,
dma_p->mem_va, dma_p->alength, dma_flags,
DDI_DMA_SLEEP, NULL, &dma_cookie, &ncookies);
if (err != DDI_DMA_MAPPED) {
dmfe_error(dmfep, "DMA mapping failed: %d", err);
return (DDI_FAILURE);
}
if ((dma_p->ncookies = ncookies) != 1) {
dmfe_error(dmfep, "Too many DMA cookeis: %d", ncookies);
return (DDI_FAILURE);
}
dma_p->mem_dvma = dma_cookie.dmac_address;
if (setup > 0) {
dma_p->setup_dvma = dma_p->mem_dvma + memsize;
dma_p->setup_va = dma_p->mem_va + memsize;
} else {
dma_p->setup_dvma = 0;
dma_p->setup_va = NULL;
}
return (DDI_SUCCESS);
}
static int
dmfe_alloc_bufs(dmfe_t *dmfep)
{
size_t memsize;
int err;
memsize = dmfep->tx.n_desc * sizeof (struct tx_desc_type);
err = dmfe_alloc_dma_mem(dmfep, memsize, SETUPBUF_SIZE, DMFE_SLOP,
&dmfe_reg_accattr, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
&dmfep->tx_desc);
if (err != DDI_SUCCESS) {
dmfe_error(dmfep, "TX descriptor allocation failed");
return (DDI_FAILURE);
}
memsize = dmfep->tx.n_desc * DMFE_BUF_SIZE;
err = dmfe_alloc_dma_mem(dmfep, memsize, 0, 0,
&dmfe_data_accattr, DDI_DMA_WRITE | DMFE_DMA_MODE,
&dmfep->tx_buff);
if (err != DDI_SUCCESS) {
dmfe_error(dmfep, "TX buffer allocation failed");
return (DDI_FAILURE);
}
memsize = dmfep->rx.n_desc * sizeof (struct rx_desc_type);
err = dmfe_alloc_dma_mem(dmfep, memsize, 0, DMFE_SLOP,
&dmfe_reg_accattr, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
&dmfep->rx_desc);
if (err != DDI_SUCCESS) {
dmfe_error(dmfep, "RX descriptor allocation failed");
return (DDI_FAILURE);
}
memsize = dmfep->rx.n_desc * DMFE_BUF_SIZE;
err = dmfe_alloc_dma_mem(dmfep, memsize, 0, 0,
&dmfe_data_accattr, DDI_DMA_READ | DMFE_DMA_MODE, &dmfep->rx_buff);
if (err != DDI_SUCCESS) {
dmfe_error(dmfep, "RX buffer allocation failed");
return (DDI_FAILURE);
}
dmfep->tx_mcast = kmem_zalloc(dmfep->tx.n_desc / NBBY, KM_SLEEP);
dmfep->tx_bcast = kmem_zalloc(dmfep->tx.n_desc / NBBY, KM_SLEEP);
return (DDI_SUCCESS);
}
static void
dmfe_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;
dma_p->mem_dvma = 0;
dma_p->setup_dvma = 0;
}
if (dma_p->acc_hdl != NULL) {
ddi_dma_mem_free(&dma_p->acc_hdl);
dma_p->acc_hdl = NULL;
dma_p->mem_va = NULL;
dma_p->setup_va = NULL;
}
}
static void
dmfe_free_bufs(dmfe_t *dmfep)
{
dmfe_free_dma_mem(&dmfep->rx_buff);
dmfe_free_dma_mem(&dmfep->rx_desc);
dmfe_free_dma_mem(&dmfep->tx_buff);
dmfe_free_dma_mem(&dmfep->tx_desc);
if (dmfep->tx_mcast)
kmem_free(dmfep->tx_mcast, dmfep->tx.n_desc / NBBY);
if (dmfep->tx_bcast)
kmem_free(dmfep->tx_bcast, dmfep->tx.n_desc / NBBY);
}
static void
dmfe_unattach(dmfe_t *dmfep)
{
if (dmfep->cycid != NULL) {
ddi_periodic_delete(dmfep->cycid);
dmfep->cycid = NULL;
}
if (dmfep->ksp_drv != NULL)
kstat_delete(dmfep->ksp_drv);
if (dmfep->progress & PROGRESS_HWINT) {
ddi_remove_intr(dmfep->devinfo, 0, dmfep->iblk);
}
if (dmfep->progress & PROGRESS_SOFTINT)
ddi_remove_softintr(dmfep->factotum_id);
if (dmfep->mii != NULL)
mii_free(dmfep->mii);
if (dmfep->progress & PROGRESS_MUTEX) {
mutex_destroy(dmfep->txlock);
mutex_destroy(dmfep->rxlock);
mutex_destroy(dmfep->oplock);
}
dmfe_free_bufs(dmfep);
if (dmfep->io_handle != NULL)
ddi_regs_map_free(&dmfep->io_handle);
kmem_free(dmfep, sizeof (*dmfep));
}
static int
dmfe_config_init(dmfe_t *dmfep, chip_id_t *idp)
{
ddi_acc_handle_t handle;
uint32_t regval;
if (pci_config_setup(dmfep->devinfo, &handle) != DDI_SUCCESS)
return (DDI_FAILURE);
idp->vendor = pci_config_get16(handle, PCI_CONF_VENID);
idp->device = pci_config_get16(handle, PCI_CONF_DEVID);
idp->revision = pci_config_get8(handle, PCI_CONF_REVID);
regval = pci_config_get32(handle, PCI_CONF_COMM);
pci_config_put32(handle, PCI_CONF_COMM, (regval | PCI_COMM_ME));
regval = pci_config_get32(handle, PCI_DMFE_CONF_CFDD);
pci_config_put32(handle, PCI_DMFE_CONF_CFDD,
regval & ~(CFDD_SLEEP | CFDD_SNOOZE));
pci_config_teardown(&handle);
return (DDI_SUCCESS);
}
struct ks_index {
int index;
char *name;
};
static const struct ks_index ks_drv_names[] = {
{ KS_INTERRUPT, "intr" },
{ KS_CYCLIC_RUN, "cyclic_run" },
{ KS_TX_STALL, "tx_stall_detect" },
{ KS_CHIP_ERROR, "chip_error_interrupt" },
{ KS_FACTOTUM_RUN, "factotum_run" },
{ KS_RECOVERY, "factotum_recover" },
{ -1, NULL }
};
static void
dmfe_init_kstats(dmfe_t *dmfep, int instance)
{
kstat_t *ksp;
kstat_named_t *knp;
const struct ks_index *ksip;
ksp = kstat_create(DRIVER_NAME, instance, "dmfe_events", "net",
KSTAT_TYPE_NAMED, KS_DRV_COUNT, KSTAT_FLAG_PERSISTENT);
if (ksp != NULL) {
for (knp = ksp->ks_data, ksip = ks_drv_names;
ksip->name != NULL; ++ksip) {
kstat_named_init(&knp[ksip->index], ksip->name,
KSTAT_DATA_UINT64);
}
dmfep->ksp_drv = ksp;
dmfep->knp_drv = knp;
kstat_install(ksp);
} else {
dmfe_error(dmfep, "kstat_create() for dmfe_events failed");
}
}
static int
dmfe_resume(dev_info_t *devinfo)
{
dmfe_t *dmfep;
chip_id_t chipid;
boolean_t restart = B_FALSE;
dmfep = ddi_get_driver_private(devinfo);
if (dmfep == NULL)
return (DDI_FAILURE);
if (dmfep->devinfo != devinfo)
return (DDI_FAILURE);
if (dmfe_config_init(dmfep, &chipid) != DDI_SUCCESS)
return (DDI_FAILURE);
if (chipid.vendor != dmfep->chipid.vendor)
return (DDI_FAILURE);
if (chipid.device != dmfep->chipid.device)
return (DDI_FAILURE);
if (chipid.revision != dmfep->chipid.revision)
return (DDI_FAILURE);
mutex_enter(dmfep->oplock);
mutex_enter(dmfep->txlock);
dmfep->suspended = B_FALSE;
mutex_exit(dmfep->txlock);
if (dmfep->mac_state == DMFE_MAC_STARTED) {
dmfe_restart(dmfep);
restart = B_TRUE;
}
mutex_exit(dmfep->oplock);
if (restart) {
mii_resume(dmfep->mii);
mac_tx_update(dmfep->mh);
}
return (DDI_SUCCESS);
}
static int
dmfe_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
{
mac_register_t *macp;
dmfe_t *dmfep;
uint32_t csr6;
int instance;
int err;
instance = ddi_get_instance(devinfo);
switch (cmd) {
default:
return (DDI_FAILURE);
case DDI_RESUME:
return (dmfe_resume(devinfo));
case DDI_ATTACH:
break;
}
dmfep = kmem_zalloc(sizeof (*dmfep), KM_SLEEP);
ddi_set_driver_private(devinfo, dmfep);
dmfep->devinfo = devinfo;
dmfep->dmfe_guard = DMFE_GUARD;
#if DMFEDEBUG
dmfep->debug = ddi_prop_get_int(DDI_DEV_T_ANY, devinfo, 0,
debug_propname, dmfe_debug);
#endif
dmfep->cycid = NULL;
(void) snprintf(dmfep->ifname, sizeof (dmfep->ifname), "dmfe%d",
instance);
csr6 = TX_THRESHOLD_HI | STORE_AND_FORWARD | EXT_MII_IF | OPN_25_MB1;
dmfep->opmode = ddi_prop_get_int(DDI_DEV_T_ANY, devinfo,
DDI_PROP_DONTPASS, opmode_propname, csr6);
if (dmfe_config_init(dmfep, &dmfep->chipid) != DDI_SUCCESS) {
dmfe_error(dmfep, "dmfe_config_init() failed");
goto attach_fail;
}
err = ddi_regs_map_setup(devinfo, DMFE_PCI_RNUMBER,
&dmfep->io_reg, 0, 0, &dmfe_reg_accattr, &dmfep->io_handle);
if (err != DDI_SUCCESS) {
dmfe_error(dmfep, "ddi_regs_map_setup() failed");
goto attach_fail;
}
dmfe_find_mac_address(dmfep);
dmfep->tx.n_desc = dmfe_tx_desc;
dmfep->rx.n_desc = dmfe_rx_desc;
err = dmfe_alloc_bufs(dmfep);
if (err != DDI_SUCCESS) {
goto attach_fail;
}
if (ddi_add_softintr(devinfo, DDI_SOFTINT_LOW, &dmfep->factotum_id,
NULL, NULL, dmfe_factotum, (caddr_t)dmfep) != DDI_SUCCESS) {
dmfe_error(dmfep, "ddi_add_softintr() failed");
goto attach_fail;
}
dmfep->progress |= PROGRESS_SOFTINT;
if (ddi_get_iblock_cookie(devinfo, 0, &dmfep->iblk) != DDI_SUCCESS) {
dmfe_error(dmfep, "ddi_get_iblock_cookie() failed");
goto attach_fail;
}
mutex_init(dmfep->milock, NULL, MUTEX_DRIVER, NULL);
mutex_init(dmfep->oplock, NULL, MUTEX_DRIVER, dmfep->iblk);
mutex_init(dmfep->rxlock, NULL, MUTEX_DRIVER, dmfep->iblk);
mutex_init(dmfep->txlock, NULL, MUTEX_DRIVER, dmfep->iblk);
dmfep->progress |= PROGRESS_MUTEX;
if (ddi_add_intr(devinfo, 0, NULL, NULL,
dmfe_interrupt, (caddr_t)dmfep) != DDI_SUCCESS) {
dmfe_error(dmfep, "ddi_add_intr() failed");
goto attach_fail;
}
dmfep->progress |= PROGRESS_HWINT;
dmfe_init_kstats(dmfep, instance);
mutex_enter(dmfep->oplock);
mutex_enter(dmfep->rxlock);
mutex_enter(dmfep->txlock);
dmfe_reset(dmfep);
bzero(dmfep->tx_desc.setup_va, SETUPBUF_SIZE);
bzero(dmfep->mcast_refs, MCASTBUF_SIZE);
dmfep->addr_set = B_FALSE;
dmfep->opmode &= ~(PROMISC_MODE | PASS_MULTICAST);
dmfep->mac_state = DMFE_MAC_RESET;
mutex_exit(dmfep->txlock);
mutex_exit(dmfep->rxlock);
mutex_exit(dmfep->oplock);
if (dmfe_init_phy(dmfep) != B_TRUE)
goto attach_fail;
(void) dmfe_m_unicst(dmfep, dmfep->curr_addr);
if ((macp = mac_alloc(MAC_VERSION)) == NULL)
goto attach_fail;
macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
macp->m_driver = dmfep;
macp->m_dip = devinfo;
macp->m_src_addr = dmfep->curr_addr;
macp->m_callbacks = &dmfe_m_callbacks;
macp->m_min_sdu = 0;
macp->m_max_sdu = ETHERMTU;
macp->m_margin = VLAN_TAGSZ;
err = mac_register(macp, &dmfep->mh);
mac_free(macp);
if (err != 0)
goto attach_fail;
ASSERT(dmfep->dmfe_guard == DMFE_GUARD);
ASSERT(dmfep->cycid == NULL);
dmfep->cycid = ddi_periodic_add(dmfe_cyclic, dmfep,
dmfe_tick_us * 1000, DDI_IPL_0);
return (DDI_SUCCESS);
attach_fail:
dmfe_unattach(dmfep);
return (DDI_FAILURE);
}
static int
dmfe_suspend(dmfe_t *dmfep)
{
mii_suspend(dmfep->mii);
mutex_enter(dmfep->oplock);
dmfe_stop(dmfep);
mutex_enter(dmfep->txlock);
dmfep->suspended = B_TRUE;
mutex_exit(dmfep->txlock);
mutex_exit(dmfep->oplock);
return (DDI_SUCCESS);
}
static int
dmfe_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
{
dmfe_t *dmfep;
dmfep = ddi_get_driver_private(devinfo);
switch (cmd) {
default:
return (DDI_FAILURE);
case DDI_SUSPEND:
return (dmfe_suspend(dmfep));
case DDI_DETACH:
break;
}
if (mac_unregister(dmfep->mh) != DDI_SUCCESS)
return (DDI_FAILURE);
dmfe_unattach(dmfep);
return (DDI_SUCCESS);
}
DDI_DEFINE_STREAM_OPS(dmfe_dev_ops, nulldev, nulldev, dmfe_attach, dmfe_detach,
nodev, NULL, D_MP, NULL, ddi_quiesce_not_supported);
static struct modldrv dmfe_modldrv = {
&mod_driverops,
dmfe_ident,
&dmfe_dev_ops
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&dmfe_modldrv, NULL
};
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
int
_init(void)
{
uint32_t tmp100;
uint32_t tmp10;
int i;
int status;
tmp100 = (dmfe_tx100_stall_us+dmfe_tick_us-1)/dmfe_tick_us;
tmp10 = (dmfe_tx10_stall_us+dmfe_tick_us-1)/dmfe_tick_us;
for (i = 0; i <= TX_PROCESS_MAX_STATE; ++i) {
switch (i) {
case TX_PROCESS_STATE(TX_PROCESS_FETCH_DATA):
case TX_PROCESS_STATE(TX_PROCESS_WAIT_END):
stall_100_tix[i] = tmp100;
stall_10_tix[i] = tmp10;
break;
case TX_PROCESS_STATE(TX_PROCESS_SUSPEND):
default:
stall_100_tix[i] = tmp100 * 20;
stall_10_tix[i] = tmp10 * 20;
break;
}
}
mac_init_ops(&dmfe_dev_ops, "dmfe");
status = mod_install(&modlinkage);
if (status == DDI_SUCCESS)
dmfe_log_init();
return (status);
}
int
_fini(void)
{
int status;
status = mod_remove(&modlinkage);
if (status == DDI_SUCCESS) {
mac_fini_ops(&dmfe_dev_ops);
dmfe_log_fini();
}
return (status);
}