| // SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause |
| |
| /* Interrupt related logic for Mellanox Gigabit Ethernet driver |
| * |
| * Copyright (C) 2020-2021 NVIDIA CORPORATION & AFFILIATES |
| */ |
| |
| #include <linux/interrupt.h> |
| |
| #include "mlxbf_gige.h" |
| #include "mlxbf_gige_regs.h" |
| |
| static irqreturn_t mlxbf_gige_error_intr(int irq, void *dev_id) |
| { |
| struct mlxbf_gige *priv; |
| u64 int_status; |
| |
| priv = dev_id; |
| |
| int_status = readq(priv->base + MLXBF_GIGE_INT_STATUS); |
| |
| if (int_status & MLXBF_GIGE_INT_STATUS_HW_ACCESS_ERROR) |
| priv->stats.hw_access_errors++; |
| |
| if (int_status & MLXBF_GIGE_INT_STATUS_TX_CHECKSUM_INPUTS) { |
| priv->stats.tx_invalid_checksums++; |
| /* This error condition is latched into MLXBF_GIGE_INT_STATUS |
| * when the GigE silicon operates on the offending |
| * TX WQE. The write to MLXBF_GIGE_INT_STATUS at the bottom |
| * of this routine clears this error condition. |
| */ |
| } |
| |
| if (int_status & MLXBF_GIGE_INT_STATUS_TX_SMALL_FRAME_SIZE) { |
| priv->stats.tx_small_frames++; |
| /* This condition happens when the networking stack invokes |
| * this driver's "start_xmit()" method with a packet whose |
| * size < 60 bytes. The GigE silicon will automatically pad |
| * this small frame up to a minimum-sized frame before it is |
| * sent. The "tx_small_frame" condition is latched into the |
| * MLXBF_GIGE_INT_STATUS register when the GigE silicon |
| * operates on the offending TX WQE. The write to |
| * MLXBF_GIGE_INT_STATUS at the bottom of this routine |
| * clears this condition. |
| */ |
| } |
| |
| if (int_status & MLXBF_GIGE_INT_STATUS_TX_PI_CI_EXCEED_WQ_SIZE) |
| priv->stats.tx_index_errors++; |
| |
| if (int_status & MLXBF_GIGE_INT_STATUS_SW_CONFIG_ERROR) |
| priv->stats.sw_config_errors++; |
| |
| if (int_status & MLXBF_GIGE_INT_STATUS_SW_ACCESS_ERROR) |
| priv->stats.sw_access_errors++; |
| |
| /* Clear all error interrupts by writing '1' back to |
| * all the asserted bits in INT_STATUS. Do not write |
| * '1' back to 'receive packet' bit, since that is |
| * managed separately. |
| */ |
| |
| int_status &= ~MLXBF_GIGE_INT_STATUS_RX_RECEIVE_PACKET; |
| |
| writeq(int_status, priv->base + MLXBF_GIGE_INT_STATUS); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t mlxbf_gige_rx_intr(int irq, void *dev_id) |
| { |
| struct mlxbf_gige *priv; |
| |
| priv = dev_id; |
| |
| /* NOTE: GigE silicon automatically disables "packet rx" interrupt by |
| * setting MLXBF_GIGE_INT_MASK bit0 upon triggering the interrupt |
| * to the ARM cores. Software needs to re-enable "packet rx" |
| * interrupts by clearing MLXBF_GIGE_INT_MASK bit0. |
| */ |
| |
| napi_schedule(&priv->napi); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t mlxbf_gige_llu_plu_intr(int irq, void *dev_id) |
| { |
| return IRQ_HANDLED; |
| } |
| |
| int mlxbf_gige_request_irqs(struct mlxbf_gige *priv) |
| { |
| int err; |
| |
| err = request_irq(priv->error_irq, mlxbf_gige_error_intr, 0, |
| "mlxbf_gige_error", priv); |
| if (err) { |
| dev_err(priv->dev, "Request error_irq failure\n"); |
| return err; |
| } |
| |
| err = request_irq(priv->rx_irq, mlxbf_gige_rx_intr, 0, |
| "mlxbf_gige_rx", priv); |
| if (err) { |
| dev_err(priv->dev, "Request rx_irq failure\n"); |
| goto free_error_irq; |
| } |
| |
| err = request_irq(priv->llu_plu_irq, mlxbf_gige_llu_plu_intr, 0, |
| "mlxbf_gige_llu_plu", priv); |
| if (err) { |
| dev_err(priv->dev, "Request llu_plu_irq failure\n"); |
| goto free_rx_irq; |
| } |
| |
| return 0; |
| |
| free_rx_irq: |
| free_irq(priv->rx_irq, priv); |
| |
| free_error_irq: |
| free_irq(priv->error_irq, priv); |
| |
| return err; |
| } |
| |
| void mlxbf_gige_free_irqs(struct mlxbf_gige *priv) |
| { |
| free_irq(priv->error_irq, priv); |
| free_irq(priv->rx_irq, priv); |
| free_irq(priv->llu_plu_irq, priv); |
| } |