diff options
author | Luiz Otavio O Souza <luiz@netgate.com> | 2016-12-30 20:30:00 -0600 |
---|---|---|
committer | Luiz Otavio O Souza <luiz@netgate.com> | 2016-12-30 20:30:00 -0600 |
commit | 1af1408e09373ae856cfef567d79849c7e7e0d25 (patch) | |
tree | 8a4a2bc017c297dea8c977c53f8c1099e26edbe6 /sys/arm/ti | |
parent | ba2be30f109cb2d0c83d41dff268b04f085252b4 (diff) | |
parent | 0591c0c87ec4e420c1a1e9a0c10f761ff4a832c2 (diff) | |
download | FreeBSD-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.c | 9 | ||||
-rw-r--r-- | sys/arm/ti/am335x/am335x_scm.c | 169 | ||||
-rw-r--r-- | sys/arm/ti/am335x/am335x_scm.h | 11 | ||||
-rw-r--r-- | sys/arm/ti/am335x/files.am335x | 1 | ||||
-rw-r--r-- | sys/arm/ti/cpsw/if_cpsw.c | 853 | ||||
-rw-r--r-- | sys/arm/ti/cpsw/if_cpswreg.h | 29 | ||||
-rw-r--r-- | sys/arm/ti/cpsw/if_cpswvar.h | 19 | ||||
-rw-r--r-- | sys/arm/ti/ti_gpio.c | 13 | ||||
-rw-r--r-- | sys/arm/ti/ti_scm.c | 5 | ||||
-rw-r--r-- | sys/arm/ti/ti_spi.c | 6 |
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, ®); + 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, ®); + /* 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, ®); + ti_scm_reg_read_4(SCM_MAC_ID0_HI + sc->unit * 8, ®); 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, ®); + ti_scm_reg_read_4(SCM_MAC_ID0_LO + sc->unit * 8, ®); 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 (ðerswitch_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); |