Module Name:    src
Committed By:   ozaki-r
Date:           Wed Feb 26 03:58:33 UTC 2014

Modified Files:
        src/sys/arch/arm/omap: if_cpsw.c if_cpswreg.h

Log Message:
Make cpsw driver work without uboot support

On some eval boards such as BeagleBone, the cpsw device is initialized
rightly by the uboot of the boards so that the cpsw driver doesn't need
to do some initializations but works fine.

The patch adds initializations to make the driver work solely. It also
adds support for 1000BaseT (RGMII) PHY that is equipped on some boards,
e.g., CKB-3352.

Reviewed by christos@


To generate a diff of this commit:
cvs rdiff -u -r1.4 -r1.5 src/sys/arch/arm/omap/if_cpsw.c
cvs rdiff -u -r1.1 -r1.2 src/sys/arch/arm/omap/if_cpswreg.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/arm/omap/if_cpsw.c
diff -u src/sys/arch/arm/omap/if_cpsw.c:1.4 src/sys/arch/arm/omap/if_cpsw.c:1.5
--- src/sys/arch/arm/omap/if_cpsw.c:1.4	Wed Dec 18 12:53:26 2013
+++ src/sys/arch/arm/omap/if_cpsw.c	Wed Feb 26 03:58:33 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_cpsw.c,v 1.4 2013/12/18 12:53:26 skrll Exp $	*/
+/*	$NetBSD: if_cpsw.c,v 1.5 2014/02/26 03:58:33 ozaki-r Exp $	*/
 
 /*
  * Copyright (c) 2013 Jonathan A. Kollasch
@@ -53,7 +53,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(1, "$NetBSD: if_cpsw.c,v 1.4 2013/12/18 12:53:26 skrll Exp $");
+__KERNEL_RCSID(1, "$NetBSD: if_cpsw.c,v 1.5 2014/02/26 03:58:33 ozaki-r Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -122,6 +122,7 @@ struct cpsw_softc {
 	bus_addr_t sc_rxdescs_pa;
 	struct ethercom sc_ec;
 	struct mii_data sc_mii;
+	bool sc_phy_has_1000t;
 	callout_t sc_tick_ch;
 	void *sc_ih;
 	struct cpsw_ring_data *sc_rdp;
@@ -163,6 +164,11 @@ static int cpsw_rxintr(void *);
 static int cpsw_txintr(void *);
 static int cpsw_miscintr(void *);
 
+/* ALE support */
+#define CPSW_MAX_ALE_ENTRIES	1024
+
+static int cpsw_ale_update_addresses(struct cpsw_softc *, int purge);
+
 CFATTACH_DECL_NEW(cpsw, sizeof(struct cpsw_softc),
     cpsw_match, cpsw_attach, NULL, NULL);
 
@@ -318,6 +324,18 @@ cpsw_match(device_t parent, cfdata_t cf,
 	return 0;
 }
 
+static bool
+cpsw_phy_has_1000t(struct cpsw_softc * const sc)
+{
+	struct ifmedia_entry *ifm;
+
+	TAILQ_FOREACH(ifm, &sc->sc_mii.mii_media.ifm_list, ifm_list) {
+		if (IFM_SUBTYPE(ifm->ifm_media) == IFM_1000_T)
+			return true;
+	}
+	return false;
+}
+
 static void
 cpsw_attach(device_t parent, device_t self, void *aux)
 {
@@ -469,13 +487,28 @@ cpsw_attach(device_t parent, device_t se
 	sc->sc_ec.ec_mii = &sc->sc_mii;
 	ifmedia_init(&sc->sc_mii.mii_media, 0, ether_mediachange,
 	    ether_mediastatus);
+
+	/* Initialize MDIO */
+	cpsw_write_4(sc, MDIOCONTROL, MDIOCTL_ENABLE | MDIOCTL_FAULTENB | MDIOCTL_CLKDIV(0xff));
+	/* Clear ALE */
+	cpsw_write_4(sc, CPSW_ALE_CONTROL, ALECTL_CLEAR_TABLE);
+
 	mii_attach(self, &sc->sc_mii, 0xffffffff, MII_PHY_ANY, 0, 0);
 	if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) {
 		aprint_error_dev(self, "no PHY found!\n");
+		sc->sc_phy_has_1000t = false;
 		ifmedia_add(&sc->sc_mii.mii_media,
 		    IFM_ETHER|IFM_MANUAL, 0, NULL);
 		ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_MANUAL);
 	} else {
+		sc->sc_phy_has_1000t = cpsw_phy_has_1000t(sc);
+		if (sc->sc_phy_has_1000t) {
+			aprint_normal_dev(sc->sc_dev, "1000baseT PHY found. setting RGMII Mode\n");
+			/* Select the Interface RGMII Mode in the Control Module */
+			sitara_cm_reg_write_4(CPSW_GMII_SEL,
+			    GMIISEL_GMII2_SEL(RGMII_MODE) | GMIISEL_GMII1_SEL(RGMII_MODE));
+		}
+
 		ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO);
 	}
 
