PERFORCE change 204933 for review
Jakub Wojciech Klama
jceel at FreeBSD.org
Sat Jan 21 03:05:48 UTC 2012
http://p4web.freebsd.org/@@204933?ac=10
Change 204933 by jceel at jceel_cyclone on 2012/01/21 03:05:31
Introduce RX filtering based on built-in hash filter. Turn off promiscuous mode by default.
Affected files ...
.. //depot/projects/soc2011/jceel_lpc/sys/arm/lpc/if_lpe.c#10 edit
.. //depot/projects/soc2011/jceel_lpc/sys/arm/lpc/if_lpereg.h#6 edit
Differences ...
==== //depot/projects/soc2011/jceel_lpc/sys/arm/lpc/if_lpe.c#10 (text+ko) ====
@@ -133,8 +133,8 @@
bus_space_handle_t lpe_bsh;
#define LPE_FLAG_LINK (1 << 0)
uint32_t lpe_flags;
+ int lpe_watchdog_timer;
struct callout lpe_tick;
-
struct lpe_chain_data lpe_cdata;
struct lpe_ring_data lpe_rdata;
};
@@ -154,16 +154,20 @@
static void lpe_stop(struct lpe_softc *);
static void lpe_stop_locked(struct lpe_softc *);
static int lpe_ioctl(struct ifnet *, u_long, caddr_t);
+static void lpe_set_rxmode(struct lpe_softc *);
+static void lpe_set_rxfilter(struct lpe_softc *);
static void lpe_intr(void *);
static void lpe_rxintr(struct lpe_softc *);
static void lpe_txintr(struct lpe_softc *);
static void lpe_tick(void *);
+static void lpe_watchdog(struct lpe_softc *);
static int lpe_encap(struct lpe_softc *, struct mbuf **);
static int lpe_dma_alloc(struct lpe_softc *);
static int lpe_dma_alloc_rx(struct lpe_softc *);
static int lpe_dma_alloc_tx(struct lpe_softc *);
static int lpe_init_rx(struct lpe_softc *);
static int lpe_init_rxbuf(struct lpe_softc *, int);
+static void lpe_discard_rxbuf(struct lpe_softc *, int);
static void lpe_dmamap_cb(void *, bus_dma_segment_t *, int, int);
static int lpe_ifmedia_upd(struct ifnet *);
static void lpe_ifmedia_sts(struct ifnet *, struct ifmediareq *);
@@ -177,6 +181,13 @@
#define lpe_write_4(_sc, _reg, _val) \
bus_space_write_4((_sc)->lpe_bst, (_sc)->lpe_bsh, (_reg), (_val))
+#define LPE_HWDESC_RXERRS (LPE_HWDESC_CRCERROR | LPE_HWDESC_SYMBOLERROR | \
+ LPE_HWDESC_LENGTHERROR | LPE_HWDESC_ALIGNERROR | LPE_HWDESC_OVERRUN | \
+ LPE_HWDESC_RXNODESCR)
+
+#define LPE_HWDESC_TXERRS (LPE_HWDESC_EXCDEFER | LPE_HWDESC_EXCCOLL | \
+ LPE_HWDESC_LATECOLL | LPE_HWDESC_UNDERRUN | LPE_HWDESC_TXNODESCR)
+
static int
lpe_probe(device_t dev)
{
@@ -305,6 +316,12 @@
struct lpe_softc *sc = device_get_softc(dev);
lpe_stop(sc);
+
+ if_free(sc->lpe_ifp);
+ bus_teardown_intr(dev, sc->lpe_irq_res, sc->lpe_intrhand);
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lpe_irq_res);
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lpe_mem_res);
+
return (0);
}
@@ -313,6 +330,7 @@
{
struct lpe_softc *sc = device_get_softc(dev);
uint32_t val;
+ int result;
lpe_write_4(sc, LPE_MCMD, LPE_MCMD_READ);
lpe_write_4(sc, LPE_MADR,
@@ -331,12 +349,10 @@
return (0);
lpe_write_4(sc, LPE_MCMD, 0);
+ result = (lpe_read_4(sc, LPE_MRDD) & LPE_MRDD_DATAMASK);
+ debugf("phy=%d reg=%d result=0x%04x\n", phy, reg, result);
- int x = (lpe_read_4(sc, LPE_MRDD) & LPE_MRDD_DATAMASK);
-
-
- debugf("phy=%d reg=%d result=0x%04x\n", phy, reg, x);
- return (x);
+ return (result);
}
static int
@@ -397,9 +413,9 @@
LPE_COMMAND_TXRESET | LPE_COMMAND_RXRESET);
/* Set station address */
- lpe_write_4(sc, LPE_SA0, sc->lpe_enaddr[0] << 8 | sc->lpe_enaddr[1]);
- lpe_write_4(sc, LPE_SA1, sc->lpe_enaddr[2] << 8 | sc->lpe_enaddr[3]);
- lpe_write_4(sc, LPE_SA2, sc->lpe_enaddr[4] << 8 | sc->lpe_enaddr[5]);
+ lpe_write_4(sc, LPE_SA2, sc->lpe_enaddr[1] << 8 | sc->lpe_enaddr[0]);
+ lpe_write_4(sc, LPE_SA1, sc->lpe_enaddr[3] << 8 | sc->lpe_enaddr[2]);
+ lpe_write_4(sc, LPE_SA0, sc->lpe_enaddr[5] << 8 | sc->lpe_enaddr[4]);
/* Leave soft reset mode */
mac1 = lpe_read_4(sc, LPE_MAC1);
@@ -433,8 +449,7 @@
/* Enable Tx and Rx */
cmd = lpe_read_4(sc, LPE_COMMAND);
lpe_write_4(sc, LPE_COMMAND, cmd | LPE_COMMAND_RXENABLE |
- LPE_COMMAND_TXENABLE | LPE_COMMAND_PASSRUNTFRAME |
- LPE_COMMAND_PASSRXFILTER);
+ LPE_COMMAND_TXENABLE | LPE_COMMAND_PASSRUNTFRAME);
/* Enable receive */
mac1 = lpe_read_4(sc, LPE_MAC1);
@@ -446,7 +461,7 @@
lpe_write_4(sc, LPE_MCFG, LPE_MCFG_CLKSEL(7));
/* Set up Rx filter */
- lpe_write_4(sc, LPE_RXFILTER_CTRL, 0xffffffff);
+ lpe_set_rxmode(sc);
/* Enable interrupts */
lpe_write_4(sc, LPE_INTENABLE, LPE_INT_RXOVERRUN | LPE_INT_RXERROR |
@@ -503,8 +518,6 @@
lpe_lock_assert(sc);
- debugf("entry");
-
while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
if (lpe_read_4(sc, LPE_TXDESC_PROD) ==
lpe_read_4(sc, LPE_TXDESC_CONS) - 5)
@@ -523,6 +536,7 @@
/* Submit new descriptor list */
if (encap) {
lpe_write_4(sc, LPE_TXDESC_PROD, sc->lpe_cdata.lpe_tx_prod);
+ sc->lpe_watchdog_timer = 5;
}
}
@@ -541,7 +555,7 @@
prod = sc->lpe_cdata.lpe_tx_prod;
txd = &sc->lpe_cdata.lpe_tx_desc[prod];
- debugf("lpe_encap: starting with prod=%d\n", prod);
+ debugf("starting with prod=%d\n", prod);
err = bus_dmamap_load_mbuf_sg(sc->lpe_cdata.lpe_tx_buf_tag,
txd->lpe_txdesc_dmamap, *m_head, segs, &nsegs, BUS_DMA_NOWAIT);
@@ -599,6 +613,19 @@
lpe_stop_locked(struct lpe_softc *sc)
{
lpe_lock_assert(sc);
+
+ callout_stop(&sc->lpe_tick);
+
+ /* Disable interrupts */
+ lpe_write_4(sc, LPE_INTCLEAR, 0xffffffff);
+
+ /* Stop EMAC */
+ lpe_write_4(sc, LPE_MAC1, 0);
+ lpe_write_4(sc, LPE_MAC2, 0);
+ lpe_write_4(sc, LPE_COMMAND, 0);
+
+ sc->lpe_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ sc->lpe_ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
}
static int
@@ -607,9 +634,29 @@
struct lpe_softc *sc = ifp->if_softc;
struct mii_data *mii = device_get_softc(sc->lpe_miibus);
struct ifreq *ifr = (struct ifreq *)data;
- int err;
+ int err = 0;
switch (cmd) {
+ case SIOCSIFFLAGS:
+ lpe_lock(sc);
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ lpe_set_rxmode(sc);
+ lpe_set_rxfilter(sc);
+ } else
+ lpe_init_locked(sc);
+ } else
+ lpe_stop(sc);
+ lpe_unlock(sc);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ lpe_lock(sc);
+ lpe_set_rxfilter(sc);
+ lpe_unlock(sc);
+ }
+ break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
err = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
@@ -622,6 +669,57 @@
return (err);
}
+static void lpe_set_rxmode(struct lpe_softc *sc)
+{
+ struct ifnet *ifp = sc->lpe_ifp;
+ uint32_t rxfilt;
+
+ rxfilt = LPE_RXFILTER_UNIHASH | LPE_RXFILTER_MULTIHASH | LPE_RXFILTER_PERFECT;
+
+ if (ifp->if_flags & IFF_BROADCAST)
+ rxfilt |= LPE_RXFILTER_BROADCAST;
+
+ if (ifp->if_flags & IFF_PROMISC)
+ rxfilt |= LPE_RXFILTER_UNICAST | LPE_RXFILTER_MULTICAST;
+
+ if (ifp->if_flags & IFF_ALLMULTI)
+ rxfilt |= LPE_RXFILTER_MULTICAST;
+
+ lpe_write_4(sc, LPE_RXFILTER_CTRL, rxfilt);
+}
+
+static void lpe_set_rxfilter(struct lpe_softc *sc)
+{
+ struct ifnet *ifp = sc->lpe_ifp;
+ struct ifmultiaddr *ifma;
+ int index;
+ uint32_t hashl, hashh;
+
+ hashl = 0;
+ hashh = 0;
+
+ if_maddr_rlock(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+
+ device_printf(sc->lpe_dev, "rx filter add: %6D\n", LLADDR((struct sockaddr_dl *)ifma->ifma_addr), ":");
+
+ index = ether_crc32_be(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr), ETHER_ADDR_LEN) >> 23 & 0x3f;
+
+ if (index > 31)
+ hashh |= (1 << (index - 32));
+ else
+ hashl |= (1 << index);
+ }
+ if_maddr_runlock(ifp);
+
+ /* Program new hash filter */
+ lpe_write_4(sc, LPE_HASHFILTER_L, hashl);
+ lpe_write_4(sc, LPE_HASHFILTER_H, hashh);
+}
+
static void
lpe_intr(void *arg)
{
@@ -666,16 +764,26 @@
hwd = &sc->lpe_rdata.lpe_rx_ring[cons];
hws = &sc->lpe_rdata.lpe_rx_status[cons];
+ /* Check received frame for errors */
+ if (hws->lhs_info & LPE_HWDESC_RXERRS) {
+ ifp->if_ierrors++;
+ lpe_discard_rxbuf(sc, cons);
+ lpe_init_rxbuf(sc, cons);
+ goto skip;
+ }
+
m = rxd->lpe_rxdesc_mbuf;
m->m_pkthdr.rcvif = ifp;
m->m_data += 2;
+ ifp->if_ipackets++;
+
lpe_unlock(sc);
(*ifp->if_input)(ifp, m);
lpe_lock(sc);
lpe_init_rxbuf(sc, cons);
-
+skip:
LPE_INC(cons, LPE_RXDESC_NUM);
lpe_write_4(sc, LPE_RXDESC_CONS, cons);
}
@@ -684,13 +792,12 @@
static void
lpe_txintr(struct lpe_softc *sc)
{
+ struct ifnet *ifp = sc->lpe_ifp;
struct lpe_hwdesc *hwd;
struct lpe_hwstatus *hws;
struct lpe_txdesc *txd;
int cons, last;
- debugf("transmit interrupt\n");
-
for (;;) {
cons = lpe_read_4(sc, LPE_TXDESC_CONS);
last = sc->lpe_cdata.lpe_tx_last;
@@ -705,6 +812,14 @@
bus_dmamap_sync(sc->lpe_cdata.lpe_tx_buf_tag,
txd->lpe_txdesc_dmamap, BUS_DMASYNC_POSTWRITE);
+ ifp->if_collisions += LPE_HWDESC_COLLISIONS(hws->lhs_info);
+
+ /* XXX tylko dla ostatniego fragmentu */
+ if (hws->lhs_info & LPE_HWDESC_TXERRS)
+ ifp->if_oerrors++;
+ else
+ ifp->if_opackets++;
+
if (txd->lpe_txdesc_first) {
bus_dmamap_unload(sc->lpe_cdata.lpe_tx_buf_tag,
txd->lpe_txdesc_dmamap);
@@ -714,8 +829,12 @@
txd->lpe_txdesc_first = 0;
}
+ sc->lpe_cdata.lpe_tx_used--;
LPE_INC(sc->lpe_cdata.lpe_tx_last, LPE_TXDESC_NUM);
}
+
+ if (!sc->lpe_cdata.lpe_tx_used)
+ sc->lpe_watchdog_timer = 0;
}
static void
@@ -725,11 +844,33 @@
struct mii_data *mii = device_get_softc(sc->lpe_miibus);
lpe_lock_assert(sc);
+
mii_tick(mii);
+ lpe_watchdog(sc);
callout_reset(&sc->lpe_tick, hz, lpe_tick, sc);
}
+static void
+lpe_watchdog(struct lpe_softc *sc)
+{
+ struct ifnet *ifp = sc->lpe_ifp;
+
+ lpe_lock_assert(sc);
+
+ if (sc->lpe_watchdog_timer == 0 || sc->lpe_watchdog_timer--)
+ return;
+
+ /* Chip has stopped responding */
+ device_printf(sc->lpe_dev, "WARNING: chip hangup, restarting...\n");
+ lpe_stop_locked(sc);
+ lpe_init_locked(sc);
+
+ /* Try to resend packets */
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ lpe_start_locked(ifp);
+}
+
static int
lpe_dma_alloc(struct lpe_softc *sc)
{
@@ -1022,7 +1163,6 @@
return (0);
}
-#if 0
static void
lpe_discard_rxbuf(struct lpe_softc *sc, int n)
{
@@ -1032,16 +1172,16 @@
rxd = &sc->lpe_cdata.lpe_rx_desc[n];
hwd = &sc->lpe_rdata.lpe_rx_ring[n];
- bus_dmamap_unload(rxch->dve_rx_buf_tag, rxd->dve_rxdesc_dmamap);
+ bus_dmamap_unload(sc->lpe_cdata.lpe_rx_buf_tag, rxd->lpe_rxdesc_dmamap);
- hwd->hw_flagslen = 0;
+ hwd->lhr_data = 0;
+ hwd->lhr_control = 0;
- if (rxd->dve_rxdesc_mbuf) {
- m_freem(rxd->dve_rxdesc_mbuf);
- rxd->dve_rxdesc_mbuf = NULL;
+ if (rxd->lpe_rxdesc_mbuf) {
+ m_freem(rxd->lpe_rxdesc_mbuf);
+ rxd->lpe_rxdesc_mbuf = NULL;
}
}
-#endif
static void
lpe_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
==== //depot/projects/soc2011/jceel_lpc/sys/arm/lpc/if_lpereg.h#6 (text+ko) ====
@@ -116,6 +116,14 @@
#define LPE_FLOWCONTROL_COUNTER 0x170
#define LPE_FLOWCONTROL_STATUS 0x174
#define LPE_RXFILTER_CTRL 0x200
+#define LPE_RXFILTER_UNICAST (1 << 0)
+#define LPE_RXFILTER_BROADCAST (1 << 1)
+#define LPE_RXFILTER_MULTICAST (1 << 2)
+#define LPE_RXFILTER_UNIHASH (1 << 3)
+#define LPE_RXFILTER_MULTIHASH (1 << 4)
+#define LPE_RXFILTER_PERFECT (1 << 5)
+#define LPE_RXFILTER_WOL (1 << 12)
+#define LPE_RXFILTER_FILTWOL (1 << 13)
#define LPE_RXFILTER_WOL_STATUS 0x204
#define LPE_RXFILTER_WOL_CLEAR 0x208
#define LPE_HASHFILTER_L 0x210
@@ -159,13 +167,26 @@
/* These are valid for both Rx and Tx descriptors */
#define LPE_HWDESC_SIZE_MASK (1 << 10)
+#define LPE_HWDESC_INTERRUPT (1 << 31)
-/* These are valid for Tx descriptors only */
-#define LPE_HWDESC_INTERRUPT (1 << 31)
+/* These are valid for Tx descriptors */
+#define LPE_HWDESC_LAST (1 << 30)
#define LPE_HWDESC_CRC (1 << 29)
#define LPE_HWDESC_PAD (1 << 28)
+#define LPE_HWDESC_HUGE (1 << 27)
+#define LPE_HWDESC_OVERRIDE (1 << 26)
-/* These are valid for Rx descriptors only */
+/* These are valid for Tx status descriptors */
+#define LPE_HWDESC_COLLISIONS(_n) (((_n) >> 21) & 0x7)
+#define LPE_HWDESC_DEFER (1 << 25)
+#define LPE_HWDESC_EXCDEFER (1 << 26)
+#define LPE_HWDESC_EXCCOLL (1 << 27)
+#define LPE_HWDESC_LATECOLL (1 << 28)
+#define LPE_HWDESC_UNDERRUN (1 << 29)
+#define LPE_HWDESC_TXNODESCR (1 << 30)
+#define LPE_HWDESC_ERROR (1 << 31)
+
+/* These are valid for Rx status descriptors */
#define LPE_HWDESC_CONTROL (1 << 18)
#define LPE_HWDESC_VLAN (1 << 19)
#define LPE_HWDESC_FAILFILTER (1 << 20)
@@ -177,7 +198,7 @@
#define LPE_HWDESC_RANGEERROR (1 << 26)
#define LPE_HWDESC_ALIGNERROR (1 << 27)
#define LPE_HWDESC_OVERRUN (1 << 28)
-#define LPE_HWDESC_NODESCR (1 << 29)
+#define LPE_HWDESC_RXNODESCR (1 << 29)
#define LPE_HWDESC_LASTFLAG (1 << 30)
#define LPE_HWDESC_ERROR (1 << 31)
More information about the p4-projects
mailing list