162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2017 Intel Deutschland GmbH
462306a36Sopenharmony_ci * Copyright (C) 2018-2022 Intel Corporation
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include "iwl-trans.h"
762306a36Sopenharmony_ci#include "iwl-fh.h"
862306a36Sopenharmony_ci#include "iwl-context-info.h"
962306a36Sopenharmony_ci#include "internal.h"
1062306a36Sopenharmony_ci#include "iwl-prph.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic void *_iwl_pcie_ctxt_info_dma_alloc_coherent(struct iwl_trans *trans,
1362306a36Sopenharmony_ci						    size_t size,
1462306a36Sopenharmony_ci						    dma_addr_t *phys,
1562306a36Sopenharmony_ci						    int depth)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	void *result;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	if (WARN(depth > 2,
2062306a36Sopenharmony_ci		 "failed to allocate DMA memory not crossing 2^32 boundary"))
2162306a36Sopenharmony_ci		return NULL;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	result = dma_alloc_coherent(trans->dev, size, phys, GFP_KERNEL);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	if (!result)
2662306a36Sopenharmony_ci		return NULL;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	if (unlikely(iwl_txq_crosses_4g_boundary(*phys, size))) {
2962306a36Sopenharmony_ci		void *old = result;
3062306a36Sopenharmony_ci		dma_addr_t oldphys = *phys;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci		result = _iwl_pcie_ctxt_info_dma_alloc_coherent(trans, size,
3362306a36Sopenharmony_ci								phys,
3462306a36Sopenharmony_ci								depth + 1);
3562306a36Sopenharmony_ci		dma_free_coherent(trans->dev, size, old, oldphys);
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	return result;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_civoid *iwl_pcie_ctxt_info_dma_alloc_coherent(struct iwl_trans *trans,
4262306a36Sopenharmony_ci					    size_t size,
4362306a36Sopenharmony_ci					    dma_addr_t *phys)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	return _iwl_pcie_ctxt_info_dma_alloc_coherent(trans, size, phys, 0);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ciint iwl_pcie_ctxt_info_alloc_dma(struct iwl_trans *trans,
4962306a36Sopenharmony_ci				 const void *data, u32 len,
5062306a36Sopenharmony_ci				 struct iwl_dram_data *dram)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	dram->block = iwl_pcie_ctxt_info_dma_alloc_coherent(trans, len,
5362306a36Sopenharmony_ci							    &dram->physical);
5462306a36Sopenharmony_ci	if (!dram->block)
5562306a36Sopenharmony_ci		return -ENOMEM;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	dram->size = len;
5862306a36Sopenharmony_ci	memcpy(dram->block, data, len);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	return 0;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_civoid iwl_pcie_ctxt_info_free_paging(struct iwl_trans *trans)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct iwl_self_init_dram *dram = &trans->init_dram;
6662306a36Sopenharmony_ci	int i;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (!dram->paging) {
6962306a36Sopenharmony_ci		WARN_ON(dram->paging_cnt);
7062306a36Sopenharmony_ci		return;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	/* free paging*/
7462306a36Sopenharmony_ci	for (i = 0; i < dram->paging_cnt; i++)
7562306a36Sopenharmony_ci		dma_free_coherent(trans->dev, dram->paging[i].size,
7662306a36Sopenharmony_ci				  dram->paging[i].block,
7762306a36Sopenharmony_ci				  dram->paging[i].physical);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	kfree(dram->paging);
8062306a36Sopenharmony_ci	dram->paging_cnt = 0;
8162306a36Sopenharmony_ci	dram->paging = NULL;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ciint iwl_pcie_init_fw_sec(struct iwl_trans *trans,
8562306a36Sopenharmony_ci			 const struct fw_img *fw,
8662306a36Sopenharmony_ci			 struct iwl_context_info_dram *ctxt_dram)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct iwl_self_init_dram *dram = &trans->init_dram;
8962306a36Sopenharmony_ci	int i, ret, lmac_cnt, umac_cnt, paging_cnt;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	if (WARN(dram->paging,
9262306a36Sopenharmony_ci		 "paging shouldn't already be initialized (%d pages)\n",
9362306a36Sopenharmony_ci		 dram->paging_cnt))
9462306a36Sopenharmony_ci		iwl_pcie_ctxt_info_free_paging(trans);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	lmac_cnt = iwl_pcie_get_num_sections(fw, 0);
9762306a36Sopenharmony_ci	/* add 1 due to separator */
9862306a36Sopenharmony_ci	umac_cnt = iwl_pcie_get_num_sections(fw, lmac_cnt + 1);
9962306a36Sopenharmony_ci	/* add 2 due to separators */
10062306a36Sopenharmony_ci	paging_cnt = iwl_pcie_get_num_sections(fw, lmac_cnt + umac_cnt + 2);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	dram->fw = kcalloc(umac_cnt + lmac_cnt, sizeof(*dram->fw), GFP_KERNEL);
10362306a36Sopenharmony_ci	if (!dram->fw)
10462306a36Sopenharmony_ci		return -ENOMEM;
10562306a36Sopenharmony_ci	dram->paging = kcalloc(paging_cnt, sizeof(*dram->paging), GFP_KERNEL);
10662306a36Sopenharmony_ci	if (!dram->paging)
10762306a36Sopenharmony_ci		return -ENOMEM;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	/* initialize lmac sections */
11062306a36Sopenharmony_ci	for (i = 0; i < lmac_cnt; i++) {
11162306a36Sopenharmony_ci		ret = iwl_pcie_ctxt_info_alloc_dma(trans, fw->sec[i].data,
11262306a36Sopenharmony_ci						   fw->sec[i].len,
11362306a36Sopenharmony_ci						   &dram->fw[dram->fw_cnt]);
11462306a36Sopenharmony_ci		if (ret)
11562306a36Sopenharmony_ci			return ret;
11662306a36Sopenharmony_ci		ctxt_dram->lmac_img[i] =
11762306a36Sopenharmony_ci			cpu_to_le64(dram->fw[dram->fw_cnt].physical);
11862306a36Sopenharmony_ci		dram->fw_cnt++;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/* initialize umac sections */
12262306a36Sopenharmony_ci	for (i = 0; i < umac_cnt; i++) {
12362306a36Sopenharmony_ci		/* access FW with +1 to make up for lmac separator */
12462306a36Sopenharmony_ci		ret = iwl_pcie_ctxt_info_alloc_dma(trans,
12562306a36Sopenharmony_ci						   fw->sec[dram->fw_cnt + 1].data,
12662306a36Sopenharmony_ci						   fw->sec[dram->fw_cnt + 1].len,
12762306a36Sopenharmony_ci						   &dram->fw[dram->fw_cnt]);
12862306a36Sopenharmony_ci		if (ret)
12962306a36Sopenharmony_ci			return ret;
13062306a36Sopenharmony_ci		ctxt_dram->umac_img[i] =
13162306a36Sopenharmony_ci			cpu_to_le64(dram->fw[dram->fw_cnt].physical);
13262306a36Sopenharmony_ci		dram->fw_cnt++;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	/*
13662306a36Sopenharmony_ci	 * Initialize paging.
13762306a36Sopenharmony_ci	 * Paging memory isn't stored in dram->fw as the umac and lmac - it is
13862306a36Sopenharmony_ci	 * stored separately.
13962306a36Sopenharmony_ci	 * This is since the timing of its release is different -
14062306a36Sopenharmony_ci	 * while fw memory can be released on alive, the paging memory can be
14162306a36Sopenharmony_ci	 * freed only when the device goes down.
14262306a36Sopenharmony_ci	 * Given that, the logic here in accessing the fw image is a bit
14362306a36Sopenharmony_ci	 * different - fw_cnt isn't changing so loop counter is added to it.
14462306a36Sopenharmony_ci	 */
14562306a36Sopenharmony_ci	for (i = 0; i < paging_cnt; i++) {
14662306a36Sopenharmony_ci		/* access FW with +2 to make up for lmac & umac separators */
14762306a36Sopenharmony_ci		int fw_idx = dram->fw_cnt + i + 2;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		ret = iwl_pcie_ctxt_info_alloc_dma(trans, fw->sec[fw_idx].data,
15062306a36Sopenharmony_ci						   fw->sec[fw_idx].len,
15162306a36Sopenharmony_ci						   &dram->paging[i]);
15262306a36Sopenharmony_ci		if (ret)
15362306a36Sopenharmony_ci			return ret;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci		ctxt_dram->virtual_img[i] =
15662306a36Sopenharmony_ci			cpu_to_le64(dram->paging[i].physical);
15762306a36Sopenharmony_ci		dram->paging_cnt++;
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	return 0;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ciint iwl_pcie_ctxt_info_init(struct iwl_trans *trans,
16462306a36Sopenharmony_ci			    const struct fw_img *fw)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
16762306a36Sopenharmony_ci	struct iwl_context_info *ctxt_info;
16862306a36Sopenharmony_ci	struct iwl_context_info_rbd_cfg *rx_cfg;
16962306a36Sopenharmony_ci	u32 control_flags = 0, rb_size;
17062306a36Sopenharmony_ci	dma_addr_t phys;
17162306a36Sopenharmony_ci	int ret;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	ctxt_info = iwl_pcie_ctxt_info_dma_alloc_coherent(trans,
17462306a36Sopenharmony_ci							  sizeof(*ctxt_info),
17562306a36Sopenharmony_ci							  &phys);
17662306a36Sopenharmony_ci	if (!ctxt_info)
17762306a36Sopenharmony_ci		return -ENOMEM;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	trans_pcie->ctxt_info_dma_addr = phys;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	ctxt_info->version.version = 0;
18262306a36Sopenharmony_ci	ctxt_info->version.mac_id =
18362306a36Sopenharmony_ci		cpu_to_le16((u16)iwl_read32(trans, CSR_HW_REV));
18462306a36Sopenharmony_ci	/* size is in DWs */
18562306a36Sopenharmony_ci	ctxt_info->version.size = cpu_to_le16(sizeof(*ctxt_info) / 4);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	switch (trans_pcie->rx_buf_size) {
18862306a36Sopenharmony_ci	case IWL_AMSDU_2K:
18962306a36Sopenharmony_ci		rb_size = IWL_CTXT_INFO_RB_SIZE_2K;
19062306a36Sopenharmony_ci		break;
19162306a36Sopenharmony_ci	case IWL_AMSDU_4K:
19262306a36Sopenharmony_ci		rb_size = IWL_CTXT_INFO_RB_SIZE_4K;
19362306a36Sopenharmony_ci		break;
19462306a36Sopenharmony_ci	case IWL_AMSDU_8K:
19562306a36Sopenharmony_ci		rb_size = IWL_CTXT_INFO_RB_SIZE_8K;
19662306a36Sopenharmony_ci		break;
19762306a36Sopenharmony_ci	case IWL_AMSDU_12K:
19862306a36Sopenharmony_ci		rb_size = IWL_CTXT_INFO_RB_SIZE_16K;
19962306a36Sopenharmony_ci		break;
20062306a36Sopenharmony_ci	default:
20162306a36Sopenharmony_ci		WARN_ON(1);
20262306a36Sopenharmony_ci		rb_size = IWL_CTXT_INFO_RB_SIZE_4K;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	WARN_ON(RX_QUEUE_CB_SIZE(trans->cfg->num_rbds) > 12);
20662306a36Sopenharmony_ci	control_flags = IWL_CTXT_INFO_TFD_FORMAT_LONG;
20762306a36Sopenharmony_ci	control_flags |=
20862306a36Sopenharmony_ci		u32_encode_bits(RX_QUEUE_CB_SIZE(trans->cfg->num_rbds),
20962306a36Sopenharmony_ci				IWL_CTXT_INFO_RB_CB_SIZE);
21062306a36Sopenharmony_ci	control_flags |= u32_encode_bits(rb_size, IWL_CTXT_INFO_RB_SIZE);
21162306a36Sopenharmony_ci	ctxt_info->control.control_flags = cpu_to_le32(control_flags);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* initialize RX default queue */
21462306a36Sopenharmony_ci	rx_cfg = &ctxt_info->rbd_cfg;
21562306a36Sopenharmony_ci	rx_cfg->free_rbd_addr = cpu_to_le64(trans_pcie->rxq->bd_dma);
21662306a36Sopenharmony_ci	rx_cfg->used_rbd_addr = cpu_to_le64(trans_pcie->rxq->used_bd_dma);
21762306a36Sopenharmony_ci	rx_cfg->status_wr_ptr = cpu_to_le64(trans_pcie->rxq->rb_stts_dma);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	/* initialize TX command queue */
22062306a36Sopenharmony_ci	ctxt_info->hcmd_cfg.cmd_queue_addr =
22162306a36Sopenharmony_ci		cpu_to_le64(trans->txqs.txq[trans->txqs.cmd.q_id]->dma_addr);
22262306a36Sopenharmony_ci	ctxt_info->hcmd_cfg.cmd_queue_size =
22362306a36Sopenharmony_ci		TFD_QUEUE_CB_SIZE(IWL_CMD_QUEUE_SIZE);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/* allocate ucode sections in dram and set addresses */
22662306a36Sopenharmony_ci	ret = iwl_pcie_init_fw_sec(trans, fw, &ctxt_info->dram);
22762306a36Sopenharmony_ci	if (ret) {
22862306a36Sopenharmony_ci		dma_free_coherent(trans->dev, sizeof(*trans_pcie->ctxt_info),
22962306a36Sopenharmony_ci				  ctxt_info, trans_pcie->ctxt_info_dma_addr);
23062306a36Sopenharmony_ci		return ret;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	trans_pcie->ctxt_info = ctxt_info;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	iwl_enable_fw_load_int_ctx_info(trans);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	/* Configure debug, if exists */
23862306a36Sopenharmony_ci	if (iwl_pcie_dbg_on(trans))
23962306a36Sopenharmony_ci		iwl_pcie_apply_destination(trans);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* kick FW self load */
24262306a36Sopenharmony_ci	iwl_write64(trans, CSR_CTXT_INFO_BA, trans_pcie->ctxt_info_dma_addr);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* Context info will be released upon alive or failure to get one */
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	return 0;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_civoid iwl_pcie_ctxt_info_free(struct iwl_trans *trans)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	if (!trans_pcie->ctxt_info)
25462306a36Sopenharmony_ci		return;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	dma_free_coherent(trans->dev, sizeof(*trans_pcie->ctxt_info),
25762306a36Sopenharmony_ci			  trans_pcie->ctxt_info,
25862306a36Sopenharmony_ci			  trans_pcie->ctxt_info_dma_addr);
25962306a36Sopenharmony_ci	trans_pcie->ctxt_info_dma_addr = 0;
26062306a36Sopenharmony_ci	trans_pcie->ctxt_info = NULL;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	iwl_pcie_ctxt_info_free_fw_img(trans);
26362306a36Sopenharmony_ci}
264