336 lines
8.8 KiB
C
336 lines
8.8 KiB
C
/*
|
|
* 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 <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>
|
|
/* 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 ; 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;
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|