aboutsummaryrefslogtreecommitdiff
path: root/lib/utils/cache/fdt_cache.c
blob: 2a9e8a6dd53c008eec8f319d0e19c56ea1d11a1a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/*
 * 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);
}