/* * Copyright (c) 2016 embedded brains GmbH. All rights reserved. * * embedded brains GmbH * Dornierstr. 4 * 82178 Puchheim * Germany * * * 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. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS 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. */ #include #include #include #include #define __INSIDE_RTEMS_BSD_TCPIP_STACK__ 1 #define __BSD_VISIBLE 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Number of interfaces supported by the driver */ #define NIFACES 1 /** Enable/Disable CopyAllFrame */ #define GMAC_CAF_DISABLE 0 #define GMAC_CAF_ENABLE 1 /** Enable/Disable NoBroadCast */ #define GMAC_NBC_DISABLE 0 #define GMAC_NBC_ENABLE 1 /** The PIN list of PIO for GMAC */ #define BOARD_GMAC_PINS \ { (PIO_PD0A_GTXCK | PIO_PD1A_GTXEN | PIO_PD2A_GTX0 | PIO_PD3A_GTX1 \ | PIO_PD4A_GRXDV | PIO_PD5A_GRX0 | PIO_PD6A_GRX1 \ | PIO_PD7A_GRXER \ | PIO_PD8A_GMDC | PIO_PD9A_GMDIO), PIOD, ID_PIOD, PIO_PERIPH_A, \ PIO_DEFAULT } /** The runtime pin configure list for GMAC */ #define BOARD_GMAC_RUN_PINS BOARD_GMAC_PINS /** The PIN list of PIO for GMAC */ #define BOARD_GMAC_RESET_PIN \ { PIO_PC10, PIOC, ID_PIOC, \ PIO_OUTPUT_1, \ PIO_PULLUP } #define GMII_BMCR 0x0 // Basic Mode Control Register #define GMII_PHYID1R 0x2 // PHY Identifier Register 1 #define GMII_PHYID2R 0x3 // PHY Identifier Register 2 #define GMII_RESET (1 << 15) // 1= Software Reset; 0=Normal Operation /** definitions: MII_PHYID1 */ #define GMII_OUI_MSB 0x0022 /** definitions: MII_PHYID2 */ #define GMII_OUI_LSB 0x1572 // KSZ8061 PHY Id2 /** Multicast Enable */ #define GMAC_MC_ENABLE (1u << 6) #define HASH_INDEX_AMOUNT 6 #define HASH_ELEMENTS_PER_INDEX 8 #define MAC_ADDR_MASK 0x0000FFFFFFFFFFFF #define MAC_IDX_MASK (1u << 0) /** Promiscuous Mode Enable */ #define GMAC_PROM_ENABLE (1u << 4) /** RX Defines */ #define GMAC_RX_BD_COUNT 8 #define GMAC_RX_BUFFER_SIZE 1536 #define GMAC_RX_BUF_DESC_ADDR_MASK 0xFFFFFFFC #define GMAC_RX_SET_OFFSET (1u << 15) #define GMAC_RX_SET_USED_WRAP ((1u << 1) | (1u << 0)) #define GMAC_RX_SET_WRAP (1u << 1) #define GMAC_RX_SET_USED (1u << 0) /** TX Defines */ #define GMAC_TX_BD_COUNT 128 #define GMAC_TX_SET_EOF (1u << 15) #define GMAC_TX_SET_WRAP (1u << 30) #define GMAC_TX_SET_USED (1u << 31) #define GMAC_DESCRIPTOR_ALIGNMENT 8 /** IEEE defined Registers */ #define GMII_ANLPAR 0x5 // Auto_negotiation Link Partner Ability Register /** Events */ #define ATSAMV7_ETH_EVENT_INTERRUPT RTEMS_EVENT_1 #define ATSAMV7_ETH_START_TRANSMIT_EVENT RTEMS_EVENT_2 #define ATSAMV7_ETH_RX_DATA_OFFSET 2 #define WATCHDOG_TIMEOUT 5 /** The PINs for GMAC */ static const Pin gmacPins[] = { BOARD_GMAC_RUN_PINS }; static const Pin gmacResetPin = BOARD_GMAC_RESET_PIN; typedef struct if_atsam_gmac { /** The GMAC driver instance */ sGmacd gGmacd; uint32_t retries; uint8_t phy_address; } if_atsam_gmac; /* * Per-device data */ typedef struct if_atsam_softc { /* * Data */ struct arpcom arpcom; if_atsam_gmac Gmac_inst; struct rtems_mdio_info mdio; uint8_t GMacAddress[6]; rtems_id rx_daemon_tid; rtems_id tx_daemon_tid; rtems_vector_number interrupt_number; struct mbuf **rx_mbuf; struct mbuf **tx_mbuf; unsigned tx_bd_remove; unsigned tx_bd_insert; volatile sGmacTxDescriptor *tx_bd_base; uint32_t anlpar; size_t rx_bd_fill_idx; /* * Statistics */ unsigned rx_overrun_errors; unsigned rx_interrupts; unsigned tx_tur_errors; unsigned tx_rlex_errors; unsigned tx_tfc_errors; unsigned tx_hresp_errors; unsigned tx_interrupts; } if_atsam_softc; static struct if_atsam_softc if_atsam_softc_inst; static struct mbuf *if_atsam_new_mbuf(struct ifnet *ifp) { struct mbuf *m; MGETHDR(m, M_DONTWAIT, MT_DATA); if (m != NULL) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) != 0) { m->m_pkthdr.rcvif = ifp; m->m_data = mtod(m, char *); } else { m_free(m); m = NULL; } rtems_cache_invalidate_multiple_data_lines(mtod(m, void *), GMAC_RX_BUFFER_SIZE); } return (m); } static uint8_t if_atsam_wait_phy(Gmac *pHw, uint32_t retry) { volatile uint32_t retry_count = 0; while (!GMAC_IsIdle(pHw)) { if (retry == 0) { continue; } retry_count++; if (retry_count >= retry) { return (1); } usleep(100); } return (0); } static uint8_t if_atsam_write_phy(Gmac *pHw, uint8_t PhyAddress, uint8_t Address, uint32_t Value, uint32_t retry) { GMAC_PHYMaintain(pHw, PhyAddress, Address, 0, (uint16_t)Value); TRACE_DEBUG(" Write Access\n\r"); if (if_atsam_wait_phy(pHw, retry) == 1) { TRACE_ERROR("TimeOut WritePhy\n\r"); return (1); } return (0); } static uint8_t if_atsam_read_phy(Gmac *pHw, uint8_t PhyAddress, uint8_t Address, uint32_t *pvalue, uint32_t retry) { TRACE_DEBUG(" Read Access\n\r"); GMAC_PHYMaintain(pHw, PhyAddress, Address, 1, 0); if (if_atsam_wait_phy(pHw, retry) == 1) { TRACE_ERROR("TimeOut ReadPhy\n\r"); return (1); } *pvalue = GMAC_PHYData(pHw); return (0); } static void atsamv7_find_valid_phy(if_atsam_gmac *gmac_inst) { Gmac *pHw = gmac_inst->gGmacd.pHw; uint32_t retry_max; uint32_t value = 0; uint8_t rc; uint8_t phy_address; uint8_t cnt; TRACE_DEBUG("GMACB_FindValidPhy\n\r"); phy_address = gmac_inst->phy_address; retry_max = gmac_inst->retries; if (phy_address != -1) { return; } /* Find another one */ if (value != GMII_OUI_MSB) { rc = 0xFF; for (cnt = 0; cnt < 32; cnt++) { phy_address = (phy_address + 1) & 0x1F; if (if_atsam_read_phy(pHw, phy_address, GMII_PHYID1R, &value, retry_max) == 1) { TRACE_ERROR("MACB PROBLEM\n\r"); } TRACE_DEBUG("_PHYID1 : 0x%X, addr: %d\n\r", value, phy_address); if (value == GMII_OUI_MSB) { rc = phy_address; break; } } } if (rc != 0xFF) { TRACE_DEBUG("** Valid PHY Found: %d\n\r", rc); if_atsam_read_phy(pHw, phy_address, GMII_PHYID1R, &value, retry_max); TRACE_DEBUG("_PHYID1R : 0x%X, addr: %d\n\r", value, phy_address); if_atsam_read_phy(pHw, phy_address, GMII_PHYID2R, &value, retry_max); TRACE_DEBUG("_EMSR : 0x%X, addr: %d\n\r", value, phy_address); gmac_inst->phy_address = phy_address; } } static uint8_t if_atsam_reset_phy(if_atsam_gmac *gmac_inst) { uint32_t retry_max; uint32_t bmcr = GMII_RESET; uint8_t phy_address; uint32_t timeout = 10; uint8_t ret = 0; Gmac *pHw = gmac_inst->gGmacd.pHw; TRACE_DEBUG(" GMACB_ResetPhy\n\r"); phy_address = gmac_inst->phy_address; retry_max = gmac_inst->retries; bmcr = GMII_RESET; if_atsam_write_phy(pHw, phy_address, GMII_BMCR, bmcr, retry_max); do { if_atsam_read_phy(pHw, phy_address, GMII_BMCR, &bmcr, retry_max); timeout--; } while ((bmcr & GMII_RESET) && timeout); if (!timeout) { ret = 1; } return (ret); } static uint8_t if_atsam_init_phy(if_atsam_gmac *gmac_inst, uint32_t mck, const Pin *pResetPins, uint32_t nbResetPins, const Pin *pGmacPins, uint32_t nbGmacPins) { uint8_t rc = 1; Gmac *pHw = gmac_inst->gGmacd.pHw; /* Perform RESET */ TRACE_DEBUG("RESET PHY\n\r"); if (pResetPins) { /* Configure PINS */ PIO_Configure(pResetPins, nbResetPins); TRACE_DEBUG(" Hard Reset of GMACD Phy\n\r"); PIO_Clear(pResetPins); usleep(100); PIO_Set(pResetPins); } /* Configure GMAC runtime pins */ if (rc) { PIO_Configure(pGmacPins, nbGmacPins); rc = GMAC_SetMdcClock(pHw, mck); if (!rc) { TRACE_ERROR("No Valid MDC clock\n\r"); return (0); } if_atsam_reset_phy(gmac_inst); } else { TRACE_ERROR("PHY Reset Timeout\n\r"); } return (rc); } static int if_atsam_mdio_read(int phy, void *arg, unsigned reg, uint32_t *pval) { if_atsam_softc *sc = (if_atsam_softc *)arg; int err; TRACE_DEBUG("Mdio read\n\r"); TRACE_DEBUG("%i\n", phy); if ((phy <= 0) || (phy >= 31)) { /* * invalid phy number */ TRACE_ERROR("Mdio read error\n\r"); return (EINVAL); } else { err = if_atsam_read_phy(sc->Gmac_inst.gGmacd.pHw, (uint8_t)phy, (uint8_t)reg, pval, 1); } return (err); } static int if_atsam_mdio_write(int phy, void *arg, unsigned reg, uint32_t pval) { if_atsam_softc *sc = (if_atsam_softc *)arg; int err = 0; TRACE_DEBUG("Mdio write\n\r"); if ((phy <= 0) && (phy <= 31)) { /* * invalid phy number */ TRACE_DEBUG("%i\n", phy); TRACE_ERROR("Mdio write error\n\r"); return (EINVAL); } else { err = if_atsam_write_phy(sc->Gmac_inst.gGmacd.pHw, (uint8_t)phy, (uint8_t)reg, pval, 1); } return (err); } /* * Interrupt Handler for the network driver */ static void if_atsam_interrupt_handler(void *arg) { if_atsam_softc *sc = (if_atsam_softc *)arg; uint32_t irq_status_val; rtems_event_set rx_event = 0; rtems_event_set tx_event = 0; Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; /* Get interrupt status */ irq_status_val = GMAC_GetItStatus(pHw, 0); /* Check receive interrupts */ if ((irq_status_val & GMAC_IER_ROVR) != 0) { ++sc->rx_overrun_errors; rx_event = ATSAMV7_ETH_EVENT_INTERRUPT; } if ((irq_status_val & GMAC_IER_RCOMP) != 0) { rx_event = ATSAMV7_ETH_EVENT_INTERRUPT; } /* Send events to receive task and switch off rx interrupts */ if (rx_event != 0) { ++sc->rx_interrupts; /* Erase the interrupts for RX completion and errors */ GMAC_DisableIt(pHw, GMAC_IER_RCOMP | GMAC_IER_ROVR, 0); (void)rtems_bsdnet_event_send(sc->rx_daemon_tid, rx_event); } if ((irq_status_val & GMAC_IER_TUR) != 0) { ++sc->tx_tur_errors; tx_event = ATSAMV7_ETH_EVENT_INTERRUPT; } if ((irq_status_val & GMAC_IER_RLEX) != 0) { ++sc->tx_rlex_errors; tx_event = ATSAMV7_ETH_EVENT_INTERRUPT; } if ((irq_status_val & GMAC_IER_TFC) != 0) { ++sc->tx_tfc_errors; tx_event = ATSAMV7_ETH_EVENT_INTERRUPT; } if ((irq_status_val & GMAC_IER_HRESP) != 0) { TRACE_DEBUG("Tx interrupts: %u\n", sc->tx_interrupts); ++sc->tx_hresp_errors; tx_event = ATSAMV7_ETH_EVENT_INTERRUPT; } if ((irq_status_val & GMAC_IER_TCOMP) != 0) { tx_event = ATSAMV7_ETH_EVENT_INTERRUPT; } /* Send events to transmit task and switch off tx interrupts */ if (tx_event != 0) { ++sc->tx_interrupts; /* Erase the interrupts for TX completion and errors */ GMAC_DisableIt(pHw, GMAC_INT_TX_BITS, 0); (void)rtems_bsdnet_event_send(sc->tx_daemon_tid, tx_event); } } /* * Receive daemon */ static void if_atsam_rx_daemon(void *arg) { TRACE_DEBUG(" rx daemon\n\r"); if_atsam_softc *sc = (if_atsam_softc *)arg; rtems_event_set events = 0; void *rx_bd_base; struct mbuf *m; struct mbuf *n; volatile sGmacRxDescriptor *buffer_desc; int frame_len; struct ether_header *eh; uint32_t tmp_rx_bd_address; Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; /* Allocate memory space for priority queue descriptor list */ rx_bd_base = rtems_cache_coherent_allocate(sizeof(sGmacRxDescriptor), GMAC_DESCRIPTOR_ALIGNMENT, 0); assert(rx_bd_base != NULL); buffer_desc = (sGmacRxDescriptor *)rx_bd_base; buffer_desc->addr.val = GMAC_RX_SET_USED_WRAP; buffer_desc->status.val = 0; GMAC_SetRxQueue(pHw, (uint32_t)buffer_desc, 1); GMAC_SetRxQueue(pHw, (uint32_t)buffer_desc, 2); /* Allocate memory space for buffer descriptor list */ rx_bd_base = rtems_cache_coherent_allocate( GMAC_RX_BD_COUNT * sizeof(sGmacRxDescriptor), GMAC_DESCRIPTOR_ALIGNMENT, 0); assert(rx_bd_base != NULL); buffer_desc = (sGmacRxDescriptor *)rx_bd_base; /* Create descriptor list and mark as empty */ for (sc->rx_bd_fill_idx = 0; sc->rx_bd_fill_idx < GMAC_RX_BD_COUNT; ++sc->rx_bd_fill_idx) { m = if_atsam_new_mbuf(&sc->arpcom.ac_if); assert(m != NULL); sc->rx_mbuf[sc->rx_bd_fill_idx] = m; buffer_desc->addr.val = ((uint32_t)m->m_data) & GMAC_RX_BUF_DESC_ADDR_MASK; buffer_desc->status.val = 0; if (sc->rx_bd_fill_idx == (GMAC_RX_BD_COUNT - 1)) { buffer_desc->addr.bm.bWrap = 1; } else { buffer_desc++; } } buffer_desc = (sGmacRxDescriptor *)rx_bd_base; /* Set 2 Byte Receive Buffer Offset */ pHw->GMAC_NCFGR |= GMAC_RX_SET_OFFSET; /* Write Buffer Queue Base Address Register */ GMAC_ReceiveEnable(pHw, 0); GMAC_SetRxQueue(pHw, (uint32_t)buffer_desc, 0); /* Set address for address matching */ TRACE_DEBUG("Connect the board to a host PC via an ethernet cable\n\r"); GMAC_SetAddress(pHw, 0, sc->GMacAddress); TRACE_DEBUG("-- MAC %x:%x:%x:%x:%x:%x\n\r", sc->GMacAddress[0], sc->GMacAddress[1], sc->GMacAddress[2], sc->GMacAddress[3], sc->GMacAddress[4], sc->GMacAddress[5]); /* Enable Receiving of data */ GMAC_ReceiveEnable(pHw, 1); /* Setup the interrupts for RX completion and errors */ GMAC_EnableIt(pHw, GMAC_IER_RCOMP | GMAC_IER_ROVR, 0); sc->rx_bd_fill_idx = 0; while (true) { TRACE_DEBUG("Wait for receive event\n"); /* Wait for events */ rtems_bsdnet_event_receive(ATSAMV7_ETH_EVENT_INTERRUPT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events); TRACE_DEBUG("Receive event received\n"); /* * Check for all packets with a set ownership bit */ while (buffer_desc->addr.bm.bOwnership == 1) { if (buffer_desc->status.bm.bEof == 1) { TRACE_DEBUG("Buffer Descriptor %i\n", sc->rx_bd_fill_idx); m = sc->rx_mbuf[sc->rx_bd_fill_idx]; /* New mbuf for desc */ n = if_atsam_new_mbuf(&sc->arpcom.ac_if); if (n != NULL) { frame_len = (int) (buffer_desc->status.bm.len); /* Discard Ethernet header */ int sz = frame_len - ETHER_HDR_LEN; /* Update mbuf */ eh = (struct ether_header *) (mtod(m, char *) + 2); m->m_len = sz; m->m_pkthdr.len = sz; m->m_data = (void *)(eh + 1); ether_input(&sc->arpcom.ac_if, eh, m); m = n; } sc->rx_mbuf[sc->rx_bd_fill_idx] = m; tmp_rx_bd_address = (uint32_t)m->m_data & GMAC_RX_BUF_DESC_ADDR_MASK; /* Switch pointer to next buffer descriptor */ if (sc->rx_bd_fill_idx == (GMAC_RX_BD_COUNT - 1)) { tmp_rx_bd_address |= GMAC_RX_SET_WRAP; sc->rx_bd_fill_idx = 0; } else { ++sc->rx_bd_fill_idx; } /* * Give ownership to GMAC for further processing */ tmp_rx_bd_address &= ~GMAC_RX_SET_USED; _ARM_Data_synchronization_barrier(); buffer_desc->addr.val = tmp_rx_bd_address; buffer_desc = (sGmacRxDescriptor *)rx_bd_base + sc->rx_bd_fill_idx; } } /* Setup the interrupts for RX completion and errors */ GMAC_EnableIt(pHw, GMAC_IER_RCOMP | GMAC_IER_ROVR, 0); } } /* * Update of current transmit buffer position. */ static void if_atsam_tx_bd_pos_update(size_t *pos) { *pos = (*pos + 1) % GMAC_TX_BD_COUNT; } /* * Cleanup transmit file descriptors by freeing mbufs which are not needed any * longer due to correct transmission. */ static void if_atsam_tx_bd_cleanup(if_atsam_softc *sc) { struct mbuf *m; volatile sGmacTxDescriptor *cur; bool eof_needed = false; while (sc->tx_bd_remove != sc->tx_bd_insert) { cur = sc->tx_bd_base + sc->tx_bd_remove; if (((cur->status.bm.bUsed == 1) && !eof_needed) || eof_needed) { eof_needed = true; cur->status.val |= GMAC_TX_SET_USED; m = sc->tx_mbuf[sc->tx_bd_remove]; m_free(m); if_atsam_tx_bd_pos_update(&sc->tx_bd_remove); if (cur->status.bm.bLastBuffer) { break; } } else { break; } } } /* * Prepare Ethernet frame to start transmission. */ static void if_atsam_send_packet(if_atsam_softc *sc, struct mbuf *m) { rtems_event_set events = 0; volatile sGmacTxDescriptor *cur; volatile sGmacTxDescriptor *start_packet_tx_bd = 0; int pos = 0; unsigned insert_next_pos; uint32_t tmp_val = 0; Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; TRACE_DEBUG("TX Send Packet\n"); if_atsam_tx_bd_cleanup(sc); /* Wait for interrupt in case no buffer descriptors are available */ /* Wait for events */ while (true) { insert_next_pos = sc->tx_bd_insert; if_atsam_tx_bd_pos_update(&insert_next_pos); if (sc->tx_bd_remove == insert_next_pos) { /* Setup the interrupts for TX completion and errors */ GMAC_EnableIt(pHw, GMAC_INT_TX_BITS, 0); rtems_bsdnet_event_receive(ATSAMV7_ETH_EVENT_INTERRUPT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events); if_atsam_tx_bd_cleanup(sc); } /* * Get current mbuf for data fill */ cur = &sc->tx_bd_base[sc->tx_bd_insert]; /* Set the transfer data */ rtems_cache_flush_multiple_data_lines(mtod(m, const void *), (size_t)m->m_len); if (m->m_len) { cur->addr = (uint32_t)(mtod(m, void *)); tmp_val = (uint32_t)m->m_len | GMAC_TX_SET_USED; if (sc->tx_bd_insert == (GMAC_TX_BD_COUNT - 1)) { tmp_val |= GMAC_TX_SET_WRAP; } if (pos == 0) { start_packet_tx_bd = cur; } sc->tx_mbuf[sc->tx_bd_insert] = m; m = m->m_next; if_atsam_tx_bd_pos_update(&sc->tx_bd_insert); } else { /* Discard empty mbufs */ m = m_free(m); } /* * Send out the buffer once the complete mbuf_chain has been * processed */ if (m == NULL) { tmp_val |= GMAC_TX_SET_EOF; tmp_val &= ~GMAC_TX_SET_USED; _ARM_Data_synchronization_barrier(); cur->status.val = tmp_val; start_packet_tx_bd->status.val &= ~GMAC_TX_SET_USED; break; } else { if (pos > 0) { tmp_val &= ~GMAC_TX_SET_USED; } pos++; cur->status.val = tmp_val; } } } /* * Transmit daemon */ static void if_atsam_tx_daemon(void *arg) { TRACE_DEBUG(" tx daemon\n\r"); if_atsam_softc *sc = (if_atsam_softc *)arg; rtems_event_set events = 0; sGmacTxDescriptor *buffer_desc; int bd_number; void *tx_bd_base; struct mbuf *m; Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; struct ifnet *ifp = &sc->arpcom.ac_if; GMAC_TransmitEnable(pHw, 0); /* Allocate memory space for priority queue descriptor list */ tx_bd_base = rtems_cache_coherent_allocate(sizeof(sGmacTxDescriptor), GMAC_DESCRIPTOR_ALIGNMENT, 0); assert(tx_bd_base != NULL); buffer_desc = (sGmacTxDescriptor *)tx_bd_base; buffer_desc->addr = 0; buffer_desc->status.val = GMAC_TX_SET_USED | GMAC_TX_SET_WRAP; GMAC_SetTxQueue(pHw, (uint32_t)buffer_desc, 1); GMAC_SetTxQueue(pHw, (uint32_t)buffer_desc, 2); /* Allocate memory space for buffer descriptor list */ tx_bd_base = rtems_cache_coherent_allocate( GMAC_TX_BD_COUNT * sizeof(sGmacTxDescriptor), GMAC_DESCRIPTOR_ALIGNMENT, 0); assert(tx_bd_base != NULL); buffer_desc = (sGmacTxDescriptor *)tx_bd_base; /* Create descriptor list and mark as empty */ for (bd_number = 0; bd_number < GMAC_TX_BD_COUNT; bd_number++) { buffer_desc->addr = 0; buffer_desc->status.val = GMAC_TX_SET_USED; if (bd_number == (GMAC_TX_BD_COUNT - 1)) { buffer_desc->status.bm.bWrap = 1; } else { buffer_desc++; } } buffer_desc = (sGmacTxDescriptor *)tx_bd_base; /* Write Buffer Queue Base Address Register */ GMAC_SetTxQueue(pHw, (uint32_t)buffer_desc, 0); /* Enable Transmission of data */ GMAC_TransmitEnable(pHw, 1); /* Set variables in context */ sc->tx_bd_remove = 0; sc->tx_bd_insert = 0; sc->tx_bd_base = tx_bd_base; while (true) { TRACE_DEBUG("Wait for TX Transmit Start Event\n"); /* Wait for events */ rtems_bsdnet_event_receive(ATSAMV7_ETH_START_TRANSMIT_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events); TRACE_DEBUG("TX Transmit Event received\n"); /* * Send packets till queue is empty */ while (true) { /* * Get the mbuf chain to transmit */ IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); if (!m) { break; } if_atsam_send_packet(sc, m); _ARM_Data_synchronization_barrier(); GMAC_TransmissionStart(pHw); } ifp->if_flags &= ~IFF_OACTIVE; } } /* * Send packet (caller provides header). */ static void if_atsam_enet_start(struct ifnet *ifp) { if_atsam_softc *sc = (if_atsam_softc *)ifp->if_softc; TRACE_DEBUG(" in start\n\r"); ifp->if_flags |= IFF_OACTIVE; rtems_bsdnet_event_send(sc->tx_daemon_tid, ATSAMV7_ETH_START_TRANSMIT_EVENT); } /* * Attach a watchdog for autonegotiation to the system */ static void if_atsam_interface_watchdog(struct ifnet *ifp) { uint32_t anlpar; uint8_t speed = GMAC_SPEED_100M; uint8_t full_duplex = GMAC_DUPLEX_FULL; if_atsam_softc *sc = (if_atsam_softc *)ifp->if_softc; Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; uint8_t phy = sc->Gmac_inst.phy_address; uint32_t retries = sc->Gmac_inst.retries; TRACE_DEBUG("Entered Watchdog\n\r"); if (if_atsam_read_phy(pHw, phy, GMII_ANLPAR, &anlpar, retries)) { anlpar = 0; } if (sc->anlpar != anlpar) { TRACE_DEBUG("Entered Watchdog Loop\n\r"); /* Set up the GMAC link speed */ if (anlpar & ANLPAR_TX_FD) { /* Set MII for 100BaseTx and Full Duplex */ speed = GMAC_SPEED_100M; full_duplex = GMAC_DUPLEX_FULL; } else if (anlpar & ANLPAR_10_FD) { /* Set MII for 10BaseTx and Full Duplex */ speed = GMAC_SPEED_10M; full_duplex = GMAC_DUPLEX_FULL; } else if (anlpar & ANLPAR_TX) { /* Set MII for 100BaseTx and half Duplex */ speed = GMAC_SPEED_100M; full_duplex = GMAC_DUPLEX_HALF; } else if (anlpar & ANLPAR_10) { /* Set MII for 10BaseTx and half Duplex */ speed = GMAC_SPEED_10M; full_duplex = GMAC_DUPLEX_HALF; } else { /* Set MII for 100BaseTx and Full Duplex */ speed = GMAC_SPEED_100M; full_duplex = GMAC_DUPLEX_FULL; } GMAC_SetLinkSpeed(pHw, speed, full_duplex); sc->anlpar = anlpar; } ifp->if_timer = WATCHDOG_TIMEOUT; } /* * Sets up the hardware and chooses the interface to be used */ static void if_atsam_init(void *arg) { rtems_status_code status; TRACE_DEBUG(" in setup hardware\n\r"); if_atsam_softc *sc = (if_atsam_softc *)arg; struct ifnet *ifp = &sc->arpcom.ac_if; uint32_t dmac_cfg = 0; if (sc->arpcom.ac_if.if_flags & IFF_RUNNING) { return; } sc->arpcom.ac_if.if_flags |= IFF_RUNNING; sc->interrupt_number = GMAC_IRQn; /* Disable Watchdog */ WDT_Disable(WDT); /* Enable Peripheral Clock */ if ((PMC->PMC_PCSR1 & (1u << 7)) != (1u << 7)) { PMC->PMC_PCER1 = 1 << 7; } /* Setup interrupts */ NVIC_ClearPendingIRQ(GMAC_IRQn); NVIC_EnableIRQ(GMAC_IRQn); GMACD_Init(&sc->Gmac_inst.gGmacd, GMAC, ID_GMAC, GMAC_CAF_ENABLE, GMAC_NBC_DISABLE); /* Enable MDIO interface */ GMAC_EnableMdio(sc->Gmac_inst.gGmacd.pHw); /* PHY initialize */ if (!if_atsam_init_phy(&sc->Gmac_inst, BOARD_MCK, &gmacResetPin, 1, gmacPins, PIO_LISTSIZE(gmacPins))) { TRACE_ERROR("PHY Initialize ERROR!\n\r"); } /* Find valid Phy */ atsamv7_find_valid_phy(&sc->Gmac_inst); /* Set Link Speed */ sc->anlpar = 0xFFFFFFFF; if_atsam_interface_watchdog(ifp); /* Configuration of DMAC */ dmac_cfg = (GMAC_DCFGR_DRBS(GMAC_RX_BUFFER_SIZE >> 6)) | GMAC_DCFGR_RXBMS(3) | GMAC_DCFGR_TXPBMS | GMAC_DCFGR_FBLDO_INCR16; GMAC_SetDMAConfig(sc->Gmac_inst.gGmacd.pHw, dmac_cfg, 0); /* Shut down Transmit and Receive */ GMAC_ReceiveEnable(sc->Gmac_inst.gGmacd.pHw, 0); GMAC_TransmitEnable(sc->Gmac_inst.gGmacd.pHw, 0); GMAC_StatisticsWriteEnable(sc->Gmac_inst.gGmacd.pHw, 1); /* * Allocate mbuf pointers */ sc->rx_mbuf = malloc(GMAC_RX_BD_COUNT * sizeof *sc->rx_mbuf, M_MBUF, M_NOWAIT); sc->tx_mbuf = malloc(GMAC_TX_BD_COUNT * sizeof *sc->rx_mbuf, M_MBUF, M_NOWAIT); /* Install interrupt handler */ status = rtems_interrupt_handler_install(sc->interrupt_number, "Ethernet", RTEMS_INTERRUPT_UNIQUE, if_atsam_interrupt_handler, sc); assert(status == RTEMS_SUCCESSFUL); /* * Start driver tasks */ sc->rx_daemon_tid = rtems_bsdnet_newproc("SCrx", 4096, if_atsam_rx_daemon, sc); sc->tx_daemon_tid = rtems_bsdnet_newproc("SCtx", 4096, if_atsam_tx_daemon, sc); /* Start Watchdog Timer */ ifp->if_timer = 1; } /* * Stop the device */ static void if_atsam_stop(struct if_atsam_softc *sc) { struct ifnet *ifp = &sc->arpcom.ac_if; Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; TRACE_DEBUG(" in stop\n\r"); ifp->if_flags &= ~IFF_RUNNING; /* Disable MDIO interface and TX/RX */ pHw->GMAC_NCR &= ~(GMAC_NCR_RXEN | GMAC_NCR_TXEN); pHw->GMAC_NCR &= ~GMAC_NCR_MPE; } /* * Show interface statistics */ static void if_atsam_stats(struct if_atsam_softc *sc) { int eno = EIO; int media = 0; Gmac *pHw; TRACE_DEBUG(" in stats\n\r"); media = (int)IFM_MAKEWORD(0, 0, 0, sc->Gmac_inst.phy_address); eno = rtems_mii_ioctl(&sc->mdio, sc, SIOCGIFMEDIA, &media); rtems_bsdnet_semaphore_release(); if (eno == 0) { rtems_ifmedia2str(media, NULL, 0); printf("\n"); } pHw = sc->Gmac_inst.gGmacd.pHw; printf("\n** Context Statistics **\n"); printf("Rx interrupts: %u\n", sc->rx_interrupts); printf("Tx interrupts: %u\n\n", sc->tx_interrupts); printf("\n** Statistics **\n"); printf("Octets Transmitted Low: %lu\n", pHw->GMAC_OTLO); printf("Octets Transmitted High: %lu\n", pHw->GMAC_OTHI); printf("Frames Transmitted: %lu\n", pHw->GMAC_FT); printf("Broadcast Frames Transmitted: %lu\n", pHw->GMAC_BCFT); printf("Multicast Frames Transmitted: %lu\n", pHw->GMAC_MFT); printf("Pause Frames Transmitted: %lu\n", pHw->GMAC_PFT); printf("64 Byte Frames Transmitted: %lu\n", pHw->GMAC_BFT64); printf("65 to 127 Byte Frames Transmitted: %lu\n", pHw->GMAC_TBFT127); printf("128 to 255 Byte Frames Transmitted: %lu\n", pHw->GMAC_TBFR255); printf("256 to 511 Byte Frames Transmitted: %lu\n", pHw->GMAC_TBFT511); printf("512 to 1023 Byte Frames Transmitted: %lu\n", pHw->GMAC_TBFT1023); printf("1024 to 1518 Byte Frames Transmitted: %lu\n", pHw->GMAC_TBFT1518); printf("Greater Than 1518 Byte Frames Transmitted: %lu\n", pHw->GMAC_GTBFT1518); printf("Transmit Underruns: %lu\n", pHw->GMAC_TUR); printf("Single Collision Frames: %lu\n", pHw->GMAC_SCF); printf("Multiple Collision Frames: %lu\n", pHw->GMAC_MCF); printf("Excessive Collisions: %lu\n", pHw->GMAC_EC); printf("Late Collisions: %lu\n", pHw->GMAC_LC); printf("Deferred Transmission Frames: %lu\n", pHw->GMAC_DTF); printf("Carrier Sense Errors: %lu\n", pHw->GMAC_CSE); printf("Octets Received Low: %lu\n", pHw->GMAC_ORLO); printf("Octets Received High: %lu\n", pHw->GMAC_ORHI); printf("Frames Received: %lu\n", pHw->GMAC_FR); printf("Broadcast Frames Received: %lu\n", pHw->GMAC_BCFR); printf("Multicast Frames Received: %lu\n", pHw->GMAC_MFR); printf("Pause Frames Received: %lu\n", pHw->GMAC_PFR); printf("64 Byte Frames Received: %lu\n", pHw->GMAC_BFR64); printf("65 to 127 Byte Frames Received: %lu\n", pHw->GMAC_TBFR127); printf("128 to 255 Byte Frames Received: %lu\n", pHw->GMAC_TBFR255); printf("256 to 511 Byte Frames Received: %lu\n", pHw->GMAC_TBFR511); printf("512 to 1023 Byte Frames Received: %lu\n", pHw->GMAC_TBFR1023); printf("1024 to 1518 Byte Frames Received: %lu\n", pHw->GMAC_TBFR1518); printf("1519 to Maximum Byte Frames Received: %lu\n", pHw->GMAC_TBFR1518); printf("Undersize Frames Received: %lu\n", pHw->GMAC_UFR); printf("Oversize Frames Received: %lu\n", pHw->GMAC_OFR); printf("Jabbers Received: %lu\n", pHw->GMAC_JR); printf("Frame Check Sequence Errors: %lu\n", pHw->GMAC_FCSE); printf("Length Field Frame Errors: %lu\n", pHw->GMAC_LFFE); printf("Receive Symbol Errors: %lu\n", pHw->GMAC_RSE); printf("Alignment Errors: %lu\n", pHw->GMAC_AE); printf("Receive Resource Errors: %lu\n", pHw->GMAC_RRE); printf("Receive Overrun: %lu\n", pHw->GMAC_ROE); printf("IP Header Checksum Errors: %lu\n", pHw->GMAC_IHCE); printf("TCP Checksum Errors: %lu\n", pHw->GMAC_TCE); printf("UDP Checksum Errors: %lu\n", pHw->GMAC_UCE); rtems_bsdnet_semaphore_obtain(); } /* * Calculates the index that is to be sent into the hash registers */ static void if_atsam_get_hash_index(uint64_t addr, uint32_t *val) { uint64_t tmp_val; uint8_t i, j; uint64_t idx; int offset = 0; addr &= MAC_ADDR_MASK; for (i = 0; i < HASH_INDEX_AMOUNT; ++i) { tmp_val = 0; offset = 0; for (j = 0; j < HASH_ELEMENTS_PER_INDEX; j++) { idx = (addr >> (offset + i)) & MAC_IDX_MASK; tmp_val ^= idx; offset += HASH_INDEX_AMOUNT; } if (tmp_val > 0) { *val |= (1u << i); } } } /* * Dis/Enable promiscuous Mode */ static void if_atsam_promiscuous_mode(if_atsam_softc *sc, bool enable) { Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; if (enable) { pHw->GMAC_NCFGR |= GMAC_PROM_ENABLE; } else { pHw->GMAC_NCFGR &= ~GMAC_PROM_ENABLE; } } /* * Multicast handler */ static int if_atsam_multicast_control(bool add, struct ifreq *ifr, if_atsam_softc *sc) { int eno = 0; struct arpcom *ac = &sc->arpcom; Gmac *pHw = sc->Gmac_inst.gGmacd.pHw; /* Switch off Multicast Hashing */ pHw->GMAC_NCFGR &= ~GMAC_MC_ENABLE; if (add) { eno = ether_addmulti(ifr, ac); } else { eno = ether_delmulti(ifr, ac); } if (eno == ENETRESET) { struct ether_multistep step; struct ether_multi *enm; eno = 0; pHw->GMAC_HRB = 0; pHw->GMAC_HRT = 0; ETHER_FIRST_MULTI(step, ac, enm); while (enm != NULL) { uint64_t addrlo = 0; uint64_t addrhi = 0; uint32_t val = 0; memcpy(&addrlo, enm->enm_addrlo, ETHER_ADDR_LEN); memcpy(&addrhi, enm->enm_addrhi, ETHER_ADDR_LEN); while (addrlo <= addrhi) { if_atsam_get_hash_index(addrlo, &val); if (val < 32) { pHw->GMAC_HRB |= (1u << val); } else { pHw->GMAC_HRT |= (1u << (val - 32)); } ++addrlo; } ETHER_NEXT_MULTI(step, enm); } } /* Switch on Multicast Hashing */ pHw->GMAC_NCFGR |= GMAC_MC_ENABLE; return (eno); } /* * Driver ioctl handler */ static int if_atsam_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data) { struct if_atsam_softc *sc = (if_atsam_softc *)ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; int rv = 0; bool prom_enable; TRACE_DEBUG(" in ioctl\n\r"); switch (command) { case SIOCGIFMEDIA: case SIOCSIFMEDIA: TRACE_DEBUG("MEDIA\n"); rtems_mii_ioctl(&sc->mdio, sc, command, &ifr->ifr_media); break; case SIOCGIFADDR: case SIOCSIFADDR: TRACE_DEBUG("Address\n"); ether_ioctl(ifp, command, data); break; case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_flags & IFF_RUNNING) { /* Don't do anything */ } else { if_atsam_init(sc); } prom_enable = ((ifp->if_flags & IFF_PROMISC) != 0); if_atsam_promiscuous_mode(sc, prom_enable); } else { if (ifp->if_flags & IFF_RUNNING) { if_atsam_stop(sc); } } break; case SIOCADDMULTI: case SIOCDELMULTI: if_atsam_multicast_control(command == SIOCADDMULTI, ifr, sc); case SIO_RTEMS_SHOW_STATS: TRACE_DEBUG("SHOW STATS\n"); if_atsam_stats(sc); break; default: rv = EINVAL; break; } return (rv); } /* * Attach an SAMV71 driver to the system */ static int if_atsam_driver_attach(struct rtems_bsdnet_ifconfig *config) { if_atsam_softc *sc = &if_atsam_softc_inst; struct ifnet *ifp = &sc->arpcom.ac_if; if_atsam_config *conf = config->drv_ctrl; int unitNumber; char *unitName; if (conf != NULL) { sc->Gmac_inst.retries = conf->mdio_retries; sc->Gmac_inst.phy_address = conf->phy_addr; } else { sc->Gmac_inst.retries = 10; sc->Gmac_inst.phy_address = -1; } /* The MAC address used */ memcpy(sc->GMacAddress, config->hardware_address, ETHER_ADDR_LEN); memcpy(sc->arpcom.ac_enaddr, sc->GMacAddress, ETHER_ADDR_LEN); /* * Parse driver name */ unitNumber = rtems_bsdnet_parse_driver_name(config, &unitName); assert(unitNumber == 0); assert(ifp->if_softc == NULL); /* MDIO */ sc->mdio.mdio_r = if_atsam_mdio_read; sc->mdio.mdio_w = if_atsam_mdio_write; sc->mdio.has_gmii = 1; /* * Set up network interface values */ ifp->if_softc = sc; ifp->if_unit = (short int)unitNumber; ifp->if_name = unitName; ifp->if_mtu = ETHERMTU; ifp->if_init = if_atsam_init; ifp->if_ioctl = if_atsam_ioctl; ifp->if_start = if_atsam_enet_start; ifp->if_output = ether_output; ifp->if_watchdog = if_atsam_interface_watchdog; ifp->if_flags = IFF_MULTICAST | IFF_BROADCAST | IFF_SIMPLEX; ifp->if_snd.ifq_maxlen = ifqmaxlen; ifp->if_timer = 0; /* * Attach the interface */ if_attach(ifp); ether_ifattach(ifp); return (1); } int if_atsam_attach(struct rtems_bsdnet_ifconfig *config, int attaching) { (void)attaching; return (if_atsam_driver_attach(config)); }