How to switch AGPIO (GPIO 18/19) - Omega2 FW vs LEDE build



  • Hi,

    Yeah, probably I'm a thread necromancer... But I think I found why (when, how) the official Omega2 image sets AGPIO to digital IO, and which is not done by the original LEDE project.

    I did the following trick, and it seems it succeeded: I connect one channel of my scope to one of the GPIOs (GPIO17) which was ~1.65V by default during boot and changed to 0V at a point in time and I attached the other probe to the TX line.

    As it clearly show the next screenshot, the line switched to digital at SD card initialization:

    0_1501790261164_omega2_agpio_switch.png

    It changes right after the kernel prints MSDC device init.. So, I searched for it in the kernel source and I found.

    Here is the corresponding snippet (it is located in linux-4.9.37/drivers/mmc/host/mtk-mmc/sd.c😞

    static int __init mt_msdc_init(void)
    {
        int ret;
    /* +++ by chhung */
        u32 reg;
    
    #if defined (CONFIG_MTD_ANY_RALINK)
        extern int ra_check_flash_type(void);
        if(ra_check_flash_type() == 2) { /* NAND */
    	    printk("%s: !!!!! SDXC Module Initialize Fail !!!!!", __func__);
    	    return 0;
        }
    #endif
        printk("MTK MSDC device init.\n");
        mtk_sd_device.dev.platform_data = &msdclinux-4.9.37/drivers/mmc/host/mtk-mmc/sd.c0_hw;
    if (ralink_soc == MT762X_SOC_MT7620A || ralink_soc == MT762X_SOC_MT7621AT) {
    //#if defined (CONFIG_RALINK_MT7620) || defined (CONFIG_RALINK_MT7621)
        reg = sdr_read32((volatile u32*)(RALINK_SYSCTL_BASE + 0x60)) & ~(0x3<<18);
    //#if defined (CONFIG_RALINK_MT7620)
    	if (ralink_soc == MT762X_SOC_MT7620A)
        		reg |= 0x1<<18;
    //#endif
    } else {
    //#elif defined (CONFIG_RALINK_MT7628)
        /* TODO: maybe omitted when RAether already toggle AGPIO_CFG */
        reg = sdr_read32((volatile u32*)(RALINK_SYSCTL_BASE + 0x3c));
        reg |= 0x1e << 16;
        sdr_write32((volatile u32*)(RALINK_SYSCTL_BASE + 0x3c), reg);
    
        reg = sdr_read32((volatile u32*)(RALINK_SYSCTL_BASE + 0x60)) & ~(0x3<<10);
    #if defined (CONFIG_MTK_MMC_EMMC_8BIT)
        reg |= 0x3<<26 | 0x3<<28 | 0x3<<30;
        msdc0_hw.data_pins      = 8,
    #endif
    //#endif
    }
        sdr_write32((volatile u32*)(RALINK_SYSCTL_BASE + 0x60), reg);
        //platform_device_register(&mtk_sd_device);
    /* end of +++ */
    
        ret = platform_driver_register(&mt_msdc_driver);
        if (ret) {
            printk(KERN_ERR DRV_NAME ": Can't register driver");
            return ret;
        }
        printk(KERN_INFO DRV_NAME ": MediaTek MT6575 MSDC Driver\n");
    
    #if defined (MT6575_SD_DEBUG)
        msdc_debug_proc_init();
    #endif
        return 0;
    }
    

    It contains the part which actually do the job:

        reg = sdr_read32((volatile u32*)(RALINK_SYSCTL_BASE + 0x3c));
        reg |= 0x1e << 16;
        sdr_write32((volatile u32*)(RALINK_SYSCTL_BASE + 0x3c), reg);
    

    It sets all the four bits of AGPIO_CFG (EPHY_GPIO_AIO_EN, bit 20..17 (GPIO14, GPIO15, GPIO16 and GPIO17)) to 1 - thus enabling them as digital GPIO.

    So, it does not happen in any user space utility (like swconfig), but it happens in the kernel by enabling SD card.

    Hope this is still useful for some of you 😉

    /sza2



  • @sza2-sza2 wow! Impressive analysis and I admire your patience to figure that out!

    Indeed, that is useful to know. Technically I had „solved“ the problem with directly writing to the AGPIO_CFG register, but I was wary of that hack without knowing why this was needed in the first place. Now I feel better 😉

    LEDE is a router box OS, so it makes sense it makes the choice for Ethernet (AGPIO) by default. But still, it has to make that choice at some point, because after reset, the MT7688 is not in AGPIO mode. So there must be code somewhere else which enabkes AGPIO mode on in the first place. Probably low level ethernet init code...

    IMHO it would make sense for a non-router device like the Omega2 to never ever switch these pins to AGPIO, because otherwise there's a short period of unpredictable signal level left between reset and the time whatever code has a chance to switch back to GPIO mode, making them unusable for some purposes.

    Your work motivates me to dig in a bit and find (and eventually patch) that initialisation code 🙂



  • @luz Hi,

    I cannot recall perfectly, but as far as I remember, those pins became ~1.65V during bootloader time. Actually, I saw a glitch on the line somewhere before it permanently set to 0V, and it was already during kernel boot (maybe around MTD initialization) but I did not dig deeper. Probably, later on I'll check it - although my online radio receiver already works, so there is no compelling reason 🙂

    /sza2



  • @sza2-sza2 thinking about it, it is likely AGPIO is enabled in the bootloader already. Onion's modified uboot for sure can use the ethernet, and probably that code was originally written for router-type boxes with all ethernet ports enabled.

    Unfortunately, as per now, we don't have the Omega uboot source, so no way to check or even patch. But I hope this is only a matter of time, as it was for the LEDE tree which is finally available now! (Update 2017-10-07: bootloader sources are available now as well)



  • @luz Hi,

    Finally, I had some time to capture the boot process.GPIO17 and UART TX lines were connected to Saleae Logic's inputs. I recorded the waveform from issuing the reboot command on previously booted system, through U-boot, until the next kernel boot (official Omega2 image).

    Since I was not able to upload the captured file to this comment (although the zipped file was smaller than the 2MB limit), I put in on my Google Drive: omega2_boot.logicdata - if any of you interested in.

    There are three spot where GPIO17 changes its state:

    In U-boot at Resetting MT7628 PHY:
    0_1502221184114_uboot.png
    Where it is switched to Ethernet (there is a short period when it seems to configured as GPIO output (for ~70us)).

    During kernel boot around MTD partitions check:
    0_1502221650435_kernel_glitch.png
    This takes ~700us.

    And finally, SD init set those pins to normal GPIOs as I figured it out previously:
    0_1502221719552_sd_init.png

    There are three voltage levels:

    • ~2.5V (I assume this is when the pin is GPIO input and the logic analyzer pulls somewhat down the pin)
    • ~1.5V (when the pin is configured for Ethernet)
    • ~3.3V (probably the pin is configured as GPIO output)

    So, based on the above, I suppose, it is proven that U-boot changes the state from normal GPIO to Ethernet.

    /sza2



  • @sza2-sza2 Your analysis is correct, the problem affects pin 14 through 29, and is easily fixed is in the bootloader:

    diff --git a/drivers/rt2880_eth.c b/drivers/rt2880_eth.c
    index 7c83a46..9b6f1ec 100644
    --- a/drivers/rt2880_eth.c
    +++ b/drivers/rt2880_eth.c
    @@ -2134,6 +2134,8 @@ void rt305x_esw_init(void)
     /*TODO: Init MT7628 ASIC PHY HERE*/
            i = RALINK_REG(RT2880_AGPIOCFG_REG);
            i = i & ~(MT7628_EPHY_EN);
    +       // only enable PHY 0
    +       i = i | (0x1e << 16);
            RALINK_REG(RT2880_AGPIOCFG_REG) = i;
    
            printf("Resetting MT7628 PHY.\n");
    
    


  • @wdu cool, thanks for figuring that out!

    @Lazar-Demin that should be fixed in the Omega2 bootloader. Only Phy0 is usable in the Omega2, so it makes no sense to enable Phy1..4 at all. With the Omega2S, the other Phys can be used but still I think the bootloader should only enable the port it actually uses (Phy0) and leave the others in GPIO mode.



  • @luz @wdu Thank you for your analysis and solution for this issue. This thread has served me a great purpose for a similar issue.

    I need to use phy0 as WAN, phy1 as LAN and other three phy ports as GPIO pins. When I tried the solution that is mentioned in this thread, it disables all the lan ports (except phy0).

    When I need only phy1 to be used as LAN, what is the registry value that I should use?

    Following, is the change I tried.

    volatile uint32_t *reg = (volatile uint32_t*) (gpio_mmap_reg + 0x3c);
    unsigned int v;
    v = *reg;
    v |= 0x1e << 16;
    *(volatile uint32_t*) (reg) = v;
    


  • @Siddharth-Velappan Unfortunately, it is not possible to switch on phy1..4 individually. Only phy0 can be switched independently; the other 4 always are all in phy or all in GPIO mode.

    The AGPIO_CFG register layout suggests that there is a separate disable bit for each phy, but that's not the case. See the note in the MT7688 datasheet on page 59 reading "When any bit of bit[20:17] is set to 1, P1 ~ P4 will be swiched to digital PADs together."

    It seems to me that this is one of the small oversights in the MT7688 that probably happened when MediaTek had to "IoT-ze" a former router chip design (too) quickly. Other examples of this are the SPI not working correctly in full duplex mode and the PWM units that are not connected to DMA...



  • @luz Hello! This topic is very interesting for me! I'm using an omega2s and I wanted to use eth0 and eth1 (with a custom board). I've recompiled the official firmware adding devmem, installed it and ran:

    devmem 0x1000003C 32 0x00E001FF
    

    Now, the registry has the 'correct' value but the switch isn't working. I've ran:

    root@Omega-89C9:~# swconfig dev switch0 show
    Global attributes:
    	enable_vlan: 0
    	alternate_vlan_disable: 0
    	bc_storm_protect: 0
    	led_frequency: 0
    Port 0:
    	disable: 0
    	doubletag: 1
    	untag: 1
    	led: 5
    	lan: 1
    	recv_bad: 0
    	recv_good: 845
    	tr_bad: 0
    	tr_good: 816
    	pvid: 0
    	link: port:0 link:up speed:100baseT full-duplex 
    Port 1:
    	disable: 0
    	doubletag: 1
    	untag: 1
    	led: 5
    	lan: 1
    	recv_bad: 0
    	recv_good: 0
    	tr_bad: 0
    	tr_good: 0
    	pvid: 0
    	link: port:1 link:down
    Port 2:
    	disable: 0
    	doubletag: 1
    	untag: 1
    	led: 5
    	lan: 1
    	recv_bad: 0
    	recv_good: 0
    	tr_bad: 0
    	tr_good: 0
    	pvid: 0
    	link: port:2 link:down
    Port 3:
    	disable: 0
    	doubletag: 1
    	untag: 1
    	led: 5
    	lan: 1
    	recv_bad: 0
    	recv_good: 0
    	tr_bad: 0
    	tr_good: 0
    	pvid: 0
    	link: port:3 link:down
    Port 4:
    	disable: 0
    	doubletag: 1
    	untag: 1
    	led: 5
    	lan: 1
    	recv_bad: 0
    	recv_good: 0
    	tr_bad: 0
    	tr_good: 0
    	pvid: 0
    	link: port:4 link:down
    Port 5:
    	disable: 1
    	doubletag: 1
    	untag: 1
    	led: ???
    	lan: 1
    	recv_bad: 0
    	recv_good: 0
    	tr_bad: 0
    	tr_good: 0
    	pvid: 0
    	link: port:5 link:down
    Port 6:
    	disable: 0
    	doubletag: 1
    	untag: 1
    	led: ???
    	lan: ???
    	recv_bad: ???
    	recv_good: ???
    	tr_bad: ???
    	tr_good: ???
    	pvid: 0
    	link: port:6 link:up speed:1000baseT full-duplex 
    VLAN 0:
    	ports: 0 1 2 3 4 5 6 
    root@Omega-89C9:~#
    

    but the output is the same as before running devmem and if I connect an ethernet cable on the second port, I don't see any link. What am I missing? Maybe the custom board is not working properly? Or Do I have to configure something in /etc/config/network? Or with swconfig? Sorry for all these questions but I'm relatively new to omega2 and swconfig 🙂

    Thank you so much for your support,
    Paolo



  • @Paolo-Ciuffetti it seems that this is related to @Dino-Ciuffetti 's question on another thread, is it?

    As you say, I also guess it's a matter of getting /etc/config/network right, and understand swconfig. Most probably (but I'm not sure), enabling/disabling the switch is not something that can be done while up and running, but must be set up in the startup sequence.

    I would try to glean information from existing MT7688 based router's firmware (Minibox V2.0, androegg.de...).



  • Yes @luz, we are working on the same project. I'm using informations taken from this thread and this link. I'm rebuilding a new firmware with modifications on sd.c (like /sza2 wrote in this thread) and 02.network (see the linked link) to enable 2 lans (1 wan, 1 lan) and see what happens.

    On very first message you wrote that the address modification (devmem 0x1000003C 32 0x00E001FF) worked for you, but super ugly. It's not working for us, so I asked on this thread more informations on how to make it works for us too.. 🙂

    I'll post my results on this topic when the new firmware will be ready and installed on my omega2s 🙂



  • @luz what did you end up doing to resolve this issue? I'm not too excited about rebuilding the boot loader with a change but I'm also using this pin to control an external relay and the relay is activating during boot time when GPIO18 is in analog mode and goes to ~1.65V.

    I'm thinking about moving to another available GPIO that doesn't exhibit this behavior but wanted to hear what you had to say first.



  • Hi @Chris-Morgan, I did not rebuild the boot loader so far, but just avoided using PHY pins for outputs that need to be stable during powerup in the projects I did since.

    Would be nice though if @onion would (or maybe already has?) fix this in their bootloader, such that newer Omega2's would no longer suffer from that problem.



  • @luz I'm still seeing it here with a pretty new Omega2+ and the latest firmware, although I'm not sure if the oupgrade process upgrades the boot loader or not.



Looks like your connection to Community was lost, please wait while we try to reconnect.