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