| /* |
| * Copyright Synopsys 2011-2012 |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * Sandeep Patil (sandeep.patil@codito.com) |
| * Pradeep Sawlani (pradeep.sawlani@codito.com) |
| * -Initial Version |
| * |
| * Vineet Gupta (vgupta@synopsys.com): May 2011 |
| * -Fixed the networking flakiness (brain-dead Rx and Tx routines) |
| * -Rx fixes |
| * = No need to tinker with status regs (since no interrupts) |
| * = Instead of starting at BD 0, remember where a pkt is seen and in |
| * subseq Rx call, poll the next BD, where chance of seeing pkt |
| * is more likely. @rxbd_cntr used to track that |
| * = Entire D$ was flushed thrice for one pkt Rx, only needed once. |
| * -Tx fixes |
| * = again useless tinkering with status reg, no need to touch it at all |
| * = old code would write out to BD-x, wait for emac to pick it up |
| * and then free it up in same flow. Now, we write to BD-x and simply |
| * move to next BD, giving emac all the time. More imp it prevents the |
| * need to poll the BD and/or status-reg. |
| * = Added a loop to scan for a free BD, in case one pointed to by |
| * @txbd_cntr is in use by emac. |
| */ |
| |
| |
| |
| //#include <cmd_confdefs.h> /* Command configs */ |
| #include <asm/u-boot.h> /* Board data structure */ |
| #include <asm/arc_emac.h> /* MDIO macros and ARC_EMAC register macros */ |
| #include <config.h> /* ARC700 clock freq. */ |
| #include <asm/errno.h> |
| #include <malloc.h> |
| #include <common.h> |
| #include <net.h> |
| #include <linux/types.h> |
| |
| //#define RX_DBG |
| #ifdef RX_DBG |
| #define RX_PRINT(x) do { x; } while(0) |
| #else |
| #define RX_PRINT(x) |
| #endif |
| |
| /* rx buffer descriptor. We align descriptors to 32 so info and |
| * data lie on the same cache line. This also satisfies the 8 byte |
| * alignment required by the VMAC */ |
| volatile struct aa3_buffer_desc rxbd[RX_BDT_LEN] __attribute__((aligned(32))); |
| |
| /* tx buffer descriptor */ |
| volatile struct aa3_buffer_desc txbd[TX_BDT_LEN] __attribute__((aligned(32))); |
| |
| /* transmit buffer counter */ |
| static int txbd_cntr; |
| static int rxbd_cntr; |
| |
| char default_addr[10] = { 0x00 ,0x01 ,0x02 ,0x03 ,0x04 ,0x05 }; |
| int arc_eth_rx (struct eth_device * net_current) |
| { |
| unsigned int i,len, loop; |
| unsigned int cnt=0; |
| volatile unsigned char *recv_data; |
| volatile unsigned int *stat_reg,*reg_base_addr = VMAC_REG_BASEADDR; |
| stat_reg = reg_base_addr + STAT_OFFSET; |
| |
| /* This is where pkt was found - last time */ |
| i = rxbd_cntr; |
| |
| for (loop=0 ; loop < RX_BDT_LEN;loop++) { |
| |
| i = (i + 1) & (RX_BDT_LEN - 1); /* poll the next one */ |
| |
| if((rxbd[i].info & OWN_MASK) == 0) { |
| |
| rxbd_cntr = i; /* remember that we saw a pkt here */ |
| RX_PRINT(cnt++); |
| |
| /* Ckh if this BD has a new pkt */ |
| if( (rxbd[i].info & FRST_MASK) && (rxbd[i].info & LAST_MASK) ) { |
| recv_data = (volatile unsigned char *)rxbd[i].data; |
| len = rxbd[i].info & LEN_MASK; |
| |
| if( !(rxbd[i].data = malloc(ETH_MTU + VMAC_BUFFER_PAD))) { |
| printf("Out of memory,dropping packet\n"); |
| rxbd[i].info = OWN_MASK | (ETH_MTU + VMAC_BUFFER_PAD); |
| } |
| else { |
| rxbd[i].info = OWN_MASK | (ETH_MTU + VMAC_BUFFER_PAD); |
| flush_cache(1,2); |
| RX_PRINT(printf("emac: Rx @ BD %d\n", i)); |
| NetReceive((uchar *)recv_data,len); |
| free((void *)recv_data); |
| } |
| } |
| else { |
| printf("Rx chained, Packet bigger than device MTU\n"); |
| rxbd[i].info = OWN_MASK | (ETH_MTU + VMAC_BUFFER_PAD); |
| } |
| |
| } |
| } |
| |
| //RX_PRINT(if(!cnt) printf("emac: BD Ring empty [%d..%d]\n",rxbd_cntr,i)); |
| |
| return (0); |
| } |
| |
| int arc_eth_send (struct eth_device *net_current, volatile void *packet, int length) |
| { |
| |
| volatile unsigned int *reg_base_addr = VMAC_REG_BASEADDR; |
| volatile unsigned int *stat_reg = reg_base_addr + STAT_OFFSET; |
| int cnt = 0; |
| |
| if (length > ETH_MTU) { |
| printf("eth_send:packet length greater than MTU\n"); |
| return -EMSGSIZE; |
| } |
| if ( length < 64 ) |
| length = 64; |
| |
| do { |
| if( (txbd[txbd_cntr].info & OWN_MASK) == 0) { |
| flush_cache(1,2); |
| txbd[txbd_cntr].data = (void *) packet; |
| txbd[txbd_cntr].info = OWN_MASK | length | FRST_MASK | LAST_MASK; |
| |
| /* Force emac to poll BDs */ |
| *stat_reg |= TXPL_MASK; |
| txbd_cntr = (txbd_cntr + 1) & (TX_BDT_LEN-1); |
| return 0; |
| } |
| else { |
| txbd_cntr = (txbd_cntr + 1) & (TX_BDT_LEN-1); |
| } |
| } |
| while (++cnt < TX_BDT_LEN); |
| |
| printf("Out of Tx Buffers\n"); |
| return -ENOMEM; |
| } |
| void arc_eth_halt(struct eth_device * net_current) |
| { |
| |
| } |
| |
| void eth_stop(void) |
| { |
| volatile unsigned int *reg_base_addr = VMAC_REG_BASEADDR; |
| volatile unsigned int *control_reg = reg_base_addr + CONTROL_OFFSET; |
| volatile unsigned int *macl = reg_base_addr + ADDRL_OFFSET; |
| volatile unsigned int *mach = reg_base_addr + ADDRH_OFFSET; |
| volatile unsigned int *reg; |
| volatile unsigned int *stat_reg; |
| unsigned int temp; |
| |
| reg = reg_base_addr + ENABLE_OFFSET; |
| *reg = 0; |
| |
| reg = reg_base_addr + STAT_OFFSET; |
| *reg = 0xffffffff; |
| |
| txbd_cntr = 0; |
| (*control_reg) = (*control_reg) & (~EN_MASK) ; /* VMAC disabled */ |
| *control_reg = 0; |
| *macl = 0; |
| *mach = 0; |
| stat_reg = reg_base_addr + STAT_OFFSET; |
| *stat_reg = 0; |
| |
| printf("EMAC : RESET\n"); |
| reg = reg_base_addr + MDIO_DATA_OFFSET; |
| stat_reg = reg_base_addr + STAT_OFFSET; |
| |
| __mdio_write(stat_reg, reg, PHY_ID, LXT970A_CTRL_REG, LXT970A_CTRL_RESET); |
| |
| do { |
| __mdio_read (stat_reg, reg, PHY_ID, LXT970A_CTRL_REG, temp); |
| } while (temp & LXT970A_CTRL_RESET); |
| |
| printf("LXT970A : RESET\n"); |
| } |
| |
| void aa3_emac_set_address(void *ptr) |
| { |
| char *mac_addr = (char *)ptr ; |
| volatile unsigned int *reg; |
| volatile unsigned int *reg_base_addr = VMAC_REG_BASEADDR; |
| |
| reg = reg_base_addr + ADDRL_OFFSET; |
| *reg = *((unsigned int *)mac_addr); |
| reg = reg_base_addr + ADDRH_OFFSET; |
| (*reg) = (*(unsigned int *) (mac_addr+4)) & 0x0000ffff; |
| } |
| |
| |
| |
| int arc_eth_init(bd_t *bd) |
| { |
| volatile unsigned int *reg; |
| volatile unsigned int *stat_reg; |
| volatile unsigned int *reg_base_addr; |
| int i; |
| unsigned int temp,duplex ; |
| static int initialised = 0 ; |
| struct eth_device *dev; |
| |
| |
| reg_base_addr = (unsigned int *) (VMAC_REG_BASEADDR); |
| if(initialised) { |
| reg = reg_base_addr + CONTROL_OFFSET; |
| goto out; |
| } |
| reg = reg_base_addr + ENABLE_OFFSET; |
| (*reg) = 0; /* Disable all interrupts as we are not handling */ |
| |
| reg = reg_base_addr + MDIO_DATA_OFFSET; |
| stat_reg = reg_base_addr + STAT_OFFSET; |
| |
| /* Reset the PHY */ |
| __mdio_write(stat_reg, reg, PHY_ID, LXT970A_CTRL_REG, LXT970A_CTRL_RESET); |
| |
| /* Wait till the PHY has finished resetting */ |
| do { |
| __mdio_read (stat_reg, reg, PHY_ID, LXT970A_CTRL_REG, temp); |
| } while (temp & LXT970A_CTRL_RESET); |
| |
| |
| |
| /* Advertize capabilities */ |
| temp = LXT970A_AUTONEG_ADV_10BTX_FULL | |
| LXT970A_AUTONEG_ADV_10BT | AUTONEG_ADV_IEEE_8023; |
| |
| /* If the system clock is greater than 25Mhz then advertize 100 */ |
| #if (CONFIG_ARC_CLK > 25000000) |
| temp = LXT970A_AUTONEG_ADV_100BTX_FULL | LXT970A_AUTONEG_ADV_100BTX; |
| #endif |
| __mdio_write(stat_reg, reg, PHY_ID, LXT970A_AUTONEG_ADV_REG, temp); |
| |
| /* Start Auto-Negotioation */ |
| __mdio_write(stat_reg, reg, PHY_ID, LXT970A_CTRL_REG, |
| (LXT970A_CTRL_AUTONEG | LXT970A_CTRL_RESTART_AUTO)); |
| |
| /* Wait for Auto Negotiation to complete */ |
| do { |
| __mdio_read(stat_reg, reg, PHY_ID, LXT970A_STATUS2_REG, temp); |
| } while (!(temp & LXT970A_STATUS2_COMPLETE)); |
| |
| // Check polarity |
| |
| if (temp & LXT970A_STATUS2_POLARITY) |
| { |
| printf("EMAC : LXT Polarity bit set\n"); |
| temp = ~temp; // invert it |
| } |
| |
| /* Check if full duplex is supported and set the vmac accordingly */ |
| if (temp & LXT970A_STATUS2_FULL) |
| { |
| printf("EMAC : Full duplex\n"); |
| duplex = ENFL_MASK; |
| } |
| else |
| { |
| printf("EMAC : Half duplex\n"); |
| duplex = 0; |
| } |
| |
| |
| |
| |
| |
| /* Allocate and set buffers for rx BD's */ |
| for ( i=0 ; i<RX_BDT_LEN ; i++) { |
| rxbd[i].data = malloc(ETH_MTU +VMAC_BUFFER_PAD); |
| rxbd[i].info = OWN_MASK | (ETH_MTU + VMAC_BUFFER_PAD); |
| } |
| |
| /* All TX BD's owned by CPU */ |
| for ( i=0 ; i<TX_BDT_LEN ; i++) { |
| txbd[i].data = 0; |
| txbd[i].info = 0; |
| } |
| |
| // seed with max value so it starts with 0 in rx loop |
| rxbd_cntr = RX_BDT_LEN - 1; |
| |
| /* Set EMAC hardware address */ |
| aa3_emac_set_address((void *)&default_addr); |
| |
| /* Initialize logical address filter */ |
| reg = reg_base_addr + LAFL_OFFSET; |
| (*reg ) = 0x0; |
| reg = reg_base_addr + LAFH_OFFSET; |
| (*reg ) = 0x0; |
| |
| /* Set rx BD ring pointer */ |
| reg = reg_base_addr + RXRINGPTR_OFFSET; |
| (*reg) = (unsigned int )rxbd; |
| |
| /* Set tx BD ring pointer */ |
| reg = reg_base_addr + TXRINGPTR_OFFSET; |
| (*reg) = (unsigned int )txbd; |
| |
| /* Set Poll rate so that it polls every 1 ms*/ |
| reg = reg_base_addr + POLLRATE_OFFSET; |
| (*reg) = (CONFIG_ARC_CLK/1000000); |
| |
| |
| /* Set CONTROL */ |
| reg = reg_base_addr + CONTROL_OFFSET; |
| (*reg) = (RX_BDT_LEN << 24) | /* RX buffer desc table len */ |
| (TX_BDT_LEN << 16) | /* TX buffer des tabel len */ |
| TXRN_MASK | /* TX enable */ |
| RXRN_MASK | /* RX enable */ |
| PROM_MASK | |
| duplex; /* Full Duplex enable */ |
| |
| dev = (struct eth_device *) malloc(sizeof(*dev)); |
| sprintf(dev->name,"ARC EMAC"); |
| dev->init = arc_eth_init; |
| dev->halt = arc_eth_halt; |
| dev->send = arc_eth_send; |
| dev->recv = arc_eth_rx; |
| eth_register(dev); |
| out: |
| (*reg) |= EN_MASK; /* VMAC enable */ |
| initialised = 1; |
| return (0); |
| } |
| |