Commit c842379d authored by Sibi Sankar's avatar Sibi Sankar Committed by Bjorn Andersson
Browse files

remoteproc: mss: q6v5-mss: Add modem support on SC7280



Add out of reset sequence support for modem sub-system on SC7280 SoCs.
It requires access to an additional set of qaccept registers, external
power/clk control registers and halt vq6 register to put the modem back
into reset.

Signed-off-by: default avatarSibi Sankar <sibis@codeaurora.org>
Reviewed-by: default avatarStephen Boyd <swboyd@chromium.org>
Signed-off-by: default avatarBjorn Andersson <bjorn.andersson@linaro.org>
Link: https://lore.kernel.org/r/1631886935-14691-6-git-send-email-sibis@codeaurora.org
parent c42c0a5e
Loading
Loading
Loading
Loading
+249 −4
Original line number Diff line number Diff line
@@ -77,6 +77,14 @@

#define HALT_ACK_TIMEOUT_US		100000

/* QACCEPT Register Offsets */
#define QACCEPT_ACCEPT_REG		0x0
#define QACCEPT_ACTIVE_REG		0x4
#define QACCEPT_DENY_REG		0x8
#define QACCEPT_REQ_REG			0xC

#define QACCEPT_TIMEOUT_US		50

/* QDSP6SS_RESET */
#define Q6SS_STOP_CORE			BIT(0)
#define Q6SS_CORE_ARES			BIT(1)
@@ -143,6 +151,9 @@ struct rproc_hexagon_res {
	bool has_alt_reset;
	bool has_mba_logs;
	bool has_spare_reg;
	bool has_qaccept_regs;
	bool has_ext_cntl_regs;
	bool has_vq6;
};

struct q6v5 {
@@ -158,8 +169,18 @@ struct q6v5 {
	u32 halt_q6;
	u32 halt_modem;
	u32 halt_nc;
	u32 halt_vq6;
	u32 conn_box;

	u32 qaccept_mdm;
	u32 qaccept_cx;
	u32 qaccept_axi;

	u32 axim1_clk_off;
	u32 crypto_clk_off;
	u32 force_clk_on;
	u32 rscc_disable;

	struct reset_control *mss_restart;
	struct reset_control *pdc_reset;

@@ -201,6 +222,9 @@ struct q6v5 {
	bool has_alt_reset;
	bool has_mba_logs;
	bool has_spare_reg;
	bool has_qaccept_regs;
	bool has_ext_cntl_regs;
	bool has_vq6;
	int mpss_perm;
	int mba_perm;
	const char *hexagon_mdt_image;
@@ -213,6 +237,7 @@ enum {
	MSS_MSM8996,
	MSS_MSM8998,
	MSS_SC7180,
	MSS_SC7280,
	MSS_SDM845,
};

@@ -473,6 +498,12 @@ static int q6v5_reset_assert(struct q6v5 *qproc)
		regmap_update_bits(qproc->conn_map, qproc->conn_box,
				   AXI_GATING_VALID_OVERRIDE, 0);
		ret = reset_control_deassert(qproc->mss_restart);
	} else if (qproc->has_ext_cntl_regs) {
		regmap_write(qproc->conn_map, qproc->rscc_disable, 0);
		reset_control_assert(qproc->pdc_reset);
		reset_control_assert(qproc->mss_restart);
		reset_control_deassert(qproc->pdc_reset);
		ret = reset_control_deassert(qproc->mss_restart);
	} else {
		ret = reset_control_assert(qproc->mss_restart);
	}
@@ -490,7 +521,7 @@ static int q6v5_reset_deassert(struct q6v5 *qproc)
		ret = reset_control_reset(qproc->mss_restart);
		writel(0, qproc->rmb_base + RMB_MBA_ALT_RESET);
		reset_control_deassert(qproc->pdc_reset);
	} else if (qproc->has_spare_reg) {
	} else if (qproc->has_spare_reg || qproc->has_ext_cntl_regs) {
		ret = reset_control_reset(qproc->mss_restart);
	} else {
		ret = reset_control_deassert(qproc->mss_restart);
@@ -604,7 +635,7 @@ static int q6v5proc_reset(struct q6v5 *qproc)
		}

