cvmx-pow.c revision 215990
1/***********************license start*************** 2 * Copyright (c) 2003-2010 Cavium Networks (support@cavium.com). All rights 3 * reserved. 4 * 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are 8 * met: 9 * 10 * * Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * * Redistributions in binary form must reproduce the above 14 * copyright notice, this list of conditions and the following 15 * disclaimer in the documentation and/or other materials provided 16 * with the distribution. 17 18 * * Neither the name of Cavium Networks nor the names of 19 * its contributors may be used to endorse or promote products 20 * derived from this software without specific prior written 21 * permission. 22 23 * This Software, including technical data, may be subject to U.S. export control 24 * laws, including the U.S. Export Administration Act and its associated 25 * regulations, and may be subject to export or import regulations in other 26 * countries. 27 28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" 29 * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR 30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO 31 * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR 32 * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM 33 * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, 34 * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF 35 * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR 36 * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR 37 * PERFORMANCE OF THE SOFTWARE LIES WITH YOU. 38 ***********************license end**************************************/ 39 40 41 42 43 44 45 46/** 47 * @file 48 * 49 * Interface to the hardware Packet Order / Work unit. 50 * 51 * <hr>$Revision: 29727 $<hr> 52 */ 53 54#include "cvmx.h" 55#include "cvmx-pow.h" 56 57/** 58 * @INTERNAL 59 * This structure stores the internal POW state captured by 60 * cvmx_pow_capture(). It is purposely not exposed to the user 61 * since the format may change without notice. 62 */ 63typedef struct 64{ 65 cvmx_pow_tag_load_resp_t sstatus[16][8]; 66 cvmx_pow_tag_load_resp_t smemload[2048][3]; 67 cvmx_pow_tag_load_resp_t sindexload[16][4]; 68} __cvmx_pow_dump_t; 69 70typedef enum 71{ 72 CVMX_POW_LIST_UNKNOWN=0, 73 CVMX_POW_LIST_FREE=1, 74 CVMX_POW_LIST_INPUT=2, 75 CVMX_POW_LIST_CORE=CVMX_POW_LIST_INPUT+8, 76 CVMX_POW_LIST_DESCHED=CVMX_POW_LIST_CORE+16, 77 CVMX_POW_LIST_NOSCHED=CVMX_POW_LIST_DESCHED+16, 78} __cvmx_pow_list_types_t; 79 80static const char *__cvmx_pow_list_names[] = { 81 "Unknown", 82 "Free List", 83 "Queue 0", "Queue 1", "Queue 2", "Queue 3", 84 "Queue 4", "Queue 5", "Queue 6", "Queue 7", 85 "Core 0", "Core 1", "Core 2", "Core 3", 86 "Core 4", "Core 5", "Core 6", "Core 7", 87 "Core 8", "Core 9", "Core 10", "Core 11", 88 "Core 12", "Core 13", "Core 14", "Core 15", 89 "Desched 0", "Desched 1", "Desched 2", "Desched 3", 90 "Desched 4", "Desched 5", "Desched 6", "Desched 7", 91 "Desched 8", "Desched 9", "Desched 10", "Desched 11", 92 "Desched 12", "Desched 13", "Desched 14", "Desched 15", 93 "Nosched 0", "Nosched 1", "Nosched 2", "Nosched 3", 94 "Nosched 4", "Nosched 5", "Nosched 6", "Nosched 7", 95 "Nosched 8", "Nosched 9", "Nosched 10", "Nosched 11", 96 "Nosched 12", "Nosched 13", "Nosched 14", "Nosched 15" 97}; 98 99 100/** 101 * Return the number of POW entries supported by this chip 102 * 103 * @return Number of POW entries 104 */ 105int cvmx_pow_get_num_entries(void) 106{ 107 if (OCTEON_IS_MODEL(OCTEON_CN30XX)) 108 return 64; 109 else if (OCTEON_IS_MODEL(OCTEON_CN31XX) || OCTEON_IS_MODEL(OCTEON_CN50XX)) 110 return 256; 111 else if (OCTEON_IS_MODEL(OCTEON_CN52XX)) 112 return 512; 113 else if (OCTEON_IS_MODEL(OCTEON_CN63XX)) 114 return 1024; 115 else 116 return 2048; 117} 118 119 120/** 121 * Store the current POW internal state into the supplied 122 * buffer. It is recommended that you pass a buffer of at least 123 * 128KB. The format of the capture may change based on SDK 124 * version and Octeon chip. 125 * 126 * @param buffer Buffer to store capture into 127 * @param buffer_size 128 * The size of the supplied buffer 129 * 130 * @return Zero on sucess, negative on failure 131 */ 132int cvmx_pow_capture(void *buffer, int buffer_size) 133{ 134 __cvmx_pow_dump_t *dump = (__cvmx_pow_dump_t*)buffer; 135 int num_cores; 136 int num_pow_entries = cvmx_pow_get_num_entries(); 137 int core; 138 int index; 139 int bits; 140 141 if (buffer_size < (int)sizeof(__cvmx_pow_dump_t)) 142 { 143 cvmx_dprintf("cvmx_pow_capture: Buffer too small\n"); 144 return -1; 145 } 146 147 num_cores = cvmx_octeon_num_cores(); 148 149 /* Read all core related state */ 150 for (core=0; core<num_cores; core++) 151 { 152 cvmx_pow_load_addr_t load_addr; 153 load_addr.u64 = 0; 154 load_addr.sstatus.mem_region = CVMX_IO_SEG; 155 load_addr.sstatus.is_io = 1; 156 load_addr.sstatus.did = CVMX_OCT_DID_TAG_TAG1; 157 load_addr.sstatus.coreid = core; 158 for (bits=0; bits<8; bits++) 159 { 160 load_addr.sstatus.get_rev = (bits & 1) != 0; 161 load_addr.sstatus.get_cur = (bits & 2) != 0; 162 load_addr.sstatus.get_wqp = (bits & 4) != 0; 163 if ((load_addr.sstatus.get_cur == 0) && load_addr.sstatus.get_rev) 164 dump->sstatus[core][bits].u64 = -1; 165 else 166 dump->sstatus[core][bits].u64 = cvmx_read_csr(load_addr.u64); 167 } 168 } 169 170 /* Read all internal POW entries */ 171 for (index=0; index<num_pow_entries; index++) 172 { 173 cvmx_pow_load_addr_t load_addr; 174 load_addr.u64 = 0; 175 load_addr.smemload.mem_region = CVMX_IO_SEG; 176 load_addr.smemload.is_io = 1; 177 load_addr.smemload.did = CVMX_OCT_DID_TAG_TAG2; 178 load_addr.smemload.index = index; 179 for (bits=0; bits<3; bits++) 180 { 181 load_addr.smemload.get_des = (bits & 1) != 0; 182 load_addr.smemload.get_wqp = (bits & 2) != 0; 183 dump->smemload[index][bits].u64 = cvmx_read_csr(load_addr.u64); 184 } 185 } 186 187 /* Read all group and queue pointers */ 188 for (index=0; index<16; index++) 189 { 190 cvmx_pow_load_addr_t load_addr; 191 load_addr.u64 = 0; 192 load_addr.sindexload.mem_region = CVMX_IO_SEG; 193 load_addr.sindexload.is_io = 1; 194 load_addr.sindexload.did = CVMX_OCT_DID_TAG_TAG3; 195 load_addr.sindexload.qosgrp = index; 196 for (bits=0; bits<4; bits++) 197 { 198 load_addr.sindexload.get_rmt = (bits & 1) != 0; 199 load_addr.sindexload.get_des_get_tail = (bits & 2) != 0; 200 /* The first pass only has 8 valid index values */ 201 if ((load_addr.sindexload.get_rmt == 0) && 202 (load_addr.sindexload.get_des_get_tail == 0) && 203 (index >= 8)) 204 dump->sindexload[index][bits].u64 = -1; 205 else 206 dump->sindexload[index][bits].u64 = cvmx_read_csr(load_addr.u64); 207 } 208 } 209 return 0; 210} 211 212 213/** 214 * Function to display a POW internal queue to the user 215 * 216 * @param name User visible name for the queue 217 * @param name_param Parameter for printf in creating the name 218 * @param valid Set if the queue contains any elements 219 * @param has_one Set if the queue contains exactly one element 220 * @param head The head pointer 221 * @param tail The tail pointer 222 */ 223static void __cvmx_pow_display_list(const char *name, int name_param, int valid, int has_one, uint64_t head, uint64_t tail) 224{ 225 printf(name, name_param); 226 printf(": "); 227 if (valid) 228 { 229 if (has_one) 230 printf("One element index=%llu(0x%llx)\n", CAST64(head), CAST64(head)); 231 else 232 printf("Multiple elements head=%llu(0x%llx) tail=%llu(0x%llx)\n", CAST64(head), CAST64(head), CAST64(tail), CAST64(tail)); 233 } 234 else 235 printf("Empty\n"); 236} 237 238 239/** 240 * Mark which list a POW entry is on. Print a warning message if the 241 * entry is already on a list. This happens if the POW changed while 242 * the capture was running. 243 * 244 * @param entry_num Entry number to mark 245 * @param entry_type List type 246 * @param entry_list Array to store marks 247 * 248 * @return Zero on success, negative if already on a list 249 */ 250static int __cvmx_pow_entry_mark_list(int entry_num, __cvmx_pow_list_types_t entry_type, uint8_t entry_list[]) 251{ 252 if (entry_list[entry_num] == 0) 253 { 254 entry_list[entry_num] = entry_type; 255 return 0; 256 } 257 else 258 { 259 printf("\nWARNING: Entry %d already on list %s, but we tried to add it to %s\n", 260 entry_num, __cvmx_pow_list_names[entry_list[entry_num]], __cvmx_pow_list_names[entry_type]); 261 return -1; 262 } 263} 264 265 266/** 267 * Display a list and mark all elements on the list as belonging to 268 * the list. 269 * 270 * @param entry_type Type of the list to display and mark 271 * @param dump POW capture data 272 * @param entry_list Array to store marks in 273 * @param valid Set if the queue contains any elements 274 * @param has_one Set if the queue contains exactly one element 275 * @param head The head pointer 276 * @param tail The tail pointer 277 */ 278static void __cvmx_pow_display_list_and_walk(__cvmx_pow_list_types_t entry_type, 279 __cvmx_pow_dump_t *dump, uint8_t entry_list[], 280 int valid, int has_one, uint64_t head, uint64_t tail) 281{ 282 __cvmx_pow_display_list(__cvmx_pow_list_names[entry_type], 0, valid, has_one, head, tail); 283 if (valid) 284 { 285 if (has_one) 286 __cvmx_pow_entry_mark_list(head, entry_type, entry_list); 287 else 288 { 289 while (head != tail) 290 { 291 if (__cvmx_pow_entry_mark_list(head, entry_type, entry_list)) 292 break; 293 head = dump->smemload[head][0].s_smemload0.next_index; 294 } 295 __cvmx_pow_entry_mark_list(tail, entry_type, entry_list); 296 } 297 } 298} 299 300 301/** 302 * Dump a POW capture to the console in a human readable format. 303 * 304 * @param buffer POW capture from cvmx_pow_capture() 305 * @param buffer_size 306 * Size of the buffer 307 */ 308void cvmx_pow_display(void *buffer, int buffer_size) 309{ 310 __cvmx_pow_dump_t *dump = (__cvmx_pow_dump_t*)buffer; 311 int num_pow_entries = cvmx_pow_get_num_entries(); 312 int num_cores; 313 int core; 314 int index; 315 uint8_t entry_list[2048]; 316 317 if (buffer_size < (int)sizeof(__cvmx_pow_dump_t)) 318 { 319 cvmx_dprintf("cvmx_pow_dump: Buffer too small\n"); 320 return; 321 } 322 323 memset(entry_list, 0, sizeof(entry_list)); 324 num_cores = cvmx_octeon_num_cores(); 325 326 printf("POW Display Start\n"); 327 328 /* Print the free list info */ 329 __cvmx_pow_display_list_and_walk(CVMX_POW_LIST_FREE, dump, entry_list, 330 dump->sindexload[0][0].sindexload0.free_val, 331 dump->sindexload[0][0].sindexload0.free_one, 332 dump->sindexload[0][0].sindexload0.free_head, 333 dump->sindexload[0][0].sindexload0.free_tail); 334 335 /* Print the core state */ 336 for (core=0; core<num_cores; core++) 337 { 338 const int bit_rev = 1; 339 const int bit_cur = 2; 340 const int bit_wqp = 4; 341 printf("Core %d State: tag=%s,0x%08x", core, 342 OCT_TAG_TYPE_STRING(dump->sstatus[core][bit_cur].s_sstatus2.tag_type), 343 dump->sstatus[core][bit_cur].s_sstatus2.tag); 344 if (dump->sstatus[core][bit_cur].s_sstatus2.tag_type != CVMX_POW_TAG_TYPE_NULL_NULL) 345 { 346 __cvmx_pow_entry_mark_list(dump->sstatus[core][bit_cur].s_sstatus2.index, CVMX_POW_LIST_CORE + core, entry_list); 347 printf(" grp=%d", dump->sstatus[core][bit_cur].s_sstatus2.grp); 348 printf(" wqp=0x%016llx", CAST64(dump->sstatus[core][bit_cur|bit_wqp].s_sstatus4.wqp)); 349 printf(" index=%d", dump->sstatus[core][bit_cur].s_sstatus2.index); 350 if (dump->sstatus[core][bit_cur].s_sstatus2.head) 351 printf(" head"); 352 else 353 printf(" prev=%d", dump->sstatus[core][bit_cur|bit_rev].s_sstatus3.revlink_index); 354 if (dump->sstatus[core][bit_cur].s_sstatus2.tail) 355 printf(" tail"); 356 else 357 printf(" next=%d", dump->sstatus[core][bit_cur].s_sstatus2.link_index); 358 } 359 360 if (dump->sstatus[core][0].s_sstatus0.pend_switch) 361 { 362 printf(" pend_switch=%d", dump->sstatus[core][0].s_sstatus0.pend_switch); 363 printf(" pend_switch_full=%d", dump->sstatus[core][0].s_sstatus0.pend_switch_full); 364 printf(" pend_switch_null=%d", dump->sstatus[core][0].s_sstatus0.pend_switch_null); 365 } 366 367 if (dump->sstatus[core][0].s_sstatus0.pend_desched) 368 { 369 printf(" pend_desched=%d", dump->sstatus[core][0].s_sstatus0.pend_desched); 370 printf(" pend_desched_switch=%d", dump->sstatus[core][0].s_sstatus0.pend_desched_switch); 371 printf(" pend_nosched=%d", dump->sstatus[core][0].s_sstatus0.pend_nosched); 372 if (dump->sstatus[core][0].s_sstatus0.pend_desched_switch) 373 printf(" pend_grp=%d", dump->sstatus[core][0].s_sstatus0.pend_grp); 374 } 375 376 if (dump->sstatus[core][0].s_sstatus0.pend_new_work) 377 { 378 if (dump->sstatus[core][0].s_sstatus0.pend_new_work_wait) 379 printf(" (Waiting for work)"); 380 else 381 printf(" (Getting work)"); 382 } 383 if (dump->sstatus[core][0].s_sstatus0.pend_null_rd) 384 printf(" pend_null_rd=%d", dump->sstatus[core][0].s_sstatus0.pend_null_rd); 385 if (dump->sstatus[core][0].s_sstatus0.pend_nosched_clr) 386 { 387 printf(" pend_nosched_clr=%d", dump->sstatus[core][0].s_sstatus0.pend_nosched_clr); 388 printf(" pend_index=%d", dump->sstatus[core][0].s_sstatus0.pend_index); 389 } 390 if (dump->sstatus[core][0].s_sstatus0.pend_switch || 391 (dump->sstatus[core][0].s_sstatus0.pend_desched && 392 dump->sstatus[core][0].s_sstatus0.pend_desched_switch)) 393 { 394 printf(" pending tag=%s,0x%08x", 395 OCT_TAG_TYPE_STRING(dump->sstatus[core][0].s_sstatus0.pend_type), 396 dump->sstatus[core][0].s_sstatus0.pend_tag); 397 } 398 if (dump->sstatus[core][0].s_sstatus0.pend_nosched_clr) 399 printf(" pend_wqp=0x%016llx\n", CAST64(dump->sstatus[core][bit_wqp].s_sstatus1.pend_wqp)); 400 printf("\n"); 401 } 402 403 /* Print out the state of the nosched list and the 16 deschedule lists. */ 404 __cvmx_pow_display_list_and_walk(CVMX_POW_LIST_NOSCHED, dump, entry_list, 405 dump->sindexload[0][2].sindexload1.nosched_val, 406 dump->sindexload[0][2].sindexload1.nosched_one, 407 dump->sindexload[0][2].sindexload1.nosched_head, 408 dump->sindexload[0][2].sindexload1.nosched_tail); 409 for (index=0; index<16; index++) 410 { 411 __cvmx_pow_display_list_and_walk(CVMX_POW_LIST_DESCHED + index, dump, entry_list, 412 dump->sindexload[index][2].sindexload1.des_val, 413 dump->sindexload[index][2].sindexload1.des_one, 414 dump->sindexload[index][2].sindexload1.des_head, 415 dump->sindexload[index][2].sindexload1.des_tail); 416 } 417 418 /* Print out the state of the 8 internal input queues */ 419 for (index=0; index<8; index++) 420 { 421 __cvmx_pow_display_list_and_walk(CVMX_POW_LIST_INPUT + index, dump, entry_list, 422 dump->sindexload[index][0].sindexload0.loc_val, 423 dump->sindexload[index][0].sindexload0.loc_one, 424 dump->sindexload[index][0].sindexload0.loc_head, 425 dump->sindexload[index][0].sindexload0.loc_tail); 426 } 427 428 /* Print out the state of the 16 memory queues */ 429 for (index=0; index<8; index++) 430 { 431 const char *name; 432 if (dump->sindexload[index][1].sindexload2.rmt_is_head) 433 name = "Queue %da Memory (is head)"; 434 else 435 name = "Queue %da Memory"; 436 __cvmx_pow_display_list(name, index, 437 dump->sindexload[index][1].sindexload2.rmt_val, 438 dump->sindexload[index][1].sindexload2.rmt_one, 439 dump->sindexload[index][1].sindexload2.rmt_head, 440 dump->sindexload[index][3].sindexload3.rmt_tail); 441 if (dump->sindexload[index+8][1].sindexload2.rmt_is_head) 442 name = "Queue %db Memory (is head)"; 443 else 444 name = "Queue %db Memory"; 445 __cvmx_pow_display_list(name, index, 446 dump->sindexload[index+8][1].sindexload2.rmt_val, 447 dump->sindexload[index+8][1].sindexload2.rmt_one, 448 dump->sindexload[index+8][1].sindexload2.rmt_head, 449 dump->sindexload[index+8][3].sindexload3.rmt_tail); 450 } 451 452 /* Print out each of the internal POW entries. Each entry has a tag, group, 453 wqe, and possibly a next pointer. The next pointer is only valid if this 454 entry isn't make as a tail */ 455 for (index=0; index<num_pow_entries; index++) 456 { 457 printf("Entry %d(%-10s): tag=%s,0x%08x grp=%d wqp=0x%016llx", index, 458 __cvmx_pow_list_names[entry_list[index]], 459 OCT_TAG_TYPE_STRING(dump->smemload[index][0].s_smemload0.tag_type), 460 dump->smemload[index][0].s_smemload0.tag, 461 dump->smemload[index][0].s_smemload0.grp, 462 CAST64(dump->smemload[index][2].s_smemload1.wqp)); 463 if (dump->smemload[index][0].s_smemload0.tail) 464 printf(" tail"); 465 else 466 printf(" next=%d", dump->smemload[index][0].s_smemload0.next_index); 467 if (entry_list[index] >= CVMX_POW_LIST_DESCHED) 468 { 469 printf(" prev=%d", dump->smemload[index][1].s_smemload2.fwd_index); 470 printf(" nosched=%d", dump->smemload[index][1].s_smemload2.nosched); 471 if (dump->smemload[index][1].s_smemload2.pend_switch) 472 { 473 printf(" pending tag=%s,0x%08x", 474 OCT_TAG_TYPE_STRING(dump->smemload[index][1].s_smemload2.pend_type), 475 dump->smemload[index][1].s_smemload2.pend_tag); 476 } 477 } 478 printf("\n"); 479 } 480 481 printf("POW Display End\n"); 482} 483 484