⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cs8900.c

📁 zilog的实时操作系统RZK,可以移植到多种处理器上
💻 C
📖 第 1 页 / 共 2 页
字号:
 /*
 * Copyright 2001, Metro Link, Inc.
 * All Rights Reserved
 *
 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Metro Link, Inc. and may
 * contain proprietary, confidential and trade secret information of
 * Metro Link, Inc. and/or its partners.
 *  
 * The contents of this file may not be disclosed to third parties, copied or
 * duplicated in any form, in whole or in part, without the prior written
 * permission of Metro Link, Inc.
 *
 */
/*
 * File			:	cs8900.c
 *
 * Description	:	Driver file for the crystal ethernet controller.
 *					
 * Copyright 2004, ZiLOG Inc.
 * All Rights Reserved
 *
 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of ZiLOG Inc., and might
 * contain proprietary, confidential and trade secret information of
 * ZiLOG, our partners and parties from which this code has been licensed.
 * 
 * The contents of this file may not be disclosed to third parties, copied or
 * duplicated in any form, in whole or in part, without the prior written
 * permission of ZiLOG Inc.
 */

#include <stdio.h>
#include "ZSysgen.h"
#include "ZTypes.h"
#include "ZThread.h"
#include "ZMessageQ.h"
#include "ZInterrupt.h"
#include "ZSemaphore.h"
#include "EtherMgr.h"
#include "cs8900a.h"
#include "_ez80.h"

#define TX_WAITING      2
#define TX_DONE         1
#define PKTTOOBIG	   -2


#define EMAC_POLL_THD_STACK_SIZE 1024
#define CS8900_LINK_OK 0x80

extern void PutToQueue(QUEUE_REF_t *refnode, QUEUE_NODE_t *node) ;
extern QUEUE_NODE_t *GetFromQueue(QUEUE_REF_t *refnode) ;
extern UINT8 EMAC_Task_Stack[] ;
extern UINT8 EMAC_TASK_PRIO ;
extern UINT EMAC_THD_STACK_SIZE ;
extern void emacisr(void) ;
extern RZK_MESSAGEQHANDLE_t rxQueueHandle ; /* receive queue handle */

void CS8900IntTask( void );
void cs8900PollTask(void) ;

RZK_THREADHANDLE_t							emacInterruptThdHdl ;	/* emac interrupt thread handle */
RZK_THREADHANDLE_t   						emacPollThdHdl ;	/* emac poll thread handle */
UINT8 										EMAC_Poll_Task_Stack[EMAC_POLL_THD_STACK_SIZE] ;

IORegExt16									p_emac_base = (IORegExt16) 0x300;	// IO Base address for CS8900A
INT8 										xinu_eth_irq = IV_PD4; 				// Use IV_NULL if no NIC Irq line.

UINT32          							emac_isr_count  = 0;

INT8 										doing_tx = RZK_FALSE;
INT8										tx_q_size = 0;
QUEUE_REF_t									mac_tx_q;

#define CS8900A_INTRQ0						0x00
#define CS8900A_INTRQ1						0x01
#define CS8900A_INTRQ2						0x02
#define CS8900A_INTRQ3						0x03
#define CS8900A_DISABLE_INTRQ				0x04
#define HASH_TABLE_SIZE						4


ETH_DEV_STRUCT_t       						emac[1];
void			  							(*rxnotifyfunc)(void);
EMAC_FRAME_t *								txbuffnext = 0;
ETH_PKT_t *									tx_pep_next;
UINT16                						CRCErrors, 
											RuntErrors, 
											ExcessLengthErrors, 
											RxMissCount,
											TxColCount;

INT8										bRegistersInitialized = RZK_FALSE;
IORegExt16  								pDataPort0;
IORegExt16									pTxCmd;
IORegExt16									pTxLength;
IORegExt16									pISQ;
IORegExt16									pPktPagePtr;
IORegExt16									pPktPageData0;
IORegExt8	  								pDataPortL0;
IORegExt8   								pDataPortH0;

