xref: /netbsd-src/external/gpl3/gdb/dist/sim/bfin/dv-bfin_ebiu_amc.c (revision 16dce51364ebe8aeafbae46bc5aa167b8115bc45)
1 /* Blackfin External Bus Interface Unit (EBIU) Asynchronous Memory Controller
2    (AMC) model.
3 
4    Copyright (C) 2010-2017 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_base, 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 
61 static const char * const bf50x_mmr_names[] =
62 {
63   "EBIU_AMGCTL", "EBIU_AMBCTL0", "EBIU_AMBCTL1",
64   [mmr_idx (bf50x.mode)] = "EBIU_MODE", "EBIU_FCTL",
65 };
66 static const char * const bf53x_mmr_names[] =
67 {
68   "EBIU_AMGCTL", "EBIU_AMBCTL0", "EBIU_AMBCTL1",
69 };
70 static const char * const bf54x_mmr_names[] =
71 {
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 = amc->bank_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       if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true))
125 	return 0;
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.  */
136       if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true))
137 	return 0;
138       break;
139     case mmr_offset(bf50x.fctl):
140       /* XXX: implement this.  */
141       if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true))
142 	return 0;
143       break;
144     default:
145       dv_bfin_mmr_invalid (me, addr, nr_bytes, true);
146       return 0;
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):
161       if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true))
162 	return 0;
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);
173       return 0;
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):
188       if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true))
189 	return 0;
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);
212       return 0;
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 
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 
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):
249       if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, false))
250 	return 0;
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);
260       return 0;
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):
275       if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, false))
276 	return 0;
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);
285       return 0;
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):
300       if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, false))
301 	return 0;
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);
314       return 0;
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 
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 
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 
354   if (addr + nr_bytes > ARRAY_SIZE (amc->slaves))
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, &reg))
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 				     &reg.address,
383 				     &attach_space, &attach_address, me);
384   hw_unit_size_to_attach_size (hw_parent (me), &reg.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.  */
420       amc->bank_base     = BFIN_EBIU_AMC_BASE;
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.  */
435       amc->bank_base     = BFIN_EBIU_AMC_BASE;
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.  */
456       amc->bank_base     = BFIN_EBIU_AMC_BASE;
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 
475 const struct hw_descriptor dv_bfin_ebiu_amc_descriptor[] =
476 {
477   {"bfin_ebiu_amc", bfin_ebiu_amc_finish,},
478   {NULL, NULL},
479 };
480