blob: 3f0f970f2d9e848ea799f94bd05106776f1b932a [file] [log] [blame]
#include <common.h>
#include <config.h>
#include <asm/arch/hardware.h>
#include <asm/byteorder.h>
//#include <malloc.h>
#include <net.h>
#include <command.h>
#include <miiphy.h>
#include <asm/arch/clkcore_c2000.h>
#include <asm/arch/gpio_c2000.h>
#include "c2000_eth.h"
#ifdef CONFIG_AR8328_SWITCH
#include "ar8328.h"
#endif
struct gemac_s gem_info[] = {
/* PORT_0 configuration */
{
/* GEMAC config */
.gemac_mode = RGMII,
.gemac_speed = SPEED_1000M,
.gemac_duplex = DUPLEX_FULL,
.flags = EMAC0_FLAGS,
/* phy iface */
.phy_reg_index = EMAC_PORT_0,
.phy_address = EMAC0_PHY_ADDR,
},
/* PORT_1 configuration */
{
/* GEMAC config */
.gemac_mode = RGMII,
.gemac_speed = SPEED_1000M,
.gemac_duplex = DUPLEX_FULL,
.flags = EMAC1_FLAGS,
/* phy iface */
.phy_reg_index = EMAC_PORT_0,
.phy_address = EMAC1_PHY_ADDR,
},
/* PORT_2 configuration */
{
/* GEMAC config */
.gemac_mode = RGMII,
.gemac_speed = SPEED_1000M,
.gemac_duplex = DUPLEX_FULL,
.flags = EMAC2_FLAGS,
/* phy iface */
.phy_reg_index = EMAC_PORT_0,
.phy_address = 0,
},
};
#define MAX_GEMACS 3
static struct c2000_eth_dev *gemac_list[MAX_GEMACS];
/********************************************************************
* Helper functions for phy read/write
*******************************************************************/
/* Max MII register/address (we support) */
#define MII_REGISTER_MAX 31
#define MII_ADDRESS_MAX 31
#define MDIO_TIMEOUT 5000
/********************************************************************
* gem_phy_man_rd :
* Performs phy management read operation.
*******************************************************************/
static int gem_phy_man_rd(struct c2000_eth_dev *priv, u32 phy_addr, u32 phy_reg)
{
u32 write_data;
write_data = 0x60020000 | ( (phy_addr & (u32) 0x1f) << 23) | ( (phy_reg & (u32) 0x1f) << 18); // read_op
writel(write_data, priv->phyregisters + EMAC_PHY_MANAGEMENT);
return 0;
}
static int gem_phy_man_wr(struct c2000_eth_dev *priv, u32 phy_addr, u32 phy_reg, u32 val)
{
u32 write_data;
write_data = 0x50020000 | ( (phy_addr & (u32) 0x1f) << 23) | ( (phy_reg & (u32) 0x1f) << 18) | (val & (u32) 0xffff); // write_op
writel(write_data, priv->phyregisters + EMAC_PHY_MANAGEMENT);
return 0;
}
/** gem_phy_man_data
* Read the data section of phy management register.
* After a successful read opeeration the data will be stored in
* in this register in lower 16bits.
*/
static u32 gem_phy_man_data(struct c2000_eth_dev *priv)
{
u32 value;
value = readl(priv->phyregisters + EMAC_PHY_MANAGEMENT) & 0xFFFF;
return value;
}
#define EMAC_PHY_IDLE (1 << 2)
static int gem_phy_man_idle(struct c2000_eth_dev *priv)
{
u32 value;
value = readl(priv->phyregisters + EMAC_NETWORK_STATUS);
return ((value & EMAC_PHY_IDLE) == EMAC_PHY_IDLE);
}
static int gem_phy_timeout(struct c2000_eth_dev *priv, int timeout)
{
while(!gem_phy_man_idle(priv)) {
if (timeout-- <= 0) {
printf("Phy MDIO read/write timeout\n");
return -1;
}
//udelay(1);
}
return 0;
}
static struct c2000_eth_dev* get_gemac(char *devname)
{
int i;
for (i = 0; i < MAX_GEMACS; i++) {
if (!strcmp(gemac_list[i]->dev->name, devname))
return gemac_list[i];
}
return NULL;
}
/** PHY read function
* Reads a 16bit value from a MII register
*
* @param[in] mdev Pointer to MII device structure
* @param[in] phy_addr
* @param[in] phy_reg
*
* @return 16bit value on success, a negivie value (-1) on error
*/
static int c2000_phy_read(char *devname, unsigned char phy_addr, unsigned char phy_reg, unsigned short *value)
{
struct c2000_eth_dev *priv = get_gemac(devname);
if (!priv) {
printf("Unknown device %s\n", devname);
return -1;
}
if ((phy_addr > MII_ADDRESS_MAX) || (phy_reg > MII_REGISTER_MAX))
return -1;
gem_phy_man_rd(priv, phy_addr, phy_reg);
if (gem_phy_timeout(priv, MDIO_TIMEOUT))
return -1;
*value = gem_phy_man_data(priv);
dprint("%s: Addr: %d, Reg: %d, Val: 0x%x\n",
__func__, phy_addr, phy_reg, *value);
return 0;
}
/** PHY write function
* Writes a 16bit value to a MII register
*
* @param[in] mdev Pointer to MII device structure
* @param[in] phy_addr
* @param[in] phy_reg
* @param[in] value Value to be written to Phy
*
* @return On success returns 0, a negative value (-1) on error
*/
static int c2000_phy_write(char *devname, unsigned char phy_addr, unsigned char phy_reg, unsigned short value)
{
struct c2000_eth_dev *priv = get_gemac(devname);
if (!priv) {
printf("Unknown device %s\n", devname);
return -1;
}
if ((phy_addr > MII_ADDRESS_MAX) || (phy_reg > MII_REGISTER_MAX))
return -1;
gem_phy_man_wr(priv, phy_addr, phy_reg, value);
if (gem_phy_timeout(priv, MDIO_TIMEOUT))
return -1;
dprint("%s: Addr: %d, Reg: %d, Val: 0x%x\n",
__func__, phy_addr, phy_reg, value);
return 0;
}
/** MAC Address converter
* Convert standard byte style ethernet address to format compatible with MAC.
*
* @param[in] enet_byte_addr Pointer to the mac address in byte format
* @param[out] Pointer to MAC_ADDR structure
*
* @return 0 on success, -1 on failure
*/
int gemac_enet_addr_byte_mac(u8 *enet_byte_addr, MAC_ADDR *enet_addr)
{
if ((enet_byte_addr == NULL) || (enet_addr == NULL))
{
return -1;
}
else
{
enet_addr->bottom = enet_byte_addr[0] |
(enet_byte_addr[1] << 8) |
(enet_byte_addr[2] << 16) |
(enet_byte_addr[3] << 24);
enet_addr->top = enet_byte_addr[4] |
(enet_byte_addr[5] << 8);
return 0;
}
}
/** Configures ethernet address to GEMAC ADDR1 location.
*
* @param[in] edev Pointer to the eth device structure
* @param[in] addr Pointer to ethernet address in byte order
*
* @return 0 on success (always success)
*/
static int c2000_set_ethaddr(struct eth_device *dev, u8 *enet_byte_addr)
{
struct c2000_eth_dev *priv = (struct c2000_eth_dev *)dev->priv;
MAC_ADDR enet_address = {0x0, 0x0};
gemac_enet_addr_byte_mac(enet_byte_addr, &enet_address);
gemac_set_laddr1(priv->gem->gemac_base, &enet_address);
return 0;
}
/** Get/Read configured ethernet mac address from GEMAC.
*
* @param[in] edev Pointer to eth device structure
* @param[out] addr Pointer to ethernet address to be copied
*
* @return 0 on success, always success.
*/
static int c2000_get_ethaddr(struct eth_device *edev, unsigned char *addr)
{
struct c2000_eth_dev *priv = (struct c2000_eth_dev *)edev->priv;
MAC_ADDR enet_address = {0x0, 0x0};
u16 *p = (u16 *)addr;
enet_address = gem_get_laddr1(priv->gem->gemac_base);
p[0] = enet_address.bottom & 0xffff;
p[1] = (enet_address.bottom >> 16) & 0xffff;
p[2] = enet_address.top & 0xffff;
return 0;
}
/** Stops or Disables GEMAC pointing to this eth iface.
*
* @param[in] edev Pointer to eth device structure.
*
* @return none
*/
static void c2000_eth_halt(struct eth_device *edev)
{
struct c2000_eth_dev *priv = (struct c2000_eth_dev *)edev->priv;
// printf("%s:\n",__func__);
gemac_disable(priv->gem->gemac_base);
gpi_disable(priv->gem->egpi_base);
return;
}
#ifdef CONFIG_BOARD_C2KASIC
int c2000_eth_board_init(char *devname, int eth_port)
{
//eth0 port is chosen as criteria for bringingup out of reset because
//all MDIO access can happen through EMAC0 and without bringing eth0 first
//no PHY configuration can happen and no point in removing reset without eth0
if(eth_port != 0)
return 0;
// printf("%s: Bringing PHY out of reset\n",__func__);
//Bring all PHYs out of reset. The correct way is to assert, deassert and re-assert the GPIO reset signal
//with delay in between
writel(readl(COMCERTO_GPIO_OE_REG) | GPIO_3, COMCERTO_GPIO_OE_REG);
writel(readl(COMCERTO_GPIO_OUTPUT_REG) | GPIO_3, COMCERTO_GPIO_OUTPUT_REG);
udelay(100000);
writel(readl(COMCERTO_GPIO_OUTPUT_REG) & ~GPIO_3, COMCERTO_GPIO_OUTPUT_REG);
udelay(100000);
writel(readl(COMCERTO_GPIO_OUTPUT_REG) | GPIO_3, COMCERTO_GPIO_OUTPUT_REG);
udelay(100000);
}
#else
int c2000_eth_board_init(char *devname, int eth_port)
{
//eth0 port is chosen as criteria for bringingup out of reset because
//all MDIO access can happen through EMAC0 and without bringing eth0 first
//no Switch/PHY configuration can happen and no point in removing reset without eth0
if(eth_port == 0)
{
//Bring AR8327 switch out of reset. Toggle reset
writel(readl(COMCERTO_GPIO_OE_REG) | GPIO_27, COMCERTO_GPIO_OE_REG);
writel(readl(COMCERTO_GPIO_OUTPUT_REG) | GPIO_27, COMCERTO_GPIO_OUTPUT_REG);
udelay(10000);
writel(readl(COMCERTO_GPIO_OUTPUT_REG) & ~GPIO_27, COMCERTO_GPIO_OUTPUT_REG);
udelay(10000);
writel(readl(COMCERTO_GPIO_OUTPUT_REG) | GPIO_27, COMCERTO_GPIO_OUTPUT_REG);
udelay(10000);
//AR8327 Switch init
athrs17_init(devname);
//AR8327 WAN PHY4 init
athrs17_phy_setup(devname,EMAC0_PHY_ADDR);
}
else
{
//AR8327 LAN PHYs init
athrs17_phy_setup(devname,EMAC1_PHY_ADDR);
}
}
#endif
static int c2000_eth_init(struct eth_device *dev, bd_t * bd)
{
struct c2000_eth_dev *priv = (struct c2000_eth_dev *)dev->priv;
struct gemac_s *gem = priv->gem;
u32 phyaddr;
// printf("## %s\n",__func__);
/* GEMAC init */
pfe_gemac_init(gem->gemac_base, gem->gemac_mode, gem->gemac_speed, gem->gemac_duplex);
/* set ethernet mac address */
c2000_set_ethaddr(dev, dev->enetaddr);
phyaddr = gem_info[priv->gemac_port].phy_address;
if (!(gem->flags & GEMAC_NO_PHY) && (gem->gemac_mode == RGMII) )
{
miiphy_enable_rgmii_rx_delay(dev->name, phyaddr);
miiphy_enable_rgmii_tx_delay(dev->name, phyaddr);
}
/* Re-negotiate speed and duplex */
if (!(gem->flags & GEMAC_NO_PHY))
{
int speed = gem->gemac_speed;
int duplex = gem->gemac_duplex;
miiphy_speed_duplex(dev->name, phyaddr, &speed, &duplex);
switch (speed)
{
case _10BASET:
speed = SPEED_10M;
break;
case _100BASET:
speed = SPEED_100M;
break;
case _1000BASET:
speed = SPEED_1000M;
break;
}
duplex = (duplex == HALF) ? DUPLEX_HALF:DUPLEX_FULL;
gemac_set_speed(gem->gemac_base, speed);
gemac_set_duplex(gem->gemac_base, duplex);
}
/* Enable GPI */
gpi_enable(priv->gem->egpi_base);
/* Enable GEMAC for tx and rx */
gemac_enable(priv->gem->gemac_base);
return 0;
}
static int c2000_eth_send(struct eth_device *dev, volatile void *data, int length)
{
struct c2000_eth_dev *priv = (struct c2000_eth_dev *)dev->priv;
int rc;
uint64_t tx_tmo;
int i;
rc = pfe_send(priv->gemac_port, data, length);
if (rc < 0) {
printf("Tx Q full\n");
return 0;
}
while (1) {
rc = pfe_tx_done();
if (rc == 0)
break;
udelay(100);
i++;
if(i == 30000)
printf("Tx timeout, send failed\n");
break;
}
return 0;
}
static int c2000_eth_recv(struct eth_device *dev)
{
struct c2000_eth_dev *priv = (struct c2000_eth_dev *)dev->priv;
u32 pkt_buf;
int len;
int phy_port;
len = pfe_recv(&pkt_buf, &phy_port);
if (len < 0)
return 0; //no packet in rx
dprint("Rx pkt: pkt_buf(%08x), phy_port(%d), len(%d)\n", pkt_buf, phy_port, len);
if (phy_port != priv->gemac_port) {
printf("Rx pkt not on expected port\n");
return 0;
}
// Pass the packet up to the protocol layers.
NetReceive((unsigned char *)pkt_buf, len);
return 0;
}
int c2000_gemac_initialize(bd_t * bis, int index, char *devname)
{
struct eth_device *dev;
struct c2000_eth_dev *priv;
struct pfe *pfe;
int i;
//Bring HFE and GEMTX out of reset
writel(0x0, HFE_RESET);
writel(0x0, GEMTX_RESET);
dev = (struct eth_device *)malloc(sizeof(struct eth_device));
if (!dev)
return -1;
memset(dev, 0, sizeof(struct eth_device));
priv = (struct c2000_eth_dev *)malloc(sizeof(struct c2000_eth_dev));
if (!priv)
return -1;
gemac_list[index] = priv;
priv->gemac_port = index;
priv->gem = &gem_info[priv->gemac_port];
priv->dev = dev;
pfe = &priv->pfe;
pfe->cbus_baseaddr = (void *)COMCERTO_AXI_HFE_CFG_BASE;
pfe->ddr_baseaddr = (void *)CONFIG_DDR_BASEADDR;
pfe->ddr_phys_baseaddr = (unsigned long)CONFIG_DDR_PHYS_BASEADDR;
sprintf(dev->name, devname);
dev->priv = priv;
dev->init = c2000_eth_init;
dev->halt = c2000_eth_halt;
dev->send = c2000_eth_send;
dev->recv = c2000_eth_recv;
/* Tell u-boot to get the addr from the env */
for (i = 0; i < 6; i++)
dev->enetaddr[i] = 0;
switch(priv->gemac_port) {
case EMAC_PORT_0:
default:
priv->gem->gemac_base = EMAC1_BASE_ADDR;
priv->gem->egpi_base = EGPI1_BASE_ADDR;
break;
case EMAC_PORT_1:
priv->gem->gemac_base = EMAC2_BASE_ADDR;
priv->gem->egpi_base = EGPI2_BASE_ADDR;
break;
case EMAC_PORT_2:
priv->gem->gemac_base = EMAC3_BASE_ADDR;
priv->gem->egpi_base = EGPI3_BASE_ADDR;
break;
}
pfe_probe(pfe);
priv->phyregisters = (void *)gem_info[gem_info[priv->gemac_port].phy_reg_index].gemac_base;
if(priv->gemac_port == 0)
{
gemac_set_mdc_div(priv->gem->gemac_base, MDC_DIV_96);
gemac_enable_mdio(priv->gem->gemac_base);
miiphy_register(dev->name, c2000_phy_read, c2000_phy_write);
}
c2000_eth_board_init(dev->name, priv->gemac_port);
eth_register(dev);
}