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