[rtems-libbsd commit] ffec: Add interrupt coalescing support

Sebastian Huber sebh at rtems.org
Wed Oct 25 12:31:12 UTC 2017


Module:    rtems-libbsd
Branch:    master
Commit:    0323c286e32934979043d14d69660db4ba4c3181
Changeset: http://git.rtems.org/rtems-libbsd/commit/?id=0323c286e32934979043d14d69660db4ba4c3181

Author:    Sebastian Huber <sebastian.huber at embedded-brains.de>
Date:      Wed Oct 25 08:01:27 2017 +0200

ffec: Add interrupt coalescing support

Code is an adapted from the TSEC (if_tsec) network interface driver.

Update #3090.

---

 freebsd/sys/dev/ffec/if_ffec.c    | 175 ++++++++++++++++++++++++++++++++++++++
 freebsd/sys/dev/ffec/if_ffecreg.h |  11 +++
 2 files changed, 186 insertions(+)

diff --git a/freebsd/sys/dev/ffec/if_ffec.c b/freebsd/sys/dev/ffec/if_ffec.c
index f37dcbd..d44d6c5 100644
--- a/freebsd/sys/dev/ffec/if_ffec.c
+++ b/freebsd/sys/dev/ffec/if_ffec.c
@@ -2,6 +2,8 @@
 
 /*-
  * Copyright (c) 2013 Ian Lepore <ian at freebsd.org>
+ * Copyright (C) 2007-2008 Semihalf, Rafal Jaworowski
+ * Copyright (C) 2006-2007 Semihalf, Piotr Kruszynski
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -138,6 +140,10 @@ static struct ofw_compat_data compat_data[] = {
 
 #define	MAX_IRQ_COUNT 3
 
+/* Interrupt Coalescing types */
+#define	FEC_IC_RX		0
+#define	FEC_IC_TX		1
+
 struct ffec_bufmap {
 	struct mbuf	*mbuf;
 	bus_dmamap_t	map;
@@ -188,6 +194,12 @@ struct ffec_softc {
 	struct ffec_bufmap	txbuf_map[TX_DESC_COUNT];
 	uint32_t		tx_idx_head;
 	uint32_t		tx_idx_tail;
+
+	/* interrupt coalescing */
+	int		rx_ic_time;	/* RW, valid values 0..65535 */
+	int		rx_ic_count;	/* RW, valid values 0..255 */
+	int		tx_ic_time;
+	int		tx_ic_count;
 };
 
 #define	FFEC_LOCK(sc)			mtx_lock(&(sc)->mtx)
@@ -204,6 +216,13 @@ static void ffec_encap(struct ifnet *ifp, struct ffec_softc *sc,
     struct mbuf *m0, int *start_tx);
 static void ffec_txstart_locked(struct ffec_softc *sc);
 static void ffec_txfinish_locked(struct ffec_softc *sc);
+static void ffec_add_sysctls(struct ffec_softc *sc);
+static int ffec_sysctl_ic_time(SYSCTL_HANDLER_ARGS);
+static int ffec_sysctl_ic_count(SYSCTL_HANDLER_ARGS);
+static void ffec_set_ic(struct ffec_softc *sc, bus_size_t off, int count,
+    int time);
+static void ffec_set_rxic(struct ffec_softc *sc);
+static void ffec_set_txic(struct ffec_softc *sc);
 
 static inline uint16_t
 RD2(struct ffec_softc *sc, bus_size_t off)
@@ -1302,6 +1321,9 @@ ffec_init_locked(struct ffec_softc *sc)
 	 * available in ffec_attach() or ffec_stop().
 	 */
 	WR4(sc, FEC_RDAR_REG, FEC_RDAR_RDAR);
+
+	ffec_set_rxic(sc);
+	ffec_set_txic(sc);
 }
 
 static void
@@ -1495,6 +1517,151 @@ ffec_detach(device_t dev)
 	return (0);
 }
 
