Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a13ff6e4 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: emac: Handle the tx timestamp in workQ"

parents e0f5ca7a a7651a4c
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -591,6 +591,28 @@ struct emac_tpd_ring {
	u32 last_produce_idx;
};

#define EMAC_HWTXTSTAMP_FIFO_DEPTH          8
#define EMAC_TX_POLL_HWTXTSTAMP_THRESHOLD   EMAC_HWTXTSTAMP_FIFO_DEPTH

/* HW tx timestamp */
struct emac_hwtxtstamp {
	u32 ts_idx;
	u32 sec;
	u32 ns;
};

struct emac_tx_tstamp_stats {
	u32 tx;
	u32 rx;
	u32 deliver;
	u32 drop;
	u32 lost;
	u32 timeout;
	u32 sched;
	u32 poll;
	u32 tx_poll;
};

/* tx queue */
struct emac_tx_queue {
	struct device         *dev;     /* device for dma mapping */
@@ -639,6 +661,13 @@ struct emac_adapter {
	struct emac_hw hw;
	struct emac_hw_stats hw_stats;

	/* tx timestamping queue */
	struct sk_buff_head         hwtxtstamp_pending_queue;
	struct sk_buff_head         hwtxtstamp_ready_queue;
	struct work_struct          hwtxtstamp_task;
	spinlock_t                  hwtxtstamp_lock; /* lock for hwtxtstamp */
	struct emac_tx_tstamp_stats hwtxtstamp_stats;

	struct work_struct emac_task;
	struct timer_list  emac_timer;
	unsigned long	link_jiffies;
+17 −0
Original line number Diff line number Diff line
@@ -1496,3 +1496,20 @@ void emac_hw_set_mac_addr(struct emac_hw *hw, u8 *addr)
	emac_reg_w32(hw, EMAC, EMAC_MAC_STA_ADDR1, sta);
	wmb();
}

/* Read one entry from the HW tx timestamp FIFO */
bool emac_hw_read_tx_tstamp(struct emac_hw *hw, struct emac_hwtxtstamp *ts)
{
	u32 ts_idx;

	ts_idx = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_TX_TS_INX);

	if (ts_idx & EMAC_WRAPPER_TX_TS_EMPTY)
		return false;

	ts->ns = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_TX_TS_LO);
	ts->sec = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_TX_TS_HI);
	ts->ts_idx = ts_idx & EMAC_WRAPPER_TX_TS_INX_BMSK;

	return true;
}
+4 −0
Original line number Diff line number Diff line
@@ -72,6 +72,10 @@ extern void emac_hw_stop_mac(struct emac_hw *hw);

extern void emac_hw_set_mac_addr(struct emac_hw *hw, u8 *addr);

/* TX Timestamp */
extern bool emac_hw_read_tx_tstamp(struct emac_hw *hw,
				   struct emac_hwtxtstamp *ts);

#define IMR_NORMAL_MASK         (\
		ISR_ERROR       |\
		ISR_GPHY_LINK   |\
+155 −29
Original line number Diff line number Diff line
@@ -52,6 +52,20 @@ const char emac_drv_version[] = DRV_VERSION;

#define EMAC_RSS_IDT_SIZE     256

#define EMAC_SKB_CB(skb) ((struct emac_skb_cb *)(skb)->cb)

struct emac_skb_cb {
	u32           tpd_idx;
	unsigned long jiffies;
};

#define EMAC_HWTXTSTAMP_CB(skb) ((struct emac_hwtxtstamp_cb *)(skb)->cb)

struct emac_hwtxtstamp_cb {
	u32 sec;
	u32 ns;
};

static int msm_emac_msglvl = -1;
module_param_named(msglvl, msm_emac_msglvl, int, S_IRUGO | S_IWUSR | S_IWGRP);

@@ -444,42 +458,122 @@ static void emac_clean_rfdesc(struct emac_rx_queue *rxque,
	rxque->rfd.process_idx = consume_idx;
}

static void emac_read_tx_tstamp_fifo(struct emac_hw *hw,
				     struct emac_tx_queue *txque)
