brintos

brintos / linux-shallow public Read only

0
0
Text · 28.2 KiB · e913dab Raw
1124 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2016-2018 Linaro Ltd.4 * Copyright (C) 2014 Sony Mobile Communications AB5 * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.6 */7#include <linux/clk.h>8#include <linux/delay.h>9#include <linux/io.h>10#include <linux/iopoll.h>11#include <linux/kernel.h>12#include <linux/mfd/syscon.h>13#include <linux/module.h>14#include <linux/of_address.h>15#include <linux/of_reserved_mem.h>16#include <linux/platform_device.h>17#include <linux/regmap.h>18#include <linux/regulator/consumer.h>19#include <linux/reset.h>20#include <linux/soc/qcom/mdt_loader.h>21#include "qcom_common.h"22#include "qcom_pil_info.h"23#include "qcom_q6v5.h"24 25#define WCSS_CRASH_REASON		42126 27/* Q6SS Register Offsets */28#define Q6SS_RESET_REG		0x01429#define Q6SS_GFMUX_CTL_REG		0x02030#define Q6SS_PWR_CTL_REG		0x03031#define Q6SS_MEM_PWR_CTL		0x0B032#define Q6SS_STRAP_ACC			0x11033#define Q6SS_CGC_OVERRIDE		0x03434#define Q6SS_BCR_REG			0x600035 36/* AXI Halt Register Offsets */37#define AXI_HALTREQ_REG			0x038#define AXI_HALTACK_REG			0x439#define AXI_IDLE_REG			0x840 41#define HALT_ACK_TIMEOUT_MS		10042 43/* Q6SS_RESET */44#define Q6SS_STOP_CORE			BIT(0)45#define Q6SS_CORE_ARES			BIT(1)46#define Q6SS_BUS_ARES_ENABLE		BIT(2)47 48/* Q6SS_BRC_RESET */49#define Q6SS_BRC_BLK_ARES		BIT(0)50 51/* Q6SS_GFMUX_CTL */52#define Q6SS_CLK_ENABLE			BIT(1)53#define Q6SS_SWITCH_CLK_SRC		BIT(8)54 55/* Q6SS_PWR_CTL */56#define Q6SS_L2DATA_STBY_N		BIT(18)57#define Q6SS_SLP_RET_N			BIT(19)58#define Q6SS_CLAMP_IO			BIT(20)59#define QDSS_BHS_ON			BIT(21)60#define QDSS_Q6_MEMORIES		GENMASK(15, 0)61 62/* Q6SS parameters */63#define Q6SS_LDO_BYP		BIT(25)64#define Q6SS_BHS_ON		BIT(24)65#define Q6SS_CLAMP_WL		BIT(21)66#define Q6SS_CLAMP_QMC_MEM		BIT(22)67#define HALT_CHECK_MAX_LOOPS		20068#define Q6SS_XO_CBCR		GENMASK(5, 3)69#define Q6SS_SLEEP_CBCR		GENMASK(5, 2)70 71/* Q6SS config/status registers */72#define TCSR_GLOBAL_CFG0	0x073#define TCSR_GLOBAL_CFG1	0x474#define SSCAON_CONFIG		0x875#define SSCAON_STATUS		0xc76#define Q6SS_BHS_STATUS		0x7877#define Q6SS_RST_EVB		0x1078 79#define BHS_EN_REST_ACK		BIT(0)80#define SSCAON_ENABLE		BIT(13)81#define SSCAON_BUS_EN		BIT(15)82#define SSCAON_BUS_MUX_MASK	GENMASK(18, 16)83 84#define MEM_BANKS		1985#define TCSR_WCSS_CLK_MASK	0x1F86#define TCSR_WCSS_CLK_ENABLE	0x1487 88#define MAX_HALT_REG		389enum {90	WCSS_IPQ8074,91	WCSS_QCS404,92};93 94struct wcss_data {95	const char *firmware_name;96	unsigned int crash_reason_smem;97	u32 version;98	bool aon_reset_required;99	bool wcss_q6_reset_required;100	const char *ssr_name;101	const char *sysmon_name;102	int ssctl_id;103	const struct rproc_ops *ops;104	bool requires_force_stop;105};106 107struct q6v5_wcss {108	struct device *dev;109 110	void __iomem *reg_base;111	void __iomem *rmb_base;112 113	struct regmap *halt_map;114	u32 halt_q6;115	u32 halt_wcss;116	u32 halt_nc;117 118	struct clk *xo;119	struct clk *ahbfabric_cbcr_clk;120	struct clk *gcc_abhs_cbcr;121	struct clk *gcc_axim_cbcr;122	struct clk *lcc_csr_cbcr;123	struct clk *ahbs_cbcr;124	struct clk *tcm_slave_cbcr;125	struct clk *qdsp6ss_abhm_cbcr;126	struct clk *qdsp6ss_sleep_cbcr;127	struct clk *qdsp6ss_axim_cbcr;128	struct clk *qdsp6ss_xo_cbcr;129	struct clk *qdsp6ss_core_gfmux;130	struct clk *lcc_bcr_sleep;131	struct regulator *cx_supply;132	struct qcom_sysmon *sysmon;133 134	struct reset_control *wcss_aon_reset;135	struct reset_control *wcss_reset;136	struct reset_control *wcss_q6_reset;137	struct reset_control *wcss_q6_bcr_reset;138 139	struct qcom_q6v5 q6v5;140 141	phys_addr_t mem_phys;142	phys_addr_t mem_reloc;143	void *mem_region;144	size_t mem_size;145 146	unsigned int crash_reason_smem;147	u32 version;148	bool requires_force_stop;149 150	struct qcom_rproc_glink glink_subdev;151	struct qcom_rproc_pdm pdm_subdev;152	struct qcom_rproc_ssr ssr_subdev;153};154 155static int q6v5_wcss_reset(struct q6v5_wcss *wcss)156{157	int ret;158	u32 val;159	int i;160 161	/* Assert resets, stop core */162	val = readl(wcss->reg_base + Q6SS_RESET_REG);163	val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE;164	writel(val, wcss->reg_base + Q6SS_RESET_REG);165 166	/* BHS require xo cbcr to be enabled */167	val = readl(wcss->reg_base + Q6SS_XO_CBCR);168	val |= 0x1;169	writel(val, wcss->reg_base + Q6SS_XO_CBCR);170 171	/* Read CLKOFF bit to go low indicating CLK is enabled */172	ret = readl_poll_timeout(wcss->reg_base + Q6SS_XO_CBCR,173				 val, !(val & BIT(31)), 1,174				 HALT_CHECK_MAX_LOOPS);175	if (ret) {176		dev_err(wcss->dev,177			"xo cbcr enabling timed out (rc:%d)\n", ret);178		return ret;179	}180	/* Enable power block headswitch and wait for it to stabilize */181	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);182	val |= Q6SS_BHS_ON;183	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);184	udelay(1);185 186	/* Put LDO in bypass mode */187	val |= Q6SS_LDO_BYP;188	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);189 190	/* Deassert Q6 compiler memory clamp */191	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);192	val &= ~Q6SS_CLAMP_QMC_MEM;193	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);194 195	/* Deassert memory peripheral sleep and L2 memory standby */196	val |= Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N;197	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);198 199	/* Turn on L1, L2, ETB and JU memories 1 at a time */200	val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);201	for (i = MEM_BANKS; i >= 0; i--) {202		val |= BIT(i);203		writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL);204		/*205		 * Read back value to ensure the write is done then206		 * wait for 1us for both memory peripheral and data207		 * array to turn on.208		 */209		val |= readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);210		udelay(1);211	}212	/* Remove word line clamp */213	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);214	val &= ~Q6SS_CLAMP_WL;215	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);216 217	/* Remove IO clamp */218	val &= ~Q6SS_CLAMP_IO;219	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);220 221	/* Bring core out of reset */222	val = readl(wcss->reg_base + Q6SS_RESET_REG);223	val &= ~Q6SS_CORE_ARES;224	writel(val, wcss->reg_base + Q6SS_RESET_REG);225 226	/* Turn on core clock */227	val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);228	val |= Q6SS_CLK_ENABLE;229	writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);230 231	/* Start core execution */232	val = readl(wcss->reg_base + Q6SS_RESET_REG);233	val &= ~Q6SS_STOP_CORE;234	writel(val, wcss->reg_base + Q6SS_RESET_REG);235 236	return 0;237}238 239static int q6v5_wcss_start(struct rproc *rproc)240{241	struct q6v5_wcss *wcss = rproc->priv;242	int ret;243 244	qcom_q6v5_prepare(&wcss->q6v5);245 246	/* Release Q6 and WCSS reset */247	ret = reset_control_deassert(wcss->wcss_reset);248	if (ret) {249		dev_err(wcss->dev, "wcss_reset failed\n");250		return ret;251	}252 253	ret = reset_control_deassert(wcss->wcss_q6_reset);254	if (ret) {255		dev_err(wcss->dev, "wcss_q6_reset failed\n");256		goto wcss_reset;257	}258 259	/* Lithium configuration - clock gating and bus arbitration */260	ret = regmap_update_bits(wcss->halt_map,261				 wcss->halt_nc + TCSR_GLOBAL_CFG0,262				 TCSR_WCSS_CLK_MASK,263				 TCSR_WCSS_CLK_ENABLE);264	if (ret)265		goto wcss_q6_reset;266 267	ret = regmap_update_bits(wcss->halt_map,268				 wcss->halt_nc + TCSR_GLOBAL_CFG1,269				 1, 0);270	if (ret)271		goto wcss_q6_reset;272 273	/* Write bootaddr to EVB so that Q6WCSS will jump there after reset */274	writel(rproc->bootaddr >> 4, wcss->reg_base + Q6SS_RST_EVB);275 276	ret = q6v5_wcss_reset(wcss);277	if (ret)278		goto wcss_q6_reset;279 280	ret = qcom_q6v5_wait_for_start(&wcss->q6v5, 5 * HZ);281	if (ret == -ETIMEDOUT)282		dev_err(wcss->dev, "start timed out\n");283 284	return ret;285 286wcss_q6_reset:287	reset_control_assert(wcss->wcss_q6_reset);288 289wcss_reset:290	reset_control_assert(wcss->wcss_reset);291 292	return ret;293}294 295static int q6v5_wcss_qcs404_power_on(struct q6v5_wcss *wcss)296{297	unsigned long val;298	int ret, idx;299 300	/* Toggle the restart */301	reset_control_assert(wcss->wcss_reset);302	usleep_range(200, 300);303	reset_control_deassert(wcss->wcss_reset);304	usleep_range(200, 300);305 306	/* Enable GCC_WDSP_Q6SS_AHBS_CBCR clock */307	ret = clk_prepare_enable(wcss->gcc_abhs_cbcr);308	if (ret)309		return ret;310 311	/* Remove reset to the WCNSS QDSP6SS */312	reset_control_deassert(wcss->wcss_q6_bcr_reset);313 314	/* Enable Q6SSTOP_AHBFABRIC_CBCR clock */315	ret = clk_prepare_enable(wcss->ahbfabric_cbcr_clk);316	if (ret)317		goto disable_gcc_abhs_cbcr_clk;318 319	/* Enable the LCCCSR CBC clock, Q6SSTOP_Q6SSTOP_LCC_CSR_CBCR clock */320	ret = clk_prepare_enable(wcss->lcc_csr_cbcr);321	if (ret)322		goto disable_ahbfabric_cbcr_clk;323 324	/* Enable the Q6AHBS CBC, Q6SSTOP_Q6SS_AHBS_CBCR clock */325	ret = clk_prepare_enable(wcss->ahbs_cbcr);326	if (ret)327		goto disable_csr_cbcr_clk;328 329	/* Enable the TCM slave CBC, Q6SSTOP_Q6SS_TCM_SLAVE_CBCR clock */330	ret = clk_prepare_enable(wcss->tcm_slave_cbcr);331	if (ret)332		goto disable_ahbs_cbcr_clk;333 334	/* Enable the Q6SS AHB master CBC, Q6SSTOP_Q6SS_AHBM_CBCR clock */335	ret = clk_prepare_enable(wcss->qdsp6ss_abhm_cbcr);336	if (ret)337		goto disable_tcm_slave_cbcr_clk;338 339	/* Enable the Q6SS AXI master CBC, Q6SSTOP_Q6SS_AXIM_CBCR clock */340	ret = clk_prepare_enable(wcss->qdsp6ss_axim_cbcr);341	if (ret)342		goto disable_abhm_cbcr_clk;343 344	/* Enable the Q6SS XO CBC */345	val = readl(wcss->reg_base + Q6SS_XO_CBCR);346	val |= BIT(0);347	writel(val, wcss->reg_base + Q6SS_XO_CBCR);348	/* Read CLKOFF bit to go low indicating CLK is enabled */349	ret = readl_poll_timeout(wcss->reg_base + Q6SS_XO_CBCR,350				 val, !(val & BIT(31)), 1,351				 HALT_CHECK_MAX_LOOPS);352	if (ret) {353		dev_err(wcss->dev,354			"xo cbcr enabling timed out (rc:%d)\n", ret);355		goto disable_xo_cbcr_clk;356	}357 358	writel(0, wcss->reg_base + Q6SS_CGC_OVERRIDE);359 360	/* Enable QDSP6 sleep clock clock */361	val = readl(wcss->reg_base + Q6SS_SLEEP_CBCR);362	val |= BIT(0);363	writel(val, wcss->reg_base + Q6SS_SLEEP_CBCR);364 365	/* Enable the Enable the Q6 AXI clock, GCC_WDSP_Q6SS_AXIM_CBCR*/366	ret = clk_prepare_enable(wcss->gcc_axim_cbcr);367	if (ret)368		goto disable_sleep_cbcr_clk;369 370	/* Assert resets, stop core */371	val = readl(wcss->reg_base + Q6SS_RESET_REG);372	val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE;373	writel(val, wcss->reg_base + Q6SS_RESET_REG);374 375	/* Program the QDSP6SS PWR_CTL register */376	writel(0x01700000, wcss->reg_base + Q6SS_PWR_CTL_REG);377 378	writel(0x03700000, wcss->reg_base + Q6SS_PWR_CTL_REG);379 380	writel(0x03300000, wcss->reg_base + Q6SS_PWR_CTL_REG);381 382	writel(0x033C0000, wcss->reg_base + Q6SS_PWR_CTL_REG);383 384	/*385	 * Enable memories by turning on the QDSP6 memory foot/head switch, one386	 * bank at a time to avoid in-rush current387	 */388	for (idx = 28; idx >= 0; idx--) {389		writel((readl(wcss->reg_base + Q6SS_MEM_PWR_CTL) |390			(1 << idx)), wcss->reg_base + Q6SS_MEM_PWR_CTL);391	}392 393	writel(0x031C0000, wcss->reg_base + Q6SS_PWR_CTL_REG);394	writel(0x030C0000, wcss->reg_base + Q6SS_PWR_CTL_REG);395 396	val = readl(wcss->reg_base + Q6SS_RESET_REG);397	val &= ~Q6SS_CORE_ARES;398	writel(val, wcss->reg_base + Q6SS_RESET_REG);399 400	/* Enable the Q6 core clock at the GFM, Q6SSTOP_QDSP6SS_GFMUX_CTL */401	val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);402	val |= Q6SS_CLK_ENABLE | Q6SS_SWITCH_CLK_SRC;403	writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);404 405	/* Enable sleep clock branch needed for BCR circuit */406	ret = clk_prepare_enable(wcss->lcc_bcr_sleep);407	if (ret)408		goto disable_core_gfmux_clk;409 410	return 0;411 412disable_core_gfmux_clk:413	val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);414	val &= ~(Q6SS_CLK_ENABLE | Q6SS_SWITCH_CLK_SRC);415	writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);416	clk_disable_unprepare(wcss->gcc_axim_cbcr);417disable_sleep_cbcr_clk:418	val = readl(wcss->reg_base + Q6SS_SLEEP_CBCR);419	val &= ~Q6SS_CLK_ENABLE;420	writel(val, wcss->reg_base + Q6SS_SLEEP_CBCR);421disable_xo_cbcr_clk:422	val = readl(wcss->reg_base + Q6SS_XO_CBCR);423	val &= ~Q6SS_CLK_ENABLE;424	writel(val, wcss->reg_base + Q6SS_XO_CBCR);425	clk_disable_unprepare(wcss->qdsp6ss_axim_cbcr);426disable_abhm_cbcr_clk:427	clk_disable_unprepare(wcss->qdsp6ss_abhm_cbcr);428disable_tcm_slave_cbcr_clk:429	clk_disable_unprepare(wcss->tcm_slave_cbcr);430disable_ahbs_cbcr_clk:431	clk_disable_unprepare(wcss->ahbs_cbcr);432disable_csr_cbcr_clk:433	clk_disable_unprepare(wcss->lcc_csr_cbcr);434disable_ahbfabric_cbcr_clk:435	clk_disable_unprepare(wcss->ahbfabric_cbcr_clk);436disable_gcc_abhs_cbcr_clk:437	clk_disable_unprepare(wcss->gcc_abhs_cbcr);438 439	return ret;440}441 442static inline int q6v5_wcss_qcs404_reset(struct q6v5_wcss *wcss)443{444	unsigned long val;445 446	writel(0x80800000, wcss->reg_base + Q6SS_STRAP_ACC);447 448	/* Start core execution */449	val = readl(wcss->reg_base + Q6SS_RESET_REG);450	val &= ~Q6SS_STOP_CORE;451	writel(val, wcss->reg_base + Q6SS_RESET_REG);452 453	return 0;454}455 456static int q6v5_qcs404_wcss_start(struct rproc *rproc)457{458	struct q6v5_wcss *wcss = rproc->priv;459	int ret;460 461	ret = clk_prepare_enable(wcss->xo);462	if (ret)463		return ret;464 465	ret = regulator_enable(wcss->cx_supply);466	if (ret)467		goto disable_xo_clk;468 469	qcom_q6v5_prepare(&wcss->q6v5);470 471	ret = q6v5_wcss_qcs404_power_on(wcss);472	if (ret) {473		dev_err(wcss->dev, "wcss clk_enable failed\n");474		goto disable_cx_supply;475	}476 477	writel(rproc->bootaddr >> 4, wcss->reg_base + Q6SS_RST_EVB);478 479	q6v5_wcss_qcs404_reset(wcss);480 481	ret = qcom_q6v5_wait_for_start(&wcss->q6v5, 5 * HZ);482	if (ret == -ETIMEDOUT) {483		dev_err(wcss->dev, "start timed out\n");484		goto disable_cx_supply;485	}486 487	return 0;488 489disable_cx_supply:490	regulator_disable(wcss->cx_supply);491disable_xo_clk:492	clk_disable_unprepare(wcss->xo);493 494	return ret;495}496 497static void q6v5_wcss_halt_axi_port(struct q6v5_wcss *wcss,498				    struct regmap *halt_map,499				    u32 offset)500{501	unsigned long timeout;502	unsigned int val;503	int ret;504 505	/* Check if we're already idle */506	ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);507	if (!ret && val)508		return;509 510	/* Assert halt request */511	regmap_write(halt_map, offset + AXI_HALTREQ_REG, 1);512 513	/* Wait for halt */514	timeout = jiffies + msecs_to_jiffies(HALT_ACK_TIMEOUT_MS);515	for (;;) {516		ret = regmap_read(halt_map, offset + AXI_HALTACK_REG, &val);517		if (ret || val || time_after(jiffies, timeout))518			break;519 520		msleep(1);521	}522 523	ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);524	if (ret || !val)525		dev_err(wcss->dev, "port failed halt\n");526 527	/* Clear halt request (port will remain halted until reset) */528	regmap_write(halt_map, offset + AXI_HALTREQ_REG, 0);529}530 531static int q6v5_qcs404_wcss_shutdown(struct q6v5_wcss *wcss)532{533	unsigned long val;534	int ret;535 536	q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_wcss);537 538	/* assert clamps to avoid MX current inrush */539	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);540	val |= (Q6SS_CLAMP_IO | Q6SS_CLAMP_WL | Q6SS_CLAMP_QMC_MEM);541	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);542 543	/* Disable memories by turning off memory foot/headswitch */544	writel((readl(wcss->reg_base + Q6SS_MEM_PWR_CTL) &545		~QDSS_Q6_MEMORIES),546		wcss->reg_base + Q6SS_MEM_PWR_CTL);547 548	/* Clear the BHS_ON bit */549	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);550	val &= ~Q6SS_BHS_ON;551	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);552 553	clk_disable_unprepare(wcss->ahbfabric_cbcr_clk);554	clk_disable_unprepare(wcss->lcc_csr_cbcr);555	clk_disable_unprepare(wcss->tcm_slave_cbcr);556	clk_disable_unprepare(wcss->qdsp6ss_abhm_cbcr);557	clk_disable_unprepare(wcss->qdsp6ss_axim_cbcr);558 559	val = readl(wcss->reg_base + Q6SS_SLEEP_CBCR);560	val &= ~BIT(0);561	writel(val, wcss->reg_base + Q6SS_SLEEP_CBCR);562 563	val = readl(wcss->reg_base + Q6SS_XO_CBCR);564	val &= ~BIT(0);565	writel(val, wcss->reg_base + Q6SS_XO_CBCR);566 567	clk_disable_unprepare(wcss->ahbs_cbcr);568	clk_disable_unprepare(wcss->lcc_bcr_sleep);569 570	val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);571	val &= ~(Q6SS_CLK_ENABLE | Q6SS_SWITCH_CLK_SRC);572	writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);573 574	clk_disable_unprepare(wcss->gcc_abhs_cbcr);575 576	ret = reset_control_assert(wcss->wcss_reset);577	if (ret) {578		dev_err(wcss->dev, "wcss_reset failed\n");579		return ret;580	}581	usleep_range(200, 300);582 583	ret = reset_control_deassert(wcss->wcss_reset);584	if (ret) {585		dev_err(wcss->dev, "wcss_reset failed\n");586		return ret;587	}588	usleep_range(200, 300);589 590	clk_disable_unprepare(wcss->gcc_axim_cbcr);591 592	return 0;593}594 595static int q6v5_wcss_powerdown(struct q6v5_wcss *wcss)596{597	int ret;598	u32 val;599 600	/* 1 - Assert WCSS/Q6 HALTREQ */601	q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_wcss);602 603	/* 2 - Enable WCSSAON_CONFIG */604	val = readl(wcss->rmb_base + SSCAON_CONFIG);605	val |= SSCAON_ENABLE;606	writel(val, wcss->rmb_base + SSCAON_CONFIG);607 608	/* 3 - Set SSCAON_CONFIG */609	val |= SSCAON_BUS_EN;610	val &= ~SSCAON_BUS_MUX_MASK;611	writel(val, wcss->rmb_base + SSCAON_CONFIG);612 613	/* 4 - SSCAON_CONFIG 1 */614	val |= BIT(1);615	writel(val, wcss->rmb_base + SSCAON_CONFIG);616 617	/* 5 - wait for SSCAON_STATUS */618	ret = readl_poll_timeout(wcss->rmb_base + SSCAON_STATUS,619				 val, (val & 0xffff) == 0x400, 1000,620				 HALT_CHECK_MAX_LOOPS);621	if (ret) {622		dev_err(wcss->dev,623			"can't get SSCAON_STATUS rc:%d)\n", ret);624		return ret;625	}626 627	/* 6 - De-assert WCSS_AON reset */628	reset_control_assert(wcss->wcss_aon_reset);629 630	/* 7 - Disable WCSSAON_CONFIG 13 */631	val = readl(wcss->rmb_base + SSCAON_CONFIG);632	val &= ~SSCAON_ENABLE;633	writel(val, wcss->rmb_base + SSCAON_CONFIG);634 635	/* 8 - De-assert WCSS/Q6 HALTREQ */636	reset_control_assert(wcss->wcss_reset);637 638	return 0;639}640 641static int q6v5_q6_powerdown(struct q6v5_wcss *wcss)642{643	int ret;644	u32 val;645	int i;646 647	/* 1 - Halt Q6 bus interface */648	q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_q6);649 650	/* 2 - Disable Q6 Core clock */651	val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);652	val &= ~Q6SS_CLK_ENABLE;653	writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);654 655	/* 3 - Clamp I/O */656	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);657	val |= Q6SS_CLAMP_IO;658	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);659 660	/* 4 - Clamp WL */661	val |= QDSS_BHS_ON;662	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);663 664	/* 5 - Clear Erase standby */665	val &= ~Q6SS_L2DATA_STBY_N;666	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);667 668	/* 6 - Clear Sleep RTN */669	val &= ~Q6SS_SLP_RET_N;670	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);671 672	/* 7 - turn off Q6 memory foot/head switch one bank at a time */673	for (i = 0; i < 20; i++) {674		val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);675		val &= ~BIT(i);676		writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL);677		mdelay(1);678	}679 680	/* 8 - Assert QMC memory RTN */681	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);682	val |= Q6SS_CLAMP_QMC_MEM;683	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);684 685	/* 9 - Turn off BHS */686	val &= ~Q6SS_BHS_ON;687	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);688	udelay(1);689 690	/* 10 - Wait till BHS Reset is done */691	ret = readl_poll_timeout(wcss->reg_base + Q6SS_BHS_STATUS,692				 val, !(val & BHS_EN_REST_ACK), 1000,693				 HALT_CHECK_MAX_LOOPS);694	if (ret) {695		dev_err(wcss->dev, "BHS_STATUS not OFF (rc:%d)\n", ret);696		return ret;697	}698 699	/* 11 -  Assert WCSS reset */700	reset_control_assert(wcss->wcss_reset);701 702	/* 12 - Assert Q6 reset */703	reset_control_assert(wcss->wcss_q6_reset);704 705	return 0;706}707 708static int q6v5_wcss_stop(struct rproc *rproc)709{710	struct q6v5_wcss *wcss = rproc->priv;711	int ret;712 713	/* WCSS powerdown */714	if (wcss->requires_force_stop) {715		ret = qcom_q6v5_request_stop(&wcss->q6v5, NULL);716		if (ret == -ETIMEDOUT) {717			dev_err(wcss->dev, "timed out on wait\n");718			return ret;719		}720	}721 722	if (wcss->version == WCSS_QCS404) {723		ret = q6v5_qcs404_wcss_shutdown(wcss);724		if (ret)725			return ret;726	} else {727		ret = q6v5_wcss_powerdown(wcss);728		if (ret)729			return ret;730 731		/* Q6 Power down */732		ret = q6v5_q6_powerdown(wcss);733		if (ret)734			return ret;735	}736 737	qcom_q6v5_unprepare(&wcss->q6v5);738 739	return 0;740}741 742static void *q6v5_wcss_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)743{744	struct q6v5_wcss *wcss = rproc->priv;745	int offset;746 747	offset = da - wcss->mem_reloc;748	if (offset < 0 || offset + len > wcss->mem_size)749		return NULL;750 751	return wcss->mem_region + offset;752}753 754static int q6v5_wcss_load(struct rproc *rproc, const struct firmware *fw)755{756	struct q6v5_wcss *wcss = rproc->priv;757	int ret;758 759	ret = qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware,760				    0, wcss->mem_region, wcss->mem_phys,761				    wcss->mem_size, &wcss->mem_reloc);762	if (ret)763		return ret;764 765	qcom_pil_info_store("wcnss", wcss->mem_phys, wcss->mem_size);766 767	return ret;768}769 770static const struct rproc_ops q6v5_wcss_ipq8074_ops = {771	.start = q6v5_wcss_start,772	.stop = q6v5_wcss_stop,773	.da_to_va = q6v5_wcss_da_to_va,774	.load = q6v5_wcss_load,775	.get_boot_addr = rproc_elf_get_boot_addr,776};777 778static const struct rproc_ops q6v5_wcss_qcs404_ops = {779	.start = q6v5_qcs404_wcss_start,780	.stop = q6v5_wcss_stop,781	.da_to_va = q6v5_wcss_da_to_va,782	.load = q6v5_wcss_load,783	.get_boot_addr = rproc_elf_get_boot_addr,784	.parse_fw = qcom_register_dump_segments,785};786 787static int q6v5_wcss_init_reset(struct q6v5_wcss *wcss,788				const struct wcss_data *desc)789{790	struct device *dev = wcss->dev;791 792	if (desc->aon_reset_required) {793		wcss->wcss_aon_reset = devm_reset_control_get_exclusive(dev, "wcss_aon_reset");794		if (IS_ERR(wcss->wcss_aon_reset)) {795			dev_err(wcss->dev, "fail to acquire wcss_aon_reset\n");796			return PTR_ERR(wcss->wcss_aon_reset);797		}798	}799 800	wcss->wcss_reset = devm_reset_control_get_exclusive(dev, "wcss_reset");801	if (IS_ERR(wcss->wcss_reset)) {802		dev_err(wcss->dev, "unable to acquire wcss_reset\n");803		return PTR_ERR(wcss->wcss_reset);804	}805 806	if (desc->wcss_q6_reset_required) {807		wcss->wcss_q6_reset = devm_reset_control_get_exclusive(dev, "wcss_q6_reset");808		if (IS_ERR(wcss->wcss_q6_reset)) {809			dev_err(wcss->dev, "unable to acquire wcss_q6_reset\n");810			return PTR_ERR(wcss->wcss_q6_reset);811		}812	}813 814	wcss->wcss_q6_bcr_reset = devm_reset_control_get_exclusive(dev, "wcss_q6_bcr_reset");815	if (IS_ERR(wcss->wcss_q6_bcr_reset)) {816		dev_err(wcss->dev, "unable to acquire wcss_q6_bcr_reset\n");817		return PTR_ERR(wcss->wcss_q6_bcr_reset);818	}819 820	return 0;821}822 823static int q6v5_wcss_init_mmio(struct q6v5_wcss *wcss,824			       struct platform_device *pdev)825{826	unsigned int halt_reg[MAX_HALT_REG] = {0};827	struct device_node *syscon;828	struct resource *res;829	int ret;830 831	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6");832	if (!res)833		return -EINVAL;834 835	wcss->reg_base = devm_ioremap(&pdev->dev, res->start,836				      resource_size(res));837	if (!wcss->reg_base)838		return -ENOMEM;839 840	if (wcss->version == WCSS_IPQ8074) {841		wcss->rmb_base = devm_platform_ioremap_resource_byname(pdev, "rmb");842		if (IS_ERR(wcss->rmb_base))843			return PTR_ERR(wcss->rmb_base);844	}845 846	syscon = of_parse_phandle(pdev->dev.of_node,847				  "qcom,halt-regs", 0);848	if (!syscon) {849		dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n");850		return -EINVAL;851	}852 853	wcss->halt_map = syscon_node_to_regmap(syscon);854	of_node_put(syscon);855	if (IS_ERR(wcss->halt_map))856		return PTR_ERR(wcss->halt_map);857 858	ret = of_property_read_variable_u32_array(pdev->dev.of_node,859						  "qcom,halt-regs",860						  halt_reg, 0,861						  MAX_HALT_REG);862	if (ret < 0) {863		dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n");864		return -EINVAL;865	}866 867	wcss->halt_q6 = halt_reg[0];868	wcss->halt_wcss = halt_reg[1];869	wcss->halt_nc = halt_reg[2];870 871	return 0;872}873 874static int q6v5_alloc_memory_region(struct q6v5_wcss *wcss)875{876	struct reserved_mem *rmem = NULL;877	struct device_node *node;878	struct device *dev = wcss->dev;879 880	node = of_parse_phandle(dev->of_node, "memory-region", 0);881	if (node)882		rmem = of_reserved_mem_lookup(node);883	of_node_put(node);884 885	if (!rmem) {886		dev_err(dev, "unable to acquire memory-region\n");887		return -EINVAL;888	}889 890	wcss->mem_phys = rmem->base;891	wcss->mem_reloc = rmem->base;892	wcss->mem_size = rmem->size;893	wcss->mem_region = devm_ioremap_wc(dev, wcss->mem_phys, wcss->mem_size);894	if (!wcss->mem_region) {895		dev_err(dev, "unable to map memory region: %pa+%pa\n",896			&rmem->base, &rmem->size);897		return -EBUSY;898	}899 900	return 0;901}902 903static int q6v5_wcss_init_clock(struct q6v5_wcss *wcss)904{905	int ret;906 907	wcss->xo = devm_clk_get(wcss->dev, "xo");908	if (IS_ERR(wcss->xo)) {909		ret = PTR_ERR(wcss->xo);910		if (ret != -EPROBE_DEFER)911			dev_err(wcss->dev, "failed to get xo clock");912		return ret;913	}914 915	wcss->gcc_abhs_cbcr = devm_clk_get(wcss->dev, "gcc_abhs_cbcr");916	if (IS_ERR(wcss->gcc_abhs_cbcr)) {917		ret = PTR_ERR(wcss->gcc_abhs_cbcr);918		if (ret != -EPROBE_DEFER)919			dev_err(wcss->dev, "failed to get gcc abhs clock");920		return ret;921	}922 923	wcss->gcc_axim_cbcr = devm_clk_get(wcss->dev, "gcc_axim_cbcr");924	if (IS_ERR(wcss->gcc_axim_cbcr)) {925		ret = PTR_ERR(wcss->gcc_axim_cbcr);926		if (ret != -EPROBE_DEFER)927			dev_err(wcss->dev, "failed to get gcc axim clock\n");928		return ret;929	}930 931	wcss->ahbfabric_cbcr_clk = devm_clk_get(wcss->dev,932						"lcc_ahbfabric_cbc");933	if (IS_ERR(wcss->ahbfabric_cbcr_clk)) {934		ret = PTR_ERR(wcss->ahbfabric_cbcr_clk);935		if (ret != -EPROBE_DEFER)936			dev_err(wcss->dev, "failed to get ahbfabric clock\n");937		return ret;938	}939 940	wcss->lcc_csr_cbcr = devm_clk_get(wcss->dev, "tcsr_lcc_cbc");941	if (IS_ERR(wcss->lcc_csr_cbcr)) {942		ret = PTR_ERR(wcss->lcc_csr_cbcr);943		if (ret != -EPROBE_DEFER)944			dev_err(wcss->dev, "failed to get csr cbcr clk\n");945		return ret;946	}947 948	wcss->ahbs_cbcr = devm_clk_get(wcss->dev,949				       "lcc_abhs_cbc");950	if (IS_ERR(wcss->ahbs_cbcr)) {951		ret = PTR_ERR(wcss->ahbs_cbcr);952		if (ret != -EPROBE_DEFER)953			dev_err(wcss->dev, "failed to get ahbs_cbcr clk\n");954		return ret;955	}956 957	wcss->tcm_slave_cbcr = devm_clk_get(wcss->dev,958					    "lcc_tcm_slave_cbc");959	if (IS_ERR(wcss->tcm_slave_cbcr)) {960		ret = PTR_ERR(wcss->tcm_slave_cbcr);961		if (ret != -EPROBE_DEFER)962			dev_err(wcss->dev, "failed to get tcm cbcr clk\n");963		return ret;964	}965 966	wcss->qdsp6ss_abhm_cbcr = devm_clk_get(wcss->dev, "lcc_abhm_cbc");967	if (IS_ERR(wcss->qdsp6ss_abhm_cbcr)) {968		ret = PTR_ERR(wcss->qdsp6ss_abhm_cbcr);969		if (ret != -EPROBE_DEFER)970			dev_err(wcss->dev, "failed to get abhm cbcr clk\n");971		return ret;972	}973 974	wcss->qdsp6ss_axim_cbcr = devm_clk_get(wcss->dev, "lcc_axim_cbc");975	if (IS_ERR(wcss->qdsp6ss_axim_cbcr)) {976		ret = PTR_ERR(wcss->qdsp6ss_axim_cbcr);977		if (ret != -EPROBE_DEFER)978			dev_err(wcss->dev, "failed to get axim cbcr clk\n");979		return ret;980	}981 982	wcss->lcc_bcr_sleep = devm_clk_get(wcss->dev, "lcc_bcr_sleep");983	if (IS_ERR(wcss->lcc_bcr_sleep)) {984		ret = PTR_ERR(wcss->lcc_bcr_sleep);985		if (ret != -EPROBE_DEFER)986			dev_err(wcss->dev, "failed to get bcr cbcr clk\n");987		return ret;988	}989 990	return 0;991}992 993static int q6v5_wcss_init_regulator(struct q6v5_wcss *wcss)994{995	wcss->cx_supply = devm_regulator_get(wcss->dev, "cx");996	if (IS_ERR(wcss->cx_supply))997		return PTR_ERR(wcss->cx_supply);998 999	regulator_set_load(wcss->cx_supply, 100000);1000 1001	return 0;1002}1003 1004static int q6v5_wcss_probe(struct platform_device *pdev)1005{1006	const struct wcss_data *desc;1007	struct q6v5_wcss *wcss;1008	struct rproc *rproc;1009	int ret;1010 1011	desc = device_get_match_data(&pdev->dev);1012	if (!desc)1013		return -EINVAL;1014 1015	rproc = devm_rproc_alloc(&pdev->dev, pdev->name, desc->ops,1016				 desc->firmware_name, sizeof(*wcss));1017	if (!rproc) {1018		dev_err(&pdev->dev, "failed to allocate rproc\n");1019		return -ENOMEM;1020	}1021 1022	wcss = rproc->priv;1023	wcss->dev = &pdev->dev;1024	wcss->version = desc->version;1025 1026	wcss->version = desc->version;1027	wcss->requires_force_stop = desc->requires_force_stop;1028 1029	ret = q6v5_wcss_init_mmio(wcss, pdev);1030	if (ret)1031		return ret;1032 1033	ret = q6v5_alloc_memory_region(wcss);1034	if (ret)1035		return ret;1036 1037	if (wcss->version == WCSS_QCS404) {1038		ret = q6v5_wcss_init_clock(wcss);1039		if (ret)1040			return ret;1041 1042		ret = q6v5_wcss_init_regulator(wcss);1043		if (ret)1044			return ret;1045	}1046 1047	ret = q6v5_wcss_init_reset(wcss, desc);1048	if (ret)1049		return ret;1050 1051	ret = qcom_q6v5_init(&wcss->q6v5, pdev, rproc, desc->crash_reason_smem, NULL, NULL);1052	if (ret)1053		return ret;1054 1055	qcom_add_glink_subdev(rproc, &wcss->glink_subdev, "q6wcss");1056	qcom_add_pdm_subdev(rproc, &wcss->pdm_subdev);1057	qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, "q6wcss");1058 1059	if (desc->ssctl_id)1060		wcss->sysmon = qcom_add_sysmon_subdev(rproc,1061						      desc->sysmon_name,1062						      desc->ssctl_id);1063 1064	ret = rproc_add(rproc);1065	if (ret)1066		return ret;1067 1068	platform_set_drvdata(pdev, rproc);1069 1070	return 0;1071}1072 1073static void q6v5_wcss_remove(struct platform_device *pdev)1074{1075	struct rproc *rproc = platform_get_drvdata(pdev);1076	struct q6v5_wcss *wcss = rproc->priv;1077 1078	qcom_q6v5_deinit(&wcss->q6v5);1079	qcom_remove_pdm_subdev(rproc, &wcss->pdm_subdev);1080	rproc_del(rproc);1081}1082 1083static const struct wcss_data wcss_ipq8074_res_init = {1084	.firmware_name = "IPQ8074/q6_fw.mdt",1085	.crash_reason_smem = WCSS_CRASH_REASON,1086	.aon_reset_required = true,1087	.wcss_q6_reset_required = true,1088	.ops = &q6v5_wcss_ipq8074_ops,1089	.requires_force_stop = true,1090};1091 1092static const struct wcss_data wcss_qcs404_res_init = {1093	.crash_reason_smem = WCSS_CRASH_REASON,1094	.firmware_name = "wcnss.mdt",1095	.version = WCSS_QCS404,1096	.aon_reset_required = false,1097	.wcss_q6_reset_required = false,1098	.ssr_name = "mpss",1099	.sysmon_name = "wcnss",1100	.ssctl_id = 0x12,1101	.ops = &q6v5_wcss_qcs404_ops,1102	.requires_force_stop = false,1103};1104 1105static const struct of_device_id q6v5_wcss_of_match[] = {1106	{ .compatible = "qcom,ipq8074-wcss-pil", .data = &wcss_ipq8074_res_init },1107	{ .compatible = "qcom,qcs404-wcss-pil", .data = &wcss_qcs404_res_init },1108	{ },1109};1110MODULE_DEVICE_TABLE(of, q6v5_wcss_of_match);1111 1112static struct platform_driver q6v5_wcss_driver = {1113	.probe = q6v5_wcss_probe,1114	.remove_new = q6v5_wcss_remove,1115	.driver = {1116		.name = "qcom-q6v5-wcss-pil",1117		.of_match_table = q6v5_wcss_of_match,1118	},1119};1120module_platform_driver(q6v5_wcss_driver);1121 1122MODULE_DESCRIPTION("Hexagon WCSS Peripheral Image Loader");1123MODULE_LICENSE("GPL v2");1124