efx_intr.c revision 228078
1227569Sphilip/*- 2227569Sphilip * Copyright 2007-2009 Solarflare Communications Inc. All rights reserved. 3227569Sphilip * 4227569Sphilip * Redistribution and use in source and binary forms, with or without 5227569Sphilip * modification, are permitted provided that the following conditions 6227569Sphilip * are met: 7227569Sphilip * 1. Redistributions of source code must retain the above copyright 8227569Sphilip * notice, this list of conditions and the following disclaimer. 9227569Sphilip * 2. Redistributions in binary form must reproduce the above copyright 10227569Sphilip * notice, this list of conditions and the following disclaimer in the 11227569Sphilip * documentation and/or other materials provided with the distribution. 12227569Sphilip * 13227569Sphilip * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND 14227569Sphilip * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15227569Sphilip * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16227569Sphilip * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17227569Sphilip * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18227569Sphilip * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19227569Sphilip * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20227569Sphilip * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21227569Sphilip * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22227569Sphilip * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23227569Sphilip * SUCH DAMAGE. 24227569Sphilip */ 25227569Sphilip 26228078Sphilip#include <sys/cdefs.h> 27228078Sphilip__FBSDID("$FreeBSD: head/sys/dev/sfxge/common/efx_intr.c 228078 2011-11-28 17:19:05Z philip $"); 28228078Sphilip 29227569Sphilip#include "efsys.h" 30227569Sphilip#include "efx.h" 31227569Sphilip#include "efx_types.h" 32227569Sphilip#include "efx_regs.h" 33227569Sphilip#include "efx_impl.h" 34227569Sphilip 35227569Sphilip __checkReturn int 36227569Sphilipefx_intr_init( 37227569Sphilip __in efx_nic_t *enp, 38227569Sphilip __in efx_intr_type_t type, 39227569Sphilip __in efsys_mem_t *esmp) 40227569Sphilip{ 41227569Sphilip efx_intr_t *eip = &(enp->en_intr); 42227569Sphilip efx_oword_t oword; 43227569Sphilip int rc; 44227569Sphilip 45227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 46227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NIC); 47227569Sphilip 48227569Sphilip if (enp->en_mod_flags & EFX_MOD_INTR) { 49227569Sphilip rc = EINVAL; 50227569Sphilip goto fail1; 51227569Sphilip } 52227569Sphilip 53227569Sphilip enp->en_mod_flags |= EFX_MOD_INTR; 54227569Sphilip 55227569Sphilip eip->ei_type = type; 56227569Sphilip eip->ei_esmp = esmp; 57227569Sphilip 58227569Sphilip /* 59227569Sphilip * bug17213 workaround. 60227569Sphilip * 61227569Sphilip * Under legacy interrupts, don't share a level between fatal 62227569Sphilip * interrupts and event queue interrupts. Under MSI-X, they 63227569Sphilip * must share, or we won't get an interrupt. 64227569Sphilip */ 65227569Sphilip if (enp->en_family == EFX_FAMILY_SIENA && 66227569Sphilip eip->ei_type == EFX_INTR_LINE) 67227569Sphilip eip->ei_level = 0x1f; 68227569Sphilip else 69227569Sphilip eip->ei_level = 0; 70227569Sphilip 71227569Sphilip /* Enable all the genuinely fatal interrupts */ 72227569Sphilip EFX_SET_OWORD(oword); 73227569Sphilip EFX_SET_OWORD_FIELD(oword, FRF_AZ_ILL_ADR_INT_KER_EN, 0); 74227569Sphilip EFX_SET_OWORD_FIELD(oword, FRF_AZ_RBUF_OWN_INT_KER_EN, 0); 75227569Sphilip EFX_SET_OWORD_FIELD(oword, FRF_AZ_TBUF_OWN_INT_KER_EN, 0); 76227569Sphilip if (enp->en_family >= EFX_FAMILY_SIENA) 77227569Sphilip EFX_SET_OWORD_FIELD(oword, FRF_CZ_SRAM_PERR_INT_P_KER_EN, 0); 78227569Sphilip EFX_BAR_WRITEO(enp, FR_AZ_FATAL_INTR_REG_KER, &oword); 79227569Sphilip 80227569Sphilip /* Set up the interrupt address register */ 81227569Sphilip EFX_POPULATE_OWORD_3(oword, 82227569Sphilip FRF_AZ_NORM_INT_VEC_DIS_KER, (type == EFX_INTR_MESSAGE) ? 1 : 0, 83227569Sphilip FRF_AZ_INT_ADR_KER_DW0, EFSYS_MEM_ADDR(esmp) & 0xffffffff, 84227569Sphilip FRF_AZ_INT_ADR_KER_DW1, EFSYS_MEM_ADDR(esmp) >> 32); 85227569Sphilip EFX_BAR_WRITEO(enp, FR_AZ_INT_ADR_REG_KER, &oword); 86227569Sphilip 87227569Sphilip return (0); 88227569Sphilip 89227569Sphilipfail1: 90227569Sphilip EFSYS_PROBE1(fail1, int, rc); 91227569Sphilip 92227569Sphilip return (rc); 93227569Sphilip} 94227569Sphilip 95227569Sphilip void 96227569Sphilipefx_intr_enable( 97227569Sphilip __in efx_nic_t *enp) 98227569Sphilip{ 99227569Sphilip efx_intr_t *eip = &(enp->en_intr); 100227569Sphilip efx_oword_t oword; 101227569Sphilip 102227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 103227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_INTR); 104227569Sphilip 105227569Sphilip EFX_BAR_READO(enp, FR_AZ_INT_EN_REG_KER, &oword); 106227569Sphilip 107227569Sphilip EFX_SET_OWORD_FIELD(oword, FRF_AZ_KER_INT_LEVE_SEL, eip->ei_level); 108227569Sphilip EFX_SET_OWORD_FIELD(oword, FRF_AZ_DRV_INT_EN_KER, 1); 109227569Sphilip EFX_BAR_WRITEO(enp, FR_AZ_INT_EN_REG_KER, &oword); 110227569Sphilip} 111227569Sphilip 112227569Sphilip void 113227569Sphilipefx_intr_disable( 114227569Sphilip __in efx_nic_t *enp) 115227569Sphilip{ 116227569Sphilip efx_oword_t oword; 117227569Sphilip 118227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 119227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_INTR); 120227569Sphilip 121227569Sphilip EFX_BAR_READO(enp, FR_AZ_INT_EN_REG_KER, &oword); 122227569Sphilip EFX_SET_OWORD_FIELD(oword, FRF_AZ_DRV_INT_EN_KER, 0); 123227569Sphilip EFX_BAR_WRITEO(enp, FR_AZ_INT_EN_REG_KER, &oword); 124227569Sphilip 125227569Sphilip EFSYS_SPIN(10); 126227569Sphilip} 127227569Sphilip 128227569Sphilip void 129227569Sphilipefx_intr_disable_unlocked( 130227569Sphilip __in efx_nic_t *enp) 131227569Sphilip{ 132227569Sphilip efx_oword_t oword; 133227569Sphilip 134227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 135227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_INTR); 136227569Sphilip 137227569Sphilip EFSYS_BAR_READO(enp->en_esbp, FR_AZ_INT_EN_REG_KER_OFST, 138227569Sphilip &oword, B_FALSE); 139227569Sphilip EFX_SET_OWORD_FIELD(oword, FRF_AZ_DRV_INT_EN_KER, 0); 140227569Sphilip EFSYS_BAR_WRITEO(enp->en_esbp, FR_AZ_INT_EN_REG_KER_OFST, 141227569Sphilip &oword, B_FALSE); 142227569Sphilip} 143227569Sphilip 144227569Sphilip __checkReturn int 145227569Sphilipefx_intr_trigger( 146227569Sphilip __in efx_nic_t *enp, 147227569Sphilip __in unsigned int level) 148227569Sphilip{ 149227569Sphilip efx_intr_t *eip = &(enp->en_intr); 150227569Sphilip efx_oword_t oword; 151227569Sphilip unsigned int count; 152227569Sphilip uint32_t sel; 153227569Sphilip int rc; 154227569Sphilip 155227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 156227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_INTR); 157227569Sphilip 158227569Sphilip /* bug16757: No event queues can be initialized */ 159227569Sphilip EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_EV)); 160227569Sphilip 161227569Sphilip switch (enp->en_family) { 162227569Sphilip case EFX_FAMILY_FALCON: 163227569Sphilip if (level > EFX_NINTR_FALCON) { 164227569Sphilip rc = EINVAL; 165227569Sphilip goto fail1; 166227569Sphilip } 167227569Sphilip break; 168227569Sphilip 169227569Sphilip case EFX_FAMILY_SIENA: 170227569Sphilip if (level > EFX_NINTR_SIENA) { 171227569Sphilip rc = EINVAL; 172227569Sphilip goto fail1; 173227569Sphilip } 174227569Sphilip break; 175227569Sphilip 176227569Sphilip default: 177227569Sphilip EFSYS_ASSERT(B_FALSE); 178227569Sphilip break; 179227569Sphilip } 180227569Sphilip 181227569Sphilip if (level > EFX_MASK32(FRF_AZ_KER_INT_LEVE_SEL)) 182227569Sphilip return (ENOTSUP); /* avoid EFSYS_PROBE() */ 183227569Sphilip 184227569Sphilip sel = level; 185227569Sphilip 186227569Sphilip /* Trigger a test interrupt */ 187227569Sphilip EFX_BAR_READO(enp, FR_AZ_INT_EN_REG_KER, &oword); 188227569Sphilip EFX_SET_OWORD_FIELD(oword, FRF_AZ_KER_INT_LEVE_SEL, sel); 189227569Sphilip EFX_SET_OWORD_FIELD(oword, FRF_AZ_KER_INT_KER, 1); 190227569Sphilip EFX_BAR_WRITEO(enp, FR_AZ_INT_EN_REG_KER, &oword); 191227569Sphilip 192227569Sphilip /* 193227569Sphilip * Wait up to 100ms for the interrupt to be raised before restoring 194227569Sphilip * KER_INT_LEVE_SEL. Ignore a failure to raise (the caller will 195227569Sphilip * observe this soon enough anyway), but always reset KER_INT_LEVE_SEL 196227569Sphilip */ 197227569Sphilip count = 0; 198227569Sphilip do { 199227569Sphilip EFSYS_SPIN(100); /* 100us */ 200227569Sphilip 201227569Sphilip EFX_BAR_READO(enp, FR_AZ_INT_EN_REG_KER, &oword); 202227569Sphilip } while (EFX_OWORD_FIELD(oword, FRF_AZ_KER_INT_KER) && ++count < 1000); 203227569Sphilip 204227569Sphilip EFX_SET_OWORD_FIELD(oword, FRF_AZ_KER_INT_LEVE_SEL, eip->ei_level); 205227569Sphilip EFX_BAR_WRITEO(enp, FR_AZ_INT_EN_REG_KER, &oword); 206227569Sphilip 207227569Sphilip return (0); 208227569Sphilip 209227569Sphilipfail1: 210227569Sphilip EFSYS_PROBE1(fail1, int, rc); 211227569Sphilip 212227569Sphilip return (rc); 213227569Sphilip} 214227569Sphilip 215227569Sphilipstatic __checkReturn boolean_t 216227569Sphilipefx_intr_check_fatal( 217227569Sphilip __in efx_nic_t *enp) 218227569Sphilip{ 219227569Sphilip efx_intr_t *eip = &(enp->en_intr); 220227569Sphilip efsys_mem_t *esmp = eip->ei_esmp; 221227569Sphilip efx_oword_t oword; 222227569Sphilip 223227569Sphilip /* Read the syndrome */ 224227569Sphilip EFSYS_MEM_READO(esmp, 0, &oword); 225227569Sphilip 226227569Sphilip if (EFX_OWORD_FIELD(oword, FSF_AZ_NET_IVEC_FATAL_INT) != 0) { 227227569Sphilip EFSYS_PROBE(fatal); 228227569Sphilip 229227569Sphilip /* Clear the fatal interrupt condition */ 230227569Sphilip EFX_SET_OWORD_FIELD(oword, FSF_AZ_NET_IVEC_FATAL_INT, 0); 231227569Sphilip EFSYS_MEM_WRITEO(esmp, 0, &oword); 232227569Sphilip 233227569Sphilip return (B_TRUE); 234227569Sphilip } 235227569Sphilip 236227569Sphilip return (B_FALSE); 237227569Sphilip} 238227569Sphilip 239227569Sphilip void 240227569Sphilipefx_intr_status_line( 241227569Sphilip __in efx_nic_t *enp, 242227569Sphilip __out boolean_t *fatalp, 243227569Sphilip __out uint32_t *qmaskp) 244227569Sphilip{ 245227569Sphilip efx_intr_t *eip = &(enp->en_intr); 246227569Sphilip efx_dword_t dword; 247227569Sphilip 248227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 249227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_INTR); 250227569Sphilip 251227569Sphilip /* 252227569Sphilip * Read the queue mask and implicitly acknowledge the 253227569Sphilip * interrupt. 254227569Sphilip */ 255227569Sphilip EFX_BAR_READD(enp, FR_BZ_INT_ISR0_REG, &dword, B_FALSE); 256227569Sphilip *qmaskp = EFX_DWORD_FIELD(dword, EFX_DWORD_0); 257227569Sphilip 258227569Sphilip EFSYS_PROBE1(qmask, uint32_t, *qmaskp); 259227569Sphilip 260227569Sphilip if (*qmaskp & (1U << eip->ei_level)) 261227569Sphilip *fatalp = efx_intr_check_fatal(enp); 262227569Sphilip else 263227569Sphilip *fatalp = B_FALSE; 264227569Sphilip} 265227569Sphilip 266227569Sphilip void 267227569Sphilipefx_intr_status_message( 268227569Sphilip __in efx_nic_t *enp, 269227569Sphilip __in unsigned int message, 270227569Sphilip __out boolean_t *fatalp) 271227569Sphilip{ 272227569Sphilip efx_intr_t *eip = &(enp->en_intr); 273227569Sphilip 274227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 275227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_INTR); 276227569Sphilip 277227569Sphilip if (message == eip->ei_level) 278227569Sphilip *fatalp = efx_intr_check_fatal(enp); 279227569Sphilip else 280227569Sphilip *fatalp = B_FALSE; 281227569Sphilip} 282227569Sphilip 283227569Sphilip void 284227569Sphilipefx_intr_fatal( 285227569Sphilip __in efx_nic_t *enp) 286227569Sphilip{ 287227569Sphilip#if EFSYS_OPT_DECODE_INTR_FATAL 288227569Sphilip efx_oword_t fatal; 289227569Sphilip efx_oword_t mem_per; 290227569Sphilip 291227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 292227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_INTR); 293227569Sphilip 294227569Sphilip EFX_BAR_READO(enp, FR_AZ_FATAL_INTR_REG_KER, &fatal); 295227569Sphilip EFX_ZERO_OWORD(mem_per); 296227569Sphilip 297227569Sphilip if (EFX_OWORD_FIELD(fatal, FRF_AZ_SRM_PERR_INT_KER) != 0 || 298227569Sphilip EFX_OWORD_FIELD(fatal, FRF_AZ_MEM_PERR_INT_KER) != 0) 299227569Sphilip EFX_BAR_READO(enp, FR_AZ_MEM_STAT_REG, &mem_per); 300227569Sphilip 301227569Sphilip if (EFX_OWORD_FIELD(fatal, FRF_AZ_SRAM_OOB_INT_KER) != 0) 302227569Sphilip EFSYS_ERR(enp->en_esip, EFX_ERR_SRAM_OOB, 0, 0); 303227569Sphilip 304227569Sphilip if (EFX_OWORD_FIELD(fatal, FRF_AZ_BUFID_DC_OOB_INT_KER) != 0) 305227569Sphilip EFSYS_ERR(enp->en_esip, EFX_ERR_BUFID_DC_OOB, 0, 0); 306227569Sphilip 307227569Sphilip if (EFX_OWORD_FIELD(fatal, FRF_AZ_MEM_PERR_INT_KER) != 0) 308227569Sphilip EFSYS_ERR(enp->en_esip, EFX_ERR_MEM_PERR, 309227569Sphilip EFX_OWORD_FIELD(mem_per, EFX_DWORD_0), 310227569Sphilip EFX_OWORD_FIELD(mem_per, EFX_DWORD_1)); 311227569Sphilip 312227569Sphilip if (EFX_OWORD_FIELD(fatal, FRF_AZ_RBUF_OWN_INT_KER) != 0) 313227569Sphilip EFSYS_ERR(enp->en_esip, EFX_ERR_RBUF_OWN, 0, 0); 314227569Sphilip 315227569Sphilip if (EFX_OWORD_FIELD(fatal, FRF_AZ_TBUF_OWN_INT_KER) != 0) 316227569Sphilip EFSYS_ERR(enp->en_esip, EFX_ERR_TBUF_OWN, 0, 0); 317227569Sphilip 318227569Sphilip if (EFX_OWORD_FIELD(fatal, FRF_AZ_RDESCQ_OWN_INT_KER) != 0) 319227569Sphilip EFSYS_ERR(enp->en_esip, EFX_ERR_RDESQ_OWN, 0, 0); 320227569Sphilip 321227569Sphilip if (EFX_OWORD_FIELD(fatal, FRF_AZ_TDESCQ_OWN_INT_KER) != 0) 322227569Sphilip EFSYS_ERR(enp->en_esip, EFX_ERR_TDESQ_OWN, 0, 0); 323227569Sphilip 324227569Sphilip if (EFX_OWORD_FIELD(fatal, FRF_AZ_EVQ_OWN_INT_KER) != 0) 325227569Sphilip EFSYS_ERR(enp->en_esip, EFX_ERR_EVQ_OWN, 0, 0); 326227569Sphilip 327227569Sphilip if (EFX_OWORD_FIELD(fatal, FRF_AZ_EVF_OFLO_INT_KER) != 0) 328227569Sphilip EFSYS_ERR(enp->en_esip, EFX_ERR_EVFF_OFLO, 0, 0); 329227569Sphilip 330227569Sphilip if (EFX_OWORD_FIELD(fatal, FRF_AZ_ILL_ADR_INT_KER) != 0) 331227569Sphilip EFSYS_ERR(enp->en_esip, EFX_ERR_ILL_ADDR, 0, 0); 332227569Sphilip 333227569Sphilip if (EFX_OWORD_FIELD(fatal, FRF_AZ_SRM_PERR_INT_KER) != 0) 334227569Sphilip EFSYS_ERR(enp->en_esip, EFX_ERR_SRAM_PERR, 335227569Sphilip EFX_OWORD_FIELD(mem_per, EFX_DWORD_0), 336227569Sphilip EFX_OWORD_FIELD(mem_per, EFX_DWORD_1)); 337227569Sphilip#else 338227569Sphilip EFSYS_ASSERT(0); 339227569Sphilip#endif 340227569Sphilip} 341227569Sphilip 342227569Sphilip void 343227569Sphilipefx_intr_fini( 344227569Sphilip __in efx_nic_t *enp) 345227569Sphilip{ 346227569Sphilip efx_oword_t oword; 347227569Sphilip 348227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 349227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NIC); 350227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_INTR); 351227569Sphilip 352227569Sphilip /* Clear the interrupt address register */ 353227569Sphilip EFX_ZERO_OWORD(oword); 354227569Sphilip EFX_BAR_WRITEO(enp, FR_AZ_INT_ADR_REG_KER, &oword); 355227569Sphilip 356227569Sphilip enp->en_mod_flags &= ~EFX_MOD_INTR; 357227569Sphilip} 358