UINT16             							RxFilter = IndividualA | BroadcastA | RxOKA;
UINT16             							HashTable[ HASH_TABLE_SIZE ] = {0,0,0,0};
INT8        								b_mc_count = 0x00;

RZK_SEMAPHOREHANDLE_t g_MacSem;

/*
 * Function prototypes.
 */
extern void RxFunc(void);

extern void
nic_read
(
	UINT16             			Length,
  	INT8 *							pBuf,
	IORegExt16						p_io_base
);
extern void
nic_discard
(
	UINT16             			Length,
	IORegExt16						p_io_base
);
extern void
nic_write
(
	UINT16             			Length,
  	INT8 *							pBuf,
	IORegExt16						p_io_base
);
void ETHMADDFUNC
(
	INT8 *							pAddr
);
INT8
MulticastHash
(
	INT8 *							ether_addr
);
static DDF_STATUS_t DataPathChk(void);



RZK_SEMAPHOREHANDLE_t g_MacSem;
/*
 * ===========================================================================
 * =
 */
void
setup_registers
(
	IORegExt16						pIOBase
)
{
	if( bRegistersInitialized == RZK_FALSE )
	{
		pDataPort0    = pIOBase + 0x00;
		pTxCmd        = pIOBase + 0x02;
		pTxLength     = pIOBase + 0x03;
		pISQ			  = pIOBase + 0x04;
		pPktPagePtr   = pIOBase + 0x05;
		pPktPageData0 = pIOBase + 0x06;

		pDataPortL0   = (IORegExt8)pIOBase + 0x00;
		pDataPortH0   = (IORegExt8)pIOBase + 0x01;

  		bRegistersInitialized = RZK_TRUE;
	}
}

void
EmacEnableIrq
(
	void
)
{
	setup_registers( p_emac_base );
	PKTPAGEPTR    = BUSCTL;
	PKTPAGEDATA0  = ADDR_BUSCTL | ENABLE_IRQ;
}


void
EmacDisableIrq
(
	void
)
{
	setup_registers( p_emac_base );
	PKTPAGEPTR    = BUSCTL;
	PKTPAGEDATA0  = ADDR_BUSCTL;
}



void 
EmacReset
(
	void
)
{
	volatile INT8					bData;

//kprintf("R+");
	/*
	 * Setup internal register used to access the CS8900A in IO mode. 
	 */
	setup_registers( p_emac_base );

	/*
	 * Read a byte from an odd memory location to make sure CS8900 internal 8bit
	 * state machine is synchronized to the driver.
	 */
	bData = DATAPORTH0;
	
	/*
    * Issue a software reset to CS8900S.  This will put the device into a known
 	 * initial state.
	 */	
	PKTPAGEPTR   = SELFCTL;
	PKTPAGEDATA0 = ADDR_SELFCTL | RESETBIT;
	PKTPAGEPTR   = SELFST;
	while( !(PKTPAGEDATA0 & INITDONE) )//changed
	{
		/*
	 	 * Wait till CS8900A init is complete 
		 */
	}

	/*
	 * Note that the Reset will Clear all interrupt enables and leave the CS8900A
	 * interrupt request lines in high impedance.  This may cause a voltage to 
	 * appear on the GPIO interrupt pin and generate spurious interrupts.
	 * Therefore, we always enable the Interrupt line of the CS8900A immediately
	 * after the reset is finished.  This will ensure that the IRQ line is driven
	 * low and since no interrupt enable bits have been set, no interrupts will
	 * be generated.
	 */

	/*
	 * Enable IRQ0 (active high)
	 */
	PKTPAGEPTR    = IRQNUMBER;
	PKTPAGEDATA0  = CS8900A_INTRQ0;
	PKTPAGEPTR    = BUSCTL;
	PKTPAGEDATA0  = ADDR_BUSCTL; // | ENABLE_IRQ;
//kprintf("R-");
}




