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.

AM3352 Using emac1(0x4A100300) as default and only ETH0 device in linux

Hello,

I have a custom board highly inspired by the BeagleBone Black that has one Ethernet port.  This port is connected to an external phy on CPU gmii2_xxx signals.  There is no phy connected to the gmii1_xxxx port.  Also, this phy is connected on the mdio bus with address 0.

I am trying to get this phy to register as a functional eth0 device in linux and I haven't found a way.  I was able to make it work in u-boot 2015-04 by doing the following modifications:

In my board file board.c:

// Before
static struct cpsw_slave_data cpsw_slaves[] = {

{
.slave_reg_ofs = 0x208,
.sliver_reg_ofs = 0xd80,
.phy_addr = 0,
},
{
.slave_reg_ofs = 0x308,
.sliver_reg_ofs = 0xdc0,
.phy_addr = 1,
},
};

// After
static struct cpsw_slave_data cpsw_slaves[] = {

{
.slave_reg_ofs = 0x308,
.sliver_reg_ofs = 0xdc0,
.phy_addr = 0,
},
{
.slave_reg_ofs = 0x208,
.sliver_reg_ofs = 0xd80,
.phy_addr = 1,
},
};

In my mux.c file being compiled along my board file, I made sure the pin mux was set properly:

static struct module_pin_mux mii2_pin_mux[] = {
{OFFSET(gpmc_wpn), MODE(1) | RXACTIVE}, /* MII2_RXERR */
{OFFSET(gpmc_a0), MODE(1)}, /* MII2_TXEN */
{OFFSET(gpmc_a1), MODE(1) | RXACTIVE}, /* MII2_RXDV */
{OFFSET(gpmc_a2), MODE(1)}, /* MII2_TXD3 */
{OFFSET(gpmc_a3), MODE(1)}, /* MII2_TXD2 */
{OFFSET(gpmc_a4), MODE(1)}, /* MII2_TXD1 */
{OFFSET(gpmc_a5), MODE(1)}, /* MII2_TXD0 */
{OFFSET(gpmc_a6), MODE(1) | RXACTIVE}, /* MII2_TXCLK */
{OFFSET(gpmc_a7), MODE(1) | RXACTIVE}, /* MII2_RXCLK */
{OFFSET(gpmc_a8), MODE(1) | RXACTIVE}, /* MII2_RXD3 */
{OFFSET(gpmc_a9), MODE(1) | RXACTIVE}, /* MII2_RXD2 */
{OFFSET(gpmc_a10), MODE(1) | RXACTIVE}, /* MII2_RXD1 */
{OFFSET(gpmc_a11), MODE(1) | RXACTIVE}, /* MII2_RXD0 */
{OFFSET(mdio_data), MODE(0) | RXACTIVE | PULLUP_EN}, /* MDIO_DATA */
{OFFSET(mdio_clk), MODE(0) | PULLUP_EN}, /* MDIO_CLK */
{-1},
};

void enable_board_pin_mux(void)
{
...
configure_module_pin_mux(mii2_pin_mux);
...
}

In the drivers/net/cpsw.c file, I changed:

// Before
static inline u32 cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num)
{
if (priv->host_port == 0)
return slave_num + 1;
else
return slave_num;
}

//After
static inline u32 cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num)

{
return 2;
}

This was enough to make the ethernet port fully functionnal in u-boot.

For Linux, I tried many different settings in the device tree with no results.  Here is what I believe should work but doesn't:

