18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2016-2018 Linaro Ltd.
48c2ecf20Sopenharmony_ci * Copyright (C) 2014 Sony Mobile Communications AB
58c2ecf20Sopenharmony_ci * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/of_reserved_mem.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <linux/regmap.h>
148c2ecf20Sopenharmony_ci#include <linux/reset.h>
158c2ecf20Sopenharmony_ci#include <linux/soc/qcom/mdt_loader.h>
168c2ecf20Sopenharmony_ci#include "qcom_common.h"
178c2ecf20Sopenharmony_ci#include "qcom_pil_info.h"
188c2ecf20Sopenharmony_ci#include "qcom_q6v5.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define WCSS_CRASH_REASON		421
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* Q6SS Register Offsets */
238c2ecf20Sopenharmony_ci#define Q6SS_RESET_REG		0x014
248c2ecf20Sopenharmony_ci#define Q6SS_GFMUX_CTL_REG		0x020
258c2ecf20Sopenharmony_ci#define Q6SS_PWR_CTL_REG		0x030
268c2ecf20Sopenharmony_ci#define Q6SS_MEM_PWR_CTL		0x0B0
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* AXI Halt Register Offsets */
298c2ecf20Sopenharmony_ci#define AXI_HALTREQ_REG			0x0
308c2ecf20Sopenharmony_ci#define AXI_HALTACK_REG			0x4
318c2ecf20Sopenharmony_ci#define AXI_IDLE_REG			0x8
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define HALT_ACK_TIMEOUT_MS		100
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* Q6SS_RESET */
368c2ecf20Sopenharmony_ci#define Q6SS_STOP_CORE			BIT(0)
378c2ecf20Sopenharmony_ci#define Q6SS_CORE_ARES			BIT(1)
388c2ecf20Sopenharmony_ci#define Q6SS_BUS_ARES_ENABLE		BIT(2)
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/* Q6SS_GFMUX_CTL */
418c2ecf20Sopenharmony_ci#define Q6SS_CLK_ENABLE			BIT(1)
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* Q6SS_PWR_CTL */
448c2ecf20Sopenharmony_ci#define Q6SS_L2DATA_STBY_N		BIT(18)
458c2ecf20Sopenharmony_ci#define Q6SS_SLP_RET_N			BIT(19)
468c2ecf20Sopenharmony_ci#define Q6SS_CLAMP_IO			BIT(20)
478c2ecf20Sopenharmony_ci#define QDSS_BHS_ON			BIT(21)
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* Q6SS parameters */
508c2ecf20Sopenharmony_ci#define Q6SS_LDO_BYP		BIT(25)
518c2ecf20Sopenharmony_ci#define Q6SS_BHS_ON		BIT(24)
528c2ecf20Sopenharmony_ci#define Q6SS_CLAMP_WL		BIT(21)
538c2ecf20Sopenharmony_ci#define Q6SS_CLAMP_QMC_MEM		BIT(22)
548c2ecf20Sopenharmony_ci#define HALT_CHECK_MAX_LOOPS		200
558c2ecf20Sopenharmony_ci#define Q6SS_XO_CBCR		GENMASK(5, 3)
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* Q6SS config/status registers */
588c2ecf20Sopenharmony_ci#define TCSR_GLOBAL_CFG0	0x0
598c2ecf20Sopenharmony_ci#define TCSR_GLOBAL_CFG1	0x4
608c2ecf20Sopenharmony_ci#define SSCAON_CONFIG		0x8
618c2ecf20Sopenharmony_ci#define SSCAON_STATUS		0xc
628c2ecf20Sopenharmony_ci#define Q6SS_BHS_STATUS		0x78
638c2ecf20Sopenharmony_ci#define Q6SS_RST_EVB		0x10
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define BHS_EN_REST_ACK		BIT(0)
668c2ecf20Sopenharmony_ci#define SSCAON_ENABLE		BIT(13)
678c2ecf20Sopenharmony_ci#define SSCAON_BUS_EN		BIT(15)
688c2ecf20Sopenharmony_ci#define SSCAON_BUS_MUX_MASK	GENMASK(18, 16)
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#define MEM_BANKS		19
718c2ecf20Sopenharmony_ci#define TCSR_WCSS_CLK_MASK	0x1F
728c2ecf20Sopenharmony_ci#define TCSR_WCSS_CLK_ENABLE	0x14
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistruct q6v5_wcss {
758c2ecf20Sopenharmony_ci	struct device *dev;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	void __iomem *reg_base;
788c2ecf20Sopenharmony_ci	void __iomem *rmb_base;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	struct regmap *halt_map;
818c2ecf20Sopenharmony_ci	u32 halt_q6;
828c2ecf20Sopenharmony_ci	u32 halt_wcss;
838c2ecf20Sopenharmony_ci	u32 halt_nc;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	struct reset_control *wcss_aon_reset;
868c2ecf20Sopenharmony_ci	struct reset_control *wcss_reset;
878c2ecf20Sopenharmony_ci	struct reset_control *wcss_q6_reset;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	struct qcom_q6v5 q6v5;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	phys_addr_t mem_phys;
928c2ecf20Sopenharmony_ci	phys_addr_t mem_reloc;
938c2ecf20Sopenharmony_ci	void *mem_region;
948c2ecf20Sopenharmony_ci	size_t mem_size;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	struct qcom_rproc_glink glink_subdev;
978c2ecf20Sopenharmony_ci	struct qcom_rproc_ssr ssr_subdev;
988c2ecf20Sopenharmony_ci};
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic int q6v5_wcss_reset(struct q6v5_wcss *wcss)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	int ret;
1038c2ecf20Sopenharmony_ci	u32 val;
1048c2ecf20Sopenharmony_ci	int i;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* Assert resets, stop core */
1078c2ecf20Sopenharmony_ci	val = readl(wcss->reg_base + Q6SS_RESET_REG);
1088c2ecf20Sopenharmony_ci	val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE;
1098c2ecf20Sopenharmony_ci	writel(val, wcss->reg_base + Q6SS_RESET_REG);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* BHS require xo cbcr to be enabled */
1128c2ecf20Sopenharmony_ci	val = readl(wcss->reg_base + Q6SS_XO_CBCR);
1138c2ecf20Sopenharmony_ci	val |= 0x1;
1148c2ecf20Sopenharmony_ci	writel(val, wcss->reg_base + Q6SS_XO_CBCR);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/* Read CLKOFF bit to go low indicating CLK is enabled */
1178c2ecf20Sopenharmony_ci	ret = readl_poll_timeout(wcss->reg_base + Q6SS_XO_CBCR,
1188c2ecf20Sopenharmony_ci				 val, !(val & BIT(31)), 1,
1198c2ecf20Sopenharmony_ci				 HALT_CHECK_MAX_LOOPS);
1208c2ecf20Sopenharmony_ci	if (ret) {
1218c2ecf20Sopenharmony_ci		dev_err(wcss->dev,
1228c2ecf20Sopenharmony_ci			"xo cbcr enabling timed out (rc:%d)\n", ret);
1238c2ecf20Sopenharmony_ci		return ret;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci	/* Enable power block headswitch and wait for it to stabilize */
1268c2ecf20Sopenharmony_ci	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
1278c2ecf20Sopenharmony_ci	val |= Q6SS_BHS_ON;
1288c2ecf20Sopenharmony_ci	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
1298c2ecf20Sopenharmony_ci	udelay(1);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	/* Put LDO in bypass mode */
1328c2ecf20Sopenharmony_ci	val |= Q6SS_LDO_BYP;
1338c2ecf20Sopenharmony_ci	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	/* Deassert Q6 compiler memory clamp */
1368c2ecf20Sopenharmony_ci	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
1378c2ecf20Sopenharmony_ci	val &= ~Q6SS_CLAMP_QMC_MEM;
1388c2ecf20Sopenharmony_ci	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* Deassert memory peripheral sleep and L2 memory standby */
1418c2ecf20Sopenharmony_ci	val |= Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N;
1428c2ecf20Sopenharmony_ci	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/* Turn on L1, L2, ETB and JU memories 1 at a time */
1458c2ecf20Sopenharmony_ci	val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
1468c2ecf20Sopenharmony_ci	for (i = MEM_BANKS; i >= 0; i--) {
1478c2ecf20Sopenharmony_ci		val |= BIT(i);
1488c2ecf20Sopenharmony_ci		writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL);
1498c2ecf20Sopenharmony_ci		/*
1508c2ecf20Sopenharmony_ci		 * Read back value to ensure the write is done then
1518c2ecf20Sopenharmony_ci		 * wait for 1us for both memory peripheral and data
1528c2ecf20Sopenharmony_ci		 * array to turn on.
1538c2ecf20Sopenharmony_ci		 */
1548c2ecf20Sopenharmony_ci		val |= readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
1558c2ecf20Sopenharmony_ci		udelay(1);
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci	/* Remove word line clamp */
1588c2ecf20Sopenharmony_ci	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
1598c2ecf20Sopenharmony_ci	val &= ~Q6SS_CLAMP_WL;
1608c2ecf20Sopenharmony_ci	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/* Remove IO clamp */
1638c2ecf20Sopenharmony_ci	val &= ~Q6SS_CLAMP_IO;
1648c2ecf20Sopenharmony_ci	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* Bring core out of reset */
1678c2ecf20Sopenharmony_ci	val = readl(wcss->reg_base + Q6SS_RESET_REG);
1688c2ecf20Sopenharmony_ci	val &= ~Q6SS_CORE_ARES;
1698c2ecf20Sopenharmony_ci	writel(val, wcss->reg_base + Q6SS_RESET_REG);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	/* Turn on core clock */
1728c2ecf20Sopenharmony_ci	val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);
1738c2ecf20Sopenharmony_ci	val |= Q6SS_CLK_ENABLE;
1748c2ecf20Sopenharmony_ci	writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	/* Start core execution */
1778c2ecf20Sopenharmony_ci	val = readl(wcss->reg_base + Q6SS_RESET_REG);
1788c2ecf20Sopenharmony_ci	val &= ~Q6SS_STOP_CORE;
1798c2ecf20Sopenharmony_ci	writel(val, wcss->reg_base + Q6SS_RESET_REG);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return 0;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic int q6v5_wcss_start(struct rproc *rproc)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	struct q6v5_wcss *wcss = rproc->priv;
1878c2ecf20Sopenharmony_ci	int ret;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	qcom_q6v5_prepare(&wcss->q6v5);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	/* Release Q6 and WCSS reset */
1928c2ecf20Sopenharmony_ci	ret = reset_control_deassert(wcss->wcss_reset);
1938c2ecf20Sopenharmony_ci	if (ret) {
1948c2ecf20Sopenharmony_ci		dev_err(wcss->dev, "wcss_reset failed\n");
1958c2ecf20Sopenharmony_ci		return ret;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	ret = reset_control_deassert(wcss->wcss_q6_reset);
1998c2ecf20Sopenharmony_ci	if (ret) {
2008c2ecf20Sopenharmony_ci		dev_err(wcss->dev, "wcss_q6_reset failed\n");
2018c2ecf20Sopenharmony_ci		goto wcss_reset;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	/* Lithium configuration - clock gating and bus arbitration */
2058c2ecf20Sopenharmony_ci	ret = regmap_update_bits(wcss->halt_map,
2068c2ecf20Sopenharmony_ci				 wcss->halt_nc + TCSR_GLOBAL_CFG0,
2078c2ecf20Sopenharmony_ci				 TCSR_WCSS_CLK_MASK,
2088c2ecf20Sopenharmony_ci				 TCSR_WCSS_CLK_ENABLE);
2098c2ecf20Sopenharmony_ci	if (ret)
2108c2ecf20Sopenharmony_ci		goto wcss_q6_reset;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	ret = regmap_update_bits(wcss->halt_map,
2138c2ecf20Sopenharmony_ci				 wcss->halt_nc + TCSR_GLOBAL_CFG1,
2148c2ecf20Sopenharmony_ci				 1, 0);
2158c2ecf20Sopenharmony_ci	if (ret)
2168c2ecf20Sopenharmony_ci		goto wcss_q6_reset;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	/* Write bootaddr to EVB so that Q6WCSS will jump there after reset */
2198c2ecf20Sopenharmony_ci	writel(rproc->bootaddr >> 4, wcss->reg_base + Q6SS_RST_EVB);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	ret = q6v5_wcss_reset(wcss);
2228c2ecf20Sopenharmony_ci	if (ret)
2238c2ecf20Sopenharmony_ci		goto wcss_q6_reset;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	ret = qcom_q6v5_wait_for_start(&wcss->q6v5, 5 * HZ);
2268c2ecf20Sopenharmony_ci	if (ret == -ETIMEDOUT)
2278c2ecf20Sopenharmony_ci		dev_err(wcss->dev, "start timed out\n");
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	return ret;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ciwcss_q6_reset:
2328c2ecf20Sopenharmony_ci	reset_control_assert(wcss->wcss_q6_reset);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ciwcss_reset:
2358c2ecf20Sopenharmony_ci	reset_control_assert(wcss->wcss_reset);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	return ret;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic void q6v5_wcss_halt_axi_port(struct q6v5_wcss *wcss,
2418c2ecf20Sopenharmony_ci				    struct regmap *halt_map,
2428c2ecf20Sopenharmony_ci				    u32 offset)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	unsigned long timeout;
2458c2ecf20Sopenharmony_ci	unsigned int val;
2468c2ecf20Sopenharmony_ci	int ret;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	/* Check if we're already idle */
2498c2ecf20Sopenharmony_ci	ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);
2508c2ecf20Sopenharmony_ci	if (!ret && val)
2518c2ecf20Sopenharmony_ci		return;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	/* Assert halt request */
2548c2ecf20Sopenharmony_ci	regmap_write(halt_map, offset + AXI_HALTREQ_REG, 1);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	/* Wait for halt */
2578c2ecf20Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(HALT_ACK_TIMEOUT_MS);
2588c2ecf20Sopenharmony_ci	for (;;) {
2598c2ecf20Sopenharmony_ci		ret = regmap_read(halt_map, offset + AXI_HALTACK_REG, &val);
2608c2ecf20Sopenharmony_ci		if (ret || val || time_after(jiffies, timeout))
2618c2ecf20Sopenharmony_ci			break;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci		msleep(1);
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);
2678c2ecf20Sopenharmony_ci	if (ret || !val)
2688c2ecf20Sopenharmony_ci		dev_err(wcss->dev, "port failed halt\n");
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	/* Clear halt request (port will remain halted until reset) */
2718c2ecf20Sopenharmony_ci	regmap_write(halt_map, offset + AXI_HALTREQ_REG, 0);
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic int q6v5_wcss_powerdown(struct q6v5_wcss *wcss)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	int ret;
2778c2ecf20Sopenharmony_ci	u32 val;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	/* 1 - Assert WCSS/Q6 HALTREQ */
2808c2ecf20Sopenharmony_ci	q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_wcss);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	/* 2 - Enable WCSSAON_CONFIG */
2838c2ecf20Sopenharmony_ci	val = readl(wcss->rmb_base + SSCAON_CONFIG);
2848c2ecf20Sopenharmony_ci	val |= SSCAON_ENABLE;
2858c2ecf20Sopenharmony_ci	writel(val, wcss->rmb_base + SSCAON_CONFIG);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	/* 3 - Set SSCAON_CONFIG */
2888c2ecf20Sopenharmony_ci	val |= SSCAON_BUS_EN;
2898c2ecf20Sopenharmony_ci	val &= ~SSCAON_BUS_MUX_MASK;
2908c2ecf20Sopenharmony_ci	writel(val, wcss->rmb_base + SSCAON_CONFIG);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	/* 4 - SSCAON_CONFIG 1 */
2938c2ecf20Sopenharmony_ci	val |= BIT(1);
2948c2ecf20Sopenharmony_ci	writel(val, wcss->rmb_base + SSCAON_CONFIG);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	/* 5 - wait for SSCAON_STATUS */
2978c2ecf20Sopenharmony_ci	ret = readl_poll_timeout(wcss->rmb_base + SSCAON_STATUS,
2988c2ecf20Sopenharmony_ci				 val, (val & 0xffff) == 0x400, 1000,
2998c2ecf20Sopenharmony_ci				 HALT_CHECK_MAX_LOOPS);
3008c2ecf20Sopenharmony_ci	if (ret) {
3018c2ecf20Sopenharmony_ci		dev_err(wcss->dev,
3028c2ecf20Sopenharmony_ci			"can't get SSCAON_STATUS rc:%d)\n", ret);
3038c2ecf20Sopenharmony_ci		return ret;
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	/* 6 - De-assert WCSS_AON reset */
3078c2ecf20Sopenharmony_ci	reset_control_assert(wcss->wcss_aon_reset);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/* 7 - Disable WCSSAON_CONFIG 13 */
3108c2ecf20Sopenharmony_ci	val = readl(wcss->rmb_base + SSCAON_CONFIG);
3118c2ecf20Sopenharmony_ci	val &= ~SSCAON_ENABLE;
3128c2ecf20Sopenharmony_ci	writel(val, wcss->rmb_base + SSCAON_CONFIG);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	/* 8 - De-assert WCSS/Q6 HALTREQ */
3158c2ecf20Sopenharmony_ci	reset_control_assert(wcss->wcss_reset);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	return 0;
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic int q6v5_q6_powerdown(struct q6v5_wcss *wcss)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	int ret;
3238c2ecf20Sopenharmony_ci	u32 val;
3248c2ecf20Sopenharmony_ci	int i;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	/* 1 - Halt Q6 bus interface */
3278c2ecf20Sopenharmony_ci	q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_q6);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	/* 2 - Disable Q6 Core clock */
3308c2ecf20Sopenharmony_ci	val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);
3318c2ecf20Sopenharmony_ci	val &= ~Q6SS_CLK_ENABLE;
3328c2ecf20Sopenharmony_ci	writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	/* 3 - Clamp I/O */
3358c2ecf20Sopenharmony_ci	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
3368c2ecf20Sopenharmony_ci	val |= Q6SS_CLAMP_IO;
3378c2ecf20Sopenharmony_ci	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	/* 4 - Clamp WL */
3408c2ecf20Sopenharmony_ci	val |= QDSS_BHS_ON;
3418c2ecf20Sopenharmony_ci	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	/* 5 - Clear Erase standby */
3448c2ecf20Sopenharmony_ci	val &= ~Q6SS_L2DATA_STBY_N;
3458c2ecf20Sopenharmony_ci	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	/* 6 - Clear Sleep RTN */
3488c2ecf20Sopenharmony_ci	val &= ~Q6SS_SLP_RET_N;
3498c2ecf20Sopenharmony_ci	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* 7 - turn off Q6 memory foot/head switch one bank at a time */
3528c2ecf20Sopenharmony_ci	for (i = 0; i < 20; i++) {
3538c2ecf20Sopenharmony_ci		val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
3548c2ecf20Sopenharmony_ci		val &= ~BIT(i);
3558c2ecf20Sopenharmony_ci		writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL);
3568c2ecf20Sopenharmony_ci		mdelay(1);
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	/* 8 - Assert QMC memory RTN */
3608c2ecf20Sopenharmony_ci	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
3618c2ecf20Sopenharmony_ci	val |= Q6SS_CLAMP_QMC_MEM;
3628c2ecf20Sopenharmony_ci	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	/* 9 - Turn off BHS */
3658c2ecf20Sopenharmony_ci	val &= ~Q6SS_BHS_ON;
3668c2ecf20Sopenharmony_ci	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
3678c2ecf20Sopenharmony_ci	udelay(1);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	/* 10 - Wait till BHS Reset is done */
3708c2ecf20Sopenharmony_ci	ret = readl_poll_timeout(wcss->reg_base + Q6SS_BHS_STATUS,
3718c2ecf20Sopenharmony_ci				 val, !(val & BHS_EN_REST_ACK), 1000,
3728c2ecf20Sopenharmony_ci				 HALT_CHECK_MAX_LOOPS);
3738c2ecf20Sopenharmony_ci	if (ret) {
3748c2ecf20Sopenharmony_ci		dev_err(wcss->dev, "BHS_STATUS not OFF (rc:%d)\n", ret);
3758c2ecf20Sopenharmony_ci		return ret;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	/* 11 -  Assert WCSS reset */
3798c2ecf20Sopenharmony_ci	reset_control_assert(wcss->wcss_reset);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	/* 12 - Assert Q6 reset */
3828c2ecf20Sopenharmony_ci	reset_control_assert(wcss->wcss_q6_reset);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	return 0;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic int q6v5_wcss_stop(struct rproc *rproc)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	struct q6v5_wcss *wcss = rproc->priv;
3908c2ecf20Sopenharmony_ci	int ret;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	/* WCSS powerdown */
3938c2ecf20Sopenharmony_ci	ret = qcom_q6v5_request_stop(&wcss->q6v5);
3948c2ecf20Sopenharmony_ci	if (ret == -ETIMEDOUT) {
3958c2ecf20Sopenharmony_ci		dev_err(wcss->dev, "timed out on wait\n");
3968c2ecf20Sopenharmony_ci		return ret;
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	ret = q6v5_wcss_powerdown(wcss);
4008c2ecf20Sopenharmony_ci	if (ret)
4018c2ecf20Sopenharmony_ci		return ret;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	/* Q6 Power down */
4048c2ecf20Sopenharmony_ci	ret = q6v5_q6_powerdown(wcss);
4058c2ecf20Sopenharmony_ci	if (ret)
4068c2ecf20Sopenharmony_ci		return ret;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	qcom_q6v5_unprepare(&wcss->q6v5);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	return 0;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic void *q6v5_wcss_da_to_va(struct rproc *rproc, u64 da, size_t len)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	struct q6v5_wcss *wcss = rproc->priv;
4168c2ecf20Sopenharmony_ci	int offset;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	offset = da - wcss->mem_reloc;
4198c2ecf20Sopenharmony_ci	if (offset < 0 || offset + len > wcss->mem_size)
4208c2ecf20Sopenharmony_ci		return NULL;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	return wcss->mem_region + offset;
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic int q6v5_wcss_load(struct rproc *rproc, const struct firmware *fw)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	struct q6v5_wcss *wcss = rproc->priv;
4288c2ecf20Sopenharmony_ci	int ret;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	ret = qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware,
4318c2ecf20Sopenharmony_ci				    0, wcss->mem_region, wcss->mem_phys,
4328c2ecf20Sopenharmony_ci				    wcss->mem_size, &wcss->mem_reloc);
4338c2ecf20Sopenharmony_ci	if (ret)
4348c2ecf20Sopenharmony_ci		return ret;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	qcom_pil_info_store("wcnss", wcss->mem_phys, wcss->mem_size);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	return ret;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cistatic const struct rproc_ops q6v5_wcss_ops = {
4428c2ecf20Sopenharmony_ci	.start = q6v5_wcss_start,
4438c2ecf20Sopenharmony_ci	.stop = q6v5_wcss_stop,
4448c2ecf20Sopenharmony_ci	.da_to_va = q6v5_wcss_da_to_va,
4458c2ecf20Sopenharmony_ci	.load = q6v5_wcss_load,
4468c2ecf20Sopenharmony_ci	.get_boot_addr = rproc_elf_get_boot_addr,
4478c2ecf20Sopenharmony_ci};
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_cistatic int q6v5_wcss_init_reset(struct q6v5_wcss *wcss)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	struct device *dev = wcss->dev;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	wcss->wcss_aon_reset = devm_reset_control_get(dev, "wcss_aon_reset");
4548c2ecf20Sopenharmony_ci	if (IS_ERR(wcss->wcss_aon_reset)) {
4558c2ecf20Sopenharmony_ci		dev_err(wcss->dev, "unable to acquire wcss_aon_reset\n");
4568c2ecf20Sopenharmony_ci		return PTR_ERR(wcss->wcss_aon_reset);
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	wcss->wcss_reset = devm_reset_control_get(dev, "wcss_reset");
4608c2ecf20Sopenharmony_ci	if (IS_ERR(wcss->wcss_reset)) {
4618c2ecf20Sopenharmony_ci		dev_err(wcss->dev, "unable to acquire wcss_reset\n");
4628c2ecf20Sopenharmony_ci		return PTR_ERR(wcss->wcss_reset);
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	wcss->wcss_q6_reset = devm_reset_control_get(dev, "wcss_q6_reset");
4668c2ecf20Sopenharmony_ci	if (IS_ERR(wcss->wcss_q6_reset)) {
4678c2ecf20Sopenharmony_ci		dev_err(wcss->dev, "unable to acquire wcss_q6_reset\n");
4688c2ecf20Sopenharmony_ci		return PTR_ERR(wcss->wcss_q6_reset);
4698c2ecf20Sopenharmony_ci	}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	return 0;
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cistatic int q6v5_wcss_init_mmio(struct q6v5_wcss *wcss,
4758c2ecf20Sopenharmony_ci			       struct platform_device *pdev)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	struct of_phandle_args args;
4788c2ecf20Sopenharmony_ci	struct resource *res;
4798c2ecf20Sopenharmony_ci	int ret;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6");
4828c2ecf20Sopenharmony_ci	wcss->reg_base = devm_ioremap_resource(&pdev->dev, res);
4838c2ecf20Sopenharmony_ci	if (IS_ERR(wcss->reg_base))
4848c2ecf20Sopenharmony_ci		return PTR_ERR(wcss->reg_base);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb");
4878c2ecf20Sopenharmony_ci	wcss->rmb_base = devm_ioremap_resource(&pdev->dev, res);
4888c2ecf20Sopenharmony_ci	if (IS_ERR(wcss->rmb_base))
4898c2ecf20Sopenharmony_ci		return PTR_ERR(wcss->rmb_base);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
4928c2ecf20Sopenharmony_ci					       "qcom,halt-regs", 3, 0, &args);
4938c2ecf20Sopenharmony_ci	if (ret < 0) {
4948c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n");
4958c2ecf20Sopenharmony_ci		return -EINVAL;
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	wcss->halt_map = syscon_node_to_regmap(args.np);
4998c2ecf20Sopenharmony_ci	of_node_put(args.np);
5008c2ecf20Sopenharmony_ci	if (IS_ERR(wcss->halt_map))
5018c2ecf20Sopenharmony_ci		return PTR_ERR(wcss->halt_map);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	wcss->halt_q6 = args.args[0];
5048c2ecf20Sopenharmony_ci	wcss->halt_wcss = args.args[1];
5058c2ecf20Sopenharmony_ci	wcss->halt_nc = args.args[2];
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	return 0;
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_cistatic int q6v5_alloc_memory_region(struct q6v5_wcss *wcss)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	struct reserved_mem *rmem = NULL;
5138c2ecf20Sopenharmony_ci	struct device_node *node;
5148c2ecf20Sopenharmony_ci	struct device *dev = wcss->dev;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	node = of_parse_phandle(dev->of_node, "memory-region", 0);
5178c2ecf20Sopenharmony_ci	if (node)
5188c2ecf20Sopenharmony_ci		rmem = of_reserved_mem_lookup(node);
5198c2ecf20Sopenharmony_ci	of_node_put(node);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (!rmem) {
5228c2ecf20Sopenharmony_ci		dev_err(dev, "unable to acquire memory-region\n");
5238c2ecf20Sopenharmony_ci		return -EINVAL;
5248c2ecf20Sopenharmony_ci	}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	wcss->mem_phys = rmem->base;
5278c2ecf20Sopenharmony_ci	wcss->mem_reloc = rmem->base;
5288c2ecf20Sopenharmony_ci	wcss->mem_size = rmem->size;
5298c2ecf20Sopenharmony_ci	wcss->mem_region = devm_ioremap_wc(dev, wcss->mem_phys, wcss->mem_size);
5308c2ecf20Sopenharmony_ci	if (!wcss->mem_region) {
5318c2ecf20Sopenharmony_ci		dev_err(dev, "unable to map memory region: %pa+%pa\n",
5328c2ecf20Sopenharmony_ci			&rmem->base, &rmem->size);
5338c2ecf20Sopenharmony_ci		return -EBUSY;
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	return 0;
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic int q6v5_wcss_probe(struct platform_device *pdev)
5408c2ecf20Sopenharmony_ci{
5418c2ecf20Sopenharmony_ci	struct q6v5_wcss *wcss;
5428c2ecf20Sopenharmony_ci	struct rproc *rproc;
5438c2ecf20Sopenharmony_ci	int ret;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_wcss_ops,
5468c2ecf20Sopenharmony_ci			    "IPQ8074/q6_fw.mdt", sizeof(*wcss));
5478c2ecf20Sopenharmony_ci	if (!rproc) {
5488c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to allocate rproc\n");
5498c2ecf20Sopenharmony_ci		return -ENOMEM;
5508c2ecf20Sopenharmony_ci	}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	wcss = rproc->priv;
5538c2ecf20Sopenharmony_ci	wcss->dev = &pdev->dev;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	ret = q6v5_wcss_init_mmio(wcss, pdev);
5568c2ecf20Sopenharmony_ci	if (ret)
5578c2ecf20Sopenharmony_ci		goto free_rproc;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	ret = q6v5_alloc_memory_region(wcss);
5608c2ecf20Sopenharmony_ci	if (ret)
5618c2ecf20Sopenharmony_ci		goto free_rproc;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	ret = q6v5_wcss_init_reset(wcss);
5648c2ecf20Sopenharmony_ci	if (ret)
5658c2ecf20Sopenharmony_ci		goto free_rproc;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	ret = qcom_q6v5_init(&wcss->q6v5, pdev, rproc, WCSS_CRASH_REASON, NULL);
5688c2ecf20Sopenharmony_ci	if (ret)
5698c2ecf20Sopenharmony_ci		goto free_rproc;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	qcom_add_glink_subdev(rproc, &wcss->glink_subdev, "q6wcss");
5728c2ecf20Sopenharmony_ci	qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, "q6wcss");
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	ret = rproc_add(rproc);
5758c2ecf20Sopenharmony_ci	if (ret)
5768c2ecf20Sopenharmony_ci		goto free_rproc;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, rproc);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	return 0;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_cifree_rproc:
5838c2ecf20Sopenharmony_ci	rproc_free(rproc);
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	return ret;
5868c2ecf20Sopenharmony_ci}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_cistatic int q6v5_wcss_remove(struct platform_device *pdev)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	struct rproc *rproc = platform_get_drvdata(pdev);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	rproc_del(rproc);
5938c2ecf20Sopenharmony_ci	rproc_free(rproc);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	return 0;
5968c2ecf20Sopenharmony_ci}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_cistatic const struct of_device_id q6v5_wcss_of_match[] = {
5998c2ecf20Sopenharmony_ci	{ .compatible = "qcom,ipq8074-wcss-pil" },
6008c2ecf20Sopenharmony_ci	{ },
6018c2ecf20Sopenharmony_ci};
6028c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, q6v5_wcss_of_match);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_cistatic struct platform_driver q6v5_wcss_driver = {
6058c2ecf20Sopenharmony_ci	.probe = q6v5_wcss_probe,
6068c2ecf20Sopenharmony_ci	.remove = q6v5_wcss_remove,
6078c2ecf20Sopenharmony_ci	.driver = {
6088c2ecf20Sopenharmony_ci		.name = "qcom-q6v5-wcss-pil",
6098c2ecf20Sopenharmony_ci		.of_match_table = q6v5_wcss_of_match,
6108c2ecf20Sopenharmony_ci	},
6118c2ecf20Sopenharmony_ci};
6128c2ecf20Sopenharmony_cimodule_platform_driver(q6v5_wcss_driver);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Hexagon WCSS Peripheral Image Loader");
6158c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
616