1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2024 Oxide Computer Company
14 */
15
16 #include "ena.h"
17
18 /*
19 * We currently limit the number of Tx/Rx queues to the number of
20 * available interrupts (minus one for the admin queue).
21 */
22 static uint_t
ena_io_intr(caddr_t arg1,caddr_t arg2)23 ena_io_intr(caddr_t arg1, caddr_t arg2)
24 {
25 ena_t *ena = (ena_t *)arg1;
26 uint16_t vector = (uintptr_t)(void *)arg2;
27 ASSERT3U(vector, >, 0);
28 ASSERT3U(vector, <, ena->ena_num_intrs);
29 ena_txq_t *txq = &ena->ena_txqs[vector - 1];
30 ena_rxq_t *rxq = &ena->ena_rxqs[vector - 1];
31 uint32_t intr_ctrl;
32
33 if ((ena->ena_state & ENA_STATE_STARTED) == 0)
34 return (DDI_INTR_CLAIMED);
35
36 ASSERT3P(txq, !=, NULL);
37 ASSERT3P(rxq, !=, NULL);
38 ena_tx_intr_work(txq);
39 ena_rx_intr_work(rxq);
40
41 /*
42 * The Rx/Tx queue share the same interrupt, only need to
43 * unmask interrupts for one of them.
44 */
45 intr_ctrl = ena_hw_abs_read32(ena, txq->et_cq_unmask_addr);
46 ENAHW_REG_INTR_UNMASK(intr_ctrl);
47 ena_hw_abs_write32(ena, txq->et_cq_unmask_addr, intr_ctrl);
48 return (DDI_INTR_CLAIMED);
49 }
50
51 static uint_t
ena_admin_intr(caddr_t arg1,caddr_t arg2)52 ena_admin_intr(caddr_t arg1, caddr_t arg2)
53 {
54 ena_t *ena = (ena_t *)arg1;
55
56 if ((ena->ena_state & ENA_STATE_STARTED) != 0)
57 ena_aenq_work(ena);
58 return (DDI_INTR_CLAIMED);
59 }
60
61 void
ena_intr_remove_handlers(ena_t * ena,bool resetting)62 ena_intr_remove_handlers(ena_t *ena, bool resetting)
63 {
64 VERIFY0(resetting);
65
66 for (int i = 0; i < ena->ena_num_intrs; i++) {
67 int ret = ddi_intr_remove_handler(ena->ena_intr_handles[i]);
68
69 /* Nothing we can really do except log. */
70 if (ret != DDI_SUCCESS) {
71 ena_err(ena, "failed to remove interrupt handler for "
72 "vector %d: %d", i, ret);
73 }
74 }
75 }
76
77 /*
78 * The ena driver uses separate interrupt handlers for the admin queue
79 * and I/O queues.
80 */
81 bool
ena_intr_add_handlers(ena_t * ena)82 ena_intr_add_handlers(ena_t *ena)
83 {
84 ASSERT3S(ena->ena_num_intrs, >=, 2);
85 if (ddi_intr_add_handler(ena->ena_intr_handles[0], ena_admin_intr, ena,
86 (void *)(uintptr_t)0) != DDI_SUCCESS) {
87 ena_err(ena, "failed to add admin interrupt handler");
88 return (false);
89 }
90
91 for (int i = 1; i < ena->ena_num_intrs; i++) {
92 caddr_t vector = (void *)(uintptr_t)(i);
93 int ret = ddi_intr_add_handler(ena->ena_intr_handles[i],
94 ena_io_intr, ena, vector);
95
96 if (ret != DDI_SUCCESS) {
97 ena_err(ena, "failed to add I/O interrupt handler "
98 "for vector %u", i);
99
100 /*
101 * If we fail to add any I/O handler, then all
102 * successfully added handlers are removed,
103 * including the admin handler. For example,
104 * when i=2 we remove handler 1 (the first I/O
105 * handler), and when i=1 we remove handler 0
106 * (the admin handler).
107 */
108 while (i >= 1) {
109 i--;
110 (void) ddi_intr_remove_handler(
111 ena->ena_intr_handles[i]);
112 }
113
114 return (false);
115 }
116 }
117
118 return (true);
119 }
120
121 bool
ena_intrs_disable(ena_t * ena)122 ena_intrs_disable(ena_t *ena)
123 {
124 int ret;
125
126 if (ena->ena_intr_caps & DDI_INTR_FLAG_BLOCK) {
127 if ((ret = ddi_intr_block_disable(ena->ena_intr_handles,
128 ena->ena_num_intrs)) != DDI_SUCCESS) {
129 ena_err(ena, "failed to block disable interrupts: %d",
130 ret);
131 return (false);
132 }
133 } else {
134 for (int i = 0; i < ena->ena_num_intrs; i++) {
135 ret = ddi_intr_disable(ena->ena_intr_handles[i]);
136 if (ret != DDI_SUCCESS) {
137 ena_err(ena, "failed to disable interrupt "
138 "%d: %d", i, ret);
139 return (false);
140 }
141 }
142 }
143
144 return (true);
145 }
146
147 bool
ena_intrs_enable(ena_t * ena)148 ena_intrs_enable(ena_t *ena)
149 {
150 int ret;
151
152 if (ena->ena_intr_caps & DDI_INTR_FLAG_BLOCK) {
153 if ((ret = ddi_intr_block_enable(ena->ena_intr_handles,
154 ena->ena_num_intrs)) != DDI_SUCCESS) {
155 ena_err(ena, "failed to block enable interrupts: %d",
156 ret);
157 return (false);
158 }
159 } else {
160 for (int i = 0; i < ena->ena_num_intrs; i++) {
161 if ((ret = ddi_intr_enable(ena->ena_intr_handles[i])) !=
162 DDI_SUCCESS) {
163 ena_err(ena, "failed to enable interrupt "
164 "%d: %d", i, ret);
165
166 /*
167 * If we fail to enable any interrupt,
168 * then all interrupts are disabled.
169 */
170 while (i >= 1) {
171 i--;
172 (void) ddi_intr_disable(
173 ena->ena_intr_handles[i]);
174 }
175
176 return (false);
177 }
178 }
179 }
180
181 return (true);
182 }
183