mac: ethernet@4a100000 {
compatible = "ti,cpsw";
ti,hwmods = "cpgmac0";
clocks = <&cpsw_125mhz_gclk>, <&cpsw_cpts_rft_clk>;
clock-names = "fck", "cpts";
cpdma_channels = <8>;
ale_entries = <1024>;
bd_ram_size = <0x2000>;
no_bd_ram = <0>;
rx_descs = <64>;
mac_control = <0x20>;
slaves = <2>;
active_slave = <1>; // <---------------------------- INSTEAD OF 0
cpts_clock_mult = <0x80000000>;
cpts_clock_shift = <29>;
reg = <0x4a100000 0x800
0x4a101200 0x100>;
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;
/*
* c0_rx_thresh_pend
* c0_rx_pend
* c0_tx_pend
* c0_misc_pend
*/
interrupts = <40 41 42 43>;
ranges;
syscon = <&cm>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&cpsw_default>;
pinctrl-1 = <&cpsw_sleep>;
status = "okay";

davinci_mdio: mdio@4a101000 {
compatible = "ti,davinci_mdio";
#address-cells = <1>;
#size-cells = <0>;
ti,hwmods = "davinci_mdio";
bus_freq = <1000000>;
reg = <0x4a101000 0x100>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&davinci_mdio_default>;
pinctrl-1 = <&davinci_mdio_sleep>;
status = "okay";
};

cpsw_emac0: slave@4a100200 {
/* Filled in by U-Boot */
mac-address = [ 00 00 00 00 00 00 ];
phy_id = <&davinci_mdio>, <1>;  // <------------------THIS PHY IS UNUSED, ADDRESS 1 IS DUMMY
phy-mode = "mii";
};

cpsw_emac1: slave@4a100300 {
/* Filled in by U-Boot */
mac-address = [ 00 00 00 00 00 00 ];
phy_id = <&davinci_mdio>, <0>;  // <------------------emac1 phy address is 0
phy-mode = "mii";
};

phy_sel: cpsw-phy-sel@44e10650 {
compatible = "ti,am3352-cpsw-phy-sel";
reg= <0x44e10650 0x4>;
reg-names = "gmii-sel";
};
};

This registers eth0 in linux but it does not seem to use emac1:

[...]
[ 7.229462] davinci_mdio 4a101000.mdio: davinci mdio revision 1.6
[ 7.235843] davinci_mdio 4a101000.mdio: detected phy mask fffffffe
[ 7.247930] libphy: 4a101000.mdio: probed
[ 7.252251] davinci_mdio 4a101000.mdio: phy[0]: device 4a101000.mdio:00, driver SMSC LAN8710/LAN8720
[ 7.262763] cpsw 4a100000.ethernet: Detected MACID = d0:5f:b8:f5:f9:aa
[...]
[ 10.957585] net eth0: initializing cpsw version 1.12 (0)
[ 10.992743] net eth0: phy found : id is : 0x7c0f1
[ 10.997762] libphy: PHY 4a101000.mdio:01 not found
[ 11.002851] net eth0: phy 4a101000.mdio:01 not found on slave 1  // <----------------------------- Slave 1 was assigned ID 0, this doesn't make sense ?!
[ 11.067328] net eth0: BQL enabled
[...]
[ 14.070123] cpsw 4a100000.ethernet eth0: Link is Up - 100Mbps/Full - flow control rx/tx

# ifconfig
eth0 Link encap:Ethernet HWaddr d0:5f:b8:f5:f9:aa
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:2 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:684 (684.0 B)
Interrupt:167

lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:160 errors:0 dropped:0 overruns:0 frame:0
TX packets:160 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:12960 (12.6 KiB) TX bytes:12960 (12.6 KiB)

How can I get a PHY connected to emac1 through gmii2_xxx signals and having an MDIO address of 0 to work on linux ?  I am running Debian with kernel 3.18.

Any help appreciated, thanks,

