Linux Device Tree to Hardware Mapping - linux-device-driver

I was wondering how some specific details in the device relate to the hardware and where to find this information(like schematic, datasheet etc).
An example of a usb node is given below:
In the picture above I was wondering how do you find CLK_BUS_OHCI2 or RST_BUS_EHCI2 on the hardware. If you go to the include files you get a value (CLK_BUS_OHCI2 = 39), but I am not sure how that relates to actual hardware. Like which register or which pin etc.

Nowadays I'm working with a similar platform, Allwinner A64 and I'm trying to understand how DTS, clocks, resets work. Fortunately your question came up in the search engine results. Gaurav Pathak's answer clarified most of the things for me and I want to extend a little bit from his point to help those who are trying to fill in the gap about bridging DTS and hardware.
I will refer to Linux kernel 5.7 and Allwinner A64 User Manual (v1.1).
There's a resemblance between uart4 and ehci2 in the means of node definition; for instance, we have property of clocks and resets in both of them. But ccu clock output usage were implemented different.
Let's take a closer look at uart4 node.
uart4: serial#1c29000 {
compatible = "snps,dw-apb-uart";
reg = <0x01c29000 0x400>;
interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
reg-io-width = <4>;
clocks = <&ccu CLK_BUS_UART4>;
resets = <&ccu RST_BUS_UART4>;
status = "disabled";
};
Now we already know that CLK_BUS_UART4 is included from include/dt-bindings/clock/sun50i-a64-ccu.h.
<&ccu CLK_BUS_UART4>
A further search for CLK_BUS_UART4 reveals that include/dt-bindings/clock/sun50i-a64-ccu.h is also indirectly (through drivers/clk/sunxi-ng/ccu-sun50i-a64.h) included from drivers/clk/sunxi-ng/ccu-sun50i-a64.c.
Then it was possible to refer to CLK_BUS_UART4 like below (L807 # ccu-sun50i-a64.c)
[CLK_BUS_UART4] = &bus_uart4_clk.common.hw
But what is bus_uart4_clk? Let's see.
It is defined at L381 # ccu-sun50i-a64.c. This is how it looks:
static SUNXI_CCU_GATE(bus_uart4_clk, "bus-uart4", "apb2",
0x06c, BIT(20), 0);
SUNXI_CCU_GATE is a macro to manage gating register of Clock Control Unit. 4th argument, 0x06c refers to the register offset and 5th argument BIT(20) implies the Nth bit (20, in this case) at the offset of 0x06c is adjusted to drive bus_uart4_clk.
Same thing applies for the resets property. To give a specific example; just search for RST_BUS_UART4 and 0x2d8 at Linux kernel source then look at the bottom of Page 142 of the A64 User Manual.
I hope there were not too much nonsense... Please correct me if I'm wrong.

Well, as far as I know in the below structure
ehci2: usb#01c1c000 {
compatible = "allwinner,sun8i-h3-ehci", "generic-ehci";
reg = <0x01c1c000 0x100>;
interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_BUS_EHCI2>, <&ccu CLK_BUS_OHCI2>;
resets = <&ccu RST_BUS_EHCI2>, <&ccu RST_BUS_OHCI2>;
phys = <&usbphy 2>;
phy-names = "usb";
status = "disabled";
};
clocks = <&ccu CLK_BUS_EHCI2>, <&ccu CLK_BUS_OHCI2>; represents consumer clocks i.e. input clocks and is called "phandle + clock specifier pairs". As you mentioned CLK_BUS_OHCI2 has value 39, that means USB controller will take input clock from the output 39 of ccu clock source.
In the dtsi file from where you posted the above screenshot, there should be a structure that defines the ccu for example like this below:
ccu: clk#01c20060 {
#clock-cells = <1>;
compatible = "allwinner,sun7i-a20-ahb-gates-clk";
reg = <0x01c20060 0x8>;
clocks = <&ahb>;
clock-output-names = "ahb_usb0", "ahb_ehci0",
"ahb_ohci0", "ahb_ehci1", "ahb_ohci1",
"ahb_ss", "ahb_dma", "ahb_bist", "ahb_mmc0",
"ahb_mmc1", "ahb_mmc2", "ahb_mmc3", "ahb_ms",
"ahb_nand", "ahb_sdram", "ahb_ace",
"ahb_emac", "ahb_ts", "ahb_spi0", "ahb_spi1",
"ahb_spi2", "ahb_spi3", "ahb_sata",
"ahb_hstimer", "ahb_ve", "ahb_tvd", "ahb_tve0",
"ahb_tve1", "ahb_lcd0", "ahb_lcd1", "ahb_csi0",
"ahb_csi1", "ahb_hdmi1", "ahb_hdmi0",
"ahb_de_be0", "ahb_de_be1", "ahb_de_fe0",
"ahb_de_fe1", "ahb_gmac", "ahb_ehci2",
"ahb_mali";
};
In the above structure there are multiple clock source outputs, so clock source 39 should be utilised by the above USB controller for taking clock input, note that #clock-cells = <1>; represents multiple clock output and #clock-cells = <0>; is for single clock output.
The ccu structure is just an example.

