Search code examples
linuxdevice-treepandaboardarchlinux-arm

UART4 with Pandaboard and Arch Linux


I'm trying to use UART4 in my Pandaboard with Arch Linux. I'm using the latest kernel (4.2.0-2-ARCH) so I can't configure MUX in the old way using omap_mux, I have to do it using Device Tree Overlay. This is new to me so it's hard, I've never done this before. I have been reading some post about how to use them in Beaglebone boards in sites like this and this. So I downloaded the OMAP4 Technical Reference Manual (download here). Table 18-504 shows the UART4 control register. Based on that and the urls above I created and compiled the following Device Tree Overlay filling the register with 0's, this should set the MUX for UART4 functionality:

// Util: http://lxr.free-electrons.com/source/arch/arm/boot/dts/omap4-panda-es.dts
//       http://www.valvers.com/embedded-linux/beaglebone-black/step04-gpio/


/dts-v1/;
/plugin/;


/ {
        model = "TI OMAP4 PandaBoard-ES";
        compatible = "ti,omap4-panda-es", "ti,omap4460";


        part-number = "ANDRES-IO";


        fragment@0 {
                target = <&am33xx_pinmux>;
                __overlay__ {
                        uart4_pins: pinmux_uart4_pins {
                                pinctrl-single,pins = <
                                                        0x15C 0x00  // kernel pin 142 (uart4 tx y rx - address 0x4A10 015C)
                                                      >;
                        };
                };
        };


        fragment@1 {
                target = <&ocp>;
                __overlay__ {
                        uart4_pins_helper {
                                compatible = "panda-pinmux-helper";
                                pinctrl-names = "default";
                                pinctrl-0 = <&uart4_pins>;
                                status = "okay";
                        };
                };
        };
};

I copied the compiled file to /lib/firmware/ but after that I don't know how to use or enable it. Beaglebone boards have bone_capemgr but I can't see such a thing in Pandaboard.

Other OS like Ubuntu has UART4 already configured, I tried looking for the Device Tree Overlay they use but couldn't find anything.


