#include <rthw.h> #include <rtthread.h> #include <netif/ethernetif.h> #include "sam7x_emac.h" #include "AT91SAM7X256.h" #include "lwipopts.h" #define MAX_ADDR_LEN 6 #define EMAC_PIO_CFG (AT91C_PB0_ETXCK_EREFCK | \ AT91C_PB1_ETXEN | \ AT91C_PB2_ETX0 | \ AT91C_PB3_ETX1 | \ AT91C_PB4_ECRS | \ AT91C_PB5_ERX0 | \ AT91C_PB6_ERX1 | \ AT91C_PB7_ERXER | \ AT91C_PB8_EMDC | \ AT91C_PB9_EMDIO | \ AT91C_PB10_ETX2 | \ AT91C_PB11_ETX3 | \ AT91C_PB10_ETX2 | \ AT91C_PB13_ERX2 | \ AT91C_PB14_ERX3 | \ AT91C_PB15_ERXDV_ECRSDV| \ AT91C_PB16_ECOL | \ AT91C_PB17_ERXCK) #define RB_BUFFER_SIZE 8 /* max number of receive buffers */ #define ETH_RX_BUF_SIZE 128 #define TB_BUFFER_SIZE 4 #define ETH_TX_BUF_SIZE (PBUF_POOL_BUFSIZE) struct rbf_t { rt_uint32_t addr; rt_uint32_t status; }; static rt_uint32_t current_rb_index; /* current receive buffer index */ static volatile struct rbf_t rb_descriptors[RB_BUFFER_SIZE]; static volatile struct rbf_t tb_descriptors[TB_BUFFER_SIZE]; static rt_uint8_t rx_buf[RB_BUFFER_SIZE][ETH_RX_BUF_SIZE] __attribute__ ((aligned (8))); static rt_uint8_t tx_buf[TB_BUFFER_SIZE][ETH_TX_BUF_SIZE] __attribute__ ((aligned (8))); static struct rt_semaphore tx_sem; struct net_device { /* inherit from ethernet device */ struct eth_device parent; /* interface address info. */ rt_uint8_t dev_addr[MAX_ADDR_LEN]; /* hw address */ }; static struct net_device sam7x_dev_entry; static struct net_device *sam7x_dev =&sam7x_dev_entry; AT91PS_EMAC pEmac = AT91C_BASE_EMAC; rt_inline void write_phy(rt_uint8_t addr, rt_uint32_t value) { AT91C_BASE_EMAC->EMAC_MAN = ((0x01<<30) | (2 << 16) | (1 << 28) | (AT91C_PHY_ADDR << 23) | (addr << 18)) | value; /* Wait until IDLE bit in Network Status register is cleared */ while (!(AT91C_BASE_EMAC->EMAC_NSR & AT91C_EMAC_IDLE)); } rt_inline rt_uint32_t read_phy(rt_uint8_t addr) { AT91C_BASE_EMAC->EMAC_MAN = (0x01<<30) | (0x02 << 16) | (0x02 << 28) | (AT91C_PHY_ADDR << 23) | (addr << 18); /* Wait until IDLE bit in Network Status register is cleared */ while (!(AT91C_BASE_EMAC->EMAC_NSR & AT91C_EMAC_IDLE)); return (AT91C_BASE_EMAC->EMAC_MAN & 0x0000ffff); } rt_inline void sam7xether_reset_tx_desc(void) { static rt_uint32_t index = 0; if(tb_descriptors[index].status & TxDESC_STATUS_USED) { while(!tb_descriptors[index].status & TxDESC_STATUS_LAST_BUF) { index ++; if(index >= TB_BUFFER_SIZE)index = 0; tb_descriptors[index].status |= TxDESC_STATUS_USED; } index ++; if(index >= TB_BUFFER_SIZE)index = 0; } } /* interrupt service routing */ static void sam7xether_isr(int irq) { /* Variable definitions can be made now. */ volatile rt_uint32_t isr, rsr; /* get status */ isr = AT91C_BASE_EMAC->EMAC_ISR; rsr = AT91C_BASE_EMAC->EMAC_RSR; if( ( isr & AT91C_EMAC_RCOMP ) || ( rsr & AT91C_EMAC_REC ) ) { rt_err_t result; /* a frame has been received */ result = eth_device_ready((struct eth_device*)&(sam7x_dev->parent)); RT_ASSERT(result == RT_EOK); AT91C_BASE_EMAC->EMAC_RSR = AT91C_EMAC_REC; } if( isr & AT91C_EMAC_TCOMP ) { /* A frame has been transmitted. Mark all the buffers as free */ sam7xether_reset_tx_desc(); AT91C_BASE_EMAC->EMAC_TSR = AT91C_EMAC_COMP; } } rt_inline void linksetup(void) { rt_uint32_t value; /* Check if this is a RTL8201 PHY. */ rt_uint16_t id1 = read_phy(PHY_REG_PHYID1); rt_uint16_t id2 = read_phy(PHY_REG_PHYID2); if (((id2 << 16) | (id1 & 0xfff0)) == MII_RTL8201_ID) { rt_uint32_t tout; /* Configure the PHY device */ /* Use autonegotiation about the link speed. */ write_phy (PHY_REG_BMCR, PHY_AUTO_NEG); /* Wait to complete Auto_Negotiation. */ for (tout = 0; tout < 0x100000; tout++) { value = read_phy (PHY_REG_BMSR); if (value & BMSR_ANEGCOMPLETE) break; /* autonegotiation finished. */ } /* Check the link status. */ for (tout = 0; tout < 0x10000; tout++) { value = read_phy (PHY_REG_BMSR); if (value & BMSR_LINKST) break; /* Link is on. */ } } value = read_phy (PHY_REG_ANLPAR); /* Update the MAC register NCFGR. */ AT91C_BASE_EMAC->EMAC_NCFGR &= ~(AT91C_EMAC_SPD | AT91C_EMAC_FD); /* set full duplex . */ if (value & 0xA000) AT91C_BASE_EMAC->EMAC_NCFGR |= AT91C_EMAC_FD; /* set speed */ if (value & 0xC000) AT91C_BASE_EMAC->EMAC_NCFGR |= AT91C_EMAC_SPD; } /* * Set the MAC address. */ rt_inline void update_mac_address(struct net_device* device) { AT91C_BASE_EMAC->EMAC_SA1L = (device->dev_addr[3] << 24) | (device->dev_addr[2] << 16) | (device->dev_addr[1] << 8) | device->dev_addr[0]; AT91C_BASE_EMAC->EMAC_SA1H = (device->dev_addr[5] << 8) | device->dev_addr[4]; } rt_inline void sam7xether_desc_init() { rt_uint32_t i; /* Rx Buffer Descriptor initialization */ current_rb_index = 0; for (i = 0; i < RB_BUFFER_SIZE; i++) { rb_descriptors[i].addr = (rt_uint32_t)&(rx_buf[i][0]); rb_descriptors[i].status = 0; } /* Set the WRAP bit at the end of the list descriptor. */ rb_descriptors[RB_BUFFER_SIZE-1].addr |= 0x02; /* Set Rx Queue pointer to descriptor list. */ AT91C_BASE_EMAC->EMAC_RBQP = (unsigned int)&(rb_descriptors[0]); /* Tx Buffer Descriptor initialization */ for (i = 0; i < TB_BUFFER_SIZE; i++) { tb_descriptors[i].addr = (rt_uint32_t)&(tx_buf[i][0]); tb_descriptors[i].status = TxDESC_STATUS_USED; } /* Set the WRAP bit at the end of the list descriptor. */ tb_descriptors[TB_BUFFER_SIZE-1].status |= TxDESC_STATUS_WRAP; /* Set Tx Queue pointer to descriptor list. */ AT91C_BASE_EMAC->EMAC_TBQP = (unsigned int)&(tb_descriptors[0]); } /* RT-Thread Device Interface */ /* initialize the interface */ rt_err_t sam7xether_init(rt_device_t dev) { /* enable peripheral clock for EMAC and PIO B */ AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOB | 1 << AT91C_ID_EMAC; /* Disable pull up on RXDV => PHY normal mode (not in test mode), */ /* and set MII mode. PHY has internal pull down. */ AT91C_BASE_PIOB->PIO_PPUDR = (1<<16) | (1 << 15); /* Clear PB18 <=> PHY powerdown */ AT91C_BASE_PIOB->PIO_PER = 1<<18; AT91C_BASE_PIOB->PIO_OER = 1<<18; AT91C_BASE_PIOB->PIO_CODR = 1<<18; /* EMAC IO init for EMAC-PHY communication. */ AT91C_BASE_PIOB->PIO_ASR = EMAC_PIO_CFG; AT91C_BASE_PIOB->PIO_PDR = EMAC_PIO_CFG; // Set in Periph mode /* Enable communication between EMAC-PHY. */ AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_MPE; /* MDC = MCK/32 */ AT91C_BASE_EMAC->EMAC_NCFGR |= 2<<10; /* Reset PHY */ AT91C_BASE_PIOB->PIO_PPUDR = AT91C_PB7_ERXER; AT91C_BASE_RSTC->RSTC_RMR = 0xA5000000 | (0x08 << 8) ; AT91C_BASE_RSTC->RSTC_RCR = 0xA5000000 | AT91C_RSTC_EXTRST; while(!(AT91C_BASE_RSTC->RSTC_RSR & AT91C_RSTC_NRSTL)); linksetup(); /* Disable management port in MAC control register. */ AT91C_BASE_EMAC->EMAC_NCR &= ~AT91C_EMAC_MPE; /* Enable EMAC in MII mode, enable clock ERXCK and ETXCK */ AT91C_BASE_EMAC->EMAC_USRIO= AT91C_EMAC_CLKEN; /* Transmit and Receive disable. */ AT91C_BASE_EMAC->EMAC_NCR &= ~(AT91C_EMAC_RE | AT91C_EMAC_TE); /* init descriptor */ sam7xether_desc_init(); /* Clear receive and transmit status registers. */ AT91C_BASE_EMAC->EMAC_RSR = (AT91C_EMAC_OVR | AT91C_EMAC_REC | AT91C_EMAC_BNA); AT91C_BASE_EMAC->EMAC_TSR = (AT91C_EMAC_UND | AT91C_EMAC_COMP| AT91C_EMAC_BEX | AT91C_EMAC_RLES| AT91C_EMAC_COL | AT91C_EMAC_UBR); /* Configure EMAC operation mode. */ AT91C_BASE_EMAC->EMAC_NCFGR |= (AT91C_EMAC_BIG | AT91C_EMAC_DRFCS); AT91C_BASE_EMAC->EMAC_NCR |= (AT91C_EMAC_TE | AT91C_EMAC_RE | AT91C_EMAC_WESTAT); /* update MAC address */ update_mac_address(sam7x_dev); /* enable interrupt */ AT91C_BASE_EMAC->EMAC_IER = AT91C_EMAC_RCOMP | AT91C_EMAC_TCOMP; /* setup interrupt */ rt_hw_interrupt_install(AT91C_ID_EMAC, sam7xether_isr, RT_NULL); *(volatile unsigned int*)(0xFFFFF000 + AT91C_ID_EMAC * 4) = AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL | 5; // AT91C_AIC_SMR(AT91C_ID_EMAC) = AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL | 5; rt_hw_interrupt_umask(AT91C_ID_EMAC); return RT_EOK; } /* control the interface */ rt_err_t sam7xether_control(rt_device_t dev, rt_uint8_t cmd, void *args) { switch(cmd) { case NIOCTL_GADDR: /* get mac address */ if(args) rt_memcpy(args, sam7x_dev_entry.dev_addr, 6); else return -RT_ERROR; break; default : break; } return RT_EOK; } /* Open the ethernet interface */ rt_err_t sam7xether_open(rt_device_t dev, rt_uint16_t oflags) { return RT_EOK; } /* Close the interface */ rt_err_t sam7xether_close(rt_device_t dev) { return RT_EOK; } /* Read */ rt_size_t sam7xether_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size) { rt_set_errno(-RT_ENOSYS); return 0; } /* Write */ rt_size_t sam7xether_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size) { rt_set_errno(-RT_ENOSYS); return 0; } /* See the header file for descriptions of public functions. */ void sam7xether_write_frame(rt_uint8_t *ptr, rt_uint32_t length, rt_bool_t eof) { rt_uint8_t *buf_ptr; static rt_uint32_t current_tb_index = 0; rt_uint32_t is_last, tx_offset = 0, remain, pdu_length; while(tx_offset < length) { /* check whether buffer is available */ while(!(tb_descriptors[current_tb_index].status & TxDESC_STATUS_USED)) { /* no buffer */ rt_thread_delay(5); } /* Get the address of the buffer from the descriptor, then copy the data into the buffer. */ buf_ptr = (rt_uint8_t *)tb_descriptors[current_tb_index].addr; /* How much can we write to the buffer? */ remain = length - tx_offset; pdu_length = (remain <= ETH_TX_BUF_SIZE)? remain : ETH_TX_BUF_SIZE; /* Copy the data into the buffer. */ rt_memcpy(buf_ptr, &ptr[tx_offset], pdu_length ); tx_offset += pdu_length; /* Is this the last data for the frame? */ if((eof == RT_TRUE) && ( tx_offset >= length )) is_last = TxDESC_STATUS_LAST_BUF; else is_last = 0; /* Fill out the necessary in the descriptor to get the data sent, then move to the next descriptor, wrapping if necessary. */ if(current_tb_index >= (TB_BUFFER_SIZE - 1)) { tb_descriptors[current_tb_index].status = ( pdu_length & TxDESC_STATUS_BUF_SIZE ) | is_last | TxDESC_STATUS_WRAP; current_tb_index = 0; } else { tb_descriptors[current_tb_index].status = ( pdu_length & TxDESC_STATUS_BUF_SIZE ) | is_last; current_tb_index++; } /* If this is the last buffer to be sent for this frame we can start the transmission. */ if(is_last) { AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TSTART; } } } /* ethernet device interface */ /* * Transmit packet. */ rt_err_t sam7xether_tx( rt_device_t dev, struct pbuf* p) { struct pbuf* q; /* lock tx operation */ rt_sem_take(&tx_sem, RT_WAITING_FOREVER); for (q = p; q != NULL; q = q->next) { if (q->next == RT_NULL) sam7xether_write_frame(q->payload, q->len, RT_TRUE); else sam7xether_write_frame(q->payload, q->len, RT_FALSE); } rt_sem_release(&tx_sem); return 0; } void sam7xether_read_frame(rt_uint8_t* ptr, rt_uint32_t section_length, rt_uint32_t total) { static rt_uint8_t* src_ptr; register rt_uint32_t buf_remain, section_remain; static rt_uint32_t section_read = 0, buf_offset = 0, frame_read = 0; if(ptr == RT_NULL) { /* Reset our state variables ready for the next read from this buffer. */ src_ptr = (rt_uint8_t *)(rb_descriptors[current_rb_index].addr & RxDESC_FLAG_ADDR_MASK); frame_read = (rt_uint32_t)0; buf_offset = (rt_uint32_t)0; } else { /* Loop until we have obtained the required amount of data. */ section_read = 0; while( section_read < section_length ) { buf_remain = (ETH_RX_BUF_SIZE - buf_offset); section_remain = section_length - section_read; if( section_remain > buf_remain ) { /* more data on section than buffer size */ rt_memcpy(&ptr[ section_read ], &src_ptr[buf_offset], buf_remain); section_read += buf_remain; frame_read += buf_remain; /* free buffer */ rb_descriptors[current_rb_index].addr &= ~RxDESC_FLAG_OWNSHIP; /* move to the next frame. */ current_rb_index++; if(current_rb_index >= RB_BUFFER_SIZE) current_rb_index = 0; /* Reset the variables for the new buffer. */ src_ptr = (rt_uint8_t *)(rb_descriptors[current_rb_index].addr & RxDESC_FLAG_ADDR_MASK); buf_offset = 0; } else { /* more data on buffer than section size */ rt_memcpy(&ptr[section_read], &src_ptr[buf_offset], section_remain); buf_offset += section_remain; section_read += section_remain; frame_read += section_remain; /* finish this read */ if((buf_offset >= ETH_RX_BUF_SIZE) || (frame_read >= total)) { /* free buffer */ rb_descriptors[current_rb_index].addr &= ~(RxDESC_FLAG_OWNSHIP); /* move to the next frame. */ current_rb_index++; if( current_rb_index >= RB_BUFFER_SIZE ) current_rb_index = 0; src_ptr = (rt_uint8_t*)(rb_descriptors[current_rb_index].addr & RxDESC_FLAG_ADDR_MASK); buf_offset = 0; } } } } } struct pbuf *sam7xether_rx(rt_device_t dev) { struct pbuf *p = RT_NULL; /* skip fragment frame */ while((rb_descriptors[current_rb_index].addr & RxDESC_FLAG_OWNSHIP) && !(rb_descriptors[current_rb_index].status & RxDESC_STATUS_FRAME_START)) { rb_descriptors[current_rb_index].addr &= (~RxDESC_FLAG_OWNSHIP); current_rb_index++; if(current_rb_index >= RB_BUFFER_SIZE) current_rb_index = 0; } if ((rb_descriptors[current_rb_index].addr & RxDESC_FLAG_OWNSHIP)) { struct pbuf* q; rt_uint32_t index, pkt_len = 0; /* first of all, find the frame length */ index = current_rb_index; while (rb_descriptors[index].addr & RxDESC_FLAG_OWNSHIP) { pkt_len = rb_descriptors[index].status & RxDESC_STATUS_BUF_SIZE; if (pkt_len > 0) break; index ++; if (index > RB_BUFFER_SIZE) index = 0; } if (pkt_len) { p = pbuf_alloc(PBUF_LINK, pkt_len, PBUF_RAM); if(p != RT_NULL) { sam7xether_read_frame(RT_NULL, 0, pkt_len); for(q = p; q != RT_NULL; q= q->next) sam7xether_read_frame(q->payload, q->len, pkt_len); } else { rt_kprintf("no memory in pbuf\n"); } } } /* enable interrupt */ AT91C_BASE_EMAC->EMAC_IER = AT91C_EMAC_RCOMP; return p; } int sam7xether_register(char *name) { rt_err_t result; /* init rt-thread device interface */ sam7x_dev_entry.parent.parent.init = sam7xether_init; sam7x_dev_entry.parent.parent.open = sam7xether_open; sam7x_dev_entry.parent.parent.close = sam7xether_close; sam7x_dev_entry.parent.parent.read = sam7xether_read; sam7x_dev_entry.parent.parent.write = sam7xether_write; sam7x_dev_entry.parent.parent.control = sam7xether_control; sam7x_dev_entry.parent.eth_rx = sam7xether_rx; sam7x_dev_entry.parent.eth_tx = sam7xether_tx; /* Update MAC address */ sam7x_dev_entry.dev_addr[0] = 0x1e; sam7x_dev_entry.dev_addr[1] = 0x30; sam7x_dev_entry.dev_addr[2] = 0x6c; sam7x_dev_entry.dev_addr[3] = 0xa2; sam7x_dev_entry.dev_addr[4] = 0x45; sam7x_dev_entry.dev_addr[5] = 0x5e; /* update mac address */ update_mac_address(sam7x_dev); rt_sem_init(&tx_sem, "emac", 1, RT_IPC_FLAG_FIFO); result = eth_device_init(&(sam7x_dev->parent), (char*)name); RT_ASSERT(result == RT_EOK); return RT_EOK; }