Friday 12 April 2013

Mission Failed: Disabling UART completely on a Raspberry Pi

Pins 8 and 10 on the Raspberry Pi is usually configured to act as the transmit and receive pins for an RS-232 serial port (the Pi's mini UART). However, if you want to use these as plain GPIO pins, you can do so by simply configuring them as GPIO inputs or -outputs using the python-GPIO library... until your Raspberry Pi reboots, that is.

Upon reboot, you will find that one or both of these pins are set to high due to them being booted into ALT0 (read: UART) mode. This obviously causes problems if you are using these pins for GPIO in an application (unless you can ensure that your Pi is *never* rebooted). I have tried in vain to find a solution for this, but it seems to be a firmware issue (or at least baked deeply into the kernel). The following post simply documents what I tried, but it provides insight on two things: how to deactivate the default serial console running on /dev/ttyAMA0, and how to compile a custom kernel for your Raspberry Pi.

tl;dr: Tried to completely disable UART (pins 8 and 10) on a Raspberry Pi by forcing it not to boot into ALT0 mode. You can't, it seems - or at least I have not found a method. Please leave comment if you know of a way!

My attempt: disable serial console, then remove UART drivers from kernel


Note: I started down this path because pins 8 and 10 on the Pi are not driven high when the Pi is turned on without an SD card (i.e. without an operating system). This led me to suspect that the OS is setting the pins into ALT0 mode in software. As far as I can tell, I was wrong. Now I suspect it's the firmware, but that's sadly not solved as easily.

The serial console device /dev/ttyAMA0 is created automatically by the default Raspbian kernel (since it uses it for diagnostic- and boot messages, and as a login console) and because of this, the UART driver is compiled directly into to the kernel and not as an unloadable module. The following steps changes that, first by deactivating most serial messages and the serial login prompt. Next, the kernel is recompiled to build the UART driver as a separate unloadable module. Finally, the UART driver is disabled.

  1. Disable boot- and debug message logging to the serial console (this is configured to be active by default on Raspbian):

    Edit /boot/cmdline.txt:
    sudo vim /boot/cmdline.txt
    Remove the options referring to the serial console ("/dev/ttyAMA0"); in other words, change it from this:

    dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

    to this:

    dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

  2. Next you must disable the serial console login prompt. Edit /etc/inittab:
    sudo vim /etc/inittab
    At the end of the file you will find the following line:
    T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100
    Disable it by commenting it out (put a # character at the start of the line), or delete it.

  3. Longest step: Kernel recompilation
    Before you start, make sure your system is up to date:
    sudo apt-get update
    sudo apt-get dist-upgrade
    
    Next, install the compiler and some packages needed for recompiling the kernel:
    sudo apt-get install gcc make git libncurses5-dev
    Make and change into a directory in which you can build the kernel:
    sudo mkdir /opt/build
    sudo chmod 777 /opt/build
    cd /opt/build
    
    Download a copy of the kernel source from the github repository:
    git clone --depth 1 git://github.com/raspberrypi/linux.git
    cd linux
    Now you are ready to configure and rebuild the kernel. First, make sure your build directory is clean:
    make mrproper
    Next, save the configuration of your currently-running kernel (this allows your new kernel to be configured based on your current kernel settings, which saves a lot of time):
    zcat /proc/config.gz > .config
    To ensure that your current kernel configuration is up-to-date with the kernel you downloaded, do:
    make oldconfig
    Now we are going to modify the kernel to modularize the UART driver and remove bootup dependencies on it:
    make menuconfig
    You will be presented with the Linux kernel configuration menu. Since the configuration is based on your currently-running kernel's configuration, we only need to change two things:

    1. Disable all /dev/ttyAMA0 kernel boot messages: Highlight "Boot options" and press [ENTER]. Now highlight the "Default kernel command string"; it will look like the following:

      (dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait)

      Press [ENTER] to select it. Edit the string and remove the "console" and "kgdboc" options; i.e. change it to:

      dwc_otg.lpm_enable=0 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait

      Once edited, press [ESC] twice to go back to the previous level. Continue pressing [ESC] until you get back to the main menu.

    2. Modularize UART driver (so that it can be disabled): From the main kernel configuration menu, go to Device Drivers -> Character devices -> Serial drivers, then highlight "ARM AMBA PL011 serial port support" and press M to modularize it.

      Now press [ESC] until you get back to the main menu. Press [ESC] twice again, and save the configuration when prompted.
    You are now ready to start the build. Build the kernel and its modules by entering:
    make && make modules
  4. Install the custom kernel
    Your newly-built kernel image will be /opt/build/linux/arch/arm/boot/Image. Copy the new kernel image to the Raspberry Pi's boot partition, using a different name in case you need to roll back later:
    sudo cp -v arch/arm/boot/Image /boot/kernel_mod_uart.img
    Now edit the file /boot/config.txt:
    sudo vim /boot/config.txt
    In this file, change the "kernel=" line to the following (or add it if it does not exist):
    kernel=kernel_mod_uart.img
    Finally, install the kernel modules that you compiled earlier, by issuing (in your build directory):
    sudo make modules_install
    You have now replaced the default kernel with your "UART-as-a-module" version. Reboot the Raspberry Pi to load the new kernel and ensure everything is OK.

  5. Final step: Disable UART driver module
    The new kernel still loads and sets up the UART device, but now you can disable it. Edit the file /etc/modprobe.d/raspi-blacklist.conf:
    sudo vim /etc/modprobe.d/raspi-blacklist.conf
    Add the following line to the file, and save it:
    blacklist amba_pl011
    You may now reboot the Pi for the changes to take affect, or simply remove the already-loaded driver directly by entering:
    sudo rmmod amba_pl011


Conclusion


Sadly, pins 8 and 10 of the Raspberry Pi are still set to ALT0 mode upon bootup after applying the changes documented above. I have not looked into the kernel source for other clues (for my needs it is far simpler to use two different pins). If you know of way to force them to be set to GPIO mode (and driven LOW), please let me know.

No comments:

Post a Comment