diff --git a/src/platform/stm32h7/stm32h7_eth.c b/src/platform/stm32h7/stm32h7_eth.c index 64400a3b..f5df7713 100644 --- a/src/platform/stm32h7/stm32h7_eth.c +++ b/src/platform/stm32h7/stm32h7_eth.c @@ -177,6 +177,9 @@ typedef struct stm32h7_eth { uint8_t se_tx_wrptr; // Where we will write next TX desc uint8_t se_phyaddr; + gpio_t se_phyrst; + ethphy_mode_t se_mode; + int se_flags; const ethphy_class_t *se_ethphy_class; @@ -277,10 +280,173 @@ mii_write(void *arg, uint16_t reg, uint16_t value) } +static error_t stm32h7_eth_output(struct ether_netif *eni, pbuf_t *pb, + pbuf_tx_cb_t *txcb, uint32_t id); +static void stm32h7_eth_irq(void *arg); + +static void +stm32h7_eth_mac_init(stm32h7_eth_t *se) +{ + se->se_eni.eni_addr[0] = 0x02; + se->se_eni.eni_addr[1] = 0x00; + + uint32_t uuidcrc = crc32(0, (void *)0x1ff1e800, 12); + memcpy(&se->se_eni.eni_addr[2], &uuidcrc, 4); + + se->se_tx_wrptr = 0; + se->se_next_rx = 0; + STAILQ_INIT(&se->se_rx_scatter_queue); + se->se_txring = xalloc(sizeof(desc_t) * ETH_TX_RING_SIZE, 0, + MEM_TYPE_DMA | MEM_TYPE_NO_CACHE); + se->se_rxring = xalloc(sizeof(desc_t) * ETH_RX_RING_SIZE, 0, + MEM_TYPE_DMA | MEM_TYPE_NO_CACHE); + + // Soft reset + reg_set_bit(ETH_DMAMR, 0); + + int i = 0; + while(reg_rd(ETH_DMAMR) & 1) { + i++; + if(i == 1000000) { + evlog(LOG_ERR, "Ethernet MAC failed to initialize, no clock?"); + return; + } + } + usleep(10); + +#ifdef ENABLE_NET_PTP + if(se->se_flags & STM32H7_ETH_ENABLE_PTP_TIMESTAMPING) { + + const unsigned int ahb_freq = clk_get_freq(CLK_ETH1MACEN); + + uint32_t ssinc = (1000000000 / ahb_freq) + 1; + + uint64_t numerator = 1000000000ULL; + uint64_t denominator = ahb_freq * ssinc; + se->se_addend = (numerator << 32) / denominator; + + se->se_ppb_scale_mult = se->se_addend / 1000; + se->se_ppb_scale_div = 1000000; + printf("AHB: %d\n", ahb_freq); + printf("ssinc:%d addend:0x%x scale_mult:%d scale_div:%d\n", + ssinc, se->se_addend, se->se_ppb_scale_mult, se->se_ppb_scale_div); + + reg_wr(ETH_MACTSCR, + (1 << 11) | // PTPv2 ethernet + (1 << 10) | // Enable timestamping for PTPv2 + (1 << 9) | // Rollover ns at 999999999 + (1 << 1) | // Fine mode + (1 << 0) | // Enable + 0); + + reg_set_bits(ETH_MACSSIR, 16, 8, ssinc); + + reg_wr(ETH_MACSTSUR, 0); + reg_wr(ETH_MACSTNUR, 0); + reg_set_bit(ETH_MACTSCR, 2); + + reg_wr(ETH_MACTSAR, se->se_addend); + reg_set_bit(ETH_MACTSCR, 5); + while(reg_get_bit(ETH_MACTSCR, 5)); + + se->se_eni.eni_adjust_mac_clock = stm32h7_eth_set_clock; + + reg_wr(ETH_MACPPSCR, 1); + } +#endif + + // DMA config + + reg_wr(ETH_MACPFR, 1 << 31); // Receive ALL + + reg_set_bit(ETH_MTLTXQOMR, 1); + + reg_set_bits(ETH_DMACTXCR, 16, 5, 1); + + reg_set_bits(ETH_DMACRXCR, 16, 5, 1); + + reg_set_bits(ETH_DMACRXCR, 1, 14, PBUF_DATA_SIZE - DMA_BUFFER_PAD); + + memset(se->se_txring, 0, sizeof(desc_t) * ETH_TX_RING_SIZE); + memset(se->se_rxring, 0, sizeof(desc_t) * ETH_RX_RING_SIZE); + + reg_wr(ETH_DMACTXDLAR, (uint32_t)se->se_txring); + reg_wr(ETH_DMACTXRLR, ETH_TX_RING_SIZE - 1); + reg_wr(ETH_DMACTXDTPR, (uint32_t)se->se_txring); + + reg_wr(ETH_DMACRXDLAR, (uint32_t)se->se_rxring); + reg_wr(ETH_DMACRXRLR, ETH_RX_RING_SIZE - 1); + + for(int i = 0; i < ETH_RX_RING_SIZE; i++) { + void *buf = pbuf_data_get(0); + if(buf == NULL) + panic("no pbufs"); + rx_desc_give(se, i, buf); + } + + reg_wr(ETH_DMACIER, (1 << 15) | (1 << 6) | (1 << 2) | (1 << 0)); + + reg_set_bit(ETH_MACCR, 21); // CST: Strip CRC for type packets in RX path + // reg_set_bit(ETH_MACCR, 20); // ACS: Strip CRC in RX path + + reg_set_bit(ETH_MACCR, 0); // Enable RX + reg_set_bit(ETH_MACCR, 1); // Enable TX + + reg_set_bit(ETH_MACCR, 14); // 100Mbit + reg_set_bit(ETH_MACCR, 13); // FullDuplex + + // Start DMA + reg_set_bit(ETH_DMACRXCR, 0); + reg_set_bit(ETH_DMACTXCR, 0); + + se->se_eni.eni_output = stm32h7_eth_output; + + se->se_eni.eni_ni.ni_flags |= + NETIF_F_TX_IPV4_CKSUM_OFFLOAD | + NETIF_F_TX_ICMP_CKSUM_OFFLOAD | + NETIF_F_TX_UDP_CKSUM_OFFLOAD | + NETIF_F_TX_TCP_CKSUM_OFFLOAD; + + ether_netif_init(&se->se_eni, "eth0", &stm32h7_eth_device_class); + + irq_enable_fn_arg(61, IRQ_LEVEL_NET, stm32h7_eth_irq, se); +} + + static void *__attribute__((noreturn)) stm32h7_phy_thread(void *arg) { stm32h7_eth_t *se = arg; + + if(se->se_phyrst != GPIO_UNUSED) { + gpio_conf_output(se->se_phyrst, GPIO_PUSH_PULL, + GPIO_SPEED_LOW, GPIO_PULL_NONE); + + // If we are coming from power on reset we need to wait 50ms + usleep(50000); + gpio_set_output(se->se_phyrst, 0); + // PHY reset pulse must be at least 25 us + usleep(50); + gpio_set_output(se->se_phyrst, 1); + // wait atl least 2ms after PHY reset + usleep(4000); + } + + ethphy_dev_t ed = { + .ed_mode = se->se_mode, + .ed_regio = &stm32h7_eth_mdio, + .ed_arg = se, + }; + error_t err = driver_probe(DRIVER_TYPE_ETHPHY, &ed); + if(err) { + evlog(LOG_ERR, "stm32h7: PHY init failed"); + while(1) + usleep(1000000); + } + se->se_ethphy_class = ed.ed_class; + + stm32h7_eth_mac_init(se); + int current_up = 0; while(1) { mii_read(se, 1); @@ -670,159 +836,16 @@ stm32h7_eth_init(gpio_t phyrst, const uint8_t *gpios, size_t gpio_count, GPIO_SPEED_VERY_HIGH, GPIO_PULL_NONE); } - if(phyrst != GPIO_UNUSED) { - gpio_conf_output(phyrst, GPIO_PUSH_PULL, - GPIO_SPEED_LOW, GPIO_PULL_NONE); - gpio_set_output(phyrst, 0); - udelay(50); - gpio_set_output(phyrst, 1); - udelay(4000); - } - clk_enable(CLK_ETH1MACEN); clk_enable(CLK_ETH1TXEN); clk_enable(CLK_ETH1RXEN); reset_peripheral(CLK_ETH1MACEN); se->se_phyaddr = phy_addr; + se->se_phyrst = phyrst; + se->se_mode = mode; + se->se_flags = flags; - { - ethphy_dev_t ed = { - .ed_mode = mode, - .ed_regio = &stm32h7_eth_mdio, - .ed_arg = se, - }; - error_t err = driver_probe(DRIVER_TYPE_ETHPHY, &ed); - if(err && err != ERR_NOT_FOUND) { - evlog(LOG_ERR, "stm32h7: PHY init failed"); - return; - } - se->se_ethphy_class = ed.ed_class; - } - - se->se_eni.eni_addr[0] = 0x02; - se->se_eni.eni_addr[1] = 0x00; - - uint32_t uuidcrc = crc32(0, (void *)0x1ff1e800, 12); - memcpy(&se->se_eni.eni_addr[2], &uuidcrc, 4); - - se->se_tx_wrptr = 0; - se->se_next_rx = 0; - STAILQ_INIT(&se->se_rx_scatter_queue); - se->se_txring = xalloc(sizeof(desc_t) * ETH_TX_RING_SIZE, 0, - MEM_TYPE_DMA | MEM_TYPE_NO_CACHE); - se->se_rxring = xalloc(sizeof(desc_t) * ETH_RX_RING_SIZE, 0, - MEM_TYPE_DMA | MEM_TYPE_NO_CACHE); - - // Soft reset - reg_set_bit(ETH_DMAMR, 0); - - int i = 0; - while(reg_rd(ETH_DMAMR) & 1) { - i++; - if(i == 1000000) { - evlog(LOG_ERR, "Ethernet MAC failed to initialize, no clock?"); - return; - } - } - udelay(10); - -#ifdef ENABLE_NET_PTP - if(flags & STM32H7_ETH_ENABLE_PTP_TIMESTAMPING) { - - const unsigned int ahb_freq = clk_get_freq(CLK_ETH1MACEN); - - uint32_t ssinc = (1000000000 / ahb_freq) + 1; - - uint64_t numerator = 1000000000ULL; - uint64_t denominator = ahb_freq * ssinc; - se->se_addend = (numerator << 32) / denominator; - - se->se_ppb_scale_mult = se->se_addend / 1000; - se->se_ppb_scale_div = 1000000; - printf("AHB: %d\n", ahb_freq); - printf("ssinc:%d addend:0x%x scale_mult:%d scale_div:%d\n", - ssinc, se->se_addend, se->se_ppb_scale_mult, se->se_ppb_scale_div); - - reg_wr(ETH_MACTSCR, - (1 << 11) | // PTPv2 ethernet - (1 << 10) | // Enable timestamping for PTPv2 - (1 << 9) | // Rollover ns at 999999999 - (1 << 1) | // Fine mode - (1 << 0) | // Enable - 0); - - reg_set_bits(ETH_MACSSIR, 16, 8, ssinc); - - reg_wr(ETH_MACSTSUR, 0); - reg_wr(ETH_MACSTNUR, 0); - reg_set_bit(ETH_MACTSCR, 2); - - reg_wr(ETH_MACTSAR, se->se_addend); - reg_set_bit(ETH_MACTSCR, 5); - while(reg_get_bit(ETH_MACTSCR, 5)); - - se->se_eni.eni_adjust_mac_clock = stm32h7_eth_set_clock; - - reg_wr(ETH_MACPPSCR, 1); - } -#endif - - // DMA config - - reg_wr(ETH_MACPFR, 1 << 31); // Receive ALL - - reg_set_bit(ETH_MTLTXQOMR, 1); - - reg_set_bits(ETH_DMACTXCR, 16, 5, 1); - - reg_set_bits(ETH_DMACRXCR, 16, 5, 1); - - reg_set_bits(ETH_DMACRXCR, 1, 14, PBUF_DATA_SIZE - DMA_BUFFER_PAD); - - memset(se->se_txring, 0, sizeof(desc_t) * ETH_TX_RING_SIZE); - memset(se->se_rxring, 0, sizeof(desc_t) * ETH_RX_RING_SIZE); - - reg_wr(ETH_DMACTXDLAR, (uint32_t)se->se_txring); - reg_wr(ETH_DMACTXRLR, ETH_TX_RING_SIZE - 1); - reg_wr(ETH_DMACTXDTPR, (uint32_t)se->se_txring); - - reg_wr(ETH_DMACRXDLAR, (uint32_t)se->se_rxring); - reg_wr(ETH_DMACRXRLR, ETH_RX_RING_SIZE - 1); - - for(int i = 0; i < ETH_RX_RING_SIZE; i++) { - void *buf = pbuf_data_get(0); - if(buf == NULL) - panic("no pbufs"); - rx_desc_give(se, i, buf); - } - - reg_wr(ETH_DMACIER, (1 << 15) | (1 << 6) | (1 << 2) | (1 << 0)); - - reg_set_bit(ETH_MACCR, 21); // CST: Strip CRC for type packets in RX path - // reg_set_bit(ETH_MACCR, 20); // ACS: Strip CRC in RX path - - reg_set_bit(ETH_MACCR, 0); // Enable RX - reg_set_bit(ETH_MACCR, 1); // Enable TX - - reg_set_bit(ETH_MACCR, 14); // 100Mbit - reg_set_bit(ETH_MACCR, 13); // FullDuplex - - // Start DMA - reg_set_bit(ETH_DMACRXCR, 0); - reg_set_bit(ETH_DMACTXCR, 0); - - se->se_eni.eni_output = stm32h7_eth_output; - - se->se_eni.eni_ni.ni_flags |= - NETIF_F_TX_IPV4_CKSUM_OFFLOAD | - NETIF_F_TX_ICMP_CKSUM_OFFLOAD | - NETIF_F_TX_UDP_CKSUM_OFFLOAD | - NETIF_F_TX_TCP_CKSUM_OFFLOAD; - - ether_netif_init(&se->se_eni, "eth0", &stm32h7_eth_device_class); - - irq_enable_fn_arg(61, IRQ_LEVEL_NET, stm32h7_eth_irq, se); thread_create(stm32h7_phy_thread, se, 512, "phy", 0, 4); }