1/* 2 * Copyright (c) 2006 Advanced Micro Devices, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 * DEALINGS IN THE SOFTWARE. 21 * 22 * Neither the name of the Advanced Micro Devices, Inc. nor the names of its 23 * contributors may be used to endorse or promote products derived from this 24 * software without specific prior written permission. 25 */ 26 27 /* 28 * Cimarron MSR access routines. These routines allow the user to query the 29 * state of the GeodeLink Bus and read and write model-specfic registers. 30 */ 31 32/*--------------------------------------------------------------*/ 33/* MSR GLOBALS */ 34/* These variables hold a local copy of the GeodeLink mapping */ 35/* as well as a lookup table for easy device addressing. */ 36/*--------------------------------------------------------------*/ 37 38GEODELINK_NODE gliu_nodes[24]; 39GEODELINK_NODE msr_dev_lookup[MSR_DEVICE_EMPTY]; 40 41#define GET_DEVICE_ID(macrohigh, macrolow) ((macrolow >> 12) & 0xFF) 42 43/*--------------------------------------------------------------------------- 44 * msr_init_table 45 * 46 * This routine initializes the internal MSR table in Cimarron. This table is 47 * used for any MSR device accesses. 48 *--------------------------------------------------------------------------*/ 49 50int 51msr_init_table(void) 52{ 53 Q_WORD msr_value = { 0, 0 }; 54 unsigned int i, j; 55 int return_value = CIM_STATUS_OK; 56 57 /* CHECK FOR VALID GEODELINK CONFIGURATION 58 * The CPU and the three GLIUs are assumed to be at known static 59 * addresses, so we will check the device IDs at these addresses as proof 60 * of a valid GeodeLink configuration 61 */ 62 63 MSR_READ(MSR_GEODELINK_CAP, MSR_ADDRESS_VAIL, &msr_value); 64 if (GET_DEVICE_ID(msr_value.high, msr_value.low) != MSR_CLASS_CODE_VAIL) 65 return_value = CIM_STATUS_ERROR; 66 67 MSR_READ(MSR_GEODELINK_CAP, MSR_ADDRESS_GLIU0, &msr_value); 68 if (GET_DEVICE_ID(msr_value.high, msr_value.low) != MSR_CLASS_CODE_GLIU) 69 return_value = CIM_STATUS_ERROR; 70 71 MSR_READ(MSR_GEODELINK_CAP, MSR_ADDRESS_GLIU1, &msr_value); 72 if (GET_DEVICE_ID(msr_value.high, msr_value.low) != MSR_CLASS_CODE_GLIU) 73 return_value = CIM_STATUS_ERROR; 74 75 MSR_READ(MSR_GEODELINK_CAP, MSR_ADDRESS_GLIU2, &msr_value); 76 if (GET_DEVICE_ID(msr_value.high, msr_value.low) != MSR_CLASS_CODE_GLIU) 77 return_value = CIM_STATUS_ERROR; 78 79 if (return_value == CIM_STATUS_OK) { 80 /* BUILD LOCAL COPY OF THE GEODELINK BUS */ 81 82 msr_create_geodelink_table(gliu_nodes); 83 84 /* CLEAR TABLE STATUS */ 85 86 for (i = 0; i < MSR_DEVICE_EMPTY; i++) 87 msr_dev_lookup[i].device_id = MSR_DEVICE_NOTFOUND; 88 89 /* CREATE EASY LOOKUP TABLE FOR FUTURE HARDWARE ACCESS */ 90 /* Note that MSR_DEVICE_EMPTY is the index after the last */ 91 /* available device. Also note that we fill in known */ 92 /* devices before filling in the rest of the table. */ 93 94 msr_dev_lookup[MSR_DEVICE_GEODELX_GLIU0].address_from_cpu = 95 MSR_ADDRESS_GLIU0; 96 msr_dev_lookup[MSR_DEVICE_GEODELX_GLIU0].device_id = MSR_DEVICE_PRESENT; 97 msr_dev_lookup[MSR_DEVICE_GEODELX_GLIU1].address_from_cpu = 98 MSR_ADDRESS_GLIU1; 99 msr_dev_lookup[MSR_DEVICE_GEODELX_GLIU1].device_id = MSR_DEVICE_PRESENT; 100 msr_dev_lookup[MSR_DEVICE_5535_GLIU].address_from_cpu = 101 MSR_ADDRESS_GLIU2; 102 msr_dev_lookup[MSR_DEVICE_5535_GLIU].device_id = MSR_DEVICE_PRESENT; 103 msr_dev_lookup[MSR_DEVICE_GEODELX_VAIL].address_from_cpu = 104 MSR_ADDRESS_VAIL; 105 msr_dev_lookup[MSR_DEVICE_GEODELX_VAIL].device_id = MSR_DEVICE_PRESENT; 106 107 for (i = 0; i < MSR_DEVICE_EMPTY; i++) { 108 if (msr_dev_lookup[i].device_id == MSR_DEVICE_NOTFOUND) { 109 for (j = 0; j < 24; j++) { 110 if (gliu_nodes[j].device_id == i) 111 break; 112 } 113 114 if (j == 24) 115 msr_dev_lookup[i].device_id = MSR_DEVICE_NOTFOUND; 116 else { 117 msr_dev_lookup[i].device_id = MSR_DEVICE_PRESENT; 118 msr_dev_lookup[i].address_from_cpu = 119 gliu_nodes[j].address_from_cpu; 120 } 121 } 122 } 123 } 124 else { 125 /* ERROR OUT THE GEODELINK TABLES */ 126 127 for (i = 0; i < 24; i++) { 128 gliu_nodes[i].address_from_cpu = 0xFFFFFFFF; 129 gliu_nodes[i].device_id = MSR_DEVICE_EMPTY; 130 } 131 132 for (i = 0; i < MSR_DEVICE_EMPTY; i++) { 133 msr_dev_lookup[i].address_from_cpu = 0xFFFFFFFF; 134 msr_dev_lookup[i].device_id = MSR_DEVICE_NOTFOUND; 135 } 136 } 137 return return_value; 138} 139 140/*--------------------------------------------------------------------------- 141 * msr_create_geodelink_table 142 * 143 * This routine dumps the contents of the GeodeLink bus into an array of 144 * 24 GEODELINK_NODE structures. Indexes 0-7 represent ports 0-7 of GLIU0, 145 * indexes 8-15 represent ports 0-7 of GLIU1 and indexes 16-23 represent 146 * ports 0-7 of GLIU2 (5535). 147 *--------------------------------------------------------------------------*/ 148 149int 150msr_create_geodelink_table(GEODELINK_NODE * gliu_nodes) 151{ 152 unsigned long mbiu_port_count, reflective; 153 unsigned long port, index; 154 unsigned long gliu_count = 0; 155 int glcp_count = 0; 156 int usb_count = 0; 157 int mpci_count = 0; 158 Q_WORD msr_value = { 0, 0 }; 159 160 /* ALL THREE GLIUS ARE IN ONE ARRAY */ 161 /* Entries 0-7 contain the port information for GLIU0, entries */ 162 /* 8-15 contain GLIU1 and 15-23 contain GLIU2. We perform the */ 163 /* enumeration in two passes. The first simply fills in the */ 164 /* addresses and class codes at each node. The second pass */ 165 /* translates the class codes into indexes into Cimarron's device */ 166 /* lookup table. */ 167 168 /* COUNT GLIU0 PORTS */ 169 170 MSR_READ(MSR_GLIU_CAP, MSR_ADDRESS_GLIU0, &msr_value); 171 mbiu_port_count = (msr_value.high >> NUM_PORTS_SHIFT) & 7; 172 173 /* FIND REFLECTIVE PORT */ 174 /* Query the GLIU for the port through which we are communicating. */ 175 /* We will avoid accesses to this port to avoid a self-reference. */ 176 177 MSR_READ(MSR_GLIU_WHOAMI, MSR_ADDRESS_GLIU0, &msr_value); 178 reflective = msr_value.low & WHOAMI_MASK; 179 180 /* SPECIAL CASE FOR PORT 0 */ 181 /* GLIU0 port 0 is a special case, as it points back to GLIU0. GLIU0 */ 182 /* responds at address 0x10000xxx, which does not equal 0 << 29. */ 183 184 gliu_nodes[0].address_from_cpu = MSR_ADDRESS_GLIU0; 185 gliu_nodes[0].device_id = MSR_CLASS_CODE_GLIU; 186 187 /* ENUMERATE ALL PORTS */ 188 189 for (port = 1; port < 8; port++) { 190 /* FILL IN ADDRESS */ 191 192 gliu_nodes[port].address_from_cpu = port << 29; 193 194 if (port == reflective) 195 gliu_nodes[port].device_id = MSR_CLASS_CODE_REFLECTIVE; 196 else if (port > mbiu_port_count) 197 gliu_nodes[port].device_id = MSR_CLASS_CODE_UNPOPULATED; 198 else { 199 MSR_READ(MSR_GEODELINK_CAP, gliu_nodes[port].address_from_cpu, 200 &msr_value); 201 gliu_nodes[port].device_id = 202 GET_DEVICE_ID(msr_value.high, msr_value.low); 203 } 204 } 205 206 /* COUNT GLIU1 PORTS */ 207 208 MSR_READ(MSR_GLIU_CAP, MSR_ADDRESS_GLIU1, &msr_value); 209 mbiu_port_count = (msr_value.high >> NUM_PORTS_SHIFT) & 7; 210 211 /* FIND REFLECTIVE PORT */ 212 213 MSR_READ(MSR_GLIU_WHOAMI, MSR_ADDRESS_GLIU1, &msr_value); 214 reflective = msr_value.low & WHOAMI_MASK; 215 216 /* ENUMERATE ALL PORTS */ 217 218 for (port = 0; port < 8; port++) { 219 index = port + 8; 220 221 /* FILL IN ADDRESS */ 222 223 gliu_nodes[index].address_from_cpu = (0x02l << 29) + (port << 26); 224 225 if (port == reflective) 226 gliu_nodes[index].device_id = MSR_CLASS_CODE_REFLECTIVE; 227 else if (port > mbiu_port_count) 228 gliu_nodes[index].device_id = MSR_CLASS_CODE_UNPOPULATED; 229 else { 230 MSR_READ(MSR_GEODELINK_CAP, gliu_nodes[index].address_from_cpu, 231 &msr_value); 232 gliu_nodes[index].device_id = 233 GET_DEVICE_ID(msr_value.high, msr_value.low); 234 } 235 } 236 237 /* COUNT GLIU2 PORTS */ 238 239 MSR_READ(MSR_GLIU_CAP, MSR_ADDRESS_GLIU2, &msr_value); 240 mbiu_port_count = (msr_value.high >> NUM_PORTS_SHIFT) & 7; 241 242 /* FIND REFLECTIVE PORT */ 243 244 MSR_READ(MSR_GLIU_WHOAMI, MSR_ADDRESS_GLIU2, &msr_value); 245 reflective = msr_value.low & WHOAMI_MASK; 246 247 /* FILL IN PORT 0 AND 1 */ 248 /* Port 0 on 5535 is MBIU2. Port 1 is MPCI, but it is referenced at */ 249 /* a special address. */ 250 251 gliu_nodes[16].address_from_cpu = MSR_ADDRESS_GLIU2; 252 gliu_nodes[16].device_id = MSR_CLASS_CODE_GLIU; 253 254 gliu_nodes[17].address_from_cpu = MSR_ADDRESS_5535MPCI; 255 gliu_nodes[17].device_id = MSR_CLASS_CODE_MPCI; 256 257 /* ENUMERATE ALL PORTS */ 258 259 for (port = 2; port < 8; port++) { 260 index = port + 16; 261 262 /* FILL IN ADDRESS */ 263 264 gliu_nodes[index].address_from_cpu = 265 (0x02l << 29) + (0x04l << 26) + (0x02l << 23) + (port << 20); 266 267 if (port == reflective) 268 gliu_nodes[index].device_id = MSR_CLASS_CODE_REFLECTIVE; 269 else if (port > mbiu_port_count) 270 gliu_nodes[index].device_id = MSR_CLASS_CODE_UNPOPULATED; 271 else { 272 MSR_READ(MSR_GEODELINK_CAP, gliu_nodes[index].address_from_cpu, 273 &msr_value); 274 gliu_nodes[index].device_id = 275 GET_DEVICE_ID(msr_value.high, msr_value.low); 276 } 277 } 278 279 /* SECOND PASS - TRANSLATION */ 280 /* Now that the class codes for each device are stored in the */ 281 /* array, we walk through the array and translate the class */ 282 /* codes to table indexes. For class codes that have multiple */ 283 /* instances, the table indexes are sequential. */ 284 285 for (port = 0; port < 24; port++) { 286 /* SPECIAL CASE FOR GLIU UNITS */ 287 /* A GLIU can be both on another port and on its own port. These */ 288 /* end up as the same address, but are shown as duplicate nodes in */ 289 /* the GeodeLink table. */ 290 291 if ((port & 7) == 0) 292 gliu_count = port >> 3; 293 294 switch (gliu_nodes[port].device_id) { 295 /* UNPOPULATED OR REFLECTIVE NODES */ 296 297 case MSR_CLASS_CODE_UNPOPULATED: 298 index = MSR_DEVICE_EMPTY; 299 break; 300 case MSR_CLASS_CODE_REFLECTIVE: 301 index = MSR_DEVICE_REFLECTIVE; 302 break; 303 304 /* KNOWN CLASS CODES */ 305 306 case MSR_CLASS_CODE_GLIU: 307 index = MSR_DEVICE_GEODELX_GLIU0 + gliu_count++; 308 break; 309 case MSR_CLASS_CODE_GLCP: 310 index = MSR_DEVICE_GEODELX_GLCP + glcp_count++; 311 break; 312 case MSR_CLASS_CODE_MPCI: 313 index = MSR_DEVICE_GEODELX_MPCI + mpci_count++; 314 break; 315 case MSR_CLASS_CODE_USB: 316 index = MSR_DEVICE_5535_USB2 + usb_count++; 317 break; 318 case MSR_CLASS_CODE_USB2: 319 index = MSR_DEVICE_5536_USB_2_0; 320 break; 321 case MSR_CLASS_CODE_ATAC: 322 index = MSR_DEVICE_5535_ATAC; 323 break; 324 case MSR_CLASS_CODE_MDD: 325 index = MSR_DEVICE_5535_MDD; 326 break; 327 case MSR_CLASS_CODE_ACC: 328 index = MSR_DEVICE_5535_ACC; 329 break; 330 case MSR_CLASS_CODE_MC: 331 index = MSR_DEVICE_GEODELX_MC; 332 break; 333 case MSR_CLASS_CODE_GP: 334 index = MSR_DEVICE_GEODELX_GP; 335 break; 336 case MSR_CLASS_CODE_VG: 337 index = MSR_DEVICE_GEODELX_VG; 338 break; 339 case MSR_CLASS_CODE_DF: 340 index = MSR_DEVICE_GEODELX_DF; 341 break; 342 case MSR_CLASS_CODE_FG: 343 index = MSR_DEVICE_GEODELX_FG; 344 break; 345 case MSR_CLASS_CODE_VIP: 346 index = MSR_DEVICE_GEODELX_VIP; 347 break; 348 case MSR_CLASS_CODE_AES: 349 index = MSR_DEVICE_GEODELX_AES; 350 break; 351 case MSR_CLASS_CODE_VAIL: 352 index = MSR_DEVICE_GEODELX_VAIL; 353 break; 354 default: 355 index = MSR_DEVICE_EMPTY; 356 break; 357 } 358 359 gliu_nodes[port].device_id = index; 360 } 361 362 return CIM_STATUS_OK; 363} 364 365/*--------------------------------------------------------------------------- 366 * msr_create_device_list 367 * 368 * This routine dumps a list of all known GeodeLX/5535 devices as well as their 369 * respective status and address. 370 *--------------------------------------------------------------------------*/ 371 372int 373msr_create_device_list(GEODELINK_NODE * gliu_nodes, int max_devices) 374{ 375 int i, count; 376 377 if (max_devices < MSR_DEVICE_EMPTY) 378 count = max_devices; 379 else 380 count = MSR_DEVICE_EMPTY; 381 382 for (i = 0; i < count; i++) { 383 gliu_nodes[i].address_from_cpu = msr_dev_lookup[i].address_from_cpu; 384 gliu_nodes[i].device_id = msr_dev_lookup[i].device_id; 385 } 386 387 return CIM_STATUS_OK; 388} 389 390/*-------------------------------------------------------------------- 391 * msr_read64 392 * 393 * Performs a 64-bit read from 'msr_register' in device 'device'. 'device' is 394 * an index into Cimarron's table of known GeodeLink devices. 395 *-------------------------------------------------------------------*/ 396 397int 398msr_read64(unsigned long device, unsigned long msr_register, Q_WORD * msr_value) 399{ 400 if (device < MSR_DEVICE_EMPTY) { 401 if (msr_dev_lookup[device].device_id == MSR_DEVICE_PRESENT) { 402 MSR_READ(msr_register, msr_dev_lookup[device].address_from_cpu, 403 msr_value); 404 return CIM_STATUS_OK; 405 } 406 } 407 408 msr_value->low = msr_value->high = 0; 409 return CIM_STATUS_DEVNOTFOUND; 410} 411 412/*-------------------------------------------------------------------- 413 * msr_write64 414 * 415 * Performs a 64-bit write to 'msr_register' in device 'device'. 'device' is 416 * an index into Cimarron's table of known GeodeLink devices. 417 *-------------------------------------------------------------------*/ 418 419int 420msr_write64(unsigned long device, unsigned long msr_register, 421 Q_WORD * msr_value) 422{ 423 if (device < MSR_DEVICE_EMPTY) { 424 if (msr_dev_lookup[device].device_id == MSR_DEVICE_PRESENT) { 425 MSR_WRITE(msr_register, msr_dev_lookup[device].address_from_cpu, 426 msr_value); 427 return CIM_STATUS_OK; 428 } 429 } 430 return CIM_STATUS_DEVNOTFOUND; 431} 432