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