Solution

  • I solved it!!!!! Maybe is something trivial but it was really hard for me and I learned a lot. There is no much info about how to do it in Pandaboard, just for Beagleboards. So first, the Device Tree Overlay file it's loaded only at boot, we can't load it dynamically as with Beagleboard because we don't have a bone_capemgr. The compiled .dtb file is located in /boot/dtbs (at least in Arch Linux), there are a lot of .dtb files there but only one is loaded at boot depending on the board and you can see which one is loaded when booting, for example, in my case it is:

    U-Boot 2015.04 (Jun 07 2015 - 19:26:06) Arch Linux ARM
    CPU  : OMAP4460 ES1.1
    Board: OMAP4 Panda
    I2C:   ready
    DRAM:  1 GiB
    MMC:   OMAP SD/MMC: 0
    ** Unable to use mmc 0:1 for loading the env **
    Using default environment
    Net:   No ethernet found.
    Hit any key to stop autoboot:  0 
    starting USB...
    USB0:   USB EHCI 1.00
    scanning bus 0 for devices... 3 USB Device(s) found
           scanning usb for storage devices... 0 Storage Device(s) found
           scanning usb for ethernet devices... 1 Ethernet Device(s) found
    switch to partitions #0, OK
    mmc0 is current device
    mmc found on device 0
    Checking for: /boot/uEnv.txt ...
    74 bytes read in 13 ms (4.9 KiB/s)
    Loaded environment from /boot/uEnv.txt
    Checking if uenvcmd is set ...
    4984312 bytes read in 244 ms (19.5 MiB/s)
    loading /boot/dtbs/omap4-panda-es.dtb ...
    100695 bytes read in 380 ms (257.8 KiB/s)
    ** File not found /boot/initramfs-linux.img **
    Kernel image @ 0x82000000 [ 0x000000 - 0x4c0df8 ]
    ## Flattened Device Tree blob at 88000000
       Booting using the fdt blob at 0x88000000
       Loading Device Tree to 8ffe4000, end 8ffff956 ... OK
    Starting kernel ...
    

    I have a Pandaboard ES so the loaded file is /boot/dtbs/omap4-panda-es.dtb. I decompiled the file so I can add the UART4 MUX settings using dtc -I dtb -O dts omap4-panda-es.dtb > omap4-panda-es.dts (taken from here). So now we have a omap4-panda-es.dts which is the complete Device Tree Overlays that sets everything up, I just need to add the UART4 MUX settings. We have to use the pinctrl-single,pins property. Here is a really good explanation about pinctrl-single,pins:

    The pin configuration nodes for pinctrl-single are specified as pinctrl register offset and value pairs using pinctrl-single,pins. Only the bits specified in pinctrl-single,function-mask are updated. For example, setting a pin for a device could be done with: pinctrl-single,pins = <0xdc 0x118>; Where 0xdc is the offset from the pinctrl register base address for the device pinctrl register, and 0x118 contains the desired value of the pinctrl register.

    This was something I misunderstood at the beginning, I though the pinctrl address was absolute but it's relative to the base address in the tree. In my case for example there are a lot of pinmux_tfp410_pins, pinmux_dss_hdmi_pins, pinmux_i2c1_pins, etc. All these pinmux_* are under a parent called pinmux@40, that means that the address specified in pinctrl is relative to 0x40, but this pinmux@40 is under another node called scm@100000 which is inside another node called l4@4a000000, so the addresses inside pinmux@40 are relative to the base address of the node that is the sum of all these addresses, that is 0x4a000000 + 0x100000 + 0x40 = 0x4a100040, so 0xa100040 is the base address, all the address specified in pinctrl are relative to 0xa100040. So, according to Table 18-504 in the OMAP4 Technical Reference Manual (download available here) the address for the UART4 control register that control the MUX and some other things is 0x4A10015C. The base address of pinctrl is 0x4a100040 so the address we must specify in pinctrl is 0x11c because 0x4a100040 + 0x11c = 0x4A10015C. All the Device Tree Overlays I found of other linux distros that support Pandaboard use the same base address 0x4a100040 (here for example). So I added under the node pinmux@40 this:

    // Set the UART4 MUX, it doesn't come by default so I had to add it
    //  "linux,phandle" has the same value aas "phandle", it's just a reference number, just make sure
    //  it is not being used in another part of the tree (it will refuse to compile if you do it wrong)
    // The phandle is used for reference in "serial@4806e000" at "pinctrl-0"
    pinmux_uart4_pins {
        pinctrl-single,pins = <
            0x11c 0x100       // uart4_rx.uart4_rx INPUT | MODE0
            0x11e 0           // uart4_tx.uart4_tx OUTPUT | MODE0
        >;
        linux,phandle = <0xfff>;
        phandle = <0xfff>;
    };
    

    I took this settings from here but just changing the 0x100 and 0 will change the settings in the register. In my case I also had to add:

    linux,phandle = <0xfff>;
    phandle = <0xfff>;
    

    I don't see this in Ubuntu for example (https://github.com/Canonical-kernel/Ubuntu-kernel/blob/master/arch/arm/boot/dts/omap4.dtsi) but I don't know why or what is the purpose of this phandle, all I know if that they are used as a reference, a reference I need to put somewhere else in the Device Tree, just make sure it is unique, it can be any 32-bit value but must be unique inside the tree. In my case there was another node referring UART4:

    serial@4806e000 {
         compatible = "ti,omap4-uart";
         reg = <0x4806e000 0x100>;
         interrupts = <0x0 0x46 0x4>;
         ti,hwmods = "uart4";
         clock-frequency = <0x2dc6c00>;
         interrupts-extended = <0x1 0x0 0x46 0x4 0x82 0x11c>;
         linux,phandle = <0x121>;
          phandle = <0x121>;
     };
    

    So there I had to use phandle otherwise MUX settings won't be applied:

     serial@4806e000 {
         compatible = "ti,omap4-uart";
         reg = <0x4806e000 0x100>;
         interrupts = <0x0 0x46 0x4>;
         ti,hwmods = "uart4";
         pinctrl-names = "default";
         pinctrl-0 = <0xfff>;
         clock-frequency = <0x2dc6c00>;
         interrupts-extended = <0x1 0x0 0x46 0x4 0x82 0x11c>;
         linux,phandle = <0x121>;
         phandle = <0x121>;
     };
    

    Finally, at the end of the file there a lot of definitions, one for each node, for example

    i2c1_pins = "/ocp/l4@4a000000/scm@100000/pinmux@40/pinmux_i2c1_pins";
    i2c2_pins = "/ocp/l4@4a000000/scm@100000/pinmux@40/pinmux_i2c2_pins";
    i2c3_pins = "/ocp/l4@4a000000/scm@100000/pinmux@40/pinmux_i2c3_pins";
    i2c4_pins = "/ocp/l4@4a000000/scm@100000/pinmux@40/pinmux_i2c4_pins";
    wl12xx_gpio = "/ocp/l4@4a000000/scm@100000/pinmux@40/pinmux_wl12xx_gpio";
    wl12xx_pins = "/ocp/l4@4a000000/scm@100000/pinmux@40/pinmux_wl12xx_pins";
    twl6030_pins = "/ocp/l4@4a000000/scm@100000/pinmux@40/pinmux_twl6030_pins";
    

    They just describe where each node is located, here we can clearly see what the base address is. So here I just added this:

    uart4_pins = "/ocp/l4@4a000000/scm@100000/pinmux@40/pinmux_uart4_pins";
    

    Now we have a complete .dts file which should get the UART4 working. We must compile it using dtc -O dtb -o omap4-panda-es.dtb -b O -@ omap4-panda-es.dts, this will generate a .dtb file that will replace the one in /boot/dtbs, so replace it and reboot! After rebooting run cat /sys/kernel/debug/pinctrl/4a100040.pinmux/pinmux-functions, it should show something like this:

    function: pinmux_dss_dpi_pins, groups = [ pinmux_dss_dpi_pins ]
    function: pinmux_tfp410_pins, groups = [ pinmux_tfp410_pins ]
    function: pinmux_dss_hdmi_pins, groups = [ pinmux_dss_hdmi_pins ]
    function: pinmux_tpd12s015_pins, groups = [ pinmux_tpd12s015_pins ]
    function: pinmux_hsusbb1_pins, groups = [ pinmux_hsusbb1_pins ]
    function: pinmux_uart4_pins, groups = [ pinmux_uart4_pins ]
    function: pinmux_wl12xx_pins, groups = [ pinmux_wl12xx_pins ]
    function: gpio_led_pmx, groups = [ gpio_led_pmx ]
    function: pinmux_wl12xx_gpio, groups = [ pinmux_wl12xx_gpio ]
    function: pinmux_i2c1_pins, groups = [ pinmux_i2c1_pins ]
    function: pinmux_twl6030_pins, groups = [ pinmux_twl6030_pins ]
    function: pinmux_twl6040_pins, groups = [ pinmux_twl6040_pins ]
    function: pinmux_i2c2_pins, groups = [ pinmux_i2c2_pins ]
    function: pinmux_i2c3_pins, groups = [ pinmux_i2c3_pins ]
    function: pinmux_i2c4_pins, groups = [ pinmux_i2c4_pins ]
    function: pinmux_wl12xx_pins, groups = [ pinmux_wl12xx_pins ]
    function: pinmux_mcpdm_pins, groups = [ pinmux_mcpdm_pins ]
    function: pinmux_mcbsp1_pins, groups = [ pinmux_mcbsp1_pins ]
    

    If we see uart4 everything is fine and it should be working! Otherwise there is something wrong in the .dts file. We can test if the uart is working by running for example echo -e "AT" > /dev/ttyO3, remember that /dev/ttyO3 is UART4. I hope this will be useful for someone!

    Just for reference here is the complete .dts and compiled .dtb file with working UART4: https://gist.github.com/dragondgold/1aaabf93279006b703f3