162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (c) 2019 MediaTek Inc. 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <asm/barrier.h> 662306a36Sopenharmony_ci#include <linux/clk.h> 762306a36Sopenharmony_ci#include <linux/dma-mapping.h> 862306a36Sopenharmony_ci#include <linux/err.h> 962306a36Sopenharmony_ci#include <linux/interrupt.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/of_address.h> 1362306a36Sopenharmony_ci#include <linux/of_platform.h> 1462306a36Sopenharmony_ci#include <linux/of_reserved_mem.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/remoteproc.h> 1762306a36Sopenharmony_ci#include <linux/remoteproc/mtk_scp.h> 1862306a36Sopenharmony_ci#include <linux/rpmsg/mtk_rpmsg.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "mtk_common.h" 2162306a36Sopenharmony_ci#include "remoteproc_internal.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define MAX_CODE_SIZE 0x500000 2462306a36Sopenharmony_ci#define SECTION_NAME_IPI_BUFFER ".ipi_buffer" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/** 2762306a36Sopenharmony_ci * scp_get() - get a reference to SCP. 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * @pdev: the platform device of the module requesting SCP platform 3062306a36Sopenharmony_ci * device for using SCP API. 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * Return: Return NULL if failed. otherwise reference to SCP. 3362306a36Sopenharmony_ci **/ 3462306a36Sopenharmony_cistruct mtk_scp *scp_get(struct platform_device *pdev) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 3762306a36Sopenharmony_ci struct device_node *scp_node; 3862306a36Sopenharmony_ci struct platform_device *scp_pdev; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci scp_node = of_parse_phandle(dev->of_node, "mediatek,scp", 0); 4162306a36Sopenharmony_ci if (!scp_node) { 4262306a36Sopenharmony_ci dev_err(dev, "can't get SCP node\n"); 4362306a36Sopenharmony_ci return NULL; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci scp_pdev = of_find_device_by_node(scp_node); 4762306a36Sopenharmony_ci of_node_put(scp_node); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (WARN_ON(!scp_pdev)) { 5062306a36Sopenharmony_ci dev_err(dev, "SCP pdev failed\n"); 5162306a36Sopenharmony_ci return NULL; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return platform_get_drvdata(scp_pdev); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scp_get); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/** 5962306a36Sopenharmony_ci * scp_put() - "free" the SCP 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * @scp: mtk_scp structure from scp_get(). 6262306a36Sopenharmony_ci **/ 6362306a36Sopenharmony_civoid scp_put(struct mtk_scp *scp) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci put_device(scp->dev); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scp_put); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic void scp_wdt_handler(struct mtk_scp *scp, u32 scp_to_host) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci dev_err(scp->dev, "SCP watchdog timeout! 0x%x", scp_to_host); 7262306a36Sopenharmony_ci rproc_report_crash(scp->rproc, RPROC_WATCHDOG); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic void scp_init_ipi_handler(void *data, unsigned int len, void *priv) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct mtk_scp *scp = priv; 7862306a36Sopenharmony_ci struct scp_run *run = data; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci scp->run.signaled = run->signaled; 8162306a36Sopenharmony_ci strscpy(scp->run.fw_ver, run->fw_ver, SCP_FW_VER_LEN); 8262306a36Sopenharmony_ci scp->run.dec_capability = run->dec_capability; 8362306a36Sopenharmony_ci scp->run.enc_capability = run->enc_capability; 8462306a36Sopenharmony_ci wake_up_interruptible(&scp->run.wq); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void scp_ipi_handler(struct mtk_scp *scp) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct mtk_share_obj __iomem *rcv_obj = scp->recv_buf; 9062306a36Sopenharmony_ci struct scp_ipi_desc *ipi_desc = scp->ipi_desc; 9162306a36Sopenharmony_ci u8 tmp_data[SCP_SHARE_BUFFER_SIZE]; 9262306a36Sopenharmony_ci scp_ipi_handler_t handler; 9362306a36Sopenharmony_ci u32 id = readl(&rcv_obj->id); 9462306a36Sopenharmony_ci u32 len = readl(&rcv_obj->len); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (len > SCP_SHARE_BUFFER_SIZE) { 9762306a36Sopenharmony_ci dev_err(scp->dev, "ipi message too long (len %d, max %d)", len, 9862306a36Sopenharmony_ci SCP_SHARE_BUFFER_SIZE); 9962306a36Sopenharmony_ci return; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci if (id >= SCP_IPI_MAX) { 10262306a36Sopenharmony_ci dev_err(scp->dev, "No such ipi id = %d\n", id); 10362306a36Sopenharmony_ci return; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci scp_ipi_lock(scp, id); 10762306a36Sopenharmony_ci handler = ipi_desc[id].handler; 10862306a36Sopenharmony_ci if (!handler) { 10962306a36Sopenharmony_ci dev_err(scp->dev, "No such ipi id = %d\n", id); 11062306a36Sopenharmony_ci scp_ipi_unlock(scp, id); 11162306a36Sopenharmony_ci return; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci memcpy_fromio(tmp_data, &rcv_obj->share_buf, len); 11562306a36Sopenharmony_ci handler(tmp_data, len, ipi_desc[id].priv); 11662306a36Sopenharmony_ci scp_ipi_unlock(scp, id); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci scp->ipi_id_ack[id] = true; 11962306a36Sopenharmony_ci wake_up(&scp->ack_wq); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int scp_elf_read_ipi_buf_addr(struct mtk_scp *scp, 12362306a36Sopenharmony_ci const struct firmware *fw, 12462306a36Sopenharmony_ci size_t *offset); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int scp_ipi_init(struct mtk_scp *scp, const struct firmware *fw) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci int ret; 12962306a36Sopenharmony_ci size_t offset; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* read the ipi buf addr from FW itself first */ 13262306a36Sopenharmony_ci ret = scp_elf_read_ipi_buf_addr(scp, fw, &offset); 13362306a36Sopenharmony_ci if (ret) { 13462306a36Sopenharmony_ci /* use default ipi buf addr if the FW doesn't have it */ 13562306a36Sopenharmony_ci offset = scp->data->ipi_buf_offset; 13662306a36Sopenharmony_ci if (!offset) 13762306a36Sopenharmony_ci return ret; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci dev_info(scp->dev, "IPI buf addr %#010zx\n", offset); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci scp->recv_buf = (struct mtk_share_obj __iomem *) 14262306a36Sopenharmony_ci (scp->sram_base + offset); 14362306a36Sopenharmony_ci scp->send_buf = (struct mtk_share_obj __iomem *) 14462306a36Sopenharmony_ci (scp->sram_base + offset + sizeof(*scp->recv_buf)); 14562306a36Sopenharmony_ci memset_io(scp->recv_buf, 0, sizeof(*scp->recv_buf)); 14662306a36Sopenharmony_ci memset_io(scp->send_buf, 0, sizeof(*scp->send_buf)); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic void mt8183_scp_reset_assert(struct mtk_scp *scp) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci u32 val; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci val = readl(scp->reg_base + MT8183_SW_RSTN); 15662306a36Sopenharmony_ci val &= ~MT8183_SW_RSTN_BIT; 15762306a36Sopenharmony_ci writel(val, scp->reg_base + MT8183_SW_RSTN); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic void mt8183_scp_reset_deassert(struct mtk_scp *scp) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci u32 val; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci val = readl(scp->reg_base + MT8183_SW_RSTN); 16562306a36Sopenharmony_ci val |= MT8183_SW_RSTN_BIT; 16662306a36Sopenharmony_ci writel(val, scp->reg_base + MT8183_SW_RSTN); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic void mt8192_scp_reset_assert(struct mtk_scp *scp) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci writel(1, scp->reg_base + MT8192_CORE0_SW_RSTN_SET); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic void mt8192_scp_reset_deassert(struct mtk_scp *scp) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci writel(1, scp->reg_base + MT8192_CORE0_SW_RSTN_CLR); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic void mt8183_scp_irq_handler(struct mtk_scp *scp) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci u32 scp_to_host; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci scp_to_host = readl(scp->reg_base + MT8183_SCP_TO_HOST); 18462306a36Sopenharmony_ci if (scp_to_host & MT8183_SCP_IPC_INT_BIT) 18562306a36Sopenharmony_ci scp_ipi_handler(scp); 18662306a36Sopenharmony_ci else 18762306a36Sopenharmony_ci scp_wdt_handler(scp, scp_to_host); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* SCP won't send another interrupt until we set SCP_TO_HOST to 0. */ 19062306a36Sopenharmony_ci writel(MT8183_SCP_IPC_INT_BIT | MT8183_SCP_WDT_INT_BIT, 19162306a36Sopenharmony_ci scp->reg_base + MT8183_SCP_TO_HOST); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic void mt8192_scp_irq_handler(struct mtk_scp *scp) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci u32 scp_to_host; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci scp_to_host = readl(scp->reg_base + MT8192_SCP2APMCU_IPC_SET); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (scp_to_host & MT8192_SCP_IPC_INT_BIT) { 20162306a36Sopenharmony_ci scp_ipi_handler(scp); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* 20462306a36Sopenharmony_ci * SCP won't send another interrupt until we clear 20562306a36Sopenharmony_ci * MT8192_SCP2APMCU_IPC. 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_ci writel(MT8192_SCP_IPC_INT_BIT, 20862306a36Sopenharmony_ci scp->reg_base + MT8192_SCP2APMCU_IPC_CLR); 20962306a36Sopenharmony_ci } else { 21062306a36Sopenharmony_ci scp_wdt_handler(scp, scp_to_host); 21162306a36Sopenharmony_ci writel(1, scp->reg_base + MT8192_CORE0_WDT_IRQ); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic irqreturn_t scp_irq_handler(int irq, void *priv) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct mtk_scp *scp = priv; 21862306a36Sopenharmony_ci int ret; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci ret = clk_prepare_enable(scp->clk); 22162306a36Sopenharmony_ci if (ret) { 22262306a36Sopenharmony_ci dev_err(scp->dev, "failed to enable clocks\n"); 22362306a36Sopenharmony_ci return IRQ_NONE; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci scp->data->scp_irq_handler(scp); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci clk_disable_unprepare(scp->clk); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return IRQ_HANDLED; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic int scp_elf_load_segments(struct rproc *rproc, const struct firmware *fw) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct device *dev = &rproc->dev; 23662306a36Sopenharmony_ci struct elf32_hdr *ehdr; 23762306a36Sopenharmony_ci struct elf32_phdr *phdr; 23862306a36Sopenharmony_ci int i, ret = 0; 23962306a36Sopenharmony_ci const u8 *elf_data = fw->data; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci ehdr = (struct elf32_hdr *)elf_data; 24262306a36Sopenharmony_ci phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* go through the available ELF segments */ 24562306a36Sopenharmony_ci for (i = 0; i < ehdr->e_phnum; i++, phdr++) { 24662306a36Sopenharmony_ci u32 da = phdr->p_paddr; 24762306a36Sopenharmony_ci u32 memsz = phdr->p_memsz; 24862306a36Sopenharmony_ci u32 filesz = phdr->p_filesz; 24962306a36Sopenharmony_ci u32 offset = phdr->p_offset; 25062306a36Sopenharmony_ci void __iomem *ptr; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n", 25362306a36Sopenharmony_ci phdr->p_type, da, memsz, filesz); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (phdr->p_type != PT_LOAD) 25662306a36Sopenharmony_ci continue; 25762306a36Sopenharmony_ci if (!filesz) 25862306a36Sopenharmony_ci continue; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (filesz > memsz) { 26162306a36Sopenharmony_ci dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n", 26262306a36Sopenharmony_ci filesz, memsz); 26362306a36Sopenharmony_ci ret = -EINVAL; 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (offset + filesz > fw->size) { 26862306a36Sopenharmony_ci dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n", 26962306a36Sopenharmony_ci offset + filesz, fw->size); 27062306a36Sopenharmony_ci ret = -EINVAL; 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* grab the kernel address for this device address */ 27562306a36Sopenharmony_ci ptr = (void __iomem *)rproc_da_to_va(rproc, da, memsz, NULL); 27662306a36Sopenharmony_ci if (!ptr) { 27762306a36Sopenharmony_ci dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz); 27862306a36Sopenharmony_ci ret = -EINVAL; 27962306a36Sopenharmony_ci break; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* put the segment where the remote processor expects it */ 28362306a36Sopenharmony_ci scp_memcpy_aligned(ptr, elf_data + phdr->p_offset, filesz); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci return ret; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int scp_elf_read_ipi_buf_addr(struct mtk_scp *scp, 29062306a36Sopenharmony_ci const struct firmware *fw, 29162306a36Sopenharmony_ci size_t *offset) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci struct elf32_hdr *ehdr; 29462306a36Sopenharmony_ci struct elf32_shdr *shdr, *shdr_strtab; 29562306a36Sopenharmony_ci int i; 29662306a36Sopenharmony_ci const u8 *elf_data = fw->data; 29762306a36Sopenharmony_ci const char *strtab; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci ehdr = (struct elf32_hdr *)elf_data; 30062306a36Sopenharmony_ci shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff); 30162306a36Sopenharmony_ci shdr_strtab = shdr + ehdr->e_shstrndx; 30262306a36Sopenharmony_ci strtab = (const char *)(elf_data + shdr_strtab->sh_offset); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci for (i = 0; i < ehdr->e_shnum; i++, shdr++) { 30562306a36Sopenharmony_ci if (strcmp(strtab + shdr->sh_name, 30662306a36Sopenharmony_ci SECTION_NAME_IPI_BUFFER) == 0) { 30762306a36Sopenharmony_ci *offset = shdr->sh_addr; 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return -ENOENT; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic int mt8183_scp_clk_get(struct mtk_scp *scp) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct device *dev = scp->dev; 31862306a36Sopenharmony_ci int ret = 0; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci scp->clk = devm_clk_get(dev, "main"); 32162306a36Sopenharmony_ci if (IS_ERR(scp->clk)) { 32262306a36Sopenharmony_ci dev_err(dev, "Failed to get clock\n"); 32362306a36Sopenharmony_ci ret = PTR_ERR(scp->clk); 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return ret; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic int mt8192_scp_clk_get(struct mtk_scp *scp) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci return mt8183_scp_clk_get(scp); 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int mt8195_scp_clk_get(struct mtk_scp *scp) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci scp->clk = NULL; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return 0; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic int mt8183_scp_before_load(struct mtk_scp *scp) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci /* Clear SCP to host interrupt */ 34462306a36Sopenharmony_ci writel(MT8183_SCP_IPC_INT_BIT, scp->reg_base + MT8183_SCP_TO_HOST); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* Reset clocks before loading FW */ 34762306a36Sopenharmony_ci writel(0x0, scp->reg_base + MT8183_SCP_CLK_SW_SEL); 34862306a36Sopenharmony_ci writel(0x0, scp->reg_base + MT8183_SCP_CLK_DIV_SEL); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* Initialize TCM before loading FW. */ 35162306a36Sopenharmony_ci writel(0x0, scp->reg_base + MT8183_SCP_L1_SRAM_PD); 35262306a36Sopenharmony_ci writel(0x0, scp->reg_base + MT8183_SCP_TCM_TAIL_SRAM_PD); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* Turn on the power of SCP's SRAM before using it. */ 35562306a36Sopenharmony_ci writel(0x0, scp->reg_base + MT8183_SCP_SRAM_PDN); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* 35862306a36Sopenharmony_ci * Set I-cache and D-cache size before loading SCP FW. 35962306a36Sopenharmony_ci * SCP SRAM logical address may change when cache size setting differs. 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_ci writel(MT8183_SCP_CACHE_CON_WAYEN | MT8183_SCP_CACHESIZE_8KB, 36262306a36Sopenharmony_ci scp->reg_base + MT8183_SCP_CACHE_CON); 36362306a36Sopenharmony_ci writel(MT8183_SCP_CACHESIZE_8KB, scp->reg_base + MT8183_SCP_DCACHE_CON); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic void scp_sram_power_on(void __iomem *addr, u32 reserved_mask) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci int i; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci for (i = 31; i >= 0; i--) 37362306a36Sopenharmony_ci writel(GENMASK(i, 0) & ~reserved_mask, addr); 37462306a36Sopenharmony_ci writel(0, addr); 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic void scp_sram_power_off(void __iomem *addr, u32 reserved_mask) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci int i; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci writel(0, addr); 38262306a36Sopenharmony_ci for (i = 0; i < 32; i++) 38362306a36Sopenharmony_ci writel(GENMASK(i, 0) & ~reserved_mask, addr); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic int mt8186_scp_before_load(struct mtk_scp *scp) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci /* Clear SCP to host interrupt */ 38962306a36Sopenharmony_ci writel(MT8183_SCP_IPC_INT_BIT, scp->reg_base + MT8183_SCP_TO_HOST); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* Reset clocks before loading FW */ 39262306a36Sopenharmony_ci writel(0x0, scp->reg_base + MT8183_SCP_CLK_SW_SEL); 39362306a36Sopenharmony_ci writel(0x0, scp->reg_base + MT8183_SCP_CLK_DIV_SEL); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* Turn on the power of SCP's SRAM before using it. Enable 1 block per time*/ 39662306a36Sopenharmony_ci scp_sram_power_on(scp->reg_base + MT8183_SCP_SRAM_PDN, 0); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* Initialize TCM before loading FW. */ 39962306a36Sopenharmony_ci writel(0x0, scp->reg_base + MT8183_SCP_L1_SRAM_PD); 40062306a36Sopenharmony_ci writel(0x0, scp->reg_base + MT8183_SCP_TCM_TAIL_SRAM_PD); 40162306a36Sopenharmony_ci writel(0x0, scp->reg_base + MT8186_SCP_L1_SRAM_PD_P1); 40262306a36Sopenharmony_ci writel(0x0, scp->reg_base + MT8186_SCP_L1_SRAM_PD_p2); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* 40562306a36Sopenharmony_ci * Set I-cache and D-cache size before loading SCP FW. 40662306a36Sopenharmony_ci * SCP SRAM logical address may change when cache size setting differs. 40762306a36Sopenharmony_ci */ 40862306a36Sopenharmony_ci writel(MT8183_SCP_CACHE_CON_WAYEN | MT8183_SCP_CACHESIZE_8KB, 40962306a36Sopenharmony_ci scp->reg_base + MT8183_SCP_CACHE_CON); 41062306a36Sopenharmony_ci writel(MT8183_SCP_CACHESIZE_8KB, scp->reg_base + MT8183_SCP_DCACHE_CON); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci return 0; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic int mt8192_scp_before_load(struct mtk_scp *scp) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci /* clear SPM interrupt, SCP2SPM_IPC_CLR */ 41862306a36Sopenharmony_ci writel(0xff, scp->reg_base + MT8192_SCP2SPM_IPC_CLR); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci writel(1, scp->reg_base + MT8192_CORE0_SW_RSTN_SET); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* enable SRAM clock */ 42362306a36Sopenharmony_ci scp_sram_power_on(scp->reg_base + MT8192_L2TCM_SRAM_PD_0, 0); 42462306a36Sopenharmony_ci scp_sram_power_on(scp->reg_base + MT8192_L2TCM_SRAM_PD_1, 0); 42562306a36Sopenharmony_ci scp_sram_power_on(scp->reg_base + MT8192_L2TCM_SRAM_PD_2, 0); 42662306a36Sopenharmony_ci scp_sram_power_on(scp->reg_base + MT8192_L1TCM_SRAM_PDN, 0); 42762306a36Sopenharmony_ci scp_sram_power_on(scp->reg_base + MT8192_CPU0_SRAM_PD, 0); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* enable MPU for all memory regions */ 43062306a36Sopenharmony_ci writel(0xff, scp->reg_base + MT8192_CORE0_MEM_ATT_PREDEF); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int mt8195_scp_before_load(struct mtk_scp *scp) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci /* clear SPM interrupt, SCP2SPM_IPC_CLR */ 43862306a36Sopenharmony_ci writel(0xff, scp->reg_base + MT8192_SCP2SPM_IPC_CLR); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci writel(1, scp->reg_base + MT8192_CORE0_SW_RSTN_SET); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* enable SRAM clock */ 44362306a36Sopenharmony_ci scp_sram_power_on(scp->reg_base + MT8192_L2TCM_SRAM_PD_0, 0); 44462306a36Sopenharmony_ci scp_sram_power_on(scp->reg_base + MT8192_L2TCM_SRAM_PD_1, 0); 44562306a36Sopenharmony_ci scp_sram_power_on(scp->reg_base + MT8192_L2TCM_SRAM_PD_2, 0); 44662306a36Sopenharmony_ci scp_sram_power_on(scp->reg_base + MT8192_L1TCM_SRAM_PDN, 44762306a36Sopenharmony_ci MT8195_L1TCM_SRAM_PDN_RESERVED_RSI_BITS); 44862306a36Sopenharmony_ci scp_sram_power_on(scp->reg_base + MT8192_CPU0_SRAM_PD, 0); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* enable MPU for all memory regions */ 45162306a36Sopenharmony_ci writel(0xff, scp->reg_base + MT8192_CORE0_MEM_ATT_PREDEF); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci return 0; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic int scp_load(struct rproc *rproc, const struct firmware *fw) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct mtk_scp *scp = rproc->priv; 45962306a36Sopenharmony_ci struct device *dev = scp->dev; 46062306a36Sopenharmony_ci int ret; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci ret = clk_prepare_enable(scp->clk); 46362306a36Sopenharmony_ci if (ret) { 46462306a36Sopenharmony_ci dev_err(dev, "failed to enable clocks\n"); 46562306a36Sopenharmony_ci return ret; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* Hold SCP in reset while loading FW. */ 46962306a36Sopenharmony_ci scp->data->scp_reset_assert(scp); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci ret = scp->data->scp_before_load(scp); 47262306a36Sopenharmony_ci if (ret < 0) 47362306a36Sopenharmony_ci goto leave; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci ret = scp_elf_load_segments(rproc, fw); 47662306a36Sopenharmony_cileave: 47762306a36Sopenharmony_ci clk_disable_unprepare(scp->clk); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return ret; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic int scp_parse_fw(struct rproc *rproc, const struct firmware *fw) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct mtk_scp *scp = rproc->priv; 48562306a36Sopenharmony_ci struct device *dev = scp->dev; 48662306a36Sopenharmony_ci int ret; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci ret = clk_prepare_enable(scp->clk); 48962306a36Sopenharmony_ci if (ret) { 49062306a36Sopenharmony_ci dev_err(dev, "failed to enable clocks\n"); 49162306a36Sopenharmony_ci return ret; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci ret = scp_ipi_init(scp, fw); 49562306a36Sopenharmony_ci clk_disable_unprepare(scp->clk); 49662306a36Sopenharmony_ci return ret; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic int scp_start(struct rproc *rproc) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct mtk_scp *scp = rproc->priv; 50262306a36Sopenharmony_ci struct device *dev = scp->dev; 50362306a36Sopenharmony_ci struct scp_run *run = &scp->run; 50462306a36Sopenharmony_ci int ret; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci ret = clk_prepare_enable(scp->clk); 50762306a36Sopenharmony_ci if (ret) { 50862306a36Sopenharmony_ci dev_err(dev, "failed to enable clocks\n"); 50962306a36Sopenharmony_ci return ret; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci run->signaled = false; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci scp->data->scp_reset_deassert(scp); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci ret = wait_event_interruptible_timeout( 51762306a36Sopenharmony_ci run->wq, 51862306a36Sopenharmony_ci run->signaled, 51962306a36Sopenharmony_ci msecs_to_jiffies(2000)); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (ret == 0) { 52262306a36Sopenharmony_ci dev_err(dev, "wait SCP initialization timeout!\n"); 52362306a36Sopenharmony_ci ret = -ETIME; 52462306a36Sopenharmony_ci goto stop; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci if (ret == -ERESTARTSYS) { 52762306a36Sopenharmony_ci dev_err(dev, "wait SCP interrupted by a signal!\n"); 52862306a36Sopenharmony_ci goto stop; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci clk_disable_unprepare(scp->clk); 53262306a36Sopenharmony_ci dev_info(dev, "SCP is ready. FW version %s\n", run->fw_ver); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci return 0; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistop: 53762306a36Sopenharmony_ci scp->data->scp_reset_assert(scp); 53862306a36Sopenharmony_ci clk_disable_unprepare(scp->clk); 53962306a36Sopenharmony_ci return ret; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic void *mt8183_scp_da_to_va(struct mtk_scp *scp, u64 da, size_t len) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci int offset; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (da < scp->sram_size) { 54762306a36Sopenharmony_ci offset = da; 54862306a36Sopenharmony_ci if (offset >= 0 && (offset + len) <= scp->sram_size) 54962306a36Sopenharmony_ci return (void __force *)scp->sram_base + offset; 55062306a36Sopenharmony_ci } else if (scp->dram_size) { 55162306a36Sopenharmony_ci offset = da - scp->dma_addr; 55262306a36Sopenharmony_ci if (offset >= 0 && (offset + len) <= scp->dram_size) 55362306a36Sopenharmony_ci return scp->cpu_addr + offset; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci return NULL; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic void *mt8192_scp_da_to_va(struct mtk_scp *scp, u64 da, size_t len) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci int offset; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (da >= scp->sram_phys && 56462306a36Sopenharmony_ci (da + len) <= scp->sram_phys + scp->sram_size) { 56562306a36Sopenharmony_ci offset = da - scp->sram_phys; 56662306a36Sopenharmony_ci return (void __force *)scp->sram_base + offset; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* optional memory region */ 57062306a36Sopenharmony_ci if (scp->l1tcm_size && 57162306a36Sopenharmony_ci da >= scp->l1tcm_phys && 57262306a36Sopenharmony_ci (da + len) <= scp->l1tcm_phys + scp->l1tcm_size) { 57362306a36Sopenharmony_ci offset = da - scp->l1tcm_phys; 57462306a36Sopenharmony_ci return (void __force *)scp->l1tcm_base + offset; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* optional memory region */ 57862306a36Sopenharmony_ci if (scp->dram_size && 57962306a36Sopenharmony_ci da >= scp->dma_addr && 58062306a36Sopenharmony_ci (da + len) <= scp->dma_addr + scp->dram_size) { 58162306a36Sopenharmony_ci offset = da - scp->dma_addr; 58262306a36Sopenharmony_ci return scp->cpu_addr + offset; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci return NULL; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic void *scp_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci struct mtk_scp *scp = rproc->priv; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci return scp->data->scp_da_to_va(scp, da, len); 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic void mt8183_scp_stop(struct mtk_scp *scp) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci /* Disable SCP watchdog */ 59862306a36Sopenharmony_ci writel(0, scp->reg_base + MT8183_WDT_CFG); 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic void mt8192_scp_stop(struct mtk_scp *scp) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci /* Disable SRAM clock */ 60462306a36Sopenharmony_ci scp_sram_power_off(scp->reg_base + MT8192_L2TCM_SRAM_PD_0, 0); 60562306a36Sopenharmony_ci scp_sram_power_off(scp->reg_base + MT8192_L2TCM_SRAM_PD_1, 0); 60662306a36Sopenharmony_ci scp_sram_power_off(scp->reg_base + MT8192_L2TCM_SRAM_PD_2, 0); 60762306a36Sopenharmony_ci scp_sram_power_off(scp->reg_base + MT8192_L1TCM_SRAM_PDN, 0); 60862306a36Sopenharmony_ci scp_sram_power_off(scp->reg_base + MT8192_CPU0_SRAM_PD, 0); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci /* Disable SCP watchdog */ 61162306a36Sopenharmony_ci writel(0, scp->reg_base + MT8192_CORE0_WDT_CFG); 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic void mt8195_scp_stop(struct mtk_scp *scp) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci /* Disable SRAM clock */ 61762306a36Sopenharmony_ci scp_sram_power_off(scp->reg_base + MT8192_L2TCM_SRAM_PD_0, 0); 61862306a36Sopenharmony_ci scp_sram_power_off(scp->reg_base + MT8192_L2TCM_SRAM_PD_1, 0); 61962306a36Sopenharmony_ci scp_sram_power_off(scp->reg_base + MT8192_L2TCM_SRAM_PD_2, 0); 62062306a36Sopenharmony_ci scp_sram_power_off(scp->reg_base + MT8192_L1TCM_SRAM_PDN, 62162306a36Sopenharmony_ci MT8195_L1TCM_SRAM_PDN_RESERVED_RSI_BITS); 62262306a36Sopenharmony_ci scp_sram_power_off(scp->reg_base + MT8192_CPU0_SRAM_PD, 0); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* Disable SCP watchdog */ 62562306a36Sopenharmony_ci writel(0, scp->reg_base + MT8192_CORE0_WDT_CFG); 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic int scp_stop(struct rproc *rproc) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci struct mtk_scp *scp = rproc->priv; 63162306a36Sopenharmony_ci int ret; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci ret = clk_prepare_enable(scp->clk); 63462306a36Sopenharmony_ci if (ret) { 63562306a36Sopenharmony_ci dev_err(scp->dev, "failed to enable clocks\n"); 63662306a36Sopenharmony_ci return ret; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci scp->data->scp_reset_assert(scp); 64062306a36Sopenharmony_ci scp->data->scp_stop(scp); 64162306a36Sopenharmony_ci clk_disable_unprepare(scp->clk); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci return 0; 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic const struct rproc_ops scp_ops = { 64762306a36Sopenharmony_ci .start = scp_start, 64862306a36Sopenharmony_ci .stop = scp_stop, 64962306a36Sopenharmony_ci .load = scp_load, 65062306a36Sopenharmony_ci .da_to_va = scp_da_to_va, 65162306a36Sopenharmony_ci .parse_fw = scp_parse_fw, 65262306a36Sopenharmony_ci .sanity_check = rproc_elf_sanity_check, 65362306a36Sopenharmony_ci}; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci/** 65662306a36Sopenharmony_ci * scp_get_device() - get device struct of SCP 65762306a36Sopenharmony_ci * 65862306a36Sopenharmony_ci * @scp: mtk_scp structure 65962306a36Sopenharmony_ci **/ 66062306a36Sopenharmony_cistruct device *scp_get_device(struct mtk_scp *scp) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci return scp->dev; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scp_get_device); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci/** 66762306a36Sopenharmony_ci * scp_get_rproc() - get rproc struct of SCP 66862306a36Sopenharmony_ci * 66962306a36Sopenharmony_ci * @scp: mtk_scp structure 67062306a36Sopenharmony_ci **/ 67162306a36Sopenharmony_cistruct rproc *scp_get_rproc(struct mtk_scp *scp) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci return scp->rproc; 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scp_get_rproc); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci/** 67862306a36Sopenharmony_ci * scp_get_vdec_hw_capa() - get video decoder hardware capability 67962306a36Sopenharmony_ci * 68062306a36Sopenharmony_ci * @scp: mtk_scp structure 68162306a36Sopenharmony_ci * 68262306a36Sopenharmony_ci * Return: video decoder hardware capability 68362306a36Sopenharmony_ci **/ 68462306a36Sopenharmony_ciunsigned int scp_get_vdec_hw_capa(struct mtk_scp *scp) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci return scp->run.dec_capability; 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scp_get_vdec_hw_capa); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci/** 69162306a36Sopenharmony_ci * scp_get_venc_hw_capa() - get video encoder hardware capability 69262306a36Sopenharmony_ci * 69362306a36Sopenharmony_ci * @scp: mtk_scp structure 69462306a36Sopenharmony_ci * 69562306a36Sopenharmony_ci * Return: video encoder hardware capability 69662306a36Sopenharmony_ci **/ 69762306a36Sopenharmony_ciunsigned int scp_get_venc_hw_capa(struct mtk_scp *scp) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci return scp->run.enc_capability; 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scp_get_venc_hw_capa); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci/** 70462306a36Sopenharmony_ci * scp_mapping_dm_addr() - Mapping SRAM/DRAM to kernel virtual address 70562306a36Sopenharmony_ci * 70662306a36Sopenharmony_ci * @scp: mtk_scp structure 70762306a36Sopenharmony_ci * @mem_addr: SCP views memory address 70862306a36Sopenharmony_ci * 70962306a36Sopenharmony_ci * Mapping the SCP's SRAM address / 71062306a36Sopenharmony_ci * DMEM (Data Extended Memory) memory address / 71162306a36Sopenharmony_ci * Working buffer memory address to 71262306a36Sopenharmony_ci * kernel virtual address. 71362306a36Sopenharmony_ci * 71462306a36Sopenharmony_ci * Return: Return ERR_PTR(-EINVAL) if mapping failed, 71562306a36Sopenharmony_ci * otherwise the mapped kernel virtual address 71662306a36Sopenharmony_ci **/ 71762306a36Sopenharmony_civoid *scp_mapping_dm_addr(struct mtk_scp *scp, u32 mem_addr) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci void *ptr; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci ptr = scp_da_to_va(scp->rproc, mem_addr, 0, NULL); 72262306a36Sopenharmony_ci if (!ptr) 72362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci return ptr; 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scp_mapping_dm_addr); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_cistatic int scp_map_memory_region(struct mtk_scp *scp) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci int ret; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci ret = of_reserved_mem_device_init(scp->dev); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* reserved memory is optional. */ 73662306a36Sopenharmony_ci if (ret == -ENODEV) { 73762306a36Sopenharmony_ci dev_info(scp->dev, "skipping reserved memory initialization."); 73862306a36Sopenharmony_ci return 0; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (ret) { 74262306a36Sopenharmony_ci dev_err(scp->dev, "failed to assign memory-region: %d\n", ret); 74362306a36Sopenharmony_ci return -ENOMEM; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* Reserved SCP code size */ 74762306a36Sopenharmony_ci scp->dram_size = MAX_CODE_SIZE; 74862306a36Sopenharmony_ci scp->cpu_addr = dma_alloc_coherent(scp->dev, scp->dram_size, 74962306a36Sopenharmony_ci &scp->dma_addr, GFP_KERNEL); 75062306a36Sopenharmony_ci if (!scp->cpu_addr) 75162306a36Sopenharmony_ci return -ENOMEM; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci return 0; 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic void scp_unmap_memory_region(struct mtk_scp *scp) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci if (scp->dram_size == 0) 75962306a36Sopenharmony_ci return; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci dma_free_coherent(scp->dev, scp->dram_size, scp->cpu_addr, 76262306a36Sopenharmony_ci scp->dma_addr); 76362306a36Sopenharmony_ci of_reserved_mem_device_release(scp->dev); 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_cistatic int scp_register_ipi(struct platform_device *pdev, u32 id, 76762306a36Sopenharmony_ci ipi_handler_t handler, void *priv) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci struct mtk_scp *scp = platform_get_drvdata(pdev); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci return scp_ipi_register(scp, id, handler, priv); 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic void scp_unregister_ipi(struct platform_device *pdev, u32 id) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci struct mtk_scp *scp = platform_get_drvdata(pdev); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci scp_ipi_unregister(scp, id); 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic int scp_send_ipi(struct platform_device *pdev, u32 id, void *buf, 78262306a36Sopenharmony_ci unsigned int len, unsigned int wait) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci struct mtk_scp *scp = platform_get_drvdata(pdev); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci return scp_ipi_send(scp, id, buf, len, wait); 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic struct mtk_rpmsg_info mtk_scp_rpmsg_info = { 79062306a36Sopenharmony_ci .send_ipi = scp_send_ipi, 79162306a36Sopenharmony_ci .register_ipi = scp_register_ipi, 79262306a36Sopenharmony_ci .unregister_ipi = scp_unregister_ipi, 79362306a36Sopenharmony_ci .ns_ipi_id = SCP_IPI_NS_SERVICE, 79462306a36Sopenharmony_ci}; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cistatic void scp_add_rpmsg_subdev(struct mtk_scp *scp) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci scp->rpmsg_subdev = 79962306a36Sopenharmony_ci mtk_rpmsg_create_rproc_subdev(to_platform_device(scp->dev), 80062306a36Sopenharmony_ci &mtk_scp_rpmsg_info); 80162306a36Sopenharmony_ci if (scp->rpmsg_subdev) 80262306a36Sopenharmony_ci rproc_add_subdev(scp->rproc, scp->rpmsg_subdev); 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_cistatic void scp_remove_rpmsg_subdev(struct mtk_scp *scp) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci if (scp->rpmsg_subdev) { 80862306a36Sopenharmony_ci rproc_remove_subdev(scp->rproc, scp->rpmsg_subdev); 80962306a36Sopenharmony_ci mtk_rpmsg_destroy_rproc_subdev(scp->rpmsg_subdev); 81062306a36Sopenharmony_ci scp->rpmsg_subdev = NULL; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic int scp_probe(struct platform_device *pdev) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 81762306a36Sopenharmony_ci struct device_node *np = dev->of_node; 81862306a36Sopenharmony_ci struct mtk_scp *scp; 81962306a36Sopenharmony_ci struct rproc *rproc; 82062306a36Sopenharmony_ci struct resource *res; 82162306a36Sopenharmony_ci const char *fw_name = "scp.img"; 82262306a36Sopenharmony_ci int ret, i; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci ret = rproc_of_parse_firmware(dev, 0, &fw_name); 82562306a36Sopenharmony_ci if (ret < 0 && ret != -EINVAL) 82662306a36Sopenharmony_ci return ret; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci rproc = devm_rproc_alloc(dev, np->name, &scp_ops, fw_name, sizeof(*scp)); 82962306a36Sopenharmony_ci if (!rproc) 83062306a36Sopenharmony_ci return dev_err_probe(dev, -ENOMEM, "unable to allocate remoteproc\n"); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci scp = rproc->priv; 83362306a36Sopenharmony_ci scp->rproc = rproc; 83462306a36Sopenharmony_ci scp->dev = dev; 83562306a36Sopenharmony_ci scp->data = of_device_get_match_data(dev); 83662306a36Sopenharmony_ci platform_set_drvdata(pdev, scp); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram"); 83962306a36Sopenharmony_ci scp->sram_base = devm_ioremap_resource(dev, res); 84062306a36Sopenharmony_ci if (IS_ERR(scp->sram_base)) 84162306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(scp->sram_base), 84262306a36Sopenharmony_ci "Failed to parse and map sram memory\n"); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci scp->sram_size = resource_size(res); 84562306a36Sopenharmony_ci scp->sram_phys = res->start; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci /* l1tcm is an optional memory region */ 84862306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "l1tcm"); 84962306a36Sopenharmony_ci scp->l1tcm_base = devm_ioremap_resource(dev, res); 85062306a36Sopenharmony_ci if (IS_ERR(scp->l1tcm_base)) { 85162306a36Sopenharmony_ci ret = PTR_ERR(scp->l1tcm_base); 85262306a36Sopenharmony_ci if (ret != -EINVAL) { 85362306a36Sopenharmony_ci return dev_err_probe(dev, ret, "Failed to map l1tcm memory\n"); 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci } else { 85662306a36Sopenharmony_ci scp->l1tcm_size = resource_size(res); 85762306a36Sopenharmony_ci scp->l1tcm_phys = res->start; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci scp->reg_base = devm_platform_ioremap_resource_byname(pdev, "cfg"); 86162306a36Sopenharmony_ci if (IS_ERR(scp->reg_base)) 86262306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(scp->reg_base), 86362306a36Sopenharmony_ci "Failed to parse and map cfg memory\n"); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci ret = scp->data->scp_clk_get(scp); 86662306a36Sopenharmony_ci if (ret) 86762306a36Sopenharmony_ci return ret; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci ret = scp_map_memory_region(scp); 87062306a36Sopenharmony_ci if (ret) 87162306a36Sopenharmony_ci return ret; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci mutex_init(&scp->send_lock); 87462306a36Sopenharmony_ci for (i = 0; i < SCP_IPI_MAX; i++) 87562306a36Sopenharmony_ci mutex_init(&scp->ipi_desc[i].lock); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* register SCP initialization IPI */ 87862306a36Sopenharmony_ci ret = scp_ipi_register(scp, SCP_IPI_INIT, scp_init_ipi_handler, scp); 87962306a36Sopenharmony_ci if (ret) { 88062306a36Sopenharmony_ci dev_err(dev, "Failed to register IPI_SCP_INIT\n"); 88162306a36Sopenharmony_ci goto release_dev_mem; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci init_waitqueue_head(&scp->run.wq); 88562306a36Sopenharmony_ci init_waitqueue_head(&scp->ack_wq); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci scp_add_rpmsg_subdev(scp); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0), NULL, 89062306a36Sopenharmony_ci scp_irq_handler, IRQF_ONESHOT, 89162306a36Sopenharmony_ci pdev->name, scp); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci if (ret) { 89462306a36Sopenharmony_ci dev_err(dev, "failed to request irq\n"); 89562306a36Sopenharmony_ci goto remove_subdev; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci ret = rproc_add(rproc); 89962306a36Sopenharmony_ci if (ret) 90062306a36Sopenharmony_ci goto remove_subdev; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci return 0; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ciremove_subdev: 90562306a36Sopenharmony_ci scp_remove_rpmsg_subdev(scp); 90662306a36Sopenharmony_ci scp_ipi_unregister(scp, SCP_IPI_INIT); 90762306a36Sopenharmony_cirelease_dev_mem: 90862306a36Sopenharmony_ci scp_unmap_memory_region(scp); 90962306a36Sopenharmony_ci for (i = 0; i < SCP_IPI_MAX; i++) 91062306a36Sopenharmony_ci mutex_destroy(&scp->ipi_desc[i].lock); 91162306a36Sopenharmony_ci mutex_destroy(&scp->send_lock); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci return ret; 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic void scp_remove(struct platform_device *pdev) 91762306a36Sopenharmony_ci{ 91862306a36Sopenharmony_ci struct mtk_scp *scp = platform_get_drvdata(pdev); 91962306a36Sopenharmony_ci int i; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci rproc_del(scp->rproc); 92262306a36Sopenharmony_ci scp_remove_rpmsg_subdev(scp); 92362306a36Sopenharmony_ci scp_ipi_unregister(scp, SCP_IPI_INIT); 92462306a36Sopenharmony_ci scp_unmap_memory_region(scp); 92562306a36Sopenharmony_ci for (i = 0; i < SCP_IPI_MAX; i++) 92662306a36Sopenharmony_ci mutex_destroy(&scp->ipi_desc[i].lock); 92762306a36Sopenharmony_ci mutex_destroy(&scp->send_lock); 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic const struct mtk_scp_of_data mt8183_of_data = { 93162306a36Sopenharmony_ci .scp_clk_get = mt8183_scp_clk_get, 93262306a36Sopenharmony_ci .scp_before_load = mt8183_scp_before_load, 93362306a36Sopenharmony_ci .scp_irq_handler = mt8183_scp_irq_handler, 93462306a36Sopenharmony_ci .scp_reset_assert = mt8183_scp_reset_assert, 93562306a36Sopenharmony_ci .scp_reset_deassert = mt8183_scp_reset_deassert, 93662306a36Sopenharmony_ci .scp_stop = mt8183_scp_stop, 93762306a36Sopenharmony_ci .scp_da_to_va = mt8183_scp_da_to_va, 93862306a36Sopenharmony_ci .host_to_scp_reg = MT8183_HOST_TO_SCP, 93962306a36Sopenharmony_ci .host_to_scp_int_bit = MT8183_HOST_IPC_INT_BIT, 94062306a36Sopenharmony_ci .ipi_buf_offset = 0x7bdb0, 94162306a36Sopenharmony_ci}; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic const struct mtk_scp_of_data mt8186_of_data = { 94462306a36Sopenharmony_ci .scp_clk_get = mt8195_scp_clk_get, 94562306a36Sopenharmony_ci .scp_before_load = mt8186_scp_before_load, 94662306a36Sopenharmony_ci .scp_irq_handler = mt8183_scp_irq_handler, 94762306a36Sopenharmony_ci .scp_reset_assert = mt8183_scp_reset_assert, 94862306a36Sopenharmony_ci .scp_reset_deassert = mt8183_scp_reset_deassert, 94962306a36Sopenharmony_ci .scp_stop = mt8183_scp_stop, 95062306a36Sopenharmony_ci .scp_da_to_va = mt8183_scp_da_to_va, 95162306a36Sopenharmony_ci .host_to_scp_reg = MT8183_HOST_TO_SCP, 95262306a36Sopenharmony_ci .host_to_scp_int_bit = MT8183_HOST_IPC_INT_BIT, 95362306a36Sopenharmony_ci .ipi_buf_offset = 0x3bdb0, 95462306a36Sopenharmony_ci}; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cistatic const struct mtk_scp_of_data mt8188_of_data = { 95762306a36Sopenharmony_ci .scp_clk_get = mt8195_scp_clk_get, 95862306a36Sopenharmony_ci .scp_before_load = mt8192_scp_before_load, 95962306a36Sopenharmony_ci .scp_irq_handler = mt8192_scp_irq_handler, 96062306a36Sopenharmony_ci .scp_reset_assert = mt8192_scp_reset_assert, 96162306a36Sopenharmony_ci .scp_reset_deassert = mt8192_scp_reset_deassert, 96262306a36Sopenharmony_ci .scp_stop = mt8192_scp_stop, 96362306a36Sopenharmony_ci .scp_da_to_va = mt8192_scp_da_to_va, 96462306a36Sopenharmony_ci .host_to_scp_reg = MT8192_GIPC_IN_SET, 96562306a36Sopenharmony_ci .host_to_scp_int_bit = MT8192_HOST_IPC_INT_BIT, 96662306a36Sopenharmony_ci}; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cistatic const struct mtk_scp_of_data mt8192_of_data = { 96962306a36Sopenharmony_ci .scp_clk_get = mt8192_scp_clk_get, 97062306a36Sopenharmony_ci .scp_before_load = mt8192_scp_before_load, 97162306a36Sopenharmony_ci .scp_irq_handler = mt8192_scp_irq_handler, 97262306a36Sopenharmony_ci .scp_reset_assert = mt8192_scp_reset_assert, 97362306a36Sopenharmony_ci .scp_reset_deassert = mt8192_scp_reset_deassert, 97462306a36Sopenharmony_ci .scp_stop = mt8192_scp_stop, 97562306a36Sopenharmony_ci .scp_da_to_va = mt8192_scp_da_to_va, 97662306a36Sopenharmony_ci .host_to_scp_reg = MT8192_GIPC_IN_SET, 97762306a36Sopenharmony_ci .host_to_scp_int_bit = MT8192_HOST_IPC_INT_BIT, 97862306a36Sopenharmony_ci}; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic const struct mtk_scp_of_data mt8195_of_data = { 98162306a36Sopenharmony_ci .scp_clk_get = mt8195_scp_clk_get, 98262306a36Sopenharmony_ci .scp_before_load = mt8195_scp_before_load, 98362306a36Sopenharmony_ci .scp_irq_handler = mt8192_scp_irq_handler, 98462306a36Sopenharmony_ci .scp_reset_assert = mt8192_scp_reset_assert, 98562306a36Sopenharmony_ci .scp_reset_deassert = mt8192_scp_reset_deassert, 98662306a36Sopenharmony_ci .scp_stop = mt8195_scp_stop, 98762306a36Sopenharmony_ci .scp_da_to_va = mt8192_scp_da_to_va, 98862306a36Sopenharmony_ci .host_to_scp_reg = MT8192_GIPC_IN_SET, 98962306a36Sopenharmony_ci .host_to_scp_int_bit = MT8192_HOST_IPC_INT_BIT, 99062306a36Sopenharmony_ci}; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_cistatic const struct of_device_id mtk_scp_of_match[] = { 99362306a36Sopenharmony_ci { .compatible = "mediatek,mt8183-scp", .data = &mt8183_of_data }, 99462306a36Sopenharmony_ci { .compatible = "mediatek,mt8186-scp", .data = &mt8186_of_data }, 99562306a36Sopenharmony_ci { .compatible = "mediatek,mt8188-scp", .data = &mt8188_of_data }, 99662306a36Sopenharmony_ci { .compatible = "mediatek,mt8192-scp", .data = &mt8192_of_data }, 99762306a36Sopenharmony_ci { .compatible = "mediatek,mt8195-scp", .data = &mt8195_of_data }, 99862306a36Sopenharmony_ci {}, 99962306a36Sopenharmony_ci}; 100062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mtk_scp_of_match); 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_cistatic struct platform_driver mtk_scp_driver = { 100362306a36Sopenharmony_ci .probe = scp_probe, 100462306a36Sopenharmony_ci .remove_new = scp_remove, 100562306a36Sopenharmony_ci .driver = { 100662306a36Sopenharmony_ci .name = "mtk-scp", 100762306a36Sopenharmony_ci .of_match_table = mtk_scp_of_match, 100862306a36Sopenharmony_ci }, 100962306a36Sopenharmony_ci}; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cimodule_platform_driver(mtk_scp_driver); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 101462306a36Sopenharmony_ciMODULE_DESCRIPTION("MediaTek SCP control driver"); 1015