📄 eth_ns9750.c
字号:
#include <arch/ns9750.h>#include <arch/ns9750_eth.h>#include <arch/eth.h>#include <arch/ioregs.h>#include <target/io.h>#include <target/mem.h>#include <target/net/eth.h>#include <target/net/eth_util.h>#include <target/net/mac.h>#include "board.h"#define NS_ETH_FRAME_LEN (0x600)#define NS_ETH_PHY_ADDRESS (0x0000)#define NS_MII_POLE_TIMEOUT (10000)#ifdef DEBUG_ETH#define DEBUG_FUNC() hprintf("%s()\n", __FUNCTION__)#define DEBUG_INFO(args...) hprintf(args)#else#define DEBUG_FUNC()#define DEBUG_INFO(args...)#endiftypedef struct rx_buffer_desc_t{ unsigned int *src; unsigned int len; unsigned int *dest; union{ unsigned int reg; struct{ unsigned status : 16; unsigned res : 12; unsigned full : 1; unsigned enable : 1; unsigned intr : 1; unsigned wrap : 1; }bits; }s;} rx_buffer_desc_t;typedef struct tx_buffer_desc_t{ unsigned int *src; unsigned int len; unsigned int *desc; union{ unsigned int reg; struct{ unsigned status : 16; unsigned res : 12; unsigned full : 1; unsigned last : 1; unsigned intr : 1; unsigned wrap : 1; }bits; }s;} tx_buffer_desc_t;volatile rx_buffer_desc_t rx_desc[4];volatile tx_buffer_desc_t *tx_desc;unsigned char *txbuf = (unsigned char *)0x02800000;unsigned char *rxbuf = (unsigned char *)0x02000000;static u32ns_mii_poll_busy(void){ u32 timeout = NS_MII_POLE_TIMEOUT; while((IO_ETH_MIND & NS_ETH_MIND_BUSY) && timeout--); return timeout;}static u16ns_mii_read(u16 reg){ IO_ETH_MADR = (NS_ETH_PHY_ADDRESS << 8) | reg; IO_ETH_MCMD = NS_ETH_MCMD_READ; if(!ns_mii_poll_busy()) hprintf("MII still busy in read\n"); IO_ETH_MCMD = 0; return (u16)IO_ETH_MRDD;}static voidns_mii_write(u16 reg, u16 data){ IO_ETH_MADR = (NS_ETH_PHY_ADDRESS << 8) | reg; IO_ETH_MWTD = data; if(!ns_mii_poll_busy()) hprintf("MII still busy in write\n");}static voidns_link_print_changed(void){ u16 status; u16 control; control = ns_mii_read(PHY_COMMON_CTRL); if((control & PHY_COMMON_CTRL_AUTO_NEG) == PHY_COMMON_CTRL_AUTO_NEG){ /* PHY_COMMON_STAT_LNK_STAT is only set on autonegotiation */ status = ns_mii_read(PHY_COMMON_STAT); if(status & PHY_COMMON_STAT_LNK_STAT){ status = ns_mii_read(PHY_LXT971_STAT2); status &= (PHY_LXT971_STAT2_100BTX | PHY_LXT971_STAT2_DUPLEX_MODE | PHY_LXT971_STAT2_AUTO_NEG); /* mask out all uninteresting parts */ } } /* print new link status */ DEBUG_INFO("link mode %d Mbps %s duplex %s\n", (status & PHY_LXT971_STAT2_100BTX) ? 100 : 10, (status & PHY_LXT971_STAT2_DUPLEX_MODE) ? "full" : "half", (status & PHY_LXT971_STAT2_AUTO_NEG) ? "(auto)" : "");}static voidns_link_update_egcr(void){ u32 status; u32 egcr; u32 mac2; u32 ipgt; egcr = IO_ETH_EGCR1; mac2 = IO_ETH_MAC2 & ~NS_ETH_MAC2_FULLD; ipgt = IO_ETH_IPGT & ~NS_ETH_IPGT_MA; status = ns_mii_read(PHY_LXT971_STAT2); if(status & PHY_LXT971_STAT2_DUPLEX_MODE){ mac2 |= NS_ETH_MAC2_FULLD; ipgt |= 0x15; /* see [1] p. 339 */ }else{ ipgt |= 0x12; /* see [1] p. 339 */ } IO_ETH_MAC2 = mac2; IO_ETH_EGCR1 = egcr; IO_ETH_IPGT = ipgt;}static void ns_link_auto_negotiate(void){ u16 status; DEBUG_FUNC(); status = ns_mii_read(PHY_COMMON_STAT); if((status & (PHY_COMMON_STAT_AN_COMP | PHY_COMMON_STAT_LNK_STAT)) == (PHY_COMMON_STAT_AN_COMP | PHY_COMMON_STAT_LNK_STAT)){ ns_link_print_changed(); ns_link_update_egcr(); return; } /* run auto-negotation */ /* define what we are capable of */ ns_mii_write(PHY_COMMON_AUTO_ADV, PHY_COMMON_AUTO_ADV_100BTXFD | PHY_COMMON_AUTO_ADV_100BTX | PHY_COMMON_AUTO_ADV_10BTFD | PHY_COMMON_AUTO_ADV_10BT | PHY_COMMON_AUTO_ADV_802_3); /* start auto-negotiation */ ns_mii_write(PHY_COMMON_CTRL, PHY_COMMON_CTRL_AUTO_NEG | PHY_COMMON_CTRL_RES_AUTO); /* wait for completion */ while(1){ status = ns_mii_read(PHY_COMMON_STAT); if((status & (PHY_COMMON_STAT_AN_COMP | PHY_COMMON_STAT_LNK_STAT)) == (PHY_COMMON_STAT_AN_COMP | PHY_COMMON_STAT_LNK_STAT)){ /* lucky we are, auto-negotiation succeeded */ ns_link_print_changed(); ns_link_update_egcr(); return; } }}static intns_link_init(void){ u16 val; DEBUG_FUNC(); ns_link_auto_negotiate(); val = ns_mii_read(PHY_COMMON_STAT); if(val & PHY_COMMON_STAT_LNK_STAT){ return 0; } return -1;}static int phy_init(void){ return ns_link_init();}void ns9750_enable_phy_module(void){}void ns9750_disable_phy_module(void){}int ns9750_eth_send(const eth_frame *ethfr, const void *pfr, const unsigned int pfrlen){ u32 txlen; DEBUG_FUNC(); if(pfrlen + ETH_FRAME_LEN < 60){ safe_memset(txbuf, 0, 60); txlen = 60; }else{ txlen = pfrlen + ETH_FRAME_LEN; } if(txlen > LEN_TxBuf){ return -1; } safe_memcpy(&txbuf[0], ethfr, ETH_FRAME_LEN); safe_memcpy(&txbuf[ETH_FRAME_LEN], pfr, pfrlen); tx_desc = (tx_buffer_desc_t*)(NS_ETH_TXBD + NS9750_ETH_PHYS_BASE); tx_desc->src = (unsigned int *)txbuf; tx_desc->len = txlen; tx_desc->s.reg = 0x00000000; tx_desc->s.reg = 0xf0000000; IO_ETH_TXPTR = 0; IO_ETH_TXRPTR = 0; IO_ETH_EGCR1 |= NS_ETH_EGCR1_ETXDMA; IO_ETH_EGCR2 &= ~NS_ETH_EGCR2_TCLER; IO_ETH_EGCR2 |= NS_ETH_EGCR2_TCLER; while(!(tx_desc->s.reg & NS_ETH_ETSR_TXOK)); return 0;}int ns9750_eth_rxbuf_free(const int idx){ DEBUG_FUNC(); DEBUG_INFO("idx: %d\n", idx); rx_desc[idx].s.bits.full = 0; rx_desc[idx].s.bits.status = 0; IO_ETH_RXFREE = (0x1 << idx); IO_ETH_EINTR = (NS_ETH_EINTR_RXDONEA >> idx); return 0;}int ns9750_eth_recv(const unsigned char *mac, eth_frame **ethfr, void **pbuf, unsigned int *pbuflen, int *timeout){ rx_buffer_desc_t *desc; int idx; u32 status; DEBUG_FUNC(); while(1){ while(1){ status = IO_ETH_EINTR; if(status & NS_ETH_EINTR_RXDONEA){ idx = 0; break; }else if(status & NS_ETH_EINTR_RXDONEB){ idx = 1; break; }else if(status & NS_ETH_EINTR_RXDONEC){ idx = 2; break; }else if(status & NS_ETH_EINTR_RXDONED){ idx = 3; break; }else{ *timeout -= 1; if(*timeout < 0){ return -1; } mdelay(1); } } desc = (rx_buffer_desc_t *)&rx_desc[idx]; *ethfr = (eth_frame *)desc->src; *pbuf = (void *)((u32)desc->src + ETH_FRAME_LEN); *pbuflen = desc->len - ETH_FRAME_LEN - 4; if(safe_memcmp((*ethfr)->dmac, broadcast_mac, 6) == 0){ //DEBUG_INFO("broadcast packet detect\n"); }else if(safe_memcmp((*ethfr)->dmac, mac, 6) == 0){ //DEBUG_INFO("packet detect\n"); }else{ ns9750_eth_rxbuf_free(idx); continue; } return idx; } return 0;}static int eth_indaddr_write(const unsigned char *mac){ IO_ETH_SA1 = ((mac[5] << 8) | mac[4]); IO_ETH_SA2 = ((mac[3] << 8) | mac[2]); IO_ETH_SA3 = ((mac[1] << 8) | mac[0]); return 0;}static int eth_reset(void){ unsigned long val; DEBUG_FUNC(); /* Reset MAC */ IO_ETH_EGCR1 |= NS_ETH_EGCR1_MAC_HRST; udelay(5); IO_ETH_EGCR1 &= ~NS_ETH_EGCR1_MAC_HRST; /* Reset and Initialize PHY */ IO_ETH_MAC1 &= ~NS_ETH_MAC1_SRST; /* Set MDIO Clock */ IO_ETH_MCFG = NS_ETH_MCFG_CLKS_40; /* Reset PHY */ val = ns_mii_read(PHY_COMMON_CTRL); ns_mii_write(PHY_COMMON_CTRL, val | PHY_COMMON_CTRL_RESET); udelay(350); /* now take the highest MDIO clock possible after detection */ IO_ETH_MCFG = NS_ETH_MCFG_CLKS_40; val = ns_mii_read(PHY_COMMON_ID1); DEBUG_INFO("phy id1: %p\n", val); val = ns_mii_read(PHY_COMMON_ID2); DEBUG_INFO("phy id2: %p\n", val); return 0;}static voidns9750_eth_update_rx_desc(void){ int timeout = 20; IO_ETH_EGCR1 |= NS_ETH_EGCR1_ERXINIT; /* [1] Tab. 221 states less than 5us */ while(--timeout && !(IO_ETH_EGSR & NS_ETH_EGSR_RXINIT)) /* wait for finish */ udelay(1); if(!timeout) hprintf("ethernet descriptor update failed\n"); /* @TODO do we need to clear RXINIT? */ IO_ETH_EGCR1 &= ~NS_ETH_EGCR1_ERXINIT; /* all 4 descriptors free */ IO_ETH_RXFREE = 0xf;}static int eth_enable(void){ DEBUG_FUNC(); /* enable hardware */ /* enable receive and transmit FIFO, use 10/100 Mbps MII */ IO_ETH_EGCR1 = ( NS_ETH_EGCR1_ETXWM | NS_ETH_EGCR1_ERX | NS_ETH_EGCR1_ETX |#ifdef NS9750_RXALIGN NS_ETH_EGCR1_RXALIGN |#endif NS_ETH_EGCR1_PHY_MODE_MII | NS_ETH_EGCR1_ITXA); /* EINTR can only be reseted with ERX and ETX on */ IO_ETH_EINTR |= 0; IO_ETH_MAC1 = NS_ETH_MAC1_RXEN; /* the linux kernel may give packets < 60 bytes, for example arp */ IO_ETH_MAC2 = NS_ETH_MAC2_CRCEN | NS_ETH_MAC2_PADEN; /* program the first descriptors for each ring. */ IO_ETH_RXAPTR = (unsigned long)&rx_desc[0]; IO_ETH_RXBPTR = (unsigned long)&rx_desc[1]; IO_ETH_RXCPTR = (unsigned long)&rx_desc[2]; IO_ETH_RXDPTR = (unsigned long)&rx_desc[3]; ns9750_eth_update_rx_desc(); IO_ETH_EGCR1 |= NS_ETH_EGCR1_ERXDMA; /* clear statistics */ IO_ETH_EGCR2 = NS_ETH_EGCR2_CLRCNT; /* enable statistics */ IO_ETH_EGCR2 = NS_ETH_EGCR2_AUTOZ | NS_ETH_EGCR2_STEN; IO_ETH_SAFR |= NS_ETH_SAFR_BROAD ; return 0;}static int queue_init(void){ int i; DEBUG_FUNC(); for(i=0;i<4;i++){ rx_desc[i].s.reg = 0; rx_desc[i].dest = 0; rx_desc[i].src = (unsigned int *)&rxbuf[NS_ETH_FRAME_LEN * i]; rx_desc[i].len = NS_ETH_FRAME_LEN; rx_desc[i].s.bits.wrap = 1; //!((i + 1) % (NS_RX_DESC_RING)) ? 1 : 0; rx_desc[i].s.bits.intr = 0; rx_desc[i].s.bits.enable = 1; rx_desc[i].s.bits.full = 0; } return 0;}int ns9750_eth_init(const unsigned char *ipaddr){ int ret; hwif_eth hwif; DEBUG_FUNC(); safe_memset(&hwif, 0, sizeof(hwif_eth)); hwif.enable_phy_module = ns9750_enable_phy_module; hwif.disable_phy_module = ns9750_disable_phy_module; hwif.eth_send = ns9750_eth_send; hwif.eth_recv = ns9750_eth_recv; hwif.eth_rxbuf_free = ns9750_eth_rxbuf_free; safe_memcpy(&hwif.eth_ipaddr, ipaddr, 4); arch_get_mac(hwif.eth_mac); register_hwif_eth(&hwif); enable_phy_module(); eth_reset(); ret = phy_init(); if(ret == -1){ hprintf("Link is down\n"); return -1; } eth_indaddr_write(hwif.eth_mac); queue_init(); eth_enable(); return 0;}int ns9750_eth_shutdown(void){ DEBUG_FUNC(); IO_ETH_MAC1 &= ~NS_ETH_MAC1_RXEN; IO_ETH_EGCR1 &= ~(NS_ETH_EGCR1_ERX | NS_ETH_EGCR1_ERXDMA | NS_ETH_EGCR1_ETX | NS_ETH_EGCR1_ETXDMA); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -