1d34599bcSLad Prabhakar // SPDX-License-Identifier: GPL-2.0 2d34599bcSLad Prabhakar /* 3d34599bcSLad Prabhakar * non-coherent cache functions for Andes AX45MP 4d34599bcSLad Prabhakar * 5d34599bcSLad Prabhakar * Copyright (C) 2023 Renesas Electronics Corp. 6d34599bcSLad Prabhakar */ 7d34599bcSLad Prabhakar 8d34599bcSLad Prabhakar #include <linux/cacheflush.h> 9d34599bcSLad Prabhakar #include <linux/cacheinfo.h> 10d34599bcSLad Prabhakar #include <linux/dma-direction.h> 11d34599bcSLad Prabhakar #include <linux/of_address.h> 12d34599bcSLad Prabhakar #include <linux/of_platform.h> 13d34599bcSLad Prabhakar 14d34599bcSLad Prabhakar #include <asm/dma-noncoherent.h> 15d34599bcSLad Prabhakar 16d34599bcSLad Prabhakar /* L2 cache registers */ 17d34599bcSLad Prabhakar #define AX45MP_L2C_REG_CTL_OFFSET 0x8 18d34599bcSLad Prabhakar 19d34599bcSLad Prabhakar #define AX45MP_L2C_REG_C0_CMD_OFFSET 0x40 20d34599bcSLad Prabhakar #define AX45MP_L2C_REG_C0_ACC_OFFSET 0x48 21d34599bcSLad Prabhakar #define AX45MP_L2C_REG_STATUS_OFFSET 0x80 22d34599bcSLad Prabhakar 23d34599bcSLad Prabhakar /* D-cache operation */ 24d34599bcSLad Prabhakar #define AX45MP_CCTL_L1D_VA_INVAL 0 /* Invalidate an L1 cache entry */ 25d34599bcSLad Prabhakar #define AX45MP_CCTL_L1D_VA_WB 1 /* Write-back an L1 cache entry */ 26d34599bcSLad Prabhakar 27d34599bcSLad Prabhakar /* L2 CCTL status */ 28d34599bcSLad Prabhakar #define AX45MP_CCTL_L2_STATUS_IDLE 0 29d34599bcSLad Prabhakar 30d34599bcSLad Prabhakar /* L2 CCTL status cores mask */ 31d34599bcSLad Prabhakar #define AX45MP_CCTL_L2_STATUS_C0_MASK 0xf 32d34599bcSLad Prabhakar 33d34599bcSLad Prabhakar /* L2 cache operation */ 34d34599bcSLad Prabhakar #define AX45MP_CCTL_L2_PA_INVAL 0x8 /* Invalidate an L2 cache entry */ 35d34599bcSLad Prabhakar #define AX45MP_CCTL_L2_PA_WB 0x9 /* Write-back an L2 cache entry */ 36d34599bcSLad Prabhakar 37d34599bcSLad Prabhakar #define AX45MP_L2C_REG_PER_CORE_OFFSET 0x10 38d34599bcSLad Prabhakar #define AX45MP_CCTL_L2_STATUS_PER_CORE_OFFSET 4 39d34599bcSLad Prabhakar 40d34599bcSLad Prabhakar #define AX45MP_L2C_REG_CN_CMD_OFFSET(n) \ 41d34599bcSLad Prabhakar (AX45MP_L2C_REG_C0_CMD_OFFSET + ((n) * AX45MP_L2C_REG_PER_CORE_OFFSET)) 42d34599bcSLad Prabhakar #define AX45MP_L2C_REG_CN_ACC_OFFSET(n) \ 43d34599bcSLad Prabhakar (AX45MP_L2C_REG_C0_ACC_OFFSET + ((n) * AX45MP_L2C_REG_PER_CORE_OFFSET)) 44d34599bcSLad Prabhakar #define AX45MP_CCTL_L2_STATUS_CN_MASK(n) \ 45d34599bcSLad Prabhakar (AX45MP_CCTL_L2_STATUS_C0_MASK << ((n) * AX45MP_CCTL_L2_STATUS_PER_CORE_OFFSET)) 46d34599bcSLad Prabhakar 47d34599bcSLad Prabhakar #define AX45MP_CCTL_REG_UCCTLBEGINADDR_NUM 0x80b 48d34599bcSLad Prabhakar #define AX45MP_CCTL_REG_UCCTLCOMMAND_NUM 0x80c 49d34599bcSLad Prabhakar 50d34599bcSLad Prabhakar #define AX45MP_CACHE_LINE_SIZE 64 51d34599bcSLad Prabhakar 52d34599bcSLad Prabhakar struct ax45mp_priv { 53d34599bcSLad Prabhakar void __iomem *l2c_base; 54d34599bcSLad Prabhakar u32 ax45mp_cache_line_size; 55d34599bcSLad Prabhakar }; 56d34599bcSLad Prabhakar 57d34599bcSLad Prabhakar static struct ax45mp_priv ax45mp_priv; 58d34599bcSLad Prabhakar 59d34599bcSLad Prabhakar /* L2 Cache operations */ 60d34599bcSLad Prabhakar static inline uint32_t ax45mp_cpu_l2c_get_cctl_status(void) 61d34599bcSLad Prabhakar { 62d34599bcSLad Prabhakar return readl(ax45mp_priv.l2c_base + AX45MP_L2C_REG_STATUS_OFFSET); 63d34599bcSLad Prabhakar } 64d34599bcSLad Prabhakar 65d34599bcSLad Prabhakar static void ax45mp_cpu_cache_operation(unsigned long start, unsigned long end, 66d34599bcSLad Prabhakar unsigned int l1_op, unsigned int l2_op) 67d34599bcSLad Prabhakar { 68d34599bcSLad Prabhakar unsigned long line_size = ax45mp_priv.ax45mp_cache_line_size; 69d34599bcSLad Prabhakar void __iomem *base = ax45mp_priv.l2c_base; 70d34599bcSLad Prabhakar int mhartid = smp_processor_id(); 71d34599bcSLad Prabhakar unsigned long pa; 72d34599bcSLad Prabhakar 73d34599bcSLad Prabhakar while (end > start) { 74d34599bcSLad Prabhakar csr_write(AX45MP_CCTL_REG_UCCTLBEGINADDR_NUM, start); 75d34599bcSLad Prabhakar csr_write(AX45MP_CCTL_REG_UCCTLCOMMAND_NUM, l1_op); 76d34599bcSLad Prabhakar 77d34599bcSLad Prabhakar pa = virt_to_phys((void *)start); 78d34599bcSLad Prabhakar writel(pa, base + AX45MP_L2C_REG_CN_ACC_OFFSET(mhartid)); 79d34599bcSLad Prabhakar writel(l2_op, base + AX45MP_L2C_REG_CN_CMD_OFFSET(mhartid)); 80d34599bcSLad Prabhakar while ((ax45mp_cpu_l2c_get_cctl_status() & 81d34599bcSLad Prabhakar AX45MP_CCTL_L2_STATUS_CN_MASK(mhartid)) != 82d34599bcSLad Prabhakar AX45MP_CCTL_L2_STATUS_IDLE) 83d34599bcSLad Prabhakar ; 84d34599bcSLad Prabhakar 85d34599bcSLad Prabhakar start += line_size; 86d34599bcSLad Prabhakar } 87d34599bcSLad Prabhakar } 88d34599bcSLad Prabhakar 89d34599bcSLad Prabhakar /* Write-back L1 and L2 cache entry */ 90d34599bcSLad Prabhakar static inline void ax45mp_cpu_dcache_wb_range(unsigned long start, unsigned long end) 91d34599bcSLad Prabhakar { 92d34599bcSLad Prabhakar ax45mp_cpu_cache_operation(start, end, AX45MP_CCTL_L1D_VA_WB, 93d34599bcSLad Prabhakar AX45MP_CCTL_L2_PA_WB); 94d34599bcSLad Prabhakar } 95d34599bcSLad Prabhakar 96d34599bcSLad Prabhakar /* Invalidate the L1 and L2 cache entry */ 97d34599bcSLad Prabhakar static inline void ax45mp_cpu_dcache_inval_range(unsigned long start, unsigned long end) 98d34599bcSLad Prabhakar { 99d34599bcSLad Prabhakar ax45mp_cpu_cache_operation(start, end, AX45MP_CCTL_L1D_VA_INVAL, 100d34599bcSLad Prabhakar AX45MP_CCTL_L2_PA_INVAL); 101d34599bcSLad Prabhakar } 102d34599bcSLad Prabhakar 103d34599bcSLad Prabhakar static void ax45mp_dma_cache_inv(phys_addr_t paddr, size_t size) 104d34599bcSLad Prabhakar { 105d34599bcSLad Prabhakar unsigned long start = (unsigned long)phys_to_virt(paddr); 106d34599bcSLad Prabhakar unsigned long end = start + size; 107d34599bcSLad Prabhakar unsigned long line_size; 108d34599bcSLad Prabhakar unsigned long flags; 109d34599bcSLad Prabhakar 110d34599bcSLad Prabhakar if (unlikely(start == end)) 111d34599bcSLad Prabhakar return; 112d34599bcSLad Prabhakar 113d34599bcSLad Prabhakar line_size = ax45mp_priv.ax45mp_cache_line_size; 114d34599bcSLad Prabhakar 115d34599bcSLad Prabhakar start = start & (~(line_size - 1)); 116d34599bcSLad Prabhakar end = ((end + line_size - 1) & (~(line_size - 1))); 117d34599bcSLad Prabhakar 118d34599bcSLad Prabhakar local_irq_save(flags); 119d34599bcSLad Prabhakar 120d34599bcSLad Prabhakar ax45mp_cpu_dcache_inval_range(start, end); 121d34599bcSLad Prabhakar 122d34599bcSLad Prabhakar local_irq_restore(flags); 123d34599bcSLad Prabhakar } 124d34599bcSLad Prabhakar 125d34599bcSLad Prabhakar static void ax45mp_dma_cache_wback(phys_addr_t paddr, size_t size) 126d34599bcSLad Prabhakar { 127d34599bcSLad Prabhakar unsigned long start = (unsigned long)phys_to_virt(paddr); 128d34599bcSLad Prabhakar unsigned long end = start + size; 129d34599bcSLad Prabhakar unsigned long line_size; 130d34599bcSLad Prabhakar unsigned long flags; 131d34599bcSLad Prabhakar 132*50b30655SLad Prabhakar if (unlikely(start == end)) 133*50b30655SLad Prabhakar return; 134*50b30655SLad Prabhakar 135d34599bcSLad Prabhakar line_size = ax45mp_priv.ax45mp_cache_line_size; 136d34599bcSLad Prabhakar start = start & (~(line_size - 1)); 137*50b30655SLad Prabhakar end = ((end + line_size - 1) & (~(line_size - 1))); 138d34599bcSLad Prabhakar local_irq_save(flags); 139d34599bcSLad Prabhakar ax45mp_cpu_dcache_wb_range(start, end); 140d34599bcSLad Prabhakar local_irq_restore(flags); 141d34599bcSLad Prabhakar } 142d34599bcSLad Prabhakar 143d34599bcSLad Prabhakar static void ax45mp_dma_cache_wback_inv(phys_addr_t paddr, size_t size) 144d34599bcSLad Prabhakar { 145d34599bcSLad Prabhakar ax45mp_dma_cache_wback(paddr, size); 146d34599bcSLad Prabhakar ax45mp_dma_cache_inv(paddr, size); 147d34599bcSLad Prabhakar } 148d34599bcSLad Prabhakar 149d34599bcSLad Prabhakar static int ax45mp_get_l2_line_size(struct device_node *np) 150d34599bcSLad Prabhakar { 151d34599bcSLad Prabhakar int ret; 152d34599bcSLad Prabhakar 153d34599bcSLad Prabhakar ret = of_property_read_u32(np, "cache-line-size", &ax45mp_priv.ax45mp_cache_line_size); 154d34599bcSLad Prabhakar if (ret) { 155d34599bcSLad Prabhakar pr_err("Failed to get cache-line-size, defaulting to 64 bytes\n"); 156d34599bcSLad Prabhakar return ret; 157d34599bcSLad Prabhakar } 158d34599bcSLad Prabhakar 159d34599bcSLad Prabhakar if (ax45mp_priv.ax45mp_cache_line_size != AX45MP_CACHE_LINE_SIZE) { 160d34599bcSLad Prabhakar pr_err("Expected cache-line-size to be 64 bytes (found:%u)\n", 161d34599bcSLad Prabhakar ax45mp_priv.ax45mp_cache_line_size); 162d34599bcSLad Prabhakar return -EINVAL; 163d34599bcSLad Prabhakar } 164d34599bcSLad Prabhakar 165d34599bcSLad Prabhakar return 0; 166d34599bcSLad Prabhakar } 167d34599bcSLad Prabhakar 168d34599bcSLad Prabhakar static const struct riscv_nonstd_cache_ops ax45mp_cmo_ops __initdata = { 169d34599bcSLad Prabhakar .wback = &ax45mp_dma_cache_wback, 170d34599bcSLad Prabhakar .inv = &ax45mp_dma_cache_inv, 171d34599bcSLad Prabhakar .wback_inv = &ax45mp_dma_cache_wback_inv, 172d34599bcSLad Prabhakar }; 173d34599bcSLad Prabhakar 174d34599bcSLad Prabhakar static const struct of_device_id ax45mp_cache_ids[] = { 175d34599bcSLad Prabhakar { .compatible = "andestech,ax45mp-cache" }, 176d34599bcSLad Prabhakar { /* sentinel */ } 177d34599bcSLad Prabhakar }; 178d34599bcSLad Prabhakar 179d34599bcSLad Prabhakar static int __init ax45mp_cache_init(void) 180d34599bcSLad Prabhakar { 181d34599bcSLad Prabhakar struct device_node *np; 182d34599bcSLad Prabhakar struct resource res; 183d34599bcSLad Prabhakar int ret; 184d34599bcSLad Prabhakar 185d34599bcSLad Prabhakar np = of_find_matching_node(NULL, ax45mp_cache_ids); 186d34599bcSLad Prabhakar if (!of_device_is_available(np)) 187d34599bcSLad Prabhakar return -ENODEV; 188d34599bcSLad Prabhakar 189d34599bcSLad Prabhakar ret = of_address_to_resource(np, 0, &res); 190d34599bcSLad Prabhakar if (ret) 191d34599bcSLad Prabhakar return ret; 192d34599bcSLad Prabhakar 193d34599bcSLad Prabhakar /* 194d34599bcSLad Prabhakar * If IOCP is present on the Andes AX45MP core riscv_cbom_block_size 195d34599bcSLad Prabhakar * will be 0 for sure, so we can definitely rely on it. If 196d34599bcSLad Prabhakar * riscv_cbom_block_size = 0 we don't need to handle CMO using SW any 197d34599bcSLad Prabhakar * more so we just return success here and only if its being set we 198d34599bcSLad Prabhakar * continue further in the probe path. 199d34599bcSLad Prabhakar */ 200d34599bcSLad Prabhakar if (!riscv_cbom_block_size) 201d34599bcSLad Prabhakar return 0; 202d34599bcSLad Prabhakar 203d34599bcSLad Prabhakar ax45mp_priv.l2c_base = ioremap(res.start, resource_size(&res)); 204d34599bcSLad Prabhakar if (!ax45mp_priv.l2c_base) 205d34599bcSLad Prabhakar return -ENOMEM; 206d34599bcSLad Prabhakar 207d34599bcSLad Prabhakar ret = ax45mp_get_l2_line_size(np); 208d34599bcSLad Prabhakar if (ret) { 209d34599bcSLad Prabhakar iounmap(ax45mp_priv.l2c_base); 210d34599bcSLad Prabhakar return ret; 211d34599bcSLad Prabhakar } 212d34599bcSLad Prabhakar 213d34599bcSLad Prabhakar riscv_noncoherent_register_cache_ops(&ax45mp_cmo_ops); 214d34599bcSLad Prabhakar 215d34599bcSLad Prabhakar return 0; 216d34599bcSLad Prabhakar } 217d34599bcSLad Prabhakar early_initcall(ax45mp_cache_init); 218