net/qede/base: support doorbell overflow recovery
[dpdk.git] / drivers / net / qede / base / ecore_int.c
index e7dfe04..d86f56e 100644 (file)
@@ -414,31 +414,136 @@ ecore_general_attention_35(struct ecore_hwfn *p_hwfn)
        return ECORE_SUCCESS;
 }
 
-#define ECORE_DORQ_ATTENTION_REASON_MASK (0xfffff)
-#define ECORE_DORQ_ATTENTION_OPAQUE_MASK (0xffff)
-#define ECORE_DORQ_ATTENTION_SIZE_MASK  (0x7f0000)
-#define ECORE_DORQ_ATTENTION_SIZE_SHIFT         (16)
+#define ECORE_DORQ_ATTENTION_REASON_MASK       (0xfffff)
+#define ECORE_DORQ_ATTENTION_OPAQUE_MASK       (0xffff)
+#define ECORE_DORQ_ATTENTION_OPAQUE_SHIFT      (0x0)
+#define ECORE_DORQ_ATTENTION_SIZE_MASK         (0x7f)
+#define ECORE_DORQ_ATTENTION_SIZE_SHIFT                (16)
+
+#define ECORE_DB_REC_COUNT                     10
+#define ECORE_DB_REC_INTERVAL                  100
+
+/* assumes sticky overflow indication was set for this PF */
+static enum _ecore_status_t ecore_db_rec_attn(struct ecore_hwfn *p_hwfn,
+                                             struct ecore_ptt *p_ptt)
+{
+       u8 count = ECORE_DB_REC_COUNT;
+       u32 usage = 1;
+
+       /* wait for usage to zero or count to run out. This is necessary since
+        * EDPM doorbell transactions can take multiple 64b cycles, and as such
+        * can "split" over the pci. Possibly, the doorbell drop can happen with
+        * half an EDPM in the queue and other half dropped. Another EDPM
+        * doorbell to the same address (from doorbell recovery mechanism or
+        * from the doorbelling entity) could have first half dropped and second
+        * half interperted as continuation of the first. To prevent such
+        * malformed doorbells from reaching the device, flush the queue before
+        * releaseing the overflow sticky indication.
+        */
+       while (count-- && usage) {
+               usage = ecore_rd(p_hwfn, p_ptt, DORQ_REG_PF_USAGE_CNT);
+               OSAL_UDELAY(ECORE_DB_REC_INTERVAL);
+       }
+
+       /* should have been depleted by now */
+       if (usage) {
+               DP_NOTICE(p_hwfn->p_dev, false,
+                         "DB recovery: doorbell usage failed to zero after %d usec. usage was %x\n",
+                         ECORE_DB_REC_INTERVAL * ECORE_DB_REC_COUNT, usage);
+               return ECORE_TIMEOUT;
+       }
+
+       /* flush any pedning (e)dpm as they may never arrive */
+       ecore_wr(p_hwfn, p_ptt, DORQ_REG_DPM_FORCE_ABORT, 0x1);
+
+       /* release overflow sticky indication (stop silently dropping
+        * everything)
+        */
+       ecore_wr(p_hwfn, p_ptt, DORQ_REG_PF_OVFL_STICKY, 0x0);
+
+       /* repeat all last doorbells (doorbell drop recovery) */
+       ecore_db_recovery_execute(p_hwfn, DB_REC_REAL_DEAL);
+
+       return ECORE_SUCCESS;
+}
 
 static enum _ecore_status_t ecore_dorq_attn_cb(struct ecore_hwfn *p_hwfn)
 {
-       u32 reason;
+       u32 int_sts, first_drop_reason, details, address, overflow,
+               all_drops_reason;
+       struct ecore_ptt *p_ptt = p_hwfn->p_dpc_ptt;
+       enum _ecore_status_t rc;
 
-       reason = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt, DORQ_REG_DB_DROP_REASON) &
-           ECORE_DORQ_ATTENTION_REASON_MASK;
-       if (reason) {
-               u32 details = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
-                                      DORQ_REG_DB_DROP_DETAILS);
+       int_sts = ecore_rd(p_hwfn, p_ptt, DORQ_REG_INT_STS);
+       DP_NOTICE(p_hwfn->p_dev, false, "DORQ attention. int_sts was %x\n",
+                 int_sts);
 
