/*
* Copyright (c) 2004, SUNET, Swedish University Computer Network.
* All rights reserved.
*
* Written by Anders Magnusson for SUNET, Swedish University Computer Network.
*
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed for the NetBSD Project by
* SUNET, Swedish University Computer Network.
* 4. The name of SUNET may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY SUNET ``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 SUNET
* 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.
*/
/*
* Device driver for the S2io Xframe Ten Gigabit Ethernet controller.
*
* TODO (in no specific order):
* HW VLAN support.
* IPv6 HW cksum.
*/
/*
* Use clever macros to avoid a bunch of #ifdef's.
*/
#define XCONCAT3(x, y, z) x ## y ## z
#define CONCAT3(x, y, z) XCONCAT3(x, y, z)
#define NDESC_BUFMODE CONCAT3(NDESC_, RX_MODE, BUFMODE)
#define rxd_4k CONCAT3(rxd, RX_MODE, _4k)
#define rxdesc ___CONCAT(rxd, RX_MODE)
/*
* The MAC addr may be all FF's, which is not good.
* Resolve it by writing some magics to GPIO_CONTROL and
* force a chip reset to read in the serial eeprom again.
*/
for (i = 0; i < sizeof(fix_mac)/sizeof(fix_mac[0]); i++) {
PIF_WCSR(GPIO_CONTROL, fix_mac[i]);
PIF_RCSR(GPIO_CONTROL);
}
/*
* Reset the chip and restore the PCI registers.
*/
PIF_WCSR(SW_RESET, 0xa5a5a50000000000ULL);
DELAY(500000);
for (i = 0; i < 64; i += 4)
pci_conf_write(pa->pa_pc, pa->pa_tag, i, sc->sc_pciregs[i/4]);
/*
* Restore the byte order registers.
*/
#if BYTE_ORDER == LITTLE_ENDIAN
val = (uint64_t)0xFFFFFFFFFFFFFFFFULL;
val &= ~(TxF_R_SE | RxF_W_SE);
PIF_WCSR(SWAPPER_CTRL, val);
PIF_WCSR(SWAPPER_CTRL, val);
#elif BYTE_ORDER == BIG_ENDIAN
/* do nothing */
#else
#error bad endianness!
#endif
/*
* Create transmit DMA maps.
* Make them large for TSO.
*/
for (i = 0; i < NTXDESCS; i++) {
if (bus_dmamap_create(sc->sc_dmat, XGE_IP_MAXPACKET,
NTXFRAGS, MCLBYTES, 0, 0, &sc->sc_txm[i])) {
aprint_error("%s: cannot create TX DMA maps\n", XNAME);
return;
}
}
sc->sc_lasttx = NTXDESCS-1;
/*
* RxDMA initialization.
* Only use one out of 8 possible receive queues.
*/
if (xge_alloc_rxmem(sc)) { /* allocate rx descriptor memory */
aprint_error("%s: failed allocating rxmem\n", XNAME);
return;
}
/* Create receive buffer DMA maps */
for (i = 0; i < NRXREAL; i++) {
if (bus_dmamap_create(sc->sc_dmat, XGE_MAX_MTU,
NRXFRAGS, MCLBYTES, 0, 0, &sc->sc_rxm[i])) {
aprint_error("%s: cannot create RX DMA maps\n", XNAME);
return;
}
}
/* allocate mbufs to receive descriptors */
for (i = 0; i < NRXREAL; i++)
if (xge_add_rxbuf(sc, i))
panic("out of mbufs too early");
/* 14, setup receive ring priority */
PIF_WCSR(RX_QUEUE_PRIORITY, 0ULL); /* only use one ring */
/* 15, setup receive ring round-robin calendar */
PIF_WCSR(RX_W_ROUND_ROBIN_0, 0ULL); /* only use one ring */
PIF_WCSR(RX_W_ROUND_ROBIN_1, 0ULL);
PIF_WCSR(RX_W_ROUND_ROBIN_2, 0ULL);
PIF_WCSR(RX_W_ROUND_ROBIN_3, 0ULL);
PIF_WCSR(RX_W_ROUND_ROBIN_4, 0ULL);
/* 16, write receive ring start address */
PIF_WCSR(PRC_RXD0_0, (uint64_t)sc->sc_rxmap->dm_segs[0].ds_addr);
/* PRC_RXD0_[1-7] are not used */
default:
if ((error = ether_ioctl(ifp, cmd, data)) != ENETRESET)
break;
error = 0;
if (cmd != SIOCADDMULTI && cmd != SIOCDELMULTI)
;
else if (ifp->if_flags & IFF_RUNNING) {
/* Change multicast list */
xge_mcast_filter(sc);
}
break;
}
splx(s);
return error;
}
void
xge_mcast_filter(struct xge_softc *sc)
{
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
struct ethercom *ec = &sc->sc_ethercom;
struct ether_multi *enm;
struct ether_multistep step;
int i, numaddr = 1; /* first slot used for card unicast address */
uint64_t val;
ETHER_LOCK(ec);
ETHER_FIRST_MULTI(step, ec, enm);
while (enm != NULL) {
if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) {
/* Skip ranges */
ETHER_UNLOCK(ec);
goto allmulti;
}
if (numaddr == MAX_MCAST_ADDR) {
ETHER_UNLOCK(ec);
goto allmulti;
}
for (val = 0, i = 0; i < ETHER_ADDR_LEN; i++) {
val <<= 8;
val |= enm->enm_addrlo[i];
}
PIF_WCSR(RMAC_ADDR_DATA0_MEM, val << 16);
PIF_WCSR(RMAC_ADDR_DATA1_MEM, 0xFFFFFFFFFFFFFFFFULL);
PIF_WCSR(RMAC_ADDR_CMD_MEM, RMAC_ADDR_CMD_MEM_WE |
RMAC_ADDR_CMD_MEM_STR | RMAC_ADDR_CMD_MEM_OFF(numaddr));
while (PIF_RCSR(RMAC_ADDR_CMD_MEM) & RMAC_ADDR_CMD_MEM_STR)
;
numaddr++;
ETHER_NEXT_MULTI(step, enm);
}
ETHER_UNLOCK(ec);
/* set the remaining entries to the broadcast address */
for (i = numaddr; i < MAX_MCAST_ADDR; i++) {
PIF_WCSR(RMAC_ADDR_DATA0_MEM, 0xffffffffffff0000ULL);
PIF_WCSR(RMAC_ADDR_DATA1_MEM, 0xFFFFFFFFFFFFFFFFULL);
PIF_WCSR(RMAC_ADDR_CMD_MEM, RMAC_ADDR_CMD_MEM_WE |
RMAC_ADDR_CMD_MEM_STR | RMAC_ADDR_CMD_MEM_OFF(i));
while (PIF_RCSR(RMAC_ADDR_CMD_MEM) & RMAC_ADDR_CMD_MEM_STR)
;
}
ifp->if_flags &= ~IFF_ALLMULTI;
return;
allmulti:
/* Just receive everything with the multicast bit set */
ifp->if_flags |= IFF_ALLMULTI;
PIF_WCSR(RMAC_ADDR_DATA0_MEM, 0x8000000000000000ULL);
PIF_WCSR(RMAC_ADDR_DATA1_MEM, 0xF000000000000000ULL);
PIF_WCSR(RMAC_ADDR_CMD_MEM, RMAC_ADDR_CMD_MEM_WE |
RMAC_ADDR_CMD_MEM_STR | RMAC_ADDR_CMD_MEM_OFF(1));
while (PIF_RCSR(RMAC_ADDR_CMD_MEM) & RMAC_ADDR_CMD_MEM_STR)
;
}
/*
* Allocate DMA memory for transmit descriptor fragments.
* Only one map is used for all descriptors.
*/
int
xge_alloc_txmem(struct xge_softc *sc)
{
struct txd *txp;
bus_dma_segment_t seg;
bus_addr_t txdp;
void *kva;
int i, rseg, state;
#define TXMAPSZ (NTXDESCS*NTXFRAGS*sizeof(struct txd))
state = 0;
if (bus_dmamem_alloc(sc->sc_dmat, TXMAPSZ, PAGE_SIZE, 0,
&seg, 1, &rseg, BUS_DMA_NOWAIT))
goto err;
state++;
if (bus_dmamem_map(sc->sc_dmat, &seg, rseg, TXMAPSZ, &kva,
BUS_DMA_NOWAIT))
goto err;
err:
if (state > 2)
bus_dmamap_destroy(sc->sc_dmat, sc->sc_txmap);
if (state > 1)
bus_dmamem_unmap(sc->sc_dmat, kva, TXMAPSZ);
if (state > 0)
bus_dmamem_free(sc->sc_dmat, &seg, rseg);
return ENOBUFS;
}
/*
* Allocate DMA memory for receive descriptor,
* only one map is used for all descriptors.
* link receive descriptor pages together.
*/
int
xge_alloc_rxmem(struct xge_softc *sc)
{
struct rxd_4k *rxpp;
bus_dma_segment_t seg;
void *kva;
int i, rseg, state;
/* setup receive page link pointers */
for (rxpp = (struct rxd_4k *)kva, i = 0; i < NRXPAGES; i++, rxpp++) {
sc->sc_rxd_4k[i] = rxpp;
rxpp->r4_next = (uint64_t)sc->sc_rxmap->dm_segs[0].ds_addr +
(i*sizeof(struct rxd_4k)) + sizeof(struct rxd_4k);
}
sc->sc_rxd_4k[NRXPAGES-1]->r4_next =
(uint64_t)sc->sc_rxmap->dm_segs[0].ds_addr;
return 0;
err:
if (state > 2)
bus_dmamap_destroy(sc->sc_dmat, sc->sc_txmap);
if (state > 1)
bus_dmamem_unmap(sc->sc_dmat, kva, TXMAPSZ);
if (state > 0)
bus_dmamem_free(sc->sc_dmat, &seg, rseg);
return ENOBUFS;
}
/*
* Add a new mbuf chain to descriptor id.
*/
int
xge_add_rxbuf(struct xge_softc *sc, int id)
{
struct rxdesc *rxd;
struct mbuf *m[5];
int page, desc, error;
#if RX_MODE == RX_MODE_5
int i;
#endif
page = id/NDESC_BUFMODE;
desc = id%NDESC_BUFMODE;
rxd = &sc->sc_rxd_4k[page]->r4_rxd[desc];
/*
* Allocate mbufs.
* Currently five mbufs and two clusters are used,
* the hardware will put (ethernet, ip, tcp/udp) headers in
* their own buffer and the clusters are only used for data.
*/
#if RX_MODE == RX_MODE_1
MGETHDR(m[0], M_DONTWAIT, MT_DATA);
if (m[0] == NULL)
return ENOBUFS;
MCLGET(m[0], M_DONTWAIT);
if ((m[0]->m_flags & M_EXT) == 0) {
m_freem(m[0]);
return ENOBUFS;
}
m[0]->m_len = m[0]->m_pkthdr.len = m[0]->m_ext.ext_size;
#elif RX_MODE == RX_MODE_3
#error missing rxmode 3.
#elif RX_MODE == RX_MODE_5
MGETHDR(m[0], M_DONTWAIT, MT_DATA);
for (i = 1; i < 5; i++) {
MGET(m[i], M_DONTWAIT, MT_DATA);
}
if (m[3])
MCLGET(m[3], M_DONTWAIT);
if (m[4])
MCLGET(m[4], M_DONTWAIT);
if (!m[0] || !m[1] || !m[2] || !m[3] || !m[4] ||
((m[3]->m_flags & M_EXT) == 0) || ((m[4]->m_flags & M_EXT) == 0)) {
/* Out of something */
for (i = 0; i < 5; i++)
if (m[i] != NULL)
m_free(m[i]);
return ENOBUFS;
}
/* Link'em together */
m[0]->m_next = m[1];
m[1]->m_next = m[2];
m[2]->m_next = m[3];
m[3]->m_next = m[4];
#else
#error bad mode RX_MODE
#endif
if (sc->sc_rxb[id])
bus_dmamap_unload(sc->sc_dmat, sc->sc_rxm[id]);
sc->sc_rxb[id] = m[0];
/*
* These magics comes from the FreeBSD driver.
*/
int
xge_setup_xgxs(struct xge_softc *sc)
{
/* The magic numbers are described in the users guide */
/* Workaround for TX Lane XAUI initialization error.
Read Xpak PHY register 24 for XAUI lane status */
PIF_WCSR(DTX_CONTROL, 0x0018040000000000ULL); DELAY(50);
PIF_WCSR(DTX_CONTROL, 0x00180400000000e0ULL); DELAY(50);
PIF_WCSR(DTX_CONTROL, 0x00180400000000ecULL); DELAY(50);
/*
* Reading the MDIO control with value 0x1804001c0F001c
* means the TxLanes were already in sync
* Reading the MDIO control with value 0x1804000c0x001c
* means some TxLanes are not in sync where x is a 4-bit
* value representing each lanes
*/
#if 0
val = PIF_RCSR(MDIO_CONTROL);
if (val != 0x1804001c0F001cULL) {
printf("%s: MDIO_CONTROL: %llx != %llx\n",
XNAME, val, 0x1804001c0F001cULL);
return 1;
}
#endif
/* Set and remove the DTE XS INTLoopBackN */
PIF_WCSR(DTX_CONTROL, 0x0000051500000000ULL); DELAY(50);
PIF_WCSR(DTX_CONTROL, 0x00000515604000e0ULL); DELAY(50);
PIF_WCSR(DTX_CONTROL, 0x00000515604000e4ULL); DELAY(50);
PIF_WCSR(DTX_CONTROL, 0x00000515204000e4ULL); DELAY(50);
PIF_WCSR(DTX_CONTROL, 0x00000515204000ecULL); DELAY(50);
#if 0
/* Reading the DTX control register Should be 0x5152040001c */
val = PIF_RCSR(DTX_CONTROL);
if (val != 0x5152040001cULL) {
printf("%s: DTX_CONTROL: %llx != %llx\n",
XNAME, val, 0x5152040001cULL);
return 1;
}
#endif