static inline bool emac_skb_cb_expired(struct sk_buff *skb)
{
	struct emac_buffer *tpbuf;
	u32 ts_idx = 0;
	u32 sec, ns;
	if (time_is_after_jiffies(EMAC_SKB_CB(skb)->jiffies +
				  msecs_to_jiffies(100)))
		return false;
	return true;
}

	while (1) {
		ts_idx = emac_reg_r32(hw, EMAC_CSR,
				      EMAC_EMAC_WRAPPER_TX_TS_INX);
		if (ts_idx & EMAC_WRAPPER_TX_TS_EMPTY)
/* proper lock must be acquired before polling */
static void emac_poll_hwtxtstamp(struct emac_adapter *adpt)
{
	struct sk_buff_head *pending_q = &adpt->hwtxtstamp_pending_queue;
	struct sk_buff_head *q = &adpt->hwtxtstamp_ready_queue;
	struct sk_buff *skb, *skb_tmp;
	struct emac_hwtxtstamp hwtxtstamp;

	while (emac_hw_read_tx_tstamp(&adpt->hw, &hwtxtstamp)) {
		bool found = false;

		adpt->hwtxtstamp_stats.rx++;

		skb_queue_walk_safe(pending_q, skb, skb_tmp) {
			if (EMAC_SKB_CB(skb)->tpd_idx == hwtxtstamp.ts_idx) {
				struct sk_buff *pskb;

				EMAC_HWTXTSTAMP_CB(skb)->sec = hwtxtstamp.sec;
				EMAC_HWTXTSTAMP_CB(skb)->ns = hwtxtstamp.ns;
				/* the tx timestamps for all the pending
				   packets before this one are lost
				 */
				while ((pskb = __skb_dequeue(pending_q))
				       != skb) {
					EMAC_HWTXTSTAMP_CB(pskb)->sec = 0;
					EMAC_HWTXTSTAMP_CB(pskb)->ns = 0;
					__skb_queue_tail(q, pskb);
					adpt->hwtxtstamp_stats.lost++;
				}
				__skb_queue_tail(q, skb);
				found = true;
				break;
			}
		}

		ns = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_TX_TS_LO);
		sec = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_TX_TS_HI);
		if (!found) {
			emac_dbg(adpt, tx_done,
				 "no entry(tpd=%d) found, drop tx timestamp\n",
				 hwtxtstamp.ts_idx);
			adpt->hwtxtstamp_stats.drop++;
		}
	}

		ts_idx &= EMAC_WRAPPER_TX_TS_INX_BMSK;
		if ((ts_idx < txque->tpd.consume_idx) ||
		    (ts_idx > txque->tpd.last_produce_idx)) {
			emac_warn(hw->adpt, tx_done,
				  "zombie timestamp desc idx %d\n", ts_idx);
			continue;
	skb_queue_walk_safe(pending_q, skb, skb_tmp) {
		/* No packet after this one expires */
		if (!emac_skb_cb_expired(skb))
			break;
		adpt->hwtxtstamp_stats.timeout++;
		emac_dbg(adpt, tx_done,
			 "tx timestamp timeout: tpd_idx=%d\n",
			 EMAC_SKB_CB(skb)->tpd_idx);

		__skb_unlink(skb, pending_q);
		EMAC_HWTXTSTAMP_CB(skb)->sec = 0;
		EMAC_HWTXTSTAMP_CB(skb)->ns = 0;
		__skb_queue_tail(q, skb);
	}
}

		tpbuf  = GET_TPD_BUFFER(txque, ts_idx);
static void emac_schedule_hwtxtstamp_task(struct emac_adapter *adpt)
{
	if (CHK_ADPT_FLAG(STATE_DOWN))
		return;

	if (schedule_work(&adpt->hwtxtstamp_task))
		adpt->hwtxtstamp_stats.sched++;
}