-               DP_INFO(p_hwfn->p_dev,
-                       "DORQ db_drop: address 0x%08x Opaque FID 0x%04x"
-                       " Size [bytes] 0x%08x Reason: 0x%08x\n",
-                       ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
-                                DORQ_REG_DB_DROP_DETAILS_ADDRESS),
-                       (u16)(details & ECORE_DORQ_ATTENTION_OPAQUE_MASK),
-                       ((details & ECORE_DORQ_ATTENTION_SIZE_MASK) >>
-                        ECORE_DORQ_ATTENTION_SIZE_SHIFT) * 4, reason);
+       /* int_sts may be zero since all PFs were interrupted for doorbell
+        * overflow but another one already handled it. Can abort here. If
+        * This PF also requires overflow recovery we will be interrupted again
+        */
+       if (!int_sts)
+               return ECORE_SUCCESS;
+
+       /* check if db_drop or overflow happened */
+       if (int_sts & (DORQ_REG_INT_STS_DB_DROP |
+                      DORQ_REG_INT_STS_DORQ_FIFO_OVFL_ERR)) {
+               /* obtain data about db drop/overflow */
+               first_drop_reason = ecore_rd(p_hwfn, p_ptt,
+                                 DORQ_REG_DB_DROP_REASON) &
+                                 ECORE_DORQ_ATTENTION_REASON_MASK;
+               details = ecore_rd(p_hwfn, p_ptt,
+                                  DORQ_REG_DB_DROP_DETAILS);
+               address = ecore_rd(p_hwfn, p_ptt,
+                                  DORQ_REG_DB_DROP_DETAILS_ADDRESS);
+               overflow = ecore_rd(p_hwfn, p_ptt,
+                                   DORQ_REG_PF_OVFL_STICKY);
+               all_drops_reason = ecore_rd(p_hwfn, p_ptt,
+                                           DORQ_REG_DB_DROP_DETAILS_REASON);
+
+               /* log info */
+               DP_NOTICE(p_hwfn->p_dev, false,
+                         "Doorbell drop occurred\n"
+                         "Address\t\t0x%08x\t(second BAR address)\n"
+                         "FID\t\t0x%04x\t\t(Opaque FID)\n"
+                         "Size\t\t0x%04x\t\t(in bytes)\n"
+                         "1st drop reason\t0x%08x\t(details on first drop since last handling)\n"
+                         "Sticky reasons\t0x%08x\t(all drop reasons since last handling)\n"
+                         "Overflow\t0x%x\t\t(a per PF indication)\n",
+                         address,
+                         GET_FIELD(details, ECORE_DORQ_ATTENTION_OPAQUE),
+                         GET_FIELD(details, ECORE_DORQ_ATTENTION_SIZE) * 4,
+                         first_drop_reason, all_drops_reason, overflow);
+
+               /* if this PF caused overflow, initiate recovery */
+               if (overflow) {
+                       rc = ecore_db_rec_attn(p_hwfn, p_ptt);
+                       if (rc != ECORE_SUCCESS)
+                               return rc;
+               }
+
+               /* clear the doorbell drop details and prepare for next drop */
+               ecore_wr(p_hwfn, p_ptt, DORQ_REG_DB_DROP_DETAILS_REL, 0);
+
+               /* mark interrupt as handeld (note: even if drop was due to a
+                * different reason than overflow we mark as handled)
+                */
+               ecore_wr(p_hwfn, p_ptt, DORQ_REG_INT_STS_WR,
+                        DORQ_REG_INT_STS_DB_DROP |
+                        DORQ_REG_INT_STS_DORQ_FIFO_OVFL_ERR);
+
+               /* if there are no indications otherthan drop indications,
+                * success
+                */
+               if ((int_sts & ~(DORQ_REG_INT_STS_DB_DROP |
+                                DORQ_REG_INT_STS_DORQ_FIFO_OVFL_ERR |
+                                DORQ_REG_INT_STS_DORQ_FIFO_AFULL)) == 0)
+                       return ECORE_SUCCESS;
        }
 
+       /* some other indication was present - non recoverable */
+       DP_INFO(p_hwfn, "DORQ fatal attention\n");
+
        return ECORE_INVAL;
 }
 
@@ -851,32 +956,38 @@ ecore_int_deassertion_aeu_bit(struct ecore_hwfn *p_hwfn,
  * @brief ecore_int_deassertion_parity - handle a single parity AEU source
  *
  * @param p_hwfn
- * @param p_aeu - descriptor of an AEU bit which caused the
- *              parity
+ * @param p_aeu - descriptor of an AEU bit which caused the parity
+ * @param aeu_en_reg - address of the AEU enable register
  * @param bit_index
  */
 static void ecore_int_deassertion_parity(struct ecore_hwfn *p_hwfn,
                                         struct aeu_invert_reg_bit *p_aeu,
-                                        u8 bit_index)
+                                        u32 aeu_en_reg, u8 bit_index)
 {
-       u32 block_id = p_aeu->block_index;
-
-       DP_INFO(p_hwfn->p_dev, "%s[%d] parity attention is set\n",
-               p_aeu->bit_name, bit_index);
+       u32 block_id = p_aeu->block_index, mask, val;
 
-       if (block_id == MAX_BLOCK_ID)
-               return;
-
-       ecore_int_attn_print(p_hwfn, block_id,
-                            ATTN_TYPE_PARITY, false);
-
-       /* In A0, there's a single parity bit for several blocks */
-       if (block_id == BLOCK_BTB) {
-               ecore_int_attn_print(p_hwfn, BLOCK_OPTE,
-                                    ATTN_TYPE_PARITY, false);
-               ecore_int_attn_print(p_hwfn, BLOCK_MCP,
-                                    ATTN_TYPE_PARITY, false);
+       DP_NOTICE(p_hwfn->p_dev, false,
+                 "%s parity attention is set [address 0x%08x, bit %d]\n",
+                 p_aeu->bit_name, aeu_en_reg, bit_index);
+
+       if (block_id != MAX_BLOCK_ID) {
+               ecore_int_attn_print(p_hwfn, block_id, ATTN_TYPE_PARITY, false);
+
+               /* In A0, there's a single parity bit for several blocks */
+               if (block_id == BLOCK_BTB) {
+                       ecore_int_attn_print(p_hwfn, BLOCK_OPTE,
+                                            ATTN_TYPE_PARITY, false);
+                       ecore_int_attn_print(p_hwfn, BLOCK_MCP,
+                                            ATTN_TYPE_PARITY, false);
+               }
        }
+
+       /* Prevent this parity error from being re-asserted */
+       mask = ~(0x1 << bit_index);
+       val = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt, aeu_en_reg);
+       ecore_wr(p_hwfn, p_hwfn->p_dpc_ptt, aeu_en_reg, val & mask);
+       DP_INFO(p_hwfn, "`%s' - Disabled future parity errors\n",
+               p_aeu->bit_name);
 }
 
 /**
@@ -891,8 +1002,7 @@ static enum _ecore_status_t ecore_int_deassertion(struct ecore_hwfn *p_hwfn,
                                                  u16 deasserted_bits)
 {
        struct ecore_sb_attn_info *sb_attn_sw = p_hwfn->p_sb_attn;
-       u32 aeu_inv_arr[NUM_ATTN_REGS], aeu_mask;
-       bool b_parity = false;
+       u32 aeu_inv_arr[NUM_ATTN_REGS], aeu_mask, aeu_en, en;
        u8 i, j, k, bit_idx;
        enum _ecore_status_t rc = ECORE_SUCCESS;
 
@@ -908,11 +1018,11 @@ static enum _ecore_status_t ecore_int_deassertion(struct ecore_hwfn *p_hwfn,
        /* Handle parity attentions first */
        for (i = 0; i < NUM_ATTN_REGS; i++) {
                struct aeu_invert_reg *p_aeu = &sb_attn_sw->p_aeu_desc[i];
-               u32 en = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
-                                 MISC_REG_AEU_ENABLE1_IGU_OUT_0 +
-                                 i * sizeof(u32));
+               u32 parities;
 
-               u32 parities = sb_attn_sw->parity_mask[i] & aeu_inv_arr[i] & en;
+               aeu_en = MISC_REG_AEU_ENABLE1_IGU_OUT_0 + i * sizeof(u32);
+               en = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt, aeu_en);
+               parities = sb_attn_sw->parity_mask[i] & aeu_inv_arr[i] & en;
 
                /* Skip register in which no parity bit is currently set */
                if (!parities)
@@ -922,11 +1032,9 @@ static enum _ecore_status_t ecore_int_deassertion(struct ecore_hwfn *p_hwfn,
                        struct aeu_invert_reg_bit *p_bit = &p_aeu->bits[j];
 
                        if (ecore_int_is_parity_flag(p_hwfn, p_bit) &&
-                           !!(parities & (1 << bit_idx))) {
+                           !!(parities & (1 << bit_idx)))
                                ecore_int_deassertion_parity(p_hwfn, p_bit,
-                                                            bit_idx);
-                               b_parity = true;
-                       }
+                                                            aeu_en, bit_idx);
 
                        bit_idx += ATTENTION_LENGTH(p_bit->flags);
                }
@@ -941,10 +1049,13 @@ static enum _ecore_status_t ecore_int_deassertion(struct ecore_hwfn *p_hwfn,
                        continue;
 
                for (i = 0; i < NUM_ATTN_REGS; i++) {
-                       u32 aeu_en = MISC_REG_AEU_ENABLE1_IGU_OUT_0 +
-                           i * sizeof(u32) + k * sizeof(u32) * NUM_ATTN_REGS;
-                       u32 en = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt, aeu_en);
-                       u32 bits = aeu_inv_arr[i] & en;
+                       u32 bits;
+
+                       aeu_en = MISC_REG_AEU_ENABLE1_IGU_OUT_0 +
+                                i * sizeof(u32) +
+                                k * sizeof(u32) * NUM_ATTN_REGS;
+                       en = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt, aeu_en);
+                       bits = aeu_inv_arr[i] & en;
 
                        /* Skip if no bit from this group is currently set */
                        if (!bits)