]>
Commit | Line | Data |
---|---|---|
ef016f83 MF |
1 | /* Blackfin External Bus Interface Unit (EBIU) Asynchronous Memory Controller |
2 | (AMC) model. | |
3 | ||
4 | Copyright (C) 2010-2011 Free Software Foundation, Inc. | |
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; | |
32 | bu32 bank_size; | |
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 | ||
84 | amben_old = MIN ((amc->amgctl >> 1) & 0x7, 4); | |
85 | amben = MIN ((amgctl >> 1) & 0x7, 4); | |
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 | { | |
92 | addr = BFIN_EBIU_AMC_BASE + i * amc->bank_size; | |
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): | |
124 | dv_bfin_mmr_require_16 (me, addr, nr_bytes, true); | |
125 | bfin_ebiu_amc_write_amgctl (me, amc, value); | |
126 | break; | |
127 | case mmr_offset(bf50x.ambctl0): | |
128 | amc->bf50x.ambctl0 = value; | |
129 | break; | |
130 | case mmr_offset(bf50x.ambctl1): | |
131 | amc->bf50x.ambctl1 = value; | |
132 | break; | |
133 | case mmr_offset(bf50x.mode): | |
134 | /* XXX: implement this. */ | |
135 | dv_bfin_mmr_require_16 (me, addr, nr_bytes, true); | |
136 | break; | |
137 | case mmr_offset(bf50x.fctl): | |
138 | /* XXX: implement this. */ | |
139 | dv_bfin_mmr_require_16 (me, addr, nr_bytes, true); | |
140 | break; | |
141 | default: | |
142 | dv_bfin_mmr_invalid (me, addr, nr_bytes, true); | |
143 | break; | |
144 | } | |
145 | ||
146 | return nr_bytes; | |
147 | } | |
148 | ||
149 | static unsigned | |
150 | bf53x_ebiu_amc_io_write_buffer (struct hw *me, const void *source, int space, | |
151 | address_word addr, unsigned nr_bytes, | |
152 | struct bfin_ebiu_amc *amc, bu32 mmr_off, | |
153 | bu32 value) | |
154 | { | |
155 | switch (mmr_off) | |
156 | { | |
157 | case mmr_offset(amgctl): | |
158 | dv_bfin_mmr_require_16 (me, addr, nr_bytes, true); | |
159 | bfin_ebiu_amc_write_amgctl (me, amc, value); | |
160 | break; | |
161 | case mmr_offset(bf53x.ambctl0): | |
162 | amc->bf53x.ambctl0 = value; | |
163 | break; | |
164 | case mmr_offset(bf53x.ambctl1): | |
165 | amc->bf53x.ambctl1 = value; | |
166 | break; | |
167 | default: | |
168 | dv_bfin_mmr_invalid (me, addr, nr_bytes, true); | |
169 | break; | |
170 | } | |
171 | ||
172 | return nr_bytes; | |
173 | } | |
174 | ||
175 | static unsigned | |
176 | bf54x_ebiu_amc_io_write_buffer (struct hw *me, const void *source, int space, | |
177 | address_word addr, unsigned nr_bytes, | |
178 | struct bfin_ebiu_amc *amc, bu32 mmr_off, | |
179 | bu32 value) | |
180 | { | |
181 | switch (mmr_off) | |
182 | { | |
183 | case mmr_offset(amgctl): | |
184 | dv_bfin_mmr_require_16 (me, addr, nr_bytes, true); | |
185 | bfin_ebiu_amc_write_amgctl (me, amc, value); | |
186 | break; | |
187 | case mmr_offset(bf54x.ambctl0): | |
188 | amc->bf54x.ambctl0 = value; | |
189 | break; | |
190 | case mmr_offset(bf54x.ambctl1): | |
191 | amc->bf54x.ambctl1 = value; | |
192 | break; | |
193 | case mmr_offset(bf54x.mbsctl): | |
194 | /* XXX: implement this. */ | |
195 | break; | |
196 | case mmr_offset(bf54x.arbstat): | |
197 | /* XXX: implement this. */ | |
198 | break; | |
199 | case mmr_offset(bf54x.mode): | |
200 | /* XXX: implement this. */ | |
201 | break; | |
202 | case mmr_offset(bf54x.fctl): | |
203 | /* XXX: implement this. */ | |
204 | break; | |
205 | default: | |
206 | dv_bfin_mmr_invalid (me, addr, nr_bytes, true); | |
207 | break; | |
208 | } | |
209 | ||
210 | return nr_bytes; | |
211 | } | |
212 | ||
213 | static unsigned | |
214 | bfin_ebiu_amc_io_write_buffer (struct hw *me, const void *source, int space, | |
215 | address_word addr, unsigned nr_bytes) | |
216 | { | |
217 | struct bfin_ebiu_amc *amc = hw_data (me); | |
218 | bu32 mmr_off; | |
219 | bu32 value; | |
220 | ||
221 | value = dv_load_4 (source); | |
222 | mmr_off = addr - amc->base; | |
223 | ||
224 | HW_TRACE_WRITE (); | |
225 | ||
226 | return amc->io_write (me, source, space, addr, nr_bytes, | |
227 | amc, mmr_off, value); | |
228 | } | |
229 | ||
230 | static unsigned | |
231 | bf50x_ebiu_amc_io_read_buffer (struct hw *me, void *dest, int space, | |
232 | address_word addr, unsigned nr_bytes, | |
233 | struct bfin_ebiu_amc *amc, bu32 mmr_off, | |
234 | void *valuep, bu16 *value16, bu32 *value32) | |
235 | { | |
236 | switch (mmr_off) | |
237 | { | |
238 | case mmr_offset(amgctl): | |
239 | case mmr_offset(bf50x.fctl): | |
240 | dv_bfin_mmr_require_16 (me, addr, nr_bytes, false); | |
241 | dv_store_2 (dest, *value16); | |
242 | break; | |
243 | case mmr_offset(bf50x.ambctl0): | |
244 | case mmr_offset(bf50x.ambctl1): | |
245 | case mmr_offset(bf50x.mode): | |
246 | dv_store_4 (dest, *value32); | |
247 | break; | |
248 | default: | |
249 | dv_bfin_mmr_invalid (me, addr, nr_bytes, false); | |
250 | break; | |
251 | } | |
252 | ||
253 | return nr_bytes; | |
254 | } | |
255 | ||
256 | static unsigned | |
257 | bf53x_ebiu_amc_io_read_buffer (struct hw *me, void *dest, int space, | |
258 | address_word addr, unsigned nr_bytes, | |
259 | struct bfin_ebiu_amc *amc, bu32 mmr_off, | |
260 | void *valuep, bu16 *value16, bu32 *value32) | |
261 | { | |
262 | switch (mmr_off) | |
263 | { | |
264 | case mmr_offset(amgctl): | |
265 | dv_bfin_mmr_require_16 (me, addr, nr_bytes, false); | |
266 | dv_store_2 (dest, *value16); | |
267 | break; | |
268 | case mmr_offset(bf53x.ambctl0): | |
269 | case mmr_offset(bf53x.ambctl1): | |
270 | dv_store_4 (dest, *value32); | |
271 | break; | |
272 | default: | |
273 | dv_bfin_mmr_invalid (me, addr, nr_bytes, false); | |
274 | break; | |
275 | } | |
276 | ||
277 | return nr_bytes; | |
278 | } | |
279 | ||
280 | static unsigned | |
281 | bf54x_ebiu_amc_io_read_buffer (struct hw *me, void *dest, int space, | |
282 | address_word addr, unsigned nr_bytes, | |
283 | struct bfin_ebiu_amc *amc, bu32 mmr_off, | |
284 | void *valuep, bu16 *value16, bu32 *value32) | |
285 | { | |
286 | switch (mmr_off) | |
287 | { | |
288 | case mmr_offset(amgctl): | |
289 | dv_bfin_mmr_require_16 (me, addr, nr_bytes, false); | |
290 | dv_store_2 (dest, *value16); | |
291 | break; | |
292 | case mmr_offset(bf54x.ambctl0): | |
293 | case mmr_offset(bf54x.ambctl1): | |
294 | case mmr_offset(bf54x.mbsctl): | |
295 | case mmr_offset(bf54x.arbstat): | |
296 | case mmr_offset(bf54x.mode): | |
297 | case mmr_offset(bf54x.fctl): | |
298 | dv_store_4 (dest, *value32); | |
299 | break; | |
300 | default: | |
301 | dv_bfin_mmr_invalid (me, addr, nr_bytes, false); | |
302 | break; | |
303 | } | |
304 | ||
305 | return nr_bytes; | |
306 | } | |
307 | ||
308 | static unsigned | |
309 | bfin_ebiu_amc_io_read_buffer (struct hw *me, void *dest, int space, | |
310 | address_word addr, unsigned nr_bytes) | |
311 | { | |
312 | struct bfin_ebiu_amc *amc = hw_data (me); | |
313 | bu32 mmr_off; | |
314 | void *valuep; | |
315 | ||
316 | mmr_off = addr - amc->base; | |
317 | valuep = (void *)((unsigned long)amc + mmr_base() + mmr_off); | |
318 | ||
319 | HW_TRACE_READ (); | |
320 | ||
321 | return amc->io_read (me, dest, space, addr, nr_bytes, amc, | |
322 | mmr_off, valuep, valuep, valuep); | |
323 | } | |
324 | ||
325 | static void | |
326 | bfin_ebiu_amc_attach_address_callback (struct hw *me, | |
327 | int level, | |
328 | int space, | |
329 | address_word addr, | |
330 | address_word nr_bytes, | |
331 | struct hw *client) | |
332 | { | |
333 | struct bfin_ebiu_amc *amc = hw_data (me); | |
334 | ||
335 | HW_TRACE ((me, "attach - level=%d, space=%d, addr=0x%lx, nr_bytes=%lu, client=%s", | |
336 | level, space, (unsigned long) addr, (unsigned long) nr_bytes, hw_path (client))); | |
337 | ||
338 | if (addr + nr_bytes > 4) | |
339 | hw_abort (me, "ebiu amc attaches are done in terms of banks"); | |
340 | ||
341 | while (nr_bytes--) | |
342 | amc->slaves[addr + nr_bytes] = client; | |
343 | ||
344 | bfin_ebiu_amc_write_amgctl (me, amc, amc->amgctl); | |
345 | } | |
346 | ||
347 | static void | |
348 | attach_bfin_ebiu_amc_regs (struct hw *me, struct bfin_ebiu_amc *amc, | |
349 | unsigned reg_size) | |
350 | { | |
351 | address_word attach_address; | |
352 | int attach_space; | |
353 | unsigned attach_size; | |
354 | reg_property_spec reg; | |
355 | ||
356 | if (hw_find_property (me, "reg") == NULL) | |
357 | hw_abort (me, "Missing \"reg\" property"); | |
358 | ||
359 | if (!hw_find_reg_array_property (me, "reg", 0, ®)) | |
360 | hw_abort (me, "\"reg\" property must contain three addr/size entries"); | |
361 | ||
362 | if (hw_find_property (me, "type") == NULL) | |
363 | hw_abort (me, "Missing \"type\" property"); | |
364 | ||
365 | hw_unit_address_to_attach_address (hw_parent (me), | |
366 | ®.address, | |
367 | &attach_space, &attach_address, me); | |
368 | hw_unit_size_to_attach_size (hw_parent (me), ®.size, &attach_size, me); | |
369 | ||
370 | if (attach_size != reg_size) | |
371 | hw_abort (me, "\"reg\" size must be %#x", reg_size); | |
372 | ||
373 | hw_attach_address (hw_parent (me), | |
374 | 0, attach_space, attach_address, attach_size, me); | |
375 | ||
376 | amc->base = attach_address; | |
377 | } | |
378 | ||
379 | static void | |
380 | bfin_ebiu_amc_finish (struct hw *me) | |
381 | { | |
382 | struct bfin_ebiu_amc *amc; | |
383 | bu32 amgctl; | |
384 | unsigned reg_size; | |
385 | ||
386 | amc = HW_ZALLOC (me, struct bfin_ebiu_amc); | |
387 | ||
388 | set_hw_data (me, amc); | |
389 | set_hw_io_read_buffer (me, bfin_ebiu_amc_io_read_buffer); | |
390 | set_hw_io_write_buffer (me, bfin_ebiu_amc_io_write_buffer); | |
391 | set_hw_attach_address (me, bfin_ebiu_amc_attach_address_callback); | |
392 | ||
393 | amc->type = hw_find_integer_property (me, "type"); | |
394 | ||
395 | switch (amc->type) | |
396 | { | |
397 | case 500 ... 509: | |
398 | amc->io_write = bf50x_ebiu_amc_io_write_buffer; | |
399 | amc->io_read = bf50x_ebiu_amc_io_read_buffer; | |
400 | mmr_names = bf50x_mmr_names; | |
401 | reg_size = sizeof (amc->bf50x) + 4; | |
402 | ||
403 | /* Initialize the AMC. */ | |
404 | amc->bank_size = 1 * 1024 * 1024; | |
405 | amgctl = 0x00F3; | |
406 | amc->bf50x.ambctl0 = 0x0000FFC2; | |
407 | amc->bf50x.ambctl1 = 0x0000FFC2; | |
408 | amc->bf50x.mode = 0x0001; | |
409 | amc->bf50x.fctl = 0x0002; | |
410 | break; | |
411 | case 540 ... 549: | |
412 | amc->io_write = bf54x_ebiu_amc_io_write_buffer; | |
413 | amc->io_read = bf54x_ebiu_amc_io_read_buffer; | |
414 | mmr_names = bf54x_mmr_names; | |
415 | reg_size = sizeof (amc->bf54x) + 4; | |
416 | ||
417 | /* Initialize the AMC. */ | |
418 | amc->bank_size = 64 * 1024 * 1024; | |
419 | amgctl = 0x0002; | |
420 | amc->bf54x.ambctl0 = 0xFFC2FFC2; | |
421 | amc->bf54x.ambctl1 = 0xFFC2FFC2; | |
422 | amc->bf54x.fctl = 0x0006; | |
423 | break; | |
424 | case 510 ... 519: | |
425 | case 522 ... 527: | |
426 | case 531 ... 533: | |
427 | case 534: | |
428 | case 536: | |
429 | case 537: | |
430 | case 538 ... 539: | |
431 | case 561: | |
432 | amc->io_write = bf53x_ebiu_amc_io_write_buffer; | |
433 | amc->io_read = bf53x_ebiu_amc_io_read_buffer; | |
434 | mmr_names = bf53x_mmr_names; | |
435 | reg_size = sizeof (amc->bf53x) + 4; | |
436 | ||
437 | /* Initialize the AMC. */ | |
438 | if (amc->type == 561) | |
439 | amc->bank_size = 64 * 1024 * 1024; | |
440 | else | |
441 | amc->bank_size = 1 * 1024 * 1024; | |
442 | amgctl = 0x00F2; | |
443 | amc->bf53x.ambctl0 = 0xFFC2FFC2; | |
444 | amc->bf53x.ambctl1 = 0xFFC2FFC2; | |
445 | break; | |
446 | case 590 ... 599: /* BF59x has no AMC. */ | |
447 | default: | |
448 | hw_abort (me, "no support for EBIU AMC on this Blackfin model yet"); | |
449 | } | |
450 | ||
451 | attach_bfin_ebiu_amc_regs (me, amc, reg_size); | |
452 | ||
453 | bfin_ebiu_amc_write_amgctl (me, amc, amgctl); | |
454 | } | |
455 | ||
81d126c3 MF |
456 | const struct hw_descriptor dv_bfin_ebiu_amc_descriptor[] = |
457 | { | |
ef016f83 MF |
458 | {"bfin_ebiu_amc", bfin_ebiu_amc_finish,}, |
459 | {NULL, NULL}, | |
460 | }; |