#include #include "steth.h" #define AN_RETRY 6 static int running; static int an_clock; static int an_happy; static int ready; static uint8_t __attribute__ ((aligned (4))) eth_buf[ETH_BUF_LEN]; static uint8_t sa[ETHARP_HWADDR_LEN]; extern uint32_t TxBD; extern uint32_t RxBD; static void eth_smi_transact_wtimeo (void) { int timeout = 200; /* Begin transaction. */ ETH_MACMIIAR |= ETH_MACMIIAR_MB; /* Wait for not busy. */ while (ETH_MACMIIAR & ETH_MACMIIAR_MB) { if (! (timeout--)) return; delay_us (10); } } static void eth_smi_write_wtimeo (uint8_t phy, uint8_t reg, uint16_t data) { /* Write operation MW=1*/ ETH_MACMIIAR = (ETH_MACMIIAR & ETH_MACMIIAR_CR) | /* save clocks */ (phy << ETH_MACMIIAR_PA_SHIFT) | (reg << ETH_MACMIIAR_MR_SHIFT) | ETH_MACMIIAR_MW; ETH_MACMIIDR = data & ETH_MACMIIDR_MD; eth_smi_transact_wtimeo(); } static uint16_t eth_smi_read_wtimeo (uint8_t phy, uint8_t reg) { /* Read operation MW=0*/ ETH_MACMIIAR = (ETH_MACMIIAR & ETH_MACMIIAR_CR) | /* save clocks */ (phy << ETH_MACMIIAR_PA_SHIFT) | (reg << ETH_MACMIIAR_MR_SHIFT); eth_smi_transact_wtimeo(); return (uint16_t) (ETH_MACMIIDR & ETH_MACMIIDR_MD); } static void eth_smi_bit_set_wtimeo (uint8_t phy, uint8_t reg, uint16_t setbits) { uint16_t val = eth_smi_read_wtimeo (phy, reg); eth_smi_write_wtimeo (phy, reg, val | setbits); } static bool phy_link_isup_wtimeo (uint8_t phy) { return eth_smi_read (phy, PHY_REG_BSR) & PHY_REG_BSR_UP; } static enum phy_status phy_link_status_wtimeo (uint8_t phy) { #define PHY_REG_CR1 0x1E return eth_smi_read_wtimeo (phy, PHY_REG_CR1) & 0x07; } static void phy_autoneg_enable_wtimeo (uint8_t phy) { eth_smi_bit_set_wtimeo (phy, PHY_REG_BCR, PHY_REG_BCR_AN | PHY_REG_BCR_ANRST); } void steth_calculate_mac (void) { uint32_t uid[3]; uint8_t *ptr; unsigned i; desig_get_unique_id (uid); ptr = (uint8_t *)uid; for (i = 0; i < ETHARP_HWADDR_LEN; ++i) sa[i] ^= * (ptr++); for (i = 0; i < ETHARP_HWADDR_LEN; ++i) sa[i] ^= * (ptr++); sa[0] &= 0xfe; /*Clear I/G */ sa[0] |= 0x2; /*Set U/L */ printf ("MAC ADDRESS is %02x:%02x:%02x:%02x:%02x:%02x\r\n", sa[0], sa[1], sa[2], sa[3], sa[4], sa[5]); } #define ETH_DEBUG(a) \ do { static uint32_t w; \ uint32_t v=a; \ if (v!=w) {\ printf (" " #a": %08x (%08x +%08x -%08x)\r\n", \ (unsigned) v,(unsigned) (v^w),(unsigned) ((v^w) &v),(unsigned) ((v^w)&w)); \ }\ w=v; \ } while (0) static void eth_debug (void) { printf ("Net:\r\n"); ETH_DEBUG (ETH_MACCR); ETH_DEBUG (ETH_MACFFR); ETH_DEBUG (ETH_MACFCR); ETH_DEBUG (ETH_MACDBGR); ETH_DEBUG (ETH_MACSR); ETH_DEBUG (ETH_DMAOMR); ETH_DEBUG (ETH_DMASR); ETH_DEBUG (ETH_DMAIER); ETH_DEBUG (ETH_DMACHTDR); ETH_DEBUG (ETH_DMACHRDR); ETH_DEBUG (ETH_DMAOMR); ETH_DEBUG (ETH_DMATDLAR); ETH_DEBUG (ETH_DMARDLAR); ETH_DEBUG (ETH_DMABMR); } #define PHY_REGS 0x20 static void phy_show_reg (unsigned i) { static uint16_t last_phy[PHY_REGS]; uint16_t cur = eth_smi_read_wtimeo (PHY, i); if (cur == last_phy[i]) return; printf (" phy:%02x %4x (was %4x +%4x -%4x)\r\n", i, (unsigned) cur, (unsigned) last_phy[i], (unsigned) ((last_phy[i] ^ cur) & cur), (unsigned) ((last_phy[i] ^ cur) & last_phy[i])); last_phy[i] = cur; } static void phy_debug (void) { unsigned i; for (i = 0; i < PHY_REGS; ++i) phy_show_reg (i); } static bool phy_link_an_done (uint8_t phy) { return eth_smi_read_wtimeo (phy, PHY_REG_BSR) & PHY_REG_BSR_ANDONE; } static bool eth_tx_pbuf (struct pbuf *p) { uint8_t *buf, *ptr; uint32_t len; if (ETH_DES0 (TxBD) & ETH_TDES0_OWN) return false; buf = (void *)ETH_DES2 (TxBD); for (len = 0, ptr = buf; p; p = p->next) { memcpy (ptr, p->payload, p->len); ptr += p->len; len += p->len; } ETH_DES1 (TxBD) = len & ETH_TDES1_TBS1; ETH_DES0 (TxBD) |= ETH_TDES0_LS | ETH_TDES0_FS | ETH_TDES0_OWN; TxBD = ETH_DES3 (TxBD); if (ETH_DMASR & ETH_DMASR_TBUS) { ETH_DMASR = ETH_DMASR_TBUS; ETH_DMATPDR = 0; } return true; } static err_t steth_tx (struct netif *netif, struct pbuf *p) { unsigned tries = 3; while (!eth_tx_pbuf (p)) { delay_us (10); if (! (tries--)) return ERR_IF; } return ERR_OK; } static bool eth_rx_ptp (uint8_t *ppkt, uint32_t *len, uint32_t maxlen, uint64_t *tstamp) { bool fs = false; bool ls = false; bool overrun = false; uint32_t l = 0; while (! (ETH_DES0 (RxBD) & ETH_RDES0_OWN) && !ls) { l = (ETH_DES0 (RxBD) & ETH_RDES0_FL) >> ETH_RDES0_FL_SHIFT; fs |= ETH_DES0 (RxBD) & ETH_RDES0_FS; ls |= ETH_DES0 (RxBD) & ETH_RDES0_LS; /* frame buffer overrun ?*/ overrun |= fs && (maxlen < l); if (fs && !overrun) { memcpy (ppkt, (void *)ETH_DES2 (RxBD), l); ppkt += l; *len += l; maxlen -= l; } *tstamp = ptp_to_u64 (ETH_DES6 (RxBD), ETH_DES7 (RxBD)); ETH_DES0 (RxBD) = ETH_RDES0_OWN; RxBD = ETH_DES3 (RxBD); } if (ETH_DMASR & ETH_DMASR_RBUS) { ETH_DMASR = ETH_DMASR_RBUS; ETH_DMARPDR = 0; } return fs && ls && !overrun; } static err_t steth_rx (void) { struct pbuf *p; uint32_t len; p = pbuf_alloc (PBUF_RAW, MTU, PBUF_POOL); if (!p) return ERR_MEM; len = 0; p->ptp_timestamp = 0; //get_ptp(); if (!eth_rx_ptp (p->payload, &len, MTU, &p->ptp_timestamp)) { pbuf_free (p); return ERR_IF; } pbuf_realloc (p, len); return if0.input (p, &if0); } static void steth_nis (void) { if (eth_irq_ack_pending (ETH_DMASR_RS)) while (! (ETH_DES0 (RxBD) & ETH_RDES0_OWN)) steth_rx(); } void eth_isr (void) { if (eth_irq_ack_pending (ETH_DMASR_NIS)) steth_nis(); } err_t steth_lwip_init (struct netif *netif) { LWIP_ASSERT ("netif != NULL", (netif != NULL)); #if LWIP_NETIF_HOSTNAME /* Initialize interface hostname */ netif->hostname = "lwip"; #endif /* LWIP_NETIF_HOSTNAME */ netif->name[0] = 's'; netif->name[1] = 't'; /* We directly use etharp_output() here to save a function call. * You can instead declare your own function an call etharp_output() * from it if you have to do some checks before sending (e.g. if link * is available...) */ netif->output = etharp_output; netif->linkoutput = steth_tx; /* set MAC hardware address length */ netif->hwaddr_len = ETHARP_HWADDR_LEN; /* set MAC hardware address */ memcpy (netif->hwaddr, sa, ETHARP_HWADDR_LEN); /* maximum transfer unit */ netif->mtu = 1500; /* device capabilities */ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_IGMP; return ERR_OK; } static void my_eth_init (uint8_t phy, enum eth_clk clock) { ETH_MACMIIAR = clock; ETH_MACCR = ETH_MACCR_CSTF | ETH_MACCR_FES | ETH_MACCR_DM | ETH_MACCR_APCS | ETH_MACCR_RD; ETH_MACFFR = ETH_MACFFR_RA | ETH_MACFFR_PM | ETH_MACFFR_PAM; ETH_MACHTHR = 0; /* pass all frames */ ETH_MACHTLR = 0; ETH_MACFCR = (0x100 << ETH_MACFCR_PT_SHIFT); ETH_MACVLANTR = 0; ETH_DMAOMR = ETH_DMAOMR_DTCEFD | ETH_DMAOMR_RSF | ETH_DMAOMR_DFRF | ETH_DMAOMR_TSF | ETH_DMAOMR_FEF | ETH_DMAOMR_OSF; ETH_DMABMR = ETH_DMABMR_AAB | ETH_DMABMR_FB | (32 << ETH_DMABMR_RDP_SHIFT) | (32 << ETH_DMABMR_PBL_SHIFT) | ETH_DMABMR_PM_2_1 | ETH_DMABMR_USP; } static void eth_reset (void) { unsigned i; printf ("Eth_reset()\r\n"); rcc_periph_reset_hold (RST_ETHMAC); delay_us (1000); #ifndef SYSCFG_PMC_MII_RMII_SEL #define SYSCFG_PMC_MII_RMII_SEL (1UL << 23) #endif SYSCFG_PMC |= SYSCFG_PMC_MII_RMII_SEL; delay_us (1000); rcc_periph_reset_release (RST_ETHMAC); ETH_DMABMR |= ETH_DMABMR_SR; i = 0; while (ETH_DMABMR & ETH_DMABMR_SR) { delay_us (1000); i++; if (i > 10) { printf ("No 50MHz clock to ethernet MAC\n"); return; } } /*MDC = HCLK / 102 (0b100) => 1.6MHz */ my_eth_init (PHY, ETH_CLK_150_168MHZ); //phy_set_ignore_address(); phy_debug(); eth_set_mac (sa); eth_desc_init (eth_buf, TX_BUFS, RX_BUFS, FRAME_SZ, FRAME_SZ, 1); // eth_enable_checksum_offload(); eth_irq_enable (ETH_DMAIER_NISE); eth_irq_enable (ETH_DMAIER_RIE); eth_irq_enable (ETH_DMASR_TS); } static void eth_start_an (void) { printf ("starting autonegociation\r\n"); eth_smi_write_wtimeo (PHY, PHY_REG_ANTX, 0x1e1); phy_autoneg_enable_wtimeo (PHY); } void steth_init (void) { rcc_periph_reset_hold (RST_ETHMAC); delay_ms (1); #ifndef SYSCFG_PMC_MII_RMII_SEL #define SYSCFG_PMC_MII_RMII_SEL (1UL << 23) #endif SYSCFG_PMC |= SYSCFG_PMC_MII_RMII_SEL; delay_ms (1); rcc_periph_reset_release (RST_ETHMAC); #ifdef NRST MAP_OUTPUT_PP (NRST); #endif MAP_OUTPUT_PP (RXD0); MAP_OUTPUT_PP (RXD1); MAP_OUTPUT_PP (CRS_DV); MAP_AF_100 (REF_CLK, GPIO_AF11); SET (RXD0); SET (RXD1); SET (CRS_DV); #ifdef NRST delay_ms (1); CLEAR (NRST); delay_ms (1); SET (NRST); delay_ms (1); #endif MAP_AF_100 (MDIO, GPIO_AF11); MAP_AF_100 (CRS_DV, GPIO_AF11); #ifdef RXER MAP_AF_100 (RXER, GPIO_AF11); #endif MAP_AF_100 (TXEN, GPIO_AF11); MAP_AF_100 (TXD0, GPIO_AF11); MAP_AF_100 (TXD1, GPIO_AF11); MAP_AF_100 (MDC, GPIO_AF11); MAP_AF_100 (RXD0, GPIO_AF11); MAP_AF_100 (RXD1, GPIO_AF11); /* The switch to RMII has be done with steth under reset, with no clock */ eth_reset(); phy_debug(); eth_debug(); eth_start_an(); nvic_enable_irq (NVIC_ETH_IRQ); /* Fire up timestamping hardware */ ETH_PTPTSCR |= ETH_PTPTSCR_TSE | ETH_PTPTSCR_TSSARFE; ETH_PTPTSCR &= ~ETH_PTPTSCR_TSFCU; ETH_PTPSSIR = 1; ready++; } #if 0 static void eth_stop (void) { ETH_MACCR &= (ETH_MACCR_TE & ETH_MACCR_RE); ETH_DMAOMR &= ~ (ETH_DMAOMR_FTF | ETH_DMAOMR_ST | ETH_DMAOMR_SR); } #endif void steth_slow_tick (void) { //printf("eth slow tick an_clock=%d\r\n",an_clock); phy_debug(); #if 0 eth_debug(); #endif if (!ready) return; an_happy = phy_link_an_done (PHY); if ((!phy_link_isup_wtimeo (PHY) || !an_happy) && running) { printf ("stopping nic\r\n"); //eth_reset(); ETH_MACCR |= ETH_MACCR_RD; running = 0; dhcp_stop (&if0); netif_set_link_down (&if0); } if (!phy_link_isup_wtimeo (PHY) && an_happy) { eth_start_an(); an_clock = 0; } if (!an_happy) { an_clock++; if (an_clock > AN_RETRY) { eth_start_an(); an_clock = 0; } } else an_clock = 0; if (phy_link_isup_wtimeo (PHY) && an_happy && !running) { printf ("autonegociation done\r\n"); printf ("phy link status %x\r\n", phy_link_status (PHY)); switch (phy_link_status_wtimeo (PHY)) { case LINK_HD_10M: TRACE; break; case LINK_HD_100M: TRACE; ETH_MACCR &= ~ETH_MACCR_DM; ETH_MACCR |= ETH_MACCR_ROD; break; default: ; } ETH_MACCR &= ~ETH_MACCR_RD; eth_start(); printf ("starting nic\r\n"); running++; netif_set_link_up (&if0); dhcp_start (&if0); mdns_resp_restart (&if0); } // printf("end slow tick\r\n"); }