Related

What's the difference between of clk_get and of_clk_get? (linux)

I ask it here to check if my guess is correct.
This page says
Currently we have devm_clk_get() which gives a managed-resource clk
(by name), or of_clk_get() which gives an unmanaged resource clk (by
id).
So I guess clk_get is selecting the clock using 'clock-names' (which is input to a clock consumer device) in the device tree, and of_clk_get is for selecting the clock using 'clock id' (which is the first number in the index-name pair of the clock input specifier.
And I see these lines in a driver drivers/tty/serial/bcm63xx_uart.c (linux 5.15.68).
clk = clk_get(&pdev->dev, "refclk"); // first try
if (IS_ERR(clk) && pdev->dev.of_node)
clk = of_clk_get(pdev->dev.of_node, 0); // second try
It looks like it first try to get the input clock from the name "refclk" and if it fails, it trys to get the clock from the clock speicifer, 0 meaning the first input clock. Is my understanding correct? Are both tries using device tree information?
I see in arch/arm64/boot/dts/broadcom/bcm4908/bcm4908.dtsi,
clocks {
periph_clk: periph_clk {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <50000000>;
clock-output-names = "periph";
};
};
uart0: serial#640 {
compatible = "brcm,bcm6345-uart";
reg = <0x640 0x18>;
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&periph_clk>;
clock-names = "refclk";
status = "okay";
};

Unreset GPIO Pins While System Resetting

Is it possible to unreset some GPIO pins while using NVIC_SystemReset function at STM32 ?
Thanks in advance for your answers
Best Regards
I try to reach NVIC_SystemReset function. But not clear inside of this function.
Also this project is running on KEIL
Looks like it is not possible, NVIC_SystemReset issues a general reset of all subsystems.
But probably, instead of system reset, you can just reset all peripheral expect one you need keep working, using peripheral reset registers in Reset and Clock Control module (RCC): RCC_AHB1RSTR, RCC_AHB2RSTR, RCC_APB1RSTR, RCC_APB1RSTR.
Example:
// Issue reset of SPI2, SPI3, I2C1, I2C2, I2C3 on APB1 bus
RCC->APB1RSTR = RCC_APB1RSTR_I2C3RST | RCC_APB1RSTR_I2C2RST | RCC_APB1RSTR_I2C1RST
| RCC_APB1RSTR_SPI3RST | RCC_APB1RSTR_SPI2RST;
__DMB(); // Data memory barrier
RCC->APB1RSTR = 0; // deassert all reset signals
See detailed information in RCC registers description in Reference Manual for your MCU.
You may also need to disable all interrupts in NVIC:
for (int i = 0 ; i < 8 ; i++) NVIC->ICER[i] = 0xFFFFFFFFUL; // disable all interrupts
for (int i = 0 ; i < 8 ; i++) NVIC->ICPR[i] = 0xFFFFFFFFUL; // clear all pending flags
if you want to restart the program, you can reload stack pointer to its top value, located at offset 0 of the flash memory and jump to the start address which is stored at offset 4. Note: flash memory is addressed starting from 0x08000000 address in the address space.
uint32_t stack_top = *((volatile uint32_t*)FLASH_BASE);
uint32_t entry_point = *((volatile uint32_t*)(FLASH_BASE + 4));
__set_MSP(stack_top); // set stack top
__ASM volatile ("BX %0" : : "r" (entry_point | 1) ); // jump to the entry point with bit 1 set

