1/* 2 * This file is provided under a dual BSD/GPLv2 license. When using or 3 * redistributing this file, you may do so under either license. 4 * 5 * GPL LICENSE SUMMARY 6 * 7 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of version 2 of the GNU General Public License as 11 * published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, but 14 * WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 21 * The full GNU General Public License is included in this distribution 22 * in the file called LICENSE.GPL. 23 * 24 * BSD LICENSE 25 * 26 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. 27 * All rights reserved. 28 * 29 * Redistribution and use in source and binary forms, with or without 30 * modification, are permitted provided that the following conditions 31 * are met: 32 * 33 * * Redistributions of source code must retain the above copyright 34 * notice, this list of conditions and the following disclaimer. 35 * * Redistributions in binary form must reproduce the above copyright 36 * notice, this list of conditions and the following disclaimer in 37 * the documentation and/or other materials provided with the 38 * distribution. 39 * * Neither the name of Intel Corporation nor the names of its 40 * contributors may be used to endorse or promote products derived 41 * from this software without specific prior written permission. 42 * 43 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 44 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 45 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 46 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 47 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 48 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 49 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 50 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 51 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 52 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 53 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 54 */ 55 56/** 57 * This file contains the implementation of the SCIC_SDS_REMOTE_NODE_TABLE 58 * public, protected, and private methods. 59 * 60 * 61 */ 62#include "remote_node_table.h" 63#include "remote_node_context.h" 64 65/** 66 * 67 * @remote_node_table: This is the remote node index table from which the 68 * selection will be made. 69 * @group_table_index: This is the index to the group table from which to 70 * search for an available selection. 71 * 72 * This routine will find the bit position in absolute bit terms of the next 32 73 * + bit position. If there are available bits in the first u32 then it is 74 * just bit position. u32 This is the absolute bit position for an available 75 * group. 76 */ 77static u32 sci_remote_node_table_get_group_index( 78 struct sci_remote_node_table *remote_node_table, 79 u32 group_table_index) 80{ 81 u32 dword_index; 82 u32 *group_table; 83 u32 bit_index; 84 85 group_table = remote_node_table->remote_node_groups[group_table_index]; 86 87 for (dword_index = 0; dword_index < remote_node_table->group_array_size; dword_index++) { 88 if (group_table[dword_index] != 0) { 89 for (bit_index = 0; bit_index < 32; bit_index++) { 90 if ((group_table[dword_index] & (1 << bit_index)) != 0) { 91 return (dword_index * 32) + bit_index; 92 } 93 } 94 } 95 } 96 97 return SCIC_SDS_REMOTE_NODE_TABLE_INVALID_INDEX; 98} 99 100/** 101 * 102 * @out]: remote_node_table This the remote node table in which to clear the 103 * selector. 104 * @set_index: This is the remote node selector in which the change will be 105 * made. 106 * @group_index: This is the bit index in the table to be modified. 107 * 108 * This method will clear the group index entry in the specified group index 109 * table. none 110 */ 111static void sci_remote_node_table_clear_group_index( 112 struct sci_remote_node_table *remote_node_table, 113 u32 group_table_index, 114 u32 group_index) 115{ 116 u32 dword_index; 117 u32 bit_index; 118 u32 *group_table; 119 120 BUG_ON(group_table_index >= SCU_STP_REMOTE_NODE_COUNT); 121 BUG_ON(group_index >= (u32)(remote_node_table->group_array_size * 32)); 122 123 dword_index = group_index / 32; 124 bit_index = group_index % 32; 125 group_table = remote_node_table->remote_node_groups[group_table_index]; 126 127 group_table[dword_index] = group_table[dword_index] & ~(1 << bit_index); 128} 129 130/** 131 * 132 * @out]: remote_node_table This the remote node table in which to set the 133 * selector. 134 * @group_table_index: This is the remote node selector in which the change 135 * will be made. 136 * @group_index: This is the bit position in the table to be modified. 137 * 138 * This method will set the group index bit entry in the specified gropu index 139 * table. none 140 */ 141static void sci_remote_node_table_set_group_index( 142 struct sci_remote_node_table *remote_node_table, 143 u32 group_table_index, 144 u32 group_index) 145{ 146 u32 dword_index; 147 u32 bit_index; 148 u32 *group_table; 149 150 BUG_ON(group_table_index >= SCU_STP_REMOTE_NODE_COUNT); 151 BUG_ON(group_index >= (u32)(remote_node_table->group_array_size * 32)); 152 153 dword_index = group_index / 32; 154 bit_index = group_index % 32; 155 group_table = remote_node_table->remote_node_groups[group_table_index]; 156 157 group_table[dword_index] = group_table[dword_index] | (1 << bit_index); 158} 159 160/** 161 * 162 * @out]: remote_node_table This is the remote node table in which to modify 163 * the remote node availability. 164 * @remote_node_index: This is the remote node index that is being returned to 165 * the table. 166 * 167 * This method will set the remote to available in the remote node allocation 168 * table. none 169 */ 170static void sci_remote_node_table_set_node_index( 171 struct sci_remote_node_table *remote_node_table, 172 u32 remote_node_index) 173{ 174 u32 dword_location; 175 u32 dword_remainder; 176 u32 slot_normalized; 177 u32 slot_position; 178 179 BUG_ON( 180 (remote_node_table->available_nodes_array_size * SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD) 181 <= (remote_node_index / SCU_STP_REMOTE_NODE_COUNT) 182 ); 183 184 dword_location = remote_node_index / SCIC_SDS_REMOTE_NODES_PER_DWORD; 185 dword_remainder = remote_node_index % SCIC_SDS_REMOTE_NODES_PER_DWORD; 186 slot_normalized = (dword_remainder / SCU_STP_REMOTE_NODE_COUNT) * sizeof(u32); 187 slot_position = remote_node_index % SCU_STP_REMOTE_NODE_COUNT; 188 189 remote_node_table->available_remote_nodes[dword_location] |= 190 1 << (slot_normalized + slot_position); 191} 192 193/** 194 * 195 * @out]: remote_node_table This is the remote node table from which to clear 196 * the available remote node bit. 197 * @remote_node_index: This is the remote node index which is to be cleared 198 * from the table. 199 * 200 * This method clears the remote node index from the table of available remote 201 * nodes. none 202 */ 203static void sci_remote_node_table_clear_node_index( 204 struct sci_remote_node_table *remote_node_table, 205 u32 remote_node_index) 206{ 207 u32 dword_location; 208 u32 dword_remainder; 209 u32 slot_position; 210 u32 slot_normalized; 211 212 BUG_ON( 213 (remote_node_table->available_nodes_array_size * SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD) 214 <= (remote_node_index / SCU_STP_REMOTE_NODE_COUNT) 215 ); 216 217 dword_location = remote_node_index / SCIC_SDS_REMOTE_NODES_PER_DWORD; 218 dword_remainder = remote_node_index % SCIC_SDS_REMOTE_NODES_PER_DWORD; 219 slot_normalized = (dword_remainder / SCU_STP_REMOTE_NODE_COUNT) * sizeof(u32); 220 slot_position = remote_node_index % SCU_STP_REMOTE_NODE_COUNT; 221 222 remote_node_table->available_remote_nodes[dword_location] &= 223 ~(1 << (slot_normalized + slot_position)); 224} 225 226/** 227 * 228 * @out]: remote_node_table The remote node table from which the slot will be 229 * cleared. 230 * @group_index: The index for the slot that is to be cleared. 231 * 232 * This method clears the entire table slot at the specified slot index. none 233 */ 234static void sci_remote_node_table_clear_group( 235 struct sci_remote_node_table *remote_node_table, 236 u32 group_index) 237{ 238 u32 dword_location; 239 u32 dword_remainder; 240 u32 dword_value; 241 242 BUG_ON( 243 (remote_node_table->available_nodes_array_size * SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD) 244 <= (group_index / SCU_STP_REMOTE_NODE_COUNT) 245 ); 246 247 dword_location = group_index / SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD; 248 dword_remainder = group_index % SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD; 249 250 dword_value = remote_node_table->available_remote_nodes[dword_location]; 251 dword_value &= ~(SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE << (dword_remainder * 4)); 252 remote_node_table->available_remote_nodes[dword_location] = dword_value; 253} 254 255/** 256 * 257 * @remote_node_table: 258 * 259 * THis method sets an entire remote node group in the remote node table. 260 */ 261static void sci_remote_node_table_set_group( 262 struct sci_remote_node_table *remote_node_table, 263 u32 group_index) 264{ 265 u32 dword_location; 266 u32 dword_remainder; 267 u32 dword_value; 268 269 BUG_ON( 270 (remote_node_table->available_nodes_array_size * SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD) 271 <= (group_index / SCU_STP_REMOTE_NODE_COUNT) 272 ); 273 274 dword_location = group_index / SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD; 275 dword_remainder = group_index % SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD; 276 277 dword_value = remote_node_table->available_remote_nodes[dword_location]; 278 dword_value |= (SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE << (dword_remainder * 4)); 279 remote_node_table->available_remote_nodes[dword_location] = dword_value; 280} 281 282/** 283 * 284 * @remote_node_table: This is the remote node table that for which the group 285 * value is to be returned. 286 * @group_index: This is the group index to use to find the group value. 287 * 288 * This method will return the group value for the specified group index. The 289 * bit values at the specified remote node group index. 290 */ 291static u8 sci_remote_node_table_get_group_value( 292 struct sci_remote_node_table *remote_node_table, 293 u32 group_index) 294{ 295 u32 dword_location; 296 u32 dword_remainder; 297 u32 dword_value; 298 299 dword_location = group_index / SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD; 300 dword_remainder = group_index % SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD; 301 302 dword_value = remote_node_table->available_remote_nodes[dword_location]; 303 dword_value &= (SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE << (dword_remainder * 4)); 304 dword_value = dword_value >> (dword_remainder * 4); 305 306 return (u8)dword_value; 307} 308 309/** 310 * 311 * @out]: remote_node_table The remote that which is to be initialized. 312 * @remote_node_entries: The number of entries to put in the table. 313 * 314 * This method will initialize the remote node table for use. none 315 */ 316void sci_remote_node_table_initialize( 317 struct sci_remote_node_table *remote_node_table, 318 u32 remote_node_entries) 319{ 320 u32 index; 321 322 /* 323 * Initialize the raw data we could improve the speed by only initializing 324 * those entries that we are actually going to be used */ 325 memset( 326 remote_node_table->available_remote_nodes, 327 0x00, 328 sizeof(remote_node_table->available_remote_nodes) 329 ); 330 331 memset( 332 remote_node_table->remote_node_groups, 333 0x00, 334 sizeof(remote_node_table->remote_node_groups) 335 ); 336 337 /* Initialize the available remote node sets */ 338 remote_node_table->available_nodes_array_size = (u16) 339 (remote_node_entries / SCIC_SDS_REMOTE_NODES_PER_DWORD) 340 + ((remote_node_entries % SCIC_SDS_REMOTE_NODES_PER_DWORD) != 0); 341 342 343 /* Initialize each full DWORD to a FULL SET of remote nodes */ 344 for (index = 0; index < remote_node_entries; index++) { 345 sci_remote_node_table_set_node_index(remote_node_table, index); 346 } 347 348 remote_node_table->group_array_size = (u16) 349 (remote_node_entries / (SCU_STP_REMOTE_NODE_COUNT * 32)) 350 + ((remote_node_entries % (SCU_STP_REMOTE_NODE_COUNT * 32)) != 0); 351 352 for (index = 0; index < (remote_node_entries / SCU_STP_REMOTE_NODE_COUNT); index++) { 353 /* 354 * These are all guaranteed to be full slot values so fill them in the 355 * available sets of 3 remote nodes */ 356 sci_remote_node_table_set_group_index(remote_node_table, 2, index); 357 } 358 359 /* Now fill in any remainders that we may find */ 360 if ((remote_node_entries % SCU_STP_REMOTE_NODE_COUNT) == 2) { 361 sci_remote_node_table_set_group_index(remote_node_table, 1, index); 362 } else if ((remote_node_entries % SCU_STP_REMOTE_NODE_COUNT) == 1) { 363 sci_remote_node_table_set_group_index(remote_node_table, 0, index); 364 } 365} 366 367/** 368 * 369 * @out]: remote_node_table The remote node table from which to allocate a 370 * remote node. 371 * @table_index: The group index that is to be used for the search. 372 * 373 * This method will allocate a single RNi from the remote node table. The 374 * table index will determine from which remote node group table to search. 375 * This search may fail and another group node table can be specified. The 376 * function is designed to allow a serach of the available single remote node 377 * group up to the triple remote node group. If an entry is found in the 378 * specified table the remote node is removed and the remote node groups are 379 * updated. The RNi value or an invalid remote node context if an RNi can not 380 * be found. 381 */ 382static u16 sci_remote_node_table_allocate_single_remote_node( 383 struct sci_remote_node_table *remote_node_table, 384 u32 group_table_index) 385{ 386 u8 index; 387 u8 group_value; 388 u32 group_index; 389 u16 remote_node_index = SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX; 390 391 group_index = sci_remote_node_table_get_group_index( 392 remote_node_table, group_table_index); 393 394 /* We could not find an available slot in the table selector 0 */ 395 if (group_index != SCIC_SDS_REMOTE_NODE_TABLE_INVALID_INDEX) { 396 group_value = sci_remote_node_table_get_group_value( 397 remote_node_table, group_index); 398 399 for (index = 0; index < SCU_STP_REMOTE_NODE_COUNT; index++) { 400 if (((1 << index) & group_value) != 0) { 401 /* We have selected a bit now clear it */ 402 remote_node_index = (u16)(group_index * SCU_STP_REMOTE_NODE_COUNT 403 + index); 404 405 sci_remote_node_table_clear_group_index( 406 remote_node_table, group_table_index, group_index 407 ); 408 409 sci_remote_node_table_clear_node_index( 410 remote_node_table, remote_node_index 411 ); 412 413 if (group_table_index > 0) { 414 sci_remote_node_table_set_group_index( 415 remote_node_table, group_table_index - 1, group_index 416 ); 417 } 418 419 break; 420 } 421 } 422 } 423 424 return remote_node_index; 425} 426 427/** 428 * 429 * @remote_node_table: This is the remote node table from which to allocate the 430 * remote node entries. 431 * @group_table_index: THis is the group table index which must equal two (2) 432 * for this operation. 433 * 434 * This method will allocate three consecutive remote node context entries. If 435 * there are no remaining triple entries the function will return a failure. 436 * The remote node index that represents three consecutive remote node entries 437 * or an invalid remote node context if none can be found. 438 */ 439static u16 sci_remote_node_table_allocate_triple_remote_node( 440 struct sci_remote_node_table *remote_node_table, 441 u32 group_table_index) 442{ 443 u32 group_index; 444 u16 remote_node_index = SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX; 445 446 group_index = sci_remote_node_table_get_group_index( 447 remote_node_table, group_table_index); 448 449 if (group_index != SCIC_SDS_REMOTE_NODE_TABLE_INVALID_INDEX) { 450 remote_node_index = (u16)group_index * SCU_STP_REMOTE_NODE_COUNT; 451 452 sci_remote_node_table_clear_group_index( 453 remote_node_table, group_table_index, group_index 454 ); 455 456 sci_remote_node_table_clear_group( 457 remote_node_table, group_index 458 ); 459 } 460 461 return remote_node_index; 462} 463 464/** 465 * 466 * @remote_node_table: This is the remote node table from which the remote node 467 * allocation is to take place. 468 * @remote_node_count: This is ther remote node count which is one of 469 * SCU_SSP_REMOTE_NODE_COUNT(1) or SCU_STP_REMOTE_NODE_COUNT(3). 470 * 471 * This method will allocate a remote node that mataches the remote node count 472 * specified by the caller. Valid values for remote node count is 473 * SCU_SSP_REMOTE_NODE_COUNT(1) or SCU_STP_REMOTE_NODE_COUNT(3). u16 This is 474 * the remote node index that is returned or an invalid remote node context. 475 */ 476u16 sci_remote_node_table_allocate_remote_node( 477 struct sci_remote_node_table *remote_node_table, 478 u32 remote_node_count) 479{ 480 u16 remote_node_index = SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX; 481 482 if (remote_node_count == SCU_SSP_REMOTE_NODE_COUNT) { 483 remote_node_index = 484 sci_remote_node_table_allocate_single_remote_node( 485 remote_node_table, 0); 486 487 if (remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) { 488 remote_node_index = 489 sci_remote_node_table_allocate_single_remote_node( 490 remote_node_table, 1); 491 } 492 493 if (remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) { 494 remote_node_index = 495 sci_remote_node_table_allocate_single_remote_node( 496 remote_node_table, 2); 497 } 498 } else if (remote_node_count == SCU_STP_REMOTE_NODE_COUNT) { 499 remote_node_index = 500 sci_remote_node_table_allocate_triple_remote_node( 501 remote_node_table, 2); 502 } 503 504 return remote_node_index; 505} 506 507/** 508 * 509 * @remote_node_table: 510 * 511 * This method will free a single remote node index back to the remote node 512 * table. This routine will update the remote node groups 513 */ 514static void sci_remote_node_table_release_single_remote_node( 515 struct sci_remote_node_table *remote_node_table, 516 u16 remote_node_index) 517{ 518 u32 group_index; 519 u8 group_value; 520 521 group_index = remote_node_index / SCU_STP_REMOTE_NODE_COUNT; 522 523 group_value = sci_remote_node_table_get_group_value(remote_node_table, group_index); 524 525 /* 526 * Assert that we are not trying to add an entry to a slot that is already 527 * full. */ 528 BUG_ON(group_value == SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE); 529 530 if (group_value == 0x00) { 531 /* 532 * There are no entries in this slot so it must be added to the single 533 * slot table. */ 534 sci_remote_node_table_set_group_index(remote_node_table, 0, group_index); 535 } else if ((group_value & (group_value - 1)) == 0) { 536 /* 537 * There is only one entry in this slot so it must be moved from the 538 * single slot table to the dual slot table */ 539 sci_remote_node_table_clear_group_index(remote_node_table, 0, group_index); 540 sci_remote_node_table_set_group_index(remote_node_table, 1, group_index); 541 } else { 542 /* 543 * There are two entries in the slot so it must be moved from the dual 544 * slot table to the tripple slot table. */ 545 sci_remote_node_table_clear_group_index(remote_node_table, 1, group_index); 546 sci_remote_node_table_set_group_index(remote_node_table, 2, group_index); 547 } 548 549 sci_remote_node_table_set_node_index(remote_node_table, remote_node_index); 550} 551 552/** 553 * 554 * @remote_node_table: This is the remote node table to which the remote node 555 * index is to be freed. 556 * 557 * This method will release a group of three consecutive remote nodes back to 558 * the free remote nodes. 559 */ 560static void sci_remote_node_table_release_triple_remote_node( 561 struct sci_remote_node_table *remote_node_table, 562 u16 remote_node_index) 563{ 564 u32 group_index; 565 566 group_index = remote_node_index / SCU_STP_REMOTE_NODE_COUNT; 567 568 sci_remote_node_table_set_group_index( 569 remote_node_table, 2, group_index 570 ); 571 572 sci_remote_node_table_set_group(remote_node_table, group_index); 573} 574 575/** 576 * 577 * @remote_node_table: The remote node table to which the remote node index is 578 * to be freed. 579 * @remote_node_count: This is the count of consecutive remote nodes that are 580 * to be freed. 581 * 582 * This method will release the remote node index back into the remote node 583 * table free pool. 584 */ 585void sci_remote_node_table_release_remote_node_index( 586 struct sci_remote_node_table *remote_node_table, 587 u32 remote_node_count, 588 u16 remote_node_index) 589{ 590 if (remote_node_count == SCU_SSP_REMOTE_NODE_COUNT) { 591 sci_remote_node_table_release_single_remote_node( 592 remote_node_table, remote_node_index); 593 } else if (remote_node_count == SCU_STP_REMOTE_NODE_COUNT) { 594 sci_remote_node_table_release_triple_remote_node( 595 remote_node_table, remote_node_index); 596 } 597} 598 599