| /* | |
| * SPDX-License-Identifier: BSD-2-Clause | |
| * | |
| * Copyright (c) 2025 SiFive Inc. | |
| */ | |
| #include <libfdt.h> | |
| #include <sbi/sbi_error.h> | |
| #include <sbi/sbi_scratch.h> | |
| #include <sbi_utils/cache/fdt_cache.h> | |
| #include <sbi_utils/cache/fdt_cmo_helper.h> | |
| #include <sbi_utils/fdt/fdt_helper.h> | |
| static unsigned long flc_offset; | |
| #define get_hart_flc(_s) \ | |
| sbi_scratch_read_type(_s, struct cache_device *, flc_offset) | |
| #define set_hart_flc(_s, _p) \ | |
| sbi_scratch_write_type(_s, struct cache_device *, flc_offset, _p) | |
| int fdt_cmo_private_flc_flush_all(void) | |
| { | |
| struct cache_device *flc = get_hart_flc(sbi_scratch_thishart_ptr()); | |
| if (!flc || !flc->cpu_private) | |
| return SBI_ENODEV; | |
| return cache_flush_all(flc); | |
| } | |
| int fdt_cmo_llc_flush_all(void) | |
| { | |
| struct cache_device *llc = get_hart_flc(sbi_scratch_thishart_ptr()); | |
| if (!llc) | |
| return SBI_ENODEV; | |
| while (llc->next) | |
| llc = llc->next; | |
| return cache_flush_all(llc); | |
| } | |
| int fdt_cmo_private_flc_enable(bool enable) | |
| { | |
| struct cache_device *flc = get_hart_flc(sbi_scratch_thishart_ptr()); | |
| if (!flc || !flc->cpu_private) | |
| return SBI_ENODEV; | |
| return cache_enable(flc, enable); | |
| } | |
| int fdt_cmo_llc_enable(bool enable) | |
| { | |
| struct cache_device *llc = get_hart_flc(sbi_scratch_thishart_ptr()); | |
| if (!llc) | |
| return SBI_ENODEV; | |
| while (llc->next) | |
| llc = llc->next; | |
| return cache_enable(llc, enable); | |
| } | |
| static int fdt_cmo_cold_init(const void *fdt) | |
| { | |
| struct sbi_scratch *scratch; | |
| struct cache_device *dev; | |
| int cpu_offset, cpus_offset, rc; | |
| u32 hartid; | |
| cpus_offset = fdt_path_offset(fdt, "/cpus"); | |
| if (cpus_offset < 0) | |
| return SBI_EINVAL; | |
| fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) { | |
| rc = fdt_parse_hart_id(fdt, cpu_offset, &hartid); | |
| if (rc) | |
| continue; | |
| scratch = sbi_hartid_to_scratch(hartid); | |
| if (!scratch) | |
| continue; | |
| rc = fdt_next_cache_get(fdt, cpu_offset, &dev); | |
| if (rc && rc != SBI_ENOENT) | |
| return rc; | |
| if (rc == SBI_ENOENT) | |
| dev = NULL; | |
| set_hart_flc(scratch, dev); | |
| } | |
| return SBI_OK; | |
| } | |
| static int fdt_cmo_warm_init(void) | |
| { | |
| struct cache_device *cur = get_hart_flc(sbi_scratch_thishart_ptr()); | |
| int rc; | |
| while (cur) { | |
| if (cur->ops && cur->ops->warm_init) { | |
| rc = cur->ops->warm_init(cur); | |
| if (rc) | |
| return rc; | |
| } | |
| cur = cur->next; | |
| } | |
| return SBI_OK; | |
| } | |
| int fdt_cmo_init(bool cold_boot) | |
| { | |
| const void *fdt = fdt_get_address(); | |
| int rc; | |
| if (cold_boot) { | |
| flc_offset = sbi_scratch_alloc_type_offset(struct cache_device *); | |
| if (!flc_offset) | |
| return SBI_ENOMEM; | |
| rc = fdt_cmo_cold_init(fdt); | |
| if (rc) | |
| return rc; | |
| } | |
| rc = fdt_cmo_warm_init(); | |
| if (rc) | |
| return rc; | |
| return SBI_OK; | |
| } |