This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Linux/DP83867IR: Zynq with DP83867IR PHY - Configuration Issue - Unable to receive data correctly - Register Help

Part Number: DP83867IR

Tool/software: Linux

Hi, 

We moved our system to a TI DP83867IR PHY and I seem to be running into a configuration issue. I'm looking for some help to verify that my PHY is functioning as expected. My end goal is to resolve the failure to get an IP from the DHCP server. I believe this issue is related to the fact that I cannot receive data correctly. This failure only seems present in Linux. Details below. (MAC address is hidden on purpose) 

Issue:

Sending discover...
Sending discover...
No lease, forking to background

Output:

This output shows that the system is able to acquire an address via DHCP, download a boot image, and boot the target successfully from u-boot. 

U-Boot 2015.07

Gem.e000b000:12 is connected to Gem.e000b000. Reconnecting to Gem.e000b000
Gem.e000b000 Waiting for PHY auto negotiation to complete....... done
BOOTP broadcast 1
BOOTP broadcast 2
DHCP client bound to address XXX.XXX.XXX.XXX 6 (547 ms)

u-boot> boot
Gem.e000b000 Waiting for PHY auto negotiation to complete....... done
Using Gem.e000b000 device
TFTP from server 10.10.0.2; our IP address is 10.10.0.1
Filename 'myfile'.
Load address: 0x3e00000
Loading: T #################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#############
1.3 MiB/s
done

This output shows that the MAC and PHY are registered in the system correctly. I'm using the dp83867 driver from a 4.0 kernel. 

[ 1.292814] macb e000b000.ethernet eth0: Cadence GEM rev 0x00020118 at 0xe000b000 irq 148 (XX:XX:XX:XX:XX:XX)

[ 1.302722] macb e000b000.ethernet eth0: attached PHY driver [TI DP83867] (mii_bus:phy_addr=e000b000.etherne:0c, irq=-1)

This output shows that the interface was created but failed to grab an IP address via DHCP. 

root@root# ifconfig
eth0 Link encap:Ethernet HWaddr XX:XX:XX:XX:XX:XX 
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:2736 (2.6 KiB)
Interrupt:148 Base address:0xb000

This output shows the information about the PHY and link status. 

root@root# mii-tool -v
eth0: negotiated 1000baseT-FD flow-control, link ok
product info: vendor 08:00:28, model 35 rev 1
basic mode: autonegotiation enabled
basic status: autonegotiation complete, link ok
capabilities: 1000baseT-HD 1000baseT-FD 100baseTx-FD 100baseTx-HD 10baseT-FD 10baseT-HD
advertising: 1000baseT-FD 100baseTx-FD 100baseTx-HD 10baseT-FD 10baseT-HD
link partner: 1000baseT-HD 1000baseT-FD 100baseTx-FD 100baseTx-HD 10baseT-FD 10baseT-HD flow-control
root@root

This output shows the information about the transactions / packets. I believe the issue is highlighted in red. This leads me to believe that I'm simply dealing with a timing issue for RX of my system. 

root@root# ethtool -S eth0
NIC statistics:
tx_octets: 428148
tx_frames: 6641
tx_broadcast_frames: 14
tx_multicast_frames: 0
tx_pause_frames: 0
tx_64_byte_frames: 6629
tx_65_127_byte_frames: 1
tx_128_255_byte_frames: 0
tx_256_511_byte_frames: 11
tx_512_1023_byte_frames: 0
tx_1024_1518_byte_frames: 0
tx_greater_than_1518_byte_frames: 0
tx_underrun: 0
tx_single_collision_frames: 0
tx_multiple_collision_frames: 0
tx_excessive_collisions: 0
tx_late_collisions: 0
tx_deferred_frames: 0
tx_carrier_sense_errors: 0
rx_octets: 10065099
rx_frames: 6721
rx_broadcast_frames: 93
rx_multicast_frames: 0
rx_pause_frames: 0
rx_64_byte_frames: 54
rx_65_127_byte_frames: 29
rx_128_255_byte_frames: 6
rx_256_511_byte_frames: 7
rx_512_1023_byte_frames: 1
rx_1024_1518_byte_frames: 6624
rx_greater_than_1518_byte_frames: 0
rx_undersized_frames: 0
rx_oversize_frames: 0
rx_jabbers: 0
rx_frame_check_sequence_errors: 0
rx_length_field_frame_errors: 0
rx_symbol_errors: 0
rx_alignment_errors: 0
rx_resource_errors: 0
rx_overruns: 0
rx_ip_header_checksum_errors: 0
rx_tcp_checksum_errors: 0
rx_udp_checksum_errors: 0
root@root#

If I manually assign an IP address to my eth0 interface I'm able to view the ARP broadcasts coming from my device. Also, If I ping I see the request and reply using a packet analyzer (wireshark). My issue is my system doesn't see the reply. 

root@root:~# ping 10.10.0.2

PING 10.10.0.2 (10.10.0.2): 56 data bytes
^C
--- 10.10.0.2 ping statistics ---
28 packets transmitted, 0 packets received, 100% packet loss
root@root:~#

PHY Register settings. I tried modifying register 0x86 to different values to adjust for rx/tx timing with no luck. The register is acting like it is RO when it should be RW?

0x00 0x1140
0x01 0x796d
0x02 0x2000
0x03 0xa231
0x04 0x01e1
0x05 0xc5e1
0x09 0x0300
0x0a 0x3800
0x10 0x5048
0x11 0xbc02
0x14 0x29c7
0x1e 0x0002
0x32 0x0000
0x6e 0x00a8
0x86 0x006d

TI DP83867 Driver with Debug Prints

/*
 * Driver for the Texas Instruments DP83867 PHY
 *
 * Copyright (C) 2015 Texas Instruments Inc.
 *
 * 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.
 *
 * 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.
 */

#include <linux/ethtool.h>
#include <linux/kernel.h>
#include <linux/mii.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy.h>

#include <dt-bindings/net/ti-dp83867.h>

#define DP83867_PHY_ID		0x2000a231
#define DP83867_DEVADDR		0x1f

#define MII_DP83867_PHYCTRL	0x10
#define MII_DP83867_MICR	0x12
#define MII_DP83867_ISR		0x13
#define DP83867_CTRL		0x1f

/* Extended Registers */
#define DP83867_RGMIICTL	0x0032
#define DP83867_RGMIIDCTL	0x0086

#define DP83867_SW_RESET	BIT(15)
#define DP83867_SW_RESTART	BIT(14)

/* MICR Interrupt bits */
#define MII_DP83867_MICR_AN_ERR_INT_EN		BIT(15)
#define MII_DP83867_MICR_SPEED_CHNG_INT_EN	BIT(14)
#define MII_DP83867_MICR_DUP_MODE_CHNG_INT_EN	BIT(13)
#define MII_DP83867_MICR_PAGE_RXD_INT_EN	BIT(12)
#define MII_DP83867_MICR_AUTONEG_COMP_INT_EN	BIT(11)
#define MII_DP83867_MICR_LINK_STS_CHNG_INT_EN	BIT(10)
#define MII_DP83867_MICR_FALSE_CARRIER_INT_EN	BIT(8)
#define MII_DP83867_MICR_SLEEP_MODE_CHNG_INT_EN	BIT(4)
#define MII_DP83867_MICR_WOL_INT_EN		BIT(3)
#define MII_DP83867_MICR_XGMII_ERR_INT_EN	BIT(2)
#define MII_DP83867_MICR_POL_CHNG_INT_EN	BIT(1)
#define MII_DP83867_MICR_JABBER_INT_EN		BIT(0)

/* RGMIICTL bits */
#define DP83867_RGMII_TX_CLK_DELAY_EN		BIT(1)
#define DP83867_RGMII_RX_CLK_DELAY_EN		BIT(0)

/* PHY CTRL bits */
#define DP83867_PHYCR_FIFO_DEPTH_SHIFT		14
#define DP83867_MDI_CROSSOVER               5
#define DP83867_MDI_CROSSOVER_AUTO          1

/* RGMIIDCTL bits */
#define DP83867_RGMII_TX_CLK_DELAY_SHIFT	4

struct dp83867_private {
	int rx_id_delay;
	int tx_id_delay;
	int fifo_depth;
};

static int dp83867_ack_interrupt(struct phy_device *phydev)
{
	int err = phy_read(phydev, MII_DP83867_ISR);

	if (err < 0)
		return err;

	return 0;
}

static int dp83867_config_intr(struct phy_device *phydev)
{
	int micr_status;

	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
		micr_status = phy_read(phydev, MII_DP83867_MICR);
		if (micr_status < 0)
			return micr_status;

		micr_status |=
			(MII_DP83867_MICR_AN_ERR_INT_EN |
			MII_DP83867_MICR_SPEED_CHNG_INT_EN |
			MII_DP83867_MICR_DUP_MODE_CHNG_INT_EN |
			MII_DP83867_MICR_SLEEP_MODE_CHNG_INT_EN);

		return phy_write(phydev, MII_DP83867_MICR, micr_status);
	}

	micr_status = 0x0;
	return phy_write(phydev, MII_DP83867_MICR, micr_status);
}

#ifdef CONFIG_OF_MDIO
static int dp83867_of_init(struct phy_device *phydev)
{
	struct dp83867_private *dp83867 = phydev->priv;
	struct device *dev = &phydev->dev;
	struct device_node *of_node = dev->of_node;
	int ret;

	if (!of_node && dev->parent->of_node)
		of_node = dev->parent->of_node;

	if (!phydev->dev.of_node)
		return -ENODEV;

	ret = of_property_read_u32(of_node, "ti,rx-internal-delay",
				   &dp83867->rx_id_delay);
	if (ret)
		return ret;

	ret = of_property_read_u32(of_node, "ti,tx-internal-delay",
				   &dp83867->tx_id_delay);
	if (ret)
		return ret;

	return of_property_read_u32(of_node, "ti,fifo-depth",
				   &dp83867->fifo_depth);
}
#else
static int dp83867_of_init(struct phy_device *phydev)
{
	return 0;
}
#endif /* CONFIG_OF_MDIO */

static int dp83867_config_init(struct phy_device *phydev)
{
	struct dp83867_private *dp83867;
	int ret;
	u16 val, delay;

	if (!phydev->priv) {
		dp83867 = devm_kzalloc(&phydev->dev, sizeof(*dp83867),
				       GFP_KERNEL);
		if (!dp83867)
			return -ENOMEM;

		phydev->priv = dp83867;
		ret = dp83867_of_init(phydev);
		if (ret)
			return ret;
	} else {
		dp83867 = (struct dp83867_private *)phydev->priv;
	}

    printk("phy fifo depth %x\n", dp83867->fifo_depth);
	if (phy_interface_is_rgmii(phydev)) {
		ret = phy_write(phydev, MII_DP83867_PHYCTRL,
			(DP83867_MDI_CROSSOVER_AUTO << DP83867_MDI_CROSSOVER) |
			(dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT));
		if (ret)
			return ret;
	}

	printk("reg 0x10 val:%x\n", phy_read(phydev, MII_DP83867_PHYCTRL));

	if ((phydev->interface >= PHY_INTERFACE_MODE_RGMII_ID) &&
	    (phydev->interface <= PHY_INTERFACE_MODE_RGMII_RXID)) {
		val = phy_read_mmd_indirect(phydev, DP83867_RGMIICTL,
					    DP83867_DEVADDR, phydev->addr);

        printk("old reg 0x32 val:%x\n", val);

		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
			val |= (DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN);

		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
			val |= DP83867_RGMII_TX_CLK_DELAY_EN;

		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
			val |= DP83867_RGMII_RX_CLK_DELAY_EN;

		phy_write_mmd_indirect(phydev, DP83867_RGMIICTL,
				       DP83867_DEVADDR, phydev->addr, val);

        printk("dts rx id delay %x\n", dp83867->rx_id_delay);
        printk("dts tx id delay %x\n", dp83867->tx_id_delay);

		delay = (dp83867->rx_id_delay |
			(dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT));

        printk("delay %x\n", delay);

		phy_write_mmd_indirect(phydev, DP83867_RGMIIDCTL,
				       DP83867_DEVADDR, phydev->addr, delay);

		val = phy_read_mmd_indirect(phydev, DP83867_RGMIICTL,
					    DP83867_DEVADDR, phydev->addr);

        printk("new reg 0x32 val:%x\n", val);

		val = phy_read_mmd_indirect(phydev, DP83867_RGMIIDCTL,
					    DP83867_DEVADDR, phydev->addr);

        printk("new reg 0x86 val:%x\n", val);
	}

	return 0;
}

static int dp83867_phy_reset(struct phy_device *phydev)
{
	int err;
	err = phy_write(phydev, DP83867_CTRL, DP83867_SW_RESET);
	if (err < 0)
		return err;

    printk("phy reset....success\n");
	return dp83867_config_init(phydev);
}

static struct phy_driver dp83867_driver[] = {
	{
		.phy_id		= DP83867_PHY_ID,
		.phy_id_mask	= 0xfffffff0,
		.name		= "TI DP83867",
		.features	= PHY_GBIT_FEATURES,
		.flags		= PHY_HAS_INTERRUPT,

		.config_init	= dp83867_config_init,
		.soft_reset	= dp83867_phy_reset,

		/* IRQ related */
		.ack_interrupt	= dp83867_ack_interrupt,
		.config_intr	= dp83867_config_intr,

		.config_aneg	= genphy_config_aneg,
		.read_status	= genphy_read_status,
		.suspend	= genphy_suspend,
		.resume		= genphy_resume,

		.driver		= {.owner = THIS_MODULE,}
	},
};
module_phy_driver(dp83867_driver);

static struct mdio_device_id __maybe_unused dp83867_tbl[] = {
	{ DP83867_PHY_ID, 0xfffffff0 },
	{ }
};

MODULE_DEVICE_TABLE(mdio, dp83867_tbl);

MODULE_DESCRIPTION("Texas Instruments DP83867 PHY driver");
MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com");
MODULE_LICENSE("GPL");

Output From Code (Playing with delays)

[ 1.292634] libphy: MACB_mii_bus: probed
[ 1.300599] phy reset....success
[ 1.303750] phy fifo depth 0
[ 1.306668] reg 0x10 val:20
[ 1.309530] old reg 0x32 val:d3
[ 1.312696] dts rx id delay b
[ 1.315576] dts tx id delay 8
[ 1.318543] delay 8b
[ 1.320925] new reg 0x32 val:d3
[ 1.324092] new reg 0x86 val:8b

Thank you and I look forward to your expertise. 

-Daniel

 

  • Hi,

    The DP83867 driver you are using will adjust the TX_CLK and TX_Dx skew. However per your description of the issue, you are able to send the data out from Linux.
    I believe you shall try adjusting the RX_CLK and RX_Dx delay as problem is in recieving the data.

    Does MAC has capability to adjust the delay ?

    DP83867 also provide capability to adjust this but for that you shall have following MACRO set in driver and can try with different values.

    #define DP83867_RGMII_RX_CLK_DELAY_EN BIT(0) --> set to 1.


    Regards,
    Geet
  • Hi Geet, 

    That bit is set in the config section of the code?

    if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
                val |= (DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN);

    Why is it when I write to the PHY at address 0x86 the value doesn't hold. For instance I should be able to write 0x0077 to address 0x86 correct?

    Thank you,

    Daniel

  • Hi,

    Let me know if you are still looking for information on this topic.

    Regards,
    Geet