static void emac_hwtxtstamp_task_routine(struct work_struct *work)
{
	struct emac_adapter *adpt = container_of(work, struct emac_adapter,
						 hwtxtstamp_task);
	struct sk_buff *skb;
	struct sk_buff_head q;
	unsigned long flags;

	adpt->hwtxtstamp_stats.poll++;

	__skb_queue_head_init(&q);

	while (1) {
		spin_lock_irqsave(&adpt->hwtxtstamp_lock, flags);
		if (adpt->hwtxtstamp_pending_queue.qlen)
			emac_poll_hwtxtstamp(adpt);
		skb_queue_splice_tail_init(&adpt->hwtxtstamp_ready_queue, &q);
		spin_unlock_irqrestore(&adpt->hwtxtstamp_lock, flags);

		if (tpbuf->skb &&
		    (skb_shinfo(tpbuf->skb)->tx_flags & SKBTX_HW_TSTAMP)) {
		if (!q.qlen)
			break;

		while ((skb = __skb_dequeue(&q))) {
			struct emac_hwtxtstamp_cb *cb = EMAC_HWTXTSTAMP_CB(skb);

			if (cb->sec || cb->ns) {
				struct skb_shared_hwtstamps ts;

			ts.hwtstamp = ktime_set(sec, ns);
			ts.syststamp = ktime_add_ns(ts.hwtstamp,
						    hw->tstamp_tx_offset);
			skb_tstamp_tx(tpbuf->skb, &ts);
				ts.hwtstamp = ktime_set(cb->sec, cb->ns);
				ts.syststamp = ktime_add_ns(
					ts.hwtstamp, adpt->hw.tstamp_tx_offset);
				skb_tstamp_tx(skb, &ts);
				adpt->hwtxtstamp_stats.deliver++;
			}
			dev_kfree_skb_any(skb);
		}
	}

	if (adpt->hwtxtstamp_pending_queue.qlen)
		emac_schedule_hwtxtstamp_task(adpt);
}

/* Process receive event */
@@ -600,7 +694,6 @@ static void emac_handle_tx(struct emac_adapter *adpt,
		 txque->que_idx, hw_consume_idx);

	while (txque->tpd.consume_idx != hw_consume_idx) {
		emac_read_tx_tstamp_fifo(hw, txque);
		tpbuf = GET_TPD_BUFFER(txque, txque->tpd.consume_idx);
		if (tpbuf->dma) {
			dma_unmap_single(txque->dev, tpbuf->dma, tpbuf->length,
@@ -822,8 +915,29 @@ static void emac_tx_map(struct emac_adapter *adpt,

	if (CHK_HW_FLAG(TS_TX_EN) &&
	    (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
		struct sk_buff *skb_ts = skb_clone(skb, GFP_ATOMIC);

		if (likely(skb_ts)) {
			unsigned long flags;

			emac_set_tpdesc_tstamp_sav(txque);
		skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
			skb_ts->sk = skb->sk;
			EMAC_SKB_CB(skb_ts)->tpd_idx =
				txque->tpd.last_produce_idx;
			EMAC_SKB_CB(skb_ts)->jiffies = get_jiffies_64();
			skb_shinfo(skb_ts)->tx_flags |= SKBTX_IN_PROGRESS;
			spin_lock_irqsave(&adpt->hwtxtstamp_lock, flags);
			if (adpt->hwtxtstamp_pending_queue.qlen >=
			    EMAC_TX_POLL_HWTXTSTAMP_THRESHOLD) {
				emac_poll_hwtxtstamp(adpt);
				adpt->hwtxtstamp_stats.tx_poll++;
			}
			__skb_queue_tail(&adpt->hwtxtstamp_pending_queue,
					 skb_ts);
			spin_unlock_irqrestore(&adpt->hwtxtstamp_lock, flags);
			adpt->hwtxtstamp_stats.tx++;
			emac_schedule_hwtxtstamp_task(adpt);
		}
	}

	/* The last buffer info contain the skb address,
@@ -1559,6 +1673,7 @@ void emac_down(struct emac_adapter *adpt, u32 ctrl)
{
	struct net_device *netdev = adpt->netdev;
	struct emac_hw *hw = &adpt->hw;
	unsigned long flags;
	int i;

	SET_ADPT_FLAG(STATE_DOWN);
@@ -1581,6 +1696,12 @@ void emac_down(struct emac_adapter *adpt, u32 ctrl)
	CLI_ADPT_FLAG(TASK_CHK_SGMII_REQ);
	del_timer_sync(&adpt->emac_timer);

	cancel_work_sync(&adpt->hwtxtstamp_task);
	spin_lock_irqsave(&adpt->hwtxtstamp_lock, flags);
	__skb_queue_purge(&adpt->hwtxtstamp_pending_queue);
	__skb_queue_purge(&adpt->hwtxtstamp_ready_queue);
	spin_unlock_irqrestore(&adpt->hwtxtstamp_lock, flags);

	if (ctrl & EMAC_HW_CTRL_RESET_MAC)
		emac_hw_reset_mac(hw);

@@ -2693,6 +2814,11 @@ static int emac_probe(struct platform_device *pdev)
		netif_napi_add(netdev, &adpt->rx_queue[i].napi,
			       emac_napi_rtx, 64);

	spin_lock_init(&adpt->hwtxtstamp_lock);
	skb_queue_head_init(&adpt->hwtxtstamp_pending_queue);
	skb_queue_head_init(&adpt->hwtxtstamp_ready_queue);
	INIT_WORK(&adpt->hwtxtstamp_task, emac_hwtxtstamp_task_routine);

	SET_HW_FLAG(VLANSTRIP_EN);
	SET_ADPT_FLAG(STATE_DOWN);
	strlcpy(netdev->name, "eth%d", sizeof(netdev->name));