[Linux] Utilizing Driver ID Information and DTS
U-BOOT Device Driver
As of this writing, the structure of modern U-BOOT is similar to that of the Linux kernel.
To cover various chips, the evolution of drivers
must also proceed in a direction that supports multiple chipsets.
To develop a minimal driver that applies a single interface
across diverse environments (SoCs),
techniques for distinguishing between different SoCs are crucial.
This is not unique to U-BOOT drivers.
From the driver’s perspective, the specific SoC currently in use is meaningless;
it simply needs to parse specific information and call the appropriate functions.
Parsing the Device Tree
The Linux kernel introduced the device tree
to efficiently manage the growing number of devices.
U-BOOT has followed the kernel’s lead.
Commands are executed based strictly on the parsed information from device tree properties.
Below are a few examples.
(The kernel also has APIs that serve similar roles.)
In U-BOOT, there is the DTS parsing API dev_read_TYPE.
...
#address-cells = <1>;
#size-cells = <1>;
...
foo@71004000 {
compatible = "steve,foo";
regs = <0x71004000 0x4>;
clock = <&clk_peri PERI_TCT>;
clock-frequency = "1000000";
};
uint32_t reg;
uint32_t freq;
reg = dev_read_addr_index(dev, 0);
freq = dev_read_u32(dev, "clock-frequency");
In this case, 0x71004000 is stored in reg, and 1000000 is stored in freq.
Parsing the udevice ID
A mapping table is required to distinguish devices.
In U-BOOT, the udevice_id type is widely used.
In the mapping table, devices are primarily distinguished by compatible and data.
struct udevice_id {
const char *compatible;
ulong data;
};
static const struct udevice_id bar_ids[] = {
{ .compatible = "steve,foo", .data = FOO_DATA },
{ }
};
U_BOOT_DRIVER(bar) = {
.name = "bar",
.id = UCLASS_BAR,
.of_match = bar_ids,
.ops = &bar_ops,
.probe = bar_probe,
.of_to_plat = bar_ofdata_to_platdata,
};
The compatible property distinguishes the driver,
while the data property can distinguish behaviors within the same driver.
To utilize the data property, you can use dev_get_driver_data(dev)
within the function connected to of_to_plat.
struct foo_dev {
int data;
};
static int bar_ofdata_to_platdata(udevice dev)
{
struct foo_dev *foo = dev_get_priv(dev);
...
foo->data = dev_get_driver_data(dev);
}
dev_get_priv is an API that retrieves the private information of the udevice.
- Getting the driver_data during the driver probe phase is also perfectly fine.
Leave a comment