1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2024, Telechips Inc. 4 * 5 * Driver for the Orbit Memory Controller. 6 * https://www.openedges.com/memorycontroller 7 */ 8 9 #include <assert.h> 10 #include <drivers/openedges_omc.h> 11 #include <io.h> 12 #include <kernel/panic.h> 13 #include <trace.h> 14 #include <util.h> 15 16 #define ACTION_OFF U(0x1004) 17 18 #define INT_STATUS U(0x1010) 19 #define INT_CLEAR U(0x1014) 20 #define FAIL_ADDRESS_LOW_OFF U(0x1020) 21 #define FAIL_ADDRESS_HIGH_OFF U(0x1024) 22 #define FAIL_CONTROL_OFF U(0x1028) 23 #define FAIL_ID_OFF U(0x102c) 24 #define FAIL_DIRECTION_OFF(d) (U(0x20) * (d)) 25 26 #define REGION_COUNT U(17) 27 #define REGION_BASE_LOW_OFF U(0x1100) 28 #define REGION_BASE_HIGH_OFF U(0x1104) 29 #define REGION_TOP_LOW_OFF U(0x1108) 30 #define REGION_TOP_HIGH_OFF U(0x110c) 31 #define REGION_ATTRIBUTES_OFF U(0x1110) 32 #define REGION_ID_ACCESS_OFF U(0x1114) 33 #define REGION_NUM_OFF(region) (U(0x20) * (region)) 34 35 #define ADDRESS_CTRL0_OFF U(0x1f00) 36 #define ADDRESS_CTRL1_OFF U(0x1f04) 37 #define REGION0_START_OFF U(0x1f10) 38 #define REGION0_END_OFF U(0x1f14) 39 #define REGION0_CFG_OFF U(0x1f18) 40 #define REGION1_START_OFF U(0x1f20) 41 #define REGION1_END_OFF U(0x1f24) 42 #define REGION1_CFG_OFF U(0x1f28) 43 #define CHIP0_SIZE_OFF U(0x1f30) 44 #define CHIP1_SIZE_OFF U(0x1f34) 45 46 #define INT_TYPE_READ U(0) 47 #define INT_TYPE_WRITE U(1) 48 #define INT_TYPE_MAX U(2) 49 50 #define INT_STATUS_OVERLAP BIT(16) 51 #define INT_STATUS_OVERRUN BIT(8) 52 #define INT_STATUS_STATUS BIT(0) 53 #define INT_STATUS_MASK (INT_STATUS_OVERLAP | INT_STATUS_OVERRUN | \ 54 INT_STATUS_STATUS) 55 56 #define INT_CLEAR_CLEAR_SHIFT U(0) 57 #define INT_CLEAR_CLEAR_MASK U(0x1) 58 59 #define FAIL_CONTROL_NONSECURE BIT(21) 60 #define FAIL_CONTROL_PRIVILEGED BIT(20) 61 62 #define FAIL_ID_AID_SHIFT U(8) 63 #define FAIL_ID_AID_MASK GENMASK_32(27, 8) 64 #define FAIL_ID_MID_SHIFT U(0) 65 #define FAIL_ID_MID_MASK GENMASK_32(7, 0) 66 67 #define REG_ATTR_S_WR_EN BIT(31) 68 #define REG_ATTR_S_RD_EN BIT(30) 69 #define REG_ATTR_FILTER_EN BIT(0) 70 71 struct omc_instance { 72 vaddr_t base; 73 uint32_t size; 74 uint8_t num_filters; 75 }; 76 77 static struct omc_instance tzc; 78 79 static void omc_write32(uint8_t filter, uint32_t offs, uint32_t val) 80 { 81 vaddr_t filter_offs = filter * tzc.size; 82 83 io_write32(tzc.base + filter_offs + offs, val); 84 } 85 86 static uint32_t omc_read32(uint8_t filter, uint32_t offs) 87 { 88 vaddr_t filter_offs = filter * tzc.size; 89 90 return io_read32(tzc.base + filter_offs + offs); 91 } 92 93 static void omc_write64(uint8_t filter, uint32_t offs, uint64_t val) 94 { 95 vaddr_t filter_offs = filter * tzc.size; 96 97 io_write64(tzc.base + filter_offs + offs, val); 98 } 99 100 static uint64_t omc_read64(uint8_t filter, uint32_t offs) 101 { 102 vaddr_t filter_offs = filter * tzc.size; 103 104 return io_read64(tzc.base + filter_offs + offs); 105 } 106 107 static void omc_write_region_base(uint8_t filter, uint32_t region, uint64_t val) 108 { 109 omc_write64(filter, REGION_BASE_LOW_OFF + REGION_NUM_OFF(region), val); 110 } 111 112 static void omc_write_region_top(uint8_t filter, uint32_t region, uint64_t val) 113 { 114 omc_write64(filter, REGION_TOP_LOW_OFF + REGION_NUM_OFF(region), val); 115 } 116 117 static void omc_write_region_attributes(uint8_t filter, uint32_t region, 118 uint32_t val) 119 { 120 omc_write32(filter, REGION_ATTRIBUTES_OFF + REGION_NUM_OFF(region), 121 val); 122 } 123 124 static void omc_write_region_id_access(uint8_t filter, uint32_t region, 125 uint32_t val) 126 { 127 omc_write32(filter, REGION_ID_ACCESS_OFF + REGION_NUM_OFF(region), val); 128 } 129 130 static uint64_t omc_read_start_address(uint8_t filter) 131 { 132 return SHIFT_U64(omc_read32(filter, REGION0_START_OFF), 8); 133 } 134 135 void omc_init(vaddr_t base, uint32_t size, uint8_t num) 136 { 137 assert(base); 138 139 tzc.base = base; 140 tzc.size = size; 141 tzc.num_filters = num; 142 } 143 144 void omc_configure_region(uint8_t region, const struct omc_region_config *cfg) 145 { 146 uint8_t filter = 0; 147 uint32_t attr = 0; 148 uint64_t start_addr = 0; 149 150 if (!tzc.base) 151 panic("tzc.base is not registered"); 152 else if (!cfg) 153 panic("cfg is null"); 154 else if (cfg->filters >> tzc.num_filters) 155 panic("cfg->filters is overflowed"); 156 else if (region >= REGION_COUNT) 157 panic("region is overflowed"); 158 else if ((cfg->base | (cfg->top + 1)) & 0xFFF) 159 panic("region base or (top + 1) is not 4KB aligned"); 160 161 for (filter = 0; filter < tzc.num_filters; filter++) { 162 if (cfg->flags & OMC_FLAG_RELATIVE_ADDR) 163 start_addr = omc_read_start_address(filter); 164 else 165 start_addr = 0; 166 167 omc_write_region_base(filter, region, start_addr + cfg->base); 168 omc_write_region_top(filter, region, start_addr + cfg->top); 169 170 /* Assign the region to a filter and set secure attributes */ 171 attr = REG_ATTR_S_WR_EN | REG_ATTR_S_RD_EN; 172 if (cfg->filters & BIT(filter)) 173 attr |= REG_ATTR_FILTER_EN; 174 omc_write_region_attributes(filter, region, attr); 175 176 omc_write_region_id_access(filter, region, 177 cfg->ns_device_access); 178 } 179 } 180 181 void omc_set_action(enum omc_action action) 182 { 183 uint8_t filter = 0; 184 185 if (!tzc.base) 186 panic("tzc.base is null"); 187 188 for (filter = 0; filter < tzc.num_filters; filter++) 189 omc_write32(filter, ACTION_OFF, (uint32_t)action); 190 } 191 192 void omc_fail_dump(uint8_t filter) 193 { 194 uint64_t __maybe_unused addr = 0; 195 uint32_t status = 0; 196 uint32_t __maybe_unused ctrl = 0; 197 uint32_t __maybe_unused nsaid = 0; 198 uint32_t direction = 0; 199 200 for (direction = INT_TYPE_READ; direction < INT_TYPE_MAX; direction++) { 201 status = omc_read32(filter, 202 INT_STATUS + FAIL_DIRECTION_OFF(direction)); 203 if (!(status & INT_STATUS_MASK)) 204 continue; 205 206 if (status & INT_STATUS_OVERLAP) 207 EMSG("Overlap violation on filter %"PRIu8, filter); 208 209 if (status & INT_STATUS_OVERRUN) 210 EMSG("Overrun violation on filter %"PRIu8, filter); 211 212 if (status & INT_STATUS_STATUS) 213 EMSG("Permission violation on filter %"PRIu8, filter); 214 215 ctrl = omc_read32(filter, FAIL_CONTROL_OFF + 216 FAIL_DIRECTION_OFF(direction)); 217 addr = omc_read64(filter, FAIL_ADDRESS_LOW_OFF + 218 FAIL_DIRECTION_OFF(direction)); 219 nsaid = omc_read32(filter, FAIL_ID_OFF + 220 FAIL_DIRECTION_OFF(direction)); 221 EMSG("Violation @%#"PRIx64 222 ", %ssecure %sprivileged %s, MID %#"PRIx32", AID %#"PRIx32, 223 addr, 224 (ctrl & FAIL_CONTROL_NONSECURE) ? "non-" : "", 225 (ctrl & FAIL_CONTROL_PRIVILEGED) ? "" : "un", 226 (direction == INT_TYPE_WRITE) ? "write" : "read", 227 (nsaid & FAIL_ID_MID_MASK) >> FAIL_ID_MID_SHIFT, 228 (nsaid & FAIL_ID_AID_MASK) >> FAIL_ID_AID_SHIFT); 229 } 230 } 231 232 void omc_int_clear(uint8_t filter) 233 { 234 if (!tzc.base) 235 panic("tzc.base is null"); 236 237 omc_write32(filter, INT_CLEAR, BIT(0)); 238 } 239