summaryrefslogtreecommitdiffstats
path: root/sys/arm/ti
diff options
context:
space:
mode:
authorLuiz Otavio O Souza <luiz@netgate.com>2016-12-30 20:30:00 -0600
committerLuiz Otavio O Souza <luiz@netgate.com>2016-12-30 20:30:00 -0600
commit1af1408e09373ae856cfef567d79849c7e7e0d25 (patch)
tree8a4a2bc017c297dea8c977c53f8c1099e26edbe6 /sys/arm/ti
parentba2be30f109cb2d0c83d41dff268b04f085252b4 (diff)
parent0591c0c87ec4e420c1a1e9a0c10f761ff4a832c2 (diff)
downloadFreeBSD-src-1af1408e09373ae856cfef567d79849c7e7e0d25.zip
FreeBSD-src-1af1408e09373ae856cfef567d79849c7e7e0d25.tar.gz
Merge remote-tracking branch 'origin/stable/11' into devel-11
Diffstat (limited to 'sys/arm/ti')
-rw-r--r--sys/arm/ti/am335x/am335x_prcm.c9
-rw-r--r--sys/arm/ti/am335x/am335x_scm.c169
-rw-r--r--sys/arm/ti/am335x/am335x_scm.h11
-rw-r--r--sys/arm/ti/am335x/files.am335x1
-rw-r--r--sys/arm/ti/cpsw/if_cpsw.c853
-rw-r--r--sys/arm/ti/cpsw/if_cpswreg.h29
-rw-r--r--sys/arm/ti/cpsw/if_cpswvar.h19
-rw-r--r--sys/arm/ti/ti_gpio.c13
-rw-r--r--sys/arm/ti/ti_scm.c5
-rw-r--r--sys/arm/ti/ti_spi.c6
10 files changed, 859 insertions, 256 deletions
diff --git a/sys/arm/ti/am335x/am335x_prcm.c b/sys/arm/ti/am335x/am335x_prcm.c
index f72bb54..f793432 100644
--- a/sys/arm/ti/am335x/am335x_prcm.c
+++ b/sys/arm/ti/am335x/am335x_prcm.c
@@ -52,6 +52,8 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
+#include "am335x_scm.h"
+
#define CM_PER 0
#define CM_PER_L4LS_CLKSTCTRL (CM_PER + 0x000)
#define CM_PER_L3S_CLKSTCTRL (CM_PER + 0x004)
@@ -619,10 +621,9 @@ am335x_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq)
{
uint32_t ctrl_status;
- /* Read the input clock freq from the control module */
- /* control_status reg (0x40) */
- if (ti_scm_reg_read_4(0x40, &ctrl_status))
- return ENXIO;
+ /* Read the input clock freq from the control module. */
+ if (ti_scm_reg_read_4(SCM_CTRL_STATUS, &ctrl_status))
+ return (ENXIO);
switch ((ctrl_status>>22) & 0x3) {
case 0x0:
diff --git a/sys/arm/ti/am335x/am335x_scm.c b/sys/arm/ti/am335x/am335x_scm.c
new file mode 100644
index 0000000..3d0dff4
--- /dev/null
+++ b/sys/arm/ti/am335x/am335x_scm.c
@@ -0,0 +1,169 @@
+/*-
+ * Copyright (c) 2016 Rubicon Communications, LLC (Netgate)
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <arm/ti/am335x/am335x_scm.h>
+#include <arm/ti/ti_cpuid.h>
+#include <arm/ti/ti_scm.h>
+
+#define TZ_ZEROC 2731
+
+struct am335x_scm_softc {
+ int sc_last_temp;
+ struct sysctl_oid *sc_temp_oid;
+};
+
+static int
+am335x_scm_temp_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ device_t dev;
+ int i, temp;
+ struct am335x_scm_softc *sc;
+ uint32_t reg;
+
+ dev = (device_t)arg1;
+ sc = device_get_softc(dev);
+
+ /* Read the temperature and convert to Kelvin. */
+ for(i = 50; i > 0; i--) {
+ ti_scm_reg_read_4(SCM_BGAP_CTRL, &reg);
+ if ((reg & SCM_BGAP_EOCZ) == 0)
+ break;
+ DELAY(50);
+ }
+ if ((reg & SCM_BGAP_EOCZ) == 0) {
+ sc->sc_last_temp =
+ (reg >> SCM_BGAP_TEMP_SHIFT) & SCM_BGAP_TEMP_MASK;
+ sc->sc_last_temp *= 10;
+ }
+ temp = sc->sc_last_temp + TZ_ZEROC;
+
+ return (sysctl_handle_int(oidp, &temp, 0, req));
+}
+
+static void
+am335x_scm_identify(driver_t *driver, device_t parent)
+{
+ device_t child;
+
+ /* AM335x only. */
+ if (ti_chip() != CHIP_AM335X)
+ return;
+
+ /* Make sure we attach only once. */
+ if (device_find_child(parent, "am335x_scm", -1) != NULL)
+ return;
+
+ child = device_add_child(parent, "am335x_scm", -1);
+ if (child == NULL)
+ device_printf(parent, "cannot add ti_scm child\n");
+}
+
+static int
+am335x_scm_probe(device_t dev)
+{
+
+ device_set_desc(dev, "AM335x Control Module Extension");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+am335x_scm_attach(device_t dev)
+{
+ struct am335x_scm_softc *sc;
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid_list *tree;
+ uint32_t reg;
+
+ /* Set ADC to continous mode, clear output reset. */
+ reg = SCM_BGAP_CLRZ | SCM_BGAP_CONTCONV;
+ ti_scm_reg_write_4(SCM_BGAP_CTRL, reg);
+ /* Flush write. */
+ ti_scm_reg_read_4(SCM_BGAP_CTRL, &reg);
+ /* Start the ADC conversion. */
+ reg = SCM_BGAP_CLRZ | SCM_BGAP_CONTCONV | SCM_BGAP_SOC;
+ ti_scm_reg_write_4(SCM_BGAP_CTRL, reg);
+
+ /* Temperature sysctl. */
+ sc = device_get_softc(dev);
+ ctx = device_get_sysctl_ctx(dev);
+ tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+ sc->sc_temp_oid = SYSCTL_ADD_PROC(ctx, tree, OID_AUTO,
+ "temperature", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ dev, 0, am335x_scm_temp_sysctl, "IK", "Current temperature");
+
+ return (0);
+}
+
+static int
+am335x_scm_detach(device_t dev)
+{
+ struct am335x_scm_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ /* Remove temperature sysctl. */
+ if (sc->sc_temp_oid != NULL)
+ sysctl_remove_oid(sc->sc_temp_oid, 1, 0);
+
+ /* Stop the bandgap ADC. */
+ ti_scm_reg_write_4(SCM_BGAP_CTRL, SCM_BGAP_BGOFF);
+
+ return (0);
+}
+
+static device_method_t am335x_scm_methods[] = {
+ DEVMETHOD(device_identify, am335x_scm_identify),
+ DEVMETHOD(device_probe, am335x_scm_probe),
+ DEVMETHOD(device_attach, am335x_scm_attach),
+ DEVMETHOD(device_detach, am335x_scm_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t am335x_scm_driver = {
+ "am335x_scm",
+ am335x_scm_methods,
+ sizeof(struct am335x_scm_softc),
+};
+
+static devclass_t am335x_scm_devclass;
+
+DRIVER_MODULE(am335x_scm, ti_scm, am335x_scm_driver, am335x_scm_devclass, 0, 0);
+MODULE_VERSION(am335x_scm, 1);
+MODULE_DEPEND(am335x_scm, ti_scm, 1, 1, 1);
diff --git a/sys/arm/ti/am335x/am335x_scm.h b/sys/arm/ti/am335x/am335x_scm.h
index bfb4cd2..6116be0 100644
--- a/sys/arm/ti/am335x/am335x_scm.h
+++ b/sys/arm/ti/am335x/am335x_scm.h
@@ -29,10 +29,21 @@
#define __AM335X_SCM_H__
/* AM335x-specific registers for control module (scm) */
+#define SCM_CTRL_STATUS 0x40
+#define SCM_BGAP_CTRL 0x448
+#define SCM_BGAP_TEMP_MASK 0xff
+#define SCM_BGAP_TEMP_SHIFT 8
+#define SCM_BGAP_BGOFF (1 << 6)
+#define SCM_BGAP_SOC (1 << 4)
+#define SCM_BGAP_CLRZ (1 << 3)
+#define SCM_BGAP_CONTCONV (1 << 2)
+#define SCM_BGAP_EOCZ (1 << 1)
#define SCM_USB_CTRL0 0x620
#define SCM_USB_STS0 0x624
#define SCM_USB_CTRL1 0x628
#define SCM_USB_STS1 0x62C
+#define SCM_MAC_ID0_LO 0x630
+#define SCM_MAC_ID0_HI 0x634
#define SCM_PWMSS_CTRL 0x664
#endif /* __AM335X_SCM_H__ */
diff --git a/sys/arm/ti/am335x/files.am335x b/sys/arm/ti/am335x/files.am335x
index cc87659..a728211 100644
--- a/sys/arm/ti/am335x/files.am335x
+++ b/sys/arm/ti/am335x/files.am335x
@@ -13,6 +13,7 @@ arm/ti/am335x/am335x_pwmss.c standard
arm/ti/am335x/am335x_ehrpwm.c standard
arm/ti/am335x/am335x_ecap.c standard
arm/ti/am335x/am335x_rtc.c optional am335x_rtc
+arm/ti/am335x/am335x_scm.c standard
arm/ti/am335x/am335x_scm_padconf.c standard
arm/ti/am335x/am335x_usbss.c optional musb fdt
arm/ti/am335x/am335x_musb.c optional musb fdt
diff --git a/sys/arm/ti/cpsw/if_cpsw.c b/sys/arm/ti/cpsw/if_cpsw.c
index 70204c0..8a72aa1 100644
--- a/sys/arm/ti/cpsw/if_cpsw.c
+++ b/sys/arm/ti/cpsw/if_cpsw.c
@@ -47,36 +47,33 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_cpsw.h"
+
#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/endian.h>
-#include <sys/mbuf.h>
-#include <sys/lock.h>
-#include <sys/mutex.h>
+#include <sys/bus.h>
#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mbuf.h>
#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
#include <sys/socket.h>
+#include <sys/sockio.h>
#include <sys/sysctl.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/stdarg.h>
+
#include <net/ethernet.h>
#include <net/bpf.h>
#include <net/if.h>
-#include <net/if_arp.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
-#include <net/if_var.h>
-#include <net/if_vlan_var.h>
-
-#include <netinet/in_systm.h>
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <sys/sockio.h>
-#include <sys/bus.h>
-#include <machine/bus.h>
-#include <sys/rman.h>
-#include <machine/resource.h>
+#include <arm/ti/ti_scm.h>
+#include <arm/ti/am335x/am335x_scm.h>
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
@@ -84,11 +81,14 @@ __FBSDID("$FreeBSD$");
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
+
+#ifdef CPSW_ETHERSWITCH
+#include <dev/etherswitch/etherswitch.h>
+#include "etherswitch_if.h"
+#endif
#include "if_cpswreg.h"
#include "if_cpswvar.h"
-
-#include <arm/ti/ti_scm.h>
#include "miibus_if.h"
@@ -124,6 +124,7 @@ static void cpsw_intr_rx(void *arg);
static struct mbuf *cpsw_rx_dequeue(struct cpsw_softc *);
static void cpsw_rx_enqueue(struct cpsw_softc *);
static void cpswp_start(struct ifnet *);
+static void cpsw_intr_tx(void *);
static void cpswp_tx_enqueue(struct cpswp_softc *);
static int cpsw_tx_dequeue(struct cpsw_softc *);
@@ -149,6 +150,19 @@ static void cpsw_add_sysctls(struct cpsw_softc *);
static void cpsw_stats_collect(struct cpsw_softc *);
static int cpsw_stats_sysctl(SYSCTL_HANDLER_ARGS);
+#ifdef CPSW_ETHERSWITCH
+static etherswitch_info_t *cpsw_getinfo(device_t);
+static int cpsw_getport(device_t, etherswitch_port_t *);
+static int cpsw_setport(device_t, etherswitch_port_t *);
+static int cpsw_getconf(device_t, etherswitch_conf_t *);
+static int cpsw_getvgroup(device_t, etherswitch_vlangroup_t *);
+static int cpsw_setvgroup(device_t, etherswitch_vlangroup_t *);
+static int cpsw_readreg(device_t, int);
+static int cpsw_writereg(device_t, int, int);
+static int cpsw_readphy(device_t, int, int);
+static int cpsw_writephy(device_t, int, int, int);
+#endif
+
/*
* Arbitrary limit on number of segments in an mbuf to be transmitted.
* Packets with more segments than this will be defragmented before
@@ -165,8 +179,23 @@ static device_method_t cpsw_methods[] = {
DEVMETHOD(device_shutdown, cpsw_shutdown),
DEVMETHOD(device_suspend, cpsw_suspend),
DEVMETHOD(device_resume, cpsw_resume),
+ /* Bus interface */
+ DEVMETHOD(bus_add_child, device_add_child_ordered),
/* OFW methods */
DEVMETHOD(ofw_bus_get_node, cpsw_get_node),
+#ifdef CPSW_ETHERSWITCH
+ /* etherswitch interface */
+ DEVMETHOD(etherswitch_getinfo, cpsw_getinfo),
+ DEVMETHOD(etherswitch_readreg, cpsw_readreg),
+ DEVMETHOD(etherswitch_writereg, cpsw_writereg),
+ DEVMETHOD(etherswitch_readphyreg, cpsw_readphy),
+ DEVMETHOD(etherswitch_writephyreg, cpsw_writephy),
+ DEVMETHOD(etherswitch_getport, cpsw_getport),
+ DEVMETHOD(etherswitch_setport, cpsw_setport),
+ DEVMETHOD(etherswitch_getvgroup, cpsw_getvgroup),
+ DEVMETHOD(etherswitch_setvgroup, cpsw_setvgroup),
+ DEVMETHOD(etherswitch_getconf, cpsw_getconf),
+#endif
DEVMETHOD_END
};
@@ -201,11 +230,20 @@ static driver_t cpswp_driver = {
static devclass_t cpswp_devclass;
+#ifdef CPSW_ETHERSWITCH
+DRIVER_MODULE(etherswitch, cpswss, etherswitch_driver, etherswitch_devclass, 0, 0);
+MODULE_DEPEND(cpswss, etherswitch, 1, 1, 1);
+#endif
+
DRIVER_MODULE(cpsw, cpswss, cpswp_driver, cpswp_devclass, 0, 0);
DRIVER_MODULE(miibus, cpsw, miibus_driver, miibus_devclass, 0, 0);
MODULE_DEPEND(cpsw, ether, 1, 1, 1);
MODULE_DEPEND(cpsw, miibus, 1, 1, 1);
+#ifdef CPSW_ETHERSWITCH
+static struct cpsw_vlangroups cpsw_vgroups[CPSW_VLANS];
+#endif
+
static uint32_t slave_mdio_addr[] = { 0x4a100200, 0x4a100300 };
static struct resource_spec irq_res_spec[] = {
@@ -216,6 +254,15 @@ static struct resource_spec irq_res_spec[] = {
{ -1, 0 }
};
+static struct {
+ void (*cb)(void *);
+} cpsw_intr_cb[] = {
+ { cpsw_intr_rx_thresh },
+ { cpsw_intr_rx },
+ { cpsw_intr_tx },
+ { cpsw_intr_misc },
+};
+
/* Number of entries here must match size of stats
* array in struct cpswp_softc. */
static struct cpsw_stat {
@@ -262,8 +309,6 @@ static struct cpsw_stat {
* Basic debug support.
*/
-#define IF_DEBUG(_sc) if ((_sc)->if_flags & IFF_DEBUG)
-
static void
cpsw_debugf_head(const char *funcname)
{
@@ -272,7 +317,6 @@ cpsw_debugf_head(const char *funcname)
printf("%02d:%02d:%02d %s ", t / (60 * 60), (t / 60) % 60, t % 60, funcname);
}
-#include <machine/stdarg.h>
static void
cpsw_debugf(const char *fmt, ...)
{
@@ -286,20 +330,12 @@ cpsw_debugf(const char *fmt, ...)
}
#define CPSW_DEBUGF(_sc, a) do { \
- if (sc->debug) { \
- cpsw_debugf_head(__func__); \
- cpsw_debugf a; \
- } \
-} while (0)
-
-#define CPSWP_DEBUGF(_sc, a) do { \
- IF_DEBUG((_sc)) { \
+ if ((_sc)->debug) { \
cpsw_debugf_head(__func__); \
cpsw_debugf a; \
} \
} while (0)
-
/*
* Locking macros
*/
@@ -319,25 +355,6 @@ cpsw_debugf(const char *fmt, ...)
#define CPSW_RX_UNLOCK(sc) mtx_unlock(&(sc)->rx.lock)
#define CPSW_RX_LOCK_ASSERT(sc) mtx_assert(&(sc)->rx.lock, MA_OWNED)
-#define CPSW_GLOBAL_LOCK(sc) do { \
- if ((mtx_owned(&(sc)->tx.lock) ? 1 : 0) != \
- (mtx_owned(&(sc)->rx.lock) ? 1 : 0)) { \
- panic("cpsw deadlock possibility detection!"); \
- } \
- mtx_lock(&(sc)->tx.lock); \
- mtx_lock(&(sc)->rx.lock); \
-} while (0)
-
-#define CPSW_GLOBAL_UNLOCK(sc) do { \
- CPSW_RX_UNLOCK(sc); \
- CPSW_TX_UNLOCK(sc); \
-} while (0)
-
-#define CPSW_GLOBAL_LOCK_ASSERT(sc) do { \
- CPSW_TX_LOCK_ASSERT(sc); \
- CPSW_RX_LOCK_ASSERT(sc); \
-} while (0)
-
#define CPSW_PORT_LOCK(_sc) do { \
mtx_assert(&(_sc)->lock, MA_NOTOWNED); \
mtx_lock(&(_sc)->lock); \
@@ -363,6 +380,8 @@ cpsw_debugf(const char *fmt, ...)
bus_write_region_4(sc->mem_res, slot->bd_offset, (uint32_t *) val, 4)
#define cpsw_cpdma_write_bd_next(sc, slot, next_slot) \
cpsw_write_4(sc, slot->bd_offset, cpsw_cpdma_bd_paddr(sc, next_slot))
+#define cpsw_cpdma_write_bd_flags(sc, slot, val) \
+ bus_write_2(sc->mem_res, slot->bd_offset + 14, val)
#define cpsw_cpdma_read_bd_flags(sc, slot) \
bus_read_2(sc->mem_res, slot->bd_offset + 14)
#define cpsw_write_hdp_slot(sc, queue, slot) \
@@ -410,7 +429,8 @@ cpsw_dump_slot(struct cpsw_softc *sc, struct cpsw_slot *slot)
int i;
cpsw_cpdma_read_bd(sc, slot, &bd);
- printf("BD Addr: 0x%08x Next: 0x%08x\n", cpsw_cpdma_bd_paddr(sc, slot), bd.next);
+ printf("BD Addr : 0x%08x Next : 0x%08x\n",
+ cpsw_cpdma_bd_paddr(sc, slot), bd.next);
printf(" BufPtr: 0x%08x BufLen: 0x%08x\n", bd.bufptr, bd.buflen);
printf(" BufOff: 0x%08x PktLen: 0x%08x\n", bd.bufoff, bd.pktlen);
printf(" Flags: ");
@@ -444,7 +464,7 @@ cpsw_dump_queue(struct cpsw_softc *sc, struct cpsw_slots *q)
int others = 0;
STAILQ_FOREACH(slot, q, next) {
- if (i > 4)
+ if (i > CPSW_TXFRAGS)
++others;
else
cpsw_dump_slot(sc, slot);
@@ -582,6 +602,11 @@ cpsw_init(struct cpsw_softc *sc)
struct cpsw_slot *slot;
uint32_t reg;
+ /* Disable the interrupt pacing. */
+ reg = cpsw_read_4(sc, CPSW_WR_INT_CONTROL);
+ reg &= ~(CPSW_WR_INT_PACE_EN | CPSW_WR_INT_PRESCALE_MASK);
+ cpsw_write_4(sc, CPSW_WR_INT_CONTROL, reg);
+
/* Clear ALE */
cpsw_write_4(sc, CPSW_ALE_CONTROL, CPSW_ALE_CTL_CLEAR_TBL);
@@ -596,23 +621,22 @@ cpsw_init(struct cpsw_softc *sc)
cpsw_write_4(sc, CPSW_PORT_P0_CPDMA_RX_CH_MAP, 0);
/* Initialize ALE: set host port to forwarding(3). */
- cpsw_write_4(sc, CPSW_ALE_PORTCTL(0), 3);
+ cpsw_write_4(sc, CPSW_ALE_PORTCTL(0),
+ ALE_PORTCTL_INGRESS | ALE_PORTCTL_FORWARD);
cpsw_write_4(sc, CPSW_SS_PTYPE, 0);
/* Enable statistics for ports 0, 1 and 2 */
cpsw_write_4(sc, CPSW_SS_STAT_PORT_EN, 7);
- /* Experiment: Turn off flow control */
- /* This seems to fix the watchdog resets that have plagued
- earlier versions of this driver; I'm not yet sure if there
- are negative effects yet. */
+ /* Turn off flow control. */
cpsw_write_4(sc, CPSW_SS_FLOW_CONTROL, 0);
/* Make IP hdr aligned with 4 */
cpsw_write_4(sc, CPSW_CPDMA_RX_BUFFER_OFFSET, 2);
/* Initialize RX Buffer Descriptors */
+ cpsw_write_4(sc, CPSW_CPDMA_RX_PENDTHRESH(0), 0);
cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), 0);
/* Enable TX & RX DMA */
@@ -622,13 +646,16 @@ cpsw_init(struct cpsw_softc *sc)
/* Enable Interrupts for core 0 */
cpsw_write_4(sc, CPSW_WR_C_RX_THRESH_EN(0), 0xFF);
cpsw_write_4(sc, CPSW_WR_C_RX_EN(0), 0xFF);
+ cpsw_write_4(sc, CPSW_WR_C_TX_EN(0), 0xFF);
cpsw_write_4(sc, CPSW_WR_C_MISC_EN(0), 0x1F);
/* Enable host Error Interrupt */
cpsw_write_4(sc, CPSW_CPDMA_DMA_INTMASK_SET, 3);
- /* Enable interrupts for RX Channel 0 */
- cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_SET, 1);
+ /* Enable interrupts for RX and TX on Channel 0 */
+ cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_SET,
+ CPSW_CPDMA_RX_INT(0) | CPSW_CPDMA_RX_INT_THRESH(0));
+ cpsw_write_4(sc, CPSW_CPDMA_TX_INTMASK_SET, 1);
/* Initialze MDIO - ENABLE, PREAMBLE=0, FAULTENB, CLKDIV=0xFF */
/* TODO Calculate MDCLK=CLK/(CLKDIV+1) */
@@ -645,6 +672,8 @@ cpsw_init(struct cpsw_softc *sc)
if (slot != NULL)
cpsw_write_hdp_slot(sc, &sc->rx, slot);
cpsw_rx_enqueue(sc);
+ cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), sc->rx.active_queue_len);
+ cpsw_write_4(sc, CPSW_CPDMA_RX_PENDTHRESH(0), CPSW_TXFRAGS);
/* Activate network interface. */
sc->rx.running = 1;
@@ -677,22 +706,14 @@ cpsw_probe(device_t dev)
static int
cpsw_intr_attach(struct cpsw_softc *sc)
{
+ int i;
- /* Note: We don't use sc->irq_res[2] (TX interrupt) */
- if (bus_setup_intr(sc->dev, sc->irq_res[0],
- INTR_TYPE_NET | INTR_MPSAFE, NULL, cpsw_intr_rx_thresh,
- sc, &sc->ih_cookie[0]) != 0) {
- return (-1);
- }
- if (bus_setup_intr(sc->dev, sc->irq_res[1],
- INTR_TYPE_NET | INTR_MPSAFE, NULL, cpsw_intr_rx,
- sc, &sc->ih_cookie[1]) != 0) {
- return (-1);
- }
- if (bus_setup_intr(sc->dev, sc->irq_res[3],
- INTR_TYPE_NET | INTR_MPSAFE, NULL, cpsw_intr_misc,
- sc, &sc->ih_cookie[3]) != 0) {
- return (-1);
+ for (i = 0; i < CPSW_INTR_COUNT; i++) {
+ if (bus_setup_intr(sc->dev, sc->irq_res[i],
+ INTR_TYPE_NET | INTR_MPSAFE, NULL,
+ cpsw_intr_cb[i].cb, sc, &sc->ih_cookie[i]) != 0) {
+ return (-1);
+ }
}
return (0);
@@ -876,6 +897,11 @@ cpsw_attach(device_t dev)
return (ENXIO);
}
+#ifdef CPSW_ETHERSWITCH
+ for (i = 0; i < CPSW_VLANS; i++)
+ cpsw_vgroups[i].vid = -1;
+#endif
+
/* Reset the controller. */
cpsw_reset(sc);
cpsw_init(sc);
@@ -889,6 +915,7 @@ cpsw_attach(device_t dev)
return (ENXIO);
}
}
+ bus_generic_probe(dev);
bus_generic_attach(dev);
return (0);
@@ -943,7 +970,12 @@ cpsw_detach(device_t dev)
mtx_destroy(&sc->rx.lock);
mtx_destroy(&sc->tx.lock);
- return (0);
+ /* Detach the switch device, if present. */
+ error = bus_generic_detach(dev);
+ if (error != 0)
+ return (error);
+
+ return (device_delete_children(dev));
}
static phandle_t
@@ -1019,14 +1051,14 @@ cpswp_attach(device_t dev)
IFQ_SET_READY(&ifp->if_snd);
/* Get high part of MAC address from control module (mac_id[0|1]_hi) */
- ti_scm_reg_read_4(0x634 + sc->unit * 8, &reg);
+ ti_scm_reg_read_4(SCM_MAC_ID0_HI + sc->unit * 8, &reg);
mac_addr[0] = reg & 0xFF;
mac_addr[1] = (reg >> 8) & 0xFF;
mac_addr[2] = (reg >> 16) & 0xFF;
mac_addr[3] = (reg >> 24) & 0xFF;
/* Get low part of MAC address from control module (mac_id[0|1]_lo) */
- ti_scm_reg_read_4(0x630 + sc->unit * 8, &reg);
+ ti_scm_reg_read_4(SCM_MAC_ID0_LO + sc->unit * 8, &reg);
mac_addr[4] = reg & 0xFF;
mac_addr[5] = (reg >> 8) & 0xFF;
@@ -1055,7 +1087,7 @@ cpswp_detach(device_t dev)
struct cpswp_softc *sc;
sc = device_get_softc(dev);
- CPSWP_DEBUGF(sc, (""));
+ CPSW_DEBUGF(sc->swsc, (""));
if (device_is_attached(dev)) {
ether_ifdetach(sc->ifp);
CPSW_PORT_LOCK(sc);
@@ -1101,7 +1133,7 @@ cpswp_init(void *arg)
{
struct cpswp_softc *sc = arg;
- CPSWP_DEBUGF(sc, (""));
+ CPSW_DEBUGF(sc->swsc, (""));
CPSW_PORT_LOCK(sc);
cpswp_init_locked(arg);
CPSW_PORT_UNLOCK(sc);
@@ -1110,11 +1142,14 @@ cpswp_init(void *arg)
static void
cpswp_init_locked(void *arg)
{
+#ifdef CPSW_ETHERSWITCH
+ int i;
+#endif
struct cpswp_softc *sc = arg;
struct ifnet *ifp;
uint32_t reg;
- CPSWP_DEBUGF(sc, (""));
+ CPSW_DEBUGF(sc->swsc, (""));
CPSW_PORT_LOCK_ASSERT(sc);
ifp = sc->ifp;
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
@@ -1140,8 +1175,9 @@ cpswp_init_locked(void *arg)
reg |= CPSW_SL_MACTL_GMII_ENABLE;
cpsw_write_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit), reg);
- /* Initialize ALE: set port to forwarding(3), initialize addrs */
- cpsw_write_4(sc->swsc, CPSW_ALE_PORTCTL(sc->unit + 1), 3);
+ /* Initialize ALE: set port to forwarding, initialize addrs */
+ cpsw_write_4(sc->swsc, CPSW_ALE_PORTCTL(sc->unit + 1),
+ ALE_PORTCTL_INGRESS | ALE_PORTCTL_FORWARD);
cpswp_ale_update_addresses(sc, 1);
if (sc->swsc->dualemac) {
@@ -1152,6 +1188,14 @@ cpswp_init_locked(void *arg)
(1 << (sc->unit + 1)) | (1 << 0), /* Member list */
(1 << (sc->unit + 1)) | (1 << 0), /* Untagged egress */
(1 << (sc->unit + 1)) | (1 << 0), 0); /* mcast reg flood */
+#ifdef CPSW_ETHERSWITCH
+ for (i = 0; i < CPSW_VLANS; i++) {
+ if (cpsw_vgroups[i].vid != -1)
+ continue;
+ cpsw_vgroups[i].vid = sc->vlan;
+ break;
+ }
+#endif
}
mii_mediachg(sc->mii);
@@ -1182,58 +1226,52 @@ cpsw_shutdown(device_t dev)
}
static void
-cpsw_rx_teardown_locked(struct cpsw_softc *sc)
+cpsw_rx_teardown(struct cpsw_softc *sc)
{
- struct ifnet *ifp;
- struct mbuf *received, *next;
int i = 0;
+ CPSW_RX_LOCK(sc);
CPSW_DEBUGF(sc, ("starting RX teardown"));
+ sc->rx.teardown = 1;
cpsw_write_4(sc, CPSW_CPDMA_RX_TEARDOWN, 0);
- for (;;) {
- received = cpsw_rx_dequeue(sc);
- CPSW_GLOBAL_UNLOCK(sc);
- while (received != NULL) {
- next = received->m_nextpkt;
- received->m_nextpkt = NULL;
- ifp = received->m_pkthdr.rcvif;
- (*ifp->if_input)(ifp, received);
- if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
- received = next;
- }
- CPSW_GLOBAL_LOCK(sc);
- if (!sc->rx.running) {
- CPSW_DEBUGF(sc,
- ("finished RX teardown (%d retries)", i));
- return;
- }
+ CPSW_RX_UNLOCK(sc);
+ while (sc->rx.running) {
if (++i > 10) {
device_printf(sc->dev,
"Unable to cleanly shutdown receiver\n");
return;
}
- DELAY(10);
+ DELAY(200);
}
+ if (!sc->rx.running)
+ CPSW_DEBUGF(sc, ("finished RX teardown (%d retries)", i));
}
static void
-cpsw_tx_teardown_locked(struct cpsw_softc *sc)
+cpsw_tx_teardown(struct cpsw_softc *sc)
{
int i = 0;
+ CPSW_TX_LOCK(sc);
CPSW_DEBUGF(sc, ("starting TX teardown"));
- cpsw_write_4(sc, CPSW_CPDMA_TX_TEARDOWN, 0);
+ /* Start the TX queue teardown if queue is not empty. */
+ if (STAILQ_FIRST(&sc->tx.active) != NULL)
+ cpsw_write_4(sc, CPSW_CPDMA_TX_TEARDOWN, 0);
+ else
+ sc->tx.teardown = 1;
cpsw_tx_dequeue(sc);
while (sc->tx.running && ++i < 10) {
- DELAY(10);
+ DELAY(200);
cpsw_tx_dequeue(sc);
}
if (sc->tx.running) {
device_printf(sc->dev,
"Unable to cleanly shutdown transmitter\n");
}
- CPSW_DEBUGF(sc, ("finished TX teardown (%d retries, %d idle buffers)",
- i, sc->tx.active_queue_len));
+ CPSW_DEBUGF(sc,
+ ("finished TX teardown (%d retries, %d idle buffers)", i,
+ sc->tx.active_queue_len));
+ CPSW_TX_UNLOCK(sc);
}
static void
@@ -1243,7 +1281,7 @@ cpswp_stop_locked(struct cpswp_softc *sc)
uint32_t reg;
ifp = sc->ifp;
- CPSWP_DEBUGF(sc, (""));
+ CPSW_DEBUGF(sc->swsc, (""));
CPSW_PORT_LOCK_ASSERT(sc);
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
@@ -1258,10 +1296,8 @@ cpswp_stop_locked(struct cpswp_softc *sc)
/* Tear down the RX/TX queues. */
if (cpsw_ports_down(sc->swsc)) {
- CPSW_GLOBAL_LOCK(sc->swsc);
- cpsw_rx_teardown_locked(sc->swsc);
- cpsw_tx_teardown_locked(sc->swsc);
- CPSW_GLOBAL_UNLOCK(sc->swsc);
+ cpsw_rx_teardown(sc->swsc);
+ cpsw_tx_teardown(sc->swsc);
}
/* Stop MAC RX/TX modules. */
@@ -1365,7 +1401,7 @@ cpswp_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
if (ifp->if_flags & IFF_UP) {
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
changed = ifp->if_flags ^ sc->if_flags;
- CPSWP_DEBUGF(sc,
+ CPSW_DEBUGF(sc->swsc,
("SIOCSIFFLAGS: UP & RUNNING (changed=0x%x)",
changed));
if (changed & IFF_PROMISC)
@@ -1375,13 +1411,12 @@ cpswp_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
cpsw_set_allmulti(sc,
ifp->if_flags & IFF_ALLMULTI);
} else {
- CPSWP_DEBUGF(sc,
- ("SIOCSIFFLAGS: UP but not RUNNING; starting up"));
+ CPSW_DEBUGF(sc->swsc,
+ ("SIOCSIFFLAGS: starting up"));
cpswp_init_locked(sc);
}
} else if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- CPSWP_DEBUGF(sc,
- ("SIOCSIFFLAGS: not UP but RUNNING; shutting down"));
+ CPSW_DEBUGF(sc->swsc, ("SIOCSIFFLAGS: shutting down"));
cpswp_stop_locked(sc);
}
@@ -1478,9 +1513,6 @@ cpswp_miibus_writereg(device_t dev, int phy, int reg, int value)
return (0);
}
- if ((cpsw_read_4(sc->swsc, sc->phyaccess) & MDIO_PHYACCESS_ACK) == 0)
- device_printf(dev, "Failed to write to PHY.\n");
-
return (0);
}
@@ -1491,7 +1523,7 @@ cpswp_miibus_statchg(device_t dev)
uint32_t mac_control, reg;
sc = device_get_softc(dev);
- CPSWP_DEBUGF(sc, (""));
+ CPSW_DEBUGF(sc->swsc, (""));
reg = CPSW_SL_MACCONTROL(sc->unit);
mac_control = cpsw_read_4(sc->swsc, reg);
@@ -1524,16 +1556,22 @@ cpswp_miibus_statchg(device_t dev)
static void
cpsw_intr_rx(void *arg)
{
- struct cpsw_softc *sc = arg;
+ struct cpsw_softc *sc;
struct ifnet *ifp;
struct mbuf *received, *next;
+ sc = (struct cpsw_softc *)arg;
CPSW_RX_LOCK(sc);
+ if (sc->rx.teardown) {
+ sc->rx.running = 0;
+ sc->rx.teardown = 0;
+ cpsw_write_cp(sc, &sc->rx, 0xfffffffc);
+ }
received = cpsw_rx_dequeue(sc);
cpsw_rx_enqueue(sc);
cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 1);
CPSW_RX_UNLOCK(sc);
-
+
while (received != NULL) {
next = received->m_nextpkt;
received->m_nextpkt = NULL;
@@ -1548,20 +1586,28 @@ static struct mbuf *
cpsw_rx_dequeue(struct cpsw_softc *sc)
{
struct cpsw_cpdma_bd bd;
- struct cpsw_slot *slot;
+ struct cpsw_slot *last, *slot;
struct cpswp_softc *psc;
struct mbuf *mb_head, *mb_tail;
int port, removed = 0;
+ last = NULL;
mb_head = mb_tail = NULL;
/* Pull completed packets off hardware RX queue. */
while ((slot = STAILQ_FIRST(&sc->rx.active)) != NULL) {
cpsw_cpdma_read_bd(sc, slot, &bd);
- if (bd.flags & CPDMA_BD_OWNER)
- break; /* Still in use by hardware */
- CPSW_DEBUGF(sc, ("Removing received packet from RX queue"));
+ /*
+ * Stop on packets still in use by hardware, but do not stop
+ * on packets with the teardown complete flag, they will be
+ * discarded later.
+ */
+ if ((bd.flags & (CPDMA_BD_OWNER | CPDMA_BD_TDOWNCMPLT)) ==
+ CPDMA_BD_OWNER)
+ break;
+
+ last = slot;
++removed;
STAILQ_REMOVE_HEAD(&sc->rx.active, next);
STAILQ_INSERT_TAIL(&sc->rx.avail, slot, next);
@@ -1570,16 +1616,14 @@ cpsw_rx_dequeue(struct cpsw_softc *sc)
bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap);
if (bd.flags & CPDMA_BD_TDOWNCMPLT) {
- CPSW_DEBUGF(sc, ("RX teardown in progress"));
+ CPSW_DEBUGF(sc, ("RX teardown is complete"));
m_freem(slot->mbuf);
slot->mbuf = NULL;
- cpsw_write_cp(sc, &sc->rx, 0xfffffffc);
sc->rx.running = 0;
+ sc->rx.teardown = 0;
break;
}
- cpsw_write_cp_slot(sc, &sc->rx, slot);
-
port = (bd.flags & CPDMA_BD_PORT_MASK) - 1;
KASSERT(port >= 0 && port <= 1,
("patcket received with invalid port: %d", port));
@@ -1612,15 +1656,20 @@ cpsw_rx_dequeue(struct cpsw_softc *sc)
}
mb_tail = slot->mbuf;
slot->mbuf = NULL;
+ if (sc->rx_batch > 0 && sc->rx_batch == removed)
+ break;
}
if (removed != 0) {
+ cpsw_write_cp_slot(sc, &sc->rx, last);
sc->rx.queue_removes += removed;
- sc->rx.active_queue_len -= removed;
sc->rx.avail_queue_len += removed;
+ sc->rx.active_queue_len -= removed;
if (sc->rx.avail_queue_len > sc->rx.max_avail_queue_len)
sc->rx.max_avail_queue_len = sc->rx.avail_queue_len;
+ CPSW_DEBUGF(sc, ("Removed %d received packet(s) from RX queue", removed));
}
+
return (mb_head);
}
@@ -1629,13 +1678,16 @@ cpsw_rx_enqueue(struct cpsw_softc *sc)
{
bus_dma_segment_t seg[1];
struct cpsw_cpdma_bd bd;
- struct cpsw_slots tmpqueue = STAILQ_HEAD_INITIALIZER(tmpqueue);
- struct cpsw_slot *slot, *prev_slot = NULL;
- struct cpsw_slot *last_old_slot, *first_new_slot;
+ struct cpsw_slot *first_new_slot, *last_old_slot, *next, *slot;
int error, nsegs, added = 0;
+ uint32_t flags;
/* Register new mbufs with hardware. */
+ first_new_slot = NULL;
+ last_old_slot = STAILQ_LAST(&sc->rx.active, cpsw_slot, next);
while ((slot = STAILQ_FIRST(&sc->rx.avail)) != NULL) {
+ if (first_new_slot == NULL)
+ first_new_slot = slot;
if (slot->mbuf == NULL) {
slot->mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
if (slot->mbuf == NULL) {
@@ -1665,8 +1717,11 @@ cpsw_rx_enqueue(struct cpsw_softc *sc)
bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_PREREAD);
- /* Create and submit new rx descriptor*/
- bd.next = 0;
+ /* Create and submit new rx descriptor. */
+ if ((next = STAILQ_NEXT(slot, next)) != NULL)
+ bd.next = cpsw_cpdma_bd_paddr(sc, next);
+ else
+ bd.next = 0;
bd.bufptr = seg->ds_addr;
bd.bufoff = 0;
bd.buflen = MCLBYTES - 1;
@@ -1675,38 +1730,35 @@ cpsw_rx_enqueue(struct cpsw_softc *sc)
cpsw_cpdma_write_bd(sc, slot, &bd);
++added;
- if (prev_slot != NULL)
- cpsw_cpdma_write_bd_next(sc, prev_slot, slot);
- prev_slot = slot;
STAILQ_REMOVE_HEAD(&sc->rx.avail, next);
- sc->rx.avail_queue_len--;
- STAILQ_INSERT_TAIL(&tmpqueue, slot, next);
+ STAILQ_INSERT_TAIL(&sc->rx.active, slot, next);
}
- if (added == 0)
+ if (added == 0 || first_new_slot == NULL)
return;
CPSW_DEBUGF(sc, ("Adding %d buffers to RX queue", added));
/* Link new entries to hardware RX queue. */
- last_old_slot = STAILQ_LAST(&sc->rx.active, cpsw_slot, next);
- first_new_slot = STAILQ_FIRST(&tmpqueue);
- STAILQ_CONCAT(&sc->rx.active, &tmpqueue);
- if (first_new_slot == NULL) {
- return;
- } else if (last_old_slot == NULL) {
+ if (last_old_slot == NULL) {
/* Start a fresh queue. */
cpsw_write_hdp_slot(sc, &sc->rx, first_new_slot);
} else {
/* Add buffers to end of current queue. */
cpsw_cpdma_write_bd_next(sc, last_old_slot, first_new_slot);
/* If underrun, restart queue. */
- if (cpsw_cpdma_read_bd_flags(sc, last_old_slot) & CPDMA_BD_EOQ) {
+ if ((flags = cpsw_cpdma_read_bd_flags(sc, last_old_slot)) &
+ CPDMA_BD_EOQ) {
+ flags &= ~CPDMA_BD_EOQ;
+ cpsw_cpdma_write_bd_flags(sc, last_old_slot, flags);
cpsw_write_hdp_slot(sc, &sc->rx, first_new_slot);
+ sc->rx.queue_restart++;
}
}
sc->rx.queue_adds += added;
+ sc->rx.avail_queue_len -= added;
sc->rx.active_queue_len += added;
+ cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), added);
if (sc->rx.active_queue_len > sc->rx.max_active_queue_len) {
sc->rx.max_active_queue_len = sc->rx.active_queue_len;
}
@@ -1715,24 +1767,39 @@ cpsw_rx_enqueue(struct cpsw_softc *sc)
static void
cpswp_start(struct ifnet *ifp)
{
- struct cpswp_softc *sc = ifp->if_softc;
+ struct cpswp_softc *sc;
- CPSW_TX_LOCK(sc->swsc);
- if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && sc->swsc->tx.running) {
- cpswp_tx_enqueue(sc);
- cpsw_tx_dequeue(sc->swsc);
+ sc = ifp->if_softc;
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
+ sc->swsc->tx.running == 0) {
+ return;
}
+ CPSW_TX_LOCK(sc->swsc);
+ cpswp_tx_enqueue(sc);
+ cpsw_tx_dequeue(sc->swsc);
CPSW_TX_UNLOCK(sc->swsc);
}
static void
+cpsw_intr_tx(void *arg)
+{
+ struct cpsw_softc *sc;
+
+ sc = (struct cpsw_softc *)arg;
+ CPSW_TX_LOCK(sc);
+ if (cpsw_read_4(sc, CPSW_CPDMA_TX_CP(0)) == 0xfffffffc)
+ cpsw_write_cp(sc, &sc->tx, 0xfffffffc);
+ cpsw_tx_dequeue(sc);
+ cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 2);
+ CPSW_TX_UNLOCK(sc);
+}
+
+static void
cpswp_tx_enqueue(struct cpswp_softc *sc)
{
bus_dma_segment_t segs[CPSW_TXFRAGS];
struct cpsw_cpdma_bd bd;
- struct cpsw_slots tmpqueue = STAILQ_HEAD_INITIALIZER(tmpqueue);
- struct cpsw_slot *slot, *prev_slot = NULL;
- struct cpsw_slot *last_old_slot, *first_new_slot;
+ struct cpsw_slot *first_new_slot, *last, *last_old_slot, *next, *slot;
struct mbuf *m0;
int error, flags, nsegs, seg, added = 0, padlen;
@@ -1742,6 +1809,9 @@ cpswp_tx_enqueue(struct cpswp_softc *sc)
((sc->unit + 1) & CPDMA_BD_PORT_MASK);
}
/* Pull pending packets from IF queue and prep them for DMA. */
+ last = NULL;
+ first_new_slot = NULL;
+ last_old_slot = STAILQ_LAST(&sc->swsc->tx.active, cpsw_slot, next);
while ((slot = STAILQ_FIRST(&sc->swsc->tx.avail)) != NULL) {
IF_DEQUEUE(&sc->ifp->if_snd, m0);
if (m0 == NULL)
@@ -1769,7 +1839,7 @@ cpswp_tx_enqueue(struct cpswp_softc *sc)
"Can't defragment packet; dropping\n");
m_freem(slot->mbuf);
} else {
- CPSWP_DEBUGF(sc,
+ CPSW_DEBUGF(sc->swsc,
("Requeueing defragmented packet"));
IF_PREPEND(&sc->ifp->if_snd, m0);
}
@@ -1789,16 +1859,28 @@ cpswp_tx_enqueue(struct cpswp_softc *sc)
bus_dmamap_sync(sc->swsc->mbuf_dtag, slot->dmamap,
BUS_DMASYNC_PREWRITE);
- CPSWP_DEBUGF(sc,
+ CPSW_DEBUGF(sc->swsc,
("Queueing TX packet: %d segments + %d pad bytes",
nsegs, padlen));
+ if (first_new_slot == NULL)
+ first_new_slot = slot;
+
+ /* Link from the previous descriptor. */
+ if (last != NULL)
+ cpsw_cpdma_write_bd_next(sc->swsc, last, slot);
+
slot->ifp = sc->ifp;
+
/* If there is only one segment, the for() loop
* gets skipped and the single buffer gets set up
* as both SOP and EOP. */
- /* Start by setting up the first buffer */
- bd.next = 0;
+ if (nsegs > 1) {
+ next = STAILQ_NEXT(slot, next);
+ bd.next = cpsw_cpdma_bd_paddr(sc->swsc, next);
+ } else
+ bd.next = 0;
+ /* Start by setting up the first buffer. */
bd.bufptr = segs[0].ds_addr;
bd.bufoff = 0;
bd.buflen = segs[0].ds_len;
@@ -1807,19 +1889,16 @@ cpswp_tx_enqueue(struct cpswp_softc *sc)
for (seg = 1; seg < nsegs; ++seg) {
/* Save the previous buffer (which isn't EOP) */
cpsw_cpdma_write_bd(sc->swsc, slot, &bd);
- if (prev_slot != NULL) {
- cpsw_cpdma_write_bd_next(sc->swsc, prev_slot,
- slot);
- }
- prev_slot = slot;
STAILQ_REMOVE_HEAD(&sc->swsc->tx.avail, next);
- sc->swsc->tx.avail_queue_len--;
- STAILQ_INSERT_TAIL(&tmpqueue, slot, next);
- ++added;
+ STAILQ_INSERT_TAIL(&sc->swsc->tx.active, slot, next);
slot = STAILQ_FIRST(&sc->swsc->tx.avail);
/* Setup next buffer (which isn't SOP) */
- bd.next = 0;
+ if (nsegs > seg + 1) {
+ next = STAILQ_NEXT(slot, next);
+ bd.next = cpsw_cpdma_bd_paddr(sc->swsc, next);
+ } else
+ bd.next = 0;
bd.bufptr = segs[seg].ds_addr;
bd.bufoff = 0;
bd.buflen = segs[seg].ds_len;
@@ -1829,25 +1908,18 @@ cpswp_tx_enqueue(struct cpswp_softc *sc)
/* Save the final buffer. */
if (padlen <= 0)
bd.flags |= CPDMA_BD_EOP;
+ else {
+ next = STAILQ_NEXT(slot, next);
+ bd.next = cpsw_cpdma_bd_paddr(sc->swsc, next);
+ }
cpsw_cpdma_write_bd(sc->swsc, slot, &bd);
- if (prev_slot != NULL)
- cpsw_cpdma_write_bd_next(sc->swsc, prev_slot, slot);
- prev_slot = slot;
STAILQ_REMOVE_HEAD(&sc->swsc->tx.avail, next);
- sc->swsc->tx.avail_queue_len--;
- STAILQ_INSERT_TAIL(&tmpqueue, slot, next);
- ++added;
+ STAILQ_INSERT_TAIL(&sc->swsc->tx.active, slot, next);
if (padlen > 0) {
slot = STAILQ_FIRST(&sc->swsc->tx.avail);
- STAILQ_REMOVE_HEAD(&sc->swsc->tx.avail, next);
- sc->swsc->tx.avail_queue_len--;
- STAILQ_INSERT_TAIL(&tmpqueue, slot, next);
- ++added;
- /* Setup buffer of null pad bytes (definitely EOP) */
- cpsw_cpdma_write_bd_next(sc->swsc, prev_slot, slot);
- prev_slot = slot;
+ /* Setup buffer of null pad bytes (definitely EOP). */
bd.next = 0;
bd.bufptr = sc->swsc->null_mbuf_paddr;
bd.bufoff = 0;
@@ -1856,8 +1928,14 @@ cpswp_tx_enqueue(struct cpswp_softc *sc)
bd.flags = CPDMA_BD_EOP | CPDMA_BD_OWNER | flags;
cpsw_cpdma_write_bd(sc->swsc, slot, &bd);
++nsegs;
+
+ STAILQ_REMOVE_HEAD(&sc->swsc->tx.avail, next);
+ STAILQ_INSERT_TAIL(&sc->swsc->tx.active, slot, next);
}
+ last = slot;
+
+ added += nsegs;
if (nsegs > sc->swsc->tx.longest_chain)
sc->swsc->tx.longest_chain = nsegs;
@@ -1866,33 +1944,27 @@ cpswp_tx_enqueue(struct cpswp_softc *sc)
BPF_MTAP(sc->ifp, m0);
}
- /* Attach the list of new buffers to the hardware TX queue. */
- last_old_slot = STAILQ_LAST(&sc->swsc->tx.active, cpsw_slot, next);
- first_new_slot = STAILQ_FIRST(&tmpqueue);
- STAILQ_CONCAT(&sc->swsc->tx.active, &tmpqueue);
- if (first_new_slot == NULL) {
+ if (first_new_slot == NULL)
return;
- } else if (last_old_slot == NULL) {
- /* Start a fresh queue. */
- sc->swsc->last_hdp = cpsw_cpdma_bd_paddr(sc->swsc, first_new_slot);
- cpsw_write_hdp_slot(sc->swsc, &sc->swsc->tx, first_new_slot);
- } else {
+
+ /* Attach the list of new buffers to the hardware TX queue. */
+ if (last_old_slot != NULL &&
+ (cpsw_cpdma_read_bd_flags(sc->swsc, last_old_slot) &
+ CPDMA_BD_EOQ) == 0) {
/* Add buffers to end of current queue. */
cpsw_cpdma_write_bd_next(sc->swsc, last_old_slot,
first_new_slot);
- /* If underrun, restart queue. */
- if (cpsw_cpdma_read_bd_flags(sc->swsc, last_old_slot) &
- CPDMA_BD_EOQ) {
- sc->swsc->last_hdp = cpsw_cpdma_bd_paddr(sc->swsc, first_new_slot);
- cpsw_write_hdp_slot(sc->swsc, &sc->swsc->tx,
- first_new_slot);
- }
+ } else {
+ /* Start a fresh queue. */
+ cpsw_write_hdp_slot(sc->swsc, &sc->swsc->tx, first_new_slot);
}
sc->swsc->tx.queue_adds += added;
+ sc->swsc->tx.avail_queue_len -= added;
sc->swsc->tx.active_queue_len += added;
if (sc->swsc->tx.active_queue_len > sc->swsc->tx.max_active_queue_len) {
sc->swsc->tx.max_active_queue_len = sc->swsc->tx.active_queue_len;
}
+ CPSW_DEBUGF(sc->swsc, ("Queued %d TX packet(s)", added));
}
static int
@@ -1902,27 +1974,31 @@ cpsw_tx_dequeue(struct cpsw_softc *sc)
struct cpsw_cpdma_bd bd;
uint32_t flags, removed = 0;
- slot = STAILQ_FIRST(&sc->tx.active);
- if (slot == NULL && cpsw_read_cp(sc, &sc->tx) == 0xfffffffc) {
- CPSW_DEBUGF(sc, ("TX teardown of an empty queue"));
- cpsw_write_cp(sc, &sc->tx, 0xfffffffc);
- sc->tx.running = 0;
- return (0);
- }
-
/* Pull completed buffers off the hardware TX queue. */
+ slot = STAILQ_FIRST(&sc->tx.active);
while (slot != NULL) {
flags = cpsw_cpdma_read_bd_flags(sc, slot);
- if (flags & CPDMA_BD_OWNER)
+
+ /* TearDown complete is only marked on the SOP for the packet. */
+ if ((flags & (CPDMA_BD_SOP | CPDMA_BD_TDOWNCMPLT)) ==
+ (CPDMA_BD_SOP | CPDMA_BD_TDOWNCMPLT)) {
+ sc->tx.teardown = 1;
+ }
+
+ if ((flags & CPDMA_BD_OWNER) != 0 && sc->tx.teardown == 0)
break; /* Hardware is still using this packet. */
- CPSW_DEBUGF(sc, ("TX removing completed packet"));
bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap);
m_freem(slot->mbuf);
slot->mbuf = NULL;
- if (slot->ifp)
- if_inc_counter(slot->ifp, IFCOUNTER_OPACKETS, 1);
+
+ if (slot->ifp) {
+ if (sc->tx.teardown == 0)
+ if_inc_counter(slot->ifp, IFCOUNTER_OPACKETS, 1);
+ else
+ if_inc_counter(slot->ifp, IFCOUNTER_OQDROPS, 1);
+ }
/* Dequeue any additional buffers used by this packet. */
while (slot != NULL && slot->mbuf == NULL) {
@@ -1933,37 +2009,34 @@ cpsw_tx_dequeue(struct cpsw_softc *sc)
slot = STAILQ_FIRST(&sc->tx.active);
}
- /* TearDown complete is only marked on the SOP for the packet. */
- if ((flags & (CPDMA_BD_SOP | CPDMA_BD_TDOWNCMPLT)) ==
- (CPDMA_BD_EOP | CPDMA_BD_TDOWNCMPLT)) {
- CPSW_DEBUGF(sc, ("TX teardown in progress"));
- cpsw_write_cp(sc, &sc->tx, 0xfffffffc);
- // TODO: Increment a count of dropped TX packets
- sc->tx.running = 0;
- break;
- }
+ cpsw_write_cp_slot(sc, &sc->tx, last_removed_slot);
- if ((flags & CPDMA_BD_EOP) == 0)
- flags = cpsw_cpdma_read_bd_flags(sc, last_removed_slot);
- if ((flags & (CPDMA_BD_EOP | CPDMA_BD_EOQ)) ==
+ /* Restart the TX queue if necessary. */
+ cpsw_cpdma_read_bd(sc, last_removed_slot, &bd);
+ if (slot != NULL && bd.next != 0 && (bd.flags &
+ (CPDMA_BD_EOP | CPDMA_BD_OWNER | CPDMA_BD_EOQ)) ==
(CPDMA_BD_EOP | CPDMA_BD_EOQ)) {
- cpsw_cpdma_read_bd(sc, last_removed_slot, &bd);
- if (bd.next != 0 && bd.next != sc->last_hdp) {
- /* Restart the queue. */
- sc->last_hdp = bd.next;
- cpsw_write_4(sc, sc->tx.hdp_offset, bd.next);
- }
+ cpsw_write_hdp_slot(sc, &sc->tx, slot);
+ sc->tx.queue_restart++;
+ break;
}
}
if (removed != 0) {
- cpsw_write_cp_slot(sc, &sc->tx, last_removed_slot);
sc->tx.queue_removes += removed;
sc->tx.active_queue_len -= removed;
sc->tx.avail_queue_len += removed;
if (sc->tx.avail_queue_len > sc->tx.max_avail_queue_len)
sc->tx.max_avail_queue_len = sc->tx.avail_queue_len;
+ CPSW_DEBUGF(sc, ("TX removed %d completed packet(s)", removed));
}
+
+ if (sc->tx.teardown && STAILQ_EMPTY(&sc->tx.active)) {
+ CPSW_DEBUGF(sc, ("TX teardown is complete"));
+ sc->tx.teardown = 0;
+ sc->tx.running = 0;
+ }
+
return (removed);
}
@@ -1976,11 +2049,25 @@ cpsw_tx_dequeue(struct cpsw_softc *sc)
static void
cpsw_intr_rx_thresh(void *arg)
{
- struct cpsw_softc *sc = arg;
- uint32_t stat = cpsw_read_4(sc, CPSW_WR_C_RX_THRESH_STAT(0));
+ struct cpsw_softc *sc;
+ struct ifnet *ifp;
+ struct mbuf *received, *next;
- CPSW_DEBUGF(sc, ("stat=%x", stat));
+ sc = (struct cpsw_softc *)arg;
+ CPSW_RX_LOCK(sc);
+ received = cpsw_rx_dequeue(sc);
+ cpsw_rx_enqueue(sc);
cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 0);
+ CPSW_RX_UNLOCK(sc);
+
+ while (received != NULL) {
+ next = received->m_nextpkt;
+ received->m_nextpkt = NULL;
+ ifp = received->m_pkthdr.rcvif;
+ (*ifp->if_input)(ifp, received);
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
+ received = next;
+ }
}
static void
@@ -2118,7 +2205,7 @@ cpswp_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
struct mii_data *mii;
sc = ifp->if_softc;
- CPSWP_DEBUGF(sc, (""));
+ CPSW_DEBUGF(sc->swsc, (""));
CPSW_PORT_LOCK(sc);
mii = sc->mii;
@@ -2135,7 +2222,7 @@ cpswp_ifmedia_upd(struct ifnet *ifp)
struct cpswp_softc *sc;
sc = ifp->if_softc;
- CPSWP_DEBUGF(sc, (""));
+ CPSW_DEBUGF(sc->swsc, (""));
CPSW_PORT_LOCK(sc);
mii_mediachg(sc->mii);
sc->media_status = sc->mii->mii_media.ifm_media;
@@ -2152,6 +2239,11 @@ cpsw_tx_watchdog_full_reset(struct cpsw_softc *sc)
cpsw_debugf_head("CPSW watchdog");
device_printf(sc->dev, "watchdog timeout\n");
+ printf("CPSW_CPDMA_TX%d_HDP=0x%x\n", 0,
+ cpsw_read_4(sc, CPSW_CPDMA_TX_HDP(0)));
+ printf("CPSW_CPDMA_TX%d_CP=0x%x\n", 0,
+ cpsw_read_4(sc, CPSW_CPDMA_TX_CP(0)));
+ cpsw_dump_queue(sc, &sc->tx.active);
for (i = 0; i < CPSW_PORTS; i++) {
if (!sc->dualemac && i != sc->active_slave)
continue;
@@ -2168,7 +2260,7 @@ cpsw_tx_watchdog(void *msc)
struct cpsw_softc *sc;
sc = msc;
- CPSW_GLOBAL_LOCK(sc);
+ CPSW_TX_LOCK(sc);
if (sc->tx.active_queue_len == 0 || !sc->tx.running) {
sc->watchdog.timer = 0; /* Nothing to do. */
} else if (sc->tx.queue_removes > sc->tx.queue_removes_at_last_tick) {
@@ -2185,7 +2277,7 @@ cpsw_tx_watchdog(void *msc)
}
}
sc->tx.queue_removes_at_last_tick = sc->tx.queue_removes;
- CPSW_GLOBAL_UNLOCK(sc);
+ CPSW_TX_UNLOCK(sc);
/* Schedule another timeout one second from now */
callout_reset(&sc->watchdog.callout, hz, cpsw_tx_watchdog, sc);
@@ -2491,6 +2583,51 @@ cpsw_stat_attached(SYSCTL_HANDLER_ARGS)
}
static int
+cpsw_intr_coalesce(SYSCTL_HANDLER_ARGS)
+{
+ int error;
+ struct cpsw_softc *sc;
+ uint32_t ctrl, intr_per_ms;
+
+ sc = (struct cpsw_softc *)arg1;
+ error = sysctl_handle_int(oidp, &sc->coal_us, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ ctrl = cpsw_read_4(sc, CPSW_WR_INT_CONTROL);
+ ctrl &= ~(CPSW_WR_INT_PACE_EN | CPSW_WR_INT_PRESCALE_MASK);
+ if (sc->coal_us == 0) {
+ /* Disable the interrupt pace hardware. */
+ cpsw_write_4(sc, CPSW_WR_INT_CONTROL, ctrl);
+ cpsw_write_4(sc, CPSW_WR_C_RX_IMAX(0), 0);
+ cpsw_write_4(sc, CPSW_WR_C_TX_IMAX(0), 0);
+ return (0);
+ }
+
+ if (sc->coal_us > CPSW_WR_C_IMAX_US_MAX)
+ sc->coal_us = CPSW_WR_C_IMAX_US_MAX;
+ if (sc->coal_us < CPSW_WR_C_IMAX_US_MIN)
+ sc->coal_us = CPSW_WR_C_IMAX_US_MIN;
+ intr_per_ms = 1000 / sc->coal_us;
+ /* Just to make sure... */
+ if (intr_per_ms > CPSW_WR_C_IMAX_MAX)
+ intr_per_ms = CPSW_WR_C_IMAX_MAX;
+ if (intr_per_ms < CPSW_WR_C_IMAX_MIN)
+ intr_per_ms = CPSW_WR_C_IMAX_MIN;
+
+ /* Set the prescale to produce 4us pulses from the 125 Mhz clock. */
+ ctrl |= (125 * 4) & CPSW_WR_INT_PRESCALE_MASK;
+
+ /* Enable the interrupt pace hardware. */
+ cpsw_write_4(sc, CPSW_WR_C_RX_IMAX(0), intr_per_ms);
+ cpsw_write_4(sc, CPSW_WR_C_TX_IMAX(0), intr_per_ms);
+ ctrl |= CPSW_WR_INT_C0_RX_PULSE | CPSW_WR_INT_C0_TX_PULSE;
+ cpsw_write_4(sc, CPSW_WR_INT_CONTROL, ctrl);
+
+ return (0);
+}
+
+static int
cpsw_stat_uptime(SYSCTL_HANDLER_ARGS)
{
struct cpsw_softc *swsc;
@@ -2538,6 +2675,9 @@ cpsw_add_queue_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node,
SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "totalDequeued",
CTLFLAG_RD, &queue->queue_removes, 0,
"Total buffers removed from queue");
+ SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "queueRestart",
+ CTLFLAG_RD, &queue->queue_restart, 0,
+ "Total times the queue has been restarted");
SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "longestChain",
CTLFLAG_RD, &queue->longest_chain, 0,
"Max buffers used for a single packet");
@@ -2571,10 +2711,17 @@ cpsw_add_sysctls(struct cpsw_softc *sc)
SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "debug",
CTLFLAG_RW, &sc->debug, 0, "Enable switch debug messages");
+ SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "rx_batch",
+ CTLFLAG_RW, &sc->rx_batch, 0, "Set the rx batch size");
+
SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, "attachedSecs",
CTLTYPE_UINT | CTLFLAG_RD, sc, 0, cpsw_stat_attached, "IU",
"Time since driver attach");
+ SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, "intr_coalesce_us",
+ CTLTYPE_UINT | CTLFLAG_RW, sc, 0, cpsw_intr_coalesce, "IU",
+ "minimum time between interrupts");
+
node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "ports",
CTLFLAG_RD, NULL, "CPSW Ports Statistics");
ports_parent = SYSCTL_CHILDREN(node);
@@ -2618,3 +2765,229 @@ cpsw_add_sysctls(struct cpsw_softc *sc)
CTLFLAG_RD, NULL, "Watchdog Statistics");
cpsw_add_watchdog_sysctls(ctx, node, sc);
}
+
+#ifdef CPSW_ETHERSWITCH
+static etherswitch_info_t etherswitch_info = {
+ .es_nports = CPSW_PORTS + 1,
+ .es_nvlangroups = CPSW_VLANS,
+ .es_name = "TI Common Platform Ethernet Switch (CPSW)",
+ .es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q,
+};
+
+static etherswitch_info_t *
+cpsw_getinfo(device_t dev)
+{
+ return (&etherswitch_info);
+}
+
+static int
+cpsw_getport(device_t dev, etherswitch_port_t *p)
+{
+ int err;
+ struct cpsw_softc *sc;
+ struct cpswp_softc *psc;
+ struct ifmediareq *ifmr;
+ uint32_t reg;
+
+ if (p->es_port < 0 || p->es_port > CPSW_PORTS)
+ return (ENXIO);
+
+ err = 0;
+ sc = device_get_softc(dev);
+ if (p->es_port == CPSW_CPU_PORT) {
+ p->es_flags |= ETHERSWITCH_PORT_CPU;
+ ifmr = &p->es_ifmr;
+ ifmr->ifm_current = ifmr->ifm_active =
+ IFM_ETHER | IFM_1000_T | IFM_FDX;
+ ifmr->ifm_mask = 0;
+ ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
+ ifmr->ifm_count = 0;
+ } else {
+ psc = device_get_softc(sc->port[p->es_port - 1].dev);
+ err = ifmedia_ioctl(psc->ifp, &p->es_ifr,
+ &psc->mii->mii_media, SIOCGIFMEDIA);
+ }
+ reg = cpsw_read_4(sc, CPSW_PORT_P_VLAN(p->es_port));
+ p->es_pvid = reg & ETHERSWITCH_VID_MASK;
+
+ reg = cpsw_read_4(sc, CPSW_ALE_PORTCTL(p->es_port));
+ if (reg & ALE_PORTCTL_DROP_UNTAGGED)
+ p->es_flags |= ETHERSWITCH_PORT_DROPUNTAGGED;
+ if (reg & ALE_PORTCTL_INGRESS)
+ p->es_flags |= ETHERSWITCH_PORT_INGRESS;
+
+ return (err);
+}
+
+static int
+cpsw_setport(device_t dev, etherswitch_port_t *p)
+{
+ struct cpsw_softc *sc;
+ struct cpswp_softc *psc;
+ struct ifmedia *ifm;
+ uint32_t reg;
+
+ if (p->es_port < 0 || p->es_port > CPSW_PORTS)
+ return (ENXIO);
+
+ sc = device_get_softc(dev);
+ if (p->es_pvid != 0) {
+ cpsw_write_4(sc, CPSW_PORT_P_VLAN(p->es_port),
+ p->es_pvid & ETHERSWITCH_VID_MASK);
+ }
+
+ reg = cpsw_read_4(sc, CPSW_ALE_PORTCTL(p->es_port));
+ if (p->es_flags & ETHERSWITCH_PORT_DROPUNTAGGED)
+ reg |= ALE_PORTCTL_DROP_UNTAGGED;
+ else
+ reg &= ~ALE_PORTCTL_DROP_UNTAGGED;
+ if (p->es_flags & ETHERSWITCH_PORT_INGRESS)
+ reg |= ALE_PORTCTL_INGRESS;
+ else
+ reg &= ~ALE_PORTCTL_INGRESS;
+ cpsw_write_4(sc, CPSW_ALE_PORTCTL(p->es_port), reg);
+
+ /* CPU port does not allow media settings. */
+ if (p->es_port == CPSW_CPU_PORT)
+ return (0);
+
+ psc = device_get_softc(sc->port[p->es_port - 1].dev);
+ ifm = &psc->mii->mii_media;
+
+ return (ifmedia_ioctl(psc->ifp, &p->es_ifr, ifm, SIOCSIFMEDIA));
+}
+
+static int
+cpsw_getconf(device_t dev, etherswitch_conf_t *conf)
+{
+
+ /* Return the VLAN mode. */
+ conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
+ conf->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
+
+ return (0);
+}
+
+static int
+cpsw_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
+{
+ int i, vid;
+ uint32_t ale_entry[3];
+ struct cpsw_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (vg->es_vlangroup >= CPSW_VLANS)
+ return (EINVAL);
+
+ vg->es_vid = 0;
+ vid = cpsw_vgroups[vg->es_vlangroup].vid;
+ if (vid == -1)
+ return (0);
+
+ for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) {
+ cpsw_ale_read_entry(sc, i, ale_entry);
+ if (ALE_TYPE(ale_entry) != ALE_TYPE_VLAN)
+ continue;
+ if (vid != ALE_VLAN(ale_entry))
+ continue;
+
+ vg->es_fid = 0;
+ vg->es_vid = ALE_VLAN(ale_entry) | ETHERSWITCH_VID_VALID;
+ vg->es_member_ports = ALE_VLAN_MEMBERS(ale_entry);
+ vg->es_untagged_ports = ALE_VLAN_UNTAG(ale_entry);
+ }
+
+ return (0);
+}
+
+static void
+cpsw_remove_vlan(struct cpsw_softc *sc, int vlan)
+{
+ int i;
+ uint32_t ale_entry[3];
+
+ for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) {
+ cpsw_ale_read_entry(sc, i, ale_entry);
+ if (ALE_TYPE(ale_entry) != ALE_TYPE_VLAN)
+ continue;
+ if (vlan != ALE_VLAN(ale_entry))
+ continue;
+ ale_entry[0] = ale_entry[1] = ale_entry[2] = 0;
+ cpsw_ale_write_entry(sc, i, ale_entry);
+ break;
+ }
+}
+
+static int
+cpsw_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
+{
+ int i;
+ struct cpsw_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ for (i = 0; i < CPSW_VLANS; i++) {
+ /* Is this Vlan ID in use by another vlangroup ? */
+ if (vg->es_vlangroup != i && cpsw_vgroups[i].vid == vg->es_vid)
+ return (EINVAL);
+ }
+
+ if (vg->es_vid == 0) {
+ if (cpsw_vgroups[vg->es_vlangroup].vid == -1)
+ return (0);
+ cpsw_remove_vlan(sc, cpsw_vgroups[vg->es_vlangroup].vid);
+ cpsw_vgroups[vg->es_vlangroup].vid = -1;
+ vg->es_untagged_ports = 0;
+ vg->es_member_ports = 0;
+ vg->es_vid = 0;
+ return (0);
+ }
+
+ vg->es_vid &= ETHERSWITCH_VID_MASK;
+ vg->es_member_ports &= CPSW_PORTS_MASK;
+ vg->es_untagged_ports &= CPSW_PORTS_MASK;
+
+ if (cpsw_vgroups[vg->es_vlangroup].vid != -1 &&
+ cpsw_vgroups[vg->es_vlangroup].vid != vg->es_vid)
+ return (EINVAL);
+
+ cpsw_vgroups[vg->es_vlangroup].vid = vg->es_vid;
+ cpsw_ale_update_vlan_table(sc, vg->es_vid, vg->es_member_ports,
+ vg->es_untagged_ports, vg->es_member_ports, 0);
+
+ return (0);
+}
+
+static int
+cpsw_readreg(device_t dev, int addr)
+{
+
+ /* Not supported. */
+ return (0);
+}
+
+static int
+cpsw_writereg(device_t dev, int addr, int value)
+{
+
+ /* Not supported. */
+ return (0);
+}
+
+static int
+cpsw_readphy(device_t dev, int phy, int reg)
+{
+
+ /* Not supported. */
+ return (0);
+}
+
+static int
+cpsw_writephy(device_t dev, int phy, int reg, int data)
+{
+
+ /* Not supported. */
+ return (0);
+}
+#endif
diff --git a/sys/arm/ti/cpsw/if_cpswreg.h b/sys/arm/ti/cpsw/if_cpswreg.h
index bea6f19..6d6a647 100644
--- a/sys/arm/ti/cpsw/if_cpswreg.h
+++ b/sys/arm/ti/cpsw/if_cpswreg.h
@@ -64,10 +64,13 @@
#define CPSW_CPDMA_RX_INTSTAT_MASKED (CPSW_CPDMA_OFFSET + 0xA4)
#define CPSW_CPDMA_RX_INTMASK_SET (CPSW_CPDMA_OFFSET + 0xA8)
#define CPSW_CPDMA_RX_INTMASK_CLEAR (CPSW_CPDMA_OFFSET + 0xAc)
+#define CPSW_CPDMA_RX_INT_THRESH(_ch) (1 << (8 + ((_ch) & 7)))
+#define CPSW_CPDMA_RX_INT(_ch) (1 << (0 + ((_ch) & 7)))
#define CPSW_CPDMA_DMA_INTSTAT_RAW (CPSW_CPDMA_OFFSET + 0xB0)
#define CPSW_CPDMA_DMA_INTSTAT_MASKED (CPSW_CPDMA_OFFSET + 0xB4)
#define CPSW_CPDMA_DMA_INTMASK_SET (CPSW_CPDMA_OFFSET + 0xB8)
#define CPSW_CPDMA_DMA_INTMASK_CLEAR (CPSW_CPDMA_OFFSET + 0xBC)
+#define CPSW_CPDMA_RX_PENDTHRESH(p) (CPSW_CPDMA_OFFSET + 0x0c0 + ((p) * 0x04))
#define CPSW_CPDMA_RX_FREEBUFFER(p) (CPSW_CPDMA_OFFSET + 0x0e0 + ((p) * 0x04))
#define CPSW_STATS_OFFSET 0x0900
@@ -103,6 +106,14 @@
#define ALE_VLAN_UNTAG(_a) ((_a[0] >> 24) & 7)
#define ALE_VLAN_MEMBERS(_a) (_a[0] & 7)
#define CPSW_ALE_PORTCTL(p) (CPSW_ALE_OFFSET + 0x40 + ((p) * 0x04))
+#define ALE_PORTCTL_NO_SA_UPDATE (1 << 5)
+#define ALE_PORTCTL_NO_LEARN (1 << 4)
+#define ALE_PORTCTL_INGRESS (1 << 3)
+#define ALE_PORTCTL_DROP_UNTAGGED (1 << 2)
+#define ALE_PORTCTL_FORWARD 3
+#define ALE_PORTCTL_LEARN 2
+#define ALE_PORTCTL_BLOCKED 1
+#define ALE_PORTCTL_DISABLED 0
/* SL1 is at 0x0D80, SL2 is at 0x0DC0 */
#define CPSW_SL_OFFSET 0x0D80
@@ -138,6 +149,17 @@
#define CPSW_WR_SOFT_RESET (CPSW_WR_OFFSET + 0x04)
#define CPSW_WR_CONTROL (CPSW_WR_OFFSET + 0x08)
#define CPSW_WR_INT_CONTROL (CPSW_WR_OFFSET + 0x0c)
+#define CPSW_WR_INT_C0_RX_PULSE (1 << 16)
+#define CPSW_WR_INT_C0_TX_PULSE (1 << 17)
+#define CPSW_WR_INT_C1_RX_PULSE (1 << 18)
+#define CPSW_WR_INT_C1_TX_PULSE (1 << 19)
+#define CPSW_WR_INT_C2_RX_PULSE (1 << 20)
+#define CPSW_WR_INT_C2_TX_PULSE (1 << 21)
+#define CPSW_WR_INT_PACE_EN \
+ (CPSW_WR_INT_C0_RX_PULSE | CPSW_WR_INT_C0_TX_PULSE | \
+ CPSW_WR_INT_C1_RX_PULSE | CPSW_WR_INT_C1_TX_PULSE | \
+ CPSW_WR_INT_C2_RX_PULSE | CPSW_WR_INT_C2_TX_PULSE)
+#define CPSW_WR_INT_PRESCALE_MASK 0xfff
#define CPSW_WR_C_RX_THRESH_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x10)
#define CPSW_WR_C_RX_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x14)
#define CPSW_WR_C_TX_EN(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x18)
@@ -151,6 +173,13 @@
#define CPSW_WR_C_MISC_HOST_PEND (1 << 2)
#define CPSW_WR_C_MISC_MDIOLINK (1 << 1)
#define CPSW_WR_C_MISC_MDIOUSER (1 << 0)
+#define CPSW_WR_C_RX_IMAX(p) (CPSW_WR_OFFSET + (0x08 * (p)) + 0x70)
+#define CPSW_WR_C_TX_IMAX(p) (CPSW_WR_OFFSET + (0x08 * (p)) + 0x74)
+#define CPSW_WR_C_IMAX_MASK 0x3f
+#define CPSW_WR_C_IMAX_MAX 63
+#define CPSW_WR_C_IMAX_MIN 2
+#define CPSW_WR_C_IMAX_US_MAX 500
+#define CPSW_WR_C_IMAX_US_MIN 16
#define CPSW_CPPI_RAM_OFFSET 0x2000
#define CPSW_CPPI_RAM_SIZE 0x2000
diff --git a/sys/arm/ti/cpsw/if_cpswvar.h b/sys/arm/ti/cpsw/if_cpswvar.h
index 953766b..f037dd5 100644
--- a/sys/arm/ti/cpsw/if_cpswvar.h
+++ b/sys/arm/ti/cpsw/if_cpswvar.h
@@ -33,13 +33,23 @@
#define CPSW_INTR_COUNT 4
/* MII BUS */
-#define CPSW_MIIBUS_RETRIES 5
-#define CPSW_MIIBUS_DELAY 1000
+#define CPSW_MIIBUS_RETRIES 20
+#define CPSW_MIIBUS_DELAY 100
#define CPSW_MAX_ALE_ENTRIES 1024
#define CPSW_SYSCTL_COUNT 34
+#ifdef CPSW_ETHERSWITCH
+#define CPSW_CPU_PORT 0
+#define CPSW_PORTS_MASK 0x7
+#define CPSW_VLANS 128 /* Arbitrary number. */
+
+struct cpsw_vlangroups {
+ int vid;
+};
+#endif
+
struct cpsw_slot {
uint32_t bd_offset; /* Offset of corresponding BD within CPPI RAM. */
bus_dmamap_t dmamap;
@@ -52,11 +62,13 @@ STAILQ_HEAD(cpsw_slots, cpsw_slot);
struct cpsw_queue {
struct mtx lock;
int running;
+ int teardown;
struct cpsw_slots active;
struct cpsw_slots avail;
uint32_t queue_adds; /* total bufs added */
uint32_t queue_removes; /* total bufs removed */
uint32_t queue_removes_at_last_tick; /* Used by watchdog */
+ uint32_t queue_restart;
int queue_slots;
int active_queue_len;
int max_active_queue_len;
@@ -77,13 +89,14 @@ struct cpsw_softc {
int active_slave;
int debug;
int dualemac;
+ int rx_batch;
phandle_t node;
struct bintime attach_uptime; /* system uptime when attach happened. */
struct cpsw_port port[2];
+ unsigned coal_us;
/* RX and TX buffer tracking */
struct cpsw_queue rx, tx;
- uint32_t last_hdp;
/* We expect 1 memory resource and 4 interrupts from the device tree. */
int mem_rid;
diff --git a/sys/arm/ti/ti_gpio.c b/sys/arm/ti/ti_gpio.c
index 583a65a..fbaa2dd 100644
--- a/sys/arm/ti/ti_gpio.c
+++ b/sys/arm/ti/ti_gpio.c
@@ -782,7 +782,8 @@ ti_gpio_detach(device_t dev)
/* Disable all interrupts */
if (sc->sc_mem_res != NULL)
ti_gpio_intr_clr(sc, 0xffffffff);
- gpiobus_detach_bus(dev);
+ if (sc->sc_busdev != NULL)
+ gpiobus_detach_bus(dev);
#ifdef INTRNG
if (sc->sc_isrcs != NULL)
ti_gpio_pic_detach(sc);
@@ -801,10 +802,12 @@ ti_gpio_detach(device_t dev)
bus_teardown_intr(dev, sc->sc_irq_res,
sc->sc_irq_hdl);
}
- bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
- sc->sc_irq_res);
- bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
- sc->sc_mem_res);
+ if (sc->sc_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
+ sc->sc_irq_res);
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
+ sc->sc_mem_res);
TI_GPIO_LOCK_DESTROY(sc);
return (0);
diff --git a/sys/arm/ti/ti_scm.c b/sys/arm/ti/ti_scm.c
index 0752a21..ae36a9b 100644
--- a/sys/arm/ti/ti_scm.c
+++ b/sys/arm/ti/ti_scm.c
@@ -129,7 +129,10 @@ ti_scm_attach(device_t dev)
ti_scm_sc = sc;
- return (0);
+ /* Attach platform extensions, if any. */
+ bus_generic_probe(dev);
+
+ return (bus_generic_attach(dev));
}
int
diff --git a/sys/arm/ti/ti_spi.c b/sys/arm/ti/ti_spi.c
index e35f365..68805a8 100644
--- a/sys/arm/ti/ti_spi.c
+++ b/sys/arm/ti/ti_spi.c
@@ -445,9 +445,9 @@ ti_spi_gcd(int a, int b)
static int
ti_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
{
- int cs, err;
+ int err;
struct ti_spi_softc *sc;
- uint32_t reg;
+ uint32_t reg, cs;
sc = device_get_softc(dev);
@@ -458,7 +458,7 @@ ti_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
/* Get the proper chip select for this child. */
spibus_get_cs(child, &cs);
- if (cs < 0 || cs > sc->sc_numcs) {
+ if (cs > sc->sc_numcs) {
device_printf(dev, "Invalid chip select %d requested by %s\n",
cs, device_get_nameunit(child));
return (EINVAL);
OpenPOWER on IntegriCloud