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
omc_write32(uint8_t filter,uint32_t offs,uint32_t val)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
omc_read32(uint8_t filter,uint32_t offs)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
omc_write64(uint8_t filter,uint32_t offs,uint64_t val)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
omc_read64(uint8_t filter,uint32_t offs)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
omc_write_region_base(uint8_t filter,uint32_t region,uint64_t val)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
omc_write_region_top(uint8_t filter,uint32_t region,uint64_t val)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
omc_write_region_attributes(uint8_t filter,uint32_t region,uint32_t val)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
omc_write_region_id_access(uint8_t filter,uint32_t region,uint32_t val)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
omc_read_start_address(uint8_t filter)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
omc_init(vaddr_t base,uint32_t size,uint8_t num)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
omc_configure_region(uint8_t region,const struct omc_region_config * cfg)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
omc_set_action(enum omc_action action)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
omc_fail_dump(uint8_t filter)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
omc_int_clear(uint8_t filter)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