svn commit: r296993 - head/sys/arm/ti/cpsw

Luiz Otavio O Souza loos at FreeBSD.org
Thu Mar 17 19:35:09 UTC 2016


Author: loos
Date: Thu Mar 17 19:35:08 2016
New Revision: 296993
URL: https://svnweb.freebsd.org/changeset/base/296993

Log:
  Add support for dual emac mode.
  
  In dual emac mode, the CPSW subsystem provides two independent ethernets.
  
  This is implemented (as recommended by TI's TRM) with a mixture of switch
  settings (vlans) and specific features of CPSW subsystem.
  
  The driver was splitted to accommodate the shared parts (RX and TX rings
  for example) while it still provides two independent ethernets.
  
  Each of the ethernet ports driver has it's own set of MDIO registers among
  the other private settings.
  
  Previously this driver always operate in promisc mode, now the Switch ALE
  (address table entry) is properly initialized and enabled.
  
  The driver is also tested (and known to work) with both ports operating in
  single port mode (active_slave 0 or 1).
  
  Tested on uBMC (dual emac mode, both ports in single mode, giga and fast
  ethernet) and BBB (single port, fast ethernet).
  
  Sponsored by:	Rubicon Communications (Netgate)

Modified:
  head/sys/arm/ti/cpsw/if_cpsw.c
  head/sys/arm/ti/cpsw/if_cpswreg.h
  head/sys/arm/ti/cpsw/if_cpswvar.h

Modified: head/sys/arm/ti/cpsw/if_cpsw.c
==============================================================================
--- head/sys/arm/ti/cpsw/if_cpsw.c	Thu Mar 17 19:28:15 2016	(r296992)
+++ head/sys/arm/ti/cpsw/if_cpsw.c	Thu Mar 17 19:35:08 2016	(r296993)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2012 Damjan Marion <dmarion at Freebsd.org>
+ * Copyright (c) 2016 Rubicon Communications, LLC (Netgate)
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -93,50 +94,54 @@ __FBSDID("$FreeBSD$");
 
 /* Device probe/attach/detach. */
 static int cpsw_probe(device_t);
-static void cpsw_init_slots(struct cpsw_softc *);
 static int cpsw_attach(device_t);
-static void cpsw_free_slot(struct cpsw_softc *, struct cpsw_slot *);
 static int cpsw_detach(device_t);
+static int cpswp_probe(device_t);
+static int cpswp_attach(device_t);
+static int cpswp_detach(device_t);
+
+static phandle_t cpsw_get_node(device_t, device_t);
 
 /* Device Init/shutdown. */
-static void cpsw_init(void *);
-static void cpsw_init_locked(void *);
 static int cpsw_shutdown(device_t);
-static void cpsw_shutdown_locked(struct cpsw_softc *);
+static void cpswp_init(void *);
+static void cpswp_init_locked(void *);
+static void cpswp_stop_locked(struct cpswp_softc *);
 
 /* Device Suspend/Resume. */
 static int cpsw_suspend(device_t);
 static int cpsw_resume(device_t);
 
 /* Ioctl. */
-static int cpsw_ioctl(struct ifnet *, u_long command, caddr_t data);
+static int cpswp_ioctl(struct ifnet *, u_long command, caddr_t data);
 
-static int cpsw_miibus_readreg(device_t, int phy, int reg);
-static int cpsw_miibus_writereg(device_t, int phy, int reg, int value);
-static void cpsw_miibus_statchg(device_t);
+static int cpswp_miibus_readreg(device_t, int phy, int reg);
+static int cpswp_miibus_writereg(device_t, int phy, int reg, int value);
+static void cpswp_miibus_statchg(device_t);
 
 /* Send/Receive packets. */
 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 cpsw_start(struct ifnet *);
-static void cpsw_tx_enqueue(struct cpsw_softc *);
+static void cpswp_start(struct ifnet *);
+static void cpswp_tx_enqueue(struct cpswp_softc *);
 static int cpsw_tx_dequeue(struct cpsw_softc *);
 
 /* Misc interrupts and watchdog. */
 static void cpsw_intr_rx_thresh(void *);
 static void cpsw_intr_misc(void *);
-static void cpsw_tick(void *);
-static void cpsw_ifmedia_sts(struct ifnet *, struct ifmediareq *);
-static int cpsw_ifmedia_upd(struct ifnet *);
-static void cpsw_tx_watchdog(struct cpsw_softc *);
+static void cpswp_tick(void *);
+static void cpswp_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+static int cpswp_ifmedia_upd(struct ifnet *);
+static void cpsw_tx_watchdog(void *);
 
 /* ALE support */
-static void cpsw_ale_read_entry(struct cpsw_softc *, uint16_t idx, uint32_t *ale_entry);
-static void cpsw_ale_write_entry(struct cpsw_softc *, uint16_t idx, uint32_t *ale_entry);
-static int cpsw_ale_mc_entry_set(struct cpsw_softc *, uint8_t portmap, uint8_t *mac);
-static int cpsw_ale_update_addresses(struct cpsw_softc *, int purge);
+static void cpsw_ale_read_entry(struct cpsw_softc *, uint16_t, uint32_t *);
+static void cpsw_ale_write_entry(struct cpsw_softc *, uint16_t, uint32_t *);
+static int cpsw_ale_mc_entry_set(struct cpsw_softc *, uint8_t, int, uint8_t *);
 static void cpsw_ale_dump_table(struct cpsw_softc *);
+static int cpsw_ale_update_vlan_table(struct cpsw_softc *, int, int, int);
+static int cpswp_ale_update_addresses(struct cpswp_softc *, int);
 
 /* Statistics and sysctls. */
 static void cpsw_add_sysctls(struct cpsw_softc *);
@@ -150,24 +155,7 @@ static int cpsw_stats_sysctl(SYSCTL_HAND
  */
 #define	CPSW_TXFRAGS		8
 
-/*
- * TODO: The CPSW subsystem (CPSW_SS) can drive two independent PHYs
- * as separate Ethernet ports.  To properly support this, we should
- * break this into two separate devices: a CPSW_SS device that owns
- * the interrupts and actually talks to the CPSW hardware, and a
- * separate CPSW Ethernet child device for each Ethernet port.  The RX
- * interrupt, for example, would be part of CPSW_SS; it would receive
- * a packet, note the input port, and then dispatch it to the child
- * device's interface queue.  Similarly for transmit.
- *
- * It's not clear to me whether the device tree should be restructured
- * with a cpsw_ss node and two child nodes.  That would allow specifying
- * MAC addresses for each port, for example, but might be overkill.
- *
- * Unfortunately, I don't have hardware right now that supports two
- * Ethernet ports via CPSW.
- */
-
+/* Shared resources. */
 static device_method_t cpsw_methods[] = {
 	/* Device interface */
 	DEVMETHOD(device_probe,		cpsw_probe),
@@ -176,26 +164,49 @@ static device_method_t cpsw_methods[] = 
 	DEVMETHOD(device_shutdown,	cpsw_shutdown),
 	DEVMETHOD(device_suspend,	cpsw_suspend),
 	DEVMETHOD(device_resume,	cpsw_resume),
-	/* MII interface */
-	DEVMETHOD(miibus_readreg,	cpsw_miibus_readreg),
-	DEVMETHOD(miibus_writereg,	cpsw_miibus_writereg),
-	DEVMETHOD(miibus_statchg,	cpsw_miibus_statchg),
-	{ 0, 0 }
+	/* OFW methods */
+	DEVMETHOD(ofw_bus_get_node,	cpsw_get_node),
+	DEVMETHOD_END
 };
 
 static driver_t cpsw_driver = {
-	"cpsw",
+	"cpswss",
 	cpsw_methods,
 	sizeof(struct cpsw_softc),
 };
 
 static devclass_t cpsw_devclass;
 
-DRIVER_MODULE(cpsw, simplebus, cpsw_driver, cpsw_devclass, 0, 0);
+DRIVER_MODULE(cpswss, simplebus, cpsw_driver, cpsw_devclass, 0, 0);
+
+/* Port/Slave resources. */
+static device_method_t cpswp_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		cpswp_probe),
+	DEVMETHOD(device_attach,	cpswp_attach),
+	DEVMETHOD(device_detach,	cpswp_detach),
+	/* MII interface */
+	DEVMETHOD(miibus_readreg,	cpswp_miibus_readreg),
+	DEVMETHOD(miibus_writereg,	cpswp_miibus_writereg),
+	DEVMETHOD(miibus_statchg,	cpswp_miibus_statchg),
+	DEVMETHOD_END
+};
+
+static driver_t cpswp_driver = {
+	"cpsw",
+	cpswp_methods,
+	sizeof(struct cpswp_softc),
+};
+
+static devclass_t cpswp_devclass;
+
+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);
 