		goto pbl_wait;
	} else if (qproc->version == MSS_SC7180) {
	} else if (qproc->version == MSS_SC7180 || qproc->version == MSS_SC7280) {
		val = readl(qproc->reg_base + QDSP6SS_SLEEP);
		val |= Q6SS_CBCR_CLKEN;
		writel(val, qproc->reg_base + QDSP6SS_SLEEP);
@@ -787,6 +818,89 @@ static int q6v5proc_reset(struct q6v5 *qproc)
	return ret;
}

static int q6v5proc_enable_qchannel(struct q6v5 *qproc, struct regmap *map, u32 offset)
{
	unsigned int val;
	int ret;

	if (!qproc->has_qaccept_regs)
		return 0;

	if (qproc->has_ext_cntl_regs) {
		regmap_write(qproc->conn_map, qproc->rscc_disable, 0);
		regmap_write(qproc->conn_map, qproc->force_clk_on, 1);

		ret = regmap_read_poll_timeout(qproc->halt_map, qproc->axim1_clk_off, val,
					       !val, 1, Q6SS_CBCR_TIMEOUT_US);
		if (ret) {
			dev_err(qproc->dev, "failed to enable axim1 clock\n");
			return -ETIMEDOUT;
		}
	}

	regmap_write(map, offset + QACCEPT_REQ_REG, 1);

	/* Wait for accept */
	ret = regmap_read_poll_timeout(map, offset + QACCEPT_ACCEPT_REG, val, val, 5,
				       QACCEPT_TIMEOUT_US);
	if (ret) {
		dev_err(qproc->dev, "qchannel enable failed\n");
		return -ETIMEDOUT;
	}

	return 0;
}

static void q6v5proc_disable_qchannel(struct q6v5 *qproc, struct regmap *map, u32 offset)
{
	int ret;
	unsigned int val, retry;
	unsigned int nretry = 10;
	bool takedown_complete = false;

	if (!qproc->has_qaccept_regs)
		return;

	while (!takedown_complete && nretry) {
		nretry--;

		/* Wait for active transactions to complete */
		regmap_read_poll_timeout(map, offset + QACCEPT_ACTIVE_REG, val, !val, 5,
					 QACCEPT_TIMEOUT_US);

		/* Request Q-channel transaction takedown */
		regmap_write(map, offset + QACCEPT_REQ_REG, 0);

		/*
		 * If the request is denied, reset the Q-channel takedown request,
		 * wait for active transactions to complete and retry takedown.
		 */
		retry = 10;
		while (retry) {
			usleep_range(5, 10);
			retry--;
			ret = regmap_read(map, offset + QACCEPT_DENY_REG, &val);
			if (!ret && val) {
				regmap_write(map, offset + QACCEPT_REQ_REG, 1);
				break;
			}

			ret = regmap_read(map, offset + QACCEPT_ACCEPT_REG, &val);
			if (!ret && !val) {
				takedown_complete = true;
				break;
			}
		}

		if (!retry)
			break;
	}

	/* Rely on mss_restart to clear out pending transactions on takedown failure */
	if (!takedown_complete)
		dev_err(qproc->dev, "qchannel takedown failed\n");
}

static void q6v5proc_halt_axi_port(struct q6v5 *qproc,
				   struct regmap *halt_map,
				   u32 offset)
