brintos

brintos / linux-shallow public Read only

0
0
Text · 24.4 KiB · 6717238 Raw
910 lines · c
1// SPDX-License-Identifier: ISC2/*3 * Copyright (c) 2012-2017 Qualcomm Atheros, Inc.4 * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.5 */6 7#include <linux/interrupt.h>8 9#include "wil6210.h"10#include "trace.h"11 12/*13 * Theory of operation:14 *15 * There is ISR pseudo-cause register,16 * dma_rgf->DMA_RGF.PSEUDO_CAUSE.PSEUDO_CAUSE17 * Its bits represents OR'ed bits from 3 real ISR registers:18 * TX, RX, and MISC.19 *20 * Registers may be configured to either "write 1 to clear" or21 * "clear on read" mode22 *23 * When handling interrupt, one have to mask/unmask interrupts for the24 * real ISR registers, or hardware may malfunction.25 *26 */27 28#define WIL6210_IRQ_DISABLE		(0xFFFFFFFFUL)29#define WIL6210_IRQ_DISABLE_NO_HALP	(0xF7FFFFFFUL)30#define WIL6210_IMC_RX		(BIT_DMA_EP_RX_ICR_RX_DONE | \31				 BIT_DMA_EP_RX_ICR_RX_HTRSH)32#define WIL6210_IMC_RX_NO_RX_HTRSH (WIL6210_IMC_RX & \33				    (~(BIT_DMA_EP_RX_ICR_RX_HTRSH)))34#define WIL6210_IMC_TX		(BIT_DMA_EP_TX_ICR_TX_DONE | \35				BIT_DMA_EP_TX_ICR_TX_DONE_N(0))36#define WIL6210_IMC_TX_EDMA		BIT_TX_STATUS_IRQ37#define WIL6210_IMC_RX_EDMA		BIT_RX_STATUS_IRQ38#define WIL6210_IMC_MISC_NO_HALP	(ISR_MISC_FW_READY | \39					 ISR_MISC_MBOX_EVT | \40					 ISR_MISC_FW_ERROR)41#define WIL6210_IMC_MISC		(WIL6210_IMC_MISC_NO_HALP | \42					 BIT_DMA_EP_MISC_ICR_HALP)43#define WIL6210_IRQ_PSEUDO_MASK (u32)(~(BIT_DMA_PSEUDO_CAUSE_RX | \44					BIT_DMA_PSEUDO_CAUSE_TX | \45					BIT_DMA_PSEUDO_CAUSE_MISC))46 47#if defined(CONFIG_WIL6210_ISR_COR)48/* configure to Clear-On-Read mode */49#define WIL_ICR_ICC_VALUE	(0xFFFFFFFFUL)50#define WIL_ICR_ICC_MISC_VALUE	(0xF7FFFFFFUL)51 52static inline void wil_icr_clear(u32 x, void __iomem *addr)53{54}55#else /* defined(CONFIG_WIL6210_ISR_COR) */56/* configure to Write-1-to-Clear mode */57#define WIL_ICR_ICC_VALUE	(0UL)58#define WIL_ICR_ICC_MISC_VALUE	(0UL)59 60static inline void wil_icr_clear(u32 x, void __iomem *addr)61{62	writel(x, addr);63}64#endif /* defined(CONFIG_WIL6210_ISR_COR) */65 66static inline u32 wil_ioread32_and_clear(void __iomem *addr)67{68	u32 x = readl(addr);69 70	wil_icr_clear(x, addr);71 72	return x;73}74 75static void wil6210_mask_irq_tx(struct wil6210_priv *wil)76{77	wil_w(wil, RGF_DMA_EP_TX_ICR + offsetof(struct RGF_ICR, IMS),78	      WIL6210_IRQ_DISABLE);79}80 81static void wil6210_mask_irq_tx_edma(struct wil6210_priv *wil)82{83	wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, IMS),84	      WIL6210_IRQ_DISABLE);85}86 87static void wil6210_mask_irq_rx(struct wil6210_priv *wil)88{89	wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, IMS),90	      WIL6210_IRQ_DISABLE);91}92 93static void wil6210_mask_irq_rx_edma(struct wil6210_priv *wil)94{95	wil_w(wil, RGF_INT_GEN_RX_ICR + offsetof(struct RGF_ICR, IMS),96	      WIL6210_IRQ_DISABLE);97}98 99static void wil6210_mask_irq_misc(struct wil6210_priv *wil, bool mask_halp)100{101	wil_dbg_irq(wil, "mask_irq_misc: mask_halp(%s)\n",102		    mask_halp ? "true" : "false");103 104	wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, IMS),105	      mask_halp ? WIL6210_IRQ_DISABLE : WIL6210_IRQ_DISABLE_NO_HALP);106}107 108void wil6210_mask_halp(struct wil6210_priv *wil)109{110	wil_dbg_irq(wil, "mask_halp\n");111 112	wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, IMS),113	      BIT_DMA_EP_MISC_ICR_HALP);114}115 116static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil)117{118	wil_dbg_irq(wil, "mask_irq_pseudo\n");119 120	wil_w(wil, RGF_DMA_PSEUDO_CAUSE_MASK_SW, WIL6210_IRQ_DISABLE);121 122	clear_bit(wil_status_irqen, wil->status);123}124 125void wil6210_unmask_irq_tx(struct wil6210_priv *wil)126{127	wil_w(wil, RGF_DMA_EP_TX_ICR + offsetof(struct RGF_ICR, IMC),128	      WIL6210_IMC_TX);129}130 131void wil6210_unmask_irq_tx_edma(struct wil6210_priv *wil)132{133	wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, IMC),134	      WIL6210_IMC_TX_EDMA);135}136 137void wil6210_unmask_irq_rx(struct wil6210_priv *wil)138{139	bool unmask_rx_htrsh = atomic_read(&wil->connected_vifs) > 0;140 141	wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, IMC),142	      unmask_rx_htrsh ? WIL6210_IMC_RX : WIL6210_IMC_RX_NO_RX_HTRSH);143}144 145void wil6210_unmask_irq_rx_edma(struct wil6210_priv *wil)146{147	wil_w(wil, RGF_INT_GEN_RX_ICR + offsetof(struct RGF_ICR, IMC),148	      WIL6210_IMC_RX_EDMA);149}150 151static void wil6210_unmask_irq_misc(struct wil6210_priv *wil, bool unmask_halp)152{153	wil_dbg_irq(wil, "unmask_irq_misc: unmask_halp(%s)\n",154		    unmask_halp ? "true" : "false");155 156	wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, IMC),157	      unmask_halp ? WIL6210_IMC_MISC : WIL6210_IMC_MISC_NO_HALP);158}159 160static void wil6210_unmask_halp(struct wil6210_priv *wil)161{162	wil_dbg_irq(wil, "unmask_halp\n");163 164	wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, IMC),165	      BIT_DMA_EP_MISC_ICR_HALP);166}167 168static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil)169{170	wil_dbg_irq(wil, "unmask_irq_pseudo\n");171 172	set_bit(wil_status_irqen, wil->status);173 174	wil_w(wil, RGF_DMA_PSEUDO_CAUSE_MASK_SW, WIL6210_IRQ_PSEUDO_MASK);175}176 177void wil_mask_irq(struct wil6210_priv *wil)178{179	wil_dbg_irq(wil, "mask_irq\n");180 181	wil6210_mask_irq_tx(wil);182	wil6210_mask_irq_tx_edma(wil);183	wil6210_mask_irq_rx(wil);184	wil6210_mask_irq_rx_edma(wil);185	wil6210_mask_irq_misc(wil, true);186	wil6210_mask_irq_pseudo(wil);187}188 189void wil_unmask_irq(struct wil6210_priv *wil)190{191	wil_dbg_irq(wil, "unmask_irq\n");192 193	wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, ICC),194	      WIL_ICR_ICC_VALUE);195	wil_w(wil, RGF_DMA_EP_TX_ICR + offsetof(struct RGF_ICR, ICC),196	      WIL_ICR_ICC_VALUE);197	wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, ICC),198	      WIL_ICR_ICC_MISC_VALUE);199	wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, ICC),200	      WIL_ICR_ICC_VALUE);201	wil_w(wil, RGF_INT_GEN_RX_ICR + offsetof(struct RGF_ICR, ICC),202	      WIL_ICR_ICC_VALUE);203 204	wil6210_unmask_irq_pseudo(wil);205	if (wil->use_enhanced_dma_hw) {206		wil6210_unmask_irq_tx_edma(wil);207		wil6210_unmask_irq_rx_edma(wil);208	} else {209		wil6210_unmask_irq_tx(wil);210		wil6210_unmask_irq_rx(wil);211	}212	wil6210_unmask_irq_misc(wil, true);213}214 215void wil_configure_interrupt_moderation_edma(struct wil6210_priv *wil)216{217	u32 moderation;218 219	wil_s(wil, RGF_INT_GEN_IDLE_TIME_LIMIT, WIL_EDMA_IDLE_TIME_LIMIT_USEC);220 221	wil_s(wil, RGF_INT_GEN_TIME_UNIT_LIMIT, WIL_EDMA_TIME_UNIT_CLK_CYCLES);222 223	/* Update RX and TX moderation */224	moderation = wil->rx_max_burst_duration |225		(WIL_EDMA_AGG_WATERMARK << WIL_EDMA_AGG_WATERMARK_POS);226	wil_w(wil, RGF_INT_CTRL_INT_GEN_CFG_0, moderation);227	wil_w(wil, RGF_INT_CTRL_INT_GEN_CFG_1, moderation);228 229	/* Treat special events as regular230	 * (set bit 0 to 0x1 and clear bits 1-8)231	 */232	wil_c(wil, RGF_INT_COUNT_ON_SPECIAL_EVT, 0x1FE);233	wil_s(wil, RGF_INT_COUNT_ON_SPECIAL_EVT, 0x1);234}235 236void wil_configure_interrupt_moderation(struct wil6210_priv *wil)237{238	struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr;239 240	wil_dbg_irq(wil, "configure_interrupt_moderation\n");241 242	/* disable interrupt moderation for monitor243	 * to get better timestamp precision244	 */245	if (wdev->iftype == NL80211_IFTYPE_MONITOR)246		return;247 248	/* Disable and clear tx counter before (re)configuration */249	wil_w(wil, RGF_DMA_ITR_TX_CNT_CTL, BIT_DMA_ITR_TX_CNT_CTL_CLR);250	wil_w(wil, RGF_DMA_ITR_TX_CNT_TRSH, wil->tx_max_burst_duration);251	wil_info(wil, "set ITR_TX_CNT_TRSH = %d usec\n",252		 wil->tx_max_burst_duration);253	/* Configure TX max burst duration timer to use usec units */254	wil_w(wil, RGF_DMA_ITR_TX_CNT_CTL,255	      BIT_DMA_ITR_TX_CNT_CTL_EN | BIT_DMA_ITR_TX_CNT_CTL_EXT_TIC_SEL);256 257	/* Disable and clear tx idle counter before (re)configuration */258	wil_w(wil, RGF_DMA_ITR_TX_IDL_CNT_CTL, BIT_DMA_ITR_TX_IDL_CNT_CTL_CLR);259	wil_w(wil, RGF_DMA_ITR_TX_IDL_CNT_TRSH, wil->tx_interframe_timeout);260	wil_info(wil, "set ITR_TX_IDL_CNT_TRSH = %d usec\n",261		 wil->tx_interframe_timeout);262	/* Configure TX max burst duration timer to use usec units */263	wil_w(wil, RGF_DMA_ITR_TX_IDL_CNT_CTL, BIT_DMA_ITR_TX_IDL_CNT_CTL_EN |264	      BIT_DMA_ITR_TX_IDL_CNT_CTL_EXT_TIC_SEL);265 266	/* Disable and clear rx counter before (re)configuration */267	wil_w(wil, RGF_DMA_ITR_RX_CNT_CTL, BIT_DMA_ITR_RX_CNT_CTL_CLR);268	wil_w(wil, RGF_DMA_ITR_RX_CNT_TRSH, wil->rx_max_burst_duration);269	wil_info(wil, "set ITR_RX_CNT_TRSH = %d usec\n",270		 wil->rx_max_burst_duration);271	/* Configure TX max burst duration timer to use usec units */272	wil_w(wil, RGF_DMA_ITR_RX_CNT_CTL,273	      BIT_DMA_ITR_RX_CNT_CTL_EN | BIT_DMA_ITR_RX_CNT_CTL_EXT_TIC_SEL);274 275	/* Disable and clear rx idle counter before (re)configuration */276	wil_w(wil, RGF_DMA_ITR_RX_IDL_CNT_CTL, BIT_DMA_ITR_RX_IDL_CNT_CTL_CLR);277	wil_w(wil, RGF_DMA_ITR_RX_IDL_CNT_TRSH, wil->rx_interframe_timeout);278	wil_info(wil, "set ITR_RX_IDL_CNT_TRSH = %d usec\n",279		 wil->rx_interframe_timeout);280	/* Configure TX max burst duration timer to use usec units */281	wil_w(wil, RGF_DMA_ITR_RX_IDL_CNT_CTL, BIT_DMA_ITR_RX_IDL_CNT_CTL_EN |282	      BIT_DMA_ITR_RX_IDL_CNT_CTL_EXT_TIC_SEL);283}284 285static irqreturn_t wil6210_irq_rx(int irq, void *cookie)286{287	struct wil6210_priv *wil = cookie;288	u32 isr;289	bool need_unmask = true;290 291	wil6210_mask_irq_rx(wil);292 293	isr = wil_ioread32_and_clear(wil->csr +294				     HOSTADDR(RGF_DMA_EP_RX_ICR) +295				     offsetof(struct RGF_ICR, ICR));296 297	trace_wil6210_irq_rx(isr);298	wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr);299 300	if (unlikely(!isr)) {301		wil_err_ratelimited(wil, "spurious IRQ: RX\n");302		wil6210_unmask_irq_rx(wil);303		return IRQ_NONE;304	}305 306	/* RX_DONE and RX_HTRSH interrupts are the same if interrupt307	 * moderation is not used. Interrupt moderation may cause RX308	 * buffer overflow while RX_DONE is delayed. The required309	 * action is always the same - should empty the accumulated310	 * packets from the RX ring.311	 */312	if (likely(isr & (BIT_DMA_EP_RX_ICR_RX_DONE |313			  BIT_DMA_EP_RX_ICR_RX_HTRSH))) {314		wil_dbg_irq(wil, "RX done / RX_HTRSH received, ISR (0x%x)\n",315			    isr);316 317		isr &= ~(BIT_DMA_EP_RX_ICR_RX_DONE |318			 BIT_DMA_EP_RX_ICR_RX_HTRSH);319		if (likely(test_bit(wil_status_fwready, wil->status))) {320			if (likely(test_bit(wil_status_napi_en, wil->status))) {321				wil_dbg_txrx(wil, "NAPI(Rx) schedule\n");322				need_unmask = false;323				napi_schedule(&wil->napi_rx);324			} else {325				wil_err_ratelimited(326					wil,327					"Got Rx interrupt while stopping interface\n");328			}329		} else {330			wil_err_ratelimited(wil, "Got Rx interrupt while in reset\n");331		}332	}333 334	if (unlikely(isr))335		wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr);336 337	/* Rx IRQ will be enabled when NAPI processing finished */338 339	atomic_inc(&wil->isr_count_rx);340 341	if (unlikely(need_unmask))342		wil6210_unmask_irq_rx(wil);343 344	return IRQ_HANDLED;345}346 347static irqreturn_t wil6210_irq_rx_edma(int irq, void *cookie)348{349	struct wil6210_priv *wil = cookie;350	u32 isr;351	bool need_unmask = true;352 353	wil6210_mask_irq_rx_edma(wil);354 355	isr = wil_ioread32_and_clear(wil->csr +356				     HOSTADDR(RGF_INT_GEN_RX_ICR) +357				     offsetof(struct RGF_ICR, ICR));358 359	trace_wil6210_irq_rx(isr);360	wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr);361 362	if (unlikely(!isr)) {363		wil_err(wil, "spurious IRQ: RX\n");364		wil6210_unmask_irq_rx_edma(wil);365		return IRQ_NONE;366	}367 368	if (likely(isr & BIT_RX_STATUS_IRQ)) {369		wil_dbg_irq(wil, "RX status ring\n");370		isr &= ~BIT_RX_STATUS_IRQ;371		if (likely(test_bit(wil_status_fwready, wil->status))) {372			if (likely(test_bit(wil_status_napi_en, wil->status))) {373				wil_dbg_txrx(wil, "NAPI(Rx) schedule\n");374				need_unmask = false;375				napi_schedule(&wil->napi_rx);376			} else {377				wil_err(wil,378					"Got Rx interrupt while stopping interface\n");379			}380		} else {381			wil_err(wil, "Got Rx interrupt while in reset\n");382		}383	}384 385	if (unlikely(isr))386		wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr);387 388	/* Rx IRQ will be enabled when NAPI processing finished */389 390	atomic_inc(&wil->isr_count_rx);391 392	if (unlikely(need_unmask))393		wil6210_unmask_irq_rx_edma(wil);394 395	return IRQ_HANDLED;396}397 398static irqreturn_t wil6210_irq_tx_edma(int irq, void *cookie)399{400	struct wil6210_priv *wil = cookie;401	u32 isr;402	bool need_unmask = true;403 404	wil6210_mask_irq_tx_edma(wil);405 406	isr = wil_ioread32_and_clear(wil->csr +407				     HOSTADDR(RGF_INT_GEN_TX_ICR) +408				     offsetof(struct RGF_ICR, ICR));409 410	trace_wil6210_irq_tx(isr);411	wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);412 413	if (unlikely(!isr)) {414		wil_err(wil, "spurious IRQ: TX\n");415		wil6210_unmask_irq_tx_edma(wil);416		return IRQ_NONE;417	}418 419	if (likely(isr & BIT_TX_STATUS_IRQ)) {420		wil_dbg_irq(wil, "TX status ring\n");421		isr &= ~BIT_TX_STATUS_IRQ;422		if (likely(test_bit(wil_status_fwready, wil->status))) {423			wil_dbg_txrx(wil, "NAPI(Tx) schedule\n");424			need_unmask = false;425			napi_schedule(&wil->napi_tx);426		} else {427			wil_err(wil, "Got Tx status ring IRQ while in reset\n");428		}429	}430 431	if (unlikely(isr))432		wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr);433 434	/* Tx IRQ will be enabled when NAPI processing finished */435 436	atomic_inc(&wil->isr_count_tx);437 438	if (unlikely(need_unmask))439		wil6210_unmask_irq_tx_edma(wil);440 441	return IRQ_HANDLED;442}443 444static irqreturn_t wil6210_irq_tx(int irq, void *cookie)445{446	struct wil6210_priv *wil = cookie;447	u32 isr;448	bool need_unmask = true;449 450	wil6210_mask_irq_tx(wil);451 452	isr = wil_ioread32_and_clear(wil->csr +453				     HOSTADDR(RGF_DMA_EP_TX_ICR) +454				     offsetof(struct RGF_ICR, ICR));455 456	trace_wil6210_irq_tx(isr);457	wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);458 459	if (unlikely(!isr)) {460		wil_err_ratelimited(wil, "spurious IRQ: TX\n");461		wil6210_unmask_irq_tx(wil);462		return IRQ_NONE;463	}464 465	if (likely(isr & BIT_DMA_EP_TX_ICR_TX_DONE)) {466		wil_dbg_irq(wil, "TX done\n");467		isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;468		/* clear also all VRING interrupts */469		isr &= ~(BIT(25) - 1UL);470		if (likely(test_bit(wil_status_fwready, wil->status))) {471			wil_dbg_txrx(wil, "NAPI(Tx) schedule\n");472			need_unmask = false;473			napi_schedule(&wil->napi_tx);474		} else {475			wil_err_ratelimited(wil, "Got Tx interrupt while in reset\n");476		}477	}478 479	if (unlikely(isr))480		wil_err_ratelimited(wil, "un-handled TX ISR bits 0x%08x\n",481				    isr);482 483	/* Tx IRQ will be enabled when NAPI processing finished */484 485	atomic_inc(&wil->isr_count_tx);486 487	if (unlikely(need_unmask))488		wil6210_unmask_irq_tx(wil);489 490	return IRQ_HANDLED;491}492 493static void wil_notify_fw_error(struct wil6210_priv *wil)494{495	struct device *dev = &wil->main_ndev->dev;496	char *envp[3] = {497		[0] = "SOURCE=wil6210",498		[1] = "EVENT=FW_ERROR",499		[2] = NULL,500	};501	wil_err(wil, "Notify about firmware error\n");502	kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);503}504 505static void wil_cache_mbox_regs(struct wil6210_priv *wil)506{507	/* make shadow copy of registers that should not change on run time */508	wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX,509			     sizeof(struct wil6210_mbox_ctl));510	wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx);511	wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);512}513 514static bool wil_validate_mbox_regs(struct wil6210_priv *wil)515{516	size_t min_size = sizeof(struct wil6210_mbox_hdr) +517		sizeof(struct wmi_cmd_hdr);518 519	if (wil->mbox_ctl.rx.entry_size < min_size) {520		wil_err(wil, "rx mbox entry too small (%d)\n",521			wil->mbox_ctl.rx.entry_size);522		return false;523	}524	if (wil->mbox_ctl.tx.entry_size < min_size) {525		wil_err(wil, "tx mbox entry too small (%d)\n",526			wil->mbox_ctl.tx.entry_size);527		return false;528	}529 530	return true;531}532 533static irqreturn_t wil6210_irq_misc(int irq, void *cookie)534{535	struct wil6210_priv *wil = cookie;536	u32 isr;537 538	wil6210_mask_irq_misc(wil, false);539 540	isr = wil_ioread32_and_clear(wil->csr +541				     HOSTADDR(RGF_DMA_EP_MISC_ICR) +542				     offsetof(struct RGF_ICR, ICR));543 544	trace_wil6210_irq_misc(isr);545	wil_dbg_irq(wil, "ISR MISC 0x%08x\n", isr);546 547	if (!isr) {548		wil_err(wil, "spurious IRQ: MISC\n");549		wil6210_unmask_irq_misc(wil, false);550		return IRQ_NONE;551	}552 553	if (isr & ISR_MISC_FW_ERROR) {554		u32 fw_assert_code = wil_r(wil, wil->rgf_fw_assert_code_addr);555		u32 ucode_assert_code =556			wil_r(wil, wil->rgf_ucode_assert_code_addr);557 558		wil_err(wil,559			"Firmware error detected, assert codes FW 0x%08x, UCODE 0x%08x\n",560			fw_assert_code, ucode_assert_code);561		clear_bit(wil_status_fwready, wil->status);562		/*563		 * do not clear @isr here - we do 2-nd part in thread564		 * there, user space get notified, and it should be done565		 * in non-atomic context566		 */567	}568 569	if (isr & ISR_MISC_FW_READY) {570		wil_dbg_irq(wil, "IRQ: FW ready\n");571		wil_cache_mbox_regs(wil);572		if (wil_validate_mbox_regs(wil))573			set_bit(wil_status_mbox_ready, wil->status);574		/**575		 * Actual FW ready indicated by the576		 * WMI_FW_READY_EVENTID577		 */578		isr &= ~ISR_MISC_FW_READY;579	}580 581	if (isr & BIT_DMA_EP_MISC_ICR_HALP) {582		isr &= ~BIT_DMA_EP_MISC_ICR_HALP;583		if (wil->halp.handle_icr) {584			/* no need to handle HALP ICRs until next vote */585			wil->halp.handle_icr = false;586			wil_dbg_irq(wil, "irq_misc: HALP IRQ invoked\n");587			wil6210_mask_irq_misc(wil, true);588			complete(&wil->halp.comp);589		}590	}591 592	wil->isr_misc = isr;593 594	if (isr) {595		return IRQ_WAKE_THREAD;596	} else {597		wil6210_unmask_irq_misc(wil, false);598		return IRQ_HANDLED;599	}600}601 602static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)603{604	struct wil6210_priv *wil = cookie;605	u32 isr = wil->isr_misc;606 607	trace_wil6210_irq_misc_thread(isr);608	wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr);609 610	if (isr & ISR_MISC_FW_ERROR) {611		wil->recovery_state = fw_recovery_pending;612		wil_fw_core_dump(wil);613		wil_notify_fw_error(wil);614		isr &= ~ISR_MISC_FW_ERROR;615		if (wil->platform_ops.notify) {616			wil_err(wil, "notify platform driver about FW crash");617			wil->platform_ops.notify(wil->platform_handle,618						 WIL_PLATFORM_EVT_FW_CRASH);619		} else {620			wil_fw_error_recovery(wil);621		}622	}623	if (isr & ISR_MISC_MBOX_EVT) {624		wil_dbg_irq(wil, "MBOX event\n");625		wmi_recv_cmd(wil);626		isr &= ~ISR_MISC_MBOX_EVT;627	}628 629	if (isr)630		wil_dbg_irq(wil, "un-handled MISC ISR bits 0x%08x\n", isr);631 632	wil->isr_misc = 0;633 634	wil6210_unmask_irq_misc(wil, false);635 636	/* in non-triple MSI case, this is done inside wil6210_thread_irq637	 * because it has to be done after unmasking the pseudo.638	 */639	if (wil->n_msi == 3 && wil->suspend_resp_rcvd) {640		wil_dbg_irq(wil, "set suspend_resp_comp to true\n");641		wil->suspend_resp_comp = true;642		wake_up_interruptible(&wil->wq);643	}644 645	return IRQ_HANDLED;646}647 648/* thread IRQ handler */649static irqreturn_t wil6210_thread_irq(int irq, void *cookie)650{651	struct wil6210_priv *wil = cookie;652 653	wil_dbg_irq(wil, "Thread IRQ\n");654	/* Discover real IRQ cause */655	if (wil->isr_misc)656		wil6210_irq_misc_thread(irq, cookie);657 658	wil6210_unmask_irq_pseudo(wil);659 660	if (wil->suspend_resp_rcvd) {661		wil_dbg_irq(wil, "set suspend_resp_comp to true\n");662		wil->suspend_resp_comp = true;663		wake_up_interruptible(&wil->wq);664	}665 666	return IRQ_HANDLED;667}668 669/* DEBUG670 * There is subtle bug in hardware that causes IRQ to raise when it should be671 * masked. It is quite rare and hard to debug.672 *673 * Catch irq issue if it happens and print all I can.674 */675static int wil6210_debug_irq_mask(struct wil6210_priv *wil, u32 pseudo_cause)676{677	u32 icm_rx, icr_rx, imv_rx;678	u32 icm_tx, icr_tx, imv_tx;679	u32 icm_misc, icr_misc, imv_misc;680 681	if (!test_bit(wil_status_irqen, wil->status)) {682		if (wil->use_enhanced_dma_hw) {683			icm_rx = wil_ioread32_and_clear(wil->csr +684					HOSTADDR(RGF_INT_GEN_RX_ICR) +685					offsetof(struct RGF_ICR, ICM));686			icr_rx = wil_ioread32_and_clear(wil->csr +687					HOSTADDR(RGF_INT_GEN_RX_ICR) +688					offsetof(struct RGF_ICR, ICR));689			imv_rx = wil_r(wil, RGF_INT_GEN_RX_ICR +690				   offsetof(struct RGF_ICR, IMV));691			icm_tx = wil_ioread32_and_clear(wil->csr +692					HOSTADDR(RGF_INT_GEN_TX_ICR) +693					offsetof(struct RGF_ICR, ICM));694			icr_tx = wil_ioread32_and_clear(wil->csr +695					HOSTADDR(RGF_INT_GEN_TX_ICR) +696					offsetof(struct RGF_ICR, ICR));697			imv_tx = wil_r(wil, RGF_INT_GEN_TX_ICR +698					   offsetof(struct RGF_ICR, IMV));699		} else {700			icm_rx = wil_ioread32_and_clear(wil->csr +701					HOSTADDR(RGF_DMA_EP_RX_ICR) +702					offsetof(struct RGF_ICR, ICM));703			icr_rx = wil_ioread32_and_clear(wil->csr +704					HOSTADDR(RGF_DMA_EP_RX_ICR) +705					offsetof(struct RGF_ICR, ICR));706			imv_rx = wil_r(wil, RGF_DMA_EP_RX_ICR +707				   offsetof(struct RGF_ICR, IMV));708			icm_tx = wil_ioread32_and_clear(wil->csr +709					HOSTADDR(RGF_DMA_EP_TX_ICR) +710					offsetof(struct RGF_ICR, ICM));711			icr_tx = wil_ioread32_and_clear(wil->csr +712					HOSTADDR(RGF_DMA_EP_TX_ICR) +713					offsetof(struct RGF_ICR, ICR));714			imv_tx = wil_r(wil, RGF_DMA_EP_TX_ICR +715					   offsetof(struct RGF_ICR, IMV));716		}717		icm_misc = wil_ioread32_and_clear(wil->csr +718				HOSTADDR(RGF_DMA_EP_MISC_ICR) +719				offsetof(struct RGF_ICR, ICM));720		icr_misc = wil_ioread32_and_clear(wil->csr +721				HOSTADDR(RGF_DMA_EP_MISC_ICR) +722				offsetof(struct RGF_ICR, ICR));723		imv_misc = wil_r(wil, RGF_DMA_EP_MISC_ICR +724				     offsetof(struct RGF_ICR, IMV));725 726		/* HALP interrupt can be unmasked when misc interrupts are727		 * masked728		 */729		if (icr_misc & BIT_DMA_EP_MISC_ICR_HALP)730			return 0;731 732		wil_err(wil, "IRQ when it should be masked: pseudo 0x%08x\n"733				"Rx   icm:icr:imv 0x%08x 0x%08x 0x%08x\n"734				"Tx   icm:icr:imv 0x%08x 0x%08x 0x%08x\n"735				"Misc icm:icr:imv 0x%08x 0x%08x 0x%08x\n",736				pseudo_cause,737				icm_rx, icr_rx, imv_rx,738				icm_tx, icr_tx, imv_tx,739				icm_misc, icr_misc, imv_misc);740 741		return -EINVAL;742	}743 744	return 0;745}746 747static irqreturn_t wil6210_hardirq(int irq, void *cookie)748{749	irqreturn_t rc = IRQ_HANDLED;750	struct wil6210_priv *wil = cookie;751	u32 pseudo_cause = wil_r(wil, RGF_DMA_PSEUDO_CAUSE);752 753	/**754	 * pseudo_cause is Clear-On-Read, no need to ACK755	 */756	if (unlikely((pseudo_cause == 0) || ((pseudo_cause & 0xff) == 0xff)))757		return IRQ_NONE;758 759	/* IRQ mask debug */760	if (unlikely(wil6210_debug_irq_mask(wil, pseudo_cause)))761		return IRQ_NONE;762 763	trace_wil6210_irq_pseudo(pseudo_cause);764	wil_dbg_irq(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause);765 766	wil6210_mask_irq_pseudo(wil);767 768	/* Discover real IRQ cause769	 * There are 2 possible phases for every IRQ:770	 * - hard IRQ handler called right here771	 * - threaded handler called later772	 *773	 * Hard IRQ handler reads and clears ISR.774	 *775	 * If threaded handler requested, hard IRQ handler776	 * returns IRQ_WAKE_THREAD and saves ISR register value777	 * for the threaded handler use.778	 *779	 * voting for wake thread - need at least 1 vote780	 */781	if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_RX) &&782	    (wil->txrx_ops.irq_rx(irq, cookie) == IRQ_WAKE_THREAD))783		rc = IRQ_WAKE_THREAD;784 785	if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_TX) &&786	    (wil->txrx_ops.irq_tx(irq, cookie) == IRQ_WAKE_THREAD))787		rc = IRQ_WAKE_THREAD;788 789	if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_MISC) &&790	    (wil6210_irq_misc(irq, cookie) == IRQ_WAKE_THREAD))791		rc = IRQ_WAKE_THREAD;792 793	/* if thread is requested, it will unmask IRQ */794	if (rc != IRQ_WAKE_THREAD)795		wil6210_unmask_irq_pseudo(wil);796 797	return rc;798}799 800static int wil6210_request_3msi(struct wil6210_priv *wil, int irq)801{802	int rc;803 804	/* IRQ's are in the following order:805	 * - Tx806	 * - Rx807	 * - Misc808	 */809	rc = request_irq(irq, wil->txrx_ops.irq_tx, IRQF_SHARED,810			 WIL_NAME "_tx", wil);811	if (rc)812		return rc;813 814	rc = request_irq(irq + 1, wil->txrx_ops.irq_rx, IRQF_SHARED,815			 WIL_NAME "_rx", wil);816	if (rc)817		goto free0;818 819	rc = request_threaded_irq(irq + 2, wil6210_irq_misc,820				  wil6210_irq_misc_thread,821				  IRQF_SHARED, WIL_NAME "_misc", wil);822	if (rc)823		goto free1;824 825	return 0;826free1:827	free_irq(irq + 1, wil);828free0:829	free_irq(irq, wil);830 831	return rc;832}833 834/* can't use wil_ioread32_and_clear because ICC value is not set yet */835static inline void wil_clear32(void __iomem *addr)836{837	u32 x = readl(addr);838 839	writel(x, addr);840}841 842void wil6210_clear_irq(struct wil6210_priv *wil)843{844	wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) +845		    offsetof(struct RGF_ICR, ICR));846	wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) +847		    offsetof(struct RGF_ICR, ICR));848	wil_clear32(wil->csr + HOSTADDR(RGF_INT_GEN_RX_ICR) +849		    offsetof(struct RGF_ICR, ICR));850	wil_clear32(wil->csr + HOSTADDR(RGF_INT_GEN_TX_ICR) +851		    offsetof(struct RGF_ICR, ICR));852	wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) +853		    offsetof(struct RGF_ICR, ICR));854	wmb(); /* make sure write completed */855}856 857void wil6210_set_halp(struct wil6210_priv *wil)858{859	wil_dbg_irq(wil, "set_halp\n");860 861	wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, ICS),862	      BIT_DMA_EP_MISC_ICR_HALP);863}864 865void wil6210_clear_halp(struct wil6210_priv *wil)866{867	wil_dbg_irq(wil, "clear_halp\n");868 869	wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, ICR),870	      BIT_DMA_EP_MISC_ICR_HALP);871	wil6210_unmask_halp(wil);872}873 874int wil6210_init_irq(struct wil6210_priv *wil, int irq)875{876	int rc;877 878	wil_dbg_misc(wil, "init_irq: %s, n_msi=%d\n",879		     wil->n_msi ? "MSI" : "INTx", wil->n_msi);880 881	if (wil->use_enhanced_dma_hw) {882		wil->txrx_ops.irq_tx = wil6210_irq_tx_edma;883		wil->txrx_ops.irq_rx = wil6210_irq_rx_edma;884	} else {885		wil->txrx_ops.irq_tx = wil6210_irq_tx;886		wil->txrx_ops.irq_rx = wil6210_irq_rx;887	}888 889	if (wil->n_msi == 3)890		rc = wil6210_request_3msi(wil, irq);891	else892		rc = request_threaded_irq(irq, wil6210_hardirq,893					  wil6210_thread_irq,894					  wil->n_msi ? 0 : IRQF_SHARED,895					  WIL_NAME, wil);896	return rc;897}898 899void wil6210_fini_irq(struct wil6210_priv *wil, int irq)900{901	wil_dbg_misc(wil, "fini_irq:\n");902 903	wil_mask_irq(wil);904	free_irq(irq, wil);905	if (wil->n_msi == 3) {906		free_irq(irq + 1, wil);907		free_irq(irq + 2, wil);908	}909}910