@@ -791,6 +824,8 @@ cpsw_init(struct ifnet *ifp)
 
 	/* Reset and init Sliver port 1 and 2 */
 	for (i = 0; i < 2; i++) {
+		uint32_t macctl;
+
 		/* Reset */
 		cpsw_write_4(sc, CPSW_SL_SOFT_RESET(i), 1);
 		while(cpsw_read_4(sc, CPSW_SL_SOFT_RESET(i)) & 1);
@@ -805,9 +840,12 @@ cpsw_init(struct ifnet *ifp)
 		cpsw_write_4(sc, CPSW_PORT_P_SA_LO(i+1),
 		    sc->sc_enaddr[4] | (sc->sc_enaddr[5] << 8));
 
-		/* Set MACCONTROL for ports 0,1: FULLDUPLEX(1), GMII_EN(5),
-		   IFCTL_A(15), IFCTL_B(16) FIXME */
-		cpsw_write_4(sc, CPSW_SL_MACCONTROL(i), 1 | (1<<5) | (1<<15));
+		/* Set MACCONTROL for ports 0,1 */
+		macctl = SLMACCTL_FULLDUPLEX | SLMACCTL_GMII_EN |
+		    SLMACCTL_IFCTL_A;
+		if (sc->sc_phy_has_1000t)
+			macctl |= SLMACCTL_GIG;
+		cpsw_write_4(sc, CPSW_SL_MACCONTROL(i), macctl);
 
 		/* Set ALE port to forwarding(3) */
 		cpsw_write_4(sc, CPSW_ALE_PORTCTL(i+1), 3);
@@ -820,6 +858,9 @@ cpsw_init(struct ifnet *ifp)
 	/* Set ALE port to forwarding(3) */
 	cpsw_write_4(sc, CPSW_ALE_PORTCTL(0), 3);
 
+	/* Initialize addrs */
+	cpsw_ale_update_addresses(sc, 1);
+
 	cpsw_write_4(sc, CPSW_SS_PTYPE, 0);
 	cpsw_write_4(sc, CPSW_SS_STAT_PORT_EN, 7);
 
@@ -1242,3 +1283,194 @@ cpsw_miscintr(void *arg)
 
 	return 1;
 }
+
+/*
+ *
+ * ALE support routines.
+ *
+ */
+
+static void
+cpsw_ale_entry_init(uint32_t *ale_entry)
+{
+	ale_entry[0] = ale_entry[1] = ale_entry[2] = 0;
+}
+
+static void
+cpsw_ale_entry_set_mac(uint32_t *ale_entry, const uint8_t *mac)
+{
+	ale_entry[0] = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
+	ale_entry[1] = mac[0] << 8 | mac[1];
+}
+
+static void
+cpsw_ale_entry_set_bcast_mac(uint32_t *ale_entry)
+{
+	ale_entry[0] = 0xffffffff;
+	ale_entry[1] = 0x0000ffff;
+}
+
+static void
+cpsw_ale_entry_set(uint32_t *ale_entry, ale_entry_filed_t field, uint32_t val)
+{
+	/* Entry type[61:60] is addr entry(1), Mcast fwd state[63:62] is fw(3)*/
+	switch (field) {
+	case ALE_ENTRY_TYPE:
+		/* [61:60] */
+		ale_entry[1] |= (val & 0x3) << 28;
+		break;
+	case ALE_MCAST_FWD_STATE:
+		/* [63:62] */
+		ale_entry[1] |= (val & 0x3) << 30;
+		break;
+	case ALE_PORT_MASK:
+		/* [68:66] */
+		ale_entry[2] |= (val & 0x7) << 2;
+		break;
+	case ALE_PORT_NUMBER:
+		/* [67:66] */
+		ale_entry[2] |= (val & 0x3) << 2;
+		break;
+	default:
+		panic("Invalid ALE entry field: %d\n", field);
+	}
+
+	return;
+}
+
+static bool
+cpsw_ale_entry_mac_match(const uint32_t *ale_entry, const uint8_t *mac)
+{
+	return (((ale_entry[1] >> 8) & 0xff) == mac[0]) &&
+	    (((ale_entry[1] >> 0) & 0xff) == mac[1]) &&
+	    (((ale_entry[0] >>24) & 0xff) == mac[2]) &&
+	    (((ale_entry[0] >>16) & 0xff) == mac[3]) &&
+	    (((ale_entry[0] >> 8) & 0xff) == mac[4]) &&
+	    (((ale_entry[0] >> 0) & 0xff) == mac[5]);
+}
+
+static void
+cpsw_ale_set_outgoing_mac(struct cpsw_softc *sc, int port, const uint8_t *mac)
+{
+	cpsw_write_4(sc, CPSW_PORT_P_SA_HI(port),
+	    mac[3] << 24 | mac[2] << 16 | mac[1] << 8 | mac[0]);
+	cpsw_write_4(sc, CPSW_PORT_P_SA_LO(port),
+	    mac[5] << 8 | mac[4]);
+}
+
+static void
+cpsw_ale_read_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry)
+{
+	cpsw_write_4(sc, CPSW_ALE_TBLCTL, idx & 1023);
+	ale_entry[0] = cpsw_read_4(sc, CPSW_ALE_TBLW0);
+	ale_entry[1] = cpsw_read_4(sc, CPSW_ALE_TBLW1);
+	ale_entry[2] = cpsw_read_4(sc, CPSW_ALE_TBLW2);
+}
+
+static void
+cpsw_ale_write_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry)
+{
+	cpsw_write_4(sc, CPSW_ALE_TBLW0, ale_entry[0]);
+	cpsw_write_4(sc, CPSW_ALE_TBLW1, ale_entry[1]);
+	cpsw_write_4(sc, CPSW_ALE_TBLW2, ale_entry[2]);
+	cpsw_write_4(sc, CPSW_ALE_TBLCTL, 1 << 31 | (idx & 1023));
+}
+
+static int
+cpsw_ale_remove_all_mc_entries(struct cpsw_softc *sc)
+{
+	int i;
+	uint32_t ale_entry[3];
+
+	/* First two entries are link address and broadcast. */
+	for (i = 2; i < CPSW_MAX_ALE_ENTRIES; i++) {
+		cpsw_ale_read_entry(sc, i, ale_entry);
+		if (((ale_entry[1] >> 28) & 3) == 1 && /* Address entry */
+		    ((ale_entry[1] >> 8) & 1) == 1) { /* MCast link addr */
+			ale_entry[0] = ale_entry[1] = ale_entry[2] = 0;
+			cpsw_ale_write_entry(sc, i, ale_entry);
+		}
+	}
+	return CPSW_MAX_ALE_ENTRIES;
+}
+
+static int
+cpsw_ale_mc_entry_set(struct cpsw_softc *sc, uint8_t portmask, uint8_t *mac)
+{
+	int free_index = -1, matching_index = -1, i;
+	uint32_t ale_entry[3];
+
+	/* Find a matching entry or a free entry. */
+	for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) {
+		cpsw_ale_read_entry(sc, i, ale_entry);
+
+		/* Entry Type[61:60] is 0 for free entry */ 
+		if (free_index < 0 && ((ale_entry[1] >> 28) & 3) == 0) {
+			free_index = i;
+		}
+
+		if (cpsw_ale_entry_mac_match(ale_entry, mac)) {
+			matching_index = i;
+			break;
+		}
+	}
+
+	if (matching_index < 0) {
+		if (free_index < 0)
+			return ENOMEM;
+		i = free_index;
+	}
+
+	cpsw_ale_entry_init(ale_entry);
+
+	cpsw_ale_entry_set_mac(ale_entry, mac);
+	cpsw_ale_entry_set(ale_entry, ALE_ENTRY_TYPE, ALE_TYPE_ADDRESS);
+	cpsw_ale_entry_set(ale_entry, ALE_MCAST_FWD_STATE, ALE_FWSTATE_FWONLY);
+	cpsw_ale_entry_set(ale_entry, ALE_PORT_MASK, portmask);
+
+	cpsw_ale_write_entry(sc, i, ale_entry);
+
+	return 0;
+}
+
+static int
+cpsw_ale_update_addresses(struct cpsw_softc *sc, int purge)
+{
+	uint8_t *mac = sc->sc_enaddr;
+	uint32_t ale_entry[3];
+	int i;
+	struct ethercom * const ec = &sc->sc_ec;
+	struct ether_multi *ifma;
+
+	cpsw_ale_entry_init(ale_entry);
+	/* Route incoming packets for our MAC address to Port 0 (host). */
+	/* For simplicity, keep this entry at table index 0 in the ALE. */
+	cpsw_ale_entry_set_mac(ale_entry, mac);
+	cpsw_ale_entry_set(ale_entry, ALE_ENTRY_TYPE, ALE_TYPE_ADDRESS);
+	cpsw_ale_entry_set(ale_entry, ALE_PORT_NUMBER, 0);
+	cpsw_ale_write_entry(sc, 0, ale_entry);
+
+	/* Set outgoing MAC Address for Ports 1 and 2. */
+	for (i = 1; i < 3; ++i)
+		cpsw_ale_set_outgoing_mac(sc, i, mac);
+
+	/* Keep the broadcast address at table entry 1. */
+	cpsw_ale_entry_init(ale_entry);
+	cpsw_ale_entry_set_bcast_mac(ale_entry);
+	cpsw_ale_entry_set(ale_entry, ALE_ENTRY_TYPE, ALE_TYPE_ADDRESS);
+	cpsw_ale_entry_set(ale_entry, ALE_MCAST_FWD_STATE, ALE_FWSTATE_FWONLY);
+	cpsw_ale_entry_set(ale_entry, ALE_PORT_MASK, ALE_PORT_MASK_ALL);
+	cpsw_ale_write_entry(sc, 1, ale_entry);
+
+	/* SIOCDELMULTI doesn't specify the particular address
+	   being removed, so we have to remove all and rebuild. */
+	if (purge)
+		cpsw_ale_remove_all_mc_entries(sc);
+
+	/* Set other multicast addrs desired. */
+	LIST_FOREACH(ifma, &ec->ec_multiaddrs, enm_list) {
+		cpsw_ale_mc_entry_set(sc, ALE_PORT_MASK_ALL, ifma->enm_addrlo);
+	}
+
+	return 0;
+}

