Home | History | Annotate | Line # | Download | only in bfin
dv-bfin_ebiu_amc.c revision 1.1.1.5
      1 /* Blackfin External Bus Interface Unit (EBIU) Asynchronous Memory Controller
      2    (AMC) model.
      3 
      4    Copyright (C) 2010-2019 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