drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c

Source file repositories/reference/linux-study-clean/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c

File Facts

System
Linux kernel
Corpus path
drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c
Extension
.c
Size
19040 bytes
Lines
683
Domain
Driver Families
Bucket
drivers/net
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

if (FIELD_GET(DRB_HDR_DTYP, le32_to_cpu(cur_drb->header)) == DES_DTYP_PD) {
			cur_drb_skb = drb_skb_base + cur_idx;
			if (!cur_drb_skb->is_msg)
				dma_unmap_single(dpmaif_ctrl->dev, cur_drb_skb->bus_addr,
						 cur_drb_skb->data_len, DMA_TO_DEVICE);

			if (!FIELD_GET(DRB_HDR_CONT, le32_to_cpu(cur_drb->header))) {
				if (!cur_drb_skb->skb) {
					dev_err(dpmaif_ctrl->dev,
						"txq%u: DRB check fail, invalid skb\n", q_num);
					continue;
				}

				dev_kfree_skb_any(cur_drb_skb->skb);
			}

			cur_drb_skb->skb = NULL;
		}

		spin_lock_irqsave(&txq->tx_lock, flags);
		cur_idx = t7xx_ring_buf_get_next_wr_idx(drb_cnt, cur_idx);
		txq->drb_release_rd_idx = cur_idx;
		spin_unlock_irqrestore(&txq->tx_lock, flags);

		if (atomic_inc_return(&txq->tx_budget) > txq->drb_size_cnt / 8)
			cb->state_notify(dpmaif_ctrl->t7xx_dev, DMPAIF_TXQ_STATE_IRQ, txq->index);
	}

	if (FIELD_GET(DRB_HDR_CONT, le32_to_cpu(cur_drb->header)))
		dev_err(dpmaif_ctrl->dev, "txq%u: DRB not marked as the last one\n", q_num);

	return i;
}

static int t7xx_dpmaif_tx_release(struct dpmaif_ctrl *dpmaif_ctrl,
				  unsigned int q_num, unsigned int budget)
{
	struct dpmaif_tx_queue *txq = &dpmaif_ctrl->txq[q_num];
	unsigned int rel_cnt, real_rel_cnt;

	/* Update read index from HW */
	t7xx_dpmaif_update_drb_rd_idx(dpmaif_ctrl, q_num);

	rel_cnt = t7xx_ring_buf_rd_wr_count(txq->drb_size_cnt, txq->drb_release_rd_idx,
					    txq->drb_rd_idx, DPMAIF_READ);

	real_rel_cnt = min_not_zero(budget, rel_cnt);
	if (real_rel_cnt)
		real_rel_cnt = t7xx_dpmaif_release_tx_buffer(dpmaif_ctrl, q_num, real_rel_cnt);

	return real_rel_cnt < rel_cnt ? -EAGAIN : 0;
}

static bool t7xx_dpmaif_drb_ring_not_empty(struct dpmaif_tx_queue *txq)
{
	return !!t7xx_dpmaif_update_drb_rd_idx(txq->dpmaif_ctrl, txq->index);
}

static void t7xx_dpmaif_tx_done(struct work_struct *work)
{
	struct dpmaif_tx_queue *txq = container_of(work, struct dpmaif_tx_queue, dpmaif_tx_work);
	struct dpmaif_ctrl *dpmaif_ctrl = txq->dpmaif_ctrl;
	struct dpmaif_hw_info *hw_info;
	int ret;

	ret = pm_runtime_resume_and_get(dpmaif_ctrl->dev);
	if (ret < 0 && ret != -EACCES)
		return;

	/* The device may be in low power state. Disable sleep if needed */
	t7xx_pci_disable_sleep(dpmaif_ctrl->t7xx_dev);
	if (t7xx_pci_sleep_disable_complete(dpmaif_ctrl->t7xx_dev)) {
		hw_info = &dpmaif_ctrl->hw_info;
		ret = t7xx_dpmaif_tx_release(dpmaif_ctrl, txq->index, txq->drb_size_cnt);
		if (ret == -EAGAIN ||
		    (t7xx_dpmaif_ul_clr_done(hw_info, txq->index) &&
		     t7xx_dpmaif_drb_ring_not_empty(txq))) {
			queue_work(dpmaif_ctrl->txq[txq->index].worker,
				   &dpmaif_ctrl->txq[txq->index].dpmaif_tx_work);
			/* Give the device time to enter the low power state */
			t7xx_dpmaif_clr_ip_busy_sts(hw_info);
		} else {
			t7xx_dpmaif_clr_ip_busy_sts(hw_info);
			t7xx_dpmaif_unmask_ulq_intr(hw_info, txq->index);
		}
	}

	t7xx_pci_enable_sleep(dpmaif_ctrl->t7xx_dev);
	pm_runtime_put_autosuspend(dpmaif_ctrl->dev);
}

Annotation

Implementation Notes