/* $NetBSD: lan9118.c,v 1.36 2020/01/29 14:53:40 thorpej Exp $ */
/*
* Copyright (c) 2008 KIYOHARA Takashi
* 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
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: lan9118.c,v 1.36 2020/01/29 14:53:40 thorpej Exp $");
/*
* The LAN9118 Family
* * The LAN9118 is targeted for 32-bit applications requiring high
* performance, and provides the highest level of performance possible for
* a non-PCI 10/100 Ethernet controller.
*
* * The LAN9117 is designed to provide the highest level of performance
* possible for 16-bit applications. It also has an external MII interface,
* which can be used to attach an external PHY.
*
* * The LAN9116 and LAN9115 are designed for performance-sensitive
* applications with less intensive performance requirements. The LAN9116
* is for 32-bit host processors, while the LAN9115 is for 16-bit
* applications, which may also require an external PHY. Both devices
* deliver superior levels of performance.
*
* The LAN9218 Family
* Also support HP Auto-MDIX.
*/
/*
* Number of instance of Internal PHY is always 0. External PHY
* number that above.
*/
mii_attach(sc->sc_dev, mii, 0xffffffff, 1, MII_OFFSET_ANY, 0);
if (sc->sc_id == LAN9118_ID_9115 || sc->sc_id == LAN9118_ID_9117 ||
sc->sc_id == LAN9218_ID_9215 || sc->sc_id == LAN9218_ID_9217) {
if (bus_space_read_4(sc->sc_iot, sc->sc_ioh, LAN9118_HW_CFG) &
LAN9118_HW_CFG_EXT_PHY_DET) {
/*
* We always have a internal PHY at phy1.
* In addition, external PHY is attached.
*/
DPRINTFN(1, ("%s: detect External PHY\n", __func__));
/* Switch MII and SMI */
bus_space_write_4(sc->sc_iot, sc->sc_ioh,
LAN9118_HW_CFG,
LAN9118_HW_CFG_MBO |
LAN9118_HW_CFG_PHY_CLK_SEL_CD);
delay(1); /* Wait 5 cycle */
bus_space_write_4(sc->sc_iot, sc->sc_ioh,
LAN9118_HW_CFG,
LAN9118_HW_CFG_MBO |
LAN9118_HW_CFG_PHY_CLK_SEL_EMII |
LAN9118_HW_CFG_SMI_SEL |
LAN9118_HW_CFG_EXT_PHY_EN);
delay(1); /* Once wait more 5 cycle */
/* Call mii_attach, avoid at phy1. */
mii_attach(sc->sc_dev, mii, 0xffffffff,
0, MII_OFFSET_ANY, 0);
for (i = 2; i < MII_NPHY; i++)
mii_attach(sc->sc_dev, mii, 0xffffffff,
i, MII_OFFSET_ANY, 0);
}
}
if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
return;
totlen = 0;
for (;;) {
IFQ_POLL(&ifp->if_snd, m0);
if (m0 == NULL)
break;
tdfree = LAN9118_TX_FIFO_INF_TDFREE(bus_space_read_4(sc->sc_iot,
sc->sc_ioh, LAN9118_TX_FIFO_INF));
if (tdfree < 2036) {
/*
* 2036 is the possible maximum FIFO consumption
* for the most fragmented frame.
*/
ifp->if_flags |= IFF_OACTIVE;
break;
}
IFQ_DEQUEUE(&ifp->if_snd, m0);
/*
* WE ARE NOW COMMITTED TO TRANSMITTING THE PACKET.
*/
/*
* Check mbuf chain -- "middle" buffers must be >= 4 bytes
* and maximum # of buffers is 86.
*/
m = m0;
n = 0;
while (m) {
if (m->m_len < 4 || ++n > 86) {
/* Copy mbuf chain. */
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL)
goto discard; /* discard packet */
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_freem(m);
goto discard; /* discard packet */
}
m_copydata(m0, 0, m0->m_pkthdr.len,
mtod(m, void *));
m->m_pkthdr.len = m->m_len = m0->m_pkthdr.len;
m_freem(m0);
m0 = m;
break;
}
m = m->m_next;
}
if (ifp->if_flags & IFF_ALLMULTI)
mac_cr |= LAN9118_MAC_CR_MCPAS;
else {
ETHER_LOCK(ec);
ETHER_FIRST_MULTI(step, ec, enm);
while (enm != NULL) {
if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
ETHER_ADDR_LEN) != 0) {
/*
* We must listen to a range of multicast
* addresses. For now, just accept all
* multicasts, rather than trying to set
* only those filter bits needed to match
* the range. (At this time, the only use
* of address ranges is for IP multicast
* routing, for which the range is big enough
* to require all bits set.)
*/
ifp->if_flags |= IFF_ALLMULTI;
mac_cr |= LAN9118_MAC_CR_MCPAS;
break;
}
h = ether_crc32_le(enm->enm_addrlo,
ETHER_ADDR_LEN) >> 26;
hashes[h >> 5] |= 1 << (h & 0x1f);
static void
lan9118_rxintr(struct lan9118_softc *sc)
{
struct ifnet *ifp = &sc->sc_ec.ec_if;
struct mbuf *m;
uint32_t rx_fifo_inf, rx_status;
int pktlen;
const int pad = ETHER_HDR_LEN % sizeof(uint32_t);
DPRINTFN(3, ("%s\n", __func__));
for (;;) {
rx_fifo_inf = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
LAN9118_RX_FIFO_INF);
if (LAN9118_RX_FIFO_INF_RXSUSED(rx_fifo_inf) == 0)
break;
rx_status = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
LAN9118_RXSFIFOP);
pktlen = LAN9118_RXS_PKTLEN(rx_status);
DPRINTFN(3, ("%s: rx_status=0x%x(pktlen %d)\n",
__func__, rx_status, pktlen));
if (rx_status & (LAN9118_RXS_ES | LAN9118_RXS_LENERR |
LAN9118_RXS_RWTO | LAN9118_RXS_MIIERR | LAN9118_RXS_DBIT)) {
if (rx_status & LAN9118_RXS_LENERR)
aprint_error_dev(sc->sc_dev, "Length Error\n");
if (rx_status & LAN9118_RXS_RUNTF)
aprint_error_dev(sc->sc_dev, "Runt Frame\n");
if (rx_status & LAN9118_RXS_FTL)
aprint_error_dev(sc->sc_dev,
"Frame Too Long\n");
if (rx_status & LAN9118_RXS_RWTO)
aprint_error_dev(sc->sc_dev,
"Receive Watchdog time-out\n");
if (rx_status & LAN9118_RXS_MIIERR)
aprint_error_dev(sc->sc_dev, "MII Error\n");
if (rx_status & LAN9118_RXS_DBIT)
aprint_error_dev(sc->sc_dev, "Drabbling Bit\n");
if (rx_status & LAN9118_RXS_COLS)
aprint_error_dev(sc->sc_dev,
"Collision Seen\n");
if (rx_status & LAN9118_RXS_CRCERR)
aprint_error_dev(sc->sc_dev, "CRC Error\n");
dropit:
if_statinc(ifp, if_ierrors);
/*
* Receive Data FIFO Fast Forward
* When performing a fast-forward, there must be at
* least 4 DWORDs of data in the RX data FIFO for the
* packet being discarded.
*/
if (pktlen >= 4 * sizeof(uint32_t)) {
uint32_t rx_dp_ctl;
bus_space_write_4(sc->sc_iot, sc->sc_ioh,
LAN9118_RX_DP_CTL,
LAN9118_RX_DP_CTL_RX_FFWD);
/* DP_FFWD bit is self clearing */
do {
rx_dp_ctl = bus_space_read_4(sc->sc_iot,
sc->sc_ioh, LAN9118_RX_DP_CTL);
} while (rx_dp_ctl & LAN9118_RX_DP_CTL_RX_FFWD);
} else {
/* For less than 4 DWORDs do not use RX_FFWD. */
uint32_t garbage[4];
for (;;) {
tx_fifo_inf = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
LAN9118_TX_FIFO_INF);
if (LAN9118_TX_FIFO_INF_TXSUSED(tx_fifo_inf) == 0)
break;
tx_status = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
LAN9118_TXSFIFOP);
DPRINTFN(3, ("%s: tx_status=0x%x\n", __func__, tx_status));
if (tx_status & LAN9118_TXS_ES) {
if (tx_status & LAN9118_TXS_LOC)
aprint_error_dev(sc->sc_dev,
"Loss Of Carrier\n");
if ((tx_status & LAN9118_TXS_NC) && !fdx)
aprint_error_dev(sc->sc_dev, "No Carrier\n");
if (tx_status & LAN9118_TXS_LCOL)
aprint_error_dev(sc->sc_dev,
"Late Collision\n");
if (tx_status & LAN9118_TXS_ECOL) {
/* Rearch 16 collision */
if_statadd(ifp, if_collisions, 16);
aprint_error_dev(sc->sc_dev,
"Excessive Collision\n");
}
if (LAN9118_TXS_COLCNT(tx_status) != 0)
aprint_error_dev(sc->sc_dev,
"Collision Count: %d\n",
LAN9118_TXS_COLCNT(tx_status));
if (tx_status & LAN9118_TXS_ED)
aprint_error_dev(sc->sc_dev,
"Excessive Deferral\n");
if (tx_status & LAN9118_TXS_DEFERRED)
aprint_error_dev(sc->sc_dev, "Deferred\n");
if_statinc(ifp, if_oerrors);
} else
if_statinc(ifp, if_opackets);
}
tdfree = LAN9118_TX_FIFO_INF_TDFREE(tx_fifo_inf);
if (tdfree == LAN9118_TX_DATA_FIFO_SIZE)
/* FIFO empty */
ifp->if_timer = 0;
if (tdfree >= 2036)
/*
* 2036 is the possible maximum FIFO consumption
* for the most fragmented frame.
*/
ifp->if_flags &= ~IFF_OACTIVE;
}