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