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 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 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 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 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 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 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