+static uint32_t slave_mdio_addr[] = { 0x4a100200, 0x4a100300 };
+
 static struct resource_spec irq_res_spec[] = {
 	{ SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE },
 	{ SYS_RES_IRQ, 1, RF_ACTIVE | RF_SHAREABLE },
@@ -205,7 +216,7 @@ static struct resource_spec irq_res_spec
 };
 
 /* Number of entries here must match size of stats
- * array in struct cpsw_softc. */
+ * array in struct cpswp_softc. */
 static struct cpsw_stat {
 	int	reg;
 	char *oid;
@@ -250,7 +261,7 @@ static struct cpsw_stat {
  * Basic debug support.
  */
 
-#define	IF_DEBUG(sc)		if (sc->cpsw_if_flags & IFF_DEBUG)
+#define	IF_DEBUG(_sc)		if ((_sc)->if_flags & IFF_DEBUG)
 
 static void
 cpsw_debugf_head(const char *funcname)
@@ -273,13 +284,21 @@ cpsw_debugf(const char *fmt, ...)
 
 }
 
-#define	CPSW_DEBUGF(a) do {						\
-	IF_DEBUG(sc) {							\
+#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)) {						\
+		cpsw_debugf_head(__func__);				\
+		cpsw_debugf a;						\
+	}								\
+} while (0)
+
+
 /*
  * Locking macros
  */
@@ -318,11 +337,20 @@ cpsw_debugf(const char *fmt, ...)
 		CPSW_RX_LOCK_ASSERT(sc);				\
 } while (0)
 
+#define CPSW_PORT_LOCK(_sc) do {					\
+		mtx_assert(&(_sc)->lock, MA_NOTOWNED);			\
+		mtx_lock(&(_sc)->lock);					\
+} while (0)
+
+#define	CPSW_PORT_UNLOCK(_sc)	mtx_unlock(&(_sc)->lock)
+#define	CPSW_PORT_LOCK_ASSERT(_sc)	mtx_assert(&(_sc)->lock, MA_OWNED)
+
 /*
  * Read/Write macros
  */
-#define	cpsw_read_4(sc, reg)		bus_read_4(sc->mem_res, reg)
-#define	cpsw_write_4(sc, reg, val)	bus_write_4(sc->mem_res, reg, val)
+#define	cpsw_read_4(_sc, _reg)		bus_read_4((_sc)->mem_res, (_reg))
+#define	cpsw_write_4(_sc, _reg, _val)					\
+	bus_write_4((_sc)->mem_res, (_reg), (_val))
 
 #define	cpsw_cpdma_bd_offset(i)	(CPSW_CPPI_RAM_OFFSET + ((i)*16))
 
@@ -426,32 +454,12 @@ cpsw_dump_queue(struct cpsw_softc *sc, s
 	printf("\n");
 }
 
-#define	CPSW_DUMP_QUEUE(sc, q) do {				\
+#define CPSW_DUMP_QUEUE(sc, q) do {				\
 	IF_DEBUG(sc) {						\
 		cpsw_dump_queue(sc, q);				\
 	}							\
 } while (0)
 
-/*
- *
- * Device Probe, Attach, Detach.
- *
- */
-
-static int
-cpsw_probe(device_t dev)
-{
-
-	if (!ofw_bus_status_okay(dev))
-		return (ENXIO);
-
-	if (!ofw_bus_is_compatible(dev, "ti,cpsw"))
-		return (ENXIO);
-
-	device_set_desc(dev, "3-port Switch Ethernet Subsystem");
-	return (BUS_PROBE_DEFAULT);
-}
-
 static void
 cpsw_init_slots(struct cpsw_softc *sc)
 {
@@ -468,50 +476,6 @@ cpsw_init_slots(struct cpsw_softc *sc)
 	}
 }
 
