diff options
author | David Crocker <dcrocker@eschertech.com> | 2020-06-16 13:56:31 +0300 |
---|---|---|
committer | David Crocker <dcrocker@eschertech.com> | 2020-06-16 13:56:31 +0300 |
commit | 11293866589245174b8914b304726bb70ffa6a82 (patch) | |
tree | 2ae669fa2aacc318e31b61d8e634d406172b7acb /src/Hardware/SAME70 | |
parent | da9c9f5f56dec2cbf13c2f0cdc1681d11375fa68 (diff) |
Moved SAME70 GMAC files to Hardware/SAME70
Diffstat (limited to 'src/Hardware/SAME70')
-rw-r--r-- | src/Hardware/SAME70/same70_gmac.cpp | 797 | ||||
-rw-r--r-- | src/Hardware/SAME70/same70_gmac.h | 83 |
2 files changed, 880 insertions, 0 deletions
diff --git a/src/Hardware/SAME70/same70_gmac.cpp b/src/Hardware/SAME70/same70_gmac.cpp new file mode 100644 index 00000000..33212834 --- /dev/null +++ b/src/Hardware/SAME70/same70_gmac.cpp @@ -0,0 +1,797 @@ +/** + * \file + * + * \brief GMAC (Gigabit MAC) driver for lwIP. + * + * Copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ + +#include "same70_gmac.h" + +#if defined(SAME70) && SAME70 +#include "pmc/pmc.h" +#endif + +#include "conf_eth.h" +#include <cstring> + +extern "C" { +#include <Hardware/ksz8081rna/ethernet_phy.h> +#include "lwip/opt.h" +#include "lwip/sys.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "netif/etharp.h" +} + +#define __nocache __attribute__((section(".ram_nocache"))) + +extern void delay(uint32_t ms); + +// We can't #include RepRapFirmware.h here because that leads to a duplicate definition of ERR_TIMEOUT +#include <RTOSIface/RTOSIface.h> +#include <TaskPriorities.h> + +extern Mutex lwipMutex; + +constexpr size_t EthernetTaskStackWords = 250; +static Task<EthernetTaskStackWords> ethernetTask; + +// Error counters +unsigned int rxErrorCount; +unsigned int rxBuffersNotFullyPopulatedCount; +unsigned int txErrorCount; +unsigned int txBufferNotFreeCount; +unsigned int txBufferTooShortCount; + +/** Network interface identifier. */ +#define IFNAME0 'e' +#define IFNAME1 'n' + +/** Maximum transfer unit. */ +#define NET_MTU 1500 + +/** Network link speed. */ +#define NET_LINK_SPEED 100000000 + +/* Interrupt priorities. (lowest value = highest priority) */ +/* ISRs using FreeRTOS *FromISR APIs must have priorities below or equal to */ +/* configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY. */ + +/** The GMAC interrupts to enable */ +#define GMAC_INT_GROUP (GMAC_ISR_RCOMP | GMAC_ISR_ROVR | GMAC_ISR_HRESP | GMAC_ISR_TCOMP | GMAC_ISR_TUR | GMAC_ISR_TFC) + +/** The GMAC TX errors to handle */ +#define GMAC_TX_ERRORS (GMAC_TSR_TFC | GMAC_TSR_HRESP) + +/** The GMAC RX errors to handle */ +#define GMAC_RX_ERRORS (GMAC_RSR_RXOVR | GMAC_RSR_HNO) + +/** TX descriptor lists */ +alignas(8) static gmac_tx_descriptor_t gs_tx_desc_null; +/** RX descriptors lists */ +alignas(8) static gmac_rx_descriptor_t gs_rx_desc_null; +/** + * GMAC driver structure. + */ +struct gmac_device { + /** + * Pointer to allocated TX buffer. + * Section 3.6 of AMBA 2.0 spec states that burst should not cross + * 1K Boundaries. + * Receive buffer manager writes are burst of 2 words => 3 lsb bits + * of the address shall be set to 0. + */ + /** Pointer to Rx descriptor list (must be 8-byte aligned). */ + volatile gmac_rx_descriptor_t rx_desc[GMAC_RX_BUFFERS]; + /** Pointer to Tx descriptor list (must be 8-byte aligned). */ + volatile gmac_tx_descriptor_t tx_desc[GMAC_TX_BUFFERS]; + /** RX pbuf pointer list. */ + struct pbuf *rx_pbuf[GMAC_RX_BUFFERS]; + /** TX buffers. */ + uint8_t tx_buf[GMAC_TX_BUFFERS][GMAC_TX_UNITSIZE]; + + /** RX index for current processing TD. */ + uint32_t us_rx_idx; + /** Circular buffer head pointer by upper layer (buffer to be sent). */ + uint32_t us_tx_idx; + + /** Reference to lwIP netif structure. */ + struct netif *netif; +}; + +/** + * GMAC driver instance. + */ +__nocache __aligned(8) static struct gmac_device gs_gmac_dev; + +/** + * MAC address to use. + */ +static uint8_t gs_uc_mac_address[] = +{ + ETHERNET_CONF_ETHADDR0, + ETHERNET_CONF_ETHADDR1, + ETHERNET_CONF_ETHADDR2, + ETHERNET_CONF_ETHADDR3, + ETHERNET_CONF_ETHADDR4, + ETHERNET_CONF_ETHADDR5 +}; + +static bool rxPbufsFullyPopulated = false; + +#if LWIP_STATS +/** Used to compute lwIP bandwidth. */ +uint32_t lwip_tx_count = 0; +uint32_t lwip_rx_count = 0; +uint32_t lwip_tx_rate = 0; +uint32_t lwip_rx_rate = 0; +#endif + +/** + * \brief GMAC interrupt handler. + */ +extern "C" void GMAC_Handler() noexcept +{ + /* Get interrupt status. */ + const uint32_t ul_isr = gmac_get_interrupt_status(GMAC); + + /* RX interrupts. */ + if (ul_isr & GMAC_INT_GROUP) + { + ethernetTask.GiveFromISR(); + } +} + +/** + * \brief Populate the RX descriptor ring buffers with pbufs. + * + * \note Make sure that the p->payload pointer is 32 bits aligned. + * (since the lsb are used as status bits by GMAC). + * + * \param p_gmac_dev Pointer to driver data structure. + * \param startAt The index to start populating from + */ +static void gmac_rx_populate_queue(struct gmac_device *p_gmac_dev, uint32_t startAt) noexcept +{ + uint32_t ul_index = startAt; + + /* Set up the RX descriptors. */ + do + { + if (p_gmac_dev->rx_pbuf[ul_index] == nullptr) + { + /* Allocate a new pbuf with the maximum size. */ + pbuf * const p = pbuf_alloc(PBUF_RAW, (u16_t) GMAC_FRAME_LENTGH_MAX, PBUF_POOL); + if (p == nullptr) + { + LWIP_DEBUGF(NETIF_DEBUG, ("gmac_rx_populate_queue: pbuf allocation failure\n")); + rxPbufsFullyPopulated = false; + ++rxBuffersNotFullyPopulatedCount; + return; + } + + /* Make sure lwIP is well configured so one pbuf can contain the maximum packet size. */ + LWIP_ASSERT("gmac_rx_populate_queue: pbuf size too small!", pbuf_clen(p) <= 1); + + /* Make sure that the payload buffer is properly aligned. */ + LWIP_ASSERT("gmac_rx_populate_queue: unaligned p->payload buffer address", + (((uint32_t)p->payload & 0xFFFFFFFC) == (uint32_t)p->payload)); + + // dc42 do this first to avoid a race condition with DMA, because writing addr.val transfers ownership back to the GMAC, so it should be the last thing we do + /* Reset status value. */ + p_gmac_dev->rx_desc[ul_index].status.val = 0; + + /* Save pbuf pointer to be sent to lwIP upper layer. */ + p_gmac_dev->rx_pbuf[ul_index] = p; + + if (ul_index == GMAC_RX_BUFFERS - 1) + { + p_gmac_dev->rx_desc[ul_index].addr.val = (u32_t) p->payload | GMAC_RXD_WRAP; + } + else + { + p_gmac_dev->rx_desc[ul_index].addr.val = (u32_t) p->payload; + } + + LWIP_DEBUGF(NETIF_DEBUG, + ("gmac_rx_populate_queue: new pbuf allocated: %p [idx=%u]\n", + p, ul_index)); + } + + ++ul_index; + if (ul_index == GMAC_RX_BUFFERS) + { + ul_index = 0; + } + } while (ul_index != startAt); + + rxPbufsFullyPopulated = true; +} + +/** + * \brief Set up the RX descriptor ring buffers. + * + * This function sets up the descriptor list used for receive packets. + * + * \param ps_gmac_dev Pointer to driver data structure. + */ +static void gmac_rx_init(struct gmac_device *ps_gmac_dev) noexcept +{ + uint32_t ul_index = 0; + + /* Init RX index. */ + ps_gmac_dev->us_rx_idx = 0; + + /* Set up the RX descriptors. */ + for (ul_index = 0; ul_index < GMAC_RX_BUFFERS; ul_index++) + { + ps_gmac_dev->rx_pbuf[ul_index] = nullptr; + ps_gmac_dev->rx_desc[ul_index].addr.val = GMAC_RXD_OWNERSHIP; // mark it as not free for hardware to use, until we have allocated the pbuf + ps_gmac_dev->rx_desc[ul_index].status.val = 0; + } + ps_gmac_dev->rx_desc[ul_index - 1].addr.val |= GMAC_RXD_WRAP; + + /* Build RX buffer and descriptors. */ + gmac_rx_populate_queue(ps_gmac_dev, 0); + + /* Set receive buffer queue base address pointer. */ + gmac_set_rx_queue(GMAC, (uint32_t) &ps_gmac_dev->rx_desc[0]); +} + +/** + * \brief Set up the TX descriptor ring buffers. + * + * This function sets up the descriptor list used for receive packets. + * + * \param ps_gmac_dev Pointer to driver data structure. + */ +static void gmac_tx_init(struct gmac_device *ps_gmac_dev) noexcept +{ + uint32_t ul_index; + + /* Init TX index pointer. */ + ps_gmac_dev->us_tx_idx = 0; + + /* Set up the TX descriptors. */ + for (ul_index = 0; ul_index < GMAC_TX_BUFFERS; ul_index++) + { + ps_gmac_dev->tx_desc[ul_index].addr = reinterpret_cast<uint32_t>(&ps_gmac_dev->tx_buf[ul_index][0]); + ps_gmac_dev->tx_desc[ul_index].status.val = GMAC_TXD_USED | GMAC_TXD_LAST; + } + ps_gmac_dev->tx_desc[ul_index - 1].status.val |= GMAC_TXD_WRAP; + + /* Set receive buffer queue base address pointer. */ + gmac_set_tx_queue(GMAC, (uint32_t) &ps_gmac_dev->tx_desc[0]); +} + +/** + * \brief Initialize GMAC and PHY. + * + * \note Called from ethernetif_init(). + * + * \param netif the lwIP network interface structure for this ethernetif. + */ +static void gmac_low_level_init(struct netif *netif) noexcept +{ +#if 0 // chrishamm + volatile uint32_t ul_delay; +#endif + + /* Set MAC hardware address length. */ + netif->hwaddr_len = sizeof(gs_uc_mac_address); + /* Set MAC hardware address. */ + netif->hwaddr[0] = gs_uc_mac_address[0]; + netif->hwaddr[1] = gs_uc_mac_address[1]; + netif->hwaddr[2] = gs_uc_mac_address[2]; + netif->hwaddr[3] = gs_uc_mac_address[3]; + netif->hwaddr[4] = gs_uc_mac_address[4]; + netif->hwaddr[5] = gs_uc_mac_address[5]; + + /* Set maximum transfer unit. */ + netif->mtu = NET_MTU; + + /* Device capabilities. */ + netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP +#if LWIP_IGMP // chrishamm + | NETIF_FLAG_IGMP +#endif + ; + +#if 0 // chrishamm: Just like with the EMAC on the Duet, we initialise the GMAC step-by-step to avoid blocking + /* Wait for PHY to be ready (CAT811: Max400ms). */ + ul_delay = sysclk_get_cpu_hz() / 1000 / 3 * 400; + while (ul_delay--) { + } +#endif + + /* Init MAC PHY driver. */ + if (ethernet_phy_init(GMAC, BOARD_GMAC_PHY_ADDR, SystemCoreClock) != GMAC_OK) { + LWIP_DEBUGF(NETIF_DEBUG, ("gmac_low_level_init: PHY init ERROR!\n")); + return; + } + +#if 0 // chrishamm: See ethernetif_establish_link() + /* Auto Negotiate, work in RMII mode. */ + if (ethernet_phy_auto_negotiate(GMAC, BOARD_GMAC_PHY_ADDR) != GMAC_OK) { + LWIP_DEBUGF(NETIF_DEBUG, ("gmac_low_level_init: auto negotiate ERROR!\n")); + return; + } + + /* Establish ethernet link. */ + while (ethernet_phy_set_link(GMAC, BOARD_GMAC_PHY_ADDR, 1) != GMAC_OK) { + LWIP_DEBUGF(NETIF_DEBUG, ("gmac_low_level_init: set link ERROR!\n")); + return; + } + + /* Set link up*/ + netif->flags |= NETIF_FLAG_LINK_UP; +#endif +} + +/** + * \brief This function should do the actual transmission of the packet. The + * packet is contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + * \param netif the lwIP network interface structure for this ethernetif. + * \param p the MAC packet to send (e.g. IP packet including MAC addresses and type). + * + * \return ERR_OK if the packet could be sent. + * an err_t value if the packet couldn't be sent. + */ +static err_t gmac_low_level_output(netif *p_netif, struct pbuf *p) noexcept +{ + gmac_device *const ps_gmac_dev = static_cast<gmac_device *>(p_netif->state); + + /* Handle GMAC underrun or AHB errors. */ + if (gmac_get_tx_status(GMAC) & GMAC_TX_ERRORS) + { + ++txErrorCount; + LWIP_DEBUGF(NETIF_DEBUG, ("gmac_low_level_output: GMAC ERROR, reinit TX...\n")); + + gmac_enable_transmit(GMAC, false); + + LINK_STATS_INC(link.err); + LINK_STATS_INC(link.drop); + + /* Reinit TX descriptors. */ + gmac_tx_init(ps_gmac_dev); + + /* Clear error status. */ + gmac_clear_tx_status(GMAC, GMAC_TX_ERRORS); + + gmac_enable_transmit(GMAC, true); + } + + while ((ps_gmac_dev->tx_desc[ps_gmac_dev->us_tx_idx].status.val & GMAC_TXD_USED) == 0) + { + ++txBufferNotFreeCount; + delay(1); + } + + // Copy pbuf chain into TX buffer + { + uint8_t *buffer = reinterpret_cast<uint8_t*>(ps_gmac_dev->tx_desc[ps_gmac_dev->us_tx_idx].addr); + size_t totalLength = 0; + for (const pbuf *q = p; q != NULL; q = q->next) + { + totalLength += q->len; + if (totalLength > GMAC_TX_UNITSIZE) + { + ++txBufferTooShortCount; + return ERR_BUF; + } + memcpy(buffer, q->payload, q->len); + buffer += q->len; + } + } + + // Set length and mark the buffer to be sent by GMAC + uint32_t txStat = p->tot_len | GMAC_TXD_LAST; + if (ps_gmac_dev->us_tx_idx == GMAC_TX_BUFFERS - 1) + { + txStat |= GMAC_TXD_WRAP; + } + ps_gmac_dev->tx_desc[ps_gmac_dev->us_tx_idx].status.val = txStat; + + LWIP_DEBUGF(NETIF_DEBUG, + ("gmac_low_level_output: DMA buffer sent, size=%d [idx=%u]\n", + p->tot_len, ps_gmac_dev->us_tx_idx)); + + ps_gmac_dev->us_tx_idx = (ps_gmac_dev->us_tx_idx + 1) % GMAC_TX_BUFFERS; + + /* Now start to transmission. */ + gmac_start_transmission(GMAC); + +#if LWIP_STATS + lwip_tx_count += p->tot_len; +#endif + LINK_STATS_INC(link.xmit); + + return ERR_OK; +} + +/** + * \brief Use pre-allocated pbuf as DMA source and return the incoming packet. + * + * \param netif the lwIP network interface structure for this ethernetif. + * + * \return a pbuf filled with the received packet (including MAC header). + * 0 on memory error. + */ +static pbuf *gmac_low_level_input(struct netif *netif) noexcept +{ + gmac_device *ps_gmac_dev = static_cast<gmac_device *>(netif->state); + + if (gmac_get_rx_status(GMAC) & GMAC_RX_ERRORS) + { + // Handle GMAC overrun or AHB errors + ++rxErrorCount; + gmac_enable_receive(GMAC, false); + + LINK_STATS_INC(link.err); + LINK_STATS_INC(link.drop); + + /* Free all RX pbufs. */ + for (uint32_t ul_index = 0; ul_index < GMAC_RX_BUFFERS; ul_index++) + { + if (ps_gmac_dev->rx_pbuf[ul_index] != 0) + { + pbuf_free(ps_gmac_dev->rx_pbuf[ul_index]); + ps_gmac_dev->rx_pbuf[ul_index] = nullptr; + } + } + + /* Reinit RX descriptors. */ + gmac_rx_init(ps_gmac_dev); + + /* Clear error status. */ + gmac_clear_rx_status(GMAC, GMAC_RX_ERRORS); + + gmac_enable_receive(GMAC, true); + return nullptr; + } + + volatile gmac_rx_descriptor_t * const p_rx = &ps_gmac_dev->rx_desc[ps_gmac_dev->us_rx_idx]; + pbuf * const p = ((p_rx->addr.val & GMAC_RXD_OWNERSHIP) == GMAC_RXD_OWNERSHIP) + ? ps_gmac_dev->rx_pbuf[ps_gmac_dev->us_rx_idx] + : nullptr; + + /* Check if a packet has been received and processed by GMAC. */ + if (p != nullptr) + { + /* Packet is a SOF since packet size is set to maximum. */ + const uint32_t length = p_rx->status.val & GMAC_RXD_LEN_MASK; + + /* Fetch pre-allocated pbuf. */ + p->len = length; + + /* Remove this pbuf from its descriptor. */ + ps_gmac_dev->rx_pbuf[ps_gmac_dev->us_rx_idx] = nullptr; + rxPbufsFullyPopulated = false; + + LWIP_DEBUGF(NETIF_DEBUG, + ("gmac_low_level_input: DMA buffer %p received, size=%u [idx=%u]\n", + p, length, ps_gmac_dev->us_rx_idx)); + + /* Set pbuf total packet size. */ + p->tot_len = length; + LINK_STATS_INC(link.recv); + + ps_gmac_dev->us_rx_idx = (ps_gmac_dev->us_rx_idx + 1) % GMAC_RX_BUFFERS; + +#if LWIP_STATS + lwip_rx_count += length; +#endif + } + + /* Fill empty descriptors with new pbufs. */ + if (!rxPbufsFullyPopulated) + { + gmac_rx_populate_queue(ps_gmac_dev, ps_gmac_dev->us_rx_idx); + } + + return p; +} + +/** + * \brief GMAC task function. This function waits for the notification + * semaphore from the interrupt, processes the incoming packet and then + * passes it to the lwIP stack. + * + * \param pvParameters A pointer to the gmac_device instance. + */ +extern "C" [[noreturn]] void gmac_task(void *pvParameters) noexcept +{ + gmac_device * const ps_gmac_dev = static_cast<gmac_device*>(pvParameters); + netif * const p_netif = ps_gmac_dev->netif; + + while (1) + { + // Process the incoming packets + { + MutexLocker lock(lwipMutex); + while (ethernetif_input(p_netif)) { } + } + + // Wait for the RX notification from the ISR + TaskBase::Take((rxPbufsFullyPopulated) ? 1000 : 20); + } +} + +/** + * \brief This function should be called when a packet is ready to be + * read from the interface. It uses the function gmac_low_level_input() + * that handles the actual reception of bytes from the network interface. + * Then the type of the received packet is determined and the appropriate + * input function is called. + * + * \param netif the lwIP network interface structure for this ethernetif. + */ +bool ethernetif_input(struct netif *netif) noexcept +{ + struct eth_hdr *ethhdr; + struct pbuf *p; + + /* Move received packet into a new pbuf. */ + p = gmac_low_level_input(netif); + if (p == nullptr) + { + return false; + } + + /* Points to packet payload, which starts with an Ethernet header. */ + ethhdr = static_cast<struct eth_hdr*>(p->payload); + + switch (htons(ethhdr->type)) { + case ETHTYPE_IP: + case ETHTYPE_ARP: +#if defined(PPPOE_SUPPORT) && PPPOE_SUPPORT + case ETHTYPE_PPPOEDISC: + case ETHTYPE_PPPOE: +#endif /* PPPOE_SUPPORT */ + /* Send packet to lwIP for processing. */ + if (netif->input(p, netif) != ERR_OK) { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + /* Free buffer. */ + pbuf_free(p); + } + break; + + default: + /* Free buffer. */ + pbuf_free(p); + break; + } + return true; +} + +/** + * \brief Should be called at the beginning of the program to set up the + * network interface. It calls the function gmac_low_level_init() to do the + * actual setup of the hardware. + * + * \param netif the lwIP network interface structure for this ethernetif. + * + * \return ERR_OK if the loopif is initialized. + * ERR_MEM if private data couldn't be allocated. + * any other err_t on error. + */ +err_t ethernetif_init(struct netif *netif) noexcept +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + + gs_gmac_dev.netif = netif; + +#if LWIP_NETIF_HOSTNAME && 0 // chrishamm: RRF sets the hostname explicitly + /* Initialize interface hostname. */ + netif->hostname = "gmacdev"; +#endif /* LWIP_NETIF_HOSTNAME */ + + /* + * Initialize the snmp variables and counters inside the struct netif. + * The last argument should be replaced with your link speed, in units + * of bits per second. + */ +#if defined(LWIP_SNMP) && LWIP_SNMP + NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, NET_LINK_SPEED); +#endif /* LWIP_SNMP */ + + netif->state = &gs_gmac_dev; + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; + + /* 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 = gmac_low_level_output; + + /* Initialize the hardware */ + gmac_low_level_init(netif); + + ethernetTask.Create(gmac_task, "ETHERNET", &gs_gmac_dev, TaskPriority::EthernetPriority); + return ERR_OK; +} + +void ethernetif_hardware_init() noexcept +{ + /* Enable GMAC clock. */ + pmc_enable_periph_clk(ID_GMAC); + + /* Disable TX & RX and more. */ + gmac_network_control(GMAC, 0); + gmac_disable_interrupt(GMAC, ~0u); + + gmac_clear_statistics(GMAC); + + /* Clear all status bits in the receive status register. */ + gmac_clear_rx_status(GMAC, GMAC_RSR_BNA | GMAC_RSR_REC | GMAC_RSR_RXOVR + | GMAC_RSR_HNO); + + /* Clear all status bits in the transmit status register. */ + gmac_clear_tx_status(GMAC, GMAC_TSR_UBR | GMAC_TSR_COL | GMAC_TSR_RLE + | GMAC_TSR_TXGO | GMAC_TSR_TFC | GMAC_TSR_TXCOMP + | GMAC_TSR_HRESP); + + /* Clear interrupts. */ + gmac_get_interrupt_status(GMAC); + + /* Enable the copy of data into the buffers + ignore broadcasts, and not copy FCS. */ + gmac_enable_copy_all(GMAC, false); + gmac_disable_broadcast(GMAC, false); + + /* Set RX buffer size to 1536. */ + gmac_set_rx_bufsize(GMAC, 0x18); + + /* Clear interrupts */ + gmac_get_priority_interrupt_status(GMAC, GMAC_QUE_2); + gmac_get_priority_interrupt_status(GMAC, GMAC_QUE_1); +#if (SAMV71B || SAME70B) + gmac_get_priority_interrupt_status(GMAC, GMAC_QUE_3); + gmac_get_priority_interrupt_status(GMAC, GMAC_QUE_4); + gmac_get_priority_interrupt_status(GMAC, GMAC_QUE_5); +#endif + + /* Set Tx Priority */ + gs_tx_desc_null.addr = (uint32_t)0xFFFFFFFF; + gs_tx_desc_null.status.val = GMAC_TXD_WRAP | GMAC_TXD_USED; + gmac_set_tx_priority_queue(GMAC, (uint32_t)&gs_tx_desc_null, GMAC_QUE_2); + gmac_set_tx_priority_queue(GMAC, (uint32_t)&gs_tx_desc_null, GMAC_QUE_1); +#if (SAMV71B || SAME70B) + gmac_set_tx_priority_queue(GMAC, (uint32_t)&gs_tx_desc_null, GMAC_QUE_3); + gmac_set_tx_priority_queue(GMAC, (uint32_t)&gs_tx_desc_null, GMAC_QUE_4); + gmac_set_tx_priority_queue(GMAC, (uint32_t)&gs_tx_desc_null, GMAC_QUE_5); +#endif + + /* Set Rx Priority */ + gs_rx_desc_null.addr.val = (uint32_t)0xFFFFFFFF & GMAC_RXD_ADDR_MASK; + gs_rx_desc_null.addr.val |= GMAC_RXD_WRAP; + gs_rx_desc_null.status.val = 0; + gmac_set_rx_priority_queue(GMAC, (uint32_t)&gs_rx_desc_null, GMAC_QUE_2); + gmac_set_rx_priority_queue(GMAC, (uint32_t)&gs_rx_desc_null, GMAC_QUE_1); +#if (SAMV71B || SAME70B) + gmac_set_rx_priority_queue(GMAC, (uint32_t)&gs_rx_desc_null, GMAC_QUE_3); + gmac_set_rx_priority_queue(GMAC, (uint32_t)&gs_rx_desc_null, GMAC_QUE_4); + gmac_set_rx_priority_queue(GMAC, (uint32_t)&gs_rx_desc_null, GMAC_QUE_5); +#endif + + gmac_rx_init(&gs_gmac_dev); + gmac_tx_init(&gs_gmac_dev); + + /* Enable Rx, Tx and the statistics register. */ + gmac_enable_transmit(GMAC, true); + gmac_enable_receive(GMAC, true); + gmac_enable_statistics_write(GMAC, true); + + /* Set up the interrupts for transmission and errors. */ + gmac_enable_interrupt(GMAC, GMAC_INT_GROUP); + + /* Set GMAC address. */ + gmac_set_address(GMAC, 0, gs_uc_mac_address); + + /* Enable NVIC GMAC interrupt. */ +#if 0 // chrishamm: NVIC priorities are assigned by RepRapFirmware + NVIC_SetPriority(GMAC_IRQn, INT_PRIORITY_GMAC); +#endif + NVIC_EnableIRQ(GMAC_IRQn); +} + +bool ethernetif_establish_link() noexcept +{ + /* Auto Negotiate, work in RMII mode. */ + uint8_t result = ethernet_phy_auto_negotiate(GMAC, BOARD_GMAC_PHY_ADDR); + if (result != GMAC_OK) { + if (result != GMAC_TIMEOUT) + { + // chrishamm: It is expected that the function above will return ERR_TIMEOUT a few times + LWIP_DEBUGF(NETIF_DEBUG, ("gmac_low_level_init: auto negotiate ERROR!\n")); + } + return false; + } + + /* Establish ethernet link. */ + if (ethernet_phy_set_link(GMAC, BOARD_GMAC_PHY_ADDR, 1) != GMAC_OK) { + LWIP_DEBUGF(NETIF_DEBUG, ("gmac_low_level_init: set link ERROR!\n")); + return false; + } + + return true; +} + +// Ask the PHY if the link is still up +bool ethernetif_link_established() noexcept +{ + gmac_enable_management(GMAC, true); + + uint32_t ul_stat1; + if (gmac_phy_read(GMAC, BOARD_GMAC_PHY_ADDR, GMII_BMSR, &ul_stat1) != GMAC_OK) { + gmac_enable_management(GMAC, false); + return false; + } + + if ((ul_stat1 & GMII_LINK_STATUS) == 0) { + gmac_enable_management(GMAC, false); + return false; + } + + gmac_enable_management(GMAC, false); + return true; +} + +void ethernetif_set_mac_address(const uint8_t macAddress[]) noexcept +{ + // This function must be called once before low_level_init(), because that is where the + // MAC address of the netif is assigned + for (size_t i = 0; i < 6; ++i) + { + gs_uc_mac_address[i] = macAddress[i]; + } +} + +// This is called when we shut down +void ethernetif_terminate() noexcept +{ + NVIC_DisableIRQ(GMAC_IRQn); + ethernetTask.TerminateAndUnlink(); +} + +extern "C" u32_t millis() noexcept; + +extern "C" u32_t sys_now() noexcept +{ + return millis(); +} + +// End diff --git a/src/Hardware/SAME70/same70_gmac.h b/src/Hardware/SAME70/same70_gmac.h new file mode 100644 index 00000000..dfdaca6d --- /dev/null +++ b/src/Hardware/SAME70/same70_gmac.h @@ -0,0 +1,83 @@ +/** + * + * \file + * + * \brief GMAC (Gigabit MAC) driver for lwIP. + * + * Copyright (c) 2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ + +#ifndef ETHERNETIF_H_INCLUDED +#define ETHERNETIF_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lwip/err.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "netif/etharp.h" + +#ifdef __cplusplus +} + +err_t ethernetif_init(struct netif *netif) noexcept; // called by LwIP to initialise the interface + +void ethernetif_terminate() noexcept; // called when we shut down + +bool ethernetif_input(struct netif *netif) noexcept; // checks for a new packet and returns true if one was processed + +void ethernetif_hardware_init() noexcept; // initialises the low-level hardware interface + +bool ethernetif_establish_link() noexcept; // attempts to establish link and returns true on success + +bool ethernetif_link_established() noexcept; // asks the PHY if the link is still up + +void ethernetif_set_mac_address(const uint8_t macAddress[]) noexcept; + +#endif + +// Error counters +extern unsigned int rxErrorCount; +extern unsigned int rxBuffersNotFullyPopulatedCount; +extern unsigned int txErrorCount; +extern unsigned int txBufferNotFreeCount; +extern unsigned int txBufferTooShortCount; + +#endif /* ETHERNETIF_H_INCLUDED */ |