Introduction

In Linux systems, there are many drivers and peripherals on any target platform. If we look at Variscite’s VAR-SOM-AM62 for example, we will find dozens of on-chip peripherals in addition to those external peripheral ICs that may be present on a carrier board like the Symphony-Board development platform, all with multiple components spanning both Linux user and kernel spaces.

At Variscite, we provide comprehensive reference designs for all of our System on Modules, and thus there are many drivers and peripherals enabled in our base Linux distributions. However, some of these devices may not be present on your end product, and thus, you may want to disable them.

A non-comprehensive list of reasons to disable unused drivers/devices is below:

  • Security reasons
  • The driver or resource conflicts with another function
  • The driver being present without the device causes runtime issues for the customer’s system
  • You would like to decrease the Linux image size

In this blog, we are going to examine how we can disable unused drivers and devices in Linux. While the examples presented here utilize our VAR-SOM-AM62, the methods can also be extended to our other SoM families.

🅘 This post will focus on devices using kernel based drivers and not user-space implemented device drivers.


Table of Content:

Kernel Driver/Device Architecture

While this blog post will not provide an extensive in depth explanation of kernel internals, it is important that we review and discuss the basic Linux architecture of drivers and devices. To start, let us first define a few of the terms to be used in this post:

device: A physical hardware peripheral of some sort. This could be an on chip memory-mapped peripheral like a GPIO controller or an off-chip hardware device like a ADC IC, or even an external composite hardware device like a PCIe video card.

driver: Software that is used to properly configure and run a device in Linux. This often spans multiple drivers in real devices, however we will focus on simple single driver use cases.

Since Linux is a monolithic kernel, the drivers run kernel code with the same access to resources as other kernel code. As such, drivers can generally be present in two forms:

Built-in: The driver code is compiled into the kernel image itself and is thus already present at runtime.

LKM (Loadable Kernel Module): The driver code is built as a separate binary object file whose contents are dynamically loaded into the kernel sometime later at runtime (drivers can also be unloaded).

The kernel build system contains a configuration function (usually performed with menuconfig), which allows us to configure drivers, such that they are either built into the kernel image, or built externally as loadable modules.

🅘 Many drivers can be configured as built-in or LKMs – however, some can only be configured as one or the other.

It is worth mentioning, that in addition to building LKMs from the kernel tree source, some drivers contain code outside the Linux kernel which has not been “up-streamed” and are built “out of tree.”

 

VAR-SOM-AM62 System on Module

TI AM625 System on Module

 

How Linux Discovers Devices

Next, we should discuss how devices are discovered and “married” with the appropriate driver. That is, we will be answering the question: “How does a driver manage to communicate and ultimately configure and control the device at hand?”

In Linux, all devices are considered to live on some type of bus. This bus is ultimately a physical concept, but in the kernel, can also be thought of as a virtual concept. As such, these devices ultimately fall in two major categories:

discoverable: These are devices which are discoverable on the bus on which they exist. A good example of this is a USB device. The USB system is able to automatically “discover” any new devices which are plugged into it, and thus, ensures that the proper driver is selected in order to communicate with the device.

non-discoverable: In Linux, these devices often live on the “platform bus” (and are called “platform devices”). This is very common for on-chip peripherals. For example, an on chip GPIO controller is probably not discoverable. Its interface is simply some memory mapped registers and an interrupt. Thus, the Linux system has to be informed about its existence, and at which address. The configuration of such a device and its driver, is often performed via device tree.

This is a major over-simplification, but it is sufficient for our purposes in discussing how we disable unused drivers and peripherals.

Disabling Drivers and Peripherals

Finally, we can discuss how we actually disable drivers and peripherals in our Linux system. There are multiple methods, and choosing one is usually going to depend on the driver at hand and what you are trying to achieve. Sometimes, it might be desirable or necessary to employ more than one of these.

Disable Driver in the Kernel Configuration

This is probably the most straightforward approach of all, and applies to built-in drivers or LKMs whose source lives in the kernel tree. With this approach, we can simply disable the driver in the kernel configuration, and it will no longer be built. In ‘Variscite Kernel Configuration Guide’ article, we extensively discussed kernel configuration giving you a full guide on the steps needed to perform this method on our SoMs.

Disable Auto-Loading of LKMs

The default behavior of the Linux distros from Variscite is to auto-load installed kernel modules at boot time. If a module is still installed, another method to disable it is to simply not load it at runtime. This can be achieved with a sequence of commands like the following in Linux:

echo "blacklist module_name" >> /etc/modprobe.d/modulename-config.conf
echo "install module_name /bin/false" >> /etc/modprobe.d/modulename-config.conf
sync

Upon reboot, the module should no longer be loaded.

Disable Device in Device Tree

In the case of platform devices, or other devices that are configured via device tree, we can often make modifications to the device tree in order to disable the device. This portion of the article assumes some familiarity with device trees, therefore, Variscite customers should consult our blog guide on ‘Getting Started with Variscite Device Trees’ in order to review basic device tree concepts before attempting any device tree methods.

🅘 Remember that not all hardware devices utilize device tree entries.

There are 3 primary methods we will present that can be used to disable devices in a device tree:
All of the examples below will use the following node as an example:

somedevnode: device@a000000 {
    compatible = "foo,device";
    status = "okay";
};

Methods

  1. Remove the device tree node definition:This is usually the most straight forward approach:
    --- a/myfile.dts
    +++ b/myfile.dts
    @@ -1,4 +0,0 @@
    -somedevnode: device@a000000 {
    -    compatible = "foo,device";
    -    status = "okay";
    -};
    
  2. Delete the node via /delete-node/ directive:
    Often times, we are utilizing devices that may have been defined in included dtsi files. For example, this is often the case for Variscite SOMs. If the node has been defined or enabled in such included dtsi file, you may not be able to or may not want to remove the original definition.
    Instead, you can add a directive as follows:
    /delete-node/ somedevnode;

🅘 Deleted nodes will also need to have any references to them removed from properties of other nodes, or the device tree will not compile.

  1. Set the status property to “disabled”:
    Often times it is desirable to still maintain the original node definition. In that case, we can simply set the status property to “disabled”
    somedevnode: device@a000000 {
        compatible = "foo,device";
        status = "disabled";
    };
    

Disable Device via sysfs

Another method that is often useful for development purposes, is to unbind a device via the sysfs interface. This is often helpful for testing and development purposes and can provide a quick method to disable the device driver after it has been enabled at boot time.

🅘 This does not remove the driver, but simply removes the device from the driver’s list of devices, freeing its hardware resources.

For this example, we will examine the leds-gpio driver. On our Symphony-Board development platform, we utilize this driver to bind to an existing GPIO line that is connected to an external LED and utilize the “Heartbeat” function of the driver to blink the LED.

If we want to control this GPIO from userspace using gpioset, we currently cannot:

root@am62x-var-som:~# gpioinfo 3     
gpiochip3 - 8 lines:
        line   0:      unnamed  "Heartbeat"  output   active-low [used]
...

root@am62x-var-som:~# gpioset 3 0=1
gpioset: error setting the GPIO line values: Device or resource busy

This is because the GPIO line is currently claimed by the leds-gpio driver. So let us unbind it. We can look for the driver in sysfs, and find the device that is bound to it:

root@am62x-var-som:~# ls /sys/bus/platform/drivers/leds-gpio/ -l
total 0
--w------- 1 root root 4096 Sep 15 14:26 bind
lrwxrwxrwx 1 root root    0 Sep 15 14:26 gpio-leds -> ../../../../devices/platform/gpio-leds
--w------- 1 root root 4096 Sep 13 02:30 uevent
--w------- 1 root root 4096 Sep 15 14:25 unbind

In this case, the bus-id of the device is gpio-leds. So using that, we can unbind it as follows:

root@am62x-var-som:~# echo gpio-leds > /sys/bus/platform/drivers/leds-gpio/unbind 

After doing this, we will find that the device has disappeared in the sysfs directory, the GPIO is listed as unused, and we can now utilize the line:

root@am62x-var-som:~# ls /sys/bus/platform/drivers/leds-gpio/ -l
total 0
--w------- 1 root root 4096 Sep 15 14:26 bind
--w------- 1 root root 4096 Sep 13 02:30 uevent
--w------- 1 root root 4096 Sep 15 14:26 unbind
root@am62x-var-som:~# gpioinfo 3
gpiochip3 - 8 lines:
        line   0:      unnamed       unused  output  active-high 
...

root@am62x-var-som:~# gpioset 3 0=1
root@am62x-var-som:~# echo $?
0

🅘 This method is rather brute force and can cause issues in more complex situations. It is best reserved for development purposes rather than in production.

Disabling Wi-Fi/Bluetooth Module Drivers on Variscite SoMs

Variscite’s SoMs have many optional components, and there are many different ordering variants of each SoM, with different configurations of components/devices populated on it. An example of such an optional device is a Wi-Fi/Bluetooth module. Our software is written so that it can be used on different SoM variants of the same family without issue, but we often find that customers may want to disable the driver and its interface completely. Using some of the concepts we outlined previously, we will show how we can perform this on Variscite’s VAR-SOM-AM62.

🅘 The guide here does not present all build steps, but mainly the source changes needed. The build and integration has been well documented in the other guides presented above.

🅘 The method presented here is very dependent on which Wi-Fi module is used. However, the concepts here can be extended to Variscite’s various SoMs with some modification.

  1. Disable the configuration for the driver module in the kernel:We will start by disabling the driver for the Wi-Fi module. In the case of the VAR-SOM-AM62, the populated module utilizes the Broadcom WLAN driver (CONFIG_BRCMFMAC). As shown below, we find that this is currently set as “M” for module:Disabling Wi-Fi/Bluetooth Module Drivers on Variscite SoMsand so we remove itDisabling Wi-Fi/Bluetooth Module Drivers on Variscite SoMsand then save our configuration and exit.
  2. Remove the related device tree entries:
    Next, we should disable any devices in our device tree. In this case, we will simply disable the entire SD peripheral used for the Wi-Fi module interface:
    --- a/arch/arm64/boot/dts/ti/k3-am625-var-som-symphony.dts
    +++ b/arch/arm64/boot/dts/ti/k3-am625-var-som-symphony.dts
    @@ -639,3 +639,7 @@ &wkup_uart0 {
            /* WKUP UART0 is used by DM firmware */
            status = "reserved";
     };
    +
    +&sdhci2 {
    +       status = "disabled";
    +};
    
  3. Finally, disable any supporting firmware, scripts, etc. in the BSP:
    This will differ depending on which of our distros you are using, however, for this example, we will focus on our Yocto Dunfell release. In this case, we simply need to remove the related recipes that are normally added. We can do this in our image recipe, or in local.conf as follows:
    MACHINE_ESSENTIAL_EXTRA_RDEPENDS:remove = " \
    	bcm43xx-utils \
    	brcm-patchram-plus \
    	linux-firmware-bcm4339 \
    	linux-firmware-bcm43430 \
    "

That is all! We have successfully disabled the Wi-Fi driver and device in our design.

Final Thoughts

As we have presented here, removing drivers and disabling unused devices can be a valuable and fairly straightforward exercise to perform on your custom design. Variscite has greatly helped to streamline this and other development processes by providing our customers with ready to go reference hardware and software right out of the box.

Subscribe to Variscite’s newsletter for more technical articles, updates and upcoming webinars.

 

Related Resources

Webinar: Getting Started with Device Trees on Variscite SOMs
Blog post: i.MX Device Tree Pinmux Settings
Blog post: Variscite Kernel Configuration Guide
Blog post: Creating a Custom Yocto BSP Layer
Blog post: Getting Started with Variscite Device Trees