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:
patacongo
2011-12-12 21:55:10 +00:00
parent 041d1e6191
commit fcd24b1c21
3 changed files with 167 additions and 52 deletions
+3
View File
@@ -2255,3 +2255,6 @@
* arch/arm/srcm/stm32/stm32_eth.c: Adds an Ethernet driver for the STM32 F4. * 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 * 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. 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.
+157 -50
View File
@@ -453,14 +453,17 @@
#endif #endif
/* Interrupt bit sets *******************************************************/ /* 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 \ #define ETH_DMAINT_NORMAL \
(ETH_DMAINT_TI | ETH_DMAINT_TBUI |ETH_DMAINT_RI | ETH_DMAINT_ERI) (ETH_DMAINT_TI | ETH_DMAINT_TBUI |ETH_DMAINT_RI | ETH_DMAINT_ERI)
#define ETH_DMAINT_ABNORMAL \ #define ETH_DMAINT_ABNORMAL \
(ETH_DMAINT_TPSI | ETH_DMAINT_TJTI | ETH_DMAINT_ROI | ETH_DMAINT_TUI | \ (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) ETH_DMAINT_FBEI)
/* Normal receive, transmit, error interrupt enable bit sets */ /* 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_putreg(uint32_t val, uint32_t addr);
static void stm32_checksetup(void); static void stm32_checksetup(void);
#else #else
# define stm32_getreg(addr) getreg16(addr) # define stm32_getreg(addr) getreg32(addr)
# define stm32_putreg(val,addr) putreg16(val,addr) # define stm32_putreg(val,addr) putreg32(val,addr)
# define stm32_checksetup() # define stm32_checksetup()
#endif #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_transmit(FAR struct stm32_ethmac_s *priv);
static int stm32_uiptxpoll(struct uip_driver_s *dev); static int stm32_uiptxpoll(struct uip_driver_s *dev);
static void stm32_dopoll(FAR struct stm32_ethmac_s *priv);
/* Interrupt handling */ /* 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) static int stm32_transmit(FAR struct stm32_ethmac_s *priv)
{ {
struct eth_txdesc_s *txdesc; struct eth_txdesc_s *txdesc;
struct eth_txdesc_s *txfirst;
uint32_t regval; uint32_t regval;
/* The internal uIP buffer size may be configured to be larger than the /* 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 #if CONFIG_NET_BUFSIZE > CONFIG_STM32_ETH_BUFSIZE
struct eth_txdesc_s *txnext;
uint8_t *buffer; uint8_t *buffer;
int bufcount; int bufcount;
int lastsize; 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. * must have assured that there is no transmission in progress.
*/ */
txdesc = priv->txhead; txdesc = priv->txhead;
nllvdbg("d_len: %d txhead: %08x tdes0: %08x\n", txdesc, txdesc->tdes0); 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); 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); 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) if (priv->dev.d_len > CONFIG_STM32_ETH_BUFSIZE)
{ {
/* Yes... how many buffers will be need to send the packet? */ /* 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 */ /* Set up all but the last TX descriptor */
txnext = txdesc;
buffer = priv->dev.d_buf; buffer = priv->dev.d_buf;
for (i = 0; i < bufcount; i++) for (i = 0; i < bufcount; i++)
{ {
/* This could be a normal event but the design does not handle it */ /* 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 */ /* Set the Buffer1 address pointer */
txdesc->tdes2 = (uint32_t)buffer; txdesc->tdes2 = (uint32_t)buffer;
/* Set the buffer size in all TX descriptors, set the last segment /* Set the buffer size in all TX descriptors */
* bit in the last TX descriptor
*/
if (i == (bufcount-1)) if (i == (bufcount-1))
{ {
txnext->tdes0 |= ETH_TDES0_LS; /* This is the last segment. Set the last segment bit in the
txnext->tdes1 = lastsize; * 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; buffer += lastsize;
} }
else 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; buffer += CONFIG_STM32_ETH_BUFSIZE;
} }
/* Give descriptor back to DMA */ /* Give the descriptor to DMA */
txnext->tdes0 |= ETH_TDES0_OWN; txdesc->tdes0 |= ETH_TDES0_OWN;
txnext = (struct eth_txdesc_s *)txnext->tdes3; txdesc = (struct eth_txdesc_s *)txdesc->tdes3;
} }
/* Remember the start of the next available TX descriptor */
txdesc = txnext;
} }
else else
#endif #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 */ /* 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_buf = NULL;
priv->dev.d_len = 0;
/* If there is no other TX buffer, in flight, then remember this /* If there is no other TX buffer, in flight, then remember the location
* as the location to check for TX done events. * of the TX descriptor. This is the location to check for TX done events.
*/ */
if (!priv->txtail) if (!priv->txtail)
{ {
DEBUGASSERT(priv->inflight == 0); DEBUGASSERT(priv->inflight == 0);
priv->txtail = txdesc; priv->txtail = txfirst;
} }
/* Increment the number of TX transfer in-flight */ /* 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; 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, /* If the polling resulted in data that should be sent out on the network,
* the field d_len is set to a value > 0. * 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); uip_arp_out(&priv->dev);
stm32_transmit(priv); 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 /* 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 * 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) if ((priv->txhead->tdes0 & ETH_TDES0_OWN) == 0)
{ {
/* There is room in the device to hold another packet. Return a non- /* We have to terminate the poll if we have no more descriptors
* zero value to terminate the poll. * available for another transfer.
*/ */
return -EBUSY; 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 /* 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; 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 * Function: stm32_freesegment
* *
@@ -1519,7 +1612,7 @@ static void stm32_txdone(FAR struct stm32_ethmac_s *priv)
/* Then poll uIP for new XMIT data */ /* 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. * 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 */ /* 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 */ /* 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, ...) static void stm32_polltimer(int argc, uint32_t arg, ...)
{ {
FAR struct stm32_ethmac_s *priv = (FAR struct stm32_ethmac_s *)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 /* 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 * cannot perform the timer poll if we are unable to accept another packet
* transmission. * 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 ((priv->txhead->tdes0 & ETH_TDES0_OWN) == 0)
{ {
/* If so, update TCP timing states and poll uIP for new XMIT data. Hmmm.. /* If we have the descriptor, then perform the timer poll. Allocate a
* might be bug here. Does this mean if there is a transmit in progress, * buffer for the poll.
* we will missing TCP time state updates?
*/ */
(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 */ /* Setup the watchdog poll timer again */
@@ -1827,18 +1943,9 @@ static int stm32_txavail(struct uip_driver_s *dev)
if (priv->ifup) if (priv->ifup)
{ {
/* Check if the next TX descriptor is owned by the Ethernet DMA or /* Poll uIP for new XMIT data */
* 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) stm32_dopoll(priv);
{
/* If we have the descriptor, then poll uIP for new XMIT data */
(void)uip_poll(&priv->dev, stm32_uiptxpoll);
}
} }
irqrestore(flags); irqrestore(flags);
+7 -2
View File
@@ -128,7 +128,7 @@ CONFIG_STM32_BKPSRAM=n
CONFIG_STM32_CCMDATARAM=n CONFIG_STM32_CCMDATARAM=n
CONFIG_STM32_DMA1=n CONFIG_STM32_DMA1=n
CONFIG_STM32_DMA2=n CONFIG_STM32_DMA2=n
CONFIG_STM32_ETHMAC=n CONFIG_STM32_ETHMAC=y
CONFIG_STM32_OTGHS=n CONFIG_STM32_OTGHS=n
# AHB2: # AHB2:
CONFIG_STM32_DCMI=n 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. # defined. This provides the value of the mode bits indicating full duplex mode.
# CONFIG_STM32_ETH_PTP - Precision Time Protocol (PTP). Not supported # CONFIG_STM32_ETH_PTP - Precision Time Protocol (PTP). Not supported
# but some hooks are indicated with this condition. # 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_PHYADDR=0x01
CONFIG_STM32_MII=y CONFIG_STM32_MII=y
@@ -296,6 +299,7 @@ CONFIG_STM32_PHYSR_100MBPS=0x0000
CONFIG_STM32_PHYSR_MODE=0x0004 CONFIG_STM32_PHYSR_MODE=0x0004
CONFIG_STM32_PHYSR_FULLDUPLEX=0x0004 CONFIG_STM32_PHYSR_FULLDUPLEX=0x0004
CONFIG_STM32_ETH_PTP=n CONFIG_STM32_ETH_PTP=n
CONFIG_STM32_ETHMAC_REGDEBUG=n
# #
# General build options # General build options
@@ -419,6 +423,7 @@ CONFIG_DEBUG_FS=n
CONFIG_DEBUG_GRAPHICS=n CONFIG_DEBUG_GRAPHICS=n
CONFIG_DEBUG_LCD=n CONFIG_DEBUG_LCD=n
CONFIG_DEBUG_USB=n CONFIG_DEBUG_USB=n
CONFIG_DEBUG_NET=n
CONFIG_HAVE_CXX=y CONFIG_HAVE_CXX=y
CONFIG_MM_REGIONS=2 CONFIG_MM_REGIONS=2
CONFIG_ARCH_LOWPUTC=y CONFIG_ARCH_LOWPUTC=y
@@ -711,7 +716,7 @@ CONFIG_MMCSD_HAVECARDDETECT=n
# CONFIG_NET_BROADCAST - Broadcast support # CONFIG_NET_BROADCAST - Broadcast support
# CONFIG_NET_FWCACHE_SIZE - number of packets to remember when looking for duplicates # 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_NOINTS=n
CONFIG_NET_MULTIBUFFER=y CONFIG_NET_MULTIBUFFER=y
CONFIG_NET_IPv6=n CONFIG_NET_IPv6=n