|
|
1.1 ! root 1: /* $Id: bus.c,v 1.3 2003/05/16 21:48:08 fredette Exp $ */ ! 2: ! 3: /* generic/gen-bus.c - generic bus support: */ ! 4: ! 5: /* ! 6: * Copyright (c) 2003 Matt Fredette ! 7: * All rights reserved. ! 8: * ! 9: * Redistribution and use in source and binary forms, with or without ! 10: * modification, are permitted provided that the following conditions ! 11: * are met: ! 12: * 1. Redistributions of source code must retain the above copyright ! 13: * notice, this list of conditions and the following disclaimer. ! 14: * 2. Redistributions in binary form must reproduce the above copyright ! 15: * notice, this list of conditions and the following disclaimer in the ! 16: * documentation and/or other materials provided with the distribution. ! 17: * 3. All advertising materials mentioning features or use of this software ! 18: * must display the following acknowledgement: ! 19: * This product includes software developed by Matt Fredette. ! 20: * 4. The name of the author may not be used to endorse or promote products ! 21: * derived from this software without specific prior written permission. ! 22: * ! 23: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ! 24: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ! 25: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ! 26: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, ! 27: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ! 28: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ! 29: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ! 30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, ! 31: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ! 32: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ! 33: * POSSIBILITY OF SUCH DAMAGE. ! 34: */ ! 35: ! 36: #include <tme/common.h> ! 37: _TME_RCSID("$Id: bus.c,v 1.3 2003/05/16 21:48:08 fredette Exp $"); ! 38: ! 39: /* includes: */ ! 40: #include <tme/generic/bus.h> ! 41: #include <stdlib.h> ! 42: #include <string.h> ! 43: ! 44: /* macros: */ ! 45: #define TME_BUS_LINE_RESET (0) ! 46: #define TME_BUS_LINE_HALT (1) ! 47: #define TME_BUS_LINE_INT (2) ! 48: ! 49: /* this does a binary search of the addressable connections: */ ! 50: int ! 51: tme_bus_address_search(struct tme_bus *bus, tme_bus_addr_t address) ! 52: { ! 53: int left, right, pivot; ! 54: struct tme_bus_connection_int *conn_int; ! 55: ! 56: /* initialize for the search: */ ! 57: left = 0; ! 58: right = bus->tme_bus_addressables_count - 1; ! 59: ! 60: /* do the search: */ ! 61: pivot = 0; ! 62: for (; left <= right; ) { ! 63: ! 64: /* get the pivot: */ ! 65: pivot = (left + right) / 2; ! 66: conn_int = bus->tme_bus_addressables[pivot]; ! 67: ! 68: /* if we have to move left: */ ! 69: if (address < conn_int->tme_bus_connection_int_address) { ! 70: /* if we're done searching, pivot is already the index of the ! 71: first element we need to shift to the right in order to ! 72: insert a new element: */ ! 73: right = pivot - 1; ! 74: } ! 75: ! 76: /* if we have to move right: */ ! 77: else if (address > (conn_int->tme_bus_connection_int_address ! 78: + conn_int->tme_bus_connection_int_address_last)) { ! 79: /* if we're done searching, pivot + 1 is the index of the ! 80: first element we need to shift to the right in order to ! 81: insert a new element: */ ! 82: left = ++pivot; ! 83: } ! 84: ! 85: /* we found the addressable: */ ! 86: else { ! 87: return (pivot); ! 88: } ! 89: } ! 90: ! 91: /* we failed to find an addressable that covers the address: */ ! 92: return (-1 - pivot); ! 93: } ! 94: ! 95: /* this fills a TLB entry: */ ! 96: int ! 97: tme_bus_tlb_fill(struct tme_bus *bus, ! 98: struct tme_bus_connection_int *conn_int_asker, ! 99: struct tme_bus_tlb *tlb, ! 100: tme_bus_addr_t address, ! 101: unsigned int cycles) ! 102: { ! 103: int pivot; ! 104: struct tme_bus_connection_int *conn_int; ! 105: struct tme_bus_connection *conn_bus_other; ! 106: tme_bus_addr_t sourced_address_mask, conn_address; ! 107: tme_bus_addr_t hole_first, hole_last; ! 108: struct tme_bus_tlb tlb_bus; ! 109: void *cycle_fault_private; ! 110: tme_bus_cycle_handler cycle_fault; ! 111: int rc; ! 112: ! 113: /* get the sourced address mask: */ ! 114: sourced_address_mask = conn_int_asker->tme_bus_connection_int_sourced; ! 115: ! 116: /* search for this address on the bus: */ ! 117: pivot = tme_bus_address_search(bus, sourced_address_mask | address); ! 118: ! 119: /* if this address doesn't exist: */ ! 120: if (pivot < 0) { ! 121: ! 122: /* save the bus' fault cycle handler: */ ! 123: cycle_fault_private = tlb->tme_bus_tlb_cycle_private; ! 124: cycle_fault = tlb->tme_bus_tlb_cycle; ! 125: ! 126: /* initialize the TLB entry: */ ! 127: tme_bus_tlb_initialize(tlb); ! 128: ! 129: /* this TLB entry can cover the entire hole in the address space, ! 130: limited by the sourced address mask of this device: */ ! 131: pivot = -1 - pivot; ! 132: hole_first = (pivot == 0 ! 133: ? 0 ! 134: : (bus->tme_bus_addressables[pivot - 1]->tme_bus_connection_int_address ! 135: + bus->tme_bus_addressables[pivot - 1]->tme_bus_connection_int_address_last ! 136: + 1)); ! 137: hole_first = TME_MAX(hole_first, sourced_address_mask); ! 138: hole_last = (pivot == bus->tme_bus_addressables_count ! 139: ? bus->tme_bus_address_mask ! 140: : bus->tme_bus_addressables[pivot]->tme_bus_connection_int_address - 1); ! 141: hole_last = TME_MIN(hole_last, ! 142: sourced_address_mask ! 143: + conn_int_asker->tme_bus_connection_int_address_last); ! 144: TME_ATOMIC_WRITE(tme_bus_addr_t, tlb->tme_bus_tlb_addr_first, hole_first); ! 145: TME_ATOMIC_WRITE(tme_bus_addr_t, tlb->tme_bus_tlb_addr_last, hole_last); ! 146: ! 147: /* reads and writes are allowed: */ ! 148: tlb->tme_bus_tlb_cycles_ok = TME_BUS_CYCLE_READ | TME_BUS_CYCLE_WRITE; ! 149: ! 150: /* reads and writes in this region always fault: */ ! 151: tlb->tme_bus_tlb_cycle_private = cycle_fault_private; ! 152: tlb->tme_bus_tlb_cycle = cycle_fault; ! 153: rc = TME_OK; ! 154: } ! 155: ! 156: /* otherwise, this address does exist: */ ! 157: else { ! 158: conn_int = bus->tme_bus_addressables[pivot]; ! 159: conn_bus_other = ! 160: (struct tme_bus_connection *) conn_int->tme_bus_connection_int.tme_bus_connection.tme_connection_other; ! 161: ! 162: /* call the TLB fill function for the connection: */ ! 163: conn_address = (sourced_address_mask | address) - conn_int->tme_bus_connection_int_address; ! 164: rc = (*conn_bus_other->tme_bus_tlb_fill)(conn_bus_other, tlb, ! 165: conn_address, cycles); ! 166: ! 167: /* if that succeeded: */ ! 168: if (rc == TME_OK) { ! 169: ! 170: /* create the mapping TLB entry: */ ! 171: TME_ATOMIC_WRITE(tme_bus_addr_t, tlb_bus.tme_bus_tlb_addr_first, ! 172: (conn_int->tme_bus_connection_int_address ! 173: - sourced_address_mask)); ! 174: TME_ATOMIC_WRITE(tme_bus_addr_t, tlb_bus.tme_bus_tlb_addr_last, ! 175: (conn_int->tme_bus_connection_int_address ! 176: + conn_int->tme_bus_connection_int_address_last ! 177: - sourced_address_mask)); ! 178: tlb_bus.tme_bus_tlb_cycles_ok = TME_BUS_CYCLE_READ | TME_BUS_CYCLE_WRITE; ! 179: ! 180: /* map the filled TLB entry: */ ! 181: tme_bus_tlb_map(tlb, conn_address, &tlb_bus, address); ! 182: } ! 183: } ! 184: ! 185: /* done: */ ! 186: return (rc); ! 187: } ! 188: ! 189: /* this allocates a new TLB set: */ ! 190: int ! 191: tme_bus_tlb_set_allocate(struct tme_bus *bus, ! 192: struct tme_bus_connection_int *conn_int_asker, ! 193: unsigned int count, unsigned int sizeof_one, ! 194: TME_ATOMIC_POINTER_TYPE(struct tme_bus_tlb **) _tlbs) ! 195: { ! 196: struct tme_bus_connection *conn_bus_other, *conn_bus_dma; ! 197: int conn_int_i; ! 198: int rc; ! 199: struct tme_bus_tlb *tlbs, *tlb; ! 200: unsigned int tlb_i; ! 201: ! 202: /* at most one of our addressable connections may provide a TLB set ! 203: allocator. generally, this means that connection is ! 204: DMA-controller-like connection to the bus, where it may need to ! 205: invalidate at any later time the TLBs it fills out, due to sudden ! 206: changes in how the DMA region on the bus is mapped: */ ! 207: conn_bus_dma = NULL; ! 208: for (conn_int_i = 0; ! 209: conn_int_i < bus->tme_bus_addressables_count; ! 210: conn_int_i++) { ! 211: conn_bus_other = ! 212: (struct tme_bus_connection *) bus->tme_bus_addressables[conn_int_i]->tme_bus_connection_int.tme_bus_connection.tme_connection_other; ! 213: ! 214: /* if this bus connection offers a TLB set allocator, it is ! 215: a DMA-controller-like connection to the bus: */ ! 216: if (conn_bus_other->tme_bus_tlb_set_allocate != NULL) { ! 217: ! 218: /* if there is more than one of these, it is likely a ! 219: configuration error. if we had some way of specifying which ! 220: of several DMA regions a given connection will always use, we ! 221: could avoid this: */ ! 222: if (conn_bus_dma != NULL) { ! 223: abort(); ! 224: } ! 225: ! 226: conn_bus_dma = conn_bus_other; ! 227: } ! 228: } ! 229: ! 230: /* if there is a DMA-controller-like connection to the bus, ! 231: let it allocate the TLB set: */ ! 232: if (conn_bus_dma != NULL) { ! 233: rc = (*conn_bus_dma->tme_bus_tlb_set_allocate) ! 234: (conn_bus_dma, count, sizeof_one, _tlbs); ! 235: } ! 236: ! 237: /* otherwise, allocate and initialize a singleton set ourselves: */ ! 238: else { ! 239: tlbs = (struct tme_bus_tlb *) tme_malloc(count * sizeof_one); ! 240: tlb = tlbs; ! 241: for (tlb_i = 0; tlb_i < count; tlb_i++) { ! 242: tme_bus_tlb_invalidate(tlb); ! 243: tlb = (struct tme_bus_tlb *) (((tme_uint8_t *) tlb) + sizeof_one); ! 244: } ! 245: TME_ATOMIC_WRITE(struct tme_bus_tlb *, *_tlbs, tlbs); ! 246: rc = TME_OK; ! 247: } ! 248: ! 249: /* done: */ ! 250: return (rc); ! 251: } ! 252: ! 253: /* this returns nonzero if the connection's address space is available: */ ! 254: int ! 255: tme_bus_connection_ok(struct tme_bus *bus, ! 256: struct tme_bus_connection_int *conn_int) ! 257: { ! 258: int pivot_start, pivot_end; ! 259: ! 260: /* if this connection isn't addressable, it's always OK: */ ! 261: if (!conn_int->tme_bus_connection_int_addressable) { ! 262: return (TRUE); ! 263: } ! 264: ! 265: /* the connection must fit on the bus: */ ! 266: if (conn_int->tme_bus_connection_int_address_last > ! 267: (bus->tme_bus_address_mask ! 268: - conn_int->tme_bus_connection_int_address)) { ! 269: return (FALSE); ! 270: } ! 271: ! 272: /* search for anything covering the start or end of the new ! 273: addressable: */ ! 274: pivot_start = ! 275: tme_bus_address_search(bus, ! 276: conn_int->tme_bus_connection_int_address); ! 277: pivot_end = ! 278: tme_bus_address_search(bus, ! 279: (conn_int->tme_bus_connection_int_address ! 280: + conn_int->tme_bus_connection_int_address_last)); ! 281: ! 282: /* both searches must have failed, and they must have stopped at the ! 283: same point in the sorted addressables, further indicating that no ! 284: addressable exists anywhere *between* the start and end of the ! 285: new addressable, either. otherwise, this connection fails: */ ! 286: if (pivot_start >= 0 ! 287: || pivot_end >= 0 ! 288: || pivot_start != pivot_end) { ! 289: return (FALSE); ! 290: } ! 291: ! 292: /* this connection's address space is available: */ ! 293: return (TRUE); ! 294: } ! 295: ! 296: /* this makes a new connection: */ ! 297: int ! 298: tme_bus_connection_make(struct tme_bus *bus, ! 299: struct tme_bus_connection_int *conn_int, ! 300: unsigned int state) ! 301: { ! 302: int pivot; ! 303: ! 304: /* if this connection is not full, return now: */ ! 305: if (state == TME_CONNECTION_HALF) { ! 306: return (TME_OK); ! 307: } ! 308: ! 309: /* add this connection to our list: */ ! 310: conn_int->tme_bus_connection_int.tme_bus_connection.tme_connection_next ! 311: = (struct tme_connection *) bus->tme_bus_connections; ! 312: bus->tme_bus_connections = conn_int; ! 313: ! 314: /* if this connection is addressable, and this is connection is now ! 315: fully made, add it to our list of addressables: */ ! 316: if (conn_int->tme_bus_connection_int_addressable ! 317: && state == TME_CONNECTION_FULL) { ! 318: ! 319: /* search for the place to insert this new addressable: */ ! 320: pivot = tme_bus_address_search(bus, conn_int->tme_bus_connection_int_address); ! 321: assert(pivot < 0); ! 322: pivot = -1 - pivot; ! 323: ! 324: /* if we have to, grow the addressable array: */ ! 325: if (bus->tme_bus_addressables_count ! 326: == bus->tme_bus_addressables_size) { ! 327: bus->tme_bus_addressables_size += (bus->tme_bus_addressables_size >> 1) + 1; ! 328: bus->tme_bus_addressables = tme_renew(struct tme_bus_connection_int *, ! 329: bus->tme_bus_addressables, ! 330: bus->tme_bus_addressables_size); ! 331: } ! 332: ! 333: /* move all of the later addressables down: */ ! 334: memmove(&bus->tme_bus_addressables[pivot + 1], ! 335: &bus->tme_bus_addressables[pivot], ! 336: sizeof(bus->tme_bus_addressables[pivot]) ! 337: * (bus->tme_bus_addressables_count ! 338: - pivot)); ! 339: ! 340: /* insert this new addressable: */ ! 341: bus->tme_bus_addressables[pivot] = conn_int; ! 342: bus->tme_bus_addressables_count++; ! 343: } ! 344: ! 345: return (TME_OK); ! 346: } ! 347: ! 348: /* this breaks a connection: */ ! 349: int ! 350: tme_bus_connection_break(struct tme_bus *bus, ! 351: struct tme_bus_connection_int *conn_int, ! 352: unsigned int state) ! 353: { ! 354: abort(); ! 355: } ! 356: ! 357: /* this map the first bus TLB entry to be valid on another bus, according to ! 358: the information in the second bus TLB entry: */ ! 359: void ! 360: tme_bus_tlb_map(struct tme_bus_tlb *tlb0, tme_bus_addr_t addr0, ! 361: const struct tme_bus_tlb *tlb1, tme_bus_addr_t addr1) ! 362: { ! 363: tme_bus_addr_t extra_before0, extra_after0; ! 364: tme_bus_addr_t extra_before1, extra_after1; ! 365: tme_bus_addr_t addr_offset; ! 366: unsigned int cycles_ok; ! 367: ! 368: /* get the address offset: */ ! 369: addr_offset = addr1 - addr0; ! 370: ! 371: /* intersect the amount of bus address space covered: */ ! 372: extra_before0 = addr0 - TME_ATOMIC_READ(tme_bus_addr_t, tlb0->tme_bus_tlb_addr_first); ! 373: extra_after0 = TME_ATOMIC_READ(tme_bus_addr_t, tlb0->tme_bus_tlb_addr_last) - addr0; ! 374: extra_before1 = addr1 - TME_ATOMIC_READ(tme_bus_addr_t, tlb1->tme_bus_tlb_addr_first); ! 375: extra_after1 = TME_ATOMIC_READ(tme_bus_addr_t, tlb1->tme_bus_tlb_addr_last) - addr1; ! 376: TME_ATOMIC_WRITE(tme_bus_addr_t, tlb0->tme_bus_tlb_addr_first, ! 377: addr1 - TME_MIN(extra_before0, extra_before1)); ! 378: TME_ATOMIC_WRITE(tme_bus_addr_t, tlb0->tme_bus_tlb_addr_last, ! 379: addr1 + TME_MIN(extra_after0, extra_after1)); ! 380: ! 381: /* intersect the kinds of bus cycles allowed: */ ! 382: cycles_ok = (tlb0->tme_bus_tlb_cycles_ok &= tlb1->tme_bus_tlb_cycles_ok); ! 383: if (!(cycles_ok & TME_BUS_CYCLE_READ)) { ! 384: tlb0->tme_bus_tlb_emulator_off_read = TME_EMULATOR_OFF_UNDEF; ! 385: } ! 386: else if (tlb0->tme_bus_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF) { ! 387: tlb0->tme_bus_tlb_emulator_off_read -= addr_offset; ! 388: } ! 389: if (!(cycles_ok & TME_BUS_CYCLE_WRITE)) { ! 390: tlb0->tme_bus_tlb_emulator_off_write = TME_EMULATOR_OFF_UNDEF; ! 391: } ! 392: else if (tlb0->tme_bus_tlb_emulator_off_write != TME_EMULATOR_OFF_UNDEF) { ! 393: tlb0->tme_bus_tlb_emulator_off_write -= addr_offset; ! 394: } ! 395: ! 396: /* update the address shift for the cycle handler: */ ! 397: tlb0->tme_bus_tlb_addr_offset -= addr_offset; ! 398: } ! 399: ! 400: /* this invalidates a bus TLB entry: */ ! 401: void ! 402: tme_bus_tlb_invalidate(struct tme_bus_tlb *tlb) ! 403: { ! 404: ! 405: /* make the first address covered all-bits-one. the only bus TLB ! 406: entries this will not invalidate are those that have a last ! 407: address covered of all-bits one: */ ! 408: TME_ATOMIC_WRITE(tme_bus_addr_t, tlb->tme_bus_tlb_addr_first, -1); ! 409: ! 410: /* make the last address covered all-bits-zero. this will ! 411: invalidate the TLB entries we didn't catch above: */ ! 412: TME_ATOMIC_WRITE(tme_bus_addr_t, tlb->tme_bus_tlb_addr_last, 0); ! 413: } ! 414: ! 415: /* this initializes a bus TLB entry: */ ! 416: void ! 417: tme_bus_tlb_initialize(struct tme_bus_tlb *tlb) ! 418: { ! 419: ! 420: /* make the first address covered all-bits-one: */ ! 421: TME_ATOMIC_WRITE(tme_bus_addr_t, tlb->tme_bus_tlb_addr_first, -1); ! 422: ! 423: /* make the last address covered all-bits-zero: */ ! 424: TME_ATOMIC_WRITE(tme_bus_addr_t, tlb->tme_bus_tlb_addr_last, 0); ! 425: ! 426: /* no fast (memory) transfers allowed: */ ! 427: tlb->tme_bus_tlb_emulator_off_read = TME_EMULATOR_OFF_UNDEF; ! 428: tlb->tme_bus_tlb_emulator_off_write = TME_EMULATOR_OFF_UNDEF; ! 429: tlb->tme_bus_tlb_rwlock = NULL; ! 430: ! 431: /* no bus cycles allowed: */ ! 432: tlb->tme_bus_tlb_cycles_ok = TME_BUS_CYCLE_UNDEF; ! 433: ! 434: /* no address offset or shift: */ ! 435: tlb->tme_bus_tlb_addr_offset = 0; ! 436: tlb->tme_bus_tlb_addr_shift = 0; ! 437: ! 438: /* no bus cycle handler: */ ! 439: tlb->tme_bus_tlb_cycle_private = NULL; ! 440: tlb->tme_bus_tlb_cycle = NULL; ! 441: ! 442: /* no bus fault handlers: */ ! 443: tlb->tme_bus_tlb_fault_handler_count = 0; ! 444: } ! 445: ! 446: /* this calls a TLB entry's fault handlers: */ ! 447: int ! 448: tme_bus_tlb_fault(struct tme_bus_tlb *tlb, struct tme_bus_cycle *cycle, int rc) ! 449: { ! 450: unsigned int i; ! 451: ! 452: /* call all of the fault handlers: */ ! 453: for (i = 0; i < tlb->tme_bus_tlb_fault_handler_count; i++) { ! 454: rc = ((*tlb->tme_bus_tlb_fault_handlers[i].tme_bus_tlb_fault_handler) ! 455: (tlb->tme_bus_tlb_fault_handlers[i].tme_bus_tlb_fault_handler_private, ! 456: tlb, cycle, rc)); ! 457: } ! 458: ! 459: return (rc); ! 460: } ! 461: ! 462: /* this parses any bus address: */ ! 463: tme_bus_addr_t ! 464: tme_bus_addr_parse_any(const char *address_string, int *_failed) ! 465: { ! 466: unsigned long address; ! 467: char *units; ! 468: ! 469: /* catch a NULL string: */ ! 470: if (address_string == NULL) { ! 471: *_failed = TRUE; ! 472: return (0); ! 473: } ! 474: ! 475: /* assume we will succeed: */ ! 476: *_failed = FALSE; ! 477: ! 478: /* convert the string: */ ! 479: address = strtoul(address_string, &units, 0); ! 480: if (units == address_string) { ! 481: *_failed = TRUE; ! 482: return (0); ! 483: } ! 484: ! 485: /* handle any units: */ ! 486: if (!strcmp(units, "GB") ! 487: || !strcasecmp(units, "G")) { ! 488: return (((tme_bus_addr_t) address) * 1024 * 1024 * 1024); ! 489: } ! 490: else if (!strcmp(units, "MB") ! 491: || !strcasecmp(units, "M")) { ! 492: return (((tme_bus_addr_t) address) * 1024 * 1024); ! 493: } ! 494: else if (!strcmp(units, "KB") ! 495: || !strcasecmp(units, "k")) { ! 496: return (((tme_bus_addr_t) address) * 1024); ! 497: } ! 498: else if (*units == '\0') { ! 499: return ((tme_bus_addr_t) address); ! 500: } ! 501: *_failed = TRUE; ! 502: return (0); ! 503: } ! 504: ! 505: /* this parses a bus address that has a restricted range: */ ! 506: tme_bus_addr_t ! 507: tme_bus_addr_parse(const char *address_string, tme_bus_addr_t failure_value) ! 508: { ! 509: int failed; ! 510: tme_bus_addr_t address; ! 511: address = tme_bus_addr_parse_any(address_string, &failed); ! 512: return (failed ? failure_value : address); ! 513: } ! 514: ! 515: /* this transfers bytes between the two participants in a bus cycle: */ ! 516: void ! 517: tme_bus_cycle_xfer(struct tme_bus_cycle *cycle_init, struct tme_bus_cycle *cycle_resp) ! 518: { ! 519: struct tme_bus_cycle *cycle_reader, *cycle_writer; ! 520: int buffer_increment_mask_reader, buffer_increment_mask_writer; ! 521: int port_size_reader, port_size_writer; ! 522: int port_overlap_lane_least, port_overlap_size, port_overlap_size_lg2; ! 523: int lane, lane_end; ! 524: int lane_reader, lane_writer; ! 525: int lane_in_reader, lane_in_writer; ! 526: int lane_routing_offset_reader, lane_routing_offset_writer; ! 527: tme_bus_lane_t lane_routing_reader, lane_routing_writer; ! 528: tme_uint8_t lane_value; ! 529: int warn_on_lane; ! 530: unsigned int cycle_size_reader, cycle_size_writer; ! 531: ! 532: /* sort the initiator and responder into bus reader and bus writer: */ ! 533: if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_READ) { ! 534: assert(cycle_resp->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE); ! 535: cycle_reader = cycle_init; ! 536: cycle_writer = cycle_resp; ! 537: } ! 538: else { ! 539: assert(cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE); ! 540: assert(cycle_resp->tme_bus_cycle_type == TME_BUS_CYCLE_READ); ! 541: cycle_reader = cycle_resp; ! 542: cycle_writer = cycle_init; ! 543: } ! 544: ! 545: /* get the increment masks for the reader and writer. since ! 546: tme_bus_cycle_buffer_increment is always 1 or -1, this mask is ! 547: used to negate values without multiplication: */ ! 548: if (cycle_reader->tme_bus_cycle_buffer_increment == -1) { ! 549: buffer_increment_mask_reader = -1; ! 550: } ! 551: else { ! 552: assert(cycle_reader->tme_bus_cycle_buffer_increment == 1); ! 553: buffer_increment_mask_reader = 0; ! 554: } ! 555: if (cycle_writer->tme_bus_cycle_buffer_increment == -1) { ! 556: buffer_increment_mask_writer = -1; ! 557: } ! 558: else { ! 559: assert(cycle_writer->tme_bus_cycle_buffer_increment == 1); ! 560: buffer_increment_mask_writer = 0; ! 561: } ! 562: #define _TME_BUS_CYCLE_BUFFER_MULTIPLY(value, mask) \ ! 563: (((value) ^ (mask)) + ((mask) & 1)) ! 564: ! 565: /* get the sizes, in bytes, of the reader and writer ports: */ ! 566: port_size_reader = (1 << TME_BUS_CYCLE_PORT_SIZE_LG2(cycle_reader->tme_bus_cycle_port)); ! 567: port_size_writer = (1 << TME_BUS_CYCLE_PORT_SIZE_LG2(cycle_writer->tme_bus_cycle_port)); ! 568: ! 569: /* determine how the writer's port and the reader's port overlap: */ ! 570: port_overlap_size = port_size_writer; ! 571: port_overlap_lane_least = TME_BUS_CYCLE_PORT_LANE_LEAST(cycle_writer->tme_bus_cycle_port); ! 572: lane = TME_BUS_CYCLE_PORT_LANE_LEAST(cycle_reader->tme_bus_cycle_port); ! 573: if (port_overlap_lane_least < lane) { ! 574: port_overlap_size -= (lane - port_overlap_lane_least); ! 575: port_overlap_lane_least = lane; ! 576: } ! 577: lane += port_size_reader; ! 578: if ((port_overlap_lane_least + port_overlap_size) > lane) { ! 579: port_overlap_size -= (lane - (port_overlap_lane_least + port_overlap_size)); ! 580: } ! 581: assert(port_overlap_size > 0); ! 582: for (port_overlap_size_lg2 = 0; ! 583: (port_overlap_size >>= 1) != 0; ! 584: port_overlap_size_lg2++); ! 585: ! 586: /* select the reader's lane routing: */ ! 587: lane_routing_offset_reader = ! 588: TME_BUS_ROUTER_INDEX(TME_BUS_CYCLE_PORT_SIZE_LG2(cycle_reader->tme_bus_cycle_port), ! 589: port_overlap_size_lg2, ! 590: port_overlap_lane_least ! 591: - TME_BUS_CYCLE_PORT_LANE_LEAST(cycle_reader->tme_bus_cycle_port)); ! 592: ! 593: /* select the writer's lane routing: */ ! 594: lane_routing_offset_writer = ! 595: TME_BUS_ROUTER_INDEX(TME_BUS_CYCLE_PORT_SIZE_LG2(cycle_writer->tme_bus_cycle_port), ! 596: port_overlap_size_lg2, ! 597: port_overlap_lane_least ! 598: - TME_BUS_CYCLE_PORT_LANE_LEAST(cycle_writer->tme_bus_cycle_port)); ! 599: ! 600: /* loop over all byte lanes in one or both ports: */ ! 601: lane = TME_MIN(TME_BUS_CYCLE_PORT_LANE_LEAST(cycle_reader->tme_bus_cycle_port), ! 602: TME_BUS_CYCLE_PORT_LANE_LEAST(cycle_writer->tme_bus_cycle_port)); ! 603: lane_end = TME_MAX(TME_BUS_CYCLE_PORT_LANE_LEAST(cycle_reader->tme_bus_cycle_port) + port_size_reader, ! 604: TME_BUS_CYCLE_PORT_LANE_LEAST(cycle_writer->tme_bus_cycle_port) + port_size_writer); ! 605: cycle_size_reader = cycle_size_writer = 0; ! 606: for (; lane < lane_end; lane++) { ! 607: ! 608: /* assume that we won't have to warn on this lane: */ ! 609: warn_on_lane = FALSE; ! 610: ! 611: /* see if this lane falls in the reader or writer's port: */ ! 612: lane_reader = lane - TME_BUS_CYCLE_PORT_LANE_LEAST(cycle_reader->tme_bus_cycle_port); ! 613: lane_writer = lane - TME_BUS_CYCLE_PORT_LANE_LEAST(cycle_writer->tme_bus_cycle_port); ! 614: lane_in_reader = (lane_reader >= 0 && lane_reader < port_size_reader); ! 615: lane_in_writer = (lane_writer >= 0 && lane_writer < port_size_writer); ! 616: ! 617: /* get the value being written to this byte lane. assume a ! 618: garbage value: */ ! 619: lane_value = 0xd2; ! 620: ! 621: /* if this lane is in the writer's port, it may supply a real ! 622: lane value: */ ! 623: if (lane_in_writer) { ! 624: ! 625: /* get the routing for the writer: */ ! 626: lane_routing_writer = ! 627: cycle_writer->tme_bus_cycle_lane_routing[lane_routing_offset_writer + lane_writer]; ! 628: ! 629: /* if the writer doesn't expect this lane to be connected to the ! 630: reader, we will issue a warning on this lane: */ ! 631: if ((lane_routing_writer & TME_BUS_LANE_WARN) ! 632: && lane_in_reader) { ! 633: warn_on_lane = TRUE; ! 634: } ! 635: lane_routing_writer &= ~TME_BUS_LANE_WARN; ! 636: ! 637: /* dispatch on the routing to get the lane value: */ ! 638: if (lane_routing_writer == TME_BUS_LANE_ABORT) { ! 639: abort(); ! 640: } ! 641: else if (lane_routing_writer != TME_BUS_LANE_UNDEF) { ! 642: if (!(lane_routing_writer & TME_BUS_LANE_ROUTE_WRITE_IGNORE) ! 643: && lane_routing_writer >= cycle_size_writer) { ! 644: cycle_size_writer = lane_routing_writer + 1; ! 645: } ! 646: lane_routing_writer &= ~TME_BUS_LANE_ROUTE_WRITE_IGNORE; ! 647: ! 648: /* if the writer is the responder, make sure that only bytes ! 649: in the given register are ever referenced. given the ! 650: writer's port size, we could warp the reference index as ! 651: needed, but hopefully we'll never have to: */ ! 652: assert(!(cycle_writer == cycle_resp ! 653: && (((cycle_writer->tme_bus_cycle_address + lane_routing_writer) ! 654: ^ cycle_writer->tme_bus_cycle_address) ! 655: & ~(port_size_writer - 1)) != 0)); ! 656: ! 657: lane_value = ! 658: *(cycle_writer->tme_bus_cycle_buffer ! 659: + _TME_BUS_CYCLE_BUFFER_MULTIPLY(lane_routing_writer, ! 660: buffer_increment_mask_writer)); ! 661: } ! 662: } ! 663: ! 664: /* if this lane is in the reader's port, it may take the lane ! 665: value: */ ! 666: if (lane_in_reader) { ! 667: ! 668: /* get the routing for the reader: */ ! 669: lane_routing_reader = ! 670: cycle_reader->tme_bus_cycle_lane_routing[lane_routing_offset_reader + lane_reader]; ! 671: ! 672: /* if the reader doesn't expect this lane to be connected to the ! 673: writer, we will issue a warning on this lane: */ ! 674: if ((lane_routing_reader & TME_BUS_LANE_WARN) ! 675: && lane_in_writer) { ! 676: warn_on_lane = TRUE; ! 677: } ! 678: lane_routing_reader &= ~TME_BUS_LANE_WARN; ! 679: ! 680: /* dispatch on the routing to take the lane value: */ ! 681: if (lane_routing_reader == TME_BUS_LANE_ABORT) { ! 682: abort(); ! 683: } ! 684: else if (lane_routing_reader != TME_BUS_LANE_UNDEF ! 685: && !(lane_routing_reader & TME_BUS_LANE_ROUTE_WRITE_IGNORE)) { ! 686: if (lane_routing_reader >= cycle_size_reader) { ! 687: cycle_size_reader = lane_routing_reader + 1; ! 688: } ! 689: ! 690: /* if the reader is the responder, make sure that only bytes ! 691: in the given register are ever referenced. given the ! 692: reader's port size, we could warp the reference index as ! 693: needed, but hopefully we'll never have to: */ ! 694: assert(!(cycle_reader == cycle_resp ! 695: && (((cycle_reader->tme_bus_cycle_address + lane_routing_reader) ! 696: ^ cycle_reader->tme_bus_cycle_address) ! 697: & ~(port_size_reader - 1)) != 0)); ! 698: ! 699: *(cycle_reader->tme_bus_cycle_buffer ! 700: + _TME_BUS_CYCLE_BUFFER_MULTIPLY(lane_routing_reader, ! 701: buffer_increment_mask_reader)) = ! 702: lane_value; ! 703: } ! 704: } ! 705: ! 706: /* if we need to issue a warning on this lane: */ ! 707: if (warn_on_lane) { ! 708: /* XXX TBD: */ ! 709: abort(); ! 710: } ! 711: } ! 712: ! 713: /* give the reader feedback: */ ! 714: cycle_reader->tme_bus_cycle_size = cycle_size_reader; ! 715: cycle_reader->tme_bus_cycle_address += cycle_size_reader; ! 716: cycle_reader->tme_bus_cycle_buffer += ! 717: _TME_BUS_CYCLE_BUFFER_MULTIPLY(cycle_size_reader, ! 718: buffer_increment_mask_reader); ! 719: cycle_reader->tme_bus_cycle_lane_routing += lane_routing_offset_reader; ! 720: cycle_reader->tme_bus_cycle_port = ! 721: TME_BUS_CYCLE_PORT(port_overlap_lane_least, port_overlap_size_lg2); ! 722: ! 723: /* give the writer feedback: */ ! 724: cycle_writer->tme_bus_cycle_size = cycle_size_writer; ! 725: cycle_writer->tme_bus_cycle_address += cycle_size_writer; ! 726: cycle_writer->tme_bus_cycle_buffer += ! 727: _TME_BUS_CYCLE_BUFFER_MULTIPLY(cycle_size_writer, ! 728: buffer_increment_mask_writer); ! 729: cycle_writer->tme_bus_cycle_lane_routing += lane_routing_offset_writer; ! 730: cycle_writer->tme_bus_cycle_port = ! 731: TME_BUS_CYCLE_PORT(port_overlap_lane_least, port_overlap_size_lg2); ! 732: } ! 733: ! 734: /* this handles a bus cycle for a memory-like device: */ ! 735: void ! 736: tme_bus_cycle_xfer_memory(struct tme_bus_cycle *cycle_init, tme_uint8_t *memory, tme_bus_addr_t address_last) ! 737: { ! 738: tme_uint8_t memory_junk[sizeof(tme_bus_addr_t)]; ! 739: struct tme_bus_cycle cycle_resp; ! 740: ! 741: /* check the starting address: */ ! 742: assert(cycle_init->tme_bus_cycle_address <= address_last); ! 743: ! 744: /* get the start of the buffer for this starting address: */ ! 745: if (memory != NULL) { ! 746: memory += cycle_init->tme_bus_cycle_address; ! 747: } ! 748: else { ! 749: assert(sizeof(memory_junk) ! 750: >= (1 << TME_BUS_CYCLE_PORT_SIZE_LG2(cycle_init->tme_bus_cycle_port))); ! 751: memory = memory_junk; ! 752: } ! 753: ! 754: /* create the responder cycle: */ ! 755: cycle_resp.tme_bus_cycle_buffer = memory; ! 756: cycle_resp.tme_bus_cycle_buffer_increment = 1; ! 757: cycle_resp.tme_bus_cycle_lane_routing = cycle_init->tme_bus_cycle_lane_routing; ! 758: cycle_resp.tme_bus_cycle_address = cycle_init->tme_bus_cycle_address; ! 759: cycle_resp.tme_bus_cycle_type = (cycle_init->tme_bus_cycle_type ! 760: ^ (TME_BUS_CYCLE_WRITE ! 761: | TME_BUS_CYCLE_READ)); ! 762: cycle_resp.tme_bus_cycle_port = cycle_init->tme_bus_cycle_port; ! 763: ! 764: /* run the cycle: */ ! 765: tme_bus_cycle_xfer(cycle_init, &cycle_resp); ! 766: ! 767: /* check the finishing address: */ ! 768: assert((cycle_init->tme_bus_cycle_address - 1) <= address_last); ! 769: } ! 770:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.