Defining custom getc, putc funcitons for additonal uart in uboot

I want to enable additional uart (the one on J17 header) during uboot process.

  1. From previous posts, as a first step, I have made the below dtb changes to the /Linux_for_Tegra/sources/u-boot/arch/arm/dts/tegra186-p2771-0000-500.dtb file to enable the UART2. I have added the serial@0xc280000 serial controller.
[i]serial@3100000 {
		compatible = "nvidia,tegra186-uart", "nvidia,tegra20-uart";
		reg = <0x0 0x3100000 0x0 0x10000>;
		reg-shift = <0x2>;
		status = "disabled";
	};
[/i]

[b]	serial@0xc280000 {
		compatible = "nvidia,tegra186-uart", "nvidia,tegra20-uart";
		reg = <0x0 0xc280000 0x0 0x10000>;
		reg-shift = <0x2>;
		status = "okay";[/b]
	};
  1. For now, I wanted to assume the base address and offsets of the serial controller is correct and move on to create a custom getc, puts function to access the serial port.

  2. However, I have not been able to figure out a way to do this. The post reminds me that the uart is just a memory location (a register) where you can copy the bytes into the memory and set some “start transfer” register to send the data. Setting the baud rate would be a matter of setting the right value to some other register. This is what I would be doing for a bare metal coding in a micro controller. But I expected that u-boot might already have the low-level stuff covered and let me access the uart using a file read write operation.

  3. The organization of uboot code is confusing and after 'grep’ing through various files from different vendor boards, I am still not sure how to write a wrapper for the sending data to additional UART2. Some vendors have explicit functions to enable_uart. But I am unable to find one for tegra-186 (TX2)

  4. There was a post on a different forum that talks about using the serial.h file to get access to eserial#_device.puts(" ") . But, I was unable to compile the uboot code after following the instructions.

  5. So w.r.t. TX2, where does the uart that was enabled in the device tree, get mapped to the functions like getc, putc? or How to use the existing definitions in uboot code to access different UARTs?

Hi,
We have tried it after booting to kernel. Don’t have experience of enabling this function in u-boot. You would need other users to share experience.

The u-boot source code is in
https://developer.nvidia.com/embedded/dlc/r32-3-1_Release_v1.0/Sources/T186/public_sources.tbz2

You might consider looking for docs on how to change the serial console port to a different UART and starting by transferring the serial console to your other UART. This is just a method to verify the serial UART is set up and working at the intended base address, and then when you’ve verified, to transfer serial console back to the original port (while keeping most of the device tree which worked for the new UART when it was a serial console).

You might also be interested these files in the U-Boot source:

doc/device-tree-bindings/chosen.txt
doc/driver-model/serial-howto.txt
drivers/serial/serial-uclass.c

(search for “stdout-path”)

There is a note for setting up a “stdout-path” via the device tree “chosen” node. You would not want to produce a conflict by trying to compete with serial console for stdout-path, but I am guessing that if you examined the U-Boot source where stdout-path is actually used, then you could produce another path of your own (such as a FILE* which is usable by fputc() and friends).

As a first step, I managed to switch the behaviour of uart0(in J21 header) to uart2(in J17 header).

Now the uart2 is able to spit out all the debug messages during booting process. Once the booting is complete, it becomes a console.

I wanted to document the procedure so that it could be useful to others. Do correct me if I had made any unnecessary changes.

  1. Change the Linux_for_Tegra/p2771-0000.conf.common file
...
ROOTFSSIZE=28GiB;
-CMDLINE_ADD="console=ttyS0,115200n8 console=tty0 fbcon=map:0 net.ifnames=0";
+CMDLINE_ADD="console=ttyS2,115200n8 console=tty0 fbcon=map:0 net.ifnames=0";
target_board="t186ref";
...
  1. Change tegra186-mb1-bct-misc-si-l4t.cfg file present in the below paths. I don’t know which of this modification got things working. I changed both the files.

Linux_for_Tegra/bootloader/t186ref/BCT
Linux_for_Tegra/bootloader/

...
##### debug variables #####
debug.enable_log = 1;
-debug.uart_instance = 0;
+debug.uart_instance = 2;
...
  1. changing device tree file in u-boot source code
    Linux_for_Tegra/sources/u-boot/arch/arm/dts/tegra186-p2771-0000-500.dtb
...
chosen {
-		stdout-path = "/serial@3100000";
+		stdout-path = "/serial@c280000";
	};
...
...
-	serial@3100000 {
-		compatible = "nvidia,tegra186-uart", "nvidia,tegra20-uart";
-		reg = <0x0 0x3100000 0x0 0x10000>;
-		reg-shift = <0x2>;
-		status = "disabled";
-	};

