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