To share a device between two drivers in Linux, you can use the binding and unbinding operations which are supported in a modern Linux kernel.
In order to bind / unbind a device to / from a driver, simply write the bus ID of the device to the bind / unbind file of that driver. See Manual driver binding and unbinding for more details.
For example, uart4
of imx6qdl has the following device node:
uart4: serial@21f0000 {
compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
reg = <0x021f0000 0x4000>;
interrupts = <0 29 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6QDL_CLK_UART_IPG>,
<&clks IMX6QDL_CLK_UART_SERIAL>;
clock-names = "ipg", "per";
dmas = <&sdma 31 4 0>, <&sdma 32 4 0>;
dma-names = "rx", "tx";
status = "disabled";
};
The bus ID of this device will be 21f0000.serial
.
And the driver that matches the compatible
property is imx-uart
.
The sysfs path of imx-uart
will be /sys/bus/platform/drivers/imx-uart
.
So, in order to unbind uart4
from imx-uart
:
echo "21f0000.serial" > /sys/bus/platform/drivers/imx-uart/unbind
And to bind them again:
echo "21f0000.serial" > /sys/bus/platform/drivers/imx-uart/bind
Now if you want to use uart4
for other purpose, e.g. as a port connecting to a printer speaking proprietary protocol, you should add a new string to compatible
property to be compatible with both drivers:
&uart4 {
compatible = "fsl,imx6q-uart-raw", "fsl,imx6q-uart", "fsl,imx21-uart";
};
And your proprietary driver should have fsl,imx6q-uart-raw
as compatible
of struct of_device_id
.
Then binding and unbinding operations might be (assuming driver name is imx-uart-raw
):
echo "21f0000.serial" > /sys/bus/platform/drivers/imx-uart-raw/bind
echo "21f0000.serial" > /sys/bus/platform/drivers/imx-uart-raw/unbind
Besides, please note the following things:
- Make sure each driver supports that device, not only the
compatible
property of that device in the device tree should list each driver it can be bound to, but also each driver’s source code should work properly with that device. - Make sure the
probe()
andremove()
callback functions of each driver works, especiallyremove()
should tear down all the resources constructed inprobe()
. Likewise,bind()
andunbind()
callbacks ofstruct component_ops
if any. - A device couldn’t be bound to more than one driver at any time. You can check the
driver
symlink in the device directory before binding. - The default binding doesn’t rely on the order of
compatible
property, but the linked order of driver binaries, which is basically the order of the driver in the Makefile. - If you do the binding dynamically from an application and use udev to change the permissions of the device, make sure udev rules triggered before the application tries to open / read / write that device to avoid failure.
Have fun!