-/*
- * bind an interrupt, add the relevant info to sc->interrupts
- */
-static int
-cpsw_attach_interrupt(struct cpsw_softc *sc, struct resource *res, driver_intr_t *handler, const char *description)
-{
-	void **pcookie;
-	int error;
-
-	sc->interrupts[sc->interrupt_count].res = res;
-	sc->interrupts[sc->interrupt_count].description = description;
-	pcookie = &sc->interrupts[sc->interrupt_count].ih_cookie;
-
-	error = bus_setup_intr(sc->dev, res, INTR_TYPE_NET | INTR_MPSAFE,
-	    NULL, *handler, sc, pcookie);
-	if (error)
-		device_printf(sc->dev,
-		    "could not setup %s\n", description);
-	else
-		++sc->interrupt_count;
-	return (error);
-}
-
-/*
- * teardown everything in sc->interrupts.
- */
-static void
-cpsw_detach_interrupts(struct cpsw_softc *sc)
-{
-	int error;
-	int i;
-
-	for (i = 0; i < sizeof(sc->interrupts) / sizeof(sc->interrupts[0]); ++i) {
-		if (!sc->interrupts[i].ih_cookie)
-			continue;
-		error = bus_teardown_intr(sc->dev,
-		    sc->interrupts[i].res, sc->interrupts[i].ih_cookie);
-		if (error)
-			device_printf(sc->dev, "could not release %s\n",
-			    sc->interrupts[i].description);
-		sc->interrupts[i].ih_cookie = NULL;
-	}
-}
-
 static int
 cpsw_add_slots(struct cpsw_softc *sc, struct cpsw_queue *queue, int requested)
 {
@@ -527,7 +491,7 @@ cpsw_add_slots(struct cpsw_softc *sc, st
 		if (slot == NULL)
 			return (0);
 		if (bus_dmamap_create(sc->mbuf_dtag, 0, &slot->dmamap)) {
-			if_printf(sc->ifp, "failed to create dmamap\n");
+			device_printf(sc->dev, "failed to create dmamap\n");
 			return (ENOMEM);
 		}
 		STAILQ_REMOVE_HEAD(&sc->avail, next);
@@ -538,56 +502,294 @@ cpsw_add_slots(struct cpsw_softc *sc, st
 	return (0);
 }
 
-static int
-cpsw_attach(device_t dev)
+static void
+cpsw_free_slot(struct cpsw_softc *sc, struct cpsw_slot *slot)
 {
-	bus_dma_segment_t segs[1];
-	struct cpsw_softc *sc = device_get_softc(dev);
-	struct mii_softc *miisc;
-	struct ifnet *ifp;
-	int phy, nsegs, error;
+	int error;
+
+	if (slot->dmamap) {
+		if (slot->mbuf)
+			bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap);
+		error = bus_dmamap_destroy(sc->mbuf_dtag, slot->dmamap);
+		KASSERT(error == 0, ("Mapping still active"));
+		slot->dmamap = NULL;
+	}
+	if (slot->mbuf) {
+		m_freem(slot->mbuf);
+		slot->mbuf = NULL;
+	}
+}
+
+static void
+cpsw_reset(struct cpsw_softc *sc)
+{
+	int i;
+
+	callout_stop(&sc->watchdog.callout);
+
+	/* Reset RMII/RGMII wrapper. */
+	cpsw_write_4(sc, CPSW_WR_SOFT_RESET, 1);
+	while (cpsw_read_4(sc, CPSW_WR_SOFT_RESET) & 1)
+		;
+
+	/* Disable TX and RX interrupts for all cores. */
+	for (i = 0; i < 3; ++i) {
+		cpsw_write_4(sc, CPSW_WR_C_RX_THRESH_EN(i), 0x00);
+		cpsw_write_4(sc, CPSW_WR_C_TX_EN(i), 0x00);
+		cpsw_write_4(sc, CPSW_WR_C_RX_EN(i), 0x00);
+		cpsw_write_4(sc, CPSW_WR_C_MISC_EN(i), 0x00);
+	}
+
+	/* Reset CPSW subsystem. */
+	cpsw_write_4(sc, CPSW_SS_SOFT_RESET, 1);
+	while (cpsw_read_4(sc, CPSW_SS_SOFT_RESET) & 1)
+		;
+
+	/* Reset Sliver port 1 and 2 */
+	for (i = 0; i < 2; i++) {
+		/* Reset */
+		cpsw_write_4(sc, CPSW_SL_SOFT_RESET(i), 1);
+		while (cpsw_read_4(sc, CPSW_SL_SOFT_RESET(i)) & 1)
+			;
+	}
+
+	/* Reset DMA controller. */
+	cpsw_write_4(sc, CPSW_CPDMA_SOFT_RESET, 1);
+	while (cpsw_read_4(sc, CPSW_CPDMA_SOFT_RESET) & 1)
+		;
+
+	/* Disable TX & RX DMA */
+	cpsw_write_4(sc, CPSW_CPDMA_TX_CONTROL, 0);
+	cpsw_write_4(sc, CPSW_CPDMA_RX_CONTROL, 0);
+
+	/* Clear all queues. */
+	for (i = 0; i < 8; i++) {
+		cpsw_write_4(sc, CPSW_CPDMA_TX_HDP(i), 0);
+		cpsw_write_4(sc, CPSW_CPDMA_RX_HDP(i), 0);
+		cpsw_write_4(sc, CPSW_CPDMA_TX_CP(i), 0);
+		cpsw_write_4(sc, CPSW_CPDMA_RX_CP(i), 0);
+	}
+
+	/* Clear all interrupt Masks */
+	cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_CLEAR, 0xFFFFFFFF);
+	cpsw_write_4(sc, CPSW_CPDMA_TX_INTMASK_CLEAR, 0xFFFFFFFF);
+}
+
+static void
+cpsw_init(struct cpsw_softc *sc)
+{
+	struct cpsw_slot *slot;
 	uint32_t reg;
-	pcell_t phy_id[3];
-	u_long mem_base, mem_size;
-	phandle_t child;
-	int len;
 
-	CPSW_DEBUGF((""));
+	/* Clear ALE */
+	cpsw_write_4(sc, CPSW_ALE_CONTROL, CPSW_ALE_CTL_CLEAR_TBL);
 
-	getbinuptime(&sc->attach_uptime);
-	sc->dev = dev;
-	sc->node = ofw_bus_get_node(dev);
+	/* Enable ALE */
+	reg = CPSW_ALE_CTL_ENABLE;
+	if (sc->dualemac)
+		reg |= CPSW_ALE_CTL_VLAN_AWARE;
+	cpsw_write_4(sc, CPSW_ALE_CONTROL, reg);
 
-	/* TODO: handle multiple slaves */
-	phy = -1;
+	/* Set Host Port Mapping. */
+	cpsw_write_4(sc, CPSW_PORT_P0_CPDMA_TX_PRI_MAP, 0x76543210);
+	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_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. */
+	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_FREEBUFFER(0), 0);
+
+	/* Enable TX & RX DMA */
+	cpsw_write_4(sc, CPSW_CPDMA_TX_CONTROL, 1);
+	cpsw_write_4(sc, CPSW_CPDMA_RX_CONTROL, 1);
+
+	/* 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_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);
+
+	/* Initialze MDIO - ENABLE, PREAMBLE=0, FAULTENB, CLKDIV=0xFF */
+	/* TODO Calculate MDCLK=CLK/(CLKDIV+1) */
+	cpsw_write_4(sc, MDIOCONTROL, MDIOCTL_ENABLE | MDIOCTL_FAULTENB | 0xff);
+
+	/* Select MII in GMII_SEL, Internal Delay mode */
+	//ti_scm_reg_write_4(0x650, 0);
+
+	/* Initialize active queues. */
+	slot = STAILQ_FIRST(&sc->tx.active);
+	if (slot != NULL)
+		cpsw_write_hdp_slot(sc, &sc->tx, slot);
+	slot = STAILQ_FIRST(&sc->rx.active);
+	if (slot != NULL)
+		cpsw_write_hdp_slot(sc, &sc->rx, slot);
+	cpsw_rx_enqueue(sc);
+
+	/* Activate network interface. */
+	sc->rx.running = 1;
+	sc->tx.running = 1;
+	sc->watchdog.timer = 0;
+	callout_init(&sc->watchdog.callout, 0);
+	callout_reset(&sc->watchdog.callout, hz, cpsw_tx_watchdog, sc);
+}
+
+/*
+ *
+ * Device Probe, Attach, Detach.
+ *
+ */
+
+static int
+cpsw_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (!ofw_bus_is_compatible(dev, "ti,cpsw"))
+		return (ENXIO);
+
+	device_set_desc(dev, "3-port Switch Ethernet Subsystem");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+cpsw_intr_attach(struct cpsw_softc *sc)
+{
+
+	/* 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);
+	}
+
+	return (0);
+}
+
+static void
+cpsw_intr_detach(struct cpsw_softc *sc)
+{
+	int i;
+
+	for (i = 0; i < CPSW_INTR_COUNT; i++) {
+		if (sc->ih_cookie[i]) {
+			bus_teardown_intr(sc->dev, sc->irq_res[i],
+			    sc->ih_cookie[i]);
+		}
+	}
+}
+
+static int
+cpsw_get_fdt_data(struct cpsw_softc *sc, int port)
+{
+	char *name;
+	int len, phy, vlan;
+	pcell_t phy_id[3], vlan_id;
+	phandle_t child;
+	unsigned long mdio_child_addr;
 
 	/* Find any slave with phy_id */
+	phy = -1;
+	vlan = -1;
 	for (child = OF_child(sc->node); child != 0; child = OF_peer(child)) {
-		len = OF_getproplen(child, "phy_id");
-		if (len <= 0)
+		if (OF_getprop_alloc(child, "name", 1, (void **)&name) < 0)
 			continue;
-
-		/* Get phy address from fdt */
-		if (OF_getencprop(child, "phy_id", phy_id, len) <= 0)
+		if (sscanf(name, "slave@%x", &mdio_child_addr) != 1) {
+			free(name, M_OFWPROP);
 			continue;
+		}
+		free(name, M_OFWPROP);
+		if (mdio_child_addr != slave_mdio_addr[port])
+			continue;
+
+		len = OF_getproplen(child, "phy_id");
+		if (len / sizeof(pcell_t) == 2) {
+			/* Get phy address from fdt */
+			if (OF_getencprop(child, "phy_id", phy_id, len) > 0)
+				phy = phy_id[1];
+		}
 
-		phy = phy_id[1];
-		/* TODO: get memory window for MDIO */
+		len = OF_getproplen(child, "dual_emac_res_vlan");
+		if (len / sizeof(pcell_t) == 1) {
+			/* Get phy address from fdt */
+			if (OF_getencprop(child, "dual_emac_res_vlan",
+			    &vlan_id, len) > 0) {
+				vlan = vlan_id;
+			}
+		}
 
 		break;
 	}
-
-	if (phy == -1) {
-		device_printf(dev, "failed to get PHY address from FDT\n");
+	if (phy == -1)
 		return (ENXIO);
+	sc->port[port].phy = phy;
+	sc->port[port].vlan = vlan;
+
+	return (0);
+}
+
+static int
+cpsw_attach(device_t dev)
+{
+	bus_dma_segment_t segs[1];
+	int error, i, nsegs;
+	struct cpsw_softc *sc;
+	uint32_t reg;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	sc->node = ofw_bus_get_node(dev);
+	getbinuptime(&sc->attach_uptime);
+
+	if (OF_getencprop(sc->node, "active_slave", &sc->active_slave,
+	    sizeof(sc->active_slave)) <= 0) {
+		sc->active_slave = 0;
 	}
+	if (sc->active_slave > 1)
+		sc->active_slave = 1;
 
-	mem_base = 0;
-	mem_size = 0;
+	if (OF_hasprop(sc->node, "dual_emac"))
+		sc->dualemac = 1;
 
-	if (fdt_regsize(sc->node, &mem_base, &mem_size) != 0) {
-		device_printf(sc->dev, "no regs property in cpsw node\n");
-		return (ENXIO);
+	for (i = 0; i < CPSW_PORTS; i++) {
+		if (!sc->dualemac && i != sc->active_slave)
+			continue;
+		if (cpsw_get_fdt_data(sc, i) != 0) {
+			device_printf(dev,
+			    "failed to get PHY address from FDT\n");
+			return (ENXIO);
+		}
 	}
 
 	/* Initialize mutexes */
@@ -605,9 +807,8 @@ cpsw_attach(device_t dev)
 	}
 
 	sc->mem_rid = 0;
-	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, 
-	    &sc->mem_rid, mem_base, mem_base + CPSW_MEMWINDOW_SIZE -1,
-	    CPSW_MEMWINDOW_SIZE, RF_ACTIVE);
+	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 
+	    &sc->mem_rid, RF_ACTIVE);
 	if (sc->mem_res == NULL) {
 		device_printf(sc->dev, "failed to allocate memory resource\n");
 		cpsw_detach(dev);
@@ -637,14 +838,6 @@ cpsw_attach(device_t dev)
 		return (error);
 	}
 
