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 * @file 43 * 44 * Interface to the Octeon extended error status. 45 * 46 * <hr>$Revision: 44252 $<hr> 47 */ 48#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 49#include <asm/octeon/cvmx.h> 50#include <asm/octeon/cvmx-error.h> 51#include <asm/octeon/cvmx-error-custom.h> 52#include <asm/octeon/cvmx-pcie.h> 53#include <asm/octeon/cvmx-srio.h> 54#include <asm/octeon/cvmx-pexp-defs.h> 55#else 56#include "cvmx.h" 57#include "cvmx-error.h" 58#include "cvmx-error-custom.h" 59#include "cvmx-pcie.h" 60#include "cvmx-srio.h" 61#include "cvmx-interrupt.h" 62#endif 63 64#define MAX_TABLE_SIZE 1024 /* Max number of error status bits we can support */ 65 66extern int cvmx_error_initialize_cn63xx(void); 67extern int cvmx_error_initialize_cn63xxp1(void); 68extern int cvmx_error_initialize_cn58xxp1(void); 69extern int cvmx_error_initialize_cn58xx(void); 70extern int cvmx_error_initialize_cn56xxp1(void); 71extern int cvmx_error_initialize_cn56xx(void); 72extern int cvmx_error_initialize_cn50xx(void); 73extern int cvmx_error_initialize_cn52xxp1(void); 74extern int cvmx_error_initialize_cn52xx(void); 75extern int cvmx_error_initialize_cn38xxp2(void); 76extern int cvmx_error_initialize_cn38xx(void); 77extern int cvmx_error_initialize_cn31xx(void); 78extern int cvmx_error_initialize_cn30xx(void); 79 80/* Each entry in this array represents a status bit function or chain */ 81static CVMX_SHARED cvmx_error_info_t __cvmx_error_table[MAX_TABLE_SIZE]; 82static CVMX_SHARED int __cvmx_error_table_size = 0; 83static CVMX_SHARED cvmx_error_flags_t __cvmx_error_flags; 84 85#define REG_MATCH(h, reg_type, status_addr, status_mask) \ 86 ((h->reg_type == reg_type) && (h->status_addr == status_addr) && (h->status_mask == status_mask)) 87 88/** 89 * @INTERNAL 90 * Read a status or enable register from the hardware 91 * 92 * @param reg_type Register type to read 93 * @param addr Address to read 94 * 95 * @return Result of the read 96 */ 97static uint64_t __cvmx_error_read_hw(cvmx_error_register_t reg_type, uint64_t addr) 98{ 99 switch (reg_type) 100 { 101 case __CVMX_ERROR_REGISTER_NONE: 102 return 0; 103 case CVMX_ERROR_REGISTER_IO64: 104 return cvmx_read_csr(addr); 105 case CVMX_ERROR_REGISTER_IO32: 106 return cvmx_read64_uint32(addr ^ 4); 107 case CVMX_ERROR_REGISTER_PCICONFIG: 108 return cvmx_pcie_cfgx_read(addr>>32, addr&0xffffffffull); 109 case CVMX_ERROR_REGISTER_SRIOMAINT: 110 { 111 uint32_t r; 112 if (cvmx_srio_config_read32(addr>>32, 0, -1, 0, 0, addr&0xffffffffull, &r)) 113 return 0; 114 else 115 return r; 116 } 117 } 118 return 0; 119} 120 121/** 122 * @INTERNAL 123 * Write a status or enable register to the hardware 124 * 125 * @param reg_type Register type to write 126 * @param addr Address to write 127 * @param value Value to write 128 */ 129static void __cvmx_error_write_hw(cvmx_error_register_t reg_type, uint64_t addr, uint64_t value) 130{ 131 switch (reg_type) 132 { 133 case __CVMX_ERROR_REGISTER_NONE: 134 return; 135 case CVMX_ERROR_REGISTER_IO64: 136 cvmx_write_csr(addr, value); 137 return; 138 case CVMX_ERROR_REGISTER_IO32: 139 cvmx_write64_uint32(addr ^ 4, value); 140 return; 141 case CVMX_ERROR_REGISTER_PCICONFIG: 142 cvmx_pcie_cfgx_write(addr>>32, addr&0xffffffffull, value); 143 return; 144 case CVMX_ERROR_REGISTER_SRIOMAINT: 145 { 146 cvmx_srio_config_write32(addr>>32, 0, -1, 0, 0, addr&0xffffffffull, value); 147 return; 148 } 149 } 150} 151 152/** 153 * @INTERNAL 154 * Function for processing non leaf error status registers. This function 155 * calls all handlers for this passed register and all children linked 156 * to it. 157 * 158 * @param info Error register to check 159 * 160 * @return Number of error status bits found or zero if no bits were set. 161 */ 162int __cvmx_error_decode(const cvmx_error_info_t *info) 163{ 164 uint64_t status; 165 uint64_t enable; 166 int i; 167 int handled = 0; 168 169 /* Read the status and enable state */ 170 status = __cvmx_error_read_hw(info->reg_type, info->status_addr); 171 if (info->enable_addr) 172 enable = __cvmx_error_read_hw(info->reg_type, info->enable_addr); 173 else 174 enable = 0; 175 176 for (i = 0; i < __cvmx_error_table_size; i++) 177 { 178 const cvmx_error_info_t *h = &__cvmx_error_table[i]; 179 uint64_t masked_status = status; 180 181 /* If this is a child of the current register then recurse and process 182 the child */ 183 if ((h->parent.reg_type == info->reg_type) && 184 (h->parent.status_addr == info->status_addr) && 185 (status & h->parent.status_mask)) 186 handled += __cvmx_error_decode(h); 187 188 if ((h->reg_type != info->reg_type) || (h->status_addr != info->status_addr)) 189 continue; 190 191 /* If the corresponding enable bit is not set then we have nothing to do */ 192 if (h->enable_addr && h->enable_mask) 193 { 194 if (!(enable & h->enable_mask)) 195 continue; 196 } 197 198 /* Apply the mask to eliminate irrelevant bits */ 199 if (h->status_mask) 200 masked_status &= h->status_mask; 201 202 /* Finally call the handler function unless it is this function */ 203 if (masked_status && h->func && (h->func != __cvmx_error_decode)) 204 handled += h->func(h); 205 } 206 /* Ths should be the total errors found */ 207 return handled; 208} 209 210/** 211 * @INTERNAL 212 * This error bit handler simply prints a message and clears the status bit 213 * 214 * @param info Error register to check 215 * 216 * @return 217 */ 218int __cvmx_error_display(const cvmx_error_info_t *info) 219{ 220 const char *message = (const char *)(long)info->user_info; 221 /* This assumes that all bits in the status register are RO or R/W1C */ 222 __cvmx_error_write_hw(info->reg_type, info->status_addr, info->status_mask); 223 cvmx_safe_printf("%s", message); 224 return 1; 225} 226 227/** 228 * Initalize the error status system. This should be called once 229 * before any other functions are called. This function adds default 230 * handlers for most all error events but does not enable them. Later 231 * calls to cvmx_error_enable() are needed. 232 * 233 * @param flags Optional flags. 234 * 235 * @return Zero on success, negative on failure. 236 */ 237int cvmx_error_initialize(cvmx_error_flags_t flags) 238{ 239 __cvmx_error_flags = flags; 240 if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS2_X)) 241 { 242 if (cvmx_error_initialize_cn63xx()) 243 return -1; 244 } 245 else if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_X)) 246 { 247 if (cvmx_error_initialize_cn63xxp1()) 248 return -1; 249 } 250 else if (OCTEON_IS_MODEL(OCTEON_CN58XX_PASS1_X)) 251 { 252 if (cvmx_error_initialize_cn58xxp1()) 253 return -1; 254 } 255 else if (OCTEON_IS_MODEL(OCTEON_CN58XX)) 256 { 257 if (cvmx_error_initialize_cn58xx()) 258 return -1; 259 } 260 else if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X)) 261 { 262 if (cvmx_error_initialize_cn56xxp1()) 263 return -1; 264 } 265 else if (OCTEON_IS_MODEL(OCTEON_CN56XX)) 266 { 267 if (cvmx_error_initialize_cn56xx()) 268 return -1; 269 } 270 else if (OCTEON_IS_MODEL(OCTEON_CN50XX)) 271 { 272 if (cvmx_error_initialize_cn50xx()) 273 return -1; 274 } 275 else if (OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X)) 276 { 277 if (cvmx_error_initialize_cn52xxp1()) 278 return -1; 279 } 280 else if (OCTEON_IS_MODEL(OCTEON_CN52XX)) 281 { 282 if (cvmx_error_initialize_cn52xx()) 283 return -1; 284 } 285 else if (OCTEON_IS_MODEL(OCTEON_CN38XX_PASS2)) 286 { 287 if (cvmx_error_initialize_cn38xxp2()) 288 return -1; 289 } 290 else if (OCTEON_IS_MODEL(OCTEON_CN38XX)) 291 { 292 if (cvmx_error_initialize_cn38xx()) 293 return -1; 294 } 295 else if (OCTEON_IS_MODEL(OCTEON_CN31XX)) 296 { 297 if (cvmx_error_initialize_cn31xx()) 298 return -1; 299 } 300 else if (OCTEON_IS_MODEL(OCTEON_CN30XX)) 301 { 302 if (cvmx_error_initialize_cn30xx()) 303 return -1; 304 } 305 else 306 { 307 cvmx_warn("cvmx_error_initialize() needs update for this Octeon model\n"); 308 return -1; 309 } 310 311 if (__cvmx_error_custom_initialize()) 312 return -1; 313 314 /* Enable all of the purely internal error sources by default */ 315 cvmx_error_enable_group(CVMX_ERROR_GROUP_INTERNAL, 0); 316 317 /* Enable DDR error reporting based on the memory controllers */ 318 if (OCTEON_IS_MODEL(OCTEON_CN56XX)) 319 { 320 cvmx_l2c_cfg_t l2c_cfg; 321 l2c_cfg.u64 = cvmx_read_csr(CVMX_L2C_CFG); 322 if (l2c_cfg.s.dpres0) 323 cvmx_error_enable_group(CVMX_ERROR_GROUP_LMC, 0); 324 if (l2c_cfg.s.dpres1) 325 cvmx_error_enable_group(CVMX_ERROR_GROUP_LMC, 1); 326 } 327 else 328 cvmx_error_enable_group(CVMX_ERROR_GROUP_LMC, 0); 329 330 /* Old PCI parts don't have a common PCI init, so enable error 331 reporting if the bootloader told us we are a PCI host. PCIe 332 is handled when cvmx_pcie_rc_initialize is called */ 333 if (!octeon_has_feature(OCTEON_FEATURE_PCIE) && 334 (cvmx_sysinfo_get()->bootloader_config_flags & CVMX_BOOTINFO_CFG_FLAG_PCI_HOST)) 335 cvmx_error_enable_group(CVMX_ERROR_GROUP_PCI, 0); 336 337 /* FIXME: Why is this needed for CN63XX? */ 338 if (OCTEON_IS_MODEL(OCTEON_CN63XX)) 339 cvmx_write_csr(CVMX_PEXP_SLI_INT_SUM, 1); 340 341 return 0; 342} 343 344/** 345 * Poll the error status registers and call the appropriate error 346 * handlers. This should be called in the RSL interrupt handler 347 * for your application or operating system. 348 * 349 * @return Number of error handlers called. Zero means this call 350 * found no errors and was spurious. 351 */ 352int cvmx_error_poll(void) 353{ 354 int i; 355 int count = 0; 356 /* Call all handlers that don't have a parent */ 357 for (i = 0; i < __cvmx_error_table_size; i++) 358 if (__cvmx_error_table[i].parent.reg_type == __CVMX_ERROR_REGISTER_NONE) 359 count += __cvmx_error_decode(&__cvmx_error_table[i]); 360 return count; 361} 362 363/** 364 * Register to be called when an error status bit is set. Most users 365 * will not need to call this function as cvmx_error_initialize() 366 * registers default handlers for most error conditions. This function 367 * is normally used to add more handlers without changing the existing 368 * handlers. 369 * 370 * @param new_info Information about the handler for a error register. The 371 * structure passed is copied and can be destroyed after the 372 * call. All members of the structure must be populated, even the 373 * parent information. 374 * 375 * @return Zero on success, negative on failure. 376 */ 377int cvmx_error_add(const cvmx_error_info_t *new_info) 378{ 379 if (__cvmx_error_table_size >= MAX_TABLE_SIZE) 380 { 381 cvmx_warn("cvmx-error table full\n"); 382 return -1; 383 } 384 __cvmx_error_table[__cvmx_error_table_size] = *new_info; 385 __cvmx_error_table_size++; 386 return 0; 387} 388 389/** 390 * Remove all handlers for a status register and mask. Normally 391 * this function should not be called. Instead a new handler should be 392 * installed to replace the existing handler. In the even that all 393 * reporting of a error bit should be removed, then use this 394 * function. 395 * 396 * @param reg_type Type of the status register to remove 397 * @param status_addr 398 * Status register to remove. 399 * @param status_mask 400 * All handlers for this status register with this mask will be 401 * removed. 402 * @param old_info If not NULL, this is filled with information about the handler 403 * that was removed. 404 * 405 * @return Zero on success, negative on failure (not found). 406 */ 407int cvmx_error_remove(cvmx_error_register_t reg_type, 408 uint64_t status_addr, uint64_t status_mask, 409 cvmx_error_info_t *old_info) 410{ 411 int found = 0; 412 int i; 413 for (i = 0; i < __cvmx_error_table_size; i++) 414 { 415 cvmx_error_info_t *h = &__cvmx_error_table[i]; 416 if (!REG_MATCH(h, reg_type, status_addr, status_mask)) 417 continue; 418 if (old_info) 419 *old_info = *h; 420 memset(h, 0, sizeof(*h)); 421 found = 1; 422 } 423 if (found) 424 return 0; 425 else 426 { 427 cvmx_warn("cvmx-error remove couldn't find requested register\n"); 428 return -1; 429 } 430} 431 432/** 433 * Change the function and user_info for an existing error status 434 * register. This function should be used to replace the default 435 * handler with an application specific version as needed. 436 * 437 * @param reg_type Type of the status register to change 438 * @param status_addr 439 * Status register to change. 440 * @param status_mask 441 * All handlers for this status register with this mask will be 442 * changed. 443 * @param new_func New function to use to handle the error status 444 * @param new_user_info 445 * New user info parameter for the function 446 * @param old_func If not NULL, the old function is returned. Useful for restoring 447 * the old handler. 448 * @param old_user_info 449 * If not NULL, the old user info parameter. 450 * 451 * @return Zero on success, negative on failure 452 */ 453int cvmx_error_change_handler(cvmx_error_register_t reg_type, 454 uint64_t status_addr, uint64_t status_mask, 455 cvmx_error_func_t new_func, uint64_t new_user_info, 456 cvmx_error_func_t *old_func, uint64_t *old_user_info) 457{ 458 int found = 0; 459 int i; 460 for (i = 0; i < __cvmx_error_table_size; i++) 461 { 462 cvmx_error_info_t *h = &__cvmx_error_table[i]; 463 if (!REG_MATCH(h, reg_type, status_addr, status_mask)) 464 continue; 465 if (old_func) 466 *old_func = h->func; 467 if (old_user_info) 468 *old_user_info = h->user_info; 469 h->func = new_func; 470 h->user_info = new_user_info; 471 found = 1; 472 } 473 if (found) 474 return 0; 475 else 476 { 477 cvmx_warn("cvmx-error change couldn't find requested register\n"); 478 return -1; 479 } 480} 481 482/** 483 * Enable all error registers for a logical group. This should be 484 * called whenever a logical group is brought online. 485 * 486 * @param group Logical group to enable 487 * @param group_index 488 * Index for the group as defined in the cvmx_error_group_t 489 * comments. 490 * 491 * @return Zero on success, negative on failure. 492 */ 493int cvmx_error_enable_group(cvmx_error_group_t group, int group_index) 494{ 495 int i; 496 uint64_t enable; 497 498 if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM) 499 return 0; 500 501 for (i = 0; i < __cvmx_error_table_size; i++) 502 { 503 const cvmx_error_info_t *h = &__cvmx_error_table[i]; 504 /* Skip entries that have a different group or group index. We 505 also skip entries that don't have an enable */ 506 if ((h->group != group) || (h->group_index != group_index) || (!h->enable_addr)) 507 continue; 508 /* Skip entries that have flags that don't match the user's 509 selected flags */ 510 if (h->flags && (h->flags != (h->flags & __cvmx_error_flags))) 511 continue; 512 /* Update the enables for this entry */ 513 enable = __cvmx_error_read_hw(h->reg_type, h->enable_addr); 514 if (h->reg_type == CVMX_ERROR_REGISTER_PCICONFIG) 515 enable &= ~h->enable_mask; /* PCI bits have reversed polarity */ 516 else 517 enable |= h->enable_mask; 518 __cvmx_error_write_hw(h->reg_type, h->enable_addr, enable); 519 } 520 return 0; 521} 522 523/** 524 * Disable all error registers for a logical group. This should be 525 * called whenever a logical group is brought offline. Many blocks 526 * will report spurious errors when offline unless this function 527 * is called. 528 * 529 * @param group Logical group to disable 530 * @param group_index 531 * Index for the group as defined in the cvmx_error_group_t 532 * comments. 533 * 534 * @return Zero on success, negative on failure. 535 */ 536int cvmx_error_disable_group(cvmx_error_group_t group, int group_index) 537{ 538 int i; 539 uint64_t enable; 540 541 if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM) 542 return 0; 543 544 for (i = 0; i < __cvmx_error_table_size; i++) 545 { 546 const cvmx_error_info_t *h = &__cvmx_error_table[i]; 547 /* Skip entries that have a different group or group index. We 548 also skip entries that don't have an enable */ 549 if ((h->group != group) || (h->group_index != group_index) || (!h->enable_addr)) 550 continue; 551 /* Update the enables for this entry */ 552 enable = __cvmx_error_read_hw(h->reg_type, h->enable_addr); 553 if (h->reg_type == CVMX_ERROR_REGISTER_PCICONFIG) 554 enable |= h->enable_mask; /* PCI bits have reversed polarity */ 555 else 556 enable &= ~h->enable_mask; 557 __cvmx_error_write_hw(h->reg_type, h->enable_addr, enable); 558 } 559 return 0; 560} 561 562/** 563 * Enable all handlers for a specific status register mask. 564 * 565 * @param reg_type Type of the status register 566 * @param status_addr 567 * Status register address 568 * @param status_mask 569 * All handlers for this status register with this mask will be 570 * enabled. 571 * 572 * @return Zero on success, negative on failure. 573 */ 574int cvmx_error_enable(cvmx_error_register_t reg_type, 575 uint64_t status_addr, uint64_t status_mask) 576{ 577 int found = 0; 578 int i; 579 uint64_t enable; 580 for (i = 0; i < __cvmx_error_table_size; i++) 581 { 582 cvmx_error_info_t *h = &__cvmx_error_table[i]; 583 if (!REG_MATCH(h, reg_type, status_addr, status_mask) || !h->enable_addr) 584 continue; 585 enable = __cvmx_error_read_hw(h->reg_type, h->enable_addr); 586 if (h->reg_type == CVMX_ERROR_REGISTER_PCICONFIG) 587 enable &= ~h->enable_mask; /* PCI bits have reversed polarity */ 588 else 589 enable |= h->enable_mask; 590 __cvmx_error_write_hw(h->reg_type, h->enable_addr, enable); 591 h->flags &= ~CVMX_ERROR_FLAGS_DISABLED; 592 found = 1; 593 } 594 if (found) 595 return 0; 596 else 597 { 598 cvmx_warn("cvmx-error enable couldn't find requested register\n"); 599 return -1; 600 } 601} 602 603/** 604 * Disable all handlers for a specific status register and mask. 605 * 606 * @param reg_type Type of the status register 607 * @param status_addr 608 * Status register address 609 * @param status_mask 610 * All handlers for this status register with this mask will be 611 * disabled. 612 * 613 * @return Zero on success, negative on failure. 614 */ 615int cvmx_error_disable(cvmx_error_register_t reg_type, 616 uint64_t status_addr, uint64_t status_mask) 617{ 618 int found = 0; 619 int i; 620 uint64_t enable; 621 for (i = 0; i < __cvmx_error_table_size; i++) 622 { 623 cvmx_error_info_t *h = &__cvmx_error_table[i]; 624 if (!REG_MATCH(h, reg_type, status_addr, status_mask) || !h->enable_addr) 625 continue; 626 enable = __cvmx_error_read_hw(h->reg_type, h->enable_addr); 627 if (h->reg_type == CVMX_ERROR_REGISTER_PCICONFIG) 628 enable |= h->enable_mask; /* PCI bits have reversed polarity */ 629 else 630 enable &= ~h->enable_mask; 631 __cvmx_error_write_hw(h->reg_type, h->enable_addr, enable); 632 h->flags |= CVMX_ERROR_FLAGS_DISABLED; 633 found = 1; 634 } 635 if (found) 636 return 0; 637 else 638 { 639 cvmx_warn("cvmx-error disable couldn't find requested register\n"); 640 return -1; 641 } 642} 643 644