1/* Blackfin External Bus Interface Unit (EBIU) Asynchronous Memory Controller 2 (AMC) model. 3 4 Copyright (C) 2010-2020 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 28struct 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 61static const char * const bf50x_mmr_names[] = 62{ 63 "EBIU_AMGCTL", "EBIU_AMBCTL0", "EBIU_AMBCTL1", 64 [mmr_idx (bf50x.mode)] = "EBIU_MODE", "EBIU_FCTL", 65}; 66static const char * const bf53x_mmr_names[] = 67{ 68 "EBIU_AMGCTL", "EBIU_AMBCTL0", "EBIU_AMBCTL1", 69}; 70static const char * const bf54x_mmr_names[] = 71{ 72 "EBIU_AMGCTL", "EBIU_AMBCTL0", "EBIU_AMBCTL1", 73 "EBIU_MSBCTL", "EBIU_ARBSTAT", "EBIU_MODE", "EBIU_FCTL", 74}; 75static const char * const *mmr_names; 76#define mmr_name(off) (mmr_names[(off) / 4] ? : "<INV>") 77 78static void 79bfin_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 115static unsigned 116bf50x_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 152static unsigned 153bf53x_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 179static unsigned 180bf54x_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 218static unsigned 219bfin_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 239static unsigned 240bf50x_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 266static unsigned 267bf53x_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 291static unsigned 292bf54x_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 320static unsigned 321bfin_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 341static void 342bfin_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 363static void 364attach_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, ®)) 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 ®.address, 383 &attach_space, &attach_address, me); 384 hw_unit_size_to_attach_size (hw_parent (me), ®.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 395static void 396bfin_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 475const struct hw_descriptor dv_bfin_ebiu_amc_descriptor[] = 476{ 477 {"bfin_ebiu_amc", bfin_ebiu_amc_finish,}, 478 {NULL, NULL}, 479}; 480