This commit adds driver for the Onsemi NCV7410 10BASE-T1S Ethernet MAC-PHY. Signed-off-by: michal matias <mich4l.matias@gmail.com>
1662 lines
43 KiB
C
1662 lines
43 KiB
C
/****************************************************************************
|
|
* drivers/net/ncv7410.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <debug.h>
|
|
#include <errno.h>
|
|
|
|
#include <nuttx/spi/spi.h>
|
|
#include <sys/endian.h>
|
|
|
|
#include <nuttx/irq.h>
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/wqueue.h>
|
|
#include <nuttx/signal.h>
|
|
#include <nuttx/mutex.h>
|
|
#include <nuttx/net/ncv7410.h>
|
|
#include <nuttx/net/netdev_lowerhalf.h>
|
|
|
|
#include "ncv7410.h"
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define NCVWORK LPWORK
|
|
|
|
#define NCV_RESET_TRIES 5
|
|
|
|
/* Maximum frame size = (MTU + LL heaader size) + FCS size */
|
|
|
|
#define NCV_MAX_FRAME_SIZE(p) (p->dev.netdev.d_pktsize + 4)
|
|
|
|
/* Packet Memory ************************************************************/
|
|
|
|
/* Maximum number of allocated tx and rx packets */
|
|
|
|
#define NCV7410_TX_QUOTA 1
|
|
#define NCV7410_RX_QUOTA 2
|
|
|
|
#if CONFIG_IOB_NBUFFERS < (NCV7410_TX_QUOTA + NCV7410_RX_QUOTA)
|
|
# error "CONFIG_IOB_NBUFFERS must be > (NCV7410_TX_QUOTA + NCV7410_RX_QUOTA)"
|
|
#endif
|
|
|
|
#ifndef CONFIG_SCHED_LPWORK
|
|
# error "CONFIG_SCHED_LPWORK is needed by NCV7410 driver"
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
/* The ncv7410_driver_s encapsulates all state information for a single
|
|
* hardware interface
|
|
*/
|
|
|
|
enum ncv_ifstate_e
|
|
{
|
|
NCV_RESET,
|
|
NCV_INIT_DOWN,
|
|
NCV_INIT_UP
|
|
};
|
|
|
|
struct ncv7410_driver_s
|
|
{
|
|
struct netdev_lowerhalf_s dev; /* Driver data visible by the net stack
|
|
* (must be placed first) */
|
|
mutex_t lock; /* Lock for data race prevention */
|
|
FAR struct spi_dev_s *spi; /* The SPI device instance */
|
|
int irqnum; /* irq number of the interrupt pin */
|
|
struct ncv7410_config_s *config; /* NCV7410 configuration */
|
|
uint8_t ifstate; /* Driver state from ncv_ifstate_e enum */
|
|
|
|
struct work_s interrupt_work; /* wq handle for the interrupt work */
|
|
struct work_s io_work; /* wq handle for the io work */
|
|
|
|
int txc; /* TX credits */
|
|
int rca; /* RX chunks available */
|
|
|
|
FAR netpkt_t *tx_pkt; /* Pointer to the TX netpacket */
|
|
FAR netpkt_t *rx_pkt; /* Pointer to the RX netpacket */
|
|
int tx_pkt_idx; /* Position in the TX netpacket */
|
|
int rx_pkt_idx; /* Position in the RX netpacket */
|
|
int tx_pkt_len; /* Length of the TX packet */
|
|
bool rx_pkt_ready; /* RX packet ready to be received flag */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
/* Bit calculations */
|
|
|
|
static int ncv_get_parity(uint32_t word);
|
|
static uint8_t ncv_bitrev8(uint8_t byte);
|
|
|
|
/* SPI transfers */
|
|
|
|
static int ncv_write_reg(FAR struct ncv7410_driver_s *priv,
|
|
oa_regid_t regid, uint32_t word);
|
|
|
|
static int ncv_read_reg(FAR struct ncv7410_driver_s *priv,
|
|
oa_regid_t regid, FAR uint32_t *word);
|
|
|
|
static int ncv_set_clear_bits(FAR struct ncv7410_driver_s *priv,
|
|
oa_regid_t regid,
|
|
uint32_t setbits, uint32_t clearbits);
|
|
|
|
static int ncv_poll_footer(FAR struct ncv7410_driver_s *priv,
|
|
FAR uint32_t *footer);
|
|
|
|
static int ncv_exchange_chunk(FAR struct ncv7410_driver_s *priv,
|
|
FAR uint8_t *txbuf, FAR uint8_t *rxbuf,
|
|
uint32_t header, uint32_t *footer);
|
|
|
|
/* Interrupt handling */
|
|
|
|
static int ncv_interrupt(int irq, FAR void *context, FAR void *arg);
|
|
static void ncv_interrupt_work(FAR void *arg);
|
|
|
|
/* Data Transaction Protocol logic */
|
|
|
|
static void ncv_io_work(FAR void *arg);
|
|
static uint32_t ncv_prepare_chunk_exchange(FAR struct ncv7410_driver_s *priv,
|
|
FAR uint8_t *txbuf);
|
|
static bool ncv_can_rx(FAR struct ncv7410_driver_s *priv);
|
|
static void ncv_try_finish_tx_packet(FAR struct ncv7410_driver_s *priv);
|
|
static void ncv_handle_rx_chunk(FAR struct ncv7410_driver_s *priv,
|
|
uint32_t footer, FAR uint8_t *rxbuf);
|
|
static void ncv_finalize_rx_packet(FAR struct ncv7410_driver_s *priv);
|
|
static void ncv_release_tx_packet(FAR struct ncv7410_driver_s *priv);
|
|
static void ncv_release_rx_packet(FAR struct ncv7410_driver_s *priv);
|
|
|
|
/* SPI inline utility functions */
|
|
|
|
static inline void ncv_select_spi(FAR struct ncv7410_driver_s *priv);
|
|
static inline void ncv_deselect_spi(FAR struct ncv7410_driver_s *priv);
|
|
|
|
/* ncv7410 reset and configuration */
|
|
|
|
static int ncv_reset(FAR struct ncv7410_driver_s *priv);
|
|
static int ncv_config(FAR struct ncv7410_driver_s *priv);
|
|
static int ncv_enable(FAR struct ncv7410_driver_s *priv);
|
|
static int ncv_disable(FAR struct ncv7410_driver_s *priv);
|
|
static int ncv_init_mac_addr(FAR struct ncv7410_driver_s *priv);
|
|
|
|
/* Driver buffer manipulation */
|
|
|
|
static void ncv_reset_driver_buffers(FAR struct ncv7410_driver_s *priv);
|
|
|
|
/* NuttX callback functions */
|
|
|
|
static int ncv7410_ifup(FAR struct netdev_lowerhalf_s *dev);
|
|
static int ncv7410_ifdown(FAR struct netdev_lowerhalf_s *dev);
|
|
static int ncv7410_transmit(FAR struct netdev_lowerhalf_s *dev,
|
|
FAR netpkt_t *pkt);
|
|
static FAR netpkt_t *ncv7410_receive(FAR struct netdev_lowerhalf_s *dev);
|
|
|
|
/* Debug */
|
|
|
|
#ifdef CONFIG_DEBUG_NET_INFO
|
|
static void ncv_print_footer(uint32_t footer);
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct netdev_ops_s g_ncv7410_ops =
|
|
{
|
|
.ifup = ncv7410_ifup,
|
|
.ifdown = ncv7410_ifdown,
|
|
.transmit = ncv7410_transmit,
|
|
.receive = ncv7410_receive,
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_interrupt
|
|
*
|
|
* Description:
|
|
* Schedule interrupt work when the interrupt signal from MAC-PHY is
|
|
* received.
|
|
*
|
|
* Input Parameters:
|
|
* irq - not used
|
|
* context - not used
|
|
* arg - ncv7410_driver_s priv structure to be passed to the interrupt
|
|
* worker
|
|
*
|
|
* Returned Value:
|
|
* OK is always returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ncv_interrupt(int irq, FAR void *context, FAR void *arg)
|
|
{
|
|
FAR struct ncv7410_driver_s *priv = (FAR struct ncv7410_driver_s *)arg;
|
|
|
|
ninfo("NCV7410 interrupt!\n");
|
|
|
|
/* schedule interrupt work */
|
|
|
|
work_queue(NCVWORK, &priv->interrupt_work, ncv_interrupt_work, priv, 0);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_interrupt_work
|
|
*
|
|
* Description:
|
|
* Identify the interrupt source and perform necessary work.
|
|
*
|
|
* Input Parameters:
|
|
* arg - pointer to driver private data
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void ncv_interrupt_work(FAR void *arg)
|
|
{
|
|
FAR struct ncv7410_driver_s *priv = (FAR struct ncv7410_driver_s *)arg;
|
|
uint32_t footer;
|
|
|
|
nxmutex_lock(&priv->lock);
|
|
|
|
if (priv->ifstate != NCV_INIT_UP)
|
|
{
|
|
nxmutex_unlock(&priv->lock);
|
|
return;
|
|
}
|
|
|
|
ninfo("NCV7410 interrupt worker invoked!\n");
|
|
|
|
/* poll the data chunk footer */
|
|
|
|
if (ncv_poll_footer(priv, &footer))
|
|
{
|
|
nerr("Polling footer unsuccessful\n");
|
|
|
|
/* TODO: don't */
|
|
|
|
PANIC();
|
|
}
|
|
|
|
#ifdef CONFIG_DEBUG_NET_INFO
|
|
ncv_print_footer(footer);
|
|
#endif
|
|
|
|
/* if EXST in the footer, check enabled sources
|
|
* STATUS0, link-status in clause 22 phy registers
|
|
* (not yet implemented)
|
|
*/
|
|
|
|
/* update MAC-PHY buffer status */
|
|
|
|
priv->txc = oa_tx_credits(footer);
|
|
priv->rca = oa_rx_available(footer);
|
|
|
|
if ((priv->tx_pkt && priv->txc) || priv->rca)
|
|
{
|
|
/* schedule IO work */
|
|
|
|
work_queue(NCVWORK, &priv->io_work, ncv_io_work, priv, 0);
|
|
}
|
|
|
|
nxmutex_unlock(&priv->lock);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_io_work
|
|
*
|
|
* Description:
|
|
* Exchange data chunk with the MAC-PHY.
|
|
*
|
|
* Input Parameters:
|
|
* arg - pointer to driver private data
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void ncv_io_work(FAR void *arg)
|
|
{
|
|
FAR struct ncv7410_driver_s *priv = (FAR struct ncv7410_driver_s *)arg;
|
|
|
|
uint8_t txbuf[NCV_CHUNK_DEFAULT_PAYLOAD_SIZE];
|
|
uint8_t rxbuf[NCV_CHUNK_DEFAULT_PAYLOAD_SIZE];
|
|
|
|
uint32_t header;
|
|
uint32_t footer;
|
|
|
|
nxmutex_lock(&priv->lock);
|
|
|
|
if (priv->ifstate != NCV_INIT_UP)
|
|
{
|
|
nxmutex_unlock(&priv->lock);
|
|
return;
|
|
}
|
|
|
|
header = ncv_prepare_chunk_exchange(priv, txbuf);
|
|
|
|
/* Perform the SPI exchange */
|
|
|
|
if (ncv_exchange_chunk(priv, txbuf, rxbuf, header, &footer))
|
|
{
|
|
nerr("Error during chunk exchange\n");
|
|
|
|
/* TODO: do not panic, the best is probably to report the error
|
|
* and reset MAC to some defined state and reset driver
|
|
*/
|
|
|
|
PANIC();
|
|
}
|
|
|
|
ncv_try_finish_tx_packet(priv);
|
|
|
|
ncv_handle_rx_chunk(priv, footer, rxbuf);
|
|
|
|
/* schedule further work if needed */
|
|
|
|
if ((priv->tx_pkt && priv->txc) || priv->rca)
|
|
{
|
|
work_queue(NCVWORK, &priv->io_work, ncv_io_work, priv, 0);
|
|
}
|
|
|
|
nxmutex_unlock(&priv->lock);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_prepare_chunk_exchange
|
|
*
|
|
* Description:
|
|
* Determine whether there is data to transmit or receive.
|
|
* Set the appropriate header bitfields and fill the txbuf accordingly.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
* txbuf - pointer to the transmit chunk buffer
|
|
*
|
|
* Returned Value:
|
|
* Returns the prepared chunk header.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static uint32_t ncv_prepare_chunk_exchange(FAR struct ncv7410_driver_s *priv,
|
|
FAR uint8_t *txbuf)
|
|
{
|
|
uint32_t header = 0;
|
|
int txlen;
|
|
|
|
if (priv->tx_pkt && priv->txc)
|
|
{
|
|
header |= (1 << OA_DV_POS); /* Data Valid */
|
|
|
|
if (priv->tx_pkt_idx == 0)
|
|
{
|
|
header |= (1 << OA_SV_POS) /* Start Valid */
|
|
| (0 << OA_SWO_POS); /* Start Word Offset = 0 */
|
|
}
|
|
|
|
txlen = priv->tx_pkt_len - priv->tx_pkt_idx;
|
|
|
|
if (txlen <= NCV_CHUNK_DEFAULT_PAYLOAD_SIZE)
|
|
{
|
|
header |= (1 << OA_EV_POS) /* End Valid */
|
|
| ((txlen - 1) << OA_EBO_POS); /* End Byte Offset */
|
|
}
|
|
else
|
|
{
|
|
txlen = NCV_CHUNK_DEFAULT_PAYLOAD_SIZE;
|
|
}
|
|
|
|
/* copy data from network to txbuf */
|
|
|
|
netpkt_copyout(&priv->dev, txbuf, priv->tx_pkt,
|
|
txlen, priv->tx_pkt_idx);
|
|
priv->tx_pkt_idx += txlen;
|
|
}
|
|
|
|
if (ncv_can_rx(priv) == false)
|
|
{
|
|
header |= (1 << OA_NORX_POS); /* no rx */
|
|
}
|
|
|
|
return header;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_can_rx
|
|
*
|
|
* Description:
|
|
* Determine whether rx data is available and whether it can be received.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
*
|
|
* Returned Value:
|
|
* If it is possible to receive an rx chunk, true is returned,
|
|
* otherwise false is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static bool ncv_can_rx(FAR struct ncv7410_driver_s *priv)
|
|
{
|
|
if (!priv->rca)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (priv->rx_pkt_ready)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (priv->rx_pkt)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/* no RX packet, try to alloc */
|
|
|
|
priv->rx_pkt = netpkt_alloc(&priv->dev, NETPKT_RX);
|
|
if (priv->rx_pkt)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
ninfo("INFO: Failed to alloc rx netpkt\n");
|
|
|
|
/* there is no buffer for rx data */
|
|
|
|
return false;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_try_finish_tx_packet
|
|
*
|
|
* Description:
|
|
* Check whether the entire packet has been transmitted.
|
|
* If so, free the tx netpkt and notify the upperhalf.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void ncv_try_finish_tx_packet(FAR struct ncv7410_driver_s *priv)
|
|
{
|
|
if (priv->tx_pkt && (priv->tx_pkt_idx == priv->tx_pkt_len))
|
|
{
|
|
ncv_release_tx_packet(priv);
|
|
netdev_lower_txdone(&priv->dev);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_handle_rx_chunk
|
|
*
|
|
* Description:
|
|
* Parse the received footer, update buffer status and handle data
|
|
* in the rxbuf.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
* footer - the received footer
|
|
* rxbuf - pointer to the received data buffer
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void ncv_handle_rx_chunk(FAR struct ncv7410_driver_s *priv,
|
|
uint32_t footer, FAR uint8_t *rxbuf)
|
|
{
|
|
int rxlen;
|
|
int newlen;
|
|
|
|
/* update buffer status */
|
|
|
|
priv->txc = oa_tx_credits(footer);
|
|
priv->rca = oa_rx_available(footer);
|
|
|
|
/* check rx_pkt && !rx_pkt_ready,
|
|
* oa_data_valid flag might have been set due to an SPI error
|
|
*/
|
|
|
|
if (oa_data_valid(footer) && priv->rx_pkt && !priv->rx_pkt_ready)
|
|
{
|
|
if (oa_start_valid(footer))
|
|
{
|
|
priv->rx_pkt_idx = 0;
|
|
}
|
|
|
|
if (oa_end_valid(footer))
|
|
{
|
|
if (oa_frame_drop(footer))
|
|
{
|
|
ncv_release_rx_packet(priv);
|
|
return;
|
|
}
|
|
|
|
rxlen = oa_end_byte_offset(footer) + 1;
|
|
}
|
|
else
|
|
{
|
|
rxlen = NCV_CHUNK_DEFAULT_PAYLOAD_SIZE;
|
|
}
|
|
|
|
newlen = priv->rx_pkt_idx + rxlen;
|
|
|
|
if (newlen > NCV_MAX_FRAME_SIZE(priv))
|
|
{
|
|
nwarn("Dropping chunk of a packet that is too long");
|
|
|
|
/* set index so that a subsequent chunk with
|
|
* smaller payload won't pass
|
|
*/
|
|
|
|
priv->rx_pkt_idx = NCV_MAX_FRAME_SIZE(priv) + 1;
|
|
return;
|
|
}
|
|
|
|
netpkt_copyin(&priv->dev, priv->rx_pkt, rxbuf,
|
|
rxlen, priv->rx_pkt_idx);
|
|
priv->rx_pkt_idx = newlen;
|
|
|
|
if (oa_end_valid(footer))
|
|
{
|
|
/* finalize packet and notify the upper */
|
|
|
|
ncv_finalize_rx_packet(priv);
|
|
netdev_lower_rxready(&priv->dev);
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_finalize_rx_packet
|
|
*
|
|
* Description:
|
|
* Strip down last 4 bytes (FCS) from the rx packet and mark it ready.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void ncv_finalize_rx_packet(FAR struct ncv7410_driver_s *priv)
|
|
{
|
|
netpkt_setdatalen(&priv->dev, priv->rx_pkt,
|
|
netpkt_getdatalen(&priv->dev, priv->rx_pkt) - 4);
|
|
priv->rx_pkt_ready = true;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_release_tx_packet
|
|
*
|
|
* Description:
|
|
* Release the tx packet.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void ncv_release_tx_packet(FAR struct ncv7410_driver_s *priv)
|
|
{
|
|
netpkt_free(&priv->dev, priv->tx_pkt, NETPKT_TX);
|
|
priv->tx_pkt = NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_release_rx_packet
|
|
*
|
|
* Description:
|
|
* Release the rx packet.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void ncv_release_rx_packet(FAR struct ncv7410_driver_s *priv)
|
|
{
|
|
netpkt_free(&priv->dev, priv->rx_pkt, NETPKT_RX);
|
|
priv->rx_pkt = NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_get_parity
|
|
*
|
|
* Description:
|
|
* Obtain parity of a 32-bit word.
|
|
*
|
|
* Input Parameters:
|
|
* word - 32-bit word, subject to the parity calculation
|
|
*
|
|
* Returned Value:
|
|
* If the parity of the word is even, zero is returned.
|
|
* Otherwise one is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ncv_get_parity(uint32_t word)
|
|
{
|
|
/* www-graphics.stanford.edu/~seander/bithacks.html */
|
|
|
|
word ^= word >> 1;
|
|
word ^= word >> 2;
|
|
word = (word & 0x11111111u) * 0x11111111u;
|
|
return (word >> 28) & 1;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_bitrev8
|
|
*
|
|
* Description:
|
|
* Perform a bit reverse of a byte.
|
|
*
|
|
* Input Parameters:
|
|
* b - byte to be reversed
|
|
*
|
|
* Returned Value:
|
|
* Byte with reversed bits is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static uint8_t ncv_bitrev8(uint8_t byte)
|
|
{
|
|
/* https://stackoverflow.com/a/2602885 */
|
|
|
|
byte = (byte & 0xf0) >> 4 | (byte & 0x0f) << 4;
|
|
byte = (byte & 0xcc) >> 2 | (byte & 0x33) << 2;
|
|
byte = (byte & 0xaa) >> 1 | (byte & 0x55) << 1;
|
|
return byte;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_(select/deselect)_spi
|
|
*
|
|
* Description:
|
|
* Helper functions to setup SPI hardware.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline void ncv_select_spi(FAR struct ncv7410_driver_s *priv)
|
|
{
|
|
SPI_LOCK(priv->spi, true);
|
|
|
|
SPI_SETMODE(priv->spi, OA_SPI_MODE);
|
|
SPI_SETBITS(priv->spi, OA_SPI_NBITS);
|
|
SPI_HWFEATURES(priv->spi, 0); /* disable HW features */
|
|
SPI_SETFREQUENCY(priv->spi, CONFIG_NCV7410_FREQUENCY);
|
|
|
|
SPI_SELECT(priv->spi, priv->config->id, true);
|
|
}
|
|
|
|
static inline void ncv_deselect_spi(FAR struct ncv7410_driver_s *priv)
|
|
{
|
|
SPI_SELECT(priv->spi, priv->config->id, false);
|
|
|
|
SPI_LOCK(priv->spi, false);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_write_reg
|
|
*
|
|
* Description:
|
|
* Write to a MAC-PHY register.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
* regid - Register id encapsulating MMS and ADDR
|
|
* word - 32-bit word to be written to the register
|
|
*
|
|
* Returned Value:
|
|
* On a successful transaction OK is returned, otherwise ERROR is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ncv_write_reg(FAR struct ncv7410_driver_s *priv,
|
|
oa_regid_t regid, uint32_t word)
|
|
{
|
|
uint32_t txdata[3];
|
|
uint32_t rxdata[3];
|
|
uint8_t mms = OA_REGID_GET_MMS(regid);
|
|
uint16_t addr = OA_REGID_GET_ADDR(regid);
|
|
|
|
/* prepare header */
|
|
|
|
uint32_t header = (1 << OA_WNR_POS) /* Write Not Read */
|
|
| (mms << OA_MMS_POS)
|
|
| (addr << OA_ADDR_POS);
|
|
int parity = ncv_get_parity(header);
|
|
header |= parity ? 0 : OA_P_MASK; /* make header odd parity */
|
|
|
|
/* convert to big endian */
|
|
|
|
header = htobe32(header);
|
|
word = htobe32(word);
|
|
|
|
/* prepare exchange */
|
|
|
|
txdata[0] = header;
|
|
txdata[1] = word;
|
|
|
|
ncv_select_spi(priv);
|
|
SPI_EXCHANGE(priv->spi, txdata, rxdata, 12);
|
|
ncv_deselect_spi(priv);
|
|
if (rxdata[1] != header)
|
|
{
|
|
nerr("Error writing register\n");
|
|
return ERROR;
|
|
}
|
|
|
|
ninfo("Writing register OK\n");
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_read_reg
|
|
*
|
|
* Description:
|
|
* Read a MAC-PHY register.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
* regid - register id encapsulating MMS and ADDR
|
|
* word - pointer to a 32-bit destination variable
|
|
*
|
|
* Returned Value:
|
|
* On successful transaction OK is returned, otherwise ERROR is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ncv_read_reg(FAR struct ncv7410_driver_s *priv,
|
|
oa_regid_t regid, FAR uint32_t *word)
|
|
{
|
|
uint32_t txdata[3];
|
|
uint32_t rxdata[3];
|
|
uint8_t mms = OA_REGID_GET_MMS(regid);
|
|
uint16_t addr = OA_REGID_GET_ADDR(regid);
|
|
int parity;
|
|
uint32_t header;
|
|
|
|
/* prepare header */
|
|
|
|
header = (mms << OA_MMS_POS)
|
|
| (addr << OA_ADDR_POS);
|
|
parity = ncv_get_parity(header);
|
|
header |= parity ? 0 : OA_P_MASK; /* make header odd parity */
|
|
|
|
/* convert to big endian */
|
|
|
|
header = htobe32(header);
|
|
|
|
/* prepare exchange */
|
|
|
|
txdata[0] = header;
|
|
|
|
ncv_select_spi(priv);
|
|
SPI_EXCHANGE(priv->spi, txdata, rxdata, 12);
|
|
ncv_deselect_spi(priv);
|
|
|
|
*word = be32toh(rxdata[2]);
|
|
if (rxdata[1] != header)
|
|
{
|
|
nerr("Error reading register\n");
|
|
return ERROR;
|
|
}
|
|
|
|
ninfo("Reading register OK\n");
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_set_clear_bits
|
|
*
|
|
* Description:
|
|
* Perform a read-modify-write operation on a given register
|
|
* while setting bits from the setbits argument and clearing bits from
|
|
* the clearbits argument.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
* regid - register id of the register to be modified
|
|
* setbits - bits set to one will be set in the register
|
|
* clearbits - bits set to one will be cleared in the register
|
|
*
|
|
* Returned Value:
|
|
* On a successful transaction OK is returned, otherwise ERROR is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ncv_set_clear_bits(FAR struct ncv7410_driver_s *priv,
|
|
oa_regid_t regid,
|
|
uint32_t setbits, uint32_t clearbits)
|
|
{
|
|
uint32_t regval;
|
|
|
|
if (ncv_read_reg(priv, regid, ®val))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
regval |= setbits;
|
|
regval &= ~clearbits;
|
|
|
|
if (ncv_write_reg(priv, regid, regval))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_exchange_chunk
|
|
*
|
|
* Description:
|
|
* Send a data chunk to MAC-PHY and simultaneously receive chunk.
|
|
*
|
|
* Computing header parity, checking footer parity, converting to proper
|
|
* endianness and setting DNC flag is done by this function.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
* txbuf - buffer with transmit chunk data
|
|
* rxbuf - buffer to save the received chunk to
|
|
* header - header controlling the transaction
|
|
* footer - pointer to a 32-bit value for the footer
|
|
*
|
|
* Returned Value:
|
|
* On a successful transaction OK is returned, otherwise ERROR is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ncv_exchange_chunk(FAR struct ncv7410_driver_s *priv,
|
|
FAR uint8_t *txbuf, FAR uint8_t *rxbuf,
|
|
uint32_t header, uint32_t *footer)
|
|
{
|
|
header |= (1 << OA_DNC_POS);
|
|
header |= (!ncv_get_parity(header) << OA_P_POS);
|
|
header = htobe32(header);
|
|
|
|
ncv_select_spi(priv);
|
|
|
|
/* this depends on SW Chip Select */
|
|
|
|
SPI_EXCHANGE(priv->spi, (uint8_t *) &header, rxbuf, 4);
|
|
SPI_EXCHANGE(priv->spi, txbuf,
|
|
&rxbuf[4], NCV_CHUNK_DEFAULT_PAYLOAD_SIZE - 4);
|
|
SPI_EXCHANGE(priv->spi, &txbuf[NCV_CHUNK_DEFAULT_PAYLOAD_SIZE - 4],
|
|
(uint8_t *)footer, 4);
|
|
ncv_deselect_spi(priv);
|
|
|
|
*footer = be32toh(*footer);
|
|
if (!ncv_get_parity(*footer))
|
|
{
|
|
nerr("Wrong parity in the footer\n");
|
|
return ERROR;
|
|
}
|
|
|
|
if (oa_header_bad(*footer))
|
|
{
|
|
nerr("HDRB set in the footer\n");
|
|
return ERROR;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_poll_footer
|
|
*
|
|
* Description:
|
|
* Poll a data transaction chunk footer.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
* footer - pointer to a 32-bit footer destination variable
|
|
*
|
|
* Returned Value:
|
|
* On a successful transaction OK is returned, otherwise ERROR is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ncv_poll_footer(FAR struct ncv7410_driver_s *priv,
|
|
FAR uint32_t *footer)
|
|
{
|
|
uint8_t txdata[NCV_CHUNK_DEFAULT_PAYLOAD_SIZE];
|
|
uint8_t rxdata[NCV_CHUNK_DEFAULT_PAYLOAD_SIZE];
|
|
uint32_t header;
|
|
|
|
header = (1 << OA_DNC_POS) /* Data Not Control */
|
|
| (1 << OA_NORX_POS); /* No Read */
|
|
|
|
if (ncv_exchange_chunk(priv, txdata, rxdata, header, footer))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_reset
|
|
*
|
|
* Description:
|
|
* Perform SW reset of the MAC-PHY.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
*
|
|
* Returned Value:
|
|
* On a successful reset OK is returned, otherwise ERROR is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ncv_reset(FAR struct ncv7410_driver_s *priv)
|
|
{
|
|
int tries = NCV_RESET_TRIES;
|
|
uint32_t regval = (1 << OA_RESET_SWRESET_POS);
|
|
|
|
if (ncv_write_reg(priv, OA_RESET_REGID, regval))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
/* check whether the RESET bit cleared itself */
|
|
|
|
do
|
|
{
|
|
if (ncv_read_reg(priv, OA_RESET_REGID, ®val))
|
|
{
|
|
return ERROR;
|
|
}
|
|
}
|
|
while (tries-- && (regval & OA_RESET_SWRESET_MASK));
|
|
|
|
if (regval & OA_RESET_SWRESET_MASK)
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
/* check whether the reset complete flag is set */
|
|
|
|
tries = NCV_RESET_TRIES;
|
|
|
|
do
|
|
{
|
|
if (ncv_read_reg(priv, OA_STATUS0_REGID, ®val))
|
|
{
|
|
return ERROR;
|
|
}
|
|
}
|
|
while (tries-- && !(regval & OA_STATUS0_RESETC_MASK));
|
|
|
|
if (!(regval & OA_STATUS0_RESETC_MASK))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
/* clear HDRE in STATUS0 (due to a bug in NCV7410) */
|
|
|
|
if (ncv_write_reg(priv, OA_STATUS0_REGID, (1 << OA_STATUS0_HDRE_POS)))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
/* clear reset complete flag */
|
|
|
|
if (ncv_write_reg(priv, OA_STATUS0_REGID, (1 << OA_STATUS0_RESETC_POS)))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
/* blink with LEDs for debugging purposes */
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
regval = 0x0302;
|
|
if (ncv_write_reg(priv, NCV_DIO_CONFIG_REGID, regval))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
nxsig_usleep(250000);
|
|
regval = 0x0203;
|
|
if (ncv_write_reg(priv, NCV_DIO_CONFIG_REGID, regval))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
nxsig_usleep(250000);
|
|
}
|
|
|
|
/* set DIOs to default */
|
|
|
|
regval = NCV_DIO_CONFIG_DEF;
|
|
if (ncv_write_reg(priv, NCV_DIO_CONFIG_REGID, regval))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_config
|
|
*
|
|
* Description:
|
|
* Configure the MAC-PHY into promiscuous mode and set the SYNC flag.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
*
|
|
* Returned Value:
|
|
* On success OK is returned, otherwise ERROR is returned.
|
|
*
|
|
* Assumptions:
|
|
* The function is called after the MAC address is initialized.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ncv_config(FAR struct ncv7410_driver_s *priv)
|
|
{
|
|
uint32_t regval;
|
|
#ifndef CONFIG_NET_PROMISCUOUS
|
|
uint8_t *mac = priv->dev.netdev.d_mac.ether.ether_addr_octet;
|
|
#endif
|
|
|
|
ninfo("Configuring NCV7410\n");
|
|
|
|
/* setup LEDs DIO0: txrx blink
|
|
* DIO1: link enabled and link status up
|
|
*/
|
|
|
|
regval = (NCV_DIO_TXRX_FUNC << NCV_DIO0_FUNC_POS)
|
|
| (NCV_DIO_LINK_CTRL_FUNC << NCV_DIO1_FUNC_POS)
|
|
| (1 << NCV_DIO0_OUT_VAL_POS)
|
|
| (1 << NCV_DIO1_OUT_VAL_POS);
|
|
|
|
if (ncv_write_reg(priv, NCV_DIO_CONFIG_REGID, regval))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
/* enable MAC TX, RX, enable transmit FCS computation on MAC,
|
|
* enable MAC address filtering
|
|
*/
|
|
|
|
regval = (1 << NCV_MAC_CONTROL0_FCSA_POS)
|
|
| (1 << NCV_MAC_CONTROL0_TXEN_POS)
|
|
| (1 << NCV_MAC_CONTROL0_RXEN_POS)
|
|
| (1 << NCV_MAC_CONTROL0_ADRF_POS);
|
|
|
|
#ifdef CONFIG_NET_PROMISCUOUS
|
|
/* disable MAC address filtering */
|
|
|
|
regval &= ~(1 << NCV_MAC_CONTROL0_ADRF_POS);
|
|
#endif
|
|
|
|
if (ncv_write_reg(priv, NCV_MAC_CONTROL0_REGID, regval))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
#ifndef CONFIG_NET_PROMISCUOUS
|
|
/* setup MAC address filter */
|
|
|
|
regval = (mac[2] << 24)
|
|
| (mac[3] << 16)
|
|
| (mac[4] << 8)
|
|
| (mac[5]);
|
|
|
|
if (ncv_write_reg(priv, NCV_ADDRFILT0L_REGID, regval))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
regval = (1 << 31) /* enable filter */
|
|
| (mac[0] << 8)
|
|
| (mac[1]);
|
|
|
|
if (ncv_write_reg(priv, NCV_ADDRFILT0H_REGID, regval))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
regval = 0xffffffff;
|
|
|
|
if (ncv_write_reg(priv, NCV_ADDRMASK0L_REGID, regval))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
regval = 0x0000ffff;
|
|
|
|
if (ncv_write_reg(priv, NCV_ADDRMASK0H_REGID, regval))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* enable rx buffer overflow interrupt */
|
|
|
|
regval = OA_IMSK0_DEF & ~(1 << OA_IMSK0_RXBOEM_POS);
|
|
|
|
if (ncv_write_reg(priv, OA_IMSK0_REGID, regval))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
/* setup SPI protocol and set SYNC flag */
|
|
|
|
regval = (1 << OA_CONFIG0_SYNC_POS)
|
|
| (1 << OA_CONFIG0_CSARFE_POS)
|
|
| (1 << OA_CONFIG0_ZARFE_POS)
|
|
| (1 << OA_CONFIG0_RXCTE_POS) /* a bit lower latency */
|
|
| (3 << OA_CONFIG0_TXCTHRESH_POS)
|
|
| (6 << OA_CONFIG0_CPS_POS);
|
|
|
|
if (ncv_write_reg(priv, OA_CONFIG0_REGID, regval))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_enable
|
|
*
|
|
* Description:
|
|
* Enable TX and RX on the MAC-PHY.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
*
|
|
* Returned Value:
|
|
* On success OK is returned, otherwise ERROR is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ncv_enable(FAR struct ncv7410_driver_s *priv)
|
|
{
|
|
/* enable PHY */
|
|
|
|
uint32_t setbits;
|
|
|
|
ninfo("Enabling NCV7410\n");
|
|
|
|
/* enable RX and TX in PHY */
|
|
|
|
setbits = (1 << OA_PHY_CONTROL_LCTL_POS);
|
|
|
|
if (ncv_set_clear_bits(priv, OA_PHY_CONTROL_REGID, setbits, 0))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
/* enable PHY interrupt */
|
|
|
|
setbits = (1 << OA_IMSK0_PHYINTM_POS);
|
|
|
|
if (ncv_set_clear_bits(priv, OA_IMSK0_REGID, setbits, 0))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_disable
|
|
*
|
|
* Description:
|
|
* Disable TX and RX on the MAC-PHY.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
*
|
|
* Returned Value:
|
|
* On success OK is returned, otherwise ERROR is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ncv_disable(FAR struct ncv7410_driver_s *priv)
|
|
{
|
|
/* disable PHY */
|
|
|
|
uint32_t clearbits;
|
|
|
|
ninfo("Disabling NCV7410\n");
|
|
|
|
/* disable PHY interrupt */
|
|
|
|
clearbits = (1 << OA_IMSK0_PHYINTM_POS);
|
|
|
|
if (ncv_set_clear_bits(priv, OA_IMSK0_REGID, 0, clearbits))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
/* disable RX and TX in PHY */
|
|
|
|
clearbits = (1 << OA_PHY_CONTROL_LCTL_POS);
|
|
|
|
if (ncv_set_clear_bits(priv, OA_PHY_CONTROL_REGID, 0, clearbits))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_init_mac_addr
|
|
*
|
|
* Description:
|
|
* Read the MAC-PHY's factory-assigned MAC address and copy it into
|
|
* the network device state structure.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
*
|
|
* Returned Value:
|
|
* On success OK is returned, otherwise ERROR is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ncv_init_mac_addr(FAR struct ncv7410_driver_s *priv)
|
|
{
|
|
uint32_t regval;
|
|
uint8_t mac[6];
|
|
|
|
if (ncv_read_reg(priv, OA_PHYID_REGID, ®val))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
mac[0] = ncv_bitrev8(regval >> 26);
|
|
mac[1] = ncv_bitrev8(regval >> 18);
|
|
mac[2] = ncv_bitrev8(regval >> 10);
|
|
|
|
if (ncv_read_reg(priv, NCV_MACID1_REGID, ®val))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
mac[3] = regval;
|
|
|
|
if (ncv_read_reg(priv, NCV_MACID0_REGID, ®val))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
mac[4] = regval >> 8;
|
|
mac[5] = regval;
|
|
|
|
memcpy(&priv->dev.netdev.d_mac.ether, &mac, sizeof(struct ether_addr));
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_reset_driver_buffers
|
|
*
|
|
* Description:
|
|
* If allocated, release both tx and rx netpackets and reset buffer status
|
|
* to the default.
|
|
*
|
|
* Input Parameters:
|
|
* priv - pointer to the driver-specific state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void ncv_reset_driver_buffers(FAR struct ncv7410_driver_s *priv)
|
|
{
|
|
priv->txc = 0;
|
|
priv->rca = 0;
|
|
|
|
if (priv->tx_pkt)
|
|
{
|
|
ncv_release_tx_packet(priv);
|
|
}
|
|
|
|
if (priv->rx_pkt)
|
|
{
|
|
ncv_release_rx_packet(priv);
|
|
}
|
|
|
|
priv->tx_pkt_idx = 0;
|
|
priv->rx_pkt_idx = 0;
|
|
priv->tx_pkt_len = 0;
|
|
priv->rx_pkt_ready = false;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv_print_footer
|
|
*
|
|
* Description:
|
|
* print individual bitfield of a receive chunk footer
|
|
*
|
|
* Input Parameters:
|
|
* None
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_DEBUG_NET_INFO
|
|
static void ncv_print_footer(uint32_t footer)
|
|
{
|
|
ninfo("Footer:\n");
|
|
ninfo(" EXST: %d\n", oa_ext_status(footer));
|
|
ninfo(" HDRB: %d\n", oa_header_bad(footer));
|
|
ninfo(" SYNC: %d\n", oa_mac_phy_sync(footer));
|
|
ninfo(" RCA: %d\n", oa_rx_available(footer));
|
|
ninfo(" DV: %d\n", oa_data_valid(footer));
|
|
ninfo(" SV: %d\n", oa_start_valid(footer));
|
|
ninfo(" SWO: %d\n", oa_start_word_offset(footer));
|
|
ninfo(" FD: %d\n", oa_frame_drop(footer));
|
|
ninfo(" EV: %d\n", oa_end_valid(footer));
|
|
ninfo(" EBO: %d\n", oa_end_byte_offset(footer));
|
|
ninfo(" RTSA: %d\n", oa_rx_frame_timestamp_added(footer));
|
|
ninfo(" RTSP: %d\n", oa_rx_frame_timestamp_parity(footer));
|
|
ninfo(" TXC: %d\n", oa_tx_credits(footer));
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Netdev upperhalf callbacks
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: ncv7410_ifup
|
|
*
|
|
* Description:
|
|
* NuttX callback: Bring up the Ethernet interface
|
|
*
|
|
* Input Parameters:
|
|
* dev - reference to the NuttX driver state structure
|
|
*
|
|
* Returned Values:
|
|
* On success OK is returned, otherwise negated errno is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ncv7410_ifup(FAR struct netdev_lowerhalf_s *dev)
|
|
{
|
|
FAR struct ncv7410_driver_s *priv = (FAR struct ncv7410_driver_s *)dev;
|
|
|
|
if (priv->ifstate == NCV_INIT_UP)
|
|
{
|
|
nerr("Tried to bring NCV7410 interface up when already up\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ninfo("Bringing up NCV7410\n");
|
|
|
|
if (priv->ifstate == NCV_RESET)
|
|
{
|
|
if (ncv_config(priv) == ERROR)
|
|
{
|
|
nerr("Error configuring NCV7410\n");
|
|
return -EIO;
|
|
}
|
|
|
|
priv->ifstate = NCV_INIT_DOWN;
|
|
}
|
|
|
|
/* set NCV_INIT_UP prior to enabling to allow ncv_interrupt_work right
|
|
* after MAC-PHY enable
|
|
*/
|
|
|
|
priv->ifstate = NCV_INIT_UP;
|
|
|
|
if (ncv_enable(priv) == ERROR)
|
|
{
|
|
nerr("Error enabling NCV7410\n");
|
|
priv->ifstate = NCV_INIT_DOWN;
|
|
return -EIO;
|
|
}
|
|
|
|
/* schedule interrupt work to initialize txc and rca */
|
|
|
|
work_queue(NCVWORK, &priv->interrupt_work, ncv_interrupt_work, priv, 0);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv7410_ifdown
|
|
*
|
|
* Description:
|
|
* NuttX callback: Shut down the Ethernet interface.
|
|
*
|
|
* Input Parameters:
|
|
* dev - reference to the NuttX driver state structure
|
|
*
|
|
* Returned Values:
|
|
* On success OK is returned, otherwise negated errno is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ncv7410_ifdown(FAR struct netdev_lowerhalf_s *dev)
|
|
{
|
|
FAR struct ncv7410_driver_s *priv = (FAR struct ncv7410_driver_s *)dev;
|
|
|
|
nxmutex_lock(&priv->lock);
|
|
|
|
if (priv->ifstate != NCV_INIT_UP)
|
|
{
|
|
nxmutex_unlock(&priv->lock);
|
|
nerr("Tried to bring the NCV7410 interface down but it is not up\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
work_cancel(NCVWORK, &priv->interrupt_work);
|
|
work_cancel(NCVWORK, &priv->io_work);
|
|
|
|
if (ncv_disable(priv) == ERROR)
|
|
{
|
|
nxmutex_unlock(&priv->lock);
|
|
nerr("Error disabling NCV7410\n");
|
|
return -EIO;
|
|
}
|
|
|
|
ncv_reset_driver_buffers(priv);
|
|
|
|
priv->ifstate = NCV_INIT_DOWN;
|
|
|
|
nxmutex_unlock(&priv->lock);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv7410_transmit
|
|
*
|
|
* Description:
|
|
* NuttX callback: Transmit the given packet.
|
|
*
|
|
* Input Parameters:
|
|
* dev - reference to the NuttX driver state structure
|
|
* pkt - network packet to be transmitted
|
|
*
|
|
* Returned Values:
|
|
* On success OK is returned, otherwise negated errno is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int ncv7410_transmit(FAR struct netdev_lowerhalf_s *dev,
|
|
FAR netpkt_t *pkt)
|
|
{
|
|
FAR struct ncv7410_driver_s *priv = (FAR struct ncv7410_driver_s *)dev;
|
|
|
|
nxmutex_lock(&priv->lock);
|
|
|
|
if (priv->tx_pkt || priv->ifstate != NCV_INIT_UP)
|
|
{
|
|
/* previous tx packet was not yet sent to the network
|
|
* or the interface was shut down while waiting for the lock
|
|
*/
|
|
|
|
nxmutex_unlock(&priv->lock);
|
|
return -EAGAIN;
|
|
}
|
|
|
|
priv->tx_pkt_idx = 0;
|
|
priv->tx_pkt_len = netpkt_getdatalen(dev, pkt);
|
|
priv->tx_pkt = pkt;
|
|
|
|
nxmutex_unlock(&priv->lock);
|
|
|
|
work_queue(NCVWORK, &priv->io_work, ncv_io_work, priv, 0);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ncv7410_receive
|
|
*
|
|
* Description:
|
|
* NuttX callback: Claims an rx packet if available.
|
|
*
|
|
* Input Parameters:
|
|
* dev - reference to the NuttX driver state structure
|
|
*
|
|
* Returned Values:
|
|
* If the rx packet is ready, its pointer is returned.
|
|
* NULL is returned otherwise.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static FAR netpkt_t *ncv7410_receive(FAR struct netdev_lowerhalf_s *dev)
|
|
{
|
|
FAR struct ncv7410_driver_s *priv = (FAR struct ncv7410_driver_s *)dev;
|
|
|
|
nxmutex_lock(&priv->lock);
|
|
|
|
if (priv->rx_pkt_ready)
|
|
{
|
|
netpkt_t *retval = priv->rx_pkt;
|
|
priv->rx_pkt_ready = false;
|
|
priv->rx_pkt = NULL;
|
|
nxmutex_unlock(&priv->lock);
|
|
return retval;
|
|
}
|
|
|
|
nxmutex_unlock(&priv->lock);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: ncv7410_initialize
|
|
*
|
|
* Description:
|
|
* Initialize the Ethernet driver.
|
|
*
|
|
* Input Parameters:
|
|
* spi - reference to the SPI driver state data
|
|
* irq - irq number of the pin connected to MAC-PHY's interrupt signal
|
|
*
|
|
* Returned Value:
|
|
* On success OK is returned, otherwise negated errno is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int ncv7410_initialize(FAR struct spi_dev_s *spi, int irq,
|
|
struct ncv7410_config_s *config)
|
|
{
|
|
FAR struct ncv7410_driver_s *priv = NULL;
|
|
FAR struct netdev_lowerhalf_s *netdev = NULL;
|
|
int retval;
|
|
|
|
/* Allocate the interface structure */
|
|
|
|
priv = kmm_zalloc(sizeof(*priv));
|
|
if (priv == NULL)
|
|
{
|
|
nerr("Could not allocate data for ncv7410 priv\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
priv->spi = spi; /* Save the SPI instance */
|
|
priv->irqnum = irq; /* Save the Interrupt Request Number */
|
|
priv->config = config; /* Save the reference to the configuration */
|
|
|
|
/* Reset NCV7410 chip */
|
|
|
|
if (ncv_reset(priv))
|
|
{
|
|
nerr("Error resetting NCV7410\n");
|
|
retval = -EIO;
|
|
goto errout;
|
|
}
|
|
|
|
priv->ifstate = NCV_RESET;
|
|
ninfo("Resetting NCV7410 OK\n");
|
|
|
|
if (ncv_init_mac_addr(priv))
|
|
{
|
|
nerr("Error initializing NCV7410 MAC address\n");
|
|
retval = -EIO;
|
|
goto errout;
|
|
}
|
|
|
|
ninfo("Initializing MAC address OK\n");
|
|
|
|
/* Attach ISR */
|
|
|
|
irq_attach(priv->irqnum, ncv_interrupt, priv);
|
|
|
|
/* Init lock */
|
|
|
|
nxmutex_init(&priv->lock);
|
|
|
|
/* Register the device with the OS */
|
|
|
|
netdev = &priv->dev;
|
|
netdev->quota[NETPKT_TX] = NCV7410_TX_QUOTA;
|
|
netdev->quota[NETPKT_RX] = NCV7410_RX_QUOTA;
|
|
netdev->ops = &g_ncv7410_ops;
|
|
|
|
retval = netdev_lower_register(netdev, NET_LL_ETHERNET);
|
|
if (retval == OK)
|
|
{
|
|
ninfo("Successfully registered NCV7410 network driver\n");
|
|
return OK;
|
|
}
|
|
|
|
nerr("Error registering NCV7410 network driver: %d\n", retval);
|
|
|
|
errout:
|
|
kmm_free(priv);
|
|
return retval;
|
|
}
|