mirror of
https://git.rtems.org/rtems-libbsd/
synced 2025-05-14 02:30:28 +08:00

Under unknown conditions the receive path ended up in a frozen state. In this state, the DMA and driver descriptor head were equal and all receive descriptors had the used bit set. So, the DMA was unable to store received frames. However, the receive daemon was never woken up to refill the receive buffers. It seems that the RXUBR interrupt can be used to recover from this state. Update #4652.
1665 lines
44 KiB
C
1665 lines
44 KiB
C
/*
|
|
* Copyright (c) 2016 - 2017 embedded brains GmbH. All rights reserved.
|
|
*
|
|
* embedded brains GmbH
|
|
* Dornierstr. 4
|
|
* 82178 Puchheim
|
|
* Germany
|
|
* <info@embedded-brains.de>
|
|
*
|
|
* 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 <machine/rtems-bsd-kernel-space.h>
|
|
|
|
#include <bsp.h>
|
|
|
|
#ifdef LIBBSP_ARM_ATSAM_BSP_H
|
|
|
|
#include <bsp/irq.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/sbuf.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/module.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <net/bpf.h>
|
|
#include <net/if.h>
|
|
#include <net/if_dl.h>
|
|
#include <net/if_var.h>
|
|
#include <net/if_types.h>
|
|
#include <net/if_media.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_ether.h>
|
|
|
|
#include <dev/mii/mii.h>
|
|
#include <dev/mii/miivar.h>
|
|
|
|
#include <libchip/chip.h>
|
|
#include <libchip/include/gmacd.h>
|
|
#include <libchip/include/pio.h>
|
|
|
|
#include <rtems/bsd/local/miibus_if.h>
|
|
#include <rtems/bsd/if_atsam.h>
|
|
#include <rtems/bsd/bsd.h>
|
|
|
|
/*
|
|
* 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
|
|
|
|
/** RX Defines */
|
|
#define GMAC_RX_BUFFER_SIZE 1536
|
|
#define GMAC_RX_BUF_DESC_ADDR_MASK 0xFFFFFFFC
|
|
|
|
#define GMAC_DESCRIPTOR_ALIGNMENT 8
|
|
|
|
/** Events */
|
|
#define ATSAMV7_ETH_RX_EVENT_INTERRUPT RTEMS_EVENT_1
|
|
|
|
/* FIXME: Make these configurable */
|
|
#define MDIO_RETRIES 10
|
|
#define MDIO_PHY MII_PHY_ANY
|
|
#define IGNORE_RX_ERR false
|
|
|
|
#define RX_INTERRUPTS (GMAC_ISR_RCOMP | GMAC_ISR_RXUBR | GMAC_ISR_ROVR)
|
|
|
|
#define RX_DESC_LOG2 3
|
|
#define RX_DESC_COUNT (1U << RX_DESC_LOG2)
|
|
#define RX_DESC_WRAP(idx) \
|
|
((((idx) + 1) & RX_DESC_COUNT) >> (RX_DESC_LOG2 - 1))
|
|
RTEMS_STATIC_ASSERT(RX_DESC_WRAP(RX_DESC_COUNT - 1) ==
|
|
GMAC_RX_WRAP_BIT, rx_desc_wrap);
|
|
RTEMS_STATIC_ASSERT(RX_DESC_WRAP(RX_DESC_COUNT - 2) ==
|
|
0, rx_desc_no_wrap);
|
|
|
|
#define TX_DESC_LOG2 6
|
|
#define TX_DESC_COUNT (1U << TX_DESC_LOG2)
|
|
#define TX_DESC_WRAP(idx) \
|
|
((((idx) + 1) & TX_DESC_COUNT) << (30 - TX_DESC_LOG2))
|
|
RTEMS_STATIC_ASSERT(TX_DESC_WRAP(TX_DESC_COUNT - 1) ==
|
|
GMAC_TX_WRAP_BIT, tx_desc_wrap);
|
|
RTEMS_STATIC_ASSERT(TX_DESC_WRAP(TX_DESC_COUNT - 2) ==
|
|
0, tx_desc_no_wrap);
|
|
|
|
/** The PINs for GMAC */
|
|
static const Pin gmacPins[] = { BOARD_GMAC_RUN_PINS };
|
|
|
|
typedef struct if_atsam_gmac {
|
|
/** The GMAC driver instance */
|
|
sGmacd gGmacd;
|
|
uint32_t retries;
|
|
} if_atsam_gmac;
|
|
|
|
struct if_atsam_tx_bds {
|
|
volatile sGmacTxDescriptor bds[TX_DESC_COUNT];
|
|
};
|
|
|
|
struct if_atsam_rx_bds {
|
|
volatile sGmacRxDescriptor bds[RX_DESC_COUNT];
|
|
};
|
|
|
|
/*
|
|
* Per-device data
|
|
*/
|
|
typedef struct if_atsam_softc {
|
|
/*
|
|
* Data
|
|
*/
|
|
device_t dev;
|
|
struct ifnet *ifp;
|
|
struct mtx mtx;
|
|
if_atsam_gmac Gmac_inst;
|
|
size_t tx_idx_head;
|
|
size_t tx_idx_tail;
|
|
struct if_atsam_tx_bds *tx;
|
|
struct mbuf *tx_mbufs[TX_DESC_COUNT];
|
|
size_t rx_idx_head;
|
|
struct if_atsam_rx_bds *rx;
|
|
struct mbuf *rx_mbufs[RX_DESC_COUNT];
|
|
uint8_t GMacAddress[6];
|
|
rtems_id rx_daemon_tid;
|
|
rtems_vector_number interrupt_number;
|
|
struct callout tick_ch;
|
|
|
|
/*
|
|
* Settings for a fixed speed.
|
|
*/
|
|
bool fixed_speed;
|
|
uint32_t media;
|
|
uint32_t duplex;
|
|
struct ifmedia ifmedia;
|
|
|
|
/*
|
|
* MII bus (only used if no fixed speed)
|
|
*/
|
|
device_t miibus;
|
|
uint8_t link_speed;
|
|
uint8_t link_duplex;
|
|
|
|
/*
|
|
* Statistics
|
|
*/
|
|
struct if_atsam_stats {
|
|
/* Software */
|
|
uint32_t rx_overrun_errors;
|
|
uint32_t rx_used_bit_reads;
|
|
uint32_t rx_interrupts;
|
|
uint32_t tx_tur_errors;
|
|
uint32_t tx_rlex_errors;
|
|
uint32_t tx_tfc_errors;
|
|
uint32_t tx_hresp_errors;
|
|
|
|
/* Hardware */
|
|
uint64_t octets_transm;
|
|
uint32_t frames_transm;
|
|
uint32_t broadcast_frames_transm;
|
|
uint32_t multicast_frames_transm;
|
|
uint32_t pause_frames_transm;
|
|
uint32_t frames_64_byte_transm;
|
|
uint32_t frames_65_to_127_byte_transm;
|
|
uint32_t frames_128_to_255_byte_transm;
|
|
uint32_t frames_256_to_511_byte_transm;
|
|
uint32_t frames_512_to_1023_byte_transm;
|
|
uint32_t frames_1024_to_1518_byte_transm;
|
|
uint32_t frames_greater_1518_byte_transm;
|
|
uint32_t transmit_underruns;
|
|
uint32_t single_collision_frames;
|
|
uint32_t multiple_collision_frames;
|
|
uint32_t excessive_collisions;
|
|
uint32_t late_collisions;
|
|
uint32_t deferred_transmission_frames;
|
|
uint32_t carrier_sense_errors;
|
|
uint64_t octets_rec;
|
|
uint32_t frames_rec;
|
|
uint32_t broadcast_frames_rec;
|
|
uint32_t multicast_frames_rec;
|
|
uint32_t pause_frames_rec;
|
|
uint32_t frames_64_byte_rec;
|
|
uint32_t frames_65_to_127_byte_rec;
|
|
uint32_t frames_128_to_255_byte_rec;
|
|
uint32_t frames_256_to_511_byte_rec;
|
|
uint32_t frames_512_to_1023_byte_rec;
|
|
uint32_t frames_1024_to_1518_byte_rec;
|
|
uint32_t frames_1519_to_maximum_byte_rec;
|
|
uint32_t undersize_frames_rec;
|
|
uint32_t oversize_frames_rec;
|
|
uint32_t jabbers_rec;
|
|
uint32_t frame_check_sequence_errors;
|
|
uint32_t length_field_frame_errors;
|
|
uint32_t receive_symbol_errors;
|
|
uint32_t alignment_errors;
|
|
uint32_t receive_resource_errors;
|
|
uint32_t receive_overrun;
|
|
uint32_t ip_header_checksum_errors;
|
|
uint32_t tcp_checksum_errors;
|
|
uint32_t udp_checksum_errors;
|
|
} stats;
|
|
|
|
int if_flags;
|
|
} if_atsam_softc;
|
|
|
|
static void if_atsam_poll_hw_stats(struct if_atsam_softc *sc);
|
|
|
|
#define IF_ATSAM_LOCK(sc) mtx_lock(&(sc)->mtx)
|
|
|
|
#define IF_ATSAM_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
|
|
|
|
static struct mbuf *if_atsam_new_mbuf(struct ifnet *ifp)
|
|
{
|
|
struct mbuf *m;
|
|
|
|
MGETHDR(m, M_NOWAIT, MT_DATA);
|
|
if (m != NULL) {
|
|
MCLGET(m, M_NOWAIT);
|
|
if ((m->m_flags & M_EXT) != 0) {
|
|
m->m_pkthdr.rcvif = ifp;
|
|
m->m_data = mtod(m, char *);
|
|
rtems_cache_invalidate_multiple_data_lines(mtod(m, void *),
|
|
GMAC_RX_BUFFER_SIZE);
|
|
} else {
|
|
m_free(m);
|
|
m = NULL;
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
rtems_task_wake_after(1);
|
|
}
|
|
|
|
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);
|
|
if (if_atsam_wait_phy(pHw, retry) == 1) {
|
|
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)
|
|
{
|
|
GMAC_PHYMaintain(pHw, PhyAddress, Address, 1, 0);
|
|
if (if_atsam_wait_phy(pHw, retry) == 1) {
|
|
return (1);
|
|
}
|
|
*pvalue = GMAC_PHYData(pHw);
|
|
return (0);
|
|
}
|
|
|
|
|
|
static int
|
|
if_atsam_miibus_readreg(device_t dev, int phy, int reg)
|
|
{
|
|
uint32_t val;
|
|
uint8_t err;
|
|
if_atsam_softc *sc = device_get_softc(dev);
|
|
|
|
IF_ATSAM_LOCK(sc);
|
|
err = if_atsam_read_phy(sc->Gmac_inst.gGmacd.pHw,
|
|
(uint8_t)phy, (uint8_t)reg, &val, sc->Gmac_inst.retries);
|
|
IF_ATSAM_UNLOCK(sc);
|
|
|
|
return (err == 0 ? val : 0);
|
|
}
|
|
|
|
|
|
static int
|
|
if_atsam_miibus_writereg(device_t dev, int phy, int reg, int data)
|
|
{
|
|
if_atsam_softc *sc = device_get_softc(dev);
|
|
|
|
IF_ATSAM_LOCK(sc);
|
|
(void)if_atsam_write_phy(sc->Gmac_inst.gGmacd.pHw,
|
|
(uint8_t)phy, (uint8_t)reg, data, sc->Gmac_inst.retries);
|
|
IF_ATSAM_UNLOCK(sc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
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 */
|
|
if (pResetPins) {
|
|
/* Configure PINS */
|
|
PIO_Configure(pResetPins, nbResetPins);
|
|
PIO_Clear(pResetPins);
|
|
rtems_task_wake_after(1);
|
|
PIO_Set(pResetPins);
|
|
}
|
|
/* Configure GMAC runtime pins */
|
|
if (rc) {
|
|
PIO_Configure(pGmacPins, nbGmacPins);
|
|
rc = GMAC_SetMdcClock(pHw, mck);
|
|
|
|
if (!rc) {
|
|
return (0);
|
|
}
|
|
}
|
|
return (rc);
|
|
}
|
|
|
|
|
|
/*
|
|
* Interrupt Handler for the network driver
|
|
*/
|
|
static void if_atsam_interrupt_handler(void *arg)
|
|
{
|
|
if_atsam_softc *sc = (if_atsam_softc *)arg;
|
|
Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
|
|
uint32_t is;
|
|
|
|
/* Get interrupt status */
|
|
is = pHw->GMAC_ISR;
|
|
|
|
if (__predict_false((is & GMAC_TX_ERR_BIT) != 0)) {
|
|
if ((is & GMAC_IER_TUR) != 0) {
|
|
++sc->stats.tx_tur_errors;
|
|
}
|
|
if ((is & GMAC_IER_RLEX) != 0) {
|
|
++sc->stats.tx_rlex_errors;
|
|
}
|
|
if ((is & GMAC_IER_TFC) != 0) {
|
|
++sc->stats.tx_tfc_errors;
|
|
}
|
|
if ((is & GMAC_IER_HRESP) != 0) {
|
|
++sc->stats.tx_hresp_errors;
|
|
}
|
|
}
|
|
|
|
/* Check receive interrupts */
|
|
if (__predict_true((is & RX_INTERRUPTS) != 0)) {
|
|
if (__predict_false((is & GMAC_ISR_RXUBR) != 0)) {
|
|
++sc->stats.rx_used_bit_reads;
|
|
}
|
|
|
|
if (__predict_false((is & GMAC_ISR_ROVR) != 0)) {
|
|
++sc->stats.rx_overrun_errors;
|
|
}
|
|
|
|
++sc->stats.rx_interrupts;
|
|
|
|
/* Disable RX interrupts */
|
|
pHw->GMAC_IDR = RX_INTERRUPTS;
|
|
|
|
(void)rtems_event_send(sc->rx_daemon_tid,
|
|
ATSAMV7_ETH_RX_EVENT_INTERRUPT);
|
|
}
|
|
}
|
|
|
|
static void
|
|
if_atsam_rx_update_mbuf(struct mbuf *m, uint32_t status)
|
|
{
|
|
int frame_len;
|
|
|
|
frame_len = (int)(status & GMAC_LENGTH_FRAME);
|
|
|
|
m->m_data = mtod(m, char*)+ETHER_ALIGN;
|
|
m->m_len = frame_len;
|
|
m->m_pkthdr.len = frame_len;
|
|
|
|
/* check checksum offload result */
|
|
m->m_pkthdr.csum_flags = 0;
|
|
switch ((status >> 22) & 0x3) {
|
|
case GMAC_RXDESC_ST_CKSUM_RESULT_IP_CHECKED:
|
|
m->m_pkthdr.csum_flags = CSUM_IP_CHECKED | CSUM_IP_VALID;
|
|
m->m_pkthdr.csum_data = 0xffff;
|
|
break;
|
|
case GMAC_RXDESC_ST_CKSUM_RESULT_IP_AND_TCP_CHECKED:
|
|
case GMAC_RXDESC_ST_CKSUM_RESULT_IP_AND_UDP_CHECKED:
|
|
m->m_pkthdr.csum_flags = CSUM_IP_CHECKED | CSUM_IP_VALID |
|
|
CSUM_L4_VALID | CSUM_L4_CALC;
|
|
m->m_pkthdr.csum_data = 0xffff;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
if_atsam_rx_daemon(rtems_task_argument arg)
|
|
{
|
|
if_atsam_softc *sc = (if_atsam_softc *)arg;
|
|
struct ifnet *ifp = sc->ifp;
|
|
volatile sGmacRxDescriptor *base;
|
|
struct if_atsam_rx_bds *rx;
|
|
Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
|
|
size_t idx;
|
|
struct mbuf **mbufs;
|
|
struct mbuf *m;
|
|
|
|
IF_ATSAM_LOCK(sc);
|
|
|
|
if (IGNORE_RX_ERR) {
|
|
pHw->GMAC_NCFGR |= GMAC_NCFGR_IRXER;
|
|
} else {
|
|
pHw->GMAC_NCFGR &= ~GMAC_NCFGR_IRXER;
|
|
}
|
|
|
|
/* Allocate memory space for priority queue descriptor list */
|
|
base = rtems_cache_coherent_allocate(sizeof(*base),
|
|
GMAC_DESCRIPTOR_ALIGNMENT, 0);
|
|
assert(base != NULL);
|
|
|
|
base->addr.val = GMAC_RX_OWNERSHIP_BIT | GMAC_RX_WRAP_BIT;
|
|
base->status.val = 0;
|
|
|
|
GMAC_SetRxQueue(pHw, (uint32_t)base, 1);
|
|
GMAC_SetRxQueue(pHw, (uint32_t)base, 2);
|
|
|
|
/* Allocate memory space for buffer descriptor list */
|
|
rx = rtems_cache_coherent_allocate(sizeof(*rx),
|
|
GMAC_DESCRIPTOR_ALIGNMENT, 0);
|
|
assert(rx != NULL);
|
|
sc->rx = rx;
|
|
mbufs = &sc->rx_mbufs[0];
|
|
|
|
/* Create descriptor list and mark as empty */
|
|
for (idx = 0; idx < RX_DESC_COUNT; ++idx) {
|
|
m = if_atsam_new_mbuf(ifp);
|
|
assert(m != NULL);
|
|
mbufs[idx] = m;
|
|
rx->bds[idx].addr.val = mtod(m, uint32_t) | RX_DESC_WRAP(idx);
|
|
}
|
|
|
|
/* Set 2 Byte Receive Buffer Offset */
|
|
pHw->GMAC_NCFGR |= GMAC_NCFGR_RXBUFO(2);
|
|
|
|
/* Write Buffer Queue Base Address Register */
|
|
GMAC_ReceiveEnable(pHw, 0);
|
|
GMAC_SetRxQueue(pHw, (uint32_t)&rx->bds[0], 0);
|
|
|
|
/* Set address for address matching */
|
|
GMAC_SetAddress(pHw, 0, sc->GMacAddress);
|
|
|
|
/* Enable Receiving of data */
|
|
GMAC_ReceiveEnable(pHw, 1);
|
|
|
|
IF_ATSAM_UNLOCK(sc);
|
|
|
|
idx = 0;
|
|
|
|
while (true) {
|
|
rtems_event_set out;
|
|
|
|
sc->rx_idx_head = idx;
|
|
|
|
/* Enable RX interrupts */
|
|
pHw->GMAC_IER = RX_INTERRUPTS;
|
|
|
|
(void) rtems_event_receive(ATSAMV7_ETH_RX_EVENT_INTERRUPT,
|
|
RTEMS_EVENT_ALL | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &out);
|
|
|
|
while (true) {
|
|
uint32_t addr;
|
|
uint32_t status;
|
|
|
|
addr = rx->bds[idx].addr.val;
|
|
if ((addr & GMAC_RX_OWNERSHIP_BIT) == 0) {
|
|
break;
|
|
}
|
|
|
|
status = rx->bds[idx].status.val;
|
|
m = mbufs[idx];
|
|
|
|
if (__predict_true((status & GMAC_RX_EOF_BIT) != 0)) {
|
|
struct mbuf *n;
|
|
|
|
n = if_atsam_new_mbuf(ifp);
|
|
if (n != NULL) {
|
|
if_atsam_rx_update_mbuf(m, status);
|
|
(*ifp->if_input)(ifp, m);
|
|
m = n;
|
|
}
|
|
} else {
|
|
if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
|
|
}
|
|
|
|
mbufs[idx] = m;
|
|
rx->bds[idx].addr.val = mtod(m, uint32_t) |
|
|
RX_DESC_WRAP(idx);
|
|
|
|
idx = (idx + 1) % RX_DESC_COUNT;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
if_atsam_tx_reclaim(struct if_atsam_softc *sc, struct ifnet *ifp)
|
|
{
|
|
uint32_t head_idx;
|
|
uint32_t tail_idx;
|
|
volatile sGmacTxDescriptor *base;
|
|
|
|
head_idx = sc->tx_idx_head;
|
|
tail_idx = sc->tx_idx_tail;
|
|
base = &sc->tx->bds[0];
|
|
|
|
while (head_idx != tail_idx) {
|
|
uint32_t status;
|
|
ift_counter cnt;
|
|
struct mbuf *m;
|
|
|
|
status = base[tail_idx].status.val;
|
|
|
|
if ((status & GMAC_TX_USED_BIT) == 0) {
|
|
break;
|
|
}
|
|
|
|
if (__predict_true((status & GMAC_TX_ERR_BITS) == 0)) {
|
|
cnt = IFCOUNTER_OPACKETS;
|
|
} else {
|
|
cnt = IFCOUNTER_OERRORS;
|
|
}
|
|
|
|
while ((m = sc->tx_mbufs[tail_idx]) == NULL ) {
|
|
base[tail_idx].status.val = status | GMAC_TX_USED_BIT;
|
|
tail_idx = (tail_idx + 1) % TX_DESC_COUNT;
|
|
status = base[tail_idx].status.val;
|
|
|
|
if (__predict_false((status & GMAC_TX_ERR_BITS) != 0)) {
|
|
cnt = IFCOUNTER_OERRORS;
|
|
}
|
|
}
|
|
|
|
base[tail_idx].status.val = status | GMAC_TX_USED_BIT;
|
|
if_inc_counter(ifp, cnt, 1);
|
|
sc->tx_mbufs[tail_idx] = NULL;
|
|
m_freem(m);
|
|
|
|
tail_idx = (tail_idx + 1) % TX_DESC_COUNT;
|
|
}
|
|
|
|
sc->tx_idx_tail = tail_idx;
|
|
}
|
|
|
|
static void
|
|
if_atsam_cache_flush(uintptr_t begin, uintptr_t size)
|
|
{
|
|
uintptr_t end;
|
|
uintptr_t mask;
|
|
|
|
/* Align begin and end of the data to a cache line */
|
|
end = begin + size;
|
|
mask = CPU_CACHE_LINE_BYTES - 1;
|
|
begin &= ~mask;
|
|
end = (end + mask) & ~mask;
|
|
rtems_cache_flush_multiple_data_lines((void *)begin, end - begin);
|
|
}
|
|
|
|
static int
|
|
if_atsam_tx_enqueue(struct if_atsam_softc *sc, struct ifnet *ifp, struct mbuf *m)
|
|
{
|
|
size_t head_idx;
|
|
size_t tail_idx;
|
|
size_t capacity;
|
|
size_t idx;
|
|
volatile sGmacTxDescriptor *base;
|
|
volatile sGmacTxDescriptor *desc;
|
|
size_t bufs;
|
|
uint32_t status;
|
|
struct mbuf *n;
|
|
|
|
head_idx = sc->tx_idx_head;
|
|
tail_idx = sc->tx_idx_tail;
|
|
capacity = (tail_idx - head_idx - 1) % TX_DESC_COUNT;
|
|
|
|
idx = head_idx;
|
|
base = &sc->tx->bds[0];
|
|
bufs = 0;
|
|
n = m;
|
|
|
|
do {
|
|
uint32_t size;
|
|
|
|
desc = &base[idx];
|
|
|
|
size = (uint32_t)n->m_len;
|
|
if (__predict_true(size > 0)) {
|
|
uintptr_t begin;
|
|
|
|
++bufs;
|
|
if (__predict_false(bufs > capacity)) {
|
|
return (ENOBUFS);
|
|
}
|
|
|
|
begin = mtod(n, uintptr_t);
|
|
desc->addr = (uint32_t)begin;
|
|
status = GMAC_TX_USED_BIT | TX_DESC_WRAP(idx) | size;
|
|
desc->status.val = status;
|
|
if_atsam_cache_flush(begin, size);
|
|
idx = (idx + 1) % TX_DESC_COUNT;
|
|
}
|
|
|
|
n = n->m_next;
|
|
} while (n != NULL);
|
|
|
|
sc->tx_idx_head = idx;
|
|
|
|
idx = (idx - 1) % TX_DESC_COUNT;
|
|
desc = &base[idx];
|
|
sc->tx_mbufs[idx] = m;
|
|
status = GMAC_TX_LAST_BUFFER_BIT;
|
|
|
|
while (idx != head_idx) {
|
|
desc->status.val = (desc->status.val & ~GMAC_TX_USED_BIT) |
|
|
status;
|
|
status = 0;
|
|
|
|
idx = (idx - 1) % TX_DESC_COUNT;
|
|
desc = &base[idx];
|
|
}
|
|
|
|
desc->status.val = (desc->status.val & ~GMAC_TX_USED_BIT) | status;
|
|
_ARM_Data_synchronization_barrier();
|
|
sc->Gmac_inst.gGmacd.pHw->GMAC_NCR |= GMAC_NCR_TSTART;
|
|
ETHER_BPF_MTAP(ifp, m);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
if_atsam_transmit(struct ifnet *ifp, struct mbuf *m)
|
|
{
|
|
struct if_atsam_softc *sc;
|
|
int error;
|
|
|
|
if (__predict_false((m->m_flags & M_VLANTAG) != 0)) {
|
|
struct mbuf *n;
|
|
|
|
n = ether_vlanencap(m, m->m_pkthdr.ether_vtag);
|
|
if (n == NULL) {
|
|
m_freem(m);
|
|
return (ENOBUFS);
|
|
}
|
|
|
|
m = n;
|
|
}
|
|
|
|
sc = ifp->if_softc;
|
|
IF_ATSAM_LOCK(sc);
|
|
|
|
error = if_atsam_tx_enqueue(sc, ifp, m);
|
|
if_atsam_tx_reclaim(sc, ifp);
|
|
|
|
if (__predict_false(error != 0)) {
|
|
struct mbuf *n;
|
|
|
|
n = m_defrag(m, M_NOWAIT);
|
|
if (n != NULL) {
|
|
m = n;
|
|
}
|
|
|
|
error = if_atsam_tx_enqueue(sc, ifp, m);
|
|
if (error != 0) {
|
|
m_freem(m);
|
|
if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
|
|
}
|
|
}
|
|
|
|
IF_ATSAM_UNLOCK(sc);
|
|
return (error);
|
|
}
|
|
|
|
static uint8_t if_atsam_get_gmac_linkspeed_from_media(uint32_t media_subtype)
|
|
{
|
|
switch (media_subtype) {
|
|
case IFM_10_T:
|
|
return GMAC_SPEED_10M;
|
|
break;
|
|
case IFM_100_TX:
|
|
return GMAC_SPEED_100M;
|
|
break;
|
|
case IFM_1000_T:
|
|
return GMAC_SPEED_1000M;
|
|
break;
|
|
default:
|
|
return 0xFF;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static uint8_t if_atsam_get_gmac_duplex_from_media(uint32_t media_options)
|
|
{
|
|
if (media_options & IFM_FDX) {
|
|
return GMAC_DUPLEX_FULL;
|
|
} else {
|
|
return GMAC_DUPLEX_HALF;
|
|
}
|
|
}
|
|
|
|
|
|
static void if_atsam_miibus_statchg(device_t dev)
|
|
{
|
|
uint8_t link_speed = GMAC_SPEED_100M;
|
|
uint8_t link_duplex = GMAC_DUPLEX_FULL;
|
|
if_atsam_softc *sc = device_get_softc(dev);
|
|
struct mii_data *mii = device_get_softc(sc->miibus);
|
|
|
|
if(sc->fixed_speed)
|
|
return;
|
|
|
|
Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
|
|
|
|
link_duplex = if_atsam_get_gmac_duplex_from_media(
|
|
IFM_OPTIONS(mii->mii_media_active));
|
|
|
|
link_speed = if_atsam_get_gmac_linkspeed_from_media(
|
|
IFM_SUBTYPE(mii->mii_media_active));
|
|
|
|
if (sc->link_speed != link_speed || sc->link_duplex != link_duplex) {
|
|
GMAC_SetLinkSpeed(pHw, link_speed, link_duplex);
|
|
sc->link_speed = link_speed;
|
|
sc->link_duplex = link_duplex;
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
if_atsam_mii_ifmedia_upd(struct ifnet *ifp)
|
|
{
|
|
if_atsam_softc *sc;
|
|
struct mii_data *mii;
|
|
|
|
sc = ifp->if_softc;
|
|
if (sc->fixed_speed || sc->miibus == NULL)
|
|
return (ENXIO);
|
|
|
|
mii = device_get_softc(sc->miibus);
|
|
return (mii_mediachg(mii));
|
|
}
|
|
|
|
|
|
static void
|
|
if_atsam_mii_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
|
|
{
|
|
if_atsam_softc *sc;
|
|
struct mii_data *mii;
|
|
|
|
sc = ifp->if_softc;
|
|
if (sc->fixed_speed || sc->miibus == NULL)
|
|
return;
|
|
|
|
mii = device_get_softc(sc->miibus);
|
|
mii_pollstat(mii);
|
|
ifmr->ifm_active = mii->mii_media_active;
|
|
ifmr->ifm_status = mii->mii_media_status;
|
|
}
|
|
|
|
|
|
static int
|
|
if_atsam_media_change(struct ifnet *ifp __unused)
|
|
{
|
|
/* Do nothing. */
|
|
return (0);
|
|
}
|
|
|
|
|
|
static void
|
|
if_atsam_media_status(struct ifnet *ifp, struct ifmediareq *imr)
|
|
{
|
|
if_atsam_softc *sc = (if_atsam_softc *)ifp->if_softc;
|
|
|
|
imr->ifm_status = IFM_AVALID | IFM_ACTIVE;
|
|
imr->ifm_active = IFM_ETHER | sc->media | sc->duplex;
|
|
}
|
|
|
|
|
|
static void
|
|
if_atsam_tick(void *context)
|
|
{
|
|
if_atsam_softc *sc = context;
|
|
|
|
if_atsam_poll_hw_stats(sc);
|
|
|
|
IF_ATSAM_UNLOCK(sc);
|
|
|
|
if (!sc->fixed_speed) {
|
|
mii_tick(device_get_softc(sc->miibus));
|
|
}
|
|
callout_reset(&sc->tick_ch, hz, if_atsam_tick, sc);
|
|
}
|
|
|
|
static void
|
|
if_atsam_setup_tx(struct if_atsam_softc *sc)
|
|
{
|
|
sGmacTxDescriptor *base;
|
|
struct if_atsam_tx_bds *tx;
|
|
size_t i;
|
|
Gmac *pHw;
|
|
|
|
pHw = sc->Gmac_inst.gGmacd.pHw;
|
|
GMAC_TransmitEnable(pHw, 0);
|
|
|
|
/* Allocate memory space for priority queue descriptor list */
|
|
base = rtems_cache_coherent_allocate(sizeof(base),
|
|
GMAC_DESCRIPTOR_ALIGNMENT, 0);
|
|
assert(base != NULL);
|
|
|
|
base->addr = 0;
|
|
base->status.val = GMAC_TX_USED_BIT | GMAC_TX_WRAP_BIT;
|
|
|
|
GMAC_SetTxQueue(pHw, (uint32_t)base, 1);
|
|
GMAC_SetTxQueue(pHw, (uint32_t)base, 2);
|
|
|
|
/* Allocate memory space for buffer descriptor list */
|
|
tx = rtems_cache_coherent_allocate(sizeof(*sc->tx),
|
|
GMAC_DESCRIPTOR_ALIGNMENT, 0);
|
|
assert(tx != NULL);
|
|
|
|
/* Set variables in context */
|
|
sc->tx = tx;
|
|
|
|
/* Create descriptor list and mark as empty */
|
|
for (i = 0; i < TX_DESC_COUNT; ++i) {
|
|
tx->bds[i].addr = 0;
|
|
tx->bds[i].status.val = GMAC_TX_USED_BIT | TX_DESC_WRAP(i);
|
|
}
|
|
|
|
/* Write Buffer Queue Base Address Register */
|
|
GMAC_SetTxQueue(pHw, (uint32_t)&tx->bds[0], 0);
|
|
|
|
/* Enable Transmission of data */
|
|
GMAC_TransmitEnable(pHw, 1);
|
|
}
|
|
|
|
static void
|
|
if_atsam_init(if_atsam_softc *sc)
|
|
{
|
|
rtems_status_code status;
|
|
uint32_t dmac_cfg = 0;
|
|
|
|
sc->interrupt_number = GMAC_IRQn;
|
|
|
|
/* Enable Peripheral Clock */
|
|
if ((PMC->PMC_PCSR1 & (1u << 7)) != (1u << 7)) {
|
|
PMC->PMC_PCER1 = 1 << 7;
|
|
}
|
|
/* Setup interrupts */
|
|
NVIC_ClearPendingIRQ(GMAC_IRQn);
|
|
|
|
/* 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_DCFGR_TXCOEN;
|
|
GMAC_SetDMAConfig(sc->Gmac_inst.gGmacd.pHw, dmac_cfg, 0);
|
|
|
|
/* Enable hardware checksum offload for receive */
|
|
sc->Gmac_inst.gGmacd.pHw->GMAC_NCFGR |= GMAC_NCFGR_RXCOEN;
|
|
|
|
/* Use Multicast Hash Filter */
|
|
sc->Gmac_inst.gGmacd.pHw->GMAC_NCFGR |= GMAC_NCFGR_MTIHEN;
|
|
sc->Gmac_inst.gGmacd.pHw->GMAC_HRB = 0;
|
|
sc->Gmac_inst.gGmacd.pHw->GMAC_HRT = 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);
|
|
|
|
/* 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
|
|
*/
|
|
|
|
status = rtems_task_create(rtems_build_name('S', 'C', 'r', 'x'),
|
|
rtems_bsd_get_task_priority(device_get_name(sc->dev)), 4096,
|
|
RTEMS_DEFAULT_MODES, RTEMS_DEFAULT_MODES, &sc->rx_daemon_tid);
|
|
assert(status == RTEMS_SUCCESSFUL);
|
|
|
|
status = rtems_task_start(sc->rx_daemon_tid, if_atsam_rx_daemon,
|
|
(rtems_task_argument)sc);
|
|
assert(status == RTEMS_SUCCESSFUL);
|
|
|
|
callout_reset(&sc->tick_ch, hz, if_atsam_tick, sc);
|
|
|
|
if_atsam_setup_tx(sc);
|
|
}
|
|
|
|
static int
|
|
if_atsam_get_hash_index(const uint8_t *eaddr)
|
|
{
|
|
uint64_t eaddr64;
|
|
int index;
|
|
int i;
|
|
|
|
eaddr64 = eaddr[5];
|
|
|
|
for (i = 4; i >= 0; --i) {
|
|
eaddr64 <<= 8;
|
|
eaddr64 |= eaddr[i];
|
|
}
|
|
|
|
index = 0;
|
|
|
|
for (i = 0; i < 6; ++i) {
|
|
uint64_t bits;
|
|
int j;
|
|
int hash;
|
|
|
|
bits = eaddr64 >> i;
|
|
hash = bits & 1;
|
|
|
|
for (j = 1; j < 8; ++j) {
|
|
bits >>= 6;
|
|
hash ^= bits & 1;
|
|
}
|
|
|
|
index |= hash << i;
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
static void
|
|
if_atsam_setup_rxfilter(struct if_atsam_softc *sc)
|
|
{
|
|
struct ifnet *ifp;
|
|
struct ifmultiaddr *ifma;
|
|
uint64_t mhash;
|
|
Gmac *pHw;
|
|
|
|
pHw = sc->Gmac_inst.gGmacd.pHw;
|
|
|
|
if ((sc->ifp->if_flags & IFF_PROMISC) != 0) {
|
|
pHw->GMAC_NCFGR |= GMAC_NCFGR_CAF;
|
|
} else {
|
|
pHw->GMAC_NCFGR &= ~GMAC_NCFGR_CAF;
|
|
}
|
|
|
|
ifp = sc->ifp;
|
|
|
|
if ((ifp->if_flags & IFF_ALLMULTI))
|
|
mhash = 0xffffffffffffffffLLU;
|
|
else {
|
|
mhash = 0;
|
|
if_maddr_rlock(ifp);
|
|
CK_STAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) {
|
|
if (ifma->ifma_addr->sa_family != AF_LINK)
|
|
continue;
|
|
mhash |= 1LLU << if_atsam_get_hash_index(
|
|
LLADDR((struct sockaddr_dl *) ifma->ifma_addr));
|
|
}
|
|
if_maddr_runlock(ifp);
|
|
}
|
|
|
|
pHw->GMAC_HRB = (uint32_t)mhash;
|
|
pHw->GMAC_HRT = (uint32_t)(mhash >> 32);
|
|
}
|
|
|
|
static void
|
|
if_atsam_start_locked(struct if_atsam_softc *sc)
|
|
{
|
|
struct ifnet *ifp = sc->ifp;
|
|
Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
|
|
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
|
|
return;
|
|
}
|
|
|
|
ifp->if_drv_flags |= IFF_DRV_RUNNING;
|
|
|
|
if_atsam_setup_rxfilter(sc);
|
|
|
|
/* Enable TX/RX */
|
|
pHw->GMAC_NCR |= GMAC_NCR_RXEN | GMAC_NCR_TXEN;
|
|
}
|
|
|
|
static void
|
|
if_atsam_start(void *arg)
|
|
{
|
|
struct if_atsam_softc *sc = arg;
|
|
|
|
IF_ATSAM_LOCK(sc);
|
|
if_atsam_start_locked(sc);
|
|
IF_ATSAM_UNLOCK(sc);
|
|
}
|
|
|
|
static void
|
|
if_atsam_stop_locked(struct if_atsam_softc *sc)
|
|
{
|
|
struct ifnet *ifp = sc->ifp;
|
|
Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
|
|
size_t i;
|
|
|
|
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
|
|
|
|
/* Disable TX/RX */
|
|
pHw->GMAC_NCR &= ~(GMAC_NCR_RXEN | GMAC_NCR_TXEN);
|
|
|
|
/* Reinitialize the TX descriptors */
|
|
|
|
sc->tx_idx_head = 0;
|
|
sc->tx_idx_tail = 0;
|
|
|
|
for (i = 0; i < TX_DESC_COUNT; ++i) {
|
|
sc->tx->bds[i].addr = 0;
|
|
sc->tx->bds[i].status.val = GMAC_TX_USED_BIT | TX_DESC_WRAP(i);
|
|
m_freem(sc->tx_mbufs[i]);
|
|
sc->tx_mbufs[i] = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
if_atsam_poll_hw_stats(struct if_atsam_softc *sc)
|
|
{
|
|
uint64_t octets;
|
|
Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
|
|
|
|
octets = pHw->GMAC_OTLO;
|
|
octets |= (uint64_t)pHw->GMAC_OTHI << 32;
|
|
sc->stats.octets_transm += octets;
|
|
sc->stats.frames_transm += pHw->GMAC_FT;
|
|
sc->stats.broadcast_frames_transm += pHw->GMAC_BCFT;
|
|
sc->stats.multicast_frames_transm += pHw->GMAC_MFT;
|
|
sc->stats.pause_frames_transm += pHw->GMAC_PFT;
|
|
sc->stats.frames_64_byte_transm += pHw->GMAC_BFT64;
|
|
sc->stats.frames_65_to_127_byte_transm += pHw->GMAC_TBFT127;
|
|
sc->stats.frames_128_to_255_byte_transm += pHw->GMAC_TBFT255;
|
|
sc->stats.frames_256_to_511_byte_transm += pHw->GMAC_TBFT511;
|
|
sc->stats.frames_512_to_1023_byte_transm += pHw->GMAC_TBFT1023;
|
|
sc->stats.frames_1024_to_1518_byte_transm += pHw->GMAC_TBFT1518;
|
|
sc->stats.frames_greater_1518_byte_transm += pHw->GMAC_GTBFT1518;
|
|
sc->stats.transmit_underruns += pHw->GMAC_TUR;
|
|
sc->stats.single_collision_frames += pHw->GMAC_SCF;
|
|
sc->stats.multiple_collision_frames += pHw->GMAC_MCF;
|
|
sc->stats.excessive_collisions += pHw->GMAC_EC;
|
|
sc->stats.late_collisions += pHw->GMAC_LC;
|
|
sc->stats.deferred_transmission_frames += pHw->GMAC_DTF;
|
|
sc->stats.carrier_sense_errors += pHw->GMAC_CSE;
|
|
|
|
octets = pHw->GMAC_ORLO;
|
|
octets |= (uint64_t)pHw->GMAC_ORHI << 32;
|
|
sc->stats.octets_rec += octets;
|
|
sc->stats.frames_rec += pHw->GMAC_FR;
|
|
sc->stats.broadcast_frames_rec += pHw->GMAC_BCFR;
|
|
sc->stats.multicast_frames_rec += pHw->GMAC_MFR;
|
|
sc->stats.pause_frames_rec += pHw->GMAC_PFR;
|
|
sc->stats.frames_64_byte_rec += pHw->GMAC_BFR64;
|
|
sc->stats.frames_65_to_127_byte_rec += pHw->GMAC_TBFR127;
|
|
sc->stats.frames_128_to_255_byte_rec += pHw->GMAC_TBFR255;
|
|
sc->stats.frames_256_to_511_byte_rec += pHw->GMAC_TBFR511;
|
|
sc->stats.frames_512_to_1023_byte_rec += pHw->GMAC_TBFR1023;
|
|
sc->stats.frames_1024_to_1518_byte_rec += pHw->GMAC_TBFR1518;
|
|
sc->stats.frames_1519_to_maximum_byte_rec += pHw->GMAC_TMXBFR;
|
|
sc->stats.undersize_frames_rec += pHw->GMAC_UFR;
|
|
sc->stats.oversize_frames_rec += pHw->GMAC_OFR;
|
|
sc->stats.jabbers_rec += pHw->GMAC_JR;
|
|
sc->stats.frame_check_sequence_errors += pHw->GMAC_FCSE;
|
|
sc->stats.length_field_frame_errors += pHw->GMAC_LFFE;
|
|
sc->stats.receive_symbol_errors += pHw->GMAC_RSE;
|
|
sc->stats.alignment_errors += pHw->GMAC_AE;
|
|
sc->stats.receive_resource_errors += pHw->GMAC_RRE;
|
|
sc->stats.receive_overrun += pHw->GMAC_ROE;
|
|
|
|
sc->stats.ip_header_checksum_errors += pHw->GMAC_IHCE;
|
|
sc->stats.tcp_checksum_errors += pHw->GMAC_TCE;
|
|
sc->stats.udp_checksum_errors += pHw->GMAC_UCE;
|
|
}
|
|
|
|
static int
|
|
if_atsam_stats_reset(SYSCTL_HANDLER_ARGS)
|
|
{
|
|
struct if_atsam_softc *sc = arg1;
|
|
int value;
|
|
int error;
|
|
|
|
value = 0;
|
|
error = sysctl_handle_int(oidp, &value, 0, req);
|
|
if (error != 0 || req->newptr == NULL) {
|
|
return (error);
|
|
}
|
|
|
|
if (value != 0) {
|
|
IF_ATSAM_LOCK(sc);
|
|
if_atsam_poll_hw_stats(sc);
|
|
memset(&sc->stats, 0, sizeof(sc->stats));
|
|
IF_ATSAM_UNLOCK(sc);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
if_atsam_sysctl_reg(SYSCTL_HANDLER_ARGS)
|
|
{
|
|
u_int value;
|
|
|
|
value = *(uint32_t *)arg1;
|
|
return (sysctl_handle_int(oidp, &value, 0, req));
|
|
}
|
|
|
|
static int
|
|
if_atsam_sysctl_tx_desc(SYSCTL_HANDLER_ARGS)
|
|
{
|
|
struct if_atsam_softc *sc = arg1;
|
|
Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
|
|
struct sbuf *sb;
|
|
int error;
|
|
size_t i;
|
|
|
|
error = sysctl_wire_old_buffer(req, 0);
|
|
if (error != 0) {
|
|
return (error);
|
|
}
|
|
|
|
sb = sbuf_new_for_sysctl(NULL, NULL, 1024, req);
|
|
if (sb == NULL) {
|
|
return (ENOMEM);
|
|
}
|
|
|
|
sbuf_printf(sb, "\n\tHead %u\n", sc->tx_idx_head);
|
|
sbuf_printf(sb, "\tTail %u\n", sc->tx_idx_tail);
|
|
sbuf_printf(sb, "\tDMA %u\n",
|
|
(pHw->GMAC_TBQB - (uintptr_t)&sc->tx->bds[0]) / 8);
|
|
|
|
for (i = 0; i < TX_DESC_COUNT; ++i) {
|
|
sbuf_printf(sb, "\t[%2u] %08x %08x\n", i,
|
|
sc->tx->bds[i].status.val, sc->tx->bds[i].addr);
|
|
}
|
|
|
|
error = sbuf_finish(sb);
|
|
sbuf_delete(sb);
|
|
return (error);
|
|
}
|
|
|
|
static int
|
|
if_atsam_sysctl_rx_desc(SYSCTL_HANDLER_ARGS)
|
|
{
|
|
struct if_atsam_softc *sc = arg1;
|
|
Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
|
|
struct sbuf *sb;
|
|
int error;
|
|
size_t i;
|
|
|
|
error = sysctl_wire_old_buffer(req, 0);
|
|
if (error != 0) {
|
|
return (error);
|
|
}
|
|
|
|
sb = sbuf_new_for_sysctl(NULL, NULL, 1024, req);
|
|
if (sb == NULL) {
|
|
return (ENOMEM);
|
|
}
|
|
|
|
sbuf_printf(sb, "\n\tHead %u\n", sc->rx_idx_head);
|
|
sbuf_printf(sb, "\tDMA %u\n",
|
|
(pHw->GMAC_RBQB - (uintptr_t)&sc->rx->bds[0]) / 8);
|
|
|
|
for (i = 0; i < RX_DESC_COUNT; ++i) {
|
|
sbuf_printf(sb, "\t[%2u] %08x %08x\n", i,
|
|
sc->rx->bds[i].status.val, sc->rx->bds[i].addr);
|
|
}
|
|
|
|
error = sbuf_finish(sb);
|
|
sbuf_delete(sb);
|
|
return (error);
|
|
}
|
|
|
|
static void
|
|
if_atsam_add_sysctls(device_t dev)
|
|
{
|
|
struct if_atsam_softc *sc = device_get_softc(dev);
|
|
Gmac *pHw = sc->Gmac_inst.gGmacd.pHw;
|
|
struct sysctl_ctx_list *ctx;
|
|
struct sysctl_oid_list *base;
|
|
struct sysctl_oid_list *statsnode;
|
|
struct sysctl_oid_list *hwstatsnode;
|
|
struct sysctl_oid_list *child;
|
|
struct sysctl_oid *tree;
|
|
|
|
ctx = device_get_sysctl_ctx(dev);
|
|
base = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
|
|
|
|
tree = SYSCTL_ADD_NODE(ctx, base, OID_AUTO, "regs", CTLFLAG_RD,
|
|
NULL, "if_atsam registers");
|
|
child = SYSCTL_CHILDREN(tree);
|
|
|
|
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "txdesc", CTLTYPE_STRING |
|
|
CTLFLAG_RD, sc, 0, if_atsam_sysctl_tx_desc, "A",
|
|
"Transmit Descriptors");
|
|
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxdesc", CTLTYPE_STRING |
|
|
CTLFLAG_RD, sc, 0, if_atsam_sysctl_rx_desc, "A",
|
|
"Receive Descriptors");
|
|
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "imr", CTLTYPE_UINT |
|
|
CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_IMR), 0,
|
|
if_atsam_sysctl_reg, "I", "IMR");
|
|
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "isr", CTLTYPE_UINT |
|
|
CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_ISR), 0,
|
|
if_atsam_sysctl_reg, "I", "ISR");
|
|
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rsr", CTLTYPE_UINT |
|
|
CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_RSR), 0,
|
|
if_atsam_sysctl_reg, "I", "RSR");
|
|
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tsr", CTLTYPE_UINT |
|
|
CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_TSR), 0,
|
|
if_atsam_sysctl_reg, "I", "TSR");
|
|
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "nsr", CTLTYPE_UINT |
|
|
CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_NSR), 0,
|
|
if_atsam_sysctl_reg, "I", "NSR");
|
|
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "ncfgr", CTLTYPE_UINT |
|
|
CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_NCFGR), 0,
|
|
if_atsam_sysctl_reg, "I", "NCFGR");
|
|
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "ncr", CTLTYPE_UINT |
|
|
CTLFLAG_RD, __DEVOLATILE(uint32_t *, &pHw->GMAC_NCR), 0,
|
|
if_atsam_sysctl_reg, "I", "NCR");
|
|
|
|
tree = SYSCTL_ADD_NODE(ctx, base, OID_AUTO, "stats", CTLFLAG_RD,
|
|
NULL, "if_atsam statistics");
|
|
statsnode = SYSCTL_CHILDREN(tree);
|
|
|
|
SYSCTL_ADD_PROC(ctx, statsnode, OID_AUTO, "reset", CTLTYPE_INT |
|
|
CTLFLAG_WR, sc, 0, if_atsam_stats_reset, "I", "Reset");
|
|
|
|
tree = SYSCTL_ADD_NODE(ctx, statsnode, OID_AUTO, "sw", CTLFLAG_RD,
|
|
NULL, "if_atsam software statistics");
|
|
child = SYSCTL_CHILDREN(tree);
|
|
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_overrun_errors",
|
|
CTLFLAG_RD, &sc->stats.rx_overrun_errors, 0,
|
|
"RX overrun errors");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_used_bit_reads",
|
|
CTLFLAG_RD, &sc->stats.rx_used_bit_reads, 0,
|
|
"RX used bit reads");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_interrupts",
|
|
CTLFLAG_RD, &sc->stats.rx_interrupts, 0,
|
|
"Rx interrupts");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_tur_errors",
|
|
CTLFLAG_RD, &sc->stats.tx_tur_errors, 0,
|
|
"Error Tur Tx interrupts");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_rlex_errors",
|
|
CTLFLAG_RD, &sc->stats.tx_rlex_errors, 0,
|
|
"Error Rlex Tx interrupts");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_tfc_errors",
|
|
CTLFLAG_RD, &sc->stats.tx_tfc_errors, 0,
|
|
"Error Tfc Tx interrupts");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_hresp_errors",
|
|
CTLFLAG_RD, &sc->stats.tx_hresp_errors, 0,
|
|
"Error Hresp Tx interrupts");
|
|
|
|
tree = SYSCTL_ADD_NODE(ctx, statsnode, OID_AUTO, "hw", CTLFLAG_RD,
|
|
NULL, "if_atsam hardware statistics");
|
|
hwstatsnode = SYSCTL_CHILDREN(tree);
|
|
|
|
tree = SYSCTL_ADD_NODE(ctx, hwstatsnode, OID_AUTO, "tx", CTLFLAG_RD,
|
|
NULL, "if_atsam hardware transmit statistics");
|
|
child = SYSCTL_CHILDREN(tree);
|
|
|
|
SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "octets",
|
|
CTLFLAG_RD, &sc->stats.octets_transm,
|
|
"Octets Transmitted");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames",
|
|
CTLFLAG_RD, &sc->stats.frames_transm, 0,
|
|
"Frames Transmitted");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "broadcast_frames",
|
|
CTLFLAG_RD, &sc->stats.broadcast_frames_transm, 0,
|
|
"Broadcast Frames Transmitted");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "multicast_frames",
|
|
CTLFLAG_RD, &sc->stats.multicast_frames_transm, 0,
|
|
"Multicast Frames Transmitted");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "pause_frames",
|
|
CTLFLAG_RD, &sc->stats.pause_frames_transm, 0,
|
|
"Pause Frames Transmitted");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_64_bytes",
|
|
CTLFLAG_RD, &sc->stats.frames_64_byte_transm, 0,
|
|
"64 Byte Frames Transmitted");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_65_to_127_bytes",
|
|
CTLFLAG_RD, &sc->stats.frames_65_to_127_byte_transm, 0,
|
|
"65 to 127 Byte Frames Transmitted");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_128_to_255_bytes",
|
|
CTLFLAG_RD, &sc->stats.frames_128_to_255_byte_transm, 0,
|
|
"128 to 255 Byte Frames Transmitted");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_256_to_511_bytes",
|
|
CTLFLAG_RD, &sc->stats.frames_256_to_511_byte_transm, 0,
|
|
"256 to 511 Byte Frames Transmitted");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_512_to_1023_bytes",
|
|
CTLFLAG_RD, &sc->stats.frames_512_to_1023_byte_transm, 0,
|
|
"512 to 1023 Byte Frames Transmitted");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1024_to_1518_bytes",
|
|
CTLFLAG_RD, &sc->stats.frames_1024_to_1518_byte_transm, 0,
|
|
"1024 to 1518 Byte Frames Transmitted");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1519_to_maximum_bytes",
|
|
CTLFLAG_RD, &sc->stats.frames_greater_1518_byte_transm, 0,
|
|
"Greater Than 1518 Byte Frames Transmitted");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "transmit_underruns",
|
|
CTLFLAG_RD, &sc->stats.transmit_underruns, 0,
|
|
"Transmit Underruns");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "single_collision_frames",
|
|
CTLFLAG_RD, &sc->stats.single_collision_frames, 0,
|
|
"Single Collision Frames");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "multiple_collision_frames",
|
|
CTLFLAG_RD, &sc->stats.multiple_collision_frames, 0,
|
|
"Multiple Collision Frames");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "excessive_collisions",
|
|
CTLFLAG_RD, &sc->stats.excessive_collisions, 0,
|
|
"Excessive Collisions");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "late_collisions",
|
|
CTLFLAG_RD, &sc->stats.late_collisions, 0,
|
|
"Late Collisions");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "deferred_transmission_frames",
|
|
CTLFLAG_RD, &sc->stats.deferred_transmission_frames, 0,
|
|
"Deferred Transmission Frames");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "carrier_sense_errors",
|
|
CTLFLAG_RD, &sc->stats.carrier_sense_errors, 0,
|
|
"Carrier Sense Errors");
|
|
|
|
tree = SYSCTL_ADD_NODE(ctx, hwstatsnode, OID_AUTO, "rx", CTLFLAG_RD,
|
|
NULL, "if_atsam hardware receive statistics");
|
|
child = SYSCTL_CHILDREN(tree);
|
|
|
|
SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "octets",
|
|
CTLFLAG_RD, &sc->stats.octets_rec,
|
|
"Octets Received");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames",
|
|
CTLFLAG_RD, &sc->stats.frames_rec, 0,
|
|
"Frames Received");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "broadcast_frames",
|
|
CTLFLAG_RD, &sc->stats.broadcast_frames_rec, 0,
|
|
"Broadcast Frames Received");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "multicast_frames",
|
|
CTLFLAG_RD, &sc->stats.multicast_frames_rec, 0,
|
|
"Multicast Frames Received");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "pause_frames",
|
|
CTLFLAG_RD, &sc->stats.pause_frames_rec, 0,
|
|
"Pause Frames Received");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_64_bytes",
|
|
CTLFLAG_RD, &sc->stats.frames_64_byte_rec, 0,
|
|
"64 Byte Frames Received");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_65_to_127_bytes",
|
|
CTLFLAG_RD, &sc->stats.frames_65_to_127_byte_rec, 0,
|
|
"65 to 127 Byte Frames Received");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_128_to_255_bytes",
|
|
CTLFLAG_RD, &sc->stats.frames_128_to_255_byte_rec, 0,
|
|
"128 to 255 Byte Frames Received");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_256_to_511_bytes",
|
|
CTLFLAG_RD, &sc->stats.frames_256_to_511_byte_rec, 0,
|
|
"256 to 511 Byte Frames Received");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_512_to_1023_bytes",
|
|
CTLFLAG_RD, &sc->stats.frames_512_to_1023_byte_rec, 0,
|
|
"512 to 1023 Byte Frames Received");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1024_to_1518_bytes",
|
|
CTLFLAG_RD, &sc->stats.frames_1024_to_1518_byte_rec, 0,
|
|
"1024 to 1518 Byte Frames Received");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frames_1519_to_maximum_bytes",
|
|
CTLFLAG_RD, &sc->stats.frames_1519_to_maximum_byte_rec, 0,
|
|
"1519 to Maximum Byte Frames Received");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "undersize_frames",
|
|
CTLFLAG_RD, &sc->stats.undersize_frames_rec, 0,
|
|
"Undersize Frames Received");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "oversize_frames",
|
|
CTLFLAG_RD, &sc->stats.oversize_frames_rec, 0,
|
|
"Oversize Frames Received");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "jabbers",
|
|
CTLFLAG_RD, &sc->stats.jabbers_rec, 0,
|
|
"Jabbers Received");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "frame_check_sequence_errors",
|
|
CTLFLAG_RD, &sc->stats.frame_check_sequence_errors, 0,
|
|
"Frame Check Sequence Errors");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "length_field_frame_errors",
|
|
CTLFLAG_RD, &sc->stats.length_field_frame_errors, 0,
|
|
"Length Field Frame Errors");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "receive_symbol_errors",
|
|
CTLFLAG_RD, &sc->stats.receive_symbol_errors, 0,
|
|
"Receive Symbol Errors");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "alignment_errors",
|
|
CTLFLAG_RD, &sc->stats.alignment_errors, 0,
|
|
"Alignment Errors");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "receive_resource_errors",
|
|
CTLFLAG_RD, &sc->stats.receive_resource_errors, 0,
|
|
"Receive Resource Errors");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "receive_overrun",
|
|
CTLFLAG_RD, &sc->stats.receive_overrun, 0,
|
|
"Receive Overrun");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ip_header_checksum_errors",
|
|
CTLFLAG_RD, &sc->stats.ip_header_checksum_errors, 0,
|
|
"IP Header Checksum Errors");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tcp_checksum_errors",
|
|
CTLFLAG_RD, &sc->stats.tcp_checksum_errors, 0,
|
|
"TCP Checksum Errors");
|
|
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "udp_checksum_errors",
|
|
CTLFLAG_RD, &sc->stats.udp_checksum_errors, 0,
|
|
"UDP Checksum Errors");
|
|
}
|
|
|
|
static int
|
|
if_atsam_mediaioctl(if_atsam_softc *sc, struct ifreq *ifr, u_long command)
|
|
{
|
|
if (sc->fixed_speed) {
|
|
return ifmedia_ioctl(sc->ifp, ifr, &sc->ifmedia, command);
|
|
} else {
|
|
struct mii_data *mii;
|
|
|
|
if (sc->miibus == NULL)
|
|
return (EINVAL);
|
|
|
|
mii = device_get_softc(sc->miibus);
|
|
return (ifmedia_ioctl(sc->ifp, ifr, &mii->mii_media, command));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Driver ioctl handler
|
|
*/
|
|
static int
|
|
if_atsam_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data)
|
|
{
|
|
if_atsam_softc *sc = (if_atsam_softc *)ifp->if_softc;
|
|
struct ifreq *ifr = (struct ifreq *)data;
|
|
int rv = 0;
|
|
|
|
switch (command) {
|
|
case SIOCGIFMEDIA:
|
|
case SIOCSIFMEDIA:
|
|
rv = if_atsam_mediaioctl(sc, ifr, command);
|
|
break;
|
|
case SIOCSIFFLAGS:
|
|
IF_ATSAM_LOCK(sc);
|
|
if (ifp->if_flags & IFF_UP) {
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
|
|
if ((ifp->if_flags ^ sc->if_flags) &
|
|
(IFF_PROMISC | IFF_ALLMULTI)) {
|
|
if_atsam_setup_rxfilter(sc);
|
|
}
|
|
} else {
|
|
if_atsam_start_locked(sc);
|
|
}
|
|
} else {
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
|
|
if_atsam_stop_locked(sc);
|
|
}
|
|
}
|
|
sc->if_flags = ifp->if_flags;
|
|
IF_ATSAM_UNLOCK(sc);
|
|
break;
|
|
case SIOCADDMULTI:
|
|
case SIOCDELMULTI:
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
|
|
IF_ATSAM_LOCK(sc);
|
|
if_atsam_setup_rxfilter(sc);
|
|
IF_ATSAM_UNLOCK(sc);
|
|
}
|
|
break;
|
|
default:
|
|
rv = ether_ioctl(ifp, command, data);
|
|
break;
|
|
}
|
|
return (rv);
|
|
}
|
|
|
|
/*
|
|
* Attach an SAMV71 driver to the system
|
|
*/
|
|
static int if_atsam_driver_attach(device_t dev)
|
|
{
|
|
if_atsam_softc *sc;
|
|
struct ifnet *ifp;
|
|
int unit;
|
|
uint8_t eaddr[ETHER_ADDR_LEN];
|
|
|
|
sc = device_get_softc(dev);
|
|
unit = device_get_unit(dev);
|
|
assert(unit == 0);
|
|
|
|
sc->dev = dev;
|
|
sc->ifp = ifp = if_alloc(IFT_ETHER);
|
|
|
|
mtx_init(&sc->mtx, device_get_nameunit(sc->dev), MTX_NETWORK_LOCK,
|
|
MTX_DEF);
|
|
|
|
rtems_bsd_if_atsam_get_if_media_props(device_get_name(sc->dev), unit,
|
|
&sc->fixed_speed, &sc->media, &sc->duplex);
|
|
rtems_bsd_get_mac_address(device_get_name(sc->dev), unit, eaddr);
|
|
|
|
sc->Gmac_inst.retries = MDIO_RETRIES;
|
|
|
|
memcpy(sc->GMacAddress, eaddr, ETHER_ADDR_LEN);
|
|
|
|
/* Set Initial Link Speed */
|
|
sc->link_speed = GMAC_SPEED_100M;
|
|
sc->link_duplex = GMAC_DUPLEX_FULL;
|
|
|
|
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_atsam_init_phy(&sc->Gmac_inst, BOARD_MCK, NULL, 0,
|
|
gmacPins, PIO_LISTSIZE(gmacPins));
|
|
|
|
/*
|
|
* MII Bus
|
|
*/
|
|
callout_init_mtx(&sc->tick_ch, &sc->mtx, CALLOUT_RETURNUNLOCKED);
|
|
if (!sc->fixed_speed) {
|
|
mii_attach(dev, &sc->miibus, ifp, if_atsam_mii_ifmedia_upd,
|
|
if_atsam_mii_ifmedia_sts, BMSR_DEFCAPMASK,
|
|
MDIO_PHY, MII_OFFSET_ANY, 0);
|
|
} else {
|
|
ifmedia_init(&sc->ifmedia, 0, if_atsam_media_change,
|
|
if_atsam_media_status);
|
|
ifmedia_add(&sc->ifmedia, IFM_ETHER | sc->media
|
|
| sc->duplex, 0, NULL);
|
|
ifmedia_set(&sc->ifmedia, IFM_ETHER | sc->media
|
|
| sc->duplex);
|
|
|
|
GMAC_SetLinkSpeed(sc->Gmac_inst.gGmacd.pHw,
|
|
if_atsam_get_gmac_linkspeed_from_media(sc->media),
|
|
if_atsam_get_gmac_duplex_from_media(sc->duplex));
|
|
|
|
if_link_state_change(sc->ifp, LINK_STATE_UP);
|
|
}
|
|
|
|
/*
|
|
* Set up network interface values
|
|
*/
|
|
ifp->if_softc = sc;
|
|
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
|
|
ifp->if_init = if_atsam_start;
|
|
ifp->if_ioctl = if_atsam_ioctl;
|
|
ifp->if_transmit = if_atsam_transmit;
|
|
ifp->if_qflush = if_qflush;
|
|
ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX;
|
|
ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6 |
|
|
IFCAP_VLAN_HWCSUM | IFCAP_VLAN_HWTAGGING;
|
|
ifp->if_capenable = ifp->if_capabilities;
|
|
ifp->if_hwassist = CSUM_IP | CSUM_IP_UDP | CSUM_IP_TCP |
|
|
CSUM_IP6_UDP | CSUM_IP6_TCP;
|
|
IFQ_SET_MAXLEN(&ifp->if_snd, TX_DESC_COUNT - 1);
|
|
ifp->if_snd.ifq_drv_maxlen = TX_DESC_COUNT - 1;
|
|
IFQ_SET_READY(&ifp->if_snd);
|
|
ifp->if_hdrlen = sizeof(struct ether_vlan_header);
|
|
|
|
/*
|
|
* Attach the interface
|
|
*/
|
|
ether_ifattach(ifp, eaddr);
|
|
|
|
if_atsam_add_sysctls(dev);
|
|
if_atsam_init(sc);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
if_atsam_probe(device_t dev)
|
|
{
|
|
int unit = device_get_unit(dev);
|
|
int error;
|
|
|
|
if (unit >= 0 && unit < NIFACES) {
|
|
error = BUS_PROBE_DEFAULT;
|
|
} else {
|
|
error = ENXIO;
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
|
|
static device_method_t if_atsam_methods[] = {
|
|
DEVMETHOD(device_probe, if_atsam_probe),
|
|
DEVMETHOD(device_attach, if_atsam_driver_attach),
|
|
DEVMETHOD(miibus_readreg, if_atsam_miibus_readreg),
|
|
DEVMETHOD(miibus_writereg, if_atsam_miibus_writereg),
|
|
DEVMETHOD(miibus_statchg, if_atsam_miibus_statchg),
|
|
DEVMETHOD_END
|
|
};
|
|
|
|
static driver_t if_atsam_nexus_driver = {
|
|
"if_atsam",
|
|
if_atsam_methods,
|
|
sizeof(struct if_atsam_softc)
|
|
};
|
|
|
|
static devclass_t if_atsam_devclass;
|
|
DRIVER_MODULE(if_atsam, nexus, if_atsam_nexus_driver, if_atsam_devclass, 0, 0);
|
|
MODULE_DEPEND(if_atsam, nexus, 1, 1, 1);
|
|
MODULE_DEPEND(if_atsam, ether, 1, 1, 1);
|
|
MODULE_DEPEND(if_atsam, miibus, 1, 1, 1);
|
|
DRIVER_MODULE(miibus, if_atsam, miibus_driver, miibus_devclass, NULL, NULL);
|
|
|
|
#endif /* LIBBSP_ARM_ATSAM_BSP_H */
|