1 /*
2 * Copyright (C) 2018 Marvell International Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 * https://spdx.org/licenses
6 */
7
8 /* GWIN unit device driver for Marvell AP810 SoC */
9
10 #include <common/debug.h>
11 #include <drivers/marvell/gwin.h>
12 #include <lib/mmio.h>
13
14 #include <armada_common.h>
15 #include <mvebu.h>
16 #include <mvebu_def.h>
17
18 #if LOG_LEVEL >= LOG_LEVEL_INFO
19 #define DEBUG_ADDR_MAP
20 #endif
21
22 /* common defines */
23 #define WIN_ENABLE_BIT (0x1)
24 #define WIN_TARGET_MASK (0xF)
25 #define WIN_TARGET_SHIFT (0x8)
26 #define WIN_TARGET(tgt) (((tgt) & WIN_TARGET_MASK) \
27 << WIN_TARGET_SHIFT)
28
29 /* Bits[43:26] of the physical address are the window base,
30 * which is aligned to 64MB
31 */
32 #define ADDRESS_RSHIFT (26)
33 #define ADDRESS_LSHIFT (10)
34 #define GWIN_ALIGNMENT_64M (0x4000000)
35
36 /* AP registers */
37 #define GWIN_CR_OFFSET(ap, win) (MVEBU_GWIN_BASE(ap) + 0x0 + \
38 (0x10 * (win)))
39 #define GWIN_ALR_OFFSET(ap, win) (MVEBU_GWIN_BASE(ap) + 0x8 + \
40 (0x10 * (win)))
41 #define GWIN_AHR_OFFSET(ap, win) (MVEBU_GWIN_BASE(ap) + 0xc + \
42 (0x10 * (win)))
43
44 #define CCU_GRU_CR_OFFSET(ap) (MVEBU_CCU_GRU_BASE(ap))
45 #define CCR_GRU_CR_GWIN_MBYPASS (1 << 1)
46
gwin_check(struct addr_map_win * win)47 static void gwin_check(struct addr_map_win *win)
48 {
49 /* The base is always 64M aligned */
50 if (IS_NOT_ALIGN(win->base_addr, GWIN_ALIGNMENT_64M)) {
51 win->base_addr &= ~(GWIN_ALIGNMENT_64M - 1);
52 NOTICE("%s: Align the base address to 0x%llx\n",
53 __func__, win->base_addr);
54 }
55
56 /* size parameter validity check */
57 if (IS_NOT_ALIGN(win->win_size, GWIN_ALIGNMENT_64M)) {
58 win->win_size = ALIGN_UP(win->win_size, GWIN_ALIGNMENT_64M);
59 NOTICE("%s: Aligning window size to 0x%llx\n",
60 __func__, win->win_size);
61 }
62 }
63
gwin_enable_window(int ap_index,struct addr_map_win * win,uint32_t win_num)64 static void gwin_enable_window(int ap_index, struct addr_map_win *win,
65 uint32_t win_num)
66 {
67 uint32_t alr, ahr;
68 uint64_t end_addr;
69
70 if ((win->target_id & WIN_TARGET_MASK) != win->target_id) {
71 ERROR("target ID = %d, is invalid\n", win->target_id);
72 return;
73 }
74
75 /* calculate 64bit end-address */
76 end_addr = (win->base_addr + win->win_size - 1);
77
78 alr = (uint32_t)((win->base_addr >> ADDRESS_RSHIFT) << ADDRESS_LSHIFT);
79 ahr = (uint32_t)((end_addr >> ADDRESS_RSHIFT) << ADDRESS_LSHIFT);
80
81 /* write start address and end address for GWIN */
82 mmio_write_32(GWIN_ALR_OFFSET(ap_index, win_num), alr);
83 mmio_write_32(GWIN_AHR_OFFSET(ap_index, win_num), ahr);
84
85 /* write the target ID and enable the window */
86 mmio_write_32(GWIN_CR_OFFSET(ap_index, win_num),
87 WIN_TARGET(win->target_id) | WIN_ENABLE_BIT);
88 }
89
gwin_disable_window(int ap_index,uint32_t win_num)90 static void gwin_disable_window(int ap_index, uint32_t win_num)
91 {
92 uint32_t win_reg;
93
94 win_reg = mmio_read_32(GWIN_CR_OFFSET(ap_index, win_num));
95 win_reg &= ~WIN_ENABLE_BIT;
96 mmio_write_32(GWIN_CR_OFFSET(ap_index, win_num), win_reg);
97 }
98
99 /* Insert/Remove temporary window for using the out-of reset default
100 * CPx base address to access the CP configuration space prior to
101 * the further base address update in accordance with address mapping
102 * design.
103 *
104 * NOTE: Use the same window array for insertion and removal of
105 * temporary windows.
106 */
gwin_temp_win_insert(int ap_index,struct addr_map_win * win,int size)107 void gwin_temp_win_insert(int ap_index, struct addr_map_win *win, int size)
108 {
109 uint32_t win_id;
110
111 for (int i = 0; i < size; i++) {
112 win_id = MVEBU_GWIN_MAX_WINS - i - 1;
113 gwin_check(win);
114 gwin_enable_window(ap_index, win, win_id);
115 win++;
116 }
117 }
118
119 /*
120 * NOTE: Use the same window array for insertion and removal of
121 * temporary windows.
122 */
gwin_temp_win_remove(int ap_index,struct addr_map_win * win,int size)123 void gwin_temp_win_remove(int ap_index, struct addr_map_win *win, int size)
124 {
125 uint32_t win_id;
126
127 for (int i = 0; i < size; i++) {
128 uint64_t base;
129 uint32_t target;
130
131 win_id = MVEBU_GWIN_MAX_WINS - i - 1;
132
133 target = mmio_read_32(GWIN_CR_OFFSET(ap_index, win_id));
134 target >>= WIN_TARGET_SHIFT;
135 target &= WIN_TARGET_MASK;
136
137 base = mmio_read_32(GWIN_ALR_OFFSET(ap_index, win_id));
138 base >>= ADDRESS_LSHIFT;
139 base <<= ADDRESS_RSHIFT;
140
141 if (win->target_id != target) {
142 ERROR("%s: Trying to remove bad window-%d!\n",
143 __func__, win_id);
144 continue;
145 }
146 gwin_disable_window(ap_index, win_id);
147 win++;
148 }
149 }
150
151 #ifdef DEBUG_ADDR_MAP
dump_gwin(int ap_index)152 static void dump_gwin(int ap_index)
153 {
154 uint32_t win_num;
155
156 /* Dump all GWIN windows */
157 printf("\tbank target start end\n");
158 printf("\t----------------------------------------------------\n");
159 for (win_num = 0; win_num < MVEBU_GWIN_MAX_WINS; win_num++) {
160 uint32_t cr;
161 uint64_t alr, ahr;
162
163 cr = mmio_read_32(GWIN_CR_OFFSET(ap_index, win_num));
164 /* Window enabled */
165 if (cr & WIN_ENABLE_BIT) {
166 alr = mmio_read_32(GWIN_ALR_OFFSET(ap_index, win_num));
167 alr = (alr >> ADDRESS_LSHIFT) << ADDRESS_RSHIFT;
168 ahr = mmio_read_32(GWIN_AHR_OFFSET(ap_index, win_num));
169 ahr = (ahr >> ADDRESS_LSHIFT) << ADDRESS_RSHIFT;
170 printf("\tgwin %d 0x%016llx 0x%016llx\n",
171 (cr >> 8) & 0xF, alr, ahr);
172 }
173 }
174 }
175 #endif
176
init_gwin(int ap_index)177 int init_gwin(int ap_index)
178 {
179 struct addr_map_win *win;
180 uint32_t win_id;
181 uint32_t win_count;
182 uint32_t win_reg;
183
184 INFO("Initializing GWIN Address decoding\n");
185
186 /* Get the array of the windows and its size */
187 marvell_get_gwin_memory_map(ap_index, &win, &win_count);
188 if (win_count <= 0) {
189 INFO("no windows configurations found\n");
190 return 0;
191 }
192
193 if (win_count > MVEBU_GWIN_MAX_WINS) {
194 ERROR("number of windows is bigger than %d\n",
195 MVEBU_GWIN_MAX_WINS);
196 return 0;
197 }
198
199 /* disable all windows */
200 for (win_id = 0; win_id < MVEBU_GWIN_MAX_WINS; win_id++)
201 gwin_disable_window(ap_index, win_id);
202
203 /* enable relevant windows */
204 for (win_id = 0; win_id < win_count; win_id++, win++) {
205 gwin_check(win);
206 gwin_enable_window(ap_index, win, win_id);
207 }
208
209 /* GWIN Miss feature has not verified, therefore any access towards
210 * remote AP should be accompanied with proper configuration to
211 * GWIN registers group and therefore the GWIN Miss feature
212 * should be set into Bypass mode, need to make sure all GWIN regions
213 * are defined correctly that will assure no GWIN miss occurrance
214 * JIRA-AURORA2-1630
215 */
216 INFO("Update GWIN miss bypass\n");
217 win_reg = mmio_read_32(CCU_GRU_CR_OFFSET(ap_index));
218 win_reg |= CCR_GRU_CR_GWIN_MBYPASS;
219 mmio_write_32(CCU_GRU_CR_OFFSET(ap_index), win_reg);
220
221 #ifdef DEBUG_ADDR_MAP
222 dump_gwin(ap_index);
223 #endif
224
225 INFO("Done GWIN Address decoding Initializing\n");
226
227 return 0;
228 }
229