162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause-Clear
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
462306a36Sopenharmony_ci * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "dp_rx.h"
862306a36Sopenharmony_ci#include "debug.h"
962306a36Sopenharmony_ci#include "hif.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ciconst struct ce_attr ath12k_host_ce_config_qcn9274[] = {
1262306a36Sopenharmony_ci	/* CE0: host->target HTC control and raw streams */
1362306a36Sopenharmony_ci	{
1462306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS,
1562306a36Sopenharmony_ci		.src_nentries = 16,
1662306a36Sopenharmony_ci		.src_sz_max = 2048,
1762306a36Sopenharmony_ci		.dest_nentries = 0,
1862306a36Sopenharmony_ci	},
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	/* CE1: target->host HTT + HTC control */
2162306a36Sopenharmony_ci	{
2262306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS,
2362306a36Sopenharmony_ci		.src_nentries = 0,
2462306a36Sopenharmony_ci		.src_sz_max = 2048,
2562306a36Sopenharmony_ci		.dest_nentries = 512,
2662306a36Sopenharmony_ci		.recv_cb = ath12k_htc_rx_completion_handler,
2762306a36Sopenharmony_ci	},
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	/* CE2: target->host WMI */
3062306a36Sopenharmony_ci	{
3162306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS,
3262306a36Sopenharmony_ci		.src_nentries = 0,
3362306a36Sopenharmony_ci		.src_sz_max = 2048,
3462306a36Sopenharmony_ci		.dest_nentries = 128,
3562306a36Sopenharmony_ci		.recv_cb = ath12k_htc_rx_completion_handler,
3662306a36Sopenharmony_ci	},
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	/* CE3: host->target WMI (mac0) */
3962306a36Sopenharmony_ci	{
4062306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS,
4162306a36Sopenharmony_ci		.src_nentries = 32,
4262306a36Sopenharmony_ci		.src_sz_max = 2048,
4362306a36Sopenharmony_ci		.dest_nentries = 0,
4462306a36Sopenharmony_ci	},
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	/* CE4: host->target HTT */
4762306a36Sopenharmony_ci	{
4862306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
4962306a36Sopenharmony_ci		.src_nentries = 2048,
5062306a36Sopenharmony_ci		.src_sz_max = 256,
5162306a36Sopenharmony_ci		.dest_nentries = 0,
5262306a36Sopenharmony_ci	},
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/* CE5: target->host pktlog */
5562306a36Sopenharmony_ci	{
5662306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS,
5762306a36Sopenharmony_ci		.src_nentries = 0,
5862306a36Sopenharmony_ci		.src_sz_max = 2048,
5962306a36Sopenharmony_ci		.dest_nentries = 512,
6062306a36Sopenharmony_ci		.recv_cb = ath12k_dp_htt_htc_t2h_msg_handler,
6162306a36Sopenharmony_ci	},
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	/* CE6: target autonomous hif_memcpy */
6462306a36Sopenharmony_ci	{
6562306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
6662306a36Sopenharmony_ci		.src_nentries = 0,
6762306a36Sopenharmony_ci		.src_sz_max = 0,
6862306a36Sopenharmony_ci		.dest_nentries = 0,
6962306a36Sopenharmony_ci	},
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* CE7: host->target WMI (mac1) */
7262306a36Sopenharmony_ci	{
7362306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS,
7462306a36Sopenharmony_ci		.src_nentries = 32,
7562306a36Sopenharmony_ci		.src_sz_max = 2048,
7662306a36Sopenharmony_ci		.dest_nentries = 0,
7762306a36Sopenharmony_ci	},
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/* CE8: target autonomous hif_memcpy */
8062306a36Sopenharmony_ci	{
8162306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
8262306a36Sopenharmony_ci		.src_nentries = 0,
8362306a36Sopenharmony_ci		.src_sz_max = 0,
8462306a36Sopenharmony_ci		.dest_nentries = 0,
8562306a36Sopenharmony_ci	},
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* CE9: MHI */
8862306a36Sopenharmony_ci	{
8962306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
9062306a36Sopenharmony_ci		.src_nentries = 0,
9162306a36Sopenharmony_ci		.src_sz_max = 0,
9262306a36Sopenharmony_ci		.dest_nentries = 0,
9362306a36Sopenharmony_ci	},
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/* CE10: MHI */
9662306a36Sopenharmony_ci	{
9762306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
9862306a36Sopenharmony_ci		.src_nentries = 0,
9962306a36Sopenharmony_ci		.src_sz_max = 0,
10062306a36Sopenharmony_ci		.dest_nentries = 0,
10162306a36Sopenharmony_ci	},
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* CE11: MHI */
10462306a36Sopenharmony_ci	{
10562306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
10662306a36Sopenharmony_ci		.src_nentries = 0,
10762306a36Sopenharmony_ci		.src_sz_max = 0,
10862306a36Sopenharmony_ci		.dest_nentries = 0,
10962306a36Sopenharmony_ci	},
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* CE12: CV Prefetch */
11262306a36Sopenharmony_ci	{
11362306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
11462306a36Sopenharmony_ci		.src_nentries = 0,
11562306a36Sopenharmony_ci		.src_sz_max = 0,
11662306a36Sopenharmony_ci		.dest_nentries = 0,
11762306a36Sopenharmony_ci	},
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	/* CE13: CV Prefetch */
12062306a36Sopenharmony_ci	{
12162306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
12262306a36Sopenharmony_ci		.src_nentries = 0,
12362306a36Sopenharmony_ci		.src_sz_max = 0,
12462306a36Sopenharmony_ci		.dest_nentries = 0,
12562306a36Sopenharmony_ci	},
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	/* CE14: target->host dbg log */
12862306a36Sopenharmony_ci	{
12962306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS,
13062306a36Sopenharmony_ci		.src_nentries = 0,
13162306a36Sopenharmony_ci		.src_sz_max = 2048,
13262306a36Sopenharmony_ci		.dest_nentries = 512,
13362306a36Sopenharmony_ci		.recv_cb = ath12k_htc_rx_completion_handler,
13462306a36Sopenharmony_ci	},
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* CE15: reserved for future use */
13762306a36Sopenharmony_ci	{
13862306a36Sopenharmony_ci		.flags = (CE_ATTR_FLAGS | CE_ATTR_DIS_INTR),
13962306a36Sopenharmony_ci		.src_nentries = 0,
14062306a36Sopenharmony_ci		.src_sz_max = 0,
14162306a36Sopenharmony_ci		.dest_nentries = 0,
14262306a36Sopenharmony_ci	},
14362306a36Sopenharmony_ci};
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ciconst struct ce_attr ath12k_host_ce_config_wcn7850[] = {
14662306a36Sopenharmony_ci	/* CE0: host->target HTC control and raw streams */
14762306a36Sopenharmony_ci	{
14862306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS,
14962306a36Sopenharmony_ci		.src_nentries = 16,
15062306a36Sopenharmony_ci		.src_sz_max = 2048,
15162306a36Sopenharmony_ci		.dest_nentries = 0,
15262306a36Sopenharmony_ci	},
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/* CE1: target->host HTT + HTC control */
15562306a36Sopenharmony_ci	{
15662306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS,
15762306a36Sopenharmony_ci		.src_nentries = 0,
15862306a36Sopenharmony_ci		.src_sz_max = 2048,
15962306a36Sopenharmony_ci		.dest_nentries = 512,
16062306a36Sopenharmony_ci		.recv_cb = ath12k_htc_rx_completion_handler,
16162306a36Sopenharmony_ci	},
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* CE2: target->host WMI */
16462306a36Sopenharmony_ci	{
16562306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS,
16662306a36Sopenharmony_ci		.src_nentries = 0,
16762306a36Sopenharmony_ci		.src_sz_max = 2048,
16862306a36Sopenharmony_ci		.dest_nentries = 64,
16962306a36Sopenharmony_ci		.recv_cb = ath12k_htc_rx_completion_handler,
17062306a36Sopenharmony_ci	},
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* CE3: host->target WMI (mac0) */
17362306a36Sopenharmony_ci	{
17462306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS,
17562306a36Sopenharmony_ci		.src_nentries = 32,
17662306a36Sopenharmony_ci		.src_sz_max = 2048,
17762306a36Sopenharmony_ci		.dest_nentries = 0,
17862306a36Sopenharmony_ci	},
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* CE4: host->target HTT */
18162306a36Sopenharmony_ci	{
18262306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
18362306a36Sopenharmony_ci		.src_nentries = 2048,
18462306a36Sopenharmony_ci		.src_sz_max = 256,
18562306a36Sopenharmony_ci		.dest_nentries = 0,
18662306a36Sopenharmony_ci	},
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* CE5: target->host pktlog */
18962306a36Sopenharmony_ci	{
19062306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS,
19162306a36Sopenharmony_ci		.src_nentries = 0,
19262306a36Sopenharmony_ci		.src_sz_max = 0,
19362306a36Sopenharmony_ci		.dest_nentries = 0,
19462306a36Sopenharmony_ci	},
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* CE6: target autonomous hif_memcpy */
19762306a36Sopenharmony_ci	{
19862306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
19962306a36Sopenharmony_ci		.src_nentries = 0,
20062306a36Sopenharmony_ci		.src_sz_max = 0,
20162306a36Sopenharmony_ci		.dest_nentries = 0,
20262306a36Sopenharmony_ci	},
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/* CE7: host->target WMI (mac1) */
20562306a36Sopenharmony_ci	{
20662306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
20762306a36Sopenharmony_ci		.src_nentries = 0,
20862306a36Sopenharmony_ci		.src_sz_max = 2048,
20962306a36Sopenharmony_ci		.dest_nentries = 0,
21062306a36Sopenharmony_ci	},
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	/* CE8: target autonomous hif_memcpy */
21362306a36Sopenharmony_ci	{
21462306a36Sopenharmony_ci		.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
21562306a36Sopenharmony_ci		.src_nentries = 0,
21662306a36Sopenharmony_ci		.src_sz_max = 0,
21762306a36Sopenharmony_ci		.dest_nentries = 0,
21862306a36Sopenharmony_ci	},
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci};
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic int ath12k_ce_rx_buf_enqueue_pipe(struct ath12k_ce_pipe *pipe,
22362306a36Sopenharmony_ci					 struct sk_buff *skb, dma_addr_t paddr)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct ath12k_base *ab = pipe->ab;
22662306a36Sopenharmony_ci	struct ath12k_ce_ring *ring = pipe->dest_ring;
22762306a36Sopenharmony_ci	struct hal_srng *srng;
22862306a36Sopenharmony_ci	unsigned int write_index;
22962306a36Sopenharmony_ci	unsigned int nentries_mask = ring->nentries_mask;
23062306a36Sopenharmony_ci	struct hal_ce_srng_dest_desc *desc;
23162306a36Sopenharmony_ci	int ret;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	lockdep_assert_held(&ab->ce.ce_lock);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	write_index = ring->write_index;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	srng = &ab->hal.srng_list[ring->hal_ring_id];
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	spin_lock_bh(&srng->lock);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	ath12k_hal_srng_access_begin(ab, srng);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (unlikely(ath12k_hal_srng_src_num_free(ab, srng, false) < 1)) {
24462306a36Sopenharmony_ci		ret = -ENOSPC;
24562306a36Sopenharmony_ci		goto exit;
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	desc = ath12k_hal_srng_src_get_next_entry(ab, srng);
24962306a36Sopenharmony_ci	if (!desc) {
25062306a36Sopenharmony_ci		ret = -ENOSPC;
25162306a36Sopenharmony_ci		goto exit;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	ath12k_hal_ce_dst_set_desc(desc, paddr);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	ring->skb[write_index] = skb;
25762306a36Sopenharmony_ci	write_index = CE_RING_IDX_INCR(nentries_mask, write_index);
25862306a36Sopenharmony_ci	ring->write_index = write_index;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	pipe->rx_buf_needed--;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	ret = 0;
26362306a36Sopenharmony_ciexit:
26462306a36Sopenharmony_ci	ath12k_hal_srng_access_end(ab, srng);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	spin_unlock_bh(&srng->lock);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return ret;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic int ath12k_ce_rx_post_pipe(struct ath12k_ce_pipe *pipe)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct ath12k_base *ab = pipe->ab;
27462306a36Sopenharmony_ci	struct sk_buff *skb;
27562306a36Sopenharmony_ci	dma_addr_t paddr;
27662306a36Sopenharmony_ci	int ret = 0;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (!(pipe->dest_ring || pipe->status_ring))
27962306a36Sopenharmony_ci		return 0;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	spin_lock_bh(&ab->ce.ce_lock);
28262306a36Sopenharmony_ci	while (pipe->rx_buf_needed) {
28362306a36Sopenharmony_ci		skb = dev_alloc_skb(pipe->buf_sz);
28462306a36Sopenharmony_ci		if (!skb) {
28562306a36Sopenharmony_ci			ret = -ENOMEM;
28662306a36Sopenharmony_ci			goto exit;
28762306a36Sopenharmony_ci		}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci		WARN_ON_ONCE(!IS_ALIGNED((unsigned long)skb->data, 4));
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci		paddr = dma_map_single(ab->dev, skb->data,
29262306a36Sopenharmony_ci				       skb->len + skb_tailroom(skb),
29362306a36Sopenharmony_ci				       DMA_FROM_DEVICE);
29462306a36Sopenharmony_ci		if (unlikely(dma_mapping_error(ab->dev, paddr))) {
29562306a36Sopenharmony_ci			ath12k_warn(ab, "failed to dma map ce rx buf\n");
29662306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
29762306a36Sopenharmony_ci			ret = -EIO;
29862306a36Sopenharmony_ci			goto exit;
29962306a36Sopenharmony_ci		}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci		ATH12K_SKB_RXCB(skb)->paddr = paddr;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		ret = ath12k_ce_rx_buf_enqueue_pipe(pipe, skb, paddr);
30462306a36Sopenharmony_ci		if (ret) {
30562306a36Sopenharmony_ci			ath12k_warn(ab, "failed to enqueue rx buf: %d\n", ret);
30662306a36Sopenharmony_ci			dma_unmap_single(ab->dev, paddr,
30762306a36Sopenharmony_ci					 skb->len + skb_tailroom(skb),
30862306a36Sopenharmony_ci					 DMA_FROM_DEVICE);
30962306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
31062306a36Sopenharmony_ci			goto exit;
31162306a36Sopenharmony_ci		}
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ciexit:
31562306a36Sopenharmony_ci	spin_unlock_bh(&ab->ce.ce_lock);
31662306a36Sopenharmony_ci	return ret;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic int ath12k_ce_completed_recv_next(struct ath12k_ce_pipe *pipe,
32062306a36Sopenharmony_ci					 struct sk_buff **skb, int *nbytes)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	struct ath12k_base *ab = pipe->ab;
32362306a36Sopenharmony_ci	struct hal_ce_srng_dst_status_desc *desc;
32462306a36Sopenharmony_ci	struct hal_srng *srng;
32562306a36Sopenharmony_ci	unsigned int sw_index;
32662306a36Sopenharmony_ci	unsigned int nentries_mask;
32762306a36Sopenharmony_ci	int ret = 0;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	spin_lock_bh(&ab->ce.ce_lock);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	sw_index = pipe->dest_ring->sw_index;
33262306a36Sopenharmony_ci	nentries_mask = pipe->dest_ring->nentries_mask;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	srng = &ab->hal.srng_list[pipe->status_ring->hal_ring_id];
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	spin_lock_bh(&srng->lock);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	ath12k_hal_srng_access_begin(ab, srng);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	desc = ath12k_hal_srng_dst_get_next_entry(ab, srng);
34162306a36Sopenharmony_ci	if (!desc) {
34262306a36Sopenharmony_ci		ret = -EIO;
34362306a36Sopenharmony_ci		goto err;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	*nbytes = ath12k_hal_ce_dst_status_get_length(desc);
34762306a36Sopenharmony_ci	if (*nbytes == 0) {
34862306a36Sopenharmony_ci		ret = -EIO;
34962306a36Sopenharmony_ci		goto err;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	*skb = pipe->dest_ring->skb[sw_index];
35362306a36Sopenharmony_ci	pipe->dest_ring->skb[sw_index] = NULL;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
35662306a36Sopenharmony_ci	pipe->dest_ring->sw_index = sw_index;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	pipe->rx_buf_needed++;
35962306a36Sopenharmony_cierr:
36062306a36Sopenharmony_ci	ath12k_hal_srng_access_end(ab, srng);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	spin_unlock_bh(&srng->lock);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	spin_unlock_bh(&ab->ce.ce_lock);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	return ret;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic void ath12k_ce_recv_process_cb(struct ath12k_ce_pipe *pipe)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct ath12k_base *ab = pipe->ab;
37262306a36Sopenharmony_ci	struct sk_buff *skb;
37362306a36Sopenharmony_ci	struct sk_buff_head list;
37462306a36Sopenharmony_ci	unsigned int nbytes, max_nbytes;
37562306a36Sopenharmony_ci	int ret;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	__skb_queue_head_init(&list);
37862306a36Sopenharmony_ci	while (ath12k_ce_completed_recv_next(pipe, &skb, &nbytes) == 0) {
37962306a36Sopenharmony_ci		max_nbytes = skb->len + skb_tailroom(skb);
38062306a36Sopenharmony_ci		dma_unmap_single(ab->dev, ATH12K_SKB_RXCB(skb)->paddr,
38162306a36Sopenharmony_ci				 max_nbytes, DMA_FROM_DEVICE);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		if (unlikely(max_nbytes < nbytes)) {
38462306a36Sopenharmony_ci			ath12k_warn(ab, "rxed more than expected (nbytes %d, max %d)",
38562306a36Sopenharmony_ci				    nbytes, max_nbytes);
38662306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
38762306a36Sopenharmony_ci			continue;
38862306a36Sopenharmony_ci		}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		skb_put(skb, nbytes);
39162306a36Sopenharmony_ci		__skb_queue_tail(&list, skb);
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	while ((skb = __skb_dequeue(&list))) {
39562306a36Sopenharmony_ci		ath12k_dbg(ab, ATH12K_DBG_AHB, "rx ce pipe %d len %d\n",
39662306a36Sopenharmony_ci			   pipe->pipe_num, skb->len);
39762306a36Sopenharmony_ci		pipe->recv_cb(ab, skb);
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	ret = ath12k_ce_rx_post_pipe(pipe);
40162306a36Sopenharmony_ci	if (ret && ret != -ENOSPC) {
40262306a36Sopenharmony_ci		ath12k_warn(ab, "failed to post rx buf to pipe: %d err: %d\n",
40362306a36Sopenharmony_ci			    pipe->pipe_num, ret);
40462306a36Sopenharmony_ci		mod_timer(&ab->rx_replenish_retry,
40562306a36Sopenharmony_ci			  jiffies + ATH12K_CE_RX_POST_RETRY_JIFFIES);
40662306a36Sopenharmony_ci	}
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic struct sk_buff *ath12k_ce_completed_send_next(struct ath12k_ce_pipe *pipe)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	struct ath12k_base *ab = pipe->ab;
41262306a36Sopenharmony_ci	struct hal_ce_srng_src_desc *desc;
41362306a36Sopenharmony_ci	struct hal_srng *srng;
41462306a36Sopenharmony_ci	unsigned int sw_index;
41562306a36Sopenharmony_ci	unsigned int nentries_mask;
41662306a36Sopenharmony_ci	struct sk_buff *skb;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	spin_lock_bh(&ab->ce.ce_lock);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	sw_index = pipe->src_ring->sw_index;
42162306a36Sopenharmony_ci	nentries_mask = pipe->src_ring->nentries_mask;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	srng = &ab->hal.srng_list[pipe->src_ring->hal_ring_id];
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	spin_lock_bh(&srng->lock);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	ath12k_hal_srng_access_begin(ab, srng);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	desc = ath12k_hal_srng_src_reap_next(ab, srng);
43062306a36Sopenharmony_ci	if (!desc) {
43162306a36Sopenharmony_ci		skb = ERR_PTR(-EIO);
43262306a36Sopenharmony_ci		goto err_unlock;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	skb = pipe->src_ring->skb[sw_index];
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	pipe->src_ring->skb[sw_index] = NULL;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
44062306a36Sopenharmony_ci	pipe->src_ring->sw_index = sw_index;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cierr_unlock:
44362306a36Sopenharmony_ci	spin_unlock_bh(&srng->lock);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	spin_unlock_bh(&ab->ce.ce_lock);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	return skb;
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic void ath12k_ce_send_done_cb(struct ath12k_ce_pipe *pipe)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct ath12k_base *ab = pipe->ab;
45362306a36Sopenharmony_ci	struct sk_buff *skb;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	while (!IS_ERR(skb = ath12k_ce_completed_send_next(pipe))) {
45662306a36Sopenharmony_ci		if (!skb)
45762306a36Sopenharmony_ci			continue;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci		dma_unmap_single(ab->dev, ATH12K_SKB_CB(skb)->paddr, skb->len,
46062306a36Sopenharmony_ci				 DMA_TO_DEVICE);
46162306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic void ath12k_ce_srng_msi_ring_params_setup(struct ath12k_base *ab, u32 ce_id,
46662306a36Sopenharmony_ci						 struct hal_srng_params *ring_params)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	u32 msi_data_start;
46962306a36Sopenharmony_ci	u32 msi_data_count, msi_data_idx;
47062306a36Sopenharmony_ci	u32 msi_irq_start;
47162306a36Sopenharmony_ci	u32 addr_lo;
47262306a36Sopenharmony_ci	u32 addr_hi;
47362306a36Sopenharmony_ci	int ret;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	ret = ath12k_hif_get_user_msi_vector(ab, "CE",
47662306a36Sopenharmony_ci					     &msi_data_count, &msi_data_start,
47762306a36Sopenharmony_ci					     &msi_irq_start);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (ret)
48062306a36Sopenharmony_ci		return;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	ath12k_hif_get_msi_address(ab, &addr_lo, &addr_hi);
48362306a36Sopenharmony_ci	ath12k_hif_get_ce_msi_idx(ab, ce_id, &msi_data_idx);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	ring_params->msi_addr = addr_lo;
48662306a36Sopenharmony_ci	ring_params->msi_addr |= (dma_addr_t)(((uint64_t)addr_hi) << 32);
48762306a36Sopenharmony_ci	ring_params->msi_data = (msi_data_idx % msi_data_count) + msi_data_start;
48862306a36Sopenharmony_ci	ring_params->flags |= HAL_SRNG_FLAGS_MSI_INTR;
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic int ath12k_ce_init_ring(struct ath12k_base *ab,
49262306a36Sopenharmony_ci			       struct ath12k_ce_ring *ce_ring,
49362306a36Sopenharmony_ci			       int ce_id, enum hal_ring_type type)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct hal_srng_params params = { 0 };
49662306a36Sopenharmony_ci	int ret;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	params.ring_base_paddr = ce_ring->base_addr_ce_space;
49962306a36Sopenharmony_ci	params.ring_base_vaddr = ce_ring->base_addr_owner_space;
50062306a36Sopenharmony_ci	params.num_entries = ce_ring->nentries;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (!(CE_ATTR_DIS_INTR & ab->hw_params->host_ce_config[ce_id].flags))
50362306a36Sopenharmony_ci		ath12k_ce_srng_msi_ring_params_setup(ab, ce_id, &params);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	switch (type) {
50662306a36Sopenharmony_ci	case HAL_CE_SRC:
50762306a36Sopenharmony_ci		if (!(CE_ATTR_DIS_INTR & ab->hw_params->host_ce_config[ce_id].flags))
50862306a36Sopenharmony_ci			params.intr_batch_cntr_thres_entries = 1;
50962306a36Sopenharmony_ci		break;
51062306a36Sopenharmony_ci	case HAL_CE_DST:
51162306a36Sopenharmony_ci		params.max_buffer_len = ab->hw_params->host_ce_config[ce_id].src_sz_max;
51262306a36Sopenharmony_ci		if (!(ab->hw_params->host_ce_config[ce_id].flags & CE_ATTR_DIS_INTR)) {
51362306a36Sopenharmony_ci			params.intr_timer_thres_us = 1024;
51462306a36Sopenharmony_ci			params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN;
51562306a36Sopenharmony_ci			params.low_threshold = ce_ring->nentries - 3;
51662306a36Sopenharmony_ci		}
51762306a36Sopenharmony_ci		break;
51862306a36Sopenharmony_ci	case HAL_CE_DST_STATUS:
51962306a36Sopenharmony_ci		if (!(ab->hw_params->host_ce_config[ce_id].flags & CE_ATTR_DIS_INTR)) {
52062306a36Sopenharmony_ci			params.intr_batch_cntr_thres_entries = 1;
52162306a36Sopenharmony_ci			params.intr_timer_thres_us = 0x1000;
52262306a36Sopenharmony_ci		}
52362306a36Sopenharmony_ci		break;
52462306a36Sopenharmony_ci	default:
52562306a36Sopenharmony_ci		ath12k_warn(ab, "Invalid CE ring type %d\n", type);
52662306a36Sopenharmony_ci		return -EINVAL;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	/* TODO: Init other params needed by HAL to init the ring */
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	ret = ath12k_hal_srng_setup(ab, type, ce_id, 0, &params);
53262306a36Sopenharmony_ci	if (ret < 0) {
53362306a36Sopenharmony_ci		ath12k_warn(ab, "failed to setup srng: %d ring_id %d\n",
53462306a36Sopenharmony_ci			    ret, ce_id);
53562306a36Sopenharmony_ci		return ret;
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	ce_ring->hal_ring_id = ret;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	return 0;
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic struct ath12k_ce_ring *
54462306a36Sopenharmony_ciath12k_ce_alloc_ring(struct ath12k_base *ab, int nentries, int desc_sz)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	struct ath12k_ce_ring *ce_ring;
54762306a36Sopenharmony_ci	dma_addr_t base_addr;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	ce_ring = kzalloc(struct_size(ce_ring, skb, nentries), GFP_KERNEL);
55062306a36Sopenharmony_ci	if (!ce_ring)
55162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	ce_ring->nentries = nentries;
55462306a36Sopenharmony_ci	ce_ring->nentries_mask = nentries - 1;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	/* Legacy platforms that do not support cache
55762306a36Sopenharmony_ci	 * coherent DMA are unsupported
55862306a36Sopenharmony_ci	 */
55962306a36Sopenharmony_ci	ce_ring->base_addr_owner_space_unaligned =
56062306a36Sopenharmony_ci		dma_alloc_coherent(ab->dev,
56162306a36Sopenharmony_ci				   nentries * desc_sz + CE_DESC_RING_ALIGN,
56262306a36Sopenharmony_ci				   &base_addr, GFP_KERNEL);
56362306a36Sopenharmony_ci	if (!ce_ring->base_addr_owner_space_unaligned) {
56462306a36Sopenharmony_ci		kfree(ce_ring);
56562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	ce_ring->base_addr_ce_space_unaligned = base_addr;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	ce_ring->base_addr_owner_space =
57162306a36Sopenharmony_ci		PTR_ALIGN(ce_ring->base_addr_owner_space_unaligned,
57262306a36Sopenharmony_ci			  CE_DESC_RING_ALIGN);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	ce_ring->base_addr_ce_space = ALIGN(ce_ring->base_addr_ce_space_unaligned,
57562306a36Sopenharmony_ci					    CE_DESC_RING_ALIGN);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	return ce_ring;
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic int ath12k_ce_alloc_pipe(struct ath12k_base *ab, int ce_id)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	struct ath12k_ce_pipe *pipe = &ab->ce.ce_pipe[ce_id];
58362306a36Sopenharmony_ci	const struct ce_attr *attr = &ab->hw_params->host_ce_config[ce_id];
58462306a36Sopenharmony_ci	struct ath12k_ce_ring *ring;
58562306a36Sopenharmony_ci	int nentries;
58662306a36Sopenharmony_ci	int desc_sz;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	pipe->attr_flags = attr->flags;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if (attr->src_nentries) {
59162306a36Sopenharmony_ci		pipe->send_cb = ath12k_ce_send_done_cb;
59262306a36Sopenharmony_ci		nentries = roundup_pow_of_two(attr->src_nentries);
59362306a36Sopenharmony_ci		desc_sz = ath12k_hal_ce_get_desc_size(HAL_CE_DESC_SRC);
59462306a36Sopenharmony_ci		ring = ath12k_ce_alloc_ring(ab, nentries, desc_sz);
59562306a36Sopenharmony_ci		if (IS_ERR(ring))
59662306a36Sopenharmony_ci			return PTR_ERR(ring);
59762306a36Sopenharmony_ci		pipe->src_ring = ring;
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	if (attr->dest_nentries) {
60162306a36Sopenharmony_ci		pipe->recv_cb = attr->recv_cb;
60262306a36Sopenharmony_ci		nentries = roundup_pow_of_two(attr->dest_nentries);
60362306a36Sopenharmony_ci		desc_sz = ath12k_hal_ce_get_desc_size(HAL_CE_DESC_DST);
60462306a36Sopenharmony_ci		ring = ath12k_ce_alloc_ring(ab, nentries, desc_sz);
60562306a36Sopenharmony_ci		if (IS_ERR(ring))
60662306a36Sopenharmony_ci			return PTR_ERR(ring);
60762306a36Sopenharmony_ci		pipe->dest_ring = ring;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci		desc_sz = ath12k_hal_ce_get_desc_size(HAL_CE_DESC_DST_STATUS);
61062306a36Sopenharmony_ci		ring = ath12k_ce_alloc_ring(ab, nentries, desc_sz);
61162306a36Sopenharmony_ci		if (IS_ERR(ring))
61262306a36Sopenharmony_ci			return PTR_ERR(ring);
61362306a36Sopenharmony_ci		pipe->status_ring = ring;
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	return 0;
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_civoid ath12k_ce_per_engine_service(struct ath12k_base *ab, u16 ce_id)
62062306a36Sopenharmony_ci{
62162306a36Sopenharmony_ci	struct ath12k_ce_pipe *pipe = &ab->ce.ce_pipe[ce_id];
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	if (pipe->send_cb)
62462306a36Sopenharmony_ci		pipe->send_cb(pipe);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	if (pipe->recv_cb)
62762306a36Sopenharmony_ci		ath12k_ce_recv_process_cb(pipe);
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_civoid ath12k_ce_poll_send_completed(struct ath12k_base *ab, u8 pipe_id)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	struct ath12k_ce_pipe *pipe = &ab->ce.ce_pipe[pipe_id];
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	if ((pipe->attr_flags & CE_ATTR_DIS_INTR) && pipe->send_cb)
63562306a36Sopenharmony_ci		pipe->send_cb(pipe);
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ciint ath12k_ce_send(struct ath12k_base *ab, struct sk_buff *skb, u8 pipe_id,
63962306a36Sopenharmony_ci		   u16 transfer_id)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	struct ath12k_ce_pipe *pipe = &ab->ce.ce_pipe[pipe_id];
64262306a36Sopenharmony_ci	struct hal_ce_srng_src_desc *desc;
64362306a36Sopenharmony_ci	struct hal_srng *srng;
64462306a36Sopenharmony_ci	unsigned int write_index, sw_index;
64562306a36Sopenharmony_ci	unsigned int nentries_mask;
64662306a36Sopenharmony_ci	int ret = 0;
64762306a36Sopenharmony_ci	u8 byte_swap_data = 0;
64862306a36Sopenharmony_ci	int num_used;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	/* Check if some entries could be regained by handling tx completion if
65162306a36Sopenharmony_ci	 * the CE has interrupts disabled and the used entries is more than the
65262306a36Sopenharmony_ci	 * defined usage threshold.
65362306a36Sopenharmony_ci	 */
65462306a36Sopenharmony_ci	if (pipe->attr_flags & CE_ATTR_DIS_INTR) {
65562306a36Sopenharmony_ci		spin_lock_bh(&ab->ce.ce_lock);
65662306a36Sopenharmony_ci		write_index = pipe->src_ring->write_index;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci		sw_index = pipe->src_ring->sw_index;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		if (write_index >= sw_index)
66162306a36Sopenharmony_ci			num_used = write_index - sw_index;
66262306a36Sopenharmony_ci		else
66362306a36Sopenharmony_ci			num_used = pipe->src_ring->nentries - sw_index +
66462306a36Sopenharmony_ci				   write_index;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci		spin_unlock_bh(&ab->ce.ce_lock);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci		if (num_used > ATH12K_CE_USAGE_THRESHOLD)
66962306a36Sopenharmony_ci			ath12k_ce_poll_send_completed(ab, pipe->pipe_num);
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	if (test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags))
67362306a36Sopenharmony_ci		return -ESHUTDOWN;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	spin_lock_bh(&ab->ce.ce_lock);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	write_index = pipe->src_ring->write_index;
67862306a36Sopenharmony_ci	nentries_mask = pipe->src_ring->nentries_mask;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	srng = &ab->hal.srng_list[pipe->src_ring->hal_ring_id];
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	spin_lock_bh(&srng->lock);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	ath12k_hal_srng_access_begin(ab, srng);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	if (unlikely(ath12k_hal_srng_src_num_free(ab, srng, false) < 1)) {
68762306a36Sopenharmony_ci		ath12k_hal_srng_access_end(ab, srng);
68862306a36Sopenharmony_ci		ret = -ENOBUFS;
68962306a36Sopenharmony_ci		goto unlock;
69062306a36Sopenharmony_ci	}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	desc = ath12k_hal_srng_src_get_next_reaped(ab, srng);
69362306a36Sopenharmony_ci	if (!desc) {
69462306a36Sopenharmony_ci		ath12k_hal_srng_access_end(ab, srng);
69562306a36Sopenharmony_ci		ret = -ENOBUFS;
69662306a36Sopenharmony_ci		goto unlock;
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	if (pipe->attr_flags & CE_ATTR_BYTE_SWAP_DATA)
70062306a36Sopenharmony_ci		byte_swap_data = 1;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	ath12k_hal_ce_src_set_desc(desc, ATH12K_SKB_CB(skb)->paddr,
70362306a36Sopenharmony_ci				   skb->len, transfer_id, byte_swap_data);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	pipe->src_ring->skb[write_index] = skb;
70662306a36Sopenharmony_ci	pipe->src_ring->write_index = CE_RING_IDX_INCR(nentries_mask,
70762306a36Sopenharmony_ci						       write_index);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	ath12k_hal_srng_access_end(ab, srng);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ciunlock:
71262306a36Sopenharmony_ci	spin_unlock_bh(&srng->lock);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	spin_unlock_bh(&ab->ce.ce_lock);
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	return ret;
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic void ath12k_ce_rx_pipe_cleanup(struct ath12k_ce_pipe *pipe)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	struct ath12k_base *ab = pipe->ab;
72262306a36Sopenharmony_ci	struct ath12k_ce_ring *ring = pipe->dest_ring;
72362306a36Sopenharmony_ci	struct sk_buff *skb;
72462306a36Sopenharmony_ci	int i;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	if (!(ring && pipe->buf_sz))
72762306a36Sopenharmony_ci		return;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	for (i = 0; i < ring->nentries; i++) {
73062306a36Sopenharmony_ci		skb = ring->skb[i];
73162306a36Sopenharmony_ci		if (!skb)
73262306a36Sopenharmony_ci			continue;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci		ring->skb[i] = NULL;
73562306a36Sopenharmony_ci		dma_unmap_single(ab->dev, ATH12K_SKB_RXCB(skb)->paddr,
73662306a36Sopenharmony_ci				 skb->len + skb_tailroom(skb), DMA_FROM_DEVICE);
73762306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
73862306a36Sopenharmony_ci	}
73962306a36Sopenharmony_ci}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_civoid ath12k_ce_cleanup_pipes(struct ath12k_base *ab)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	struct ath12k_ce_pipe *pipe;
74462306a36Sopenharmony_ci	int pipe_num;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	for (pipe_num = 0; pipe_num < ab->hw_params->ce_count; pipe_num++) {
74762306a36Sopenharmony_ci		pipe = &ab->ce.ce_pipe[pipe_num];
74862306a36Sopenharmony_ci		ath12k_ce_rx_pipe_cleanup(pipe);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci		/* Cleanup any src CE's which have interrupts disabled */
75162306a36Sopenharmony_ci		ath12k_ce_poll_send_completed(ab, pipe_num);
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci		/* NOTE: Should we also clean up tx buffer in all pipes? */
75462306a36Sopenharmony_ci	}
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_civoid ath12k_ce_rx_post_buf(struct ath12k_base *ab)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	struct ath12k_ce_pipe *pipe;
76062306a36Sopenharmony_ci	int i;
76162306a36Sopenharmony_ci	int ret;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	for (i = 0; i < ab->hw_params->ce_count; i++) {
76462306a36Sopenharmony_ci		pipe = &ab->ce.ce_pipe[i];
76562306a36Sopenharmony_ci		ret = ath12k_ce_rx_post_pipe(pipe);
76662306a36Sopenharmony_ci		if (ret) {
76762306a36Sopenharmony_ci			if (ret == -ENOSPC)
76862306a36Sopenharmony_ci				continue;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci			ath12k_warn(ab, "failed to post rx buf to pipe: %d err: %d\n",
77162306a36Sopenharmony_ci				    i, ret);
77262306a36Sopenharmony_ci			mod_timer(&ab->rx_replenish_retry,
77362306a36Sopenharmony_ci				  jiffies + ATH12K_CE_RX_POST_RETRY_JIFFIES);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci			return;
77662306a36Sopenharmony_ci		}
77762306a36Sopenharmony_ci	}
77862306a36Sopenharmony_ci}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_civoid ath12k_ce_rx_replenish_retry(struct timer_list *t)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	struct ath12k_base *ab = from_timer(ab, t, rx_replenish_retry);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	ath12k_ce_rx_post_buf(ab);
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_cistatic void ath12k_ce_shadow_config(struct ath12k_base *ab)
78862306a36Sopenharmony_ci{
78962306a36Sopenharmony_ci	int i;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	for (i = 0; i < ab->hw_params->ce_count; i++) {
79262306a36Sopenharmony_ci		if (ab->hw_params->host_ce_config[i].src_nentries)
79362306a36Sopenharmony_ci			ath12k_hal_srng_update_shadow_config(ab, HAL_CE_SRC, i);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci		if (ab->hw_params->host_ce_config[i].dest_nentries) {
79662306a36Sopenharmony_ci			ath12k_hal_srng_update_shadow_config(ab, HAL_CE_DST, i);
79762306a36Sopenharmony_ci			ath12k_hal_srng_update_shadow_config(ab, HAL_CE_DST_STATUS, i);
79862306a36Sopenharmony_ci		}
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_civoid ath12k_ce_get_shadow_config(struct ath12k_base *ab,
80362306a36Sopenharmony_ci				 u32 **shadow_cfg, u32 *shadow_cfg_len)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	if (!ab->hw_params->supports_shadow_regs)
80662306a36Sopenharmony_ci		return;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	ath12k_hal_srng_get_shadow_config(ab, shadow_cfg, shadow_cfg_len);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	/* shadow is already configured */
81162306a36Sopenharmony_ci	if (*shadow_cfg_len)
81262306a36Sopenharmony_ci		return;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	/* shadow isn't configured yet, configure now.
81562306a36Sopenharmony_ci	 * non-CE srngs are configured firstly, then
81662306a36Sopenharmony_ci	 * all CE srngs.
81762306a36Sopenharmony_ci	 */
81862306a36Sopenharmony_ci	ath12k_hal_srng_shadow_config(ab);
81962306a36Sopenharmony_ci	ath12k_ce_shadow_config(ab);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	/* get the shadow configuration */
82262306a36Sopenharmony_ci	ath12k_hal_srng_get_shadow_config(ab, shadow_cfg, shadow_cfg_len);
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ciint ath12k_ce_init_pipes(struct ath12k_base *ab)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	struct ath12k_ce_pipe *pipe;
82862306a36Sopenharmony_ci	int i;
82962306a36Sopenharmony_ci	int ret;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	ath12k_ce_get_shadow_config(ab, &ab->qmi.ce_cfg.shadow_reg_v3,
83262306a36Sopenharmony_ci				    &ab->qmi.ce_cfg.shadow_reg_v3_len);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	for (i = 0; i < ab->hw_params->ce_count; i++) {
83562306a36Sopenharmony_ci		pipe = &ab->ce.ce_pipe[i];
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci		if (pipe->src_ring) {
83862306a36Sopenharmony_ci			ret = ath12k_ce_init_ring(ab, pipe->src_ring, i,
83962306a36Sopenharmony_ci						  HAL_CE_SRC);
84062306a36Sopenharmony_ci			if (ret) {
84162306a36Sopenharmony_ci				ath12k_warn(ab, "failed to init src ring: %d\n",
84262306a36Sopenharmony_ci					    ret);
84362306a36Sopenharmony_ci				/* Should we clear any partial init */
84462306a36Sopenharmony_ci				return ret;
84562306a36Sopenharmony_ci			}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci			pipe->src_ring->write_index = 0;
84862306a36Sopenharmony_ci			pipe->src_ring->sw_index = 0;
84962306a36Sopenharmony_ci		}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci		if (pipe->dest_ring) {
85262306a36Sopenharmony_ci			ret = ath12k_ce_init_ring(ab, pipe->dest_ring, i,
85362306a36Sopenharmony_ci						  HAL_CE_DST);
85462306a36Sopenharmony_ci			if (ret) {
85562306a36Sopenharmony_ci				ath12k_warn(ab, "failed to init dest ring: %d\n",
85662306a36Sopenharmony_ci					    ret);
85762306a36Sopenharmony_ci				/* Should we clear any partial init */
85862306a36Sopenharmony_ci				return ret;
85962306a36Sopenharmony_ci			}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci			pipe->rx_buf_needed = pipe->dest_ring->nentries ?
86262306a36Sopenharmony_ci					      pipe->dest_ring->nentries - 2 : 0;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci			pipe->dest_ring->write_index = 0;
86562306a36Sopenharmony_ci			pipe->dest_ring->sw_index = 0;
86662306a36Sopenharmony_ci		}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci		if (pipe->status_ring) {
86962306a36Sopenharmony_ci			ret = ath12k_ce_init_ring(ab, pipe->status_ring, i,
87062306a36Sopenharmony_ci						  HAL_CE_DST_STATUS);
87162306a36Sopenharmony_ci			if (ret) {
87262306a36Sopenharmony_ci				ath12k_warn(ab, "failed to init dest status ing: %d\n",
87362306a36Sopenharmony_ci					    ret);
87462306a36Sopenharmony_ci				/* Should we clear any partial init */
87562306a36Sopenharmony_ci				return ret;
87662306a36Sopenharmony_ci			}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci			pipe->status_ring->write_index = 0;
87962306a36Sopenharmony_ci			pipe->status_ring->sw_index = 0;
88062306a36Sopenharmony_ci		}
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	return 0;
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_civoid ath12k_ce_free_pipes(struct ath12k_base *ab)
88762306a36Sopenharmony_ci{
88862306a36Sopenharmony_ci	struct ath12k_ce_pipe *pipe;
88962306a36Sopenharmony_ci	int desc_sz;
89062306a36Sopenharmony_ci	int i;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	for (i = 0; i < ab->hw_params->ce_count; i++) {
89362306a36Sopenharmony_ci		pipe = &ab->ce.ce_pipe[i];
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci		if (pipe->src_ring) {
89662306a36Sopenharmony_ci			desc_sz = ath12k_hal_ce_get_desc_size(HAL_CE_DESC_SRC);
89762306a36Sopenharmony_ci			dma_free_coherent(ab->dev,
89862306a36Sopenharmony_ci					  pipe->src_ring->nentries * desc_sz +
89962306a36Sopenharmony_ci					  CE_DESC_RING_ALIGN,
90062306a36Sopenharmony_ci					  pipe->src_ring->base_addr_owner_space,
90162306a36Sopenharmony_ci					  pipe->src_ring->base_addr_ce_space);
90262306a36Sopenharmony_ci			kfree(pipe->src_ring);
90362306a36Sopenharmony_ci			pipe->src_ring = NULL;
90462306a36Sopenharmony_ci		}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci		if (pipe->dest_ring) {
90762306a36Sopenharmony_ci			desc_sz = ath12k_hal_ce_get_desc_size(HAL_CE_DESC_DST);
90862306a36Sopenharmony_ci			dma_free_coherent(ab->dev,
90962306a36Sopenharmony_ci					  pipe->dest_ring->nentries * desc_sz +
91062306a36Sopenharmony_ci					  CE_DESC_RING_ALIGN,
91162306a36Sopenharmony_ci					  pipe->dest_ring->base_addr_owner_space,
91262306a36Sopenharmony_ci					  pipe->dest_ring->base_addr_ce_space);
91362306a36Sopenharmony_ci			kfree(pipe->dest_ring);
91462306a36Sopenharmony_ci			pipe->dest_ring = NULL;
91562306a36Sopenharmony_ci		}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci		if (pipe->status_ring) {
91862306a36Sopenharmony_ci			desc_sz =
91962306a36Sopenharmony_ci			  ath12k_hal_ce_get_desc_size(HAL_CE_DESC_DST_STATUS);
92062306a36Sopenharmony_ci			dma_free_coherent(ab->dev,
92162306a36Sopenharmony_ci					  pipe->status_ring->nentries * desc_sz +
92262306a36Sopenharmony_ci					  CE_DESC_RING_ALIGN,
92362306a36Sopenharmony_ci					  pipe->status_ring->base_addr_owner_space,
92462306a36Sopenharmony_ci					  pipe->status_ring->base_addr_ce_space);
92562306a36Sopenharmony_ci			kfree(pipe->status_ring);
92662306a36Sopenharmony_ci			pipe->status_ring = NULL;
92762306a36Sopenharmony_ci		}
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ciint ath12k_ce_alloc_pipes(struct ath12k_base *ab)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	struct ath12k_ce_pipe *pipe;
93462306a36Sopenharmony_ci	int i;
93562306a36Sopenharmony_ci	int ret;
93662306a36Sopenharmony_ci	const struct ce_attr *attr;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	spin_lock_init(&ab->ce.ce_lock);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	for (i = 0; i < ab->hw_params->ce_count; i++) {
94162306a36Sopenharmony_ci		attr = &ab->hw_params->host_ce_config[i];
94262306a36Sopenharmony_ci		pipe = &ab->ce.ce_pipe[i];
94362306a36Sopenharmony_ci		pipe->pipe_num = i;
94462306a36Sopenharmony_ci		pipe->ab = ab;
94562306a36Sopenharmony_ci		pipe->buf_sz = attr->src_sz_max;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci		ret = ath12k_ce_alloc_pipe(ab, i);
94862306a36Sopenharmony_ci		if (ret) {
94962306a36Sopenharmony_ci			/* Free any partial successful allocation */
95062306a36Sopenharmony_ci			ath12k_ce_free_pipes(ab);
95162306a36Sopenharmony_ci			return ret;
95262306a36Sopenharmony_ci		}
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	return 0;
95662306a36Sopenharmony_ci}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ciint ath12k_ce_get_attr_flags(struct ath12k_base *ab, int ce_id)
95962306a36Sopenharmony_ci{
96062306a36Sopenharmony_ci	if (ce_id >= ab->hw_params->ce_count)
96162306a36Sopenharmony_ci		return -EINVAL;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	return ab->hw_params->host_ce_config[ce_id].flags;
96462306a36Sopenharmony_ci}
965