+	serial@c280000 {
+		compatible = "nvidia,tegra186-uart", "nvidia,tegra20-uart";
+		reg = <0x0 0xc280000 0x0 0x10000>;
+		reg-shift = <0x2>;
+		status = "disabled";
+	};
...

I built the u-boot and copied the u-boot.bin,u-boot.dtb and u-boot-dtb.bin to the
Linux_for_Tegra/bootloader/t186ref/p2771-0000/500 folder

  1. Changing the bootloader dtb

/Linux_for_Tegra/bootloader/tegra186-quill-p3310-1000-c03-00-base.dtb

...
chosen {
-		bootargs = "root=/dev/mmcblk0p1 rw rootwait rootfstype=ext4 console=ttyS0,115200n8 console=tty0 fbcon=map:0 net.ifnames=0  ";
+		bootargs = "root=/dev/mmcblk0p1 rw rootwait rootfstype=ext4 console=ttyS2,115200n8 console=tty0 fbcon=map:0 net.ifnames=0  ";
		board-has-eeprom;
-		stdout-path = "/serial@3100000";
+		stdout-path = "/serial@c280000";
	};
...

	serial@3100000 {
-		compatible = "nvidia,tegra20-uart", "nvidia,tegra186-hsuart";
+		compatible = "nvidia,tegra186-hsuart";
		iommus = <0x11 0x20>;
		reg = <0x0 0x3100000 0x0 0x40>;
		reg-shift = <0x2>;
		interrupts = <0x0 0x70 0x4>;
		nvidia,memory-clients = <0xe>;
		dmas = <0x25 0x8 0x25 0x8>;
		dma-names = "rx", "tx";
		clocks = <0x10 0x37 0x10 0x10d>;
		clock-names = "serial", "parent";
		status = "okay";
		nvidia,tolerance-low-range = <0x0>;
		nvidia,tolerance-high-range = <0x4>;
		nvidia,adjust-baud-rates = <0x1c200 0x1c200 0x64>;
-		console-port;
-		sqa-automation-port;
		linux,phandle = <0x107>;
		phandle = <0x107>;
	};
...
	serial@c280000 {
-		compatible = "nvidia,tegra186-hsuart";
+		compatible = "nvidia,tegra20-uart", "nvidia,tegra186-hsuart";
		iommus = <0x11 0x20>;
		reg = <0x0 0xc280000 0x0 0x40>;
		reg-shift = <0x2>;
		interrupts = <0x0 0x72 0x4>;
		nvidia,memory-clients = <0xe>;
		dmas = <0x25 0x3 0x25 0x3>;
		dma-names = "rx", "tx";
		clocks = <0x10 0xd7 0x10 0x10d>;
		clock-names = "serial", "parent";
		resets = <0x10 0x31>;
		reset-names = "serial";
		status = "okay";
		nvidia,tolerance-low-range = <0x0>;
		nvidia,tolerance-high-range = <0x4>;
		nvidia,adjust-baud-rates = <0x1c200 0x1c200 0x64>;
+		console-port;
+		sqa-automation-port;
		linux,phandle = <0x198>;
		phandle = <0x198>;
	};
...
  1. Changing the kernel dtb

Linux_for_Tegra/kernel/dtb/tegra186-quill-p3310-1000-c03-00-base.dtb

do the changes listed for the bootloader dtb file

  1. Finally I flashed the Jetpack using the command
sudo ./flash.sh jetson-tx2 mmcblk0p1

Random information perhaps useful follows…

A comment on the device tree “serial@…” entries: It is ok to keep all of the UARTs active via “status = “ok”;” instead of “status = “disabled”;”. Initializing a UART is separate from assigning the UART a serial console function.

The ‘stdout-path = “/serial@…”;’ is basically understood by any stdout print function, and does not directly configure the UART controller. If the UART controller at the named address works, then stdout goes there. If the UART controller at the named address does not work, then stdout still goes there, and fails. This is a case of abstraction.

The “compatible” line does not pick which driver is used, but this does limit the system to attempting to use only the drivers in that comma delimited list. The bootloader understands the “tegra20-uart” (non-DMA) driver, and the Linux kernel understands both that driver and the “tegra186-hsuart” (DMA-capable) driver. If both drivers are listed, then U-Boot will choose the only one it understands, the non-DMA driver. Once in the Linux kernel either can be used, but if you are transitioning from U-Boot to Linux, then it is recommended to not switch drivers mid-use. There is no reason to remove any of the “compatible” entries unless you specifically wish to remove one of the drivers as a possible driver. The serial console uses only the non-DMA driver so that the UART continues to run without any loss when transitioning from U-Boot to Linux (the serial console remains in non-DMA driver mode).

Thanks for the response. I ended up using the same getc and puts definition, as the uart is now switched.