]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/contrib/octeon-sdk/cvmx-error.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / contrib / octeon-sdk / cvmx-error.c
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
66 extern int cvmx_error_initialize_cn63xx(void);
67 extern int cvmx_error_initialize_cn63xxp1(void);
68 extern int cvmx_error_initialize_cn58xxp1(void);
69 extern int cvmx_error_initialize_cn58xx(void);
70 extern int cvmx_error_initialize_cn56xxp1(void);
71 extern int cvmx_error_initialize_cn56xx(void);
72 extern int cvmx_error_initialize_cn50xx(void);
73 extern int cvmx_error_initialize_cn52xxp1(void);
74 extern int cvmx_error_initialize_cn52xx(void);
75 extern int cvmx_error_initialize_cn38xxp2(void);
76 extern int cvmx_error_initialize_cn38xx(void);
77 extern int cvmx_error_initialize_cn31xx(void);
78 extern int cvmx_error_initialize_cn30xx(void);
79
80 /* Each entry in this array represents a status bit function or chain */
81 static CVMX_SHARED cvmx_error_info_t __cvmx_error_table[MAX_TABLE_SIZE];
82 static CVMX_SHARED int __cvmx_error_table_size = 0;
83 static 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  */
97 static 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  */
129 static 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  */
162 int __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  */
218 int __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  */
237 int 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  */
352 int 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  */
377 int 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  */
407 int 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  */
453 int 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  */
493 int 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  */
536 int 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  */
574 int 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  */
615 int 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