diff options
author | Emil Renner Berthing <kernel@esmil.dk> | 2021-06-13 02:48:31 +0300 |
---|---|---|
committer | Emil Renner Berthing <emil.renner.berthing@canonical.com> | 2022-10-25 18:36:45 +0300 |
commit | b61c2bc9dc06cadacfdceadc4a6ee6b2f4976c0e (patch) | |
tree | 2e0bf6c5fc2caede9a8cd7076be1152e5caf5af6 | |
parent | 00674134e2e7570188d4b1041953f0d07490a99e (diff) | |
download | linux-b61c2bc9dc06cadacfdceadc4a6ee6b2f4976c0e.tar.xz |
soc: sifive: l2 cache: Add non-coherent DMA handling
Add functions to flush the caches and handle non-coherent DMA.
Signed-off-by: Emil Renner Berthing <kernel@esmil.dk>
-rw-r--r-- | drivers/soc/sifive/sifive_l2_cache.c | 57 | ||||
-rw-r--r-- | include/soc/sifive/sifive_l2_cache.h | 21 |
2 files changed, 77 insertions, 1 deletions
diff --git a/drivers/soc/sifive/sifive_l2_cache.c b/drivers/soc/sifive/sifive_l2_cache.c index 650db25d4089..da35f2c7b1d1 100644 --- a/drivers/soc/sifive/sifive_l2_cache.c +++ b/drivers/soc/sifive/sifive_l2_cache.c @@ -5,12 +5,14 @@ * Copyright (C) 2018-2019 SiFive, Inc. * */ +#include <linux/align.h> #include <linux/debugfs.h> #include <linux/interrupt.h> #include <linux/of_irq.h> #include <linux/of_address.h> #include <linux/device.h> #include <asm/cacheinfo.h> +#include <asm/page.h> #include <soc/sifive/sifive_l2_cache.h> #define SIFIVE_L2_DIRECCFIX_LOW 0x100 @@ -29,11 +31,15 @@ #define SIFIVE_L2_DATECCFAIL_HIGH 0x164 #define SIFIVE_L2_DATECCFAIL_COUNT 0x168 +#define SIFIVE_L2_FLUSH64 0x200 +#define SIFIVE_L2_FLUSH32 0x240 + #define SIFIVE_L2_CONFIG 0x00 #define SIFIVE_L2_WAYENABLE 0x08 #define SIFIVE_L2_ECCINJECTERR 0x40 #define SIFIVE_L2_MAX_ECCINTR 4 +#define SIFIVE_L2_LINE_SIZE 64 static void __iomem *l2_base; static int g_irq[SIFIVE_L2_MAX_ECCINTR]; @@ -117,6 +123,47 @@ int unregister_sifive_l2_error_notifier(struct notifier_block *nb) } EXPORT_SYMBOL_GPL(unregister_sifive_l2_error_notifier); +#ifdef CONFIG_RISCV_DMA_NONCOHERENT +static phys_addr_t uncached_offset; +DEFINE_STATIC_KEY_FALSE(sifive_l2_handle_noncoherent_key); + +void sifive_l2_flush_range(phys_addr_t start, size_t len) +{ + phys_addr_t end = start + len; + phys_addr_t line; + + if (!len) + return; + + mb(); + for (line = ALIGN_DOWN(start, SIFIVE_L2_LINE_SIZE); line < end; + line += SIFIVE_L2_LINE_SIZE) { +#ifdef CONFIG_32BIT + writel(line >> 4, l2_base + SIFIVE_L2_FLUSH32); +#else + writeq(line, l2_base + SIFIVE_L2_FLUSH64); +#endif + mb(); + } +} +EXPORT_SYMBOL_GPL(sifive_l2_flush_range); + +void *sifive_l2_set_uncached(void *addr, size_t size) +{ + phys_addr_t phys_addr = __pa(addr) + uncached_offset; + void *mem_base; + + mem_base = memremap(phys_addr, size, MEMREMAP_WT); + if (!mem_base) { + pr_err("%s memremap failed for addr %p\n", __func__, addr); + return ERR_PTR(-EINVAL); + } + + return mem_base; +} +EXPORT_SYMBOL_GPL(sifive_l2_set_uncached); +#endif /* CONFIG_RISCV_DMA_NONCOHERENT */ + static int l2_largest_wayenabled(void) { return readl(l2_base + SIFIVE_L2_WAYENABLE) & 0xFF; @@ -200,6 +247,7 @@ static int __init sifive_l2_init(void) int i, rc, intr_num; const struct of_device_id *match; unsigned long broken_irqs; + u64 __maybe_unused offset; np = of_find_matching_node_and_match(NULL, sifive_l2_ids, &match); if (!np) @@ -233,6 +281,13 @@ static int __init sifive_l2_init(void) } } +#ifdef CONFIG_RISCV_DMA_NONCOHERENT + if (!of_property_read_u64(np, "uncached-offset", &offset)) { + uncached_offset = offset; + static_branch_enable(&sifive_l2_handle_noncoherent_key); + } +#endif + l2_config_read(); l2_cache_ops.get_priv_group = l2_get_priv_group; @@ -243,4 +298,4 @@ static int __init sifive_l2_init(void) #endif return 0; } -device_initcall(sifive_l2_init); +arch_initcall_sync(sifive_l2_init); diff --git a/include/soc/sifive/sifive_l2_cache.h b/include/soc/sifive/sifive_l2_cache.h index 92ade10ed67e..2ef41da68347 100644 --- a/include/soc/sifive/sifive_l2_cache.h +++ b/include/soc/sifive/sifive_l2_cache.h @@ -7,10 +7,31 @@ #ifndef __SOC_SIFIVE_L2_CACHE_H #define __SOC_SIFIVE_L2_CACHE_H +#include <linux/io.h> +#include <linux/jump_label.h> + extern int register_sifive_l2_error_notifier(struct notifier_block *nb); extern int unregister_sifive_l2_error_notifier(struct notifier_block *nb); #define SIFIVE_L2_ERR_TYPE_CE 0 #define SIFIVE_L2_ERR_TYPE_UE 1 +DECLARE_STATIC_KEY_FALSE(sifive_l2_handle_noncoherent_key); + +static inline bool sifive_l2_handle_noncoherent(void) +{ +#ifdef CONFIG_SIFIVE_L2 + return static_branch_unlikely(&sifive_l2_handle_noncoherent_key); +#else + return false; +#endif +} + +void sifive_l2_flush_range(phys_addr_t start, size_t len); +void *sifive_l2_set_uncached(void *addr, size_t size); +static inline void sifive_l2_clear_uncached(void *addr, size_t size) +{ + memunmap(addr); +} + #endif /* __SOC_SIFIVE_L2_CACHE_H */ |