6db4831e98
Android 14
482 lines
13 KiB
C
482 lines
13 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2019 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/arm-smccc.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/device.h>
|
|
#include <linux/err.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/io.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/workqueue.h>
|
|
#include <mt-plat/aee.h>
|
|
#include <mt-plat/mtk_secure_api.h>
|
|
#include "mmsram.h"
|
|
|
|
#define MMSYSRAM_INTEN0 (0x000)
|
|
#define MMSYSRAM_INTEN1 (0x004)
|
|
#define MMSYSRAM_INSTA0 (0x010)
|
|
#define MMSYSRAM_INSTA1 (0x014)
|
|
#define MMSYSRAM_SEC_ADDR0 (0x040)
|
|
#define MMSYSRAM_SEC_CTRL0 (0x060)
|
|
|
|
#define BIT_INSTA0 (0x6FF0E0E0)
|
|
#define BIT_INSTA1 (0x000037F8)
|
|
#define BIT_SECURE_ON (0x11111111)
|
|
|
|
/* First Violation Latch Debug */
|
|
#define FVLD_APC0_LATCH_EN (0x090)
|
|
#define FVLD_APC1_LATCH_EN (0x0C0)
|
|
#define FVLD_MPU0_LATCH_EN (0x110)
|
|
#define FVLD_MPU1_LATCH_EN (0x140)
|
|
#define FVLD_APC0_VIO_ADDR (0x0A4)
|
|
#define FVLD_APC0_VIO_ID (0x0A8)
|
|
#define FVLD_APC0_VIO_WR_RD (0x0AC)
|
|
#define FVLD_APC1_VIO_ADDR (0x0D4)
|
|
#define FVLD_APC1_VIO_ID (0x0D8)
|
|
#define FVLD_APC1_VIO_WR_RD (0x0DC)
|
|
#define FVLD_MPU0_VIO_ADDR (0x124)
|
|
#define FVLD_MPU0_VIO_ID (0x128)
|
|
#define FVLD_MPU0_VIO_WR_RD (0x12C)
|
|
#define FVLD_MPU1_VIO_ADDR (0x154)
|
|
#define FVLD_MPU1_VIO_ID (0x158)
|
|
#define FVLD_MPU1_VIO_WR_RD (0x15C)
|
|
|
|
#define BIT_APC_LATCH_EN (0x00000020)
|
|
#define BIT_MPU_LATCH_EN (0x00000FF0)
|
|
|
|
#define DEC_APC0_VIO_DECERR(x) (((x) >> 5) & 0x1)
|
|
#define DEC_APC0_VIO_WR_RD(x) (((x) >> 6) & 0x3)
|
|
#define DEC_APC1_VIO_DECERR(x) (((x) >> 13) & 0x1)
|
|
#define DEC_APC1_VIO_WR_RD(x) (((x) >> 14) & 0x3)
|
|
#define DEC_MPU0_VIO_DEVAPC(x) (((x) >> 20) & 0xFF)
|
|
#define DEC_MPU0_VIO_WR_RD(x) (((x) >> 29) & 0x3)
|
|
#define DEC_MPU1_VIO_DEVAPC(x) (((x) >> 3) & 0xFF)
|
|
#define DEC_MPU1_VIO_WR_RD(x) (((x) >> 12) & 0x3)
|
|
|
|
#define DEC_APC_LATCH_EN(x) (((x) >> 5) & 0x1)
|
|
#define DEC_APC_VIO_ADDR(x) (x)
|
|
#define DEC_APC_VIO_ID(x) ((x) & 0xFFFFF)
|
|
#define DEC_APC_VIO_WR_RD(x) (((x) >> 17) & 0x3)
|
|
#define DEC_MPU_LATCH_EN(x) (((x) >> 4) & 0xFF)
|
|
#define DEC_MPU_VIO_ADDR(x) ((x) & 0x1FFFFF)
|
|
#define DEC_MPU_VIO_ID(x) ((x) & 0xFFFFF)
|
|
#define DEC_MPU_VIO_WR_RD(x) (((x) >> 17) & 0x3)
|
|
|
|
#define MAX_CLK_NUM (8)
|
|
|
|
struct mmsram_dev {
|
|
void __iomem *ctrl_base;
|
|
void __iomem *sram_paddr;
|
|
void __iomem *sram_vaddr;
|
|
ssize_t sram_size;
|
|
struct clk *clk[MAX_CLK_NUM];
|
|
const char *clk_name[MAX_CLK_NUM];
|
|
};
|
|
|
|
enum smc_mmsram_request {
|
|
MMSRAM_ENABLE_SECURE,
|
|
};
|
|
|
|
static struct work_struct dump_reg_work;
|
|
|
|
static struct mmsram_dev *mmsram;
|
|
static atomic_t clk_ref = ATOMIC_INIT(0);
|
|
static bool is_secure_on;
|
|
static bool debug_enable;
|
|
|
|
/* MTCMOS clocks should be defined before CG clocks in DTS */
|
|
static int set_clk_enable(bool is_enable)
|
|
{
|
|
int ret = 0;
|
|
|
|
#if !IS_ENABLED(CONFIG_FPGA_EARLY_PORTING)
|
|
int i, j;
|
|
|
|
if (is_enable) {
|
|
for (i = 0; i < MAX_CLK_NUM; i++) {
|
|
if (mmsram->clk[i])
|
|
ret = clk_prepare_enable(mmsram->clk[i]);
|
|
|
|
if (ret) {
|
|
pr_notice("mmsram clk(%s) enable fail:%d\n",
|
|
mmsram->clk_name[i], ret);
|
|
for (j = i - 1; j >= 0; j--)
|
|
clk_disable_unprepare(mmsram->clk[j]);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
atomic_inc(&clk_ref);
|
|
} else {
|
|
for (i = MAX_CLK_NUM - 1; i >= 0; i--)
|
|
if (mmsram->clk[i])
|
|
clk_disable_unprepare(mmsram->clk[i]);
|
|
atomic_dec(&clk_ref);
|
|
}
|
|
#endif
|
|
if (debug_enable)
|
|
pr_notice("%s:%d\n", __func__, is_enable);
|
|
return ret;
|
|
}
|
|
|
|
static void set_reg_secure(bool is_on)
|
|
{
|
|
struct arm_smccc_res res;
|
|
|
|
arm_smccc_smc(MTK_SIP_MMSRAM_CONTROL, MMSRAM_ENABLE_SECURE,
|
|
is_on ? 1 : 0, 0, 0, 0, 0, 0, &res);
|
|
}
|
|
|
|
static s32 before_reg_rw(void)
|
|
{
|
|
if (set_clk_enable(true)) {
|
|
pr_notice("%s: enable clk fail\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
set_reg_secure(false);
|
|
return 0;
|
|
}
|
|
|
|
static void after_reg_rw(void)
|
|
{
|
|
set_reg_secure(true);
|
|
set_clk_enable(false);
|
|
}
|
|
|
|
|
|
void mmsram_set_secure(bool secure_on)
|
|
{
|
|
if (secure_on == is_secure_on)
|
|
return;
|
|
is_secure_on = secure_on;
|
|
if (before_reg_rw()) {
|
|
pr_notice("%s: error before reg rw\n", __func__);
|
|
return;
|
|
}
|
|
if (secure_on)
|
|
writel(BIT_SECURE_ON, mmsram->ctrl_base + MMSYSRAM_SEC_CTRL0);
|
|
else
|
|
writel(0, mmsram->ctrl_base + MMSYSRAM_SEC_CTRL0);
|
|
after_reg_rw();
|
|
}
|
|
|
|
static void init_mmsram_reg(void)
|
|
{
|
|
if (before_reg_rw()) {
|
|
pr_notice("%s: error before reg rw\n", __func__);
|
|
return;
|
|
}
|
|
writel((0x1 << 24) | 0x160000,
|
|
mmsram->ctrl_base + MMSYSRAM_SEC_ADDR0);
|
|
|
|
writel(0x0, mmsram->ctrl_base + MMSYSRAM_INSTA0);
|
|
writel(0x0, mmsram->ctrl_base + MMSYSRAM_INSTA1);
|
|
|
|
writel(BIT_APC_LATCH_EN, mmsram->ctrl_base + FVLD_APC0_LATCH_EN);
|
|
writel(BIT_APC_LATCH_EN, mmsram->ctrl_base + FVLD_APC1_LATCH_EN);
|
|
writel(BIT_MPU_LATCH_EN, mmsram->ctrl_base + FVLD_MPU0_LATCH_EN);
|
|
writel(BIT_MPU_LATCH_EN, mmsram->ctrl_base + FVLD_MPU1_LATCH_EN);
|
|
writel(BIT_INSTA0, mmsram->ctrl_base + MMSYSRAM_INTEN0);
|
|
writel(BIT_INSTA1, mmsram->ctrl_base + MMSYSRAM_INTEN1);
|
|
|
|
after_reg_rw();
|
|
}
|
|
|
|
int mmsram_power_on(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
set_clk_enable(true);
|
|
init_mmsram_reg();
|
|
if (debug_enable)
|
|
pr_notice("mmsram power on\n");
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mmsram_power_on);
|
|
|
|
void mmsram_power_off(void)
|
|
{
|
|
set_clk_enable(false);
|
|
if (debug_enable)
|
|
pr_notice("mmsram power off\n");
|
|
}
|
|
EXPORT_SYMBOL_GPL(mmsram_power_off);
|
|
|
|
int enable_mmsram(void)
|
|
{
|
|
pr_notice("enable mmsram\n");
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(enable_mmsram);
|
|
|
|
void disable_mmsram(void)
|
|
{
|
|
pr_notice("disable mmsram\n");
|
|
}
|
|
EXPORT_SYMBOL_GPL(disable_mmsram);
|
|
|
|
void mmsram_get_info(struct mmsram_data *data)
|
|
{
|
|
data->paddr = mmsram->sram_paddr;
|
|
data->vaddr = mmsram->sram_vaddr;
|
|
data->size = mmsram->sram_size;
|
|
pr_notice("%s: pa:%#x va:%#x size:%#lx\n",
|
|
__func__, data->paddr, data->vaddr,
|
|
data->size);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mmsram_get_info);
|
|
|
|
static void dump_reg_func(struct work_struct *work)
|
|
{
|
|
u32 interrupt_monitor0, interrupt_monitor1;
|
|
void __iomem *ctrl_base = mmsram->ctrl_base;
|
|
|
|
if (before_reg_rw()) {
|
|
pr_notice("%s: error before reg rw\n", __func__);
|
|
return;
|
|
}
|
|
|
|
/* Print debug log */
|
|
interrupt_monitor0 = readl(ctrl_base + MMSYSRAM_INSTA0);
|
|
interrupt_monitor1 = readl(ctrl_base + MMSYSRAM_INSTA1);
|
|
pr_notice("apc0_vio_decerr:%#x apc0_vio_wr_rd:%#x\n",
|
|
DEC_APC0_VIO_DECERR(interrupt_monitor0),
|
|
DEC_APC0_VIO_WR_RD(interrupt_monitor0));
|
|
pr_notice("apc1_vio_decerr:%#x apc1_vio_wr_rd:%#x\n",
|
|
DEC_APC1_VIO_DECERR(interrupt_monitor0),
|
|
DEC_APC1_VIO_WR_RD(interrupt_monitor0));
|
|
pr_notice("mpu0_vio_devapc:%#x mpu0_vio_wr_rd:%#x\n",
|
|
DEC_MPU0_VIO_DEVAPC(interrupt_monitor0),
|
|
DEC_MPU0_VIO_WR_RD(interrupt_monitor0));
|
|
pr_notice("mpu1_vio_devapc:%#x mpu1_vio_wr_rd:%#x\n",
|
|
DEC_MPU1_VIO_DEVAPC(interrupt_monitor1),
|
|
DEC_MPU1_VIO_WR_RD(interrupt_monitor1));
|
|
pr_notice("apc0_latch_en:%#x apc0_vio_addr:%#x\n",
|
|
DEC_APC_LATCH_EN(readl(ctrl_base + FVLD_APC0_LATCH_EN)),
|
|
DEC_APC_VIO_ADDR(readl(ctrl_base + FVLD_APC0_VIO_ADDR)));
|
|
pr_notice("apc0_vio_id:%#x apc0_vio_wr_rd:%#x\n",
|
|
DEC_APC_VIO_ID(readl(ctrl_base + FVLD_APC0_VIO_ID)),
|
|
DEC_APC_VIO_WR_RD(readl(ctrl_base + FVLD_APC0_VIO_WR_RD)));
|
|
|
|
pr_notice("apc1_latch_en:%#x apc1_vio_addr:%#x\n",
|
|
DEC_APC_LATCH_EN(readl(ctrl_base + FVLD_APC1_LATCH_EN)),
|
|
DEC_APC_VIO_ADDR(readl(ctrl_base + FVLD_APC1_VIO_ADDR)));
|
|
pr_notice("apc1_vio_id:%#x apc1_vio_wr_rd:%#x\n",
|
|
DEC_APC_VIO_ID(readl(ctrl_base + FVLD_APC1_VIO_ID)),
|
|
DEC_APC_VIO_WR_RD(readl(ctrl_base + FVLD_APC1_VIO_WR_RD)));
|
|
|
|
pr_notice("mpu0_latch_en:%#x mpu0_vio_addr:%#x\n",
|
|
DEC_MPU_LATCH_EN(readl(ctrl_base + FVLD_MPU0_LATCH_EN)),
|
|
DEC_MPU_VIO_ADDR(readl(ctrl_base + FVLD_MPU0_VIO_ADDR)));
|
|
|
|
pr_notice("mpu0_vio_id:%#x mpu0_vio_wr_rd:%#x\n",
|
|
DEC_MPU_VIO_ID(readl(ctrl_base + FVLD_MPU0_VIO_ID)),
|
|
DEC_MPU_VIO_WR_RD(readl(ctrl_base + FVLD_MPU0_VIO_WR_RD)));
|
|
pr_notice("mpu1_latch_en:%#x mpu1_vio_addr:%#x\n",
|
|
DEC_MPU_LATCH_EN(readl(ctrl_base + FVLD_MPU1_LATCH_EN)),
|
|
DEC_MPU_VIO_ADDR(readl(ctrl_base + FVLD_MPU1_VIO_ADDR)));
|
|
pr_notice("mpu1_vio_id:%#x mpu1_vio_wr_rd:%#x\n",
|
|
DEC_MPU_VIO_ID(readl(ctrl_base + FVLD_MPU1_VIO_ID)),
|
|
DEC_MPU_VIO_WR_RD(readl(ctrl_base + FVLD_MPU1_VIO_WR_RD)));
|
|
|
|
writel(0x0, ctrl_base + MMSYSRAM_INSTA0);
|
|
writel(0x0, ctrl_base + MMSYSRAM_INSTA1);
|
|
|
|
after_reg_rw();
|
|
|
|
aee_kernel_warning("MMSRAM", "MMSRAM Violation.");
|
|
}
|
|
|
|
static irqreturn_t mmsram_irq_handler(int irq, void *data)
|
|
{
|
|
pr_notice("handle mmsram irq!\n");
|
|
schedule_work(&dump_reg_work);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int mmsram_probe(struct platform_device *pdev)
|
|
{
|
|
struct resource *res;
|
|
int irq, err, clk_num, i;
|
|
|
|
mmsram = devm_kzalloc(&pdev->dev, sizeof(*mmsram), GFP_KERNEL);
|
|
if (!mmsram)
|
|
return -ENOMEM;
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!res) {
|
|
dev_notice(&pdev->dev, "could not get resource for ctrl\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
mmsram->ctrl_base = devm_ioremap_resource(&pdev->dev, res);
|
|
if (IS_ERR(mmsram->ctrl_base)) {
|
|
dev_notice(&pdev->dev,
|
|
"could not ioremap resource for ctrl\n");
|
|
return PTR_ERR(mmsram->ctrl_base);
|
|
}
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
|
if (!res) {
|
|
dev_notice(&pdev->dev, "could not get resource for memory\n");
|
|
return -EINVAL;
|
|
}
|
|
mmsram->sram_paddr = (void *)res->start;
|
|
mmsram->sram_size = resource_size(res);
|
|
mmsram->sram_vaddr = (void __iomem *) devm_memremap(&pdev->dev,
|
|
res->start, mmsram->sram_size, MEMREMAP_WT);
|
|
if (IS_ERR(mmsram->sram_vaddr)) {
|
|
dev_notice(&pdev->dev,
|
|
"could not ioremap resource for memory\n");
|
|
return PTR_ERR(mmsram->sram_vaddr);
|
|
}
|
|
|
|
dev_notice(&pdev->dev, "probe va=%p pa=%p size=%#lx\n",
|
|
mmsram->sram_vaddr, mmsram->sram_paddr,
|
|
mmsram->sram_size);
|
|
|
|
if (!IS_ENABLED(CONFIG_FPGA_EARLY_PORTING)) {
|
|
clk_num = of_property_read_string_array(pdev->dev.of_node,
|
|
"clock-names", &mmsram->clk_name[0], MAX_CLK_NUM);
|
|
for (i = 0; i < clk_num; i++) {
|
|
mmsram->clk[i] = devm_clk_get(&pdev->dev,
|
|
mmsram->clk_name[i]);
|
|
if (IS_ERR(mmsram->clk[i])) {
|
|
dev_notice(&pdev->dev,
|
|
"could not get mmsram clk(%s) info\n",
|
|
mmsram->clk_name[i]);
|
|
return PTR_ERR(mmsram->clk[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
irq = platform_get_irq(pdev, 0);
|
|
if (irq < 0) {
|
|
dev_notice(&pdev->dev, "failed to get irq (%d)\n", irq);
|
|
return -EINVAL;
|
|
}
|
|
err = devm_request_irq(&pdev->dev, irq, mmsram_irq_handler, IRQF_SHARED,
|
|
"mtk_mmsram", mmsram);
|
|
if (err) {
|
|
dev_notice(&pdev->dev,
|
|
"failed to register ISR %d (%d)", irq, err);
|
|
return err;
|
|
}
|
|
|
|
INIT_WORK(&dump_reg_work, dump_reg_func);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define RESULT_STR_LEN 8
|
|
int test_mmsram;
|
|
struct mmsram_data *data;
|
|
int set_test_mmsram(const char *val, const struct kernel_param *kp)
|
|
{
|
|
int result;
|
|
u32 test_case, offset, value;
|
|
const char *test_str = "12345678";
|
|
char result_str[RESULT_STR_LEN + 1] = {0};
|
|
|
|
result = sscanf(val, "%d %i %i", &test_case, &offset, &value);
|
|
if (result != 3) {
|
|
pr_notice("invalid input: %s, result(%d)\n", val, result);
|
|
return -EINVAL;
|
|
}
|
|
pr_notice("%s (test_case, offset, value): (%d,%#x,%#x)\n",
|
|
__func__, test_case, offset, value);
|
|
|
|
switch (test_case) {
|
|
case 0: /* Initialize */
|
|
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
|
mmsram_get_info(data);
|
|
enable_mmsram();
|
|
mmsram_power_on();
|
|
break;
|
|
case 1: /* Uninitialize */
|
|
mmsram_power_off();
|
|
disable_mmsram();
|
|
kfree(data);
|
|
break;
|
|
case 2: /* Write value to offset */
|
|
writel(value, data->vaddr + offset);
|
|
value = readl(data->vaddr + offset);
|
|
pr_notice("write %#x success\n", value);
|
|
break;
|
|
case 3: /* Read value from offset */
|
|
value = readl(data->vaddr + offset);
|
|
pr_notice("read %#x success\n", value);
|
|
break;
|
|
case 4: /* Write test string to offset */
|
|
memcpy_toio(data->vaddr + offset, test_str, RESULT_STR_LEN);
|
|
pr_notice("write str:%s success\n", test_str);
|
|
break;
|
|
case 5: /* Write test string from offset */
|
|
memcpy_fromio(result_str, data->vaddr, RESULT_STR_LEN);
|
|
result_str[RESULT_STR_LEN] = '\0';
|
|
pr_notice("read str:%s success\n", result_str);
|
|
break;
|
|
default:
|
|
pr_notice("wrong input test_case:%d\n", test_case);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
static struct kernel_param_ops test_mmsram_ops = {
|
|
.set = set_test_mmsram,
|
|
.get = param_get_int,
|
|
};
|
|
module_param_cb(test_mmsram, &test_mmsram_ops, &test_mmsram, 0644);
|
|
MODULE_PARM_DESC(test_mmsram, "test mmsram");
|
|
|
|
static struct kernel_param_ops debug_enable_ops = {
|
|
.set = param_set_bool,
|
|
.get = param_get_bool,
|
|
};
|
|
module_param_cb(
|
|
debug_enable, &debug_enable_ops, &debug_enable, 0644);
|
|
MODULE_PARM_DESC(debug_enable, "enable or disable mmsram debug log");
|
|
|
|
static const struct of_device_id of_mmsram_match_tbl[] = {
|
|
{
|
|
.compatible = "mediatek,mmsram",
|
|
},
|
|
{}
|
|
};
|
|
|
|
static struct platform_driver mmsram_drv = {
|
|
.probe = mmsram_probe,
|
|
.driver = {
|
|
.name = "mtk-mmsram",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_mmsram_match_tbl,
|
|
},
|
|
};
|
|
static int __init mtk_mmsram_init(void)
|
|
{
|
|
s32 status;
|
|
|
|
status = platform_driver_register(&mmsram_drv);
|
|
if (status) {
|
|
pr_notice("Failed to register mmsram driver(%d)\n", status);
|
|
return -ENODEV;
|
|
}
|
|
return 0;
|
|
}
|
|
static void __exit mtk_mmsram_exit(void)
|
|
{
|
|
platform_driver_unregister(&mmsram_drv);
|
|
}
|
|
module_init(mtk_mmsram_init);
|
|
module_exit(mtk_mmsram_exit);
|
|
MODULE_DESCRIPTION("MTK MMSRAM driver");
|
|
MODULE_AUTHOR("Anthony Huang<anthony.huang@mediatek.com>");
|
|
MODULE_LICENSE("GPL");
|