Home | History | Annotate | Line # | Download | only in bfin
      1 /* Blackfin External Bus Interface Unit (EBIU) Asynchronous Memory Controller
      2    (AMC) model.
      3 
      4    Copyright (C) 2010-2024 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 /* This must come before any other includes.  */
     23 #include "defs.h"
     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;
     33   bu32 bank_base, bank_size;
     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 
     62 static const char * const bf50x_mmr_names[] =
     63 {
     64   "EBIU_AMGCTL", "EBIU_AMBCTL0", "EBIU_AMBCTL1",
     65   [mmr_idx (bf50x.mode)] = "EBIU_MODE", "EBIU_FCTL",
     66 };
     67 static const char * const bf53x_mmr_names[] =
     68 {
     69   "EBIU_AMGCTL", "EBIU_AMBCTL0", "EBIU_AMBCTL1",
     70 };
     71 static const char * const bf54x_mmr_names[] =
     72 {
     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 
     85   amben_old = min ((amc->amgctl >> 1) & 0x7, 4);
     86   amben = min ((amgctl >> 1) & 0x7, 4);
     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     {
     93       addr = amc->bank_base + i * amc->bank_size;
     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):
    125       if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true))
    126 	return 0;
    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.  */
    137       if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true))
    138 	return 0;
    139       break;
    140     case mmr_offset(bf50x.fctl):
    141       /* XXX: implement this.  */
    142       if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true))
    143 	return 0;
    144       break;
    145     default:
    146       dv_bfin_mmr_invalid (me, addr, nr_bytes, true);
    147       return 0;
    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):
    162       if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true))
    163 	return 0;
    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);
    174       return 0;
    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):
    189       if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true))
    190 	return 0;
    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);
    213       return 0;
    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 
    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 
    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):
    250       if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, false))
    251 	return 0;
    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);
    261       return 0;
    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):
    276       if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, false))
    277 	return 0;
    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);
    286       return 0;
    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):
    301       if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, false))
    302 	return 0;
    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);
    315       return 0;
    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 
    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 
    333   mmr_off = addr - amc->base;
    334   valuep = (void *)((uintptr_t)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 
    355   if (addr + nr_bytes > ARRAY_SIZE (amc->slaves))
    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, &reg))
    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 				     &reg.address,
    384 				     &attach_space, &attach_address, me);
    385   hw_unit_size_to_attach_size (hw_parent (me), &reg.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.  */
    421       amc->bank_base     = BFIN_EBIU_AMC_BASE;
    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.  */
    436       amc->bank_base     = BFIN_EBIU_AMC_BASE;
    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.  */
    457       amc->bank_base     = BFIN_EBIU_AMC_BASE;
    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 
    476 const struct hw_descriptor dv_bfin_ebiu_amc_descriptor[] =
    477 {
    478   {"bfin_ebiu_amc", bfin_ebiu_amc_finish,},
    479   {NULL, NULL},
    480 };
    481