/* *iwl_rxq_space-Returnnumberoffreeslotsavailableinqueue.
*/ staticint iwl_rxq_space(conststruct iwl_rxq *rxq)
{ /* Make sure rx queue size is a power of 2 */
WARN_ON(rxq->queue_size & (rxq->queue_size - 1));
spin_lock_bh(&rxq->lock); while (rxq->free_count) { /* Get next free Rx buffer, remove from free list */
rxb = list_first_entry(&rxq->rx_free, struct iwl_rx_mem_buffer,
list);
list_del(&rxb->list);
rxb->invalid = false; /* some low bits are expected to be unset (depending on hw) */
WARN_ON(rxb->page_dma & trans_pcie->supported_dma_mask); /* Point to Rx buffer via next RBD in circular buffer */
iwl_pcie_restock_bd(trans, rxq, rxb);
rxq->write = (rxq->write + 1) & (rxq->queue_size - 1);
rxq->free_count--;
}
spin_unlock_bh(&rxq->lock);
spin_lock_bh(&rxq->lock); while ((iwl_rxq_space(rxq) > 0) && (rxq->free_count)) {
__le32 *bd = (__le32 *)rxq->bd; /* The overwritten rxb must be a used one */
rxb = rxq->queue[rxq->write];
BUG_ON(rxb && rxb->page);
/* Get next free Rx buffer, remove from free list */
rxb = list_first_entry(&rxq->rx_free, struct iwl_rx_mem_buffer,
list);
list_del(&rxb->list);
rxb->invalid = false;
/* Point to Rx buffer via next RBD in circular buffer */
bd[rxq->write] = iwl_pcie_dma_addr2rbd_ptr(rxb->page_dma);
rxq->queue[rxq->write] = rxb;
rxq->write = (rxq->write + 1) & RX_QUEUE_MASK;
rxq->free_count--;
}
spin_unlock_bh(&rxq->lock);
/* If we've added more space for the firmware to place data, tell it.
* Increment device's write pointer in multiples of 8. */ if (rxq->write_actual != (rxq->write & ~0x7)) {
spin_lock_bh(&rxq->lock);
iwl_pcie_rxq_inc_wr_ptr(trans, rxq);
spin_unlock_bh(&rxq->lock);
}
}
/* If we were scheduled - there is at least one request */
spin_lock_bh(&rba->lock); /* swap out the rba->rbd_empty to a local list */
list_replace_init(&rba->rbd_empty, &local_empty);
spin_unlock_bh(&rba->lock);
while (pending) { int i;
LIST_HEAD(local_allocated);
gfp_t gfp_mask = GFP_KERNEL;
/* Do not post a warning if there are only a few requests */ if (pending < RX_PENDING_WATERMARK)
gfp_mask |= __GFP_NOWARN;
for (i = 0; i < RX_CLAIM_REQ_ALLOC;) { struct iwl_rx_mem_buffer *rxb; struct page *page;
/* List should never be empty - each reused RBD is *returnedtothelist,andinitialpoolcoversany *possiblegapbetweenthetimethepageisallocated *tothetimetheRBDisadded.
*/
BUG_ON(list_empty(&local_empty)); /* Get the first rxb from the rbd list */
rxb = list_first_entry(&local_empty, struct iwl_rx_mem_buffer, list);
BUG_ON(rxb->page);
/* Alloc a new receive buffer */
page = iwl_pcie_rx_alloc_page(trans, &rxb->offset,
gfp_mask); if (!page) continue;
rxb->page = page;
/* Get physical address of the RB */
rxb->page_dma = dma_map_page(trans->dev, page,
rxb->offset,
trans_pcie->rx_buf_bytes,
DMA_FROM_DEVICE); if (dma_mapping_error(trans->dev, rxb->page_dma)) {
rxb->page = NULL;
__free_pages(page, trans_pcie->rx_page_order); continue;
}
/* move the allocated entry to the out list */
list_move(&rxb->list, &local_allocated);
i++;
}
atomic_dec(&rba->req_pending);
pending--;
if (!pending) {
pending = atomic_read(&rba->req_pending); if (pending)
IWL_DEBUG_TPT(trans, "Got more pending allocation requests = %d\n",
pending);
}
spin_lock_bh(&rba->lock); /* add the allocated rbds to the allocator allocated list */
list_splice_tail(&local_allocated, &rba->rbd_allocated); /* get more empty RBDs for current pending requests */
list_splice_tail_init(&rba->rbd_empty, &local_empty);
spin_unlock_bh(&rba->lock);
atomic_inc(&rba->req_ready);
}
spin_lock_bh(&rba->lock); /* return unused rbds to the allocator empty list */
list_splice_tail(&local_empty, &rba->rbd_empty);
spin_unlock_bh(&rba->lock);
spin_lock(&rba->lock); for (i = 0; i < RX_CLAIM_REQ_ALLOC; i++) { /* Get next free Rx buffer, remove it from free list */ struct iwl_rx_mem_buffer *rxb =
list_first_entry(&rba->rbd_allocated, struct iwl_rx_mem_buffer, list);
/* Set interrupt coalescing timer to default (2048 usecs) */
iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF);
/* W/A for interrupt coalescing bug in 7260 and 3160 */ if (trans->cfg->host_interrupt_operation_mode)
iwl_set_bit(trans, CSR_INT_COALESCING, IWL_HOST_INT_OPER_MODE);
}
for (i = 0; i < trans->info.num_rxqs; i++) { /* Tell device where to find RBD free table in DRAM */
iwl_write_prph64_no_grab(trans,
RFH_Q_FRBDCB_BA_LSB(i),
trans_pcie->rxq[i].bd_dma); /* Tell device where to find RBD used table in DRAM */
iwl_write_prph64_no_grab(trans,
RFH_Q_URBDCB_BA_LSB(i),
trans_pcie->rxq[i].used_bd_dma); /* Tell device where in DRAM to update its Rx status */
iwl_write_prph64_no_grab(trans,
RFH_Q_URBD_STTS_WPTR_LSB(i),
trans_pcie->rxq[i].rb_stts_dma); /* Reset device indice tables */
iwl_write_prph_no_grab(trans, RFH_Q_FRBDCB_WIDX(i), 0);
iwl_write_prph_no_grab(trans, RFH_Q_FRBDCB_RIDX(i), 0);
iwl_write_prph_no_grab(trans, RFH_Q_URBDCB_WIDX(i), 0);
int iwl_pcie_rx_init(struct iwl_trans *trans)
{ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret = _iwl_pcie_rx_init(trans);
if (ret) return ret;
if (trans->mac_cfg->mq_rx_supported)
iwl_pcie_rx_mq_hw_init(trans); else
iwl_pcie_rx_hw_init(trans, trans_pcie->rxq);
/* Move the RBD to the used list, will be moved to allocator in batches
* before claiming or posting a request*/
list_add_tail(&rxb->list, &rxq->rx_used);
if (unlikely(emergency)) return;
/* Count the allocator owned RBDs */
rxq->used_count++;
/* If we have RX_POST_REQ_ALLOC new released rx buffers - *issuearequestforallocator.ModuloRX_CLAIM_REQ_ALLOCis *usedforthecasewefailedtoclaimRX_CLAIM_REQ_ALLOC, *afterbutwestillneedtopostanotherrequest.
*/ if ((rxq->used_count % RX_CLAIM_REQ_ALLOC) == RX_POST_REQ_ALLOC) { /* Move the 2 RBDs to the allocator ownership.
Allocator has another 6 from pool for the request completion*/
iwl_pcie_rx_move_to_allocator(rxq, rba);
len = iwl_rx_packet_len(pkt);
len += sizeof(u32); /* account for status word */
offset += ALIGN(len, FH_RSCSR_FRAME_ALIGN);
/* check that what the device tells us made sense */ if (len < sizeof(*pkt) || offset > max_len) break;
maybe_trace_iwlwifi_dev_rx(trans, pkt, len);
/* Reclaim a command buffer only if this packet is a response *toa(driver-originated)command. *Ifthepacket(e.g.Rxframe)originatedfromuCode, *thereisnocommandbuffertoreclaim. *UcodeshouldsetSEQ_RX_FRAMEbitifucode-originated,
* but apparently a few don't get set; catch them here. */
reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME); if (reclaim && !pkt->hdr.group_id) { int i;
for (i = 0; i < trans->conf.n_no_reclaim_cmds; i++) { if (trans->conf.no_reclaim_cmds[i] ==
pkt->hdr.cmd) {
reclaim = false; break;
}
}
}
if (reclaim && txq) {
u16 sequence = le16_to_cpu(pkt->hdr.sequence); int index = SEQ_TO_INDEX(sequence); int cmd_index = iwl_txq_get_cmd_index(txq, index);
/* Invoke any callbacks, transfer the buffer to caller, *andfireoffthe(possibly)blocking *iwl_trans_send_cmd()
* as we reclaim the driver command queue */ if (!rxcb._page_stolen)
iwl_pcie_hcmd_complete(trans, &rxcb); else
IWL_WARN(trans, "Claim null rxb?\n");
}
page_stolen |= rxcb._page_stolen; if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) break;
}
/* page was stolen from us -- free our reference */ if (page_stolen) {
__free_pages(rxb->page, trans_pcie->rx_page_order);
rxb->page = NULL;
}
/* Reuse the page if possible. For notification packets and *SKBsthatfailtoRxcorrectly,addthembackintothe
* rx_free list for reuse later. */ if (rxb->page != NULL) {
rxb->page_dma =
dma_map_page(trans->dev, rxb->page, rxb->offset,
trans_pcie->rx_buf_bytes,
DMA_FROM_DEVICE); if (dma_mapping_error(trans->dev, rxb->page_dma)) { /* *freethepage(s)aswelltonotbreak *theinvariantthattheitemsontheused *listhavenopage(s)
*/
__free_pages(rxb->page, trans_pcie->rx_page_order);
rxb->page = NULL;
iwl_pcie_rx_reuse_rbd(trans, rxb, rxq, emergency);
} else {
list_add_tail(&rxb->list, &rxq->rx_free);
rxq->free_count++;
}
} else
iwl_pcie_rx_reuse_rbd(trans, rxb, rxq, emergency);
}
if (WARN_ON_ONCE(!trans_pcie->rxq || !trans_pcie->rxq[queue].bd)) return budget;
rxq = &trans_pcie->rxq[queue];
restart:
spin_lock(&rxq->lock); /* uCode's read index (stored in shared DRAM) indicates the last Rx
* buffer that the driver may process (last buffer filled by ucode). */
r = iwl_get_closed_rb_stts(trans, rxq);
i = rxq->read;
/* W/A for WiFi/WiMAX coex and WiMAX own the RF */ if (trans->cfg->internal_wimax_coex &&
!trans->mac_cfg->base->apmg_not_supported &&
(!(iwl_read_prph(trans, APMG_CLK_CTRL_REG) &
APMS_CLK_VAL_MRB_FUNC_MODE) ||
(iwl_read_prph(trans, APMG_PS_CTRL_REG) &
APMG_PS_CTRL_VAL_RESET_REQ))) {
clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
iwl_op_mode_wimax_active(trans->op_mode);
wake_up(&trans_pcie->wait_command_queue); return;
}
for (i = 0; i < trans->mac_cfg->base->num_of_queues; i++) { if (!trans_pcie->txqs.txq[i]) continue;
timer_delete(&trans_pcie->txqs.txq[i]->stuck_timer);
}
if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_SC) {
u32 val = iwl_read32(trans, CSR_IPC_STATE);
if (val & CSR_IPC_STATE_TOP_RESET_REQ) {
IWL_ERR(trans, "FW requested TOP reset for FSEQ\n");
trans->do_top_reset = 1;
}
}
/* The STATUS_FW_ERROR bit is set in this function. This must happen
* before we wake up the command caller, to ensure a proper cleanup. */
iwl_trans_fw_error(trans, IWL_ERR_TYPE_IRQ);
/* Discover which interrupts are active/pending */
inta = iwl_read32(trans, CSR_INT);
/* the thread will service interrupts and re-enable them */ return inta;
}
/* a device (PCI-E) page is 4096 bytes long */ #define ICT_SHIFT 12 #define ICT_SIZE (1 << ICT_SHIFT) #define ICT_COUNT (ICT_SIZE / sizeof(u32))
/* interrupt handler using ict table, with this interrupt driver will *stopusingINTAregistertogetdevice'sinterrupt,readingthisregister *isexpensive,devicewillwriteinterruptsinICTdramtable,increment *indexthenwillfireinterrupttodriver,driverwillORallICTtable *entriesfromcurrentindexuptotableentrywith0value.theresultis *theinterruptweneedtoservice,driverwillsettheentriesbackto0and *setindex.
*/ static u32 iwl_pcie_int_cause_ict(struct iwl_trans *trans)
{ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
u32 inta;
u32 val = 0;
u32 read;
trace_iwlwifi_dev_irq(trans->dev);
/* Ignore interrupt if there's nothing in NIC to service. *ThismaybeduetoIRQsharedwithanotherdevice,
* or due to sporadic interrupts thrown from our NIC. */
read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]);
trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, read); if (!read) return0;
/* *Collectallentriesuptothefirst0,startingfromict_index; *notewealreadyreadatict_index.
*/ do {
val |= read;
IWL_DEBUG_ISR(trans, "ICT index %d value 0x%08X\n",
trans_pcie->ict_index, read);
trans_pcie->ict_tbl[trans_pcie->ict_index] = 0;
trans_pcie->ict_index =
((trans_pcie->ict_index + 1) & (ICT_COUNT - 1));
read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]);
trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index,
read);
} while (read);
/* We should not get this value, just ignore it. */ if (val == 0xffffffff)
val = 0;
/* *thisisaw/aforah/wbug.theh/wbugmaycausetheRxbit *(bit15beforeshiftingitto31)toclearwhenusinginterrupt *coalescing.fortunately,bits18and19staysetwhenthishappens *soweusethemtodecideontherealstateoftheRxbit. *Inorderwords,bit15issetifbit18orbit19areset.
*/ if (val & 0xC0000)
val |= 0x8000;
IWL_WARN(trans, "RF_KILL bit toggled to %s.\n",
hw_rfkill ? "disable radio" : "enable radio");
isr_stats->rfkill++;
if (prev != report)
iwl_trans_pcie_rf_kill(trans, report, from_irq);
mutex_unlock(&trans_pcie->mutex);
if (hw_rfkill) { if (test_and_clear_bit(STATUS_SYNC_HCMD_ACTIVE,
&trans->status))
IWL_DEBUG_RF_KILL(trans, "Rfkill while SYNC HCMD in flight\n");
wake_up(&trans_pcie->wait_command_queue);
} else {
clear_bit(STATUS_RFKILL_HW, &trans->status); if (trans_pcie->opmode_down)
clear_bit(STATUS_RFKILL_OPMODE, &trans->status);
}
}
/* dram interrupt table not set yet, *uselegacyinterrupt.
*/ if (likely(trans_pcie->use_ict))
inta = iwl_pcie_int_cause_ict(trans); else
inta = iwl_pcie_int_cause_non_ict(trans);
/* Ack/clear/reset pending uCode interrupts. *Note:SomebitsinCSR_INTare"OR"ofbitsinCSR_FH_INT_STATUS,
*/ /* There is a hardware bug in the interrupt mask function that some *interrupts(i.e.CSR_INT_BIT_SCD)canstillbegeneratedevenif *theyaredisabledintheCSR_INT_MASKregister.Furthermorethe *ICTinterrupthandlingmechanismhasanotherbugthatmightcause *theseunmaskedinterruptsfailtobedetected.Weworkaroundthe *hardwarebugsherebyACKingallthepossibleinterruptssothat *interruptcoalescingcanstillbeachieved.
*/
iwl_write32(trans, CSR_INT, inta | ~trans_pcie->inta_mask);
if (iwl_have_debug_level(IWL_DL_ISR))
IWL_DEBUG_ISR(trans, "inta 0x%08x, enabled 0x%08x\n",
inta, iwl_read32(trans, CSR_INT_MASK));
spin_unlock_bh(&trans_pcie->irq_lock);
/* Now service all interrupt bits discovered above. */ if (inta & CSR_INT_BIT_HW_ERR) {
IWL_ERR(trans, "Hardware error detected. Restarting.\n");
/* Tell the device to stop sending interrupts */
iwl_disable_interrupts(trans);
/* NIC fires this, but we don't use it, redundant with WAKEUP */ if (inta & CSR_INT_BIT_SCD) {
IWL_DEBUG_ISR(trans, "Scheduler finished to transmit the frame/frames.\n");
isr_stats->sch++;
}
/* Alive notification via Rx interrupt will do the real work */ if (inta & CSR_INT_BIT_ALIVE) {
IWL_DEBUG_ISR(trans, "Alive interrupt\n");
isr_stats->alive++; if (trans->mac_cfg->gen2) { /* *Wecanrestock,sincefirmwareconfigured *theRFH
*/
iwl_pcie_rxmq_restock(trans, trans_pcie->rxq);
}
handled |= CSR_INT_BIT_ALIVE;
}
if (inta & CSR_INT_BIT_RESET_DONE) {
iwl_trans_pcie_handle_reset_interrupt(trans);
handled |= CSR_INT_BIT_RESET_DONE;
}
/* Safely ignore these bits for debug checks below */
inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE);
/* This "Tx" DMA channel is used only for loading uCode */ if (inta & CSR_INT_BIT_FH_TX) {
iwl_write32(trans, CSR_FH_INT_STATUS, CSR_FH_INT_TX_MASK);
IWL_DEBUG_ISR(trans, "uCode load interrupt\n");
isr_stats->tx++;
handled |= CSR_INT_BIT_FH_TX; /* Wake up uCode load routine, now that load is complete */
trans_pcie->ucode_write_complete = true;
wake_up(&trans_pcie->ucode_write_waitq); /* Wake up IMR write routine, now that write to SRAM is complete */ if (trans_pcie->imr_status == IMR_D2S_REQUESTED) {
trans_pcie->imr_status = IMR_D2S_COMPLETED;
wake_up(&trans_pcie->ucode_write_waitq);
}
}
if (inta & ~(trans_pcie->inta_mask)) {
IWL_WARN(trans, "Disabled INTA bits 0x%08x were pending\n",
inta & ~trans_pcie->inta_mask);
}
if (!polling) {
spin_lock_bh(&trans_pcie->irq_lock); /* only Re-enable all interrupt if disabled by irq */ if (test_bit(STATUS_INT_ENABLED, &trans->status))
_iwl_enable_interrupts(trans); /* we are loading the firmware, enable FH_TX interrupt only */ elseif (handled & CSR_INT_BIT_FH_TX)
iwl_enable_fw_load_int(trans); /* Re-enable RF_KILL if it occurred */ elseif (handled & CSR_INT_BIT_RF_KILL)
iwl_enable_rfkill_int(trans); /* Re-enable the ALIVE / Rx interrupt if it occurred */ elseif (handled & (CSR_INT_BIT_ALIVE | CSR_INT_BIT_FH_RX))
iwl_enable_fw_load_int_ctx_info(trans, false);
spin_unlock_bh(&trans_pcie->irq_lock);
}
trans_pcie->ict_tbl =
dma_alloc_coherent(trans->dev, ICT_SIZE,
&trans_pcie->ict_tbl_dma, GFP_KERNEL); if (!trans_pcie->ict_tbl) return -ENOMEM;
/* just an API sanity check ... it is guaranteed to be aligned */ if (WARN_ON(trans_pcie->ict_tbl_dma & (ICT_SIZE - 1))) {
iwl_pcie_free_ict(trans); return -EINVAL;
}
return0;
}
/* Device is going up inform it about using ICT interrupt table, *alsoweneedtotellthedrivertostartusingICTinterrupt.
*/ void iwl_pcie_reset_ict(struct iwl_trans *trans)
{ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
u32 val;
if ((trans_pcie->shared_vec_mask & IWL_SHARED_IRQ_NON_RX) &&
inta_fh & MSIX_FH_INT_CAUSES_Q0) {
local_bh_disable(); if (napi_schedule_prep(&trans_pcie->rxq[0].napi)) {
polling = true;
__napi_schedule(&trans_pcie->rxq[0].napi);
}
local_bh_enable();
}
if ((trans_pcie->shared_vec_mask & IWL_SHARED_IRQ_FIRST_RSS) &&
inta_fh & MSIX_FH_INT_CAUSES_Q1) {
local_bh_disable(); if (napi_schedule_prep(&trans_pcie->rxq[1].napi)) {
polling = true;
__napi_schedule(&trans_pcie->rxq[1].napi);
}
local_bh_enable();
}
/* This "Tx" DMA channel is used only for loading uCode */ if (inta_fh & MSIX_FH_INT_CAUSES_D2S_CH0_NUM &&
trans_pcie->imr_status == IMR_D2S_REQUESTED) {
IWL_DEBUG_ISR(trans, "IMR Complete interrupt\n");
isr_stats->tx++;
/* Wake up IMR routine once write to SRAM is complete */ if (trans_pcie->imr_status == IMR_D2S_REQUESTED) {
trans_pcie->imr_status = IMR_D2S_COMPLETED;
wake_up(&trans_pcie->ucode_write_waitq);
}
} elseif (inta_fh & MSIX_FH_INT_CAUSES_D2S_CH0_NUM) {
IWL_DEBUG_ISR(trans, "uCode load interrupt\n");
isr_stats->tx++; /* *WakeupuCodeloadroutine, *nowthatloadiscomplete
*/
trans_pcie->ucode_write_complete = true;
wake_up(&trans_pcie->ucode_write_waitq);
/* Wake up IMR routine once write to SRAM is complete */ if (trans_pcie->imr_status == IMR_D2S_REQUESTED) {
trans_pcie->imr_status = IMR_D2S_COMPLETED;
wake_up(&trans_pcie->ucode_write_waitq);
}
}
/* Alive notification via Rx interrupt will do the real work */ if (inta_hw & MSIX_HW_INT_CAUSES_REG_ALIVE) {
IWL_DEBUG_ISR(trans, "Alive interrupt\n");
isr_stats->alive++; if (trans->mac_cfg->gen2) { /* We can restock, since firmware configured the RFH */
iwl_pcie_rxmq_restock(trans, trans_pcie->rxq);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.