原创 为AM335x移植Linux内核主线代码(11)关于AM335x的ethernet中

2014-12-3 09:59 2840 9 9 分类: MCU/ 嵌入式 文集: Linux Kernel的DTS

14.3.8.2 Functional Description

The MII Management I/F will remain idle until enabled by setting the enable bit in the MDIOControl register. The MII Management I/F will then continuously poll the link status from within the Generic Status Register of all possible 32 PHY addresses in turn recording the results in the MDIOLink register.

The linksel bit in the MDIOUserPhySel register determines the status input that is used. A change in the link status of the two PHYs being monitored will set the appropiate bit in the MDIOLinkIntRaw register and the MDIOLinkIntMasked register, if enabled by the linkint_enable bit in the MDIOUserPhySel register.

The MDIOAlive register is updated by the MII Management I/F module if the PHY
acknowledged the read of the generic status register. In addition, any PHY register read transactions initiated by the host also cause the MDIOAlive register to be updated.

At any time, the host can define a transaction for the MII Management interface module to undertake using the data, phyadr, regadr, and write fields in a MDIOUserAccess register. When the host sets the go bit in this register, the MII Management interface module will begin the transaction without without any further intervention from the host. Upon completion, the MII Management interface will clear the go bit and set the userintraw in the MDIOUserIntRaw register corresponding to the MDIOUserAccess register being used.

The corresponing bit in the MDIOUserIntMasked register may also be set depending on the mask setting in the MDIOUserIntMaskSet and MDIOUserIntMaskClr registers. A round-robin arbitration scheme is used to schedule transactions which may queued by the host in different MDIOUserAccess registers. The host should check the status of the go bit in the MDIOUserAccess before initiating a new transaction to ensure that the previous transaction has completed. The host can use the ack bit in the MDIOUserAccess register to determine the status of a read transaction.

It is necessary for software to use the MII Management interface module to setup the auto-negotiation parameters of eath PHY attached to a MAC port, retrieve the negotiation results, and setup the MACControl register in the corresponding MAC.

(下面这个程序是U-Boot中对MDIO的读操作,首先是写控制字,然后在wait_for_user_access中读取user[0].access,并检查GO是否有效。cpsw_mdio_read操作则会检查ACK,确认是否成功。)
===================================================
static inline u32 wait_for_user_access(void)
{
        u32 reg = 0;
        int timeout = MDIO_TIMEOUT;

        while (timeout-- &&
        ((reg = __raw_readl(&mdio_regs->user[0].access)) & USERACCESS_GO))
                udelay(10);

        if (timeout == -1) {
                printf("wait_for_user_access Timeout\n");
                return -ETIMEDOUT;
        }
        return reg;
}

static int cpsw_mdio_read(struct mii_dev *bus, int phy_id,
                                int dev_addr, int phy_reg)
{
        int data;
        u32 reg;

        if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
                return -EINVAL;

        wait_for_user_access();
        reg = (USERACCESS_GO | USERACCESS_READ | (phy_reg << 21) |
               (phy_id << 16));
        __raw_writel(reg, &mdio_regs->user[0].access);
        reg = wait_for_user_access();

        data = (reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -1;
        return data;
}
===================================================

14.4 Software Operation
14.4.1 Transmit Operation
14.4.2 Receive Operation
14.4.3 Initializing the MDIO Module

The following steps are performed by the application software or device driver to initialize the MDIO device:

1. Configure the PREAMBLE and CLKDIV bits in the MDIO control register (MDIOCONTROL).
2. Enable the MDIO module by setting the ENABLE bit in MDIOCONTROL.
3. The MDIO PHY alive status register (MDIOALIVE) can be read in polling fashion until a PHY connected to the system responded, and the MDIO PHY link status register (MDIOLINK) can determine whether this PHY already has a link.
4. Setup the appropriate PHY addressed in the MDIO user PHY select register (MODIOUSERPHYSELn) and set the LINKINTENB bit to enable a link change event interrupt if desirable.
 * If an interrupt on general MDIO register access is desired, set the corresponding bit in the MDIO user command complete interrupt mask set register (MDIOUSERINTMASKSET) to use the MDIO user access register (MDIOUSERACCESSn). Since only one PHY is used in this device, the application software can use one MDIOUSERACCESSn to trigger a completion interrupt; the other MDIOUSERACCESSn is not setup.

(再次对比cpsw_mdio_init函数。)
(mdio_base的地址是0x4A101000,和手册中的说明也是对应的。)
(__raw_writel用来设置了div和CONTROL_ENABLE,但是并没有设置PREAMBLE。)
(mdio_register用来注册设备list,和硬件的关系不大。)
===================================================
static void cpsw_mdio_init(char *name, u32 mdio_base, u32 div)
{
        struct mii_dev *bus = mdio_alloc();

        mdio_regs = (struct cpsw_mdio_regs *)mdio_base;
        __raw_writel(div | CONTROL_ENABLE, &mdio_regs->control);
        
        udelay(1000);
        bus->read = cpsw_mdio_read;
        bus->write = cpsw_mdio_write;
        sprintf(bus->name, name);
        mdio_register(bus);
}
===================================================

14.4.4 Writing Data to a PHY Register

The MDIO module includes a user access register (MDIOUSERACCESSn) to directly access a specified PHY device. To write a PHY register, perform the following:

1. Check to ensure that the GO bit in the MDIO user access register (MDIOUSERACCESSn) is cleared.
2. Write to the GO, WRITE, REGADR, PHYADR, and DATA bits in MDIOUSERACCESSn corresponding to the PHY and PHY register you want to write.
3. The write operation to the PHY is scheduled and completed by the MDIO module. Completion of the write operation can be determined by polling the GO bit in MDIOUSERACCESSn for a 0.
4. Completion of the operation sets the corresponding USERINTRAW bit (0 or 1) in the MDIO user command complete interrupt register (MDIOUSERINTRAW) corresponding to USERACCESSn used. If interrupts have been enabled on this bit using the MDIO user command complete interrupt mask set register (MDIOUSERINTMASKSET), then the bit is also set in the MDIO usercommand complete interrupt register (MDIOUSERINTMASKED) and an interrupt is triggered on the CPU.

14.4.5 Reading Data from a PHY Register

The MDIO module includes a user access register (MDIOUSERACCESSn) to directly access a specified PHY device. To read a PHY register, perform the following:

1. Check to ensure that the GO bit in the MDIO user access register (MDIOUSERACCESSn) is cleared.
2. Write to the GO, REGADR, and PHYADR bits in MDIOUSERACCESSn corresponding to the PHY and PHY register you want to read.
3. The read data value is available is the DATA bits in MDIOUSERACCESSn after the module completes the read operation on the serial bus. Completion of the read operation can be determined by polling the GO and ACK bits in MDIOUSERACCESSn. After the GO bit has cleared, the ACK bit is set on a successful read.
4. Completion of the operation sets the corresponding USERINTRAW bit (0 or 1) in the MDIO user command complete interrupt register (MDUIUSERINTRAW) corresponding to USERACCESSn used. If interrupts have been enabled on this bit using the MDIO user command complete interrupt mask set register (MDIOUSERINTMASKSET), then the bit is also set in the MDIO user command complete interrupt register (MDIOUSERINTMASKED) and an interrupt in triggered on the CPU.

14.4.6 Initialization and Configuration of CPSW
14.5 Ethernet Subsystem Registers

*************************************************************
AM335x技术手册的Ethernet部分到这里就阅读完成了!

要调试Ethernet功能:
第一步,是阅读AM335x中关于Ethernet的部分;
第二步,是阅读AR8031的芯片手册;
(这个之前已完成。)
第三步,修改U-Boot代码,来调试PHY芯片:

假定这个芯片刚刚焊接上去,我的工作是使它正常工作起来。(我现在面临的就是这样的状况。)U-Boot没有教程告诉用户如何调试PHY,所以我得一层一层的分析源代码,找到关键的函数,定位并解决问题。虽然没有JTAG仿真器,printf函数也是可以提供相当丰富的调试信息的。

================================
A. int board_eth_init(bd_t *bis)

这个函数定义在用户board.c文件中,被net/eth.c中的eth_initialize函数调用。
这个函数由用户根据自己的硬件来编写,在am335x_evm的mainline里面,它主要完成了以下功能,第一是读取AM335x的网卡地址,第二是将MII功能设置为rgmii模式,第三是注册cpsw。其它乱七八糟的还有判断board类型等等(which is not important)。

AM335x有两个网卡,我绘制的板子只使用了其中的一个eth0。
在board.c文件的头部定义了:
static struct ctrl_dev *cdev = (struct ctrl_dev *)CTRL_DEVICE_BASE;

因此网卡地址就是:
cdev->macid0h
cdev->macid0l
使用printf打印出来为:
0xed143388
0xdedf

由于AM335x的寄存器是小端存储,Little Endian,因此实际的结果是:
88 33 14 ed df de
使用U-Boot命令printenv ethaddr,结果刚好符合:
ethaddr=88:33:14:ed:df:de

(从网卡寄存器的位置就知道,它的地址由AM335x决定,而不是PHY芯片。PHY芯片本身不具备MAC地址。)

================================
B. int cpsw_register(struct cpsw_platform_data *data)

cpsw_register函数被board_eth_init调用。
它的主要功能是,初始化ethnet相关的结构体,注册eth,初始化MDIO,以及初始化phy芯片。

它用到的结构体分别是:
struct cpsw_platform_data *data; (入口参数)
strcut cpsw_priv *priv; (局部变量)
struct cpsw_slave *slave; (局部变量)
struct eth_device *dev; (局部变量)

首先它要为dev和priv申请空间;
然后是为priv赋值:
priv->data = *data;
priv->dev = dev;
priv->slaves = malloc(sizeof(struct cpsw_slave) * data->slaves);
priv->host_port = data->host_port_num;
priv->regs = regs;
priv->host_port_regs = reg + data->host_port_reg_ofs;
priv->dma_regs = regs + data->cpdma_reg_ofs;
priv->ale_regs = regs + data->ale_reg_ofs;
priv->descs = (void *)regs + data->bd_ram_ofs;

这些赋值的含义分别是:
(cpsw主设备使用用户定义的data);
(cpsw主设备使用局部变量dev);
(申请data->slaves个从设备空间);
(cpsw主设备使用host_port,为0);
(cpsw主设备的寄存器首地址,为0x4a100000);
(cpsw主设备的host_port_regs地址,为0x4a100108);
(cpsw主设备的dma_regs地址,为0x4a100800);
(cpsw主设备的ale_regs地址,为0x4a100d00);
(cpsw主设备的descs地址,为0x4a102000);

再然后是一个for循环:
for_each_slave(slave, priv) {
        cpsw_slave_setup(slave, idx, priv);
        idx = idx + 1;
}
展开就是:
for (slave = (priv)->slaves; slave != (priv)->slaves + (priv)->data.slaves; slave++) {
        cpsw_slave_setup(slave, idx, priv);
        idx = idx + 1;
}

slave是struct cpsw_slave型的指针,它的自增会跳到sizeof(struct cpsw_slave)处,而不是紧挨着它的下一个地址,因此这个for循环的作用,就是初始化每个struct cpsw_slave。

for循环里面实现的具体功能是为slave赋值:
slave->slave_num = slave_num;
slave->data = (struct cpsw_slave_data *)(priv->data.slave_data + slave_num);
slave->regs = regs + data->slave_reg_ofs;
slave->sliver = regs + data->sliver_reg_ofs;

这些赋值的含义分别是:
(slave的编号为slave_num,即for里面的idx);
(slave的data为用户board.c中定义的data.slave_data);
(slave的regs为0x4a100108,它属于CPSW_PORT REGISTER的地址);
(slave的sliver为0x4a100d80,它属于CPSW_SL REGISTER的地址);

然后是为dev赋值,主要是填写相应的操作(函数指针):
dev->iobase = 0;
dev->init = cpsw_init;
dev->halt = cpsw_halt;
dev->send = cpsw_send;
dev->recv = cpsw_recv;
dev->priv = priv;

上述赋值都完成之后,还需要执行四个函数:
eth_register(dev);
cpsw_mdio_init(dev->name, data->mdio_base, data->mdio_dev);
priv->bus = miiphy_get_dev_by_name(dev->name);
cpsw_phy_init(dev, slave);

================================
C. int cpsw_phy_init(struct eth_device *dev, struct cpsw_slave *slave)

cpsw_phy_init函数被cpsw_register调用。
它的主要功能是,首先connect PHY芯片,然后config PHY芯片。
mainline的打印信息“Could not get PHY for cpsw: addr 0”就来自它的connect步骤。

================================
D. struct phy_device *phy_connect(struct mii_dev *bus, int addr,
                struct eth_device *dev, phy_interface interface)  

其中,phy_connect函数会调用phy_find_by_mask函数:
struct phy_device *phy_find_by_mask(struct mii_dev *bus, unsigned phy_mask,
                phy_interface interface)
                
随后,phy_find_by_mask函数又调用get_phy_device_by_mask函数:                  
struct phy_device *get_phy_device_by_mask(struct mii_dev *bus,
                unsigned phy_mask, phy_interface interface)
 
这个get_phy_device_by_mask函数是很重要的,它包含了这么几个步骤:
search_for_existing_phy(bus, phy_mask, interface);
create_phy_by_mask(bus, phy_mask, i ? i : MDIO_DEVAD_NONE, interface);
phy_device_create(bus, ffs(phy_mask) - 1, 0xffffffff, interface);
                                      
总之,最终的目的是要找到可用的PHY。
==============
所以,先看看这个search_for_existing_phy函数:
struct phy_device *search_for_existing_phy(struct mii_dev *bus,
                unsigned phy_mask, phy_interface interface)
{
        /* If we have one, reurn the existing device, with new interface */
        while (phy_mask) {
                int addr = ffs(phy_mask) - 1;
                if (bus->phymap[addr]) {
                        bus->phymap[addr]->interface = interface;
                        return bus->phymap[addr];
                }
                phy_mask &= ~(1 << addr);
        }
        return NULL;
}                

ffs函数的功能是,找到最低的bit 1位置,find first bit set。
比如ffs(0x123)=1,ffs(0x456000)=14,ffs(0x78910)=5。
这里phy_mask的值为1,bus->phymap[addr]的值为0,这说明没有existing_phy。

==============
再来看看create_phy_by_mask函数:
struct phy_device *create_phy_by_mask(struct mii_dev *bus,
                unsigned phy_mask, int devad, phy_interface_t interface)
{
        u32 phy_id = 0xffffffff;
        while (phy_mask) {
                int addr = ffs(phy_mask) - 1;
                int r = get_phy_id(bus, addr, devad, &phy_id);
                if (r < 0)
                        return ERR_PRT(r);
                /* If the PHY ID is mostly f's, we didn't find anything */
                if ((phy_id & 0x1fffffff) != 0x1fffffff)
                        return phy_device_create(bus, addr, phy_id, interface);
                phy_mask &= ~(1 << addr);
        }
        return NULL;
}   
             
This function is really important!
因为MDIO和PHY芯片的初始化通信就在这里!
get_phy_id函数会调用bus->read函数,它指向的就是cpsw_mdio_read。

================================
E. int cpsw_mdio_read(struct mii_dev *bus, int phy_id,
                int dev_addr, int phy_reg)
{
        int data;
        u32 reg;
        if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
                return -EINVAL;
        wait_for_user_access();
        reg = (USERACCESS_GO | USERACCESS_READ | (phy_reg << 21) |
                (phy_id << 16));
        __raw_writel(reg, &mdio_regs->user[0].access);
        reg = wait_for_user_access();
        
        data = (reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -1;
        return data;
}

inline u32 wait_for_user_access(void)
{
        u32 reg = 0;
        int timeout = MDIO_TIMEOUT;
                
        while (timeout-- &&
        ((reg = __raw_readl(&mdio_regs->user[0].access)) & USERACCESS_GO)
                udelay(10);
                
        if (timeout == -1) {
                printf("Wait_for_user_access Timeout\n");
                return -ETIMEOUT;
        }
        return reg;
}

这两个函数,在之前阅读AM335x手册的Ethernet部分就提到过,它们的功能是初始化MDIO并且从MDIO读取数据。当我将MDIO的DATA引脚的MUX注释掉之后,再执行cpsw_mdio_read函数,就会发现串口一直不停的打印:
Wait_for_user_access Timeout
使用示波器观察DATA引脚,会发现它始终保持高电平。否则,DATA引脚上应该有波形出现。

当MDIO初始化完成之后,CLK就会持续输出脉冲波,这里的div使用的是99,在示波器上可以观察周期为2us,占空比为50%的脉冲波。
MDCLK frequency = clk frequency / (clkdiv + 1) = 500KHz;
从AM335x手册的Table 14-3,可知MDIO的频率满足下面的要求:
mhz50_clk RMII and 100mbps RGMII Reference clock = 50MHz;
              
Question:
现在,刚刚焊接好的PHY芯片工作并不正常,我还不知道是QFN的焊接出了问题,还是程序上的毛病,为了找出问题所在,在U-Boot代码里面,添加如下代码到cpsw_mdio_read中,功能是把MDIO的寄存器值都打印出来:
             
printf("maria: ----> %x\n", __raw_readl(CPSW_MDIO_BASE + 0));
printf("maria: ----> %x\n", __raw_readl(CPSW_MDIO_BASE + 0x04));
printf("maria: ----> %x\n", __raw_readl(CPSW_MDIO_BASE + 0x08));
printf("maria: ----> %x\n", __raw_readl(CPSW_MDIO_BASE + 0x0c));
printf("maria: ----> %x\n", __raw_readl(CPSW_MDIO_BASE + 0x10));
printf("maria: ----> %x\n", __raw_readl(CPSW_MDIO_BASE + 0x14));
printf("maria: ----> %x\n", __raw_readl(CPSW_MDIO_BASE + 0x20));
printf("maria: ----> %x\n", __raw_readl(CPSW_MDIO_BASE + 0x24));
printf("maria: ----> %x\n", __raw_readl(CPSW_MDIO_BASE + 0x28));
printf("maria: ----> %x\n", __raw_readl(CPSW_MDIO_BASE + 0x2c));
printf("maria: ----> %x\n", __raw_readl(CPSW_MDIO_BASE + 0x80));
printf("maria: ----> %x\n", __raw_readl(CPSW_MDIO_BASE + 0x84));
printf("maria: ----> %x\n", __raw_readl(CPSW_MDIO_BASE + 0x88));
printf("maria: ----> %x\n", __raw_readl(CPSW_MDIO_BASE + 0x8c));
        
打印结果:        
maria: ----> 40070106
maria: ----> 410000ff
maria: ----> 10
maria: ----> 0
maria: ----> 0
maria: ----> 0
maria: ----> 1
maria: ----> 0
maria: ----> 0
maria: ----> 0
maria: ----> 40ffff
maria: ----> 0
maria: ----> 0
maria: ----> 0
          
它们的含义是:
MDIOVER: MDIO的外设标示为0x4007,REVMAJ为0x01,REVMIN为0x06;
MDIOCONTROL: MDIO不在idle模式;
        MDIO使能;
        MDIO的Highest User Channel为1;
        MDIO采用标准Preamble,即发送标准的0xffffffff开头;
        MDIO的FAULT为0,即没有失败的读取;
        MDIO的FAULTENB为0,即禁止物理层的fault detection;
        MDIO的INTTESTENB,即禁止interrupt bits;
        MDIO的CLKDIV为99;
MDIOALIVE: MDIO的第5号PHY曾经响应(?);
MDIOLINK: MDIO还未有成功建立连接的;
MDIOLINKINTRAW: (未使用);
MDIOLINKINTMASKED: (未使用);
MDIOUSERINTRAW: 1;
MDIOUSERINTMASKED: (未使用);
MDIOUSERINTMASKSET: (未使用);
MDIOUSERINTMASKCLR: (未使用);    
MDIOUSERACCESS0: MDIO的操作已完成;
        MDIO为读操作;
        MDIO的PHY未响应;
        MDIO的REGADR为0x4;
        MDIO的PHYADR为0x0;
        MDIO的数据为0xFFFF(unvalid);
MDIOUSERPHYSEL0: (未使用);
MDIOUSERACCESS1: (未使用);
MDIOUSERPHYSEL1: (未使用);

cpsw_mdio_read和wait_for_user_access这两个函数的功能,可以与:
“14.4.5 Reading Data from a PHY Register”
中的内容做对比,更能直观的理解,对PHY进行读操作所应该执行的步骤。

 * 检查MDIOUSERACCESSn的GO bit;
 * 对MDUIUSERACCESSn的GO,REGADR,PHYADR进行写操作;
 * 当读操作完成之后,MDIOUSERACCESSn中的DATA数据就可用,此时GO被清零,同时ACK被置位;
 * 操作完成之后USERINTRAW bit会被置位,如果MDUIUSERINTMASKSET相应被设置置位了,则MDIOUSERINTMASKED中的对应中断标识位也会被置位,同时通知CPU中断;

(NOTE: 另外,还有一个很意思的现象,虽然我在U-Boot代码里面设置了对PHY地址0,REG地址2执行读操作,但是MDIO并没有执行它(也有可能执行了但因为在扫描所以不好观察),而是对PHY地址0~31进行扫描,事实上这一过程在执行cpsw_mdio_init时就开始了,从示波器上可以观察出来。)
(这是因为(14.3.8.2),当设置了MDIOControl寄存器后,MII Managerment I/F会轮流查询所有32个PHY地址的Generic Status Register(REGADDR = 1),并且记录响应的PHY地址。MDIOUserPhySel寄存器的linksel决定了接收哪个PHY的数据。)

http://e2e.ti.com/support/arm/sitara_arm/f/791/p/304929/1244188.aspx#1244188
这个帖子对MDIO的扫描现象也有说明。

由MDIOALIVE的值就知道,现在连接在MDIO上面的是phy_id为4的设备。因此在get_phy_id里面添加如下代码,查看PHY芯片的信息:

printf("maria: ++++++++++++++++++++++++++++++++++++++\n");
for (j = 0; j < 32; j++)
        bus->read(bus, 4, -1, j);

编译之后运行,就会发现串口打印出了PHY芯片AR8031的0~0x1F共32个寄存器的值。接下来就是通过AR8031的技术手册,观察这些值对应的含义。不过首先,还要弄明白为什么PHY的地址是4(实际上硬件配置的是0)?

AR8031问题:为什么phy_id是4?
答案:AR8031的PHY地址是由它的引脚初始状态决定的。
RXD0 PHYADDRESS0
RXD1 PHYADDRESS1
RXD1-0 set the lower two bits of the physical address. The upper three bits of the physical address are set to the default, "000"..
( * 这样的话,AR8031的PHY地址就应该是0啊!为什么phy_id是4?)
( *** NOTE:将AR8031的引脚补焊,发现phyaddr变为0,说明是虚焊引起了phyaddr的偏移。)


先看看MDIOALIVE寄存器的含义吧(它的值为0x00000010 = (1 << 4)):
MDIO alive. Each of the 32 bits of this register is set if the most recent access to the PHY with address corresponding to the register bit number was acknowledged by the PHY, the bit is reset if the PHY fails to acknowledge the access. Both the user and polling accesses to a PHY will cause the corresponding alive bit to be updated. The alive bits are only meant to be used to give an indication of the presence or not of a PHY with the corresponding address. Writing a 1 to any bit will clear it, writing a 0 has no effect.

再看看round-robin abtration的含义:
A round-robin arbitration scheme is used to shedule transations which may queued by the host in different MDIOUserAccess register.
轮叫调度。轮叫调度算法是以轮叫的方式依次请求调度不同的服务器,即每次调度执行:
i = (i+1) mod n
并选出第i台服务器。
它的优点是简洁,无需记录当前所有连接的状态,所以它是一种无状态调度。
-- from "百度百科"

round-robin abtration是MDIOALIVE的值不等于AR8031的PHY地址的关键,是明明AR8031的PHY地址为0,而在U-Boot中phy_id却被设置为4的原因。

关于round-robin abtration的详细介绍,在本系列第14节。
下节是解决SD卡老是被设置成read-only的小tip;
下下节则是AR8031的各个寄存器含义,正好对照之前的打印信息来理解。

文章评论0条评论)

登录后参与讨论
我要评论
0
9
关闭 站长推荐上一条 /2 下一条