This diff will properly initialize cpsw if uboot hasn't done so already. It
also adds support for 1000BaseT (RGMII) PHY found on some boards. Based heavily
on a netbsd diff. My BBB is out of comission so please test this and provide
feedback. Thanks.
diff -r 4d62a2f27093 -r 961793eb2b24 src/sys/arch/armv7/omap/if_cpsw.c
--- a/src/sys/arch/armv7/omap/if_cpsw.c Fri Apr 11 16:35:16 2014 -0400
+++ b/src/sys/arch/armv7/omap/if_cpsw.c Sat Apr 12 06:23:19 2014 -0400
@@ -127,7 +127,7 @@
struct arpcom sc_ac;
struct mii_data sc_mii;
-
+ bool sc_phy_has_1000t;
struct cpsw_ring_data *sc_rdp;
volatile u_int sc_txnext;
volatile u_int sc_txhead;
@@ -174,6 +174,10 @@
int cpsw_txintr(void *);
int cpsw_miscintr(void *);
+#define CPSW_MAX_ALE_ENTRIES 1024
+
+static int cpsw_ale_update_addresses(struct cpsw_softc *, int purge);
+
void cpsw_get_mac_addr(struct cpsw_softc *);
struct cfattach cpsw_ca = {
@@ -298,6 +302,18 @@
}
}
+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;
+}
+
void
cpsw_attach(struct device *parent, struct device *self, void *aux)
{
@@ -405,14 +421,29 @@
ifmedia_init(&sc->sc_mii.mii_media, 0, cpsw_mediachange,
cpsw_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, MII_OFFSET_ANY, 0);
if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) {
printf("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) {
+ printf("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);
}
@@ -781,6 +812,8 @@
/* 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);
@@ -795,10 +828,12 @@
cpsw_write_4(sc, CPSW_PORT_P_SA_LO(i+1),
ac->ac_enaddr[4] | (ac->ac_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) | (1<<16));
+ /* 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);
@@ -811,6 +846,9 @@
/* 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);
@@ -1220,3 +1258,191 @@
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)
+{
+ struct arpcom *ac = &sc->sc_ac;
+ uint8_t *mac = ac->ac_enaddr;
+ uint32_t ale_entry[3];
+ int i;
+ 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, &ac->ac_multiaddrs, enm_list) {
+ cpsw_ale_mc_entry_set(sc, ALE_PORT_MASK_ALL, ifma->enm_addrlo);
+ }
+
+ return 0;
+}
diff -r 4d62a2f27093 -r 961793eb2b24 src/sys/arch/armv7/omap/if_cpswreg.h
--- a/src/sys/arch/armv7/omap/if_cpswreg.h Fri Apr 11 16:35:16 2014 -0400
+++ b/src/sys/arch/armv7/omap/if_cpswreg.h Sat Apr 12 06:23:19 2014 -0400
@@ -39,6 +39,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))
@@ -47,6 +48,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)
@@ -112,7 +115,7 @@
#define CPSW_WR_C_RX_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x44)
#define CPSW_WR_C_TX_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x48)
#define CPSW_WR_C_MISC_STAT(p) (CPSW_WR_OFFSET + (0x10 * (p)) + 0x4C)
-#define CPSW_MISC_HOST_PEND 0x0004
+#define CPSW_MISC_HOST_PEND 0x0004
#define CPSW_CPPI_RAM_OFFSET 0x2000
@@ -139,4 +142,82 @@
#define CPSW_INTROFF_TX 2
#define CPSW_INTROFF_MISC 3
+ /* MDIOCONTROL Register Field */
+#define MDIOCTL_IDLE (1<<31)
+#define MDIOCTL_ENABLE (1<<30)
+#define MDIOCTL_HIGHEST_USER_CHANNEL(val) ((0xf & (val)) << 24)
+#define MDIOCTL_PREAMBLE (1<<20)
+#define MDIOCTL_FAULT (1<<19)
+#define MDIOCTL_FAULTENB (1<<18)
+#define MDIOCTL_INTTESTENB (1<<17)
+#define MDIOCTL_CLKDIV(val) (0xff & (val))
+
+/* ALE Control Register Field */
+#define ALECTL_ENABLE_ALE (1<<31)
+#define ALECTL_CLEAR_TABLE (1<<30)
+#define ALECTL_AGE_OUT_NOW (1<<29)
+#define ALECTL_EN_P0_UNI_FLOOD (1<<8)
+#define ALECTL_LEARN_NO_VID (1<<7)
+#define ALECTL_EN_VID0_MODE (1<<6)
+#define ALECTL_ENABLE_OUI_DENY (1<<5)
+#define ALECTL_BYPASS (1<<4)
+#define ALECTL_RATE_LIMIT_TX (1<<3)
+#define ALECTL_VLAN_AWARE (1<<2)
+#define ALECTL_ENABLE_AUTH_MODE (1<<1)
+#define ALECTL_ENABLE_RATE_LIMIT (1<<0)
+
+/* GMII_SEL Register Field */
+#define GMIISEL_RMII2_IO_CLK_EN (1<<7)
+#define GMIISEL_RMII1_IO_CLK_EN (1<<6)
+#define GMIISEL_RGMII2_IDMODE (1<<5)
+#define GMIISEL_RGMII1_IDMODE (1<<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 (1<<24)
+#define SLMACCTL_RX_CSF_EN (1<<23)
+#define SLMACCTL_RX_CEF_EN (1<<22)
+#define SLMACCTL_TX_SHORT_GAP_LIM_EN (1<<21)
+#define SLMACCTL_EXT_EN (1<<18)
+#define SLMACCTL_GIG_FORCE (1<<17)
+#define SLMACCTL_IFCTL_B (1<<16)
+#define SLMACCTL_IFCTL_A (1<<15)
+#define SLMACCTL_CMD_IDLE (1<<11)
+#define SLMACCTL_TX_SHORT_GAP_EN (1<<10)
+#define SLMACCTL_GIG (1<<7)
+#define SLMACCTL_TX_PACE (1<<6)
+#define SLMACCTL_GMII_EN (1<<5)
+#define SLMACCTL_TX_FLOW_EN (1<<4)
+#define SLMACCTL_RX_FLOW_EN (1<<3)
+#define SLMACCTL_MTEST (1<<2)
+#define SLMACCTL_LOOPBACK (1<<1)
+#define SLMACCTL_FULLDUPLEX (1<<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 */