162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2018-2023 Intel Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include "iwl-trans.h"
662306a36Sopenharmony_ci#include "iwl-fh.h"
762306a36Sopenharmony_ci#include "iwl-context-info-gen3.h"
862306a36Sopenharmony_ci#include "internal.h"
962306a36Sopenharmony_ci#include "iwl-prph.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic void
1262306a36Sopenharmony_ciiwl_pcie_ctxt_info_dbg_enable(struct iwl_trans *trans,
1362306a36Sopenharmony_ci			      struct iwl_prph_scratch_hwm_cfg *dbg_cfg,
1462306a36Sopenharmony_ci			      u32 *control_flags)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	enum iwl_fw_ini_allocation_id alloc_id = IWL_FW_INI_ALLOCATION_ID_DBGC1;
1762306a36Sopenharmony_ci	struct iwl_fw_ini_allocation_tlv *fw_mon_cfg;
1862306a36Sopenharmony_ci	u32 dbg_flags = 0;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	if (!iwl_trans_dbg_ini_valid(trans)) {
2162306a36Sopenharmony_ci		struct iwl_dram_data *fw_mon = &trans->dbg.fw_mon;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci		iwl_pcie_alloc_fw_monitor(trans, 0);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci		if (fw_mon->size) {
2662306a36Sopenharmony_ci			dbg_flags |= IWL_PRPH_SCRATCH_EDBG_DEST_DRAM;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci			IWL_DEBUG_FW(trans,
2962306a36Sopenharmony_ci				     "WRT: Applying DRAM buffer destination\n");
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci			dbg_cfg->hwm_base_addr = cpu_to_le64(fw_mon->physical);
3262306a36Sopenharmony_ci			dbg_cfg->hwm_size = cpu_to_le32(fw_mon->size);
3362306a36Sopenharmony_ci		}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci		goto out;
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	fw_mon_cfg = &trans->dbg.fw_mon_cfg[alloc_id];
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	switch (le32_to_cpu(fw_mon_cfg->buf_location)) {
4162306a36Sopenharmony_ci	case IWL_FW_INI_LOCATION_SRAM_PATH:
4262306a36Sopenharmony_ci		dbg_flags |= IWL_PRPH_SCRATCH_EDBG_DEST_INTERNAL;
4362306a36Sopenharmony_ci		IWL_DEBUG_FW(trans,
4462306a36Sopenharmony_ci				"WRT: Applying SMEM buffer destination\n");
4562306a36Sopenharmony_ci		break;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	case IWL_FW_INI_LOCATION_NPK_PATH:
4862306a36Sopenharmony_ci		dbg_flags |= IWL_PRPH_SCRATCH_EDBG_DEST_TB22DTF;
4962306a36Sopenharmony_ci		IWL_DEBUG_FW(trans,
5062306a36Sopenharmony_ci			     "WRT: Applying NPK buffer destination\n");
5162306a36Sopenharmony_ci		break;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	case IWL_FW_INI_LOCATION_DRAM_PATH:
5462306a36Sopenharmony_ci		if (trans->dbg.fw_mon_ini[alloc_id].num_frags) {
5562306a36Sopenharmony_ci			struct iwl_dram_data *frag =
5662306a36Sopenharmony_ci				&trans->dbg.fw_mon_ini[alloc_id].frags[0];
5762306a36Sopenharmony_ci			dbg_flags |= IWL_PRPH_SCRATCH_EDBG_DEST_DRAM;
5862306a36Sopenharmony_ci			dbg_cfg->hwm_base_addr = cpu_to_le64(frag->physical);
5962306a36Sopenharmony_ci			dbg_cfg->hwm_size = cpu_to_le32(frag->size);
6062306a36Sopenharmony_ci			dbg_cfg->debug_token_config = cpu_to_le32(trans->dbg.ucode_preset);
6162306a36Sopenharmony_ci			IWL_DEBUG_FW(trans,
6262306a36Sopenharmony_ci				     "WRT: Applying DRAM destination (debug_token_config=%u)\n",
6362306a36Sopenharmony_ci				     dbg_cfg->debug_token_config);
6462306a36Sopenharmony_ci			IWL_DEBUG_FW(trans,
6562306a36Sopenharmony_ci				     "WRT: Applying DRAM destination (alloc_id=%u, num_frags=%u)\n",
6662306a36Sopenharmony_ci				     alloc_id,
6762306a36Sopenharmony_ci				     trans->dbg.fw_mon_ini[alloc_id].num_frags);
6862306a36Sopenharmony_ci		}
6962306a36Sopenharmony_ci		break;
7062306a36Sopenharmony_ci	default:
7162306a36Sopenharmony_ci		IWL_ERR(trans, "WRT: Invalid buffer destination\n");
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ciout:
7462306a36Sopenharmony_ci	if (dbg_flags)
7562306a36Sopenharmony_ci		*control_flags |= IWL_PRPH_SCRATCH_EARLY_DEBUG_EN | dbg_flags;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ciint iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
7962306a36Sopenharmony_ci				 const struct fw_img *fw)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
8262306a36Sopenharmony_ci	struct iwl_context_info_gen3 *ctxt_info_gen3;
8362306a36Sopenharmony_ci	struct iwl_prph_scratch *prph_scratch;
8462306a36Sopenharmony_ci	struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl;
8562306a36Sopenharmony_ci	struct iwl_prph_info *prph_info;
8662306a36Sopenharmony_ci	u32 control_flags = 0;
8762306a36Sopenharmony_ci	int ret;
8862306a36Sopenharmony_ci	int cmdq_size = max_t(u32, IWL_CMD_QUEUE_SIZE,
8962306a36Sopenharmony_ci			      trans->cfg->min_txq_size);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	switch (trans_pcie->rx_buf_size) {
9262306a36Sopenharmony_ci	case IWL_AMSDU_DEF:
9362306a36Sopenharmony_ci		return -EINVAL;
9462306a36Sopenharmony_ci	case IWL_AMSDU_2K:
9562306a36Sopenharmony_ci		break;
9662306a36Sopenharmony_ci	case IWL_AMSDU_4K:
9762306a36Sopenharmony_ci		control_flags |= IWL_PRPH_SCRATCH_RB_SIZE_4K;
9862306a36Sopenharmony_ci		break;
9962306a36Sopenharmony_ci	case IWL_AMSDU_8K:
10062306a36Sopenharmony_ci		control_flags |= IWL_PRPH_SCRATCH_RB_SIZE_4K;
10162306a36Sopenharmony_ci		/* if firmware supports the ext size, tell it */
10262306a36Sopenharmony_ci		control_flags |= IWL_PRPH_SCRATCH_RB_SIZE_EXT_8K;
10362306a36Sopenharmony_ci		break;
10462306a36Sopenharmony_ci	case IWL_AMSDU_12K:
10562306a36Sopenharmony_ci		control_flags |= IWL_PRPH_SCRATCH_RB_SIZE_4K;
10662306a36Sopenharmony_ci		/* if firmware supports the ext size, tell it */
10762306a36Sopenharmony_ci		control_flags |= IWL_PRPH_SCRATCH_RB_SIZE_EXT_16K;
10862306a36Sopenharmony_ci		break;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* Allocate prph scratch */
11262306a36Sopenharmony_ci	prph_scratch = dma_alloc_coherent(trans->dev, sizeof(*prph_scratch),
11362306a36Sopenharmony_ci					  &trans_pcie->prph_scratch_dma_addr,
11462306a36Sopenharmony_ci					  GFP_KERNEL);
11562306a36Sopenharmony_ci	if (!prph_scratch)
11662306a36Sopenharmony_ci		return -ENOMEM;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	prph_sc_ctrl = &prph_scratch->ctrl_cfg;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	prph_sc_ctrl->version.version = 0;
12162306a36Sopenharmony_ci	prph_sc_ctrl->version.mac_id =
12262306a36Sopenharmony_ci		cpu_to_le16((u16)iwl_read32(trans, CSR_HW_REV));
12362306a36Sopenharmony_ci	prph_sc_ctrl->version.size = cpu_to_le16(sizeof(*prph_scratch) / 4);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	control_flags |= IWL_PRPH_SCRATCH_MTR_MODE;
12662306a36Sopenharmony_ci	control_flags |= IWL_PRPH_MTR_FORMAT_256B & IWL_PRPH_SCRATCH_MTR_FORMAT;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (trans->trans_cfg->imr_enabled)
12962306a36Sopenharmony_ci		control_flags |= IWL_PRPH_SCRATCH_IMR_DEBUG_EN;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* initialize RX default queue */
13262306a36Sopenharmony_ci	prph_sc_ctrl->rbd_cfg.free_rbd_addr =
13362306a36Sopenharmony_ci		cpu_to_le64(trans_pcie->rxq->bd_dma);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	iwl_pcie_ctxt_info_dbg_enable(trans, &prph_sc_ctrl->hwm_cfg,
13662306a36Sopenharmony_ci				      &control_flags);
13762306a36Sopenharmony_ci	prph_sc_ctrl->control.control_flags = cpu_to_le32(control_flags);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	/* initialize the Step equalizer data */
14062306a36Sopenharmony_ci	prph_sc_ctrl->step_cfg.mbx_addr_0 = cpu_to_le32(trans->mbx_addr_0_step);
14162306a36Sopenharmony_ci	prph_sc_ctrl->step_cfg.mbx_addr_1 = cpu_to_le32(trans->mbx_addr_1_step);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	/* allocate ucode sections in dram and set addresses */
14462306a36Sopenharmony_ci	ret = iwl_pcie_init_fw_sec(trans, fw, &prph_scratch->dram);
14562306a36Sopenharmony_ci	if (ret)
14662306a36Sopenharmony_ci		goto err_free_prph_scratch;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* Allocate prph information
15062306a36Sopenharmony_ci	 * currently we don't assign to the prph info anything, but it would get
15162306a36Sopenharmony_ci	 * assigned later
15262306a36Sopenharmony_ci	 *
15362306a36Sopenharmony_ci	 * We also use the second half of this page to give the device some
15462306a36Sopenharmony_ci	 * dummy TR/CR tail pointers - which shouldn't be necessary as we don't
15562306a36Sopenharmony_ci	 * use this, but the hardware still reads/writes there and we can't let
15662306a36Sopenharmony_ci	 * it go do that with a NULL pointer.
15762306a36Sopenharmony_ci	 */
15862306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(*prph_info) > PAGE_SIZE / 2);
15962306a36Sopenharmony_ci	prph_info = dma_alloc_coherent(trans->dev, PAGE_SIZE,
16062306a36Sopenharmony_ci				       &trans_pcie->prph_info_dma_addr,
16162306a36Sopenharmony_ci				       GFP_KERNEL);
16262306a36Sopenharmony_ci	if (!prph_info) {
16362306a36Sopenharmony_ci		ret = -ENOMEM;
16462306a36Sopenharmony_ci		goto err_free_prph_scratch;
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* Allocate context info */
16862306a36Sopenharmony_ci	ctxt_info_gen3 = dma_alloc_coherent(trans->dev,
16962306a36Sopenharmony_ci					    sizeof(*ctxt_info_gen3),
17062306a36Sopenharmony_ci					    &trans_pcie->ctxt_info_dma_addr,
17162306a36Sopenharmony_ci					    GFP_KERNEL);
17262306a36Sopenharmony_ci	if (!ctxt_info_gen3) {
17362306a36Sopenharmony_ci		ret = -ENOMEM;
17462306a36Sopenharmony_ci		goto err_free_prph_info;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	ctxt_info_gen3->prph_info_base_addr =
17862306a36Sopenharmony_ci		cpu_to_le64(trans_pcie->prph_info_dma_addr);
17962306a36Sopenharmony_ci	ctxt_info_gen3->prph_scratch_base_addr =
18062306a36Sopenharmony_ci		cpu_to_le64(trans_pcie->prph_scratch_dma_addr);
18162306a36Sopenharmony_ci	ctxt_info_gen3->prph_scratch_size =
18262306a36Sopenharmony_ci		cpu_to_le32(sizeof(*prph_scratch));
18362306a36Sopenharmony_ci	ctxt_info_gen3->cr_head_idx_arr_base_addr =
18462306a36Sopenharmony_ci		cpu_to_le64(trans_pcie->rxq->rb_stts_dma);
18562306a36Sopenharmony_ci	ctxt_info_gen3->tr_tail_idx_arr_base_addr =
18662306a36Sopenharmony_ci		cpu_to_le64(trans_pcie->prph_info_dma_addr + PAGE_SIZE / 2);
18762306a36Sopenharmony_ci	ctxt_info_gen3->cr_tail_idx_arr_base_addr =
18862306a36Sopenharmony_ci		cpu_to_le64(trans_pcie->prph_info_dma_addr + 3 * PAGE_SIZE / 4);
18962306a36Sopenharmony_ci	ctxt_info_gen3->mtr_base_addr =
19062306a36Sopenharmony_ci		cpu_to_le64(trans->txqs.txq[trans->txqs.cmd.q_id]->dma_addr);
19162306a36Sopenharmony_ci	ctxt_info_gen3->mcr_base_addr =
19262306a36Sopenharmony_ci		cpu_to_le64(trans_pcie->rxq->used_bd_dma);
19362306a36Sopenharmony_ci	ctxt_info_gen3->mtr_size =
19462306a36Sopenharmony_ci		cpu_to_le16(TFD_QUEUE_CB_SIZE(cmdq_size));
19562306a36Sopenharmony_ci	ctxt_info_gen3->mcr_size =
19662306a36Sopenharmony_ci		cpu_to_le16(RX_QUEUE_CB_SIZE(trans->cfg->num_rbds));
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	trans_pcie->ctxt_info_gen3 = ctxt_info_gen3;
19962306a36Sopenharmony_ci	trans_pcie->prph_info = prph_info;
20062306a36Sopenharmony_ci	trans_pcie->prph_scratch = prph_scratch;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* Allocate IML */
20362306a36Sopenharmony_ci	trans_pcie->iml = dma_alloc_coherent(trans->dev, trans->iml_len,
20462306a36Sopenharmony_ci					     &trans_pcie->iml_dma_addr,
20562306a36Sopenharmony_ci					     GFP_KERNEL);
20662306a36Sopenharmony_ci	if (!trans_pcie->iml) {
20762306a36Sopenharmony_ci		ret = -ENOMEM;
20862306a36Sopenharmony_ci		goto err_free_ctxt_info;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	memcpy(trans_pcie->iml, trans->iml, trans->iml_len);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	iwl_enable_fw_load_int_ctx_info(trans);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* kick FW self load */
21662306a36Sopenharmony_ci	iwl_write64(trans, CSR_CTXT_INFO_ADDR,
21762306a36Sopenharmony_ci		    trans_pcie->ctxt_info_dma_addr);
21862306a36Sopenharmony_ci	iwl_write64(trans, CSR_IML_DATA_ADDR,
21962306a36Sopenharmony_ci		    trans_pcie->iml_dma_addr);
22062306a36Sopenharmony_ci	iwl_write32(trans, CSR_IML_SIZE_ADDR, trans->iml_len);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	iwl_set_bit(trans, CSR_CTXT_INFO_BOOT_CTRL,
22362306a36Sopenharmony_ci		    CSR_AUTO_FUNC_BOOT_ENA);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return 0;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cierr_free_ctxt_info:
22862306a36Sopenharmony_ci	dma_free_coherent(trans->dev, sizeof(*trans_pcie->ctxt_info_gen3),
22962306a36Sopenharmony_ci			  trans_pcie->ctxt_info_gen3,
23062306a36Sopenharmony_ci			  trans_pcie->ctxt_info_dma_addr);
23162306a36Sopenharmony_ci	trans_pcie->ctxt_info_gen3 = NULL;
23262306a36Sopenharmony_cierr_free_prph_info:
23362306a36Sopenharmony_ci	dma_free_coherent(trans->dev, PAGE_SIZE, prph_info,
23462306a36Sopenharmony_ci			  trans_pcie->prph_info_dma_addr);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cierr_free_prph_scratch:
23762306a36Sopenharmony_ci	dma_free_coherent(trans->dev,
23862306a36Sopenharmony_ci			  sizeof(*prph_scratch),
23962306a36Sopenharmony_ci			prph_scratch,
24062306a36Sopenharmony_ci			trans_pcie->prph_scratch_dma_addr);
24162306a36Sopenharmony_ci	return ret;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_civoid iwl_pcie_ctxt_info_gen3_free(struct iwl_trans *trans, bool alive)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (trans_pcie->iml) {
25062306a36Sopenharmony_ci		dma_free_coherent(trans->dev, trans->iml_len, trans_pcie->iml,
25162306a36Sopenharmony_ci				  trans_pcie->iml_dma_addr);
25262306a36Sopenharmony_ci		trans_pcie->iml_dma_addr = 0;
25362306a36Sopenharmony_ci		trans_pcie->iml = NULL;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	iwl_pcie_ctxt_info_free_fw_img(trans);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (alive)
25962306a36Sopenharmony_ci		return;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	if (!trans_pcie->ctxt_info_gen3)
26262306a36Sopenharmony_ci		return;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	/* ctxt_info_gen3 and prph_scratch are still needed for PNVM load */
26562306a36Sopenharmony_ci	dma_free_coherent(trans->dev, sizeof(*trans_pcie->ctxt_info_gen3),
26662306a36Sopenharmony_ci			  trans_pcie->ctxt_info_gen3,
26762306a36Sopenharmony_ci			  trans_pcie->ctxt_info_dma_addr);
26862306a36Sopenharmony_ci	trans_pcie->ctxt_info_dma_addr = 0;
26962306a36Sopenharmony_ci	trans_pcie->ctxt_info_gen3 = NULL;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	dma_free_coherent(trans->dev, sizeof(*trans_pcie->prph_scratch),
27262306a36Sopenharmony_ci			  trans_pcie->prph_scratch,
27362306a36Sopenharmony_ci			  trans_pcie->prph_scratch_dma_addr);
27462306a36Sopenharmony_ci	trans_pcie->prph_scratch_dma_addr = 0;
27562306a36Sopenharmony_ci	trans_pcie->prph_scratch = NULL;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	/* this is needed for the entire lifetime */
27862306a36Sopenharmony_ci	dma_free_coherent(trans->dev, PAGE_SIZE, trans_pcie->prph_info,
27962306a36Sopenharmony_ci			  trans_pcie->prph_info_dma_addr);
28062306a36Sopenharmony_ci	trans_pcie->prph_info_dma_addr = 0;
28162306a36Sopenharmony_ci	trans_pcie->prph_info = NULL;
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic int iwl_pcie_load_payloads_continuously(struct iwl_trans *trans,
28562306a36Sopenharmony_ci					       const struct iwl_pnvm_image *pnvm_data,
28662306a36Sopenharmony_ci					       struct iwl_dram_data *dram)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	u32 len, len0, len1;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (pnvm_data->n_chunks != UNFRAGMENTED_PNVM_PAYLOADS_NUMBER) {
29162306a36Sopenharmony_ci		IWL_DEBUG_FW(trans, "expected 2 payloads, got %d.\n",
29262306a36Sopenharmony_ci			     pnvm_data->n_chunks);
29362306a36Sopenharmony_ci		return -EINVAL;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	len0 = pnvm_data->chunks[0].len;
29762306a36Sopenharmony_ci	len1 = pnvm_data->chunks[1].len;
29862306a36Sopenharmony_ci	if (len1 > 0xFFFFFFFF - len0) {
29962306a36Sopenharmony_ci		IWL_DEBUG_FW(trans, "sizes of payloads overflow.\n");
30062306a36Sopenharmony_ci		return -EINVAL;
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci	len = len0 + len1;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	dram->block = iwl_pcie_ctxt_info_dma_alloc_coherent(trans, len,
30562306a36Sopenharmony_ci							    &dram->physical);
30662306a36Sopenharmony_ci	if (!dram->block) {
30762306a36Sopenharmony_ci		IWL_DEBUG_FW(trans, "Failed to allocate PNVM DMA.\n");
30862306a36Sopenharmony_ci		return -ENOMEM;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	dram->size = len;
31262306a36Sopenharmony_ci	memcpy(dram->block, pnvm_data->chunks[0].data, len0);
31362306a36Sopenharmony_ci	memcpy((u8 *)dram->block + len0, pnvm_data->chunks[1].data, len1);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	return 0;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic int iwl_pcie_load_payloads_segments
31962306a36Sopenharmony_ci				(struct iwl_trans *trans,
32062306a36Sopenharmony_ci				 struct iwl_dram_regions *dram_regions,
32162306a36Sopenharmony_ci				 const struct iwl_pnvm_image *pnvm_data)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct iwl_dram_data *cur_payload_dram = &dram_regions->drams[0];
32462306a36Sopenharmony_ci	struct iwl_dram_data *desc_dram = &dram_regions->prph_scratch_mem_desc;
32562306a36Sopenharmony_ci	struct iwl_prph_scrath_mem_desc_addr_array *addresses;
32662306a36Sopenharmony_ci	const void *data;
32762306a36Sopenharmony_ci	u32 len;
32862306a36Sopenharmony_ci	int i;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/* allocate and init DRAM descriptors array */
33162306a36Sopenharmony_ci	len = sizeof(struct iwl_prph_scrath_mem_desc_addr_array);
33262306a36Sopenharmony_ci	desc_dram->block = iwl_pcie_ctxt_info_dma_alloc_coherent
33362306a36Sopenharmony_ci						(trans,
33462306a36Sopenharmony_ci						 len,
33562306a36Sopenharmony_ci						 &desc_dram->physical);
33662306a36Sopenharmony_ci	if (!desc_dram->block) {
33762306a36Sopenharmony_ci		IWL_DEBUG_FW(trans, "Failed to allocate PNVM DMA.\n");
33862306a36Sopenharmony_ci		return -ENOMEM;
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci	desc_dram->size = len;
34162306a36Sopenharmony_ci	memset(desc_dram->block, 0, len);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/* allocate DRAM region for each payload */
34462306a36Sopenharmony_ci	dram_regions->n_regions = 0;
34562306a36Sopenharmony_ci	for (i = 0; i < pnvm_data->n_chunks; i++) {
34662306a36Sopenharmony_ci		len = pnvm_data->chunks[i].len;
34762306a36Sopenharmony_ci		data = pnvm_data->chunks[i].data;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci		if (iwl_pcie_ctxt_info_alloc_dma(trans,
35062306a36Sopenharmony_ci						 data,
35162306a36Sopenharmony_ci						 len,
35262306a36Sopenharmony_ci						 cur_payload_dram)) {
35362306a36Sopenharmony_ci			iwl_trans_pcie_free_pnvm_dram_regions(dram_regions,
35462306a36Sopenharmony_ci							      trans->dev);
35562306a36Sopenharmony_ci			return -ENOMEM;
35662306a36Sopenharmony_ci		}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci		dram_regions->n_regions++;
35962306a36Sopenharmony_ci		cur_payload_dram++;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	/* fill desc with the DRAM payloads addresses */
36362306a36Sopenharmony_ci	addresses = desc_dram->block;
36462306a36Sopenharmony_ci	for (i = 0; i < pnvm_data->n_chunks; i++) {
36562306a36Sopenharmony_ci		addresses->mem_descs[i] =
36662306a36Sopenharmony_ci			cpu_to_le64(dram_regions->drams[i].physical);
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	return 0;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ciint iwl_trans_pcie_ctx_info_gen3_load_pnvm(struct iwl_trans *trans,
37462306a36Sopenharmony_ci					   const struct iwl_pnvm_image *pnvm_payloads,
37562306a36Sopenharmony_ci					   const struct iwl_ucode_capabilities *capa)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
37862306a36Sopenharmony_ci	struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl =
37962306a36Sopenharmony_ci		&trans_pcie->prph_scratch->ctrl_cfg;
38062306a36Sopenharmony_ci	struct iwl_dram_regions *dram_regions = &trans_pcie->pnvm_data;
38162306a36Sopenharmony_ci	int ret = 0;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/* only allocate the DRAM if not allocated yet */
38462306a36Sopenharmony_ci	if (trans->pnvm_loaded)
38562306a36Sopenharmony_ci		return 0;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	if (WARN_ON(prph_sc_ctrl->pnvm_cfg.pnvm_size))
38862306a36Sopenharmony_ci		return -EBUSY;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
39162306a36Sopenharmony_ci		return 0;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (!pnvm_payloads->n_chunks) {
39462306a36Sopenharmony_ci		IWL_DEBUG_FW(trans, "no payloads\n");
39562306a36Sopenharmony_ci		return -EINVAL;
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	/* save payloads in several DRAM sections */
39962306a36Sopenharmony_ci	if (fw_has_capa(capa, IWL_UCODE_TLV_CAPA_FRAGMENTED_PNVM_IMG)) {
40062306a36Sopenharmony_ci		ret = iwl_pcie_load_payloads_segments(trans,
40162306a36Sopenharmony_ci						      dram_regions,
40262306a36Sopenharmony_ci						      pnvm_payloads);
40362306a36Sopenharmony_ci		if (!ret)
40462306a36Sopenharmony_ci			trans->pnvm_loaded = true;
40562306a36Sopenharmony_ci	} else {
40662306a36Sopenharmony_ci		/* save only in one DRAM section */
40762306a36Sopenharmony_ci		ret = iwl_pcie_load_payloads_continuously
40862306a36Sopenharmony_ci						(trans,
40962306a36Sopenharmony_ci						 pnvm_payloads,
41062306a36Sopenharmony_ci						 &dram_regions->drams[0]);
41162306a36Sopenharmony_ci		if (!ret) {
41262306a36Sopenharmony_ci			dram_regions->n_regions = 1;
41362306a36Sopenharmony_ci			trans->pnvm_loaded = true;
41462306a36Sopenharmony_ci		}
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	return ret;
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic inline size_t
42162306a36Sopenharmony_ciiwl_dram_regions_size(const struct iwl_dram_regions *dram_regions)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	size_t total_size = 0;
42462306a36Sopenharmony_ci	int i;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	for (i = 0; i < dram_regions->n_regions; i++)
42762306a36Sopenharmony_ci		total_size += dram_regions->drams[i].size;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	return total_size;
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic void iwl_pcie_set_pnvm_segments(struct iwl_trans *trans)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
43562306a36Sopenharmony_ci	struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl =
43662306a36Sopenharmony_ci		&trans_pcie->prph_scratch->ctrl_cfg;
43762306a36Sopenharmony_ci	struct iwl_dram_regions *dram_regions = &trans_pcie->pnvm_data;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	prph_sc_ctrl->pnvm_cfg.pnvm_base_addr =
44062306a36Sopenharmony_ci		cpu_to_le64(dram_regions->prph_scratch_mem_desc.physical);
44162306a36Sopenharmony_ci	prph_sc_ctrl->pnvm_cfg.pnvm_size =
44262306a36Sopenharmony_ci		cpu_to_le32(iwl_dram_regions_size(dram_regions));
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_cistatic void iwl_pcie_set_continuous_pnvm(struct iwl_trans *trans)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
44862306a36Sopenharmony_ci	struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl =
44962306a36Sopenharmony_ci		&trans_pcie->prph_scratch->ctrl_cfg;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	prph_sc_ctrl->pnvm_cfg.pnvm_base_addr =
45262306a36Sopenharmony_ci		cpu_to_le64(trans_pcie->pnvm_data.drams[0].physical);
45362306a36Sopenharmony_ci	prph_sc_ctrl->pnvm_cfg.pnvm_size =
45462306a36Sopenharmony_ci		cpu_to_le32(trans_pcie->pnvm_data.drams[0].size);
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_civoid iwl_trans_pcie_ctx_info_gen3_set_pnvm(struct iwl_trans *trans,
45862306a36Sopenharmony_ci					   const struct iwl_ucode_capabilities *capa)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
46162306a36Sopenharmony_ci		return;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (fw_has_capa(capa, IWL_UCODE_TLV_CAPA_FRAGMENTED_PNVM_IMG))
46462306a36Sopenharmony_ci		iwl_pcie_set_pnvm_segments(trans);
46562306a36Sopenharmony_ci	else
46662306a36Sopenharmony_ci		iwl_pcie_set_continuous_pnvm(trans);
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ciint iwl_trans_pcie_ctx_info_gen3_load_reduce_power(struct iwl_trans *trans,
47062306a36Sopenharmony_ci						   const struct iwl_pnvm_image *payloads,
47162306a36Sopenharmony_ci						   const struct iwl_ucode_capabilities *capa)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
47462306a36Sopenharmony_ci	struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl =
47562306a36Sopenharmony_ci		&trans_pcie->prph_scratch->ctrl_cfg;
47662306a36Sopenharmony_ci	struct iwl_dram_regions *dram_regions = &trans_pcie->reduced_tables_data;
47762306a36Sopenharmony_ci	int ret = 0;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	/* only allocate the DRAM if not allocated yet */
48062306a36Sopenharmony_ci	if (trans->reduce_power_loaded)
48162306a36Sopenharmony_ci		return 0;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
48462306a36Sopenharmony_ci		return 0;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	if (WARN_ON(prph_sc_ctrl->reduce_power_cfg.size))
48762306a36Sopenharmony_ci		return -EBUSY;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (!payloads->n_chunks) {
49062306a36Sopenharmony_ci		IWL_DEBUG_FW(trans, "no payloads\n");
49162306a36Sopenharmony_ci		return -EINVAL;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	/* save payloads in several DRAM sections */
49562306a36Sopenharmony_ci	if (fw_has_capa(capa, IWL_UCODE_TLV_CAPA_FRAGMENTED_PNVM_IMG)) {
49662306a36Sopenharmony_ci		ret = iwl_pcie_load_payloads_segments(trans,
49762306a36Sopenharmony_ci						      dram_regions,
49862306a36Sopenharmony_ci						      payloads);
49962306a36Sopenharmony_ci		if (!ret)
50062306a36Sopenharmony_ci			trans->reduce_power_loaded = true;
50162306a36Sopenharmony_ci	} else {
50262306a36Sopenharmony_ci		/* save only in one DRAM section */
50362306a36Sopenharmony_ci		ret = iwl_pcie_load_payloads_continuously
50462306a36Sopenharmony_ci						(trans,
50562306a36Sopenharmony_ci						 payloads,
50662306a36Sopenharmony_ci						 &dram_regions->drams[0]);
50762306a36Sopenharmony_ci		if (!ret) {
50862306a36Sopenharmony_ci			dram_regions->n_regions = 1;
50962306a36Sopenharmony_ci			trans->reduce_power_loaded = true;
51062306a36Sopenharmony_ci		}
51162306a36Sopenharmony_ci	}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	return ret;
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic void iwl_pcie_set_reduce_power_segments(struct iwl_trans *trans)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
51962306a36Sopenharmony_ci	struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl =
52062306a36Sopenharmony_ci		&trans_pcie->prph_scratch->ctrl_cfg;
52162306a36Sopenharmony_ci	struct iwl_dram_regions *dram_regions = &trans_pcie->reduced_tables_data;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	prph_sc_ctrl->reduce_power_cfg.base_addr =
52462306a36Sopenharmony_ci		cpu_to_le64(dram_regions->prph_scratch_mem_desc.physical);
52562306a36Sopenharmony_ci	prph_sc_ctrl->reduce_power_cfg.size =
52662306a36Sopenharmony_ci		cpu_to_le32(iwl_dram_regions_size(dram_regions));
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic void iwl_pcie_set_continuous_reduce_power(struct iwl_trans *trans)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
53262306a36Sopenharmony_ci	struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl =
53362306a36Sopenharmony_ci		&trans_pcie->prph_scratch->ctrl_cfg;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	prph_sc_ctrl->reduce_power_cfg.base_addr =
53662306a36Sopenharmony_ci		cpu_to_le64(trans_pcie->reduced_tables_data.drams[0].physical);
53762306a36Sopenharmony_ci	prph_sc_ctrl->reduce_power_cfg.size =
53862306a36Sopenharmony_ci		cpu_to_le32(trans_pcie->reduced_tables_data.drams[0].size);
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_civoid
54262306a36Sopenharmony_ciiwl_trans_pcie_ctx_info_gen3_set_reduce_power(struct iwl_trans *trans,
54362306a36Sopenharmony_ci					      const struct iwl_ucode_capabilities *capa)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
54662306a36Sopenharmony_ci		return;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	if (fw_has_capa(capa, IWL_UCODE_TLV_CAPA_FRAGMENTED_PNVM_IMG))
54962306a36Sopenharmony_ci		iwl_pcie_set_reduce_power_segments(trans);
55062306a36Sopenharmony_ci	else
55162306a36Sopenharmony_ci		iwl_pcie_set_continuous_reduce_power(trans);
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
554