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/err.h> 862306a36Sopenharmony_ci#include <linux/io.h> 962306a36Sopenharmony_ci#include <linux/iopoll.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/time64.h> 1462306a36Sopenharmony_ci#include <linux/remoteproc/mtk_scp.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "mtk_common.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define SCP_TIMEOUT_US (2000 * USEC_PER_MSEC) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/** 2162306a36Sopenharmony_ci * scp_ipi_register() - register an ipi function 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * @scp: mtk_scp structure 2462306a36Sopenharmony_ci * @id: IPI ID 2562306a36Sopenharmony_ci * @handler: IPI handler 2662306a36Sopenharmony_ci * @priv: private data for IPI handler 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * Register an ipi function to receive ipi interrupt from SCP. 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * Return: 0 if ipi registers successfully, -error on error. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ciint scp_ipi_register(struct mtk_scp *scp, 3362306a36Sopenharmony_ci u32 id, 3462306a36Sopenharmony_ci scp_ipi_handler_t handler, 3562306a36Sopenharmony_ci void *priv) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci if (!scp) 3862306a36Sopenharmony_ci return -EPROBE_DEFER; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (WARN_ON(id >= SCP_IPI_MAX) || WARN_ON(handler == NULL)) 4162306a36Sopenharmony_ci return -EINVAL; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci scp_ipi_lock(scp, id); 4462306a36Sopenharmony_ci scp->ipi_desc[id].handler = handler; 4562306a36Sopenharmony_ci scp->ipi_desc[id].priv = priv; 4662306a36Sopenharmony_ci scp_ipi_unlock(scp, id); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return 0; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scp_ipi_register); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/** 5362306a36Sopenharmony_ci * scp_ipi_unregister() - unregister an ipi function 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * @scp: mtk_scp structure 5662306a36Sopenharmony_ci * @id: IPI ID 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * Unregister an ipi function to receive ipi interrupt from SCP. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_civoid scp_ipi_unregister(struct mtk_scp *scp, u32 id) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci if (!scp) 6362306a36Sopenharmony_ci return; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (WARN_ON(id >= SCP_IPI_MAX)) 6662306a36Sopenharmony_ci return; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci scp_ipi_lock(scp, id); 6962306a36Sopenharmony_ci scp->ipi_desc[id].handler = NULL; 7062306a36Sopenharmony_ci scp->ipi_desc[id].priv = NULL; 7162306a36Sopenharmony_ci scp_ipi_unlock(scp, id); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scp_ipi_unregister); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* 7662306a36Sopenharmony_ci * scp_memcpy_aligned() - Copy src to dst, where dst is in SCP SRAM region. 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * @dst: Pointer to the destination buffer, should be in SCP SRAM region. 7962306a36Sopenharmony_ci * @src: Pointer to the source buffer. 8062306a36Sopenharmony_ci * @len: Length of the source buffer to be copied. 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * Since AP access of SCP SRAM don't support byte write, this always write a 8362306a36Sopenharmony_ci * full word at a time, and may cause some extra bytes to be written at the 8462306a36Sopenharmony_ci * beginning & ending of dst. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_civoid scp_memcpy_aligned(void __iomem *dst, const void *src, unsigned int len) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci void __iomem *ptr; 8962306a36Sopenharmony_ci u32 val; 9062306a36Sopenharmony_ci unsigned int i = 0, remain; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (!IS_ALIGNED((unsigned long)dst, 4)) { 9362306a36Sopenharmony_ci ptr = (void __iomem *)ALIGN_DOWN((unsigned long)dst, 4); 9462306a36Sopenharmony_ci i = 4 - (dst - ptr); 9562306a36Sopenharmony_ci val = readl_relaxed(ptr); 9662306a36Sopenharmony_ci memcpy((u8 *)&val + (4 - i), src, i); 9762306a36Sopenharmony_ci writel_relaxed(val, ptr); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci __iowrite32_copy(dst + i, src + i, (len - i) / 4); 10162306a36Sopenharmony_ci remain = (len - i) % 4; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (remain > 0) { 10462306a36Sopenharmony_ci val = readl_relaxed(dst + len - remain); 10562306a36Sopenharmony_ci memcpy(&val, src + len - remain, remain); 10662306a36Sopenharmony_ci writel_relaxed(val, dst + len - remain); 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scp_memcpy_aligned); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/** 11262306a36Sopenharmony_ci * scp_ipi_lock() - Lock before operations of an IPI ID 11362306a36Sopenharmony_ci * 11462306a36Sopenharmony_ci * @scp: mtk_scp structure 11562306a36Sopenharmony_ci * @id: IPI ID 11662306a36Sopenharmony_ci * 11762306a36Sopenharmony_ci * Note: This should not be used by drivers other than mtk_scp. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_civoid scp_ipi_lock(struct mtk_scp *scp, u32 id) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci if (WARN_ON(id >= SCP_IPI_MAX)) 12262306a36Sopenharmony_ci return; 12362306a36Sopenharmony_ci mutex_lock(&scp->ipi_desc[id].lock); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scp_ipi_lock); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/** 12862306a36Sopenharmony_ci * scp_ipi_unlock() - Unlock after operations of an IPI ID 12962306a36Sopenharmony_ci * 13062306a36Sopenharmony_ci * @scp: mtk_scp structure 13162306a36Sopenharmony_ci * @id: IPI ID 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * Note: This should not be used by drivers other than mtk_scp. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_civoid scp_ipi_unlock(struct mtk_scp *scp, u32 id) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci if (WARN_ON(id >= SCP_IPI_MAX)) 13862306a36Sopenharmony_ci return; 13962306a36Sopenharmony_ci mutex_unlock(&scp->ipi_desc[id].lock); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scp_ipi_unlock); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/** 14462306a36Sopenharmony_ci * scp_ipi_send() - send data from AP to scp. 14562306a36Sopenharmony_ci * 14662306a36Sopenharmony_ci * @scp: mtk_scp structure 14762306a36Sopenharmony_ci * @id: IPI ID 14862306a36Sopenharmony_ci * @buf: the data buffer 14962306a36Sopenharmony_ci * @len: the data buffer length 15062306a36Sopenharmony_ci * @wait: number of msecs to wait for ack. 0 to skip waiting. 15162306a36Sopenharmony_ci * 15262306a36Sopenharmony_ci * This function is thread-safe. When this function returns, 15362306a36Sopenharmony_ci * SCP has received the data and starts the processing. 15462306a36Sopenharmony_ci * When the processing completes, IPI handler registered 15562306a36Sopenharmony_ci * by scp_ipi_register will be called in interrupt context. 15662306a36Sopenharmony_ci * 15762306a36Sopenharmony_ci * Return: 0 if sending data successfully, -error on error. 15862306a36Sopenharmony_ci **/ 15962306a36Sopenharmony_ciint scp_ipi_send(struct mtk_scp *scp, u32 id, void *buf, unsigned int len, 16062306a36Sopenharmony_ci unsigned int wait) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct mtk_share_obj __iomem *send_obj = scp->send_buf; 16362306a36Sopenharmony_ci u32 val; 16462306a36Sopenharmony_ci int ret; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (WARN_ON(id <= SCP_IPI_INIT) || WARN_ON(id >= SCP_IPI_MAX) || 16762306a36Sopenharmony_ci WARN_ON(id == SCP_IPI_NS_SERVICE) || 16862306a36Sopenharmony_ci WARN_ON(len > sizeof(send_obj->share_buf)) || WARN_ON(!buf)) 16962306a36Sopenharmony_ci return -EINVAL; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci ret = clk_prepare_enable(scp->clk); 17262306a36Sopenharmony_ci if (ret) { 17362306a36Sopenharmony_ci dev_err(scp->dev, "failed to enable clock\n"); 17462306a36Sopenharmony_ci return ret; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci mutex_lock(&scp->send_lock); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* Wait until SCP receives the last command */ 18062306a36Sopenharmony_ci ret = readl_poll_timeout_atomic(scp->reg_base + scp->data->host_to_scp_reg, 18162306a36Sopenharmony_ci val, !val, 0, SCP_TIMEOUT_US); 18262306a36Sopenharmony_ci if (ret) { 18362306a36Sopenharmony_ci dev_err(scp->dev, "%s: IPI timeout!\n", __func__); 18462306a36Sopenharmony_ci goto unlock_mutex; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci scp_memcpy_aligned(send_obj->share_buf, buf, len); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci writel(len, &send_obj->len); 19062306a36Sopenharmony_ci writel(id, &send_obj->id); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci scp->ipi_id_ack[id] = false; 19362306a36Sopenharmony_ci /* send the command to SCP */ 19462306a36Sopenharmony_ci writel(scp->data->host_to_scp_int_bit, 19562306a36Sopenharmony_ci scp->reg_base + scp->data->host_to_scp_reg); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (wait) { 19862306a36Sopenharmony_ci /* wait for SCP's ACK */ 19962306a36Sopenharmony_ci ret = wait_event_timeout(scp->ack_wq, 20062306a36Sopenharmony_ci scp->ipi_id_ack[id], 20162306a36Sopenharmony_ci msecs_to_jiffies(wait)); 20262306a36Sopenharmony_ci scp->ipi_id_ack[id] = false; 20362306a36Sopenharmony_ci if (WARN(!ret, "scp ipi %d ack time out !", id)) 20462306a36Sopenharmony_ci ret = -EIO; 20562306a36Sopenharmony_ci else 20662306a36Sopenharmony_ci ret = 0; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ciunlock_mutex: 21062306a36Sopenharmony_ci mutex_unlock(&scp->send_lock); 21162306a36Sopenharmony_ci clk_disable_unprepare(scp->clk); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return ret; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scp_ipi_send); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 21862306a36Sopenharmony_ciMODULE_DESCRIPTION("MediaTek scp IPI interface"); 219