Files
opensbi/lib/utils/fdt/fdt_driver.c
Samuel Holland 0ffe265fd9 lib: utils/fdt: Respect compatible string fallback priority
When matching drivers to DT nodes, always match all drivers against the
first compatible string before considering fallback compatible strings.
This ensures the most specific match is always selected, regardless of
the order of the drivers or match structures, as long as no compatible
string appears in multiple match structures.

Fixes: 1ccc52c427 ("lib: utils/fdt: Add helpers for generic driver initialization")
Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
2025-03-24 16:37:06 +05:30

89 lines
2.0 KiB
C

/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2024 SiFive
*/
#include <libfdt.h>
#include <sbi/sbi_console.h>
#include <sbi/sbi_error.h>
#include <sbi_utils/fdt/fdt_driver.h>
#include <sbi_utils/fdt/fdt_helper.h>
int fdt_driver_init_by_offset(const void *fdt, int nodeoff,
const struct fdt_driver *const *drivers)
{
const struct fdt_driver *driver;
const struct fdt_match *match;
int compat_len, prop_len, rc;
const char *compat_str;
if (!fdt_node_is_enabled(fdt, nodeoff))
return SBI_ENODEV;
compat_str = fdt_getprop(fdt, nodeoff, "compatible", &prop_len);
if (!compat_str)
return SBI_ENODEV;
while ((compat_len = strnlen(compat_str, prop_len) + 1) <= prop_len) {
for (int i = 0; (driver = drivers[i]); i++)
for (match = driver->match_table; match->compatible; match++)
if (!memcmp(match->compatible, compat_str, compat_len))
goto found;
compat_str += compat_len;
prop_len -= compat_len;
}
return SBI_ENODEV;
found:
if (driver->experimental)
sbi_printf("WARNING: %s driver is experimental and may change\n",
match->compatible);
rc = driver->init(fdt, nodeoff, match);
if (rc < 0) {
const char *name;
name = fdt_get_name(fdt, nodeoff, NULL);
sbi_printf("%s: %s (%s) init failed: %d\n",
__func__, name, match->compatible, rc);
}
return rc;
}
static int fdt_driver_init_scan(const void *fdt,
const struct fdt_driver *const *drivers,
bool one)
{
int nodeoff, rc;
for (nodeoff = fdt_next_node(fdt, -1, NULL);
nodeoff >= 0;
nodeoff = fdt_next_node(fdt, nodeoff, NULL)) {
rc = fdt_driver_init_by_offset(fdt, nodeoff, drivers);
if (rc == SBI_ENODEV)
continue;
if (rc < 0)
return rc;
if (one)
return 0;
}
return one ? SBI_ENODEV : 0;
}
int fdt_driver_init_all(const void *fdt,
const struct fdt_driver *const *drivers)
{
return fdt_driver_init_scan(fdt, drivers, false);
}
int fdt_driver_init_one(const void *fdt,
const struct fdt_driver *const *drivers)
{
return fdt_driver_init_scan(fdt, drivers, true);
}