/*-
* Copyright (c) 2000 Tsubai Masanari. 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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.
*/
/*
* If BPF is listening on this interface, let it see the
* packet before we commit it to the wire.
*/
bpf_mtap(ifp, m, BPF_D_OUT);
m_freem(m);
i++;
if (i == NTXBUF)
i = 0;
if (i == gmac_read_reg(sc, GMAC_TXDMACOMPLETE)) {
sc->sc_txbusy = true;
break;
}
}
}
int
gmac_put(struct gmac_softc *sc, void *buff, struct mbuf *m)
{
int len, tlen = 0;
for (; m; m = m->m_next) {
len = m->m_len;
if (len == 0)
continue;
memcpy(buff, mtod(m, void *), len);
buff = (char *)buff + len;
tlen += len;
}
if (tlen > 2048)
panic("%s: gmac_put packet overflow", device_xname(sc->sc_dev));
return tlen;
}
void
gmac_reset(struct gmac_softc *sc)
{
int i, s;
s = splnet();
gmac_stop_txdma(sc);
gmac_stop_rxdma(sc);
gmac_write_reg(sc, GMAC_SOFTWARERESET, 3);
for (i = 10; i > 0; i--) {
delay(300000); /* XXX long delay */
if ((gmac_read_reg(sc, GMAC_SOFTWARERESET) & 3) == 0)
break;
}
if (i == 0)
aprint_error_dev(sc->sc_dev, "reset timeout\n");
sc->sc_txnext = 0;
sc->sc_rxlast = 0;
for (i = 0; i < NRXBUF; i++)
sc->sc_rxlist[i].cmd = htole32(GMAC_OWN);
__asm volatile ("sync");
/* Clear hash table */
for (i = 0; i < 16; i++)
hash[i] = 0;
/* Get current RX configuration */
v = gmac_read_reg(sc, GMAC_RXMACCONFIG);
if ((ifp->if_flags & IFF_PROMISC) != 0) {
/* Turn on promiscuous mode; turn off the hash filter */
v |= GMAC_RXMAC_PR;
v &= ~GMAC_RXMAC_HEN;
ifp->if_flags |= IFF_ALLMULTI;
goto chipit;
}
/* Turn off promiscuous mode; turn on the hash filter */
v &= ~GMAC_RXMAC_PR;
v |= GMAC_RXMAC_HEN;
/*
* Set up multicast address filter by passing all multicast addresses
* through a crc generator, and then using the high order 8 bits as an
* index into the 256 bit logical address filter. The high order bit
* selects the word, while the rest of the bits select the bit within
* the word.
*/
ETHER_LOCK(ec);
ETHER_FIRST_MULTI(step, ec, enm);
while (enm != NULL) {
if (memcmp(enm->enm_addrlo, enm->enm_addrhi, 6)) {
/*
* 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.)
*/
for (i = 0; i < 16; i++)
hash[i] = 0xffff;
ifp->if_flags |= IFF_ALLMULTI;
ETHER_UNLOCK(ec);
goto chipit;
}
case SIOCSIFFLAGS:
if ((error = ifioctl_common(ifp, cmd, data)) != 0)
break;
/* XXX see the comment in ed_ioctl() about code re-use */
if ((ifp->if_flags & IFF_UP) == 0 &&
(ifp->if_flags & IFF_RUNNING) != 0) {
/*
* If interface is marked down and it is running, then
* stop it.
*/
gmac_stop(sc);
ifp->if_flags &= ~IFF_RUNNING;
} else if ((ifp->if_flags & IFF_UP) != 0 &&
(ifp->if_flags & IFF_RUNNING) == 0) {
/*
* If interface is marked up and it is stopped, then
* start it.
*/
gmac_init(sc);
} else {
/*
* Reset the interface to pick up changes in any other
* flags that affect hardware registers.
*/
gmac_reset(sc);
gmac_init(sc);
}
#ifdef GMAC_DEBUG
if (ifp->if_flags & IFF_DEBUG)
sc->sc_flags |= GMAC_DEBUGFLAG;
#endif
break;
default:
if ((error = ether_ioctl(ifp, cmd, data)) == ENETRESET) {
/*
* Multicast list has changed; set the hardware filter
* accordingly.
*/
if (ifp->if_flags & IFF_RUNNING) {
gmac_init(sc);
/* gmac_setladrf(sc); */
}
error = 0;
}
break;
}
for (i = 1000; i >= 0; i -= 10) {
if (gmac_read_reg(sc, GMAC_MIFFRAMEOUTPUT) & 0x10000)
break;
delay(10);
}
if (i < 0) {
aprint_error_dev(sc->sc_dev, "gmac_mii_readreg: timeout\n");
return ETIMEDOUT;
}
for (i = 1000; i >= 0; i -= 10) {
if (gmac_read_reg(sc, GMAC_MIFFRAMEOUTPUT) & 0x10000)
break;
delay(10);
}
if (i < 0) {
aprint_error_dev(sc->sc_dev, "gmac_mii_writereg: timeout\n");
return ETIMEDOUT;
}