This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

EMAC1 LINK interrupt

Hi,

I’d like to ask you some questions about EMAC implementation.

 I'm trying to use both EMAC0 and EMAC1 devices and need to receive link up/down interrupts for them .

As I understand there are one MDIO for two EMACs, so I tried to configure  PHY0 and PHY1  (PHY0 for EMAC0 and PHY1 for EMAC1) using registers

USERPHYSEL0 and USERPHYSEL1.

 

So I do it like this:

USERPHYSEL0 = LINKINTENB | (PHY0 & 0x1F)

USERPHYSEL1 = LINKINTENB | (PHY1 & 0x1F)

When I read this registers after configuration, I see that values was set correctly (PHY0 == 6,PHY1 == 3):

MDIO User PHY Select Register 0                                 0x00000046

MDIO User PHY Select Register 1                                 0x00000043

 

Also MISC interrupt for both EMACs is turned on in such way:

EMAC_CTRL_CnMISCEN = 0x2 // handle link interrupts only

 

Using this configuration I try plug/unplug Ethernet cables to check interrupts.

ALIVE,  LINK, LINKINTRAW, LINKINTMASKED registers show correct information about both PHYs.

 

But I can receive MACMISC0 vector interrupt for PHY0 only. When I try to write PHY1 to USERPHYSEL0, I receive MACMISC0 vector interrupt for PHY1. So PHYs work properly.

 

According to described above there are couple of questions:

  1. Is there one MDIO for both EMACs?
  2. Is PHY interrupt configuration I wrote correct? If no, please correct.
  3. How can I bind MACMISC0 vector (or EMAC0)  to PHY0 and MACMISC1 vector (or EMAC1)  to PHY1? I need to receive  MACMISC0 vector for PHY0 link status changed and MACMISC1 vector for PHY1.
  4. Possibly USERPHYSEL0 used to determine PHY for EMAC0 and USERPHYSEL0 for EMAC1. If so why cannot I receive MACMISC1 vector interrupt.
  • Hi Zinoviy,

    Yes, there are 2 EMACs and 1 MDIO.

    I suspect that you missed something in the overall EMAC Subsystem Initialization/Configuration procedure. For example I do not see that you programmed the MDIO.CONTROL register prior to enable the MDIO LINK interrupt request, as explained in the DM816x TRM, section 6.2.7.2.1 Initializing the MDIO Module.

    For proper initialization/configuration, refer to section 6.2.15 Initialization, and section 6.2.16 Interrupt Support.

    Regards,

    Pavel

  • Hi Pavel,

    Thank you for your answer.

    Like you said the problem was in not sufficient EMAC subsystem initialization. Actually I missed to setup PINCTRLx registers for EMAC1 pins.

     

    Regards Zinoviy.

  • Hi Pavel,

    Please skip Zinoviy previous message - more thorough tests shown that probem with LINK interrupt from PHY attached to EMAC 1 still exist. As was written before LINK inerrupt frim PHY attached to EMAC 0 are  risen as expected.

    You wrote that something is wrong with MDIO CONTROL register. But I cannot find any field in it were I can setup link interrupt, could you help  me?

    Here is my MDIO registers dump. Please check if everything is set correctly. See sone more coments below.

    MDIO Version Register                                           0x40070106

    MDIO Control Register                                           0x4114004a

     PHY Alive Status Register                                       0x00000048

    PHY Link Status Register                                        0x00000048

    MDIO Link Status Change Interrupt (Unmasked) Register           0x00000002

    MDIO Link Status Change Interrupt (Masked) Register             0x00000002

    MDIO User Command Complete Interrupt (Unmasked) Register        0x00000001

    MDIO User Command Complete Interrupt (Masked) Register          0x00000000

    MDIO User Command Complete Interrupt Mask Set Register          0x00000000

    MDIO User Command Complete Interrupt Mask Clear Register        0x00000000

    MDIO User Access Register 0                                     0x20a34041

    MDIO User PHY Select Register 0                                 0x00000046 //from this PHY LINK inerrupts are risen

    MDIO User Access Register 1                                     0x00000000

    MDIO User PHY Select Register 1                                 0x00000043 //we do not see LINK interrupts from this PHY

    Sorry for previous faulty notification about solved issue.

  • Hi Ihor,

    How do you define that EMAC0 LINK interrupt request is generated while EMAC1 LINK interrupt request is not generated? Is this statement based on the value of the LINKINTRAW and LINKINTMASKED register value ? Do you expect this value to be 0x3?

    What is the output of this command:

    root@dm816x-evm:~# ifconfig

    Best Regards,

    Pavel

  • Hi Pavel,

    Thank you for your prompt response.

    First of all we are debugging Custom OS on Custom HW. So we cant use Linux commands from debugging. The only thing we can do is dumping and analysing of EMAC and MDIO registers. For sure we can use osciloscope to check signals on the board if necessary.

    Possibly I wasn't clear in my explanation. According to LINKINTRAW and LINKINTMASKED registers everything works ok - I can see changes in these registers when link event appears on both PHYs. So,  driver pooling mode works as expected. Problem appear when I am trying implement driver interrupt mode.

    Currently I can receive MISC interrupt for EMAC0 only, for PHY which is configured in USERPHYSEL0.

    But also I'd like to receive MISC interrupts for  EMAC1 device when link event occurs for second PHY (I wrote it ID to USERPHYSEL1).

    Setup for MISC registers of both EMACs are the same: EMAC_CTRL_CnMISCEN = 0x2

    1. I'd like hear your suggestions why MISC interrupt is not risen by EMAC1 if MDIO detects LINK interrupt. 2. We think think that USERPHYSEL0 selects PHY for EMAC0 and USERPHYSEL1 selects PHY for EMAC1. Is our assuption correct?

    Best Regards
    Ihor

  • Hi Ihor,

    I will check these questions with our EMAC experts within TI. But first I need few more clarifications.

    When you said that MDIO detects LINK interrupt 2 (from EMAC1), is this statement based on the value of the LINKINTRAW/LINKINTMASKED registers ? I mean when EMAC0 LINK interrupt is generated, the value of these registers is 0x1, and when EMAC1 LINK interrupt is generated, the value is 0x2?

    The PHY0 ID/address is 0x6 and PHY1 ID/address is 0x3, is that correct?

    Meanwhile you can check here:

    http://processors.wiki.ti.com/index.php/TI81xx_PSP_Porting_Guide#Ethernet_Driver_-_Adding_Custom_Ethernet_Phy

    Can you also try to configure the "MDIO User Access Register 1" ? I see that MDIO User Access Register 0 has some value, while  MDIO User Access Register 1 is 0x0.

    Regards,

    Pavel

  • Hi Pavel,

    Please find my comments below your questions.

    >When you said that MDIO detects LINK interrupt 2 (from EMAC1), is this statement based on the value of the LINKINTRAW/LINKINTMASKED registers ? I mean when EMAC0 LINK interrupt is generated, the value of these registers is 0x1, and when EMAC1 LINK interrupt is generated, the value is 0x2?

    Yes

    >The PHY0 ID/address is 0x6 and PHY1 ID/address is 0x3, is that correct?

    Yes

    Thanks in advance for you support

    BR
    Ihor

  • Hi Ihor,

    When you state that the MISC interrupt is not risen by EMAC1 (if MDIO detects LINK interrupt), is this based on the fact that Miscellaneous Interrupt Status Register bit CMMISCINTSTAT[1] MDIO_LINKINT is not set to 0x1? Or this MISC interrupt is set in CMMISCINTSTAT register, bit [1], but not detected by the Cortex-A8 MPU Interrupt Controller ?

    Regards,

    Pavel


  • Hi, Pavel

    We did more tests and observed picture looks more then confusing.

    Test conditions:
    1. Plug-in and plug –out Eth cable to\from both interfaces.
    2. Succesfull Rx\Tx interruots driven communication on both interfaces (FTP, Telnet, etc).

    What we see
    .

    1. For EMAC 1 in CMMISCINTSTAT register field C_MISC_STAT was never equal to MDIO_LINKINT.
    2. We can recognize interrupt source also through MACINVECTOR register for both interfaces. But for EMAC1 LINKINT bit in this register  was never set to “1”.
    3.  Answer on your question: we neither see changes in CMMISCINTSTAT nor receive LINK interrupt in MPU Interrupt Controller for EMAC 1.

      I hope this information will give you the key to solve this puzzle...

      Best regards, Ihor
  • Hi Ihor,

    Can you share your code?

    Regards,

    Pavel

  • Also, what method are you using to boot the DM816x, is it EMAC boot? If yes, please note that EMAC boot is available only with EMAC0.

     Regards,

    Pavel

  • /*
    */
    
    #include <emac.h>
    #include <mdio.h>
    #include <lan8710a.h>
    #include "types.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdarg.h>
    #include <string.h>
    
    #include "fns_driver.h"
    
    #include "errcodes.h"
    #include "sock\sock_utils.h"
    #include "lan_interface.h"
    
    
    #include "BBeth1.h"
    
    #define CONFIG_NET_POLL_CONTROLLER
    #define virtual(val) (val - 0x80000000)
    #define physical(val) (val + 0x80000000)
    //#define	DEBUG_DUMP_PACKETS
    /*
     *  NAPI poll function
     */
    
    #ifdef DEBUG_DUMP_PACKETS
    static void emac_dump_packets(U8 *ptr, U16 len)
    {
    	int i = 0;
    
    	for (i=0;i<len;i++)
    		com_printf(" %X",ptr[i]);
    	com_printf("\n");
    }
    #endif
    
    static int emac_poll(struct net_device *dev)
    {
    	EMAC_DATA *pdata;
    	RXCH *rxch;
    	volatile EMAC_RX_BD *curr_bd,*last_bd,*processed_bd;
    	U32 ex_len = 0, len_to_alloc = 0;
    	U16 tot_len = 0,len = 0;
    	U8  *ptr, *buf_ptr;
    	int err;
    
    	pdata = netdev_priv(dev);
    	rxch = &(pdata->rxch);
    
    	curr_bd = rxch->active_head;
    	// Get the bd which contains the earliest filled data
    
    	// Process the descriptors as long as data is available
    	// when the DMA is receiving data, SOP flag will be set
    	while(curr_bd->flags_pktlen & EMAC_BUF_DESC_SOP)
    	{
    		ex_len = 0;
    	    len_to_alloc = 0;
    
    	    // Start processing once the packet is loaded
    	    if(EMAC_BUF_DESC_OWNER != (curr_bd->flags_pktlen & EMAC_BUF_DESC_OWNER))
    	    {
    	    	// Get the total length of the packet. curr_bd points to the start
    	    	// of the packet.
    	    	tot_len = (curr_bd->flags_pktlen) & 0xFFFF;
    	    	do
    	    	{
    	    		last_bd = rxch->active_tail;
    
    	    		ptr = (U8*)virtual(curr_bd->bufptr);
    	    		len = (curr_bd->bufoff_len) & 0xFFFF;
    #ifdef DEBUG_DUMP_PACKETS
    	    		com_printf("RECV: ");
    	    		emac_dump_packets(ptr, len);
    #endif //DEBUG_DUMP_PACKETS
    #ifdef OS_TARGET
    	    		if( dev->i_devID == 0 )
    	    		{
    	    			communication_interrupt_bb_other_counter ++;
    	    			total_not_bb1_pkts ++;
    	    		}
    	    		if( 1 == dev->i_devID && is_packet_backbone(ptr, len) )
    	    		{
    	    			Eth_Backbone_operation(dev, (char *)ptr, len);
    	    		}
    	    		else
    #endif
    	    			if( FNS_ENOERR != fnsNetDevUpcall(dev->ndp, (char *)ptr, len) )
    	    			{
    	    				EMAC_WARNING("ERROR fnsNetDevUpcall\n");
    	    			}
    	    			else
    	    			{
    	    				buf_ptr = (U8*)fnsNetDevAllocBuffer( dev->ndp, MAX_TRANSFER_UNIT, &err );
    	    				if( FNS_ENOERR == err)
    	    				{
    	    					// Replace the message we up-called with the new one
    	    					curr_bd->bufptr = (u32)physical(buf_ptr);
    	    				}
    	    				else
    	    				{
    	    					EMAC_WARNING("ERROR fnsNetDevAllocBuffer %d\n",err);
    	    				}
    	    			}
    
    	    		// clear flags and values
    	    		last_bd->flags_pktlen = EMAC_BUF_DESC_OWNER;
    	    		last_bd->bufoff_len = MAX_TRANSFER_UNIT;
    	    		// set next bd
    	    		processed_bd = curr_bd;
    	    		curr_bd = curr_bd->next;
    
    	    		// As processed_bd was just processed now it became last bd in circle list.
    	    		// previous last bd next flag now points to processed bd.
    	    		last_bd->next = processed_bd;
    	    		// move active head pointer to next value
    	    		rxch->active_head = processed_bd->next;
    	    		// set tail to processed bd
    	    		rxch->active_tail = processed_bd;
    	    		// set next pointer of last bd to NULL
    	    		processed_bd->next = NU_NULL;
    	    	} while((processed_bd->flags_pktlen & EMAC_BUF_DESC_EOP)
    	                != EMAC_BUF_DESC_EOP);
    
    	    	// Acknowledge that this packet is processed
    	    	EMACRxCPWrite(pdata->emac_base, 0, (unsigned int)processed_bd);
    
    	        // Check if the reception has ended. If the EOQ flag is set, the NULL
    	    	// Pointer is taken by the DMA engine. So we need to write the RX HDP
    	    	// with the next descriptor.
    	    	if(processed_bd->flags_pktlen & EMAC_BUF_DESC_EOQ)
    	    	{
    	    		EMACRxHdrDescPtrWrite(pdata->emac_base, (U32)(rxch->active_head), 0);
    	    	}
    		}
    	    curr_bd = rxch->active_head;
    	}
    	return tot_len;
    }
    
    static void _emac_inst_config(EMAC_DATA *pdata)
    {
    	if(pdata->inst_num == 0)
    	{
    		pdata->emac_base = EMAC_0_BASE;
    		pdata->emac_ctrl_base = EMAC_CTRL_0_BASE;
    		pdata->emac_ctrl_ram = EMAC_CTRL_RAM_0_BASE;
    		pdata->mdio_base = MDIO_0_BASE;
    #warning "pdata->phy_addr = 1 for ev.board and pdata->phy_addr = 6 for prot.board"
    #warning "pdata->phy_addr = 4 with FPGA and pdata->phy_addr = 6 for withot FPGA"
    
    		pdata->phy_addr = 4;//1;
    		pdata->phy_autoneg = Lan8710aAutoNegotiate;
    		pdata->phy_partnerability = Lan8710aPartnerAbilityGet;
    	}
    	else if (pdata->inst_num == 1)
    	{
    		pdata->emac_base = EMAC_1_BASE;
    		pdata->emac_ctrl_base = EMAC_CTRL_1_BASE;
    		pdata->emac_ctrl_ram = EMAC_CTRL_RAM_1_BASE;
    		pdata->mdio_base = MDIO_0_BASE;
    		pdata->phy_addr = 1;
    		#warning "pdata->phy_addr = 1 with FPGA and pdata->phy_addr = 3 for withot FPGA"
    		pdata->phy_autoneg = Lan8710aAutoNegotiate;
    		pdata->phy_partnerability = Lan8710aPartnerAbilityGet;
    	}
    	else
    	{
    		EMAC_WARNING("pdata->inst_num invalid - %d",pdata->inst_num);
    	}
    	pdata->broadcast_on = 0;
    	pdata->storm_control_counter = 0;
    	pdata->link_is_up = 0;
    }
    
    static int _emac_link_setup(EMAC_DATA *pdata)
    {
    	STATUS linkstat = -1;
    	U16 partnr_ablty;
    	U32 phyduplex;
    
    	phyduplex = EMAC_DUPLEX_HALF;
    	// Start link setup
    	if(Lan8710aAutoNegotiate(pdata->mdio_base, pdata->phy_addr,
    			(LAN8710A_100BTX | LAN8710A_100BTX_FD | LAN8710A_10BT | LAN8710A_10BT_FD)) == TRUE)
    	{
    		U32 speed = 0;
    
    		linkstat = NU_SUCCESS;
    		// Check speed value after autonegotiation finished
    		speed = Lan8710aSpeedAbilityGet(pdata->mdio_base, pdata->phy_addr);
    
    		if ( 0 != speed )
    		{
    			EMAC_TRACE( "Connection speed %d mbps\n",speed);
    			// Turn on/off gig mode.
    			if (1000 == speed)
    			{
    				EMACGIGEnable(pdata->emac_base);
    			}
    			else
    			{
    				EMACGIGDisable(pdata->emac_base);
    			}
    
    			Lan8710aPartnerAbilityGet(pdata->mdio_base, pdata->phy_addr,
    						&partnr_ablty);
    
    			// Check for 100 Mbps and duplex capability
    			// In 1000 Mbps full duplex mode supported only
    			if((1000 == speed) || (partnr_ablty & LAN8710A_100BTX_FD))
    			{
    				phyduplex = EMAC_DUPLEX_FULL;
    			}
    		}
    		else
    		{
    			EMAC_WARNING( "Connection speed illegal\n");
    			linkstat = -1;
    		}
    	}
    	else
    	{
    		EMAC_WARNING( "autonegotiate error\n");
    		linkstat = -1;
    	}
    
    	/* Set the EMAC with the negotiation results if it is successful */
    	if(linkstat == NU_SUCCESS)
    	{
    		EMACDuplexSet(pdata->emac_base, phyduplex);
    	}
    
    	/* Wait for the MII to settle down */
    	NU_Sleep(10);
    
    	return linkstat;
    }
    
    /* Update link mode if any thing has changed */
    static void emac_misc(struct net_device *dev)
    {
    	EMAC_DATA *pdata;
    	pdata = netdev_priv(dev);
    	int irq = 0;
    
    	irq = EMACIntVectorGet(pdata->emac_base);
    ////////////////////////////////////////////////
    #warning "Temporary fix, while EMAC1_MISC interrupt not working. Comment this for ev.board"
    	if (1 == dev->i_devID)
    		irq = EMAC_MACINVECTOR_LINKINT0;
    ////////////////////////////////////////////////
    	switch(irq)
    	{
    		case EMAC_MACINVECTOR_LINKINT0:
    		{
    			// Check link status in EMAC level
    			int index = MDIOAckLinkInt(pdata->mdio_base);
    			int link_is_up = 0;
    
    			//if (index != pdata->phy_addr)
    				//break;
    			// Check if emac still alive
    			if(!((MDIOPhyAliveStatusGet(pdata->mdio_base) >> pdata->phy_addr) & 0x01 ))
    			{
    				EMAC_WARNING( "MDIOPhyAliveStatusGet error\n");
    				pdata->link_is_up = 0;
    				break;
    			}
    			// Second link check - in PHY level
    			link_is_up = Lan8710aLinkStatusGet(pdata->mdio_base, pdata->phy_addr, 10000);
    
    			if (pdata->link_is_up != link_is_up)
    			{
    				pdata->link_is_up = link_is_up;
    				if(pdata->link_is_up)
    				{
    					// Start link setup (autonegotiation)
    					if(NU_SUCCESS != _emac_link_setup(pdata))
    					{
    						EMAC_WARNING( "_emac_link_setup error\n");
    						pdata->link_is_up = 0;
    						break;
    					}
    					EMAC_TRACE( "link is up! \n");
    					fnsNetDevIsUp( dev->ndp );
    				}
    				else
    				{
    					EMAC_TRACE( "link is down!\n");
    					fnsNetDevIsDown( dev->ndp );
    				}
    			}
    			break;
    		}
    		case EMAC_MACINVECTOR_STATPEND:
    		{
    			// for future use
    			break;
    		}
    		case EMAC_MACINVECTOR_USERINT0:
    		{
    			// for future use
    			break;
    		}
    		case EMAC_MACINVECTOR_HOSTPEND:
    		{
    			// for future use
    			break;
    		}
    		default:
    			break;
    	}
    }
    
    static int emac_open(struct net_device *dev)
    {
    	U32 channel;
    	U32 num_bd;
    	volatile EMAC_TX_BD *curr_txbd, *last_txbd;
    	volatile EMAC_RX_BD *curr_bd, *last_bd;
    	EMAC_DATA *pdata;
    	TXCH *txch;
    	RXCH *rxch;
    	char *p;
    	int err;
    	U8 hw_addr[6];
    
    	pdata = netdev_priv(dev);
    
    	// device capabilities
    	EMACInit(pdata->emac_ctrl_base, pdata->emac_base);
    
    	MDIOInit(pdata->mdio_base, MDIO_FREQ_INPUT, MDIO_FREQ_OUTPUT);
    	NU_Sleep(1);
    
    	EMACRxBroadCastEnable(pdata->emac_base, 0);
    
    	// Set the MAC Addresses in EMAC hardware
    	hw_addr[0] = dev->dev_mac_addr[5];
    	hw_addr[1] = dev->dev_mac_addr[4];
    	hw_addr[2] = dev->dev_mac_addr[3];
    	hw_addr[3] = dev->dev_mac_addr[2];
    	hw_addr[4] = dev->dev_mac_addr[1];
    	hw_addr[5] = dev->dev_mac_addr[0];
    
    	EMACMACSrcAddrSet(pdata->emac_base, hw_addr);
    
    	for(channel = 0; channel < 8; channel++)
    	{
    		EMACMACAddrSet(pdata->emac_base, channel, hw_addr, EMAC_MACADDR_MATCH);
    	}
    
    	// Initialize the Descriptor Memory For TX and RX
    	// Only Channel 0 is supported for both TX and RX
    
    	txch = &(pdata->txch);
    
    	txch->free_head = (volatile EMAC_TX_BD*)(pdata->emac_ctrl_ram);
    	txch->next_bd_to_process = txch->free_head;
    	txch->active_tail = NULL;
    
    	// Set the number of descriptors for the channel
    	num_bd = (SIZE_EMAC_CTRL_RAM >> 1) / sizeof(EMAC_TX_BD);
    
    	curr_txbd = txch->free_head;
    
    	// Initialize all the TX buffer Descriptors
    	while(num_bd--)
    	{
    		curr_txbd->next = curr_txbd + 1;
    		curr_txbd->flags_pktlen = 0;
    		last_txbd = curr_txbd;
    		curr_txbd = curr_txbd->next;
    	}
    	last_txbd->next = txch->free_head;
    
    	// Initialize the descriptors for the RX channel
    	rxch = &(pdata->rxch);
    	rxch->active_head = (volatile struct EMAC_RX_BD*)(curr_txbd + 1);
    
    	// Set the number of descriptors for the channel
    	num_bd = ((SIZE_EMAC_CTRL_RAM >> 1) / sizeof(EMAC_RX_BD) - 1);
    	curr_bd = rxch->active_head;
    	last_bd = curr_bd;
    
    	// Allocate the pbufs for the maximum count permitted or till the
    	// number of buffer desceriptors expire, which ever is earlier.
    	while(num_bd--)
    	{
    		p = fnsNetDevAllocBuffer( dev->ndp, MAX_TRANSFER_UNIT, &err );
    		if( FNS_ENOERR == err )
    		{
    			curr_bd->bufptr = (U32)physical(p);
    			curr_bd->bufoff_len = MAX_TRANSFER_UNIT;
    			curr_bd->next = curr_bd + 1;
    			curr_bd->flags_pktlen = EMAC_BUF_DESC_OWNER;
    
    			/* Save the pbuf */
    			last_bd = curr_bd;
    			curr_bd = curr_bd->next;
    		}
         }
    
    	last_bd->next = NULL;
    	rxch->active_tail = last_bd;
    
    	/* Enable PHY to clock out TX_CLK for gigabit transmit*/
    
    	short unsigned int tmp = 0;
    
    	Lan8710aRegRead(pdata->mdio_base, pdata->phy_addr, PHY_CONF_REG, &tmp);
    	tmp |= PHY_CONF_TXCLKEN;
    	Lan8710aRegWrite(pdata->mdio_base, pdata->phy_addr, PHY_CONF_REG, tmp);
    	Lan8710aRegRead(pdata->mdio_base, pdata->phy_addr, PHY_CONF_REG, &tmp);
    
    	// Acknowledge receive and transmit interrupts for proper interrupt pulsing
    	EMACCoreIntAck(pdata->emac_base, EMAC_INT_CORE0_RX);
    	EMACCoreIntAck(pdata->emac_base, EMAC_INT_CORE0_TX);
    	EMACCoreIntAck(pdata->emac_base, EMAC_INT_CORE0_MISC);
    
    	EMACRxUnicastSet(pdata->emac_base, 0);
    	EMACNumFreeBufSet(pdata->emac_base, 0, 10);
    
    	EMACTxEnable(pdata->emac_base);
    	EMACRxEnable(pdata->emac_base);
    #warning "For correct WP_Line working on ev.board use MISC int. for Park only.Just uncomment two lines"
    	//if (0 == pdata->inst_num)
    	//	MDIOSetLinkInt(pdata->mdio_base, 0, pdata->phy_addr);
    	MDIOSetLinkInt(pdata->mdio_base, pdata->inst_num, pdata->phy_addr);
    	// Write the RX HDP for channel 0
    	EMACRxHdrDescPtrWrite(pdata->emac_base, (U32)rxch->active_head, 0);
    
    	EMACMIIEnable(pdata->emac_base);
    /////////////////
    #warning "Temporary fix, while EMAC1_MISC interrupt not working. Comment this for ev.board"
    	if (1 == pdata->inst_num)
    		emac_misc(dev);
    /////////////////
    	// Enable the Transmission and reception, enable the interrupts for
    	// channel 0 and for control core 0
    	EMACTxIntPulseEnable(pdata->emac_base, pdata->emac_ctrl_base, 0, 0);
    	EMACRxIntPulseEnable(pdata->emac_base, pdata->emac_ctrl_base, 0, 0);
    	// Turn on misc interrupts: STAT = 0x8, HOST = 0x4, LINK = 0x2, USER = 0x1
    #warning "For correct WP_Line working on ev.board use MISC int. for Park only. Just uncomment \"if\" line"
      //if (0 == pdata->inst_num)
    		EMACMiscIntPulseEnable(pdata->emac_ctrl_base, 0, 0x2);
    
    	return NU_SUCCESS;
    }
    
    int emac_stop(struct net_device *dev)
    {
    	EMAC_DATA *pdata = netdev_priv(dev);
    
    	EMACTxTearDown(pdata->emac_base);
    	EMACRxTearDown(pdata->emac_base);
    
    	EMACInit(pdata->emac_ctrl_base, pdata->emac_base);
        return 0;
    }
    
    static int emac_no_buf_hard_start_xmit( struct m *buf_ptr )
    {
    	TXCH *txch;
        net_device *dev  = GetLanDevInfo(buf_ptr->m_ndp);
        EMAC_DATA *pdata = netdev_priv(dev);
    	volatile EMAC_TX_BD *curr_bd, *active_head, *bd_end;
    	int len_tot = 0, len = 0, i = 0;
    	U32 position = -1;
    	U8  *ptr;
    
    	dev->LockSemaphore(dev);
        // Total message length count
        if( buf_ptr->m_cell_count > 0 )
           	len_tot = buf_ptr->m_total_dlen + buf_ptr->m_ptail - buf_ptr->m_hp;
        else
        	len_tot = (m_dsize(buf_ptr));
    
    	if( len_tot > 1514 || len_tot == 0 )
    	{
    		EMAC_WARNING( "Error total packet length=%d", len_tot );
    		dev->UnockSemaphore(dev);
    		return -1;
    	}
    
        if( len_tot < MIN_PACKET_LENGTH )
               len_tot = MIN_PACKET_LENGTH;
    
    	txch = &(pdata->txch);
    
    	// Get the buffer descriptor which is free to transmit
    	curr_bd = txch->free_head;
    	// Save start address of transmit list
    	active_head = curr_bd;
        // Get header address
        ptr = (U8*)buf_ptr->m_hp;
        // Header length count
        if( buf_ptr->m_cell_count > 0 )
        	len = m_hsize(buf_ptr);
        else
        	len = len_tot;
    
        position = len;
    #ifdef DEBUG_DUMP_PACKETS
    	    		com_printf("SEND 0: ");
    	    		emac_dump_packets(ptr, len);
    #endif //DEBUG_DUMP_PACKETS
    	// Update the total packet length
    	curr_bd->flags_pktlen &= ~0xFFFF;
    	curr_bd->flags_pktlen |= len_tot;
    	// Indicate the start of the packet
    	curr_bd->flags_pktlen |= (EMAC_BUF_DESC_SOP | EMAC_BUF_DESC_OWNER);
    
    	// Copy pbuf information into TX buffer descriptors
    	curr_bd->bufptr = (U32)physical(ptr);
    	curr_bd->bufoff_len = len & 0xFFFF;
        bd_end = curr_bd;
        curr_bd = curr_bd->next;
    
    	// Copy pbuf information into TX buffer descriptors
    	// Send user data
        if( buf_ptr->m_cell_count > 0 )
        {
        	for( i = 0; i < buf_ptr->m_cell_count; i++ )
        	{
        		ptr = (U8*)buf_ptr->m_datacells[i].celldata;
        		len = (U32)buf_ptr->m_datacells[i].cellsize;
    #ifdef DEBUG_DUMP_PACKETS
    	    		com_printf("SEND %d: ",i+1);
    	    		emac_dump_packets(ptr, len);
    #endif //DEBUG_DUMP_PACKETS
    	    	curr_bd->bufptr = (U32)physical(ptr);
        	    curr_bd->bufoff_len = len & 0xFFFF;
        	    bd_end = curr_bd;
        	    curr_bd = curr_bd->next;
        	    position += len;
        	}
        }
    
    	// Indicate the end of the packet
    	bd_end->next = NULL;
    	bd_end->flags_pktlen |= EMAC_BUF_DESC_EOP;
    
    	// check if sum of all length not less then 60
    	if (position < MIN_PACKET_LENGTH)
    		bd_end->bufoff_len += (MIN_PACKET_LENGTH-position);
    
    	txch->free_head = curr_bd;
    
    	// For the first time, write the HDP with the filled bd
    	if(txch->active_tail == NULL)
    	{
    	    EMACTxHdrDescPtrWrite(pdata->emac_base, (unsigned int)(active_head), 0);
    	}
    
    	// Chain the bd's. If the DMA engine, already reached the end of the chain,
    	// the EOQ will be set. In that case, the HDP shall be written again.
    
    	else
    	{
    		curr_bd = txch->active_tail;
    	    curr_bd->next = active_head;
    	    if(curr_bd->flags_pktlen & EMAC_BUF_DESC_EOQ)
    	    {
    	      // Write the Header Descriptor Pointer and start DMA
    	      EMACTxHdrDescPtrWrite(pdata->emac_base, (unsigned int)(active_head), 0);
    	    }
    	}
    	txch->active_tail = bd_end;
    
    	dev->UnockSemaphore(dev);
        return position;
    }
    
    /* Entry point for transmitting a packet */
    static int emac_hard_start_xmit( void *ptr__t, int len__t, struct net_device *dev)
    {
    	TXCH *txch;
        EMAC_DATA *pdata = netdev_priv(dev);
    	volatile EMAC_TX_BD *curr_bd, *active_head, *bd_end;
    	U8  *ptr;
    	int len = 0;
    	int err;
    	dev->LockSemaphore(dev);
    
    	if( len__t > 1514 || len__t == 0 )
    	{
    		EMAC_WARNING( "Error total packet length=%d", len__t );
    		dev->UnockSemaphore(dev);
    		return -1;
    	}
    
        if( len < MIN_PACKET_LENGTH )
        	len = MIN_PACKET_LENGTH;
        else
        	len = len__t;
    
        // Allocate memory for packet
        ptr = (U8*)fnsNetDevAllocBuffer( dev->ndp, len, &err );
    	if( FNS_ENOERR != err)
    	{
    		EMAC_WARNING("ERROR fnsNetDevAllocBuffer %d\n",err);
    		dev->UnockSemaphore(dev);
    		return -1;
    	}
        memcpy(ptr,ptr__t,len);
    	txch = &(pdata->txch);
    
    	// Get the buffer descriptor which is free to transmit
    	curr_bd = txch->free_head;
    	// Save start address of transmit list
    	active_head = curr_bd;
    
    #ifdef DEBUG_DUMP_PACKETS
    	    		com_printf("BBN SEND: ");
    	    		emac_dump_packets(ptr, len);
    #endif //DEBUG_DUMP_PACKETS
    	/* Update the total packet length */
    	curr_bd->flags_pktlen &= ~0xFFFF;
    	curr_bd->flags_pktlen |= len;
    	/* Indicate the start of the packet */
    	curr_bd->flags_pktlen |= (EMAC_BUF_DESC_SOP | EMAC_BUF_DESC_OWNER | EMAC_BUF_DESC_EOP);
    
    	/* Copy pbuf information into TX buffer descriptors */
        curr_bd->bufptr = (U32)physical(ptr);
        curr_bd->bufoff_len = len & 0xFFFF;
        bd_end = curr_bd;
        curr_bd = curr_bd->next;
        // Indicate the end of the packet
        bd_end->next = NULL;
    
    	txch->free_head = curr_bd;
    
    	/* For the first time, write the HDP with the filled bd */
    	if(txch->active_tail == NULL)
    	{
    	    EMACTxHdrDescPtrWrite(pdata->emac_base, (unsigned int)(active_head), 0);
    	}
    
    	/*
    	 * Chain the bd's. If the DMA engine, already reached the end of the chain,
    	 * the EOQ will be set. In that case, the HDP shall be written again.
    	 */
    	else
    	{
    		curr_bd = txch->active_tail;
    	    curr_bd->next = active_head;
    	    if(curr_bd->flags_pktlen & EMAC_BUF_DESC_EOQ)
    	    {
    	      /* Write the Header Descriptor Pointer and start DMA */
    	      EMACTxHdrDescPtrWrite(pdata->emac_base, (unsigned int)(active_head), 0);
    	    }
    	}
    	txch->active_tail = bd_end;
    	dev->UnockSemaphore(dev);
    
        return len__t;
    }
    
    static int emac_xmit_complete(struct net_device *dev)
    {
    	TXCH *txch;
    	EMAC_DATA *pdata = netdev_priv(dev);
    	volatile EMAC_TX_BD *curr_bd, *next_bd_to_process;
    
    	txch = &(pdata->txch);
    	next_bd_to_process = txch->next_bd_to_process;
    
    	curr_bd = next_bd_to_process;
    
    	// Check for correct start of packet
    	while((curr_bd->flags_pktlen) & EMAC_BUF_DESC_SOP)
    	{
    
    		// Make sure that the transmission is over
    		#warning "eternal cycle - need to break when packet wrong"
    	    while((curr_bd->flags_pktlen & EMAC_BUF_DESC_OWNER) == EMAC_BUF_DESC_OWNER);
    
    	    // Traverse till the end of packet is reached
    	    while(((curr_bd->flags_pktlen) & EMAC_BUF_DESC_EOP) != EMAC_BUF_DESC_EOP)
    	    {
    	       curr_bd = curr_bd->next;
    	    }
    
    	    next_bd_to_process->flags_pktlen &= ~(EMAC_BUF_DESC_SOP);
    	    curr_bd->flags_pktlen &= ~(EMAC_BUF_DESC_EOP);
    
    	    // If there are no more data transmitted, the next interrupt
    	    // shall happen with the pbuf associated with the free_head
    	    if(curr_bd->next == NULL)
    	    	txch->next_bd_to_process = txch->free_head;
    	    else
    	    	txch->next_bd_to_process = curr_bd->next;
    
    	    // Acknowledge the EMAC
    	    EMACTxCPWrite(pdata->emac_base, 0, (U32)curr_bd);
    	    // Tell fusion that transmission is complete (not for bbn packets!)
    #ifdef OS_TARGET
    	    if( 1 == dev->i_devID && is_packet_backbone((U8*)virtual(curr_bd->bufptr),(curr_bd->bufoff_len)&0xFFFF) )
    	    {
    	    	fnsNetDevFreeBuffer( dev->ndp, (U8*)virtual(curr_bd->bufptr) );
    	    }
    	    else
    #endif
    	    {
    	    	fnsNetDevTxComplete(dev->ndp);
    	    }
    	    next_bd_to_process = txch->next_bd_to_process;
    	    curr_bd = next_bd_to_process;
    	}
    	return 0;
    }
    
    static ETH_DEVICE_STATS *emac_get_stats(struct net_device *dev)
    {
    	return &dev->stats;
    }
    
    static void emac_int_enable(struct net_device *p_dev)
    {
    	EMAC_DATA *pdata = NU_NULL;
    	pdata = netdev_priv(p_dev);
    
    	if (NU_NULL != pdata)
    	{
    		// Enable all active EMAC interrupts
    		EMACTxIntPulseEnable(pdata->emac_base, pdata->emac_ctrl_base, 0, 0);
    		EMACRxIntPulseEnable(pdata->emac_base, pdata->emac_ctrl_base, 0, 0);
    		EMACMiscIntPulseEnable(pdata->emac_ctrl_base, 0, 0x2);
    	}
    }
    
    static void emac_int_disable(struct net_device *p_dev)
    {
    	EMAC_DATA *pdata = NU_NULL;
    	pdata = netdev_priv(p_dev);
    
    	if (NU_NULL != pdata)
    	{
    		// Disable all active EMAC interrupts
    		EMACTxIntPulseDisable(pdata->emac_base, pdata->emac_ctrl_base, 0, 0);
    		EMACRxIntPulseDisable(pdata->emac_base, pdata->emac_ctrl_base, 0, 0);
    		EMACMiscIntPulseDisable(pdata->emac_ctrl_base, 0, 0xF);
    	}
    }
    
    static void emac_int_ack(struct net_device *p_dev, int vector)
    {
    	EMAC_DATA *pdata = NULL;
    	if (NULL == p_dev)
    	{
    		return;
    	}
    
    	pdata = netdev_priv(p_dev);
    
    	if ( ((0 == p_dev->i_devID) && (EMAC_RX_INT_0_VECTOR == vector)) ||
    		 ((1 == p_dev->i_devID) && (EMAC_RX_INT_1_VECTOR == vector))  )
    	{
    		EMACCoreIntAck(pdata->emac_base, EMAC_INT_CORE0_RX);
    	}
    	if ( ((0 == p_dev->i_devID) && (EMAC_TX_INT_0_VECTOR == vector)) ||
    	 	 ((1 == p_dev->i_devID) && (EMAC_TX_INT_1_VECTOR == vector))  )
    	{
    		EMACCoreIntAck(pdata->emac_base, EMAC_INT_CORE0_TX);
    	}
    	if ( ((0 == p_dev->i_devID) && (EMAC_RX_THR_0_VECTOR == vector)) ||
    		 ((1 == p_dev->i_devID) && (EMAC_RX_THR_1_VECTOR == vector))  )
    	{
    		//for future use
    	}
    	if ( ((0 == p_dev->i_devID) && (EMAC_MISCEL_0_VECTOR == vector)) ||
    		 ((1 == p_dev->i_devID) && (EMAC_MISCEL_1_VECTOR == vector))  )
    	{
    		EMACCoreIntAck(pdata->emac_base, EMAC_INT_CORE0_MISC);
    	}
    }
    
    int emac_init(struct net_device *dev)
    {
    	/* Get the instance number first */
    	EMAC_DATA *pdata;
    
    	pdata = netdev_priv(dev);
    
    	dev->LockSemaphore(dev);
    
    	dev->open 					= emac_open;
    	dev->stop 					= emac_stop;
    	dev->hard_start_xmit 		= emac_hard_start_xmit;
    	dev->no_buf_hard_start_xmit = emac_no_buf_hard_start_xmit;
    	dev->get_stats 				= emac_get_stats;
    	dev->misc 					= emac_misc;
    	dev->poll 					= emac_poll;
    	dev->xmit_complete 			= emac_xmit_complete;
    	dev->int_enable				= emac_int_enable;
    	dev->int_disable			= emac_int_disable;
    	dev->int_ack				= emac_int_ack;
    
    	pdata->inst_num = dev->i_devID;
    
    	_emac_inst_config(pdata);
    	dev->UnockSemaphore(dev);
    	return 0;
    }
    


    Hi, Pavel

    See source code in attachment. All other files we are using unchanged from "AM335x Industrial SDK".

    We are booing our device from NOR Flash,

    Sorry, source code is not of final quality yet.

    BR
    Ihor

  • Hi Ihor,

    First of all, you are using wrong SDK.

    For DM816x device, you should use EZSDK 5.05.01.04, available from here:

    http://software-dl.ti.com/dsps/dsps_public_sw/ezsdk/latest/index_FDS.html

    For AM389x device, you should use EZSDK 5.04.00.11, available from here:

    http://software-dl.ti.com/dsps/dsps_public_sw/ezsdk/5_04_00_11/index_FDS.html

    Can you try with the new SDK?

    Regards,

    Pavel