📄 drv_lan91c111.c
字号:
while (timeout--) {
status = smc_read_phy_register (PHY_STAT_REG);
if (status & PHY_STAT_ANEG_ACK) {
/* auto-negotiate complete */
break;
}
//smc_wait_ms (500); /* wait 500 millisecs */
OSTimeDly( OS_DELAY_TIME(dSYSTEM_TIME_520MS));
/* Restart auto-negotiation if remote fault */
if (status & PHY_STAT_REM_FLT) {
OS_DEBUGF(DEV_91C111_DEBUG, ("%s:PHY remote fault detected\r\n",SMC_DEV_NAME));
/* Restart auto-negotiation */
OS_DEBUGF(DEV_91C111_DEBUG, ("%s:PHY restarting auto-negotiation\r\n",SMC_DEV_NAME));
smc_write_phy_register (PHY_CNTL_REG,
PHY_CNTL_ANEG_EN |
PHY_CNTL_ANEG_RST |
PHY_CNTL_SPEED |
PHY_CNTL_DPLX);
}
}
if (timeout < 1) {
OS_DEBUGF(DEV_91C111_DEBUG, ("%s:PHY auto-negotiate timed out\r\n", SMC_DEV_NAME));
}
/* Fail if we detected an auto-negotiate remote fault */
if (status & PHY_STAT_REM_FLT) {
OS_DEBUGF(DEV_91C111_DEBUG, ("%s:PHY remote fault detected\r\n", SMC_DEV_NAME));
}
/* Re-Configure the Receive/Phy Control register */
SMC_outw (RPC_DEFAULT, RPC_REG);
}
/*
* Open and Initialize the board
*
* Set up everything, reset the card, etc ..
*
*/
static int smc_open (void)
{
int i;
/* reset the hardware */
smc_reset ();
smc_enable ();
/* conservative setting (10Mbps, HalfDuplex, no AutoNeg.) */
/* Configure the Receive/Phy Control register */
smc_phy_configure ();
SMC_SELECT_BANK (1);
for (i = 0; i < 6; i += 2)
{
INT16U address;
address = smc_mac_addr[i + 1] << 8;
address |= smc_mac_addr[i];
SMC_outw (address, ADDR0_REG + i);
}
return 0;
}
INT32U eth_init (void)
{
smc_open();
return (0);
}
#if 0
static void print_packet (INT8U * buf, INT32U length)
{
INT32U i;
INT32U remainder;
INT32U lines;
char str[1024];
char tmp[100];
sprintf (str,"\r\nPacket of length %d \r\n", length);
lines = length / 16;
remainder = length % 16;
for (i = 0; i < lines; i++) {
INT32U cur;
for (cur = 0; cur < 8; cur++) {
INT8U a, b;
a = *(buf++);
b = *(buf++);
sprintf (tmp,"%02x%02x ", a, b);
strcat(str,tmp);
}
sprintf (tmp,"\r\n");
strcat(str,tmp);
}
for (i = 0; i < remainder / 2; i++) {
INT8U a, b;
a = *(buf++);
b = *(buf++);
sprintf (tmp,"%02x%02x ", a, b);
strcat(str,tmp);
}
sprintf (tmp,"\r\n");
strcat(str,tmp);
debug_printf("%s",str);
}
#endif
/*-------------------------------------------------------------
.
. smc_rcv - receive a packet from the card
.
. There is ( at least ) a packet waiting to be read from
. chip-memory.
.
. o Read the status
. o If an error, record it
. o otherwise, read in the packet
--------------------------------------------------------------
*/
static int smc_rcv(void)
{
int packet_number;
INT16U status;
INT16U packet_length;
INT32U is_error = 0;
INT8U saved_pnr;
INT16U saved_ptr;
SMC_SELECT_BANK(2);
/* save PTR and PTR registers */
saved_pnr = SMC_inb( PN_REG );
saved_ptr = SMC_inw( PTR_REG );
packet_number = SMC_inw( RXFIFO_REG );
if ( packet_number & RXFIFO_REMPTY ) {
//SMC_outw (MC_FREEPKT, MMU_CMD_REG);
return 0;
}
/* start reading from the start of the packet */
SMC_outw( PTR_READ | PTR_RCV | PTR_AUTOINC, PTR_REG );
/* First two words are status and packet_length */
status = SMC_inw( SMC91111_DATA_REG );
packet_length = SMC_inw( SMC91111_DATA_REG );
packet_length &= 0x07ff; /* mask off top bits */
OS_DEBUGF(DEV_91C111_DEBUG,("RCV: STATUS %4x LENGTH %4x\r\n", status, packet_length ));
if ( !(status & RS_ERRORS ) ){
/* Adjust for having already read the first two words */
packet_length -= 4; /*4; */
/* set odd length for bug in LAN91C111, */
/* which never sets RS_ODDFRAME */
/* TODO ? */
OS_DEBUGF(DEV_91C111_DEBUG,(" Reading %d words and %d byte(s) \r\n",(packet_length >> 1 ), packet_length & 1 ));
SMC_insw(SMC91111_DATA_REG , RxPackets, packet_length >> 1);
OS_DEBUGF(DEV_91C111_DEBUG,("Receiving Packet\r\n"));
//print_packet( RxPackets, packet_length );
} else {
/* error ... */
/* TODO ? */
is_error = 1;
}
//while ( SMC_inw( MMU_CMD_REG ) & MC_BUSY )
udelay(100); /* Wait until not busy */
/* error or good, tell the card to get rid of this packet */
SMC_outw( MC_RELEASE, MMU_CMD_REG );
//while ( SMC_inw( MMU_CMD_REG ) & MC_BUSY )
udelay(100); /* Wait until not busy */
/* restore saved registers */
SMC_outb( saved_pnr, PN_REG );
SMC_outw( saved_ptr, PTR_REG );
if (!is_error) {
/* Pass the packet up to the protocol layers. */
RxPacketsLen = packet_length;
return packet_length;
} else {
return 0;
}
}
/*
. Function: smc_hardware_send_packet(struct net_device * )
. Purpose:
. This sends the actual packet to the SMC9xxx chip.
.
. Algorithm:
. First, see if a saved_skb is available.
. ( this should NOT be called if there is no 'saved_skb'
. Now, find the packet number that the chip allocated
. Point the data pointers at it in memory
. Set the length word in the chip's memory
. Dump the packet to chip memory
. Check if a last byte is needed ( odd length packet )
. if so, set the control flag right
. Tell the card to send it
. Enable the transmit interrupt, so I know if it failed
. Free the kernel data if I actually sent it.
*/
static int smc_send_packet (volatile void *packet, INT32U packet_length)
{
INT8U packet_no;
unsigned long ioaddr;
INT8U *buf;
INT32U length;
INT32U numPages;
INT32U try = 0;
INT32U time_out;
INT8U status;
INT8U saved_pnr;
INT16U saved_ptr;
/* save PTR and PNR registers before manipulation */
SMC_SELECT_BANK (2);
saved_pnr = SMC_inb( PN_REG );
saved_ptr = SMC_inw( PTR_REG );
OS_DEBUGF(DEV_91C111_DEBUG,("%s:smc_hardware_send_packet\r\n", SMC_DEV_NAME));
length = ETH_ZLEN < packet_length ? packet_length : ETH_ZLEN;
/* allocate memory
** The MMU wants the number of pages to be the number of 256 bytes
** 'pages', minus 1 ( since a packet can't ever have 0 pages :) )
**
** The 91C111 ignores the size bits, but the code is left intact
** for backwards and future compatibility.
**
** Pkt size for allocating is data length +6 (for additional status
** words, length and ctl!)
**
** If odd size then last byte is included in this header.
*/
numPages = ((length & 0xfffe) + 6);
numPages >>= 8; /* Divide by 256 */
if (numPages > 7) {
OS_DEBUGF(DEV_91C111_DEBUG, ("%s: Far too big packet error. \r\n", SMC_DEV_NAME));
return 0;
}
/* now, try to allocate the memory */
SMC_SELECT_BANK (2);
SMC_outw (MC_ALLOC | numPages, MMU_CMD_REG);
/* FIXME: the ALLOC_INT bit never gets set *
* so the following will always give a *
* memory allocation error. *
* same code works in armboot though *
* -ro
*/
again:
try++;
time_out = MEMORY_WAIT_TIME;
do {
status = SMC_inb (SMC91111_INT_REG);
if (status & IM_ALLOC_INT) {
/* acknowledge the interrupt */
SMC_outb (IM_ALLOC_INT, SMC91111_INT_REG);
break;
}
} while (--time_out);
if (!time_out) {
OS_DEBUGF(DEV_91C111_DEBUG, ("%s: memory allocation, try %d failed ...\r\n", SMC_DEV_NAME, try));
if (try < SMC_ALLOC_MAX_TRY)
{
if(try < SMC_ALLOC_MAX_TRY-2)
{
smc_rcv();
OS_DEBUGF(DEV_91C111_DEBUG, ("smc_rcv\r\n"));
goto again;
}else
{
SMC_outw (MC_FREEPKT, MMU_CMD_REG);
OS_DEBUGF(DEV_91C111_DEBUG, ("smc_outw\r\n"));
goto again;
}
}
else
{
OS_DEBUGF(DEV_91C111_DEBUG, ("%s: memory allocation, failed end\r\n", SMC_DEV_NAME));
return 0;
}
}
OS_DEBUGF(DEV_91C111_DEBUG, ("%s: memory allocation, try %d succeeded ...\r\n", SMC_DEV_NAME, try));
/* I can send the packet now.. */
ioaddr = SMC_BASE_ADDRESS;
ioaddr = ioaddr;
buf = (INT8U *) packet;
/* If I get here, I _know_ there is a packet slot waiting for me */
packet_no = SMC_inb (AR_REG);
if (packet_no & AR_FAILED) {
/* or isn't there? BAD CHIP! */
OS_DEBUGF(DEV_91C111_DEBUG, ("%s: Memory allocation failed. \r\n", SMC_DEV_NAME));
return 0;
}
/* we have a packet address, so tell the card to use it */
SMC_outb (packet_no, PN_REG);
/* do not write new ptr value if Write data fifo not empty */
while ( saved_ptr & PTR_NOTEMPTY )
OS_DEBUGF(DEV_91C111_DEBUG, ("Write data fifo not empty!\r\n"));
/* point to the beginning of the packet */
SMC_outw (PTR_AUTOINC, PTR_REG);
OS_DEBUGF(DEV_91C111_DEBUG, ("%s: Trying to xmit packet of length %x\r\n", SMC_DEV_NAME, length));
OS_DEBUGF(DEV_91C111_DEBUG, ("Transmitting Packet\r\n"));
//print_packet (buf, length);
/* send the packet length ( +6 for status, length and ctl byte )
and the status word ( set to zeros ) */
SMC_outw (0, SMC91111_DATA_REG);
/* send the packet length ( +6 for status words, length, and ctl */
SMC_outw ((length + 6), SMC91111_DATA_REG);
/* send the actual data
. I _think_ it's faster to send the longs first, and then
. mop up by sending the last word. It depends heavily
. on alignment, at least on the 486. Maybe it would be
. a good idea to check which is optimal? But that could take
. almost as much time as is saved?
*/
SMC_outsw (SMC91111_DATA_REG, buf, (length) >> 1);
/* Send the last byte, if there is one. */
if ((length & 1) == 0) {
SMC_outw (0, SMC91111_DATA_REG);
} else {
SMC_outw (buf[length - 1] | 0x2000, SMC91111_DATA_REG);
}
/* and let the chipset deal with it */
SMC_outw (MC_ENQUEUE, MMU_CMD_REG);
/* poll for TX INT */
/* if (poll4int (IM_TX_INT, SMC_TX_TIMEOUT)) { */
/* poll for TX_EMPTY INT - autorelease enabled */
if (poll4int(IM_TX_EMPTY_INT, SMC_TX_TIMEOUT)) {
/* sending failed */
OS_DEBUGF(DEV_91C111_DEBUG, ("%s: TX timeout, sending failed...\r\n", SMC_DEV_NAME));
/* release packet */
/* no need to release, MMU does that now */
/* SMC_outw (MC_FREEPKT, MMU_CMD_REG); */
/* wait for MMU getting ready (low) */
while (SMC_inw (MMU_CMD_REG) & MC_BUSY) {
udelay (10);
}
OS_DEBUGF(DEV_91C111_DEBUG, ("MMU ready\r\n"));
return 0;
} else {
/* ack. int */
SMC_outb (IM_TX_EMPTY_INT, SMC91111_INT_REG);
/* SMC_outb (IM_TX_INT, SMC91111_INT_REG); */
OS_DEBUGF(DEV_91C111_DEBUG, ("%s: Sent packet of length %d \r\n", SMC_DEV_NAME, length));
/* release packet */
/* no need to release, MMU does that now */
/* SMC_outw (MC_FREEPKT, MMU_CMD_REG); */
/* wait for MMU getting ready (low) */
while (SMC_inw (MMU_CMD_REG) & MC_BUSY) {
udelay (10);
}
OS_DEBUGF(DEV_91C111_DEBUG, ("MMU ready\r\n"));
}
/* restore previously saved registers */
SMC_outb( saved_pnr, PN_REG );
SMC_outw( saved_ptr, PTR_REG );
return length;
}
INT32U eth_rx (void)
{
smc_enable();
return smc_rcv();
}
INT32U eth_send (INT8U *packet, INT32U length)
{
return smc_send_packet(packet, length);
}
void smc_get_macaddr (INT8U *addr)
{
SMC_SELECT_BANK (1);
*(INT16U *)(addr+0) = SMC_inw(ADDR0_REG);
*(INT16U *)(addr+2) = SMC_inw(ADDR1_REG);
*(INT16U *)(addr+4) = SMC_inw(ADDR2_REG);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -