I'm going to launch a Linux on my development board, and i need a dts file (device tree file) to describe the whole hardware. But I only know very little about the syntax of this file which is not enough to run Linux properly on the board.
What i know now are only how to describe a unit's interrupt number, frequency, address, parent-unit and its compatible driver type (as described below):
ps7_scuwdt_0: ps7-scuwdt#f8f00620 {
compatible = "xlnx,ps7-scuwdt-1.00.a";
device_type = "watchdog";
interrupt-parent = <&ps7_scugic_0>;
interrupts = < 1 14 769 >;
reg = < 0xf8f00620 0xe0 >;
} ;
Other advanced usage or grammar is unfamiliar to me.
Take a look at the dts of the board which most closely resembles your dev-board. Use that as a reference and make changes to the dts according to the differences between the reference board and your dev-board.
Also checkout the following :
- Device-tree Documentation project at eLinux (has a vast collection of links to start reading).
- Series of articles on the basics of device tree.
- Walkthrough of migrating to device-tree.
Minimal reg + interrupt example with QEMU virtual device
Our example will add the following device tree node to the versatilepb device tree which QEMU will use due to -M versatilepb:
lkmc_platform_device#101e9000 {
compatible = "lkmc_platform_device";
reg = <0x101e9000 0x1000>;
interrupts = <18>;
interrupt-controller;
#interrupt-cells = <2>;
clocks = <&pclk>;
clock-names = "apb_pclk";
lkmc-asdf = <0x12345678>;
};
Then, by using a Linux kernel module to interact with the device, we will test the following DTS features:
registers addresses
IRQs
read custom properties from the driver
These are the main components of the example:
Linux versatile .dts patch on Linux fork
reg and interrupt match numbers hard-coded in the QEMU versatile machine (which represents the SoC)
compatible matches the platform_driver.name in the kernel module, and informs the kernel which module will handle this device
we also pass a custom property to the driver: lkmc-asdf = <0x12345678>;, which is read with of_property_read_u32
the device tree is passed to QEMU's firmware with the -dtb argument
QEMU fork:
device that reads a register and generates interrupts
insert device into -M versatilepb
kernel module Writes to memory on probe to test things out, which also generates an IRQ.
Device trees have many more features that we haven't covered, but this example should get you started, and easily allow you to play around with any new features that come up.
Further resources:
indispensable elinux tutorial: http://elinux.org/Device_Tree_Usage
play around with dtc for purely syntaxical questions. E.g., it shows how nodes are simply merged by path: https://unix.stackexchange.com/a/375923/32558
https://unix.stackexchange.com/questions/118683/what-is-a-device-tree-and-a-device-tree-blob
Lets take a example and I will explain each one of them as below
auart0: serial#8006a000 {
compatible = "fsl,imx28-auart", "fsl,imx23-auart";
reg = <0x8006a000 0x2000>;
interrupts = <112>;
dmas = <&dma_apbx 8>, <&dma_apbx 9>;
dma-names = "rx", "tx";
};
Required properties:
- compatible : Should be "fsl,-auart". The supported SoCs include
imx23 and imx28.
- reg : Address and length of the register set for the device
- interrupts : Should contain the auart interrupt numbers
- dmas: DMA specifier, consisting of a phandle to DMA controller node
and AUART DMA channel ID.
- dma-names: "rx" for RX channel, "tx" for TX channel.
Note: Each auart port should have an alias correctly numbered in "aliases"
node.
For more advance properties, please go to this link, it is very useful
Device Tree Explanation
Hope it helps!
Complementary to the other answers:
Keep in mind, that there is also a section for devicetrees in the official kernel source under root/Documentation/devicetree(https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree?h=v5.2-rc5).
Related
On the device file tree listed here we can see an UART device:
uarta: serial#70006000 {
compatible = "nvidia,tegra210-uart", "nvidia,tegra20-uart";
reg = <0x0 0x70006000 0x0 0x40>;
reg-shift = <2>;
interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA210_CLK_UARTA>;
clock-names = "serial";
resets = <&tegra_car 6>;
reset-names = "serial";
dmas = <&apbdma 8>, <&apbdma 8>;
dma-names = "rx", "tx";
status = "disabled";
};
The tegra210-uart driver is declared here
Why does uarta needs the interrupts section? Does it produce or receive interrupts? My guess is that it produces software interrupts: when it writes something to the serial, it triggers an interrupt. But why must this interrupt be listed in the device tree? And where is the code for this interrupt?
The second line of the above code block (reg = <0x0 0x70006000 0x0 0x40>) refers to an uart address. What is this address exactly? If I understand correctly, this is the address for a 8250_UART device but I'd like to know which pieces of this address do what, and how the kernel talks with this chip.
Driver documentation is available at https://www.kernel.org/doc/Documentation/devicetree/bindings/serial/nvidia%2Ctegra20-hsuart.txt
According to this forum https://forums.developer.nvidia.com/t/enabling-interrupts-for-uart-xavier-agx/166269
interrupts are used to prevent regular polling on UART.
Registering handler on this interrupt allows to only run code when effective data are received and available in DMA buffer, or when the complete outgoing message provided in DMA has been sent. Such a DMA usage example is available at: UART Tx mode with DMA enabled
Memory address 0x70006000 is the base location for UARTA controller registers (there are 4 UART [A,B,C,D] in Tegra 2) where to write commands or read status. These documents may help to understand:
https://docs.freebsd.org/en/articles/serial-uart/index.html
https://www.lammertbies.nl/comm/info/serial-uart
For instance, the Scratch Register (SCR +0x07) can be used to write and read back any byte as a way to test UART is present and controller operates as expected.
I recommend you to download reference documentation, chapter 22 for UART, available from https://developer.nvidia.com/embedded/tegra-2-reference
I am trying to write a linux device driver for a SPI device, using Atmels SAMA5d35 MPU. Linux version is 4.19.30.
I have configured the pins in my device tree and set the status to "okay".
After that the pins changed mode as expected. My question is then, how do I manipulate pinmodes during runtime? My SPI device is not always on, neither is my driver always loaded. When that is the case I want the configured SPI pins to change mode to normal GPIOs (for power-consumption).
Is it the drivers task to do that, or is it something one can define in the device tree, when the driver for example is not loaded?
I have tried to implement a pinctrl-0 for setting the pins to SPI mode, and then a pinctrl-1 for setting the same pins to normal GPIOs. My idea was to switch between these pin configuration in the device driver, but unfortunately the device tree complaints about using the same pins for both pinctrl-0 and pinctrl-1 (compiles fine, but system wont boot). Although this was just my logical thoughts. I am sure there is a more correct way to do the things I stated above.
spi0: spi#f0004000 {
status = "okay";
ext_adc: ads1258#1 {
compatible = "ti,ads1258";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&pinctrl_spi0_default>;
pinctrl-1 = <&pinctrl_spi0_sleep>;
reg = <1>; /* Hardware chipselect SPI0_NPCS1 */
spi-max-frequency = <16000000>; /* According to datasheet */
start-gpios = <&pioD 5 GPIO_ACTIVE_HIGH>;
drdy-gpios = <&pioD 22 GPIO_ACTIVE_LOW>;
enaadc-gpios = <&pioA 26 GPIO_ACTIVE_HIGH>;
};
};
My question is then, how do I manipulate pinmodes during runtime?
That's called "Runtime pinmuxing", which is described in Documentation/pinctrl.txt.
But you may not have to do that.
When that is the case I want the configured SPI pins to change mode to normal GPIOs (for power-consumption).
Is it the drivers task to do that, or is it something one can define in the device tree, when the driver for example is not loaded?
The default mode for a pin (of Atmel/Microchip SoCs) is to be unassigned to any peripheral mode, and be available for use as a GPIO.
All pins that are not assigned to a peripheral or acquired by a driver as a GPIO, end up in the pool of available GPIOs.
So when your SPI driver is not successfully installed, those pins it would use will be in the pool of available GPIOs.
I have tried to implement a pinctrl-0 for setting the pins to SPI mode, and then a pinctrl-1 for setting the same pins to normal GPIOs.
Assigning pins for use by the SPI controller is proper, but there is no mechanism AFAIK for assigning a pin to be an available GPIO.
You can only define properties in the DT for what a device, such as the SPI controller, requires.
There is no mechanism for an "alternate" assignment.
I am sure there is a more correct way to do the things I stated above.
If your driver does not install, then its resources such as its pins should not be allocated. The pins that the SPI would have used will remain unused, and end up in the pool of available GPIOs (unless something else claims/requests one of those pins).
So your DT needs to simply define what your device uses, and leave it at that.
If you still want your driver to modify the state of its pin, then study the section titled "Pin control requests from drivers" in Documentation/pinctrl.txt.
I have been trying to get a custom Yocto build off the ground for some time now. The goal is to connect a DS18b20 thermometer to the GPIO pins and test that it is indeed working. I have gotten as far as building out the base image and adding on all of the software packages I needed to it. The problem presents itself when I try to connect and detect the thermometer with my custom firmware image. It boots fine, networking works, wifi works, have not tried BT yet as I don't need it, but the GPIO pins are the ones that are not proving to be of help.
I am using a 4.9.x Linux Kernel on it. Is it recommended I downgrade to a 4.4.x Linux Kernel? I've pretty much run out of ideas at this point, just need some help to figure it out. f
This device is supported by the kernel, no need to mess with searching or writing your own w1 driver.You will need to ensure the CONFIG_W1_SLAVE_THERM, CONFIG_W1_MASTER_GPIO and CONFIG_W1 are set in you kernel config (consult the Yocto kernel documentation for setting this one-time or in a way that will persist over several builds).
This usually leaves the only remaining gap being which GPIO pin is used for w1. Without knowing which device you are using I can only provide some pointers. The connection is defined via a platform data. This can be defined in the device tree, for example with a c.h.i.p.
onewire {
compatible = "w1-gpio";
gpios = <&pio 3 2 GPIO_ACTIVE_HIGH>; /* PD2 */
pinctrl-names = "default";
pinctrl-0 = <&chip_w1_pin>;
};
Where PD2 == LCD-D2 on the header.
Alternatively it can be defined in the mach-* in the kernel, for example
static struct w1_gpio_platform_data w1_gpio_platform_data = {
.pin = GPIO_ONE_WIRE,
.is_open_drain = 0,
};
static struct platform_device raumfeld_w1_gpio_device = {
.name = "w1-gpio",
.dev = {
.platform_data = &w1_gpio_platform_data
}
};
A google search of 'w1-gpio map gpio' should return more and better details. If you can use the device tree approach, that is what they are made for. Just know that the kernel code doesn't cycle through the GPIO pins looking for connected w1 devices, this mapping has to be set.
If the device is attached and working you will be able to get its value by doing 'cat /sys/bus/w1/devices/28*/w1_slave'. If you are using python you can use https://github.com/timofurrer/w1thermsensor to make reading the temperature easy. Or use equivalent libraries for your preferred language.
I am trying to use the Xilinx interrupt controller driver in an embedded ARM FPGA system I am developing.
(https://github.com/torvalds/linux/blob/master/drivers/irqchip/irq-xilinx-intc.c)
At the end of this driver is the line:
IRQCHIP_DECLARE(xilinx_intc_xps, "xlnx,xps-intc-1.00.a", xilinx_intc_of_init);
I have added an entry in my device tree for the interrupt controller.
xil_intc: xil_intc#41810000 {
compatible = "xlnx,xps-intc-1.00.a";
interrupt-parent = <&intc>;
interrupts = <0x0 0x1e 0x04>;
reg = <0x41810000 0x10000>;
interrupt-controller;
#interrupt-cells = <2>;
xlnx,kind-of-intr = <0x0>;
xlnx,num-intr-inputs = <0x1>;
};
However, from what I can tell, the xilinx_intc_of_init function is never called during startup. I added a pr_info at the beginning of the function, but I never see it called.
The only message that seems to indicate an issue is:
[ 0.177772] irq: no irq domain found for /amba/xil_intc#41810000 !
I have successfully written and compiled other device drivers, added entries in the device tree, and had them load up and show in dmesg, but for some reason I can't get this one to work.
Any suggestions on debugging this?
For what it's worth, I'm compiling the driver into a kernel module, installing using modules_install and have added an entry to /etc/modules to load it on startup.
Edit: I am using a 4.6 kernel.
IRQCHIP_DECLARE(xilinx_intc_xps, "xlnx,xps-intc-1.00.a", xilinx_intc_of_init);
The driver irq-xilinx-intc.c is using the above call to register with the irq subsystem.
If irq driver uses IRQCHIP_DECLARE macro it will follow the below sequence to invoke the xilinx_intc_of_init() (callback function)
start_kernel() –> init_IRQ() --> irqchip_init() --> of_irq_init() --> call-back function (xilinx_intc_of_init)
If The Driver was registered using IRQCHIP_DECLARE it must be compiled into the kernel and the callback function will be invoked at kernel boot time.
It doesn't work as kernel modules/overlay like other device drivers.
I came to the conclusion that loading the irqchip driver from /lib/modules was too late in the boot process.
I ended up rebuilding the entire kernel with a modified configuration to include this driver, and then the initialization worked properly.
I guess the lesson is that IRQCHIP drivers must be compiled into the kernel and don't work as kernel modules like other device drivers? I can't find any documentation that says so, but that's the behavior I observed.
I am facing an interesting problem. I like to set a pin of my cpu. So far I created a module for controlling the pin. This module is setting the default pin configuration(high). The default pin configuration becomes active when the module is loaded. That is working fine.
Now I like to enable my default pin configuration right away from the start of boot process. To archive that I wrote a patch for the ATMEL bootloader(at91bootstrap) and the uboot(u-boot-at91). This works fine till the moment the kernel starts. At the kernel start the pins is set to the ATMEL default. They pins are defined in the device tree as gpios.
I think I have two possibilities:
1st - patch the /init/main.c at the "right" spot. Where is the right spot?
2nd - modify the device tree to set the GPIOs to an other default value. How to "re set" an gpio a value?
When you modify the pin in u-boot level; it will be override by the kernel. /init/main.c is worse idea and so I think you should not followed this approach
In kernel level if you you want to set the GPIO pin, there are two possibilities (apart from your module)
1) Kernel board file.
2) Device tree file.
Suppose if you want to set the SDIO pin as GPIO in i.MX6 board then you need to modify the code like this.
leds {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpio_leds>;
red {
gpios = <&gpio7 0 0>;
default-state = "on";
};
MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x1b0b0 //set the pin as GPIO
For the default state of the pin please refer the datasheet of your processor. And one pin may have different functionalities.
And if you want to use the same pin for some other functionalities instead of GPIO then you can choose on of the below configuration.
MX6QDL_PAD_SD3_DAT5__UART2_RX_DATA //UART2 RX Data pin
MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 //As a GPIO pin
MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA // UART1 RX data
MX6QDL_PAD_SD3_DAT6__SD3_DATA6 //SDIO pin which is default in this case
Please add more details if you are looking for something more (like pin number and device tree file name).