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