mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-30 16:39:35 +00:00
b78375a806
On DA850/OMAP-L138 it was observed that in RMII mode, auto negotiation was not performed. This patch enables auto negotiation in RMII mode. Without this patch, EMAC initialization takes more time and sometimes tftp fails in RMII mode. Signed-off-by: Rajashekhara, Sudhakar <sudhakar.raj@ti.com> Signed-off-by: Lad, Prabhakar <prabhakar.lad@ti.com> Signed-off-by: Hadli, Manjunath <manjunath.hadli@ti.com>
907 lines
23 KiB
C
907 lines
23 KiB
C
/*
|
|
* Ethernet driver for TI TMS320DM644x (DaVinci) chips.
|
|
*
|
|
* Copyright (C) 2007 Sergey Kubushyn <ksi@koi8.net>
|
|
*
|
|
* Parts shamelessly stolen from TI's dm644x_emac.c. Original copyright
|
|
* follows:
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* dm644x_emac.c
|
|
*
|
|
* TI DaVinci (DM644X) EMAC peripheral driver source for DV-EVM
|
|
*
|
|
* Copyright (C) 2005 Texas Instruments.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
* ----------------------------------------------------------------------------
|
|
|
|
* Modifications:
|
|
* ver. 1.0: Sep 2005, Anant Gole - Created EMAC version for uBoot.
|
|
* ver 1.1: Nov 2005, Anant Gole - Extended the RX logic for multiple descriptors
|
|
*
|
|
*/
|
|
#include <common.h>
|
|
#include <command.h>
|
|
#include <net.h>
|
|
#include <miiphy.h>
|
|
#include <malloc.h>
|
|
#include <linux/compiler.h>
|
|
#include <asm/arch/emac_defs.h>
|
|
#include <asm/io.h>
|
|
#include "davinci_emac.h"
|
|
|
|
unsigned int emac_dbg = 0;
|
|
#define debug_emac(fmt,args...) if (emac_dbg) printf(fmt,##args)
|
|
|
|
#ifdef EMAC_HW_RAM_ADDR
|
|
static inline unsigned long BD_TO_HW(unsigned long x)
|
|
{
|
|
if (x == 0)
|
|
return 0;
|
|
|
|
return x - EMAC_WRAPPER_RAM_ADDR + EMAC_HW_RAM_ADDR;
|
|
}
|
|
|
|
static inline unsigned long HW_TO_BD(unsigned long x)
|
|
{
|
|
if (x == 0)
|
|
return 0;
|
|
|
|
return x - EMAC_HW_RAM_ADDR + EMAC_WRAPPER_RAM_ADDR;
|
|
}
|
|
#else
|
|
#define BD_TO_HW(x) (x)
|
|
#define HW_TO_BD(x) (x)
|
|
#endif
|
|
|
|
#ifdef DAVINCI_EMAC_GIG_ENABLE
|
|
#define emac_gigabit_enable(phy_addr) davinci_eth_gigabit_enable(phy_addr)
|
|
#else
|
|
#define emac_gigabit_enable(phy_addr) /* no gigabit to enable */
|
|
#endif
|
|
|
|
#if !defined(CONFIG_SYS_EMAC_TI_CLKDIV)
|
|
#define CONFIG_SYS_EMAC_TI_CLKDIV ((EMAC_MDIO_BUS_FREQ / \
|
|
EMAC_MDIO_CLOCK_FREQ) - 1)
|
|
#endif
|
|
|
|
static void davinci_eth_mdio_enable(void);
|
|
|
|
static int gen_init_phy(int phy_addr);
|
|
static int gen_is_phy_connected(int phy_addr);
|
|
static int gen_get_link_speed(int phy_addr);
|
|
static int gen_auto_negotiate(int phy_addr);
|
|
|
|
void eth_mdio_enable(void)
|
|
{
|
|
davinci_eth_mdio_enable();
|
|
}
|
|
|
|
/* EMAC Addresses */
|
|
static volatile emac_regs *adap_emac = (emac_regs *)EMAC_BASE_ADDR;
|
|
static volatile ewrap_regs *adap_ewrap = (ewrap_regs *)EMAC_WRAPPER_BASE_ADDR;
|
|
static volatile mdio_regs *adap_mdio = (mdio_regs *)EMAC_MDIO_BASE_ADDR;
|
|
|
|
/* EMAC descriptors */
|
|
static volatile emac_desc *emac_rx_desc = (emac_desc *)(EMAC_WRAPPER_RAM_ADDR + EMAC_RX_DESC_BASE);
|
|
static volatile emac_desc *emac_tx_desc = (emac_desc *)(EMAC_WRAPPER_RAM_ADDR + EMAC_TX_DESC_BASE);
|
|
static volatile emac_desc *emac_rx_active_head = 0;
|
|
static volatile emac_desc *emac_rx_active_tail = 0;
|
|
static int emac_rx_queue_active = 0;
|
|
|
|
/* Receive packet buffers */
|
|
static unsigned char emac_rx_buffers[EMAC_MAX_RX_BUFFERS * EMAC_RXBUF_SIZE]
|
|
__aligned(ARCH_DMA_MINALIGN);
|
|
|
|
#ifndef CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT
|
|
#define CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT 3
|
|
#endif
|
|
|
|
/* PHY address for a discovered PHY (0xff - not found) */
|
|
static u_int8_t active_phy_addr[CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT];
|
|
|
|
/* number of PHY found active */
|
|
static u_int8_t num_phy;
|
|
|
|
phy_t phy[CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT];
|
|
|
|
static inline void davinci_flush_rx_descs(void)
|
|
{
|
|
/* flush the whole RX descs area */
|
|
flush_dcache_range(EMAC_WRAPPER_RAM_ADDR + EMAC_RX_DESC_BASE,
|
|
EMAC_WRAPPER_RAM_ADDR + EMAC_TX_DESC_BASE);
|
|
}
|
|
|
|
static inline void davinci_invalidate_rx_descs(void)
|
|
{
|
|
/* invalidate the whole RX descs area */
|
|
invalidate_dcache_range(EMAC_WRAPPER_RAM_ADDR + EMAC_RX_DESC_BASE,
|
|
EMAC_WRAPPER_RAM_ADDR + EMAC_TX_DESC_BASE);
|
|
}
|
|
|
|
static inline void davinci_flush_desc(emac_desc *desc)
|
|
{
|
|
flush_dcache_range((unsigned long)desc,
|
|
(unsigned long)desc + sizeof(*desc));
|
|
}
|
|
|
|
static int davinci_eth_set_mac_addr(struct eth_device *dev)
|
|
{
|
|
unsigned long mac_hi;
|
|
unsigned long mac_lo;
|
|
|
|
/*
|
|
* Set MAC Addresses & Init multicast Hash to 0 (disable any multicast
|
|
* receive)
|
|
* Using channel 0 only - other channels are disabled
|
|
* */
|
|
writel(0, &adap_emac->MACINDEX);
|
|
mac_hi = (dev->enetaddr[3] << 24) |
|
|
(dev->enetaddr[2] << 16) |
|
|
(dev->enetaddr[1] << 8) |
|
|
(dev->enetaddr[0]);
|
|
mac_lo = (dev->enetaddr[5] << 8) |
|
|
(dev->enetaddr[4]);
|
|
|
|
writel(mac_hi, &adap_emac->MACADDRHI);
|
|
#if defined(DAVINCI_EMAC_VERSION2)
|
|
writel(mac_lo | EMAC_MAC_ADDR_IS_VALID | EMAC_MAC_ADDR_MATCH,
|
|
&adap_emac->MACADDRLO);
|
|
#else
|
|
writel(mac_lo, &adap_emac->MACADDRLO);
|
|
#endif
|
|
|
|
writel(0, &adap_emac->MACHASH1);
|
|
writel(0, &adap_emac->MACHASH2);
|
|
|
|
/* Set source MAC address - REQUIRED */
|
|
writel(mac_hi, &adap_emac->MACSRCADDRHI);
|
|
writel(mac_lo, &adap_emac->MACSRCADDRLO);
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void davinci_eth_mdio_enable(void)
|
|
{
|
|
u_int32_t clkdiv;
|
|
|
|
clkdiv = CONFIG_SYS_EMAC_TI_CLKDIV;
|
|
|
|
writel((clkdiv & 0xff) |
|
|
MDIO_CONTROL_ENABLE |
|
|
MDIO_CONTROL_FAULT |
|
|
MDIO_CONTROL_FAULT_ENABLE,
|
|
&adap_mdio->CONTROL);
|
|
|
|
while (readl(&adap_mdio->CONTROL) & MDIO_CONTROL_IDLE)
|
|
;
|
|
}
|
|
|
|
/*
|
|
* Tries to find an active connected PHY. Returns 1 if address if found.
|
|
* If no active PHY (or more than one PHY) found returns 0.
|
|
* Sets active_phy_addr variable.
|
|
*/
|
|
static int davinci_eth_phy_detect(void)
|
|
{
|
|
u_int32_t phy_act_state;
|
|
int i;
|
|
int j;
|
|
unsigned int count = 0;
|
|
|
|
for (i = 0; i < CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT; i++)
|
|
active_phy_addr[i] = 0xff;
|
|
|
|
udelay(1000);
|
|
phy_act_state = readl(&adap_mdio->ALIVE);
|
|
|
|
if (phy_act_state == 0)
|
|
return 0; /* No active PHYs */
|
|
|
|
debug_emac("davinci_eth_phy_detect(), ALIVE = 0x%08x\n", phy_act_state);
|
|
|
|
for (i = 0, j = 0; i < 32; i++)
|
|
if (phy_act_state & (1 << i)) {
|
|
count++;
|
|
if (count <= CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT) {
|
|
active_phy_addr[j++] = i;
|
|
} else {
|
|
printf("%s: to many PHYs detected.\n",
|
|
__func__);
|
|
count = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
num_phy = count;
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
/* Read a PHY register via MDIO inteface. Returns 1 on success, 0 otherwise */
|
|
int davinci_eth_phy_read(u_int8_t phy_addr, u_int8_t reg_num, u_int16_t *data)
|
|
{
|
|
int tmp;
|
|
|
|
while (readl(&adap_mdio->USERACCESS0) & MDIO_USERACCESS0_GO)
|
|
;
|
|
|
|
writel(MDIO_USERACCESS0_GO |
|
|
MDIO_USERACCESS0_WRITE_READ |
|
|
((reg_num & 0x1f) << 21) |
|
|
((phy_addr & 0x1f) << 16),
|
|
&adap_mdio->USERACCESS0);
|
|
|
|
/* Wait for command to complete */
|
|
while ((tmp = readl(&adap_mdio->USERACCESS0)) & MDIO_USERACCESS0_GO)
|
|
;
|
|
|
|
if (tmp & MDIO_USERACCESS0_ACK) {
|
|
*data = tmp & 0xffff;
|
|
return(1);
|
|
}
|
|
|
|
*data = -1;
|
|
return(0);
|
|
}
|
|
|
|
/* Write to a PHY register via MDIO inteface. Blocks until operation is complete. */
|
|
int davinci_eth_phy_write(u_int8_t phy_addr, u_int8_t reg_num, u_int16_t data)
|
|
{
|
|
|
|
while (readl(&adap_mdio->USERACCESS0) & MDIO_USERACCESS0_GO)
|
|
;
|
|
|
|
writel(MDIO_USERACCESS0_GO |
|
|
MDIO_USERACCESS0_WRITE_WRITE |
|
|
((reg_num & 0x1f) << 21) |
|
|
((phy_addr & 0x1f) << 16) |
|
|
(data & 0xffff),
|
|
&adap_mdio->USERACCESS0);
|
|
|
|
/* Wait for command to complete */
|
|
while (readl(&adap_mdio->USERACCESS0) & MDIO_USERACCESS0_GO)
|
|
;
|
|
|
|
return(1);
|
|
}
|
|
|
|
/* PHY functions for a generic PHY */
|
|
static int gen_init_phy(int phy_addr)
|
|
{
|
|
int ret = 1;
|
|
|
|
if (gen_get_link_speed(phy_addr)) {
|
|
/* Try another time */
|
|
ret = gen_get_link_speed(phy_addr);
|
|
}
|
|
|
|
return(ret);
|
|
}
|
|
|
|
static int gen_is_phy_connected(int phy_addr)
|
|
{
|
|
u_int16_t dummy;
|
|
|
|
return davinci_eth_phy_read(phy_addr, MII_PHYSID1, &dummy);
|
|
}
|
|
|
|
static int get_active_phy(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < num_phy; i++)
|
|
if (phy[i].get_link_speed(active_phy_addr[i]))
|
|
return i;
|
|
|
|
return -1; /* Return error if no link */
|
|
}
|
|
|
|
static int gen_get_link_speed(int phy_addr)
|
|
{
|
|
u_int16_t tmp;
|
|
|
|
if (davinci_eth_phy_read(phy_addr, MII_STATUS_REG, &tmp) &&
|
|
(tmp & 0x04)) {
|
|
#if defined(CONFIG_DRIVER_TI_EMAC_USE_RMII) && \
|
|
defined(CONFIG_MACH_DAVINCI_DA850_EVM)
|
|
davinci_eth_phy_read(phy_addr, MII_LPA, &tmp);
|
|
|
|
/* Speed doesn't matter, there is no setting for it in EMAC. */
|
|
if (tmp & (LPA_100FULL | LPA_10FULL)) {
|
|
/* set EMAC for Full Duplex */
|
|
writel(EMAC_MACCONTROL_MIIEN_ENABLE |
|
|
EMAC_MACCONTROL_FULLDUPLEX_ENABLE,
|
|
&adap_emac->MACCONTROL);
|
|
} else {
|
|
/*set EMAC for Half Duplex */
|
|
writel(EMAC_MACCONTROL_MIIEN_ENABLE,
|
|
&adap_emac->MACCONTROL);
|
|
}
|
|
|
|
if (tmp & (LPA_100FULL | LPA_100HALF))
|
|
writel(readl(&adap_emac->MACCONTROL) |
|
|
EMAC_MACCONTROL_RMIISPEED_100,
|
|
&adap_emac->MACCONTROL);
|
|
else
|
|
writel(readl(&adap_emac->MACCONTROL) &
|
|
~EMAC_MACCONTROL_RMIISPEED_100,
|
|
&adap_emac->MACCONTROL);
|
|
#endif
|
|
return(1);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
static int gen_auto_negotiate(int phy_addr)
|
|
{
|
|
u_int16_t tmp;
|
|
u_int16_t val;
|
|
unsigned long cntr = 0;
|
|
|
|
if (!davinci_eth_phy_read(phy_addr, MII_BMCR, &tmp))
|
|
return 0;
|
|
|
|
val = tmp | BMCR_FULLDPLX | BMCR_ANENABLE |
|
|
BMCR_SPEED100;
|
|
davinci_eth_phy_write(phy_addr, MII_BMCR, val);
|
|
|
|
if (!davinci_eth_phy_read(phy_addr, MII_ADVERTISE, &val))
|
|
return 0;
|
|
|
|
val |= (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL |
|
|
ADVERTISE_10HALF);
|
|
davinci_eth_phy_write(phy_addr, MII_ADVERTISE, val);
|
|
|
|
if (!davinci_eth_phy_read(phy_addr, MII_BMCR, &tmp))
|
|
return(0);
|
|
|
|
/* Restart Auto_negotiation */
|
|
tmp |= BMCR_ANRESTART;
|
|
davinci_eth_phy_write(phy_addr, MII_BMCR, tmp);
|
|
|
|
/*check AutoNegotiate complete */
|
|
do {
|
|
udelay(40000);
|
|
if (!davinci_eth_phy_read(phy_addr, MII_BMSR, &tmp))
|
|
return 0;
|
|
|
|
if (tmp & BMSR_ANEGCOMPLETE)
|
|
break;
|
|
|
|
cntr++;
|
|
} while (cntr < 200);
|
|
|
|
if (!davinci_eth_phy_read(phy_addr, MII_BMSR, &tmp))
|
|
return(0);
|
|
|
|
if (!(tmp & BMSR_ANEGCOMPLETE))
|
|
return(0);
|
|
|
|
return(gen_get_link_speed(phy_addr));
|
|
}
|
|
/* End of generic PHY functions */
|
|
|
|
|
|
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
|
|
static int davinci_mii_phy_read(const char *devname, unsigned char addr, unsigned char reg, unsigned short *value)
|
|
{
|
|
return(davinci_eth_phy_read(addr, reg, value) ? 0 : 1);
|
|
}
|
|
|
|
static int davinci_mii_phy_write(const char *devname, unsigned char addr, unsigned char reg, unsigned short value)
|
|
{
|
|
return(davinci_eth_phy_write(addr, reg, value) ? 0 : 1);
|
|
}
|
|
#endif
|
|
|
|
static void __attribute__((unused)) davinci_eth_gigabit_enable(int phy_addr)
|
|
{
|
|
u_int16_t data;
|
|
|
|
if (davinci_eth_phy_read(phy_addr, 0, &data)) {
|
|
if (data & (1 << 6)) { /* speed selection MSB */
|
|
/*
|
|
* Check if link detected is giga-bit
|
|
* If Gigabit mode detected, enable gigbit in MAC
|
|
*/
|
|
writel(readl(&adap_emac->MACCONTROL) |
|
|
EMAC_MACCONTROL_GIGFORCE |
|
|
EMAC_MACCONTROL_GIGABIT_ENABLE,
|
|
&adap_emac->MACCONTROL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Eth device open */
|
|
static int davinci_eth_open(struct eth_device *dev, bd_t *bis)
|
|
{
|
|
dv_reg_p addr;
|
|
u_int32_t clkdiv, cnt;
|
|
volatile emac_desc *rx_desc;
|
|
int index;
|
|
|
|
debug_emac("+ emac_open\n");
|
|
|
|
/* Reset EMAC module and disable interrupts in wrapper */
|
|
writel(1, &adap_emac->SOFTRESET);
|
|
while (readl(&adap_emac->SOFTRESET) != 0)
|
|
;
|
|
#if defined(DAVINCI_EMAC_VERSION2)
|
|
writel(1, &adap_ewrap->softrst);
|
|
while (readl(&adap_ewrap->softrst) != 0)
|
|
;
|
|
#else
|
|
writel(0, &adap_ewrap->EWCTL);
|
|
for (cnt = 0; cnt < 5; cnt++) {
|
|
clkdiv = readl(&adap_ewrap->EWCTL);
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_DRIVER_TI_EMAC_USE_RMII) && \
|
|
defined(CONFIG_MACH_DAVINCI_DA850_EVM)
|
|
adap_ewrap->c0rxen = adap_ewrap->c1rxen = adap_ewrap->c2rxen = 0;
|
|
adap_ewrap->c0txen = adap_ewrap->c1txen = adap_ewrap->c2txen = 0;
|
|
adap_ewrap->c0miscen = adap_ewrap->c1miscen = adap_ewrap->c2miscen = 0;
|
|
#endif
|
|
rx_desc = emac_rx_desc;
|
|
|
|
writel(1, &adap_emac->TXCONTROL);
|
|
writel(1, &adap_emac->RXCONTROL);
|
|
|
|
davinci_eth_set_mac_addr(dev);
|
|
|
|
/* Set DMA 8 TX / 8 RX Head pointers to 0 */
|
|
addr = &adap_emac->TX0HDP;
|
|
for(cnt = 0; cnt < 16; cnt++)
|
|
writel(0, addr++);
|
|
|
|
addr = &adap_emac->RX0HDP;
|
|
for(cnt = 0; cnt < 16; cnt++)
|
|
writel(0, addr++);
|
|
|
|
/* Clear Statistics (do this before setting MacControl register) */
|
|
addr = &adap_emac->RXGOODFRAMES;
|
|
for(cnt = 0; cnt < EMAC_NUM_STATS; cnt++)
|
|
writel(0, addr++);
|
|
|
|
/* No multicast addressing */
|
|
writel(0, &adap_emac->MACHASH1);
|
|
writel(0, &adap_emac->MACHASH2);
|
|
|
|
/* Create RX queue and set receive process in place */
|
|
emac_rx_active_head = emac_rx_desc;
|
|
for (cnt = 0; cnt < EMAC_MAX_RX_BUFFERS; cnt++) {
|
|
rx_desc->next = BD_TO_HW((u_int32_t)(rx_desc + 1));
|
|
rx_desc->buffer = &emac_rx_buffers[cnt * EMAC_RXBUF_SIZE];
|
|
rx_desc->buff_off_len = EMAC_MAX_ETHERNET_PKT_SIZE;
|
|
rx_desc->pkt_flag_len = EMAC_CPPI_OWNERSHIP_BIT;
|
|
rx_desc++;
|
|
}
|
|
|
|
/* Finalize the rx desc list */
|
|
rx_desc--;
|
|
rx_desc->next = 0;
|
|
emac_rx_active_tail = rx_desc;
|
|
emac_rx_queue_active = 1;
|
|
|
|
davinci_flush_rx_descs();
|
|
|
|
/* Enable TX/RX */
|
|
writel(EMAC_MAX_ETHERNET_PKT_SIZE, &adap_emac->RXMAXLEN);
|
|
writel(0, &adap_emac->RXBUFFEROFFSET);
|
|
|
|
/*
|
|
* No fancy configs - Use this for promiscous debug
|
|
* - EMAC_RXMBPENABLE_RXCAFEN_ENABLE
|
|
*/
|
|
writel(EMAC_RXMBPENABLE_RXBROADEN, &adap_emac->RXMBPENABLE);
|
|
|
|
/* Enable ch 0 only */
|
|
writel(1, &adap_emac->RXUNICASTSET);
|
|
|
|
/* Enable MII interface and Full duplex mode */
|
|
#if defined(CONFIG_SOC_DA8XX) || \
|
|
(defined(CONFIG_OMAP34XX) && defined(CONFIG_DRIVER_TI_EMAC_USE_RMII))
|
|
writel((EMAC_MACCONTROL_MIIEN_ENABLE |
|
|
EMAC_MACCONTROL_FULLDUPLEX_ENABLE |
|
|
EMAC_MACCONTROL_RMIISPEED_100),
|
|
&adap_emac->MACCONTROL);
|
|
#else
|
|
writel((EMAC_MACCONTROL_MIIEN_ENABLE |
|
|
EMAC_MACCONTROL_FULLDUPLEX_ENABLE),
|
|
&adap_emac->MACCONTROL);
|
|
#endif
|
|
|
|
/* Init MDIO & get link state */
|
|
clkdiv = CONFIG_SYS_EMAC_TI_CLKDIV;
|
|
writel((clkdiv & 0xff) | MDIO_CONTROL_ENABLE | MDIO_CONTROL_FAULT,
|
|
&adap_mdio->CONTROL);
|
|
|
|
/* We need to wait for MDIO to start */
|
|
udelay(1000);
|
|
|
|
index = get_active_phy();
|
|
if (index == -1)
|
|
return(0);
|
|
|
|
emac_gigabit_enable(active_phy_addr[index]);
|
|
|
|
/* Start receive process */
|
|
writel(BD_TO_HW((u_int32_t)emac_rx_desc), &adap_emac->RX0HDP);
|
|
|
|
debug_emac("- emac_open\n");
|
|
|
|
return(1);
|
|
}
|
|
|
|
/* EMAC Channel Teardown */
|
|
static void davinci_eth_ch_teardown(int ch)
|
|
{
|
|
dv_reg dly = 0xff;
|
|
dv_reg cnt;
|
|
|
|
debug_emac("+ emac_ch_teardown\n");
|
|
|
|
if (ch == EMAC_CH_TX) {
|
|
/* Init TX channel teardown */
|
|
writel(0, &adap_emac->TXTEARDOWN);
|
|
do {
|
|
/*
|
|
* Wait here for Tx teardown completion interrupt to
|
|
* occur. Note: A task delay can be called here to pend
|
|
* rather than occupying CPU cycles - anyway it has
|
|
* been found that teardown takes very few cpu cycles
|
|
* and does not affect functionality
|
|
*/
|
|
dly--;
|
|
udelay(1);
|
|
if (dly == 0)
|
|
break;
|
|
cnt = readl(&adap_emac->TX0CP);
|
|
} while (cnt != 0xfffffffc);
|
|
writel(cnt, &adap_emac->TX0CP);
|
|
writel(0, &adap_emac->TX0HDP);
|
|
} else {
|
|
/* Init RX channel teardown */
|
|
writel(0, &adap_emac->RXTEARDOWN);
|
|
do {
|
|
/*
|
|
* Wait here for Rx teardown completion interrupt to
|
|
* occur. Note: A task delay can be called here to pend
|
|
* rather than occupying CPU cycles - anyway it has
|
|
* been found that teardown takes very few cpu cycles
|
|
* and does not affect functionality
|
|
*/
|
|
dly--;
|
|
udelay(1);
|
|
if (dly == 0)
|
|
break;
|
|
cnt = readl(&adap_emac->RX0CP);
|
|
} while (cnt != 0xfffffffc);
|
|
writel(cnt, &adap_emac->RX0CP);
|
|
writel(0, &adap_emac->RX0HDP);
|
|
}
|
|
|
|
debug_emac("- emac_ch_teardown\n");
|
|
}
|
|
|
|
/* Eth device close */
|
|
static void davinci_eth_close(struct eth_device *dev)
|
|
{
|
|
debug_emac("+ emac_close\n");
|
|
|
|
davinci_eth_ch_teardown(EMAC_CH_TX); /* TX Channel teardown */
|
|
davinci_eth_ch_teardown(EMAC_CH_RX); /* RX Channel teardown */
|
|
|
|
/* Reset EMAC module and disable interrupts in wrapper */
|
|
writel(1, &adap_emac->SOFTRESET);
|
|
#if defined(DAVINCI_EMAC_VERSION2)
|
|
writel(1, &adap_ewrap->softrst);
|
|
#else
|
|
writel(0, &adap_ewrap->EWCTL);
|
|
#endif
|
|
|
|
#if defined(CONFIG_DRIVER_TI_EMAC_USE_RMII) && \
|
|
defined(CONFIG_MACH_DAVINCI_DA850_EVM)
|
|
adap_ewrap->c0rxen = adap_ewrap->c1rxen = adap_ewrap->c2rxen = 0;
|
|
adap_ewrap->c0txen = adap_ewrap->c1txen = adap_ewrap->c2txen = 0;
|
|
adap_ewrap->c0miscen = adap_ewrap->c1miscen = adap_ewrap->c2miscen = 0;
|
|
#endif
|
|
debug_emac("- emac_close\n");
|
|
}
|
|
|
|
static int tx_send_loop = 0;
|
|
|
|
/*
|
|
* This function sends a single packet on the network and returns
|
|
* positive number (number of bytes transmitted) or negative for error
|
|
*/
|
|
static int davinci_eth_send_packet (struct eth_device *dev,
|
|
void *packet, int length)
|
|
{
|
|
int ret_status = -1;
|
|
int index;
|
|
tx_send_loop = 0;
|
|
|
|
index = get_active_phy();
|
|
if (index == -1) {
|
|
printf(" WARN: emac_send_packet: No link\n");
|
|
return (ret_status);
|
|
}
|
|
|
|
emac_gigabit_enable(active_phy_addr[index]);
|
|
|
|
/* Check packet size and if < EMAC_MIN_ETHERNET_PKT_SIZE, pad it up */
|
|
if (length < EMAC_MIN_ETHERNET_PKT_SIZE) {
|
|
length = EMAC_MIN_ETHERNET_PKT_SIZE;
|
|
}
|
|
|
|
/* Populate the TX descriptor */
|
|
emac_tx_desc->next = 0;
|
|
emac_tx_desc->buffer = (u_int8_t *) packet;
|
|
emac_tx_desc->buff_off_len = (length & 0xffff);
|
|
emac_tx_desc->pkt_flag_len = ((length & 0xffff) |
|
|
EMAC_CPPI_SOP_BIT |
|
|
EMAC_CPPI_OWNERSHIP_BIT |
|
|
EMAC_CPPI_EOP_BIT);
|
|
|
|
flush_dcache_range((unsigned long)packet,
|
|
(unsigned long)packet + length);
|
|
davinci_flush_desc(emac_tx_desc);
|
|
|
|
/* Send the packet */
|
|
writel(BD_TO_HW((unsigned long)emac_tx_desc), &adap_emac->TX0HDP);
|
|
|
|
/* Wait for packet to complete or link down */
|
|
while (1) {
|
|
if (!phy[index].get_link_speed(active_phy_addr[index])) {
|
|
davinci_eth_ch_teardown (EMAC_CH_TX);
|
|
return (ret_status);
|
|
}
|
|
|
|
emac_gigabit_enable(active_phy_addr[index]);
|
|
|
|
if (readl(&adap_emac->TXINTSTATRAW) & 0x01) {
|
|
ret_status = length;
|
|
break;
|
|
}
|
|
tx_send_loop++;
|
|
}
|
|
|
|
return (ret_status);
|
|
}
|
|
|
|
/*
|
|
* This function handles receipt of a packet from the network
|
|
*/
|
|
static int davinci_eth_rcv_packet (struct eth_device *dev)
|
|
{
|
|
volatile emac_desc *rx_curr_desc;
|
|
volatile emac_desc *curr_desc;
|
|
volatile emac_desc *tail_desc;
|
|
int status, ret = -1;
|
|
|
|
davinci_invalidate_rx_descs();
|
|
|
|
rx_curr_desc = emac_rx_active_head;
|
|
status = rx_curr_desc->pkt_flag_len;
|
|
if ((rx_curr_desc) && ((status & EMAC_CPPI_OWNERSHIP_BIT) == 0)) {
|
|
if (status & EMAC_CPPI_RX_ERROR_FRAME) {
|
|
/* Error in packet - discard it and requeue desc */
|
|
printf ("WARN: emac_rcv_pkt: Error in packet\n");
|
|
} else {
|
|
unsigned long tmp = (unsigned long)rx_curr_desc->buffer;
|
|
|
|
invalidate_dcache_range(tmp, tmp + EMAC_RXBUF_SIZE);
|
|
NetReceive (rx_curr_desc->buffer,
|
|
(rx_curr_desc->buff_off_len & 0xffff));
|
|
ret = rx_curr_desc->buff_off_len & 0xffff;
|
|
}
|
|
|
|
/* Ack received packet descriptor */
|
|
writel(BD_TO_HW((ulong)rx_curr_desc), &adap_emac->RX0CP);
|
|
curr_desc = rx_curr_desc;
|
|
emac_rx_active_head =
|
|
(volatile emac_desc *) (HW_TO_BD(rx_curr_desc->next));
|
|
|
|
if (status & EMAC_CPPI_EOQ_BIT) {
|
|
if (emac_rx_active_head) {
|
|
writel(BD_TO_HW((ulong)emac_rx_active_head),
|
|
&adap_emac->RX0HDP);
|
|
} else {
|
|
emac_rx_queue_active = 0;
|
|
printf ("INFO:emac_rcv_packet: RX Queue not active\n");
|
|
}
|
|
}
|
|
|
|
/* Recycle RX descriptor */
|
|
rx_curr_desc->buff_off_len = EMAC_MAX_ETHERNET_PKT_SIZE;
|
|
rx_curr_desc->pkt_flag_len = EMAC_CPPI_OWNERSHIP_BIT;
|
|
rx_curr_desc->next = 0;
|
|
davinci_flush_desc(rx_curr_desc);
|
|
|
|
if (emac_rx_active_head == 0) {
|
|
printf ("INFO: emac_rcv_pkt: active queue head = 0\n");
|
|
emac_rx_active_head = curr_desc;
|
|
emac_rx_active_tail = curr_desc;
|
|
if (emac_rx_queue_active != 0) {
|
|
writel(BD_TO_HW((ulong)emac_rx_active_head),
|
|
&adap_emac->RX0HDP);
|
|
printf ("INFO: emac_rcv_pkt: active queue head = 0, HDP fired\n");
|
|
emac_rx_queue_active = 1;
|
|
}
|
|
} else {
|
|
tail_desc = emac_rx_active_tail;
|
|
emac_rx_active_tail = curr_desc;
|
|
tail_desc->next = BD_TO_HW((ulong) curr_desc);
|
|
status = tail_desc->pkt_flag_len;
|
|
if (status & EMAC_CPPI_EOQ_BIT) {
|
|
davinci_flush_desc(tail_desc);
|
|
writel(BD_TO_HW((ulong)curr_desc),
|
|
&adap_emac->RX0HDP);
|
|
status &= ~EMAC_CPPI_EOQ_BIT;
|
|
tail_desc->pkt_flag_len = status;
|
|
}
|
|
davinci_flush_desc(tail_desc);
|
|
}
|
|
return (ret);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* This function initializes the emac hardware. It does NOT initialize
|
|
* EMAC modules power or pin multiplexors, that is done by board_init()
|
|
* much earlier in bootup process. Returns 1 on success, 0 otherwise.
|
|
*/
|
|
int davinci_emac_initialize(void)
|
|
{
|
|
u_int32_t phy_id;
|
|
u_int16_t tmp;
|
|
int i;
|
|
int ret;
|
|
struct eth_device *dev;
|
|
|
|
dev = malloc(sizeof *dev);
|
|
|
|
if (dev == NULL)
|
|
return -1;
|
|
|
|
memset(dev, 0, sizeof *dev);
|
|
sprintf(dev->name, "DaVinci-EMAC");
|
|
|
|
dev->iobase = 0;
|
|
dev->init = davinci_eth_open;
|
|
dev->halt = davinci_eth_close;
|
|
dev->send = davinci_eth_send_packet;
|
|
dev->recv = davinci_eth_rcv_packet;
|
|
dev->write_hwaddr = davinci_eth_set_mac_addr;
|
|
|
|
eth_register(dev);
|
|
|
|
davinci_eth_mdio_enable();
|
|
|
|
/* let the EMAC detect the PHYs */
|
|
udelay(5000);
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
if (readl(&adap_mdio->ALIVE))
|
|
break;
|
|
udelay(1000);
|
|
}
|
|
|
|
if (i >= 256) {
|
|
printf("No ETH PHY detected!!!\n");
|
|
return(0);
|
|
}
|
|
|
|
/* Find if PHY(s) is/are connected */
|
|
ret = davinci_eth_phy_detect();
|
|
if (!ret)
|
|
return(0);
|
|
else
|
|
debug_emac(" %d ETH PHY detected\n", ret);
|
|
|
|
/* Get PHY ID and initialize phy_ops for a detected PHY */
|
|
for (i = 0; i < num_phy; i++) {
|
|
if (!davinci_eth_phy_read(active_phy_addr[i], MII_PHYSID1,
|
|
&tmp)) {
|
|
active_phy_addr[i] = 0xff;
|
|
continue;
|
|
}
|
|
|
|
phy_id = (tmp << 16) & 0xffff0000;
|
|
|
|
if (!davinci_eth_phy_read(active_phy_addr[i], MII_PHYSID2,
|
|
&tmp)) {
|
|
active_phy_addr[i] = 0xff;
|
|
continue;
|
|
}
|
|
|
|
phy_id |= tmp & 0x0000ffff;
|
|
|
|
switch (phy_id) {
|
|
#ifdef PHY_KSZ8873
|
|
case PHY_KSZ8873:
|
|
sprintf(phy[i].name, "KSZ8873 @ 0x%02x",
|
|
active_phy_addr[i]);
|
|
phy[i].init = ksz8873_init_phy;
|
|
phy[i].is_phy_connected = ksz8873_is_phy_connected;
|
|
phy[i].get_link_speed = ksz8873_get_link_speed;
|
|
phy[i].auto_negotiate = ksz8873_auto_negotiate;
|
|
break;
|
|
#endif
|
|
#ifdef PHY_LXT972
|
|
case PHY_LXT972:
|
|
sprintf(phy[i].name, "LXT972 @ 0x%02x",
|
|
active_phy_addr[i]);
|
|
phy[i].init = lxt972_init_phy;
|
|
phy[i].is_phy_connected = lxt972_is_phy_connected;
|
|
phy[i].get_link_speed = lxt972_get_link_speed;
|
|
phy[i].auto_negotiate = lxt972_auto_negotiate;
|
|
break;
|
|
#endif
|
|
#ifdef PHY_DP83848
|
|
case PHY_DP83848:
|
|
sprintf(phy[i].name, "DP83848 @ 0x%02x",
|
|
active_phy_addr[i]);
|
|
phy[i].init = dp83848_init_phy;
|
|
phy[i].is_phy_connected = dp83848_is_phy_connected;
|
|
phy[i].get_link_speed = dp83848_get_link_speed;
|
|
phy[i].auto_negotiate = dp83848_auto_negotiate;
|
|
break;
|
|
#endif
|
|
#ifdef PHY_ET1011C
|
|
case PHY_ET1011C:
|
|
sprintf(phy[i].name, "ET1011C @ 0x%02x",
|
|
active_phy_addr[i]);
|
|
phy[i].init = gen_init_phy;
|
|
phy[i].is_phy_connected = gen_is_phy_connected;
|
|
phy[i].get_link_speed = et1011c_get_link_speed;
|
|
phy[i].auto_negotiate = gen_auto_negotiate;
|
|
break;
|
|
#endif
|
|
default:
|
|
sprintf(phy[i].name, "GENERIC @ 0x%02x",
|
|
active_phy_addr[i]);
|
|
phy[i].init = gen_init_phy;
|
|
phy[i].is_phy_connected = gen_is_phy_connected;
|
|
phy[i].get_link_speed = gen_get_link_speed;
|
|
phy[i].auto_negotiate = gen_auto_negotiate;
|
|
}
|
|
|
|
debug("Ethernet PHY: %s\n", phy[i].name);
|
|
|
|
miiphy_register(phy[i].name, davinci_mii_phy_read,
|
|
davinci_mii_phy_write);
|
|
}
|
|
|
|
#if defined(CONFIG_DRIVER_TI_EMAC_USE_RMII) && \
|
|
defined(CONFIG_MACH_DAVINCI_DA850_EVM)
|
|
for (i = 0; i < num_phy; i++) {
|
|
if (phy[i].is_phy_connected(i))
|
|
phy[i].auto_negotiate(i);
|
|
}
|
|
#endif
|
|
return(1);
|
|
}
|