+static void
+ffec_add_sysctls(struct ffec_softc *sc)
+{
+	struct sysctl_ctx_list *ctx;
+	struct sysctl_oid_list *children;
+	struct sysctl_oid *tree;
+
+	ctx = device_get_sysctl_ctx(sc->dev);
+	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
+	tree = SYSCTL_ADD_NODE(ctx, children, OID_AUTO, "int_coal",
+	    CTLFLAG_RD, 0, "FEC Interrupts coalescing");
+	children = SYSCTL_CHILDREN(tree);
+
+	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "rx_time",
+	    CTLTYPE_UINT | CTLFLAG_RW, sc, FEC_IC_RX, ffec_sysctl_ic_time,
+	    "I", "IC RX time threshold (0-65535)");
+	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "rx_count",
+	    CTLTYPE_UINT | CTLFLAG_RW, sc, FEC_IC_RX, ffec_sysctl_ic_count,
+	    "I", "IC RX frame count threshold (0-255)");
+
+	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "tx_time",
+	    CTLTYPE_UINT | CTLFLAG_RW, sc, FEC_IC_TX, ffec_sysctl_ic_time,
+	    "I", "IC TX time threshold (0-65535)");
+	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "tx_count",
+	    CTLTYPE_UINT | CTLFLAG_RW, sc, FEC_IC_TX, ffec_sysctl_ic_count,
+	    "I", "IC TX frame count threshold (0-255)");
+}
+
+/*
+ * With Interrupt Coalescing (IC) active, a transmit/receive frame
+ * interrupt is raised either upon:
+ *
+ * - threshold-defined period of time elapsed, or
+ * - threshold-defined number of frames is received/transmitted,
+ *   whichever occurs first.
+ *
+ * The following sysctls regulate IC behaviour (for TX/RX separately):
+ *
+ * dev.tsec.<unit>.int_coal.rx_time
+ * dev.tsec.<unit>.int_coal.rx_count
+ * dev.tsec.<unit>.int_coal.tx_time
+ * dev.tsec.<unit>.int_coal.tx_count
+ *
+ * Values:
+ *
+ * - 0 for either time or count disables IC on the given TX/RX path
+ *
+ * - count: 1-255 (expresses frame count number; note that value of 1 is
+ *   effectively IC off)
+ *
+ * - time: 1-65535 (value corresponds to a real time period and is
+ *   expressed in units equivalent to 64 FEC interface clocks, i.e. one timer
+ *   threshold unit is 26.5 us, 2.56 us, or 512 ns, corresponding to 10 Mbps,
+ *   100 Mbps, or 1Gbps, respectively. For detailed discussion consult the
+ *   FEC reference manual.
+ */
+static int
+ffec_sysctl_ic_time(SYSCTL_HANDLER_ARGS)
+{
+	int error;
+	uint32_t time;
+	struct ffec_softc *sc = (struct ffec_softc *)arg1;
+
+	time = (arg2 == FEC_IC_RX) ? sc->rx_ic_time : sc->tx_ic_time;
+
+	error = sysctl_handle_int(oidp, &time, 0, req);
+	if (error != 0)
+		return (error);
+
+	if (time > 65535)
+		return (EINVAL);
+
+	FFEC_LOCK(sc);
+	if (arg2 == FEC_IC_RX) {
+		sc->rx_ic_time = time;
+		ffec_set_rxic(sc);
+	} else {
+		sc->tx_ic_time = time;
+		ffec_set_txic(sc);
+	}
+	FFEC_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+ffec_sysctl_ic_count(SYSCTL_HANDLER_ARGS)
+{
+	int error;
+	uint32_t count;
+	struct ffec_softc *sc = (struct ffec_softc *)arg1;
+
+	count = (arg2 == FEC_IC_RX) ? sc->rx_ic_count : sc->tx_ic_count;
+
+	error = sysctl_handle_int(oidp, &count, 0, req);
+	if (error != 0)
+		return (error);
+
+	if (count > 255)
+		return (EINVAL);
+
+	FFEC_LOCK(sc);
+	if (arg2 == FEC_IC_RX) {
+		sc->rx_ic_count = count;
+		ffec_set_rxic(sc);
+	} else {
+		sc->tx_ic_count = count;
+		ffec_set_txic(sc);
+	}
+	FFEC_UNLOCK(sc);
+
+	return (0);
+}
+
+static void
+ffec_set_ic(struct ffec_softc *sc, bus_size_t off, int count, int time)
+{
+	uint32_t ic;
+
+	if (count == 0 || time == 0)
+		/* Disable RX IC */
+		ic = 0;
+	else {
+		ic = FEC_IC_ICEN;
+		ic |= FEC_IC_ICFT(count);
+		ic |= FEC_IC_ICTT(time);
+	}
+
+	WR4(sc, off, ic);
+}
+
+static void
+ffec_set_rxic(struct ffec_softc *sc)
+{
+
+	ffec_set_ic(sc, FEC_RXIC0_REG, sc->rx_ic_count, sc->rx_ic_time);
+}
+
+static void
+ffec_set_txic(struct ffec_softc *sc)
+{
+
+	ffec_set_ic(sc, FEC_TXIC0_REG, sc->tx_ic_count, sc->tx_ic_time);
+}
+
 static int
 ffec_attach(device_t dev)
 {
@@ -1781,6 +1948,14 @@ ffec_attach(device_t dev)
 	}
 	WR4(sc, FEC_MSCR_REG, mscr);
 
+	/* Configure defaults for interrupts coalescing */
+	sc->rx_ic_time = 768;
+	sc->rx_ic_count = RX_DESC_COUNT / 4;
+	sc->tx_ic_time = 768;
+	sc->tx_ic_count = TX_DESC_COUNT / 4;
+
+	ffec_add_sysctls(sc);
+
 	/* Set up the ethernet interface. */
 	sc->ifp = ifp = if_alloc(IFT_ETHER);
 
diff --git a/freebsd/sys/dev/ffec/if_ffecreg.h b/freebsd/sys/dev/ffec/if_ffecreg.h
index bb1d197..481bc30 100644
--- a/freebsd/sys/dev/ffec/if_ffecreg.h
+++ b/freebsd/sys/dev/ffec/if_ffecreg.h
@@ -143,6 +143,17 @@ __FBSDID("$FreeBSD$");
 #define	  FEC_OPD_PAUSE_DUR_SHIFT	  0
 #define	  FEC_OPD_PAUSE_DUR_MASK	  (0xffff << FEC_OPD_PAUSE_DUR_SHIFT)
 
+#define	FEC_TXIC0_REG			0x00f0
+#define	FEC_TXIC1_REG			0x00f4
+#define	FEC_TXIC2_REG			0x00f8
+#define	FEC_RXIC0_REG			0x0100
+#define	FEC_RXIC1_REG			0x0104
+#define	FEC_RXIC2_REG			0x0108
+#define	  FEC_IC_ICEN			  (1 <<  31)
+#define	  FEC_IC_ICCS			  (1 <<  30)
+#define	  FEC_IC_ICFT(x)		  (((x) & 0xff) << 20)
+#define	  FEC_IC_ICTT(x)		  ((x) & 0xffff)
+
 #define	FEC_IAUR_REG			0x0118
 #define	FEC_IALR_REG			0x011c
 




More information about the vc mailing list