Index: src/sys/arch/arm/omap/if_cpswreg.h
diff -u src/sys/arch/arm/omap/if_cpswreg.h:1.1 src/sys/arch/arm/omap/if_cpswreg.h:1.2
--- src/sys/arch/arm/omap/if_cpswreg.h:1.1	Thu Jan  3 21:13:26 2013
+++ src/sys/arch/arm/omap/if_cpswreg.h	Wed Feb 26 03:58:33 2014
@@ -34,6 +34,7 @@
 #define CPSW_SS_SOFT_RESET		(CPSW_SS_OFFSET + 0x08)
 #define CPSW_SS_STAT_PORT_EN		(CPSW_SS_OFFSET + 0x0C)
 #define CPSW_SS_PTYPE			(CPSW_SS_OFFSET + 0x10)
+#define CPSW_SS_RGMII_CTL		(CPSW_SS_OFFSET + 0x88)
 
 #define CPSW_PORT_OFFSET		0x0100
 #define CPSW_PORT_P_TX_PRI_MAP(p)	(CPSW_PORT_OFFSET + 0x118 + ((p-1) * 0x100))
@@ -42,6 +43,8 @@
 #define CPSW_PORT_P_SA_LO(p)		(CPSW_PORT_OFFSET + 0x120 + ((p-1) * 0x100))
 #define CPSW_PORT_P_SA_HI(p)		(CPSW_PORT_OFFSET + 0x124 + ((p-1) * 0x100))
 
+#define CPSW_GMII_SEL			0x0650
+
 #define CPSW_CPDMA_OFFSET		0x0800
 #define CPSW_CPDMA_TX_CONTROL		(CPSW_CPDMA_OFFSET + 0x04)
 #define CPSW_CPDMA_TX_TEARDOWN		(CPSW_CPDMA_OFFSET + 0x08)
