📄 ether-smc91x.c
字号:
// ported by hzh#include <stdio.h>#include <string.h>#include "utils.h"typedef unsigned long u32;typedef unsigned short u16;typedef unsigned char u8;#define inline __inline#define EINVAL 22#define msleep mdelay#define readb(a) (*(volatile u8*)(a))#define readw(a) (*(volatile u16*)(a))#define readl(a) (*(volatile u32*)(a))#define writeb(v,a) (*(volatile u8*)(a)=(v))#define writew(v,a) (*(volatile u16*)(a)=(v))#define writel(v,a) (*(volatile u32*)(a)=(v))#define insb(a,p,l) ({ \ int __i; \ for (__i=0; __i<(l); __i++) \ ((u8*)(p))[__i]= readb((a)); \})#define insw(a,p,l) ({ \ int __i; \ for (__i=0; __i<(l); __i++) \ ((u16*)(p))[__i] = readw((a)); \})#define insl(a,p,l) { \ int __i; \ for (__i=0; __i<(l); __i++) \ ((u32*)(p))[__i] = readl((a)); \}#define outsb(a,p,l) ({ \ int __i; \ for (__i=0; __i<(l); __i++) \ writeb(((u8*)(p))[__i],(a)); \})#define outsw(a,p,l) ({ \ int __i; \ for (__i=0; __i<(l); __i++) \ writew(((u16*)(p))[__i],(a)); \})#define outsl(a,p,l) { \ int __i; \ for (__i=0; __i<(l); __i++) \ writel(((u32*)(p))[__i],(a)); \}#define SMC_BASE 0x10000300u8 hwaddress[6];typedef struct { int (*init)(u32 ioaddr); int (*rcv)(u8 *buf, int size); int (*snd)(u8 *buf, int size); int (*set_ethaddr)(u8 dev_addr[6]); int (*get_ethaddr)(u8 dev_addr[6]); int base;} ether_driver_t;//--------------------------------------------------------#define MAINSTONE#define SMC_DEBUG 0#include "smc91x.h"/*************************************************************************** * defines */#if SMC_DEBUG# define DBG( x, args... ) if ( dbg>=x ) printf( args )#else# define DBG( x, args... ) ;#endif/*------------------------------------------------------------------------ . . The internal workings of the driver. If you are changing anything . here with the SMC stuff, you should have the datasheet and know . what you are doing. . -------------------------------------------------------------------------*/#define CARDNAME "LAN91x"// Use power-down feature of the chip#define POWER_DOWN 1/* . Wait time for memory to be free. This probably shouldn't be . tuned that much, as waiting for this means nothing else happens . in the system*/#define MEMORY_WAIT_TIME 16/* . This selects whether TX packets are sent one by one to the SMC91x internal . memory ans throttled until transmission completes. This may prevent . RX overruns a litle by keeping much of the memory free for RX packets . but to the expense of reduced TX throughput and increased IRQ overhead. . Note this is not a cure for a too slow data bus or too high IRQ latency. */#define THROTTLE_TX_PKTS 0/********************************************************************** * Local */static int smc_chip_base = 0;static int dbg = 3;#if SMC_DEBUGstatic void HEXDUMP(u8 *buf, int length){ int i; int remainder; int lines; lines = length / 16; remainder = length % 16; for (i = 0; i < lines ; i ++) { int cur; for (cur = 0; cur < 8; cur++) { u8 a, b; a = *buf++; b = *buf++; printf("%02x%02x ", a, b); } printf("\n"); } for (i = 0; i < remainder/2 ; i++) { u8 a, b; a = *buf++; b = *buf++; printf("%02x%02x ", a, b ); } printf("\n");}#else#define HEXDUMP(x...) do { } while(0)#endif/* this enables an interrupt in the interrupt mask register */#define SMC_ENABLE_INT(x) do { \ unsigned long flags; \ unsigned char mask; \ mask = SMC_GET_INT_MASK(); \ mask |= (x); \ SMC_SET_INT_MASK(mask); \} while (0)/* this disables an interrupt from the interrupt mask register */#define SMC_DISABLE_INT(x) do { \ unsigned long flags; \ unsigned char mask; \ mask = SMC_GET_INT_MASK(); \ mask &= ~(x); \ SMC_SET_INT_MASK(mask); \} while (0)/* wait while MMU is busy */#define SMC_WAIT_MMU_BUSY() do { \ if (SMC_GET_MMU_CMD() & MC_BUSY) { \ long timeout = 0x1000; \ while (SMC_GET_MMU_CMD() & MC_BUSY) { \ timeout --; \ if (!timeout) { \ printf("%s: timeout %s line %d\n", \ CARDNAME, __FILE__, __LINE__); \ break; \ } \ } \ } \} while (0)/********************************************************************** * Core routines *//* this does a soft reset on the device */static void smc_reset(u32 ioaddr){ DBG(2, "%s: %s\n", CARDNAME, __FUNCTION__); /* This resets the registers mostly to defaults, but doesn't affect EEPROM. That seems unnecessary */ SMC_SELECT_BANK( 0 ); SMC_SET_RCR( RCR_SOFTRST ); /* Setup the Configuration Register */ /* This is necessary because the CONFIG_REG is not affected */ /* by a soft reset */ SMC_SELECT_BANK( 1 ); SMC_SET_CONFIG( CONFIG_DEFAULT ); /* Setup for fast accesses if requested */ /* If the card/system can't handle it then there will */ /* be no recovery except for a hard reset or power cycle */ SMC_SET_CONFIG( SMC_GET_CONFIG() | CONFIG_NO_WAIT );#ifdef POWER_DOWN /* Release from possible power-down state */ /* Configuration register is not affected by Soft Reset */ SMC_SELECT_BANK( 1 ); SMC_SET_CONFIG( SMC_GET_CONFIG() | CONFIG_EPH_POWER_EN );#endif /* this should pause enough for the chip to be happy */ msleep(1); /* Disable transmit and receive functionality */ SMC_SELECT_BANK( 0 ); SMC_SET_RCR( RCR_CLEAR ); SMC_SET_TCR( TCR_CLEAR ); /* set the control register to automatically release successfully transmitted packets, to make the best use out of our limited memory */ SMC_SELECT_BANK( 1 );#if ! THROTTLE_TX_PKTS SMC_SET_CTL( SMC_GET_CTL() | CTL_AUTO_RELEASE );#else SMC_SET_CTL( SMC_GET_CTL() & ~CTL_AUTO_RELEASE );#endif /* Disable all interrupts */ SMC_SELECT_BANK( 2 ); SMC_SET_INT_MASK( 0 ); /* Reset the MMU */ SMC_SET_MMU_CMD( MC_RESET ); SMC_WAIT_MMU_BUSY();}/* Enable Interrupts, Receive, and Transmit */static void smc_enable(u32 ioaddr){ int mask; DBG(2, "%s: %s\n", CARDNAME, __FUNCTION__); /* see the header file for options in TCR/RCR DEFAULT*/ SMC_SELECT_BANK( 0 ); SMC_SET_TCR( TCR_DEFAULT); SMC_SET_RCR( RCR_DEFAULT); /* now, enable interrupts */ mask = IM_EPH_INT|IM_RX_OVRN_INT|IM_RCV_INT; SMC_SELECT_BANK( 2 ); SMC_SET_INT_MASK( mask );}/* this puts the device in an inactive state */static void smc_shutdown(unsigned long ioaddr){ DBG(2, "%s: %s\n", CARDNAME, __FUNCTION__); /* no more interrupts for me */ SMC_SELECT_BANK( 2 ); SMC_SET_INT_MASK( 0 ); /* and tell the card to stay away from that nasty outside world */ SMC_SELECT_BANK( 0 ); SMC_SET_RCR( RCR_CLEAR ); SMC_SET_TCR( TCR_CLEAR );#ifdef POWER_DOWN /* finally, shut the chip down */ SMC_SELECT_BANK( 1 ); SMC_SET_CONFIG( SMC_GET_CONFIG() & ~CONFIG_EPH_POWER_EN );#endif}/* This is the procedure to handle the receipt of a packet. */static inline int smc_rcv(u32 ioaddr, u8 *data, u16 size){ unsigned int packet_number, status, packet_len; int ret = 0; DBG(3, "%s: %s\n", CARDNAME, __FUNCTION__); packet_number = SMC_GET_RXFIFO(); if (packet_number & RXFIFO_REMPTY) { DBG(1, "%s: smc_rcv with nothing on FIFO.\n", CARDNAME); return 0; } /* read from start of packet */ SMC_SET_PTR( PTR_READ | PTR_RCV | PTR_AUTOINC ); /* First two words are status and packet length */ SMC_GET_PKT_HDR(status, packet_len); packet_len &= 0x07ff; /* mask off top bits */ DBG(2, "%s: RX PNR 0x%x STATUS 0x%04x LENGTH 0x%04x (%d)\n", CARDNAME, packet_number, status, packet_len, packet_len); if (status & RS_ERRORS) { printf(" smc_rcv : RCV error\n"); ret = -1; goto done; } if (status & RS_ODDFRAME) packet_len ++; packet_len -= 6; SMC_PULL_DATA(data, packet_len); HEXDUMP(data, packet_len); ret = packet_len;done: SMC_WAIT_MMU_BUSY(); SMC_SET_MMU_CMD( MC_RELEASE ); return ret;}/* * This is called to actually send a packet to the chip. * Returns non-zero when successful. */static void smc_hardware_send_packet( u32 ioaddr , u8 *data , int size){ unsigned int packet_no, len; unsigned char *buf; DBG(3, "%s: %s\n", CARDNAME, __FUNCTION__); packet_no = SMC_GET_AR(); if (packet_no & AR_FAILED) { printf("%s: Memory allocation failed.\n", CARDNAME); return; } /* point to the beginning of the packet */ SMC_SET_PN( packet_no ); SMC_SET_PTR( PTR_AUTOINC ); buf = data; len = size; DBG(2, "%s: TX PNR 0x%x lENGTH 0x%04x (%d) BUF 0x%p\n", CARDNAME, packet_no, len, len, buf); HEXDUMP(buf, len); /* * Send the packet length ( +6 for status words, length, and ctl. * The card will pad to 64 bytes with zeroes if packet is too small. */ SMC_PUT_PKT_HDR(0, len + 6); /* send the actual data */ SMC_PUSH_DATA(buf, len & ~1); /* Send final ctl word with the last byte if there is one */ SMC_outw( ((len & 1) ? (0x2000 | buf[len-1]) : 0), ioaddr, DATA_REG ); /* and let the chipset deal with it */ SMC_SET_MMU_CMD( MC_ENQUEUE );}/* . Since I am not sure if I will have enough room in the chip's ram . to store the packet, I call this routine which either sends it . now, or set the card to generates an interrupt when ready . for the packet. */static intsmc_send( u32 ioaddr, u8 *data, int size ){ unsigned int numPages, poll_count, status, saved_bank; DBG(3, "%s: %s\n", CARDNAME, __FUNCTION__); /* ** 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 ctl word. */ numPages = ((size & ~1) + (6 - 1)) >> 8; if (numPages > 7) { printf("%s: Far too big packet error.\n", CARDNAME); return 0; } /* now, try to allocate the memory */ saved_bank = SMC_CURRENT_BANK(); SMC_SELECT_BANK( 2 ); SMC_SET_MMU_CMD( MC_ALLOC | numPages ); /* * Poll the chip for a short amount of time in case the * allocation succeeds quickly. */ poll_count = MEMORY_WAIT_TIME; do { status = SMC_GET_INT(); if (status & IM_ALLOC_INT) { SMC_ACK_INT( IM_ALLOC_INT ); break; } } while (--poll_count); if (!poll_count) { printf("smc_send: memory allocation time out.\n"); return -1; } SMC_ENABLE_INT(IM_TX_INT | IM_TX_EMPTY_INT); smc_hardware_send_packet( ioaddr, data, size ); /* waiting for transmit done */ while ( !((status = SMC_GET_INT()) & (IM_TX_INT|IM_TX_EMPTY_INT))); if (status & IM_TX_INT) SMC_ACK_INT(IM_TX_INT); if (status & IM_TX_EMPTY_INT) SMC_ACK_INT(IM_TX_EMPTY_INT); SMC_SELECT_BANK( saved_bank ); return 0;}/********************************************************************** * PHY Control and Configuration *//*------------------------------------------------------------ . Debugging function for viewing MII Management serial bitstream .-------------------------------------------------------------*/#if SMC_DEBUG static voidPRINT_MII_STREAM(u8 *bits, int size){ int i; printf("BIT#:"); for (i = 0; i < size; ++i) printf("%d", i%10); printf("\nMDOE:"); for (i = 0; i < size; ++i) { if (bits[i] & MII_MDOE) printf("1"); else printf("0"); } printf("\nMDO :"); for (i = 0; i < size; ++i) { if (bits[i] & MII_MDO) printf("1"); else printf("0"); } printf("\nMDI :"); for (i = 0; i < size; ++i) { if (bits[i] & MII_MDI) printf("1"); else printf("0"); } printf("\n");}#else#define PRINT_MII_STREAM(x...)#endif/*------------------------------------------------------------ . Reads a register from the MII Management serial interface .-------------------------------------------------------------*/static intsmc_read_phy_register(unsigned long ioaddr, int phyaddr, int phyreg){ int oldBank; int i, mask, mii_reg; u8 bits[64]; int input_idx, phydata; int clk_idx = 0; // 32 consecutive ones on MDO to establish sync for (i = 0; i < 32; ++i) bits[clk_idx++] = MII_MDOE | MII_MDO; // Start code <01> bits[clk_idx++] = MII_MDOE; bits[clk_idx++] = MII_MDOE | MII_MDO; // Read command <10> bits[clk_idx++] = MII_MDOE | MII_MDO; bits[clk_idx++] = MII_MDOE; // Output the PHY address, msb first mask = 0x10; for (i = 0; i < 5; ++i) { if (phyaddr & mask) bits[clk_idx++] = MII_MDOE | MII_MDO; else bits[clk_idx++] = MII_MDOE; // Shift to next lowest bit mask >>= 1; } // Output the phy register number, msb first mask = 0x10; for (i = 0; i < 5; ++i) { if (phyreg & mask) bits[clk_idx++] = MII_MDOE | MII_MDO; else bits[clk_idx++] = MII_MDOE; // Shift to next lowest bit mask >>= 1; } // Tristate and turnaround (2 bit times) bits[clk_idx++] = 0; //bits[clk_idx++] = 0; // Input starts at this bit time input_idx = clk_idx; // Will input 16 bits for (i = 0; i < 16; ++i) bits[clk_idx++] = 0; // Final clock bit bits[clk_idx++] = 0; // Save the current bank oldBank = SMC_CURRENT_BANK(); // Select bank 3 SMC_SELECT_BANK( 3 ); // Get the current MII register value mii_reg = SMC_GET_MII(); // Turn off all MII Interface bits mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO); // Clock all 64 cycles for (i = 0; i < sizeof bits; ++i) { // Clock Low - output data SMC_SET_MII( mii_reg | bits[i] ); msleep(1); // Clock Hi - input data SMC_SET_MII( mii_reg | bits[i] | MII_MCLK ); msleep(1); bits[i] |= SMC_GET_MII() & MII_MDI;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -