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