/* * Copyright Codito Technologies (www.codito.com) * * board/aa3/arc_emac.c * * Copyright (C) * * 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. * * Authors : Sandeep Patil (sandeep.patil@codito.com) * Pradeep Sawlani (pradeep.sawlani@codito.com) */ //#include /* Command configs */ #include /* Board data structure */ #include /* MDIO macros and ARC_EMAC register macros */ #include /* ARC700 clock freq. */ #include #include #include #include #include /* 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[RX_BDT_LEN] __attribute__((aligned(32))); /* transmit buffer counter */ static int txbd_cntr; char default_addr[10] = { 0x00 ,0x01 ,0x02 ,0x03 ,0x04 ,0x05 }; int arc_eth_rx (struct eth_device * net_current) { unsigned int i,len; volatile unsigned char *recv_data; volatile unsigned int *stat_reg,*reg_base_addr = VMAC_REG_BASEADDR; stat_reg = reg_base_addr + STAT_OFFSET; volatile unsigned char *p; unsigned int z; if (*stat_reg & TXINT_MASK) { *stat_reg = TXINT_MASK; } //do_ping(1,2,3,4); if (!(*stat_reg & RXINT_MASK)) return; *stat_reg = RXINT_MASK; flush_cache(1,2); for (i=0 ; i < RX_BDT_LEN;i++) { if( (rxbd[i].info & OWN_MASK) == 0) { if( (rxbd[i].info & FRST_MASK) && (rxbd[i].info & LAST_MASK) ) { recv_data = (unsigned int *)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); NetReceive((uchar *)recv_data,len); free(recv_data); } } else { printf("Rx chained, Packet bigger than device MTU\n"); rxbd[i].info = OWN_MASK | (ETH_MTU + VMAC_BUFFER_PAD); } } } flush_cache(1,2); 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; volatile unsigned int *enable_reg = reg_base_addr + ENABLE_OFFSET; volatile unsigned int *control_reg = reg_base_addr + CONTROL_OFFSET; volatile unsigned int *tx_ring = reg_base_addr + TXRINGPTR_OFFSET; unsigned int z; unsigned char *p; #if 0 printf("Eth_send\n"); printf("ID REG : %x\n", *reg_base_addr); printf("Enable : %x\n", *enable_reg); printf("Control: %x\n", *control_reg); printf("TX Ring: %x\n", *tx_ring); printf("Packet : %x\n", packet); #endif if (length > ETH_MTU) { printf("eth_send:packet length greater than MTU\n"); return -EMSGSIZE; } if ( length < 64 ) length = 64; if( (txbd[txbd_cntr].info & OWN_MASK) == 0) { txbd[txbd_cntr].data = packet; txbd[txbd_cntr].info = OWN_MASK | length | FRST_MASK | LAST_MASK; flush_cache(1,2); // arc_write_uncached_32(txbd[txbd_cntr].data, packet); // arc_write_uncached_32(txbd[txbd_cntr].info, OWN_MASK | length | FRST_MASK | LAST_MASK); *stat_reg = TXPL_MASK; #if 0 p = (char *) packet; printf("Packet length %u\n", length); printf("Packet : "); for(z=0;z!=length;z++) printf("%02x:",p[z]); printf("\n"); #endif } else { printf("Out of Tx Buffers\n"); return -ENOMEM; } /* Poll for TXINT */ // while ( !(*(stat_reg) & TXINT_MASK)) *stat_reg = TXINT_MASK; txbd[txbd_cntr].data = 0; txbd[txbd_cntr].info = 0; txbd_cntr = (txbd_cntr + 1)%TX_BDT_LEN; return (0); } 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 ; iname,"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); }