From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-yw1-f171.google.com (mail-yw1-f171.google.com [209.85.128.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6A0C729ACFC for ; Fri, 19 Dec 2025 04:34:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.171 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766118884; cv=none; b=t7J1NWor+XA76vF1j4AWumuG6P2IOrdZJcTqvrr6mV6ZcO3AdFOYwIka2E7zm/u8lGAE01D8w4MeYGt+tW7TncjTRdpDgObz3TlZWvvOwzTH9wbOgq2vdjGr5NS8psEOHaU7mJyLyUaY6CLEvUzKIPpXPrWEzHR/IbjDnSaZXHs= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766118884; c=relaxed/simple; bh=bdMEo4uEw4keV7imycYpVrULG0tvoTIK0I50nmEWq3U=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=F34Bs/qCjnlw5jDsiAy9UFfDwyvlKOqvFkTgpvfpRuiOyIE0U+9v4diPwImVbAUZMMbCOaQzNo9u2bjKQALyACp9F2Bg8axtOFfVg/a6mNWiObP93iJubBncFYebRLih8VQIWd7M9EziiOyyUaU+t1x3LUtliReH2kcmDORKi4A= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=WCvqSzvt; arc=none smtp.client-ip=209.85.128.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="WCvqSzvt" Received: by mail-yw1-f171.google.com with SMTP id 00721157ae682-78f89501423so26961587b3.1 for ; Thu, 18 Dec 2025 20:34:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1766118880; x=1766723680; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=+aKpuwK8ek2NbH4AWFnKdemkwvoys762HaRuVDmW618=; b=WCvqSzvto60ZFLDkxGtrr8myv1kt2l9gL6n2QY8NItillHGicjp1Ck3YCs0nmeAWta ODnLDXklTPQCCjYQ6FHP8UqTzWsyPH+DOpVRQfn2SgXkvVlba1fhIJ+hxTOk2AGj4zFF ueekHMi3pYnNE3BdDGtZbq7qvx5C+e56pcfiyQduWXn/H5H2SaaC0C/ChP824tzjtz4S lvPH/JaTxh+0BKbIyOXSVR2kPHfITT+CITF4pyUIHXuFzi4Nl6kQ7zSAf4McdOXFKShj 6VAoG5S8sZAH2BGvhQs6iIOo6zQVfDXoJCYSgpxa27+waxiZW8dTr+p3uQhdFwZ+LIRz 1+rw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1766118880; x=1766723680; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=+aKpuwK8ek2NbH4AWFnKdemkwvoys762HaRuVDmW618=; b=k55DnfLXe/J0LAME6LCR/OKzNiAVpIhQcB2DzaKIleuw2pkoOfxCXWaRZEHF9JH/qb Du/9NjAVyeCP451YaAsksT1U0zIWX4chGutZ4HnRzPwt+oyp62hBcdSHVT8ooPA9WgRk J9R3wfX24PNH8Y8KVItx0uZ7snRwv5N2P94urW8it6OqPTYqiywPPNQGP4d/RzVDS256 EiAY5dpSjAM4faFUj5fhLUI8gTYZewPncdsA7rs0Zo9vlgdOrHIRsLM6LYxX5gnqK+Ji cZLaiwENbdrsV89WQ1goatcp5mEJEMDaCUyEYNT3L6be1AZr1bZn1SkyCbfZJlsmDogN U/Xg== X-Forwarded-Encrypted: i=1; AJvYcCVDAIWzDB4wKAkY3+UJO1KUYJf4ngt7Vu5VKXbrGh6UzUQ4AkDVLgow4PTOP2mnPoMu52m6fdk2UjKSoSM=@vger.kernel.org X-Gm-Message-State: AOJu0Yw/LCQ3T4FPEMr/ZMOFTUYV93xD5aAuZTN0BNs+cbY9ZKXGPrLq op214BQ2wkrEzbOW8QB6BbEvYbnN2DQOl/UxC9thU7m+h+TDdyrnfI13Bh3ApQ== X-Gm-Gg: AY/fxX4sNf0CZGQN+mZWDMNh0SN+go4Jj7X69Hj3vZZqVa8Pz8V+csfQKfuZLDnmB34 /o8SGa6aZnSJwtvLEbsSY9mM9gUZbWHQhhwU/e4Ko9p6YUwiVPb4xdFXZkxRGdZm+25cjYdk9tR q0gwXrNiS05LD2n8dLv3phzF9csCm1hJkR9phRPVosok55mey98RoFGDO8Px4wTrnax6mQ1Anp4 jn6520RPQqrQTdkt7AhWX07bvi0TzPL/DRrEJ/Z9ERsvI8T7Fjm5sKxaT4ehoqHoppZqZQUKZH+ DzJWwap8PQYdAZ5i6RJin3Nih8MND+4OFD7ME8YuRCGiHaCGYwnBb8H3YvjCOZbvwObgawbBiUg Br9jav/Fr6VgT7Xen2oMvUZy72kzi63UHYMCIT9mcnnQTcFktlAFpT9PAvYxnuNDN3Kx7wB8bX/ VDUYYHX6N0y/OwppLyrd2cWdkrMD4LAK9l3D7z0iA3bmx8y7+rgv3IOSQQ6c5b54XYW/cn2TgPd o12Szk0poFL8exRoZkG6AXJgEpx X-Google-Smtp-Source: AGHT+IHA788AI9tvJXH6wsn793uK6OCkUvXgrMkaQNm629WOyrL9weYZHjhWgtxDGSqEyjpSVnLE5Q== X-Received: by 2002:a05:690e:4086:b0:63f:af0f:aaf with SMTP id 956f58d0204a3-64669d2a06cmr1477472d50.1.1766118880283; Thu, 18 Dec 2025 20:34:40 -0800 (PST) Received: from nukework.lan (c-98-57-15-22.hsd1.tx.comcast.net. [98.57.15.22]) by smtp.gmail.com with ESMTPSA id 00721157ae682-78fb452c441sm5610227b3.46.2025.12.18.20.34.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 18 Dec 2025 20:34:39 -0800 (PST) From: Alexandru Gagniuc To: andersson@kernel.org, mathieu.poirier@linaro.org, krzk+dt@kernel.org, Philipp Zabel Cc: Alexandru Gagniuc , linux-arm-msm@vger.kernel.org, linux-remoteproc@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 6/9] remoteproc: qcom_q6v5_wcss: support IPQ9574 Date: Thu, 18 Dec 2025 22:34:14 -0600 Message-ID: <20251219043425.888585-6-mr.nuke.me@gmail.com> X-Mailer: git-send-email 2.45.1 In-Reply-To: <20251219043425.888585-1-mr.nuke.me@gmail.com> References: <20251219043425.888585-1-mr.nuke.me@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Q6 based firmware loading is also present on IPQ9574, when coupled with a wifi-6 device, such as QCN5024. Populate driver data for IPQ9574 with values from the downstream 5.4 kerrnel. Add the new sequences for the WCSS reset and stop. The downstream 5.4 kernel calls these "Q6V7", so keep the name. This is still worth using with the "q6v5" driver because all other parts of the driver can be seamlessly reused. The IPQ9574 uses two sets of clocks. the first, dubbed "q6_clocks" must be enabled before the Q6 is started by writing the Q6SS_RST_EVB register. The second set of clocks, "clks" should only be enabled after the Q6 is placed out of reset. Otherwise, the host CPU core that tries to start the remoteproc will hang. The downstream kernel had a funny comment, "Pray god and wait for reset to complete", which I decided to keep for entertainment value. Signed-off-by: Alexandru Gagniuc --- drivers/remoteproc/qcom_q6v5_wcss.c | 241 +++++++++++++++++++++++++++- 1 file changed, 234 insertions(+), 7 deletions(-) diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c index c27200159a88a..b62e97c92d058 100644 --- a/drivers/remoteproc/qcom_q6v5_wcss.c +++ b/drivers/remoteproc/qcom_q6v5_wcss.c @@ -31,8 +31,13 @@ #define Q6SS_MEM_PWR_CTL 0x0B0 #define Q6SS_STRAP_ACC 0x110 #define Q6SS_CGC_OVERRIDE 0x034 +#define Q6SS_BOOT_CORE_START 0x400 +#define Q6SS_BOOT_CMD 0x404 +#define Q6SS_BOOT_STATUS 0x408 #define Q6SS_BCR_REG 0x6000 +#define Q6SS_TIMEOUT_US 1000 + /* AXI Halt Register Offsets */ #define AXI_HALTREQ_REG 0x0 #define AXI_HALTACK_REG 0x4 @@ -67,6 +72,7 @@ #define HALT_CHECK_MAX_LOOPS 200 #define Q6SS_XO_CBCR GENMASK(5, 3) #define Q6SS_SLEEP_CBCR GENMASK(5, 2) +#define Q6SS_CORE_CBCR BIT(5) /* Q6SS config/status registers */ #define TCSR_GLOBAL_CFG0 0x0 @@ -77,9 +83,11 @@ #define Q6SS_RST_EVB 0x10 #define BHS_EN_REST_ACK BIT(0) +#define WCSS_HM_RET BIT(1) #define SSCAON_ENABLE BIT(13) #define SSCAON_BUS_EN BIT(15) #define SSCAON_BUS_MUX_MASK GENMASK(18, 16) +#define SSCAON_MASK GENMASK(17, 15) #define MEM_BANKS 19 #define TCSR_WCSS_CLK_MASK 0x1F @@ -88,6 +96,7 @@ #define MAX_HALT_REG 4 enum { WCSS_IPQ8074, + WCSS_IPQ9574, WCSS_QCS404, }; @@ -128,6 +137,12 @@ struct q6v5_wcss { struct clk *qdsp6ss_xo_cbcr; struct clk *qdsp6ss_core_gfmux; struct clk *lcc_bcr_sleep; + struct clk_bulk_data *clks; + /* clocks that must be started before the Q6 is booted */ + struct clk_bulk_data *q6_clks; + int num_clks; + int num_q6_clks; + struct regulator *cx_supply; struct qcom_sysmon *sysmon; @@ -236,6 +251,87 @@ static int q6v5_wcss_reset(struct q6v5_wcss *wcss) return 0; } +static int q6v7_wcss_reset(struct q6v5_wcss *wcss, struct rproc *rproc) +{ + int ret; + u32 val; + + /*1. Set TCSR GLOBAL CFG1*/ + ret = regmap_update_bits(wcss->halt_map, + wcss->halt_nc + TCSR_GLOBAL_CFG1, + 0xff00, 0x1100); + if (ret) { + dev_err(wcss->dev, "TCSE_GLOBAL_CFG1 failed\n"); + return ret; + } + + /* Enable Q6 clocks */ + ret = clk_bulk_prepare_enable(wcss->num_q6_clks, wcss->q6_clks); + if (ret) { + dev_err(wcss->dev, "failed to enable clocks, err=%d\n", ret); + return ret; + }; + + /* Write bootaddr to EVB so that Q6WCSS will jump there after reset */ + writel(rproc->bootaddr >> 4, wcss->reg_base + Q6SS_RST_EVB); + + /*2. Deassert AON Reset */ + ret = reset_control_deassert(wcss->wcss_aon_reset); + if (ret) { + dev_err(wcss->dev, "wcss_aon_reset failed\n"); + clk_bulk_disable_unprepare(wcss->num_clks, wcss->clks); + return ret; + } + + /*8. Set mpm configs*/ + /*set CFG[18:15]=1*/ + val = readl(wcss->rmb_base + SSCAON_CONFIG); + val &= ~SSCAON_MASK; + val |= SSCAON_BUS_EN; + writel(val, wcss->rmb_base + SSCAON_CONFIG); + + /*9. Wait for SSCAON_STATUS */ + ret = readl_poll_timeout(wcss->rmb_base + SSCAON_STATUS, + val, (val & 0xffff) == 0x10, 1000, + Q6SS_TIMEOUT_US * 1000); + if (ret) { + dev_err(wcss->dev, " Boot Error, SSCAON=0x%08X\n", val); + return ret; + } + + /*3. BHS require xo cbcr to be enabled */ + val = readl(wcss->reg_base + Q6SS_XO_CBCR); + val |= 0x1; + writel(val, wcss->reg_base + Q6SS_XO_CBCR); + + /*4. Enable core cbcr*/ + val = readl(wcss->reg_base + Q6SS_CORE_CBCR); + val |= 0x1; + writel(val, wcss->reg_base + Q6SS_CORE_CBCR); + + /*5. Enable sleep cbcr*/ + val = readl(wcss->reg_base + Q6SS_SLEEP_CBCR); + val |= 0x1; + writel(val, wcss->reg_base + Q6SS_SLEEP_CBCR); + + /*6. Boot core start */ + writel(0x1, wcss->reg_base + Q6SS_BOOT_CORE_START); + writel(0x1, wcss->reg_base + Q6SS_BOOT_CMD); + + /*7. Pray god and wait for reset to complete*/ + ret = readl_poll_timeout(wcss->reg_base + Q6SS_BOOT_STATUS, val, + (val & 0x01), 20000, 1000); + + /* Enable non-Q6 clocks */ + ret = clk_bulk_prepare_enable(wcss->num_clks, wcss->clks); + if (ret) { + dev_err(wcss->dev, "failed to enable clocks, err=%d\n", ret); + return ret; + }; + + return 0; +} + static int q6v5_wcss_start(struct rproc *rproc) { struct q6v5_wcss *wcss = rproc->priv; @@ -270,10 +366,20 @@ static int q6v5_wcss_start(struct rproc *rproc) if (ret) goto wcss_q6_reset; - /* Write bootaddr to EVB so that Q6WCSS will jump there after reset */ - writel(rproc->bootaddr >> 4, wcss->reg_base + Q6SS_RST_EVB); + switch (wcss->version) { + case WCSS_QCS404: + case WCSS_IPQ8074: + /* Write bootaddr to EVB so that Q6WCSS will jump there after + * reset. + */ + writel(rproc->bootaddr >> 4, wcss->reg_base + Q6SS_RST_EVB); + ret = q6v5_wcss_reset(wcss); + break; + case WCSS_IPQ9574: + ret = q6v7_wcss_reset(wcss, rproc); + break; + } - ret = q6v5_wcss_reset(wcss); if (ret) goto wcss_q6_reset; @@ -638,6 +744,41 @@ static int q6v5_wcss_powerdown(struct q6v5_wcss *wcss) return 0; } +static int q6v7_wcss_powerdown(struct q6v5_wcss *wcss) +{ + uint32_t val; + int ret; + + q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_wcss); + + val = readl(wcss->rmb_base + SSCAON_CONFIG); + val &= ~SSCAON_MASK; + val |= SSCAON_BUS_EN; + writel(val, wcss->rmb_base + SSCAON_CONFIG); + + val |= WCSS_HM_RET; + writel(val, wcss->rmb_base + SSCAON_CONFIG); + + ret = readl_poll_timeout(wcss->rmb_base + SSCAON_STATUS, + val, (val & 0xffff) == 0x400, 1000, + HALT_CHECK_MAX_LOOPS); + if (ret) { + dev_err(wcss->dev, + "can't get SSCAON_STATUS rc:%d)\n", ret); + return ret; + } + + usleep_range(2000, 4000); + + reset_control_assert(wcss->wcss_aon_reset); + + val = readl(wcss->rmb_base + SSCAON_CONFIG); + val &= ~WCSS_HM_RET; + writel(val, wcss->rmb_base + SSCAON_CONFIG); + + return 0; +} + static int q6v5_q6_powerdown(struct q6v5_wcss *wcss) { int ret; @@ -705,6 +846,25 @@ static int q6v5_q6_powerdown(struct q6v5_wcss *wcss) return 0; } +static void q6v7_q6_powerdown(struct q6v5_wcss *wcss) +{ + uint32_t val; + + q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_q6); + + /* Disable Q6 Core clock -- we don't know what bit 0 means */ + val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG); + val &= ~BIT(0); + writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG); + + clk_bulk_disable_unprepare(wcss->num_clks, wcss->clks); + clk_bulk_disable_unprepare(wcss->num_q6_clks, wcss->q6_clks); + + reset_control_assert(wcss->wcss_q6_reset); + usleep_range(1000, 2000); + reset_control_assert(wcss->wcss_reset); +} + static int q6v5_wcss_stop(struct rproc *rproc) { struct q6v5_wcss *wcss = rproc->priv; @@ -719,11 +879,21 @@ static int q6v5_wcss_stop(struct rproc *rproc) } } - if (wcss->version == WCSS_QCS404) { + switch (wcss->version) { + case WCSS_QCS404: ret = q6v5_qcs404_wcss_shutdown(wcss); if (ret) return ret; - } else { + break; + case WCSS_IPQ9574: + ret = q6v7_wcss_powerdown(wcss); + if (ret) + return ret; + + q6v7_q6_powerdown(wcss); + + break; + default: ret = q6v5_wcss_powerdown(wcss); if (ret) return ret; @@ -732,6 +902,7 @@ static int q6v5_wcss_stop(struct rproc *rproc) ret = q6v5_q6_powerdown(wcss); if (ret) return ret; + break; } qcom_q6v5_unprepare(&wcss->q6v5); @@ -838,7 +1009,9 @@ static int q6v5_wcss_init_mmio(struct q6v5_wcss *wcss, if (!wcss->reg_base) return -ENOMEM; - if (wcss->version == WCSS_IPQ8074) { + switch (wcss->version) { + case WCSS_IPQ8074: + case WCSS_IPQ9574: wcss->rmb_base = devm_platform_ioremap_resource_byname(pdev, "rmb"); if (IS_ERR(wcss->rmb_base)) return PTR_ERR(wcss->rmb_base); @@ -965,6 +1138,43 @@ static int q6v5_wcss_init_regulator(struct q6v5_wcss *wcss) return 0; } +static int ipq9574_init_clocks(struct q6v5_wcss *wcss) +{ + static const char *const q6_clks[] = { + "anoc_wcss_axi_m", "q6_ahb", "q6_ahb_s", "q6_axim", "q6ss_boot", + "mem_noc_q6_axi", "sys_noc_wcss_ahb", "wcss_acmt", "wcss_ecahb", + "wcss_q6_tbu" }; + static const char *const clks[] = { + "q6_axim2", "wcss_ahb_s", "wcss_axi_m" }; + int i, ret; + + wcss->num_clks = ARRAY_SIZE(clks); + wcss->num_q6_clks = ARRAY_SIZE(q6_clks); + + wcss->q6_clks = devm_kcalloc(wcss->dev, wcss->num_q6_clks, + sizeof(*wcss->q6_clks), GFP_KERNEL); + if (!wcss->q6_clks) + return -ENOMEM; + + wcss->clks = devm_kcalloc(wcss->dev, wcss->num_clks, + sizeof(*wcss->clks), GFP_KERNEL); + if (!wcss->clks) + return -ENOMEM; + + for (i = 0; i < wcss->num_q6_clks; i++) + wcss->q6_clks[i].id = q6_clks[i]; + + for (i = 0; i < wcss->num_clks; i++) + wcss->clks[i].id = clks[i]; + + ret = devm_clk_bulk_get(wcss->dev, wcss->num_q6_clks, wcss->q6_clks); + if (ret < 0) + return ret; + + return devm_clk_bulk_get(wcss->dev, wcss->num_clks, wcss->clks); +} + + static int q6v5_wcss_probe(struct platform_device *pdev) { const struct wcss_data *desc; @@ -997,7 +1207,8 @@ static int q6v5_wcss_probe(struct platform_device *pdev) if (ret) return ret; - if (wcss->version == WCSS_QCS404) { + switch (wcss->version) { + case WCSS_QCS404: ret = q6v5_wcss_init_clock(wcss); if (ret) return ret; @@ -1005,6 +1216,11 @@ static int q6v5_wcss_probe(struct platform_device *pdev) ret = q6v5_wcss_init_regulator(wcss); if (ret) return ret; + break; + case WCSS_IPQ9574: + ret = ipq9574_init_clocks(wcss); + if (ret) + return ret; } ret = q6v5_wcss_init_reset(wcss, desc); @@ -1067,6 +1283,16 @@ static const struct wcss_data wcss_ipq8074_res_init = { .requires_force_stop = true, }; +static const struct wcss_data wcss_ipq9574_res_init = { + .firmware_name = "IPQ9574/q6_fw.mdt", + .version = WCSS_IPQ9574, + .crash_reason_smem = WCSS_CRASH_REASON, + .aon_reset_required = true, + .ssr_name = "q6wcss", + .ops = &q6v5_wcss_ipq8074_ops, + .requires_force_stop = true, +}; + static const struct wcss_data wcss_qcs404_res_init = { .crash_reason_smem = WCSS_CRASH_REASON, .firmware_name = "wcnss.mdt", @@ -1082,6 +1308,7 @@ static const struct wcss_data wcss_qcs404_res_init = { static const struct of_device_id q6v5_wcss_of_match[] = { { .compatible = "qcom,ipq8074-wcss-pil", .data = &wcss_ipq8074_res_init }, + { .compatible = "qcom,ipq9574-wcss-pil", .data = &wcss_ipq9574_res_init }, { .compatible = "qcom,qcs404-wcss-pil", .data = &wcss_qcs404_res_init }, { }, }; -- 2.45.1