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

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_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.

View File

@ -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);

View File

@ -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