/*----------------------------------------------------------------------------*/
DDF_STATUS_t
EthInitFunc
(
	INT8 *							ieeeaddr, 
	void							(*rxnotify)(void)
)
/*
 * ieeeaddr is pointer to a char array containing 6 bytes of MAC ID which is
 * this MAC's ethernet ID. rxnotify is the address of a function which this
 * driver will call on reception of a packet. rxnotify is the address of a
 * function which must be called every time a receive frame is available in HW.
 * 
 * Return value 0 - successful initialization
 * 
 */
{
	extern INT8 b_poll_emac;
	UINT16 i,j;
	CRCErrors = RuntErrors = ExcessLengthErrors = RxMissCount = TxColCount = 0;

	g_MacSem = RZKCreateSemaphore((RZK_NAME_t*)"g_MacSem",1,RECV_ORDER_PRIORITY);
	if(g_MacSem == NULL)
		printf("\nEmac Semaphore creation error");
	EmacReset();

	/* Check if we can read the CS8900A ID */	
	PKTPAGEPTR = PRODUCTCODE;
	if( PKTPAGEDATA0 != CS8900ID )
	{
		printf("\r\nCS8900 not found ");
		return (CS8900_NOTFOUND);
	};

	/* Databus path checkout */
	if ((i = DataPathChk()) != 0) 
	{
		printf("\r\nDatapath problem detected\n");
		return (CS8900_DIAGFAIL1);
	}
	/* Setup our 6 byte (48 bit) IEEE MAC address */
	for (i = 0; i < 3; i++) 
	{
//		PKTPAGEPTR   = (unsigned short)IEEEADDR + i * 2;
		j = IEEEADDR + i * 2;
		PKTPAGEPTR   = j;
		PKTPAGEDATA0 = *(UINT16 *)ieeeaddr;
		ieeeaddr += 2;	
	}


/* Create Interrupt thread */
	emacInterruptThdHdl = RZKCreateThreadEnhanced((RZK_NAME_t*)"EMACIT",
													(RZK_PTR_t)CS8900IntTask,
													NULL, 
													EMAC_THD_STACK_SIZE, 
													EMAC_TASK_PRIO, 
													2,
													RZK_THREAD_INTERRUPT|RZK_THREAD_PREEMPTION,
													0 ) ;
		if( emacInterruptThdHdl == NULL )
		{
			return EMACDEV_ERR_RESOURCE_NOT_CREATED ;
		}

/* Create Interrupt thread */
	emacPollThdHdl = RZKCreateThreadEnhanced((RZK_NAME_t*)"EMACpoll",
													(RZK_PTR_t)cs8900PollTask,
													NULL, 
													EMAC_POLL_THD_STACK_SIZE, 
													EMAC_TASK_PRIO, 
													2,
													RZK_THREAD_PREEMPTION|RZK_THREAD_AUTOSTART,
													0 ) ;
		if( emacPollThdHdl == NULL )
		{
			return EMACDEV_ERR_RESOURCE_NOT_CREATED ;
		}

		

/* Install interrupt Handler */
#ifndef _EZ80190
	RZKInstallInterruptHandler( (RZK_FNP_ISR)emacisr, 0x58) ;
#else
	RZKInstallInterruptHandler( (RZK_FNP_ISR)emacisr, 0x4E) ;
#endif

	PKTPAGEPTR    = BUSCTL;
	PKTPAGEDATA0  = ADDR_BUSCTL | ENABLE_IRQ;

	/*
	 * Save the interrupt callback addresses
	 */
	rxnotifyfunc = rxnotify;	
	if( rxnotifyfunc == 0 )
	{
		return (ILLEGAL_CALLBACKS);
	}

	/*
	 * Start the mac poll thread if requested by user.
	 */

	PKTPAGEPTR   = LINECTL;
	PKTPAGEDATA0 = SerTxON | SerRxON | ADDR_LINECTL;
	SetRxFilter(RxFilter, (INT8 *) &HashTable[0]);

	PKTPAGEPTR   = TXCFG;
	PKTPAGEDATA0 = ADDR_TXCFG | ENABLE_ALLTXCFG;	/* Enable Tx completion INTR */
	PKTPAGEPTR   = BUFCFG;
	PKTPAGEDATA0 = ADDR_BUFCFG | ENABLE_RDY4TXINT | 0x400; // | 0x8800;	/* Enable Rdy4Tx notifications */
	PKTPAGEPTR   = RXCFG;
	PKTPAGEDATA0 = ADDR_RXCFG | BIT_RXOK;//RXALLEVENTS;	/* Enable RxOK and Err bits */


	return (EMACDEV_ERR_PEMAC_INIT_DONE);}


