for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++) {
tx_ring = &rtwpci->tx_rings[i];
len = max_num_of_tx_queue(i);
ret = rtw_pci_init_tx_ring(rtwdev, tx_ring, tx_desc_size, len); if (ret) goto out;
}
rx_desc_size = chip->rx_buf_desc_sz;
for (j = 0; j < RTK_MAX_RX_QUEUE_NUM; j++) {
rx_ring = &rtwpci->rx_rings[j];
ret = rtw_pci_init_rx_ring(rtwdev, rx_ring, rx_desc_size,
RTK_MAX_RX_DESC_NUM); if (ret) goto out;
}
return 0;
out:
tx_alloced = i; for (i = 0; i < tx_alloced; i++) {
tx_ring = &rtwpci->tx_rings[i];
rtw_pci_free_tx_ring(rtwdev, tx_ring);
}
if (!rtw_chip_wcpu_8051(rtwdev)) {
len = rtwpci->tx_rings[RTW_TX_QUEUE_H2C].r.len;
dma = rtwpci->tx_rings[RTW_TX_QUEUE_H2C].r.dma;
rtwpci->tx_rings[RTW_TX_QUEUE_H2C].r.rp = 0;
rtwpci->tx_rings[RTW_TX_QUEUE_H2C].r.wp = 0;
rtw_write16(rtwdev, RTK_PCI_TXBD_NUM_H2CQ, len & TRX_BD_IDX_MASK);
rtw_write32(rtwdev, RTK_PCI_TXBD_DESA_H2CQ, dma);
}
len = rtwpci->tx_rings[RTW_TX_QUEUE_BK].r.len;
dma = rtwpci->tx_rings[RTW_TX_QUEUE_BK].r.dma;
rtwpci->tx_rings[RTW_TX_QUEUE_BK].r.rp = 0;
rtwpci->tx_rings[RTW_TX_QUEUE_BK].r.wp = 0;
rtw_write16(rtwdev, RTK_PCI_TXBD_NUM_BKQ, len & TRX_BD_IDX_MASK);
rtw_write32(rtwdev, RTK_PCI_TXBD_DESA_BKQ, dma);
len = rtwpci->tx_rings[RTW_TX_QUEUE_BE].r.len;
dma = rtwpci->tx_rings[RTW_TX_QUEUE_BE].r.dma;
rtwpci->tx_rings[RTW_TX_QUEUE_BE].r.rp = 0;
rtwpci->tx_rings[RTW_TX_QUEUE_BE].r.wp = 0;
rtw_write16(rtwdev, RTK_PCI_TXBD_NUM_BEQ, len & TRX_BD_IDX_MASK);
rtw_write32(rtwdev, RTK_PCI_TXBD_DESA_BEQ, dma);
len = rtwpci->tx_rings[RTW_TX_QUEUE_VO].r.len;
dma = rtwpci->tx_rings[RTW_TX_QUEUE_VO].r.dma;
rtwpci->tx_rings[RTW_TX_QUEUE_VO].r.rp = 0;
rtwpci->tx_rings[RTW_TX_QUEUE_VO].r.wp = 0;
rtw_write16(rtwdev, RTK_PCI_TXBD_NUM_VOQ, len & TRX_BD_IDX_MASK);
rtw_write32(rtwdev, RTK_PCI_TXBD_DESA_VOQ, dma);
len = rtwpci->tx_rings[RTW_TX_QUEUE_VI].r.len;
dma = rtwpci->tx_rings[RTW_TX_QUEUE_VI].r.dma;
rtwpci->tx_rings[RTW_TX_QUEUE_VI].r.rp = 0;
rtwpci->tx_rings[RTW_TX_QUEUE_VI].r.wp = 0;
rtw_write16(rtwdev, RTK_PCI_TXBD_NUM_VIQ, len & TRX_BD_IDX_MASK);
rtw_write32(rtwdev, RTK_PCI_TXBD_DESA_VIQ, dma);
len = rtwpci->tx_rings[RTW_TX_QUEUE_MGMT].r.len;
dma = rtwpci->tx_rings[RTW_TX_QUEUE_MGMT].r.dma;
rtwpci->tx_rings[RTW_TX_QUEUE_MGMT].r.rp = 0;
rtwpci->tx_rings[RTW_TX_QUEUE_MGMT].r.wp = 0;
rtw_write16(rtwdev, RTK_PCI_TXBD_NUM_MGMTQ, len & TRX_BD_IDX_MASK);
rtw_write32(rtwdev, RTK_PCI_TXBD_DESA_MGMTQ, dma);
len = rtwpci->tx_rings[RTW_TX_QUEUE_HI0].r.len;
dma = rtwpci->tx_rings[RTW_TX_QUEUE_HI0].r.dma;
rtwpci->tx_rings[RTW_TX_QUEUE_HI0].r.rp = 0;
rtwpci->tx_rings[RTW_TX_QUEUE_HI0].r.wp = 0;
rtw_write16(rtwdev, RTK_PCI_TXBD_NUM_HI0Q, len & TRX_BD_IDX_MASK);
rtw_write32(rtwdev, RTK_PCI_TXBD_DESA_HI0Q, dma);
len = rtwpci->rx_rings[RTW_RX_QUEUE_MPDU].r.len;
dma = rtwpci->rx_rings[RTW_RX_QUEUE_MPDU].r.dma;
rtwpci->rx_rings[RTW_RX_QUEUE_MPDU].r.rp = 0;
rtwpci->rx_rings[RTW_RX_QUEUE_MPDU].r.wp = 0;
rtw_write16(rtwdev, RTK_PCI_RXBD_NUM_MPDUQ, len & TRX_BD_IDX_MASK);
rtw_write32(rtwdev, RTK_PCI_RXBD_DESA_MPDUQ, dma);
/* reset read/write point */
rtw_write32(rtwdev, RTK_PCI_TXBD_RWPTR_CLR, 0xffffffff);
/* reset H2C Queue index in a single write */ if (rtw_chip_wcpu_3081(rtwdev))
rtw_write32_set(rtwdev, RTK_PCI_TXBD_H2CQ_CSR,
BIT_CLR_H2CQ_HOST_IDX | BIT_CLR_H2CQ_HW_IDX);
}
if (rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE)) goto enter_deep_ps;
lockdep_assert_held(&rtwpci->irq_lock);
/* Deep PS state is not allowed to TX-DMA */ for (queue = 0; queue < RTK_MAX_TX_QUEUE_NUM; queue++) { /* BCN queue is rsvd page, does not have DMA interrupt * H2C queue is managed by firmware
*/ if (queue == RTW_TX_QUEUE_BCN ||
queue == RTW_TX_QUEUE_H2C) continue;
tx_ring = &rtwpci->tx_rings[queue];
/* check if there is any skb DMAing */ if (skb_queue_len(&tx_ring->queue)) {
tx_empty = false; break;
}
}
if (!tx_empty) {
rtw_dbg(rtwdev, RTW_DBG_PS, "TX path not empty, cannot enter deep power save state\n"); return;
}
enter_deep_ps:
set_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags);
rtw_power_mode_change(rtwdev, true);
}
/* Because the time taked by the I/O in __pci_get_hw_tx_ring_rp is a * bit dynamic, it's hard to define a reasonable fixed total timeout to * use read_poll_timeout* helper. Instead, we can ensure a reasonable * polling times, so we just use for loop with udelay here.
*/ for (i = 0; i < 30; i++) {
cur_rp = __pci_get_hw_tx_ring_rp(rtwdev, pci_q); if (cur_rp == ring->r.wp) return;
udelay(1);
}
if (!drop)
rtw_dbg(rtwdev, RTW_DBG_UNEXP, "timed out to flush pci tx ring[%d]\n", pci_q);
}
/* If all of the hardware queues are requested to flush, * flush all of the pci queues.
*/ if (queues == BIT(rtwdev->hw->queues) - 1) {
pci_queues = BIT(RTK_MAX_TX_QUEUE_NUM) - 1;
} else { for (i = 0; i < rtwdev->hw->queues; i++) if (queues & BIT(i))
pci_queues |= BIT(rtw_tx_ac_to_hwq(i));
}
/* after this we got dma mapped, there is no way back */
buf_desc = get_tx_buffer_desc(ring, tx_buf_desc_sz);
memset(buf_desc, 0, tx_buf_desc_sz);
psb_len = (skb->len - 1) / 128 + 1; if (queue == RTW_TX_QUEUE_BCN)
psb_len |= 1 << RTK_PCI_TXBD_OWN_OFFSET;
/* enqueue to wait for tx report */ if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) {
rtw_tx_report_enqueue(rtwdev, skb, tx_data->sn); continue;
}
/* always ACK for others, then they won't be marked as drop */ if (info->flags & IEEE80211_TX_CTL_NO_ACK)
info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; else
info->flags |= IEEE80211_TX_STAT_ACK;
/* offset from rx_desc to payload */
pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz +
pkt_stat.shift;
/* allocate a new skb for this frame, * discard the frame if none available
*/
new_len = pkt_stat.pkt_len + pkt_offset; new = dev_alloc_skb(new_len); if (WARN_ONCE(!new, "rx routine starvation\n")) goto next_rp;
/* put the DMA data including rx_desc from phy to new skb */
skb_put_data(new, skb->data, new_len);
next_rp: /* new skb delivered to mac80211, re-enable original skb DMA */
rtw_pci_sync_rx_desc_device(rtwdev, dma, ring, cur_rp,
buf_desc_sz);
/* host read next element in ring */ if (++cur_rp >= ring->r.len)
cur_rp = 0;
}
ring->r.rp = cur_rp; /* 'rp', the last position we have read, is seen as previous posistion * of 'wp' that is used to calculate 'count' next time.
*/
ring->r.wp = cur_rp;
rtw_write16(rtwdev, RTK_PCI_RXBD_IDX_MPDUQ, ring->r.rp);
/* disable RTW PCI interrupt to avoid more interrupts before the end of * thread function * * disable HIMR here to also avoid new HISR flag being raised before * the HISRs have been Write-1-cleared for MSI. If not all of the HISRs * are cleared, the edge-triggered interrupt will not be generated when * a new HISR flag is set.
*/
rtw_pci_disable_interrupt(rtwdev, rtwpci);
if (irq_status[0] & IMR_MGNTDOK)
rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_MGMT); if (irq_status[0] & IMR_HIGHDOK)
rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_HI0); if (irq_status[0] & IMR_BEDOK)
rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_BE); if (irq_status[0] & IMR_BKDOK)
rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_BK); if (irq_status[0] & IMR_VODOK)
rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_VO); if (irq_status[0] & IMR_VIDOK)
rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_VI); if (irq_status[3] & IMR_H2CDOK)
rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_H2C); if (irq_status[0] & IMR_ROK) {
rtw_pci_rx_isr(rtwdev);
rx = true;
} if (unlikely(irq_status[0] & IMR_C2HCMD))
rtw_fw_c2h_cmd_isr(rtwdev);
/* all of the jobs for this interrupt have been done */ if (rtwpci->running)
rtw_pci_enable_interrupt(rtwdev, rtwpci, rx);
spin_unlock_bh(&rtwpci->irq_lock);
/* Like CLKREQ, ASPM is also implemented by two HW modules, and can * only be enabled when host supports it. * * And ASPM mechanism should be enabled when driver/firmware enters * power save mode, without having heavy traffic. Because we've * experienced some inter-operability issues that the link tends * to enter L1 state on the fly even when driver is having high * throughput. This is probably because the ASPM behavior slightly * varies from different SOC.
*/ if (!(rtwpci->link_ctrl & PCI_EXP_LNKCTL_ASPM_L1)) return;
/* RTL8822CE has enabled REFCLK auto calibration, it does not need * to add clock delay to cover the REFCLK timing gap.
*/ if (chip->id == RTW_CHIP_TYPE_8822C)
rtw_dbi_write8(rtwdev, RTK_PCIE_CLKDLY_CTRL, 0);
/* Though there is standard PCIE configuration space to set the * link control register, but by Realtek's design, driver should * check if host supports CLKREQ/ASPM to enable the HW module. * * These functions are implemented by two HW modules associated, * one is responsible to access PCIE configuration space to * follow the host settings, and another is in charge of doing * CLKREQ/ASPM mechanisms, it is default disabled. Because sometimes * the host does not support it, and due to some reasons or wrong * settings (ex. CLKREQ# not Bi-Direction), it could lead to device * loss if HW misbehaves on the link. * * Hence it's designed that driver should first check the PCIE * configuration space is sync'ed and enabled, then driver can turn * on the other module that is actually working on the mechanism.
*/
ret = pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &link_ctrl); if (ret) {
rtw_err(rtwdev, "failed to read PCI cap, ret=%d\n", ret); return;
}
if (link_ctrl & PCI_EXP_LNKCTL_CLKREQ_EN)
rtw_pci_clkreq_set(rtwdev, true);
for (i = 0; i < chip->intf_table->n_gen1_para; i++) {
para = &chip->intf_table->gen1_para[i]; if (!(para->cut_mask & cut)) continue; if (para->offset == 0xffff) break;
offset = para->offset;
value = para->value; if (para->ip_sel == RTW_IP_SEL_PHY)
rtw_mdio_write(rtwdev, offset, value, true); else
rtw_dbi_write8(rtwdev, offset, value);
}
for (i = 0; i < chip->intf_table->n_gen2_para; i++) {
para = &chip->intf_table->gen2_para[i]; if (!(para->cut_mask & cut)) continue; if (para->offset == 0xffff) break;
offset = para->offset;
value = para->value; if (para->ip_sel == RTW_IP_SEL_PHY)
rtw_mdio_write(rtwdev, offset, value, false); else
rtw_dbi_write8(rtwdev, offset, value);
}
rtw_pci_link_cfg(rtwdev);
/* Disable 8821ce completion timeout by default */ if (chip->id == RTW_CHIP_TYPE_8821C) {
ret = pcie_capability_set_word(pdev, PCI_EXP_DEVCTL2,
PCI_EXP_DEVCTL2_COMP_TMOUT_DIS); if (ret)
rtw_err(rtwdev, "failed to set PCI cap, ret = %d\n",
ret);
}
/* after this driver can access to hw registers */
ret = rtw_pci_io_mapping(rtwdev, pdev); if (ret) {
rtw_err(rtwdev, "failed to request pci io region\n"); goto err_out;
}
ret = rtw_pci_init(rtwdev); if (ret) {
rtw_err(rtwdev, "failed to allocate pci resources\n"); goto err_io_unmap;
}
if (rtwpci->rx_no_aspm)
rtw_pci_link_ps(rtwdev, false);
while (work_done < budget) {
u32 work_done_once;
work_done_once = rtw_pci_rx_napi(rtwdev, rtwpci, RTW_RX_QUEUE_MPDU,
budget - work_done); if (work_done_once == 0) break;
work_done += work_done_once;
} if (work_done < budget) {
napi_complete_done(napi, work_done);
spin_lock_bh(&rtwpci->irq_lock); if (rtwpci->running)
rtw_pci_enable_interrupt(rtwdev, rtwpci, false);
spin_unlock_bh(&rtwpci->irq_lock); /* When ISR happens during polling and before napi_complete * while no further data is received. Data on the dma_ring will * not be processed immediately. Check whether dma ring is * empty and perform napi_schedule accordingly.
*/ if (rtw_pci_get_hw_rx_ring_nr(rtwdev, rtwpci))
napi_schedule(napi);
} if (rtwpci->rx_no_aspm)
rtw_pci_link_ps(rtwdev, true);
¤ 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.0.25Bemerkung:
(vorverarbeitet am 2026-04-26)
¤
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.