在zynq开发板zc706上,网络通路由下面三个设备组成:
其中zynq负责对phy进行配置,当zynq上的网络控制器以及phy完成正确配置时,能够看到RJ45上面的黄灯亮,此时表明链路已经通了。如果u-boot中已经设置了IP地址,通过网线就可以ping通电脑,此时会打印host alive这句话。
但是如果板子不是这样做的,比如是下面这种方式:
这里用到了BCM5396网络交换芯片,此时要保证网络链路通就需要对5396和两个PHY进行配置。幸运地是,PHY0由5396进行配置,当我们对5396进行正确配置后,PHY0也就配置完成了,PHY1的配置还是老样子。整个配置可以分为两步走,首先通过SPI总线配置5396,再配置PHY1,完成全部配置后,就能看到RJ45上面的黄灯亮起。
u-boot中需要修改的代码主要涉及三个地方
1、 arch/arm/lib/Board.c
其中board_init_r函数中需要在网络驱动配置前加入配置bcm5396的函数调用,这里我放在了串口驱动配置之后运行。
void board_init_r(gd_t *id, ulong dest_addr)
{
ulong malloc_start;
#if !defined(CONFIG_SYS_NO_FLASH)
ulong flash_size;
#endif
gd->flags |= GD_FLG_RELOC; /* tell others: relocation done */
bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_R, "board_init_r");
monitor_flash_len = _end_ofs;
/* Enable caches */
enable_caches();
debug("monitor flash len: %08lX\n", monitor_flash_len);
board_init(); /* Setup chipselects */
/*
* TODO: printing of the clock inforamtion of the board is now
* implemented as part of bdinfo command. Currently only support for
* davinci SOC's is added. Remove this check once all the board
* implement this.
*/
#ifdef CONFIG_CLOCKS
set_cpu_clk_info(); /* Setup clock information */
#endif
serial_initialize();
myspi_init(); /*init bcm5396*/
printf("Now running in RAM - U-Boot at: %08lx\n", dest_addr);
#ifdef CONFIG_LOGBUFFER
2、 net/Eth.c
在其中加入配置bcm5396的代码
void myspi_init()
{
XSpiPs Spi;
int i32Option,status;
int i;
unsigned char workBuf[10];
workBuf[0]=0xf0;
workBuf[1]=0x1;
g_SpiConfig = &XSpiPs_ConfigTable[0];
//Initialize the SPI device.
Spi.IsBusy = 0;
Spi.Config.BaseAddress = g_SpiConfig->BaseAddress;
//Spi.StatusHandler = StubStatusHandler;
Spi.SendBufferPtr = NULL;
Spi.RecvBufferPtr = NULL;
Spi.RequestedBytes = 0;
Spi.RemainingBytes = 0;
Spi.IsReady = 0x11111111;
XSpiPs_WriteReg(Spi.Config.BaseAddress,0,0x00020000);
//Initialize the SPI device. end
i32Option = 0x1 | 0x2 | 0x4 | 0x10;
Spi_SetOptions(&Spi, i32Option);
XSpiPs_SetSlaveSelect(&Spi,1 );
XSpiPs_SetClkPrescaler(&Spi, 6 );
workBuf[0]=0xf0;
workBuf[1]=0x1;
workBuf[2]=0;
for(i=0x10;i<=0x1f;i++)
{
status = writeBCM5396(&Spi, i, 0x20, workBuf );
}
printf("*****************Read SPI Reg of 5396******************\r\n");
printf("---BMC Status Registers(PAGE 0x10-0x1F)---\n");
for(i=0x10;i<=0x1f;i++)
{
status = readBCM5396(&Spi, i, 0x28, workBuf );
printf("port=%d,offset=0x28,data=0x%x\n",(i-0x10),workBuf[2]);
printf("port=%d,offset=0x29,data=0x%x\n",(i-0x10),workBuf[3]);
}
printf("---BMC Status Registers(PAGE 0x10-0x1F)---\n\r");
for(i=0x10;i<=0x1f;i++)
{
status = readBCM5396(&Spi, i, 0x20, workBuf );
printf("port=%d,offset=0x20,data=0x%x\n\r",(i-0x10),workBuf[2]);
printf("port=%d,offset=0x21,data=0x%x\n\r",(i-0x10),workBuf[3]);
}
}
3、 drivers/net/Zynq_gem.c
这里的修改是可选项,主要是配置phy,我这里使用的是marvell的88E1111,u-boot中提供的配置方法不起效,于是进行了手动配置。加了phy_detect和negotiat函数
static void phy_negotiat(struct eth_device *dev)
{
struct zynq_gem_priv *priv = dev->priv;
u16 control;
u16 status;
u16 temp;
u16 timeout_counter=0;
printf("Start PHY autonegotiation.\n");
phywrite(dev,priv->phyaddr, 22, 2);
phyread(dev, priv->phyaddr, 21, &control);
control |= 0x0030;
phywrite(dev, priv->phyaddr, 21, control);
phywrite(dev, priv->phyaddr, 22, 0);
phyread(dev, priv->phyaddr, 4, &control);
control |= 0x0800;
control |= 0x0400;
control |= (0x0100 | 0x0080);
control |= (0x0040 | 0x0020);
phywrite(dev, priv->phyaddr, 4, control);
phyread(dev, priv->phyaddr, 9,&control);
control |= 0x0300;
phywrite(dev, priv->phyaddr, 9,control);
phywrite(dev, priv->phyaddr, 22, 0);
phyread(dev, priv->phyaddr, 16,&control);
control |= (7 << 12); /* max number of gigabit attempts */
control |= (1 << 11); /* enable downshift */
phywrite(dev, priv->phyaddr, 16,control);
phyread(dev, priv->phyaddr, 0, &control);
control |= 0x1000;
control |= 0x0200;
phywrite(dev, priv->phyaddr, 0, control);
phyread(dev, priv->phyaddr, 0, &control);
control |= 0x8000;
phywrite(dev, priv->phyaddr, 0, control);
while (1)
{
phyread(dev, priv->phyaddr, 0, &control);
if (control & 0x8000)
{
continue;
}
else
{
break;
}
}
phyread(dev, priv->phyaddr, 1, &status);
printf("Waiting for PHY to complete autonegotiation.\n");
while ( !(status & 0x0020) )
{
phyread(dev, priv->phyaddr,19, &temp);
// timeout_counter++;
// if (timeout_counter == 30)
// {
// printf("Auto negotiation error\n");
// return;
// }
phyread(dev, priv->phyaddr, 1, &status);
}
printf("autonegotiation complete.\n");
}
static void phy_detection(struct eth_device *dev)
{
int i;
u16 phyreg;
struct zynq_gem_priv *priv = dev->priv;
if (priv->phyaddr != -1) {
phyread(dev, priv->phyaddr, PHY_DETECT_REG, &phyreg);
printf("phy reg is %d\n",phyreg);
if ((phyreg != 0xFFFF) &&
((phyreg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) {
/* Found a valid PHY address */
printf("Default phy address %d is valid\n",
priv->phyaddr);
return;
} else {
printf("PHY address is not setup correctly %d\n",
priv->phyaddr);
priv->phyaddr = -1;
}
}
printf("detecting phy address\n");
if (priv->phyaddr == -1) {
/* detect the PHY address */
for (i = 31; i >= 0; i--) {
phyread(dev, i, PHY_DETECT_REG, &phyreg);
if ((phyreg != 0xFFFF) &&
((phyreg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) {
/* Found a valid PHY address */
priv->phyaddr = i;
printf("Found valid phy address, %d\n", i);
return;
}
}
}
priv->phyaddr = 0;
printf("No PHY detected. Assuming a PHY at address 0\r\n");
}
static int zynq_gem_setup_mac(struct eth_device *dev)
{
u32 i, macaddrlow, macaddrhigh;
struct zynq_gem_regs *regs = (struct zynq_gem_regs *)dev->iobase;
/* Set the MAC bits [31:0] in BOT */
macaddrlow = dev->enetaddr[0];
macaddrlow |= dev->enetaddr[1] << 8;
macaddrlow |= dev->enetaddr[2] << 16;
macaddrlow |= dev->enetaddr[3] << 24;
/* Set MAC bits [47:32] in TOP */
macaddrhigh = dev->enetaddr[4];
macaddrhigh |= dev->enetaddr[5] << 8;
for (i = 0; i < 4; i++) {
writel(0, ®s->laddr[i][LADDR_LOW]);
writel(0, ®s->laddr[i][LADDR_HIGH]);
/* Do not use MATCHx register */
writel(0, ®s->match[i]);
}
writel(macaddrlow, ®s->laddr[0][LADDR_LOW]);
writel(macaddrhigh, ®s->laddr[0][LADDR_HIGH]);
return 0;
}
static int zynq_gem_init(struct eth_device *dev, bd_t * bis)
{
u32 i;
u16 tmp;
unsigned long clk_rate = 0;
struct phy_device *phydev;
const u32 stat_size = (sizeof(struct zynq_gem_regs) -
offsetof(struct zynq_gem_regs, stat)) / 4;
struct zynq_gem_regs *regs = (struct zynq_gem_regs *)dev->iobase;
struct zynq_gem_priv *priv = dev->priv;
const u32 supported = SUPPORTED_10baseT_Half |
SUPPORTED_10baseT_Full |
SUPPORTED_100baseT_Half |
SUPPORTED_100baseT_Full |
SUPPORTED_1000baseT_Half |
SUPPORTED_1000baseT_Full;
if (!priv->init) {
/* Disable all interrupts */
writel(0xFFFFFFFF, ®s->idr);
/* Disable the receiver & transmitter */
writel(0, ®s->nwctrl);
writel(0, ®s->txsr);
writel(0, ®s->rxsr);
writel(0, ®s->phymntnc);
/* Clear the Hash registers for the mac address
* pointed by AddressPtr
*/
writel(0x0, ®s->hashl);
/* Write bits [63:32] in TOP */
writel(0x0, ®s->hashh);
/* Clear all counters */
for (i = 0; i <= stat_size; i++)
readl(®s->stat[i]);
/* Setup RxBD space */
memset(priv->rx_bd, 0, RX_BUF * sizeof(struct emac_bd));
for (i = 0; i < RX_BUF; i++) {
priv->rx_bd[i].status = 0xF0000000;
priv->rx_bd[i].addr =
((u32)(priv->rxbuffers) +
(i * PKTSIZE_ALIGN));
}
/* WRAP bit to last BD */
priv->rx_bd[--i].addr |= ZYNQ_GEM_RXBUF_WRAP_MASK;
/* Write RxBDs to IP */
writel((u32)priv->rx_bd, ®s->rxqbase);
/* Setup for DMA Configuration register */
writel(ZYNQ_GEM_DMACR_INIT, ®s->dmacr);
/* Setup for Network Control register, MDIO, Rx and Tx enable */
setbits_le32(®s->nwctrl, ZYNQ_GEM_NWCTRL_MDEN_MASK);
priv->init++;
}
printf("phy detection.\n");
phy_detection(dev);
phy_rst(dev);
phy_negotiat(dev);
printf("--------phyaddr : 0x%x----\r\n", priv->phyaddr);
unsigned short PhyReg;
//change to page 0
phyread(dev, priv->phyaddr, 22, &PhyReg);
PhyReg = PhyReg & 0xfffe;
phywrite(dev, priv->phyaddr, 22, PhyReg);
phyread(dev, priv->phyaddr, 20, &PhyReg);
PhyReg = PhyReg | 0x82;
phywrite(dev, priv->phyaddr, 20, PhyReg);
/* reset phy */
phyread(dev, priv->phyaddr, 0, &PhyReg);
PhyReg |= 0x8000;
phywrite(dev, priv->phyaddr, 0, PhyReg);
puts("GEM link speed is 1000Mbps\n");
writel(ZYNQ_GEM_NWCFG_INIT | ZYNQ_GEM_NWCFG_SPEED1000, ®s->nwcfg);
setbits_le32(®s->nwctrl, ZYNQ_GEM_NWCTRL_RXEN_MASK |
ZYNQ_GEM_NWCTRL_TXEN_MASK);
return 0;
}
完成这三个地方配置后,重新编译u-boot,网络才算完成,可以通过tftp加载devicetree,uramdisk.image.gz,uImage,这样要比通过JTAG dow到内存中快很多,最后通过bootm启动就可以了。
zynq-uboot> tftp 0x2a00000 devicetree.dtb
Using zynq_gem device
TFTP from server 192.168.0.101; our IP address is 192.168.0.99
Filename 'devicetree.dtb'.
Load address: 0x2a00000
Loading: T #
done
Bytes transferred = 13222 (33a6 hex)
zynq-uboot> tftp 0x2000000 uramdisk.image.gz
Using zynq_gem device
TFTP from server 192.168.0.101; our IP address is 192.168.0.99
Filename 'uramdisk.image.gz'.
Load address: 0x2000000
Loading: #################################################################
#################################################################
#################################################################
#################################################################
#################################################################
####################################
done
Bytes transferred = 5289509 (50b625 hex)
zynq-uboot> tftp 0x3000000 uImage
Using zynq_gem device
TFTP from server 192.168.0.101; our IP address is 192.168.0.99
Filename 'uImage'.
Load address: 0x3000000
Loading: T #################################################################
#################################################################
#################################################################
##########################
done
Bytes transferred = 3230680 (314bd8 hex)
zynq-uboot> bootm 0x3000000 0x2000000 0x2a00000
## Booting kernel from Legacy Image at 03000000 ...
Image Name: Linux-4.0.0-dayun
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 3230616 Bytes = 3.1 MiB
Load Address: 00008000
Entry Point: 00008000
Verifying Checksum ... OK
## Loading init Ramdisk from Legacy Image at 02000000 ...
Image Name:
Image Type: ARM Linux RAMDisk Image (gzip compressed)
Data Size: 5289445 Bytes = 5 MiB
Load Address: 00800000
Entry Point: 00800000
Verifying Checksum ... OK
## Flattened Device Tree blob at 02a00000
Booting using the fdt blob at 0x02a00000
Loading Kernel Image ... OK
OK
Loading Ramdisk to 1faf4000, end 1ffff5e5 ... OK
Loading Device Tree to 1faed000, end 1faf33a5 ... OK
Starting kernel ...
Uncompressing Linux... done, booting the kernel.
Booting Linux on physical CPU 0x0
Linux version 4.0.0-dayun (root@shenvpc) (gcc version 5.2.1 20151005 (Linaro GCC 5.2-2015.11-2) ) #1 SMP PREEMPT Fri Apr 7 02:03:21 Local time zone must be set--s
CPU: ARMv7 Processor [413fc090] revision 0 (ARMv7), cr=18c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
.......