@@ -950,6 +1064,12 @@ static int q6v5_mba_load(struct q6v5 *qproc)
		goto assert_reset;
	}

	ret = q6v5proc_enable_qchannel(qproc, qproc->halt_map, qproc->qaccept_axi);
	if (ret) {
		dev_err(qproc->dev, "failed to enable axi bridge\n");
		goto disable_active_clks;
	}

	/*
	 * Some versions of the MBA firmware will upon boot wipe the MPSS region as well, so provide
	 * the Q6 access to this region.
@@ -996,8 +1116,13 @@ static int q6v5_mba_load(struct q6v5 *qproc)

halt_axi_ports:
	q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6);
	if (qproc->has_vq6)
		q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_vq6);
	q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem);
	q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc);
	q6v5proc_disable_qchannel(qproc, qproc->halt_map, qproc->qaccept_mdm);
	q6v5proc_disable_qchannel(qproc, qproc->halt_map, qproc->qaccept_cx);
	q6v5proc_disable_qchannel(qproc, qproc->halt_map, qproc->qaccept_axi);
	mba_load_err = true;
reclaim_mba:
	xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, true,
@@ -1047,6 +1172,8 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc)
	qproc->dp_size = 0;

	q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6);
	if (qproc->has_vq6)
		q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_vq6);
	q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem);
	q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc);
	if (qproc->version == MSS_MSM8996) {
@@ -1059,6 +1186,24 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc)
		writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
	}

	if (qproc->has_ext_cntl_regs) {
		regmap_write(qproc->conn_map, qproc->rscc_disable, 1);

		ret = regmap_read_poll_timeout(qproc->halt_map, qproc->axim1_clk_off, val,
					       !val, 1, Q6SS_CBCR_TIMEOUT_US);
		if (ret)
			dev_err(qproc->dev, "failed to enable axim1 clock\n");

		ret = regmap_read_poll_timeout(qproc->halt_map, qproc->crypto_clk_off, val,
					       !val, 1, Q6SS_CBCR_TIMEOUT_US);
		if (ret)
			dev_err(qproc->dev, "failed to enable crypto clock\n");
	}

	q6v5proc_disable_qchannel(qproc, qproc->halt_map, qproc->qaccept_mdm);
	q6v5proc_disable_qchannel(qproc, qproc->halt_map, qproc->qaccept_cx);
	q6v5proc_disable_qchannel(qproc, qproc->halt_map, qproc->qaccept_axi);

	q6v5_reset_assert(qproc);

	q6v5_clk_disable(qproc->dev, qproc->reset_clks,
@@ -1471,6 +1616,7 @@ static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev)
{
	struct of_phandle_args args;
	struct resource *res;
	int halt_cell_cnt = 3;
	int ret;

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6");
@@ -1483,8 +1629,11 @@ static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev)
	if (IS_ERR(qproc->rmb_base))
		return PTR_ERR(qproc->rmb_base);

	if (qproc->has_vq6)
		halt_cell_cnt++;

	ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
					       "qcom,halt-regs", 3, 0, &args);
					       "qcom,halt-regs", halt_cell_cnt, 0, &args);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n");
		return -EINVAL;
@@ -1499,6 +1648,52 @@ static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev)
	qproc->halt_modem = args.args[1];
	qproc->halt_nc = args.args[2];

	if (qproc->has_vq6)
		qproc->halt_vq6 = args.args[3];

	if (qproc->has_qaccept_regs) {
		ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
						       "qcom,qaccept-regs",
						       3, 0, &args);
		if (ret < 0) {
			dev_err(&pdev->dev, "failed to parse qaccept-regs\n");
			return -EINVAL;
		}

		qproc->qaccept_mdm = args.args[0];
		qproc->qaccept_cx = args.args[1];
		qproc->qaccept_axi = args.args[2];
	}

	if (qproc->has_ext_cntl_regs) {
		ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
						       "qcom,ext-regs",
						       2, 0, &args);
		if (ret < 0) {
			dev_err(&pdev->dev, "failed to parse ext-regs index 0\n");
			return -EINVAL;
		}

		qproc->conn_map = syscon_node_to_regmap(args.np);
		of_node_put(args.np);
		if (IS_ERR(qproc->conn_map))
			return PTR_ERR(qproc->conn_map);

		qproc->force_clk_on = args.args[0];
		qproc->rscc_disable = args.args[1];

		ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
						       "qcom,ext-regs",
						       2, 1, &args);
		if (ret < 0) {
			dev_err(&pdev->dev, "failed to parse ext-regs index 1\n");
			return -EINVAL;
		}

		qproc->axim1_clk_off = args.args[0];
		qproc->crypto_clk_off = args.args[1];
	}

	if (qproc->has_spare_reg) {
		ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
						       "qcom,spare-regs",
@@ -1590,7 +1785,7 @@ static int q6v5_init_reset(struct q6v5 *qproc)
		return PTR_ERR(qproc->mss_restart);
	}

	if (qproc->has_alt_reset || qproc->has_spare_reg) {
	if (qproc->has_alt_reset || qproc->has_spare_reg || qproc->has_ext_cntl_regs) {
		qproc->pdc_reset = devm_reset_control_get_exclusive(qproc->dev,
								    "pdc_reset");
		if (IS_ERR(qproc->pdc_reset)) {
@@ -1697,6 +1892,9 @@ static int q6v5_probe(struct platform_device *pdev)

	platform_set_drvdata(pdev, qproc);

	qproc->has_qaccept_regs = desc->has_qaccept_regs;
	qproc->has_ext_cntl_regs = desc->has_ext_cntl_regs;
	qproc->has_vq6 = desc->has_vq6;
	qproc->has_spare_reg = desc->has_spare_reg;
	ret = q6v5_init_mem(qproc, pdev);
	if (ret)
@@ -1857,9 +2055,40 @@ static const struct rproc_hexagon_res sc7180_mss = {
	.has_alt_reset = false,
	.has_mba_logs = true,
	.has_spare_reg = true,
	.has_qaccept_regs = false,
	.has_ext_cntl_regs = false,
	.has_vq6 = false,
	.version = MSS_SC7180,
};

static const struct rproc_hexagon_res sc7280_mss = {
	.hexagon_mba_image = "mba.mbn",
	.proxy_clk_names = (char*[]){
		"xo",
		"pka",
		NULL
	},
	.active_clk_names = (char*[]){
		"iface",
		"offline",
		"snoc_axi",
		NULL
	},
	.proxy_pd_names = (char*[]){
		"cx",
		"mss",
		NULL
	},
	.need_mem_protection = true,
	.has_alt_reset = false,
	.has_mba_logs = true,
	.has_spare_reg = false,
	.has_qaccept_regs = true,
	.has_ext_cntl_regs = true,
	.has_vq6 = true,
	.version = MSS_SC7280,
};

static const struct rproc_hexagon_res sdm845_mss = {
	.hexagon_mba_image = "mba.mbn",
	.proxy_clk_names = (char*[]){
@@ -1889,6 +2118,9 @@ static const struct rproc_hexagon_res sdm845_mss = {
	.has_alt_reset = true,
	.has_mba_logs = false,
	.has_spare_reg = false,
	.has_qaccept_regs = false,
	.has_ext_cntl_regs = false,
	.has_vq6 = false,
	.version = MSS_SDM845,
};

@@ -1917,6 +2149,9 @@ static const struct rproc_hexagon_res msm8998_mss = {
	.has_alt_reset = false,
	.has_mba_logs = false,
	.has_spare_reg = false,
	.has_qaccept_regs = false,
	.has_ext_cntl_regs = false,
	.has_vq6 = false,
	.version = MSS_MSM8998,
};

@@ -1948,6 +2183,9 @@ static const struct rproc_hexagon_res msm8996_mss = {
	.has_alt_reset = false,
	.has_mba_logs = false,
	.has_spare_reg = false,
	.has_qaccept_regs = false,
	.has_ext_cntl_regs = false,
	.has_vq6 = false,
	.version = MSS_MSM8996,
};

@@ -1990,6 +2228,9 @@ static const struct rproc_hexagon_res msm8916_mss = {
	.has_alt_reset = false,
	.has_mba_logs = false,
	.has_spare_reg = false,
	.has_qaccept_regs = false,
	.has_ext_cntl_regs = false,
	.has_vq6 = false,
	.version = MSS_MSM8916,
};

@@ -2040,6 +2281,9 @@ static const struct rproc_hexagon_res msm8974_mss = {
	.has_alt_reset = false,
	.has_mba_logs = false,
	.has_spare_reg = false,
	.has_qaccept_regs = false,
	.has_ext_cntl_regs = false,
	.has_vq6 = false,
	.version = MSS_MSM8974,
};

@@ -2050,6 +2294,7 @@ static const struct of_device_id q6v5_of_match[] = {
	{ .compatible = "qcom,msm8996-mss-pil", .data = &msm8996_mss},
	{ .compatible = "qcom,msm8998-mss-pil", .data = &msm8998_mss},
	{ .compatible = "qcom,sc7180-mss-pil", .data = &sc7180_mss},
	{ .compatible = "qcom,sc7280-mss-pil", .data = &sc7280_mss},
	{ .compatible = "qcom,sdm845-mss-pil", .data = &sdm845_mss},
	{ },
};