1 /* Blackfin External Bus Interface Unit (EBIU) Asynchronous Memory Controller 2 (AMC) model. 3 4 Copyright (C) 2010-2011 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_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 = BFIN_EBIU_AMC_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 dv_bfin_mmr_require_16 (me, addr, nr_bytes, true); 125 bfin_ebiu_amc_write_amgctl (me, amc, value); 126 break; 127 case mmr_offset(bf50x.ambctl0): 128 amc->bf50x.ambctl0 = value; 129 break; 130 case mmr_offset(bf50x.ambctl1): 131 amc->bf50x.ambctl1 = value; 132 break; 133 case mmr_offset(bf50x.mode): 134 /* XXX: implement this. */ 135 dv_bfin_mmr_require_16 (me, addr, nr_bytes, true); 136 break; 137 case mmr_offset(bf50x.fctl): 138 /* XXX: implement this. */ 139 dv_bfin_mmr_require_16 (me, addr, nr_bytes, true); 140 break; 141 default: 142 dv_bfin_mmr_invalid (me, addr, nr_bytes, true); 143 break; 144 } 145 146 return nr_bytes; 147 } 148 149 static unsigned 150 bf53x_ebiu_amc_io_write_buffer (struct hw *me, const void *source, int space, 151 address_word addr, unsigned nr_bytes, 152 struct bfin_ebiu_amc *amc, bu32 mmr_off, 153 bu32 value) 154 { 155 switch (mmr_off) 156 { 157 case mmr_offset(amgctl): 158 dv_bfin_mmr_require_16 (me, addr, nr_bytes, true); 159 bfin_ebiu_amc_write_amgctl (me, amc, value); 160 break; 161 case mmr_offset(bf53x.ambctl0): 162 amc->bf53x.ambctl0 = value; 163 break; 164 case mmr_offset(bf53x.ambctl1): 165 amc->bf53x.ambctl1 = value; 166 break; 167 default: 168 dv_bfin_mmr_invalid (me, addr, nr_bytes, true); 169 break; 170 } 171 172 return nr_bytes; 173 } 174 175 static unsigned 176 bf54x_ebiu_amc_io_write_buffer (struct hw *me, const void *source, int space, 177 address_word addr, unsigned nr_bytes, 178 struct bfin_ebiu_amc *amc, bu32 mmr_off, 179 bu32 value) 180 { 181 switch (mmr_off) 182 { 183 case mmr_offset(amgctl): 184 dv_bfin_mmr_require_16 (me, addr, nr_bytes, true); 185 bfin_ebiu_amc_write_amgctl (me, amc, value); 186 break; 187 case mmr_offset(bf54x.ambctl0): 188 amc->bf54x.ambctl0 = value; 189 break; 190 case mmr_offset(bf54x.ambctl1): 191 amc->bf54x.ambctl1 = value; 192 break; 193 case mmr_offset(bf54x.mbsctl): 194 /* XXX: implement this. */ 195 break; 196 case mmr_offset(bf54x.arbstat): 197 /* XXX: implement this. */ 198 break; 199 case mmr_offset(bf54x.mode): 200 /* XXX: implement this. */ 201 break; 202 case mmr_offset(bf54x.fctl): 203 /* XXX: implement this. */ 204 break; 205 default: 206 dv_bfin_mmr_invalid (me, addr, nr_bytes, true); 207 break; 208 } 209 210 return nr_bytes; 211 } 212 213 static unsigned 214 bfin_ebiu_amc_io_write_buffer (struct hw *me, const void *source, int space, 215 address_word addr, unsigned nr_bytes) 216 { 217 struct bfin_ebiu_amc *amc = hw_data (me); 218 bu32 mmr_off; 219 bu32 value; 220 221 value = dv_load_4 (source); 222 mmr_off = addr - amc->base; 223 224 HW_TRACE_WRITE (); 225 226 return amc->io_write (me, source, space, addr, nr_bytes, 227 amc, mmr_off, value); 228 } 229 230 static unsigned 231 bf50x_ebiu_amc_io_read_buffer (struct hw *me, void *dest, int space, 232 address_word addr, unsigned nr_bytes, 233 struct bfin_ebiu_amc *amc, bu32 mmr_off, 234 void *valuep, bu16 *value16, bu32 *value32) 235 { 236 switch (mmr_off) 237 { 238 case mmr_offset(amgctl): 239 case mmr_offset(bf50x.fctl): 240 dv_bfin_mmr_require_16 (me, addr, nr_bytes, false); 241 dv_store_2 (dest, *value16); 242 break; 243 case mmr_offset(bf50x.ambctl0): 244 case mmr_offset(bf50x.ambctl1): 245 case mmr_offset(bf50x.mode): 246 dv_store_4 (dest, *value32); 247 break; 248 default: 249 dv_bfin_mmr_invalid (me, addr, nr_bytes, false); 250 break; 251 } 252 253 return nr_bytes; 254 } 255 256 static unsigned 257 bf53x_ebiu_amc_io_read_buffer (struct hw *me, void *dest, int space, 258 address_word addr, unsigned nr_bytes, 259 struct bfin_ebiu_amc *amc, bu32 mmr_off, 260 void *valuep, bu16 *value16, bu32 *value32) 261 { 262 switch (mmr_off) 263 { 264 case mmr_offset(amgctl): 265 dv_bfin_mmr_require_16 (me, addr, nr_bytes, false); 266 dv_store_2 (dest, *value16); 267 break; 268 case mmr_offset(bf53x.ambctl0): 269 case mmr_offset(bf53x.ambctl1): 270 dv_store_4 (dest, *value32); 271 break; 272 default: 273 dv_bfin_mmr_invalid (me, addr, nr_bytes, false); 274 break; 275 } 276 277 return nr_bytes; 278 } 279 280 static unsigned 281 bf54x_ebiu_amc_io_read_buffer (struct hw *me, void *dest, int space, 282 address_word addr, unsigned nr_bytes, 283 struct bfin_ebiu_amc *amc, bu32 mmr_off, 284 void *valuep, bu16 *value16, bu32 *value32) 285 { 286 switch (mmr_off) 287 { 288 case mmr_offset(amgctl): 289 dv_bfin_mmr_require_16 (me, addr, nr_bytes, false); 290 dv_store_2 (dest, *value16); 291 break; 292 case mmr_offset(bf54x.ambctl0): 293 case mmr_offset(bf54x.ambctl1): 294 case mmr_offset(bf54x.mbsctl): 295 case mmr_offset(bf54x.arbstat): 296 case mmr_offset(bf54x.mode): 297 case mmr_offset(bf54x.fctl): 298 dv_store_4 (dest, *value32); 299 break; 300 default: 301 dv_bfin_mmr_invalid (me, addr, nr_bytes, false); 302 break; 303 } 304 305 return nr_bytes; 306 } 307 308 static unsigned 309 bfin_ebiu_amc_io_read_buffer (struct hw *me, void *dest, int space, 310 address_word addr, unsigned nr_bytes) 311 { 312 struct bfin_ebiu_amc *amc = hw_data (me); 313 bu32 mmr_off; 314 void *valuep; 315 316 mmr_off = addr - amc->base; 317 valuep = (void *)((unsigned long)amc + mmr_base() + mmr_off); 318 319 HW_TRACE_READ (); 320 321 return amc->io_read (me, dest, space, addr, nr_bytes, amc, 322 mmr_off, valuep, valuep, valuep); 323 } 324 325 static void 326 bfin_ebiu_amc_attach_address_callback (struct hw *me, 327 int level, 328 int space, 329 address_word addr, 330 address_word nr_bytes, 331 struct hw *client) 332 { 333 struct bfin_ebiu_amc *amc = hw_data (me); 334 335 HW_TRACE ((me, "attach - level=%d, space=%d, addr=0x%lx, nr_bytes=%lu, client=%s", 336 level, space, (unsigned long) addr, (unsigned long) nr_bytes, hw_path (client))); 337 338 if (addr + nr_bytes > 4) 339 hw_abort (me, "ebiu amc attaches are done in terms of banks"); 340 341 while (nr_bytes--) 342 amc->slaves[addr + nr_bytes] = client; 343 344 bfin_ebiu_amc_write_amgctl (me, amc, amc->amgctl); 345 } 346 347 static void 348 attach_bfin_ebiu_amc_regs (struct hw *me, struct bfin_ebiu_amc *amc, 349 unsigned reg_size) 350 { 351 address_word attach_address; 352 int attach_space; 353 unsigned attach_size; 354 reg_property_spec reg; 355 356 if (hw_find_property (me, "reg") == NULL) 357 hw_abort (me, "Missing \"reg\" property"); 358 359 if (!hw_find_reg_array_property (me, "reg", 0, ®)) 360 hw_abort (me, "\"reg\" property must contain three addr/size entries"); 361 362 if (hw_find_property (me, "type") == NULL) 363 hw_abort (me, "Missing \"type\" property"); 364 365 hw_unit_address_to_attach_address (hw_parent (me), 366 ®.address, 367 &attach_space, &attach_address, me); 368 hw_unit_size_to_attach_size (hw_parent (me), ®.size, &attach_size, me); 369 370 if (attach_size != reg_size) 371 hw_abort (me, "\"reg\" size must be %#x", reg_size); 372 373 hw_attach_address (hw_parent (me), 374 0, attach_space, attach_address, attach_size, me); 375 376 amc->base = attach_address; 377 } 378 379 static void 380 bfin_ebiu_amc_finish (struct hw *me) 381 { 382 struct bfin_ebiu_amc *amc; 383 bu32 amgctl; 384 unsigned reg_size; 385 386 amc = HW_ZALLOC (me, struct bfin_ebiu_amc); 387 388 set_hw_data (me, amc); 389 set_hw_io_read_buffer (me, bfin_ebiu_amc_io_read_buffer); 390 set_hw_io_write_buffer (me, bfin_ebiu_amc_io_write_buffer); 391 set_hw_attach_address (me, bfin_ebiu_amc_attach_address_callback); 392 393 amc->type = hw_find_integer_property (me, "type"); 394 395 switch (amc->type) 396 { 397 case 500 ... 509: 398 amc->io_write = bf50x_ebiu_amc_io_write_buffer; 399 amc->io_read = bf50x_ebiu_amc_io_read_buffer; 400 mmr_names = bf50x_mmr_names; 401 reg_size = sizeof (amc->bf50x) + 4; 402 403 /* Initialize the AMC. */ 404 amc->bank_size = 1 * 1024 * 1024; 405 amgctl = 0x00F3; 406 amc->bf50x.ambctl0 = 0x0000FFC2; 407 amc->bf50x.ambctl1 = 0x0000FFC2; 408 amc->bf50x.mode = 0x0001; 409 amc->bf50x.fctl = 0x0002; 410 break; 411 case 540 ... 549: 412 amc->io_write = bf54x_ebiu_amc_io_write_buffer; 413 amc->io_read = bf54x_ebiu_amc_io_read_buffer; 414 mmr_names = bf54x_mmr_names; 415 reg_size = sizeof (amc->bf54x) + 4; 416 417 /* Initialize the AMC. */ 418 amc->bank_size = 64 * 1024 * 1024; 419 amgctl = 0x0002; 420 amc->bf54x.ambctl0 = 0xFFC2FFC2; 421 amc->bf54x.ambctl1 = 0xFFC2FFC2; 422 amc->bf54x.fctl = 0x0006; 423 break; 424 case 510 ... 519: 425 case 522 ... 527: 426 case 531 ... 533: 427 case 534: 428 case 536: 429 case 537: 430 case 538 ... 539: 431 case 561: 432 amc->io_write = bf53x_ebiu_amc_io_write_buffer; 433 amc->io_read = bf53x_ebiu_amc_io_read_buffer; 434 mmr_names = bf53x_mmr_names; 435 reg_size = sizeof (amc->bf53x) + 4; 436 437 /* Initialize the AMC. */ 438 if (amc->type == 561) 439 amc->bank_size = 64 * 1024 * 1024; 440 else 441 amc->bank_size = 1 * 1024 * 1024; 442 amgctl = 0x00F2; 443 amc->bf53x.ambctl0 = 0xFFC2FFC2; 444 amc->bf53x.ambctl1 = 0xFFC2FFC2; 445 break; 446 case 590 ... 599: /* BF59x has no AMC. */ 447 default: 448 hw_abort (me, "no support for EBIU AMC on this Blackfin model yet"); 449 } 450 451 attach_bfin_ebiu_amc_regs (me, amc, reg_size); 452 453 bfin_ebiu_amc_write_amgctl (me, amc, amgctl); 454 } 455 456 const struct hw_descriptor dv_bfin_ebiu_amc_descriptor[] = 457 { 458 {"bfin_ebiu_amc", bfin_ebiu_amc_finish,}, 459 {NULL, NULL}, 460 }; 461