STM32 F4 Ethernet driver is fully functional
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4166 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
parent
041d1e6191
commit
fcd24b1c21
@ -2255,3 +2255,6 @@
|
||||
* arch/arm/srcm/stm32/stm32_eth.c: Adds an Ethernet driver for the STM32 F4.
|
||||
* arch/arm/srcm/stm32/stm32_dac.c and stm32_adc.c: "Skeleton" files for STM32
|
||||
DAC and ADC drivers. The actual logic will come later.
|
||||
* arch/arm/srcm/stm32/stm32_eth.c: There may be a few more lurking bugs, but
|
||||
the STM32 Ethernet driver appears to be fully functional on the STM3240G-EVAL.
|
||||
|
||||
|
@ -453,14 +453,17 @@
|
||||
#endif
|
||||
|
||||
/* Interrupt bit sets *******************************************************/
|
||||
/* All interrupts in the normal and abnormal interrupt summary */
|
||||
/* All interrupts in the normal and abnormal interrupt summary. Early transmit
|
||||
* interrupt (ETI) is excluded from the abnormal set because it causes too
|
||||
* many interrupts and is not interesting.
|
||||
*/
|
||||
|
||||
#define ETH_DMAINT_NORMAL \
|
||||
(ETH_DMAINT_TI | ETH_DMAINT_TBUI |ETH_DMAINT_RI | ETH_DMAINT_ERI)
|
||||
|
||||
#define ETH_DMAINT_ABNORMAL \
|
||||
(ETH_DMAINT_TPSI | ETH_DMAINT_TJTI | ETH_DMAINT_ROI | ETH_DMAINT_TUI | \
|
||||
ETH_DMAINT_RBUI | ETH_DMAINT_RPSI | ETH_DMAINT_RWTI | ETH_DMAINT_ETI | \
|
||||
ETH_DMAINT_RBUI | ETH_DMAINT_RPSI | ETH_DMAINT_RWTI | /* ETH_DMAINT_ETI | */ \
|
||||
ETH_DMAINT_FBEI)
|
||||
|
||||
/* Normal receive, transmit, error interrupt enable bit sets */
|
||||
@ -540,8 +543,8 @@ static uint32_t stm32_getreg(uint32_t addr);
|
||||
static void stm32_putreg(uint32_t val, uint32_t addr);
|
||||
static void stm32_checksetup(void);
|
||||
#else
|
||||
# define stm32_getreg(addr) getreg16(addr)
|
||||
# define stm32_putreg(val,addr) putreg16(val,addr)
|
||||
# define stm32_getreg(addr) getreg32(addr)
|
||||
# define stm32_putreg(val,addr) putreg32(val,addr)
|
||||
# define stm32_checksetup()
|
||||
#endif
|
||||
|
||||
@ -556,6 +559,7 @@ static inline bool stm32_isfreebuffer(FAR struct stm32_ethmac_s *priv);
|
||||
|
||||
static int stm32_transmit(FAR struct stm32_ethmac_s *priv);
|
||||
static int stm32_uiptxpoll(struct uip_driver_s *dev);
|
||||
static void stm32_dopoll(FAR struct stm32_ethmac_s *priv);
|
||||
|
||||
/* Interrupt handling */
|
||||
|
||||
@ -863,6 +867,7 @@ static inline bool stm32_isfreebuffer(FAR struct stm32_ethmac_s *priv)
|
||||
static int stm32_transmit(FAR struct stm32_ethmac_s *priv)
|
||||
{
|
||||
struct eth_txdesc_s *txdesc;
|
||||
struct eth_txdesc_s *txfirst;
|
||||
uint32_t regval;
|
||||
|
||||
/* The internal uIP buffer size may be configured to be larger than the
|
||||
@ -870,7 +875,6 @@ static int stm32_transmit(FAR struct stm32_ethmac_s *priv)
|
||||
*/
|
||||
|
||||
#if CONFIG_NET_BUFSIZE > CONFIG_STM32_ETH_BUFSIZE
|
||||
struct eth_txdesc_s *txnext;
|
||||
uint8_t *buffer;
|
||||
int bufcount;
|
||||
int lastsize;
|
||||
@ -882,8 +886,11 @@ static int stm32_transmit(FAR struct stm32_ethmac_s *priv)
|
||||
* must have assured that there is no transmission in progress.
|
||||
*/
|
||||
|
||||
txdesc = priv->txhead;
|
||||
nllvdbg("d_len: %d txhead: %08x tdes0: %08x\n", txdesc, txdesc->tdes0);
|
||||
txdesc = priv->txhead;
|
||||
txfirst = txdesc;
|
||||
|
||||
nllvdbg("d_len: %d d_buf: %p txhead: %p tdes0: %08x\n",
|
||||
priv->dev.d_len, priv->dev.d_buf, txdesc, txdesc->tdes0);
|
||||
|
||||
DEBUGASSERT(txdesc && (txdesc->tdes0 & ETH_TDES0_OWN) == 0);
|
||||
|
||||
@ -891,7 +898,7 @@ static int stm32_transmit(FAR struct stm32_ethmac_s *priv)
|
||||
|
||||
DEBUGASSERT(priv->dev.d_len > 0 && priv->dev.d_buf != NULL);
|
||||
|
||||
#if CONFIG_NET_BUFSIZE > CONFIG_STM32_ETH_BUFSIZE
|
||||
#if CONFIG_NET_BUFSIZE > CONFIG_STM32_ETH_BUFSIZE
|
||||
if (priv->dev.d_len > CONFIG_STM32_ETH_BUFSIZE)
|
||||
{
|
||||
/* Yes... how many buffers will be need to send the packet? */
|
||||
@ -907,51 +914,62 @@ static int stm32_transmit(FAR struct stm32_ethmac_s *priv)
|
||||
|
||||
/* Set up all but the last TX descriptor */
|
||||
|
||||
txnext = txdesc;
|
||||
buffer = priv->dev.d_buf;
|
||||
|
||||
for (i = 0; i < bufcount; i++)
|
||||
{
|
||||
/* This could be a normal event but the design does not handle it */
|
||||
|
||||
DEBUGASSERT((txnext->tdes0 & ETH_TDES0_OWN) == 0);
|
||||
DEBUGASSERT((txdesc->tdes0 & ETH_TDES0_OWN) == 0);
|
||||
|
||||
/* Set the Buffer1 address pointer */
|
||||
|
||||
txdesc->tdes2 = (uint32_t)buffer;
|
||||
|
||||
/* Set the buffer size in all TX descriptors, set the last segment
|
||||
* bit in the last TX descriptor
|
||||
*/
|
||||
/* Set the buffer size in all TX descriptors */
|
||||
|
||||
if (i == (bufcount-1))
|
||||
{
|
||||
txnext->tdes0 |= ETH_TDES0_LS;
|
||||
txnext->tdes1 = lastsize;
|
||||
/* This is the last segment. Set the last segment bit in the
|
||||
* last TX descriptor and ask for an interrupt when this
|
||||
* segment transfer completes.
|
||||
*/
|
||||
|
||||
txdesc->tdes0 |= (ETH_TDES0_LS | ETH_TDES0_IC);
|
||||
|
||||
/* This segement is, most likely, of fractional buffersize */
|
||||
|
||||
txdesc->tdes1 = lastsize;
|
||||
buffer += lastsize;
|
||||
}
|
||||
else
|
||||
{
|
||||
txnext->tdes1 = CONFIG_STM32_ETH_BUFSIZE;
|
||||
/* This is not the last segment. We don't want an interrupt
|
||||
* when this segment transfer completes.
|
||||
*/
|
||||
|
||||
txdesc->tdes0 &= ~ETH_TDES0_IC;
|
||||
|
||||
/* The size of the transfer is the whole buffer */
|
||||
|
||||
txdesc->tdes1 = CONFIG_STM32_ETH_BUFSIZE;
|
||||
buffer += CONFIG_STM32_ETH_BUFSIZE;
|
||||
}
|
||||
|
||||
/* Give descriptor back to DMA */
|
||||
/* Give the descriptor to DMA */
|
||||
|
||||
txnext->tdes0 |= ETH_TDES0_OWN;
|
||||
txnext = (struct eth_txdesc_s *)txnext->tdes3;
|
||||
txdesc->tdes0 |= ETH_TDES0_OWN;
|
||||
txdesc = (struct eth_txdesc_s *)txdesc->tdes3;
|
||||
}
|
||||
|
||||
/* Remember the start of the next available TX descriptor */
|
||||
|
||||
txdesc = txnext;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* The single descriptor is both the first and last segment */
|
||||
/* The single descriptor is both the first and last segment. And we do
|
||||
* want an interrupt when the transfer completes.
|
||||
*/
|
||||
|
||||
txdesc->tdes0 |= (ETH_TDES0_FS | ETH_TDES0_LS);
|
||||
txdesc->tdes0 |= (ETH_TDES0_FS | ETH_TDES0_LS | ETH_TDES0_IC);
|
||||
|
||||
/* Set frame size */
|
||||
|
||||
@ -982,15 +1000,16 @@ static int stm32_transmit(FAR struct stm32_ethmac_s *priv)
|
||||
*/
|
||||
|
||||
priv->dev.d_buf = NULL;
|
||||
priv->dev.d_len = 0;
|
||||
|
||||
/* If there is no other TX buffer, in flight, then remember this
|
||||
* as the location to check for TX done events.
|
||||
/* If there is no other TX buffer, in flight, then remember the location
|
||||
* of the TX descriptor. This is the location to check for TX done events.
|
||||
*/
|
||||
|
||||
if (!priv->txtail)
|
||||
{
|
||||
DEBUGASSERT(priv->inflight == 0);
|
||||
priv->txtail = txdesc;
|
||||
priv->txtail = txfirst;
|
||||
}
|
||||
|
||||
/* Increment the number of TX transfer in-flight */
|
||||
@ -1053,6 +1072,8 @@ static int stm32_uiptxpoll(struct uip_driver_s *dev)
|
||||
{
|
||||
FAR struct stm32_ethmac_s *priv = (FAR struct stm32_ethmac_s *)dev->d_private;
|
||||
|
||||
DEBUGASSERT(priv->dev.d_buf != NULL);
|
||||
|
||||
/* If the polling resulted in data that should be sent out on the network,
|
||||
* the field d_len is set to a value > 0.
|
||||
*/
|
||||
@ -1063,6 +1084,7 @@ static int stm32_uiptxpoll(struct uip_driver_s *dev)
|
||||
|
||||
uip_arp_out(&priv->dev);
|
||||
stm32_transmit(priv);
|
||||
DEBUGASSERT(dev->d_len == 0 && dev->d_buf == NULL);
|
||||
|
||||
/* Check if the next TX descriptor is owned by the Ethernet DMA or CPU. We
|
||||
* cannot perform the TX poll if we are unable to accept another packet for
|
||||
@ -1071,12 +1093,27 @@ static int stm32_uiptxpoll(struct uip_driver_s *dev)
|
||||
|
||||
if ((priv->txhead->tdes0 & ETH_TDES0_OWN) == 0)
|
||||
{
|
||||
/* There is room in the device to hold another packet. Return a non-
|
||||
* zero value to terminate the poll.
|
||||
/* We have to terminate the poll if we have no more descriptors
|
||||
* available for another transfer.
|
||||
*/
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* We have the descriptor, we can continue the poll. Allocate a new
|
||||
* buffer for the poll.
|
||||
*/
|
||||
|
||||
dev->d_buf = stm32_allocbuffer(priv);
|
||||
|
||||
/* We can't continue the poll if we have no buffers */
|
||||
|
||||
if (dev->d_buf == NULL)
|
||||
{
|
||||
/* Terminate the poll. */
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
/* If zero is returned, the polling will continue until all connections have
|
||||
@ -1086,6 +1123,62 @@ static int stm32_uiptxpoll(struct uip_driver_s *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Function: stm32_dopoll
|
||||
*
|
||||
* Description:
|
||||
* The function is called when a frame is received using the DMA receive
|
||||
* interrupt. It scans the RX descriptors to the the received frame.
|
||||
*
|
||||
* Parameters:
|
||||
* priv - Reference to the driver state structure
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
* Global interrupts are disabled by interrupt handling logic.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void stm32_dopoll(FAR struct stm32_ethmac_s *priv)
|
||||
{
|
||||
FAR struct uip_driver_s *dev = &priv->dev;
|
||||
|
||||
/* Check if the next TX descriptor is owned by the Ethernet DMA or
|
||||
* CPU. We cannot perform the TX poll if we are unable to accept
|
||||
* another packet for transmission.
|
||||
*/
|
||||
|
||||
if ((priv->txhead->tdes0 & ETH_TDES0_OWN) == 0)
|
||||
{
|
||||
/* If we have the descriptor, then poll uIP for new XMIT data.
|
||||
* Allocate a buffer for the poll.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(dev->d_len == 0 && dev->d_buf == NULL);
|
||||
dev->d_buf = stm32_allocbuffer(priv);
|
||||
|
||||
/* We can't poll if we have no buffers */
|
||||
|
||||
if (dev->d_buf)
|
||||
{
|
||||
(void)uip_poll(dev, stm32_uiptxpoll);
|
||||
|
||||
/* We will, most likely end up with a buffer to be freed. But it
|
||||
* might not be the same one that we allocated above.
|
||||
*/
|
||||
|
||||
if (dev->d_buf)
|
||||
{
|
||||
DEBUGASSERT(dev->d_len == 0);
|
||||
stm32_freebuffer(priv, dev->d_buf);
|
||||
dev->d_buf = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Function: stm32_freesegment
|
||||
*
|
||||
@ -1519,7 +1612,7 @@ static void stm32_txdone(FAR struct stm32_ethmac_s *priv)
|
||||
|
||||
/* Then poll uIP for new XMIT data */
|
||||
|
||||
(void)uip_poll(&priv->dev, stm32_uiptxpoll);
|
||||
stm32_dopoll(priv);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -1552,7 +1645,7 @@ static int stm32_interrupt(int irq, FAR void *context)
|
||||
* related bits (0-16) correspond in these two registers.
|
||||
*/
|
||||
|
||||
dmasr &= ~stm32_getreg(STM32_ETH_DMAIER);
|
||||
dmasr &= stm32_getreg(STM32_ETH_DMAIER);
|
||||
|
||||
/* Check if there are pending "normal" interrupts */
|
||||
|
||||
@ -1652,7 +1745,7 @@ static void stm32_txtimeout(int argc, uint32_t arg, ...)
|
||||
|
||||
/* Then poll uIP for new XMIT data */
|
||||
|
||||
(void)uip_poll(&priv->dev, stm32_uiptxpoll);
|
||||
stm32_dopoll(priv);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -1676,20 +1769,43 @@ static void stm32_txtimeout(int argc, uint32_t arg, ...)
|
||||
static void stm32_polltimer(int argc, uint32_t arg, ...)
|
||||
{
|
||||
FAR struct stm32_ethmac_s *priv = (FAR struct stm32_ethmac_s *)arg;
|
||||
FAR struct uip_driver_s *dev = &priv->dev;
|
||||
|
||||
/* Check if the next TX descriptor is owned by the Ethernet DMA or CPU. We
|
||||
* cannot perform the TX poll if we are unable to accept another packet for
|
||||
* transmission.
|
||||
* cannot perform the timer poll if we are unable to accept another packet
|
||||
* for transmission. Hmmm.. might be bug here. Does this mean if there is
|
||||
* a transmit in progress, we will missing TCP time state updates?
|
||||
*/
|
||||
|
||||
if ((priv->txhead->tdes0 & ETH_TDES0_OWN) == 0)
|
||||
{
|
||||
/* If so, update TCP timing states and poll uIP for new XMIT data. Hmmm..
|
||||
* might be bug here. Does this mean if there is a transmit in progress,
|
||||
* we will missing TCP time state updates?
|
||||
/* If we have the descriptor, then perform the timer poll. Allocate a
|
||||
* buffer for the poll.
|
||||
*/
|
||||
|
||||
(void)uip_timer(&priv->dev, stm32_uiptxpoll, STM32_POLLHSEC);
|
||||
DEBUGASSERT(dev->d_len == 0 && dev->d_buf == NULL);
|
||||
dev->d_buf = stm32_allocbuffer(priv);
|
||||
|
||||
/* We can't poll if we have no buffers */
|
||||
|
||||
if (dev->d_buf)
|
||||
{
|
||||
/* Update TCP timing states and poll uIP for new XMIT data.
|
||||
*/
|
||||
|
||||
(void)uip_timer(dev, stm32_uiptxpoll, STM32_POLLHSEC);
|
||||
|
||||
/* We will, most likely end up with a buffer to be freed. But it
|
||||
* might not be the same one that we allocated above.
|
||||
*/
|
||||
|
||||
if (dev->d_buf)
|
||||
{
|
||||
DEBUGASSERT(dev->d_len == 0);
|
||||
stm32_freebuffer(priv, dev->d_buf);
|
||||
dev->d_buf = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup the watchdog poll timer again */
|
||||
@ -1827,18 +1943,9 @@ static int stm32_txavail(struct uip_driver_s *dev)
|
||||
|
||||
if (priv->ifup)
|
||||
{
|
||||
/* Check if the next TX descriptor is owned by the Ethernet DMA or
|
||||
* CPU. We cannot perform the TX poll if we are unable to accept
|
||||
* another packet for
|
||||
* transmission.
|
||||
*/
|
||||
/* Poll uIP for new XMIT data */
|
||||
|
||||
if ((priv->txhead->tdes0 & ETH_TDES0_OWN) == 0)
|
||||
{
|
||||
/* If we have the descriptor, then poll uIP for new XMIT data */
|
||||
|
||||
(void)uip_poll(&priv->dev, stm32_uiptxpoll);
|
||||
}
|
||||
stm32_dopoll(priv);
|
||||
}
|
||||
|
||||
irqrestore(flags);
|
||||
|
@ -128,7 +128,7 @@ CONFIG_STM32_BKPSRAM=n
|
||||
CONFIG_STM32_CCMDATARAM=n
|
||||
CONFIG_STM32_DMA1=n
|
||||
CONFIG_STM32_DMA2=n
|
||||
CONFIG_STM32_ETHMAC=n
|
||||
CONFIG_STM32_ETHMAC=y
|
||||
CONFIG_STM32_OTGHS=n
|
||||
# AHB2:
|
||||
CONFIG_STM32_DCMI=n
|
||||
@ -281,6 +281,9 @@ CONFIG_SSI_POLLWAIT=y
|
||||
# defined. This provides the value of the mode bits indicating full duplex mode.
|
||||
# CONFIG_STM32_ETH_PTP - Precision Time Protocol (PTP). Not supported
|
||||
# but some hooks are indicated with this condition.
|
||||
# CONFIG_STM32_ETHMAC_REGDEBUG - If CONFIG_DEBUG is also enabled, this will
|
||||
# generate far more debug output than you could ever care to see unless you
|
||||
# are debugging low-level Ethernet driver features.
|
||||
#
|
||||
CONFIG_STM32_PHYADDR=0x01
|
||||
CONFIG_STM32_MII=y
|
||||
@ -296,6 +299,7 @@ CONFIG_STM32_PHYSR_100MBPS=0x0000
|
||||
CONFIG_STM32_PHYSR_MODE=0x0004
|
||||
CONFIG_STM32_PHYSR_FULLDUPLEX=0x0004
|
||||
CONFIG_STM32_ETH_PTP=n
|
||||
CONFIG_STM32_ETHMAC_REGDEBUG=n
|
||||
|
||||
#
|
||||
# General build options
|
||||
@ -419,6 +423,7 @@ CONFIG_DEBUG_FS=n
|
||||
CONFIG_DEBUG_GRAPHICS=n
|
||||
CONFIG_DEBUG_LCD=n
|
||||
CONFIG_DEBUG_USB=n
|
||||
CONFIG_DEBUG_NET=n
|
||||
CONFIG_HAVE_CXX=y
|
||||
CONFIG_MM_REGIONS=2
|
||||
CONFIG_ARCH_LOWPUTC=y
|
||||
@ -711,7 +716,7 @@ CONFIG_MMCSD_HAVECARDDETECT=n
|
||||
# CONFIG_NET_BROADCAST - Broadcast support
|
||||
# CONFIG_NET_FWCACHE_SIZE - number of packets to remember when looking for duplicates
|
||||
#
|
||||
CONFIG_NET=n
|
||||
CONFIG_NET=y
|
||||
CONFIG_NET_NOINTS=n
|
||||
CONFIG_NET_MULTIBUFFER=y
|
||||
CONFIG_NET_IPv6=n
|
||||
|
Loading…
Reference in New Issue
Block a user