1 /*
2  * Copyright (C) 2018 Marvell International Ltd.
3  *
4  * SPDX-License-Identifier:     BSD-3-Clause
5  * https://spdx.org/licenses
6  */
7 
8 /* IO Window unit device driver for Marvell AP807, AP807 and AP810 SoCs */
9 
10 #include <common/debug.h>
11 #include <drivers/marvell/io_win.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 /* Physical address of the base of the window = {Addr[19:0],20`h0} */
25 #define ADDRESS_SHIFT			(20 - 4)
26 #define ADDRESS_MASK			(0xFFFFFFF0)
27 #define IO_WIN_ALIGNMENT_1M		(0x100000)
28 #define IO_WIN_ALIGNMENT_64K		(0x10000)
29 
30 /* AP registers */
31 #define IO_WIN_ALR_OFFSET(ap, win)	(MVEBU_IO_WIN_BASE(ap) + 0x0 + \
32 						(0x10 * win))
33 #define IO_WIN_AHR_OFFSET(ap, win)	(MVEBU_IO_WIN_BASE(ap) + 0x8 + \
34 						(0x10 * win))
35 #define IO_WIN_CR_OFFSET(ap, win)	(MVEBU_IO_WIN_BASE(ap) + 0xC + \
36 						(0x10 * win))
37 
38 /* For storage of CR, ALR, AHR abd GCR */
39 static uint32_t io_win_regs_save[MVEBU_IO_WIN_MAX_WINS * 3 + 1];
40 
io_win_check(struct addr_map_win * win)41 static void io_win_check(struct addr_map_win *win)
42 {
43 	/* for IO The base is always 1M aligned */
44 	/* check if address is aligned to 1M */
45 	if (IS_NOT_ALIGN(win->base_addr, IO_WIN_ALIGNMENT_1M)) {
46 		win->base_addr = ALIGN_UP(win->base_addr, IO_WIN_ALIGNMENT_1M);
47 		NOTICE("%s: Align up the base address to 0x%llx\n",
48 		       __func__, win->base_addr);
49 	}
50 
51 	/* size parameter validity check */
52 	if (IS_NOT_ALIGN(win->win_size, IO_WIN_ALIGNMENT_1M)) {
53 		win->win_size = ALIGN_UP(win->win_size, IO_WIN_ALIGNMENT_1M);
54 		NOTICE("%s: Aligning size to 0x%llx\n",
55 		       __func__, win->win_size);
56 	}
57 }
58 
io_win_enable_window(int ap_index,struct addr_map_win * win,uint32_t win_num)59 static void io_win_enable_window(int ap_index, struct addr_map_win *win,
60 				 uint32_t win_num)
61 {
62 	uint32_t alr, ahr;
63 	uint64_t end_addr;
64 
65 	if (win->target_id < 0 || win->target_id >= MVEBU_IO_WIN_MAX_WINS) {
66 		ERROR("target ID = %d, is invalid\n", win->target_id);
67 		return;
68 	}
69 
70 	if ((win_num == 0) || (win_num > MVEBU_IO_WIN_MAX_WINS)) {
71 		ERROR("Enabling wrong IOW window %d!\n", win_num);
72 		return;
73 	}
74 
75 	/* calculate the end-address */
76 	end_addr = (win->base_addr + win->win_size - 1);
77 
78 	alr = (uint32_t)((win->base_addr >> ADDRESS_SHIFT) & ADDRESS_MASK);
79 	alr |= WIN_ENABLE_BIT;
80 	ahr = (uint32_t)((end_addr >> ADDRESS_SHIFT) & ADDRESS_MASK);
81 
82 	/* write start address and end address for IO window */
83 	mmio_write_32(IO_WIN_ALR_OFFSET(ap_index, win_num), alr);
84 	mmio_write_32(IO_WIN_AHR_OFFSET(ap_index, win_num), ahr);
85 
86 	/* write window target */
87 	mmio_write_32(IO_WIN_CR_OFFSET(ap_index, win_num), win->target_id);
88 }
89 
io_win_disable_window(int ap_index,uint32_t win_num)90 static void io_win_disable_window(int ap_index, uint32_t win_num)
91 {
92 	uint32_t win_reg;
93 
94 	if ((win_num == 0) || (win_num > MVEBU_IO_WIN_MAX_WINS)) {
95 		ERROR("Disabling wrong IOW window %d!\n", win_num);
96 		return;
97 	}
98 
99 	win_reg = mmio_read_32(IO_WIN_ALR_OFFSET(ap_index, win_num));
100 	win_reg &= ~WIN_ENABLE_BIT;
101 	mmio_write_32(IO_WIN_ALR_OFFSET(ap_index, win_num), win_reg);
102 }
103 
104 /* Insert/Remove temporary window for using the out-of reset default
105  * CPx base address to access the CP configuration space prior to
106  * the further base address update in accordance with address mapping
107  * design.
108  *
109  * NOTE: Use the same window array for insertion and removal of
110  *       temporary windows.
111  */
iow_temp_win_insert(int ap_index,struct addr_map_win * win,int size)112 void iow_temp_win_insert(int ap_index, struct addr_map_win *win, int size)
113 {
114 	uint32_t win_id;
115 
116 	for (int i = 0; i < size; i++) {
117 		win_id = MVEBU_IO_WIN_MAX_WINS - i - 1;
118 		io_win_check(win);
119 		io_win_enable_window(ap_index, win, win_id);
120 		win++;
121 	}
122 }
123 
124 /*
125  * NOTE: Use the same window array for insertion and removal of
126  *       temporary windows.
127  */
iow_temp_win_remove(int ap_index,struct addr_map_win * win,int size)128 void iow_temp_win_remove(int ap_index, struct addr_map_win *win, int size)
129 {
130 	uint32_t win_id;
131 
132 	/* Start from the last window and do not touch Win0 */
133 	for (int i = 0; i < size; i++) {
134 		uint64_t base;
135 		uint32_t target;
136 
137 		win_id = MVEBU_IO_WIN_MAX_WINS - i - 1;
138 
139 		target = mmio_read_32(IO_WIN_CR_OFFSET(ap_index, win_id));
140 		base = mmio_read_32(IO_WIN_ALR_OFFSET(ap_index, win_id));
141 		base &= ~WIN_ENABLE_BIT;
142 		base <<= ADDRESS_SHIFT;
143 
144 		if ((win->target_id != target) || (win->base_addr != base)) {
145 			ERROR("%s: Trying to remove bad window-%d!\n",
146 			      __func__, win_id);
147 			continue;
148 		}
149 		io_win_disable_window(ap_index, win_id);
150 		win++;
151 	}
152 }
153 
154 #ifdef DEBUG_ADDR_MAP
dump_io_win(int ap_index)155 static void dump_io_win(int ap_index)
156 {
157 	uint32_t trgt_id, win_id;
158 	uint32_t alr, ahr;
159 	uint64_t start, end;
160 
161 	/* Dump all IO windows */
162 	printf("\tbank  target     start              end\n");
163 	printf("\t----------------------------------------------------\n");
164 	for (win_id = 0; win_id < MVEBU_IO_WIN_MAX_WINS; win_id++) {
165 		alr = mmio_read_32(IO_WIN_ALR_OFFSET(ap_index, win_id));
166 		if (alr & WIN_ENABLE_BIT) {
167 			alr &= ~WIN_ENABLE_BIT;
168 			ahr = mmio_read_32(IO_WIN_AHR_OFFSET(ap_index, win_id));
169 			trgt_id = mmio_read_32(IO_WIN_CR_OFFSET(ap_index,
170 								win_id));
171 			start = ((uint64_t)alr << ADDRESS_SHIFT);
172 			end = (((uint64_t)ahr + 0x10) << ADDRESS_SHIFT);
173 			printf("\tio-win %d     0x%016llx 0x%016llx\n",
174 			       trgt_id, start, end);
175 		}
176 	}
177 	printf("\tio-win gcr is %x\n",
178 	       mmio_read_32(MVEBU_IO_WIN_BASE(ap_index) +
179 	       MVEBU_IO_WIN_GCR_OFFSET));
180 }
181 #endif
182 
iow_save_win_range(int ap_id,int win_first,int win_last,uint32_t * buffer)183 static void iow_save_win_range(int ap_id, int win_first, int win_last,
184 			       uint32_t *buffer)
185 {
186 	int win_id, idx;
187 
188 	/* Save IOW */
189 	for (idx = 0, win_id = win_first; win_id <= win_last; win_id++) {
190 		buffer[idx++] = mmio_read_32(IO_WIN_CR_OFFSET(ap_id, win_id));
191 		buffer[idx++] = mmio_read_32(IO_WIN_ALR_OFFSET(ap_id, win_id));
192 		buffer[idx++] = mmio_read_32(IO_WIN_AHR_OFFSET(ap_id, win_id));
193 	}
194 	buffer[idx] = mmio_read_32(MVEBU_IO_WIN_BASE(ap_id) +
195 				   MVEBU_IO_WIN_GCR_OFFSET);
196 }
197 
iow_restore_win_range(int ap_id,int win_first,int win_last,uint32_t * buffer)198 static void iow_restore_win_range(int ap_id, int win_first, int win_last,
199 				  uint32_t *buffer)
200 {
201 	int win_id, idx;
202 
203 	/* Restore IOW */
204 	for (idx = 0, win_id = win_first; win_id <= win_last; win_id++) {
205 		mmio_write_32(IO_WIN_CR_OFFSET(ap_id, win_id), buffer[idx++]);
206 		mmio_write_32(IO_WIN_ALR_OFFSET(ap_id, win_id), buffer[idx++]);
207 		mmio_write_32(IO_WIN_AHR_OFFSET(ap_id, win_id), buffer[idx++]);
208 	}
209 	mmio_write_32(MVEBU_IO_WIN_BASE(ap_id) + MVEBU_IO_WIN_GCR_OFFSET,
210 		      buffer[idx++]);
211 }
212 
iow_save_win_all(int ap_id)213 void iow_save_win_all(int ap_id)
214 {
215 	iow_save_win_range(ap_id, 0, MVEBU_IO_WIN_MAX_WINS - 1,
216 			   io_win_regs_save);
217 }
218 
iow_restore_win_all(int ap_id)219 void iow_restore_win_all(int ap_id)
220 {
221 	iow_restore_win_range(ap_id, 0, MVEBU_IO_WIN_MAX_WINS - 1,
222 			      io_win_regs_save);
223 }
224 
init_io_win(int ap_index)225 int init_io_win(int ap_index)
226 {
227 	struct addr_map_win *win;
228 	uint32_t win_id, win_reg;
229 	uint32_t win_count;
230 
231 	INFO("Initializing IO WIN Address decoding\n");
232 
233 	/* Get the array of the windows and its size */
234 	marvell_get_io_win_memory_map(ap_index, &win, &win_count);
235 	if (win_count <= 0)
236 		INFO("no windows configurations found\n");
237 
238 	if (win_count > MVEBU_IO_WIN_MAX_WINS) {
239 		INFO("number of windows is bigger than %d\n",
240 		     MVEBU_IO_WIN_MAX_WINS);
241 		return 0;
242 	}
243 
244 	/* Get the default target id to set the GCR */
245 	win_reg = marvell_get_io_win_gcr_target(ap_index);
246 	mmio_write_32(MVEBU_IO_WIN_BASE(ap_index) + MVEBU_IO_WIN_GCR_OFFSET,
247 		      win_reg);
248 
249 	/* disable all IO windows */
250 	for (win_id = 1; win_id < MVEBU_IO_WIN_MAX_WINS; win_id++)
251 		io_win_disable_window(ap_index, win_id);
252 
253 	/* enable relevant windows, starting from win_id = 1 because
254 	 * index 0 dedicated for BootROM
255 	 */
256 	for (win_id = 1; win_id <= win_count; win_id++, win++) {
257 		io_win_check(win);
258 		io_win_enable_window(ap_index, win, win_id);
259 	}
260 
261 #ifdef DEBUG_ADDR_MAP
262 	dump_io_win(ap_index);
263 #endif
264 
265 	INFO("Done IO WIN Address decoding Initializing\n");
266 
267 	return 0;
268 }
269