-	/* Allocate network interface */
-	ifp = sc->ifp = if_alloc(IFT_ETHER);
-	if (ifp == NULL) {
-		device_printf(dev, "if_alloc() failed\n");
-		cpsw_detach(dev);
-		return (ENOMEM);
-	}
-
 	/* Allocate the null mbuf and pre-sync it. */
 	sc->null_mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
 	memset(sc->null_mbuf->m_data, 0, sc->null_mbuf->m_ext.ext_size);
@@ -655,16 +848,6 @@ cpsw_attach(device_t dev)
 	    BUS_DMASYNC_PREWRITE);
 	sc->null_mbuf_paddr = segs[0].ds_addr;
 
-	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
-	ifp->if_softc = sc;
-	ifp->if_flags = IFF_SIMPLEX | IFF_MULTICAST | IFF_BROADCAST;
-	ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_HWCSUM; //FIXME VLAN?
-	ifp->if_capenable = ifp->if_capabilities;
-
-	ifp->if_init = cpsw_init;
-	ifp->if_start = cpsw_start;
-	ifp->if_ioctl = cpsw_ioctl;
-
 	cpsw_init_slots(sc);
 
 	/* Allocate slots to TX and RX queues. */
@@ -674,7 +857,8 @@ cpsw_attach(device_t dev)
 	STAILQ_INIT(&sc->tx.active);
 	// For now:  128 slots to TX, rest to RX.
 	// XXX TODO: start with 32/64 and grow dynamically based on demand.
