1. Arm device tree and device
driver initialization
Houcheng Lin
CCMA, ITRI
2. Agenda
• Problem: one kernel source to support all arm
boards
• Boot with device tree binary
• Device tree syntax
• Machine init code
• Device init code
• x86 platform (one kernel binary support
almost all boards)
3. Problem:
One kernel source to support all arm boards
OS kernel
memory,
mmu
timer Block
device
net
device
CPU,
cache
Hardware HAL
hardware board
Kernel
source
build
select board
4. Kernel startup code have to
initialize these hardware
• Startup code initialize following hardware
– CPU, cache, MMU (usually comes with feature reg)
– exception table, GDT, then perform mode switching
and enable MMU
– Interrupt controller, bridge chip
– timers, RTC, flash
– IO, block, network and optional devices
• Its purpose is kernel want to use them to
– enable some kernel feature
– convert into usable kernel resources
arch
machine
board
5. One kernel source support all boards,
a solution
• Horizontal and vertical divide these
code
– arch specific include and source
– machine specific include and source
– board specific include and source
• Pick one board
– Header and Makefile links defines
and objects
– One board header file that links
arch/ machine specific headers
– One board Makefile that links arch/
machine specific sources’ objects
• Kernel config file
– Generate header file and Makefile
include for needed kernel feature or
drivers
arm
exynos tegra
ve eb aa bb
7. Linux arm community’s initial work
• As hardware more powerful and support MMU,
Linux add support to arm
• Linux ARM community starts to write codes and
use this approach to support all arm boards
• Some problem when lots of arm boards’ code
committed into kenrel:
– huge number of #defines in headers
– code is hard to maintain
• redundant code
• stepping on each others toes (guys uses different names)
• merge conflicts
8. Linus comment on 2001
I don't know who to "blame". I don't care. It really
doesn't matter.
you guys need to push back on the people sending
you crap
Gaah. Guys, this whole ARM thing is a f*cking pain
in the ass
steping on others toes
That usb_musb_init() thing in
arch/arm/mach-omap2/usb-musb.c
also seems to be totally insane.
Note: the layered and division approach can’t not
handle various arm hardware and corresponding, existing sources
stop the crazy
renaming already!
crap
ARM vendors do crazy shit
9. ARM device tree
• Device tree is a data structure for describing hardware
• It extract board level detail from kernel source code to
device tree script (dts)
• Each board has one dts file
• Benefit
– moved hardware relevant #defines to dts
– keep driver or init code one one copied; describe
variations in dts file. EX: memory mapped address,
interrupt number, active-low, output pin number, pefered
phy interface, etc
– Now, single kernel binary can support multiple similar
boards
12. Device Tree Binary pass to kernel
• A device tree is passed to the kernel at boot
time, kernel reference it during initialization
r0: 0
r1: architecture ID
r2: pointer to DTB
reference [1]
13. machine-init code with device tree
1. kernel run compatible machine-init()
via compatible string match
2. Machine-init() create devices under
system bus. Bus is also match by
compatible string.
…..
……
static void __init v2m_dt_init(void)
{
l2x0_of_init(0x00400000, 0xfe0fffff);
of_platform_populate(NULL, v2m_dt_bus_match, NULL, NULL);
}
static const char * const v2m_dt_match[] __initconst = {
"arm,vexpress",
NULL,
};
DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express")
.dt_compat = v2m_dt_match,
.smp = smp_ops(vexpress_smp_ops),
.smp_init = smp_init_ops(vexpress_smp_init_ops),
.map_io = v2m_dt_map_io,
.init_early = v2m_dt_init_early,
.init_machine= v2m_dt_init,
MACHINE_END
arch/arm/mach-vexpress/v2m.c
arch/arm/mach-ooo/v3m.c
…..
……
It creates devices
under this
compatible bus
struct of_device_id v2m_dt_bus_match[] = {
{ .compatible = "simple-bus", },
{ .compatible = "arm,amba-bus", },
{ .compatible = "arm,vexpress,config-bus", },
{}
};
1.
2.
14. Device-init code with device tree
1. Kernel runs all modules’ module-init
function
2. The module-init function register
platfrom driver
3. For every created platform device, it
call its platform driver’s probe
function
4. In probe, can access properties in
device tree node and create a Linux
kernel device object with private data
#ifdef CONFIG_OF
static const struct of_device_id gdr_match[] = {
{ .compatible = "defer-reset" },
};
MODULE_DEVICE_TABLE(of, gdr_match);
#endif
static struct platform_driver gdr_driver = {
.probe = gdr_probe,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(gdr_match),
}
};
static int __init gdr_init(void)
{
….
platform_driver_register(&gdr_driver);
}
module_init(gdr_init);
int gdr_probe(struct platform_device *pdev_gdr) {
…..
of_property_read_u32(of_node, “duration”, &duration);
…..
}
1.
2.
3.
4.
15. How x86 platform works ?
• In x86, one single kernel binary can support all hardware
platforms. How can do it ?
• BIOS hides the low level hardware detail
– BIOS initialize low level (memory, cpu, clock, voltage, current, … )
– BIOS provide memory size and hard disk information to Linux
boot-loader
• Smart bus enumerate optional devices
(PCI bridge or USB root hub )
– enumerate devices to Linux kernel
– get device identity and resource requirement
– optional device’s driver may encapsulated as module and
initialized in ramdisk stage
• PC industry is much more standardized than arm hardware
$$
$$
16. X86 Platform boot, real mode
(boot-loader)
(compressed kernel)
loaded by boot-loader
http://duartes.org/gustavo/blog/post/kernel-boot-process/
17. Access >1MB in real mode
The shuffling of the kernel back and forth in memory is to overcome
limitations of the PC BIOS memory addressability (640k), and free up several
hundred kilobytes of system memory (the actual amount freed is reported by
the system). The 4k is used for handling virtual memory. The special load
instructions (trampoline) are required to 'cheat' the system, as the
instructions load part of the kernel into memory locations beyond the 640k
barrier that the (then currently running "real mode") system knows about. In
the process of shuffling the kernel around, the memory originally written to
by the BIOS, and also where the setup and system programs were loaded are
overwritten by the kernel image, hence the need for moving them to a safe
place, beyond where the uncompressed kernel image is loaded. The actual
load is slightly more complicated on x86 systems as not all BIOSs report their
memory on the same registers, report information in different ways, or map
their memory/system resources in unconventional ways. Also, some BIOSs
must be prodded for information several times or have A20 Gate problems.
http://en.wikibooks.org/wiki/The_Linux_Kernel/Booting