how is 'stream ID' or 'iommu specifier' determined in PCIe root complex mode?

This is from Documentation/devicetree/bindings/pci/pci-iommu.txt (linux-5.10.0)
PCI root complex
================
Optional properties
-------------------
- iommu-map: Maps a Requester ID to an IOMMU and associated IOMMU specifier
data.
The property is an arbitrary number of tuples of
(rid-base,iommu,iommu-base,length).
Any RID r in the interval [rid-base, rid-base + length) is associated with
the listed IOMMU, with the IOMMU specifier (r - rid-base + iommu-base).
- iommu-map-mask: A mask to be applied to each Requester ID prior to being
mapped to an IOMMU specifier per the iommu-map property.
Example (1)
===========
/ {
#address-cells = <1>;
#size-cells = <1>;
iommu: iommu#a {
reg = <0xa 0x1>;
compatible = "vendor,some-iommu";
#iommu-cells = <1>;
};
pci: pci#f {
reg = <0xf 0x1>;
compatible = "vendor,pcie-root-complex";
device_type = "pci";
/*
* The sideband data provided to the IOMMU is the RID,
* identity-mapped.
*/
iommu-map = <0x0 &iommu 0x0 0x10000>;
};
};
I can't understand the meaning of Any RID r in the interval [rid-base, rid-base + length) is associated with the listed IOMMU, with the IOMMU specifier (r - rid-base + iommu-base). I understand this iommu-map specifies the relationship between the master device (requesting transaction from the PCIe bus to this root complex) and the assigned iommu for the device. This example says, rid 0 ~ 0x10000 is assigned to iommu specifier 0 ~ 0x10000. The document says rid is formed by {bus,device,function} (16 bits).
How is this single number iommu specifier determined by the PCIe core device? Is it device dependent(with the driver's settig)? In arm64 case, the smmu-v3(arm's type of iommu) needs stream ID (and substream ID which is mapped from PASID in PCIe TLP) and when this #iommu-cells is 1, I don't know how this stream ID is passed from the PCIe core device to smmu-v3. If anyone could clarify it, it would be much grateful.

Hardware interrupt between Raspberry pi and Atmega 328

