blob: 7dd90374e664008e1b839800dab7331659d1da5a [file] [log] [blame]
/*
* drivers/net/aml_ethernet.c
*
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <linux/types.h>
#include <config.h>
#include <malloc.h>
#include <common.h>
#include <net.h>
#include <asm/u-boot.h>
#include <asm/cache.h>
/**
* Amlogic mason headers
*
* arch/arm/include/asm/arch-m*
*/
#include <asm/arch/io.h>
#include "aml_eth_reg.h"
#include "aml_phy_8700.h"
#include <asm/arch/secure_apb.h>
#define aml_eth_readl(x) (readl((x)&0xffffffffL)&0xffffffffL)
#define aml_eth_writel(val,addr) (writel(((val)&0xffffffffL),((addr)&0xffffffffL)))
#define AML_READ_CBUS_REG(reg)
/**
* arch/arm/cpu/aml_meson
*
*/
#define NET_DMA_BUFFER_SIZE 0x20000
static unsigned char g_bi_enetaddr[6] = {0, 0, 0, 0, 0, 0};
static struct _gStruct* gS = NULL;
static struct _rx_desc* g_rx = NULL;
static struct _tx_desc* g_tx = NULL;
static struct _rx_desc* g_current_rx = NULL;
static struct _tx_desc* g_current_tx = NULL;
static int g_nInitialized = 0 ;
static unsigned int g_phy_Identifier = 0;
static unsigned int g_speed_enforce=0;
static unsigned int g_mdc_clock_range=ETH_MAC_4_GMII_Addr_CR_100_150;
#define PHY_SMSC_8700 0x7c0c4
#define PHY_SMSC_8720 0x7c0f1
#define PHY_ATHEROS_8032 0x004dd023
#define PHY_ATHEROS_8035 0x004dd072
#define PHY_IC_IP101ALF 0x02430c54
#define PHY_MICREL_8091 0x221560
#define PHY_INTERNAL 0x79898963
#define PHY_RTL_8211F 0x001cc916
#define PHY_MICREL_KSZ9031 0x00221620
#define PHY_MICREL_KSZ9031RNX 0x00221622
#define MAC_MODE_RMII_CLK_EXTERNAL 0
#define MAC_MODE_RMII_CLK_INTERNAL 1
#define MAC_MODE_RGMII 2
static int g_mac_mode = MAC_MODE_RGMII;
static int g_debug = 0;
//#define ET_DEBUG
/*
M6TV
23
M6TVlite
24
M8
25
M6TVd
26
M8baby
27
G9TV
28
*/
static unsigned int get_cpuid(void){
return READ_CBUS_REG(0x1f53)&0xff;
}
#ifdef INTERNAL_PHY
#define SMI_ADDR_TSTCNTL 20
#define SMI_ADDR_TSTREAD1 21
#define SMI_ADDR_TSTREAD2 22
#define SMI_ADDR_TSTWRITE 23
#define WR_ADDR_A0CFG 0x11
#define WR_ADDR_A1CFG 0x12
#define WR_ADDR_A2CFG 0x13
#define WR_ADDR_A3CFG 0x14
#define WR_ADDR_A4CFG 0x15
#define WR_ADDR_A5CFG 0x16
#define WR_ADDR_A6CFG 0x17
#define WR_ADDR_A7CFG 0x18
#define WR_ADDR_A8CFG 0x1a
#define WR_ADDR_A9CFG 0x1b
#define WR_ADDR_A10CFG 0x1c
#define WR_ADDR_A11CFG 0x1d
#define RD_ADDR_A3CFG (0x14 << 5)
#define RD_ADDR_A4CFG (0x15 << 5)
#define RD_ADDR_A5CFG (0x16 << 5)
#define RD_ADDR_A6CFG (0x17 << 5)
#define TSTCNTL_RD ((1 << 15) | (1 << 10))
#define TSTCNTL_WR ((1 << 14) | (1 << 10))
#endif
/*
*/
static void hardware_reset(void)
{
/* PHY hardware reset */
/*
if (get_cpuid() == 0x16) {
CLEAR_CBUS_REG_MASK(PREG_PAD_GPIO5_O, 1 << 15);
udelay(500000);
SET_CBUS_REG_MASK(PREG_PAD_GPIO5_O, 1 << 15);
}
if (get_cpuid() == 0x19) {
CLEAR_CBUS_REG_MASK(PREG_PAD_GPIO1_O, 1 << 31);
udelay(500000);
SET_CBUS_REG_MASK(PREG_PAD_GPIO1_O, 1 << 31);
}
if (get_cpuid() == 0x1b) { // m8b It is not known when the chips to change //27
CLEAR_CBUS_REG_MASK(PREG_PAD_GPIO3_O, 1 << 23);
udelay(500000);
SET_CBUS_REG_MASK(PREG_PAD_GPIO3_O, 1 << 23);
}*/
if (get_cpuid() == 0x1f) { // s905
clrbits_le32(PREG_PAD_GPIO3_O, 1 << 14);
udelay(500000);
setbits_le32(PREG_PAD_GPIO3_O, 1 << 14);
}
printf("ETH PHY hardware reset OK\n");
return;
}
static void phy_reg_wr(int phyad, unsigned int reg, unsigned int val)
{
unsigned long busy = 0, tmp = 0;
unsigned int phyaddr;
unsigned int phyreg;
unsigned long reg4;
phyaddr = phyad << ETH_MAC_4_GMII_Addr_PA_P;
phyreg = reg << ETH_MAC_4_GMII_Addr_GR_P;
reg4 = phyaddr | phyreg | g_mdc_clock_range | ETH_MAC_4_GMII_Addr_GW | ETH_MAC_4_GMII_Addr_GB;
aml_eth_writel(val, ETH_MAC_5_GMII_Data);
aml_eth_writel(reg4, ETH_MAC_4_GMII_Addr);
busy = 1;
while (busy) {
tmp = aml_eth_readl(ETH_MAC_4_GMII_Addr);
busy = tmp & 1;
}
}
static unsigned int phy_reg_rd(int phyad, unsigned int reg)
{
unsigned long busy = 0, tmp = 0;
unsigned int phyaddr;
unsigned int phyreg;
unsigned long reg4;
phyaddr = phyad << ETH_MAC_4_GMII_Addr_PA_P;
phyreg = reg << ETH_MAC_4_GMII_Addr_GR_P;
reg4 = phyaddr | phyreg | g_mdc_clock_range | ETH_MAC_4_GMII_Addr_GB;
aml_eth_writel(reg4, ETH_MAC_4_GMII_Addr);
busy = 1;
while (busy) {
tmp = aml_eth_readl(ETH_MAC_4_GMII_Addr);
busy = tmp & 1;
}
tmp = aml_eth_readl(ETH_MAC_5_GMII_Data);
return tmp;
}
#ifdef INTERNAL_PHY
static void initTSTMODE(int phyad)
{
phy_reg_wr(phyad, SMI_ADDR_TSTCNTL, 0x0400);
phy_reg_wr(phyad, SMI_ADDR_TSTCNTL, 0x0000);
phy_reg_wr(phyad, SMI_ADDR_TSTCNTL, 0x0400);
}
static void closeTSTMODE(int phyad)
{
phy_reg_wr(phyad, SMI_ADDR_TSTCNTL, 0x0000);
}
static void init_internal_phy(int phyad)
{
initTSTMODE(phyad);
// write tstcntl addr val
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x1354);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR);//write addr 0
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x38);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A0CFG);//write addr 0x11
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x0c00);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A1CFG);//write addr 0x12
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x3e00);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A2CFG);//write addr 0x13
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0xf902);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A3CFG);//write addr 0x14
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x3412);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A4CFG);//write addr 0x15
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x2636);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A5CFG);//write addr 0x16
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,3);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A7CFG);//write addr 0x18
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x108);
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A9CFG);//write addr 0x1b
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0xda00);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A11CFG);//write addr 0x1d
closeTSTMODE(phyad);
}
void init_internal_phy_10B(int phyad)
{
initTSTMODE(phyad);
// write tstcntl addr val
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x0000);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR);//write addr 0
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x38);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A0CFG);//write addr 0x11
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x0c00);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A1CFG);//write addr 0x12
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x3e00);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A2CFG);//write addr 0x13
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0xf902);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A3CFG);//write addr 0x14
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x3412);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A4CFG);//write addr 0x15
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x2236);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A5CFG);//write addr 0x16
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,3);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A7CFG);//write addr 0x18
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x108);//write val by chandle (2)
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A9CFG);//write addr 0x1b
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0xda00);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A11CFG);//write addr 0x1d
closeTSTMODE(phyad);
}
void init_internal_phy_100B(int phyad)
{
initTSTMODE(phyad);
// write tstcntl addr val
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x9354);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|0x00);//write addr 0x00
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x38);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A0CFG);//write addr 0x11
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x0c00);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A1CFG);//write addr 0x12
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x3e00);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A2CFG);//write addr 0x13
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0xf902);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A3CFG);//write addr 0x14
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x3412);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A4CFG);//write addr 0x15
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0xa406);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A5CFG);//write addr 0x16
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x0003);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A7CFG);//write addr 0x18
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0x00a6);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A9CFG);//write addr 0x1b
phy_reg_wr(phyad,SMI_ADDR_TSTWRITE,0xda00);//write val
phy_reg_wr(phyad,SMI_ADDR_TSTCNTL,TSTCNTL_WR|WR_ADDR_A11CFG);//write addr 0x1d
closeTSTMODE(phyad);
}
#endif
static inline void _dcache_flush_range_for_net(unsigned long startAddr, unsigned long endAddr)
{
flush_dcache_range(startAddr, ROUND(endAddr, ETH_CACHE_LINE_SIZE));
}
static inline void _dcache_inv_range_for_net(unsigned long startAddr, unsigned long endAddr)
{
invalidate_dcache_range(startAddr, ROUND(endAddr, ETH_CACHE_LINE_SIZE));
}
static unsigned int detect_phyad(void)
{
unsigned int testval = 0;
int i;
static int s_phyad = -1;
if (s_phyad != -1) {
return s_phyad;
}
for (i = 0; i < 32; i++) {
testval = phy_reg_rd(i, PHY_SR); //read the SR register..
if (testval != 0x0000 && testval != 0xffff) {
s_phyad = i;
return s_phyad;
}
}
return 0xffff;
}
static void set_mac_mode(void)
{
printf("set_mac_mode(%d)\n", g_mac_mode);
if (g_mac_mode == 2) {
/* RGMII */
aml_eth_writel(aml_eth_readl(ETH_MAC_0_Configuration)|(ETH_MAC_0_Configuration_PS_GMII | ETH_MAC_0_Configuration_TC | ETH_MAC_0_Configuration_DM
| ETH_MAC_0_Configuration_RE | ETH_MAC_0_Configuration_TE), ETH_MAC_0_Configuration);
} else {
/* RMII */
aml_eth_writel(aml_eth_readl(ETH_MAC_0_Configuration)|(ETH_MAC_0_Configuration_PS_MII | ETH_MAC_0_Configuration_FES_100M | ETH_MAC_0_Configuration_DM
| ETH_MAC_0_Configuration_RE | ETH_MAC_0_Configuration_TE), ETH_MAC_0_Configuration);
}
aml_eth_writel((ETH_MAC_1_Frame_Filter_PM | ETH_MAC_1_Frame_Filter_RA), ETH_MAC_1_Frame_Filter);
}
static void set_mac_addrs(void *ptr)
{
unsigned int mac_filter = 0;
unsigned char * p = (unsigned char *)ptr;
mac_filter = (p[5] << 8) | p[4];
aml_eth_writel(mac_filter, ETH_MAC_Addr0_High);
mac_filter = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
aml_eth_writel(mac_filter, ETH_MAC_Addr0_Low);
}
static void netdev_chk(void)
{
unsigned int rint, rint2;
static unsigned int old_rint = -1;
unsigned int id;
int speed, full;
speed = full = 0;
id = detect_phyad();
rint2 = 3000;
if (g_speed_enforce)
{
//printf("use enforce net speed\n");
rint=phy_reg_rd(id,PHY_SR);
}else{
do {
rint = phy_reg_rd(id, PHY_SR);
if ((rint & PHY_SR_ANCOMPLETE)) {
break;
}
udelay(1000);
} while (rint2-- > 0);
if (!(rint & PHY_SR_ANCOMPLETE)) {
printf("phy auto link failed\n");
}
}
if (old_rint != rint) {
if (g_debug > 1)
printf("netdev_chk() g_phy_Identifier: 0x%x\n", g_phy_Identifier);
switch (g_phy_Identifier) {
case PHY_ATHEROS_8032:
rint2 = phy_reg_rd(id, 17);
speed = (rint2 & (1 << 14)) >> 14;
full = ((rint2) & (1 << 13));
gS->linked = rint2 & (1 << 10);
break;
case PHY_ATHEROS_8035:
rint2 = phy_reg_rd(id, 17);
speed = (rint2 & (3 << 14)) >> 14;
full = ((rint2) & (1 << 13));
gS->linked = rint2 & (1 << 10);
break;
case PHY_RTL_8211F:
rint2 = phy_reg_rd(id, 26);
speed = (rint2 & (3 << 4)) >> 4;
full = ((rint2) & (1 << 3));
rint2 = phy_reg_rd(id,1);
gS->linked = rint2&(1<<2);
break;
case PHY_MICREL_KSZ9031:
case PHY_MICREL_KSZ9031RNX:
rint2 = phy_reg_rd(id, 31);
speed = (rint2 & (1 << 4))? 0:((rint2 &(1<<5))? 1:2);
full = ((rint2) & (1 << 3));
rint2 = phy_reg_rd(id,1);
gS->linked = rint2&(1<<2);
break;
case PHY_IC_IP101ALF:
rint2 = phy_reg_rd(id,1);
gS->linked = rint2&(1<<2);
rint2 = phy_reg_rd(id,0);
speed = (rint2 & (0x1<<8))? 1:0;
rint2 = phy_reg_rd(id,5);
full = (rint2 & (0x1<<7))? 1:0;
break;
case PHY_MICREL_8091:
rint2 = phy_reg_rd(id,1);
gS->linked = rint2&(1<<2);
rint2 = phy_reg_rd(id,30);
speed = (rint2 & (0x1<<1))? 1:0;
rint2 = phy_reg_rd(id,5);
full = (rint2 & 0x1)? 1:0;
break;
case PHY_INTERNAL:
rint2 = phy_reg_rd(id, 31);
speed = (rint2 & (1 << 3)) >> 3;
full = ((rint2 >> 4) & 1);
gS->linked = rint2 & (1 << 2);
break;
case PHY_SMSC_8700:
case PHY_SMSC_8720:
default:
rint2 = phy_reg_rd(id, 31);
speed = (rint2 & (1 << 3)) >> 3;
full = ((rint2 >> 4) & 1);
gS->linked = rint2 & (1 << 2);
break;
}
/* phy_auto_negotiation_set */
if (full) {
printf("duplex\n");
aml_eth_writel(aml_eth_readl(ETH_MAC_0_Configuration) | ETH_MAC_0_Configuration_DM, ETH_MAC_0_Configuration);
} else {
printf("half duplex\n");
aml_eth_writel(aml_eth_readl(ETH_MAC_0_Configuration) & ~ ETH_MAC_0_Configuration_DM, ETH_MAC_0_Configuration);
}
if (speed == 0) {
printf("10m\n");
#ifdef INTERNAL_PHY
init_internal_phy_10B(id);
int val =0x4100b040;
aml_eth_writel(val,ETH_PLL_CNTL);
#endif
aml_eth_writel(aml_eth_readl(ETH_MAC_0_Configuration) & ~ ETH_MAC_0_Configuration_FES_100M, ETH_MAC_0_Configuration);
aml_eth_writel(((aml_eth_readl(ETH_MAC_0_Configuration)) | (ETH_MAC_0_Configuration_PS_MII)), ETH_MAC_0_Configuration); // program mac
#ifndef NEW_MAC_LOGIC
if ( get_cpuid() < 0x1B ) {
aml_eth_writel(aml_eth_readl(ETH_PLL_CNTL) & ~ETH_PLL_CNTL_DIVEN, ETH_PLL_CNTL); // Disable the Ethernet clocks
// ---------------------------------------------
// Test 50Mhz Input Divide by 2
// ---------------------------------------------
// Select divide by 20
aml_eth_writel(aml_eth_readl(ETH_PLL_CNTL) & ~ETH_PLL_CNTL_DESEND, ETH_PLL_CNTL); // desc endianess "same order"
aml_eth_writel(aml_eth_readl(ETH_PLL_CNTL) & ~ETH_PLL_CNTL_DATEND, ETH_PLL_CNTL); // data endianess "little"
aml_eth_writel(aml_eth_readl(ETH_PLL_CNTL) & ~ETH_PLL_CNTL_MACSPD, ETH_PLL_CNTL); // divide by 20
aml_eth_writel(aml_eth_readl(ETH_PLL_CNTL) | ETH_PLL_CNTL_DIVEN, ETH_PLL_CNTL); // enable Ethernet clocks
}
#endif
} else if (speed == 1) {
printf("100m\n");
#ifdef INTERNAL_PHY
init_internal_phy_100B(id);
int val = (8<<27)|(7 << 24)|(1<<16)|(1<<15)|(1 << 13)|(1 << 12)|(4 << 4)|(0 << 1);
aml_eth_writel(val,ETH_PLL_CNTL);
#endif
aml_eth_writel(aml_eth_readl(ETH_MAC_0_Configuration) | ETH_MAC_0_Configuration_FES_100M, ETH_MAC_0_Configuration); // program mac
aml_eth_writel(((aml_eth_readl(ETH_MAC_0_Configuration)) | (ETH_MAC_0_Configuration_PS_MII)), ETH_MAC_0_Configuration); // program mac
#ifndef NEW_MAC_LOGIC
if (get_cpuid()< 0x1B) {
aml_eth_writel(aml_eth_readl(ETH_PLL_CNTL) & ~ETH_PLL_CNTL_DIVEN, ETH_PLL_CNTL); // Disable the Ethernet clocks
// ---------------------------------------------
// Test 50Mhz Input Divide by 2
// ---------------------------------------------
// Select divide by 2
aml_eth_writel(aml_eth_readl(ETH_PLL_CNTL) & ~ETH_PLL_CNTL_DESEND, ETH_PLL_CNTL); // desc endianess "same order"
aml_eth_writel(aml_eth_readl(ETH_PLL_CNTL) & ~ETH_PLL_CNTL_DATEND, ETH_PLL_CNTL); // data endianess "little"
aml_eth_writel(aml_eth_readl(ETH_PLL_CNTL) | ETH_PLL_CNTL_MACSPD, ETH_PLL_CNTL); // divide by 2
aml_eth_writel(aml_eth_readl(ETH_PLL_CNTL) | ETH_PLL_CNTL_DIVEN, ETH_PLL_CNTL); // enable Ethernet clocks
}
#endif
} else {
printf("1000m\n");
#if defined(NEW_MAC_LOGIC)
aml_eth_writel(((aml_eth_readl(ETH_MAC_0_Configuration)) & (~ETH_MAC_0_Configuration_PS_MII)) | (1<<13), ETH_MAC_0_Configuration); // program mac
aml_eth_writel(aml_eth_readl(ETH_MAC_0_Configuration) & (~ETH_MAC_0_Configuration_FES_100M), ETH_MAC_0_Configuration); // program mac
#else
if (get_cpuid() >= 0x1B) {
aml_eth_writel(((aml_eth_readl(ETH_MAC_0_Configuration)) & (~ETH_MAC_0_Configuration_PS_MII)) | (1<<13), ETH_MAC_0_Configuration); // program mac
aml_eth_writel(aml_eth_readl(ETH_MAC_0_Configuration) & (~ETH_MAC_0_Configuration_FES_100M), ETH_MAC_0_Configuration); // program mac
}
#endif
}
/* link_changed */
#if defined(ET_DEBUG)
unsigned int regs = 0, val;
for (regs = 0; regs <= 31; regs++) {
val = phy_reg_rd(id, regs);
printf("reg[%d]=%x\n", regs, (unsigned)val);
}
#endif
old_rint = rint;
}
}
static void set_phy_mode(void)
{
unsigned int phyad = -1;
unsigned int val;
phyad = 1;
if (phyad > 32 || phyad < 0) {
return;
}
if (g_debug > 1)
printf("set_phy_mode() g_phy_Identifier: 0x%x\n", g_phy_Identifier);
switch (g_phy_Identifier) {
case PHY_ATHEROS_8032:
case PHY_ATHEROS_8035:
case PHY_RTL_8211F:
break;
case PHY_MICREL_8091:
val = phy_reg_rd(phyad, 0x16);
phy_reg_wr(phyad, 0x16, (val & ~(0x8020)));
break;
case PHY_INTERNAL:
break;
case PHY_SMSC_8700:
case PHY_SMSC_8720:
val = PHY_SPMD_MIIMODE_RMII | (PHY_MODE_BUS_ALL_AE << PHY_SPMD_MODE_P) | (phyad << PHY_SPMD_PHYAD_P);
phy_reg_wr(phyad, PHY_SPMD, val);
break;
default:
break;
}
}
/* Reset and idle the chip, putting all registers into
* a reasonable state */
static int eth_reset(struct _gStruct* emac_config)
{
int i, k, phyad;
unsigned int val,ori_ctl_val=0;
struct _gStruct* m=emac_config;
if (get_cpuid() >= 0x16) {
/* make sure PHY power-on */
set_phy_mode();
}
#define NET_MAX_RESET_TEST 1000
if (g_speed_enforce) ori_ctl_val=phy_reg_rd(1, PHY_CR) ;
for (i = 0; i < NET_MAX_RESET_TEST; i++) {
/* Software Reset MAC */
aml_eth_writel(ETH_DMA_0_Bus_Mode_SWR, ETH_DMA_0_Bus_Mode);
for (k = 0; k < NET_MAX_RESET_TEST; k++) {
udelay(100);
if (!(aml_eth_readl(ETH_DMA_0_Bus_Mode)&ETH_DMA_0_Bus_Mode_SWR)) {
break;
}
}
if (k >= NET_MAX_RESET_TEST) {
printf("Error: Fail to reset mac!(%d)\n", k);
return -1;
} else {
printf("Success: reset mac OK!(%d)\n", k);
}
udelay(100000);
hardware_reset();
udelay(100000);
phyad = detect_phyad();
if (phyad > 32 || phyad < 0) {
continue;
}
/* set phy work mode */
/*
val = PHY_SPMD_MIIMODE_RMII | (PHY_MODE_BUS_ALL_AE << PHY_SPMD_MODE_P) | (phyad << PHY_SPMD_PHYAD_P);
phy_reg_wr(phyad, PHY_SPMD, val);
*/
/* get phy_Identifier */
val = phy_reg_rd(phyad, 2);
g_phy_Identifier = val << 16;
val = phy_reg_rd(phyad, 3);
g_phy_Identifier |= val;
printf("find net phy id=0x%x, phyad=%d\n", (unsigned int)g_phy_Identifier, phyad);
if (g_phy_Identifier == PHY_IC_IP101ALF) {
//if(get_cpuid() == 0x16)
// WRITE_CBUS_REG(HHI_ETH_CLK_CNTL, 0x120); // phy ip101 need clock phase normal
#ifndef NEW_MAC_LOGIC
//if(get_cpuid() == 0x19)
// WRITE_CBUS_REG(HHI_ETH_CLK_CNTL, 0xf00); // phy ip101 need clock phase normal
#endif
}
/* Software Reset PHY */
phy_reg_wr(phyad, PHY_CR, PHY_CR_RST);
for (k = 0; k < NET_MAX_RESET_TEST; k++) {
udelay(1000);
val = phy_reg_rd(phyad, PHY_CR);
if (!(val & PHY_CR_RST)) {
break;
}
}
if (k >= NET_MAX_RESET_TEST) {
continue;
} else {
break;
}
}
if (i >= NET_MAX_RESET_TEST) {
printf("Error to detected phy\n");
return -1;
}
#ifdef INTERNAL_PHY
init_internal_phy(phyad);
#endif
set_phy_mode();
val = PHY_CR_AN | PHY_CR_RSTAN;
phy_reg_wr(phyad, PHY_CR, val);
if (g_speed_enforce == 1)
{
while (!(phy_reg_rd(1,1)&(1<<2))) {}; //wait line status stable
phy_reg_wr(phyad, PHY_CR, ori_ctl_val);
while (!(phy_reg_rd(1,1)&(1<<2))) {}; //wait new state stable
}
udelay(10);
set_mac_mode();
aml_eth_writel((~0), ETH_DMA_5_Status); /* clear all status flag */
aml_eth_writel(0, ETH_DMA_5_Status);
aml_eth_writel(0, ETH_DMA_6_Operation_Mode); /* stop RX and TX */
val = aml_eth_readl(ETH_DMA_8_Missed_Frame_and_Overflow); /* read to clean */
aml_eth_writel(0, ETH_DMA_7_Interrupt_Enable); /* disable all interrupt */
aml_eth_writel((8 << ETH_DMA_0_Bus_Mode_PBL_P) | ETH_DMA_0_Bus_Mode_FB, ETH_DMA_0_Bus_Mode);
printf("final_addr[rx-tx]: %p-%p\n", m->rx, m->tx);
aml_eth_writel((long)m->rx, ETH_DMA_3_Re_Descriptor_List_Addr);
aml_eth_writel((long)m->tx, ETH_DMA_4_Tr_Descriptor_List_Addr);
/* config the interrupt */
aml_eth_writel(ETH_DMA_7_Interrupt_Enable_TUE | ETH_DMA_7_Interrupt_Enable_TJE
| ETH_DMA_7_Interrupt_Enable_OVE | ETH_DMA_7_Interrupt_Enable_UNE | ETH_DMA_7_Interrupt_Enable_RIE
| ETH_DMA_7_Interrupt_Enable_RUE | ETH_DMA_7_Interrupt_Enable_RSE | ETH_DMA_7_Interrupt_Enable_FBE
| ETH_DMA_7_Interrupt_Enable_AIE | ETH_DMA_7_Interrupt_Enable_NIE, ETH_DMA_7_Interrupt_Enable);
aml_eth_writel(0, ETH_MAC_Interrupt_Mask);
printf("Ethernet reset OK\n");
return 0;
}
static void DMARXStart(void)
{
aml_eth_writel(aml_eth_readl(ETH_DMA_6_Operation_Mode) | ETH_DMA_6_Operation_Mode_SR, ETH_DMA_6_Operation_Mode);
}
static void DMATXStart(void)
{
aml_eth_writel(aml_eth_readl(ETH_DMA_6_Operation_Mode) | ETH_DMA_6_Operation_Mode_ST, ETH_DMA_6_Operation_Mode);
}
static void GetDMAStatus(unsigned int* mask, unsigned int* status)
{
*mask = aml_eth_readl(ETH_DMA_7_Interrupt_Enable);
*status = aml_eth_readl(ETH_DMA_5_Status);
}
static void eth_data_dump(unsigned char *p, int len)
{
int i, j;
char s[20];
for (i = 0; i < len; i += 16) {
printf("%p:", p);
for (j = 0; j < 16 && j < (len - i); j++) {
s[j] = (p[j] > 15 && p[j] < 128) ? p[j] : '.';
printf(" %02x", p[j]);
}
s[j] = 0;
printf(" |%s|\n", s);
p = p + 16;
}
}
static void eth_tx_dump(unsigned char *p, int len)
{
if ((g_debug == 2) || (g_debug == 4)) {
printf("=====>\n");
eth_data_dump(p, len);
}
}
static void eth_rx_dump(unsigned char *p, int len)
{
if ((g_debug == 3) || (g_debug == 4)) {
printf("<=====\n");
eth_data_dump(p, len);
}
}
static void aml_eth_halt(struct eth_device * net_current)
{
return;
}
static int aml_eth_send(struct eth_device *net_current, void *packet, int length)
{
unsigned int mask;
unsigned int status;
if (!g_nInitialized) {
return -1;
}
eth_tx_dump((unsigned char *)packet, length);
netdev_chk();
struct _tx_desc* pTx = g_current_tx;
struct _tx_desc* pDma = (struct _tx_desc*)aml_eth_readl(ETH_DMA_18_Curr_Host_Tr_Descriptor);
if (pDma != NULL) {
_dcache_inv_range_for_net((unsigned long)pDma, (unsigned long)(pDma) + sizeof(struct _tx_desc) - 1);
}
if (pDma != NULL && !(pDma->tdes0 & TDES0_OWN) && pTx != pDma) {
//this may not happend,if all the hardware work well...
//to fixed a bug of the dma maybe lost setting some tx buf to own by host,..;
//start the current_tx at pDMA
pTx = pDma;
}
if (pDma != pTx) {
_dcache_inv_range_for_net((unsigned long)pTx, (unsigned long)(pTx + 1) - 1);
}
if (length > ETH_MTU) {
goto err;
}
if (length < 14) {
printf("pbuf len error, len=%d\n", length);
goto err;
}
if (pTx->tdes0 & TDES0_OWN) {
#if 1
volatile unsigned long Cdma, Dstatus, status;
Cdma = aml_eth_readl(ETH_DMA_18_Curr_Host_Tr_Descriptor);
Dstatus = aml_eth_readl(Cdma);
status = aml_eth_readl(ETH_DMA_5_Status);
printf("Current DMA=0x%x, Dstatus=0x%x\n", (unsigned int)Cdma, (unsigned int)Dstatus);
printf("Current status=0x%x\n", (unsigned int)status);
printf("no buffer to send\n");
#endif
goto err;
}
if (!(unsigned char*)(unsigned long)pTx->tdes2) {
goto err;
}
g_current_tx = (struct _tx_desc*)(unsigned long)pTx->tdes3;
memcpy((unsigned char*)(unsigned long)pTx->tdes2, (unsigned char*)packet, length);
//pTx->tdes1 &= DescEndOfRing;
_dcache_flush_range_for_net((unsigned long)pTx->tdes2, (unsigned long)pTx->tdes2 + length - 1);
pTx->tdes1 = ((length << TDES1_TBS1_P) & TDES1_TBS1_MASK) | TDES1_FS | TDES1_LS | TDES1_TCH | TDES1_IC;
pTx->tdes0 = TDES0_OWN;
_dcache_flush_range_for_net((unsigned long)pTx, (unsigned long)(pTx + 1) - 1);
GetDMAStatus(&mask, &status);
if (status & ETH_DMA_5_Status_TS_SUSP) {
aml_eth_writel(1, ETH_DMA_1_Tr_Poll_Demand);
} else {
DMATXStart();
}
#ifdef ET_DEBUG
printf("Transfer starting...\n");
GetDMAStatus(&mask, &status);
printf("Current status=%x\n", status);
#endif
/* wait for transfer to succeed */
//unsigned tmo = get_timer (0) + 5 * CONFIG_SYS_HZ; //5S time out
unsigned tmo = 0;
do {
udelay(100);
GetDMAStatus(&mask, &status);
if (tmo++ >= 50000) {
printf("\ntransmission error %#x\n", status);
break;
}
} while (!((status & ETH_DMA_5_Status_NIS) && (status & ETH_DMA_5_Status_TI)));
if (status & ETH_DMA_5_Status_NIS) {
if (status & ETH_DMA_5_Status_TI) {
aml_eth_writel(ETH_DMA_5_Status_NIS | ETH_DMA_5_Status_TI, ETH_DMA_5_Status);
}
if (status & ETH_DMA_5_Status_TU) {
aml_eth_writel(ETH_DMA_5_Status_NIS | ETH_DMA_5_Status_TU, ETH_DMA_5_Status);
}
}
#ifdef ET_DEBUG
//test
GetDMAStatus(&mask, &status);
printf("Current status=%x\n", status);
#endif
return 0;
err:
return -1;
}
/*
* each time receive a whole packet
*/
static int aml_eth_rx(struct eth_device * net_current)
{
unsigned int mask;
unsigned int status;
int rxnum = 0;
int len = 0;
struct _rx_desc* pRx;
if (!g_nInitialized) {
return -1;
}
netdev_chk();
/* Check packet ready or not */
GetDMAStatus(&mask, &status);
if (!((status & ETH_DMA_5_Status_NIS) && (status & ETH_DMA_5_Status_RI))) {
return 0;
}
aml_eth_writel(ETH_DMA_5_Status_NIS | ETH_DMA_5_Status_RI, ETH_DMA_5_Status); //clear the int flag
if (!g_current_rx) {
g_current_rx = gS->rx;
}
pRx = g_current_rx;
_dcache_inv_range_for_net((unsigned long)pRx, (unsigned long)(pRx + 1) - 1);
while (!(pRx->rdes0 & RDES0_OWN)) {
len = (pRx->rdes0 & RDES0_FL_MASK) >> RDES0_FL_P;
if (14 >= len) {
printf("err len=%d\n", len);
goto NEXT_BUF;
}
// pbuf_header(pb, -sizeof(short));
_dcache_inv_range_for_net((unsigned long)pRx->rdes2, (unsigned long)pRx->rdes2 + len - 1);
if (!memcpy((unsigned char*)NetRxPackets[0], (unsigned char*)(unsigned long)pRx->rdes2, len)) {
printf("memcp error\n");
goto NEXT_BUF;
}
NEXT_BUF:
pRx->rdes0 = RDES0_OWN;
_dcache_flush_range_for_net((unsigned long)pRx, (unsigned long)(pRx + 1) - 1);
pRx = (struct _rx_desc*)(unsigned long)g_current_rx->rdes3;
_dcache_inv_range_for_net((unsigned long)pRx, (unsigned long)(pRx + 1) - 1);
g_current_rx = pRx;
rxnum++;
NetReceive(NetRxPackets[0], len);
eth_rx_dump((unsigned char *)NetRxPackets[0], len);
}
return len;
}
static int aml_ethernet_init(struct eth_device * net_current, bd_t *bd)
{
unsigned long net_dma_start_addr;
unsigned long net_dma_end_addr;
unsigned char *net_dma_buffer = NULL;
unsigned long tx_start, rx_start;
struct _rx_desc * pRDesc;
struct _tx_desc * pTDesc;
unsigned char * bufptr;
int i;
if (g_nInitialized) {
return 0;
}
printf("Amlogic Ethernet Init\n");
/* alloc the dma buffer */
net_dma_buffer = memalign(ETH_CACHE_LINE_SIZE, NET_DMA_BUFFER_SIZE);
net_dma_start_addr = (unsigned long)net_dma_buffer;
net_dma_end_addr = (unsigned long)(net_dma_buffer + NET_DMA_BUFFER_SIZE);
/* init the dma descriptor 128k */
gS = (struct _gStruct*)malloc(sizeof(struct _gStruct));
if (net_dma_start_addr != 0 &&
(net_dma_end_addr - net_dma_start_addr) > (CTX_BUFFER_NUM + CRX_BUFFER_NUM)*CBUFFER_SIZE +
CRX_BUFFER_NUM * sizeof(struct _rx_desc) + CTX_BUFFER_NUM * sizeof(struct _tx_desc)) {
g_rx = (struct _rx_desc*)((unsigned long)net_dma_start_addr + (CTX_BUFFER_NUM + CRX_BUFFER_NUM) * CBUFFER_SIZE);
g_tx = (struct _tx_desc*)((char *)g_rx + CRX_BUFFER_NUM * sizeof(struct _rx_desc));
} else {
printf("Error!! Ethernet DMA size is smaller");
goto error_out;
}
gS->rx = g_rx;
gS->tx = g_tx;
tx_start = net_dma_start_addr;
rx_start = net_dma_start_addr + CTX_BUFFER_NUM * CBUFFER_SIZE;
gS->rx_buf_addr = rx_start;
gS->tx_buf_addr = tx_start;
/* init mStruct */
gS->current_rx_des = 0;
gS->current_tx_des = 0;
gS->rx_len = CRX_BUFFER_NUM;
gS->tx_len = CTX_BUFFER_NUM;
gS->buffer_len = CBUFFER_SIZE;
gS->rx_frame_num = 0;
gS->current_tx_ready = 0;
gS->last_tx_sent = 0;
gS->last_tx_desc_num = 0;
gS->irq_handle = -1;
gS->linked = 0;
if (g_debug > 1) {
printf("netdma:[0x%lx-0x%lx]\n", net_dma_start_addr, net_dma_end_addr);
printf("tx_buf_num:%d \t rx_buf_num:%d buf_size:%d\n", CTX_BUFFER_NUM, CRX_BUFFER_NUM, CBUFFER_SIZE);
printf("[===dma_tx] 0x%lx-0x%lx\n", tx_start, rx_start);
printf("[===dma_rx] 0x%lx-0x%lx\n", rx_start,(unsigned long) g_rx);
}
/* init RX desc */
pRDesc = gS->rx;
bufptr = (unsigned char *) gS->rx_buf_addr;
for (i = 0; i < gS->rx_len - 1; i++) {
if (g_debug > 1) {
printf("[rx-descriptor%d] %p\n", i, bufptr);
}
pRDesc->rdes0 = RDES0_OWN;
pRDesc->rdes1 = RDES1_RCH | (gS->buffer_len & RDES1_RBS1_MASK);
pRDesc->rdes2 = (unsigned long)bufptr;
pRDesc->rdes3 = (unsigned long)pRDesc + sizeof(struct _rx_desc);
pRDesc->reverse[0] = 0;
pRDesc->reverse[1] = 0;
pRDesc->reverse[2] = 0;
pRDesc->reverse[3] = 0;
bufptr += gS->buffer_len;
pRDesc = pRDesc + 1;
}
pRDesc->rdes0 = RDES0_OWN;
pRDesc->rdes1 = RDES1_RCH | RDES1_RER | (gS->buffer_len & RDES1_RBS1_MASK); //chain buf
pRDesc->rdes2 = (unsigned long)bufptr;
pRDesc->rdes3 = (unsigned long)gS->rx; //circle
_dcache_flush_range_for_net(( unsigned long )gS->rx, (unsigned long)gS->rx + sizeof(struct _rx_desc)*gS->rx_len);
_dcache_flush_range_for_net(( unsigned long )gS->rx_buf_addr, (unsigned long)gS->rx_buf_addr + CRX_BUFFER_NUM * CBUFFER_SIZE);
/* init TX desc */
pTDesc = (struct _tx_desc *)gS->tx;
bufptr = (unsigned char *)gS->tx_buf_addr;
for (i = 0; i < gS->tx_len - 1; i++) {
if (g_debug > 1) {
printf("[tx-descriptor%d] %p\n", i, bufptr);
}
pTDesc->tdes0 = 0;
pTDesc->tdes1 = TDES1_TCH | TDES1_IC;
pTDesc->tdes2 = (unsigned long)bufptr;
pTDesc->tdes3 = (unsigned long)pTDesc + sizeof(struct _tx_desc);
pTDesc->reverse[0] = 0;
pTDesc->reverse[1] = 0;
pTDesc->reverse[2] = 0;
pTDesc->reverse[3] = 0;
bufptr += gS->buffer_len;
pTDesc = pTDesc + 1;
}
pTDesc->tdes0 = 0;
pTDesc->tdes1 = TDES1_TCH | TDES1_TER | TDES1_IC; //chain buf, enable complete interrupt
pTDesc->tdes2 = (unsigned long)bufptr;
pTDesc->tdes3 = (unsigned long)gS->tx; //circle
g_current_tx = gS->tx;
_dcache_flush_range_for_net((unsigned long)gS->tx, (unsigned long)gS->tx + sizeof(struct _tx_desc)*gS->tx_len);
_dcache_flush_range_for_net(( unsigned long )gS->tx_buf_addr, (unsigned long)gS->tx_buf_addr + CTX_BUFFER_NUM * CBUFFER_SIZE);
/* get mii interface */
g_mac_mode = (aml_eth_readl(PREG_ETH_REG0) & 0x1) ? MAC_MODE_RGMII:MAC_MODE_RMII_CLK_EXTERNAL;
/* mac and phy reset */
eth_reset(gS);
/* set mac addr */
eth_getenv_enetaddr("ethaddr", g_bi_enetaddr);
set_mac_addrs(g_bi_enetaddr);
/* get the mac and ip */
printf("MAC address is %02x:%02x:%02x:%02x:%02x:%02x\n", g_bi_enetaddr[0], g_bi_enetaddr[1],
g_bi_enetaddr[2], g_bi_enetaddr[3], g_bi_enetaddr[4], g_bi_enetaddr[5]);
/* start the dma para, but don't start the receive dma */
aml_eth_writel(ETH_DMA_6_Operation_Mode_EFC | ETH_DMA_6_Operation_Mode_TTC_16 | ETH_DMA_6_Operation_Mode_RSF | ETH_DMA_6_Operation_Mode_TSF | ETH_DMA_6_Operation_Mode_DT, ETH_DMA_6_Operation_Mode);
// | ETH_DMA_6_Operation_Mode_RTC_32 | ETH_DMA_6_Operation_Mode_FUF
netdev_chk();
DMARXStart();
g_nInitialized = 1;
return 0;
error_out:
return -1;
}
int aml_eth_init(bd_t *bis)
{
struct eth_device *dev;
dev = (struct eth_device *)malloc(sizeof(*dev));
memset(dev, 0, sizeof(*dev));
sprintf(dev->name, "Meson_Ethernet");
dev->init = aml_ethernet_init;
dev->halt = aml_eth_halt;
dev->send = aml_eth_send;
dev->recv = aml_eth_rx;
return eth_register(dev);
}
static int do_phyreg(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
unsigned int phyad, reg, value;
unsigned char *cmd = NULL;
unsigned int i;
if (argc < 2) {
return cmd_usage(cmdtp);
}
phyad = detect_phyad();
if (phyad > 32 || phyad < 0) {
return -1;
}
cmd = (unsigned char *)argv[1];
switch (*cmd) {
case 'd':
printf("=== ethernet phy register dump:\n");
for (i = 0; i < 32; i++)
printf("[reg_%d] 0x%x\n", i, phy_reg_rd(phyad, i));
break;
case 'r':
if (argc != 3) {
return cmd_usage(cmdtp);
}
printf("=== ethernet phy register read:\n");
reg = simple_strtoul(argv[2], NULL, 10);
printf("[reg_%d] 0x%x\n", reg, phy_reg_rd(phyad, reg));
break;
case 'w':
if (argc != 4) {
return cmd_usage(cmdtp);
}
printf("=== ethernet phy register write:\n");
reg = simple_strtoul(argv[2], NULL, 10);
value = simple_strtoul(argv[3], NULL, 16);
phy_reg_wr(phyad, reg, value);
printf("[reg_%d] 0x%x\n", reg, phy_reg_rd(phyad, reg));
break;
default:
return cmd_usage(cmdtp);
}
return 0;
}
static int do_macreg(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
unsigned int reg, value;
unsigned char *cmd = NULL;
unsigned int i = 0;
if (argc < 2) {
return cmd_usage(cmdtp);
}
cmd = (unsigned char *)argv[1];
switch (*cmd) {
case 'd':
printf("=== ETH_MAC register dump:\n");
//for (i = 0x0000; i <= 0x00FC; i += 0x4)
for (i = 0x0000; i <= 0x004C; i += 0x4)
printf("[0x%04x] 0x%lx\n", i, (unsigned long)aml_eth_readl(ETH_BASE + i));
#if 0
printf("=== ETH_MMC register dump:\n");
for (i = 0x0100; i <= 0x0284; i += 0x4)
printf("[0x%04x] 0x%x\n", i, aml_eth_readl(ETH_BASE + i));
#endif
printf("=== ETH_DMA register dump:\n");
for (i = 0x1000; i <= 0x1054; i += 0x4)
printf("[0x%04x] 0x%x\n", i, (unsigned int)aml_eth_readl(ETH_BASE + i));
printf("=== ethernet board config register dump:\n");
printf("[0x1076] 0x%x\n", READ_CBUS_REG(0x1076));
printf("[0x2032] 0x%x\n", READ_CBUS_REG(0x2032));
printf("[0x2042] 0x%x\n", READ_CBUS_REG(0x2042));
break;
case 'r':
if (argc != 3) {
return cmd_usage(cmdtp);
}
printf("=== ethernet mac register read:\n");
reg = simple_strtoul(argv[2], NULL, 10);
printf("[0x%04x] 0x%x\n", i, (unsigned int)aml_eth_readl(ETH_BASE + reg));
break;
case 'w':
if (argc != 4) {
return cmd_usage(cmdtp);
}
printf("=== ethernet mac register write:\n");
reg = simple_strtoul(argv[2], NULL, 10);
value = simple_strtoul(argv[3], NULL, 16);
aml_eth_writel(value, ETH_BASE + reg);
printf("[0x%04x] 0x%x\n", reg, value);
break;
default:
return cmd_usage(cmdtp);
}
return 0;
}
static int do_cbusreg(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
unsigned int reg, value;
char *cmd = NULL;
if (argc < 3) {
return cmd_usage(cmdtp);
}
cmd = argv[1];
switch (*cmd) {
case 'r':
if (argc != 3) {
return cmd_usage(cmdtp);
}
printf("=== cbus register read:\n");
reg = simple_strtoul(argv[2], NULL, 16);
printf("[0x%04x] 0x%x\n", reg, READ_CBUS_REG(reg));
break;
case 'w':
if (argc != 4) {
return cmd_usage(cmdtp);
}
printf("=== cbus register write:\n");
reg = simple_strtoul(argv[2], NULL, 16);
value = simple_strtoul(argv[3], NULL, 16);
WRITE_CBUS_REG(reg, value);
printf("[0x%04x] 0x%x\n", reg, READ_CBUS_REG(reg));
break;
default:
return cmd_usage(cmdtp);
}
return 0;
}
//loopback test.
static int do_autoping(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
unsigned int phyad = -1;
unsigned int value;
char buffer[40];
if (argc < 2) {
return cmd_usage(cmdtp);
}
phyad = detect_phyad();
if (phyad > 32 || phyad < 0) {
return -1;
}
value = phy_reg_rd(phyad, PHY_CR);
phy_reg_wr(phyad, PHY_CR, value | (1 << 14)); //phy loopback
while (1) {
if (had_ctrlc()) {
printf("Quit autoping...\n");
return 0;
}
sprintf(buffer, "ping %s ", argv[1]);
run_command(buffer, 0);
}
return 0;
}
static int do_mdc_clk(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
if (argc < 2) {
return cmd_usage(cmdtp);
}
g_mdc_clock_range=((simple_strtoul(argv[1], NULL, 16)&0xf)<<2);
printf("set value:0x%x\n",g_mdc_clock_range);
return 0;
}
int do_ethchk(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
netdev_chk();
return 0;
}
static int do_ethrst(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
eth_reset(gS);
return 0;
}
static int do_ethmode(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
unsigned char *cmd = NULL;
if (argc < 2) {
return cmd_usage(cmdtp);
}
cmd = (unsigned char *)argv[1];
switch (*cmd) {
case '0':
g_mac_mode = 0;
printf("set MAC mode: RMII 100/10 Mbps (external clk).\n");
break;
case '1':
g_mac_mode = 1;
printf("set MAC mode: RMII 100/10 Mbps (internal clk).\n");
break;
case '2':
g_mac_mode = 2;
printf("set MAC mode: RGMII 1000/100/10 Mbps.\n");
break;
default:
return cmd_usage(cmdtp);
}
if (get_cpuid() >= 0x16) {
/* config ethernet mode */
switch (g_mac_mode) {
case MAC_MODE_RMII_CLK_EXTERNAL:
if (get_cpuid() == 0x19) {
//WRITE_CBUS_REG(HHI_ETH_CLK_CNTL, 0xf00);
//WRITE_CBUS_REG(PERIPHS_PIN_MUX_6, 0x0000fde0);
//WRITE_CBUS_REG(PREG_ETHERNET_ADDR0, 0x241);
}
else{
//WRITE_CBUS_REG(HHI_ETH_CLK_CNTL, 0x130); //clock phase invert
//WRITE_CBUS_REG(PERIPHS_PIN_MUX_6, 0x8007ffe0);
//WRITE_CBUS_REG(PREG_ETHERNET_ADDR0, 0x241);
}
break;
case MAC_MODE_RMII_CLK_INTERNAL:
//WRITE_CBUS_REG(HHI_ETH_CLK_CNTL, 0x702);
//WRITE_CBUS_REG(PERIPHS_PIN_MUX_6, 0x4007ffe0);
//WRITE_CBUS_REG(PREG_ETHERNET_ADDR0, 0x241);
break;
case MAC_MODE_RGMII:
//WRITE_CBUS_REG(HHI_ETH_CLK_CNTL, 0x309);
//WRITE_CBUS_REG(PERIPHS_PIN_MUX_6, 0x4007ffe0);
//WRITE_CBUS_REG(PREG_ETHERNET_ADDR0, 0x211);
break;
default:
break;
}
udelay(1000);
//hardware_reset();
eth_reset(gS);
}
return 0;
}
static int do_ethdbg(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
unsigned char *cmd = NULL;
if (argc < 2) {
return cmd_usage(cmdtp);
}
cmd = (unsigned char *)argv[1];
switch (*cmd) {
case '0':
g_debug = 0;
break;
case '1':
g_debug = 1;
break;
case '2':
g_debug = 2;
break;
case '3':
g_debug = 3;
break;
case '4':
g_debug = 4;
break;
default:
return cmd_usage(cmdtp);
}
printf("set ethernet debug: %d\n", g_debug);
return 0;
}
static int do_netspeed(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int speed=100;
int full=0;
int data=0;
if (argc < 2) {
switch (g_phy_Identifier)
{
case PHY_ATHEROS_8032:
break;
case PHY_IC_IP101ALF:
break;
case PHY_SMSC_8700:
default:
data = phy_reg_rd(1,31);
speed = data & (1<<3);
full = ((data >>4) & 1);
printf("status: %sM %s-duplex\n",(speed?"100":"10"),(full?"full":"half"));
break;
}
return cmd_usage(cmdtp);
}
speed = simple_strtoul(argv[1], NULL, 10);
if ((speed != 0) && (speed != 100) && (speed != 10)) return -1;
data= phy_reg_rd(1,0);
if (speed == 0) //disable net speed enforce mode,ie. we enable A/N
{
g_speed_enforce=0;
data |=(1<<12)|(1<<9);
}else{
g_speed_enforce=1;
data &=~(1<<12);
if (speed == 100) //100M
{
data |= (1<<13) ;
}else if(speed == 10 ){ //10M
data &=~(1<<13);
}
if (strncmp(argv[2],"fu",2) == 0)
{
data |= (1<<8);
}else{
data &=~(1<<8);
}
}
phy_reg_wr(1, 0, data);
return 0;
}
#ifdef CONFIG_M8B
static int do_eth_cali(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
unsigned int value;
int rise=0;
int sel=0;
int i;
//char *cmd = NULL;
if (argc < 2) {
return cmd_usage(cmdtp);
}
rise = simple_strtoul(argv[1], NULL, 10);
sel = simple_strtoul(argv[2], NULL, 10);
eth_aml_reg0_t eth_reg0;
eth_reg0.d32 = READ_CBUS_REG(0x2050);
eth_reg0.b.cali_start = 1;
eth_reg0.b.cali_rise = rise;
eth_reg0.b.cali_sel = sel;
WRITE_CBUS_REG(0x2050, eth_reg0.d32);
printf("0x%x\n", READ_CBUS_REG(0x2050));
for (i=0;i<10000;i++) {
value = READ_CBUS_REG(0x2051);
if ((value>>15)&0x1)
printf("value == %x, cali_len == %d, cali_idx == %d, cali_sel =%d, cali_rise = %d\n",value,(value>>5)&0x1f,(value&0x1f),(value>>11)&0x7,(value>>14)&0x1);
}
return 0;
}
U_BOOT_CMD(
cali, 3, 1, do_eth_cali,
"configure clock phare",
" - phare mac clock.\n"
);
#endif
U_BOOT_CMD(
netspd_f, 3, 1, do_netspeed,
"enforce eth speed",
"0 - disable enforce mode,enable A/N\n"
"10 full - enforce 10M full duplex mode\n"
"10 half - enforce 10M half duplex mode\n"
"100 full - enforce 100M full duplexmode\n"
"100 half - enforce 100M half duplexmode\n"
);
U_BOOT_CMD(
phyreg, 4, 1, do_phyreg,
"ethernet phy register read/write/dump",
"d - dump phy registers\n"
" r reg - read phy register\n"
" w reg val - write phy register"
);
U_BOOT_CMD(
macreg, 4, 1, do_macreg,
"ethernet mac register read/write/dump",
"d - dump mac registers\n"
" r reg - read mac register\n"
" w reg val - write mac register"
);
U_BOOT_CMD(
cbusreg, 4, 1, do_cbusreg,
"cbus register read/write",
"r reg - read cbus register\n"
" w reg val - write cbus register"
);
U_BOOT_CMD(
mdc_clk, 2, 1, do_mdc_clk,
"do mdc clock",
"mdc data-- data is ETH_MAC_4_GMII_Addr[2..5] "
);
U_BOOT_CMD(
autoping, 2, 1, do_autoping,
"do auto ping test",
"ip"
);
U_BOOT_CMD(
ethchk, 1, 1, do_ethchk,
"check ethernet status",
""
);
U_BOOT_CMD(
ethrst, 1, 1, do_ethrst,
"reset ethernet phy",
" - reset etherent phy\n"
);
U_BOOT_CMD(
ethmode, 2, 1, do_ethmode,
"set ethernet mac mode",
"0 - set mac mode RMII (external clk).\n"
" 1 - set mac mode RMII (internal clk).\n"
" 2 - set mac mode RGMII.\n"
);
U_BOOT_CMD(
ethdbg, 2, 1, do_ethdbg,
"set ethernet debug level",
"0 - disable ethernet debug\n"
" 1 - ethernet basic info\n"
" 2 - ethernet TX debug\n"
" 3 - ethernet RX debug\n"
" 4 - ethernet TX/RX debug\n"
);