xref: /illumos-gate/usr/src/uts/common/io/i40e/i40e_intr.c (revision 338749cb)
19d26e4fcSRobert Mustacchi /*
29d26e4fcSRobert Mustacchi  * This file and its contents are supplied under the terms of the
39d26e4fcSRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
49d26e4fcSRobert Mustacchi  * You may only use this file in accordance with the terms of version
59d26e4fcSRobert Mustacchi  * 1.0 of the CDDL.
69d26e4fcSRobert Mustacchi  *
79d26e4fcSRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
89d26e4fcSRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
99d26e4fcSRobert Mustacchi  * http://www.illumos.org/license/CDDL.
109d26e4fcSRobert Mustacchi  */
119d26e4fcSRobert Mustacchi 
129d26e4fcSRobert Mustacchi /*
13*338749cbSRobert Mustacchi  * Copyright (c) 2017, Joyent, Inc.
14396505afSPaul Winder  * Copyright 2017 Tegile Systems, Inc.  All rights reserved.
159d26e4fcSRobert Mustacchi  */
169d26e4fcSRobert Mustacchi 
179d26e4fcSRobert Mustacchi /*
189d26e4fcSRobert Mustacchi  * -------------------------
199d26e4fcSRobert Mustacchi  * Interrupt Handling Theory
209d26e4fcSRobert Mustacchi  * -------------------------
219d26e4fcSRobert Mustacchi  *
229d26e4fcSRobert Mustacchi  * There are a couple different sets of interrupts that we need to worry about:
239d26e4fcSRobert Mustacchi  *
249d26e4fcSRobert Mustacchi  *   - Interrupts from receive queues
259d26e4fcSRobert Mustacchi  *   - Interrupts from transmit queues
269d26e4fcSRobert Mustacchi  *   - 'Other Interrupts', such as the administrative queue
279d26e4fcSRobert Mustacchi  *
289d26e4fcSRobert Mustacchi  * 'Other Interrupts' are asynchronous events such as a link status change event
299d26e4fcSRobert Mustacchi  * being posted to the administrative queue, unrecoverable ECC errors, and more.
309d26e4fcSRobert Mustacchi  * If we have something being posted to the administrative queue, then we go
319d26e4fcSRobert Mustacchi  * through and process it, because it's generally enabled as a separate logical
329d26e4fcSRobert Mustacchi  * interrupt. Note, we may need to do more here eventually. To re-enable the
339d26e4fcSRobert Mustacchi  * interrupts from the 'Other Interrupts' section, we need to clear the PBA and
349d26e4fcSRobert Mustacchi  * write ENA to PFINT_ICR0.
359d26e4fcSRobert Mustacchi  *
369d26e4fcSRobert Mustacchi  * Interrupts from the transmit and receive queues indicates that our requests
379d26e4fcSRobert Mustacchi  * have been processed. In the rx case, it means that we have data that we
389d26e4fcSRobert Mustacchi  * should take a look at and send up the stack. In the tx case, it means that
399d26e4fcSRobert Mustacchi  * data which we got from MAC has now been sent out on the wire and we can free
409d26e4fcSRobert Mustacchi  * the associated data. Most of the logic for acting upon the presence of this
419d26e4fcSRobert Mustacchi  * data can be found in i40e_transciever.c which handles all of the DMA, rx, and
429d26e4fcSRobert Mustacchi  * tx operations. This file is dedicated to handling and dealing with interrupt
439d26e4fcSRobert Mustacchi  * processing.
449d26e4fcSRobert Mustacchi  *
459d26e4fcSRobert Mustacchi  * All devices supported by this driver support three kinds of interrupts:
469d26e4fcSRobert Mustacchi  *
479d26e4fcSRobert Mustacchi  *   o Extended Message Signaled Interrupts (MSI-X)
489d26e4fcSRobert Mustacchi  *   o Message Signaled Interrupts (MSI)
499d26e4fcSRobert Mustacchi  *   o Legacy PCI interrupts (INTx)
509d26e4fcSRobert Mustacchi  *
519d26e4fcSRobert Mustacchi  * Generally speaking the hardware logically handles MSI and INTx the same and
529d26e4fcSRobert Mustacchi  * restricts us to only using a single interrupt, which isn't the interesting
539d26e4fcSRobert Mustacchi  * case. With MSI-X available, each physical function of the device provides the
549d26e4fcSRobert Mustacchi  * opportunity for multiple interrupts which is what we'll focus on.
559d26e4fcSRobert Mustacchi  *
569d26e4fcSRobert Mustacchi  * --------------------
579d26e4fcSRobert Mustacchi  * Interrupt Management
589d26e4fcSRobert Mustacchi  * --------------------
599d26e4fcSRobert Mustacchi  *
609d26e4fcSRobert Mustacchi  * By default, the admin queue, which consists of the asynchronous other
619d26e4fcSRobert Mustacchi  * interrupts is always bound to MSI-X vector zero. Next, we spread out all of
629d26e4fcSRobert Mustacchi  * the other interrupts that we have available to us over the remaining
639d26e4fcSRobert Mustacchi  * interrupt vectors.
649d26e4fcSRobert Mustacchi  *
659d26e4fcSRobert Mustacchi  * This means that there may be multiple queues, both tx and rx, which are
669d26e4fcSRobert Mustacchi  * mapped to the same interrupt. When the interrupt fires, we'll have to check
679d26e4fcSRobert Mustacchi  * all of them for servicing, before we go through and indicate that the
689d26e4fcSRobert Mustacchi  * interrupt is claimed.
699d26e4fcSRobert Mustacchi  *
709d26e4fcSRobert Mustacchi  * The hardware provides the means of mapping various queues to MSI-X interrupts
719d26e4fcSRobert Mustacchi  * by programming the I40E_QINT_RQCTL() and I4OE_QINT_TQCTL() registers. These
729d26e4fcSRobert Mustacchi  * registers can also be used to enable and disable whether or not the queue is
739d26e4fcSRobert Mustacchi  * a source of interrupts. As part of this, the hardware requires that we
749d26e4fcSRobert Mustacchi  * maintain a linked list of queues for each interrupt vector. While it may seem
759d26e4fcSRobert Mustacchi  * like this is only there for the purproses of ITRs, that's not the case. The
769d26e4fcSRobert Mustacchi  * first queue must be programmed in I40E_QINT_LNKLSTN(%vector) register. Each
779d26e4fcSRobert Mustacchi  * queue defines the next one in either the I40E_QINT_RQCTL or I40E_QINT_TQCTL
789d26e4fcSRobert Mustacchi  * register.
799d26e4fcSRobert Mustacchi  *
809d26e4fcSRobert Mustacchi  * Finally, the individual interrupt vector itself has the ability to be enabled
819d26e4fcSRobert Mustacchi  * and disabled. The overall interrupt is controlled through the
829d26e4fcSRobert Mustacchi  * I40E_PFINT_DYN_CTLN() register. This is used to turn on and off the interrupt
839d26e4fcSRobert Mustacchi  * as a whole.
849d26e4fcSRobert Mustacchi  *
859d26e4fcSRobert Mustacchi  * Note that this means that both the individual queue and the interrupt as a
869d26e4fcSRobert Mustacchi  * whole can be toggled and re-enabled.
879d26e4fcSRobert Mustacchi  *
889d26e4fcSRobert Mustacchi  * -------------------
899d26e4fcSRobert Mustacchi  * Non-MSIX Management
909d26e4fcSRobert Mustacchi  * -------------------
919d26e4fcSRobert Mustacchi  *
929d26e4fcSRobert Mustacchi  * We may have a case where the Operating System is unable to actually allocate
939d26e4fcSRobert Mustacchi  * any MSI-X to the system. In such a world, there is only one transmit/receive
949d26e4fcSRobert Mustacchi  * queue pair and it is bound to the same interrupt with index zero. The
959d26e4fcSRobert Mustacchi  * hardware doesn't allow us access to additional interrupt vectors in these
969d26e4fcSRobert Mustacchi  * modes. Note that technically we could support more transmit/receive queues if
979d26e4fcSRobert Mustacchi  * we wanted.
989d26e4fcSRobert Mustacchi  *
999d26e4fcSRobert Mustacchi  * In this world, because the interrupts for the admin queue and traffic are
1009d26e4fcSRobert Mustacchi  * mixed together, we have to consult ICR0 to determine what has occurred. The
1019d26e4fcSRobert Mustacchi  * QINT_TQCTL and QINT_RQCTL registers have a field, 'MSI-X 0 index' which
1029d26e4fcSRobert Mustacchi  * allows us to set a specific bit in ICR0. There are up to seven such bits;
1039d26e4fcSRobert Mustacchi  * however, we only use the bit 0 and 1 for the rx and tx queue respectively.
1049d26e4fcSRobert Mustacchi  * These are contained by the I40E_INTR_NOTX_{R|T}X_QUEUE and
1059d26e4fcSRobert Mustacchi  * I40E_INTR_NOTX_{R|T}X_MASK registers respectively.
1069d26e4fcSRobert Mustacchi  *
1079d26e4fcSRobert Mustacchi  * Unfortunately, these corresponding queue bits have no corresponding entry in
1089d26e4fcSRobert Mustacchi  * the ICR0_ENA register. So instead, when enabling interrupts on the queues, we
1099d26e4fcSRobert Mustacchi  * end up enabling it on the queue registers rather than on the MSI-X registers.
1109d26e4fcSRobert Mustacchi  * In the MSI-X world, because they can be enabled and disabled, this is
1119d26e4fcSRobert Mustacchi  * different and the queues can always be enabled and disabled, but the
1129d26e4fcSRobert Mustacchi  * interrupts themselves are toggled (ignoring the question of interrupt
1139d26e4fcSRobert Mustacchi  * blanking for polling on rings).
1149d26e4fcSRobert Mustacchi  *
1159d26e4fcSRobert Mustacchi  * Finally, we still have to set up the interrupt linked list, but the list is
1169d26e4fcSRobert Mustacchi  * instead rooted at the register I40E_PFINT_LNKLST0, rather than being tied to
1179d26e4fcSRobert Mustacchi  * one of the other MSI-X registers.
1189d26e4fcSRobert Mustacchi  *
1199d26e4fcSRobert Mustacchi  * --------------------
1209d26e4fcSRobert Mustacchi  * Interrupt Moderation
1219d26e4fcSRobert Mustacchi  * --------------------
1229d26e4fcSRobert Mustacchi  *
1239d26e4fcSRobert Mustacchi  * The XL710 hardware has three different interrupt moderation registers per
1249d26e4fcSRobert Mustacchi  * interrupt. Unsurprisingly, we use these for:
1259d26e4fcSRobert Mustacchi  *
1269d26e4fcSRobert Mustacchi  *   o RX interrupts
1279d26e4fcSRobert Mustacchi  *   o TX interrupts
1289d26e4fcSRobert Mustacchi  *   o 'Other interrupts' (link status change, admin queue, etc.)
1299d26e4fcSRobert Mustacchi  *
1309d26e4fcSRobert Mustacchi  * By default, we throttle 'other interrupts' the most, then TX interrupts, and
1319d26e4fcSRobert Mustacchi  * then RX interrupts. The default values for these were based on trying to
1329d26e4fcSRobert Mustacchi  * reason about both the importance and frequency of events. Generally speaking
1339d26e4fcSRobert Mustacchi  * 'other interrupts' are not very frequent and they're not important for the
1349d26e4fcSRobert Mustacchi  * I/O data path in and of itself (though they may indicate issues with the I/O
1359d26e4fcSRobert Mustacchi  * data path).
1369d26e4fcSRobert Mustacchi  *
1379d26e4fcSRobert Mustacchi  * On the flip side, when we're not polling, RX interrupts are very important.
1389d26e4fcSRobert Mustacchi  * The longer we wait for them, the more latency that we inject into the system.
1399d26e4fcSRobert Mustacchi  * However, if we allow interrupts to occur too frequently, we risk a few
1409d26e4fcSRobert Mustacchi  * problems:
1419d26e4fcSRobert Mustacchi  *
1429d26e4fcSRobert Mustacchi  *  1) Abusing system resources. Without proper interrupt blanking and polling,
1439d26e4fcSRobert Mustacchi  *     we can see upwards of 200k-300k interrupts per second on the system.
1449d26e4fcSRobert Mustacchi  *
1459d26e4fcSRobert Mustacchi  *  2) Not enough data coalescing to enable polling. In other words, the more
1469d26e4fcSRobert Mustacchi  *     data that we allow to build up, the more likely we'll be able to enable
1479d26e4fcSRobert Mustacchi  *     polling mode and allowing us to better handle bulk data.
1489d26e4fcSRobert Mustacchi  *
1499d26e4fcSRobert Mustacchi  * In-between the 'other interrupts' and the TX interrupts we have the
1509d26e4fcSRobert Mustacchi  * reclamation of TX buffers. This operation is not quite as important as we
1519d26e4fcSRobert Mustacchi  * generally size the ring large enough that we should be able to reclaim a
1529d26e4fcSRobert Mustacchi  * substantial amount of the descriptors that we have used per interrupt. So
1539d26e4fcSRobert Mustacchi  * while it's important that this interrupt occur, we don't necessarily need it
1549d26e4fcSRobert Mustacchi  * firing as frequently as RX; it doesn't, on its own, induce additional latency
1559d26e4fcSRobert Mustacchi  * into the system.
1569d26e4fcSRobert Mustacchi  *
1579d26e4fcSRobert Mustacchi  * Based on all this we currently assign static ITR values for the system. While
1589d26e4fcSRobert Mustacchi  * we could move to a dynamic system (the hardware supports that), we'd want to
1599d26e4fcSRobert Mustacchi  * make sure that we're seeing problems from this that we believe would be
1609d26e4fcSRobert Mustacchi  * generally helped by the added complexity.
1619d26e4fcSRobert Mustacchi  *
1629d26e4fcSRobert Mustacchi  * Based on this, the default values that we have allow for the following
1639d26e4fcSRobert Mustacchi  * interrupt thresholds:
1649d26e4fcSRobert Mustacchi  *
1659d26e4fcSRobert Mustacchi  *    o 20k interrupts/s for RX
1669d26e4fcSRobert Mustacchi  *    o 5k interrupts/s for TX
1679d26e4fcSRobert Mustacchi  *    o 2k interupts/s for 'Other Interrupts'
1689d26e4fcSRobert Mustacchi  */
1699d26e4fcSRobert Mustacchi 
1709d26e4fcSRobert Mustacchi #include "i40e_sw.h"
1719d26e4fcSRobert Mustacchi 
1729d26e4fcSRobert Mustacchi #define	I40E_INTR_NOTX_QUEUE	0
1739d26e4fcSRobert Mustacchi #define	I40E_INTR_NOTX_INTR	0
1749d26e4fcSRobert Mustacchi #define	I40E_INTR_NOTX_RX_QUEUE	0
1759d26e4fcSRobert Mustacchi #define	I40E_INTR_NOTX_RX_MASK	(1 << I40E_PFINT_ICR0_QUEUE_0_SHIFT)
1769d26e4fcSRobert Mustacchi #define	I40E_INTR_NOTX_TX_QUEUE	1
1779d26e4fcSRobert Mustacchi #define	I40E_INTR_NOTX_TX_MASK	(1 << I40E_PFINT_ICR0_QUEUE_1_SHIFT)
1789d26e4fcSRobert Mustacchi 
1799d26e4fcSRobert Mustacchi void
1809d26e4fcSRobert Mustacchi i40e_intr_set_itr(i40e_t *i40e, i40e_itr_index_t itr, uint_t val)
1819d26e4fcSRobert Mustacchi {
1829d26e4fcSRobert Mustacchi 	int i;
1839d26e4fcSRobert Mustacchi 	i40e_hw_t *hw = &i40e->i40e_hw_space;
1849d26e4fcSRobert Mustacchi 
1859d26e4fcSRobert Mustacchi 	VERIFY3U(val, <=, I40E_MAX_ITR);
1869d26e4fcSRobert Mustacchi 	VERIFY3U(itr, <, I40E_ITR_INDEX_NONE);
1879d26e4fcSRobert Mustacchi 
1889d26e4fcSRobert Mustacchi 	/*
1899d26e4fcSRobert Mustacchi 	 * No matter the interrupt mode, the ITR for other interrupts is always
1909d26e4fcSRobert Mustacchi 	 * on interrupt zero and the same is true if we're not using MSI-X.
1919d26e4fcSRobert Mustacchi 	 */
1929d26e4fcSRobert Mustacchi 	if (itr == I40E_ITR_INDEX_OTHER ||
1939d26e4fcSRobert Mustacchi 	    i40e->i40e_intr_type != DDI_INTR_TYPE_MSIX) {
1949d26e4fcSRobert Mustacchi 		I40E_WRITE_REG(hw, I40E_PFINT_ITR0(itr), val);
1959d26e4fcSRobert Mustacchi 		return;
1969d26e4fcSRobert Mustacchi 	}
1979d26e4fcSRobert Mustacchi 
198396505afSPaul Winder 	for (i = 0; i < i40e->i40e_num_trqpairs; i++) {
199396505afSPaul Winder 		I40E_WRITE_REG(hw, I40E_PFINT_ITRN(itr, i), val);
2009d26e4fcSRobert Mustacchi 	}
2019d26e4fcSRobert Mustacchi }
2029d26e4fcSRobert Mustacchi 
2039d26e4fcSRobert Mustacchi /*
2049d26e4fcSRobert Mustacchi  * Re-enable the adminq. Note that the adminq doesn't have a traditional queue
2059d26e4fcSRobert Mustacchi  * associated with it from an interrupt perspective and just lives on ICR0.
2069d26e4fcSRobert Mustacchi  * However when MSI-X interrupts are not being used, then this also enables and
2079d26e4fcSRobert Mustacchi  * disables those interrupts.
2089d26e4fcSRobert Mustacchi  */
2099d26e4fcSRobert Mustacchi static void
2109d26e4fcSRobert Mustacchi i40e_intr_adminq_enable(i40e_t *i40e)
2119d26e4fcSRobert Mustacchi {
2129d26e4fcSRobert Mustacchi 	i40e_hw_t *hw = &i40e->i40e_hw_space;
2139d26e4fcSRobert Mustacchi 	uint32_t reg;
2149d26e4fcSRobert Mustacchi 
2159d26e4fcSRobert Mustacchi 	reg = I40E_PFINT_DYN_CTL0_INTENA_MASK |
2169d26e4fcSRobert Mustacchi 	    I40E_PFINT_DYN_CTL0_CLEARPBA_MASK |
2179d26e4fcSRobert Mustacchi 	    (I40E_ITR_INDEX_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT);
2189d26e4fcSRobert Mustacchi 	I40E_WRITE_REG(hw, I40E_PFINT_DYN_CTL0, reg);
2199d26e4fcSRobert Mustacchi 	i40e_flush(hw);
2209d26e4fcSRobert Mustacchi }
2219d26e4fcSRobert Mustacchi 
2229d26e4fcSRobert Mustacchi static void
2239d26e4fcSRobert Mustacchi i40e_intr_adminq_disable(i40e_t *i40e)
2249d26e4fcSRobert Mustacchi {
2259d26e4fcSRobert Mustacchi 	i40e_hw_t *hw = &i40e->i40e_hw_space;
2269d26e4fcSRobert Mustacchi 	uint32_t reg;
2279d26e4fcSRobert Mustacchi 
2289d26e4fcSRobert Mustacchi 	reg = I40E_ITR_INDEX_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT;
2299d26e4fcSRobert Mustacchi 	I40E_WRITE_REG(hw, I40E_PFINT_DYN_CTL0, reg);
2309d26e4fcSRobert Mustacchi }
2319d26e4fcSRobert Mustacchi 
2329d26e4fcSRobert Mustacchi static void
2339d26e4fcSRobert Mustacchi i40e_intr_io_enable(i40e_t *i40e, int vector)
2349d26e4fcSRobert Mustacchi {
2359d26e4fcSRobert Mustacchi 	uint32_t reg;
2369d26e4fcSRobert Mustacchi 	i40e_hw_t *hw = &i40e->i40e_hw_space;
2379d26e4fcSRobert Mustacchi 
2389d26e4fcSRobert Mustacchi 	reg = I40E_PFINT_DYN_CTLN_INTENA_MASK |
2399d26e4fcSRobert Mustacchi 	    I40E_PFINT_DYN_CTLN_CLEARPBA_MASK |
2409d26e4fcSRobert Mustacchi 	    (I40E_ITR_INDEX_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT);
2419d26e4fcSRobert Mustacchi 	I40E_WRITE_REG(hw, I40E_PFINT_DYN_CTLN(vector - 1), reg);
2429d26e4fcSRobert Mustacchi }
2439d26e4fcSRobert Mustacchi 
2449d26e4fcSRobert Mustacchi static void
2459d26e4fcSRobert Mustacchi i40e_intr_io_disable(i40e_t *i40e, int vector)
2469d26e4fcSRobert Mustacchi {
2479d26e4fcSRobert Mustacchi 	uint32_t reg;
2489d26e4fcSRobert Mustacchi 	i40e_hw_t *hw = &i40e->i40e_hw_space;
2499d26e4fcSRobert Mustacchi 
2509d26e4fcSRobert Mustacchi 	reg = I40E_ITR_INDEX_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT;
2519d26e4fcSRobert Mustacchi 	I40E_WRITE_REG(hw, I40E_PFINT_DYN_CTLN(vector - 1), reg);
2529d26e4fcSRobert Mustacchi }
2539d26e4fcSRobert Mustacchi 
2549d26e4fcSRobert Mustacchi /*
2559d26e4fcSRobert Mustacchi  * When MSI-X interrupts are being used, then we can enable the actual
2569d26e4fcSRobert Mustacchi  * interrupts themselves. However, when they are not, we instead have to turn
2579d26e4fcSRobert Mustacchi  * towards the queue's CAUSE_ENA bit and enable that.
2589d26e4fcSRobert Mustacchi  */
2599d26e4fcSRobert Mustacchi void
2609d26e4fcSRobert Mustacchi i40e_intr_io_enable_all(i40e_t *i40e)
2619d26e4fcSRobert Mustacchi {
2629d26e4fcSRobert Mustacchi 	if (i40e->i40e_intr_type == DDI_INTR_TYPE_MSIX) {
2639d26e4fcSRobert Mustacchi 		int i;
2649d26e4fcSRobert Mustacchi 
2659d26e4fcSRobert Mustacchi 		for (i = 1; i < i40e->i40e_intr_count; i++) {
2669d26e4fcSRobert Mustacchi 			i40e_intr_io_enable(i40e, i);
2679d26e4fcSRobert Mustacchi 		}
2689d26e4fcSRobert Mustacchi 	} else {
2699d26e4fcSRobert Mustacchi 		uint32_t reg;
2709d26e4fcSRobert Mustacchi 		i40e_hw_t *hw = &i40e->i40e_hw_space;
2719d26e4fcSRobert Mustacchi 
2729d26e4fcSRobert Mustacchi 		reg = I40E_READ_REG(hw, I40E_QINT_RQCTL(I40E_INTR_NOTX_QUEUE));
2739d26e4fcSRobert Mustacchi 		reg |= I40E_QINT_RQCTL_CAUSE_ENA_MASK;
2749d26e4fcSRobert Mustacchi 		I40E_WRITE_REG(hw, I40E_QINT_RQCTL(I40E_INTR_NOTX_QUEUE), reg);
2759d26e4fcSRobert Mustacchi 
2769d26e4fcSRobert Mustacchi 		reg = I40E_READ_REG(hw, I40E_QINT_TQCTL(I40E_INTR_NOTX_QUEUE));
2779d26e4fcSRobert Mustacchi 		reg |= I40E_QINT_TQCTL_CAUSE_ENA_MASK;
2789d26e4fcSRobert Mustacchi 		I40E_WRITE_REG(hw, I40E_QINT_TQCTL(I40E_INTR_NOTX_QUEUE), reg);
2799d26e4fcSRobert Mustacchi 	}
2809d26e4fcSRobert Mustacchi }
2819d26e4fcSRobert Mustacchi 
2829d26e4fcSRobert Mustacchi /*
2839d26e4fcSRobert Mustacchi  * When MSI-X interrupts are being used, then we can disable the actual
2849d26e4fcSRobert Mustacchi  * interrupts themselves. However, when they are not, we instead have to turn
2859d26e4fcSRobert Mustacchi  * towards the queue's CAUSE_ENA bit and disable that.
2869d26e4fcSRobert Mustacchi  */
2879d26e4fcSRobert Mustacchi void
2889d26e4fcSRobert Mustacchi i40e_intr_io_disable_all(i40e_t *i40e)
2899d26e4fcSRobert Mustacchi {
2909d26e4fcSRobert Mustacchi 	if (i40e->i40e_intr_type == DDI_INTR_TYPE_MSIX) {
2919d26e4fcSRobert Mustacchi 		int i;
2929d26e4fcSRobert Mustacchi 
2939d26e4fcSRobert Mustacchi 		for (i = 1; i < i40e->i40e_intr_count; i++) {
2949d26e4fcSRobert Mustacchi 			i40e_intr_io_disable(i40e, i);
2959d26e4fcSRobert Mustacchi 		}
2969d26e4fcSRobert Mustacchi 	} else {
2979d26e4fcSRobert Mustacchi 		uint32_t reg;
2989d26e4fcSRobert Mustacchi 		i40e_hw_t *hw = &i40e->i40e_hw_space;
2999d26e4fcSRobert Mustacchi 
3009d26e4fcSRobert Mustacchi 		reg = I40E_READ_REG(hw, I40E_QINT_RQCTL(I40E_INTR_NOTX_QUEUE));
3019d26e4fcSRobert Mustacchi 		reg &= ~I40E_QINT_RQCTL_CAUSE_ENA_MASK;
3029d26e4fcSRobert Mustacchi 		I40E_WRITE_REG(hw, I40E_QINT_RQCTL(I40E_INTR_NOTX_QUEUE), reg);
3039d26e4fcSRobert Mustacchi 
3049d26e4fcSRobert Mustacchi 		reg = I40E_READ_REG(hw, I40E_QINT_TQCTL(I40E_INTR_NOTX_QUEUE));
3059d26e4fcSRobert Mustacchi 		reg &= ~I40E_QINT_TQCTL_CAUSE_ENA_MASK;
3069d26e4fcSRobert Mustacchi 		I40E_WRITE_REG(hw, I40E_QINT_TQCTL(I40E_INTR_NOTX_QUEUE), reg);
3079d26e4fcSRobert Mustacchi 	}
3089d26e4fcSRobert Mustacchi }
3099d26e4fcSRobert Mustacchi 
3109d26e4fcSRobert Mustacchi /*
3119d26e4fcSRobert Mustacchi  * As part of disabling the tx and rx queue's we're technically supposed to
3129d26e4fcSRobert Mustacchi  * remove the linked list entries. The simplest way is to clear the LNKLSTN
3139d26e4fcSRobert Mustacchi  * register by setting it to I40E_QUEUE_TYPE_EOL (0x7FF).
3149d26e4fcSRobert Mustacchi  *
3159d26e4fcSRobert Mustacchi  * Note all of the FM register access checks are performed by the caller.
3169d26e4fcSRobert Mustacchi  */
3179d26e4fcSRobert Mustacchi void
3189d26e4fcSRobert Mustacchi i40e_intr_io_clear_cause(i40e_t *i40e)
3199d26e4fcSRobert Mustacchi {
3209d26e4fcSRobert Mustacchi 	int i;
3219d26e4fcSRobert Mustacchi 	i40e_hw_t *hw = &i40e->i40e_hw_space;
3229d26e4fcSRobert Mustacchi 
3239d26e4fcSRobert Mustacchi 	if (i40e->i40e_intr_type != DDI_INTR_TYPE_MSIX) {
3249d26e4fcSRobert Mustacchi 		uint32_t reg;
3259d26e4fcSRobert Mustacchi 		reg = I40E_QUEUE_TYPE_EOL;
3269d26e4fcSRobert Mustacchi 		I40E_WRITE_REG(hw, I40E_PFINT_LNKLST0, reg);
3279d26e4fcSRobert Mustacchi 		return;
3289d26e4fcSRobert Mustacchi 	}
3299d26e4fcSRobert Mustacchi 
330396505afSPaul Winder 	for (i = 0; i < i40e->i40e_num_trqpairs; i++) {
3319d26e4fcSRobert Mustacchi 		uint32_t reg;
3329d26e4fcSRobert Mustacchi #ifdef DEBUG
3339d26e4fcSRobert Mustacchi 		/*
3349d26e4fcSRobert Mustacchi 		 * Verify that the interrupt in question is disabled. This is a
3359d26e4fcSRobert Mustacchi 		 * prerequisite of modifying the data in question.
3369d26e4fcSRobert Mustacchi 		 */
337396505afSPaul Winder 		reg = I40E_READ_REG(hw, I40E_PFINT_DYN_CTLN(i));
3389d26e4fcSRobert Mustacchi 		VERIFY0(reg & I40E_PFINT_DYN_CTLN_INTENA_MASK);
3399d26e4fcSRobert Mustacchi #endif
3409d26e4fcSRobert Mustacchi 		reg = I40E_QUEUE_TYPE_EOL;
341396505afSPaul Winder 		I40E_WRITE_REG(hw, I40E_PFINT_LNKLSTN(i), reg);
3429d26e4fcSRobert Mustacchi 	}
3439d26e4fcSRobert Mustacchi 
3449d26e4fcSRobert Mustacchi 	i40e_flush(hw);
3459d26e4fcSRobert Mustacchi }
3469d26e4fcSRobert Mustacchi 
3479d26e4fcSRobert Mustacchi /*
3489d26e4fcSRobert Mustacchi  * Finalize interrupt handling. Mostly this disables the admin queue.
3499d26e4fcSRobert Mustacchi  */
3509d26e4fcSRobert Mustacchi void
3519d26e4fcSRobert Mustacchi i40e_intr_chip_fini(i40e_t *i40e)
3529d26e4fcSRobert Mustacchi {
3539d26e4fcSRobert Mustacchi #ifdef DEBUG
3549d26e4fcSRobert Mustacchi 	int i;
3559d26e4fcSRobert Mustacchi 	uint32_t reg;
3569d26e4fcSRobert Mustacchi 
3579d26e4fcSRobert Mustacchi 	i40e_hw_t *hw = &i40e->i40e_hw_space;
3589d26e4fcSRobert Mustacchi 
3599d26e4fcSRobert Mustacchi 	/*
3609d26e4fcSRobert Mustacchi 	 * Take a look and verify that all other interrupts have been disabled
3619d26e4fcSRobert Mustacchi 	 * and the interrupt linked lists have been zeroed.
3629d26e4fcSRobert Mustacchi 	 */
3639d26e4fcSRobert Mustacchi 	if (i40e->i40e_intr_type == DDI_INTR_TYPE_MSIX) {
364396505afSPaul Winder 		for (i = 0; i < i40e->i40e_num_trqpairs; i++) {
365396505afSPaul Winder 			reg = I40E_READ_REG(hw, I40E_PFINT_DYN_CTLN(i));
3669d26e4fcSRobert Mustacchi 			VERIFY0(reg & I40E_PFINT_DYN_CTLN_INTENA_MASK);
3679d26e4fcSRobert Mustacchi 
368396505afSPaul Winder 			reg = I40E_READ_REG(hw, I40E_PFINT_LNKLSTN(i));
3699d26e4fcSRobert Mustacchi 			VERIFY3U(reg, ==, I40E_QUEUE_TYPE_EOL);
3709d26e4fcSRobert Mustacchi 		}
3719d26e4fcSRobert Mustacchi 	}
3729d26e4fcSRobert Mustacchi #endif
3739d26e4fcSRobert Mustacchi 
3749d26e4fcSRobert Mustacchi 	i40e_intr_adminq_disable(i40e);
3759d26e4fcSRobert Mustacchi }
3769d26e4fcSRobert Mustacchi 
3779d26e4fcSRobert Mustacchi /*
3789d26e4fcSRobert Mustacchi  * Enable all of the queues and set the corresponding LNKLSTN registers. Note
3799d26e4fcSRobert Mustacchi  * that we always enable queues as interrupt sources, even though we don't
3809d26e4fcSRobert Mustacchi  * enable the MSI-X interrupt vectors.
3819d26e4fcSRobert Mustacchi  */
3829d26e4fcSRobert Mustacchi static void
3839d26e4fcSRobert Mustacchi i40e_intr_init_queue_msix(i40e_t *i40e)
3849d26e4fcSRobert Mustacchi {
3859d26e4fcSRobert Mustacchi 	i40e_hw_t *hw = &i40e->i40e_hw_space;
3869d26e4fcSRobert Mustacchi 	uint32_t reg;
387396505afSPaul Winder 	int i;
3889d26e4fcSRobert Mustacchi 
3899d26e4fcSRobert Mustacchi 	/*
390396505afSPaul Winder 	 * Map queues to MSI-X interrupts. Queue i is mapped to vector i + 1.
391396505afSPaul Winder 	 * Note that we skip the ITR logic for the moment, just to make our
392396505afSPaul Winder 	 * lives as explicit and simple as possible.
3939d26e4fcSRobert Mustacchi 	 */
394396505afSPaul Winder 	for (i = 0; i < i40e->i40e_num_trqpairs; i++) {
395396505afSPaul Winder 		i40e_trqpair_t *itrq = &i40e->i40e_trqpairs[i];
396396505afSPaul Winder 
397396505afSPaul Winder 		reg = (i << I40E_PFINT_LNKLSTN_FIRSTQ_INDX_SHIFT) |
398396505afSPaul Winder 		    (I40E_QUEUE_TYPE_RX <<
399396505afSPaul Winder 		    I40E_PFINT_LNKLSTN_FIRSTQ_TYPE_SHIFT);
400396505afSPaul Winder 		I40E_WRITE_REG(hw, I40E_PFINT_LNKLSTN(i), reg);
401396505afSPaul Winder 
402396505afSPaul Winder 		reg =
403396505afSPaul Winder 		    (itrq->itrq_rx_intrvec << I40E_QINT_RQCTL_MSIX_INDX_SHIFT) |
404396505afSPaul Winder 		    (I40E_ITR_INDEX_RX << I40E_QINT_RQCTL_ITR_INDX_SHIFT) |
405396505afSPaul Winder 		    (i << I40E_QINT_RQCTL_NEXTQ_INDX_SHIFT) |
406396505afSPaul Winder 		    (I40E_QUEUE_TYPE_TX << I40E_QINT_RQCTL_NEXTQ_TYPE_SHIFT) |
407396505afSPaul Winder 		    I40E_QINT_RQCTL_CAUSE_ENA_MASK;
408396505afSPaul Winder 
409396505afSPaul Winder 		I40E_WRITE_REG(hw, I40E_QINT_RQCTL(i), reg);
410396505afSPaul Winder 
411396505afSPaul Winder 		reg =
412396505afSPaul Winder 		    (itrq->itrq_tx_intrvec << I40E_QINT_TQCTL_MSIX_INDX_SHIFT) |
413396505afSPaul Winder 		    (I40E_ITR_INDEX_TX << I40E_QINT_RQCTL_ITR_INDX_SHIFT) |
414396505afSPaul Winder 		    (I40E_QUEUE_TYPE_EOL << I40E_QINT_TQCTL_NEXTQ_INDX_SHIFT) |
415396505afSPaul Winder 		    (I40E_QUEUE_TYPE_RX << I40E_QINT_TQCTL_NEXTQ_TYPE_SHIFT) |
416396505afSPaul Winder 		    I40E_QINT_TQCTL_CAUSE_ENA_MASK;
417396505afSPaul Winder 
418396505afSPaul Winder 		I40E_WRITE_REG(hw, I40E_QINT_TQCTL(i), reg);
419396505afSPaul Winder 	}
4209d26e4fcSRobert Mustacchi 
4219d26e4fcSRobert Mustacchi }
4229d26e4fcSRobert Mustacchi 
4239d26e4fcSRobert Mustacchi /*
4249d26e4fcSRobert Mustacchi  * Set up a single queue to share the admin queue interrupt in the non-MSI-X
4259d26e4fcSRobert Mustacchi  * world. Note we do not enable the queue as an interrupt cause at this time. We
4269d26e4fcSRobert Mustacchi  * don't have any other vector of control here, unlike with the MSI-X interrupt
4279d26e4fcSRobert Mustacchi  * case.
4289d26e4fcSRobert Mustacchi  */
4299d26e4fcSRobert Mustacchi static void
4309d26e4fcSRobert Mustacchi i40e_intr_init_queue_shared(i40e_t *i40e)
4319d26e4fcSRobert Mustacchi {
4329d26e4fcSRobert Mustacchi 	i40e_hw_t *hw = &i40e->i40e_hw_space;
4339d26e4fcSRobert Mustacchi 	uint32_t reg;
4349d26e4fcSRobert Mustacchi 
4359d26e4fcSRobert Mustacchi 	VERIFY(i40e->i40e_intr_type == DDI_INTR_TYPE_FIXED ||
4369d26e4fcSRobert Mustacchi 	    i40e->i40e_intr_type == DDI_INTR_TYPE_MSI);
4379d26e4fcSRobert Mustacchi 
4389d26e4fcSRobert Mustacchi 	reg = (I40E_INTR_NOTX_QUEUE << I40E_PFINT_LNKLST0_FIRSTQ_INDX_SHIFT) |
4399d26e4fcSRobert Mustacchi 	    (I40E_QUEUE_TYPE_RX << I40E_PFINT_LNKLSTN_FIRSTQ_TYPE_SHIFT);
4409d26e4fcSRobert Mustacchi 	I40E_WRITE_REG(hw, I40E_PFINT_LNKLST0, reg);
4419d26e4fcSRobert Mustacchi 
4429d26e4fcSRobert Mustacchi 	reg = (I40E_INTR_NOTX_INTR << I40E_QINT_RQCTL_MSIX_INDX_SHIFT) |
4439d26e4fcSRobert Mustacchi 	    (I40E_ITR_INDEX_RX << I40E_QINT_RQCTL_ITR_INDX_SHIFT) |
4449d26e4fcSRobert Mustacchi 	    (I40E_INTR_NOTX_RX_QUEUE << I40E_QINT_RQCTL_MSIX0_INDX_SHIFT) |
4459d26e4fcSRobert Mustacchi 	    (I40E_INTR_NOTX_QUEUE << I40E_QINT_RQCTL_NEXTQ_INDX_SHIFT) |
4469d26e4fcSRobert Mustacchi 	    (I40E_QUEUE_TYPE_TX << I40E_QINT_RQCTL_NEXTQ_TYPE_SHIFT);
4479d26e4fcSRobert Mustacchi 
4489d26e4fcSRobert Mustacchi 	I40E_WRITE_REG(hw, I40E_QINT_RQCTL(I40E_INTR_NOTX_QUEUE), reg);
4499d26e4fcSRobert Mustacchi 
4509d26e4fcSRobert Mustacchi 	reg = (I40E_INTR_NOTX_INTR << I40E_QINT_TQCTL_MSIX_INDX_SHIFT) |
4519d26e4fcSRobert Mustacchi 	    (I40E_ITR_INDEX_TX << I40E_QINT_TQCTL_ITR_INDX_SHIFT) |
4529d26e4fcSRobert Mustacchi 	    (I40E_INTR_NOTX_TX_QUEUE << I40E_QINT_TQCTL_MSIX0_INDX_SHIFT) |
4539d26e4fcSRobert Mustacchi 	    (I40E_QUEUE_TYPE_EOL << I40E_QINT_TQCTL_NEXTQ_INDX_SHIFT) |
4549d26e4fcSRobert Mustacchi 	    (I40E_QUEUE_TYPE_RX << I40E_QINT_TQCTL_NEXTQ_TYPE_SHIFT);
4559d26e4fcSRobert Mustacchi 
4569d26e4fcSRobert Mustacchi 	I40E_WRITE_REG(hw, I40E_QINT_TQCTL(I40E_INTR_NOTX_QUEUE), reg);
4579d26e4fcSRobert Mustacchi }
4589d26e4fcSRobert Mustacchi 
4599d26e4fcSRobert Mustacchi /*
4609d26e4fcSRobert Mustacchi  * Enable the specified queue as a valid source of interrupts. Note, this should
4619d26e4fcSRobert Mustacchi  * only be used as part of the GLDv3's interrupt blanking routines. The debug
4629d26e4fcSRobert Mustacchi  * build assertions are specific to that.
4639d26e4fcSRobert Mustacchi  */
4649d26e4fcSRobert Mustacchi void
465*338749cbSRobert Mustacchi i40e_intr_rx_queue_enable(i40e_trqpair_t *itrq)
4669d26e4fcSRobert Mustacchi {
4679d26e4fcSRobert Mustacchi 	uint32_t reg;
468*338749cbSRobert Mustacchi 	uint_t queue = itrq->itrq_index;
469*338749cbSRobert Mustacchi 	i40e_hw_t *hw = &itrq->itrq_i40e->i40e_hw_space;
4709d26e4fcSRobert Mustacchi 
471*338749cbSRobert Mustacchi 	ASSERT(MUTEX_HELD(&itrq->itrq_rx_lock));
472*338749cbSRobert Mustacchi 	ASSERT(queue < itrq->itrq_i40e->i40e_num_trqpairs);
4739d26e4fcSRobert Mustacchi 
4749d26e4fcSRobert Mustacchi 	reg = I40E_READ_REG(hw, I40E_QINT_RQCTL(queue));
4759d26e4fcSRobert Mustacchi 	ASSERT0(reg & I40E_QINT_RQCTL_CAUSE_ENA_MASK);
4769d26e4fcSRobert Mustacchi 	reg |= I40E_QINT_RQCTL_CAUSE_ENA_MASK;
4779d26e4fcSRobert Mustacchi 	I40E_WRITE_REG(hw, I40E_QINT_RQCTL(queue), reg);
4789d26e4fcSRobert Mustacchi }
4799d26e4fcSRobert Mustacchi 
4809d26e4fcSRobert Mustacchi /*
4819d26e4fcSRobert Mustacchi  * Disable the specified queue as a valid source of interrupts. Note, this
4829d26e4fcSRobert Mustacchi  * should only be used as part of the GLDv3's interrupt blanking routines. The
4839d26e4fcSRobert Mustacchi  * debug build assertions are specific to that.
4849d26e4fcSRobert Mustacchi  */
4859d26e4fcSRobert Mustacchi void
486*338749cbSRobert Mustacchi i40e_intr_rx_queue_disable(i40e_trqpair_t *itrq)
4879d26e4fcSRobert Mustacchi {
4889d26e4fcSRobert Mustacchi 	uint32_t reg;
489*338749cbSRobert Mustacchi 	uint_t queue = itrq->itrq_index;
490*338749cbSRobert Mustacchi 	i40e_hw_t *hw = &itrq->itrq_i40e->i40e_hw_space;
4919d26e4fcSRobert Mustacchi 
492*338749cbSRobert Mustacchi 	ASSERT(MUTEX_HELD(&itrq->itrq_rx_lock));
493*338749cbSRobert Mustacchi 	ASSERT(queue < itrq->itrq_i40e->i40e_num_trqpairs);
4949d26e4fcSRobert Mustacchi 
4959d26e4fcSRobert Mustacchi 	reg = I40E_READ_REG(hw, I40E_QINT_RQCTL(queue));
4969d26e4fcSRobert Mustacchi 	ASSERT3U(reg & I40E_QINT_RQCTL_CAUSE_ENA_MASK, ==,
4979d26e4fcSRobert Mustacchi 	    I40E_QINT_RQCTL_CAUSE_ENA_MASK);
4989d26e4fcSRobert Mustacchi 	reg &= ~I40E_QINT_RQCTL_CAUSE_ENA_MASK;
4999d26e4fcSRobert Mustacchi 	I40E_WRITE_REG(hw, I40E_QINT_RQCTL(queue), reg);
5009d26e4fcSRobert Mustacchi }
5019d26e4fcSRobert Mustacchi 
5029d26e4fcSRobert Mustacchi /*
5039d26e4fcSRobert Mustacchi  * Start up the various chip's interrupt handling. We not only configure the
5049d26e4fcSRobert Mustacchi  * adminq here, but we also go through and configure all of the actual queues,
5059d26e4fcSRobert Mustacchi  * the interrupt linked lists, and others.
5069d26e4fcSRobert Mustacchi  */
5079d26e4fcSRobert Mustacchi void
5089d26e4fcSRobert Mustacchi i40e_intr_chip_init(i40e_t *i40e)
5099d26e4fcSRobert Mustacchi {
5109d26e4fcSRobert Mustacchi 	i40e_hw_t *hw = &i40e->i40e_hw_space;
5119d26e4fcSRobert Mustacchi 	uint32_t reg;
5129d26e4fcSRobert Mustacchi 
5139d26e4fcSRobert Mustacchi 	/*
5149d26e4fcSRobert Mustacchi 	 * Ensure that all non adminq interrupts are disabled at the chip level.
5159d26e4fcSRobert Mustacchi 	 */
5169d26e4fcSRobert Mustacchi 	i40e_intr_io_disable_all(i40e);
5179d26e4fcSRobert Mustacchi 
5189d26e4fcSRobert Mustacchi 	I40E_WRITE_REG(hw, I40E_PFINT_ICR0_ENA, 0);
5199d26e4fcSRobert Mustacchi 	(void) I40E_READ_REG(hw, I40E_PFINT_ICR0);
5209d26e4fcSRobert Mustacchi 
5219d26e4fcSRobert Mustacchi 	/*
5229d26e4fcSRobert Mustacchi 	 * Always enable all of the other-class interrupts to be on their own
5239d26e4fcSRobert Mustacchi 	 * ITR. This only needs to be set on interrupt zero, which has its own
5249d26e4fcSRobert Mustacchi 	 * special setting.
5259d26e4fcSRobert Mustacchi 	 */
5269d26e4fcSRobert Mustacchi 	reg = I40E_ITR_INDEX_OTHER << I40E_PFINT_STAT_CTL0_OTHER_ITR_INDX_SHIFT;
5279d26e4fcSRobert Mustacchi 	I40E_WRITE_REG(hw, I40E_PFINT_STAT_CTL0, reg);
5289d26e4fcSRobert Mustacchi 
5299d26e4fcSRobert Mustacchi 	/*
5309d26e4fcSRobert Mustacchi 	 * Enable interrupt types we expect to receive. At the moment, this
5319d26e4fcSRobert Mustacchi 	 * is limited to the adminq; however, we'll want to review 11.2.2.9.22
5329d26e4fcSRobert Mustacchi 	 * for more types here as we add support for detecting them, handling
5339d26e4fcSRobert Mustacchi 	 * them, and resetting the device as appropriate.
5349d26e4fcSRobert Mustacchi 	 */
5359d26e4fcSRobert Mustacchi 	reg = I40E_PFINT_ICR0_ENA_ADMINQ_MASK;
5369d26e4fcSRobert Mustacchi 	I40E_WRITE_REG(hw, I40E_PFINT_ICR0_ENA, reg);
5379d26e4fcSRobert Mustacchi 
5389d26e4fcSRobert Mustacchi 	/*
5399d26e4fcSRobert Mustacchi 	 * Always set the interrupt linked list to empty. We'll come back and
5409d26e4fcSRobert Mustacchi 	 * change this if MSI-X are actually on the scene.
5419d26e4fcSRobert Mustacchi 	 */
5429d26e4fcSRobert Mustacchi 	I40E_WRITE_REG(hw, I40E_PFINT_LNKLST0, I40E_QUEUE_TYPE_EOL);
5439d26e4fcSRobert Mustacchi 
5449d26e4fcSRobert Mustacchi 	i40e_intr_adminq_enable(i40e);
5459d26e4fcSRobert Mustacchi 
5469d26e4fcSRobert Mustacchi 	/*
5479d26e4fcSRobert Mustacchi 	 * Set up all of the queues and map them to interrupts based on the bit
5489d26e4fcSRobert Mustacchi 	 * assignments.
5499d26e4fcSRobert Mustacchi 	 */
5509d26e4fcSRobert Mustacchi 	if (i40e->i40e_intr_type == DDI_INTR_TYPE_MSIX) {
5519d26e4fcSRobert Mustacchi 		i40e_intr_init_queue_msix(i40e);
5529d26e4fcSRobert Mustacchi 	} else {
5539d26e4fcSRobert Mustacchi 		i40e_intr_init_queue_shared(i40e);
5549d26e4fcSRobert Mustacchi 	}
5559d26e4fcSRobert Mustacchi 
5569d26e4fcSRobert Mustacchi 	/*
5579d26e4fcSRobert Mustacchi 	 * Finally set all of the default ITRs for the interrupts. Note that the
5589d26e4fcSRobert Mustacchi 	 * queues will have been set up above.
5599d26e4fcSRobert Mustacchi 	 */
5609d26e4fcSRobert Mustacchi 	i40e_intr_set_itr(i40e, I40E_ITR_INDEX_RX, i40e->i40e_rx_itr);
5619d26e4fcSRobert Mustacchi 	i40e_intr_set_itr(i40e, I40E_ITR_INDEX_TX, i40e->i40e_tx_itr);
5629d26e4fcSRobert Mustacchi 	i40e_intr_set_itr(i40e, I40E_ITR_INDEX_OTHER, i40e->i40e_other_itr);
5639d26e4fcSRobert Mustacchi }
5649d26e4fcSRobert Mustacchi 
5659d26e4fcSRobert Mustacchi static void
5669d26e4fcSRobert Mustacchi i40e_intr_adminq_work(i40e_t *i40e)
5679d26e4fcSRobert Mustacchi {
5689d26e4fcSRobert Mustacchi 	struct i40e_hw *hw = &i40e->i40e_hw_space;
5699d26e4fcSRobert Mustacchi 	struct i40e_arq_event_info evt;
5709d26e4fcSRobert Mustacchi 	uint16_t remain = 1;
5719d26e4fcSRobert Mustacchi 
5729d26e4fcSRobert Mustacchi 	bzero(&evt, sizeof (struct i40e_arq_event_info));
5739d26e4fcSRobert Mustacchi 	evt.buf_len = I40E_ADMINQ_BUFSZ;
5749d26e4fcSRobert Mustacchi 	evt.msg_buf = i40e->i40e_aqbuf;
5759d26e4fcSRobert Mustacchi 
5769d26e4fcSRobert Mustacchi 	while (remain != 0) {
5779d26e4fcSRobert Mustacchi 		enum i40e_status_code ret;
5789d26e4fcSRobert Mustacchi 		uint16_t opcode;
5799d26e4fcSRobert Mustacchi 
5809d26e4fcSRobert Mustacchi 		/*
5819d26e4fcSRobert Mustacchi 		 * At the moment, the only error code that seems to be returned
5829d26e4fcSRobert Mustacchi 		 * is one saying that there's no work. In such a case we leave
5839d26e4fcSRobert Mustacchi 		 * this be.
5849d26e4fcSRobert Mustacchi 		 */
5859d26e4fcSRobert Mustacchi 		ret = i40e_clean_arq_element(hw, &evt, &remain);
5869d26e4fcSRobert Mustacchi 		if (ret != I40E_SUCCESS)
5879d26e4fcSRobert Mustacchi 			break;
5889d26e4fcSRobert Mustacchi 
5899d26e4fcSRobert Mustacchi 		opcode = LE_16(evt.desc.opcode);
5909d26e4fcSRobert Mustacchi 		switch (opcode) {
5919d26e4fcSRobert Mustacchi 		case i40e_aqc_opc_get_link_status:
5929d26e4fcSRobert Mustacchi 			mutex_enter(&i40e->i40e_general_lock);
5939d26e4fcSRobert Mustacchi 			i40e_link_check(i40e);
5949d26e4fcSRobert Mustacchi 			mutex_exit(&i40e->i40e_general_lock);
5959d26e4fcSRobert Mustacchi 			break;
5969d26e4fcSRobert Mustacchi 		default:
5979d26e4fcSRobert Mustacchi 			/*
5989d26e4fcSRobert Mustacchi 			 * Longer term we'll want to enable other causes here
5999d26e4fcSRobert Mustacchi 			 * and get these cleaned up and doing something.
6009d26e4fcSRobert Mustacchi 			 */
6019d26e4fcSRobert Mustacchi 			break;
6029d26e4fcSRobert Mustacchi 		}
6039d26e4fcSRobert Mustacchi 	}
6049d26e4fcSRobert Mustacchi }
6059d26e4fcSRobert Mustacchi 
6069d26e4fcSRobert Mustacchi static void
6079d26e4fcSRobert Mustacchi i40e_intr_rx_work(i40e_t *i40e, int queue)
6089d26e4fcSRobert Mustacchi {
609396505afSPaul Winder 	mblk_t *mp = NULL;
6109d26e4fcSRobert Mustacchi 	i40e_trqpair_t *itrq;
6119d26e4fcSRobert Mustacchi 
6129d26e4fcSRobert Mustacchi 	ASSERT(queue < i40e->i40e_num_trqpairs);
6139d26e4fcSRobert Mustacchi 	itrq = &i40e->i40e_trqpairs[queue];
6149d26e4fcSRobert Mustacchi 
6159d26e4fcSRobert Mustacchi 	mutex_enter(&itrq->itrq_rx_lock);
616396505afSPaul Winder 	if (!itrq->itrq_intr_poll)
617396505afSPaul Winder 		mp = i40e_ring_rx(itrq, I40E_POLL_NULL);
6189d26e4fcSRobert Mustacchi 	mutex_exit(&itrq->itrq_rx_lock);
6199d26e4fcSRobert Mustacchi 
6209d26e4fcSRobert Mustacchi 	if (mp != NULL) {
6219d26e4fcSRobert Mustacchi 		mac_rx_ring(i40e->i40e_mac_hdl, itrq->itrq_macrxring, mp,
6229d26e4fcSRobert Mustacchi 		    itrq->itrq_rxgen);
6239d26e4fcSRobert Mustacchi 	}
6249d26e4fcSRobert Mustacchi }
6259d26e4fcSRobert Mustacchi 
6269d26e4fcSRobert Mustacchi static void
6279d26e4fcSRobert Mustacchi i40e_intr_tx_work(i40e_t *i40e, int queue)
6289d26e4fcSRobert Mustacchi {
6299d26e4fcSRobert Mustacchi 	i40e_trqpair_t *itrq;
6309d26e4fcSRobert Mustacchi 
6319d26e4fcSRobert Mustacchi 	itrq = &i40e->i40e_trqpairs[queue];
6329d26e4fcSRobert Mustacchi 	i40e_tx_recycle_ring(itrq);
6339d26e4fcSRobert Mustacchi }
6349d26e4fcSRobert Mustacchi 
6359d26e4fcSRobert Mustacchi /*
6369d26e4fcSRobert Mustacchi  * At the moment, the only 'other' interrupt on ICR0 that we handle is the
6379d26e4fcSRobert Mustacchi  * adminq. We should go through and support the other notifications at some
6389d26e4fcSRobert Mustacchi  * point.
6399d26e4fcSRobert Mustacchi  */
6409d26e4fcSRobert Mustacchi static void
6419d26e4fcSRobert Mustacchi i40e_intr_other_work(i40e_t *i40e)
6429d26e4fcSRobert Mustacchi {
6439d26e4fcSRobert Mustacchi 	struct i40e_hw *hw = &i40e->i40e_hw_space;
6449d26e4fcSRobert Mustacchi 	uint32_t reg;
6459d26e4fcSRobert Mustacchi 
6469d26e4fcSRobert Mustacchi 	reg = I40E_READ_REG(hw, I40E_PFINT_ICR0);
6479d26e4fcSRobert Mustacchi 	if (i40e_check_acc_handle(i40e->i40e_osdep_space.ios_reg_handle) !=
6489d26e4fcSRobert Mustacchi 	    DDI_FM_OK) {
6499d26e4fcSRobert Mustacchi 		ddi_fm_service_impact(i40e->i40e_dip, DDI_SERVICE_DEGRADED);
6509d26e4fcSRobert Mustacchi 		atomic_or_32(&i40e->i40e_state, I40E_ERROR);
6519d26e4fcSRobert Mustacchi 		return;
6529d26e4fcSRobert Mustacchi 	}
6539d26e4fcSRobert Mustacchi 
6549d26e4fcSRobert Mustacchi 	if (reg & I40E_PFINT_ICR0_ADMINQ_MASK)
6559d26e4fcSRobert Mustacchi 		i40e_intr_adminq_work(i40e);
6569d26e4fcSRobert Mustacchi 
6579d26e4fcSRobert Mustacchi 	/*
6589d26e4fcSRobert Mustacchi 	 * Make sure that the adminq interrupt is not masked and then explicitly
6599d26e4fcSRobert Mustacchi 	 * enable the adminq and thus the other interrupt.
6609d26e4fcSRobert Mustacchi 	 */
6619d26e4fcSRobert Mustacchi 	reg = I40E_READ_REG(hw, I40E_PFINT_ICR0_ENA);
6629d26e4fcSRobert Mustacchi 	reg |= I40E_PFINT_ICR0_ENA_ADMINQ_MASK;
6639d26e4fcSRobert Mustacchi 	I40E_WRITE_REG(hw, I40E_PFINT_ICR0_ENA, reg);
6649d26e4fcSRobert Mustacchi 
6659d26e4fcSRobert Mustacchi 	i40e_intr_adminq_enable(i40e);
6669d26e4fcSRobert Mustacchi }
6679d26e4fcSRobert Mustacchi 
6689d26e4fcSRobert Mustacchi uint_t
6699d26e4fcSRobert Mustacchi i40e_intr_msix(void *arg1, void *arg2)
6709d26e4fcSRobert Mustacchi {
6719d26e4fcSRobert Mustacchi 	i40e_t *i40e = (i40e_t *)arg1;
6729d26e4fcSRobert Mustacchi 	int vector_idx = (int)(uintptr_t)arg2;
6739d26e4fcSRobert Mustacchi 
6749d26e4fcSRobert Mustacchi 	/*
6759d26e4fcSRobert Mustacchi 	 * When using MSI-X interrupts, vector 0 is always reserved for the
6769d26e4fcSRobert Mustacchi 	 * adminq at this time. Though longer term, we'll want to also bridge
6779d26e4fcSRobert Mustacchi 	 * some I/O to them.
6789d26e4fcSRobert Mustacchi 	 */
6799d26e4fcSRobert Mustacchi 	if (vector_idx == 0) {
6809d26e4fcSRobert Mustacchi 		i40e_intr_other_work(i40e);
6819d26e4fcSRobert Mustacchi 		return (DDI_INTR_CLAIMED);
6829d26e4fcSRobert Mustacchi 	}
6839d26e4fcSRobert Mustacchi 
684396505afSPaul Winder 	i40e_intr_rx_work(i40e, vector_idx - 1);
685396505afSPaul Winder 	i40e_intr_tx_work(i40e, vector_idx - 1);
686396505afSPaul Winder 	i40e_intr_io_enable(i40e, vector_idx);
6879d26e4fcSRobert Mustacchi 
6889d26e4fcSRobert Mustacchi 	return (DDI_INTR_CLAIMED);
6899d26e4fcSRobert Mustacchi }
6909d26e4fcSRobert Mustacchi 
6919d26e4fcSRobert Mustacchi static uint_t
6929d26e4fcSRobert Mustacchi i40e_intr_notx(i40e_t *i40e, boolean_t shared)
6939d26e4fcSRobert Mustacchi {
6949d26e4fcSRobert Mustacchi 	i40e_hw_t *hw = &i40e->i40e_hw_space;
6959d26e4fcSRobert Mustacchi 	uint32_t reg;
6969d26e4fcSRobert Mustacchi 	int ret = DDI_INTR_CLAIMED;
6979d26e4fcSRobert Mustacchi 
6989d26e4fcSRobert Mustacchi 	if (shared == B_TRUE) {
6999d26e4fcSRobert Mustacchi 		mutex_enter(&i40e->i40e_general_lock);
7009d26e4fcSRobert Mustacchi 		if (i40e->i40e_state & I40E_SUSPENDED) {
7019d26e4fcSRobert Mustacchi 			mutex_exit(&i40e->i40e_general_lock);
7029d26e4fcSRobert Mustacchi 			return (DDI_INTR_UNCLAIMED);
7039d26e4fcSRobert Mustacchi 		}
7049d26e4fcSRobert Mustacchi 		mutex_exit(&i40e->i40e_general_lock);
7059d26e4fcSRobert Mustacchi 	}
7069d26e4fcSRobert Mustacchi 
7079d26e4fcSRobert Mustacchi 	reg = I40E_READ_REG(hw, I40E_PFINT_ICR0);
7089d26e4fcSRobert Mustacchi 	if (i40e_check_acc_handle(i40e->i40e_osdep_space.ios_reg_handle) !=
7099d26e4fcSRobert Mustacchi 	    DDI_FM_OK) {
7109d26e4fcSRobert Mustacchi 		ddi_fm_service_impact(i40e->i40e_dip, DDI_SERVICE_DEGRADED);
7119d26e4fcSRobert Mustacchi 		atomic_or_32(&i40e->i40e_state, I40E_ERROR);
7129d26e4fcSRobert Mustacchi 		return (DDI_INTR_CLAIMED);
7139d26e4fcSRobert Mustacchi 	}
7149d26e4fcSRobert Mustacchi 
7159d26e4fcSRobert Mustacchi 	if (reg == 0) {
7169d26e4fcSRobert Mustacchi 		if (shared == B_TRUE)
7179d26e4fcSRobert Mustacchi 			ret = DDI_INTR_UNCLAIMED;
7189d26e4fcSRobert Mustacchi 		goto done;
7199d26e4fcSRobert Mustacchi 	}
7209d26e4fcSRobert Mustacchi 
7219d26e4fcSRobert Mustacchi 	if (reg & I40E_PFINT_ICR0_ADMINQ_MASK)
7229d26e4fcSRobert Mustacchi 		i40e_intr_adminq_work(i40e);
7239d26e4fcSRobert Mustacchi 
7249d26e4fcSRobert Mustacchi 	if (reg & I40E_INTR_NOTX_RX_MASK)
7259d26e4fcSRobert Mustacchi 		i40e_intr_rx_work(i40e, 0);
7269d26e4fcSRobert Mustacchi 
7279d26e4fcSRobert Mustacchi 	if (reg & I40E_INTR_NOTX_TX_MASK)
7289d26e4fcSRobert Mustacchi 		i40e_intr_tx_work(i40e, 0);
7299d26e4fcSRobert Mustacchi 
7309d26e4fcSRobert Mustacchi done:
7319d26e4fcSRobert Mustacchi 	i40e_intr_adminq_enable(i40e);
7329d26e4fcSRobert Mustacchi 	return (ret);
7339d26e4fcSRobert Mustacchi 
7349d26e4fcSRobert Mustacchi }
7359d26e4fcSRobert Mustacchi 
7369d26e4fcSRobert Mustacchi /* ARGSUSED */
7379d26e4fcSRobert Mustacchi uint_t
7389d26e4fcSRobert Mustacchi i40e_intr_msi(void *arg1, void *arg2)
7399d26e4fcSRobert Mustacchi {
7409d26e4fcSRobert Mustacchi 	i40e_t *i40e = (i40e_t *)arg1;
7419d26e4fcSRobert Mustacchi 
7429d26e4fcSRobert Mustacchi 	return (i40e_intr_notx(i40e, B_FALSE));
7439d26e4fcSRobert Mustacchi }
7449d26e4fcSRobert Mustacchi 
7459d26e4fcSRobert Mustacchi /* ARGSUSED */
7469d26e4fcSRobert Mustacchi uint_t
7479d26e4fcSRobert Mustacchi i40e_intr_legacy(void *arg1, void *arg2)
7489d26e4fcSRobert Mustacchi {
7499d26e4fcSRobert Mustacchi 	i40e_t *i40e = (i40e_t *)arg1;
7509d26e4fcSRobert Mustacchi 
7519d26e4fcSRobert Mustacchi 	return (i40e_intr_notx(i40e, B_TRUE));
7529d26e4fcSRobert Mustacchi }
753