@@ -133,4 +136,82 @@ struct cpsw_cpdma_bd {
 #define CPSW_INTROFF_TX		2
 #define CPSW_INTROFF_MISC	3
 
+/* MDIOCONTROL Register Field */
+#define MDIOCTL_IDLE		__BIT32(31)
+#define MDIOCTL_ENABLE		__BIT32(30)
+#define MDIOCTL_HIGHEST_USER_CHANNEL(val)	((0xf & (val)) << 24)
+#define MDIOCTL_PREAMBLE	__BIT32(20)
+#define MDIOCTL_FAULT		__BIT32(19)
+#define MDIOCTL_FAULTENB	__BIT32(18)
+#define MDIOCTL_INTTESTENB	__BIT32(17)
+#define MDIOCTL_CLKDIV(val)	(0xff & (val))
+
+/* ALE Control Register Field */
+#define ALECTL_ENABLE_ALE	__BIT32(31)
+#define ALECTL_CLEAR_TABLE	__BIT32(30)
+#define ALECTL_AGE_OUT_NOW	__BIT32(29)
+#define ALECTL_EN_P0_UNI_FLOOD	__BIT32(8)
+#define ALECTL_LEARN_NO_VID	__BIT32(7)
+#define ALECTL_EN_VID0_MODE	__BIT32(6)
+#define ALECTL_ENABLE_OUI_DENY	__BIT32(5)
+#define ALECTL_BYPASS		__BIT32(4)
+#define ALECTL_RATE_LIMIT_TX	__BIT32(3)
+#define ALECTL_VLAN_AWARE	__BIT32(2)
+#define ALECTL_ENABLE_AUTH_MODE	__BIT32(1)
+#define ALECTL_ENABLE_RATE_LIMIT	__BIT32(0)
+
+/* GMII_SEL Register Field */
+#define GMIISEL_RMII2_IO_CLK_EN	__BIT32(7)
+#define GMIISEL_RMII1_IO_CLK_EN	__BIT32(6)
+#define GMIISEL_RGMII2_IDMODE	__BIT32(5)
+#define GMIISEL_RGMII1_IDMODE	__BIT32(4)
+#define GMIISEL_GMII2_SEL(val)	((0x3 & (val)) << 2)
+#define GMIISEL_GMII1_SEL(val)	((0x3 & (val)) << 0)	
+#define GMII_MODE	0
+#define RMII_MODE	1
+#define RGMII_MODE	2
+
+/* Sliver MACCONTROL Register Field */
+#define SLMACCTL_RX_CMF_EN	__BIT32(24)
+#define SLMACCTL_RX_CSF_EN	__BIT32(23)
+#define SLMACCTL_RX_CEF_EN	__BIT32(22)
+#define SLMACCTL_TX_SHORT_GAP_LIM_EN	__BIT32(21)
+#define SLMACCTL_EXT_EN		__BIT32(18)
+#define SLMACCTL_GIG_FORCE	__BIT32(17)
+#define SLMACCTL_IFCTL_B	__BIT32(16)
+#define SLMACCTL_IFCTL_A	__BIT32(15)
+#define SLMACCTL_CMD_IDLE	__BIT32(11)
+#define SLMACCTL_TX_SHORT_GAP_EN	__BIT32(10)
+#define SLMACCTL_GIG		__BIT32(7)
+#define SLMACCTL_TX_PACE	__BIT32(6)
+#define SLMACCTL_GMII_EN	__BIT32(5)
+#define SLMACCTL_TX_FLOW_EN	__BIT32(4)
+#define SLMACCTL_RX_FLOW_EN	__BIT32(3)
+#define SLMACCTL_MTEST		__BIT32(2)
+#define SLMACCTL_LOOPBACK	__BIT32(1)
+#define SLMACCTL_FULLDUPLEX	__BIT32(0)
+
+/* ALE Address Table Entry Field */
+typedef enum {
+	ALE_ENTRY_TYPE,
+	ALE_MCAST_FWD_STATE,
+	ALE_PORT_MASK,
+	ALE_PORT_NUMBER,
+} ale_entry_filed_t;
+
+#define ALE_TYPE_FREE		0
+#define ALE_TYPE_ADDRESS	1
+#define ALE_TYPE_VLAN		2
+#define ALE_TYPE_VLAN_ADDRESS	3
+
+/*
+ * The port state(s) required for the received port on a destination address lookup
+ * in order for the multicast packet to be forwarded to the transmit port(s)
+ */
+#define ALE_FWSTATE_ALL		1	/* Blocking/Forwarding/Learning */
+#define ALE_FWSTATE_NOBLOCK	2	/* Forwarding/Learning */
+#define ALE_FWSTATE_FWONLY	3	/* Forwarding */
+
+#define ALE_PORT_MASK_ALL	7
+
 #endif /*_IF_CPSWREG_H */

Reply via email to