Hans W. Heckel Hans W. Heckel - 6 months ago 35
Linux Question

How to map resources of two devices in one Linux kernel driver using device tree?

I am writing a Linux kernel driver for a watchdog using the CPU’s internal registers and a dedicated location in (persistent) RAM for storing debug information in case of a watchdog-triggered reset.

In the old world, I had two resources of type

IORESOURCE_MEM
in my board file: the internal registers (base address of internal regs plus offset) and the location in RAM (absolute memory address).

I could access these via
platform_get_resource()
with the appropriate index, and then I could remap.

In the new world featuring a device tree, my node for the watchdog is located in the domain of internal CPU registers and I can access them without problems.

But how do I add my memory location in RAM? The base address of RAM is different from the CPU-internal registers, so I can’t just add another range to the “reg” entry (like
reg = <0x20300 0x408>, <0x3BFFE000 0x1000>;
). Do I need to add a new “device” like this, maybe?

PRAM {
#address-cells = <1>;
#size-cells = <1>;
PRAM0: Watchdog_Crash_Debug_Context {
reg = <0x3BFFE000 0x1000>;
};
};


But how do I reference it in the watchdog device driver? Or in the watchdog device tree entry?

Linux version is 4.1.18.

Answer

In the meantime I have found the solution myself. The secret was the magic function of_parse_phandle().

Device Tree setup:

soc {
    ...
    internal-regs {
        ...
        watchdog_global {
            ...
            PRAM_address = <&PRAM0>;
        };
    };
};


PRAM {
    #address-cells = <1>;
    #size-cells = <1>;
    ranges = <0 0 0x3b000000 0x5000000>;

    PRAM0: Watchdog_Crash_Debug_Context {            
        reg = <0xFFE000 0x1000>;
    };
};

Usage in code (really simple!):

struct device_node *PRAM_node = of_parse_phandle(pdev->dev.of_node, "PRAM_address", 0);
debug_info = of_iomap(PRAM_node, 0);
of_node_put(PRAM_node);

where pdev->dev.of_node refers to device tree node watchdog_global and debug_info is a pointer to a self-defined struct.