1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2024 Ruslan Bukin <br@bsdpad.com> 5 * 6 * This software was developed by the University of Cambridge Computer 7 * Laboratory (Department of Computer Science and Technology) under Innovate 8 * UK project 105694, "Digital Security by Design (DSbD) Technology Platform 9 * Prototype". 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/bus.h> 36 #include <sys/rman.h> 37 #include <sys/kernel.h> 38 #include <sys/module.h> 39 #include <machine/bus.h> 40 41 #include <dev/ofw/ofw_bus.h> 42 #include <dev/ofw/ofw_bus_subr.h> 43 #include <dev/fdt/fdt_common.h> 44 45 #include <vm/vm.h> 46 #include <vm/vm_extern.h> 47 #include <vm/pmap.h> 48 49 #define SIFIVE_CCACHE_CONFIG 0x000 50 #define CCACHE_CONFIG_WAYS_S 8 51 #define CCACHE_CONFIG_WAYS_M (0xff << CCACHE_CONFIG_WAYS_S) 52 #define SIFIVE_CCACHE_WAYENABLE 0x008 53 #define SIFIVE_CCACHE_FLUSH64 0x200 54 55 #define SIFIVE_CCACHE_LINE_SIZE 64 56 57 #define RD8(sc, off) (bus_read_8((sc)->res, (off))) 58 #define WR8(sc, off, val) (bus_write_8((sc)->res, (off), (val))) 59 #define CC_WR8(offset, value) \ 60 *(volatile uint64_t *)((uintptr_t)ccache_va + (offset)) = (value) 61 62 static struct ofw_compat_data compat_data[] = { 63 { "sifive,eic7700", 1 }, 64 { NULL, 0 } 65 }; 66 67 struct ccache_softc { 68 struct resource *res; 69 }; 70 71 static void *ccache_va = NULL; 72 73 static struct resource_spec ccache_spec[] = { 74 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 75 { -1, 0 } 76 }; 77 78 /* 79 * Non-standard EIC7700 cache-flushing routine. 80 */ 81 static void 82 ccache_flush_range(vm_offset_t start, size_t len) 83 { 84 vm_offset_t paddr; 85 vm_offset_t sva; 86 vm_offset_t step; 87 uint64_t line; 88 89 if (ccache_va == NULL || len == 0) 90 return; 91 92 mb(); 93 94 for (sva = start; len > 0;) { 95 paddr = pmap_kextract(sva); 96 step = min(PAGE_SIZE - (paddr & PAGE_MASK), len); 97 for (line = rounddown2(paddr, SIFIVE_CCACHE_LINE_SIZE); 98 line < paddr + step; 99 line += SIFIVE_CCACHE_LINE_SIZE) 100 CC_WR8(SIFIVE_CCACHE_FLUSH64, line); 101 sva += step; 102 len -= step; 103 } 104 105 mb(); 106 } 107 108 static void 109 ccache_install_hooks(void) 110 { 111 struct riscv_cache_ops eswin_ops; 112 113 eswin_ops.dcache_wbinv_range = ccache_flush_range; 114 eswin_ops.dcache_inv_range = ccache_flush_range; 115 eswin_ops.dcache_wb_range = ccache_flush_range; 116 117 riscv_cache_install_hooks(&eswin_ops, SIFIVE_CCACHE_LINE_SIZE); 118 } 119 120 static int 121 ccache_probe(device_t dev) 122 { 123 124 if (!ofw_bus_status_okay(dev)) 125 return (ENXIO); 126 127 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 128 return (ENXIO); 129 130 if (device_get_unit(dev) != 0) 131 return (ENXIO); 132 133 device_set_desc(dev, "SiFive Cache Controller"); 134 135 return (BUS_PROBE_DEFAULT); 136 } 137 138 static int 139 ccache_attach(device_t dev) 140 { 141 struct ccache_softc *sc; 142 size_t config, ways; 143 144 sc = device_get_softc(dev); 145 146 if (bus_alloc_resources(dev, ccache_spec, &sc->res) != 0) { 147 device_printf(dev, "cannot allocate resources for device\n"); 148 return (ENXIO); 149 } 150 151 /* Non-standard EIC7700 cache unit configuration. */ 152 config = RD8(sc, SIFIVE_CCACHE_CONFIG); 153 ways = (config & CCACHE_CONFIG_WAYS_M) >> CCACHE_CONFIG_WAYS_S; 154 WR8(sc, SIFIVE_CCACHE_WAYENABLE, (ways - 1)); 155 156 ccache_va = rman_get_virtual(sc->res); 157 ccache_install_hooks(); 158 159 return (0); 160 } 161 162 static device_method_t ccache_methods[] = { 163 /* Device interface */ 164 DEVMETHOD(device_probe, ccache_probe), 165 DEVMETHOD(device_attach, ccache_attach), 166 DEVMETHOD_END 167 }; 168 169 static driver_t ccache_driver = { 170 "ccache", 171 ccache_methods, 172 sizeof(struct ccache_softc), 173 }; 174 175 EARLY_DRIVER_MODULE(ccache, simplebus, ccache_driver, 0, 0, 176 BUS_PASS_BUS + BUS_PASS_ORDER_FIRST); 177 MODULE_VERSION(ccache, 1); 178