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