-	if (cpsw_add_slots(sc, &sc->tx, 128) || cpsw_add_slots(sc, &sc->rx, -1)) {
+	if (cpsw_add_slots(sc, &sc->tx, 128) ||
+	    cpsw_add_slots(sc, &sc->rx, -1)) {
 		device_printf(dev, "failed to allocate dmamaps\n");
 		cpsw_detach(dev);
 		return (ENOMEM);
@@ -682,130 +866,207 @@ cpsw_attach(device_t dev)
 	device_printf(dev, "Initial queue size TX=%d RX=%d\n",
 	    sc->tx.queue_slots, sc->rx.queue_slots);
 
-	ifp->if_snd.ifq_drv_maxlen = sc->tx.queue_slots;
-	IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen);
-	IFQ_SET_READY(&ifp->if_snd);
-
 	sc->tx.hdp_offset = CPSW_CPDMA_TX_HDP(0);
 	sc->rx.hdp_offset = CPSW_CPDMA_RX_HDP(0);
 
-	/* Get high part of MAC address from control module (mac_id0_hi) */
-	/* TODO: Get MAC ID1 as well as MAC ID0. */
-	ti_scm_reg_read_4(0x634, &reg);
-	sc->mac_addr[0] = reg & 0xFF;
-	sc->mac_addr[1] = (reg >>  8) & 0xFF;
-	sc->mac_addr[2] = (reg >> 16) & 0xFF;
-	sc->mac_addr[3] = (reg >> 24) & 0xFF;
-
-	/* Get low part of MAC address from control module (mac_id0_lo) */
-	ti_scm_reg_read_4(0x630, &reg);
-	sc->mac_addr[4] = reg & 0xFF;
-	sc->mac_addr[5] = (reg >>  8) & 0xFF;
-
-	/* Initialze MDIO - ENABLE, PREAMBLE=0, FAULTENB, CLKDIV=0xFF */
-	/* TODO Calculate MDCLK=CLK/(CLKDIV+1) */
-	cpsw_write_4(sc, MDIOCONTROL, 1 << 30 | 1 << 18 | 0xFF);
-
-	/* Clear ALE */
-	cpsw_write_4(sc, CPSW_ALE_CONTROL, 1 << 30);
-
-	/* Attach PHY(s) */
-	error = mii_attach(dev, &sc->miibus, ifp, cpsw_ifmedia_upd,
-	    cpsw_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
-	if (error) {
-		device_printf(dev, "attaching PHYs failed\n");
+	if (cpsw_intr_attach(sc) == -1) {
+		device_printf(dev, "failed to setup interrupts\n");
 		cpsw_detach(dev);
-		return (error);
+		return (ENXIO);
 	}
-	sc->mii = device_get_softc(sc->miibus);
 
-	/* Tell the MAC where to find the PHY so autoneg works */
-	miisc = LIST_FIRST(&sc->mii->mii_phys);
+	/* Reset the controller. */
+	cpsw_reset(sc);
+	cpsw_init(sc);
 
-	/* Select PHY and enable interrupts */
-	cpsw_write_4(sc, MDIOUSERPHYSEL0, 1 << 6 | (miisc->mii_phy & 0x1F));
-	
-	/* Note: We don't use sc->res[3] (TX interrupt) */
-	if (cpsw_attach_interrupt(sc, sc->irq_res[0],
-		cpsw_intr_rx_thresh, "CPSW RX threshold interrupt") ||
-	    cpsw_attach_interrupt(sc, sc->irq_res[1],
-		cpsw_intr_rx, "CPSW RX interrupt") ||
-	    cpsw_attach_interrupt(sc, sc->irq_res[3],
-		cpsw_intr_misc, "CPSW misc interrupt")) {
-		cpsw_detach(dev);
-		return (ENXIO);
+	for (i = 0; i < CPSW_PORTS; i++) {
+		if (!sc->dualemac && i != sc->active_slave)
+			continue;
+		sc->port[i].dev = device_add_child(dev, "cpsw", i);
+		if (sc->port[i].dev == NULL) {
+			cpsw_detach(dev);
+			return (ENXIO);
+		}
 	}
-
-	ether_ifattach(ifp, sc->mac_addr);
-	callout_init(&sc->watchdog.callout, 0);
+	bus_generic_attach(dev);
 
 	return (0);
 }
 
-static void
-cpsw_free_slot(struct cpsw_softc *sc, struct cpsw_slot *slot)
-{
-	int error;
-
-	if (slot->dmamap) {
-		error = bus_dmamap_destroy(sc->mbuf_dtag, slot->dmamap);
-		KASSERT(error == 0, ("Mapping still active"));
-		slot->dmamap = NULL;
-	}
-	if (slot->mbuf) {
-		m_freem(slot->mbuf);
-		slot->mbuf = NULL;
-	}
-}
-
 static int
 cpsw_detach(device_t dev)
 {
-	struct cpsw_softc *sc = device_get_softc(dev);
+	struct cpsw_softc *sc;
 	int error, i;
 
-	CPSW_DEBUGF((""));
+	bus_generic_detach(dev);
+ 	sc = device_get_softc(dev);
+
+	for (i = 0; i < CPSW_PORTS; i++) {
+		if (sc->port[i].dev)
+			device_delete_child(dev, sc->port[i].dev);
+	}
 
-	/* Stop controller and free TX queue */
 	if (device_is_attached(dev)) {
-		ether_ifdetach(sc->ifp);
-		CPSW_GLOBAL_LOCK(sc);
-		cpsw_shutdown_locked(sc);
-		CPSW_GLOBAL_UNLOCK(sc);
+		callout_stop(&sc->watchdog.callout);
 		callout_drain(&sc->watchdog.callout);
 	}
 
-	bus_generic_detach(dev);
-	if (sc->miibus)
-		device_delete_child(dev, sc->miibus);
-
 	/* Stop and release all interrupts */
-	cpsw_detach_interrupts(sc);
+	cpsw_intr_detach(sc);
 
 	/* Free dmamaps and mbufs */
 	for (i = 0; i < sizeof(sc->_slots) / sizeof(sc->_slots[0]); ++i)
 		cpsw_free_slot(sc, &sc->_slots[i]);
+
+	/* Free null mbuf. */
 	if (sc->null_mbuf_dmamap) {
+		bus_dmamap_unload(sc->mbuf_dtag, sc->null_mbuf_dmamap);
 		error = bus_dmamap_destroy(sc->mbuf_dtag, sc->null_mbuf_dmamap);
 		KASSERT(error == 0, ("Mapping still active"));
-	}
-	if (sc->null_mbuf)
 		m_freem(sc->null_mbuf);
+	}
 
 	/* Free DMA tag */
-	error = bus_dma_tag_destroy(sc->mbuf_dtag);
-	KASSERT(error == 0, ("Unable to destroy DMA tag"));
+	if (sc->mbuf_dtag) {
+		error = bus_dma_tag_destroy(sc->mbuf_dtag);
+		KASSERT(error == 0, ("Unable to destroy DMA tag"));
+	}
+
+	/* Free IO memory handler */
+	if (sc->mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res);
+	bus_release_resources(dev, irq_res_spec, sc->irq_res);
+
+	/* Destroy mutexes */
+	mtx_destroy(&sc->rx.lock);
+	mtx_destroy(&sc->tx.lock);
+
+	return (0);
+}
+
+static phandle_t
+cpsw_get_node(device_t bus, device_t dev)
+{
+
+	/* Share controller node with port device. */
+	return (ofw_bus_get_node(bus));
+}
+
+static int
+cpswp_probe(device_t dev)
+{
+
+	if (device_get_unit(dev) > 1) {
+		device_printf(dev, "Only two ports are supported.\n");
+		return (ENXIO);
+	}
+	device_set_desc(dev, "Ethernet Switch Port");
+
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+cpswp_attach(device_t dev)
+{
+	int error;
+	struct ifnet *ifp;
+	struct cpswp_softc *sc;
+	uint32_t reg;
+	uint8_t mac_addr[ETHER_ADDR_LEN];
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	sc->pdev = device_get_parent(dev);
+	sc->swsc = device_get_softc(sc->pdev);
+	sc->unit = device_get_unit(dev);
+	sc->phy = sc->swsc->port[sc->unit].phy;
+	sc->vlan = sc->swsc->port[sc->unit].vlan;
+	if (sc->swsc->dualemac && sc->vlan == -1)
+		sc->vlan = sc->unit + 1;
+
+	if (sc->unit == 0) {
+		sc->physel = MDIOUSERPHYSEL0;
+		sc->phyaccess = MDIOUSERACCESS0;
+	} else {
+		sc->physel = MDIOUSERPHYSEL1;
+		sc->phyaccess = MDIOUSERACCESS1;
+	}
+
+	mtx_init(&sc->lock, device_get_nameunit(dev), "cpsw port lock",
+	    MTX_DEF);
+
+	/* Allocate network interface */
+	ifp = sc->ifp = if_alloc(IFT_ETHER);
+	if (ifp == NULL) {
+		cpswp_detach(dev);
+		return (ENXIO);
+	}
+
+	if_initname(ifp, device_get_name(sc->dev), sc->unit);
+	ifp->if_softc = sc;
+	ifp->if_flags = IFF_SIMPLEX | IFF_MULTICAST | IFF_BROADCAST;
+	ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_HWCSUM; //FIXME VLAN?
+	ifp->if_capenable = ifp->if_capabilities;
+
+	ifp->if_init = cpswp_init;
+	ifp->if_start = cpswp_start;
+	ifp->if_ioctl = cpswp_ioctl;
+
+	ifp->if_snd.ifq_drv_maxlen = sc->swsc->tx.queue_slots;
+	IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen);
+	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);
+	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);
+	mac_addr[4] = reg & 0xFF;
+	mac_addr[5] = (reg >>  8) & 0xFF;
+
+	error = mii_attach(dev, &sc->miibus, ifp, cpswp_ifmedia_upd,
+	    cpswp_ifmedia_sts, BMSR_DEFCAPMASK, sc->phy, MII_OFFSET_ANY, 0);
+	if (error) {
+		device_printf(dev, "attaching PHYs failed\n");
+		cpswp_detach(dev);
+		return (error);
+	}
+	sc->mii = device_get_softc(sc->miibus);
+
+	/* Select PHY and enable interrupts */
+	cpsw_write_4(sc->swsc, sc->physel,
+	    MDIO_PHYSEL_LINKINTENB | (sc->phy & 0x1F));
+
+	ether_ifattach(sc->ifp, mac_addr);
+	callout_init(&sc->mii_callout, 0);
+
+	return (0);
+}
+
+static int
+cpswp_detach(device_t dev)
+{
+	struct cpswp_softc *sc;
 
-	/* Free IO memory handler */
-	bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res);
-	bus_release_resources(dev, irq_res_spec, sc->irq_res);
+	sc = device_get_softc(dev);
+	CPSWP_DEBUGF(sc, (""));
+	if (device_is_attached(dev)) {
+		ether_ifdetach(sc->ifp);
+		CPSW_PORT_LOCK(sc);
+		cpswp_stop_locked(sc);
+		CPSW_PORT_UNLOCK(sc);
+		callout_drain(&sc->mii_callout);
+	}
 
-	if (sc->ifp != NULL)
-		if_free(sc->ifp);
+	bus_generic_detach(dev);
 
-	/* Destroy mutexes */
-	mtx_destroy(&sc->rx.lock);
-	mtx_destroy(&sc->tx.lock);
+	if_free(sc->ifp);
+	mtx_destroy(&sc->lock);
 

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-all mailing list