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, ®)) 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 ®.address, 384 &attach_space, &attach_address, me); 385 hw_unit_size_to_attach_size (hw_parent (me), ®.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