1 // SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause
2 
3 /* Interrupt related logic for Mellanox Gigabit Ethernet driver
4  *
5  * Copyright (C) 2020-2021 NVIDIA CORPORATION & AFFILIATES
6  */
7 
8 #include <linux/interrupt.h>
9 
10 #include "mlxbf_gige.h"
11 #include "mlxbf_gige_regs.h"
12 
13 static irqreturn_t mlxbf_gige_error_intr(int irq, void *dev_id)
14 {
15 	struct mlxbf_gige *priv;
16 	u64 int_status;
17 
18 	priv = dev_id;
19 
20 	int_status = readq(priv->base + MLXBF_GIGE_INT_STATUS);
21 
22 	if (int_status & MLXBF_GIGE_INT_STATUS_HW_ACCESS_ERROR)
23 		priv->stats.hw_access_errors++;
24 
25 	if (int_status & MLXBF_GIGE_INT_STATUS_TX_CHECKSUM_INPUTS) {
26 		priv->stats.tx_invalid_checksums++;
27 		/* This error condition is latched into MLXBF_GIGE_INT_STATUS
28 		 * when the GigE silicon operates on the offending
29 		 * TX WQE. The write to MLXBF_GIGE_INT_STATUS at the bottom
30 		 * of this routine clears this error condition.
31 		 */
32 	}
33 
34 	if (int_status & MLXBF_GIGE_INT_STATUS_TX_SMALL_FRAME_SIZE) {
35 		priv->stats.tx_small_frames++;
36 		/* This condition happens when the networking stack invokes
37 		 * this driver's "start_xmit()" method with a packet whose
38 		 * size < 60 bytes.  The GigE silicon will automatically pad
39 		 * this small frame up to a minimum-sized frame before it is
40 		 * sent. The "tx_small_frame" condition is latched into the
41 		 * MLXBF_GIGE_INT_STATUS register when the GigE silicon
42 		 * operates on the offending TX WQE. The write to
43 		 * MLXBF_GIGE_INT_STATUS at the bottom of this routine
44 		 * clears this condition.
45 		 */
46 	}
47 
48 	if (int_status & MLXBF_GIGE_INT_STATUS_TX_PI_CI_EXCEED_WQ_SIZE)
49 		priv->stats.tx_index_errors++;
50 
51 	if (int_status & MLXBF_GIGE_INT_STATUS_SW_CONFIG_ERROR)
52 		priv->stats.sw_config_errors++;
53 
54 	if (int_status & MLXBF_GIGE_INT_STATUS_SW_ACCESS_ERROR)
55 		priv->stats.sw_access_errors++;
56 
57 	/* Clear all error interrupts by writing '1' back to
58 	 * all the asserted bits in INT_STATUS.  Do not write
59 	 * '1' back to 'receive packet' bit, since that is
60 	 * managed separately.
61 	 */
62 
63 	int_status &= ~MLXBF_GIGE_INT_STATUS_RX_RECEIVE_PACKET;
64 
65 	writeq(int_status, priv->base + MLXBF_GIGE_INT_STATUS);
66 
67 	return IRQ_HANDLED;
68 }
69 
70 static irqreturn_t mlxbf_gige_rx_intr(int irq, void *dev_id)
71 {
72 	struct mlxbf_gige *priv;
73 
74 	priv = dev_id;
75 
76 	/* NOTE: GigE silicon automatically disables "packet rx" interrupt by
77 	 *       setting MLXBF_GIGE_INT_MASK bit0 upon triggering the interrupt
78 	 *       to the ARM cores.  Software needs to re-enable "packet rx"
79 	 *       interrupts by clearing MLXBF_GIGE_INT_MASK bit0.
80 	 */
81 
82 	napi_schedule(&priv->napi);
83 
84 	return IRQ_HANDLED;
85 }
86 
87 static irqreturn_t mlxbf_gige_llu_plu_intr(int irq, void *dev_id)
88 {
89 	return IRQ_HANDLED;
90 }
91 
92 int mlxbf_gige_request_irqs(struct mlxbf_gige *priv)
93 {
94 	int err;
95 
96 	err = request_irq(priv->error_irq, mlxbf_gige_error_intr, 0,
97 			  "mlxbf_gige_error", priv);
98 	if (err) {
99 		dev_err(priv->dev, "Request error_irq failure\n");
100 		return err;
101 	}
102 
103 	err = request_irq(priv->rx_irq, mlxbf_gige_rx_intr, 0,
104 			  "mlxbf_gige_rx", priv);
105 	if (err) {
106 		dev_err(priv->dev, "Request rx_irq failure\n");
107 		goto free_error_irq;
108 	}
109 
110 	err = request_irq(priv->llu_plu_irq, mlxbf_gige_llu_plu_intr, 0,
111 			  "mlxbf_gige_llu_plu", priv);
112 	if (err) {
113 		dev_err(priv->dev, "Request llu_plu_irq failure\n");
114 		goto free_rx_irq;
115 	}
116 
117 	return 0;
118 
119 free_rx_irq:
120 	free_irq(priv->rx_irq, priv);
121 
122 free_error_irq:
123 	free_irq(priv->error_irq, priv);
124 
125 	return err;
126 }
127 
128 void mlxbf_gige_free_irqs(struct mlxbf_gige *priv)
129 {
130 	free_irq(priv->error_irq, priv);
131 	free_irq(priv->rx_irq, priv);
132 	free_irq(priv->llu_plu_irq, priv);
133 }
134