/*
* Copyright (c) 2011 Jason King.
* Copyright (c) 2000 Berkeley Software Design, Inc.
* Copyright (c) 1997, 1998, 1999, 2000
* Bill Paul <wpaul@osd.bsdi.com>. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Bill Paul.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/ethernet.h>
#include <sys/mac_ether.h>
#include "pcn.h"
#include "pcnimpl.h"
static int pcn_ddi_resume(dev_info_t *);
static int pcn_quiesce(dev_info_t *);
static void pcn_teardown(pcn_t *);
static int pcn_m_unicast(void *, const uint8_t *);
static int pcn_m_promisc(void *, boolean_t);
static int pcn_m_start(void *);
static void pcn_m_stop(void *);
void *);
const void *);
static void pcn_m_propinfo(void *, const char *, mac_prop_id_t,
static int pcn_watchdog(pcn_t *);
static void pcn_mii_notify(void *, link_state_t);
static void pcn_destroybuf(pcn_buf_t *);
static int pcn_allocrxring(pcn_t *);
static int pcn_alloctxring(pcn_t *);
static void pcn_freetxring(pcn_t *);
static void pcn_freerxring(pcn_t *);
static void pcn_resetrings(pcn_t *);
static void pcn_resetall(pcn_t *);
static void pcn_startall(pcn_t *);
static void pcn_stopall(pcn_t *);
static void pcn_reclaim(pcn_t *);
static void pcn_getfactaddr(pcn_t *);
static void pcn_start_timer(pcn_t *);
static void pcn_stop_timer(pcn_t *);
static void pcn_error(dev_info_t *, char *, ...);
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
{ 0, 0, NULL }
};
};
NULL,
NULL, /* mc_getcapab */
NULL, /* mc_open */
NULL, /* mc_close */
};
"AMD PCnet",
};
{ &pcn_modldrv, NULL }
};
};
};
DMA_ATTR_V0, /* dm_attr_version */
0, /* dma_attr_addr_lo */
0xFFFFFFFFU, /* dma_attr_addr_hi */
0x7FFFFFFFU, /* dma_attr_count_max */
4, /* dma_attr_align */
0x3F, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0xFFFFFFFFU, /* dma_attr_maxxfer */
0xFFFFFFFFU, /* dma_attr_seg */
1, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0 /* dma_attr_flags */
};
DMA_ATTR_V0, /* dm_attr_version */
0, /* dma_attr_addr_lo */
0xFFFFFFFFU, /* dma_attr_addr_hi */
0x7FFFFFFFU, /* dma_attr_count_max */
16, /* dma_attr_align */
0x3F, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0xFFFFFFFFU, /* dma_attr_maxxfer */
0xFFFFFFFFU, /* dma_attr_seg */
1, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0 /* dma_attr_flags */
};
/*
* DDI entry points
*/
int
_init(void)
{
int rc;
return (rc);
}
return (rc);
}
int
_fini(void)
{
int rc;
}
return (rc);
}
int
{
}
int
{
int rc;
switch (cmd) {
case DDI_RESUME:
return (pcn_ddi_resume(dip));
case DDI_ATTACH:
break;
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
if (ddi_intr_hilevel(dip, 0) != 0) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* Enable bus master, IO space, and memory space accesses
*/
goto fail;
}
DDI_SUCCESS) {
goto fail;
}
goto fail;
/* XXX: need to set based on device */
goto fail;
}
if (rc != DDI_SUCCESS)
goto fail;
DDI_SUCCESS) {
goto fail;
}
goto fail;
}
return (DDI_SUCCESS);
}
return (DDI_SUCCESS);
fail:
return (DDI_FAILURE);
}
int
{
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
return (DDI_FAILURE);
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
int
{
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
if (IS_RUNNING(pcnp))
return (DDI_SUCCESS);
}
int
{
return (DDI_FAILURE);
/* don't want to take the chance of blocking */
~(PCN_EXTCTL1_SINTEN));
return (DDI_SUCCESS);
}
static void
{
}
/* These will exit gracefully if not yet allocated */
}
/*
* Drains any FIFOs in the card, then pauses it
*/
static void
{
int i;
for (i = 0; i < 5000; i++) {
return;
drv_usecwait(1000);
}
}
static void
{
}
static int
{
int index;
/*
* PCNet uses the upper 6 bits of the CRC of the macaddr
* to index into a 64bit mask
*/
crc >>= 26;
if (add) {
} else {
}
}
return (0);
}
static int
{
if (IS_RUNNING(pcnp))
/* set promiscuous mode */
if (pcnp->pcn_promisc)
else
if (IS_RUNNING(pcnp))
return (0);
}
static int
{
int i;
if (IS_RUNNING(pcnp))
for (i = 0; i < 3; i++)
if (IS_RUNNING(pcnp))
return (0);
}
static mblk_t *
{
}
return (NULL);
}
break;
}
}
return (mp);
}
static boolean_t
{
int txsend;
if (len > ETHERVLANMTU) {
return (B_TRUE);
}
if (pcnp->pcn_txavail == 0) {
/* enable tx interrupt */
return (B_FALSE);
}
/*
* We copy the packet to a single buffer. NetBSD sources suggest
* that if multiple segements are ever used, VMware has a bug that will
* only allow 8 segments to be used, while the physical chips allow 16
*/
pcnp->pcn_opackets++;
pcnp->pcn_multixmt++;
else
pcnp->pcn_brdcstxmt++;
}
tmd->pcn_txstat = 0;
/* PCNet wants the 2's complement of the length of the buffer */
pcnp->pcn_txavail--;
return (B_TRUE);
}
static void
{
/* sync before reading */
/* check if chip is still working on it */
break;
pcnp->pcn_txavail++;
}
/* Disable TX interrupt */
}
}
}
static unsigned
{
if (IS_SUSPENDED(pcnp)) {
return (DDI_INTR_UNCLAIMED);
}
if (status & PCN_CSR_TINT) {
}
if (status & PCN_CSR_RINT)
if (status & PCN_CSR_ERR) {
break;
}
/* timer interrupt */
if (status2 & PCN_EXTCTL2_STINT) {
/* ack it */
break;
}
}
}
if (do_reset) {
} else {
}
if (mp)
return (DDI_INTR_CLAIMED);
}
static mblk_t *
{
break;
pcnp->pcn_errrcv++;
pcnp->pcn_align_errors++;
pcnp->pcn_overflow++;
pcnp->pcn_fcs_errors++;
} else if (len > ETHERVLANMTU) {
pcnp->pcn_errrcv++;
} else {
pcnp->pcn_errrcv++;
pcnp->pcn_norcvbuf++;
goto skip;
}
pcnp->pcn_ipackets++;
pcnp->pcn_rbytes++;
pcnp->pcn_brdcstrcv++;
else
pcnp->pcn_multircv++;
}
}
skip:
}
return (mpchain);
}
static void
{
return;
}
static int
{
return (0);
}
static void
{
}
static int
{
int i;
/*
* Issue a reset by reading from the RESET register.
* Note that we don't know if the chip is operating in
* 16-bit or 32-bit mode at this point, so we attempt
* to reset the chip both ways. If one fails, the other
* will succeed.
*/
drv_usecwait(1000);
/* Select 32-bit (DWIO) mode */
/* The timer is not affected by a reset, so explicitly disable */
/* Enable fast suspend */
/* Select Style 3 descriptors */
/* Set MAC address */
if (getfact)
/* Clear PCN_MISC_ASEL so we can set the port via PCN_CSR_MODE. */
/*
* XXX: need to find a way to determine when 10bt media is
* selected for non Am79C978, and set to PCN_PORT_10BASET
* instead of PCN_PORT_MII
*/
/* Reenable auto negotiation for external phy */
if (pcnp->pcn_promisc)
/* Initalize mcast addr filter */
for (i = 0; i < 4; i++)
/* We're not using the initialization block. */
/*
* Enable burst read and write. Also set the no underflow
* bit. This will avoid transmit underruns in ceratin
* conditions while still providing decent performance.
*/
/* Enable graceful recovery from underflow. */
/* Enable auto-padding of short TX frames. */
return (DDI_SUCCESS);
}
static void
{
}
static void
{
/* Start chip and enable interrupts */
if (IS_RUNNING(pcnp))
}
static void
{
}
/*
* The soft timer is not affected by a soft reset (according to the datasheet)
* so it must always be explicitly enabled and disabled
*/
static void
{
/*
* The frequency this fires varies based on the particular
* model, this value is largely arbitrary. It just needs to
* fire often enough to detect a stall
*/
}
static void
{
}
static int
{
return (0);
switch (stat) {
case MAC_STAT_MULTIRCV:
break;
case MAC_STAT_BRDCSTRCV:
break;
case MAC_STAT_MULTIXMT:
break;
case MAC_STAT_BRDCSTXMT:
break;
case MAC_STAT_IPACKETS:
break;
case MAC_STAT_RBYTES:
break;
case MAC_STAT_OPACKETS:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_NORCVBUF:
break;
case MAC_STAT_NOXMTBUF:
*val = 0;
break;
case MAC_STAT_COLLISIONS:
break;
case MAC_STAT_IERRORS:
break;
case MAC_STAT_OERRORS:
break;
case ETHER_STAT_ALIGN_ERRORS:
break;
case ETHER_STAT_FCS_ERRORS:
break;
case ETHER_STAT_SQE_ERRORS:
break;
case ETHER_STAT_DEFER_XMTS:
break;
break;
break;
break;
case ETHER_STAT_EX_COLLISIONS:
break;
case ETHER_STAT_MACXMT_ERRORS:
break;
break;
break;
case ETHER_STAT_MACRCV_ERRORS:
break;
case MAC_STAT_OVERFLOWS:
break;
case MAC_STAT_UNDERFLOWS:
break;
break;
case ETHER_STAT_JABBER_ERRORS:
break;
default:
return (ENOTSUP);
}
return (0);
}
static int
void *val)
{
}
static int
const void *val)
{
}
static void
{
}
static int
{
if ((pcnp->pcn_txstall_time != 0) &&
pcnp->pcn_txstall_time = 0;
return (DDI_FAILURE);
} else {
return (DDI_SUCCESS);
}
}
static uint16_t
{
/*
* At least Am79C971 with DP83840A wedge when isolating the
* external PHY so we can't allow multiple external PHYs.
* There are cards that use Am79C971 with both the internal
* and an external PHY though.
* For internal PHYs it doesn't really matter whether we can
* isolate the remaining internal and the external ones in
* the PHY drivers as the internal PHYs have to be enabled
* individually in PCN_BCR_PHYSEL, PCN_CSR_MODE, etc.
* With Am79C97{3,5,8} we don't support switching beetween
* the internal and external PHYs, yet, so we can't allow
* multiple PHYs with these either.
* Am79C97{2,6} actually only support external PHYs (not
* connectable internal ones respond at the usual addresses,
* which don't hurt if we let them show up on the bus) and
* isolating them works.
*/
return (0);
}
if (val == 0xFFFF) {
return (0);
}
return (val);
}
static void
{
}
static void
{
}
static const pcn_type_t *
{
const pcn_type_t *t;
t = pcn_devs;
return (t);
t++;
}
return (NULL);
}
static void
{
}
static uint32_t
{
return (val);
}
static uint16_t
{
return (val);
}
static void
{
}
static uint32_t
{
return (val);
}
static uint16_t
{
return (val);
}
static void
{
}
static void
{
int i;
pcnp->pcn_rxhead = 0;
pcnp->pcn_txreclaim = 0;
pcnp->pcn_txsend = 0;
/* reset rx descriptor values */
for (i = 0; i < PCN_RXRING; i++) {
}
/* reset tx descriptor values */
for (i = 0; i < PCN_TXRING; i++) {
}
/* set addresses of decriptors */
/* set the ring sizes */
}
static void
{
return;
}
static pcn_buf_t *
{
unsigned ccnt;
return (NULL);
}
return (NULL);
}
&ccnt) != DDI_DMA_MAPPED) {
return (NULL);
}
return (buf);
}
static int
{
int rval;
int i;
unsigned ncookies;
if (rval != DDI_SUCCESS) {
"descriptors");
return (DDI_FAILURE);
}
&pcnp->pcn_txdesc_acch);
if (rval != DDI_SUCCESS) {
"descriptors");
return (DDI_FAILURE);
}
&ncookies);
if (rval != DDI_DMA_MAPPED) {
"descriptors");
return (DDI_FAILURE);
}
KM_SLEEP);
for (i = 0; i < PCN_TXRING; i++) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
{
int rval;
int i;
unsigned ncookies;
if (rval != DDI_SUCCESS) {
"descriptors");
return (DDI_FAILURE);
}
&pcnp->pcn_rxdesc_acch);
if (rval != DDI_SUCCESS) {
"descriptors");
return (DDI_FAILURE);
}
&ncookies);
if (rval != DDI_DMA_MAPPED) {
"descriptors");
return (DDI_FAILURE);
}
KM_SLEEP);
for (i = 0; i < PCN_RXRING; i++) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
{
int i;
if (pcnp->pcn_txbufs) {
for (i = 0; i < PCN_TXRING; i++)
}
if (pcnp->pcn_txdesc_paddr)
if (pcnp->pcn_txdesc_acch)
if (pcnp->pcn_txdesc_dmah)
}
static void
{
int i;
if (pcnp->pcn_rxbufs) {
for (i = 0; i < PCN_RXRING; i++)
}
if (pcnp->pcn_rxdesc_paddr)
if (pcnp->pcn_rxdesc_acch)
if (pcnp->pcn_rxdesc_dmah)
}
static int
{
/*
* Note: we can *NOT* put the chip into 32-bit mode yet. If a
* lance ethernet device is present and pcn tries to attach, it can
* hang the device (requiring a hardware reset), since they only work
* in 16-bit mode.
*
* The solution is check using 16-bit operations first, and determine
* if 32-bit mode operations are supported.
*
* The safest way to do this is to read the PCI subsystem ID from
* BCR23/24 and compare that with the value read from PCI config
* space.
*/
chipid <<= 16;
/*
* The test for 0x10001000 is a hack to pacify VMware, who's
* pseudo-PCnet interface is broken. Reading the subsystem register
* from PCI config space yields 0x00000000 while reading the same value
* from I/O space yields 0x10001000. It's not supposed to be that way.
*/
/* We're in 16-bit mode. */
chipid <<= 16;
} else {
chipid <<= 16;
}
/* Set default value and override as needed */
switch (chipid) {
case Am79C970:
name = "Am79C970 PCnet-PCI";
return (DDI_FAILURE);
case Am79C970A:
name = "Am79C970A PCnet-PCI II";
return (DDI_FAILURE);
case Am79C971:
name = "Am79C971 PCnet-FAST";
break;
case Am79C972:
name = "Am79C972 PCnet-FAST+";
break;
case Am79C973:
name = "Am79C973 PCnet-FAST III";
break;
case Am79C975:
name = "Am79C975 PCnet-FAST III";
break;
case Am79C976:
name = "Am79C976";
break;
case Am79C978:
name = "Am79C978";
break;
default:
name = "Unknown";
}
name) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
{
if (dip)
else
}