You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
remoteproc/mediatek: add SCP support for mt8183
Provide a basic driver to control Cortex M4 co-processor Signed-off-by: Erin Lo <erin.lo@mediatek.com> Signed-off-by: Nicolas Boichat <drinkcat@chromium.org> Signed-off-by: Pi-Hsun Shih <pihsun@chromium.org> Link: https://lore.kernel.org/r/20191112110330.179649-3-pihsun@chromium.org Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
This commit is contained in:
@@ -23,6 +23,15 @@ config IMX_REMOTEPROC
|
||||
|
||||
It's safe to say N here.
|
||||
|
||||
config MTK_SCP
|
||||
tristate "Mediatek SCP support"
|
||||
depends on ARCH_MEDIATEK
|
||||
help
|
||||
Say y here to support Mediatek's System Companion Processor (SCP) via
|
||||
the remote processor framework.
|
||||
|
||||
It's safe to say N here.
|
||||
|
||||
config OMAP_REMOTEPROC
|
||||
tristate "OMAP remoteproc support"
|
||||
depends on ARCH_OMAP4 || SOC_OMAP5
|
||||
|
||||
@@ -10,6 +10,7 @@ remoteproc-y += remoteproc_sysfs.o
|
||||
remoteproc-y += remoteproc_virtio.o
|
||||
remoteproc-y += remoteproc_elf_loader.o
|
||||
obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o
|
||||
obj-$(CONFIG_MTK_SCP) += mtk_scp.o mtk_scp_ipi.o
|
||||
obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o
|
||||
obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o
|
||||
obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o
|
||||
|
||||
92
drivers/remoteproc/mtk_common.h
Normal file
92
drivers/remoteproc/mtk_common.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2019 MediaTek Inc.
|
||||
*/
|
||||
|
||||
#ifndef __RPROC_MTK_COMMON_H
|
||||
#define __RPROC_MTK_COMMON_H
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/remoteproc.h>
|
||||
#include <linux/remoteproc/mtk_scp.h>
|
||||
|
||||
#define MT8183_SW_RSTN 0x0
|
||||
#define MT8183_SW_RSTN_BIT BIT(0)
|
||||
#define MT8183_SCP_TO_HOST 0x1C
|
||||
#define MT8183_SCP_IPC_INT_BIT BIT(0)
|
||||
#define MT8183_SCP_WDT_INT_BIT BIT(8)
|
||||
#define MT8183_HOST_TO_SCP 0x28
|
||||
#define MT8183_HOST_IPC_INT_BIT BIT(0)
|
||||
#define MT8183_WDT_CFG 0x84
|
||||
#define MT8183_SCP_CLK_SW_SEL 0x4000
|
||||
#define MT8183_SCP_CLK_DIV_SEL 0x4024
|
||||
#define MT8183_SCP_SRAM_PDN 0x402C
|
||||
#define MT8183_SCP_L1_SRAM_PD 0x4080
|
||||
#define MT8183_SCP_TCM_TAIL_SRAM_PD 0x4094
|
||||
|
||||
#define MT8183_SCP_CACHE_SEL(x) (0x14000 + (x) * 0x3000)
|
||||
#define MT8183_SCP_CACHE_CON MT8183_SCP_CACHE_SEL(0)
|
||||
#define MT8183_SCP_DCACHE_CON MT8183_SCP_CACHE_SEL(1)
|
||||
#define MT8183_SCP_CACHESIZE_8KB BIT(8)
|
||||
#define MT8183_SCP_CACHE_CON_WAYEN BIT(10)
|
||||
|
||||
#define SCP_FW_VER_LEN 32
|
||||
#define SCP_SHARE_BUFFER_SIZE 288
|
||||
|
||||
struct scp_run {
|
||||
u32 signaled;
|
||||
s8 fw_ver[SCP_FW_VER_LEN];
|
||||
u32 dec_capability;
|
||||
u32 enc_capability;
|
||||
wait_queue_head_t wq;
|
||||
};
|
||||
|
||||
struct scp_ipi_desc {
|
||||
/* For protecting handler. */
|
||||
struct mutex lock;
|
||||
scp_ipi_handler_t handler;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
struct mtk_scp {
|
||||
struct device *dev;
|
||||
struct rproc *rproc;
|
||||
struct clk *clk;
|
||||
void __iomem *reg_base;
|
||||
void __iomem *sram_base;
|
||||
size_t sram_size;
|
||||
|
||||
struct mtk_share_obj __iomem *recv_buf;
|
||||
struct mtk_share_obj __iomem *send_buf;
|
||||
struct scp_run run;
|
||||
/* To prevent multiple ipi_send run concurrently. */
|
||||
struct mutex send_lock;
|
||||
struct scp_ipi_desc ipi_desc[SCP_IPI_MAX];
|
||||
bool ipi_id_ack[SCP_IPI_MAX];
|
||||
wait_queue_head_t ack_wq;
|
||||
|
||||
void __iomem *cpu_addr;
|
||||
phys_addr_t phys_addr;
|
||||
size_t dram_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mtk_share_obj - SRAM buffer shared with AP and SCP
|
||||
*
|
||||
* @id: IPI id
|
||||
* @len: share buffer length
|
||||
* @share_buf: share buffer data
|
||||
*/
|
||||
struct mtk_share_obj {
|
||||
u32 id;
|
||||
u32 len;
|
||||
u8 share_buf[SCP_SHARE_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
void scp_memcpy_aligned(void __iomem *dst, const void *src, unsigned int len);
|
||||
void scp_ipi_lock(struct mtk_scp *scp, u32 id);
|
||||
void scp_ipi_unlock(struct mtk_scp *scp, u32 id);
|
||||
|
||||
#endif
|
||||
610
drivers/remoteproc/mtk_scp.c
Normal file
610
drivers/remoteproc/mtk_scp.c
Normal file
File diff suppressed because it is too large
Load Diff
218
drivers/remoteproc/mtk_scp_ipi.c
Normal file
218
drivers/remoteproc/mtk_scp_ipi.c
Normal file
@@ -0,0 +1,218 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Copyright (c) 2019 MediaTek Inc.
|
||||
|
||||
#include <asm/barrier.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/remoteproc/mtk_scp.h>
|
||||
|
||||
#include "mtk_common.h"
|
||||
|
||||
/**
|
||||
* scp_ipi_register() - register an ipi function
|
||||
*
|
||||
* @scp: mtk_scp structure
|
||||
* @id: IPI ID
|
||||
* @handler: IPI handler
|
||||
* @priv: private data for IPI handler
|
||||
*
|
||||
* Register an ipi function to receive ipi interrupt from SCP.
|
||||
*
|
||||
* Returns 0 if ipi registers successfully, -error on error.
|
||||
*/
|
||||
int scp_ipi_register(struct mtk_scp *scp,
|
||||
u32 id,
|
||||
scp_ipi_handler_t handler,
|
||||
void *priv)
|
||||
{
|
||||
if (!scp) {
|
||||
dev_err(scp->dev, "scp device is not ready\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
if (WARN_ON(id >= SCP_IPI_MAX) || WARN_ON(handler == NULL))
|
||||
return -EINVAL;
|
||||
|
||||
scp_ipi_lock(scp, id);
|
||||
scp->ipi_desc[id].handler = handler;
|
||||
scp->ipi_desc[id].priv = priv;
|
||||
scp_ipi_unlock(scp, id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scp_ipi_register);
|
||||
|
||||
/**
|
||||
* scp_ipi_unregister() - unregister an ipi function
|
||||
*
|
||||
* @scp: mtk_scp structure
|
||||
* @id: IPI ID
|
||||
*
|
||||
* Unregister an ipi function to receive ipi interrupt from SCP.
|
||||
*/
|
||||
void scp_ipi_unregister(struct mtk_scp *scp, u32 id)
|
||||
{
|
||||
if (!scp)
|
||||
return;
|
||||
|
||||
if (WARN_ON(id >= SCP_IPI_MAX))
|
||||
return;
|
||||
|
||||
scp_ipi_lock(scp, id);
|
||||
scp->ipi_desc[id].handler = NULL;
|
||||
scp->ipi_desc[id].priv = NULL;
|
||||
scp_ipi_unlock(scp, id);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scp_ipi_unregister);
|
||||
|
||||
/*
|
||||
* scp_memcpy_aligned() - Copy src to dst, where dst is in SCP SRAM region.
|
||||
*
|
||||
* @dst: Pointer to the destination buffer, should be in SCP SRAM region.
|
||||
* @src: Pointer to the source buffer.
|
||||
* @len: Length of the source buffer to be copied.
|
||||
*
|
||||
* Since AP access of SCP SRAM don't support byte write, this always write a
|
||||
* full word at a time, and may cause some extra bytes to be written at the
|
||||
* beginning & ending of dst.
|
||||
*/
|
||||
void scp_memcpy_aligned(void __iomem *dst, const void *src, unsigned int len)
|
||||
{
|
||||
void __iomem *ptr;
|
||||
u32 val;
|
||||
unsigned int i = 0, remain;
|
||||
|
||||
if (!IS_ALIGNED((unsigned long)dst, 4)) {
|
||||
ptr = (void __iomem *)ALIGN_DOWN((unsigned long)dst, 4);
|
||||
i = 4 - (dst - ptr);
|
||||
val = readl_relaxed(ptr);
|
||||
memcpy((u8 *)&val + (4 - i), src, i);
|
||||
writel_relaxed(val, ptr);
|
||||
}
|
||||
|
||||
__iowrite32_copy(dst + i, src + i, (len - i) / 4);
|
||||
remain = (len - i) % 4;
|
||||
|
||||
if (remain > 0) {
|
||||
val = readl_relaxed(dst + len - remain);
|
||||
memcpy(&val, src + len - remain, remain);
|
||||
writel_relaxed(val, dst + len - remain);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scp_memcpy_aligned);
|
||||
|
||||
/**
|
||||
* scp_ipi_lock() - Lock before operations of an IPI ID
|
||||
*
|
||||
* @scp: mtk_scp structure
|
||||
* @id: IPI ID
|
||||
*
|
||||
* Note: This should not be used by drivers other than mtk_scp.
|
||||
*/
|
||||
void scp_ipi_lock(struct mtk_scp *scp, u32 id)
|
||||
{
|
||||
if (WARN_ON(id >= SCP_IPI_MAX))
|
||||
return;
|
||||
mutex_lock(&scp->ipi_desc[id].lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scp_ipi_lock);
|
||||
|
||||
/**
|
||||
* scp_ipi_lock() - Unlock after operations of an IPI ID
|
||||
*
|
||||
* @scp: mtk_scp structure
|
||||
* @id: IPI ID
|
||||
*
|
||||
* Note: This should not be used by drivers other than mtk_scp.
|
||||
*/
|
||||
void scp_ipi_unlock(struct mtk_scp *scp, u32 id)
|
||||
{
|
||||
if (WARN_ON(id >= SCP_IPI_MAX))
|
||||
return;
|
||||
mutex_unlock(&scp->ipi_desc[id].lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scp_ipi_unlock);
|
||||
|
||||
/**
|
||||
* scp_ipi_send() - send data from AP to scp.
|
||||
*
|
||||
* @scp: mtk_scp structure
|
||||
* @id: IPI ID
|
||||
* @buf: the data buffer
|
||||
* @len: the data buffer length
|
||||
* @wait: number of msecs to wait for ack. 0 to skip waiting.
|
||||
*
|
||||
* This function is thread-safe. When this function returns,
|
||||
* SCP has received the data and starts the processing.
|
||||
* When the processing completes, IPI handler registered
|
||||
* by scp_ipi_register will be called in interrupt context.
|
||||
*
|
||||
* Returns 0 if sending data successfully, -error on error.
|
||||
**/
|
||||
int scp_ipi_send(struct mtk_scp *scp, u32 id, void *buf, unsigned int len,
|
||||
unsigned int wait)
|
||||
{
|
||||
struct mtk_share_obj __iomem *send_obj = scp->send_buf;
|
||||
unsigned long timeout;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(id <= SCP_IPI_INIT) || WARN_ON(id >= SCP_IPI_MAX) ||
|
||||
WARN_ON(len > sizeof(send_obj->share_buf)) || WARN_ON(!buf))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&scp->send_lock);
|
||||
|
||||
ret = clk_prepare_enable(scp->clk);
|
||||
if (ret) {
|
||||
dev_err(scp->dev, "failed to enable clock\n");
|
||||
goto unlock_mutex;
|
||||
}
|
||||
|
||||
/* Wait until SCP receives the last command */
|
||||
timeout = jiffies + msecs_to_jiffies(2000);
|
||||
do {
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_err(scp->dev, "%s: IPI timeout!\n", __func__);
|
||||
ret = -ETIMEDOUT;
|
||||
goto clock_disable;
|
||||
}
|
||||
} while (readl(scp->reg_base + MT8183_HOST_TO_SCP));
|
||||
|
||||
scp_memcpy_aligned(send_obj->share_buf, buf, len);
|
||||
|
||||
writel(len, &send_obj->len);
|
||||
writel(id, &send_obj->id);
|
||||
|
||||
scp->ipi_id_ack[id] = false;
|
||||
/* send the command to SCP */
|
||||
writel(MT8183_HOST_IPC_INT_BIT, scp->reg_base + MT8183_HOST_TO_SCP);
|
||||
|
||||
if (wait) {
|
||||
/* wait for SCP's ACK */
|
||||
timeout = msecs_to_jiffies(wait);
|
||||
ret = wait_event_timeout(scp->ack_wq,
|
||||
scp->ipi_id_ack[id],
|
||||
timeout);
|
||||
scp->ipi_id_ack[id] = false;
|
||||
if (WARN(!ret, "scp ipi %d ack time out !", id))
|
||||
ret = -EIO;
|
||||
else
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
clock_disable:
|
||||
clk_disable_unprepare(scp->clk);
|
||||
unlock_mutex:
|
||||
mutex_unlock(&scp->send_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scp_ipi_send);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("MediaTek scp IPI interface");
|
||||
65
include/linux/remoteproc/mtk_scp.h
Normal file
65
include/linux/remoteproc/mtk_scp.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2019 MediaTek Inc.
|
||||
*/
|
||||
|
||||
#ifndef _MTK_SCP_H
|
||||
#define _MTK_SCP_H
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
typedef void (*scp_ipi_handler_t) (void *data,
|
||||
unsigned int len,
|
||||
void *priv);
|
||||
struct mtk_scp;
|
||||
|
||||
/**
|
||||
* enum ipi_id - the id of inter-processor interrupt
|
||||
*
|
||||
* @SCP_IPI_INIT: The interrupt from scp is to notfiy kernel
|
||||
* SCP initialization completed.
|
||||
* IPI_SCP_INIT is sent from SCP when firmware is
|
||||
* loaded. AP doesn't need to send IPI_SCP_INIT
|
||||
* command to SCP.
|
||||
* For other IPI below, AP should send the request
|
||||
* to SCP to trigger the interrupt.
|
||||
* @SCP_IPI_MAX: The maximum IPI number
|
||||
*/
|
||||
|
||||
enum scp_ipi_id {
|
||||
SCP_IPI_INIT = 0,
|
||||
SCP_IPI_VDEC_H264,
|
||||
SCP_IPI_VDEC_VP8,
|
||||
SCP_IPI_VDEC_VP9,
|
||||
SCP_IPI_VENC_H264,
|
||||
SCP_IPI_VENC_VP8,
|
||||
SCP_IPI_MDP_INIT,
|
||||
SCP_IPI_MDP_DEINIT,
|
||||
SCP_IPI_MDP_FRAME,
|
||||
SCP_IPI_DIP,
|
||||
SCP_IPI_ISP_CMD,
|
||||
SCP_IPI_ISP_FRAME,
|
||||
SCP_IPI_FD_CMD,
|
||||
SCP_IPI_CROS_HOST_CMD,
|
||||
SCP_IPI_MAX,
|
||||
};
|
||||
|
||||
struct mtk_scp *scp_get(struct platform_device *pdev);
|
||||
void scp_put(struct mtk_scp *scp);
|
||||
|
||||
struct device *scp_get_device(struct mtk_scp *scp);
|
||||
struct rproc *scp_get_rproc(struct mtk_scp *scp);
|
||||
|
||||
int scp_ipi_register(struct mtk_scp *scp, u32 id, scp_ipi_handler_t handler,
|
||||
void *priv);
|
||||
void scp_ipi_unregister(struct mtk_scp *scp, u32 id);
|
||||
|
||||
int scp_ipi_send(struct mtk_scp *scp, u32 id, void *buf, unsigned int len,
|
||||
unsigned int wait);
|
||||
|
||||
unsigned int scp_get_vdec_hw_capa(struct mtk_scp *scp);
|
||||
unsigned int scp_get_venc_hw_capa(struct mtk_scp *scp);
|
||||
|
||||
void *scp_mapping_dm_addr(struct mtk_scp *scp, u32 mem_addr);
|
||||
|
||||
#endif /* _MTK_SCP_H */
|
||||
Reference in New Issue
Block a user