I have connected my RPI and atmega328 together in order to control the start of an event on my arduino. In order to do so, GPIO 25 (RPI) is connected directly to pin7 (Arduino PD7). I've got a python script on the RPI witch set the GPIO 25 to high then back to LOW:
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(25, GPIO.OUT)
GPIO.output(25, 1)
#Do some stuff
GPIO.output(25, 0)
The arduino is waiting in a loop for either a physical button to be pressed or the pin7 to be set to HIGH by the RPI:
const int interrupt = 7;
const int button = 13;
const int led = 9;
void setup() {
Serial.begin(9600);
pinMode(interrupt, INPUT);
pinMode(button,INPUT);
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
}
void loop() {
bool on = false;
bool buttonOn = false;
while (!on || !buttonOn) {
on = digitalRead(interrupt);
buttonOn = digitalRead(button);
digitalWrite(led, LOW);
}
digitalWrite(led, HIGH);
delay(1000);
}
Now unfortunately this doesn't work. I have checked the logic level of the atmega328 (https://learn.sparkfun.com/tutorials/logic-levels) and it seems that 3.3V is good enough for HIGH signal.
Am I missing something with the pull up /pull down resistance? I know the PD7 on the atmega is specified as follow:
Port D is an 8-bit bi-directional I/O port with internal pull-up
resistors (selected for each bit). The Port D output buffers have
symmetrical drive characteristics with both high sink and source
capability. As inputs, Port D pins that are externally pulled low will
source current if the pull-up resistors are activated. The Port D pins
are tristated when a reset condition becomes active, even if the clock
is not running.
EDIT:
I have done more testing and I am getting the HIGH or LOW value correctly. It seems that the issue comes from the:
while ((!on) || (!buttonOn)) {
Is there an issue with Arduino and the OR operator in a while loop? Even when one condition is true and the other one is false, it never goes out of the loop.
while ((!on) || (!buttonOn)) {
}
That loop will run as long as one of the variables is false.
Yesterday I was for some reason thinking that you were reading the interrupt pin twice when reading your code.
3.3 v output should be ok to turn the Arduino input high.
You can have a wiring problem or your raspberry pi can be so fast that the arduino misses the pulse.
Change your program on the raspberry pi to leave the output high for so long (e.g. 10) seconds that you can measure it with a multimeter to see that you are setting the right pin.
Does the Arduino now see the input?

What is cache size and cache line size?

I am trying to understand the following from a DTS file. Am very new to OS/Kernel.
cpus {
#address-cells = <1>;
#size-cells = <0>;
PowerPC,8313#0 {
device_type = "cpu";
reg = <0x0>;
d-cache-line-size = <32>;
i-cache-line-size = <32>;
d-cache-size = <16384>;
i-cache-size = <16384>;
timebase-frequency = <0>;
bus-frequency = <0>;
clock-frequency = <0>;
};
};
Can anyone provide brief explanation of the above?
I understand the following.
cache block size or cache line size: the amount of data that gets transferred on a cache miss.
instruction cache (I-cache): cache that can only hold instructions.
data cache (D-cache): cache that can only hold data.
Also what does i-cache-line-size mean?
d-cache-line-size = <32>;
i-cache-line-size = <32>;
d-cache-size = <16384>;
i-cache-size = <16384>;
In certain dts files there are comments like from boot loader as follows.
cpus {
#address-cells = <1>;
#size-cells = <0>;
PowerPC,8313#0 {
device_type = "cpu";
reg = <0x0>;
d-cache-line-size = <32>;
i-cache-line-size = <32>;
d-cache-size = <16384>;
i-cache-size = <16384>;
timebase-frequency = <0>; // from bootloader
bus-frequency = <0>; // from bootloader
clock-frequency = <0>; // from bootloader
};
};
How do i find out from which file in bootloader?
Bootloader used is U-boot.
Thanks.
The DTS snippet describes the PowerPC 8313 CPU.
From the Datasheet of PowerPC 8313,
7.1.5.2 Cache Units The e300c3 provides 16-Kbyte, four-way set-associative instruction and data caches. The cache block is 32
bytes long ...
Further more,
7.1.6 Bus Interface Unit (BIU) Because the caches are on-chip, write-back caches, the most common transactions are burst-read memory
operations, burst-write memory operations, ...
... Memory accesses can occur in single-beat (1–8 bytes) and four-beat burst (32 bytes) data transfers on the 64-bit data bus.
Essentially the DTS snippet configures :
the cache-size (to 16KB)
to make complete use of the on-board cache
the line-size (to 32 bytes)
to effectively use the BIU for fastest possible transfers between the CPU and the on-chip cache.
Update: Regarding your query about where to start?...
The BEST book to start with is always the Technical Reference Manual of the processor in question. It will be filled with lot of jargon specific to your hardware that you will need to patiently go through and understand.
In parallel start brushing-up your understanding of the Linux Kernel with books like "Linux Device Drivers 3e", "Understanding the Linux Kernel" and "Professional Linux Kernel Architecture".
The boot-loaders are usually written with the hardware in mind and implemented using similar semantics as the Linux Kernel i.e. borrow heavily from it. Tons of stuff is available in random blogs all over the internet. Being active on Stackoverflow and mailing-lists like kernelnewbies is a good way to find them on regular basis.