summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2017-10-25 08:01:27 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2017-10-25 14:30:01 +0200
commit0323c286e32934979043d14d69660db4ba4c3181 (patch)
tree0ac6fd0613d9ecbc4f53af6ad5ed866a59cd3709
parentffec: Increase buffer descriptor count to 512 (diff)
downloadrtems-libbsd-0323c286e32934979043d14d69660db4ba4c3181.tar.bz2
ffec: Add interrupt coalescing support
Code is an adapted from the TSEC (if_tsec) network interface driver. Update #3090.
-rw-r--r--freebsd/sys/dev/ffec/if_ffec.c175
-rw-r--r--freebsd/sys/dev/ffec/if_ffecreg.h11
2 files changed, 186 insertions, 0 deletions
diff --git a/freebsd/sys/dev/ffec/if_ffec.c b/freebsd/sys/dev/ffec/if_ffec.c
index f37dcbd6..d44d6c5a 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@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 bb1d1979..481bc303 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