| /* |
| * SPDX-License-Identifier: BSD-2-Clause |
| * |
| * Copyright (c) 2025 SiFive Inc. |
| */ |
| |
| #include <libfdt.h> |
| #include <sbi/sbi_console.h> |
| #include <sbi/sbi_error.h> |
| #include <sbi/sbi_heap.h> |
| #include <sbi_utils/cache/fdt_cache.h> |
| #include <sbi_utils/fdt/fdt_driver.h> |
| |
| /* List of FDT cache drivers generated at compile time */ |
| extern const struct fdt_driver *const fdt_cache_drivers[]; |
| |
| int fdt_cache_add(const void *fdt, int noff, struct cache_device *dev) |
| { |
| int rc; |
| |
| dev->id = noff; |
| sbi_strncpy(dev->name, fdt_get_name(fdt, noff, NULL), sizeof(dev->name) - 1); |
| sbi_dprintf("%s: %s\n", __func__, dev->name); |
| |
| rc = fdt_next_cache_get(fdt, noff, &dev->next); |
| if (rc == SBI_ENOENT) |
| dev->next = NULL; |
| else if (rc) |
| return rc; |
| |
| return cache_add(dev); |
| } |
| |
| static int fdt_cache_add_generic(const void *fdt, int noff) |
| { |
| struct cache_device *dev; |
| int rc; |
| |
| dev = sbi_zalloc(sizeof(*dev)); |
| if (!dev) |
| return SBI_ENOMEM; |
| |
| rc = fdt_cache_add(fdt, noff, dev); |
| if (rc) { |
| sbi_free(dev); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| static int fdt_cache_find(const void *fdt, int noff, struct cache_device **out_dev) |
| { |
| struct cache_device *dev = cache_find(noff); |
| int rc; |
| |
| if (!dev) { |
| rc = fdt_driver_init_by_offset(fdt, noff, fdt_cache_drivers); |
| if (rc == SBI_ENODEV) |
| rc = fdt_cache_add_generic(fdt, noff); |
| if (rc) |
| return rc; |
| |
| dev = cache_find(noff); |
| if (!dev) |
| return SBI_EFAIL; |
| } |
| |
| if (out_dev) |
| *out_dev = dev; |
| |
| return SBI_OK; |
| } |
| |
| int fdt_next_cache_get(const void *fdt, int noff, struct cache_device **out_dev) |
| { |
| const fdt32_t *val; |
| int len; |
| |
| val = fdt_getprop(fdt, noff, "next-level-cache", &len); |
| if (!val || len < sizeof(*val)) |
| return SBI_ENOENT; |
| |
| noff = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(val[0])); |
| if (noff < 0) |
| return noff; |
| |
| return fdt_cache_find(fdt, noff, out_dev); |
| } |