18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Qualcomm Technology Inc. ADSP Peripheral Image Loader for SDM845. 48c2ecf20Sopenharmony_ci * Copyright (c) 2018, The Linux Foundation. All rights reserved. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk.h> 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/firmware.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/io.h> 128c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/of_address.h> 178c2ecf20Sopenharmony_ci#include <linux/of_device.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <linux/pm_domain.h> 208c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 218c2ecf20Sopenharmony_ci#include <linux/regmap.h> 228c2ecf20Sopenharmony_ci#include <linux/remoteproc.h> 238c2ecf20Sopenharmony_ci#include <linux/reset.h> 248c2ecf20Sopenharmony_ci#include <linux/soc/qcom/mdt_loader.h> 258c2ecf20Sopenharmony_ci#include <linux/soc/qcom/smem.h> 268c2ecf20Sopenharmony_ci#include <linux/soc/qcom/smem_state.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "qcom_common.h" 298c2ecf20Sopenharmony_ci#include "qcom_pil_info.h" 308c2ecf20Sopenharmony_ci#include "qcom_q6v5.h" 318c2ecf20Sopenharmony_ci#include "remoteproc_internal.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* time out value */ 348c2ecf20Sopenharmony_ci#define ACK_TIMEOUT 1000 358c2ecf20Sopenharmony_ci#define BOOT_FSM_TIMEOUT 10000 368c2ecf20Sopenharmony_ci/* mask values */ 378c2ecf20Sopenharmony_ci#define EVB_MASK GENMASK(27, 4) 388c2ecf20Sopenharmony_ci/*QDSP6SS register offsets*/ 398c2ecf20Sopenharmony_ci#define RST_EVB_REG 0x10 408c2ecf20Sopenharmony_ci#define CORE_START_REG 0x400 418c2ecf20Sopenharmony_ci#define BOOT_CMD_REG 0x404 428c2ecf20Sopenharmony_ci#define BOOT_STATUS_REG 0x408 438c2ecf20Sopenharmony_ci#define RET_CFG_REG 0x1C 448c2ecf20Sopenharmony_ci/*TCSR register offsets*/ 458c2ecf20Sopenharmony_ci#define LPASS_MASTER_IDLE_REG 0x8 468c2ecf20Sopenharmony_ci#define LPASS_HALTACK_REG 0x4 478c2ecf20Sopenharmony_ci#define LPASS_PWR_ON_REG 0x10 488c2ecf20Sopenharmony_ci#define LPASS_HALTREQ_REG 0x0 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define QDSP6SS_XO_CBCR 0x38 518c2ecf20Sopenharmony_ci#define QDSP6SS_CORE_CBCR 0x20 528c2ecf20Sopenharmony_ci#define QDSP6SS_SLEEP_CBCR 0x3c 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistruct adsp_pil_data { 558c2ecf20Sopenharmony_ci int crash_reason_smem; 568c2ecf20Sopenharmony_ci const char *firmware_name; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci const char *ssr_name; 598c2ecf20Sopenharmony_ci const char *sysmon_name; 608c2ecf20Sopenharmony_ci int ssctl_id; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci const char **clk_ids; 638c2ecf20Sopenharmony_ci int num_clks; 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistruct qcom_adsp { 678c2ecf20Sopenharmony_ci struct device *dev; 688c2ecf20Sopenharmony_ci struct rproc *rproc; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci struct qcom_q6v5 q6v5; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci struct clk *xo; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci int num_clks; 758c2ecf20Sopenharmony_ci struct clk_bulk_data *clks; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci void __iomem *qdsp6ss_base; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci struct reset_control *pdc_sync_reset; 808c2ecf20Sopenharmony_ci struct reset_control *restart; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci struct regmap *halt_map; 838c2ecf20Sopenharmony_ci unsigned int halt_lpass; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci int crash_reason_smem; 868c2ecf20Sopenharmony_ci const char *info_name; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci struct completion start_done; 898c2ecf20Sopenharmony_ci struct completion stop_done; 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 struct qcom_sysmon *sysmon; 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int qcom_adsp_shutdown(struct qcom_adsp *adsp) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci unsigned long timeout; 1048c2ecf20Sopenharmony_ci unsigned int val; 1058c2ecf20Sopenharmony_ci int ret; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* Reset the retention logic */ 1088c2ecf20Sopenharmony_ci val = readl(adsp->qdsp6ss_base + RET_CFG_REG); 1098c2ecf20Sopenharmony_ci val |= 0x1; 1108c2ecf20Sopenharmony_ci writel(val, adsp->qdsp6ss_base + RET_CFG_REG); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci clk_bulk_disable_unprepare(adsp->num_clks, adsp->clks); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* QDSP6 master port needs to be explicitly halted */ 1158c2ecf20Sopenharmony_ci ret = regmap_read(adsp->halt_map, 1168c2ecf20Sopenharmony_ci adsp->halt_lpass + LPASS_PWR_ON_REG, &val); 1178c2ecf20Sopenharmony_ci if (ret || !val) 1188c2ecf20Sopenharmony_ci goto reset; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci ret = regmap_read(adsp->halt_map, 1218c2ecf20Sopenharmony_ci adsp->halt_lpass + LPASS_MASTER_IDLE_REG, 1228c2ecf20Sopenharmony_ci &val); 1238c2ecf20Sopenharmony_ci if (ret || val) 1248c2ecf20Sopenharmony_ci goto reset; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci regmap_write(adsp->halt_map, 1278c2ecf20Sopenharmony_ci adsp->halt_lpass + LPASS_HALTREQ_REG, 1); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* Wait for halt ACK from QDSP6 */ 1308c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(ACK_TIMEOUT); 1318c2ecf20Sopenharmony_ci for (;;) { 1328c2ecf20Sopenharmony_ci ret = regmap_read(adsp->halt_map, 1338c2ecf20Sopenharmony_ci adsp->halt_lpass + LPASS_HALTACK_REG, &val); 1348c2ecf20Sopenharmony_ci if (ret || val || time_after(jiffies, timeout)) 1358c2ecf20Sopenharmony_ci break; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci usleep_range(1000, 1100); 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci ret = regmap_read(adsp->halt_map, 1418c2ecf20Sopenharmony_ci adsp->halt_lpass + LPASS_MASTER_IDLE_REG, &val); 1428c2ecf20Sopenharmony_ci if (ret || !val) 1438c2ecf20Sopenharmony_ci dev_err(adsp->dev, "port failed halt\n"); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cireset: 1468c2ecf20Sopenharmony_ci /* Assert the LPASS PDC Reset */ 1478c2ecf20Sopenharmony_ci reset_control_assert(adsp->pdc_sync_reset); 1488c2ecf20Sopenharmony_ci /* Place the LPASS processor into reset */ 1498c2ecf20Sopenharmony_ci reset_control_assert(adsp->restart); 1508c2ecf20Sopenharmony_ci /* wait after asserting subsystem restart from AOSS */ 1518c2ecf20Sopenharmony_ci usleep_range(200, 300); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* Clear the halt request for the AXIM and AHBM for Q6 */ 1548c2ecf20Sopenharmony_ci regmap_write(adsp->halt_map, adsp->halt_lpass + LPASS_HALTREQ_REG, 0); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* De-assert the LPASS PDC Reset */ 1578c2ecf20Sopenharmony_ci reset_control_deassert(adsp->pdc_sync_reset); 1588c2ecf20Sopenharmony_ci /* Remove the LPASS reset */ 1598c2ecf20Sopenharmony_ci reset_control_deassert(adsp->restart); 1608c2ecf20Sopenharmony_ci /* wait after de-asserting subsystem restart from AOSS */ 1618c2ecf20Sopenharmony_ci usleep_range(200, 300); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int adsp_load(struct rproc *rproc, const struct firmware *fw) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; 1698c2ecf20Sopenharmony_ci int ret; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci ret = qcom_mdt_load_no_init(adsp->dev, fw, rproc->firmware, 0, 1728c2ecf20Sopenharmony_ci adsp->mem_region, adsp->mem_phys, 1738c2ecf20Sopenharmony_ci adsp->mem_size, &adsp->mem_reloc); 1748c2ecf20Sopenharmony_ci if (ret) 1758c2ecf20Sopenharmony_ci return ret; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci qcom_pil_info_store(adsp->info_name, adsp->mem_phys, adsp->mem_size); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return 0; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic int adsp_start(struct rproc *rproc) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; 1858c2ecf20Sopenharmony_ci int ret; 1868c2ecf20Sopenharmony_ci unsigned int val; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci qcom_q6v5_prepare(&adsp->q6v5); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci ret = clk_prepare_enable(adsp->xo); 1918c2ecf20Sopenharmony_ci if (ret) 1928c2ecf20Sopenharmony_ci goto disable_irqs; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci dev_pm_genpd_set_performance_state(adsp->dev, INT_MAX); 1958c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(adsp->dev); 1968c2ecf20Sopenharmony_ci if (ret) { 1978c2ecf20Sopenharmony_ci pm_runtime_put_noidle(adsp->dev); 1988c2ecf20Sopenharmony_ci goto disable_xo_clk; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci ret = clk_bulk_prepare_enable(adsp->num_clks, adsp->clks); 2028c2ecf20Sopenharmony_ci if (ret) { 2038c2ecf20Sopenharmony_ci dev_err(adsp->dev, "adsp clk_enable failed\n"); 2048c2ecf20Sopenharmony_ci goto disable_power_domain; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* Enable the XO clock */ 2088c2ecf20Sopenharmony_ci writel(1, adsp->qdsp6ss_base + QDSP6SS_XO_CBCR); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* Enable the QDSP6SS sleep clock */ 2118c2ecf20Sopenharmony_ci writel(1, adsp->qdsp6ss_base + QDSP6SS_SLEEP_CBCR); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* Enable the QDSP6 core clock */ 2148c2ecf20Sopenharmony_ci writel(1, adsp->qdsp6ss_base + QDSP6SS_CORE_CBCR); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* Program boot address */ 2178c2ecf20Sopenharmony_ci writel(adsp->mem_phys >> 4, adsp->qdsp6ss_base + RST_EVB_REG); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* De-assert QDSP6 stop core. QDSP6 will execute after out of reset */ 2208c2ecf20Sopenharmony_ci writel(0x1, adsp->qdsp6ss_base + CORE_START_REG); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* Trigger boot FSM to start QDSP6 */ 2238c2ecf20Sopenharmony_ci writel(0x1, adsp->qdsp6ss_base + BOOT_CMD_REG); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* Wait for core to come out of reset */ 2268c2ecf20Sopenharmony_ci ret = readl_poll_timeout(adsp->qdsp6ss_base + BOOT_STATUS_REG, 2278c2ecf20Sopenharmony_ci val, (val & BIT(0)) != 0, 10, BOOT_FSM_TIMEOUT); 2288c2ecf20Sopenharmony_ci if (ret) { 2298c2ecf20Sopenharmony_ci dev_err(adsp->dev, "failed to bootup adsp\n"); 2308c2ecf20Sopenharmony_ci goto disable_adsp_clks; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci ret = qcom_q6v5_wait_for_start(&adsp->q6v5, msecs_to_jiffies(5 * HZ)); 2348c2ecf20Sopenharmony_ci if (ret == -ETIMEDOUT) { 2358c2ecf20Sopenharmony_ci dev_err(adsp->dev, "start timed out\n"); 2368c2ecf20Sopenharmony_ci goto disable_adsp_clks; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cidisable_adsp_clks: 2428c2ecf20Sopenharmony_ci clk_bulk_disable_unprepare(adsp->num_clks, adsp->clks); 2438c2ecf20Sopenharmony_cidisable_power_domain: 2448c2ecf20Sopenharmony_ci dev_pm_genpd_set_performance_state(adsp->dev, 0); 2458c2ecf20Sopenharmony_ci pm_runtime_put(adsp->dev); 2468c2ecf20Sopenharmony_cidisable_xo_clk: 2478c2ecf20Sopenharmony_ci clk_disable_unprepare(adsp->xo); 2488c2ecf20Sopenharmony_cidisable_irqs: 2498c2ecf20Sopenharmony_ci qcom_q6v5_unprepare(&adsp->q6v5); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return ret; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic void qcom_adsp_pil_handover(struct qcom_q6v5 *q6v5) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct qcom_adsp *adsp = container_of(q6v5, struct qcom_adsp, q6v5); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci clk_disable_unprepare(adsp->xo); 2598c2ecf20Sopenharmony_ci dev_pm_genpd_set_performance_state(adsp->dev, 0); 2608c2ecf20Sopenharmony_ci pm_runtime_put(adsp->dev); 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic int adsp_stop(struct rproc *rproc) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; 2668c2ecf20Sopenharmony_ci int handover; 2678c2ecf20Sopenharmony_ci int ret; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci ret = qcom_q6v5_request_stop(&adsp->q6v5); 2708c2ecf20Sopenharmony_ci if (ret == -ETIMEDOUT) 2718c2ecf20Sopenharmony_ci dev_err(adsp->dev, "timed out on wait\n"); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci ret = qcom_adsp_shutdown(adsp); 2748c2ecf20Sopenharmony_ci if (ret) 2758c2ecf20Sopenharmony_ci dev_err(adsp->dev, "failed to shutdown: %d\n", ret); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci handover = qcom_q6v5_unprepare(&adsp->q6v5); 2788c2ecf20Sopenharmony_ci if (handover) 2798c2ecf20Sopenharmony_ci qcom_adsp_pil_handover(&adsp->q6v5); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci return ret; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic void *adsp_da_to_va(struct rproc *rproc, u64 da, size_t len) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; 2878c2ecf20Sopenharmony_ci int offset; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci offset = da - adsp->mem_reloc; 2908c2ecf20Sopenharmony_ci if (offset < 0 || offset + len > adsp->mem_size) 2918c2ecf20Sopenharmony_ci return NULL; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return adsp->mem_region + offset; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic unsigned long adsp_panic(struct rproc *rproc) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci struct qcom_adsp *adsp = rproc->priv; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return qcom_q6v5_panic(&adsp->q6v5); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic const struct rproc_ops adsp_ops = { 3048c2ecf20Sopenharmony_ci .start = adsp_start, 3058c2ecf20Sopenharmony_ci .stop = adsp_stop, 3068c2ecf20Sopenharmony_ci .da_to_va = adsp_da_to_va, 3078c2ecf20Sopenharmony_ci .parse_fw = qcom_register_dump_segments, 3088c2ecf20Sopenharmony_ci .load = adsp_load, 3098c2ecf20Sopenharmony_ci .panic = adsp_panic, 3108c2ecf20Sopenharmony_ci}; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int adsp_init_clock(struct qcom_adsp *adsp, const char **clk_ids) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci int num_clks = 0; 3158c2ecf20Sopenharmony_ci int i, ret; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci adsp->xo = devm_clk_get(adsp->dev, "xo"); 3188c2ecf20Sopenharmony_ci if (IS_ERR(adsp->xo)) { 3198c2ecf20Sopenharmony_ci ret = PTR_ERR(adsp->xo); 3208c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 3218c2ecf20Sopenharmony_ci dev_err(adsp->dev, "failed to get xo clock"); 3228c2ecf20Sopenharmony_ci return ret; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci for (i = 0; clk_ids[i]; i++) 3268c2ecf20Sopenharmony_ci num_clks++; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci adsp->num_clks = num_clks; 3298c2ecf20Sopenharmony_ci adsp->clks = devm_kcalloc(adsp->dev, adsp->num_clks, 3308c2ecf20Sopenharmony_ci sizeof(*adsp->clks), GFP_KERNEL); 3318c2ecf20Sopenharmony_ci if (!adsp->clks) 3328c2ecf20Sopenharmony_ci return -ENOMEM; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci for (i = 0; i < adsp->num_clks; i++) 3358c2ecf20Sopenharmony_ci adsp->clks[i].id = clk_ids[i]; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci return devm_clk_bulk_get(adsp->dev, adsp->num_clks, adsp->clks); 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic int adsp_init_reset(struct qcom_adsp *adsp) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci adsp->pdc_sync_reset = devm_reset_control_get_optional_exclusive(adsp->dev, 3438c2ecf20Sopenharmony_ci "pdc_sync"); 3448c2ecf20Sopenharmony_ci if (IS_ERR(adsp->pdc_sync_reset)) { 3458c2ecf20Sopenharmony_ci dev_err(adsp->dev, "failed to acquire pdc_sync reset\n"); 3468c2ecf20Sopenharmony_ci return PTR_ERR(adsp->pdc_sync_reset); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci adsp->restart = devm_reset_control_get_optional_exclusive(adsp->dev, "restart"); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* Fall back to the old "cc_lpass" if "restart" is absent */ 3528c2ecf20Sopenharmony_ci if (!adsp->restart) 3538c2ecf20Sopenharmony_ci adsp->restart = devm_reset_control_get_exclusive(adsp->dev, "cc_lpass"); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (IS_ERR(adsp->restart)) { 3568c2ecf20Sopenharmony_ci dev_err(adsp->dev, "failed to acquire restart\n"); 3578c2ecf20Sopenharmony_ci return PTR_ERR(adsp->restart); 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return 0; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int adsp_init_mmio(struct qcom_adsp *adsp, 3648c2ecf20Sopenharmony_ci struct platform_device *pdev) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci struct device_node *syscon; 3678c2ecf20Sopenharmony_ci int ret; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci adsp->qdsp6ss_base = devm_platform_ioremap_resource(pdev, 0); 3708c2ecf20Sopenharmony_ci if (IS_ERR(adsp->qdsp6ss_base)) { 3718c2ecf20Sopenharmony_ci dev_err(adsp->dev, "failed to map QDSP6SS registers\n"); 3728c2ecf20Sopenharmony_ci return PTR_ERR(adsp->qdsp6ss_base); 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci syscon = of_parse_phandle(pdev->dev.of_node, "qcom,halt-regs", 0); 3768c2ecf20Sopenharmony_ci if (!syscon) { 3778c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n"); 3788c2ecf20Sopenharmony_ci return -EINVAL; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci adsp->halt_map = syscon_node_to_regmap(syscon); 3828c2ecf20Sopenharmony_ci of_node_put(syscon); 3838c2ecf20Sopenharmony_ci if (IS_ERR(adsp->halt_map)) 3848c2ecf20Sopenharmony_ci return PTR_ERR(adsp->halt_map); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci ret = of_property_read_u32_index(pdev->dev.of_node, "qcom,halt-regs", 3878c2ecf20Sopenharmony_ci 1, &adsp->halt_lpass); 3888c2ecf20Sopenharmony_ci if (ret < 0) { 3898c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no offset in syscon\n"); 3908c2ecf20Sopenharmony_ci return ret; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic int adsp_alloc_memory_region(struct qcom_adsp *adsp) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct device_node *node; 3998c2ecf20Sopenharmony_ci struct resource r; 4008c2ecf20Sopenharmony_ci int ret; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci node = of_parse_phandle(adsp->dev->of_node, "memory-region", 0); 4038c2ecf20Sopenharmony_ci if (!node) { 4048c2ecf20Sopenharmony_ci dev_err(adsp->dev, "no memory-region specified\n"); 4058c2ecf20Sopenharmony_ci return -EINVAL; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci ret = of_address_to_resource(node, 0, &r); 4098c2ecf20Sopenharmony_ci of_node_put(node); 4108c2ecf20Sopenharmony_ci if (ret) 4118c2ecf20Sopenharmony_ci return ret; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci adsp->mem_phys = adsp->mem_reloc = r.start; 4148c2ecf20Sopenharmony_ci adsp->mem_size = resource_size(&r); 4158c2ecf20Sopenharmony_ci adsp->mem_region = devm_ioremap_wc(adsp->dev, 4168c2ecf20Sopenharmony_ci adsp->mem_phys, adsp->mem_size); 4178c2ecf20Sopenharmony_ci if (!adsp->mem_region) { 4188c2ecf20Sopenharmony_ci dev_err(adsp->dev, "unable to map memory region: %pa+%zx\n", 4198c2ecf20Sopenharmony_ci &r.start, adsp->mem_size); 4208c2ecf20Sopenharmony_ci return -EBUSY; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci return 0; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic int adsp_probe(struct platform_device *pdev) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci const struct adsp_pil_data *desc; 4298c2ecf20Sopenharmony_ci struct qcom_adsp *adsp; 4308c2ecf20Sopenharmony_ci struct rproc *rproc; 4318c2ecf20Sopenharmony_ci int ret; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci desc = of_device_get_match_data(&pdev->dev); 4348c2ecf20Sopenharmony_ci if (!desc) 4358c2ecf20Sopenharmony_ci return -EINVAL; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops, 4388c2ecf20Sopenharmony_ci desc->firmware_name, sizeof(*adsp)); 4398c2ecf20Sopenharmony_ci if (!rproc) { 4408c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unable to allocate remoteproc\n"); 4418c2ecf20Sopenharmony_ci return -ENOMEM; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci adsp = (struct qcom_adsp *)rproc->priv; 4468c2ecf20Sopenharmony_ci adsp->dev = &pdev->dev; 4478c2ecf20Sopenharmony_ci adsp->rproc = rproc; 4488c2ecf20Sopenharmony_ci adsp->info_name = desc->sysmon_name; 4498c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, adsp); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci ret = adsp_alloc_memory_region(adsp); 4528c2ecf20Sopenharmony_ci if (ret) 4538c2ecf20Sopenharmony_ci goto free_rproc; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci ret = adsp_init_clock(adsp, desc->clk_ids); 4568c2ecf20Sopenharmony_ci if (ret) 4578c2ecf20Sopenharmony_ci goto free_rproc; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci pm_runtime_enable(adsp->dev); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci ret = adsp_init_reset(adsp); 4628c2ecf20Sopenharmony_ci if (ret) 4638c2ecf20Sopenharmony_ci goto disable_pm; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci ret = adsp_init_mmio(adsp, pdev); 4668c2ecf20Sopenharmony_ci if (ret) 4678c2ecf20Sopenharmony_ci goto disable_pm; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci ret = qcom_q6v5_init(&adsp->q6v5, pdev, rproc, desc->crash_reason_smem, 4708c2ecf20Sopenharmony_ci qcom_adsp_pil_handover); 4718c2ecf20Sopenharmony_ci if (ret) 4728c2ecf20Sopenharmony_ci goto disable_pm; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci qcom_add_glink_subdev(rproc, &adsp->glink_subdev, desc->ssr_name); 4758c2ecf20Sopenharmony_ci qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name); 4768c2ecf20Sopenharmony_ci adsp->sysmon = qcom_add_sysmon_subdev(rproc, 4778c2ecf20Sopenharmony_ci desc->sysmon_name, 4788c2ecf20Sopenharmony_ci desc->ssctl_id); 4798c2ecf20Sopenharmony_ci if (IS_ERR(adsp->sysmon)) { 4808c2ecf20Sopenharmony_ci ret = PTR_ERR(adsp->sysmon); 4818c2ecf20Sopenharmony_ci goto disable_pm; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci ret = rproc_add(rproc); 4858c2ecf20Sopenharmony_ci if (ret) 4868c2ecf20Sopenharmony_ci goto disable_pm; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return 0; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cidisable_pm: 4918c2ecf20Sopenharmony_ci pm_runtime_disable(adsp->dev); 4928c2ecf20Sopenharmony_cifree_rproc: 4938c2ecf20Sopenharmony_ci rproc_free(rproc); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return ret; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic int adsp_remove(struct platform_device *pdev) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct qcom_adsp *adsp = platform_get_drvdata(pdev); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci rproc_del(adsp->rproc); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev); 5058c2ecf20Sopenharmony_ci qcom_remove_sysmon_subdev(adsp->sysmon); 5068c2ecf20Sopenharmony_ci qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev); 5078c2ecf20Sopenharmony_ci pm_runtime_disable(adsp->dev); 5088c2ecf20Sopenharmony_ci rproc_free(adsp->rproc); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic const struct adsp_pil_data adsp_resource_init = { 5148c2ecf20Sopenharmony_ci .crash_reason_smem = 423, 5158c2ecf20Sopenharmony_ci .firmware_name = "adsp.mdt", 5168c2ecf20Sopenharmony_ci .ssr_name = "lpass", 5178c2ecf20Sopenharmony_ci .sysmon_name = "adsp", 5188c2ecf20Sopenharmony_ci .ssctl_id = 0x14, 5198c2ecf20Sopenharmony_ci .clk_ids = (const char*[]) { 5208c2ecf20Sopenharmony_ci "sway_cbcr", "lpass_ahbs_aon_cbcr", "lpass_ahbm_aon_cbcr", 5218c2ecf20Sopenharmony_ci "qdsp6ss_xo", "qdsp6ss_sleep", "qdsp6ss_core", NULL 5228c2ecf20Sopenharmony_ci }, 5238c2ecf20Sopenharmony_ci .num_clks = 7, 5248c2ecf20Sopenharmony_ci}; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic const struct adsp_pil_data cdsp_resource_init = { 5278c2ecf20Sopenharmony_ci .crash_reason_smem = 601, 5288c2ecf20Sopenharmony_ci .firmware_name = "cdsp.mdt", 5298c2ecf20Sopenharmony_ci .ssr_name = "cdsp", 5308c2ecf20Sopenharmony_ci .sysmon_name = "cdsp", 5318c2ecf20Sopenharmony_ci .ssctl_id = 0x17, 5328c2ecf20Sopenharmony_ci .clk_ids = (const char*[]) { 5338c2ecf20Sopenharmony_ci "sway", "tbu", "bimc", "ahb_aon", "q6ss_slave", "q6ss_master", 5348c2ecf20Sopenharmony_ci "q6_axim", NULL 5358c2ecf20Sopenharmony_ci }, 5368c2ecf20Sopenharmony_ci .num_clks = 7, 5378c2ecf20Sopenharmony_ci}; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic const struct of_device_id adsp_of_match[] = { 5408c2ecf20Sopenharmony_ci { .compatible = "qcom,qcs404-cdsp-pil", .data = &cdsp_resource_init }, 5418c2ecf20Sopenharmony_ci { .compatible = "qcom,sdm845-adsp-pil", .data = &adsp_resource_init }, 5428c2ecf20Sopenharmony_ci { }, 5438c2ecf20Sopenharmony_ci}; 5448c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, adsp_of_match); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic struct platform_driver adsp_pil_driver = { 5478c2ecf20Sopenharmony_ci .probe = adsp_probe, 5488c2ecf20Sopenharmony_ci .remove = adsp_remove, 5498c2ecf20Sopenharmony_ci .driver = { 5508c2ecf20Sopenharmony_ci .name = "qcom_q6v5_adsp", 5518c2ecf20Sopenharmony_ci .of_match_table = adsp_of_match, 5528c2ecf20Sopenharmony_ci }, 5538c2ecf20Sopenharmony_ci}; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cimodule_platform_driver(adsp_pil_driver); 5568c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("QTI SDM845 ADSP Peripheral Image Loader"); 5578c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 558