mirror of
https://github.com/physwizz/a155-U-u1.git
synced 2024-11-19 13:27:49 +00:00
1065 lines
28 KiB
C
1065 lines
28 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
//
|
|
// Copyright (c) 2021 MediaTek Inc.
|
|
|
|
#include <linux/arm-smccc.h>
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/dma-buf.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/iommu.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/fdtable.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/remoteproc/mtk_ccu.h>
|
|
#include <linux/soc/mediatek/mtk_sip_svc.h>
|
|
#include <soc/mediatek/smi.h>
|
|
#if IS_ENABLED(CONFIG_MTK_AEE_FEATURE)
|
|
#include <mt-plat/aee.h>
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_MTK_AEE_IPANIC)
|
|
#include <mt-plat/mrdump.h>
|
|
#endif
|
|
|
|
#include "mtk_ccu_isp71.h"
|
|
#include "mtk_ccu_common.h"
|
|
#include "mtk-interconnect.h"
|
|
#include "remoteproc_internal.h"
|
|
|
|
#define CCU_SET_MMQOS
|
|
/* #define CCU1_DEVICE */
|
|
#define MTK_CCU_MB_RX_TIMEOUT_SPEC 1000 /* 10ms */
|
|
|
|
#define MTK_CCU_TAG "[ccu_rproc]"
|
|
#define LOG_DBG(format, args...) \
|
|
pr_info(MTK_CCU_TAG "[%s] " format, __func__, ##args)
|
|
|
|
#if IS_ENABLED(CONFIG_MTK_AEE_IPANIC)
|
|
static struct mtk_ccu *dev_ccu;
|
|
#endif
|
|
static int mtk_ccu_probe(struct platform_device *dev);
|
|
static int mtk_ccu_remove(struct platform_device *dev);
|
|
static int mtk_ccu_read_platform_info_from_dt(struct device_node
|
|
*node, uint32_t *ccu_hw_base, uint32_t *ccu_hw_size);
|
|
static int mtk_ccu_get_power(struct device *dev);
|
|
static void mtk_ccu_put_power(struct device *dev);
|
|
|
|
static int
|
|
mtk_ccu_allocate_mem(struct device *dev, struct mtk_ccu_mem_handle *memHandle)
|
|
{
|
|
if (memHandle->meminfo.size <= 0)
|
|
return -EINVAL;
|
|
|
|
/* get buffer virtual address */
|
|
memHandle->meminfo.va = dma_alloc_attrs(dev, memHandle->meminfo.size,
|
|
&memHandle->meminfo.mva, GFP_KERNEL,
|
|
DMA_ATTR_WRITE_COMBINE | DMA_ATTR_FORCE_CONTIGUOUS);
|
|
|
|
if (memHandle->meminfo.va == NULL) {
|
|
dev_err(dev, "fail to get buffer kernel virtual address");
|
|
return -EINVAL;
|
|
}
|
|
|
|
dev_info(dev, "success: size(%x), va(%lx), mva(%lx)\n",
|
|
memHandle->meminfo.size, memHandle->meminfo.va, memHandle->meminfo.mva);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
mtk_ccu_deallocate_mem(struct device *dev, struct mtk_ccu_mem_handle *memHandle)
|
|
{
|
|
dma_free_attrs(dev, memHandle->meminfo.size, memHandle->meminfo.va,
|
|
memHandle->meminfo.mva, DMA_ATTR_WRITE_COMBINE);
|
|
memset(memHandle, 0, sizeof(struct mtk_ccu_mem_handle));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mtk_ccu_set_log_memory_address(struct mtk_ccu *ccu)
|
|
{
|
|
struct mtk_ccu_mem_info *meminfo;
|
|
int offset;
|
|
|
|
if (ccu->ext_buf.meminfo.va != 0) {
|
|
meminfo = &ccu->ext_buf.meminfo;
|
|
offset = 0;
|
|
} else {
|
|
dev_info(ccu->dev, "no log buf setting\n");
|
|
return;
|
|
}
|
|
|
|
/* log chunk1 */
|
|
ccu->log_info[0].fd = meminfo->fd;
|
|
ccu->log_info[0].size = MTK_CCU_DRAM_LOG_BUF_SIZE;
|
|
ccu->log_info[0].offset = offset;
|
|
ccu->log_info[0].mva = meminfo->mva + offset;
|
|
ccu->log_info[0].va = meminfo->va + offset;
|
|
*((uint32_t *)(ccu->log_info[0].va)) = LOG_ENDEND;
|
|
|
|
/* log chunk2 */
|
|
ccu->log_info[1].fd = meminfo->fd;
|
|
ccu->log_info[1].size = MTK_CCU_DRAM_LOG_BUF_SIZE;
|
|
ccu->log_info[1].offset = offset + MTK_CCU_DRAM_LOG_BUF_SIZE;
|
|
ccu->log_info[1].mva = ccu->log_info[0].mva + MTK_CCU_DRAM_LOG_BUF_SIZE;
|
|
ccu->log_info[1].va = ccu->log_info[0].va + MTK_CCU_DRAM_LOG_BUF_SIZE;
|
|
*((uint32_t *)(ccu->log_info[1].va)) = LOG_ENDEND;
|
|
|
|
/* sram log */
|
|
ccu->log_info[2].fd = meminfo->fd;
|
|
ccu->log_info[2].size = MTK_CCU_DRAM_LOG_BUF_SIZE;
|
|
ccu->log_info[2].offset = offset + MTK_CCU_DRAM_LOG_BUF_SIZE * 2;
|
|
ccu->log_info[2].mva = ccu->log_info[1].mva + MTK_CCU_DRAM_LOG_BUF_SIZE;
|
|
ccu->log_info[2].va = ccu->log_info[1].va + MTK_CCU_DRAM_LOG_BUF_SIZE;
|
|
|
|
ccu->log_info[3].fd = meminfo->fd;
|
|
ccu->log_info[3].size = MTK_CCU_DRAM_LOG_BUF_SIZE * 2;
|
|
ccu->log_info[3].offset = offset;
|
|
ccu->log_info[3].mva = ccu->log_info[0].mva;
|
|
ccu->log_info[3].va = ccu->log_info[0].va;
|
|
}
|
|
|
|
int mtk_ccu_sw_hw_reset(struct mtk_ccu *ccu)
|
|
{
|
|
uint32_t duration = 0;
|
|
uint32_t ccu_status;
|
|
uint8_t *ccu_base = (uint8_t *)ccu->ccu_base;
|
|
/* check halt is up */
|
|
ccu_status = readl(ccu_base + MTK_CCU_MON_ST);
|
|
LOG_DBG("polling CCU halt(0x%08x)\n", ccu_status);
|
|
duration = 0;
|
|
while ((ccu_status & 0x30) != 0x30) {
|
|
duration++;
|
|
if (duration > 1000) {
|
|
dev_err(ccu->dev,
|
|
"polling CCU halt, timeout: (0x%08x)\n", ccu_status);
|
|
mtk_smi_dbg_hang_detect("CCU");
|
|
break;
|
|
}
|
|
udelay(10);
|
|
ccu_status = readl(ccu_base + MTK_CCU_MON_ST);
|
|
}
|
|
LOG_DBG("polling CCU halt done(0x%08x)\n", ccu_status);
|
|
|
|
return true;
|
|
}
|
|
|
|
struct platform_device *mtk_ccu_get_pdev(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *ccu_node;
|
|
struct platform_device *ccu_pdev;
|
|
|
|
ccu_node = of_parse_phandle(dev->of_node, "mediatek,ccu_rproc", 0);
|
|
if (!ccu_node) {
|
|
dev_err(dev, "failed to get ccu node\n");
|
|
return NULL;
|
|
}
|
|
|
|
ccu_pdev = of_find_device_by_node(ccu_node);
|
|
if (WARN_ON(!ccu_pdev)) {
|
|
dev_err(dev, "failed to get ccu pdev\n");
|
|
of_node_put(ccu_node);
|
|
return NULL;
|
|
}
|
|
|
|
return ccu_pdev;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mtk_ccu_get_pdev);
|
|
|
|
static int mtk_ccu_run(struct mtk_ccu *ccu)
|
|
{
|
|
int32_t timeout = 100;
|
|
uint8_t *ccu_base = (uint8_t *)ccu->ccu_base;
|
|
#if defined(SECURE_CCU)
|
|
struct arm_smccc_res res;
|
|
#else
|
|
struct mtk_ccu_mem_info *bin_mem =
|
|
mtk_ccu_get_meminfo(ccu, MTK_CCU_DDR);
|
|
dma_addr_t remapOffset;
|
|
|
|
if (!bin_mem) {
|
|
dev_err(ccu->dev, "get binary memory info failed\n");
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
|
|
LOG_DBG("+\n");
|
|
|
|
/*1. Set CCU remap offset & log level*/
|
|
#if !defined(SECURE_CCU)
|
|
remapOffset = bin_mem->mva - MTK_CCU_CACHE_BASE;
|
|
writel(remapOffset >> 4, ccu_base + MTK_CCU_REG_AXI_REMAP);
|
|
#endif
|
|
writel(ccu->log_level, ccu_base + MTK_CCU_SPARE_REG04);
|
|
writel(ccu->log_taglevel, ccu_base + MTK_CCU_SPARE_REG05);
|
|
|
|
#if defined(SECURE_CCU)
|
|
writel(CCU_GO_TO_RUN, ccu_base + MTK_CCU_SPARE_REG06);
|
|
#ifdef CONFIG_ARM64
|
|
arm_smccc_smc(MTK_SIP_KERNEL_CCU_CONTROL, (u64) CCU_SMC_REQ_RUN, 0, 0, 0, 0, 0, 0, &res);
|
|
#endif
|
|
#ifdef CONFIG_ARM_PSCI
|
|
arm_smccc_smc(MTK_SIP_KERNEL_CCU_CONTROL, (u32) CCU_SMC_REQ_RUN, 0, 0, 0, 0, 0, 0, &res);
|
|
#endif
|
|
#else /* !defined(SECURE_CCU) */
|
|
/*2. Set CCU_RESET. CCU_HW_RST=0*/
|
|
writel(0x0, ccu_base + MTK_CCU_REG_RESET);
|
|
#endif
|
|
|
|
/*3. Pulling CCU init done spare register*/
|
|
while ((readl(ccu_base + MTK_CCU_SPARE_REG08)
|
|
!= CCU_STATUS_INIT_DONE) && (timeout >= 0)) {
|
|
usleep_range(50, 100);
|
|
timeout = timeout - 1;
|
|
}
|
|
if (timeout <= 0) {
|
|
dev_err(ccu->dev, "CCU init timeout\n");
|
|
dev_err(ccu->dev, "ccu initial debug info: %x\n",
|
|
readl(ccu_base + MTK_CCU_SPARE_REG17));
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
/*5. Get mailbox address in CCU's sram */
|
|
ccu->mb = (struct mtk_ccu_mailbox *)(uintptr_t)(ccu->dmem_base +
|
|
readl(ccu_base + MTK_CCU_SPARE_REG00));
|
|
LOG_DBG("ccu initial debug mb_ap2ccu: %x\n",
|
|
readl(ccu_base + MTK_CCU_SPARE_REG00));
|
|
|
|
mtk_ccu_rproc_ipc_init(ccu);
|
|
|
|
mtk_ccu_ipc_register(ccu->pdev, MTK_CCU_MSG_TO_APMCU_FLUSH_LOG,
|
|
mtk_ccu_ipc_log_handle, ccu);
|
|
mtk_ccu_ipc_register(ccu->pdev, MTK_CCU_MSG_TO_APMCU_CCU_ASSERT,
|
|
mtk_ccu_ipc_assert_handle, ccu);
|
|
mtk_ccu_ipc_register(ccu->pdev, MTK_CCU_MSG_TO_APMCU_CCU_WARNING,
|
|
mtk_ccu_ipc_warning_handle, ccu);
|
|
|
|
/*tell ccu that driver has initialized mailbox*/
|
|
writel(0, ccu_base + MTK_CCU_SPARE_REG08);
|
|
|
|
timeout = 10;
|
|
while ((readl(ccu_base + MTK_CCU_SPARE_REG08)
|
|
!= CCU_STATUS_INIT_DONE_2) && (timeout >= 0)) {
|
|
udelay(100);
|
|
timeout = timeout - 1;
|
|
}
|
|
|
|
if (timeout <= 0) {
|
|
dev_err(ccu->dev, "CCU init timeout 2\n");
|
|
dev_err(ccu->dev, "ccu initial debug info: %x\n",
|
|
readl(ccu_base + MTK_CCU_SPARE_REG17));
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
LOG_DBG("-\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_ccu_clk_prepare(struct mtk_ccu *ccu)
|
|
{
|
|
int ret;
|
|
int i = 0;
|
|
struct device *dev = ccu->dev;
|
|
|
|
LOG_DBG("Power on CCU0.\n");
|
|
ret = mtk_ccu_get_power(dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
#if defined(CCU1_DEVICE)
|
|
LOG_DBG("Power on CCU1\n");
|
|
ret = mtk_ccu_get_power(&ccu->pdev1->dev);
|
|
if (ret)
|
|
goto ERROR_poweroff_ccu;
|
|
#endif
|
|
|
|
LOG_DBG("Clock on CCU\n");
|
|
for (i = 0; i < MTK_CCU_CLK_PWR_NUM; ++i) {
|
|
ret = clk_prepare_enable(ccu->ccu_clk_pwr_ctrl[i]);
|
|
if (ret) {
|
|
dev_err(dev, "failed to enable CCU clocks #%d\n", i);
|
|
goto ERROR;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
ERROR:
|
|
for (--i; i >= 0 ; --i)
|
|
clk_disable_unprepare(ccu->ccu_clk_pwr_ctrl[i]);
|
|
|
|
#if defined(CCU1_DEVICE)
|
|
mtk_ccu_put_power(&ccu->pdev1->dev);
|
|
ERROR_poweroff_ccu:
|
|
#endif
|
|
mtk_ccu_put_power(dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void mtk_ccu_clk_unprepare(struct mtk_ccu *ccu)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MTK_CCU_CLK_PWR_NUM; ++i)
|
|
clk_disable_unprepare(ccu->ccu_clk_pwr_ctrl[i]);
|
|
mtk_ccu_put_power(ccu->dev);
|
|
#if defined(CCU1_DEVICE)
|
|
mtk_ccu_put_power(&ccu->pdev1->dev);
|
|
#endif
|
|
}
|
|
|
|
static int mtk_ccu_start(struct rproc *rproc)
|
|
{
|
|
struct mtk_ccu *ccu = (struct mtk_ccu *)rproc->priv;
|
|
uint8_t *ccu_base = (uint8_t *)ccu->ccu_base;
|
|
|
|
/*1. Set CCU log memory address from user space*/
|
|
writel((uint32_t)((ccu->log_info[0].mva) >> 8), ccu_base + MTK_CCU_SPARE_REG02);
|
|
writel((uint32_t)((ccu->log_info[1].mva) >> 8), ccu_base + MTK_CCU_SPARE_REG03);
|
|
writel((uint32_t)((ccu->log_info[2].mva) >> 8), ccu_base + MTK_CCU_SPARE_REG07);
|
|
|
|
#if defined(CCU_SET_MMQOS)
|
|
mtk_icc_set_bw(ccu->path_ccuo, MBps_to_icc(20), MBps_to_icc(30));
|
|
mtk_icc_set_bw(ccu->path_ccui, MBps_to_icc(10), MBps_to_icc(30));
|
|
mtk_icc_set_bw(ccu->path_ccug, MBps_to_icc(30), MBps_to_icc(30));
|
|
#endif
|
|
|
|
LOG_DBG("LogBuf_mva[0](0x%lx)(0x%x << 8)\n",
|
|
ccu->log_info[0].mva, readl(ccu_base + MTK_CCU_SPARE_REG02));
|
|
LOG_DBG("LogBuf_mva[1](0x%lx)(0x%x << 8)\n",
|
|
ccu->log_info[1].mva, readl(ccu_base + MTK_CCU_SPARE_REG03));
|
|
LOG_DBG("LogBuf_mva[2](0x%lx)(0x%x << 8)\n",
|
|
ccu->log_info[2].mva, readl(ccu_base + MTK_CCU_SPARE_REG07));
|
|
ccu->g_LogBufIdx = 0;
|
|
|
|
spin_lock(&ccu->ccu_poweron_lock);
|
|
ccu->poweron = true;
|
|
spin_unlock(&ccu->ccu_poweron_lock);
|
|
|
|
ccu->disirq = false;
|
|
if (devm_request_threaded_irq(ccu->dev, ccu->irq_num, NULL,
|
|
mtk_ccu_isr_handler, IRQF_ONESHOT, "ccu_rproc", ccu)) {
|
|
dev_err(ccu->dev, "fail to request ccu irq!\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
/*1. Set CCU run*/
|
|
mtk_ccu_run(ccu);
|
|
return 0;
|
|
}
|
|
|
|
void *mtk_ccu_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
|
|
{
|
|
struct mtk_ccu *ccu = (struct mtk_ccu *)rproc->priv;
|
|
struct device *dev = ccu->dev;
|
|
int offset = 0;
|
|
#if !defined(SECURE_CCU)
|
|
struct mtk_ccu_mem_info *bin_mem = mtk_ccu_get_meminfo(ccu, MTK_CCU_DDR);
|
|
#endif
|
|
|
|
if (da < MTK_CCU_CORE_DMEM_BASE) {
|
|
offset = da;
|
|
if (offset >= 0 && (offset + len) < MTK_CCU_PMEM_SIZE)
|
|
return ccu->pmem_base + offset;
|
|
} else if (da < MTK_CCU_CACHE_BASE) {
|
|
offset = da - MTK_CCU_CORE_DMEM_BASE;
|
|
if (offset >= 0 && (offset + len) < MTK_CCU_DMEM_SIZE)
|
|
return ccu->dmem_base + offset;
|
|
}
|
|
#if !defined(SECURE_CCU)
|
|
else {
|
|
offset = da - MTK_CCU_CACHE_BASE;
|
|
if (!bin_mem) {
|
|
dev_err(dev, "get binary memory info failed\n");
|
|
return NULL;
|
|
}
|
|
if (offset >= 0 && (offset + len) < MTK_CCU_CACHE_SIZE)
|
|
return bin_mem->va + offset;
|
|
}
|
|
#endif
|
|
|
|
dev_err(dev, "failed lookup da(0x%x) len(0x%x) to va, offset(%x)\n",
|
|
da, len, offset);
|
|
return NULL;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mtk_ccu_da_to_va);
|
|
|
|
static bool mtk_ccu_mb_rx_empty(struct mtk_ccu *ccu)
|
|
{
|
|
if ((!ccu) || (!(ccu->mb)))
|
|
return true;
|
|
|
|
return (readl(&ccu->mb->rear) == readl(&ccu->mb->front));
|
|
}
|
|
|
|
static int mtk_ccu_stop(struct rproc *rproc)
|
|
{
|
|
struct mtk_ccu *ccu = (struct mtk_ccu *)rproc->priv;
|
|
/* struct device *dev = &rproc->dev; */
|
|
int ret, i;
|
|
#if defined(SECURE_CCU)
|
|
struct arm_smccc_res res;
|
|
#else
|
|
int ccu_reset;
|
|
uint8_t *ccu_base = (uint8_t *)ccu->ccu_base;
|
|
#endif
|
|
|
|
/* notify CCU to shutdown*/
|
|
ret = mtk_ccu_rproc_ipc_send(ccu->pdev, MTK_CCU_FEATURE_SYSCTRL,
|
|
3, NULL, 0);
|
|
|
|
mtk_ccu_sw_hw_reset(ccu);
|
|
|
|
ccu->disirq = true;
|
|
|
|
#if !defined(SECURE_CCU)
|
|
ret = mtk_ccu_deallocate_mem(ccu->dev,
|
|
&ccu->buffer_handle[MTK_CCU_DDR]);
|
|
#endif
|
|
|
|
#if defined(SECURE_CCU)
|
|
writel(CCU_GO_TO_STOP, ccu->ccu_base + MTK_CCU_SPARE_REG06);
|
|
#ifdef CONFIG_ARM64
|
|
arm_smccc_smc(MTK_SIP_KERNEL_CCU_CONTROL, (u64) CCU_SMC_REQ_STOP,
|
|
0, 0, 0, 0, 0, 0, &res);
|
|
#endif
|
|
#ifdef CONFIG_ARM_PSCI
|
|
arm_smccc_smc(MTK_SIP_KERNEL_CCU_CONTROL, (u32) CCU_SMC_REQ_STOP,
|
|
0, 0, 0, 0, 0, 0, &res);
|
|
#endif
|
|
if (res.a0 != 0)
|
|
dev_err(ccu->dev, "stop CCU failed (%d).\n", res.a0);
|
|
else
|
|
LOG_DBG("stop CCU OK\n");
|
|
#else
|
|
ccu_reset = readl(ccu_base + MTK_CCU_REG_RESET);
|
|
writel(ccu_reset|MTK_CCU_HW_RESET_BIT, ccu_base + MTK_CCU_REG_RESET);
|
|
#endif
|
|
|
|
for (i = 0; i <= MTK_CCU_MB_RX_TIMEOUT_SPEC; ++i) {
|
|
if (mtk_ccu_mb_rx_empty(ccu))
|
|
break;
|
|
if (i < MTK_CCU_MB_RX_TIMEOUT_SPEC)
|
|
udelay(10);
|
|
}
|
|
|
|
if (i > MTK_CCU_MB_RX_TIMEOUT_SPEC)
|
|
LOG_DBG("mb_rx_empty timeout.\n");
|
|
|
|
#if defined(CCU_SET_MMQOS)
|
|
mtk_icc_set_bw(ccu->path_ccuo, MBps_to_icc(0), MBps_to_icc(0));
|
|
mtk_icc_set_bw(ccu->path_ccui, MBps_to_icc(0), MBps_to_icc(0));
|
|
mtk_icc_set_bw(ccu->path_ccug, MBps_to_icc(0), MBps_to_icc(0));
|
|
#endif
|
|
|
|
devm_free_irq(ccu->dev, ccu->irq_num, ccu);
|
|
|
|
spin_lock(&ccu->ccu_poweron_lock);
|
|
ccu->poweron = false;
|
|
spin_unlock(&ccu->ccu_poweron_lock);
|
|
|
|
mtk_ccu_clk_unprepare(ccu);
|
|
return 0;
|
|
}
|
|
|
|
#if !defined(SECURE_CCU)
|
|
static int
|
|
ccu_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
|
|
{
|
|
struct device *dev = &rproc->dev;
|
|
struct mtk_ccu *ccu = rproc->priv;
|
|
struct elf32_hdr *ehdr;
|
|
struct elf32_phdr *phdr;
|
|
int i, ret = 0;
|
|
int timeout = 10;
|
|
unsigned int status;
|
|
const u8 *elf_data = fw->data;
|
|
uint8_t *ccu_base = (uint8_t *)ccu->ccu_base;
|
|
bool is_iomem;
|
|
|
|
/* 1. Halt CCU HW before load binary */
|
|
writel(MTK_CCU_HW_RESET_BIT, ccu_base + MTK_CCU_REG_RESET);
|
|
udelay(10);
|
|
|
|
/* 2. Polling CCU HW status until ready */
|
|
status = readl(ccu_base + MTK_CCU_REG_RESET);
|
|
while ((status & 0x3) != 0x3) {
|
|
status = readl(ccu_base + MTK_CCU_REG_RESET);
|
|
udelay(300);
|
|
if (timeout < 0 && ((status & 0x3) != 0x3)) {
|
|
dev_err(dev, "wait ccu halt before load bin, timeout");
|
|
return -EFAULT;
|
|
}
|
|
timeout--;
|
|
}
|
|
|
|
ehdr = (struct elf32_hdr *)elf_data;
|
|
phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
|
|
/* 3. go through the available ELF segments */
|
|
for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
|
|
u32 da = phdr->p_paddr;
|
|
u32 memsz = phdr->p_memsz;
|
|
u32 filesz = phdr->p_filesz;
|
|
u32 offset = phdr->p_offset;
|
|
void *ptr;
|
|
|
|
if (phdr->p_type != PT_LOAD)
|
|
continue;
|
|
|
|
LOG_DBG("phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n",
|
|
phdr->p_type, da, memsz, filesz);
|
|
|
|
if (filesz > memsz) {
|
|
dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
|
|
filesz, memsz);
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
if (offset + filesz > fw->size) {
|
|
dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n",
|
|
offset + filesz, fw->size);
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
/* grab the kernel address for this device address */
|
|
ptr = rproc_da_to_va(rproc, da, memsz, &is_iomem);
|
|
if (!ptr) {
|
|
dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
/* put the segment where the remote processor expects it */
|
|
if (phdr->p_filesz)
|
|
mtk_ccu_memcpy(ptr, elf_data + phdr->p_offset, filesz);
|
|
|
|
/*
|
|
* Zero out remaining memory for this segment.
|
|
*/
|
|
if (memsz > filesz) {
|
|
mtk_ccu_memclr(ptr + ((filesz + 0x3) & (~0x3)),
|
|
memsz - filesz);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static int mtk_ccu_load(struct rproc *rproc, const struct firmware *fw)
|
|
{
|
|
struct mtk_ccu *ccu = rproc->priv;
|
|
int ret;
|
|
#if defined(SECURE_CCU)
|
|
struct arm_smccc_res res;
|
|
#endif
|
|
char error_desc[80];
|
|
|
|
/*1. prepare CCU's clks & power*/
|
|
ret = mtk_ccu_clk_prepare(ccu);
|
|
if (ret) {
|
|
dev_err(ccu->dev, "failed to prepare ccu clocks\n");
|
|
return ret;
|
|
}
|
|
|
|
LOG_DBG("Load CCU binary start\n");
|
|
|
|
#if defined(SECURE_CCU)
|
|
writel(CCU_GO_TO_LOAD, ccu->ccu_base + MTK_CCU_SPARE_REG06);
|
|
#ifdef CONFIG_ARM64
|
|
arm_smccc_smc(MTK_SIP_KERNEL_CCU_CONTROL, (u64) CCU_SMC_REQ_LOAD,
|
|
0, 0, 0, 0, 0, 0, &res);
|
|
#endif
|
|
#ifdef CONFIG_ARM_PSCI
|
|
arm_smccc_smc(MTK_SIP_KERNEL_CCU_CONTROL, (u32) CCU_SMC_REQ_LOAD,
|
|
0, 0, 0, 0, 0, 0, &res);
|
|
#endif
|
|
ret = (int)(res.a0);
|
|
if (ret != 0) {
|
|
snprintf(error_desc, 80, "load CCU binary fail(%d), clock: %d %d %d %d %d %d %d",
|
|
ret,
|
|
__clk_is_enabled(ccu->ccu_clk_pwr_ctrl[0]),
|
|
__clk_is_enabled(ccu->ccu_clk_pwr_ctrl[1]),
|
|
__clk_is_enabled(ccu->ccu_clk_pwr_ctrl[2]),
|
|
__clk_is_enabled(ccu->ccu_clk_pwr_ctrl[3]),
|
|
__clk_is_enabled(ccu->ccu_clk_pwr_ctrl[4]),
|
|
__clk_is_enabled(ccu->ccu_clk_pwr_ctrl[5]),
|
|
__clk_is_enabled(ccu->ccu_clk_pwr_ctrl[6]));
|
|
dev_err(ccu->dev, "%s\n", error_desc);
|
|
#if IS_ENABLED(CONFIG_MTK_AEE_FEATURE)
|
|
aee_kernel_warning_api(__FILE__, __LINE__, DB_OPT_DEFAULT, "CCU",
|
|
error_desc);
|
|
#else
|
|
WARN_ON(1);
|
|
#endif
|
|
goto ccu_load_err;
|
|
}
|
|
else
|
|
LOG_DBG("load CCU binary OK\n");
|
|
#else
|
|
/*2. allocate CCU's dram memory if needed*/
|
|
ccu->buffer_handle[MTK_CCU_DDR].meminfo.size = MTK_CCU_CACHE_SIZE;
|
|
ccu->buffer_handle[MTK_CCU_DDR].meminfo.cached = false;
|
|
ret = mtk_ccu_allocate_mem(ccu->dev, &ccu->buffer_handle[MTK_CCU_DDR]);
|
|
if (ret) {
|
|
dev_err(ccu->dev, "alloc mem failed\n");
|
|
goto ccu_load_err;
|
|
}
|
|
|
|
/*3. load binary*/
|
|
ret = ccu_elf_load_segments(rproc, fw);
|
|
if (ret) {
|
|
mtk_ccu_deallocate_mem(ccu->dev, &ccu->buffer_handle[MTK_CCU_DDR]);
|
|
got ccu_load_err;
|
|
}
|
|
#endif
|
|
return ret;
|
|
ccu_load_err:
|
|
mtk_ccu_clk_unprepare(ccu);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
mtk_ccu_sanity_check(struct rproc *rproc, const struct firmware *fw)
|
|
{
|
|
#if !defined(SECURE_CCU)
|
|
struct mtk_ccu *ccu = rproc->priv;
|
|
const char *name = rproc->firmware;
|
|
struct elf32_hdr *ehdr;
|
|
char class;
|
|
|
|
if (!fw) {
|
|
dev_err(ccu->dev, "failed to load %s\n", name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (fw->size < sizeof(struct elf32_hdr)) {
|
|
dev_err(ccu->dev, "Image is too small\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ehdr = (struct elf32_hdr *)fw->data;
|
|
|
|
/* We only support ELF32 at this point */
|
|
class = ehdr->e_ident[EI_CLASS];
|
|
if (class != ELFCLASS32) {
|
|
dev_err(ccu->dev, "Unsupported class: %d\n", class);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* We assume the firmware has the same endianness as the host */
|
|
# ifdef __LITTLE_ENDIAN
|
|
if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
|
|
# else /* BIG ENDIAN */
|
|
if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
|
|
# endif
|
|
dev_err(ccu->dev, "Unsupported firmware endianness\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) {
|
|
dev_err(ccu->dev, "Image is too small\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
|
|
dev_err(ccu->dev, "Image is corrupted (bad magic)\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ehdr->e_phnum == 0) {
|
|
dev_err(ccu->dev, "No loadable segments\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ehdr->e_phoff > fw->size) {
|
|
dev_err(ccu->dev, "Firmware size is too small\n");
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_MTK_AEE_IPANIC)
|
|
void get_ccu_mrdump_buffer(unsigned long *vaddr, unsigned long *size)
|
|
{
|
|
|
|
if ((!dev_ccu) || (!dev_ccu->mrdump_buf))
|
|
return;
|
|
|
|
*((uint32_t *)(dev_ccu->mrdump_buf)) = LOG_ENDEND;
|
|
*((uint32_t *)(dev_ccu->mrdump_buf +
|
|
(MTK_CCU_SRAM_LOG_BUF_SIZE / 2))) = LOG_ENDEND;
|
|
|
|
if (spin_trylock(&dev_ccu->ccu_poweron_lock)) {
|
|
if (dev_ccu->poweron) {
|
|
memcpy(dev_ccu->mrdump_buf,
|
|
dev_ccu->dmem_base + MTK_CCU_SRAM_LOG_OFFSET,
|
|
MTK_CCU_SRAM_LOG_BUF_SIZE);
|
|
memcpy(dev_ccu->mrdump_buf + MTK_CCU_SRAM_LOG_BUF_SIZE,
|
|
dev_ccu->ccu_base,
|
|
MTK_CCU_REG_LOG_BUF_SIZE - MTK_CCU_EXTRA_REG_LOG_BUF_SIZE);
|
|
memcpy(dev_ccu->mrdump_buf + MTK_CCU_MRDUMP_SRAM_BUF_SIZE
|
|
- MTK_CCU_EXTRA_REG_LOG_BUF_SIZE,
|
|
dev_ccu->ccu_base + MTK_CCU_EXTRA_REG_OFFSET,
|
|
MTK_CCU_EXTRA_REG_LOG_BUF_SIZE);
|
|
}
|
|
|
|
spin_unlock(&dev_ccu->ccu_poweron_lock);
|
|
}
|
|
|
|
memcpy(dev_ccu->mrdump_buf + MTK_CCU_MRDUMP_SRAM_BUF_SIZE,
|
|
dev_ccu->log_info[0].va, MTK_CCU_MRDUMP_BUF_DRAM_SIZE);
|
|
memcpy(dev_ccu->mrdump_buf + MTK_CCU_MRDUMP_SRAM_BUF_SIZE + MTK_CCU_MRDUMP_BUF_DRAM_SIZE,
|
|
dev_ccu->log_info[1].va, MTK_CCU_MRDUMP_BUF_DRAM_SIZE);
|
|
|
|
*vaddr = (unsigned long)dev_ccu->mrdump_buf;
|
|
*size = MTK_CCU_MRDUMP_BUF_SIZE;
|
|
}
|
|
#endif
|
|
|
|
static const struct rproc_ops ccu_ops = {
|
|
.start = mtk_ccu_start,
|
|
.stop = mtk_ccu_stop,
|
|
.da_to_va = mtk_ccu_da_to_va,
|
|
.load = mtk_ccu_load,
|
|
.sanity_check = mtk_ccu_sanity_check,
|
|
};
|
|
|
|
static int mtk_ccu_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *node = dev->of_node;
|
|
struct mtk_ccu *ccu;
|
|
struct rproc *rproc;
|
|
struct device_node *smi_node;
|
|
struct platform_device *smi_pdev;
|
|
int ret = 0;
|
|
uint32_t phy_addr;
|
|
uint32_t phy_size;
|
|
static struct lock_class_key ccu_lock_key;
|
|
const char *ccu_lock_name = "ccu_lock_class";
|
|
#if defined(CCU1_DEVICE)
|
|
struct device_node *node1;
|
|
phandle ccu_rproc1_phandle;
|
|
#endif
|
|
|
|
rproc = rproc_alloc(dev, node->name, &ccu_ops,
|
|
CCU_FW_NAME, sizeof(*ccu));
|
|
if ((!rproc) || (!rproc->priv)) {
|
|
dev_err(dev, "rproc or rproc->priv is NULL.\n");
|
|
return -EINVAL;
|
|
}
|
|
lockdep_set_class_and_name(&rproc->lock, &ccu_lock_key, ccu_lock_name);
|
|
ccu = (struct mtk_ccu *)rproc->priv;
|
|
ccu->pdev = pdev;
|
|
ccu->dev = &pdev->dev;
|
|
ccu->rproc = rproc;
|
|
|
|
platform_set_drvdata(pdev, ccu);
|
|
ret = mtk_ccu_read_platform_info_from_dt(node, &ccu->ccu_hw_base,
|
|
&ccu->ccu_hw_size);
|
|
if (ret) {
|
|
dev_err(ccu->dev, "Get CLK_TOP_CCU_SEL fail.\n");
|
|
return ret;
|
|
}
|
|
|
|
/*remap ccu_base*/
|
|
phy_addr = ccu->ccu_hw_base;
|
|
phy_size = ccu->ccu_hw_size;
|
|
ccu->ccu_base = devm_ioremap(dev, phy_addr, phy_size);
|
|
LOG_DBG("ccu_base pa: 0x%x, size: 0x%x\n", phy_addr, phy_size);
|
|
LOG_DBG("ccu_base va: 0x%lx\n", (uint64_t)ccu->ccu_base);
|
|
|
|
/*remap dmem_base*/
|
|
phy_addr = MTK_CCU_DMEM_BASE;
|
|
phy_size = MTK_CCU_DMEM_SIZE;
|
|
ccu->dmem_base = devm_ioremap(dev, phy_addr, phy_size);
|
|
LOG_DBG("dmem_base pa: 0x%x, size: 0x%x\n", phy_addr, phy_size);
|
|
LOG_DBG("dmem_base va: 0x%lx\n", (uint64_t)ccu->dmem_base);
|
|
|
|
/*remap pmem_base*/
|
|
phy_addr = MTK_CCU_PMEM_BASE;
|
|
phy_size = MTK_CCU_PMEM_SIZE;
|
|
ccu->pmem_base = devm_ioremap(dev, phy_addr, phy_size);
|
|
LOG_DBG("pmem_base pa: 0x%x, size: 0x%x\n", phy_addr, phy_size);
|
|
LOG_DBG("pmem_base va: 0x%lx\n", (uint64_t)ccu->pmem_base);
|
|
|
|
/* get Clock control from device tree. */
|
|
/*
|
|
* SMI definition is usually not ready at bring-up stage of new platform.
|
|
* Continue initialization if SMI is not defined.
|
|
*/
|
|
smi_node = of_parse_phandle(node, "mediatek,larbs", 0);
|
|
if (!smi_node) {
|
|
dev_err(ccu->dev, "get smi larb from DTS fail!\n");
|
|
/* return -ENODEV; */
|
|
} else {
|
|
smi_pdev = of_find_device_by_node(smi_node);
|
|
if (WARN_ON(!smi_pdev)) {
|
|
of_node_put(smi_node);
|
|
return -ENODEV;
|
|
}
|
|
of_node_put(smi_node);
|
|
|
|
mtk_smi_add_device_link(ccu->dev, &smi_pdev->dev);
|
|
}
|
|
pm_runtime_enable(ccu->dev);
|
|
|
|
ccu->ccu_clk_pwr_ctrl[0] = devm_clk_get(ccu->dev,
|
|
"CLK_TOP_CCUSYS_SEL");
|
|
if (IS_ERR(ccu->ccu_clk_pwr_ctrl[0])) {
|
|
dev_err(ccu->dev, "Get CLK_TOP_CCUSYS_SEL fail.\n");
|
|
return PTR_ERR(ccu->ccu_clk_pwr_ctrl[0]);
|
|
}
|
|
ccu->ccu_clk_pwr_ctrl[1] = devm_clk_get(ccu->dev,
|
|
"CLK_TOP_CCU_AHB_SEL");
|
|
if (IS_ERR(ccu->ccu_clk_pwr_ctrl[1])) {
|
|
dev_err(ccu->dev, "Get CLK_TOP_CCU_AHB_SEL fail.\n");
|
|
return PTR_ERR(ccu->ccu_clk_pwr_ctrl[1]);
|
|
}
|
|
ccu->ccu_clk_pwr_ctrl[2] = devm_clk_get(ccu->dev,
|
|
"CLK_CCU_LARB");
|
|
if (IS_ERR(ccu->ccu_clk_pwr_ctrl[2])) {
|
|
dev_err(ccu->dev, "Get CLK_CCU_LARB fail.\n");
|
|
return PTR_ERR(ccu->ccu_clk_pwr_ctrl[2]);
|
|
}
|
|
ccu->ccu_clk_pwr_ctrl[3] = devm_clk_get(ccu->dev,
|
|
"CLK_CCU_AHB");
|
|
if (IS_ERR(ccu->ccu_clk_pwr_ctrl[3])) {
|
|
dev_err(ccu->dev, "Get CLK_CCU_AHB fail.\n");
|
|
return PTR_ERR(ccu->ccu_clk_pwr_ctrl[3]);
|
|
}
|
|
ccu->ccu_clk_pwr_ctrl[4] = devm_clk_get(ccu->dev,
|
|
"CLK_CCUSYS_CCU0");
|
|
if (IS_ERR(ccu->ccu_clk_pwr_ctrl[4])) {
|
|
dev_err(ccu->dev, "Get CLK_CCUSYS_CCU0 fail.\n");
|
|
return PTR_ERR(ccu->ccu_clk_pwr_ctrl[4]);
|
|
}
|
|
|
|
ccu->ccu_clk_pwr_ctrl[5] = devm_clk_get(ccu->dev,
|
|
"CAM_LARB14");
|
|
if (IS_ERR(ccu->ccu_clk_pwr_ctrl[5])) {
|
|
dev_err(ccu->dev, "Get CAM_LARB14 fail.\n");
|
|
return PTR_ERR(ccu->ccu_clk_pwr_ctrl[5]);
|
|
}
|
|
|
|
ccu->ccu_clk_pwr_ctrl[6] = devm_clk_get(ccu->dev,
|
|
"CAM_MM1_GALS");
|
|
if (IS_ERR(ccu->ccu_clk_pwr_ctrl[6])) {
|
|
dev_err(ccu->dev, "Get CAM_MM1_GALS fail.\n");
|
|
return PTR_ERR(ccu->ccu_clk_pwr_ctrl[6]);
|
|
}
|
|
|
|
#if defined(CCU_SET_MMQOS)
|
|
ccu->path_ccug = of_mtk_icc_get(ccu->dev, "ccu_g");
|
|
ccu->path_ccuo = of_mtk_icc_get(ccu->dev, "ccu_o");
|
|
ccu->path_ccui = of_mtk_icc_get(ccu->dev, "ccu_i");
|
|
#endif
|
|
|
|
#if defined(CCU1_DEVICE)
|
|
ret = of_property_read_u32(node, "mediatek,ccu_rproc1",
|
|
&ccu_rproc1_phandle);
|
|
node1 = of_find_node_by_phandle(ccu_rproc1_phandle);
|
|
if (node1)
|
|
ccu->pdev1 = of_find_device_by_node(node1);
|
|
if (WARN_ON(!ccu->pdev1)) {
|
|
dev_err(ccu->dev, "failed to get ccu rproc1 pdev\n");
|
|
of_node_put(node1);
|
|
}
|
|
#endif
|
|
|
|
/* get irq from device irq*/
|
|
ccu->irq_num = irq_of_parse_and_map(node, 0);
|
|
LOG_DBG("ccu_probe irq_num: %d\n", ccu->irq_num);
|
|
|
|
/*prepare mutex & log's waitqueuehead*/
|
|
mutex_init(&ccu->ipc_desc_lock);
|
|
spin_lock_init(&ccu->ipc_send_lock);
|
|
spin_lock_init(&ccu->ccu_poweron_lock);
|
|
init_waitqueue_head(&ccu->WaitQueueHead);
|
|
|
|
#if IS_ENABLED(CONFIG_MTK_CCU_DEBUG)
|
|
/*register char dev for log ioctl*/
|
|
ret = mtk_ccu_reg_chardev(ccu);
|
|
if (ret)
|
|
dev_err(ccu->dev, "failed to regist char dev");
|
|
#endif
|
|
|
|
dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34));
|
|
|
|
ccu->ext_buf.meminfo.size = MTK_CCU_DRAM_LOG_BUF_SIZE * 4;
|
|
ccu->ext_buf.meminfo.cached = false;
|
|
ret = mtk_ccu_allocate_mem(ccu->dev, &ccu->ext_buf);
|
|
if (ret) {
|
|
dev_err(ccu->dev, "alloc mem failed\n");
|
|
return ret;
|
|
}
|
|
|
|
mtk_ccu_set_log_memory_address(ccu);
|
|
rproc->auto_boot = false;
|
|
|
|
#if IS_ENABLED(CONFIG_MTK_AEE_IPANIC)
|
|
ccu->mrdump_buf = kmalloc(MTK_CCU_MRDUMP_BUF_SIZE, GFP_ATOMIC);
|
|
if (!ccu->mrdump_buf) {
|
|
mtk_ccu_deallocate_mem(ccu->dev, &ccu->ext_buf);
|
|
return -EINVAL;
|
|
}
|
|
|
|
dev_ccu = ccu;
|
|
mrdump_set_extra_dump(AEE_EXTRA_FILE_CCU, get_ccu_mrdump_buffer);
|
|
#endif
|
|
|
|
ret = rproc_add(rproc);
|
|
return ret;
|
|
}
|
|
|
|
static int mtk_ccu_remove(struct platform_device *pdev)
|
|
{
|
|
struct mtk_ccu *ccu = platform_get_drvdata(pdev);
|
|
|
|
/*
|
|
* WARNING:
|
|
* With mrdump, remove CCU module will cause access violation
|
|
* at KE/SystemAPI.
|
|
*/
|
|
#if IS_ENABLED(CONFIG_MTK_AEE_IPANIC)
|
|
mrdump_set_extra_dump(AEE_EXTRA_FILE_CCU, NULL);
|
|
kfree(ccu->mrdump_buf);
|
|
#endif
|
|
mtk_ccu_deallocate_mem(ccu->dev, &ccu->ext_buf);
|
|
disable_irq(ccu->irq_num);
|
|
rproc_del(ccu->rproc);
|
|
rproc_free(ccu->rproc);
|
|
pm_runtime_disable(ccu->dev);
|
|
#if IS_ENABLED(CONFIG_MTK_CCU_DEBUG)
|
|
mtk_ccu_unreg_chardev(ccu);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_ccu_read_platform_info_from_dt(struct device_node
|
|
*node, uint32_t *ccu_hw_base, uint32_t *ccu_hw_size)
|
|
{
|
|
uint32_t reg[4] = {0, 0, 0, 0};
|
|
int ret = 0;
|
|
|
|
ret = of_property_read_u32_array(node, "reg", reg, 4);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
*ccu_hw_base = reg[1];
|
|
*ccu_hw_size = reg[3];
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CCU1_DEVICE)
|
|
static int mtk_ccu1_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
|
|
dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34));
|
|
|
|
pm_runtime_enable(dev);
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_ccu1_remove(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
|
|
pm_runtime_disable(dev);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int mtk_ccu_get_power(struct device *dev)
|
|
{
|
|
int ret = pm_runtime_get_sync(dev);
|
|
|
|
if (ret != 0)
|
|
dev_err(dev, "pm_runtime_get_sync failed %d", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void mtk_ccu_put_power(struct device *dev)
|
|
{
|
|
int ret = pm_runtime_put_sync(dev);
|
|
|
|
if (ret != 0)
|
|
dev_err(dev, "pm_runtime_put_sync failed %d", ret);
|
|
}
|
|
|
|
static const struct of_device_id mtk_ccu_of_ids[] = {
|
|
{.compatible = "mediatek,ccu_rproc", },
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, mtk_ccu_of_ids);
|
|
|
|
static struct platform_driver ccu_rproc_driver = {
|
|
.probe = mtk_ccu_probe,
|
|
.remove = mtk_ccu_remove,
|
|
.driver = {
|
|
.name = MTK_CCU_DEV_NAME,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_match_ptr(mtk_ccu_of_ids),
|
|
},
|
|
};
|
|
|
|
#if defined(CCU1_DEVICE)
|
|
static const struct of_device_id mtk_ccu1_of_ids[] = {
|
|
{.compatible = "mediatek,ccu_rproc1", },
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, mtk_ccu1_of_ids);
|
|
|
|
static struct platform_driver ccu_rproc1_driver = {
|
|
.probe = mtk_ccu1_probe,
|
|
.remove = mtk_ccu1_remove,
|
|
.driver = {
|
|
.name = MTK_CCU1_DEV_NAME,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_match_ptr(mtk_ccu1_of_ids),
|
|
},
|
|
};
|
|
#endif
|
|
|
|
static int __init ccu_init(void)
|
|
{
|
|
platform_driver_register(&ccu_rproc_driver);
|
|
#if defined(CCU1_DEVICE)
|
|
platform_driver_register(&ccu_rproc1_driver);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static void __exit ccu_exit(void)
|
|
{
|
|
platform_driver_unregister(&ccu_rproc_driver);
|
|
#if defined(CCU1_DEVICE)
|
|
platform_driver_unregister(&ccu_rproc1_driver);
|
|
#endif
|
|
}
|
|
|
|
module_init(ccu_init);
|
|
module_exit(ccu_exit);
|
|
|
|
MODULE_DESCRIPTION("MTK CCU Rproc Driver");
|
|
MODULE_LICENSE("GPL v2");
|