/*----------------------------------------------------------------------------*/


static DDF_STATUS_t
DataPathChk(void)
{
	PKTPAGEPTR   = IEEEADDR;
	PKTPAGEDATA0 = 0x5555;
	if( PKTPAGEDATA0 != 0x5555)
		return (0x5555 ^ PKTPAGEDATA0);

	PKTPAGEPTR   = (IEEEADDR + 2);
	PKTPAGEDATA0 = 0xAAAA;
	if( PKTPAGEDATA0 != 0xAAAA)
		return (0xAAAA ^ PKTPAGEDATA0);

	return (0);}

	
/*----------------------------------------------------------------------------*/


static void 
SetRxFilter(UINT16 rxflags, INT8 *cHashbits)
{
	UINT16          *Hashbits = (UINT16 *) cHashbits;
	UINT16           i;
	UINT16				temp;

	PKTPAGEPTR    = LINECTL;
	PKTPAGEDATA0 &= ~SerRxON;	/* Disable serial recv */	
	if (Hashbits != 0) 
	{	
		/* Not NULL if a valid hash index field is given */		
		for (i = 0; i < HASH_TABLE_SIZE; i++) 
		{
//			PKTPAGEPTR   = LAFILTER + (i<<1);
			temp = i<<1;
			temp += LAFILTER;
			PKTPAGEPTR   = temp;
 			PKTPAGEDATA0 = Hashbits[i];
		}	
	}	
	/* mask user flag word for valid bits and update RXCTL */	
	PKTPAGEPTR   = RXCTL;
	PKTPAGEDATA0 = rxflags | ADDR_RXCTL;

	PKTPAGEPTR    = LINECTL;
	PKTPAGEDATA0 |= SerRxON;
}



UINT16
TransmitNextPkt
(
	void
)
/*
 * Assumes interrupts or off.
 */
{
	UINT16  	 			BufAvail = RZK_FALSE;
	UINTRMASK intmask ;

//	kprintf( "Do Next Tx @ %x\n", SysUpTime );
	if( doing_tx == RZK_FALSE )
	{
/* Deque from the queue */
		tx_pep_next = (ETH_PKT_t *)	GetFromQueue( &mac_tx_q );
		if( tx_pep_next )
		{
			intmask = RZKDisableInterrupts() ;
			doing_tx = RZK_TRUE;
//			tx_q_size--;

			txbuffnext  = (EMAC_FRAME_t *) &tx_pep_next->ethPktOrder;
			TXCMD      = 0x00C0;
			TXLENGTH   = txbuffnext->length;
	   		PKTPAGEPTR = BUSST;
			BufAvail   = PKTPAGEDATA0 & RDY4TXNOW;
			RZKEnableInterrupts(intmask) ;
		}
	}
	return( BufAvail );
}


//UINT16 max_queued = 0;

DDF_STATUS_t
EthPktTransmit
(
	ETH_PKT_t *						pep
)
{	
	DDF_STATUS_t	status;
	RZK_STATE_t					CritFlag;										

	if( pep->ethPktLen <= MAXFRAMESIZE ) 
	{

		status = EMACDEV_ERR_TX_WAITING;    // CR6168 - MSS

/* Queue the packet to the Tx Queue */
//		CritFlag = RZKDisablePreemption() ;
		RZKAcquireSemaphore(g_MacSem,MAX_INFINITE_SUSPEND);

		PutToQueue( &mac_tx_q, &pep->link ); //Moved after disable preemption

		if( TransmitNextPkt() )
		{

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -