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