mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-26 14:40:41 +00:00
6e7df1d151
At this point, the remaining places where we have a symbol that is defined as CONFIG_... are in fairly odd locations. While as much dead code has been removed as possible, some of these locations are simply less obvious at first. In other cases, this code is used, but was defined in such a way as to have been missed by earlier checks. Perform a rename of all such remaining symbols to be CFG_... rather than CONFIG_... Signed-off-by: Tom Rini <trini@konsulko.com> Reviewed-by: Simon Glass <sjg@chromium.org>
869 lines
22 KiB
C
869 lines
22 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* 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.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* 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 <cpu_func.h>
|
|
#include <log.h>
|
|
#include <net.h>
|
|
#include <miiphy.h>
|
|
#include <malloc.h>
|
|
#include <asm/cache.h>
|
|
#include <linux/compiler.h>
|
|
#include <asm/arch/emac_defs.h>
|
|
#include <asm/io.h>
|
|
#include <linux/delay.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(CFG_SYS_EMAC_TI_CLKDIV)
|
|
#define CFG_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 CFG_SYS_DAVINCI_EMAC_PHY_COUNT
|
|
#define CFG_SYS_DAVINCI_EMAC_PHY_COUNT 3
|
|
#endif
|
|
|
|
/* PHY address for a discovered PHY (0xff - not found) */
|
|
static u_int8_t active_phy_addr[CFG_SYS_DAVINCI_EMAC_PHY_COUNT];
|
|
|
|
/* number of PHY found active */
|
|
static u_int8_t num_phy;
|
|
|
|
phy_t phy[CFG_SYS_DAVINCI_EMAC_PHY_COUNT];
|
|
|
|
static int davinci_emac_write_hwaddr(struct udevice *dev)
|
|
{
|
|
struct eth_pdata *pdata = dev_get_plat(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 = (pdata->enetaddr[3] << 24) |
|
|
(pdata->enetaddr[2] << 16) |
|
|
(pdata->enetaddr[1] << 8) |
|
|
(pdata->enetaddr[0]);
|
|
mac_lo = (pdata->enetaddr[5] << 8) |
|
|
(pdata->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 = CFG_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 < CFG_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 <= CFG_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;
|
|
}
|
|
|
|
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);
|
|
|
|
#ifdef DAVINCI_EMAC_GIG_ENABLE
|
|
davinci_eth_phy_read(phy_addr, MII_CTRL1000, &val);
|
|
val |= PHY_1000BTCR_1000FD;
|
|
val &= ~PHY_1000BTCR_1000HD;
|
|
davinci_eth_phy_write(phy_addr, MII_CTRL1000, val);
|
|
davinci_eth_phy_read(phy_addr, MII_CTRL1000, &val);
|
|
#endif
|
|
|
|
/* 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(struct mii_dev *bus, int addr, int devad,
|
|
int reg)
|
|
{
|
|
unsigned short value = 0;
|
|
int retval = davinci_eth_phy_read(addr, reg, &value);
|
|
|
|
return retval ? value : -EIO;
|
|
}
|
|
|
|
static int davinci_mii_phy_write(struct mii_dev *bus, int addr, int devad,
|
|
int reg, u16 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_emac_start(struct udevice *dev)
|
|
{
|
|
dv_reg_p addr;
|
|
u_int32_t clkdiv, cnt, mac_control;
|
|
uint16_t __maybe_unused lpa_val;
|
|
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_emac_write_hwaddr(dev);
|
|
|
|
/* Set DMA 8 TX / 8 RX Head pointers to 0 */
|
|
addr = &adap_emac->TX0HDP;
|
|
for (cnt = 0; cnt < 8; cnt++)
|
|
writel(0, addr++);
|
|
|
|
addr = &adap_emac->RX0HDP;
|
|
for (cnt = 0; cnt < 8; 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;
|
|
|
|
/* 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);
|
|
|
|
/* Init MDIO & get link state */
|
|
clkdiv = CFG_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);
|
|
|
|
/* Enable MII interface */
|
|
mac_control = EMAC_MACCONTROL_MIIEN_ENABLE;
|
|
#ifdef DAVINCI_EMAC_GIG_ENABLE
|
|
davinci_eth_phy_read(active_phy_addr[index], MII_STAT1000, &lpa_val);
|
|
if (lpa_val & PHY_1000BTSR_1000FD) {
|
|
debug_emac("eth_open : gigabit negotiated\n");
|
|
mac_control |= EMAC_MACCONTROL_FULLDUPLEX_ENABLE;
|
|
mac_control |= EMAC_MACCONTROL_GIGABIT_ENABLE;
|
|
}
|
|
#endif
|
|
|
|
davinci_eth_phy_read(active_phy_addr[index], MII_LPA, &lpa_val);
|
|
if (lpa_val & (LPA_100FULL | LPA_10FULL))
|
|
/* set EMAC for Full Duplex */
|
|
mac_control |= EMAC_MACCONTROL_FULLDUPLEX_ENABLE;
|
|
#if defined(CONFIG_SOC_DA8XX) || \
|
|
(defined(CONFIG_OMAP34XX) && defined(CONFIG_DRIVER_TI_EMAC_USE_RMII))
|
|
mac_control |= EMAC_MACCONTROL_RMIISPEED_100;
|
|
#endif
|
|
writel(mac_control, &adap_emac->MACCONTROL);
|
|
/* 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_emac_stop(struct udevice *dev)
|
|
{
|
|
debug_emac("+ emac_close\n");
|
|
|
|
davinci_eth_ch_teardown(EMAC_CH_TX); /* TX Channel teardown */
|
|
if (readl(&adap_emac->RXCONTROL) & 1)
|
|
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_emac_send(struct udevice *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);
|
|
}
|
|
|
|
/* 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 + ALIGN(length, PKTALIGN));
|
|
|
|
/* 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);
|
|
}
|
|
|
|
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_emac_recv(struct udevice *dev, int flags, uchar **packetp)
|
|
{
|
|
volatile emac_desc *rx_curr_desc;
|
|
volatile emac_desc *curr_desc;
|
|
volatile emac_desc *tail_desc;
|
|
int status, ret = -1;
|
|
|
|
rx_curr_desc = emac_rx_active_head;
|
|
if (!rx_curr_desc)
|
|
return 0;
|
|
*packetp = rx_curr_desc->buffer;
|
|
status = rx_curr_desc->pkt_flag_len;
|
|
if ((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;
|
|
unsigned short len =
|
|
rx_curr_desc->buff_off_len & 0xffff;
|
|
|
|
invalidate_dcache_range(tmp, tmp + ALIGN(len, PKTALIGN));
|
|
ret = len;
|
|
}
|
|
|
|
/* 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;
|
|
|
|
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) {
|
|
writel(BD_TO_HW((ulong)curr_desc),
|
|
&adap_emac->RX0HDP);
|
|
status &= ~EMAC_CPPI_EOQ_BIT;
|
|
tail_desc->pkt_flag_len = status;
|
|
}
|
|
}
|
|
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.
|
|
*/
|
|
static int davinci_emac_probe(struct udevice *dev)
|
|
{
|
|
u_int32_t phy_id;
|
|
u_int16_t tmp;
|
|
int i;
|
|
int ret;
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
int retval;
|
|
struct mii_dev *mdiodev = mdio_alloc();
|
|
if (!mdiodev)
|
|
return -ENOMEM;
|
|
strlcpy(mdiodev->name, phy[i].name, MDIO_NAME_LEN);
|
|
mdiodev->read = davinci_mii_phy_read;
|
|
mdiodev->write = davinci_mii_phy_write;
|
|
|
|
retval = mdio_register(mdiodev);
|
|
if (retval < 0)
|
|
return retval;
|
|
#ifdef DAVINCI_EMAC_GIG_ENABLE
|
|
#define PHY_CONF_REG 22
|
|
/* Enable PHY to clock out TX_CLK */
|
|
davinci_eth_phy_read(active_phy_addr[i], PHY_CONF_REG, &tmp);
|
|
tmp |= PHY_CONF_TXCLKEN;
|
|
davinci_eth_phy_write(active_phy_addr[i], PHY_CONF_REG, tmp);
|
|
davinci_eth_phy_read(active_phy_addr[i], PHY_CONF_REG, &tmp);
|
|
#endif
|
|
}
|
|
|
|
#if defined(CONFIG_TI816X) || (defined(CONFIG_DRIVER_TI_EMAC_USE_RMII) && \
|
|
defined(CONFIG_MACH_DAVINCI_DA850_EVM) && \
|
|
!defined(CONFIG_DRIVER_TI_EMAC_RMII_NO_NEGOTIATE))
|
|
for (i = 0; i < num_phy; i++) {
|
|
if (phy[i].is_phy_connected(i))
|
|
phy[i].auto_negotiate(i);
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static const struct eth_ops davinci_emac_ops = {
|
|
.start = davinci_emac_start,
|
|
.send = davinci_emac_send,
|
|
.recv = davinci_emac_recv,
|
|
.stop = davinci_emac_stop,
|
|
.write_hwaddr = davinci_emac_write_hwaddr,
|
|
};
|
|
|
|
static const struct udevice_id davinci_emac_ids[] = {
|
|
{ .compatible = "ti,davinci-dm6467-emac" },
|
|
{ .compatible = "ti,am3517-emac", },
|
|
{ .compatible = "ti,dm816-emac", },
|
|
{ }
|
|
};
|
|
|
|
U_BOOT_DRIVER(davinci_emac) = {
|
|
.name = "davinci_emac",
|
|
.id = UCLASS_ETH,
|
|
.of_match = davinci_emac_ids,
|
|
.probe = davinci_emac_probe,
|
|
.ops = &davinci_emac_ops,
|
|
.plat_auto = sizeof(struct eth_pdata),
|
|
};
|