Guillaume

  • I found a fix for the problem.  Here it is in case anyone else wants to do the same.

    There seems to be a problem with the drivers\net\ethernet\ti\cpsw.c file when parsing the phy_id property of the device tree emac slaves.  The function cpsw_probe_dt has to be modified as such:

    --- a/kernel/drivers/net/ethernet/ti/cpsw.c
    +++ b/kernel/drivers/net/ethernet/ti/cpsw.c
    @@ -2022,7 +2022,9 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
    const void *mac_addr = NULL;
    u32 phyid;
    int lenp;
    + int lenp2;
    const __be32 *parp;
    + const __be32 *parp2;
    struct device_node *mdio_node;
    struct platform_device *mdio;

    @@ -2030,13 +2032,14 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
    if (strcmp(slave_node->name, "slave"))
    continue;

    - parp = of_get_property(slave_node, "phy_id", &lenp);
    - if ((parp == NULL) || (lenp != (sizeof(void *) * 2))) {
    - dev_err(&pdev->dev, "Missing slave[%d] phy_id property\n", i);

    + parp = of_get_property(slave_node, "mdio", &lenp);
    + parp2 = of_get_property(slave_node, "phy_id", &lenp2);
    + if ((parp == NULL) || (parp2 == NULL) || (lenp != sizeof(void *)) || (lenp2 != sizeof(void *))) {
    + dev_err(&pdev->dev, "Missing slave[%d] phy_id or mdio property\n", i);

    goto no_phy_slave;
    }
    mdio_node = of_find_node_by_phandle(be32_to_cpup(parp));
    - phyid = be32_to_cpup(parp+1);
    + phyid = be32_to_cpup(parp2);
    mdio = of_find_device_by_node(mdio_node);
    of_node_put(mdio_node);
    if (!mdio) {

    The device tree will need to be modified also to work along this updated driver.  I tried to fix the issue without modifying the device tree entries but I was not able to read the second property on the phy_id line correctly, so I separated the single line in two:

    mac: ethernet@4a100000 {
    compatible = "ti,cpsw";
    ti,hwmods = "cpgmac0";
    clocks = <&cpsw_125mhz_gclk>, <&cpsw_cpts_rft_clk>;
    clock-names = "fck", "cpts";
    cpdma_channels = <8>;
    ale_entries = <1024>;
    bd_ram_size = <0x2000>;
    no_bd_ram = <0>;
    rx_descs = <64>;
    mac_control = <0x20>;
    slaves = <2>;
    active_slave = <1>; // <---------------------------- INSTEAD OF 0
    cpts_clock_mult = <0x80000000>;
    cpts_clock_shift = <29>;
    reg = <0x4a100000 0x800
    0x4a101200 0x100>;
    #address-cells = <1>;
    #size-cells = <1>;
    interrupt-parent = <&intc>;
    /*
    * c0_rx_thresh_pend
    * c0_rx_pend
    * c0_tx_pend
    * c0_misc_pend
    */
    interrupts = <40 41 42 43>;
    ranges;
    syscon = <&cm>;
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&cpsw_default>;
    pinctrl-1 = <&cpsw_sleep>;
    status = "okay";

    davinci_mdio: mdio@4a101000 {
    compatible = "ti,davinci_mdio";
    #address-cells = <1>;
    #size-cells = <0>;
    ti,hwmods = "davinci_mdio";
    bus_freq = <1000000>;
    reg = <0x4a101000 0x100>;
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&davinci_mdio_default>;
    pinctrl-1 = <&davinci_mdio_sleep>;
    status = "okay";
    };

    cpsw_emac0: slave@4a100200 {
    /* Filled in by U-Boot */
    mac-address = [ 00 00 00 00 00 00 ];
    mdio = <&davinci_mdio> // <-----------------------NOW ON TWO LINES
    phy_id = <1>;
    phy-mode = "mii";
    };

    cpsw_emac1: slave@4a100300 {
    /* Filled in by U-Boot */
    mac-address = [ 00 00 00 00 00 00 ];
    mdio = <&davinci_mdio>; // <-----------------------NOW ON TWO LINES
    phy_id = <0>;
    phy-mode = "mii";
    };

    phy_sel: cpsw-phy-sel@44e10650 {
    compatible = "ti,am3352-cpsw-phy-sel";
    reg= <0x44e10650 0x4>;
    reg-names = "gmii-sel";
    };
    };

    So the driver now allocates the proper phy_id to the proper phy.  Hope this helps.

    Guillaume

  • Thanks for updating the thread Guillaume!