]>
Commit | Line | Data |
---|---|---|
ef016f83 MF |
1 | /* Blackfin External Bus Interface Unit (EBIU) Asynchronous Memory Controller |
2 | (AMC) model. | |
3 | ||
3666a048 | 4 | Copyright (C) 2010-2021 Free Software Foundation, Inc. |
ef016f83 MF |
5 | Contributed by Analog Devices, Inc. |
6 | ||
7 | This file is part of simulators. | |
8 | ||
9 | This program is free software; you can redistribute it and/or modify | |
10 | it under the terms of the GNU General Public License as published by | |
11 | the Free Software Foundation; either version 3 of the License, or | |
12 | (at your option) any later version. | |
13 | ||
14 | This program is distributed in the hope that it will be useful, | |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | GNU General Public License for more details. | |
18 | ||
19 | You should have received a copy of the GNU General Public License | |
20 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
21 | ||
6df01ab8 MF |
22 | /* This must come before any other includes. */ |
23 | #include "defs.h" | |
ef016f83 MF |
24 | |
25 | #include "sim-main.h" | |
26 | #include "devices.h" | |
27 | #include "dv-bfin_ebiu_amc.h" | |
28 | ||
29 | struct bfin_ebiu_amc | |
30 | { | |
31 | bu32 base; | |
32 | int type; | |
8dbfaed8 | 33 | bu32 bank_base, bank_size; |
ef016f83 MF |
34 | unsigned (*io_write) (struct hw *, const void *, int, address_word, |
35 | unsigned, struct bfin_ebiu_amc *, bu32, bu32); | |
36 | unsigned (*io_read) (struct hw *, void *, int, address_word, unsigned, | |
37 | struct bfin_ebiu_amc *, bu32, void *, bu16 *, bu32 *); | |
38 | struct hw *slaves[4]; | |
39 | ||
40 | /* Order after here is important -- matches hardware MMR layout. */ | |
41 | bu16 BFIN_MMR_16(amgctl); | |
42 | union { | |
43 | struct { | |
44 | bu32 ambctl0, ambctl1; | |
45 | bu32 _pad0[5]; | |
46 | bu16 BFIN_MMR_16(mode); | |
47 | bu16 BFIN_MMR_16(fctl); | |
48 | } bf50x; | |
49 | struct { | |
50 | bu32 ambctl0, ambctl1; | |
51 | } bf53x; | |
52 | struct { | |
53 | bu32 ambctl0, ambctl1; | |
54 | bu32 mbsctl, arbstat, mode, fctl; | |
55 | } bf54x; | |
56 | }; | |
57 | }; | |
58 | #define mmr_base() offsetof(struct bfin_ebiu_amc, amgctl) | |
59 | #define mmr_offset(mmr) (offsetof(struct bfin_ebiu_amc, mmr) - mmr_base()) | |
60 | #define mmr_idx(mmr) (mmr_offset (mmr) / 4) | |
61 | ||
990d19fd MF |
62 | static const char * const bf50x_mmr_names[] = |
63 | { | |
ef016f83 MF |
64 | "EBIU_AMGCTL", "EBIU_AMBCTL0", "EBIU_AMBCTL1", |
65 | [mmr_idx (bf50x.mode)] = "EBIU_MODE", "EBIU_FCTL", | |
66 | }; | |
990d19fd MF |
67 | static const char * const bf53x_mmr_names[] = |
68 | { | |
ef016f83 MF |
69 | "EBIU_AMGCTL", "EBIU_AMBCTL0", "EBIU_AMBCTL1", |
70 | }; | |
990d19fd MF |
71 | static const char * const bf54x_mmr_names[] = |
72 | { | |
ef016f83 MF |
73 | "EBIU_AMGCTL", "EBIU_AMBCTL0", "EBIU_AMBCTL1", |
74 | "EBIU_MSBCTL", "EBIU_ARBSTAT", "EBIU_MODE", "EBIU_FCTL", | |
75 | }; | |
76 | static const char * const *mmr_names; | |
77 | #define mmr_name(off) (mmr_names[(off) / 4] ? : "<INV>") | |
78 | ||
79 | static void | |
80 | bfin_ebiu_amc_write_amgctl (struct hw *me, struct bfin_ebiu_amc *amc, | |
81 | bu16 amgctl) | |
82 | { | |
83 | bu32 amben_old, amben, addr, i; | |
84 | ||
bc273e17 MF |
85 | amben_old = min ((amc->amgctl >> 1) & 0x7, 4); |
86 | amben = min ((amgctl >> 1) & 0x7, 4); | |
ef016f83 MF |
87 | |
88 | HW_TRACE ((me, "reattaching banks: AMGCTL 0x%04x[%u] -> 0x%04x[%u]", | |
89 | amc->amgctl, amben_old, amgctl, amben)); | |
90 | ||
91 | for (i = 0; i < 4; ++i) | |
92 | { | |
8dbfaed8 | 93 | addr = amc->bank_base + i * amc->bank_size; |
ef016f83 MF |
94 | |
95 | if (i < amben_old) | |
96 | { | |
97 | HW_TRACE ((me, "detaching bank %u (%#x base)", i, addr)); | |
98 | sim_core_detach (hw_system (me), NULL, 0, 0, addr); | |
99 | } | |
100 | ||
101 | if (i < amben) | |
102 | { | |
103 | struct hw *slave = amc->slaves[i]; | |
104 | ||
105 | HW_TRACE ((me, "attaching bank %u (%#x base) to %s", i, addr, | |
106 | slave ? hw_path (slave) : "<floating pins>")); | |
107 | ||
108 | sim_core_attach (hw_system (me), NULL, 0, access_read_write_exec, | |
109 | 0, addr, amc->bank_size, 0, slave, NULL); | |
110 | } | |
111 | } | |
112 | ||
113 | amc->amgctl = amgctl; | |
114 | } | |
115 | ||
116 | static unsigned | |
117 | bf50x_ebiu_amc_io_write_buffer (struct hw *me, const void *source, int space, | |
118 | address_word addr, unsigned nr_bytes, | |
119 | struct bfin_ebiu_amc *amc, bu32 mmr_off, | |
120 | bu32 value) | |
121 | { | |
122 | switch (mmr_off) | |
123 | { | |
124 | case mmr_offset(amgctl): | |
466b619e MF |
125 | if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true)) |
126 | return 0; | |
ef016f83 MF |
127 | bfin_ebiu_amc_write_amgctl (me, amc, value); |
128 | break; | |
129 | case mmr_offset(bf50x.ambctl0): | |
130 | amc->bf50x.ambctl0 = value; | |
131 | break; | |
132 | case mmr_offset(bf50x.ambctl1): | |
133 | amc->bf50x.ambctl1 = value; | |
134 | break; | |
135 | case mmr_offset(bf50x.mode): | |
136 | /* XXX: implement this. */ | |
466b619e MF |
137 | if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true)) |
138 | return 0; | |
ef016f83 MF |
139 | break; |
140 | case mmr_offset(bf50x.fctl): | |
141 | /* XXX: implement this. */ | |
466b619e MF |
142 | if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true)) |
143 | return 0; | |
ef016f83 MF |
144 | break; |
145 | default: | |
146 | dv_bfin_mmr_invalid (me, addr, nr_bytes, true); | |
466b619e | 147 | return 0; |
ef016f83 MF |
148 | } |
149 | ||
150 | return nr_bytes; | |
151 | } | |
152 | ||
153 | static unsigned | |
154 | bf53x_ebiu_amc_io_write_buffer (struct hw *me, const void *source, int space, | |
155 | address_word addr, unsigned nr_bytes, | |
156 | struct bfin_ebiu_amc *amc, bu32 mmr_off, | |
157 | bu32 value) | |
158 | { | |
159 | switch (mmr_off) | |
160 | { | |
161 | case mmr_offset(amgctl): | |
466b619e MF |
162 | if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true)) |
163 | return 0; | |
ef016f83 MF |
164 | bfin_ebiu_amc_write_amgctl (me, amc, value); |
165 | break; | |
166 | case mmr_offset(bf53x.ambctl0): | |
167 | amc->bf53x.ambctl0 = value; | |
168 | break; | |
169 | case mmr_offset(bf53x.ambctl1): | |
170 | amc->bf53x.ambctl1 = value; | |
171 | break; | |
172 | default: | |
173 | dv_bfin_mmr_invalid (me, addr, nr_bytes, true); | |
466b619e | 174 | return 0; |
ef016f83 MF |
175 | } |
176 | ||
177 | return nr_bytes; | |
178 | } | |
179 | ||
180 | static unsigned | |
181 | bf54x_ebiu_amc_io_write_buffer (struct hw *me, const void *source, int space, | |
182 | address_word addr, unsigned nr_bytes, | |
183 | struct bfin_ebiu_amc *amc, bu32 mmr_off, | |
184 | bu32 value) | |
185 | { | |
186 | switch (mmr_off) | |
187 | { | |
188 | case mmr_offset(amgctl): | |
466b619e MF |
189 | if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true)) |
190 | return 0; | |
ef016f83 MF |
191 | bfin_ebiu_amc_write_amgctl (me, amc, value); |
192 | break; | |
193 | case mmr_offset(bf54x.ambctl0): | |
194 | amc->bf54x.ambctl0 = value; | |
195 | break; | |
196 | case mmr_offset(bf54x.ambctl1): | |
197 | amc->bf54x.ambctl1 = value; | |
198 | break; | |
199 | case mmr_offset(bf54x.mbsctl): | |
200 | /* XXX: implement this. */ | |
201 | break; | |
202 | case mmr_offset(bf54x.arbstat): | |
203 | /* XXX: implement this. */ | |
204 | break; | |
205 | case mmr_offset(bf54x.mode): | |
206 | /* XXX: implement this. */ | |
207 | break; | |
208 | case mmr_offset(bf54x.fctl): | |
209 | /* XXX: implement this. */ | |
210 | break; | |
211 | default: | |
212 | dv_bfin_mmr_invalid (me, addr, nr_bytes, true); | |
466b619e | 213 | return 0; |
ef016f83 MF |
214 | } |
215 | ||
216 | return nr_bytes; | |
217 | } | |
218 | ||
219 | static unsigned | |
220 | bfin_ebiu_amc_io_write_buffer (struct hw *me, const void *source, int space, | |
221 | address_word addr, unsigned nr_bytes) | |
222 | { | |
223 | struct bfin_ebiu_amc *amc = hw_data (me); | |
224 | bu32 mmr_off; | |
225 | bu32 value; | |
226 | ||
466b619e MF |
227 | /* Invalid access mode is higher priority than missing register. */ |
228 | if (!dv_bfin_mmr_require_16_32 (me, addr, nr_bytes, true)) | |
229 | return 0; | |
230 | ||
ef016f83 MF |
231 | value = dv_load_4 (source); |
232 | mmr_off = addr - amc->base; | |
233 | ||
234 | HW_TRACE_WRITE (); | |
235 | ||
236 | return amc->io_write (me, source, space, addr, nr_bytes, | |
237 | amc, mmr_off, value); | |
238 | } | |
239 | ||
240 | static unsigned | |
241 | bf50x_ebiu_amc_io_read_buffer (struct hw *me, void *dest, int space, | |
242 | address_word addr, unsigned nr_bytes, | |
243 | struct bfin_ebiu_amc *amc, bu32 mmr_off, | |
244 | void *valuep, bu16 *value16, bu32 *value32) | |
245 | { | |
246 | switch (mmr_off) | |
247 | { | |
248 | case mmr_offset(amgctl): | |
249 | case mmr_offset(bf50x.fctl): | |
466b619e MF |
250 | if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, false)) |
251 | return 0; | |
ef016f83 MF |
252 | dv_store_2 (dest, *value16); |
253 | break; | |
254 | case mmr_offset(bf50x.ambctl0): | |
255 | case mmr_offset(bf50x.ambctl1): | |
256 | case mmr_offset(bf50x.mode): | |
257 | dv_store_4 (dest, *value32); | |
258 | break; | |
259 | default: | |
260 | dv_bfin_mmr_invalid (me, addr, nr_bytes, false); | |
466b619e | 261 | return 0; |
ef016f83 MF |
262 | } |
263 | ||
264 | return nr_bytes; | |
265 | } | |
266 | ||
267 | static unsigned | |
268 | bf53x_ebiu_amc_io_read_buffer (struct hw *me, void *dest, int space, | |
269 | address_word addr, unsigned nr_bytes, | |
270 | struct bfin_ebiu_amc *amc, bu32 mmr_off, | |
271 | void *valuep, bu16 *value16, bu32 *value32) | |
272 | { | |
273 | switch (mmr_off) | |
274 | { | |
275 | case mmr_offset(amgctl): | |
466b619e MF |
276 | if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, false)) |
277 | return 0; | |
ef016f83 MF |
278 | dv_store_2 (dest, *value16); |
279 | break; | |
280 | case mmr_offset(bf53x.ambctl0): | |
281 | case mmr_offset(bf53x.ambctl1): | |
282 | dv_store_4 (dest, *value32); | |
283 | break; | |
284 | default: | |
285 | dv_bfin_mmr_invalid (me, addr, nr_bytes, false); | |
466b619e | 286 | return 0; |
ef016f83 MF |
287 | } |
288 | ||
289 | return nr_bytes; | |
290 | } | |
291 | ||
292 | static unsigned | |
293 | bf54x_ebiu_amc_io_read_buffer (struct hw *me, void *dest, int space, | |
294 | address_word addr, unsigned nr_bytes, | |
295 | struct bfin_ebiu_amc *amc, bu32 mmr_off, | |
296 | void *valuep, bu16 *value16, bu32 *value32) | |
297 | { | |
298 | switch (mmr_off) | |
299 | { | |
300 | case mmr_offset(amgctl): | |
466b619e MF |
301 | if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, false)) |
302 | return 0; | |
ef016f83 MF |
303 | dv_store_2 (dest, *value16); |
304 | break; | |
305 | case mmr_offset(bf54x.ambctl0): | |
306 | case mmr_offset(bf54x.ambctl1): | |
307 | case mmr_offset(bf54x.mbsctl): | |
308 | case mmr_offset(bf54x.arbstat): | |
309 | case mmr_offset(bf54x.mode): | |
310 | case mmr_offset(bf54x.fctl): | |
311 | dv_store_4 (dest, *value32); | |
312 | break; | |
313 | default: | |
314 | dv_bfin_mmr_invalid (me, addr, nr_bytes, false); | |
466b619e | 315 | return 0; |
ef016f83 MF |
316 | } |
317 | ||
318 | return nr_bytes; | |
319 | } | |
320 | ||
321 | static unsigned | |
322 | bfin_ebiu_amc_io_read_buffer (struct hw *me, void *dest, int space, | |
323 | address_word addr, unsigned nr_bytes) | |
324 | { | |
325 | struct bfin_ebiu_amc *amc = hw_data (me); | |
326 | bu32 mmr_off; | |
327 | void *valuep; | |
328 | ||
466b619e MF |
329 | /* Invalid access mode is higher priority than missing register. */ |
330 | if (!dv_bfin_mmr_require_16_32 (me, addr, nr_bytes, false)) | |
331 | return 0; | |
332 | ||
ef016f83 MF |
333 | mmr_off = addr - amc->base; |
334 | valuep = (void *)((unsigned long)amc + mmr_base() + mmr_off); | |
335 | ||
336 | HW_TRACE_READ (); | |
337 | ||
338 | return amc->io_read (me, dest, space, addr, nr_bytes, amc, | |
339 | mmr_off, valuep, valuep, valuep); | |
340 | } | |
341 | ||
342 | static void | |
343 | bfin_ebiu_amc_attach_address_callback (struct hw *me, | |
344 | int level, | |
345 | int space, | |
346 | address_word addr, | |
347 | address_word nr_bytes, | |
348 | struct hw *client) | |
349 | { | |
350 | struct bfin_ebiu_amc *amc = hw_data (me); | |
351 | ||
352 | HW_TRACE ((me, "attach - level=%d, space=%d, addr=0x%lx, nr_bytes=%lu, client=%s", | |
353 | level, space, (unsigned long) addr, (unsigned long) nr_bytes, hw_path (client))); | |
354 | ||
410bbc94 | 355 | if (addr + nr_bytes > ARRAY_SIZE (amc->slaves)) |
ef016f83 MF |
356 | hw_abort (me, "ebiu amc attaches are done in terms of banks"); |
357 | ||
358 | while (nr_bytes--) | |
359 | amc->slaves[addr + nr_bytes] = client; | |
360 | ||
361 | bfin_ebiu_amc_write_amgctl (me, amc, amc->amgctl); | |
362 | } | |
363 | ||
364 | static void | |
365 | attach_bfin_ebiu_amc_regs (struct hw *me, struct bfin_ebiu_amc *amc, | |
366 | unsigned reg_size) | |
367 | { | |
368 | address_word attach_address; | |
369 | int attach_space; | |
370 | unsigned attach_size; | |
371 | reg_property_spec reg; | |
372 | ||
373 | if (hw_find_property (me, "reg") == NULL) | |
374 | hw_abort (me, "Missing \"reg\" property"); | |
375 | ||
376 | if (!hw_find_reg_array_property (me, "reg", 0, ®)) | |
377 | hw_abort (me, "\"reg\" property must contain three addr/size entries"); | |
378 | ||
379 | if (hw_find_property (me, "type") == NULL) | |
380 | hw_abort (me, "Missing \"type\" property"); | |
381 | ||
382 | hw_unit_address_to_attach_address (hw_parent (me), | |
383 | ®.address, | |
384 | &attach_space, &attach_address, me); | |
385 | hw_unit_size_to_attach_size (hw_parent (me), ®.size, &attach_size, me); | |
386 | ||
387 | if (attach_size != reg_size) | |
388 | hw_abort (me, "\"reg\" size must be %#x", reg_size); | |
389 | ||
390 | hw_attach_address (hw_parent (me), | |
391 | 0, attach_space, attach_address, attach_size, me); | |
392 | ||
393 | amc->base = attach_address; | |
394 | } | |
395 | ||
396 | static void | |
397 | bfin_ebiu_amc_finish (struct hw *me) | |
398 | { | |
399 | struct bfin_ebiu_amc *amc; | |
400 | bu32 amgctl; | |
401 | unsigned reg_size; | |
402 | ||
403 | amc = HW_ZALLOC (me, struct bfin_ebiu_amc); | |
404 | ||
405 | set_hw_data (me, amc); | |
406 | set_hw_io_read_buffer (me, bfin_ebiu_amc_io_read_buffer); | |
407 | set_hw_io_write_buffer (me, bfin_ebiu_amc_io_write_buffer); | |
408 | set_hw_attach_address (me, bfin_ebiu_amc_attach_address_callback); | |
409 | ||
410 | amc->type = hw_find_integer_property (me, "type"); | |
411 | ||
412 | switch (amc->type) | |
413 | { | |
414 | case 500 ... 509: | |
415 | amc->io_write = bf50x_ebiu_amc_io_write_buffer; | |
416 | amc->io_read = bf50x_ebiu_amc_io_read_buffer; | |
417 | mmr_names = bf50x_mmr_names; | |
418 | reg_size = sizeof (amc->bf50x) + 4; | |
419 | ||
420 | /* Initialize the AMC. */ | |
8dbfaed8 | 421 | amc->bank_base = BFIN_EBIU_AMC_BASE; |
ef016f83 MF |
422 | amc->bank_size = 1 * 1024 * 1024; |
423 | amgctl = 0x00F3; | |
424 | amc->bf50x.ambctl0 = 0x0000FFC2; | |
425 | amc->bf50x.ambctl1 = 0x0000FFC2; | |
426 | amc->bf50x.mode = 0x0001; | |
427 | amc->bf50x.fctl = 0x0002; | |
428 | break; | |
429 | case 540 ... 549: | |
430 | amc->io_write = bf54x_ebiu_amc_io_write_buffer; | |
431 | amc->io_read = bf54x_ebiu_amc_io_read_buffer; | |
432 | mmr_names = bf54x_mmr_names; | |
433 | reg_size = sizeof (amc->bf54x) + 4; | |
434 | ||
435 | /* Initialize the AMC. */ | |
8dbfaed8 | 436 | amc->bank_base = BFIN_EBIU_AMC_BASE; |
ef016f83 MF |
437 | amc->bank_size = 64 * 1024 * 1024; |
438 | amgctl = 0x0002; | |
439 | amc->bf54x.ambctl0 = 0xFFC2FFC2; | |
440 | amc->bf54x.ambctl1 = 0xFFC2FFC2; | |
441 | amc->bf54x.fctl = 0x0006; | |
442 | break; | |
443 | case 510 ... 519: | |
444 | case 522 ... 527: | |
445 | case 531 ... 533: | |
446 | case 534: | |
447 | case 536: | |
448 | case 537: | |
449 | case 538 ... 539: | |
450 | case 561: | |
451 | amc->io_write = bf53x_ebiu_amc_io_write_buffer; | |
452 | amc->io_read = bf53x_ebiu_amc_io_read_buffer; | |
453 | mmr_names = bf53x_mmr_names; | |
454 | reg_size = sizeof (amc->bf53x) + 4; | |
455 | ||
456 | /* Initialize the AMC. */ | |
8dbfaed8 | 457 | amc->bank_base = BFIN_EBIU_AMC_BASE; |
ef016f83 MF |
458 | if (amc->type == 561) |
459 | amc->bank_size = 64 * 1024 * 1024; | |
460 | else | |
461 | amc->bank_size = 1 * 1024 * 1024; | |
462 | amgctl = 0x00F2; | |
463 | amc->bf53x.ambctl0 = 0xFFC2FFC2; | |
464 | amc->bf53x.ambctl1 = 0xFFC2FFC2; | |
465 | break; | |
466 | case 590 ... 599: /* BF59x has no AMC. */ | |
467 | default: | |
468 | hw_abort (me, "no support for EBIU AMC on this Blackfin model yet"); | |
469 | } | |
470 | ||
471 | attach_bfin_ebiu_amc_regs (me, amc, reg_size); | |
472 | ||
473 | bfin_ebiu_amc_write_amgctl (me, amc, amgctl); | |
474 | } | |
475 | ||
81d126c3 MF |
476 | const struct hw_descriptor dv_bfin_ebiu_amc_descriptor[] = |
477 | { | |
ef016f83 MF |
478 | {"bfin_ebiu_amc", bfin_ebiu_amc_finish,}, |
479 | {NULL, NULL}, | |
480 | }; |