|
|
1.1 ! root 1: /* $Id: stp222x-iommu.c,v 1.3 2010/06/05 18:59:29 fredette Exp $ */ ! 2: ! 3: /* ic/stp222x-iommu.c - emulation of the IOMMU of the UPA to SBus ! 4: interface controller (STP2220) and the UPA to PCI interface ! 5: controller (STP2222): */ ! 6: ! 7: /* ! 8: * Copyright (c) 2009 Matt Fredette ! 9: * All rights reserved. ! 10: * ! 11: * Redistribution and use in source and binary forms, with or without ! 12: * modification, are permitted provided that the following conditions ! 13: * are met: ! 14: * 1. Redistributions of source code must retain the above copyright ! 15: * notice, this list of conditions and the following disclaimer. ! 16: * 2. Redistributions in binary form must reproduce the above copyright ! 17: * notice, this list of conditions and the following disclaimer in the ! 18: * documentation and/or other materials provided with the distribution. ! 19: * 3. All advertising materials mentioning features or use of this software ! 20: * must display the following acknowledgement: ! 21: * This product includes software developed by Matt Fredette. ! 22: * 4. The name of the author may not be used to endorse or promote products ! 23: * derived from this software without specific prior written permission. ! 24: * ! 25: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ! 26: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ! 27: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ! 28: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, ! 29: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ! 30: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ! 31: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ! 32: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, ! 33: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ! 34: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ! 35: * POSSIBILITY OF SUCH DAMAGE. ! 36: */ ! 37: ! 38: #include <tme/common.h> ! 39: _TME_RCSID("$Id: stp222x-iommu.c,v 1.3 2010/06/05 18:59:29 fredette Exp $"); ! 40: ! 41: /* includes: */ ! 42: #include "stp222x-impl.h" ! 43: ! 44: /* macros: */ ! 45: ! 46: /* IOMMU register offsets: */ ! 47: #define TME_STP222X_IOMMU_REGGROUP_INDEX_CR TME_STP222X_REGGROUP_INDEX(0x00) ! 48: #define TME_STP222X_IOMMU_REGGROUP_INDEX_TSB TME_STP222X_REGGROUP_INDEX(0x08) ! 49: #define TME_STP222X_IOMMU_REGGROUP_INDEX_FLUSH TME_STP222X_REGGROUP_INDEX(0x10) ! 50: #define TME_STP222X_IOMMU_REGGROUP_INDEX_DIAG4_VA TME_STP222X_REGGROUP_INDEX(0x00) ! 51: #define TME_STP222X_IOMMU_REGGROUP_INDEX_DIAG4_COMPARE TME_STP222X_REGGROUP_INDEX(0x08) ! 52: ! 53: /* the IOMMU control register: */ ! 54: #define TME_STP222X_IOMMU_CR_XLT_ERR_MASK ((2 << 26) - (1 << 25)) ! 55: #define TME_STP222X_IOMMU_CR_XLT_ERR (1 << 24) ! 56: #define TME_STP222X_IOMMU_CR_LRU_LCKEN (1 << 23) ! 57: #define TME_STP222X_IOMMU_CR_LRU_LCKPTR ((2 << 22) - (1 << 19)) ! 58: #define TME_STP222X_IOMMU_CR_TSB_SIZE_MASK ((2 << 18) - (1 << 16)) ! 59: #define TME_STP222X_IOMMU_CR_TBW_SIZE_MASK (1 << 2) ! 60: #define TME_STP222X_IOMMU_CR_TBW_SIZE_64KB (1 << 2) ! 61: #define TME_STP222X_IOMMU_CR_TBW_SIZE_8KB (0 << 2) ! 62: #define TME_STP222X_IOMMU_CR_MMU_DE (1 << 1) ! 63: #define TME_STP222X_IOMMU_CR_MMU_EN (1 << 0) ! 64: #define TME_STP222X_IOMMU_CR_MBZ \ ! 65: (((((tme_uint32_t) 2) << 31) - (1 << 27)) \ ! 66: | ((2 << 15) - (1 << 3))) ! 67: ! 68: /* an IOMMU TTE: */ ! 69: #define TME_STP222X_IOMMU_TTE_DATA_V (((tme_uint64_t) 1) << 63) ! 70: #define TME_STP222X_IOMMU_TTE_DATA_SIZE_MASK (((tme_uint64_t) 1) << 61) ! 71: #define TME_STP222X_IOMMU_TTE_DATA_SIZE_64KB (((tme_uint64_t) 1) << 61) ! 72: #define TME_STP222X_IOMMU_TTE_DATA_SIZE_8KB (((tme_uint64_t) 0) << 61) ! 73: #define TME_STP222X_IOMMU_TTE_STREAM (((tme_uint64_t) 1) << 60) ! 74: #define TME_STP2220_IOMMU_TTE_LOCALBUS (((tme_uint64_t) 1) << 59) ! 75: #define TME_STP222X_IOMMU_TTE_DATA_PA ((((tme_uint64_t) 2) << 40) - (1 << 13)) ! 76: #define TME_STP222X_IOMMU_TTE_CACHEABLE (1 << 4) ! 77: #define TME_STP222X_IOMMU_TTE_DATA_W (1 << 1) ! 78: ! 79: /* an IOMMU TLB data: */ ! 80: #define TME_STP222X_IOMMU_TLB_DATA_V (1 << 30) ! 81: #define TME_STP2220_IOMMU_TLB_DATA_LOCAL (1 << 29) ! 82: #define TME_STP222X_IOMMU_TLB_DATA_C (1 << 28) ! 83: #define TME_STP222X_IOMMU_TLB_DATA_PPN ((2 << 27) - (1 << 0)) ! 84: ! 85: /* an IOMMU TLB tag: */ ! 86: #define TME_STP222X_IOMMU_TLB_TAG_ERROR_MASK ((2 << 24) - (1 << 23)) ! 87: #define TME_STP222X_IOMMU_TLB_TAG_ERROR_PROTECTION (0 << 23) ! 88: #define TME_STP222X_IOMMU_TLB_TAG_ERROR_INVALID (1 << 23) ! 89: #define TME_STP222X_IOMMU_TLB_TAG_ERROR_TIMEOUT (2 << 23) ! 90: #define TME_STP222X_IOMMU_TLB_TAG_ERROR_ECC_UE (3 << 23) ! 91: #define TME_STP222X_IOMMU_TLB_TAG_ERROR (1 << 22) ! 92: #define TME_STP222X_IOMMU_TLB_TAG_W (1 << 21) ! 93: #define TME_STP222X_IOMMU_TLB_TAG_S (1 << 20) ! 94: #define TME_STP222X_IOMMU_TLB_TAG_SIZE_MASK (1 << 19) ! 95: #define TME_STP222X_IOMMU_TLB_TAG_SIZE_64KB (1 << 19) ! 96: #define TME_STP222X_IOMMU_TLB_TAG_SIZE_8KB (0 << 19) ! 97: #define TME_STP222X_IOMMU_TLB_TAG_VPN ((2 << 18) - (1 << 0)) ! 98: ! 99: /* _tme_stp222x_iommu_lookup() returns a mash of TLB tag and TLB data ! 100: information. the non-page-number fields of tag and data don't ! 101: overlap, and we don't include the page numbers, which leaves bits ! 102: left over. we use these bits for additional information: */ ! 103: /* NB: the specific values for TME_STP222X_IOMMU_TLB_MASH_UPA_* are ! 104: chosen to make it easy to quickly generate the corresponding ! 105: address mask: */ ! 106: #define TME_STP222X_IOMMU_TLB_MASH_UPA_41 (1 << 27) ! 107: #define TME_STP222X_IOMMU_TLB_MASH_UPA_31 (1 << 17) ! 108: #define TME_STP222X_IOMMU_TLB_MASH_FIXED (1 << 16) ! 109: #define TME_STP222X_IOMMU_TLB_MASH_INVALID_REQUEST (1 << 15) ! 110: #define TME_STP222X_IOMMU_TLB_MASH_MISS (1 << 14) ! 111: #define TME_STP222X_IOMMU_TLB_MASH_TLB_I(n) (n) ! 112: #define TME_STP222X_IOMMU_TLB_MASH_TLB_I_WHICH(x) ((x) % TME_STP222X_IOMMU_TLB_SIZE) ! 113: ! 114: /* sizes: */ ! 115: #define TME_STP222X_IOMMU_SIZE_LOG2_8KB (13) ! 116: #define TME_STP222X_IOMMU_SIZE_LOG2_64KB (16) ! 117: #define TME_STP222X_IOMMU_SIZE_8KB (1 << TME_STP222X_IOMMU_SIZE_LOG2_8KB) ! 118: #define TME_STP222X_IOMMU_SIZE_64KB (1 << TME_STP222X_IOMMU_SIZE_LOG2_64KB) ! 119: ! 120: /* this returns the log2 of the TBW size: */ ! 121: static tme_uint32_t ! 122: _tme_stp222x_iommu_tbw_size_log2(tme_uint32_t iommu_cr) ! 123: { ! 124: tme_uint32_t tbw_size; ! 125: ! 126: /* shift TBW_SIZE down to bit zero and clear all other bits: */ ! 127: tbw_size ! 128: = ((iommu_cr ! 129: / TME_STP222X_IOMMU_CR_TBW_SIZE_MASK) ! 130: & 1); ! 131: ! 132: /* multiply this by the difference between log2(64KB) and log2(8KB), ! 133: and then add log2(8KB). if TBW_SIZE was set, the result will be ! 134: log2(64KB), otherwise it will be log2(8KB): */ ! 135: return ((tbw_size ! 136: * (TME_STP222X_IOMMU_SIZE_LOG2_64KB ! 137: - TME_STP222X_IOMMU_SIZE_LOG2_8KB)) ! 138: + TME_STP222X_IOMMU_SIZE_LOG2_8KB); ! 139: } ! 140: ! 141: /* this returns the VPN mask for a TLB entry: */ ! 142: static tme_uint32_t ! 143: _tme_stp222x_iommu_tlb_tag_vpn_mask(tme_uint32_t tlb_tag) ! 144: { ! 145: tme_uint32_t tlb_tag_vpn_mask; ! 146: ! 147: /* copy the TLB entry tag into the TLB tag VPN mask, and shift the ! 148: page size bit down to bit zero and clear all other bits: */ ! 149: tlb_tag_vpn_mask ! 150: = ((tlb_tag ! 151: / TME_STP222X_IOMMU_TLB_TAG_SIZE_64KB) ! 152: & 1); ! 153: ! 154: /* add in (64KB / 8KB) - 1. if the page size is 64KB, this will ! 155: leave bits 0..2 zero and bit 3 set, otherwise the page size is ! 156: 8KB and this will leave bits 0..2 one and bit 3 clear: */ ! 157: tlb_tag_vpn_mask ! 158: += ((TME_STP222X_IOMMU_SIZE_64KB ! 159: / TME_STP222X_IOMMU_SIZE_8KB) ! 160: - 1); ! 161: ! 162: /* set bits 3..18 of the TLB tag VPN mask: */ ! 163: tlb_tag_vpn_mask ! 164: |= (TME_STP222X_IOMMU_TLB_TAG_VPN ! 165: - ((TME_STP222X_IOMMU_SIZE_64KB ! 166: / TME_STP222X_IOMMU_SIZE_8KB) ! 167: - 1)); ! 168: ! 169: /* return the VPN mask: */ ! 170: return (tlb_tag_vpn_mask); ! 171: } ! 172: ! 173: /* this returns the page size from a TLB tag: */ ! 174: static tme_uint32_t ! 175: _tme_stp222x_iommu_tlb_page_size(tme_uint32_t tlb_tag) ! 176: { ! 177: tme_uint32_t page_size; ! 178: ! 179: /* shift the page size bit down into bit 13 (8KB) and clear all ! 180: other bits: */ ! 181: #if TME_STP222X_IOMMU_TLB_TAG_SIZE_MASK < TME_STP222X_IOMMU_SIZE_8KB ! 182: #error "TME_STP222X_IOMMU_TLB_TAG_SIZE_MASK changed" ! 183: #endif ! 184: page_size ! 185: = ((tlb_tag ! 186: / (TME_STP222X_IOMMU_TLB_TAG_SIZE_MASK ! 187: / TME_STP222X_IOMMU_SIZE_8KB)) ! 188: & TME_STP222X_IOMMU_SIZE_8KB); ! 189: ! 190: /* add 64KB - 8KB to this value. the result will be 64KB if the ! 191: page size is 64KB, and (64KB - 8KB) if the page size is 8KB: */ ! 192: #if TME_STP222X_IOMMU_TLB_TAG_SIZE_64KB != TME_STP222X_IOMMU_TLB_TAG_SIZE_MASK ! 193: #error "TME_STP222X_IOMMU_TLB_TAG_SIZE_ values changed" ! 194: #endif ! 195: page_size ! 196: += (TME_STP222X_IOMMU_SIZE_64KB ! 197: - TME_STP222X_IOMMU_SIZE_8KB); ! 198: ! 199: /* clear all other bits except for 64KB and 8KB. only one of them ! 200: will be set: */ ! 201: page_size ! 202: &= (TME_STP222X_IOMMU_SIZE_64KB ! 203: | TME_STP222X_IOMMU_SIZE_8KB); ! 204: ! 205: return (page_size); ! 206: } ! 207: ! 208: /* this looks up an address in the IOMMU and returns a mash of TLB ! 209: entry tag and data information: */ ! 210: static tme_uint32_t ! 211: _tme_stp222x_iommu_tlb_mash(const struct tme_stp222x *stp222x, ! 212: tme_bus_addr64_t io_address, ! 213: unsigned int cycle_type) ! 214: { ! 215: tme_uint32_t iommu_cr; ! 216: tme_uint32_t io_space_size_log2; ! 217: tme_uint32_t io_address_0_31; ! 218: tme_uint32_t io_address_tag; ! 219: unsigned long tlb_i; ! 220: unsigned int tlb_count; ! 221: tme_uint32_t tlb_tag; ! 222: ! 223: /* get the IOMMU control register: */ ! 224: iommu_cr = stp222x->tme_stp222x_iommu_cr; ! 225: ! 226: /* if this is an stp2220: */ ! 227: if (TME_STP222X_IS_2220(stp222x)) { ! 228: ! 229: /* if the IOMMU is disabled: */ ! 230: if ((iommu_cr & TME_STP222X_IOMMU_CR_MMU_EN) == 0) { ! 231: ! 232: /* XXX FIXME - what does the stp2220 do if the IOMMU is ! 233: disabled? */ ! 234: abort(); ! 235: } ! 236: ! 237: /* otherwise, this address is translated: */ ! 238: /* XXX FIXME - does the STP2220 IOMMU have any address ranges that ! 239: are bypassed, even when the IOMMU is enabled? */ ! 240: } ! 241: ! 242: /* otherwise, this is an stp2222: */ ! 243: else { ! 244: ! 245: /* if this lookup is for a Dual Address Cycle: */ ! 246: if (FALSE) { ! 247: ! 248: /* if this is a bypass address: */ ! 249: if ((io_address >> 50) == 0x3fff) { ! 250: ! 251: /* bits 0..40 of the address become the UPA address. if bit ! 252: 40 is clear, the address is cacheable: */ ! 253: return ((io_address & (((tme_uint64_t) 1) << 40)) ! 254: ? (TME_STP222X_IOMMU_TLB_MASH_FIXED ! 255: | TME_STP222X_IOMMU_TLB_MASH_UPA_41 ! 256: | TME_STP222X_IOMMU_TLB_DATA_V ! 257: | TME_STP222X_IOMMU_TLB_TAG_W ! 258: | TME_STP222X_IOMMU_TLB_TAG_SIZE_8KB) ! 259: : (TME_STP222X_IOMMU_TLB_MASH_FIXED ! 260: | TME_STP222X_IOMMU_TLB_MASH_UPA_41 ! 261: | TME_STP222X_IOMMU_TLB_DATA_V ! 262: | TME_STP222X_IOMMU_TLB_DATA_C ! 263: | TME_STP222X_IOMMU_TLB_TAG_W ! 264: | TME_STP222X_IOMMU_TLB_TAG_SIZE_8KB)); ! 265: } ! 266: ! 267: /* otherwise, this address is local: */ ! 268: else { ! 269: return (TME_STP222X_IOMMU_TLB_MASH_FIXED ! 270: | TME_STP222X_IOMMU_TLB_DATA_V ! 271: | TME_STP2220_IOMMU_TLB_DATA_LOCAL ! 272: | TME_STP222X_IOMMU_TLB_TAG_W ! 273: | TME_STP222X_IOMMU_TLB_TAG_SIZE_8KB); ! 274: } ! 275: } ! 276: ! 277: /* otherwise, this is a Single Address Cycle: */ ! 278: else { ! 279: ! 280: /* if this address is translated or passed-through: */ ! 281: if (io_address & (((tme_uint32_t) 1) << 31)) { ! 282: ! 283: /* if this address is passed-through, bits 0..30 of the ! 284: address become the cacheable UPA address: */ ! 285: if ((iommu_cr & TME_STP222X_IOMMU_CR_MMU_EN) == 0) { ! 286: return (TME_STP222X_IOMMU_TLB_MASH_FIXED ! 287: | TME_STP222X_IOMMU_TLB_MASH_UPA_31 ! 288: | TME_STP222X_IOMMU_TLB_DATA_V ! 289: | TME_STP222X_IOMMU_TLB_TAG_W ! 290: | TME_STP222X_IOMMU_TLB_DATA_C ! 291: | TME_STP222X_IOMMU_TLB_TAG_SIZE_8KB); ! 292: } ! 293: ! 294: /* otherwise, this address is translated: */ ! 295: } ! 296: ! 297: /* otherwise, this address is local: */ ! 298: else { ! 299: return (TME_STP222X_IOMMU_TLB_MASH_FIXED ! 300: | TME_STP222X_IOMMU_TLB_DATA_V ! 301: | TME_STP2220_IOMMU_TLB_DATA_LOCAL ! 302: | TME_STP222X_IOMMU_TLB_TAG_W ! 303: | TME_STP222X_IOMMU_TLB_TAG_SIZE_8KB); ! 304: } ! 305: } ! 306: } ! 307: ! 308: /* truncate the I/O address to 32 bits: */ ! 309: io_address_0_31 = io_address; ! 310: ! 311: /* get the size of the translatable I/O address space: */ ! 312: io_space_size_log2 ! 313: = (_tme_stp222x_iommu_tbw_size_log2(iommu_cr) ! 314: + (10 /* log2(one TSB table size unit entry count) */ ! 315: + TME_FIELD_MASK_EXTRACTU(iommu_cr, TME_STP222X_IOMMU_CR_TSB_SIZE_MASK))); ! 316: ! 317: /* if this I/O address is not within the translatable space: */ ! 318: /* "Hardware does not prevent illegal combinations [of TSB_SIZ and ! 319: TBW_SIZ] from being programmed. If an illegal combination is ! 320: programmed into the IOMMU, all translation requests will be ! 321: rejected as invalid." */ ! 322: if (io_space_size_log2 >= 32 ! 323: || (io_address_0_31 ! 324: < (0 ! 325: - (((tme_uint32_t) 1) << io_space_size_log2)))) { ! 326: ! 327: /* this translation request is invalid: */ ! 328: return (TME_STP222X_IOMMU_TLB_MASH_FIXED ! 329: | TME_STP222X_IOMMU_TLB_MASH_INVALID_REQUEST ! 330: | TME_STP222X_IOMMU_TLB_TAG_SIZE_8KB); ! 331: } ! 332: ! 333: /* convert the I/O address into something that can be easily ! 334: compared to a TLB tag: */ ! 335: io_address_tag = io_address_0_31 / TME_STP222X_IOMMU_SIZE_8KB; ! 336: ! 337: /* loop over the TLB entries from most to least recently used: */ ! 338: tlb_i = stp222x->tme_stp222x_iommu_tlb_i_mru; ! 339: tlb_count = TME_STP222X_IOMMU_TLB_SIZE; ! 340: for (;;) { ! 341: ! 342: /* get this TLB entry tag: */ ! 343: tlb_tag = stp222x->tme_stp222x_iommu_tlb_tags[tlb_i]; ! 344: ! 345: /* if we hit in this TLB entry: */ ! 346: if (((tlb_tag ! 347: ^ io_address_tag) ! 348: & _tme_stp222x_iommu_tlb_tag_vpn_mask(tlb_tag)) == 0) { ! 349: ! 350: /* this address hit in the TLB: */ ! 351: return (TME_STP222X_IOMMU_TLB_MASH_TLB_I(tlb_i) ! 352: | (tlb_tag ! 353: & (TME_STP222X_IOMMU_TLB_TAG_ERROR_MASK ! 354: | TME_STP222X_IOMMU_TLB_TAG_ERROR ! 355: | TME_STP222X_IOMMU_TLB_TAG_W ! 356: | TME_STP222X_IOMMU_TLB_TAG_S ! 357: | TME_STP222X_IOMMU_TLB_TAG_SIZE_MASK)) ! 358: | (stp222x->tme_stp222x_iommu_tlb_data[tlb_i] ! 359: & (TME_STP222X_IOMMU_TLB_DATA_V ! 360: | TME_STP2220_IOMMU_TLB_DATA_LOCAL ! 361: | TME_STP222X_IOMMU_TLB_DATA_C))); ! 362: } ! 363: ! 364: /* advance to the next TLB entry in the LRU list: */ ! 365: tlb_i = stp222x->tme_stp222x_iommu_lru_next(tlb_i); ! 366: tlb_count--; ! 367: assert ((tlb_count == 0) == (tlb_i == stp222x->tme_stp222x_iommu_tlb_i_mru)); ! 368: ! 369: /* if we missed in all of the TLB entries: */ ! 370: if (__tme_predict_false(tlb_count == 0)) { ! 371: ! 372: /* this address missed in the TLB: */ ! 373: return (TME_STP222X_IOMMU_TLB_MASH_MISS ! 374: | TME_STP222X_IOMMU_TLB_TAG_SIZE_8KB); ! 375: } ! 376: } ! 377: /* NOTREACHED */ ! 378: } ! 379: ! 380: /* this looks up an address in the IOMMU and returns a mash of TLB ! 381: entry tag and data information and any slave connection index. if ! 382: there is a slave connection index, the address is converted to be ! 383: connection-relative: */ ! 384: static tme_uint32_t ! 385: _tme_stp222x_iommu_tlb_mash_slave(struct tme_bus_connection *io_conn_bus, ! 386: tme_bus_addr64_t *_io_to_slave_address, ! 387: unsigned int cycle_type, ! 388: tme_uint32_t *_slave_conn_index) ! 389: { ! 390: struct tme_stp222x *stp222x; ! 391: tme_uint32_t tlb_mash; ! 392: unsigned long tlb_i; ! 393: unsigned long tlb_i_next; ! 394: unsigned long tlb_i_prev; ! 395: tme_bus_addr64_t upa_address; ! 396: tme_uint32_t page_size_m1; ! 397: ! 398: /* recover our data structure: */ ! 399: stp222x = io_conn_bus->tme_bus_connection.tme_connection_element->tme_element_private; ! 400: ! 401: /* look up this address in the IOMMU TLB: */ ! 402: tlb_mash = _tme_stp222x_iommu_tlb_mash(stp222x, ! 403: *_io_to_slave_address, ! 404: cycle_type); ! 405: ! 406: /* XXX FIXME - is the LRU only updated on valid translations? */ ! 407: ! 408: /* if this address hit in the IOMMU TLB: */ ! 409: if ((tlb_mash ! 410: & (TME_STP222X_IOMMU_TLB_MASH_FIXED ! 411: | TME_STP222X_IOMMU_TLB_MASH_MISS)) == 0) { ! 412: ! 413: /* if the hit TLB entry is not already the most recently used: */ ! 414: tlb_i = TME_STP222X_IOMMU_TLB_MASH_TLB_I_WHICH(tlb_mash); ! 415: if (tlb_i != stp222x->tme_stp222x_iommu_tlb_i_mru) { ! 416: ! 417: /* remove this TLB entry from the LRU list: */ ! 418: tlb_i_next = stp222x->tme_stp222x_iommu_lru_next(tlb_i); ! 419: tlb_i_prev = stp222x->tme_stp222x_iommu_lru_prev(tlb_i); ! 420: stp222x->tme_stp222x_iommu_lru_next(tlb_i_prev) = tlb_i_next; ! 421: stp222x->tme_stp222x_iommu_lru_prev(tlb_i_next) = tlb_i_prev; ! 422: ! 423: /* add this TLB entry in the LRU list before the old most ! 424: recently used TLB entry: */ ! 425: tlb_i_next = stp222x->tme_stp222x_iommu_tlb_i_mru; ! 426: tlb_i_prev = stp222x->tme_stp222x_iommu_lru_prev(tlb_i_next); ! 427: stp222x->tme_stp222x_iommu_lru_next(tlb_i_prev) = tlb_i; ! 428: stp222x->tme_stp222x_iommu_lru_prev(tlb_i) = tlb_i_prev; ! 429: stp222x->tme_stp222x_iommu_lru_next(tlb_i) = tlb_i_next; ! 430: stp222x->tme_stp222x_iommu_lru_prev(tlb_i_next) = tlb_i; ! 431: ! 432: /* this TLB entry is now the most recently used: */ ! 433: stp222x->tme_stp222x_iommu_tlb_i_mru = tlb_i; ! 434: } ! 435: } ! 436: ! 437: /* if we don't have a valid translation for this address: */ ! 438: if ((tlb_mash & TME_STP222X_IOMMU_TLB_DATA_V) == 0) { ! 439: ! 440: /* return no connection index: */ ! 441: *_slave_conn_index = TME_STP222X_CONN_NULL; ! 442: return (tlb_mash); ! 443: } ! 444: ! 445: /* if this address is translated to itself, on its local bus: */ ! 446: if (tlb_mash & TME_STP2220_IOMMU_TLB_DATA_LOCAL) { ! 447: ! 448: /* look up this address on the connection's bus: */ ! 449: *_slave_conn_index ! 450: = tme_stp222x_aspace_lookup(stp222x, ! 451: (TME_STP222X_IS_2220(stp222x) ! 452: ? TME_STP2220_ASPACE_SBUS ! 453: : ((io_conn_bus->tme_bus_connection.tme_connection_id ! 454: & TME_STP2222_CONNID_BUS_WHICH) == 0) ! 455: ? TME_STP2222_ASPACE_PCI_MEMORY(0) ! 456: : TME_STP2222_ASPACE_PCI_MEMORY(1)), ! 457: _io_to_slave_address); ! 458: } ! 459: ! 460: /* otherwise, this address is not local: */ ! 461: else { ! 462: ! 463: /* if this address is translated directly into a UPA address: */ ! 464: if (tlb_mash ! 465: & (TME_STP222X_IOMMU_TLB_MASH_UPA_41 ! 466: | TME_STP222X_IOMMU_TLB_MASH_UPA_31)) { ! 467: ! 468: /* convert the address into the UPA address: */ ! 469: #if (TME_STP222X_IOMMU_TLB_MASH_UPA_41 / TME_STP222X_IOMMU_TLB_MASH_UPA_31) != (1 << (41 - 31)) ! 470: #error "TME_STP222X_IOMMU_TLB_MASH_ values changed" ! 471: #endif ! 472: upa_address = *_io_to_slave_address; ! 473: upa_address ! 474: &= (((tlb_mash ! 475: & (TME_STP222X_IOMMU_TLB_MASH_UPA_41 ! 476: | TME_STP222X_IOMMU_TLB_MASH_UPA_31)) ! 477: * ((((tme_bus_addr64_t) 1) << 31) ! 478: / TME_STP222X_IOMMU_TLB_MASH_UPA_31)) ! 479: - 1); ! 480: } ! 481: ! 482: /* otherwise, this address is translated through the TLB entry data ! 483: into a UPA address: */ ! 484: else { ! 485: ! 486: /* get the page size mask: */ ! 487: page_size_m1 = _tme_stp222x_iommu_tlb_page_size(tlb_mash) - 1; ! 488: ! 489: /* convert the TLB entry data into the base UPA address: */ ! 490: upa_address ! 491: = (stp222x->tme_stp222x_iommu_tlb_data[TME_STP222X_IOMMU_TLB_MASH_TLB_I_WHICH(tlb_mash)] ! 492: & TME_STP222X_IOMMU_TLB_DATA_PPN); ! 493: upa_address *= TME_STP222X_IOMMU_SIZE_8KB; ! 494: upa_address &= ~ (tme_bus_addr64_t) page_size_m1; ! 495: ! 496: /* add in the offset: */ ! 497: upa_address |= ((tme_bus_addr32_t) *_io_to_slave_address) & page_size_m1; ! 498: ! 499: /* return the UPA bus and address: */ ! 500: *_slave_conn_index = TME_STP222X_CONN_UPA; ! 501: *_io_to_slave_address = upa_address; ! 502: } ! 503: } ! 504: ! 505: return (tlb_mash); ! 506: } ! 507: ! 508: /* this handles an IOMMU bus cycle: */ ! 509: void ! 510: tme_stp222x_iommu_cycle(struct tme_bus_connection *master_conn_bus, ! 511: struct tme_bus_cycle *master_cycle, ! 512: tme_uint32_t *_master_fast_cycle_types, ! 513: struct tme_completion *master_completion) ! 514: { ! 515: struct tme_stp222x *stp222x; ! 516: struct tme_bus_tlb *tsb_tlb; ! 517: unsigned long tlb_i_must_hit; ! 518: tme_bus_addr64_t slave_address; ! 519: unsigned int slave_conn_index; ! 520: tme_uint32_t tlb_mash; ! 521: tme_uint32_t iommu_cr; ! 522: tme_uint32_t tsb_index; ! 523: tme_bus_addr64_t tsb_address; ! 524: struct tme_bus_connection *slave_conn_bus; ! 525: struct tme_bus_connection *slave_conn_bus_other; ! 526: struct tme_bus_tlb tsb_tlb_local; ! 527: #if TME_STP22XX_BUS_TRANSITION ! 528: int rc; ! 529: #endif /* TME_STP22XX_BUS_TRANSITION */ ! 530: tme_uint64_t tte; ! 531: unsigned long tlb_i; ! 532: struct tme_stp222x_tlb_list *tlb_list; ! 533: signed long tlb_list_i; ! 534: struct tme_token *token; ! 535: tme_uint32_t tlb_data; ! 536: tme_uint32_t tlb_tag; ! 537: tme_uint32_t tlb_tag_error; ! 538: ! 539: /* enter: */ ! 540: stp222x = tme_stp222x_enter_master_bus(master_conn_bus); ! 541: ! 542: /* start this cycle: */ ! 543: assert (stp222x->tme_stp222x_master_completion == NULL); ! 544: stp222x->tme_stp222x_master_completion = &master_completion; ! 545: ! 546: /* start out using the stored TSB TLB: */ ! 547: tsb_tlb = &stp222x->tme_stp222x_iommu_tsb_tlb; ! 548: ! 549: /* start out allowing the address to miss in the IOMMU TLB: */ ! 550: tlb_i_must_hit = TME_STP222X_IOMMU_TLB_SIZE; ! 551: ! 552: /* loop forever: */ ! 553: for (;;) { ! 554: ! 555: /* if this cycle has been aborted: */ ! 556: if (stp222x->tme_stp222x_master_completion != &master_completion) { ! 557: tme_stp222x_leave(stp222x); ! 558: return; ! 559: } ! 560: ! 561: /* translate this address: */ ! 562: slave_address = master_cycle->tme_bus_cycle_address; ! 563: tlb_mash = _tme_stp222x_iommu_tlb_mash_slave(master_conn_bus, ! 564: &slave_address, ! 565: master_cycle->tme_bus_cycle_type, ! 566: &slave_conn_index); ! 567: ! 568: /* if the translation for this address is valid: */ ! 569: if (tlb_mash & TME_STP222X_IOMMU_TLB_DATA_V) { ! 570: ! 571: /* stop now: */ ! 572: break; ! 573: } ! 574: ! 575: /* if the translation could not miss in the IOMMU TLB: */ ! 576: if (tlb_i_must_hit < TME_STP222X_IOMMU_TLB_SIZE) { ! 577: ! 578: /* the translation must have hit the specified IOMMU TLB entry, ! 579: even though that TLB entry is apparently invalid: */ ! 580: assert ((tlb_mash & TME_STP222X_IOMMU_TLB_MASH_FIXED) == 0 ! 581: && TME_STP222X_IOMMU_TLB_MASH_TLB_I_WHICH(tlb_mash) == tlb_i_must_hit); ! 582: ! 583: /* stop now: */ ! 584: break; ! 585: } ! 586: ! 587: /* get the IOMMU control register: */ ! 588: iommu_cr = stp222x->tme_stp222x_iommu_cr; ! 589: ! 590: /* get the index in the TSB table to read: */ ! 591: tsb_index = master_cycle->tme_bus_cycle_address; ! 592: tsb_index >>= _tme_stp222x_iommu_tbw_size_log2(iommu_cr); ! 593: tsb_index ! 594: &= ((2 << ! 595: (22 ! 596: + TME_FIELD_MASK_EXTRACTU(iommu_cr, TME_STP222X_IOMMU_CR_TSB_SIZE_MASK) ! 597: - TME_STP222X_IOMMU_SIZE_LOG2_8KB)) ! 598: - 1); ! 599: ! 600: /* get the address in the TSB table to read: */ ! 601: tsb_address = stp222x->tme_stp222x_iommu_tsb + (tsb_index * sizeof(tme_uint64_t)); ! 602: ! 603: /* busy the TSB TLB: */ ! 604: tme_bus_tlb_busy(tsb_tlb); ! 605: ! 606: /* if the TSB TLB is invalid or doesn't apply: */ ! 607: if (tme_bus_tlb_is_invalid(tsb_tlb) ! 608: || tsb_address < (tme_bus_addr64_t) tsb_tlb->tme_bus_tlb_addr_first ! 609: || tsb_address > (tme_bus_addr64_t) tsb_tlb->tme_bus_tlb_addr_last) { ! 610: ! 611: /* force the TLB entry to be invalid, since after we clear its ! 612: invalid token, we may abort this cycle without storing it: */ ! 613: stp222x->tme_stp222x_iommu_tsb_tlb.tme_bus_tlb_addr_first = 1; ! 614: stp222x->tme_stp222x_iommu_tsb_tlb.tme_bus_tlb_addr_last = 0; ! 615: ! 616: /* unbusy the TSB TLB for filling: */ ! 617: tme_bus_tlb_unbusy_fill(tsb_tlb); ! 618: ! 619: /* busy the UPA connection: */ ! 620: slave_conn_bus = tme_stp222x_slave_busy_bus(stp222x, TME_STP222X_CONN_UPA); ! 621: ! 622: /* leave: */ ! 623: tme_stp222x_leave(stp222x); ! 624: ! 625: /* fill the local TLB entry: */ ! 626: slave_conn_bus_other = (struct tme_bus_connection *) slave_conn_bus->tme_bus_connection.tme_connection_other; ! 627: tsb_tlb_local.tme_bus_tlb_token = &stp222x->tme_stp222x_iommu_tsb_tlb_token; ! 628: #if TME_STP22XX_BUS_TRANSITION ! 629: rc = ! 630: #endif /* TME_STP22XX_BUS_TRANSITION */ ! 631: (*slave_conn_bus_other->tme_bus_tlb_fill) ! 632: (slave_conn_bus_other, ! 633: &tsb_tlb_local, ! 634: tsb_address, ! 635: TME_BUS_CYCLE_READ); ! 636: #if TME_STP22XX_BUS_TRANSITION ! 637: assert (rc == TME_OK); ! 638: #endif /* TME_STP22XX_BUS_TRANSITION */ ! 639: ! 640: /* reenter: */ ! 641: stp222x = tme_stp222x_enter_bus(master_conn_bus); ! 642: ! 643: /* unbusy the UPA connection: */ ! 644: tme_stp222x_slave_unbusy(stp222x); ! 645: ! 646: /* switch to using the newly filled local TLB entry: */ ! 647: tsb_tlb = &tsb_tlb_local; ! 648: ! 649: /* loop now, to make sure we're still running: */ ! 650: continue; ! 651: } ! 652: ! 653: /* if we switched to the local TLB entry: */ ! 654: if (tsb_tlb == &tsb_tlb_local) { ! 655: ! 656: /* store the local TLB entry: */ ! 657: stp222x->tme_stp222x_iommu_tsb_tlb = tsb_tlb_local; ! 658: } ! 659: ! 660: /* the TSB TLB must allow fast reading: */ ! 661: if (tsb_tlb->tme_bus_tlb_emulator_off_read == TME_EMULATOR_OFF_UNDEF) { ! 662: abort(); ! 663: } ! 664: ! 665: /* read the TTE: */ ! 666: tte = tme_memory_bus_read64(((_tme_const tme_shared tme_uint64_t *) ! 667: (tsb_tlb->tme_bus_tlb_emulator_off_read ! 668: + tsb_address)), ! 669: tsb_tlb->tme_bus_tlb_rwlock, ! 670: sizeof(tme_uint64_t), ! 671: sizeof(tme_uint64_t)); ! 672: tte = tme_betoh_u64(tte); ! 673: ! 674: /* unbusy the TSB TLB: */ ! 675: tme_bus_tlb_unbusy(tsb_tlb); ! 676: ! 677: /* if a TLB entry is already allocated to this address: */ ! 678: if ((tlb_mash & TME_STP222X_IOMMU_TLB_MASH_MISS) == 0) { ! 679: ! 680: /* replace the same TLB entry: */ ! 681: tlb_i = TME_STP222X_IOMMU_TLB_MASH_TLB_I_WHICH(tlb_mash); ! 682: } ! 683: ! 684: /* otherwise, a TLB entry isn't already allocated to this address: */ ! 685: else { ! 686: ! 687: /* get the IOMMU control register: */ ! 688: iommu_cr = stp222x->tme_stp222x_iommu_cr; ! 689: ! 690: /* if the LRU is locked: */ ! 691: if (__tme_predict_false(iommu_cr & TME_STP222X_IOMMU_CR_LRU_LCKEN)) { ! 692: ! 693: /* replace the locked TLB entry: */ ! 694: tlb_i = TME_FIELD_MASK_EXTRACTU(iommu_cr, TME_STP222X_IOMMU_CR_LRU_LCKPTR); ! 695: } ! 696: ! 697: /* otherwise, the LRU is not locked: */ ! 698: else { ! 699: ! 700: /* replace the least recently used TLB entry: */ ! 701: tlb_i = stp222x->tme_stp222x_iommu_lru_prev(stp222x->tme_stp222x_iommu_tlb_i_mru); ! 702: } ! 703: } ! 704: ! 705: /* invalidate all of the TLBs associated with this IOMMU TLB ! 706: entry: */ ! 707: tlb_list = &stp222x->tme_stp222x_iommu_tlb_list[tlb_i]; ! 708: tlb_list_i = TME_STP222X_TLB_LIST_TOKENS_COUNT - 1; ! 709: do { ! 710: token = tlb_list->tme_stp222x_tlb_list_tokens[tlb_list_i]; ! 711: if (token != NULL) { ! 712: tlb_list->tme_stp222x_tlb_list_tokens[tlb_list_i] = NULL; ! 713: tme_token_invalidate(token); ! 714: } ! 715: } while (--tlb_list_i >= 0); ! 716: ! 717: /* make and store the TLB data: */ ! 718: #if (TME_STP222X_IOMMU_TLB_DATA_PPN & 1) == 0 ! 719: #error "TME_STP222X_IOMMU_TLB_DATA_PPN changed" ! 720: #endif ! 721: tlb_data = TME_FIELD_MASK_EXTRACTU(tte, TME_STP222X_IOMMU_TTE_DATA_PA); ! 722: if (tte & TME_STP222X_IOMMU_TTE_DATA_V) { ! 723: tlb_data += TME_STP222X_IOMMU_TLB_DATA_V; ! 724: } ! 725: if (tte & TME_STP2220_IOMMU_TTE_LOCALBUS) { ! 726: if (TME_STP222X_IS_2220(stp222x)) { ! 727: tlb_data += TME_STP2220_IOMMU_TLB_DATA_LOCAL; ! 728: } ! 729: } ! 730: if (tte & TME_STP222X_IOMMU_TTE_CACHEABLE) { ! 731: tlb_data += TME_STP222X_IOMMU_TLB_DATA_C; ! 732: } ! 733: stp222x->tme_stp222x_iommu_tlb_data[tlb_i] = tlb_data; ! 734: ! 735: /* make and store the TLB tag: */ ! 736: tlb_tag = (tme_uint32_t) master_cycle->tme_bus_cycle_address; ! 737: tlb_tag /= TME_STP222X_IOMMU_SIZE_8KB; ! 738: if (tte & TME_STP222X_IOMMU_TTE_DATA_W) { ! 739: tlb_tag += TME_STP222X_IOMMU_TLB_TAG_W; ! 740: } ! 741: if (tte & TME_STP222X_IOMMU_TTE_STREAM) { ! 742: tlb_tag += TME_STP222X_IOMMU_TLB_TAG_S; ! 743: } ! 744: if (tte & TME_STP222X_IOMMU_TTE_DATA_SIZE_MASK) { ! 745: tlb_tag += TME_STP222X_IOMMU_TLB_TAG_SIZE_MASK; ! 746: } ! 747: stp222x->tme_stp222x_iommu_tlb_tags[tlb_i] = tlb_tag; ! 748: ! 749: /* loop to look up this address in the IOMMU again. this time, it ! 750: must hit this entry: */ ! 751: tlb_i_must_hit = tlb_i; ! 752: ! 753: #if TME_STP22XX_BUS_TRANSITION ! 754: ! 755: /* invalidate the last TLB that missed the IOMMU TLB (because we ! 756: just filled the IOMMU TLB, we should get the master to refill ! 757: its TLB): */ ! 758: if (stp222x->tme_stp222x_iommu_tlb_missed_token != NULL) { ! 759: tme_token_invalidate(stp222x->tme_stp222x_iommu_tlb_missed_token); ! 760: stp222x->tme_stp222x_iommu_tlb_missed_token = NULL; ! 761: } ! 762: ! 763: #endif /* TME_STP22XX_BUS_TRANSITION */ ! 764: } ! 765: ! 766: /* assume that there is no IOMMU error: */ ! 767: tlb_tag_error = !TME_STP222X_IOMMU_TLB_TAG_ERROR; ! 768: ! 769: /* if the address is not writable: */ ! 770: if (__tme_predict_false((tlb_mash & TME_STP222X_IOMMU_TLB_TAG_W) == 0)) { ! 771: ! 772: /* the master can't do fast writes: */ ! 773: *_master_fast_cycle_types &= ~TME_BUS_CYCLE_WRITE; ! 774: ! 775: /* if this is a write: */ ! 776: if (__tme_predict_false(master_cycle->tme_bus_cycle_type & TME_BUS_CYCLE_WRITE)) { ! 777: ! 778: /* there is an IOMMU protection error: */ ! 779: tlb_tag_error ! 780: = (TME_STP222X_IOMMU_TLB_TAG_ERROR ! 781: + TME_STP222X_IOMMU_TLB_TAG_ERROR_PROTECTION); ! 782: } ! 783: } ! 784: ! 785: /* if we don't have a valid translation: */ ! 786: if (__tme_predict_false((tlb_mash & TME_STP222X_IOMMU_TLB_DATA_V) == 0)) { ! 787: ! 788: /* there is an IOMMU invalid error: */ ! 789: tlb_tag_error ! 790: = (TME_STP222X_IOMMU_TLB_TAG_ERROR ! 791: + TME_STP222X_IOMMU_TLB_TAG_ERROR_INVALID); ! 792: } ! 793: ! 794: /* if there is an IOMMU error: */ ! 795: if (tlb_tag_error != !TME_STP222X_IOMMU_TLB_TAG_ERROR) { ! 796: ! 797: /* if this isn't a fixed translation: */ ! 798: if ((tlb_mash & TME_STP222X_IOMMU_TLB_MASH_FIXED) == 0) { ! 799: ! 800: /* update the error in the tag for the TLB entry: */ ! 801: tlb_i = TME_STP222X_IOMMU_TLB_MASH_TLB_I_WHICH(tlb_mash); ! 802: stp222x->tme_stp222x_iommu_tlb_tags[tlb_i] ! 803: = ((stp222x->tme_stp222x_iommu_tlb_tags[tlb_i] ! 804: & ~(TME_STP222X_IOMMU_TLB_TAG_ERROR_MASK ! 805: | TME_STP222X_IOMMU_TLB_TAG_ERROR)) ! 806: + tlb_tag_error); ! 807: } ! 808: ! 809: /* update the error in the IOMMU control register: */ ! 810: #if (TME_STP222X_IOMMU_TLB_TAG_ERROR_MASK * (TME_STP222X_IOMMU_CR_XLT_ERR_MASK / TME_STP222X_IOMMU_TLB_TAG_ERROR_MASK)) != TME_STP222X_IOMMU_CR_XLT_ERR_MASK ! 811: #error "IOMMU error masks changed" ! 812: #endif ! 813: #if (TME_STP222X_IOMMU_TLB_TAG_ERROR * (TME_STP222X_IOMMU_CR_XLT_ERR_MASK / TME_STP222X_IOMMU_TLB_TAG_ERROR_MASK)) != TME_STP222X_IOMMU_CR_XLT_ERR ! 814: #error "IOMMU error flags changed" ! 815: #endif ! 816: stp222x->tme_stp222x_iommu_cr ! 817: = ((stp222x->tme_stp222x_iommu_cr ! 818: & ~(TME_STP222X_IOMMU_CR_XLT_ERR_MASK ! 819: | TME_STP222X_IOMMU_CR_XLT_ERR)) ! 820: + (tlb_tag_error ! 821: * (TME_STP222X_IOMMU_CR_XLT_ERR_MASK ! 822: / TME_STP222X_IOMMU_TLB_TAG_ERROR_MASK))); ! 823: ! 824: /* force no connection: */ ! 825: slave_conn_index = TME_STP222X_CONN_NULL; ! 826: } ! 827: ! 828: /* run the slave bus cycle: */ ! 829: master_cycle->tme_bus_cycle_address = slave_address; ! 830: tme_stp22xx_slave_cycle(master_conn_bus, ! 831: slave_conn_index, ! 832: master_cycle, ! 833: _master_fast_cycle_types, ! 834: &master_completion); ! 835: ! 836: /* leave: */ ! 837: tme_stp222x_leave(stp222x); ! 838: } ! 839: ! 840: /* this fills a TLB entry from the IOMMU: */ ! 841: void ! 842: tme_stp222x_iommu_tlb_fill(struct tme_bus_connection *io_conn_bus, ! 843: struct tme_bus_tlb *tlb, ! 844: tme_bus_addr_t io_address_wider, ! 845: unsigned int cycle_type) ! 846: { ! 847: struct tme_stp222x *stp222x; ! 848: tme_bus_addr64_t slave_address; ! 849: tme_uint32_t tlb_mash; ! 850: tme_uint32_t slave_conn_index; ! 851: struct tme_stp222x_tlb_list *tlb_list; ! 852: unsigned int tlb_list_head; ! 853: struct tme_token *token; ! 854: struct tme_token *token_other; ! 855: struct tme_bus_tlb tlb_mapping; ! 856: tme_uint32_t page_size_m1; ! 857: tme_bus_addr64_t io_address; ! 858: ! 859: /* enter: */ ! 860: stp222x = tme_stp222x_enter_bus(io_conn_bus); ! 861: ! 862: /* translate this address: */ ! 863: slave_address = io_address_wider; ! 864: tlb_mash = _tme_stp222x_iommu_tlb_mash_slave(io_conn_bus, ! 865: &slave_address, ! 866: cycle_type, ! 867: &slave_conn_index); ! 868: ! 869: /* if the translation for this address is valid: */ ! 870: if (tlb_mash & TME_STP222X_IOMMU_TLB_DATA_V) { ! 871: ! 872: /* if this translation is fixed: */ ! 873: if (tlb_mash & TME_STP222X_IOMMU_TLB_MASH_FIXED) { ! 874: ! 875: /* track this TLB entry in the fixed list: */ ! 876: tlb_list = &stp222x->tme_stp222x_iommu_tlb_list_fixed; ! 877: } ! 878: ! 879: /* otherwise, this translation is not fixed: */ ! 880: else { ! 881: ! 882: /* track this TLB entry in the list for the IOMMU TLB entry: */ ! 883: tlb_list = &stp222x->tme_stp222x_iommu_tlb_list[TME_STP222X_IOMMU_TLB_MASH_TLB_I_WHICH(tlb_mash)]; ! 884: } ! 885: ! 886: /* track this TLB entry: */ ! 887: tlb_list_head = tlb_list->tme_stp222x_tlb_list_head; ! 888: token = tlb->tme_bus_tlb_token; ! 889: token_other = tlb_list->tme_stp222x_tlb_list_tokens[tlb_list_head]; ! 890: tlb_list->tme_stp222x_tlb_list_tokens[tlb_list_head] = token; ! 891: tlb_list->tme_stp222x_tlb_list_head = (tlb_list_head + 1) % TME_STP222X_TLB_LIST_TOKENS_COUNT; ! 892: if (token_other != NULL ! 893: && token_other != token) { ! 894: tme_token_invalidate(token_other); ! 895: } ! 896: } ! 897: ! 898: /* otherwise, the translation for this address is not valid: */ ! 899: else { ! 900: ! 901: /* we must not have any connection for this address: */ ! 902: assert (slave_conn_index == TME_STP222X_CONN_NULL); ! 903: ! 904: #if TME_STP22XX_BUS_TRANSITION ! 905: ! 906: /* track the TLB for the most recent address that didn't have a ! 907: valid translation, so we can invalidate it when the slow cycle ! 908: happens (assuming the slow cycle will fill the IOMMU TLB with a ! 909: valid translation, we want the slave to then refill its ! 910: TLB): */ ! 911: token = tlb->tme_bus_tlb_token; ! 912: token_other = stp222x->tme_stp222x_iommu_tlb_missed_token; ! 913: stp222x->tme_stp222x_iommu_tlb_missed_token = token; ! 914: if (token_other != NULL ! 915: && token_other != token) { ! 916: tme_token_invalidate(token_other); ! 917: } ! 918: ! 919: #endif /* TME_STP22XX_BUS_TRANSITION */ ! 920: } ! 921: ! 922: /* fill the TLB entry: */ ! 923: tme_stp22xx_tlb_fill(io_conn_bus, ! 924: tlb, ! 925: slave_conn_index, ! 926: slave_address, ! 927: cycle_type); ! 928: ! 929: /* leave: */ ! 930: tme_stp222x_leave(stp222x); ! 931: ! 932: /* get the page size mask: */ ! 933: page_size_m1 = _tme_stp222x_iommu_tlb_page_size(tlb_mash) - 1; ! 934: ! 935: /* map the filled TLB entry: */ ! 936: io_address = ~ (tme_bus_addr64_t) page_size_m1; ! 937: io_address &= io_address_wider; ! 938: tlb_mapping.tme_bus_tlb_addr_first = io_address; ! 939: io_address |= page_size_m1; ! 940: tlb_mapping.tme_bus_tlb_addr_last = io_address; ! 941: #if TME_STP22XX_BUS_TRANSITION ! 942: tlb_mapping.tme_bus_tlb_cycles_ok = (TME_BUS_CYCLE_READ | TME_BUS_CYCLE_WRITE); ! 943: #endif /* TME_STP22XX_BUS_TRANSITION */ ! 944: tme_bus_tlb_map(tlb, slave_address, &tlb_mapping, io_address_wider); ! 945: ! 946: /* if this address is not writable: */ ! 947: if ((tlb_mash & TME_STP222X_IOMMU_TLB_TAG_W) == 0) { ! 948: ! 949: /* this TLB entry doesn't support writes: */ ! 950: tlb->tme_bus_tlb_emulator_off_write = TME_EMULATOR_OFF_UNDEF; ! 951: #if TME_STP22XX_BUS_TRANSITION ! 952: tlb->tme_bus_tlb_cycles_ok &= ~TME_BUS_CYCLE_WRITE; ! 953: #endif /* TME_STP22XX_BUS_TRANSITION */ ! 954: } ! 955: } ! 956: ! 957: /* the IOMMU register handler: */ ! 958: void ! 959: tme_stp222x_iommu_regs(struct tme_stp222x *stp222x, ! 960: struct tme_stp222x_reg *reg) ! 961: { ! 962: tme_uint32_t reggroup_index; ! 963: const char *name; ! 964: tme_uint32_t io_address; ! 965: tme_uint32_t tlb_mash; ! 966: tme_uint32_t tlb_i; ! 967: struct tme_stp222x_tlb_list *tlb_list; ! 968: signed long tlb_list_i; ! 969: struct tme_token *token; ! 970: ! 971: /* get the register: */ ! 972: reggroup_index = TME_STP222X_REGGROUP_INDEX(reg->tme_stp222x_reg_address); ! 973: ! 974: /* if this is a write: */ ! 975: if (reg->tme_stp222x_reg_write) { ! 976: ! 977: /* dispatch on the register: */ ! 978: switch (reggroup_index) { ! 979: ! 980: case TME_STP222X_IOMMU_REGGROUP_INDEX_CR: ! 981: stp222x->tme_stp222x_iommu_cr ! 982: = (reg->tme_stp222x_reg_value ! 983: & ~TME_STP222X_IOMMU_CR_MBZ); ! 984: name = "CR"; ! 985: break; ! 986: ! 987: case TME_STP222X_IOMMU_REGGROUP_INDEX_TSB: ! 988: stp222x->tme_stp222x_iommu_tsb ! 989: = (reg->tme_stp222x_reg_value ! 990: & ((((tme_uint64_t) 2) << 40) ! 991: - (1 << 13))); ! 992: name = "TSB"; ! 993: break; ! 994: ! 995: case TME_STP222X_IOMMU_REGGROUP_INDEX_FLUSH: ! 996: ! 997: /* get the address: */ ! 998: io_address = 0 - (tme_uint32_t) TME_STP222X_IOMMU_SIZE_8KB; ! 999: io_address &= reg->tme_stp222x_reg_value; ! 1000: ! 1001: /* translate this address: */ ! 1002: tlb_mash ! 1003: = _tme_stp222x_iommu_tlb_mash(stp222x, ! 1004: io_address, ! 1005: TME_BUS_CYCLE_READ); ! 1006: ! 1007: /* if this address hit an IOMMU TLB entry: */ ! 1008: if ((tlb_mash ! 1009: & (TME_STP222X_IOMMU_TLB_MASH_FIXED ! 1010: | TME_STP222X_IOMMU_TLB_MASH_MISS)) == 0) { ! 1011: ! 1012: /* invalidate this entry: */ ! 1013: tlb_i = TME_STP222X_IOMMU_TLB_MASH_TLB_I_WHICH(tlb_mash); ! 1014: stp222x->tme_stp222x_iommu_tlb_data[tlb_i] &= ~TME_STP222X_IOMMU_TLB_DATA_V; ! 1015: ! 1016: /* invalidate all of the TLBs associated with this IOMMU TLB ! 1017: entry: */ ! 1018: tlb_list = &stp222x->tme_stp222x_iommu_tlb_list[tlb_i]; ! 1019: tlb_list_i = TME_STP222X_TLB_LIST_TOKENS_COUNT - 1; ! 1020: do { ! 1021: token = tlb_list->tme_stp222x_tlb_list_tokens[tlb_list_i]; ! 1022: if (token != NULL) { ! 1023: tlb_list->tme_stp222x_tlb_list_tokens[tlb_list_i] = NULL; ! 1024: tme_token_invalidate(token); ! 1025: } ! 1026: } while (--tlb_list_i >= 0); ! 1027: } ! 1028: name = "FLUSH"; ! 1029: break; ! 1030: ! 1031: default: ! 1032: return; ! 1033: } ! 1034: } ! 1035: ! 1036: /* otherwise, this is a read: */ ! 1037: else { ! 1038: ! 1039: /* dispatch on the register: */ ! 1040: switch (reggroup_index) { ! 1041: case TME_STP222X_IOMMU_REGGROUP_INDEX_CR: ! 1042: reg->tme_stp222x_reg_value = stp222x->tme_stp222x_iommu_cr; ! 1043: name = "CR"; ! 1044: break; ! 1045: case TME_STP222X_IOMMU_REGGROUP_INDEX_TSB: ! 1046: reg->tme_stp222x_reg_value = stp222x->tme_stp222x_iommu_tsb; ! 1047: name = "TSB"; ! 1048: break; ! 1049: /* case TME_STP222X_IOMMU_REGGROUP_INDEX_FLUSH: */ ! 1050: default: ! 1051: return; ! 1052: } ! 1053: } ! 1054: ! 1055: tme_log(TME_STP222X_LOG_HANDLE(stp222x), 2000, TME_OK, ! 1056: (TME_STP222X_LOG_HANDLE(stp222x), ! 1057: _("IOMMU %s %s 0x%" TME_PRIx64), ! 1058: name, ! 1059: (reg->tme_stp222x_reg_write ! 1060: ? "<-" ! 1061: : "->"), ! 1062: reg->tme_stp222x_reg_value)); ! 1063: ! 1064: /* this register access has been completed: */ ! 1065: reg->tme_stp222x_reg_completed = TRUE; ! 1066: } ! 1067: ! 1068: /* the IOMMU diagnostic register handler: */ ! 1069: void ! 1070: tme_stp222x_iommu_regs_diag(struct tme_stp222x *stp222x, ! 1071: struct tme_stp222x_reg *reg) ! 1072: { ! 1073: tme_uint32_t reggroup_0_3; ! 1074: tme_uint32_t reggroup_index; ! 1075: const char *name; ! 1076: signed long tlb_i; ! 1077: unsigned int lru_i; ! 1078: tme_uint32_t io_address; ! 1079: tme_uint32_t io_address_tag; ! 1080: tme_uint32_t compare; ! 1081: tme_uint32_t tlb_tag; ! 1082: ! 1083: /* XXX FIXME - what happens if diagnostics aren't enabled? */ ! 1084: if ((stp222x->tme_stp222x_iommu_cr ! 1085: & TME_STP222X_IOMMU_CR_MMU_DE) == 0) { ! 1086: abort(); ! 1087: } ! 1088: ! 1089: /* get the register: */ ! 1090: reggroup_0_3 = TME_STP222X_REGGROUP_WHICH(reg->tme_stp222x_reg_address) & 0xf; ! 1091: reggroup_index = TME_STP222X_REGGROUP_INDEX(reg->tme_stp222x_reg_address); ! 1092: ! 1093: /* if this is a write: */ ! 1094: if (reg->tme_stp222x_reg_write) { ! 1095: ! 1096: switch (reggroup_0_3) { ! 1097: case 0x4: /* STP2220 0x44, STP2222 0xa4 */ ! 1098: switch (reggroup_index) { ! 1099: case TME_STP222X_IOMMU_REGGROUP_INDEX_DIAG4_VA: ! 1100: ! 1101: /* get the address: */ ! 1102: io_address = 0 - (tme_uint32_t) TME_STP222X_IOMMU_SIZE_8KB; ! 1103: io_address &= reg->tme_stp222x_reg_value; ! 1104: stp222x->tme_stp222x_iommu_va = io_address; ! 1105: ! 1106: /* convert the address into a VPN: */ ! 1107: io_address_tag = io_address / TME_STP222X_IOMMU_SIZE_8KB; ! 1108: ! 1109: /* compare this address to all TLB tags: */ ! 1110: compare = 0; ! 1111: tlb_i = TME_STP222X_IOMMU_TLB_SIZE - 1; ! 1112: do { ! 1113: compare <<= 1; ! 1114: tlb_tag = stp222x->tme_stp222x_iommu_tlb_tags[tlb_i]; ! 1115: if (((tlb_tag ! 1116: ^ io_address_tag) ! 1117: & _tme_stp222x_iommu_tlb_tag_vpn_mask(tlb_tag)) == 0) { ! 1118: compare++; ! 1119: } ! 1120: } while (--tlb_i >= 0); ! 1121: stp222x->tme_stp222x_iommu_compare = compare; ! 1122: name = "VA"; ! 1123: break; ! 1124: ! 1125: case TME_STP222X_IOMMU_REGGROUP_INDEX_DIAG4_COMPARE: ! 1126: reg->tme_stp222x_reg_completed = TRUE; ! 1127: return; ! 1128: default: ! 1129: return; ! 1130: } ! 1131: ! 1132: default: /* STP2220 0x45, STP2222 0xa5 */ ! 1133: assert (reggroup_0_3 == 0x5); ! 1134: if (__tme_predict_false(reggroup_index < TME_STP222X_IOMMU_TLB_SIZE)) { ! 1135: return; ! 1136: } ! 1137: stp222x->tme_stp222x_iommu_tlb_tags[reggroup_index - TME_STP222X_IOMMU_TLB_SIZE] ! 1138: = reg->tme_stp222x_reg_value; ! 1139: name = "TLB_TAG"; ! 1140: break; ! 1141: ! 1142: case 0x6: /* STP2220 0x46, STP2222 0xa6 */ ! 1143: if (__tme_predict_false(reggroup_index >= TME_STP222X_IOMMU_TLB_SIZE)) { ! 1144: return; ! 1145: } ! 1146: stp222x->tme_stp222x_iommu_tlb_data[reggroup_index] ! 1147: = reg->tme_stp222x_reg_value; ! 1148: name = "TLB_DATA"; ! 1149: break; ! 1150: } ! 1151: } ! 1152: ! 1153: /* otherwise, this is a read: */ ! 1154: else { ! 1155: ! 1156: switch (reggroup_0_3) { ! 1157: case 0x4: /* STP2220 0x44, STP2222 0xa4 */ ! 1158: switch (reggroup_index) { ! 1159: case TME_STP222X_IOMMU_REGGROUP_INDEX_DIAG4_VA: ! 1160: reg->tme_stp222x_reg_value = stp222x->tme_stp222x_iommu_va; ! 1161: name = "VA"; ! 1162: break; ! 1163: case TME_STP222X_IOMMU_REGGROUP_INDEX_DIAG4_COMPARE: ! 1164: reg->tme_stp222x_reg_value = stp222x->tme_stp222x_iommu_compare; ! 1165: name = "COMPARE"; ! 1166: break; ! 1167: default: ! 1168: return; ! 1169: } ! 1170: ! 1171: default: /* STP2220 0x45, STP2222 0xa5 */ ! 1172: assert (reggroup_0_3 == 0x5); ! 1173: if (__tme_predict_false(reggroup_index < TME_STP222X_IOMMU_TLB_SIZE)) { ! 1174: tlb_i = stp222x->tme_stp222x_iommu_tlb_i_mru; ! 1175: for (lru_i = reggroup_index; ++lru_i != TME_STP222X_IOMMU_TLB_SIZE; ) { ! 1176: tlb_i = stp222x->tme_stp222x_iommu_lru_next(tlb_i); ! 1177: } ! 1178: reg->tme_stp222x_reg_value = tlb_i; ! 1179: name = "LRU"; ! 1180: } ! 1181: else { ! 1182: reg->tme_stp222x_reg_value ! 1183: = stp222x->tme_stp222x_iommu_tlb_tags[reggroup_index - TME_STP222X_IOMMU_TLB_SIZE]; ! 1184: name = "TLB_TAG"; ! 1185: } ! 1186: break; ! 1187: ! 1188: case 0x6: /* STP2220 0x46, STP2222 0xa6 */ ! 1189: if (__tme_predict_false(reggroup_index >= TME_STP222X_IOMMU_TLB_SIZE)) { ! 1190: return; ! 1191: } ! 1192: reg->tme_stp222x_reg_value ! 1193: = stp222x->tme_stp222x_iommu_tlb_data[reggroup_index]; ! 1194: name = "TLB_DATA"; ! 1195: break; ! 1196: } ! 1197: } ! 1198: ! 1199: if (reggroup_0_3 == 0x4) { /* STP2220 0x44, STP2222 0xa4 */ ! 1200: tme_log(TME_STP222X_LOG_HANDLE(stp222x), 2000, TME_OK, ! 1201: (TME_STP222X_LOG_HANDLE(stp222x), ! 1202: _("IOMMU %s %s 0x%" TME_PRIx64), ! 1203: name, ! 1204: (reg->tme_stp222x_reg_write ! 1205: ? "<-" ! 1206: : "->"), ! 1207: reg->tme_stp222x_reg_value)); ! 1208: } ! 1209: else { ! 1210: tme_log(TME_STP222X_LOG_HANDLE(stp222x), 2000, TME_OK, ! 1211: (TME_STP222X_LOG_HANDLE(stp222x), ! 1212: _("IOMMU %s[%u] %s 0x%" TME_PRIx64), ! 1213: name, ! 1214: (reggroup_index % TME_STP222X_IOMMU_TLB_SIZE), ! 1215: (reg->tme_stp222x_reg_write ! 1216: ? "<-" ! 1217: : "->"), ! 1218: reg->tme_stp222x_reg_value)); ! 1219: } ! 1220: ! 1221: /* this register access has been completed: */ ! 1222: reg->tme_stp222x_reg_completed = TRUE; ! 1223: } ! 1224: ! 1225: /* this initializes the IOMMU: */ ! 1226: void ! 1227: tme_stp222x_iommu_init(struct tme_stp222x *stp222x) ! 1228: { ! 1229: unsigned long tlb_i; ! 1230: ! 1231: /* initialize the TSB TLB: */ ! 1232: tme_token_init(&stp222x->tme_stp222x_iommu_tsb_tlb_token); ! 1233: stp222x->tme_stp222x_iommu_tsb_tlb.tme_bus_tlb_token = &stp222x->tme_stp222x_iommu_tsb_tlb_token; ! 1234: ! 1235: /* initialize the LRU list: */ ! 1236: tlb_i = 0; ! 1237: stp222x->tme_stp222x_iommu_tlb_i_mru = tlb_i; ! 1238: do { ! 1239: stp222x->tme_stp222x_iommu_lru_prev(tlb_i) = (tlb_i - 1) % TME_STP222X_IOMMU_TLB_SIZE; ! 1240: stp222x->tme_stp222x_iommu_lru_next(tlb_i) = (tlb_i + 1) % TME_STP222X_IOMMU_TLB_SIZE; ! 1241: tlb_i = (tlb_i + 1) % TME_STP222X_IOMMU_TLB_SIZE; ! 1242: } while (tlb_i != 0); ! 1243: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.