I am using the TI83867IRRGZ chip for ethernet on our card (connected to an NXP IMX6DL CPU) and having some trouble with automatic downshift from 1gbps to 100mbps when there are only 4 wires (2 pairs, A and B) in the cable.
Have had no problem with 8 wires and running 1gbps, but when I have limited the number of wires the chip doesn’t seem to automatically select lower speed.
From what I can see (SPEED SELECTION (bit14-15) in register PHYSTS 0x0011, see below) the MAC/PHY layer detects the missing wires and sets 100mbps speed, however the link layer still advertises 1000mbps and since the link partner also advertise 1000mbps it seems this speed is selected, and no data is transmitted.
I have this problem for both U-boot and Linux (old release using kernel 4.9). However I patched the dp83867 driver for Linux included in 4.9 with a new read_status function (which I found in the latest Linux driver: github.com/.../dp83867.c) which reads register PHYSTS and manully overrides the set speed:
int status = phy_read(phydev, MII_DP83867_PHYSTS);
if (status & DP83867_PHYSTS_1000)
phydev->speed = SPEED_1000;
else if (status & DP83867_PHYSTS_100)
phydev->speed = SPEED_100;
else
phydev->speed = SPEED_10;
This worked very well when connecting the card to my PC using a Linksys USB3GIG network adapter, however does not work when connecting with the same cable to a Cisco SG250-08 switch.
There may be additional changes in the newer driver that works around this, and I'm looking into patching more of that, but have to handle my older kernel missing som callback/function hooks used in the newer kernel.
For both Linux and U-boot forcing 100mbps speed or disabling 1gbps negotiation gives a working 100mbps connection regardless of device on the other end:
U-boot (as described in https://community.nxp.com/t5/i-MX-Processors/u-Boot-changing-ethernet-speed-with-i-MX6/td-p/1247947):
mii write 0 0x9 0x0
mii write 0 0x0 0x3300
Linux:
ethtool -s eth0 speed 100 duplex full autoneg on
I feel that if I could set the correct register values for the chip this should function automatically, but I cant seem to find the right configuration.
For example I've been trying to enable Speed optimization for 1000baseT mode by setting both SPEED_OPT_ENHANCED_EN (bit8) and SPEED_OPT_EN (bit9) of CFG2 (0x0014) register. But I have never seen SPEED_OPT_STATUS (bit7) of PHYSTS be 1 which I would expect if Speed Optimization is enabled(?), but it always 0.
I've done this either by adding the following to board_phy_config function in my u-boot build:
#define DP83867_CFG2 0x14
#define DP83867_SPEED_OPTIMIZED_EN (BIT(6) | BIT(8) | BIT(9))
val = phy_read(phydev, MDIO_DEVAD_NONE, DP83867_CFG2);
val |= DP83867_SPEED_OPTIMIZED_EN;
phy_write(phydev, MDIO_DEVAD_NONE, DP83867_CFG2, val);
Or manually using mii tool in the u-boot command line:
mii write 0 0x14 0x2BC7
mii write 0 0x0 0x1340 # retrigger auto-negotiation
What am I missing or have done wrong?
Register values after initalization (with 4 wires), read using "mii read 0 X" tool in U-boot (with DP83867_SPEED_OPTIMIZED_EN disabled) - connected to Linksys PC adapter:
0x00: 1140
0x01: 796D
0x02: 2000
0x03: A231
0x04: 01E1
0x05: CDE1
0x06: 006F
0x07: 2001
0x08: 7001
0x09: 0300
0x0a: 0800
0x0b: 0000
0x0c: 0000
0x0d: 401F
0x0e: 00A8
0x0f: 3000
0x10: 4040
0x11: 7C02
0x12: 0000
0x13: 9CC0
0x14: 29C7
0x15: 0000
0x16: 0000
0x17: 0040
0x18: 6150
0x19: 4444
0x1a: 0002
0x1b: 0000
0x1c: 0000
0x1d: 0000
0x1e: 0002
0x1f: 0000
Register values after initalization (with 4 wires), read using "mii read 0 X" tool in U-boot (with DP83867_SPEED_OPTIMIZED_EN enabled) - connected to Linksys PC adapter:
0x00: 1140
0x01: 796D
0x02: 2000
0x03: A231
0x04: 01E1
0x05: CDE1
0x06: 006D
0x07: 2001
0x08: 7001
0x09: 0300
0x0a: 0800
0x0b: 0000
0x0c: 0000
0x0d: 401F
0x0e: 00A8
0x0f: 3000
0x10: 4040
0x11: 7F02
0x12: 0000
0x13: 9CC6
0x14: 2BC7
0x15: 0000
0x16: 0000
0x17: 0040
0x18: 6150
0x19: 4444
0x1a: 0002
0x1b: 0000
0x1c: 0000
0x1d: 0000
0x1e: 0002
0x1f: 0000
U-Boot 2019.07-00018-g20cfbc5354-dirty (Apr 21 2022 - 15:25:58 +0200)
With ethernet section of board file:
#define ENET_PAD_CTRL (PAD_CTL_PUS_100K_UP | \
PAD_CTL_SPEED_MED | PAD_CTL_DSE_40ohm | PAD_CTL_HYS)
static iomux_v3_cfg_t const enet_pads1[] = {
MX6_PAD_ENET_MDIO__ENET_MDIO | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET_MDC__ENET_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_RGMII_TXC__RGMII_TXC | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_RGMII_TD0__RGMII_TD0 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_RGMII_TD1__RGMII_TD1 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_RGMII_TD2__RGMII_TD2 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_RGMII_TD3__RGMII_TD3 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_RGMII_TX_CTL__RGMII_TX_CTL | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET_REF_CLK__ENET_TX_CLK | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_RGMII_RXC__RGMII_RXC | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_RGMII_RD0__RGMII_RD0 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_RGMII_RD1__RGMII_RD1 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_RGMII_RD2__RGMII_RD2 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_RGMII_RD3__RGMII_RD3 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_RGMII_RX_CTL__RGMII_RX_CTL | MUX_PAD_CTRL(ENET_PAD_CTRL),
/* PHY nRST */
MX6_PAD_SD3_RST__GPIO7_IO08 | MUX_PAD_CTRL(OUTPUT_40OHM),
};
/* Configure eth0 PHY board-specific behavior, called by fec_probe */
int board_phy_config(struct phy_device *phydev)
{
unsigned short val;
/* TI DP83867 */
const int devad = 0x001f;
if (phydev->phy_id == 0x2000a231) {
/* Configure register 0x170 for ref CLKOUT */
phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_CTRL, devad);
phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_DATA, 0x0170);
phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_CTRL, devad | MII_MMD_CTRL_NOINCR);
val = phy_read(phydev, MDIO_DEVAD_NONE, MII_MMD_DATA);
val &= ~0x1f00;
val |= 0x0b00; /* chD tx clock*/
// val |= 0x0c00; /* Reference clock*/
phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_DATA, val);
}
if (phydev->drv->config) {
phydev->drv->config(phydev);
}
return 0;
}
int board_eth_init(bd_t *bis)
{
uint32_t base = IMX_FEC_BASE;
struct mii_dev *bus = NULL;
struct phy_device *phydev = NULL;
int ret;
imx_iomux_v3_setup_multiple_pads(enet_pads1, ARRAY_SIZE(enet_pads1));
gpio_direction_output(IMX_GPIO_NR(7, 8), 1);
mdelay(10);
#ifdef CONFIG_FEC_MXC
bus = fec_get_miibus(base, -1);
if (!bus)
return -EINVAL;
/* scan phy 0 */
phydev = phy_find_by_mask(bus, 0x01, PHY_INTERFACE_MODE_RGMII);
if (!phydev) {
ret = -EINVAL;
goto free_bus;
}
printf("using phy at %d\n", phydev->addr);
ret = fec_probe(bus, -1, base, bus, phydev);
if (ret)
goto free_phydev;
#endif
#ifdef CONFIG_CI_UDC
/* For otg ethernet*/
usb_eth_initialize(bis);
#endif
return 0;
free_phydev:
free(phydev);
free_bus:
free(bus);
return ret;
}
Here is the schema for the PHY: