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